From 915712919e1d041fe11ff70f5c83697df2d9cdc2 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Fri, 17 Jan 2025 13:28:03 +0100 Subject: [PATCH 01/18] Fix/Social connection after same removed --- .../views/verification/SocialProfile.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/views/verification/SocialProfile.tsx b/src/components/views/verification/SocialProfile.tsx index 1204cb0cba..882ab2d3d7 100644 --- a/src/components/views/verification/SocialProfile.tsx +++ b/src/components/views/verification/SocialProfile.tsx @@ -13,7 +13,7 @@ import { import styled from 'styled-components'; import { useRouter } from 'next/router'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useState } from 'react'; import { Shadow } from '@/components/styled-components/Shadow'; import DiscordIcon from '/public/images/icons/social/discord.svg'; import LinkedinIcon from '/public/images/icons/social/linkedin.svg'; @@ -48,16 +48,16 @@ const SocialProfile = () => { [verificationData], ); - const [twitterData, setTwitterData] = useState( - useMemo(() => findSocialMedia('twitter'), [findSocialMedia]), + const [twitterData, setTwitterData] = useState(() => + findSocialMedia('twitter'), ); - - const [linkedinData, setLinkedinData] = useState( - useMemo(() => findSocialMedia('linkedin'), [findSocialMedia]), + const [linkedinData, setLinkedinData] = useState(() => + findSocialMedia('linkedin'), ); - const [discordData, setDiscordData] = useState( - useMemo(() => findSocialMedia('discord'), [findSocialMedia]), + const [discordData, setDiscordData] = useState(() => + findSocialMedia('discord'), ); + async function handleSocialSubmit( socialNetwork: string, notAuthorized: boolean, @@ -114,7 +114,7 @@ const SocialProfile = () => { // Loop through the states to find and update the matching one Object.keys(stateHandlers).forEach(key => { if (id === Number(findSocialMedia(key)?.id)) { - stateHandlers[key](null); + stateHandlers[key](undefined); } }); From fc95b16ebaad0f99ae661f63f048bf447af8d30b Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 21 Jan 2025 11:42:01 +0100 Subject: [PATCH 02/18] fixing 8 decimals --- .../donate/Recurring/ModifySuperToken/DepositSuperToken.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx b/src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx index 07a26d23a9..c54e796dc5 100644 --- a/src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx +++ b/src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx @@ -135,7 +135,7 @@ export const DepositSuperToken: FC = ({ } else { superTokenAsset = await sf.loadWrapperSuperToken(superToken.id); } - if (token && token.decimals === 6) { + if (token && (token.decimals === 6 || token.decimals === 8)) { const divisor = BigInt(10 ** token.decimals); const currentAmount = Number(amount) / Number(divisor); newAmount = ethers.utils From 318d2c79a159b0a0baa977bb12a240797d41e481 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 21 Jan 2025 11:54:03 +0100 Subject: [PATCH 03/18] fixing 8 decimals deposit --- .../donate/Recurring/ModifySuperToken/DepositSuperToken.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx b/src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx index c54e796dc5..e6e1b13646 100644 --- a/src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx +++ b/src/components/views/donate/Recurring/ModifySuperToken/DepositSuperToken.tsx @@ -139,7 +139,7 @@ export const DepositSuperToken: FC = ({ const divisor = BigInt(10 ** token.decimals); const currentAmount = Number(amount) / Number(divisor); newAmount = ethers.utils - .parseUnits(currentAmount.toString(), 18) + .parseUnits(currentAmount.toFixed(8), 18) .toBigInt(); } const upgradeOperation = await superTokenAsset.upgrade({ From c88457eaa2d12fd83bf87f1252c291e68521e941 Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 21 Jan 2025 08:22:18 -0500 Subject: [PATCH 04/18] Update regex.ts --- src/lib/constants/regex.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/constants/regex.ts b/src/lib/constants/regex.ts index 9e0fc3f5f4..fa06a1e48e 100644 --- a/src/lib/constants/regex.ts +++ b/src/lib/constants/regex.ts @@ -50,55 +50,55 @@ export const validators = { facebook: { pattern: { value: regexList.facebook, - message: 'Invalid facebook URL', + message: 'Invalid Facebook URL', }, }, linkedin: { pattern: { value: regexList.linkedin, - message: 'Invalid linkedIn URL', + message: 'Invalid LinkedIn URL', }, }, instagram: { pattern: { value: regexList.instagram, - message: 'Invalid instagram URL', + message: 'Invalid Instagram URL', }, }, youtube: { pattern: { value: regexList.youtube, - message: 'Invalid youtube URL', + message: 'Invalid Youtube URL', }, }, reddit: { pattern: { value: regexList.reddit, - message: 'Invalid reddit URL', + message: 'Invalid Reddit URL', }, }, farcaster: { pattern: { value: regexList.website, - message: 'Invalid reddit URL', + message: 'Invalid Farcaster URL', }, }, lens: { pattern: { value: regexList.website, - message: 'Invalid reddit URL', + message: 'Invalid Lens URL', }, }, discord: { pattern: { value: regexList.discord, - message: 'Invalid discord URL', + message: 'Invalid Discord URL', }, }, telegram: { pattern: { value: regexList.telegram, - message: 'Invalid telegram URL', + message: 'Invalid Telegram URL', }, }, whatsapp: { From 4b02a86c38c7150c40ee33844987f649c1db60d0 Mon Sep 17 00:00:00 2001 From: Moe Shehab <52987806+mhmdksh@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:16:15 +0400 Subject: [PATCH 05/18] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5ba0e3cc89..c5d43391ad 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ # The Giveth DApp - ### Build status: - Develop - [![build-develop](https://github.com/Giveth/giveth-dapps-v2/actions/workflows/Build.yml/badge.svg?branch=develop)](https://github.com/Giveth/giveth-dapps-v2/actions/workflows/Build.yml) - Main - [![build-main](https://github.com/Giveth/giveth-dapps-v2/actions/workflows/Build.yml/badge.svg?branch=main)](https://github.com/Giveth/giveth-dapps-v2/actions/workflows/Build.yml) From 4e48fde864c47ad76973c346b52bb2cb7f414875 Mon Sep 17 00:00:00 2001 From: Moe Shehab <52987806+mhmdksh@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:58:42 +0400 Subject: [PATCH 06/18] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c5d43391ad..5ba0e3cc89 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # The Giveth DApp + ### Build status: - Develop - [![build-develop](https://github.com/Giveth/giveth-dapps-v2/actions/workflows/Build.yml/badge.svg?branch=develop)](https://github.com/Giveth/giveth-dapps-v2/actions/workflows/Build.yml) - Main - [![build-main](https://github.com/Giveth/giveth-dapps-v2/actions/workflows/Build.yml/badge.svg?branch=main)](https://github.com/Giveth/giveth-dapps-v2/actions/workflows/Build.yml) From 587fea0274339b838dc3697e94bd51161901f0c8 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 21 Jan 2025 16:58:50 +0100 Subject: [PATCH 07/18] Fix sitemap generator changing handling data from BE side --- pages/api/generate-sitemap.ts | 109 ++++++++-------------------------- 1 file changed, 26 insertions(+), 83 deletions(-) diff --git a/pages/api/generate-sitemap.ts b/pages/api/generate-sitemap.ts index 305e39006c..26d7ec6329 100644 --- a/pages/api/generate-sitemap.ts +++ b/pages/api/generate-sitemap.ts @@ -2,20 +2,17 @@ import fs from 'fs'; import path from 'path'; import { NextApiRequest, NextApiResponse } from 'next'; import { User } from '@sentry/types'; -import { initializeApollo } from '@/apollo/apolloClient'; -import { OPTIONS_HOME_PROJECTS } from '@/apollo/gql/gqlOptions'; -import { FETCH_ALL_PROJECTS } from '@/apollo/gql/gqlProjects'; -import { EProjectsSortBy } from '@/apollo/types/gqlEnums'; -import { getMainCategorySlug } from '@/helpers/projects'; import { escapeXml } from '@/helpers/xml'; import { IProject, IQFRound } from '@/apollo/types/types'; -import { FETCH_QF_ROUNDS_QUERY } from '@/apollo/gql/gqlQF'; -import { FETCH_ALL_USERS_BASIC_DATA } from '@/apollo/gql/gqlUser'; import { addressToUserView } from '@/lib/routeCreators'; import { shortenAddress } from '@/lib/helpers'; export const config = { - maxDuration: 300, + api: { + bodyParser: { + sizeLimit: '10mb', + }, + }, }; const URL = process.env.NEXT_PUBLIC_FRONTEND_LINK; @@ -112,8 +109,10 @@ export default async function handler( ) { const authHeader = req.headers['authorization']; + console.log('API route /api/generate-sitemap was called'); + // Only allow GET requests - if (req.method !== 'GET') { + if (req.method !== 'POST') { return res.status(405).end(); } @@ -122,18 +121,15 @@ export default async function handler( } try { - /* PROJECT SITEMAP */ + // Parse the POST data from the request body + const { projects, users, qfRounds } = req.body; - // Get first project data - const projectData = await getProjects(0); - - const projects: IProject[] = projectData.allProjects?.projects || []; + /* PROJECT SITEMAP */ - if (projectData.allProjects.totalCount > 50) { - for (let i = 50; i < projectData.allProjects.totalCount; i += 50) { - const fetchNewProjectData = await getProjects(i); - projects.push(...fetchNewProjectData.allProjects?.projects); - } + if (!projects || !Array.isArray(projects)) { + return res + .status(400) + .json({ message: 'Invalid request payload projects' }); } // Generate XML content @@ -152,11 +148,14 @@ export default async function handler( /* QF ARCHIVED ROUNDS SITEMAP */ - // Get first project data - const roundsData = await getArchivedRounds(); + if (!qfRounds || !Array.isArray(qfRounds)) { + return res + .status(400) + .json({ message: 'Invalid request payload qfRounds' }); + } // // Generate XML content - const sitemapRoundsContent = generateQFRoundsSiteMap(roundsData); + const sitemapRoundsContent = generateQFRoundsSiteMap(qfRounds); // Define the file path const filePathQFRounds = path.join( @@ -175,21 +174,14 @@ export default async function handler( /* USER SITEMAP */ - // Fetch user data - const users = await getUsers(0); - const userTotalCount = users.totalCount; - const userEntries = [...users.users]; - - // Fetch remaining users if necessary - if (userTotalCount > 50) { - for (let i = 50; i < userTotalCount; i += 50) { - const nextBatch = await getUsers(i); - userEntries.push(...nextBatch.users); - } + if (!users || !Array.isArray(users)) { + return res + .status(400) + .json({ message: 'Invalid request payload users' }); } // Generate XML content for users - const sitemapUsersContent = generateUsersSiteMap(userEntries); + const sitemapUsersContent = generateUsersSiteMap(users); // Define the file path for users sitemap const filePathUsers = path.join( @@ -215,52 +207,3 @@ export default async function handler( res.status(500).json({ error: 'Failed to generate sitemap' }); } } - -// Fetch project data from GraphQL -async function getProjects(skip: number) { - const apolloClient = initializeApollo(); - const slug = 'all'; - const { variables, notifyOnNetworkStatusChange } = OPTIONS_HOME_PROJECTS; - - const { data } = await apolloClient.query({ - query: FETCH_ALL_PROJECTS, - variables: { - ...variables, - limit: 50, - skip: skip, - sortingBy: EProjectsSortBy.INSTANT_BOOSTING, - mainCategory: getMainCategorySlug({ slug }), - notifyOnNetworkStatusChange, - }, - fetchPolicy: 'no-cache', - }); - - return data; -} - -// Fetch qf archived rounds data from GraphQL -async function getArchivedRounds() { - const apolloClient = initializeApollo(); - - const { data } = await apolloClient.query({ - query: FETCH_QF_ROUNDS_QUERY, - }); - - return data.qfRounds || []; -} - -// Fetch user data from GraphQL -async function getUsers(skip: number) { - const apolloClient = initializeApollo(); - - const { data } = await apolloClient.query({ - query: FETCH_ALL_USERS_BASIC_DATA, // Query for user data - variables: { - limit: 50, - skip: skip, - }, - fetchPolicy: 'no-cache', - }); - - return data.allUsersBasicData || { users: [], totalCount: 0 }; -} From c12cf3ce9238578a04f615e820f9b2b04658ac82 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Wed, 22 Jan 2025 18:53:00 +0100 Subject: [PATCH 08/18] Feat/Add estimated matching time --- lang/ca.json | 2 + lang/en.json | 2 + lang/es.json | 2 + public/images/icons/clock.svg | 11 ++++ src/apollo/gql/gqlProjects.ts | 2 + src/apollo/types/types.ts | 1 + src/components/modals/HarvestAll.sc.tsx | 3 +- src/components/project-card/ProjectCard.tsx | 38 ++++++++++++ .../donate/OneTime/EstimatedMatchingToast.tsx | 36 ++++++++++++ .../project/projectActionCard/QFSection.tsx | 58 +++++++++++++++++++ .../projectDonations/ProjectTotalFundCard.tsx | 35 ++++++++++- src/helpers/time.ts | 22 +++++++ 12 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 public/images/icons/clock.svg diff --git a/lang/ca.json b/lang/ca.json index 838d88b666..51d448f4cf 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -67,6 +67,7 @@ "component.pro_guide.tips.social_media.item3": "Giveth podria utilitzar els teus noms d'usuari a les xarxes socials per contactar-te o etiquetar-te en publicacions per suportar la teva recaptació de fons.", "component.qf-section.tooltip": "Aquesta estimació és els fons d'emparellament que aquest projecte rebrà si la ronda acabés ara, sense tenir en compte l'anàlisi de frau.", "component.qf-section.tooltip_polygon": "Aquesta estimació representa els fons coincidents que rebria aquest projecte si la ronda es tanqués ara, ignorant l'anàlisi del frau.", + "component.qf-section.estimated_time": "Càlcul de coincidència actualitzat fa {time} minuts", "component.qf_middle_banner.desc": " Amb FQ, el nombre de donants compta més que la quantitat donada. Dona als projectes participants en la ronda i aconsegueix que les teves donacions siguin emparellades amb el poder del finançament quadràtic!", "component.qf_middle_banner.title": "Finançament Quadràtic", "component.regenstream_card.harvest_caption": "Utilitza el botó de Recollida per reclamar recompenses líquides d'aquest RegenStream", @@ -556,6 +557,7 @@ "label.how_do_you_want_to_donate": "Com vols donar?", "label.how_it_works": "Com Funciona", "label.how_it_works?": "Com funciona?", + "label.last_updated_ago": "Última actualització: fa {time} minuts", "label.how_referrals_work": "Com funcionen les referències", "label.how_to_buy": "Com comprar", "label.how_to_refer_your_friends": "Com referir als teus amics", diff --git a/lang/en.json b/lang/en.json index 479e12339b..17d46429c6 100644 --- a/lang/en.json +++ b/lang/en.json @@ -67,6 +67,7 @@ "component.pro_guide.tips.social_media.item3": "Giveth might use your social media handles to contact you or tag you on posts to support your fundraising.", "component.qf-section.tooltip": "This estimation is the matching funds that this project would get if the round ended now, disregarding fraud analysis.", "component.qf-section.tooltip_polygon": "This estimate represents the matching funds this project would receive if the round closed now, ignoring the fraud analysis.", + "component.qf-section.estimated_time": "Estimated matching updated {time} min ago", "component.qf_middle_banner.desc": "With QF, the number of donors matters more than the amout donated. Donate to participating projects in the round and get your donations matched with the power of quadratic funding!", "component.qf_middle_banner.title": "Quadratic Funding", "component.regenstream_card.harvest_caption": "Use the Harvest button to claim liquid rewards from this RegenStream", @@ -556,6 +557,7 @@ "label.how_do_you_want_to_donate": "How do you want to donate?", "label.how_it_works": "How It Works", "label.how_it_works?": "How it works?", + "label.last_updated_ago": "Last updated: {time} min ago", "label.how_referrals_work": "How referrals work", "label.how_to_buy": "How to buy", "label.how_to_refer_your_friends": "How to refer your friends", diff --git a/lang/es.json b/lang/es.json index 955fd25553..bd7b8e0405 100644 --- a/lang/es.json +++ b/lang/es.json @@ -67,6 +67,7 @@ "component.pro_guide.tips.social_media.item3": "Giveth podría usar tus identificadores de redes sociales para contactarte o etiquetarte en publicaciones para apoyar tu recaudación de fondos.", "component.qf-section.tooltip": "Esta estimación es la cantidad de fondos complementarios que este proyecto recibiría si la ronda terminara ahora, sin tener en cuenta el análisis de fraude.", "component.qf-section.tooltip_polygon": "Esta estimación representa los fondos de contrapartida que recibiría este proyecto si la ronda se cerrara ahora, ignorando el análisis de fraude.", + "component.qf-section.estimated_time": "Cálculo de coincidencias actualizado hace {time} minutos", "component.qf_middle_banner.desc": "Con FC, el número de donantes importa más que la cantidad donada. ¡Dona a los proyectos participantes en la ronda y obtén tus donaciones emparejadas con el poder de la financiación cuadrática!", "component.qf_middle_banner.title": "Financiación Cuadrática", "component.regenstream_card.harvest_caption": "Utiliza el botón de Cosecha para reclamar recompensas líquidas de este RegenStream", @@ -554,6 +555,7 @@ "label.how_do_you_want_to_donate": "¿Cómo quieres donar?", "label.how_it_works": "Cómo Funciona", "label.how_it_works?": "¿Cómo funciona?", + "label.last_updated_ago": "Última actualización: hace {time} minutos", "label.how_referrals_work": "Cómo funcionan las referencias", "label.how_to_buy": "Cómo comprar", "label.how_to_refer_your_friends": "Como referir a tus amigos", diff --git a/public/images/icons/clock.svg b/public/images/icons/clock.svg new file mode 100644 index 0000000000..ffc8fc7a77 --- /dev/null +++ b/public/images/icons/clock.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/apollo/gql/gqlProjects.ts b/src/apollo/gql/gqlProjects.ts index 5e6008f9de..edc88d371c 100644 --- a/src/apollo/gql/gqlProjects.ts +++ b/src/apollo/gql/gqlProjects.ts @@ -21,6 +21,7 @@ export const PROJECT_CORE_FIELDS = gql` allocatedFundUSDPreferred allocatedFundUSD qfStrategy + clusterMatchingSyncAt } } `; @@ -294,6 +295,7 @@ export const FETCH_PROJECT_BY_SLUG_SINGLE_PROJECT = gql` allocatedFundUSDPreferred allocatedFundUSD qfStrategy + clusterMatchingSyncAt } campaigns { id diff --git a/src/apollo/types/types.ts b/src/apollo/types/types.ts index 9736b13dc3..bd407312ad 100644 --- a/src/apollo/types/types.ts +++ b/src/apollo/types/types.ts @@ -516,6 +516,7 @@ export interface IQFRound { minimumValidUsdValue?: number; minMBDScore: number; qfStrategy: QfStrategyEnum; + clusterMatchingSyncAt: string; } export interface IArchivedQFRound extends IQFRound { diff --git a/src/components/modals/HarvestAll.sc.tsx b/src/components/modals/HarvestAll.sc.tsx index 428b89ca57..371648d099 100644 --- a/src/components/modals/HarvestAll.sc.tsx +++ b/src/components/modals/HarvestAll.sc.tsx @@ -87,7 +87,8 @@ export const HarvestAllPending = styled(Pending)` export const TooltipContent = styled(Subline)` ${mediaQueries.tablet} { - width: 200px; + width: 250px; + text-align: left; } `; diff --git a/src/components/project-card/ProjectCard.tsx b/src/components/project-card/ProjectCard.tsx index 6e3ec3ecf3..43468eb0ec 100644 --- a/src/components/project-card/ProjectCard.tsx +++ b/src/components/project-card/ProjectCard.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; import styled from 'styled-components'; +import Image from 'next/image'; import { P, H6, @@ -36,6 +37,7 @@ import { TooltipContent } from '@/components/modals/HarvestAll.sc'; import { IconWithTooltip } from '@/components/IconWithToolTip'; import { FETCH_RECURRING_DONATIONS_BY_DATE } from '@/apollo/gql/gqlProjects'; import { client } from '@/apollo/apolloClient'; +import { calculateQFTimeDifferences } from '@/helpers/time'; const cardRadius = '12px'; const imgHeight = '226px'; @@ -106,8 +108,13 @@ const ProjectCard = (props: IProjectCard) => { allocatedFundUSD, allocatedTokenSymbol, qfStrategy, + clusterMatchingSyncAt, } = activeQFRound || {}; + const clusterMatchingSyncAtDiff = calculateQFTimeDifferences( + clusterMatchingSyncAt || '', + ); + const projectLink = slugToProjectView(slug); const donateLink = slugToProjectDonate(slug); @@ -330,6 +337,24 @@ const ProjectCard = (props: IProjectCard) => { {formatMessage({ id: 'component.qf-section.tooltip_polygon', })} + + score + {formatMessage( + { + id: 'component.qf-section.estimated_time', + }, + { + time: clusterMatchingSyncAtDiff, + }, + )} + @@ -593,4 +618,17 @@ const QFBadge = styled(Subline)` align-items: center; `; +const ToolTipBellow = styled.div` + display: flex; + align-items: center; + justify-content: flex-start; + border-top: 1px solid #121848; + margin: 7px 0; + padding: 7px 0 0 0; + line-height: 16px; + & img { + margin: 0 6px 0 0; + } +`; + export default ProjectCard; diff --git a/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx b/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx index 76db0f598c..0a17789214 100644 --- a/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx +++ b/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx @@ -1,4 +1,5 @@ import styled from 'styled-components'; +import Image from 'next/image'; import { B, Caption, @@ -20,6 +21,7 @@ import { import { IProjectAcceptedToken } from '@/apollo/types/gqlTypes'; import { formatDonation } from '@/helpers/number'; import { truncateToDecimalPlaces } from '@/lib/helpers'; +import { calculateQFTimeDifferences } from '@/helpers/time'; interface IEstimatedMatchingToast { projectData: IProject; @@ -49,8 +51,13 @@ const EstimatedMatchingToast: FC = ({ allocatedFundUSD, allocatedTokenSymbol, maximumReward, + clusterMatchingSyncAt, } = activeStartedRound || {}; + const clusterMatchingSyncAtDiff = calculateQFTimeDifferences( + clusterMatchingSyncAt || '', + ); + const decimals = isStellar ? 18 : token?.decimals || 18; const amountInUsd = (tokenPrice || 0) * @@ -86,6 +93,22 @@ const EstimatedMatchingToast: FC = ({ {formatMessage({ id: 'component.qf-section.tooltip_polygon', })} + + score + {formatMessage( + { + id: 'component.qf-section.estimated_time', + }, + { + time: clusterMatchingSyncAtDiff, + }, + )} + @@ -106,4 +129,17 @@ const Wrapper = styled.div<{ show?: boolean }>` opacity: ${({ show }) => (show ? 1 : 0)}; `; +const ToolTipBellow = styled.div` + display: flex; + align-items: center; + justify-content: flex-start; + border-top: 1px solid #121848; + margin: 7px 0; + padding: 7px 0 0 0; + line-height: 16px; + & img { + margin: 0 6px 0 0; + } +`; + export default EstimatedMatchingToast; diff --git a/src/components/views/project/projectActionCard/QFSection.tsx b/src/components/views/project/projectActionCard/QFSection.tsx index 352e987be3..9d0a1705ba 100644 --- a/src/components/views/project/projectActionCard/QFSection.tsx +++ b/src/components/views/project/projectActionCard/QFSection.tsx @@ -15,6 +15,7 @@ import { } from '@giveth/ui-design-system'; import { useIntl } from 'react-intl'; import styled from 'styled-components'; +import Image from 'next/image'; import { type FC } from 'react'; import { useRouter } from 'next/router'; import Link from 'next/link'; @@ -35,6 +36,7 @@ import { ProjectCardUserName } from '@/components/project-card/ProjectCardUserNa import { CustomH5 } from '@/components/setProfilePic/SetProfilePic'; import { ORGANIZATION } from '@/lib/constants/organizations'; import { slugToProjectView } from '@/lib/routeCreators'; +import { calculateQFTimeDifferences } from '@/helpers/time'; interface IQFSectionProps { projectData?: IProject; @@ -64,6 +66,7 @@ const QFSection: FC = ({ projectData }) => { allocatedFundUSD, allocatedTokenSymbol, allocatedFundUSDPreferred, + clusterMatchingSyncAt, } = activeStartedRound || {}; const totalEstimatedMatching = calculateTotalEstimatedMatching( projectDonationsSqrtRootSum, @@ -72,6 +75,15 @@ const QFSection: FC = ({ projectData }) => { activeStartedRound?.maximumReward, ); + // const { clusterMatchingSyncAt } = activeQFRound || {}; + console.log({ activeStartedRound }); + + console.log('activeQFRound', clusterMatchingSyncAt); + + const clusterMatchingSyncAtDiff = calculateQFTimeDifferences( + clusterMatchingSyncAt || '', + ); + const projectLink = slugToProjectView(slug!); const orgLabel = organization?.label; @@ -104,6 +116,22 @@ const QFSection: FC = ({ projectData }) => { {formatMessage({ id: 'component.qf-section.tooltip_polygon', })} + + score + {formatMessage( + { + id: 'component.qf-section.estimated_time', + }, + { + time: clusterMatchingSyncAtDiff, + }, + )} + @@ -261,6 +289,16 @@ const QFSection: FC = ({ projectData }) => { | Next update in: 3 min */} + + {formatMessage( + { + id: 'label.last_updated_ago', + }, + { + time: clusterMatchingSyncAtDiff, + }, + )} + { allocatedFundUSDPreferred, allocatedFundUSD, allocatedTokenSymbol, + clusterMatchingSyncAt, } = selectedQF || {}; + const clusterMatchingSyncAtDiff = calculateQFTimeDifferences( + clusterMatchingSyncAt || '', + ); + const selectedQFData = qfRounds?.find(round => round.id === selectedQF?.id); const notDistributedFund = @@ -218,7 +224,11 @@ const ProjectTotalFundCard = ({ selectedQF }: IProjectTotalFundCardProps) => { ) : ( - + +{' '} {formatDonation( @@ -237,6 +247,16 @@ const ProjectTotalFundCard = ({ selectedQF }: IProjectTotalFundCardProps) => { ? 'Estimated Matching' : 'Matching Funds'} + + {formatMessage( + { + id: 'label.last_updated_ago', + }, + { + time: clusterMatchingSyncAtDiff, + }, + )} + {qfRoundHistory?.distributedFundTxHash && @@ -351,8 +371,10 @@ const EstimatedMatchingTransaction = styled.div` `; const EstimatedMatchingPrice = styled(H5)` + align-self: center; color: ${semanticColors.jade[600]}; font-weight: 700; + line-height: 1.1; `; const EstimatedMatchingText = styled(SublineBold)` @@ -401,4 +423,15 @@ const BlockExplorerLink = styled(GLink)` } `; +const LastUpdate = styled.div` + width: 100%; + margin-top: 8px; + font-size: 12px; + line-height: 18px; + color: ${neutralColors.gray[700]}; + padding: 8px 0px 0px; + border-top: 1px solid rgba(0, 0, 0, 0.08); + align-self: stretch; +`; + export default ProjectTotalFundCard; diff --git a/src/helpers/time.ts b/src/helpers/time.ts index 3f456f82bf..5c3b15713a 100644 --- a/src/helpers/time.ts +++ b/src/helpers/time.ts @@ -57,3 +57,25 @@ const fetchServerTime = async () => { fetching = false; } }; + +export function calculateQFTimeDifferences( + providedTime: string | undefined, +): number { + if (!providedTime) { + return 0; + } + + const providedDate = new Date(providedTime); + const currentDate = new Date(); + + if (isNaN(providedDate.getTime())) { + throw new Error('Invalid provided time'); + } + + // Calculate the difference in milliseconds and convert to whole minutes + const diffInMinutes = Math.floor( + (currentDate.getTime() - providedDate.getTime()) / (1000 * 60), + ); + + return diffInMinutes < 0 ? 0 : diffInMinutes; +} From f3179f57931ee384ab8864542201e43807700172 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Wed, 22 Jan 2025 20:03:51 +0100 Subject: [PATCH 09/18] Feat/Improve back button on donation page --- src/components/views/donate/DonateHeader.tsx | 19 +++++++++++++++++-- src/components/views/donate/DonateIndex.tsx | 4 +++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/components/views/donate/DonateHeader.tsx b/src/components/views/donate/DonateHeader.tsx index 562bb4bbfa..15292baf67 100644 --- a/src/components/views/donate/DonateHeader.tsx +++ b/src/components/views/donate/DonateHeader.tsx @@ -26,13 +26,16 @@ import { useDonateData } from '@/context/donate.context'; import { EScrollDir, useScrollDetection } from '@/hooks/useScrollDetection'; import { useGeneralWallet } from '@/providers/generalWalletProvider'; import { setShowWelcomeModal } from '@/features/modal/modal.slice'; +import { getActiveRound } from '@/helpers/qf'; export interface IHeader { theme?: ETheme; show?: boolean; + isSuccessDonation?: boolean; } -export const DonateHeader: FC = () => { +export const DonateHeader: FC = props => { + const { isSuccessDonation } = props; const theme = useAppSelector(state => state.general.theme); const { formatMessage } = useIntl(); const { walletAddress } = useGeneralWallet(); @@ -44,6 +47,18 @@ export const DonateHeader: FC = () => { const isGIVeconomyRoute = checkIsGIVeconomyRoute(router.route); + let routePath = Routes.Project + '/' + project.slug; + + // Change route if donation done successfully + if (isSuccessDonation) { + const { qfRounds } = project; + const { activeQFRound } = getActiveRound(qfRounds); + + const isActiveQF = activeQFRound?.isActive; + + routePath = isActiveQF ? Routes.AllQFProjects : Routes.AllProjects; + } + return ( = () => { $show={scrollDir !== EScrollDir.Down} > - + { return successDonation ? ( <> - + 0} + /> From d26bf42b07848128e3eb6ada5db3d5841604a0c9 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 23 Jan 2025 13:29:12 +0100 Subject: [PATCH 10/18] Fix/cbBTC values --- src/components/AmountInput/AmountInput.tsx | 4 ++-- .../Recurring/RecurringDonationCard.tsx | 24 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/components/AmountInput/AmountInput.tsx b/src/components/AmountInput/AmountInput.tsx index 32fa1f7b7e..8a7d3c05e1 100644 --- a/src/components/AmountInput/AmountInput.tsx +++ b/src/components/AmountInput/AmountInput.tsx @@ -52,7 +52,7 @@ export const AmountInput: FC = ({ const isZero = regex.test(displayAmount); if (amount === 0n && isZero) return; - const maxDecimals = decimals === 8 ? 8 : decimals / 3; + const maxDecimals = decimals === 8 ? 6 : decimals / 3; const _displayAmount = truncateToDecimalPlaces( formatUnits(amount, decimals), @@ -80,7 +80,7 @@ export const AmountInput: FC = ({ // Allow more decimals if token has 8 decimals if (decimals === 8) { - if (_decimals?.length > 8) return; // Limit to 8 decimals + if (_decimals?.length > 6) return; // Limit to 8 decimals } else { if (_decimals?.length > decimals / 3) return; // Limit to 6 or 2 decimals for other tokens } diff --git a/src/components/views/donate/Recurring/RecurringDonationCard.tsx b/src/components/views/donate/Recurring/RecurringDonationCard.tsx index aae0670c0c..bae140a0f4 100644 --- a/src/components/views/donate/Recurring/RecurringDonationCard.tsx +++ b/src/components/views/donate/Recurring/RecurringDonationCard.tsx @@ -157,7 +157,7 @@ export const RecurringDonationCard = () => { selectedRecurringToken?.token.decimals === 6 ? 10000n : selectedRecurringToken?.token.decimals === 8 - ? 100n + ? 1000000n : 1n; // total means project + giveth @@ -360,13 +360,21 @@ export const RecurringDonationCard = () => { id: 'label.available', })} :{' '} - {truncateToDecimalPlaces( - formatUnits( - balance.value, - balance.decimals, - ), - balance.decimals / 3, - )} + {balance.decimals === 8 + ? truncateToDecimalPlaces( + formatUnits( + balance.value, + balance.decimals, + ), + 18 / 3, + ) + : truncateToDecimalPlaces( + formatUnits( + balance.value, + balance.decimals, + ), + balance.decimals / 3, + )} !isRefetching && refetch()} From 0a14d24c55cd910f0b08147104f006787f84a6cb Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 23 Jan 2025 13:44:04 +0100 Subject: [PATCH 11/18] Added mixing clusterMatchingSyncAt to graphql --- src/apollo/gql/gqlProjects.ts | 1 + src/components/views/donate/OneTime/EstimatedMatchingToast.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/apollo/gql/gqlProjects.ts b/src/apollo/gql/gqlProjects.ts index edc88d371c..60da212836 100644 --- a/src/apollo/gql/gqlProjects.ts +++ b/src/apollo/gql/gqlProjects.ts @@ -198,6 +198,7 @@ export const FETCH_PROJECT_BY_SLUG_DONATION = gql` allocatedFundUSD minimumValidUsdValue qfStrategy + clusterMatchingSyncAt } anchorContracts { address diff --git a/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx b/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx index 0a17789214..2b39ee26f7 100644 --- a/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx +++ b/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx @@ -46,6 +46,7 @@ const EstimatedMatchingToast: FC = ({ estimatedMatching || {}; const { activeStartedRound } = getActiveRound(qfRounds); + const { allocatedFundUSDPreferred, allocatedFundUSD, From cae6ba6f3c97834c1d7535dd8db3c1f835f17d8d Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 23 Jan 2025 13:50:18 +0100 Subject: [PATCH 12/18] removing tab --- src/components/views/donate/OneTime/EstimatedMatchingToast.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx b/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx index 2b39ee26f7..5a84ba2e4a 100644 --- a/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx +++ b/src/components/views/donate/OneTime/EstimatedMatchingToast.tsx @@ -46,7 +46,7 @@ const EstimatedMatchingToast: FC = ({ estimatedMatching || {}; const { activeStartedRound } = getActiveRound(qfRounds); - + const { allocatedFundUSDPreferred, allocatedFundUSD, From 6517f3733f80261468e3c39a7e6e206c9cce286b Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 23 Jan 2025 15:04:57 +0100 Subject: [PATCH 13/18] Fix/Adding right function to count estimated value on project single page --- .../project/projectActionCard/QFSection.tsx | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/views/project/projectActionCard/QFSection.tsx b/src/components/views/project/projectActionCard/QFSection.tsx index 9d0a1705ba..904199af66 100644 --- a/src/components/views/project/projectActionCard/QFSection.tsx +++ b/src/components/views/project/projectActionCard/QFSection.tsx @@ -58,8 +58,12 @@ const QFSection: FC = ({ projectData }) => { const router = useRouter(); const isOnDonatePage = router.pathname.includes('/donate'); - const { projectDonationsSqrtRootSum, matchingPool, allProjectsSum } = - estimatedMatching ?? {}; + const { + projectDonationsSqrtRootSum, + matchingPool, + allProjectsSum, + matching, + } = estimatedMatching ?? {}; const { activeStartedRound } = getActiveRound(qfRounds); const { @@ -67,6 +71,7 @@ const QFSection: FC = ({ projectData }) => { allocatedTokenSymbol, allocatedFundUSDPreferred, clusterMatchingSyncAt, + qfStrategy, } = activeStartedRound || {}; const totalEstimatedMatching = calculateTotalEstimatedMatching( projectDonationsSqrtRootSum, @@ -75,11 +80,6 @@ const QFSection: FC = ({ projectData }) => { activeStartedRound?.maximumReward, ); - // const { clusterMatchingSyncAt } = activeQFRound || {}; - console.log({ activeStartedRound }); - - console.log('activeQFRound', clusterMatchingSyncAt); - const clusterMatchingSyncAtDiff = calculateQFTimeDifferences( clusterMatchingSyncAt || '', ); @@ -95,7 +95,16 @@ const QFSection: FC = ({ projectData }) => { {formatDonation( - totalEstimatedMatching, + calculateTotalEstimatedMatching( + projectDonationsSqrtRootSum, + allProjectsSum, + allocatedFundUSDPreferred + ? allocatedFundUSD + : matchingPool, + activeStartedRound?.maximumReward, + matching, + qfStrategy, + ), allocatedFundUSDPreferred ? '$' : '', locale, true, From 64b58f6caa11ebceaa6685e954ffb6ef97a775cc Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 23 Jan 2025 15:07:52 +0100 Subject: [PATCH 14/18] fixing property --- .../views/project/projectActionCard/QFSection.tsx | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/components/views/project/projectActionCard/QFSection.tsx b/src/components/views/project/projectActionCard/QFSection.tsx index 904199af66..09e19a99a4 100644 --- a/src/components/views/project/projectActionCard/QFSection.tsx +++ b/src/components/views/project/projectActionCard/QFSection.tsx @@ -78,6 +78,8 @@ const QFSection: FC = ({ projectData }) => { allProjectsSum, allocatedFundUSDPreferred ? allocatedFundUSD : matchingPool, activeStartedRound?.maximumReward, + matching, + qfStrategy, ); const clusterMatchingSyncAtDiff = calculateQFTimeDifferences( @@ -95,16 +97,7 @@ const QFSection: FC = ({ projectData }) => { {formatDonation( - calculateTotalEstimatedMatching( - projectDonationsSqrtRootSum, - allProjectsSum, - allocatedFundUSDPreferred - ? allocatedFundUSD - : matchingPool, - activeStartedRound?.maximumReward, - matching, - qfStrategy, - ), + totalEstimatedMatching, allocatedFundUSDPreferred ? '$' : '', locale, true, From 2e757509b31cc7b6c916b180fa3aa7311e93ba1b Mon Sep 17 00:00:00 2001 From: Mitch Oz Date: Thu, 23 Jan 2025 13:24:51 -0600 Subject: [PATCH 15/18] update youtube regex handling and discord error message --- src/helpers/url.tsx | 2 +- src/lib/constants/regex.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/helpers/url.tsx b/src/helpers/url.tsx index 1e85a09025..26a15c5b63 100644 --- a/src/helpers/url.tsx +++ b/src/helpers/url.tsx @@ -191,7 +191,7 @@ export const getSocialMediaHandle = ( case 'youtube': return extractUsernameFromPattern( cleanedUrl, - /youtube\.com\/channel\/([^\/]+)/, + /youtube\.com\/(?:c\/|@)([^\/]+)/, true, ); case 'reddit': diff --git a/src/lib/constants/regex.ts b/src/lib/constants/regex.ts index fa06a1e48e..5e9971e51c 100644 --- a/src/lib/constants/regex.ts +++ b/src/lib/constants/regex.ts @@ -92,7 +92,8 @@ export const validators = { discord: { pattern: { value: regexList.discord, - message: 'Invalid Discord URL', + message: + 'Invalid Discord URL, make sure this is a server invite link', }, }, telegram: { From 72d5be7ca775d134195957aa2b2d18f1853e0381 Mon Sep 17 00:00:00 2001 From: Mitch Oz Date: Thu, 23 Jan 2025 13:35:41 -0600 Subject: [PATCH 16/18] loosen regex conditions for discord, handle different domains for discord, modify error text --- src/helpers/url.tsx | 2 +- src/lib/constants/regex.ts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/helpers/url.tsx b/src/helpers/url.tsx index 26a15c5b63..fd5d6caa03 100644 --- a/src/helpers/url.tsx +++ b/src/helpers/url.tsx @@ -209,7 +209,7 @@ export const getSocialMediaHandle = ( case 'discord': return extractUsernameFromPattern( cleanedUrl, - /discord\.gg\/([^\/]+)/, + /discord\.(?:gg|com)\/([^\/]+)/, true, ); case 'farcaster': diff --git a/src/lib/constants/regex.ts b/src/lib/constants/regex.ts index 5e9971e51c..469ade661c 100644 --- a/src/lib/constants/regex.ts +++ b/src/lib/constants/regex.ts @@ -16,8 +16,7 @@ export const regexList = { mediumBlogBanner: /]+src="(.*?)"/, telegram: /(?:https?:\/\/)?(?:www\.)?(?:t(?:elegram)?\.me|me|telegram\.org)\/(?:[a-zA-Z0-9_]{5,32}|joinchat\/[a-zA-Z0-9_]+)/, - discord: - /(?:https?:\/\/)?(?:www\.)?(?:discord\.gg|discord(?:app)?\.com\/invite)\/[\w-]+/, + discord: /(?:https?:\/\/)?(?:www\.)?discord\.[\w-]+/, phone: /^(\+\d{1,2}\s?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/, github: /^(https:\/\/)?(www\.)?github\.com\/[A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)?\/?$/, }; @@ -92,8 +91,7 @@ export const validators = { discord: { pattern: { value: regexList.discord, - message: - 'Invalid Discord URL, make sure this is a server invite link', + message: 'Invalid Discord URL', }, }, telegram: { From 5040e3dc251dc544ba0a6faafa1970c0fb9e2234 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Fri, 24 Jan 2025 13:32:24 +0100 Subject: [PATCH 17/18] fixing ending recurring donation --- .../donationsTab/recurringTab/EndStreamModal.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/views/userProfile/donationsTab/recurringTab/EndStreamModal.tsx b/src/components/views/userProfile/donationsTab/recurringTab/EndStreamModal.tsx index e2f86d091e..5cd6dcb96d 100644 --- a/src/components/views/userProfile/donationsTab/recurringTab/EndStreamModal.tsx +++ b/src/components/views/userProfile/donationsTab/recurringTab/EndStreamModal.tsx @@ -117,9 +117,13 @@ const EndStreamInnerModal: FC = ({ superToken = await sf.loadWrapperSuperToken(_superToken.id); } + const matchingContract = donation.project.anchorContracts.find( + contract => contract.networkId === recurringNetworkId, + ); + const deleteOp = superToken.deleteFlow({ sender: address, - receiver: donation.project.anchorContracts[0].address, + receiver: matchingContract?.address || '', }); const tx = await deleteOp.exec(signer); From 40a2352a017fe9a0ef05e33ac72a5beb9930ab5c Mon Sep 17 00:00:00 2001 From: Mitch Oz Date: Sun, 26 Jan 2025 21:02:31 -0600 Subject: [PATCH 18/18] fix discord channels link formatting --- src/helpers/url.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helpers/url.tsx b/src/helpers/url.tsx index fd5d6caa03..96ac29442a 100644 --- a/src/helpers/url.tsx +++ b/src/helpers/url.tsx @@ -209,7 +209,7 @@ export const getSocialMediaHandle = ( case 'discord': return extractUsernameFromPattern( cleanedUrl, - /discord\.(?:gg|com)\/([^\/]+)/, + /discord\.(?:gg|com\/channels|com)\/([^\/]+)/, true, ); case 'farcaster': @@ -256,6 +256,7 @@ export const extractUsernameFromPattern = ( ): string => { const match = url.match(regex); if (match && match[1]) { + console.log('match', match[1]); return isChannel ? `/${match[1]}` : `@${match[1]}`; // Return '@username' for users and '/channel' for channels } return url; // Fallback to original URL if no match is found