From ea83d737040a3b6f2bbd8883d89a9d832caa70dd Mon Sep 17 00:00:00 2001 From: Elliot Braem <16282460+elliotBraem@users.noreply.github.com> Date: Tue, 19 Mar 2024 23:14:56 -0600 Subject: [PATCH] Implements infinite scroll (#469) * implements infinite scroll * fix card link height * fix horizontal scroll and clean up * remove hidden * fix donation calculation --- apps/potlock/widget/Project/Card.jsx | 255 ++++---- apps/potlock/widget/Project/ListPage.jsx | 3 +- apps/potlock/widget/Project/ListSection.jsx | 635 +------------------- 3 files changed, 121 insertions(+), 772 deletions(-) diff --git a/apps/potlock/widget/Project/Card.jsx b/apps/potlock/widget/Project/Card.jsx index 692288cc..2f45978f 100644 --- a/apps/potlock/widget/Project/Card.jsx +++ b/apps/potlock/widget/Project/Card.jsx @@ -27,13 +27,18 @@ let DonateSDK = })); DonateSDK = DonateSDK({ env: props.env }); -// console.log("props in Card: ", props); +const CardLink = styled("Link")` + &:hover { + text-decoration: none; + } +`; -const Card = styled.a` +const Card = styled.div` display: flex; flex-direction: column; max-width: 400px; width: 100%; + height: 100%; overflow: hidden; border-radius: 12px; background: white; @@ -41,10 +46,8 @@ const Card = styled.a` border: 1px solid #dbdbdb; margin-left: auto; margin-right: auto; - // height: 500px; transition: all 300ms; - :hover { - text-decoration: none; + &:hover { transform: translateY(-1rem); } `; @@ -392,9 +395,9 @@ const [totalAmountNear, totalDonors] = useMemo(() => { if (!donors.includes(donation.donor_id)) { donors.push(donation.donor_id); } - if (donation.ft_id === "near" || donation.base_currency === "near") { - totalDonationAmountNear = totalDonationAmountNear.plus(new Big(donation.total_amount)); - } + // if (donation.ft_id === "near" || donation.base_currency === "near") { + totalDonationAmountNear = totalDonationAmountNear.plus(new Big(donation.total_amount)); + // } } return [totalDonationAmountNear.toString(), donors.length]; }, [donationsForProject]); @@ -436,152 +439,108 @@ const tags = getTagsFromSocialProfileData(profile); return ( <> - - - - {profile.backgroundImage?.nft ? ( - + + + + + {profile.backgroundImage?.nft ? ( + + ) : ( + background + )} + + + {profile.image?.nft ? ( + + ) : ( + avatar + )} + + + + {name} + + {description.length > MAX_DESCRIPTION_LENGTH + ? description.slice(0, MAX_DESCRIPTION_LENGTH) + "..." + : description} + + {!tags.length ? ( + "No tags" ) : ( - background + + {tags.map((tag, tagIndex) => ( + {tag} + ))} + )} - - - {profile.image?.nft ? ( - + + + + {totalAmountNear ? yoctosToUsdWithFallback(totalAmountNear, true) : "-"} + + Raised + + {payoutDetails && ( + + {payoutDetails.donorCount} + + {payoutDetails.donorCount === 1 ? "Donor" : "Donors"} + + + )} + {props.allowDonate && context.accountId && ( + { + e.preventDefault(); + openDonateModal(); }} - /> - ) : ( - avatar + disabled={!context.accountId} + > + {props.requireVerification ? "Verify to donate" : "Donate"} + )} - - - - {name} - - {description.length > MAX_DESCRIPTION_LENGTH - ? description.slice(0, MAX_DESCRIPTION_LENGTH) + "..." - : description} - - {!tags.length ? ( - "No tags" - ) : ( - - {tags.map((tag, tagIndex) => ( - {tag} - ))} - - )} - - - - {totalAmountNear ? yoctosToUsdWithFallback(totalAmountNear, true) : "-"} - Raised - + {payoutDetails && ( - - {payoutDetails.donorCount} - - {payoutDetails.donorCount === 1 ? "Donor" : "Donors"} - - - )} - {props.allowDonate && context.accountId && ( - { - e.preventDefault(); - openDonateModal(); - }} - disabled={!context.accountId} - > - {props.requireVerification ? "Verify to donate" : "Donate"} - + + Estimated matched amount + + {yoctosToNear(payoutDetails.matchingAmount, true) || "- N"} + + )} - {/* { - props.openDonateToProjectModal(projectId); - }, - }} - /> */} - {/* - {totalDonors || totalDonors === 0 ? totalDonors : "-"} - {totalDonors === 1 ? "Donor" : "Donors"} - */} - - {payoutDetails && ( - - Estimated matched amount - - {yoctosToNear(payoutDetails.matchingAmount, true) || "- N"} - - - )} - {/* {props.allowDonate && ( - - )} */} - {/* {props.requireVerification && ( - - )} */} - + + {state.donateModal.isOpen && ( <>, +}; const items = useMemo(() => { if (shouldShuffle) { @@ -26,483 +12,21 @@ const items = useMemo(() => { return props.items; }, [props.items, shouldShuffle]); -const sortList = [ - "Newest to Oldest", - "Oldest to Newest", - "Most to Least Donations", - "Least to Most Donations", -]; - -const SORT_FILTERS = { - ALL: "All", - NEW_TO_OLD: "Newest to Oldest", - OLD_TO_NEW: "Oldest to Newest", - MOST_TO_LEAST_DONATIONS: "Most to Least Donations", - LEAST_TO_MOST_DONATIONS: "Least to Most Donations", -}; - const PAGE_SIZE = 9; -const featuredProjectIds = ["magicbuild.near", "potlock.near", "yearofchef.near"]; -const featuredProjects = useMemo( - () => props.items.filter((project) => featuredProjectIds.includes(project.id)), - props.items -); - -const [allProjects, setAllProjects] = useState(items); -const [filteredProjects, setFilteredProjects] = useState(items); -const [searchTerm, setSearchTerm] = useState(""); - -// const [elements, setElements] = useState( -// props.items.slice(0, PAGE_SIZE).map((item) => props.renderItem(item)) -// ); - -// const loadMore = () => { -// const newElements = props.items -// .slice(elements.length, elements.length + PAGE_SIZE) -// .map(props.renderItem); -// setElements([...elements, ...newElements]); -// }; - -// const [page, setPage] = useState(1); -// const [lastNumberOfProject, setLastNumberOfProject] = useState(PAGE_SIZE); -// const donationContractId = "donate.potlock.near"; -// const [totalProjects, setTotalProjects] = useState(props.items); -// const [displayProject, setDisplayProject] = useState([]); -// const [lastNumberOfProject, setLastNumberOfProject] = useState(0); -// console.log("lastNumberOfProject", lastNumberOfProject); -// const [searchTerm, setSearchTerm] = useState(null); -// const [tagSelected, setTagSelected] = useState([]); -// const [featuredProjects, setFeaturedProjects] = useState([ -// { -// id: "magicbuild.near", -// status: "Approved", -// submitted_ms: 1698226284754, -// updated_ms: 1698226284754, -// review_notes: null, -// }, -// { -// id: "potlock.near", -// status: "Approved", -// submitted_ms: 1698437495305, -// updated_ms: 1698437495305, -// review_notes: null, -// }, -// { -// id: "yearofchef.near", -// status: "Approved", -// submitted_ms: 1703055390614, -// updated_ms: 1703055390614, -// review_notes: null, -// }, -// ]); -// const [tagsList, setTagsList] = useState([ -// { -// label: "Desci", -// value: "de-sci", -// selected: false, -// }, -// { -// label: "Open Source", -// value: "open-source", -// selected: false, -// }, -// { -// label: "Non Profit", -// value: "non-profit", -// selected: false, -// }, -// { -// label: "Social Impact", -// value: "social-impact", -// selected: false, -// }, -// { -// label: "Climate", -// value: "climate", -// selected: false, -// }, -// { -// label: "Public Good", -// value: "public-good", -// selected: false, -// }, -// { -// label: "Community", -// value: "community", -// selected: false, -// }, -// { -// label: "Education", -// value: "education", -// selected: false, -// }, -// ]); -// const handleTag = (key) => { -// // console.log(tagsList[key].value); -// const tags = tagsList; -// tags[key].selected = !tagsList[key].selected; -// const dataArr = props.items; -// let tagSelected = []; -// tagsList.forEach((tag) => { -// if (tag.selected) { -// tagSelected.push(tag.value); -// } -// }); -// let projectFilterBySearch = []; -// dataArr.forEach((item) => { -// const data = Social.getr(`${item.id}/profile`); -// tagSelected.forEach((tag) => { -// if (data.category == tag) { -// projectFilterBySearch.push(item); -// } -// }); -// }); -// if (tagSelected.length == 0) { -// setTotalProjects(dataArr); -// } else { -// setTotalProjects(projectFilterBySearch); -// } -// // console.log("tagsList", tagSelected); -// setTagSelected(tagSelected); - -// setDisplayProject([]); -// setLastNumberOfProject(0); - -// setTagsList(tags); -// }; -// if (!totalProjects) return "loading"; - -// const loadPotProjects = () => { -// setDisplayProject(totalProjects.map((item) => props.renderItem(item))); -// }; - -// const loadProjects = () => { -// setLastNumberOfProject(lastNumberOfProject + 9); -// setDisplayProject( -// totalProjects.slice(0, lastNumberOfProject + 9).map((item) => ( -// { -// removeProjectsFromCart(projectId); -// }, -// addProjectsToCart: (project) => { -// addProjectsToCart(project); -// }, -// setIsCartModalOpen: (isOpen) => { -// setIsCartModalOpen(isOpen); -// }, -// setAmount: (value) => { -// setAmount(value); -// }, -// setProjectId: (id) => { -// setProjectId(id); -// }, -// setNote: (note) => { -// setNote(note); -// }, -// setReferrerId: (ref) => { -// setReferrerId(ref); -// }, -// setCurrency: (cur) => { -// setCurrency(cur); -// }, -// totalAmount: (donations) => totalAmount(donations), -// }} -// key={key} -// /> -// )) -// ); -// }; - -// const totalAmount = (donations) => { -// if (!donations) return 0; -// let totalDonationAmount = new Big(0); -// for (const donation of donations) { -// totalDonationAmount = totalDonationAmount.plus(new Big(donation.total_amount)); -// } -// return props.nearToUsd -// ? (props.nearToUsd * totalDonationAmount.div(1e24).toNumber()).toFixed(2) -// : totalDonationAmount.div(1e24).toNumber().toFixed(2); -// }; - -// const sortHighestToLowest = (projects) => { -// const sort = (a, b) => { -// return parseFloat(b.total) - parseFloat(a.total); -// }; -// const projectLength = projects.length; - -// for (let i = 0; i < projectLength - 1; i++) { -// for (let j = 0; j < projectLength - 1 - i; j++) { -// if (sort(projects[j], projects[j + 1]) > 0) { -// const temp = projects[j]; -// projects[j] = projects[j + 1]; -// projects[j + 1] = temp; -// } -// } -// } - -// setTotalProjects(projects); -// setDisplayProject([]); -// setLastNumberOfProject(0); -// }; - -// const sortLowestToHighest = (projects) => { -// const sort = (a, b) => { -// return parseFloat(b.total) - parseFloat(a.total); -// }; -// const projectLength = projects.length; - -// for (let i = 0; i < projectLength - 1; i++) { -// for (let j = 0; j < projectLength - 1 - i; j++) { -// if (sort(projects[j], projects[j + 1]) < 0) { -// const temp = projects[j]; -// projects[j] = projects[j + 1]; -// projects[j + 1] = temp; -// } -// } -// } - -// setTotalProjects(projects); -// setDisplayProject([]); -// setLastNumberOfProject(0); -// }; - -// const sortNewToOld = (projects) => { -// const projectLength = projects.length; - -// for (let i = 0; i < projectLength - 1; i++) { -// for (let j = 0; j < projectLength - i - 1; j++) { -// if (projects[j].submitted_ms < projects[j + 1].submitted_ms) { -// const temp = projects[j]; -// projects[j] = projects[j + 1]; -// projects[j + 1] = temp; -// } -// } -// } -// setTotalProjects(projects); -// setDisplayProject([]); -// setLastNumberOfProject(0); -// }; - -// const sortOldToNew = (projects) => { -// const projectLength = projects.length; - -// for (let i = 0; i < projectLength - 1; i++) { -// for (let j = 0; j < projectLength - i - 1; j++) { -// if (projects[j].submitted_ms > projects[j + 1].submitted_ms) { -// const temp = projects[j]; -// projects[j] = projects[j + 1]; -// projects[j + 1] = temp; -// } -// } -// } -// setTotalProjects(projects); -// setDisplayProject([]); -// setLastNumberOfProject(0); -// }; - -// const searchByWordsPots = (projects, searchTerm) => { -// let findId = []; -// const dataArr = props.items; -// const allData = []; -// dataArr.forEach((item) => { -// const data = props.itemsAll[item.id]; -// allData.push(data); -// if (data) { -// if ( -// data.pot_description.toLowerCase().includes(searchTerm.toLowerCase()) || -// data.pot_name.toLowerCase().includes(searchTerm.toLowerCase()) -// ) { -// findId.push(item.id); -// } -// } -// }); -// let projectFilterBySearch = []; -// dataArr.forEach((project) => { -// findId.forEach((id) => { -// if (project.id == id) { -// projectFilterBySearch.push(project); -// } -// }); -// }); -// setTotalProjects(projectFilterBySearch); -// setDisplayProject([]); -// setLastNumberOfProject(0); -// }; -// const searchByWordsPot = (projects, searchTerm) => { -// let findId = []; -// const dataArr = props.items; -// let alldata = []; -// dataArr.forEach((item) => { -// const data = Social.getr(`${item.project_id}/profile`); -// alldata.push(data); -// if (data) { -// if ( -// data.description.toLowerCase().includes(searchTerm.toLowerCase()) || -// data.name.toLowerCase().includes(searchTerm.toLowerCase()) -// ) { -// findId.push(item.project_id); -// } -// } -// }); -// let projectFilterBySearch = []; -// dataArr.forEach((project) => { -// const data = Social.getr(`${project.project_id}/profile`); -// findId.forEach((id) => { -// if (tagSelected.length > 0) { -// if (data.category == tagSelected[0]) { -// if (project.project_id == id) { -// projectFilterBySearch.push(project); -// } -// } -// } else { -// if (project.project_id == id) { -// projectFilterBySearch.push(project); -// } -// } -// }); -// }); - -// setTotalProjects(projectFilterBySearch); -// setDisplayProject([]); -// setLastNumberOfProject(0); -// }; -// const searchByWords = (projects, searchTerm) => { -// let findId = []; -// const dataArr = props.items; -// let alldata = []; -// dataArr.forEach((item) => { -// const data = Social.getr(`${item.id}/profile`); -// alldata.push(data); -// if (data) { -// if ( -// data.description.toLowerCase().includes(searchTerm.toLowerCase()) || -// data.name.toLowerCase().includes(searchTerm.toLowerCase()) -// ) { -// findId.push(item.id); -// } -// } -// }); -// let projectFilterBySearch = []; -// dataArr.forEach((project) => { -// const data = Social.getr(`${project.id}/profile`); -// findId.forEach((id) => { -// if (tagSelected.length > 0) { -// if (data.category == tagSelected[0]) { -// if (project.id == id) { -// projectFilterBySearch.push(project); -// } -// } -// } else { -// if (project.id == id) { -// projectFilterBySearch.push(project); -// } -// } -// }); -// }); - -// setTotalProjects(projectFilterBySearch); -// setDisplayProject([]); -// setLastNumberOfProject(0); -// }; - -// useEffect(() => { -// const newTotalProjects = []; -// const promises = totalProjects.map((project) => { -// return Near.asyncView(donationContractId, "get_donations_for_recipient", { -// recipient_id: project.id, -// }).then((res) => { -// const total = totalAmount(res); -// newTotalProjects.push({ ...project, total }); -// }); -// }); -// Promise.all(promises).then(() => { -// setTotalProjects(newTotalProjects); -// }); -// }, []); - -// const handleSortChange = (sortType) => { -// switch (sortType) { -// case "Newest to Oldest": -// sortNewToOld(totalProjects); -// break; -// case "Oldest to Newest": -// sortOldToNew(totalProjects); -// break; -// case "Most to Least Donations": -// sortHighestToLowest(totalProjects); -// break; -// case "Least to Most Donations": -// sortLowestToHighest(totalProjects); -// break; -// } -// }; - -const searchProjects = (searchTerm) => { - // filter projects that match the search term (just id for now) - const filteredProjects = allProjects.filter((project) => { - const { id, status } = project; - const searchFields = [id, status]; - return searchFields.some((field) => field.toLowerCase().includes(searchTerm.toLowerCase())); - }); - return filteredProjects; -}; - -const sortProjects = (sortVal) => { - if (sortVal === SORT_FILTERS.ALL) { - return searchApplications(searchTerm); - } else if (sortVal === SORT_FILTERS.NEW_TO_OLD) { - const sorted = { ...allProjects }; - sorted.sort((a, b) => b.submitted_ms - a.submitted_ms); - return sorted; - } else if (sortVal === SORT_FILTERS.OLD_TO_NEW) { - const sorted = { ...allProjects }; - sorted.sort((a, b) => a.submitted_ms - b.submitted_ms); - return sorted; - } else if (sortVal === SORT_FILTERS.MOST_TO_LEAST_DONATIONS) { - const sorted = { ...allProjects }; - sorted.sort((a, b) => b.total - a.total); - return sorted; - } else if (sortVal === SORT_FILTERS.LEAST_TO_MOST_DONATIONS) { - const sorted = { ...allProjects }; - sorted.sort((a, b) => a.total - b.total); - return sorted; - } - return filtered; -}; - -const Container = styled.div` - display: flex; - flex-direction: column; +const Grid = styled.div` + display: grid; width: 100%; - gap: 48px; + padding-top: 20px; - // @media screen and (min-width: 740px) and (max-width: 1400px) { - // ${props.tab !== "pot" && "padding-top: 120px;"} - // } - @media screen and (max-width: 739px) { - ${props.tab !== "pot" && "padding-top: 40px;"} - } -`; + padding-bottom: 32px; -const ProjectList = styled.div` - display: grid; gap: 31px; + // For mobile devices (1 column) @media screen and (max-width: 739px) { grid-template-columns: repeat(1, 1fr); + ${props.tab !== "pot" && "padding-top: 40px;"} } // For tablet devices (2 columns) @@ -524,139 +48,4 @@ const ProjectList = styled.div` )} `; -const Header = styled.div` - display: flex; - flex-direction: column; - gap: 20px; - width: 100%; -`; - -const Title = styled.div` - color: #292929; - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: 24px; - letter-spacing: 1.12px; - text-transform: uppercase; -`; - -const TagsWrapper = styled.div` - display: flex; - width: 100%; - align-items: center; - gap: 12px; - flex-wrap: wrap; - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: 24px; - color: #292929; -`; - -const Tag = styled.div` - display: flex; - padding: 8px; - justify-content: center; - align-items: center; - gap: 8px; - border-radius: 4px; - background: #fff; - box-shadow: 0px -1px 0px 0px #c7c7c7 inset, 0px 0px 0px 0.5px #c7c7c7; - border: 1px solid #c7c7c7; - &:hover { - background: #fef6ee; - } -`; - -const OnBottom = styled.div` - width: 100%; - display: flex; - align-items: center; - justify-content: center; - padding: 20px 0; -`; - -// const elements = props.items.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE).map(props.renderItem); - -return ( - <> - {/* {tab != "pots" && tab != "pot" && ( - -
- Featured projects -
- - {featuredProjects.map(props.renderItem)} - -
- )} */} - - {/*
- - all {tab == "pots" ? "pots" : "projects"} - <span style={{ color: "#DD3345", marginLeft: "8px", fontWeight: 600 }}> - {props.items.length} - </span> - - { - const results = searchProjects(value); - setFilteredProjects(results); - }, - handleSortChange: (filter) => { - const sorted = sortProjects(filter); - handleSortChange(filter); - }, - }} - /> - {tab != "pots" && tab != "pot" && ( - - Tags: - {tagsList.map((tag, key) => ( - handleTag(key)} - className={`${ - tag.selected && "gap-2 bg-[#FEF6EE]" - } p-2 rounded border text-sm flex items-center cursor-pointer`} - > - {tag.selected && ( - - - - )} - {tag.label} - - ))} - - )} -
*/} - {items.map(props.renderItem)} - {/* elements.length} - // useWindow={false} - > - {elements} - */} -
- -); +return ;