From 547a477b1a95a39f60108b7a834038bb26d2887d Mon Sep 17 00:00:00 2001 From: kkatusic Date: Wed, 5 Jun 2024 00:06:46 +0200 Subject: [PATCH 01/97] started back improvement --- src/components/views/projects/ProjectsIndex.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 025a50b22f..b6d29a2486 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -157,10 +157,22 @@ const ProjectsIndex = (props: IProjectsView) => { fetchProjects(false, 0); }, [contextVariables]); + // Handle back button navigation + useEffect(() => { + console.log('projects', router); + const storedPage = localStorage.getItem('lastViewedPage'); + if (storedPage) { + for (let i = 0; i < Number(storedPage); i++) { + // fetchProjects(false, i); + } + } + }, [router, fetchProjects]); + const loadMore = useCallback(() => { if (isLoading) return; fetchProjects(true, pageNum.current + 1); pageNum.current = pageNum.current + 1; + localStorage.setItem('lastViewedPage', pageNum.current.toString()); }, [fetchProjects, isLoading]); const handleCreateButton = () => { From a69c84999e166d5669c8b7557c4def9d3234d886 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 6 Jun 2024 10:30:03 +0200 Subject: [PATCH 02/97] attempt to solve back option with useEffect and local storage --- src/components/project-card/ProjectCard.tsx | 1 + .../views/projects/ProjectsIndex.tsx | 41 +++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/components/project-card/ProjectCard.tsx b/src/components/project-card/ProjectCard.tsx index d2fbb29ac4..3662709839 100644 --- a/src/components/project-card/ProjectCard.tsx +++ b/src/components/project-card/ProjectCard.tsx @@ -122,6 +122,7 @@ const ProjectCard = (props: IProjectCard) => { { router?.events?.on('routeChangeStart', () => setIsLoading(true)); const fetchProjects = useCallback( - (isLoadMore?: boolean, loadNum?: number, userIdChanged = false) => { + ( + isLoadMore?: boolean, + loadNum?: number, + userIdChanged = false, + customLimit = 0, + ) => { + const queryLimit = customLimit || projects.length; + const variables: IQueries = { limit: userIdChanged ? filteredProjects.length > 50 ? BACKEND_QUERY_LIMIT : filteredProjects.length - : projects.length, + : queryLimit, skip: userIdChanged ? 0 : projects.length * (loadNum || 0), }; @@ -160,17 +167,6 @@ const ProjectsIndex = (props: IProjectsView) => { fetchProjects(false, 0); }, [contextVariables]); - // Handle back button navigation - useEffect(() => { - console.log('projects', router); - const storedPage = localStorage.getItem('lastViewedPage'); - if (storedPage) { - for (let i = 0; i < Number(storedPage); i++) { - // fetchProjects(false, i); - } - } - }, [router, fetchProjects]); - const loadMore = useCallback(() => { if (isLoading) return; fetchProjects(true, pageNum.current + 1); @@ -178,6 +174,25 @@ const ProjectsIndex = (props: IProjectsView) => { localStorage.setItem('lastViewedPage', pageNum.current.toString()); }, [fetchProjects, isLoading]); + // Handle back button navigation + + useEffect(() => { + const storedPage = localStorage.getItem('lastViewedPage'); + const lastVisitedProject = + localStorage.getItem('lastViewedProject') ?? null; + if ( + storedPage && + Number(storedPage) > 0 && + pageNum.current <= Number(storedPage) && + lastVisitedProject != null && + !isLoading + ) { + localStorage.removeItem('lastViewedProject'); + const limit = parseInt(storedPage) * 15; + fetchProjects(false, 0, false, limit); + } + }, [fetchProjects, isLoading]); + const handleCreateButton = () => { if (isUserRegistered(user)) { router.push(Routes.CreateProject); From e5eeddc783c6d9a106ed88307dcfc9f8bc74bfa0 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Fri, 5 Jul 2024 16:59:24 +0200 Subject: [PATCH 03/97] reverted changes --- .../views/projects/ProjectsIndex.tsx | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index a0732c7b24..78adcc5dbf 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -84,20 +84,13 @@ const ProjectsIndex = (props: IProjectsView) => { router?.events?.on('routeChangeStart', () => setIsLoading(true)); const fetchProjects = useCallback( - ( - isLoadMore?: boolean, - loadNum?: number, - userIdChanged = false, - customLimit = 0, - ) => { - const queryLimit = customLimit || projects.length; - + (isLoadMore?: boolean, loadNum?: number, userIdChanged = false) => { const variables: IQueries = { limit: userIdChanged ? filteredProjects.length > 50 ? BACKEND_QUERY_LIMIT : filteredProjects.length - : queryLimit, + : projects.length, skip: userIdChanged ? 0 : projects.length * (loadNum || 0), }; @@ -170,26 +163,6 @@ const ProjectsIndex = (props: IProjectsView) => { if (isLoading) return; fetchProjects(true, pageNum.current + 1); pageNum.current = pageNum.current + 1; - localStorage.setItem('lastViewedPage', pageNum.current.toString()); - }, [fetchProjects, isLoading]); - - // Handle back button navigation - - useEffect(() => { - const storedPage = localStorage.getItem('lastViewedPage'); - const lastVisitedProject = - localStorage.getItem('lastViewedProject') ?? null; - if ( - storedPage && - Number(storedPage) > 0 && - pageNum.current <= Number(storedPage) && - lastVisitedProject != null && - !isLoading - ) { - localStorage.removeItem('lastViewedProject'); - const limit = parseInt(storedPage) * 15; - fetchProjects(false, 0, false, limit); - } }, [fetchProjects, isLoading]); const handleCreateButton = () => { From b358443bb62483f1eadbed83328416442e4504a5 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 9 Jul 2024 00:53:44 +0200 Subject: [PATCH 04/97] started implementing useInfiniteQuery from React Query --- .../views/projects/ProjectsIndex.tsx | 83 ++++++++++++++++--- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 78adcc5dbf..a8bae33143 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -10,7 +10,7 @@ import { import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { captureException } from '@sentry/nextjs'; - +import { useInfiniteQuery } from '@tanstack/react-query'; import ProjectCard from '@/components/project-card/ProjectCard'; import Routes from '@/lib/constants/Routes'; import { isUserRegistered, showToastError } from '@/lib/helpers'; @@ -53,6 +53,30 @@ interface IQueries { connectedWalletUserId?: number; } +interface FetchProjectsParams { + pageParam?: number; + queryKey: [ + string, + { + isLoadMore: boolean; + loadNum: number; + userIdChanged: boolean; + }, + ]; +} + +interface FetchProjectsResponse { + projects: IProject[]; + totalCount: number; + nextPage: number | undefined; +} + +interface FetchProjectsResponse { + projects: IProject[]; + totalCount: number; + nextPage: number | undefined; +} + const ProjectsIndex = (props: IProjectsView) => { const { formatMessage } = useIntl(); const { projects, totalCount: _totalCount } = props; @@ -84,7 +108,20 @@ const ProjectsIndex = (props: IProjectsView) => { router?.events?.on('routeChangeStart', () => setIsLoading(true)); const fetchProjects = useCallback( - (isLoadMore?: boolean, loadNum?: number, userIdChanged = false) => { + async ({ + pageParam = 0, + queryKey, + }: FetchProjectsParams): Promise => { + const [_key, { isLoadMore, loadNum, userIdChanged }] = queryKey; + + console.log( + 'fetchProjects functions', + isLoadMore, + loadNum, + userIdChanged, + pageParam, + ); + const variables: IQueries = { limit: userIdChanged ? filteredProjects.length > 50 @@ -137,6 +174,7 @@ const ProjectsIndex = (props: IProjectsView) => { }, }); }); + return undefined; }, [ contextVariables, @@ -149,21 +187,44 @@ const ProjectsIndex = (props: IProjectsView) => { ], ); + const [isLoadMore, setIsLoadMore] = useState(false); + const [loadNum, setLoadNum] = useState(0); + const [userIdChanged, setUserIdChanged] = useState(false); + + const { + data, + error, + fetchNextPage, + hasNextPage, + isError, + isFetching, + isFetchingNextPage, + } = useInfiniteQuery({ + queryKey: ['projects', { isLoadMore, loadNum, userIdChanged }], + queryFn: fetchProjects, + getNextPageParam: lastPage => lastPage?.nextPage, + initialPageParam: 0, + }); + useEffect(() => { + console.log('fetchProjects functions call 1'); pageNum.current = 0; - fetchProjects(false, 0, true); + // fetchProjects(false, 0, true); }, [user?.id]); useEffect(() => { + console.log('fetchProjects functions call 2'); pageNum.current = 0; - fetchProjects(false, 0); + // fetchProjects(false, 0); + fetchNextPage(); }, [contextVariables]); const loadMore = useCallback(() => { - if (isLoading) return; - fetchProjects(true, pageNum.current + 1); + if (isFetching) return; + // fetchProjects(true, pageNum.current + 1); + fetchNextPage(); pageNum.current = pageNum.current + 1; - }, [fetchProjects, isLoading]); + }, [isFetching]); const handleCreateButton = () => { if (isUserRegistered(user)) { @@ -209,7 +270,7 @@ const ProjectsIndex = (props: IProjectsView) => { ) { setIsNotFound(true); } - }, [selectedMainCategory, mainCategories.length]); + }, [selectedMainCategory, mainCategories.length, isArchivedQF]); if (isNotFound) return ; @@ -251,7 +312,7 @@ const ProjectsIndex = (props: IProjectsView) => { )} - {isLoading && } + {isFetching && } {filteredProjects?.length > 0 ? ( @@ -283,14 +344,14 @@ const ProjectsIndex = (props: IProjectsView) => {
From 2845f1b3d60a7f8a9b550e4f7746d7c5098835a3 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 9 Jul 2024 13:07:00 +0200 Subject: [PATCH 05/97] reverted back again --- .../views/projects/ProjectsIndex.tsx | 139 ++++++++---------- 1 file changed, 63 insertions(+), 76 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index a8bae33143..2b186c9ed6 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -107,85 +107,72 @@ const ProjectsIndex = (props: IProjectsView) => { router?.events?.on('routeChangeStart', () => setIsLoading(true)); - const fetchProjects = useCallback( - async ({ - pageParam = 0, - queryKey, - }: FetchProjectsParams): Promise => { - const [_key, { isLoadMore, loadNum, userIdChanged }] = queryKey; - - console.log( - 'fetchProjects functions', - isLoadMore, - loadNum, - userIdChanged, - pageParam, - ); - - const variables: IQueries = { - limit: userIdChanged - ? filteredProjects.length > 50 - ? BACKEND_QUERY_LIMIT - : filteredProjects.length - : projects.length, - skip: userIdChanged ? 0 : projects.length * (loadNum || 0), - }; - - if (user?.id) { - variables.connectedWalletUserId = Number(user?.id); - } + const fetchProjects = async ({ + pageParam = 0, + queryKey, + }: FetchProjectsParams): Promise => { + const [_key, { isLoadMore, loadNum, userIdChanged }] = queryKey; + + console.log( + 'fetchProjects functions', + isLoadMore, + loadNum, + userIdChanged, + pageParam, + ); + + const variables: IQueries = { + limit: userIdChanged + ? filteredProjects.length > 50 + ? BACKEND_QUERY_LIMIT + : filteredProjects.length + : projects.length, + skip: userIdChanged ? 0 : projects.length * (loadNum || 0), + }; + + if (user?.id) { + variables.connectedWalletUserId = Number(user?.id); + } - setIsLoading(true); - if ( - contextVariables.mainCategory !== router.query?.slug?.toString() - ) - return; - - client - .query({ - query: FETCH_ALL_PROJECTS, - variables: { - ...variables, - ...contextVariables, - mainCategory: isArchivedQF - ? undefined - : getMainCategorySlug(selectedMainCategory), - qfRoundSlug: isArchivedQF ? router.query.slug : null, + setIsLoading(true); + if (contextVariables.mainCategory !== router.query?.slug?.toString()) + return; + + client + .query({ + query: FETCH_ALL_PROJECTS, + variables: { + ...variables, + ...contextVariables, + mainCategory: isArchivedQF + ? undefined + : getMainCategorySlug(selectedMainCategory), + qfRoundSlug: isArchivedQF ? router.query.slug : null, + }, + }) + .then((res: { data: { allProjects: IFetchAllProjects } }) => { + const data = res.data?.allProjects?.projects; + const count = res.data?.allProjects?.totalCount; + setTotalCount(count); + + setFilteredProjects(prevProjects => { + isInfiniteScrolling.current = + (data.length + prevProjects.length) % 45 !== 0; + return isLoadMore ? [...prevProjects, ...data] : data; + }); + setIsLoading(false); + }) + .catch((err: any) => { + setIsLoading(false); + showToastError(err); + captureException(err, { + tags: { + section: 'fetchAllProjects', }, - }) - .then((res: { data: { allProjects: IFetchAllProjects } }) => { - const data = res.data?.allProjects?.projects; - const count = res.data?.allProjects?.totalCount; - setTotalCount(count); - - setFilteredProjects(prevProjects => { - isInfiniteScrolling.current = - (data.length + prevProjects.length) % 45 !== 0; - return isLoadMore ? [...prevProjects, ...data] : data; - }); - setIsLoading(false); - }) - .catch((err: any) => { - setIsLoading(false); - showToastError(err); - captureException(err, { - tags: { - section: 'fetchAllProjects', - }, - }); }); - return undefined; - }, - [ - contextVariables, - filteredProjects.length, - isArchivedQF, - projects.length, - router.query.slug, - selectedMainCategory, - user?.id, - ], - ); + }); + return undefined; + }; const [isLoadMore, setIsLoadMore] = useState(false); const [loadNum, setLoadNum] = useState(0); From c56a54fbd9ffb40a84963f0f8865de06c87135bf Mon Sep 17 00:00:00 2001 From: kkatusic Date: Fri, 12 Jul 2024 11:22:38 +0200 Subject: [PATCH 06/97] reverted back again :( --- .../views/projects/ProjectsIndex.tsx | 192 +++++++----------- 1 file changed, 72 insertions(+), 120 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 2b186c9ed6..78adcc5dbf 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -10,7 +10,7 @@ import { import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { captureException } from '@sentry/nextjs'; -import { useInfiniteQuery } from '@tanstack/react-query'; + import ProjectCard from '@/components/project-card/ProjectCard'; import Routes from '@/lib/constants/Routes'; import { isUserRegistered, showToastError } from '@/lib/helpers'; @@ -53,30 +53,6 @@ interface IQueries { connectedWalletUserId?: number; } -interface FetchProjectsParams { - pageParam?: number; - queryKey: [ - string, - { - isLoadMore: boolean; - loadNum: number; - userIdChanged: boolean; - }, - ]; -} - -interface FetchProjectsResponse { - projects: IProject[]; - totalCount: number; - nextPage: number | undefined; -} - -interface FetchProjectsResponse { - projects: IProject[]; - totalCount: number; - nextPage: number | undefined; -} - const ProjectsIndex = (props: IProjectsView) => { const { formatMessage } = useIntl(); const { projects, totalCount: _totalCount } = props; @@ -107,111 +83,87 @@ const ProjectsIndex = (props: IProjectsView) => { router?.events?.on('routeChangeStart', () => setIsLoading(true)); - const fetchProjects = async ({ - pageParam = 0, - queryKey, - }: FetchProjectsParams): Promise => { - const [_key, { isLoadMore, loadNum, userIdChanged }] = queryKey; - - console.log( - 'fetchProjects functions', - isLoadMore, - loadNum, - userIdChanged, - pageParam, - ); - - const variables: IQueries = { - limit: userIdChanged - ? filteredProjects.length > 50 - ? BACKEND_QUERY_LIMIT - : filteredProjects.length - : projects.length, - skip: userIdChanged ? 0 : projects.length * (loadNum || 0), - }; - - if (user?.id) { - variables.connectedWalletUserId = Number(user?.id); - } + const fetchProjects = useCallback( + (isLoadMore?: boolean, loadNum?: number, userIdChanged = false) => { + const variables: IQueries = { + limit: userIdChanged + ? filteredProjects.length > 50 + ? BACKEND_QUERY_LIMIT + : filteredProjects.length + : projects.length, + skip: userIdChanged ? 0 : projects.length * (loadNum || 0), + }; + + if (user?.id) { + variables.connectedWalletUserId = Number(user?.id); + } - setIsLoading(true); - if (contextVariables.mainCategory !== router.query?.slug?.toString()) - return; - - client - .query({ - query: FETCH_ALL_PROJECTS, - variables: { - ...variables, - ...contextVariables, - mainCategory: isArchivedQF - ? undefined - : getMainCategorySlug(selectedMainCategory), - qfRoundSlug: isArchivedQF ? router.query.slug : null, - }, - }) - .then((res: { data: { allProjects: IFetchAllProjects } }) => { - const data = res.data?.allProjects?.projects; - const count = res.data?.allProjects?.totalCount; - setTotalCount(count); - - setFilteredProjects(prevProjects => { - isInfiniteScrolling.current = - (data.length + prevProjects.length) % 45 !== 0; - return isLoadMore ? [...prevProjects, ...data] : data; - }); - setIsLoading(false); - }) - .catch((err: any) => { - setIsLoading(false); - showToastError(err); - captureException(err, { - tags: { - section: 'fetchAllProjects', + setIsLoading(true); + if ( + contextVariables.mainCategory !== router.query?.slug?.toString() + ) + return; + + client + .query({ + query: FETCH_ALL_PROJECTS, + variables: { + ...variables, + ...contextVariables, + mainCategory: isArchivedQF + ? undefined + : getMainCategorySlug(selectedMainCategory), + qfRoundSlug: isArchivedQF ? router.query.slug : null, }, + }) + .then((res: { data: { allProjects: IFetchAllProjects } }) => { + const data = res.data?.allProjects?.projects; + const count = res.data?.allProjects?.totalCount; + setTotalCount(count); + + setFilteredProjects(prevProjects => { + isInfiniteScrolling.current = + (data.length + prevProjects.length) % 45 !== 0; + return isLoadMore ? [...prevProjects, ...data] : data; + }); + setIsLoading(false); + }) + .catch((err: any) => { + setIsLoading(false); + showToastError(err); + captureException(err, { + tags: { + section: 'fetchAllProjects', + }, + }); }); - }); - return undefined; - }; - - const [isLoadMore, setIsLoadMore] = useState(false); - const [loadNum, setLoadNum] = useState(0); - const [userIdChanged, setUserIdChanged] = useState(false); - - const { - data, - error, - fetchNextPage, - hasNextPage, - isError, - isFetching, - isFetchingNextPage, - } = useInfiniteQuery({ - queryKey: ['projects', { isLoadMore, loadNum, userIdChanged }], - queryFn: fetchProjects, - getNextPageParam: lastPage => lastPage?.nextPage, - initialPageParam: 0, - }); + }, + [ + contextVariables, + filteredProjects.length, + isArchivedQF, + projects.length, + router.query.slug, + selectedMainCategory, + user?.id, + ], + ); useEffect(() => { - console.log('fetchProjects functions call 1'); pageNum.current = 0; - // fetchProjects(false, 0, true); + fetchProjects(false, 0, true); }, [user?.id]); useEffect(() => { - console.log('fetchProjects functions call 2'); pageNum.current = 0; - // fetchProjects(false, 0); - fetchNextPage(); + fetchProjects(false, 0); }, [contextVariables]); const loadMore = useCallback(() => { - if (isFetching) return; - // fetchProjects(true, pageNum.current + 1); - fetchNextPage(); + if (isLoading) return; + fetchProjects(true, pageNum.current + 1); pageNum.current = pageNum.current + 1; - }, [isFetching]); + }, [fetchProjects, isLoading]); const handleCreateButton = () => { if (isUserRegistered(user)) { @@ -257,7 +209,7 @@ const ProjectsIndex = (props: IProjectsView) => { ) { setIsNotFound(true); } - }, [selectedMainCategory, mainCategories.length, isArchivedQF]); + }, [selectedMainCategory, mainCategories.length]); if (isNotFound) return ; @@ -299,7 +251,7 @@ const ProjectsIndex = (props: IProjectsView) => { )} - {isFetching && } + {isLoading && } {filteredProjects?.length > 0 ? ( @@ -331,14 +283,14 @@ const ProjectsIndex = (props: IProjectsView) => {
From 68aa294ae21526a9a2b217deebb2708e658ddbe3 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Sun, 14 Jul 2024 13:49:52 +0200 Subject: [PATCH 07/97] set up React Query useInfiniteQuery function --- .../views/projects/ProjectsIndex.tsx | 97 ++++++++++++++++--- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 78adcc5dbf..99cb0af216 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -11,6 +11,7 @@ import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { captureException } from '@sentry/nextjs'; +import { QueryFunctionContext, useInfiniteQuery } from '@tanstack/react-query'; import ProjectCard from '@/components/project-card/ProjectCard'; import Routes from '@/lib/constants/Routes'; import { isUserRegistered, showToastError } from '@/lib/helpers'; @@ -53,6 +54,24 @@ interface IQueries { connectedWalletUserId?: number; } +interface FetchProjectsResponse { + projects: IProject[]; + totalCount: number; + nextPage: number; +} + +interface FetchProjectsParams { + queryKey: [ + string, + { + isLoadMore: boolean; + loadNum: number; + userIdChanged: boolean; + }, + ]; + pageParam?: number; +} + const ProjectsIndex = (props: IProjectsView) => { const { formatMessage } = useIntl(); const { projects, totalCount: _totalCount } = props; @@ -83,8 +102,15 @@ const ProjectsIndex = (props: IProjectsView) => { router?.events?.on('routeChangeStart', () => setIsLoading(true)); + // Default values for queryKey + const [isLoadMore, setIsLoadMore] = useState(false); + const [loadNum, setLoadNum] = useState(0); + const [userIdChanged, setUserIdChanged] = useState(false); + const fetchProjects = useCallback( - (isLoadMore?: boolean, loadNum?: number, userIdChanged = false) => { + async (pageParam: number | unknown): Promise => { + console.log('fetchProjects', pageParam); + const variables: IQueries = { limit: userIdChanged ? filteredProjects.length > 50 @@ -101,8 +127,9 @@ const ProjectsIndex = (props: IProjectsView) => { setIsLoading(true); if ( contextVariables.mainCategory !== router.query?.slug?.toString() - ) - return; + ) { + return { projects: [], totalCount: 0, nextPage: 1 }; + } client .query({ @@ -127,6 +154,17 @@ const ProjectsIndex = (props: IProjectsView) => { return isLoadMore ? [...prevProjects, ...data] : data; }); setIsLoading(false); + + const result = { + projects: isLoadMore + ? [...filteredProjects, ...data] + : data, + nextPage: pageParam ? pageParam + 1 : 1, + totalCount: count, + }; + + console.log('fetchProjects result', result); + return result; }) .catch((err: any) => { setIsLoading(false); @@ -137,6 +175,8 @@ const ProjectsIndex = (props: IProjectsView) => { }, }); }); + + return { projects: [], totalCount: 0, nextPage: 1 }; }, [ contextVariables, @@ -149,21 +189,48 @@ const ProjectsIndex = (props: IProjectsView) => { ], ); - useEffect(() => { - pageNum.current = 0; - fetchProjects(false, 0, true); - }, [user?.id]); + const { + data, + error, + fetchNextPage, + hasNextPage, + isError, + isFetching, + isFetchingNextPage, + } = useInfiniteQuery({ + queryKey: ['projects'], + queryFn: ({ pageParam = 0 }: QueryFunctionContext) => + fetchProjects(pageParam), + // queryFn: ({ pageParam }) => fetchProjects(pageParam), + // queryFn: ({ pageParam = 0 }: { pageParam: number }) => + // fetchProjects(pageParam), + + // getNextPageParam: lastPage => lastPage?.nextPage, + getNextPageParam: (lastPage, pages) => { + console.log('getNextPageParam called', lastPage, pages); + return lastPage?.nextPage ?? false; + }, + initialPageParam: 0, + }); - useEffect(() => { - pageNum.current = 0; - fetchProjects(false, 0); - }, [contextVariables]); + // useEffect(() => { + // pageNum.current = 0; + // fetchProjects(false, 0, true); + // }, [user?.id]); + + // useEffect(() => { + // pageNum.current = 0; + // fetchProjects(false, 0); + // }, [contextVariables]); const loadMore = useCallback(() => { - if (isLoading) return; - fetchProjects(true, pageNum.current + 1); - pageNum.current = pageNum.current + 1; - }, [fetchProjects, isLoading]); + // if (isLoading) return; + // fetchProjects(true, pageNum.current + 1); + // pageNum.current = pageNum.current + 1; + console.log('LOAD MORE'); + fetchNextPage(); + // }, [fetchProjects, isLoading]); + }, [fetchNextPage]); const handleCreateButton = () => { if (isUserRegistered(user)) { From 0429727ba8737b5e8d50536ef6a064a25c62a809 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 16 Jul 2024 13:54:33 +0200 Subject: [PATCH 08/97] updated last page variable --- .../views/projects/ProjectsIndex.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 99cb0af216..5e8b0f98c6 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -57,7 +57,7 @@ interface IQueries { interface FetchProjectsResponse { projects: IProject[]; totalCount: number; - nextPage: number; + lastPage: number; } interface FetchProjectsParams { @@ -128,7 +128,7 @@ const ProjectsIndex = (props: IProjectsView) => { if ( contextVariables.mainCategory !== router.query?.slug?.toString() ) { - return { projects: [], totalCount: 0, nextPage: 1 }; + return { projects: [], totalCount: 0, lastPage: 0 }; } client @@ -159,7 +159,7 @@ const ProjectsIndex = (props: IProjectsView) => { projects: isLoadMore ? [...filteredProjects, ...data] : data, - nextPage: pageParam ? pageParam + 1 : 1, + lastPage: pageParam ? pageParam : 1, totalCount: count, }; @@ -176,7 +176,7 @@ const ProjectsIndex = (props: IProjectsView) => { }); }); - return { projects: [], totalCount: 0, nextPage: 1 }; + return { projects: [], totalCount: 0, lastPage: 0 }; }, [ contextVariables, @@ -206,9 +206,14 @@ const ProjectsIndex = (props: IProjectsView) => { // fetchProjects(pageParam), // getNextPageParam: lastPage => lastPage?.nextPage, - getNextPageParam: (lastPage, pages) => { - console.log('getNextPageParam called', lastPage, pages); - return lastPage?.nextPage ?? false; + // getNextPageParam: (lastPage, pages: FetchProjectsResponse[]) => { + // console.log('getNextPageParam called', pages); + // // return lastPage?.nextPage ?? false; + // return lastPage.nextPage + 1; + // }, + getNextPageParam: (returnedData: FetchProjectsResponse) => { + console.log('getNextPageParam called', returnedData); + return returnedData.lastPage + 1; }, initialPageParam: 0, }); From ef3c043df5f47342d5bafb9eba2a213407083771 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 16 Jul 2024 18:26:55 +0200 Subject: [PATCH 09/97] continuing --- .../views/projects/ProjectsIndex.tsx | 62 ++++++++++++------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 5e8b0f98c6..22041006f8 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -55,22 +55,22 @@ interface IQueries { } interface FetchProjectsResponse { - projects: IProject[]; + data: IProject[]; totalCount: number; lastPage: number; } -interface FetchProjectsParams { - queryKey: [ - string, - { - isLoadMore: boolean; - loadNum: number; - userIdChanged: boolean; - }, - ]; - pageParam?: number; -} +// interface FetchProjectsParams { +// queryKey: [ +// string, +// { +// isLoadMore: boolean; +// loadNum: number; +// userIdChanged: boolean; +// }, +// ]; +// pageParam?: number; +// } const ProjectsIndex = (props: IProjectsView) => { const { formatMessage } = useIntl(); @@ -109,7 +109,9 @@ const ProjectsIndex = (props: IProjectsView) => { const fetchProjects = useCallback( async (pageParam: number | unknown): Promise => { - console.log('fetchProjects', pageParam); + const currentPage = pageParam === undefined ? pageParam : 0; + + console.log({ currentPage }); const variables: IQueries = { limit: userIdChanged @@ -117,7 +119,7 @@ const ProjectsIndex = (props: IProjectsView) => { ? BACKEND_QUERY_LIMIT : filteredProjects.length : projects.length, - skip: userIdChanged ? 0 : projects.length * (loadNum || 0), + skip: userIdChanged ? 0 : projects.length * (currentPage || 0), }; if (user?.id) { @@ -128,7 +130,7 @@ const ProjectsIndex = (props: IProjectsView) => { if ( contextVariables.mainCategory !== router.query?.slug?.toString() ) { - return { projects: [], totalCount: 0, lastPage: 0 }; + return { data: [], totalCount: 0, lastPage: 0 }; } client @@ -156,10 +158,8 @@ const ProjectsIndex = (props: IProjectsView) => { setIsLoading(false); const result = { - projects: isLoadMore - ? [...filteredProjects, ...data] - : data, - lastPage: pageParam ? pageParam : 1, + data: data, + lastPage: currentPage, totalCount: count, }; @@ -176,7 +176,7 @@ const ProjectsIndex = (props: IProjectsView) => { }); }); - return { projects: [], totalCount: 0, lastPage: 0 }; + return { data: [], totalCount: 0, lastPage: 0 }; }, [ contextVariables, @@ -211,13 +211,29 @@ const ProjectsIndex = (props: IProjectsView) => { // // return lastPage?.nextPage ?? false; // return lastPage.nextPage + 1; // }, - getNextPageParam: (returnedData: FetchProjectsResponse) => { - console.log('getNextPageParam called', returnedData); - return returnedData.lastPage + 1; + // getNextPageParam: (returnedData: FetchProjectsResponse) => { + getNextPageParam: (lastPage, allPages, lastPageParam) => { + console.log('getNextPageParam called', lastPage); + console.log('getNextPageParam called', allPages); + console.log('getNextPageParam called', allPages); + console.log('getNextPageParam zadnja stranica', lastPage.lastPage); + return lastPage.lastPage + 1; }, initialPageParam: 0, }); + useEffect(() => { + if (data) { + console.log('Data from React Query:', data); + } + if (hasNextPage !== undefined) { + console.log('Has Next Page:', hasNextPage); + } + if (isFetchingNextPage !== undefined) { + console.log('Is Fetching Next Page:', isFetchingNextPage); + } + }, [data, hasNextPage, isFetchingNextPage]); + // useEffect(() => { // pageNum.current = 0; // fetchProjects(false, 0, true); From b231f2476bf630878fa50aed595761edd8217b13 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Fri, 19 Jul 2024 10:10:25 +0200 Subject: [PATCH 10/97] renamed variables --- .../views/projects/ProjectsIndex.tsx | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 22041006f8..a71b457d55 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -56,8 +56,8 @@ interface IQueries { interface FetchProjectsResponse { data: IProject[]; - totalCount: number; - lastPage: number; + previousCursor?: number; + nextCursor?: number; } // interface FetchProjectsParams { @@ -111,7 +111,7 @@ const ProjectsIndex = (props: IProjectsView) => { async (pageParam: number | unknown): Promise => { const currentPage = pageParam === undefined ? pageParam : 0; - console.log({ currentPage }); + console.log('currentPage', currentPage); const variables: IQueries = { limit: userIdChanged @@ -130,7 +130,7 @@ const ProjectsIndex = (props: IProjectsView) => { if ( contextVariables.mainCategory !== router.query?.slug?.toString() ) { - return { data: [], totalCount: 0, lastPage: 0 }; + return { data: [], previousCursor: 0, nextCursor: 0 }; } client @@ -147,6 +147,7 @@ const ProjectsIndex = (props: IProjectsView) => { }) .then((res: { data: { allProjects: IFetchAllProjects } }) => { const data = res.data?.allProjects?.projects; + console.log({ res }); const count = res.data?.allProjects?.totalCount; setTotalCount(count); @@ -159,8 +160,8 @@ const ProjectsIndex = (props: IProjectsView) => { const result = { data: data, - lastPage: currentPage, - totalCount: count, + previousCursor: projects.length * (currentPage || 0), + nextCursor: projects.length * (currentPage || 0) + 15, }; console.log('fetchProjects result', result); @@ -176,7 +177,7 @@ const ProjectsIndex = (props: IProjectsView) => { }); }); - return { data: [], totalCount: 0, lastPage: 0 }; + return { data: [], previousCursor: 0, nextCursor: 0 }; }, [ contextVariables, @@ -186,6 +187,8 @@ const ProjectsIndex = (props: IProjectsView) => { router.query.slug, selectedMainCategory, user?.id, + userIdChanged, + isLoadMore, ], ); @@ -201,23 +204,9 @@ const ProjectsIndex = (props: IProjectsView) => { queryKey: ['projects'], queryFn: ({ pageParam = 0 }: QueryFunctionContext) => fetchProjects(pageParam), - // queryFn: ({ pageParam }) => fetchProjects(pageParam), - // queryFn: ({ pageParam = 0 }: { pageParam: number }) => - // fetchProjects(pageParam), - - // getNextPageParam: lastPage => lastPage?.nextPage, - // getNextPageParam: (lastPage, pages: FetchProjectsResponse[]) => { - // console.log('getNextPageParam called', pages); - // // return lastPage?.nextPage ?? false; - // return lastPage.nextPage + 1; - // }, - // getNextPageParam: (returnedData: FetchProjectsResponse) => { - getNextPageParam: (lastPage, allPages, lastPageParam) => { - console.log('getNextPageParam called', lastPage); - console.log('getNextPageParam called', allPages); - console.log('getNextPageParam called', allPages); - console.log('getNextPageParam zadnja stranica', lastPage.lastPage); - return lastPage.lastPage + 1; + getNextPageParam: (lastPage, fetchedData) => { + console.log('getNextPageParam called', lastPage, fetchedData); + return lastPage.nextCursor; }, initialPageParam: 0, }); @@ -394,6 +383,12 @@ const ProjectsIndex = (props: IProjectsView) => { /> )} + ); From a77dd0f37a77157c171ba6d36e6dcd7ea399ace4 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Fri, 19 Jul 2024 14:23:41 +0200 Subject: [PATCH 11/97] last try --- .../views/projects/ProjectsIndex.tsx | 250 +++++++++++------- 1 file changed, 154 insertions(+), 96 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index a71b457d55..188f21d05d 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { Fragment, useCallback, useEffect, useRef, useState } from 'react'; import { useRouter } from 'next/router'; import { brandColors, @@ -54,24 +54,12 @@ interface IQueries { connectedWalletUserId?: number; } -interface FetchProjectsResponse { +interface Page { data: IProject[]; previousCursor?: number; nextCursor?: number; } -// interface FetchProjectsParams { -// queryKey: [ -// string, -// { -// isLoadMore: boolean; -// loadNum: number; -// userIdChanged: boolean; -// }, -// ]; -// pageParam?: number; -// } - const ProjectsIndex = (props: IProjectsView) => { const { formatMessage } = useIntl(); const { projects, totalCount: _totalCount } = props; @@ -104,14 +92,18 @@ const ProjectsIndex = (props: IProjectsView) => { // Default values for queryKey const [isLoadMore, setIsLoadMore] = useState(false); - const [loadNum, setLoadNum] = useState(0); + // const [loadNum, setLoadNum] = useState(1); const [userIdChanged, setUserIdChanged] = useState(false); const fetchProjects = useCallback( - async (pageParam: number | unknown): Promise => { - const currentPage = pageParam === undefined ? pageParam : 0; + async (pageParam: number | unknown): Promise => { + // const currentPage = pageParam === undefined ? pageParam : 0; + console.log('pageParam', pageParam); + const currentPage = typeof pageParam === 'number' ? pageParam : 0; - console.log('currentPage', currentPage); + console.log('currentPage', pageParam); + console.log('skip projects.length', projects.length); + console.log('skip', projects.length * (currentPage || 0)); const variables: IQueries = { limit: userIdChanged @@ -127,61 +119,106 @@ const ProjectsIndex = (props: IProjectsView) => { } setIsLoading(true); - if ( - contextVariables.mainCategory !== router.query?.slug?.toString() - ) { - return { data: [], previousCursor: 0, nextCursor: 0 }; - } - - client - .query({ - query: FETCH_ALL_PROJECTS, - variables: { - ...variables, - ...contextVariables, - mainCategory: isArchivedQF - ? undefined - : getMainCategorySlug(selectedMainCategory), - qfRoundSlug: isArchivedQF ? router.query.slug : null, - }, - }) - .then((res: { data: { allProjects: IFetchAllProjects } }) => { - const data = res.data?.allProjects?.projects; - console.log({ res }); - const count = res.data?.allProjects?.totalCount; - setTotalCount(count); - - setFilteredProjects(prevProjects => { - isInfiniteScrolling.current = - (data.length + prevProjects.length) % 45 !== 0; - return isLoadMore ? [...prevProjects, ...data] : data; - }); - setIsLoading(false); - - const result = { - data: data, - previousCursor: projects.length * (currentPage || 0), - nextCursor: projects.length * (currentPage || 0) + 15, - }; - - console.log('fetchProjects result', result); - return result; - }) - .catch((err: any) => { - setIsLoading(false); - showToastError(err); - captureException(err, { - tags: { - section: 'fetchAllProjects', - }, - }); - }); - - return { data: [], previousCursor: 0, nextCursor: 0 }; + // if ( + // contextVariables.mainCategory !== router.query?.slug?.toString() + // ) { + // console.log('run first'); + // return { + // data: filteredProjects, + // previousCursor: 0, + // nextCursor: 0, + // }; + // } + + // client + // .query({ + // query: FETCH_ALL_PROJECTS, + // variables: { + // ...variables, + // ...contextVariables, + // mainCategory: isArchivedQF + // ? undefined + // : getMainCategorySlug(selectedMainCategory), + // qfRoundSlug: isArchivedQF ? router.query.slug : null, + // }, + // }) + // .then((res: { data: { allProjects: IFetchAllProjects } }) => { + // const data = res.data?.allProjects?.projects; + // console.log({ res }); + // const count = res.data?.allProjects?.totalCount; + // setTotalCount(count); + + // setFilteredProjects(prevProjects => { + // isInfiniteScrolling.current = + // (data.length + prevProjects.length) % 45 !== 0; + // return isLoadMore ? [...prevProjects, ...data] : data; + // }); + // setIsLoading(false); + + // console.log('run third'); + + // const result = { + // data: data, + // previousCursor: projects.length * (currentPage || 0), + // nextCursor: projects.length * (currentPage || 0) + 15, + // }; + + // console.log('fetchProjects result', result); + // return result; + // }) + // .catch((err: any) => { + // setIsLoading(false); + // showToastError(err); + // captureException(err, { + // tags: { + // section: 'fetchAllProjects', + // }, + // }); + // }); + + // console.log('run second'); + + // return { + // data: filteredProjects, + // previousCursor: projects.length * (currentPage || 0), + // nextCursor: projects.length * (currentPage || 0) + 15, + // }; + + const res = await client.query({ + query: FETCH_ALL_PROJECTS, + variables: { + ...variables, + ...contextVariables, + mainCategory: isArchivedQF + ? undefined + : getMainCategorySlug(selectedMainCategory), + qfRoundSlug: isArchivedQF ? router.query.slug : null, + }, + }); + + const data = res.data?.allProjects?.projects; + console.log({ res }); + const count = res.data?.allProjects?.totalCount; + setTotalCount(count); + + setFilteredProjects(prevProjects => { + isInfiniteScrolling.current = + (data.length + prevProjects.length) % 45 !== 0; + return isLoadMore ? [...prevProjects, ...data] : data; + }); + + console.log('run second'); + + setIsLoading(false); + + return { + data: data, + previousCursor: currentPage ? currentPage - 1 : 0, + nextCursor: currentPage ? currentPage + 1 : 0, + }; }, [ contextVariables, - filteredProjects.length, isArchivedQF, projects.length, router.query.slug, @@ -189,6 +226,7 @@ const ProjectsIndex = (props: IProjectsView) => { user?.id, userIdChanged, isLoadMore, + filteredProjects, ], ); @@ -200,7 +238,7 @@ const ProjectsIndex = (props: IProjectsView) => { isError, isFetching, isFetchingNextPage, - } = useInfiniteQuery({ + } = useInfiniteQuery({ queryKey: ['projects'], queryFn: ({ pageParam = 0 }: QueryFunctionContext) => fetchProjects(pageParam), @@ -238,7 +276,7 @@ const ProjectsIndex = (props: IProjectsView) => { // fetchProjects(true, pageNum.current + 1); // pageNum.current = pageNum.current + 1; console.log('LOAD MORE'); - fetchNextPage(); + // fetchNextPage(); // }, [fetchProjects, isLoading]); }, [fetchNextPage]); @@ -255,28 +293,28 @@ const ProjectsIndex = (props: IProjectsView) => { const onProjectsPageOrActiveQFPage = !isQF || (isQF && activeQFRound); - useEffect(() => { - const handleObserver = (entities: any) => { - if (!isInfiniteScrolling.current) return; - const target = entities[0]; - if (target.isIntersecting) { - loadMore(); - } - }; - const option = { - root: null, - threshold: 1, - }; - const observer = new IntersectionObserver(handleObserver, option); - if (lastElementRef.current) { - observer.observe(lastElementRef.current); - } - return () => { - if (observer) { - observer.disconnect(); - } - }; - }, [loadMore]); + // useEffect(() => { + // const handleObserver = (entities: any) => { + // if (!isInfiniteScrolling.current) return; + // const target = entities[0]; + // if (target.isIntersecting) { + // loadMore(); + // } + // }; + // const option = { + // root: null, + // threshold: 1, + // }; + // const observer = new IntersectionObserver(handleObserver, option); + // if (lastElementRef.current) { + // observer.observe(lastElementRef.current); + // } + // return () => { + // if (observer) { + // observer.disconnect(); + // } + // }; + // }, [loadMore]); useEffect(() => { if ( @@ -291,6 +329,15 @@ const ProjectsIndex = (props: IProjectsView) => { if (isNotFound) return ; + // TODO: Add a loading spinner when isFetchingNextPage is true + if (isFetching && !isFetchingNextPage) { + return
Loading...
; + } + + if (isError) { + return
Error: {error.message}
; + } + return ( <> {isLoading && ( @@ -337,13 +384,24 @@ const ProjectsIndex = (props: IProjectsView) => { ) : ( )} - {filteredProjects.map((project, idx) => ( + {data?.pages.map((page, pageIndex) => ( + + {page.data.map((project, idx) => ( + + ))} + + ))} + {/* {filteredProjects.map((project, idx) => ( - ))} + ))} */} {/* */} From 164d4820b1ae604efa07170be609f721c1ae673f Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 22 Jul 2024 14:23:37 +0200 Subject: [PATCH 12/97] fetching fixed, removed unnecessary code --- .../views/projects/ProjectsIndex.tsx | 207 ++++-------------- 1 file changed, 48 insertions(+), 159 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 188f21d05d..688f1ad4e2 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -10,7 +10,6 @@ import { import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { captureException } from '@sentry/nextjs'; - import { QueryFunctionContext, useInfiniteQuery } from '@tanstack/react-query'; import ProjectCard from '@/components/project-card/ProjectCard'; import Routes from '@/lib/constants/Routes'; @@ -18,14 +17,12 @@ import { isUserRegistered, showToastError } from '@/lib/helpers'; import { FETCH_ALL_PROJECTS } from '@/apollo/gql/gqlProjects'; import { client } from '@/apollo/apolloClient'; import { IProject } from '@/apollo/types/types'; -import { IFetchAllProjects } from '@/apollo/types/gqlTypes'; import ProjectsNoResults from '@/components/views/projects/ProjectsNoResults'; import { BACKEND_QUERY_LIMIT, mediaQueries } from '@/lib/constants/constants'; import { useAppDispatch, useAppSelector } from '@/features/hooks'; import { setShowCompleteProfile } from '@/features/modal/modal.slice'; import { ProjectsBanner } from './ProjectsBanner'; import { useProjectsContext } from '@/context/projects.context'; - import { ProjectsMiddleBanner } from './MiddleBanners/ProjectsMiddleBanner'; import { ActiveQFProjectsBanner } from './qfBanner/ActiveQFProjectsBanner'; import { PassportBanner } from '@/components/PassportBanner'; @@ -54,6 +51,9 @@ interface IQueries { connectedWalletUserId?: number; } +/** + * A page of projects - return type in fetchProjects function + */ interface Page { data: IProject[]; previousCursor?: number; @@ -67,7 +67,6 @@ const ProjectsIndex = (props: IProjectsView) => { const { activeQFRound, mainCategories } = useAppSelector( state => state.general, ); - const [isLoading, setIsLoading] = useState(false); const [isNotFound, setIsNotFound] = useState(false); const [filteredProjects, setFilteredProjects] = useState(projects); @@ -84,27 +83,18 @@ const ProjectsIndex = (props: IProjectsView) => { } = useProjectsContext(); const router = useRouter(); - const pageNum = useRef(0); const lastElementRef = useRef(null); const isInfiniteScrolling = useRef(true); - router?.events?.on('routeChangeStart', () => setIsLoading(true)); - // Default values for queryKey const [isLoadMore, setIsLoadMore] = useState(false); - // const [loadNum, setLoadNum] = useState(1); const [userIdChanged, setUserIdChanged] = useState(false); const fetchProjects = useCallback( async (pageParam: number | unknown): Promise => { - // const currentPage = pageParam === undefined ? pageParam : 0; console.log('pageParam', pageParam); const currentPage = typeof pageParam === 'number' ? pageParam : 0; - console.log('currentPage', pageParam); - console.log('skip projects.length', projects.length); - console.log('skip', projects.length * (currentPage || 0)); - const variables: IQueries = { limit: userIdChanged ? filteredProjects.length > 50 @@ -118,72 +108,6 @@ const ProjectsIndex = (props: IProjectsView) => { variables.connectedWalletUserId = Number(user?.id); } - setIsLoading(true); - // if ( - // contextVariables.mainCategory !== router.query?.slug?.toString() - // ) { - // console.log('run first'); - // return { - // data: filteredProjects, - // previousCursor: 0, - // nextCursor: 0, - // }; - // } - - // client - // .query({ - // query: FETCH_ALL_PROJECTS, - // variables: { - // ...variables, - // ...contextVariables, - // mainCategory: isArchivedQF - // ? undefined - // : getMainCategorySlug(selectedMainCategory), - // qfRoundSlug: isArchivedQF ? router.query.slug : null, - // }, - // }) - // .then((res: { data: { allProjects: IFetchAllProjects } }) => { - // const data = res.data?.allProjects?.projects; - // console.log({ res }); - // const count = res.data?.allProjects?.totalCount; - // setTotalCount(count); - - // setFilteredProjects(prevProjects => { - // isInfiniteScrolling.current = - // (data.length + prevProjects.length) % 45 !== 0; - // return isLoadMore ? [...prevProjects, ...data] : data; - // }); - // setIsLoading(false); - - // console.log('run third'); - - // const result = { - // data: data, - // previousCursor: projects.length * (currentPage || 0), - // nextCursor: projects.length * (currentPage || 0) + 15, - // }; - - // console.log('fetchProjects result', result); - // return result; - // }) - // .catch((err: any) => { - // setIsLoading(false); - // showToastError(err); - // captureException(err, { - // tags: { - // section: 'fetchAllProjects', - // }, - // }); - // }); - - // console.log('run second'); - - // return { - // data: filteredProjects, - // previousCursor: projects.length * (currentPage || 0), - // nextCursor: projects.length * (currentPage || 0) + 15, - // }; - const res = await client.query({ query: FETCH_ALL_PROJECTS, variables: { @@ -197,7 +121,6 @@ const ProjectsIndex = (props: IProjectsView) => { }); const data = res.data?.allProjects?.projects; - console.log({ res }); const count = res.data?.allProjects?.totalCount; setTotalCount(count); @@ -207,14 +130,10 @@ const ProjectsIndex = (props: IProjectsView) => { return isLoadMore ? [...prevProjects, ...data] : data; }); - console.log('run second'); - - setIsLoading(false); - return { data: data, - previousCursor: currentPage ? currentPage - 1 : 0, - nextCursor: currentPage ? currentPage + 1 : 0, + previousCursor: currentPage - 1, + nextCursor: currentPage + 1, }; }, [ @@ -234,7 +153,6 @@ const ProjectsIndex = (props: IProjectsView) => { data, error, fetchNextPage, - hasNextPage, isError, isFetching, isFetchingNextPage, @@ -242,42 +160,23 @@ const ProjectsIndex = (props: IProjectsView) => { queryKey: ['projects'], queryFn: ({ pageParam = 0 }: QueryFunctionContext) => fetchProjects(pageParam), - getNextPageParam: (lastPage, fetchedData) => { - console.log('getNextPageParam called', lastPage, fetchedData); + getNextPageParam: lastPage => { return lastPage.nextCursor; }, initialPageParam: 0, }); useEffect(() => { - if (data) { - console.log('Data from React Query:', data); - } - if (hasNextPage !== undefined) { - console.log('Has Next Page:', hasNextPage); - } - if (isFetchingNextPage !== undefined) { - console.log('Is Fetching Next Page:', isFetchingNextPage); - } - }, [data, hasNextPage, isFetchingNextPage]); + setUserIdChanged(prevState => !prevState); + fetchNextPage({ cancelRefetch: true }); + }, [fetchNextPage, user?.id]); - // useEffect(() => { - // pageNum.current = 0; - // fetchProjects(false, 0, true); - // }, [user?.id]); - - // useEffect(() => { - // pageNum.current = 0; - // fetchProjects(false, 0); - // }, [contextVariables]); + useEffect(() => { + fetchNextPage({ cancelRefetch: true }); + }, [contextVariables]); const loadMore = useCallback(() => { - // if (isLoading) return; - // fetchProjects(true, pageNum.current + 1); - // pageNum.current = pageNum.current + 1; - console.log('LOAD MORE'); - // fetchNextPage(); - // }, [fetchProjects, isLoading]); + fetchNextPage(); }, [fetchNextPage]); const handleCreateButton = () => { @@ -293,28 +192,28 @@ const ProjectsIndex = (props: IProjectsView) => { const onProjectsPageOrActiveQFPage = !isQF || (isQF && activeQFRound); - // useEffect(() => { - // const handleObserver = (entities: any) => { - // if (!isInfiniteScrolling.current) return; - // const target = entities[0]; - // if (target.isIntersecting) { - // loadMore(); - // } - // }; - // const option = { - // root: null, - // threshold: 1, - // }; - // const observer = new IntersectionObserver(handleObserver, option); - // if (lastElementRef.current) { - // observer.observe(lastElementRef.current); - // } - // return () => { - // if (observer) { - // observer.disconnect(); - // } - // }; - // }, [loadMore]); + useEffect(() => { + const handleObserver = (entities: any) => { + if (!isInfiniteScrolling.current) return; + const target = entities[0]; + if (target.isIntersecting) { + loadMore(); + } + }; + const option = { + root: null, + threshold: 1, + }; + const observer = new IntersectionObserver(handleObserver, option); + if (lastElementRef.current) { + observer.observe(lastElementRef.current); + } + return () => { + if (observer) { + observer.disconnect(); + } + }; + }, [loadMore]); useEffect(() => { if ( @@ -329,18 +228,19 @@ const ProjectsIndex = (props: IProjectsView) => { if (isNotFound) return ; - // TODO: Add a loading spinner when isFetchingNextPage is true - if (isFetching && !isFetchingNextPage) { - return
Loading...
; - } - + // Handle fetching errors - if (isError) { - return
Error: {error.message}
; + showToastError(error); + captureException(error, { + tags: { + section: 'fetchAllProjects', + }, + }); } return ( <> - {isLoading && ( + {isFetching && !isFetchingNextPage && ( @@ -375,7 +275,9 @@ const ProjectsIndex = (props: IProjectsView) => { )} - {isLoading && } + {isFetching && !isFetchingNextPage && ( + + )} {filteredProjects?.length > 0 ? ( @@ -395,13 +297,6 @@ const ProjectsIndex = (props: IProjectsView) => { ))} ))} - {/* {filteredProjects.map((project, idx) => ( - - ))} */} {/* */} @@ -418,14 +313,14 @@ const ProjectsIndex = (props: IProjectsView) => {
@@ -441,12 +336,6 @@ const ProjectsIndex = (props: IProjectsView) => { /> )} - ); From 214333a0e337bf8937b28e645b3a68b12626ec38 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 22 Jul 2024 15:06:55 +0200 Subject: [PATCH 13/97] fixed loading duplication now I need to go through the all variables and states to check if something is missing --- .../views/projects/ProjectsIndex.tsx | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 688f1ad4e2..310b0bafb7 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -160,13 +160,16 @@ const ProjectsIndex = (props: IProjectsView) => { queryKey: ['projects'], queryFn: ({ pageParam = 0 }: QueryFunctionContext) => fetchProjects(pageParam), - getNextPageParam: lastPage => { + getNextPageParam: (lastPage, someData) => { + console.log('lastPage', lastPage); + console.log('someData', someData); return lastPage.nextCursor; }, initialPageParam: 0, }); useEffect(() => { + console.log('user id changed'); setUserIdChanged(prevState => !prevState); fetchNextPage({ cancelRefetch: true }); }, [fetchNextPage, user?.id]); @@ -240,7 +243,7 @@ const ProjectsIndex = (props: IProjectsView) => { return ( <> - {isFetching && !isFetchingNextPage && ( + {(isFetching || isFetchingNextPage) && ( @@ -275,9 +278,7 @@ const ProjectsIndex = (props: IProjectsView) => { )} - {isFetching && !isFetchingNextPage && ( - - )} + {isFetchingNextPage && } {filteredProjects?.length > 0 ? ( @@ -289,11 +290,14 @@ const ProjectsIndex = (props: IProjectsView) => { {data?.pages.map((page, pageIndex) => ( {page.data.map((project, idx) => ( - +
+ {project.id} + +
))}
))} @@ -313,14 +317,14 @@ const ProjectsIndex = (props: IProjectsView) => {
From b0492f4ab3fa9935f6900d1b07ca04d1aa846efa Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 23 Jul 2024 16:04:37 +0200 Subject: [PATCH 14/97] Added reset query option --- .../views/projects/ProjectsIndex.tsx | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 310b0bafb7..1d7ca1b920 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -10,7 +10,11 @@ import { import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { captureException } from '@sentry/nextjs'; -import { QueryFunctionContext, useInfiniteQuery } from '@tanstack/react-query'; +import { + QueryFunctionContext, + useInfiniteQuery, + useQueryClient, +} from '@tanstack/react-query'; import ProjectCard from '@/components/project-card/ProjectCard'; import Routes from '@/lib/constants/Routes'; import { isUserRegistered, showToastError } from '@/lib/helpers'; @@ -61,6 +65,7 @@ interface Page { } const ProjectsIndex = (props: IProjectsView) => { + const queryClient = useQueryClient(); const { formatMessage } = useIntl(); const { projects, totalCount: _totalCount } = props; const user = useAppSelector(state => state.user.userData); @@ -168,16 +173,25 @@ const ProjectsIndex = (props: IProjectsView) => { initialPageParam: 0, }); + // User signied in or singout reset query useEffect(() => { console.log('user id changed'); setUserIdChanged(prevState => !prevState); - fetchNextPage({ cancelRefetch: true }); - }, [fetchNextPage, user?.id]); + queryClient.resetQueries({ + queryKey: ['projects'], + exact: true, + }); + }, [queryClient, user?.id]); + // Reset query if contect variables change occurs useEffect(() => { - fetchNextPage({ cancelRefetch: true }); - }, [contextVariables]); + queryClient.resetQueries({ + queryKey: ['projects'], + exact: true, + }); + }, [contextVariables, queryClient]); + // Function that triggers when you scroll down - infinite loading const loadMore = useCallback(() => { fetchNextPage(); }, [fetchNextPage]); @@ -193,8 +207,13 @@ const ProjectsIndex = (props: IProjectsView) => { const showLoadMore = totalCount > filteredProjects?.length && !isInfiniteScrolling.current; + // Check if there any active QF const onProjectsPageOrActiveQFPage = !isQF || (isQF && activeQFRound); + /* + * This function will be called when the observed elements intersect with the viewport. + * Observed element is last project on the list that trigger another fetch projects to load. + */ useEffect(() => { const handleObserver = (entities: any) => { if (!isInfiniteScrolling.current) return; @@ -243,6 +262,17 @@ const ProjectsIndex = (props: IProjectsView) => { return ( <> + {(isFetching || isFetchingNextPage) && ( @@ -290,8 +320,7 @@ const ProjectsIndex = (props: IProjectsView) => { {data?.pages.map((page, pageIndex) => ( {page.data.map((project, idx) => ( -
- {project.id} +
Date: Fri, 26 Jul 2024 15:48:03 +0200 Subject: [PATCH 15/97] added scroll to row when you got back --- .../views/projects/ProjectsIndex.tsx | 74 ++++++++++++------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 1d7ca1b920..929822376e 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -97,7 +97,6 @@ const ProjectsIndex = (props: IProjectsView) => { const fetchProjects = useCallback( async (pageParam: number | unknown): Promise => { - console.log('pageParam', pageParam); const currentPage = typeof pageParam === 'number' ? pageParam : 0; const variables: IQueries = { @@ -174,22 +173,26 @@ const ProjectsIndex = (props: IProjectsView) => { }); // User signied in or singout reset query - useEffect(() => { - console.log('user id changed'); - setUserIdChanged(prevState => !prevState); - queryClient.resetQueries({ - queryKey: ['projects'], - exact: true, - }); - }, [queryClient, user?.id]); + // TODO: need to refactor, only change when user loggin or out + // useEffect(() => { + // console.log('user id changed'); + // if (user?.id) { + // setUserIdChanged(prevState => !prevState); + // queryClient.resetQueries({ + // queryKey: ['projects'], + // exact: true, + // }); + // } + // }, [queryClient, user?.id]); // Reset query if contect variables change occurs - useEffect(() => { - queryClient.resetQueries({ - queryKey: ['projects'], - exact: true, - }); - }, [contextVariables, queryClient]); + // TODO: need to refactor, + // useEffect(() => { + // queryClient.resetQueries({ + // queryKey: ['projects'], + // exact: true, + // }); + // }, [contextVariables, queryClient]); // Function that triggers when you scroll down - infinite loading const loadMore = useCallback(() => { @@ -247,10 +250,30 @@ const ProjectsIndex = (props: IProjectsView) => { } }, [selectedMainCategory, mainCategories.length]); + // Save last clicked project + const handleProjectClick = (slug: string) => { + localStorage.setItem('lastProjectClicked', slug); + }; + + // Handle last clicked project, if it exist scroll to that position + useEffect(() => { + if (!isFetching && !isFetchingNextPage) { + const lastProjectClicked = + localStorage.getItem('lastProjectClicked'); + if (lastProjectClicked) { + window.scrollTo({ + top: document.getElementById(lastProjectClicked)?.offsetTop, + behavior: 'smooth', + }); + localStorage.removeItem('lastProjectClicked'); + } + } + }, [isFetching, isFetchingNextPage]); + if (isNotFound) return ; - // Handle fetching errors - + // Handle fetching errors from React Query if (isError) { showToastError(error); captureException(error, { @@ -262,17 +285,6 @@ const ProjectsIndex = (props: IProjectsView) => { return ( <> - {(isFetching || isFetchingNextPage) && ( @@ -320,7 +332,13 @@ const ProjectsIndex = (props: IProjectsView) => { {data?.pages.map((page, pageIndex) => ( {page.data.map((project, idx) => ( -
+
+ handleProjectClick(project.slug) + } + > Date: Mon, 29 Jul 2024 14:37:13 +0200 Subject: [PATCH 16/97] removing user.id dependencies --- .../views/projects/ProjectsIndex.tsx | 44 +++---------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 929822376e..25648310cb 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -22,7 +22,7 @@ import { FETCH_ALL_PROJECTS } from '@/apollo/gql/gqlProjects'; import { client } from '@/apollo/apolloClient'; import { IProject } from '@/apollo/types/types'; import ProjectsNoResults from '@/components/views/projects/ProjectsNoResults'; -import { BACKEND_QUERY_LIMIT, mediaQueries } from '@/lib/constants/constants'; +import { mediaQueries } from '@/lib/constants/constants'; import { useAppDispatch, useAppSelector } from '@/features/hooks'; import { setShowCompleteProfile } from '@/features/modal/modal.slice'; import { ProjectsBanner } from './ProjectsBanner'; @@ -69,6 +69,7 @@ const ProjectsIndex = (props: IProjectsView) => { const { formatMessage } = useIntl(); const { projects, totalCount: _totalCount } = props; const user = useAppSelector(state => state.user.userData); + const { activeQFRound, mainCategories } = useAppSelector( state => state.general, ); @@ -91,21 +92,13 @@ const ProjectsIndex = (props: IProjectsView) => { const lastElementRef = useRef(null); const isInfiniteScrolling = useRef(true); - // Default values for queryKey - const [isLoadMore, setIsLoadMore] = useState(false); - const [userIdChanged, setUserIdChanged] = useState(false); - const fetchProjects = useCallback( async (pageParam: number | unknown): Promise => { const currentPage = typeof pageParam === 'number' ? pageParam : 0; const variables: IQueries = { - limit: userIdChanged - ? filteredProjects.length > 50 - ? BACKEND_QUERY_LIMIT - : filteredProjects.length - : projects.length, - skip: userIdChanged ? 0 : projects.length * (currentPage || 0), + limit: projects.length, + skip: projects.length * (currentPage || 0), }; if (user?.id) { @@ -128,11 +121,7 @@ const ProjectsIndex = (props: IProjectsView) => { const count = res.data?.allProjects?.totalCount; setTotalCount(count); - setFilteredProjects(prevProjects => { - isInfiniteScrolling.current = - (data.length + prevProjects.length) % 45 !== 0; - return isLoadMore ? [...prevProjects, ...data] : data; - }); + setFilteredProjects(prevProjects => [...prevProjects, ...data]); return { data: data, @@ -147,9 +136,6 @@ const ProjectsIndex = (props: IProjectsView) => { router.query.slug, selectedMainCategory, user?.id, - userIdChanged, - isLoadMore, - filteredProjects, ], ); @@ -172,19 +158,6 @@ const ProjectsIndex = (props: IProjectsView) => { initialPageParam: 0, }); - // User signied in or singout reset query - // TODO: need to refactor, only change when user loggin or out - // useEffect(() => { - // console.log('user id changed'); - // if (user?.id) { - // setUserIdChanged(prevState => !prevState); - // queryClient.resetQueries({ - // queryKey: ['projects'], - // exact: true, - // }); - // } - // }, [queryClient, user?.id]); - // Reset query if contect variables change occurs // TODO: need to refactor, // useEffect(() => { @@ -207,9 +180,6 @@ const ProjectsIndex = (props: IProjectsView) => { } }; - const showLoadMore = - totalCount > filteredProjects?.length && !isInfiniteScrolling.current; - // Check if there any active QF const onProjectsPageOrActiveQFPage = !isQF || (isQF && activeQFRound); @@ -248,7 +218,7 @@ const ProjectsIndex = (props: IProjectsView) => { ) { setIsNotFound(true); } - }, [selectedMainCategory, mainCategories.length]); + }, [selectedMainCategory, mainCategories.length, isArchivedQF]); // Save last clicked project const handleProjectClick = (slug: string) => { @@ -359,7 +329,7 @@ const ProjectsIndex = (props: IProjectsView) => { {totalCount > filteredProjects?.length && (
)} - {showLoadMore && ( + {(isFetching || isFetchingNextPage) && ( <> Date: Mon, 29 Jul 2024 14:50:34 +0200 Subject: [PATCH 17/97] fix loading button if something hook up --- src/components/views/projects/ProjectsIndex.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 25648310cb..385cbc592d 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -159,8 +159,9 @@ const ProjectsIndex = (props: IProjectsView) => { }); // Reset query if contect variables change occurs - // TODO: need to refactor, + // TODO: This is causing a bug where the page is refreshed when you come back // useEffect(() => { + // console.log('SOMETHING CHANGED'); // queryClient.resetQueries({ // queryKey: ['projects'], // exact: true, @@ -329,7 +330,7 @@ const ProjectsIndex = (props: IProjectsView) => { {totalCount > filteredProjects?.length && (
)} - {(isFetching || isFetchingNextPage) && ( + {!isFetching && !isFetchingNextPage && ( <> Date: Tue, 30 Jul 2024 14:56:06 +0200 Subject: [PATCH 18/97] fixed total count and removed unused useEffect --- .../views/projects/ProjectsIndex.tsx | 74 +++++++++---------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 385cbc592d..e3d3535071 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -121,7 +121,10 @@ const ProjectsIndex = (props: IProjectsView) => { const count = res.data?.allProjects?.totalCount; setTotalCount(count); - setFilteredProjects(prevProjects => [...prevProjects, ...data]); + setFilteredProjects(prevProjects => [ + ...prevProjects, + data.allProjects?.projects, + ]); return { data: data, @@ -158,16 +161,6 @@ const ProjectsIndex = (props: IProjectsView) => { initialPageParam: 0, }); - // Reset query if contect variables change occurs - // TODO: This is causing a bug where the page is refreshed when you come back - // useEffect(() => { - // console.log('SOMETHING CHANGED'); - // queryClient.resetQueries({ - // queryKey: ['projects'], - // exact: true, - // }); - // }, [contextVariables, queryClient]); - // Function that triggers when you scroll down - infinite loading const loadMore = useCallback(() => { fetchNextPage(); @@ -330,34 +323,37 @@ const ProjectsIndex = (props: IProjectsView) => { {totalCount > filteredProjects?.length && (
)} - {!isFetching && !isFetchingNextPage && ( - <> - -
- - ) - } - /> - - - )} + {!isFetching && + !isFetchingNextPage && + totalCount < filteredProjects?.length && ( + <> + +
+ + ) + } + /> + {totalCount} - {filteredProjects?.length} + + + )} ); From 78a9454b5d0546c6b33e55a0e386979d6db88dc6 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 30 Jul 2024 15:26:02 +0200 Subject: [PATCH 19/97] fixed total count, introduced total pages --- .../views/projects/ProjectsIndex.tsx | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index e3d3535071..2269dad0d7 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -76,7 +76,9 @@ const ProjectsIndex = (props: IProjectsView) => { const [isNotFound, setIsNotFound] = useState(false); const [filteredProjects, setFilteredProjects] = useState(projects); - const [totalCount, setTotalCount] = useState(_totalCount); + const [totalPages, setTotalPages] = useState( + Math.ceil(_totalCount / projects.length), + ); const isMobile = useMediaQuery(`(max-width: ${deviceSize.tablet - 1}px)`); const dispatch = useAppDispatch(); @@ -119,7 +121,8 @@ const ProjectsIndex = (props: IProjectsView) => { const data = res.data?.allProjects?.projects; const count = res.data?.allProjects?.totalCount; - setTotalCount(count); + const totalPages = Math.ceil(count / projects.length); + setTotalPages(totalPages); setFilteredProjects(prevProjects => [ ...prevProjects, @@ -163,8 +166,10 @@ const ProjectsIndex = (props: IProjectsView) => { // Function that triggers when you scroll down - infinite loading const loadMore = useCallback(() => { - fetchNextPage(); - }, [fetchNextPage]); + if (totalPages > (data?.pages?.length || 0)) { + fetchNextPage(); + } + }, [data?.pages.length, fetchNextPage, totalPages]); const handleCreateButton = () => { if (isUserRegistered(user)) { @@ -281,7 +286,7 @@ const ProjectsIndex = (props: IProjectsView) => { )} {onProjectsPageOrActiveQFPage && ( - + )} {isFetchingNextPage && } @@ -320,12 +325,12 @@ const ProjectsIndex = (props: IProjectsView) => { ) : ( )} - {totalCount > filteredProjects?.length && ( + {_totalCount > filteredProjects?.length && (
)} {!isFetching && !isFetchingNextPage && - totalCount < filteredProjects?.length && ( + totalPages > (data?.pages?.length || 0) && ( <> { ) } /> - {totalCount} - {filteredProjects?.length} Date: Tue, 30 Jul 2024 15:31:48 +0200 Subject: [PATCH 20/97] updated queryKey reference --- src/components/views/projects/ProjectsIndex.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 2269dad0d7..b002787bbc 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -153,7 +153,12 @@ const ProjectsIndex = (props: IProjectsView) => { isFetching, isFetchingNextPage, } = useInfiniteQuery({ - queryKey: ['projects'], + queryKey: [ + 'projects', + contextVariables, + isArchivedQF, + selectedMainCategory, + ], queryFn: ({ pageParam = 0 }: QueryFunctionContext) => fetchProjects(pageParam), getNextPageParam: (lastPage, someData) => { From e2b9ae3755f172b46014ca0da4d86b105a7b5a01 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 16 Sep 2024 17:03:47 +0200 Subject: [PATCH 21/97] fixing total count --- src/components/views/projects/ProjectsIndex.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index b002787bbc..b059e14b50 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -79,6 +79,7 @@ const ProjectsIndex = (props: IProjectsView) => { const [totalPages, setTotalPages] = useState( Math.ceil(_totalCount / projects.length), ); + const [totalCount, setTotalCount] = useState(_totalCount); const isMobile = useMediaQuery(`(max-width: ${deviceSize.tablet - 1}px)`); const dispatch = useAppDispatch(); @@ -123,6 +124,7 @@ const ProjectsIndex = (props: IProjectsView) => { const count = res.data?.allProjects?.totalCount; const totalPages = Math.ceil(count / projects.length); setTotalPages(totalPages); + setTotalCount(count); setFilteredProjects(prevProjects => [ ...prevProjects, @@ -291,7 +293,7 @@ const ProjectsIndex = (props: IProjectsView) => { )} {onProjectsPageOrActiveQFPage && ( - + )} {isFetchingNextPage && } From 2f07fb145cded0b893848f0978367b15ab29f3e9 Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 24 Sep 2024 11:10:39 -0500 Subject: [PATCH 22/97] pending chain id change --- src/components/modals/SwitchNetwork.tsx | 32 ++++++++++++++++--------- src/providers/generalWalletProvider.tsx | 19 ++++++++++++++- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/components/modals/SwitchNetwork.tsx b/src/components/modals/SwitchNetwork.tsx index 618ece34e5..12f4c4528a 100644 --- a/src/components/modals/SwitchNetwork.tsx +++ b/src/components/modals/SwitchNetwork.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react'; +import React, { FC, useState, useEffect } from 'react'; import { B, brandColors, @@ -28,6 +28,7 @@ const defaultNetworks = Object.keys(networksConfig).map(key => ({ networkId: Number(key), chainType: networksConfig[Number(key)].chainType, })); + interface ISwitchNetworkModal extends IModal { desc?: string; customNetworks?: INetworkIdWithChain[]; @@ -39,11 +40,15 @@ const SwitchNetwork: FC = ({ setShowModal, }) => { const { isAnimating, closeModal } = useModalAnimation(setShowModal); - const { switchChain } = useSwitchChain(); const { formatMessage } = useIntl(); - const { walletChainType, handleSingOutAndSignInWithEVM, chain } = - useGeneralWallet(); + const { + walletChainType, + handleSingOutAndSignInWithEVM, + pendingNetworkId, + setPendingNetworkId, + chain, + } = useGeneralWallet(); const chainId = (chain as Chain)?.id; const theme = useAppSelector(state => state.general.theme); @@ -55,6 +60,17 @@ const SwitchNetwork: FC = ({ }; }) || defaultNetworks; + const handleNetworkItemClick = (networkId: number) => { + if (walletChainType === ChainType.SOLANA) { + setPendingNetworkId(networkId); + handleSingOutAndSignInWithEVM(); + closeModal(); // Close the modal since we cannot control the wallet modal + } else { + switchChain?.({ chainId: networkId }); + closeModal(); + } + }; + return ( = ({ {desc &&

{desc}

} {networks?.map(({ networkId, chainType }) => ( { - if (walletChainType === ChainType.SOLANA) { - handleSingOutAndSignInWithEVM(); - } - switchChain?.({ chainId: networkId }); - closeModal(); - }} + onClick={() => handleNetworkItemClick(networkId)} $isSelected={networkId === chainId} key={networkId} $baseTheme={theme} diff --git a/src/providers/generalWalletProvider.tsx b/src/providers/generalWalletProvider.tsx index 8b0bb12e0e..6b0ad2f7e0 100644 --- a/src/providers/generalWalletProvider.tsx +++ b/src/providers/generalWalletProvider.tsx @@ -14,7 +14,7 @@ import { Transaction, SystemProgram, } from '@solana/web3.js'; -import { useBalance, useDisconnect, useAccount } from 'wagmi'; +import { useBalance, useDisconnect, useAccount, useSwitchChain } from 'wagmi'; import { getWalletClient } from '@wagmi/core'; import { WalletAdapterNetwork } from '@solana/wallet-adapter-base'; import { useWeb3Modal } from '@web3modal/wagmi/react'; @@ -58,6 +58,8 @@ interface IGeneralWalletContext { handleSignOutAndShowWelcomeModal: () => Promise; isOnSolana: boolean; isOnEVM: boolean; + pendingNetworkId: number | null; + setPendingNetworkId: (id: number | null) => void; } // Create the context export const GeneralWalletContext = createContext({ @@ -76,6 +78,8 @@ export const GeneralWalletContext = createContext({ handleSignOutAndShowWelcomeModal: async () => {}, isOnSolana: false, isOnEVM: false, + pendingNetworkId: null, + setPendingNetworkId: () => {}, }); const getPhantomSolanaProvider = () => { @@ -93,6 +97,9 @@ export const GeneralWalletProvider: React.FC<{ const [walletChainType, setWalletChainType] = useState( null, ); + const [pendingNetworkId, setPendingNetworkId] = useState( + null, + ); const [walletAddress, setWalletAddress] = useState(null); const [balance, setBalance] = useState(); const [isConnected, setIsConnected] = useState(false); @@ -106,6 +113,7 @@ export const GeneralWalletProvider: React.FC<{ const router = useRouter(); const { token } = useAppSelector(state => state.user); const { setVisible, visible } = useWalletModal(); + const { switchChain } = useSwitchChain(); const isGIVeconomyRoute = useMemo( () => checkIsGIVeconomyRoute(router.route), @@ -266,6 +274,13 @@ export const GeneralWalletProvider: React.FC<{ } }, [walletChainType, nonFormattedEvBalance, solanaBalance]); + useEffect(() => { + if (walletChainType === ChainType.EVM && pendingNetworkId !== null) { + switchChain?.({ chainId: pendingNetworkId }); + setPendingNetworkId(null); + } + }, [walletChainType, pendingNetworkId]); + const signMessage = async ( message: string, ): Promise => { @@ -408,6 +423,8 @@ export const GeneralWalletProvider: React.FC<{ handleSignOutAndShowWelcomeModal, isOnSolana, isOnEVM, + pendingNetworkId, + setPendingNetworkId, }; // Render the provider component with the provided context value From d9e7460a89b188e44962b58a612cb6bb4008614c Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 24 Sep 2024 11:14:04 -0500 Subject: [PATCH 23/97] removing unused variable --- src/components/modals/SwitchNetwork.tsx | 3 +-- src/providers/generalWalletProvider.tsx | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/modals/SwitchNetwork.tsx b/src/components/modals/SwitchNetwork.tsx index 12f4c4528a..c203c6ba17 100644 --- a/src/components/modals/SwitchNetwork.tsx +++ b/src/components/modals/SwitchNetwork.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState, useEffect } from 'react'; +import React, { FC } from 'react'; import { B, brandColors, @@ -45,7 +45,6 @@ const SwitchNetwork: FC = ({ const { walletChainType, handleSingOutAndSignInWithEVM, - pendingNetworkId, setPendingNetworkId, chain, } = useGeneralWallet(); diff --git a/src/providers/generalWalletProvider.tsx b/src/providers/generalWalletProvider.tsx index 6b0ad2f7e0..112fcd1da2 100644 --- a/src/providers/generalWalletProvider.tsx +++ b/src/providers/generalWalletProvider.tsx @@ -58,7 +58,6 @@ interface IGeneralWalletContext { handleSignOutAndShowWelcomeModal: () => Promise; isOnSolana: boolean; isOnEVM: boolean; - pendingNetworkId: number | null; setPendingNetworkId: (id: number | null) => void; } // Create the context @@ -78,7 +77,6 @@ export const GeneralWalletContext = createContext({ handleSignOutAndShowWelcomeModal: async () => {}, isOnSolana: false, isOnEVM: false, - pendingNetworkId: null, setPendingNetworkId: () => {}, }); @@ -423,7 +421,6 @@ export const GeneralWalletProvider: React.FC<{ handleSignOutAndShowWelcomeModal, isOnSolana, isOnEVM, - pendingNetworkId, setPendingNetworkId, }; From 1986731339924e0dfbdd34e59028eb92b9323a71 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 7 Oct 2024 16:50:17 +0200 Subject: [PATCH 24/97] removed two states added all in one --- .../views/projects/ProjectsIndex.tsx | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index b059e14b50..ac90a452c1 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -10,11 +10,7 @@ import { import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { captureException } from '@sentry/nextjs'; -import { - QueryFunctionContext, - useInfiniteQuery, - useQueryClient, -} from '@tanstack/react-query'; +import { QueryFunctionContext, useInfiniteQuery } from '@tanstack/react-query'; import ProjectCard from '@/components/project-card/ProjectCard'; import Routes from '@/lib/constants/Routes'; import { isUserRegistered, showToastError } from '@/lib/helpers'; @@ -65,21 +61,24 @@ interface Page { } const ProjectsIndex = (props: IProjectsView) => { - const queryClient = useQueryClient(); const { formatMessage } = useIntl(); const { projects, totalCount: _totalCount } = props; + const [projectsData, setProjectsData] = useState<{ + projects: IProject[]; + totalPages: number; + totalCount: number; + }>({ + projects: projects, + totalPages: Math.ceil(_totalCount / projects.length), + totalCount: _totalCount, + }); const user = useAppSelector(state => state.user.userData); const { activeQFRound, mainCategories } = useAppSelector( state => state.general, ); const [isNotFound, setIsNotFound] = useState(false); - const [filteredProjects, setFilteredProjects] = - useState(projects); - const [totalPages, setTotalPages] = useState( - Math.ceil(_totalCount / projects.length), - ); - const [totalCount, setTotalCount] = useState(_totalCount); + const isMobile = useMediaQuery(`(max-width: ${deviceSize.tablet - 1}px)`); const dispatch = useAppDispatch(); @@ -120,19 +119,22 @@ const ProjectsIndex = (props: IProjectsView) => { }, }); - const data = res.data?.allProjects?.projects; + const dataProjects = res.data?.allProjects?.projects; const count = res.data?.allProjects?.totalCount; - const totalPages = Math.ceil(count / projects.length); - setTotalPages(totalPages); - setTotalCount(count); - setFilteredProjects(prevProjects => [ - ...prevProjects, - data.allProjects?.projects, - ]); + // Calculate the total number of pages + const totalPages = Math.ceil( + count / (projectsData.projects.length || 1), + ); + + setProjectsData(prevState => ({ + projects: [...prevState.projects, ...dataProjects], + totalPages: totalPages, + totalCount: count, + })); return { - data: data, + data: dataProjects, previousCursor: currentPage - 1, nextCursor: currentPage + 1, }; @@ -141,6 +143,7 @@ const ProjectsIndex = (props: IProjectsView) => { contextVariables, isArchivedQF, projects.length, + projectsData.projects.length, router.query.slug, selectedMainCategory, user?.id, @@ -163,9 +166,7 @@ const ProjectsIndex = (props: IProjectsView) => { ], queryFn: ({ pageParam = 0 }: QueryFunctionContext) => fetchProjects(pageParam), - getNextPageParam: (lastPage, someData) => { - console.log('lastPage', lastPage); - console.log('someData', someData); + getNextPageParam: lastPage => { return lastPage.nextCursor; }, initialPageParam: 0, @@ -173,10 +174,10 @@ const ProjectsIndex = (props: IProjectsView) => { // Function that triggers when you scroll down - infinite loading const loadMore = useCallback(() => { - if (totalPages > (data?.pages?.length || 0)) { + if (projectsData.totalCount > (data?.pages?.length || 0)) { fetchNextPage(); } - }, [data?.pages.length, fetchNextPage, totalPages]); + }, [data?.pages.length, fetchNextPage, projectsData.totalCount]); const handleCreateButton = () => { if (isUserRegistered(user)) { @@ -293,11 +294,11 @@ const ProjectsIndex = (props: IProjectsView) => { )} {onProjectsPageOrActiveQFPage && ( - + )} {isFetchingNextPage && } - {filteredProjects?.length > 0 ? ( + {projectsData.projects.length > 0 ? ( {isQF ? ( @@ -332,12 +333,12 @@ const ProjectsIndex = (props: IProjectsView) => { ) : ( )} - {_totalCount > filteredProjects?.length && ( + {projectsData.totalCount > projectsData.projects.length && (
)} {!isFetching && !isFetchingNextPage && - totalPages > (data?.pages?.length || 0) && ( + projectsData.totalPages > (data?.pages?.length || 0) && ( <> Date: Mon, 7 Oct 2024 19:14:04 +0330 Subject: [PATCH 25/97] add fetchProjects --- src/components/views/projects/services.ts | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/components/views/projects/services.ts diff --git a/src/components/views/projects/services.ts b/src/components/views/projects/services.ts new file mode 100644 index 0000000000..f226f47968 --- /dev/null +++ b/src/components/views/projects/services.ts @@ -0,0 +1,52 @@ +// services/projectsService.ts + +import { client } from '@/apollo/apolloClient'; +import { FETCH_ALL_PROJECTS } from '@/apollo/gql/gqlProjects'; +import { IMainCategory, IProject } from '@/apollo/types/types'; +import { getMainCategorySlug } from '@/helpers/projects'; + +export interface IQueries { + skip?: number; + limit?: number; + connectedWalletUserId?: number; + mainCategory?: string; + qfRoundSlug?: string | null; +} + +export interface Page { + data: IProject[]; + previousCursor?: number; + nextCursor?: number; +} + +export const fetchProjects = async ( + pageParam: number, + variables: IQueries, + contextVariables: any, + isArchivedQF?: boolean, + selectedMainCategory?: IMainCategory, + routerQuerySlug?: string | string[], +): Promise => { + const currentPage = pageParam; + + const res = await client.query({ + query: FETCH_ALL_PROJECTS, + variables: { + ...variables, + ...contextVariables, + mainCategory: isArchivedQF + ? undefined + : getMainCategorySlug(selectedMainCategory), + qfRoundSlug: isArchivedQF ? routerQuerySlug : null, + }, + }); + + const dataProjects: IProject[] = res.data?.allProjects?.projects; + const count: number = res.data?.allProjects?.totalCount; + + return { + data: dataProjects, + previousCursor: currentPage > 0 ? currentPage - 1 : undefined, + nextCursor: dataProjects.length > 0 ? currentPage + 1 : undefined, + }; +}; From 0b3ea24a15ea28380f771b7b1b344ebe2f92ea45 Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 7 Oct 2024 19:36:27 +0330 Subject: [PATCH 26/97] add total count --- src/components/views/projects/services.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/projects/services.ts b/src/components/views/projects/services.ts index f226f47968..41c8e4a60e 100644 --- a/src/components/views/projects/services.ts +++ b/src/components/views/projects/services.ts @@ -17,6 +17,7 @@ export interface Page { data: IProject[]; previousCursor?: number; nextCursor?: number; + totalCount?: number; } export const fetchProjects = async ( @@ -42,11 +43,11 @@ export const fetchProjects = async ( }); const dataProjects: IProject[] = res.data?.allProjects?.projects; - const count: number = res.data?.allProjects?.totalCount; return { data: dataProjects, previousCursor: currentPage > 0 ? currentPage - 1 : undefined, nextCursor: dataProjects.length > 0 ? currentPage + 1 : undefined, + totalCount: res.data?.allProjects?.totalCount, }; }; From 0bdc3888f4b3c6bb404784438f66ccb3437ae45a Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 7 Oct 2024 19:36:58 +0330 Subject: [PATCH 27/97] update projects index --- .../views/projects/ProjectsIndex.tsx | 266 +++++++----------- 1 file changed, 106 insertions(+), 160 deletions(-) diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index ac90a452c1..21b2a291ef 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -1,24 +1,23 @@ -import { Fragment, useCallback, useEffect, useRef, useState } from 'react'; +// components/ProjectsIndex.tsx + +import { Fragment, useCallback, useEffect, useRef } from 'react'; import { useRouter } from 'next/router'; import { brandColors, - OutlineButton, - FlexCenter, Container, deviceSize, + FlexCenter, + mediaQueries, + OutlineButton, } from '@giveth/ui-design-system'; -import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { captureException } from '@sentry/nextjs'; -import { QueryFunctionContext, useInfiniteQuery } from '@tanstack/react-query'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import styled from 'styled-components'; import ProjectCard from '@/components/project-card/ProjectCard'; import Routes from '@/lib/constants/Routes'; import { isUserRegistered, showToastError } from '@/lib/helpers'; -import { FETCH_ALL_PROJECTS } from '@/apollo/gql/gqlProjects'; -import { client } from '@/apollo/apolloClient'; -import { IProject } from '@/apollo/types/types'; import ProjectsNoResults from '@/components/views/projects/ProjectsNoResults'; -import { mediaQueries } from '@/lib/constants/constants'; import { useAppDispatch, useAppSelector } from '@/features/hooks'; import { setShowCompleteProfile } from '@/features/modal/modal.slice'; import { ProjectsBanner } from './ProjectsBanner'; @@ -29,7 +28,6 @@ import { PassportBanner } from '@/components/PassportBanner'; import { QFProjectsMiddleBanner } from './MiddleBanners/QFMiddleBanner'; import { QFNoResultBanner } from './MiddleBanners/QFNoResultBanner'; import { Spinner } from '@/components/Spinner'; -import { getMainCategorySlug } from '@/helpers/projects'; import { FilterContainer } from './filter/FilterContainer'; import { SortContainer } from './sort/SortContainer'; import { ArchivedQFRoundStats } from './ArchivedQFRoundStats'; @@ -39,145 +37,87 @@ import useMediaQuery from '@/hooks/useMediaQuery'; import { QFHeader } from '@/components/views/archivedQFRounds/QFHeader'; import { DefaultQFBanner } from '@/components/DefaultQFBanner'; import NotAvailable from '@/components/NotAvailable'; +import { fetchProjects, IQueries } from './services'; +import { IProject } from '@/apollo/types/types'; export interface IProjectsView { projects: IProject[]; totalCount: number; } -interface IQueries { - skip?: number; - limit?: number; - connectedWalletUserId?: number; -} - -/** - * A page of projects - return type in fetchProjects function - */ -interface Page { - data: IProject[]; - previousCursor?: number; - nextCursor?: number; -} - const ProjectsIndex = (props: IProjectsView) => { const { formatMessage } = useIntl(); const { projects, totalCount: _totalCount } = props; - const [projectsData, setProjectsData] = useState<{ - projects: IProject[]; - totalPages: number; - totalCount: number; - }>({ - projects: projects, - totalPages: Math.ceil(_totalCount / projects.length), - totalCount: _totalCount, - }); const user = useAppSelector(state => state.user.userData); - const { activeQFRound, mainCategories } = useAppSelector( state => state.general, ); - const [isNotFound, setIsNotFound] = useState(false); - const isMobile = useMediaQuery(`(max-width: ${deviceSize.tablet - 1}px)`); - const dispatch = useAppDispatch(); - const { variables: contextVariables, selectedMainCategory, isQF, isArchivedQF, } = useProjectsContext(); - const router = useRouter(); const lastElementRef = useRef(null); const isInfiniteScrolling = useRef(true); - const fetchProjects = useCallback( - async (pageParam: number | unknown): Promise => { - const currentPage = typeof pageParam === 'number' ? pageParam : 0; - - const variables: IQueries = { - limit: projects.length, - skip: projects.length * (currentPage || 0), - }; - - if (user?.id) { - variables.connectedWalletUserId = Number(user?.id); - } + // Define the fetch function for React Query + const fetchProjectsPage = async ({ pageParam = 0 }) => { + const variables: IQueries = { + limit: 20, // Adjust the limit as needed + skip: 20 * pageParam, + }; - const res = await client.query({ - query: FETCH_ALL_PROJECTS, - variables: { - ...variables, - ...contextVariables, - mainCategory: isArchivedQF - ? undefined - : getMainCategorySlug(selectedMainCategory), - qfRoundSlug: isArchivedQF ? router.query.slug : null, - }, - }); + if (user?.id) { + variables.connectedWalletUserId = Number(user.id); + } - const dataProjects = res.data?.allProjects?.projects; - const count = res.data?.allProjects?.totalCount; - - // Calculate the total number of pages - const totalPages = Math.ceil( - count / (projectsData.projects.length || 1), - ); - - setProjectsData(prevState => ({ - projects: [...prevState.projects, ...dataProjects], - totalPages: totalPages, - totalCount: count, - })); - - return { - data: dataProjects, - previousCursor: currentPage - 1, - nextCursor: currentPage + 1, - }; - }, - [ + return await fetchProjects( + pageParam, + variables, contextVariables, isArchivedQF, - projects.length, - projectsData.projects.length, - router.query.slug, selectedMainCategory, - user?.id, - ], - ); + router.query.slug, + ); + }; + // Use the useInfiniteQuery hook with the new v5 API const { data, error, fetchNextPage, + hasNextPage, isError, isFetching, isFetchingNextPage, - } = useInfiniteQuery({ + } = useInfiniteQuery({ queryKey: [ 'projects', contextVariables, isArchivedQF, selectedMainCategory, ], - queryFn: ({ pageParam = 0 }: QueryFunctionContext) => - fetchProjects(pageParam), - getNextPageParam: lastPage => { - return lastPage.nextCursor; - }, + queryFn: fetchProjectsPage, + getNextPageParam: lastPage => lastPage.nextCursor, + getPreviousPageParam: firstPage => firstPage.previousCursor, initialPageParam: 0, + // placeholderData: keepPreviousData, + placeholderData: { + pageParams: [0], + pages: [{ data: projects, totalCount: _totalCount }], + }, }); - // Function that triggers when you scroll down - infinite loading + // Function to load more data when scrolling const loadMore = useCallback(() => { - if (projectsData.totalCount > (data?.pages?.length || 0)) { + if (hasNextPage) { fetchNextPage(); } - }, [data?.pages.length, fetchNextPage, projectsData.totalCount]); + }, [fetchNextPage, hasNextPage]); const handleCreateButton = () => { if (isUserRegistered(user)) { @@ -187,32 +127,28 @@ const ProjectsIndex = (props: IProjectsView) => { } }; - // Check if there any active QF const onProjectsPageOrActiveQFPage = !isQF || (isQF && activeQFRound); - /* - * This function will be called when the observed elements intersect with the viewport. - * Observed element is last project on the list that trigger another fetch projects to load. - */ + // Intersection Observer for infinite scrolling useEffect(() => { - const handleObserver = (entities: any) => { + const handleObserver = (entries: IntersectionObserverEntry[]) => { if (!isInfiniteScrolling.current) return; - const target = entities[0]; + const target = entries[0]; if (target.isIntersecting) { loadMore(); } }; const option = { root: null, - threshold: 1, + threshold: 1.0, }; const observer = new IntersectionObserver(handleObserver, option); if (lastElementRef.current) { observer.observe(lastElementRef.current); } return () => { - if (observer) { - observer.disconnect(); + if (observer && lastElementRef.current) { + observer.unobserve(lastElementRef.current); } }; }, [loadMore]); @@ -223,7 +159,9 @@ const ProjectsIndex = (props: IProjectsView) => { !selectedMainCategory && !isArchivedQF ) { - setIsNotFound(true); + isInfiniteScrolling.current = false; + } else { + isInfiniteScrolling.current = true; } }, [selectedMainCategory, mainCategories.length, isArchivedQF]); @@ -232,33 +170,46 @@ const ProjectsIndex = (props: IProjectsView) => { localStorage.setItem('lastProjectClicked', slug); }; - // Handle last clicked project, if it exist scroll to that position + // Scroll to last clicked project useEffect(() => { if (!isFetching && !isFetchingNextPage) { const lastProjectClicked = localStorage.getItem('lastProjectClicked'); if (lastProjectClicked) { - window.scrollTo({ - top: document.getElementById(lastProjectClicked)?.offsetTop, - behavior: 'smooth', - }); + const element = document.getElementById(lastProjectClicked); + if (element) { + window.scrollTo({ + top: element.offsetTop, + behavior: 'smooth', + }); + } localStorage.removeItem('lastProjectClicked'); } } }, [isFetching, isFetchingNextPage]); + // Handle errors + useEffect(() => { + if (isError && error) { + showToastError(error); + captureException(error, { + tags: { + section: 'fetchAllProjects', + }, + }); + } + }, [isError, error]); + + // Determine if no results should be shown + const isNotFound = + (mainCategories.length > 0 && !selectedMainCategory && !isArchivedQF) || + (!isQF && data?.pages?.[0]?.data.length === 0); + if (isNotFound) return ; - // Handle fetching errors from React Query - if (isError) { - showToastError(error); - captureException(error, { - tags: { - section: 'fetchAllProjects', - }, - }); - } + const totalCount = data?.pages[data.pages.length - 1].totalCount || 0; + console.log('data', totalCount, data); return ( <> @@ -294,11 +245,11 @@ const ProjectsIndex = (props: IProjectsView) => { )} {onProjectsPageOrActiveQFPage && ( - + )} {isFetchingNextPage && } - {projectsData.projects.length > 0 ? ( + {data?.pages.some(page => page.data.length > 0) ? ( {isQF ? ( @@ -306,7 +257,7 @@ const ProjectsIndex = (props: IProjectsView) => { ) : ( )} - {data?.pages.map((page, pageIndex) => ( + {data.pages.map((page, pageIndex) => ( {page.data.map((project, idx) => (
{ } > @@ -333,39 +283,35 @@ const ProjectsIndex = (props: IProjectsView) => { ) : ( )} - {projectsData.totalCount > projectsData.projects.length && ( -
+ {hasNextPage &&
} + {!isFetching && !isFetchingNextPage && hasNextPage && ( + <> + fetchNextPage()} + label={ + isFetchingNextPage + ? '' + : formatMessage({ + id: 'component.button.load_more', + }) + } + icon={ + isFetchingNextPage && ( + +
+ + ) + } + /> + + )} - {!isFetching && - !isFetchingNextPage && - projectsData.totalPages > (data?.pages?.length || 0) && ( - <> - -
- - ) - } - /> - - - )} ); From d83daf9637fd63e699aba7c69a43f18ef9233dd1 Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 7 Oct 2024 19:51:23 +0330 Subject: [PATCH 28/97] add LAST_PROJECT_CLICKED --- src/components/views/projects/ProjectsIndex.tsx | 7 ++++--- src/components/views/projects/constants.ts | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 src/components/views/projects/constants.ts diff --git a/src/components/views/projects/ProjectsIndex.tsx b/src/components/views/projects/ProjectsIndex.tsx index 21b2a291ef..e7490e312f 100644 --- a/src/components/views/projects/ProjectsIndex.tsx +++ b/src/components/views/projects/ProjectsIndex.tsx @@ -39,6 +39,7 @@ import { DefaultQFBanner } from '@/components/DefaultQFBanner'; import NotAvailable from '@/components/NotAvailable'; import { fetchProjects, IQueries } from './services'; import { IProject } from '@/apollo/types/types'; +import { LAST_PROJECT_CLICKED } from './constants'; export interface IProjectsView { projects: IProject[]; @@ -167,14 +168,14 @@ const ProjectsIndex = (props: IProjectsView) => { // Save last clicked project const handleProjectClick = (slug: string) => { - localStorage.setItem('lastProjectClicked', slug); + sessionStorage.setItem(LAST_PROJECT_CLICKED, slug); }; // Scroll to last clicked project useEffect(() => { if (!isFetching && !isFetchingNextPage) { const lastProjectClicked = - localStorage.getItem('lastProjectClicked'); + sessionStorage.getItem(LAST_PROJECT_CLICKED); if (lastProjectClicked) { const element = document.getElementById(lastProjectClicked); if (element) { @@ -183,7 +184,7 @@ const ProjectsIndex = (props: IProjectsView) => { behavior: 'smooth', }); } - localStorage.removeItem('lastProjectClicked'); + sessionStorage.removeItem(LAST_PROJECT_CLICKED); } } }, [isFetching, isFetchingNextPage]); diff --git a/src/components/views/projects/constants.ts b/src/components/views/projects/constants.ts new file mode 100644 index 0000000000..9248de5056 --- /dev/null +++ b/src/components/views/projects/constants.ts @@ -0,0 +1 @@ +export const LAST_PROJECT_CLICKED = 'lastProjectClicked'; From 428a9a3f9500bf72e14abf5fcc499b69a2edd4cc Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 8 Oct 2024 14:21:04 +0200 Subject: [PATCH 29/97] Fix/usdt donation on mainnet --- src/artifacts/usdtMainnetABI.json | 731 ++++++++++++++++++++++++++++++ src/lib/helpers.ts | 421 ++++++++++++++++- 2 files changed, 1149 insertions(+), 3 deletions(-) create mode 100644 src/artifacts/usdtMainnetABI.json diff --git a/src/artifacts/usdtMainnetABI.json b/src/artifacts/usdtMainnetABI.json new file mode 100644 index 0000000000..1fdc3bcce6 --- /dev/null +++ b/src/artifacts/usdtMainnetABI.json @@ -0,0 +1,731 @@ +[ + { + "constant":true, + "inputs":[ + + ], + "name":"name", + "outputs":[ + { + "name":"", + "type":"string" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"_upgradedAddress", + "type":"address" + } + ], + "name":"deprecate", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"_spender", + "type":"address" + }, + { + "name":"_value", + "type":"uint256" + } + ], + "name":"approve", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"deprecated", + "outputs":[ + { + "name":"", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"_evilUser", + "type":"address" + } + ], + "name":"addBlackList", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"totalSupply", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"_from", + "type":"address" + }, + { + "name":"_to", + "type":"address" + }, + { + "name":"_value", + "type":"uint256" + } + ], + "name":"transferFrom", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"upgradedAddress", + "outputs":[ + { + "name":"", + "type":"address" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "name":"", + "type":"address" + } + ], + "name":"balances", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"decimals", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"maximumFee", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"_totalSupply", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + + ], + "name":"unpause", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "name":"_maker", + "type":"address" + } + ], + "name":"getBlackListStatus", + "outputs":[ + { + "name":"", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "name":"", + "type":"address" + }, + { + "name":"", + "type":"address" + } + ], + "name":"allowed", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"paused", + "outputs":[ + { + "name":"", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "name":"who", + "type":"address" + } + ], + "name":"balanceOf", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + + ], + "name":"pause", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"getOwner", + "outputs":[ + { + "name":"", + "type":"address" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"owner", + "outputs":[ + { + "name":"", + "type":"address" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"symbol", + "outputs":[ + { + "name":"", + "type":"string" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"_to", + "type":"address" + }, + { + "name":"_value", + "type":"uint256" + } + ], + "name":"transfer", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"newBasisPoints", + "type":"uint256" + }, + { + "name":"newMaxFee", + "type":"uint256" + } + ], + "name":"setParams", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"amount", + "type":"uint256" + } + ], + "name":"issue", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"amount", + "type":"uint256" + } + ], + "name":"redeem", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "name":"_owner", + "type":"address" + }, + { + "name":"_spender", + "type":"address" + } + ], + "name":"allowance", + "outputs":[ + { + "name":"remaining", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"basisPointsRate", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "name":"", + "type":"address" + } + ], + "name":"isBlackListed", + "outputs":[ + { + "name":"", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"_clearedUser", + "type":"address" + } + ], + "name":"removeBlackList", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"MAX_UINT", + "outputs":[ + { + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"newOwner", + "type":"address" + } + ], + "name":"transferOwnership", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "name":"_blackListedUser", + "type":"address" + } + ], + "name":"destroyBlackFunds", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "inputs":[ + { + "name":"_initialSupply", + "type":"uint256" + }, + { + "name":"_name", + "type":"string" + }, + { + "name":"_symbol", + "type":"string" + }, + { + "name":"_decimals", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"constructor" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "name":"amount", + "type":"uint256" + } + ], + "name":"Issue", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "name":"amount", + "type":"uint256" + } + ], + "name":"Redeem", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "name":"newAddress", + "type":"address" + } + ], + "name":"Deprecate", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "name":"feeBasisPoints", + "type":"uint256" + }, + { + "indexed":false, + "name":"maxFee", + "type":"uint256" + } + ], + "name":"Params", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "name":"_blackListedUser", + "type":"address" + }, + { + "indexed":false, + "name":"_balance", + "type":"uint256" + } + ], + "name":"DestroyedBlackFunds", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "name":"_user", + "type":"address" + } + ], + "name":"AddedBlackList", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "name":"_user", + "type":"address" + } + ], + "name":"RemovedBlackList", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "name":"owner", + "type":"address" + }, + { + "indexed":true, + "name":"spender", + "type":"address" + }, + { + "indexed":false, + "name":"value", + "type":"uint256" + } + ], + "name":"Approval", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "name":"from", + "type":"address" + }, + { + "indexed":true, + "name":"to", + "type":"address" + }, + { + "indexed":false, + "name":"value", + "type":"uint256" + } + ], + "name":"Transfer", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + + ], + "name":"Pause", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + + ], + "name":"Unpause", + "type":"event" + } +] diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts index 5e2aa5922a..cda01084b3 100644 --- a/src/lib/helpers.ts +++ b/src/lib/helpers.ts @@ -19,6 +19,7 @@ import config, { isProduction } from '@/configuration'; import { AddressZero } from './constants/constants'; import { ChainType, NonEVMChain } from '@/types/config'; import { wagmiConfig } from '@/wagmiConfigs'; +import usdtMainnetABI from '@/artifacts/usdtMainnetABI.json'; declare let window: any; interface TransactionParams { @@ -375,15 +376,28 @@ async function handleErc20Transfer( params: TransactionParams, contractAddress: Address, ): Promise
{ + // 'viem' ABI contract for USDT donation fails on mainnet + // so we use the USDT mainnet ABI instead and put inside usdtMainnetABI.json file + // update for 'viem' package to fix this issue doesn't work + const ABItoUse = + contractAddress === '0xdac17f958d2ee523a2206206994597c13d831ec7' + ? usdtMainnetABI + : erc20Abi; + const baseProps = { address: contractAddress, - abi: erc20Abi, + abi: ABItoUse, }; - const decimals = await readContract(wagmiConfig, { + let decimals = await readContract(wagmiConfig, { ...baseProps, functionName: 'decimals', }); - const value = parseUnits(params.value, decimals); + + if (typeof decimals === 'bigint') { + decimals = Number(decimals.toString()); + } + + const value = parseUnits(params.value, decimals as number); const hash = await writeContract(wagmiConfig, { ...baseProps, functionName: 'transfer', @@ -687,3 +701,404 @@ export function generateRandomNonce(): number { return nonce; } + +export const contractAbiTEST = [ + { + constant: true, + inputs: [], + name: 'name', + outputs: [{ name: '', type: 'string' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [{ name: '_upgradedAddress', type: 'address' }], + name: 'deprecate', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: false, + inputs: [ + { name: '_spender', type: 'address' }, + { name: '_value', type: 'uint256' }, + ], + name: 'approve', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'deprecated', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [{ name: '_evilUser', type: 'address' }], + name: 'addBlackList', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'totalSupply', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { name: '_from', type: 'address' }, + { name: '_to', type: 'address' }, + { name: '_value', type: 'uint256' }, + ], + name: 'transferFrom', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'upgradedAddress', + outputs: [{ name: '', type: 'address' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [{ name: '', type: 'address' }], + name: 'balances', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'decimals', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'maximumFee', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: '_totalSupply', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [], + name: 'unpause', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [{ name: '_maker', type: 'address' }], + name: 'getBlackListStatus', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [ + { name: '', type: 'address' }, + { name: '', type: 'address' }, + ], + name: 'allowed', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'paused', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [{ name: 'who', type: 'address' }], + name: 'balanceOf', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [], + name: 'pause', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'getOwner', + outputs: [{ name: '', type: 'address' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'owner', + outputs: [{ name: '', type: 'address' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'symbol', + outputs: [{ name: '', type: 'string' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [ + { name: '_to', type: 'address' }, + { name: '_value', type: 'uint256' }, + ], + name: 'transfer', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: false, + inputs: [ + { name: 'newBasisPoints', type: 'uint256' }, + { name: 'newMaxFee', type: 'uint256' }, + ], + name: 'setParams', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: false, + inputs: [{ name: 'amount', type: 'uint256' }], + name: 'issue', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: false, + inputs: [{ name: 'amount', type: 'uint256' }], + name: 'redeem', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [ + { name: '_owner', type: 'address' }, + { name: '_spender', type: 'address' }, + ], + name: 'allowance', + outputs: [{ name: 'remaining', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'basisPointsRate', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [{ name: '', type: 'address' }], + name: 'isBlackListed', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [{ name: '_clearedUser', type: 'address' }], + name: 'removeBlackList', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'MAX_UINT', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: false, + inputs: [{ name: 'newOwner', type: 'address' }], + name: 'transferOwnership', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + constant: false, + inputs: [{ name: '_blackListedUser', type: 'address' }], + name: 'destroyBlackFunds', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { name: '_initialSupply', type: 'uint256' }, + { name: '_name', type: 'string' }, + { name: '_symbol', type: 'string' }, + { name: '_decimals', type: 'uint256' }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [{ indexed: false, name: 'amount', type: 'uint256' }], + name: 'Issue', + type: 'event', + }, + { + anonymous: false, + inputs: [{ indexed: false, name: 'amount', type: 'uint256' }], + name: 'Redeem', + type: 'event', + }, + { + anonymous: false, + inputs: [{ indexed: false, name: 'newAddress', type: 'address' }], + name: 'Deprecate', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: false, name: 'feeBasisPoints', type: 'uint256' }, + { indexed: false, name: 'maxFee', type: 'uint256' }, + ], + name: 'Params', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: false, name: '_blackListedUser', type: 'address' }, + { indexed: false, name: '_balance', type: 'uint256' }, + ], + name: 'DestroyedBlackFunds', + type: 'event', + }, + { + anonymous: false, + inputs: [{ indexed: false, name: '_user', type: 'address' }], + name: 'AddedBlackList', + type: 'event', + }, + { + anonymous: false, + inputs: [{ indexed: false, name: '_user', type: 'address' }], + name: 'RemovedBlackList', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, name: 'owner', type: 'address' }, + { indexed: true, name: 'spender', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, name: 'from', type: 'address' }, + { indexed: true, name: 'to', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + name: 'Transfer', + type: 'event', + }, + { + anonymous: false, + inputs: [], + name: 'Pause', + type: 'event', + }, + { + anonymous: false, + inputs: [], + name: 'Unpause', + type: 'event', + }, +] as const; From f565e1da971aea27e137aacebfe51ff81775d7f6 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 8 Oct 2024 15:06:52 +0200 Subject: [PATCH 30/97] rebuild --- src/lib/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts index cda01084b3..59fe04094f 100644 --- a/src/lib/helpers.ts +++ b/src/lib/helpers.ts @@ -376,7 +376,7 @@ async function handleErc20Transfer( params: TransactionParams, contractAddress: Address, ): Promise
{ - // 'viem' ABI contract for USDT donation fails on mainnet + // 'viem' ABI contract for USDT donation fails on mainnet // so we use the USDT mainnet ABI instead and put inside usdtMainnetABI.json file // update for 'viem' package to fix this issue doesn't work const ABItoUse = From d5192faf940e95896f36a3a89f21e134dc9e55ca Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 8 Oct 2024 15:07:02 +0200 Subject: [PATCH 31/97] rebuild --- src/lib/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts index 59fe04094f..cda01084b3 100644 --- a/src/lib/helpers.ts +++ b/src/lib/helpers.ts @@ -376,7 +376,7 @@ async function handleErc20Transfer( params: TransactionParams, contractAddress: Address, ): Promise
{ - // 'viem' ABI contract for USDT donation fails on mainnet + // 'viem' ABI contract for USDT donation fails on mainnet // so we use the USDT mainnet ABI instead and put inside usdtMainnetABI.json file // update for 'viem' package to fix this issue doesn't work const ABItoUse = From 078993e3b5c11bf37a1a129f02f8389086c3a8dd Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 8 Oct 2024 15:09:11 +0200 Subject: [PATCH 32/97] removed temp variable --- src/lib/helpers.ts | 401 --------------------------------------------- 1 file changed, 401 deletions(-) diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts index cda01084b3..93a7b53083 100644 --- a/src/lib/helpers.ts +++ b/src/lib/helpers.ts @@ -701,404 +701,3 @@ export function generateRandomNonce(): number { return nonce; } - -export const contractAbiTEST = [ - { - constant: true, - inputs: [], - name: 'name', - outputs: [{ name: '', type: 'string' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [{ name: '_upgradedAddress', type: 'address' }], - name: 'deprecate', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: false, - inputs: [ - { name: '_spender', type: 'address' }, - { name: '_value', type: 'uint256' }, - ], - name: 'approve', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'deprecated', - outputs: [{ name: '', type: 'bool' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [{ name: '_evilUser', type: 'address' }], - name: 'addBlackList', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'totalSupply', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [ - { name: '_from', type: 'address' }, - { name: '_to', type: 'address' }, - { name: '_value', type: 'uint256' }, - ], - name: 'transferFrom', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'upgradedAddress', - outputs: [{ name: '', type: 'address' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [{ name: '', type: 'address' }], - name: 'balances', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'decimals', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'maximumFee', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: '_totalSupply', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [], - name: 'unpause', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [{ name: '_maker', type: 'address' }], - name: 'getBlackListStatus', - outputs: [{ name: '', type: 'bool' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [ - { name: '', type: 'address' }, - { name: '', type: 'address' }, - ], - name: 'allowed', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'paused', - outputs: [{ name: '', type: 'bool' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [{ name: 'who', type: 'address' }], - name: 'balanceOf', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [], - name: 'pause', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'getOwner', - outputs: [{ name: '', type: 'address' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'owner', - outputs: [{ name: '', type: 'address' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'symbol', - outputs: [{ name: '', type: 'string' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [ - { name: '_to', type: 'address' }, - { name: '_value', type: 'uint256' }, - ], - name: 'transfer', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: false, - inputs: [ - { name: 'newBasisPoints', type: 'uint256' }, - { name: 'newMaxFee', type: 'uint256' }, - ], - name: 'setParams', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: false, - inputs: [{ name: 'amount', type: 'uint256' }], - name: 'issue', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: false, - inputs: [{ name: 'amount', type: 'uint256' }], - name: 'redeem', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [ - { name: '_owner', type: 'address' }, - { name: '_spender', type: 'address' }, - ], - name: 'allowance', - outputs: [{ name: 'remaining', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'basisPointsRate', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: true, - inputs: [{ name: '', type: 'address' }], - name: 'isBlackListed', - outputs: [{ name: '', type: 'bool' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [{ name: '_clearedUser', type: 'address' }], - name: 'removeBlackList', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: true, - inputs: [], - name: 'MAX_UINT', - outputs: [{ name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function', - }, - { - constant: false, - inputs: [{ name: 'newOwner', type: 'address' }], - name: 'transferOwnership', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - constant: false, - inputs: [{ name: '_blackListedUser', type: 'address' }], - name: 'destroyBlackFunds', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - { - inputs: [ - { name: '_initialSupply', type: 'uint256' }, - { name: '_name', type: 'string' }, - { name: '_symbol', type: 'string' }, - { name: '_decimals', type: 'uint256' }, - ], - payable: false, - stateMutability: 'nonpayable', - type: 'constructor', - }, - { - anonymous: false, - inputs: [{ indexed: false, name: 'amount', type: 'uint256' }], - name: 'Issue', - type: 'event', - }, - { - anonymous: false, - inputs: [{ indexed: false, name: 'amount', type: 'uint256' }], - name: 'Redeem', - type: 'event', - }, - { - anonymous: false, - inputs: [{ indexed: false, name: 'newAddress', type: 'address' }], - name: 'Deprecate', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { indexed: false, name: 'feeBasisPoints', type: 'uint256' }, - { indexed: false, name: 'maxFee', type: 'uint256' }, - ], - name: 'Params', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { indexed: false, name: '_blackListedUser', type: 'address' }, - { indexed: false, name: '_balance', type: 'uint256' }, - ], - name: 'DestroyedBlackFunds', - type: 'event', - }, - { - anonymous: false, - inputs: [{ indexed: false, name: '_user', type: 'address' }], - name: 'AddedBlackList', - type: 'event', - }, - { - anonymous: false, - inputs: [{ indexed: false, name: '_user', type: 'address' }], - name: 'RemovedBlackList', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { indexed: true, name: 'owner', type: 'address' }, - { indexed: true, name: 'spender', type: 'address' }, - { indexed: false, name: 'value', type: 'uint256' }, - ], - name: 'Approval', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { indexed: true, name: 'from', type: 'address' }, - { indexed: true, name: 'to', type: 'address' }, - { indexed: false, name: 'value', type: 'uint256' }, - ], - name: 'Transfer', - type: 'event', - }, - { - anonymous: false, - inputs: [], - name: 'Pause', - type: 'event', - }, - { - anonymous: false, - inputs: [], - name: 'Unpause', - type: 'event', - }, -] as const; From de08251390c43012729a7b22a5fe50b48ea94010 Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 8 Oct 2024 09:41:06 -0500 Subject: [PATCH 33/97] updating viem and wagmi --- package.json | 4 +- yarn.lock | 209 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 191 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index d155dec6e3..74ad14e007 100644 --- a/package.json +++ b/package.json @@ -72,8 +72,8 @@ "styled-components": "^6.1.12", "swiper": "^11.1.3", "unsplash-js": "^7.0.19", - "viem": "^2.21.16", - "wagmi": "^2.12.16" + "viem": "^2.21.19", + "wagmi": "^2.12.17" }, "devDependencies": { "@babel/preset-typescript": "^7.23.3", diff --git a/yarn.lock b/yarn.lock index 1aebaaa546..be8aa535bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17,6 +17,11 @@ resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== +"@adraffy/ens-normalize@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33" + integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== + "@ampproject/remapping@^2.2.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" @@ -2301,7 +2306,7 @@ dependencies: "@noble/hashes" "1.4.0" -"@noble/curves@^1.1.0", "@noble/curves@^1.4.0", "@noble/curves@^1.4.2": +"@noble/curves@1.6.0", "@noble/curves@^1.1.0", "@noble/curves@^1.4.0", "@noble/curves@^1.4.2", "@noble/curves@~1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.6.0.tgz#be5296ebcd5a1730fccea4786d420f87abfeb40b" integrity sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ== @@ -2854,7 +2859,7 @@ resolved "https://registry.yarnpkg.com/@safe-global/safe-modules-deployments/-/safe-modules-deployments-2.2.1.tgz#a8b88f2afc6ec04fed09968fe1e4990ed975c86e" integrity sha512-H0XpusyXVcsTuRsQSq0FoBKqRfhZH87/1DrFEmXXPXmI3fJkvxq3KpTaTTqzcqoIe/J+erwVZQUYNfL68EcvAQ== -"@scure/base@^1.1.3", "@scure/base@~1.1.0", "@scure/base@~1.1.6", "@scure/base@~1.1.8": +"@scure/base@^1.1.3", "@scure/base@~1.1.0", "@scure/base@~1.1.6", "@scure/base@~1.1.7", "@scure/base@~1.1.8": version "1.1.9" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== @@ -2877,6 +2882,15 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" +"@scure/bip32@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.5.0.tgz#dd4a2e1b8a9da60e012e776d954c4186db6328e6" + integrity sha512-8EnFYkqEQdnkuGBVpCzKxyIwDCBLDVj3oiX0EKUFre/tOjL/Hqba1D6n/8RcmaQy4f95qQFrO2A8Sr6ybh4NRw== + dependencies: + "@noble/curves" "~1.6.0" + "@noble/hashes" "~1.5.0" + "@scure/base" "~1.1.7" + "@scure/bip39@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" @@ -5229,17 +5243,17 @@ resolved "https://registry.yarnpkg.com/@vercel/speed-insights/-/speed-insights-1.0.12.tgz#71c2edffdedae98d34e306d7b0a573e6816898b4" integrity sha512-ZGQ+a7bcfWJD2VYEp2R1LHvRAMyyaFBYytZXsfnbOMkeOvzGNVxUL7aVUvisIrTZjXTSsxG45DKX7yiw6nq2Jw== -"@wagmi/connectors@5.1.14": - version "5.1.14" - resolved "https://registry.yarnpkg.com/@wagmi/connectors/-/connectors-5.1.14.tgz#392f187298ea0a68c5443ffe93d714187c01c83b" - integrity sha512-3faf6gXFI1nX/kud5e2s+8fMjnuWp14XwqHVNCOfl7nVXQlEFAvjQxI1GrGyHckfHm7e6oXdm2eJwJGgPWi0QQ== +"@wagmi/connectors@5.1.15": + version "5.1.15" + resolved "https://registry.yarnpkg.com/@wagmi/connectors/-/connectors-5.1.15.tgz#39ff0fe0f1729ce9d0dce24806d40a82fe5a48fb" + integrity sha512-Bz5EBpn8hAYFuxCWoXviwABk2VlLRuQTpJ7Yd/hu4HuuXnTdCN27cfvT+Fy2sWbwpLnr1e29LJGAUCIyYkHz7g== dependencies: "@coinbase/wallet-sdk" "4.0.4" "@metamask/sdk" "0.28.4" "@safe-global/safe-apps-provider" "0.18.3" "@safe-global/safe-apps-sdk" "9.1.0" - "@walletconnect/ethereum-provider" "2.16.1" - "@walletconnect/modal" "2.6.2" + "@walletconnect/ethereum-provider" "2.17.0" + "@walletconnect/modal" "2.7.0" cbw-sdk "npm:@coinbase/wallet-sdk@3.9.3" "@wagmi/core@2.13.8": @@ -5342,6 +5356,28 @@ lodash.isequal "4.5.0" uint8arrays "3.1.0" +"@walletconnect/core@2.17.0": + version "2.17.0" + resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.17.0.tgz#bf490e85a4702eff0f7cf81ba0d3c1016dffff33" + integrity sha512-On+uSaCfWdsMIQsECwWHZBmUXfrnqmv6B8SXRRuTJgd8tUpEvBkLQH4X7XkSm3zW6ozEkQTCagZ2ox2YPn3kbw== + dependencies: + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/jsonrpc-ws-connection" "1.0.14" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + "@walletconnect/relay-api" "1.0.11" + "@walletconnect/relay-auth" "1.0.4" + "@walletconnect/safe-json" "1.0.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.17.0" + "@walletconnect/utils" "2.17.0" + events "3.3.0" + lodash.isequal "4.5.0" + uint8arrays "3.1.0" + "@walletconnect/environment@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@walletconnect/environment/-/environment-1.0.1.tgz#1d7f82f0009ab821a2ba5ad5e5a7b8ae3b214cd7" @@ -5365,6 +5401,22 @@ "@walletconnect/utils" "2.16.1" events "3.3.0" +"@walletconnect/ethereum-provider@2.17.0": + version "2.17.0" + resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.17.0.tgz#d74feaaed6180a6799e96760d7ee867ff3a083d2" + integrity sha512-b+KTAXOb6JjoxkwpgYQQKPUcTwENGmdEdZoIDLeRicUmZTn/IQKfkMoC2frClB4YxkyoVMtj1oMV2JAax+yu9A== + dependencies: + "@walletconnect/jsonrpc-http-connection" "1.0.8" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/modal" "2.7.0" + "@walletconnect/sign-client" "2.17.0" + "@walletconnect/types" "2.17.0" + "@walletconnect/universal-provider" "2.17.0" + "@walletconnect/utils" "2.17.0" + events "3.3.0" + "@walletconnect/events@1.0.1", "@walletconnect/events@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@walletconnect/events/-/events-1.0.1.tgz#2b5f9c7202019e229d7ccae1369a9e86bda7816c" @@ -5457,6 +5509,13 @@ dependencies: valtio "1.11.2" +"@walletconnect/modal-core@2.7.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@walletconnect/modal-core/-/modal-core-2.7.0.tgz#73c13c3b7b0abf9ccdbac9b242254a86327ce0a4" + integrity sha512-oyMIfdlNdpyKF2kTJowTixZSo0PGlCJRdssUN/EZdA6H6v03hZnf09JnwpljZNfir2M65Dvjm/15nGrDQnlxSA== + dependencies: + valtio "1.11.2" + "@walletconnect/modal-ui@2.6.2": version "2.6.2" resolved "https://registry.yarnpkg.com/@walletconnect/modal-ui/-/modal-ui-2.6.2.tgz#fa57c087c57b7f76aaae93deab0f84bb68b59cf9" @@ -5467,6 +5526,16 @@ motion "10.16.2" qrcode "1.5.3" +"@walletconnect/modal-ui@2.7.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@walletconnect/modal-ui/-/modal-ui-2.7.0.tgz#dbbb7ee46a5a25f7d39db622706f2d197b268cbb" + integrity sha512-gERYvU7D7K1ANCN/8vUgsE0d2hnRemfAFZ2novm9aZBg7TEd/4EgB+AqbJ+1dc7GhOL6dazckVq78TgccHb7mQ== + dependencies: + "@walletconnect/modal-core" "2.7.0" + lit "2.8.0" + motion "10.16.2" + qrcode "1.5.3" + "@walletconnect/modal@2.6.2": version "2.6.2" resolved "https://registry.yarnpkg.com/@walletconnect/modal/-/modal-2.6.2.tgz#4b534a836f5039eeb3268b80be7217a94dd12651" @@ -5475,6 +5544,14 @@ "@walletconnect/modal-core" "2.6.2" "@walletconnect/modal-ui" "2.6.2" +"@walletconnect/modal@2.7.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@walletconnect/modal/-/modal-2.7.0.tgz#55f969796d104cce1205f5f844d8f8438b79723a" + integrity sha512-RQVt58oJ+rwqnPcIvRFeMGKuXb9qkgSmwz4noF8JZGUym3gUAzVs+uW2NQ1Owm9XOJAV+sANrtJ+VoVq1ftElw== + dependencies: + "@walletconnect/modal-core" "2.7.0" + "@walletconnect/modal-ui" "2.7.0" + "@walletconnect/qrcode-modal@^1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@walletconnect/qrcode-modal/-/qrcode-modal-1.8.0.tgz#ddd6f5c9b7ee52c16adf9aacec2a3eac4994caea" @@ -5533,6 +5610,21 @@ "@walletconnect/utils" "2.16.1" events "3.3.0" +"@walletconnect/sign-client@2.17.0": + version "2.17.0" + resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.17.0.tgz#efe811b1bb10082d964e2f0378aaa1b40f424503" + integrity sha512-sErYwvSSHQolNXni47L3Bm10ptJc1s1YoJvJd34s5E9h9+d3rj7PrhbiW9X82deN+Dm5oA8X9tC4xty1yIBrVg== + dependencies: + "@walletconnect/core" "2.17.0" + "@walletconnect/events" "1.0.1" + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/logger" "2.1.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.17.0" + "@walletconnect/utils" "2.17.0" + events "3.3.0" + "@walletconnect/sign-client@^2.7.2": version "2.16.3" resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.16.3.tgz#abf686e54408802a626a105b096aa9cc3c8a2d14" @@ -5579,6 +5671,18 @@ "@walletconnect/logger" "2.1.2" events "3.3.0" +"@walletconnect/types@2.17.0": + version "2.17.0" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.17.0.tgz#20eda5791e3172f8ab9146caa3f317701d4b3232" + integrity sha512-i1pn9URpvt9bcjRDkabuAmpA9K7mzyKoLJlbsAujRVX7pfaG7wur7u9Jz0bk1HxvuABL5LHNncTnVKSXKQ5jZA== + dependencies: + "@walletconnect/events" "1.0.1" + "@walletconnect/heartbeat" "1.2.2" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/keyvaluestorage" "1.1.1" + "@walletconnect/logger" "2.1.2" + events "3.3.0" + "@walletconnect/types@^1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-1.8.0.tgz#3f5e85b2d6b149337f727ab8a71b8471d8d9a195" @@ -5599,6 +5703,21 @@ "@walletconnect/utils" "2.16.1" events "3.3.0" +"@walletconnect/universal-provider@2.17.0": + version "2.17.0" + resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.17.0.tgz#c9d4bbd9b8f0e41b500b2488ccbc207dc5f7a170" + integrity sha512-d3V5Be7AqLrvzcdMZSBS8DmGDRdqnyLk1DWmRKAGgR6ieUWykhhUKlvfeoZtvJrIXrY7rUGYpH1X41UtFkW5Pw== + dependencies: + "@walletconnect/jsonrpc-http-connection" "1.0.8" + "@walletconnect/jsonrpc-provider" "1.0.14" + "@walletconnect/jsonrpc-types" "1.0.4" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/logger" "2.1.2" + "@walletconnect/sign-client" "2.17.0" + "@walletconnect/types" "2.17.0" + "@walletconnect/utils" "2.17.0" + events "3.3.0" + "@walletconnect/utils@2.16.1": version "2.16.1" resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.16.1.tgz#2099cc2bd16b0edc32022f64aa2c2c323b45d1d4" @@ -5643,6 +5762,28 @@ query-string "7.1.3" uint8arrays "3.1.0" +"@walletconnect/utils@2.17.0": + version "2.17.0" + resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.17.0.tgz#02b3af0b80d0c1a994d692d829d066271b04d071" + integrity sha512-1aeQvjwsXy4Yh9G6g2eGmXrEl+BzkNjHRdCrGdMYqFTFa8ROEJfTGsSH3pLsNDlOY94CoBUvJvM55q/PMoN/FQ== + dependencies: + "@stablelib/chacha20poly1305" "1.0.1" + "@stablelib/hkdf" "1.0.1" + "@stablelib/random" "1.0.2" + "@stablelib/sha256" "1.0.1" + "@stablelib/x25519" "1.0.3" + "@walletconnect/relay-api" "1.0.11" + "@walletconnect/relay-auth" "1.0.4" + "@walletconnect/safe-json" "1.0.2" + "@walletconnect/time" "1.0.2" + "@walletconnect/types" "2.17.0" + "@walletconnect/window-getters" "1.0.1" + "@walletconnect/window-metadata" "1.0.1" + detect-browser "5.3.0" + elliptic "^6.5.7" + query-string "7.1.3" + uint8arrays "3.1.0" + "@walletconnect/window-getters@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@walletconnect/window-getters/-/window-getters-1.0.0.tgz#1053224f77e725dfd611c83931b5f6c98c32bfc8" @@ -5844,7 +5985,7 @@ abitype@1.0.5: resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.5.tgz#29d0daa3eea867ca90f7e4123144c1d1270774b6" integrity sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw== -abitype@^1.0.2: +abitype@1.0.6, abitype@^1.0.2: version "1.0.6" resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.6.tgz#76410903e1d88e34f1362746e2d407513c38565b" integrity sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A== @@ -11105,6 +11246,11 @@ isows@1.0.4: resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.4.tgz#810cd0d90cc4995c26395d2aa4cfa4037ebdf061" integrity sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ== +isows@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7" + integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -16457,7 +16603,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -viem@^2.1.1, viem@^2.21.16: +viem@^2.1.1: version "2.21.16" resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.16.tgz#cf32200bbd1696bd6e86de7e1c12fcdfd77b63c1" integrity sha512-SvhaPzTj3a+zR/5OmtJ0acjA6oGDrgPg4vtO8KboXtvbjksXEkz+oFaNjZDgxpkqbps2SLi8oPCjdpRm6WgDmw== @@ -16472,6 +16618,21 @@ viem@^2.1.1, viem@^2.21.16: webauthn-p256 "0.0.5" ws "8.17.1" +viem@^2.21.19: + version "2.21.19" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.19.tgz#5e1a7efc45903d83306416ffa2e3a11ed23cd924" + integrity sha512-FdlkN+UI1IU5sYOmzvygkxsUNjDRD5YHht3gZFu2X9xFv6Z3h9pXq9ycrYQ3F17lNfb41O2Ot4/aqbUkwOv9dA== + dependencies: + "@adraffy/ens-normalize" "1.11.0" + "@noble/curves" "1.6.0" + "@noble/hashes" "1.5.0" + "@scure/bip32" "1.5.0" + "@scure/bip39" "1.4.0" + abitype "1.0.6" + isows "1.0.6" + webauthn-p256 "0.0.10" + ws "8.18.0" + vm-browserify@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -16484,12 +16645,12 @@ w3c-xmlserializer@^4.0.0: dependencies: xml-name-validator "^4.0.0" -wagmi@^2.12.16: - version "2.12.16" - resolved "https://registry.yarnpkg.com/wagmi/-/wagmi-2.12.16.tgz#4ce84b67957ba24c6d440603c747a3e97114e23f" - integrity sha512-FBw8sEuo1uJkmGyl/DDxy9LSR/3t76CKFF8SxLi6nuPIaDr1hH5KOsBbpa0a8or7cY/tSLAhpTA/YBpDfDwL0Q== +wagmi@^2.12.17: + version "2.12.17" + resolved "https://registry.yarnpkg.com/wagmi/-/wagmi-2.12.17.tgz#b3fad0dc38a67a35a2506db3dc8df90164f1ee87" + integrity sha512-WkofyvOX6XGOXrs8W0NvnzbLGIb9Di8ECqpMDW32nqwTKRxfolfN4GI/AlAMs9fjx4h3k8LGTfqa6UGLb063yg== dependencies: - "@wagmi/connectors" "5.1.14" + "@wagmi/connectors" "5.1.15" "@wagmi/core" "2.13.8" use-sync-external-store "1.2.0" @@ -16755,6 +16916,14 @@ web3@1.10.0: web3-shh "1.10.0" web3-utils "1.10.0" +webauthn-p256@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.10.tgz#877e75abe8348d3e14485932968edf3325fd2fdd" + integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA== + dependencies: + "@noble/curves" "^1.4.0" + "@noble/hashes" "^1.4.0" + webauthn-p256@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.5.tgz#0baebd2ba8a414b21cc09c0d40f9dd0be96a06bd" @@ -17047,6 +17216,11 @@ ws@8.17.1, ws@~8.17.1: resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== +ws@8.18.0, ws@^8.11.0, ws@^8.18.0, ws@^8.5.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" @@ -17061,11 +17235,6 @@ ws@^7.2.0, ws@^7.3.1, ws@^7.4.6, ws@^7.5.1, ws@^7.5.10: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.11.0, ws@^8.18.0, ws@^8.5.0: - version "8.18.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== - xhr-request-promise@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" From b48c07afe64a0efcd08c07463fdedd120f4ef9ac Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 8 Oct 2024 10:02:02 -0500 Subject: [PATCH 34/97] Update SelectTokenModal.tsx --- .../donate/OneTime/SelectTokenModal/SelectTokenModal.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/views/donate/OneTime/SelectTokenModal/SelectTokenModal.tsx b/src/components/views/donate/OneTime/SelectTokenModal/SelectTokenModal.tsx index f5dbe4f97e..a776a03470 100644 --- a/src/components/views/donate/OneTime/SelectTokenModal/SelectTokenModal.tsx +++ b/src/components/views/donate/OneTime/SelectTokenModal/SelectTokenModal.tsx @@ -148,14 +148,13 @@ const SelectTokenInnerModal: FC = ({ } } else { setCustomToken(undefined); + const sQuery: string = searchQuery; const filtered = tokens.filter( token => token.symbol .toLowerCase() - .includes(searchQuery.toLowerCase()) || - token.name - .toLowerCase() - .includes(searchQuery.toLowerCase()), + .includes(sQuery.toLowerCase()) || + token.name.toLowerCase().includes(sQuery.toLowerCase()), ); setFilteredTokens(filtered); } From 919b27b2da5fa14690b5cf8bc6379f1bd5f12214 Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 8 Oct 2024 10:09:13 -0500 Subject: [PATCH 35/97] Update useCreateEvmDonation.tsx --- src/hooks/useCreateEvmDonation.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useCreateEvmDonation.tsx b/src/hooks/useCreateEvmDonation.tsx index aac52f512b..23a89f0d0d 100644 --- a/src/hooks/useCreateEvmDonation.tsx +++ b/src/hooks/useCreateEvmDonation.tsx @@ -18,7 +18,7 @@ import { client } from '@/apollo/apolloClient'; import { CREATE_DRAFT_DONATION } from '@/apollo/gql/gqlDonations'; export const useCreateEvmDonation = () => { - const [txHash, setTxHash] = useState<`0x${string}` | undefined>(); + const [txHash, setTxHash] = useState<`0x${string}` | Address | undefined>(); const [donationSaved, setDonationSaved] = useState(false); const [donationMinted, setDonationMinted] = useState(false); const [donationId, setDonationId] = useState(0); @@ -30,7 +30,7 @@ export const useCreateEvmDonation = () => { const isSafeEnv = useIsSafeEnvironment(); const { status } = useWaitForTransactionReceipt({ - hash: txHash, + hash: txHash as `0x${string}`, onReplaced(data) { console.log('Transaction Updated', data); setTxHash(data.transaction.hash); From 71c56a1f01c6bbb52fd489c817c6c31d10373203 Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 8 Oct 2024 10:21:24 -0500 Subject: [PATCH 36/97] Update usePassport.ts --- src/hooks/usePassport.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hooks/usePassport.ts b/src/hooks/usePassport.ts index 3044bc7f4c..4cfd425eee 100644 --- a/src/hooks/usePassport.ts +++ b/src/hooks/usePassport.ts @@ -235,7 +235,9 @@ export const usePassport = () => { }); try { - const userAddressScore = await scoreUserAddress(address); + const userAddressScore = await scoreUserAddress( + address as `0x${string}`, + ); await updateState(userAddressScore); dispatch(setUserMBDScore(userAddressScore?.activeQFMBDScore)); } catch (error) { From 24ca887cd33594710b78ca16d9959fffd759823d Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 8 Oct 2024 10:30:05 -0500 Subject: [PATCH 37/97] Update transaction.ts --- src/lib/transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/transaction.ts b/src/lib/transaction.ts index 54bda2b6fe..9cd8e1542c 100644 --- a/src/lib/transaction.ts +++ b/src/lib/transaction.ts @@ -57,7 +57,7 @@ export const retryFetchEVMTransaction = async ( ) => { for (let i = 0; i < retries; i++) { const transaction = await getTransaction(wagmiConfig, { - hash: txHash, + hash: txHash as `0x${string}`, }).catch(error => { console.error( 'Attempt', From df2545d76ad076c1f01404705f7dd4ea19203a5f Mon Sep 17 00:00:00 2001 From: Kilter Date: Tue, 8 Oct 2024 11:44:59 -0500 Subject: [PATCH 38/97] Update wagmiConfigs.ts --- src/wagmiConfigs.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/wagmiConfigs.ts b/src/wagmiConfigs.ts index 03c50ba47a..e33ec15218 100644 --- a/src/wagmiConfigs.ts +++ b/src/wagmiConfigs.ts @@ -2,6 +2,7 @@ import { cookieStorage, createConfig, createStorage } from 'wagmi'; import { walletConnect, coinbaseWallet, safe } from '@wagmi/connectors'; import { createClient, http } from 'viem'; +import { mainnet } from 'viem/chains'; import configuration from './configuration'; // Get projectId at https://cloud.walletconnect.com @@ -37,6 +38,16 @@ export const wagmiConfig = createConfig({ storage: cookieStorage, }), client({ chain }) { - return createClient({ chain, transport: http() }); + // TODO: we must manage this for all chains in a better way + // basically to stop using viem's public transport + // leaving this as a hotfix to keep it quick + const infuraKey = process.env.NEXT_PUBLIC_INFURA_API_KEY; + + const customHttp = + chain.id === mainnet.id && infuraKey + ? http(`https://mainnet.infura.io/v3/${infuraKey}`) + : http(); + + return createClient({ chain, transport: customHttp }); }, }); From 763480f03b23c3877d6f5cddee4d64d14c247e51 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 10 Oct 2024 15:55:07 +0200 Subject: [PATCH 39/97] Fix/slug of the project on email verification page --- .../views/verification/EmailVerificationIndex.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/views/verification/EmailVerificationIndex.tsx b/src/components/views/verification/EmailVerificationIndex.tsx index 3df9a7a3dd..d33d41e7ff 100644 --- a/src/components/views/verification/EmailVerificationIndex.tsx +++ b/src/components/views/verification/EmailVerificationIndex.tsx @@ -78,10 +78,17 @@ export default function EmailVerificationIndex() { } function Verified() { + const [querySlug, setQuerySlug] = useState(undefined); const router = useRouter(); const { slug } = router.query; const { formatMessage } = useIntl(); + useEffect(() => { + if (slug) { + setQuerySlug(slug as string); + } + }, [slug]); + return ( <> - + Date: Tue, 15 Oct 2024 21:14:26 +0200 Subject: [PATCH 40/97] Feat/Adding security headers --- next.config.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/next.config.js b/next.config.js index e3b9624895..d290b6c412 100644 --- a/next.config.js +++ b/next.config.js @@ -163,6 +163,14 @@ const moduleExports = withBundleAnalyzer({ key: 'Access-Control-Allow-Headers', value: 'X-Requested-With, content-type, Authorization', }, + { + key: 'X-Frame-Options', + value: 'SAMEORIGIN', + }, + { + key: 'Content-Security-Policy', + value: "frame-ancestors 'self'", + }, ], }, ]; From 99af06e3499041f5f3a7e00afc8f482624d07d72 Mon Sep 17 00:00:00 2001 From: Cherik Date: Wed, 16 Oct 2024 18:16:04 +0330 Subject: [PATCH 41/97] update next --- next-env.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03dc6..a4a7b3f5cf 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. From a0806de8c40ccf8221fa1c9d8132c1b49acffd66 Mon Sep 17 00:00:00 2001 From: Cherik Date: Wed, 16 Oct 2024 18:16:09 +0330 Subject: [PATCH 42/97] add getSocialMediaHandle --- .../views/project/ProjectSocialItem.tsx | 35 ++---- src/helpers/url.tsx | 108 ++++++++++++++++++ 2 files changed, 115 insertions(+), 28 deletions(-) diff --git a/src/components/views/project/ProjectSocialItem.tsx b/src/components/views/project/ProjectSocialItem.tsx index 685c147815..6de3de69f1 100644 --- a/src/components/views/project/ProjectSocialItem.tsx +++ b/src/components/views/project/ProjectSocialItem.tsx @@ -4,6 +4,7 @@ import { B, Flex, neutralColors } from '@giveth/ui-design-system'; import { IProjectSocialMedia } from '@/apollo/types/types'; import { Shadow } from '@/components/styled-components/Shadow'; import { socialMediasArray } from '../create/SocialMediaBox/SocialMedias'; +import { ensureHttps, getSocialMediaHandle } from '@/helpers/url'; interface IProjectSocialMediaItem { socialMedia: IProjectSocialMedia; @@ -22,32 +23,6 @@ const socialMediaColor: { [key: string]: string } = { github: '#1D1E1F', }; -const removeHttpsAndWwwFromUrl = (socialMediaUrl: string) => { - return socialMediaUrl.replace('https://', '').replace('www.', ''); -}; - -/** - * Ensures that a given URL uses the https:// protocol. - * If the URL starts with http://, it will be replaced with https://. - * If the URL does not start with any protocol, https:// will be added. - * If the URL already starts with https://, it will remain unchanged. - * - * @param {string} url - The URL to be checked and possibly modified. - * @returns {string} - The modified URL with https://. - */ -function ensureHttps(url: string): string { - if (!url.startsWith('https://')) { - if (url.startsWith('http://')) { - // Replace http:// with https:// - url = url.replace('http://', 'https://'); - } else { - // Add https:// if no protocol is present - url = 'https://' + url; - } - } - return url; -} - const ProjectSocialItem = ({ socialMedia }: IProjectSocialMediaItem) => { const item = socialMediasArray.find(item => { return item.type.toLocaleLowerCase() === socialMedia.type.toLowerCase(); @@ -63,7 +38,7 @@ const ProjectSocialItem = ({ socialMedia }: IProjectSocialMediaItem) => { @@ -76,7 +51,11 @@ const ProjectSocialItem = ({ socialMedia }: IProjectSocialMediaItem) => { ], }} > - {removeHttpsAndWwwFromUrl(socialMedia.link)} + {/* Use the updated function to show a cleaner link or username */} + {getSocialMediaHandle( + socialMedia.link, + socialMedia.type, + )} diff --git a/src/helpers/url.tsx b/src/helpers/url.tsx index 2185e90b39..4fc2092f9d 100644 --- a/src/helpers/url.tsx +++ b/src/helpers/url.tsx @@ -140,3 +140,111 @@ export function removeQueryParamAndRedirect( export const convertIPFSToHTTPS = (url: string) => { return url.replace('ipfs://', 'https://ipfs.io/ipfs/'); }; + +export const getSocialMediaHandle = ( + socialMediaUrl: string, + socialMediaType: string, +) => { + let cleanedUrl = socialMediaUrl + .replace(/^https?:\/\//, '') + .replace('www.', ''); + + // Remove trailing slash if present + if (cleanedUrl.endsWith('/')) { + cleanedUrl = cleanedUrl.slice(0, -1); + } + + // Match against different social media types using custom regex + const lowerCaseType = socialMediaType.toLowerCase(); + + switch (lowerCaseType) { + case 'github': + return extractUsernameFromPattern( + cleanedUrl, + /github\.com\/([^\/]+)/, + ); + case 'x': // Former Twitter + return extractUsernameFromPattern(cleanedUrl, /x\.com\/([^\/]+)/); + case 'facebook': + return extractUsernameFromPattern( + cleanedUrl, + /facebook\.com\/([^\/]+)/, + ); + case 'instagram': + return extractUsernameFromPattern( + cleanedUrl, + /instagram\.com\/([^\/]+)/, + ); + case 'linkedin': + return extractUsernameFromPattern( + cleanedUrl, + /linkedin\.com\/(?:in|company)\/([^\/]+)/, + ); + case 'youtube': + return extractUsernameFromPattern( + cleanedUrl, + /youtube\.com\/channel\/([^\/]+)/, + ); + case 'reddit': + return extractUsernameFromPattern( + cleanedUrl, + /reddit\.com\/r\/([^\/]+)/, + ); + case 'telegram': + return extractUsernameFromPattern(cleanedUrl, /t\.me\/([^\/]+)/); + case 'discord': + return extractUsernameFromPattern( + cleanedUrl, + /discord\.gg\/([^\/]+)/, + ); + case 'farcaster': + // Assuming Farcaster uses a pattern like 'farcaster.xyz/username' + return extractUsernameFromPattern( + cleanedUrl, + /farcaster\.xyz\/([^\/]+)/, + ); + case 'lens': + // Assuming Lens uses a pattern like 'lens.xyz/username' + return extractUsernameFromPattern( + cleanedUrl, + /lens\.xyz\/([^\/]+)/, + ); + case 'website': + default: + return cleanedUrl; // Return cleaned URL for generic websites or unsupported social media + } +}; + +// Function to extract username from URL based on the regex pattern +export const extractUsernameFromPattern = ( + url: string, + regex: RegExp, +): string => { + const match = url.match(regex); + if (match && match[1]) { + return `@${match[1]}`; // Return '@username' + } + return url; // Fallback to original URL if no match is found +}; + +/** + * Ensures that a given URL uses the https:// protocol. + * If the URL starts with http://, it will be replaced with https://. + * If the URL does not start with any protocol, https:// will be added. + * If the URL already starts with https://, it will remain unchanged. + * + * @param {string} url - The URL to be checked and possibly modified. + * @returns {string} - The modified URL with https://. + */ +export function ensureHttps(url: string): string { + if (!url.startsWith('https://')) { + if (url.startsWith('http://')) { + // Replace http:// with https:// + url = url.replace('http://', 'https://'); + } else { + // Add https:// if no protocol is present + url = 'https://' + url; + } + } + return url; +} From aae420d682e62a5261065cfe70df6503bbebbfce Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 17 Oct 2024 14:58:17 +0200 Subject: [PATCH 43/97] fixing header config --- next-env.d.ts | 2 +- next.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03dc6..a4a7b3f5cf 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/next.config.js b/next.config.js index d290b6c412..e33a8980fb 100644 --- a/next.config.js +++ b/next.config.js @@ -148,7 +148,7 @@ const moduleExports = withBundleAnalyzer({ locales, defaultLocale, }, - headers: () => { + headers: async () => { return [ { // Adding CORS headers for /manifest.json From f73f735c443b1fa231223869257aebbd7ae404ae Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 17 Oct 2024 15:20:08 +0200 Subject: [PATCH 44/97] trying --- next.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/next.config.js b/next.config.js index e33a8980fb..115c0884dc 100644 --- a/next.config.js +++ b/next.config.js @@ -153,6 +153,7 @@ const moduleExports = withBundleAnalyzer({ { // Adding CORS headers for /manifest.json source: '/manifest.json', + locale: false, headers: [ { key: 'Access-Control-Allow-Origin', From 4126e18d31454128914600f57695afd9fc43606f Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 11:31:30 +0330 Subject: [PATCH 45/97] add getIndexedBlockQuery to SubgraphQueryBuilder --- src/lib/subgraph/subgraphQueryBuilder.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib/subgraph/subgraphQueryBuilder.ts b/src/lib/subgraph/subgraphQueryBuilder.ts index 9e2ea70c4f..c5ea401093 100644 --- a/src/lib/subgraph/subgraphQueryBuilder.ts +++ b/src/lib/subgraph/subgraphQueryBuilder.ts @@ -13,6 +13,15 @@ export class SubgraphQueryBuilder { `; }; + static getIndexedBlockQuery = (): string => { + return `_meta { + block { + number + } + } + `; + }; + static getBalanceQuery = ( networkConfig: NetworkConfig, userAddress?: string, @@ -233,6 +242,7 @@ export class SubgraphQueryBuilder { const givpowerConfig = networkConfig?.GIVPOWER; return ` { + ${SubgraphQueryBuilder.getIndexedBlockQuery()} ${SubgraphQueryBuilder.getBalanceQuery(networkConfig, userAddress)} ${SubgraphQueryBuilder.generateTokenDistroQueries(networkConfig, userAddress)} ${SubgraphQueryBuilder.generateFarmingQueries( From a9e1a58011efabf4b822f1542f565f3e3bd8e64c Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 11:31:38 +0330 Subject: [PATCH 46/97] add transformIndexedBlockInfo --- src/lib/subgraph/subgraphDataTransform.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/subgraph/subgraphDataTransform.ts b/src/lib/subgraph/subgraphDataTransform.ts index 4a29566861..493ba5c415 100644 --- a/src/lib/subgraph/subgraphDataTransform.ts +++ b/src/lib/subgraph/subgraphDataTransform.ts @@ -198,6 +198,10 @@ export const transformUserGIVLocked = (info: any = {}): ITokenBalance => { }; }; +const transformIndexedBlockInfo = (info: any = {}): number => { + return info?.block?.number || 0; +}; + export const transformSubgraphData = (data: any = {}): ISubgraphState => { const result: ISubgraphState = {}; Object.entries(data).forEach(([key, value]) => { @@ -229,6 +233,9 @@ export const transformSubgraphData = (data: any = {}): ISubgraphState => { case key === 'userGIVLocked': result[key] = transformUserGIVLocked(value); break; + case key === '_meta': + result['indexedBlockNumber'] = transformIndexedBlockInfo(value); + break; default: } From 0356be07cc1b81ccb9de7adc9009f61ff0e3e8d9 Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 13:25:19 +0330 Subject: [PATCH 47/97] add test --- pages/test2.tsx | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/pages/test2.tsx b/pages/test2.tsx index c9256c9843..9c4ce298bc 100644 --- a/pages/test2.tsx +++ b/pages/test2.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { useQueries } from '@tanstack/react-query'; +import { useQueries, useQuery, useQueryClient } from '@tanstack/react-query'; import { useAccount } from 'wagmi'; import { PublicKey, @@ -20,7 +20,8 @@ import { fetchSubgraphData } from '@/services/subgraph.service'; const YourApp = () => { const [failedModalType, setFailedModalType] = useState(); - const { address } = useAccount(); + const queryClient = useQueryClient(); + const { address, chain } = useAccount(); const subgraphValues = useQueries({ queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ queryKey: ['subgraph', chain.id, address], @@ -31,6 +32,14 @@ const YourApp = () => { })), }); + const { data } = useQuery({ + queryKey: ['interactedBlockNumber', chain?.id], + queryFn: () => 0, + staleTime: Infinity, + }); + + console.log('data', data); + // Solana wallet hooks const { publicKey, @@ -126,6 +135,22 @@ const YourApp = () => { Test Button
+
+ {data} + + {chain?.id && ( + + )} +
{failedModalType && ( Date: Mon, 21 Oct 2024 13:25:44 +0330 Subject: [PATCH 48/97] add useFetchSubgraphDataForAllChains --- src/hooks/useFetchSubgraphDataForAllChains.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/hooks/useFetchSubgraphDataForAllChains.ts diff --git a/src/hooks/useFetchSubgraphDataForAllChains.ts b/src/hooks/useFetchSubgraphDataForAllChains.ts new file mode 100644 index 0000000000..f8acc552cd --- /dev/null +++ b/src/hooks/useFetchSubgraphDataForAllChains.ts @@ -0,0 +1,21 @@ +import { useQueries } from '@tanstack/react-query'; +import { Address } from 'viem'; +import { useAccount } from 'wagmi'; +import config from '@/configuration'; +import { fetchSubgraphData } from '@/services/subgraph.service'; + +export const useFetchSubgraphDataForAllChains = () => { + const { address } = useAccount(); + return useQueries({ + queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ + queryKey: ['subgraph', chain.id, address] as [ + string, + number, + Address, + ], + queryFn: async () => await fetchSubgraphData(chain.id, address), + staleTime: config.SUBGRAPH_POLLING_INTERVAL, + enabled: !!address, + })), + }); +}; From 1f7a2f062c1677258dc609712935a13c9d0dc35b Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 13:26:34 +0330 Subject: [PATCH 49/97] add useInteractedBlockNumber --- src/hooks/useInteractedBlockNumber.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/hooks/useInteractedBlockNumber.ts diff --git a/src/hooks/useInteractedBlockNumber.ts b/src/hooks/useInteractedBlockNumber.ts new file mode 100644 index 0000000000..02f4f0c410 --- /dev/null +++ b/src/hooks/useInteractedBlockNumber.ts @@ -0,0 +1,11 @@ +import { useQuery } from '@tanstack/react-query'; +import { useAccount } from 'wagmi'; + +export const useInteractedBlockNumber = (_chainId?: number) => { + const { chainId: accountChainId } = useAccount(); + return useQuery({ + queryKey: ['interactedBlockNumber', _chainId || accountChainId], + queryFn: () => 0, + staleTime: Infinity, + }); +}; From 6ed292692968218599fa7be63e0258e1eb603497 Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 13:27:15 +0330 Subject: [PATCH 50/97] add useSubgraphInfo --- src/hooks/useSubgraphInfo.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/hooks/useSubgraphInfo.ts diff --git a/src/hooks/useSubgraphInfo.ts b/src/hooks/useSubgraphInfo.ts new file mode 100644 index 0000000000..653b4ed2ab --- /dev/null +++ b/src/hooks/useSubgraphInfo.ts @@ -0,0 +1,15 @@ +import { useQuery } from '@tanstack/react-query'; +import { useAccount } from 'wagmi'; +import config from '@/configuration'; +import { fetchSubgraphData } from '@/services/subgraph.service'; + +export const useSubgraphInfo = (chainId?: number) => { + const { address, chainId: accountChainId } = useAccount(); + const _chainId = chainId || accountChainId; + return useQuery({ + queryKey: ['subgraph', _chainId, address], + queryFn: async () => await fetchSubgraphData(_chainId, address), + enabled: !!_chainId, + staleTime: config.SUBGRAPH_POLLING_INTERVAL, + }); +}; From e99564866195f716ef74b8903312b51157676006 Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 13:27:21 +0330 Subject: [PATCH 51/97] add useSubgraphSyncInfo --- src/hooks/useSubgraphSyncInfo.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/hooks/useSubgraphSyncInfo.ts diff --git a/src/hooks/useSubgraphSyncInfo.ts b/src/hooks/useSubgraphSyncInfo.ts new file mode 100644 index 0000000000..b79864f016 --- /dev/null +++ b/src/hooks/useSubgraphSyncInfo.ts @@ -0,0 +1,31 @@ +import { useAccount } from 'wagmi'; +import { useMemo } from 'react'; +import { useInteractedBlockNumber } from './useInteractedBlockNumber'; +import { useSubgraphInfo } from './useSubgraphInfo'; + +export const useSubgraphSyncInfo = (chainId?: number) => { + const { chainId: accountChainId } = useAccount(); + const _chainId = chainId || accountChainId; + const interactedBlockInfo = useInteractedBlockNumber(_chainId); + const subgraphInfo = useSubgraphInfo(); + + const isSynced = useMemo(() => { + if (!subgraphInfo.data?.indexedBlockNumber) return false; + if (interactedBlockInfo.data === undefined) return false; + try { + const indexedBlockNumber = Number( + subgraphInfo.data?.indexedBlockNumber, + ); + const interactedBlockNumber = interactedBlockInfo.data; + return indexedBlockNumber >= interactedBlockNumber; + } catch (error) { + return false; + } + }, [interactedBlockInfo.data, subgraphInfo.data?.indexedBlockNumber]); + + return { + isSynced, + interactedBlockNumber: interactedBlockInfo.data, + indexedBlockNumber: subgraphInfo.data?.indexedBlockNumber, + }; +}; From 3a3e983837745fa1c7d3ec5056c6a75b8ed4111d Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 13:28:55 +0330 Subject: [PATCH 52/97] use hooks in subgraph controller --- src/components/controller/subgraph.ctrl.tsx | 31 ++++++++------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/components/controller/subgraph.ctrl.tsx b/src/components/controller/subgraph.ctrl.tsx index d4d8300455..f69f0098da 100644 --- a/src/components/controller/subgraph.ctrl.tsx +++ b/src/components/controller/subgraph.ctrl.tsx @@ -1,31 +1,18 @@ import React, { useEffect, useRef } from 'react'; import { useAccount } from 'wagmi'; -import { useQueries, useQueryClient } from '@tanstack/react-query'; +import { useQueryClient } from '@tanstack/react-query'; import { Address } from 'viem'; -import config from '@/configuration'; -import { - fetchLatestIndexedBlock, - fetchSubgraphData, -} from '@/services/subgraph.service'; +import { fetchLatestIndexedBlock } from '@/services/subgraph.service'; +import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataForAllChains'; +import { useInteractedBlockNumber } from '@/hooks/useInteractedBlockNumber'; const SubgraphController: React.FC = () => { - const { address } = useAccount(); + const { address, chain } = useAccount(); const queryClient = useQueryClient(); const pollingTimeoutsRef = useRef<{ [key: number]: NodeJS.Timeout }>({}); const refetchedChainsRef = useRef>(new Set()); - - useQueries({ - queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ - queryKey: ['subgraph', chain.id, address] as [ - string, - number, - Address, - ], - queryFn: async () => await fetchSubgraphData(chain.id, address), - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - enabled: !!address, - })), - }); + useFetchSubgraphDataForAllChains(); + useInteractedBlockNumber(chain?.id); useEffect(() => { const handleEvent = ( @@ -49,6 +36,10 @@ const SubgraphController: React.FC = () => { // Reset refetchedChainsRef for the current chain ID refetchedChainsRef.current.delete(eventChainId); + queryClient.setQueryData( + ['interactedBlockNumber', eventChainId], + blockNumber, + ); // Ensure any existing timeout is cleared if (pollingTimeoutsRef.current[eventChainId]) { From cdadfbd5576ff4fc97c22ad776698ccd5846ebc1 Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 13:29:18 +0330 Subject: [PATCH 53/97] disable action buttons if subgraph is not synced --- .../StakingPoolInfoAndActions.tsx | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/components/cards/StakingCards/BaseStakingCard/StakingPoolInfoAndActions.tsx b/src/components/cards/StakingCards/BaseStakingCard/StakingPoolInfoAndActions.tsx index f110242078..c2668d0be8 100644 --- a/src/components/cards/StakingCards/BaseStakingCard/StakingPoolInfoAndActions.tsx +++ b/src/components/cards/StakingCards/BaseStakingCard/StakingPoolInfoAndActions.tsx @@ -54,6 +54,7 @@ import LockModal from '@/components/modals/StakeLock/Lock'; import { WhatIsStreamModal } from '@/components/modals/WhatIsStream'; import { LockupDetailsModal } from '@/components/modals/LockupDetailsModal'; import ExternalLink from '@/components/ExternalLink'; +import { useSubgraphSyncInfo } from '@/hooks/useSubgraphSyncInfo'; interface IStakingPoolInfoAndActionsProps { poolStakingConfig: PoolStakingConfig | RegenPoolStakingConfig; @@ -84,15 +85,16 @@ export const StakingPoolInfoAndActions: FC = ({ const [showWhatIsGIVstreamModal, setShowWhatIsGIVstreamModal] = useState(false); + const { formatMessage } = useIntl(); + const { setChainInfo } = useFarms(); + const router = useRouter(); + const subgraphSyncedInfo = useSubgraphSyncInfo(poolStakingConfig.network); const hold = showAPRModal || showStakeModal || showUnStakeModal || showHarvestModal || showLockModal; - const { formatMessage } = useIntl(); - const { setChainInfo } = useFarms(); - const router = useRouter(); const { apr, notStakedAmount: userNotStakedAmount, @@ -354,7 +356,12 @@ export const StakingPoolInfoAndActions: FC = ({ )} setShowHarvestModal(true)} label={formatMessage({ id: 'label.harvest_rewards', @@ -363,7 +370,10 @@ export const StakingPoolInfoAndActions: FC = ({ /> {isGIVpower && ( setShowLockModal(true)} label={ started @@ -386,7 +396,8 @@ export const StakingPoolInfoAndActions: FC = ({ disabled={ isDiscontinued || exploited || - userNotStakedAmount === 0n + userNotStakedAmount === 0n || + !subgraphSyncedInfo.isSynced } onClick={() => setShowStakeModal(true)} /> @@ -404,7 +415,10 @@ export const StakingPoolInfoAndActions: FC = ({ id: 'label.unstake', })} size='small' - disabled={availableStakedToken === 0n} + disabled={ + availableStakedToken === 0n || + !subgraphSyncedInfo.isSynced + } onClick={() => setShowUnStakeModal(true)} /> From 1f559c10b925b4319669a8720596d9ed05f824ef Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 13:31:36 +0330 Subject: [PATCH 54/97] add chainEvent to harvest --- src/components/modals/HarvestAll.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/modals/HarvestAll.tsx b/src/components/modals/HarvestAll.tsx index 4853230e28..4ea9f4f0dd 100644 --- a/src/components/modals/HarvestAll.tsx +++ b/src/components/modals/HarvestAll.tsx @@ -252,11 +252,19 @@ export const HarvestAllModal: FC = ({ if (txResponse) { setState(HarvestStates.SUBMITTED); setTxHash(txResponse); - const { status } = await waitForTransaction( + const { status, blockNumber } = await waitForTransaction( txResponse, isSafeEnv, ); - + const event = new CustomEvent('chainEvent', { + detail: { + type: 'success', + chainId: chainId, + blockNumber: blockNumber, + address: address, + }, + }); + window.dispatchEvent(event); setState( status ? HarvestStates.CONFIRMED : HarvestStates.ERROR, ); From 8f5288f4a0c62c0bb9188e39727da0e719d9b7ee Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 13:45:33 +0330 Subject: [PATCH 55/97] use new hooks --- pages/test2.tsx | 15 +++-------- src/components/GIVeconomyPages/GIVbacks.tsx | 16 ++++------- src/components/GIVeconomyPages/GIVpower.tsx | 13 ++------- src/components/GIVeconomyPages/GIVstream.tsx | 27 +++++-------------- .../GIVpowerCard/GIVpowerCardIntro.tsx | 12 ++------- src/components/givfarm/RegenStreamCard.tsx | 11 +++----- src/components/menu/RewardButtonWithMenu.tsx | 13 ++------- src/components/menu/RewardItems.tsx | 14 +++------- src/components/modals/Boost/BoostModal.tsx | 14 ++-------- src/components/modals/GIVdropHarvestModal.tsx | 12 +++------ .../modals/StakeLock/LockSlider.tsx | 12 ++------- .../modals/StakeLock/LockingBrief.tsx | 12 ++------- .../modals/StakeLock/TotalGIVpowerBox.tsx | 14 ++-------- src/components/views/claim/cards/Govern.tsx | 13 +++------ src/components/views/claim/cards/Stake.tsx | 20 +++----------- .../views/userProfile/ProfileOverviewTab.tsx | 14 ++-------- .../boostedTab/EmptyPowerBoosting.tsx | 14 ++-------- .../boostedTab/ProfileBoostedTab.tsx | 14 ++-------- src/hooks/useGIVTokenDistroHelper.ts | 13 ++------- src/hooks/useStakingPool.ts | 14 ++-------- src/hooks/useTokenDistroHelper.ts | 13 ++------- 21 files changed, 55 insertions(+), 245 deletions(-) diff --git a/pages/test2.tsx b/pages/test2.tsx index 9c4ce298bc..e607b757f8 100644 --- a/pages/test2.tsx +++ b/pages/test2.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { useQueries, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useAccount } from 'wagmi'; import { PublicKey, @@ -14,23 +14,14 @@ import FailedDonation, { } from '@/components/modals/FailedDonation'; import { getTotalGIVpower } from '@/helpers/givpower'; import { formatWeiHelper } from '@/helpers/number'; -import config from '@/configuration'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataForAllChains'; const YourApp = () => { const [failedModalType, setFailedModalType] = useState(); const queryClient = useQueryClient(); const { address, chain } = useAccount(); - const subgraphValues = useQueries({ - queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ - queryKey: ['subgraph', chain.id, address], - queryFn: async () => { - return await fetchSubgraphData(chain.id, address); - }, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - })), - }); + const subgraphValues = useFetchSubgraphDataForAllChains(); const { data } = useQuery({ queryKey: ['interactedBlockNumber', chain?.id], diff --git a/src/components/GIVeconomyPages/GIVbacks.tsx b/src/components/GIVeconomyPages/GIVbacks.tsx index a92296b817..7151fe5fd5 100644 --- a/src/components/GIVeconomyPages/GIVbacks.tsx +++ b/src/components/GIVeconomyPages/GIVbacks.tsx @@ -12,7 +12,6 @@ import { import Link from 'next/link'; import { useIntl } from 'react-intl'; import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import { GIVbacksTopContainer, GIVbacksBottomContainer, @@ -43,9 +42,9 @@ import { NoWrap, TopInnerContainer } from './commons'; import links from '@/lib/constants/links'; import Routes from '@/lib/constants/Routes'; import { SubgraphDataHelper } from '@/lib/subgraph/subgraphDataHelper'; -import { fetchSubgraphData } from '@/services/subgraph.service'; import { FETCH_ALLOCATED_GIVBACKS } from '@/apollo/gql/gqlGivbacks'; import { client } from '@/apollo/apolloClient'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; export const TabGIVbacksTop = () => { const { formatMessage } = useIntl(); @@ -53,17 +52,12 @@ export const TabGIVbacksTop = () => { const [showGivBackExplain, setShowGivBackExplain] = useState(false); const [givBackStream, setGivBackStream] = useState(0n); const { givTokenDistroHelper } = useGIVTokenDistroHelper(showHarvestModal); - const { chain, address } = useAccount(); + const { chainId } = useAccount(); const dataChainId = - chain?.id === config.OPTIMISM_NETWORK_NUMBER + chainId === config.OPTIMISM_NETWORK_NUMBER ? config.OPTIMISM_NETWORK_NUMBER : config.GNOSIS_NETWORK_NUMBER; - const values = useQuery({ - queryKey: ['subgraph', dataChainId, address], - queryFn: async () => await fetchSubgraphData(dataChainId, address), - enabled: !!chain, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const values = useSubgraphInfo(dataChainId); const givTokenDistroBalance = useMemo(() => { const sdh = new SubgraphDataHelper(values.data); return sdh.getGIVTokenDistroBalance(); @@ -107,7 +101,7 @@ export const TabGIVbacksTop = () => { actionCb={() => { setShowHarvestModal(true); }} - network={chain?.id} + network={chainId} targetNetworks={[ { networkId: config.GNOSIS_NETWORK_NUMBER, diff --git a/src/components/GIVeconomyPages/GIVpower.tsx b/src/components/GIVeconomyPages/GIVpower.tsx index 9d1720f2cc..26556f949b 100644 --- a/src/components/GIVeconomyPages/GIVpower.tsx +++ b/src/components/GIVeconomyPages/GIVpower.tsx @@ -16,7 +16,6 @@ import Link from 'next/link'; import { useIntl } from 'react-intl'; import { useWeb3Modal } from '@web3modal/wagmi/react'; import { useAccount } from 'wagmi'; -import { useQueries } from '@tanstack/react-query'; import { GIVpowerTopContainer, Title, @@ -61,21 +60,13 @@ import { formatWeiHelper } from '@/helpers/number'; import { getTotalGIVpower } from '@/helpers/givpower'; import { useGeneralWallet } from '@/providers/generalWalletProvider'; import { ChainType } from '@/types/config'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataForAllChains'; export function TabPowerTop() { const { formatMessage } = useIntl(); const { open: openConnectModal } = useWeb3Modal(); const { address } = useAccount(); - const subgraphValues = useQueries({ - queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ - queryKey: ['subgraph', chain.id, address], - queryFn: async () => { - return await fetchSubgraphData(chain.id, address); - }, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - })), - }); + const subgraphValues = useFetchSubgraphDataForAllChains(); const givPower = getTotalGIVpower(subgraphValues, address); const givPowerFormatted = formatWeiHelper(givPower.total); const hasZeroGivPower = givPowerFormatted === '0'; diff --git a/src/components/GIVeconomyPages/GIVstream.tsx b/src/components/GIVeconomyPages/GIVstream.tsx index 7b20734b56..cf1047a615 100644 --- a/src/components/GIVeconomyPages/GIVstream.tsx +++ b/src/components/GIVeconomyPages/GIVstream.tsx @@ -19,7 +19,6 @@ import { } from '@giveth/ui-design-system'; import { useIntl } from 'react-intl'; import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import { Bar, FlowRateRow, @@ -52,7 +51,7 @@ import { GridWrapper, } from './GIVstream.sc'; import { IconWithTooltip } from '../IconWithToolTip'; -import { getHistory, fetchSubgraphData } from '@/services/subgraph.service'; +import { getHistory } from '@/services/subgraph.service'; import { formatWeiHelper } from '@/helpers/number'; import config from '@/configuration'; import { durationToString, shortenAddress } from '@/lib/helpers'; @@ -64,6 +63,7 @@ import { IconGIV } from '../Icons/GIV'; import { givEconomySupportedNetworks } from '@/lib/constants/constants'; import Pagination from '../Pagination'; import { SubgraphDataHelper } from '@/lib/subgraph/subgraphDataHelper'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; export const TabGIVstreamTop = () => { const { formatMessage } = useIntl(); @@ -71,13 +71,8 @@ export const TabGIVstreamTop = () => { const [rewardLiquidPart, setRewardLiquidPart] = useState(0n); const [rewardStream, setRewardStream] = useState(0n); const { givTokenDistroHelper } = useGIVTokenDistroHelper(showModal); - const { chain, address } = useAccount(); - const currentValues = useQuery({ - queryKey: ['subgraph', chain?.id, address], - queryFn: async () => await fetchSubgraphData(chain?.id, address), - enabled: !!chain, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const { chain } = useAccount(); + const currentValues = useSubgraphInfo(); const chainId = chain?.id; const sdh = new SubgraphDataHelper(currentValues.data); @@ -177,12 +172,7 @@ export const TabGIVstreamBottom = () => { const [remain, setRemain] = useState(''); useState(0n); const [streamAmount, setStreamAmount] = useState(0n); - const currentValues = useQuery({ - queryKey: ['subgraph', chain?.id, address], - queryFn: async () => await fetchSubgraphData(chain?.id, address), - enabled: !!chain, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const currentValues = useSubgraphInfo(); const chainId = chain?.id; const sdh = new SubgraphDataHelper(currentValues.data); @@ -388,12 +378,7 @@ export const GIVstreamHistory: FC = () => { const [loading, setLoading] = useState(true); const [page, setPage] = useState(0); - const currentValue = useQuery({ - queryKey: ['subgraph', chain?.id, address], - queryFn: async () => await fetchSubgraphData(chain?.id, address), - enabled: !!chain, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const currentValue = useSubgraphInfo(); const chainId = chain?.id; const sdh = new SubgraphDataHelper(currentValue.data); diff --git a/src/components/cards/StakingCards/GIVpowerCard/GIVpowerCardIntro.tsx b/src/components/cards/StakingCards/GIVpowerCard/GIVpowerCardIntro.tsx index 967bf7c45e..8672405076 100644 --- a/src/components/cards/StakingCards/GIVpowerCard/GIVpowerCardIntro.tsx +++ b/src/components/cards/StakingCards/GIVpowerCard/GIVpowerCardIntro.tsx @@ -14,8 +14,6 @@ import { useState } from 'react'; import { useIntl } from 'react-intl'; import styled from 'styled-components'; import Link from 'next/link'; -import { useQuery } from '@tanstack/react-query'; -import { useAccount } from 'wagmi'; import links from '@/lib/constants/links'; import { SubgraphDataHelper } from '@/lib/subgraph/subgraphDataHelper'; import Routes from '@/lib/constants/Routes'; @@ -24,7 +22,7 @@ import TotalGIVpowerBox from '@/components/modals/StakeLock/TotalGIVpowerBox'; import { StakeCardState } from '../BaseStakingCard/BaseStakingCard'; import { useStakingPool } from '@/hooks/useStakingPool'; import config from '@/configuration'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; import type { Dispatch, FC, SetStateAction } from 'react'; interface IGIVpowerCardIntro { @@ -42,13 +40,7 @@ const GIVpowerCardIntro: FC = ({ config.EVM_NETWORKS_CONFIG[poolNetwork].GIVPOWER || config.GNOSIS_CONFIG.GIVPOWER, ); - const { chain, address } = useAccount(); - const currentValues = useQuery({ - queryKey: ['subgraph', chain?.id, address], - queryFn: async () => await fetchSubgraphData(chain?.id, address), - enabled: !!chain, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const currentValues = useSubgraphInfo(); const sdh = new SubgraphDataHelper(currentValues.data); const userGIVLocked = sdh.getUserGIVLockedBalance(); diff --git a/src/components/givfarm/RegenStreamCard.tsx b/src/components/givfarm/RegenStreamCard.tsx index 8af1fe404e..c74c7ee5e2 100644 --- a/src/components/givfarm/RegenStreamCard.tsx +++ b/src/components/givfarm/RegenStreamCard.tsx @@ -20,7 +20,6 @@ import BigNumber from 'bignumber.js'; import styled from 'styled-components'; import { useIntl } from 'react-intl'; import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import { durationToString } from '@/lib/helpers'; import { Bar, GsPTooltip } from '@/components/GIVeconomyPages/GIVstream.sc'; import { IconWithTooltip } from '@/components/IconWithToolTip'; @@ -35,9 +34,9 @@ import { TokenDistroHelper } from '@/lib/contractHelper/TokenDistroHelper'; import { Relative } from '../styled-components/Position'; import { ArchiveAndNetworkCover } from '../ArchiveAndNetworkCover/ArchiveAndNetworkCover'; import { getSubgraphChainId } from '@/helpers/network'; -import { fetchSubgraphData } from '@/services/subgraph.service'; import { useFetchMainnetThirdPartyTokensPrice } from '@/hooks/useFetchMainnetThirdPartyTokensPrice'; import { useFetchGnosisThirdPartyTokensPrice } from '@/hooks/useFetchGnosisThirdPartyTokensPrice'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; interface RegenStreamProps { streamConfig: RegenStreamConfig; @@ -63,13 +62,9 @@ export const RegenStreamCard: FC = ({ streamConfig }) => { const [lockedAmount, setLockedAmount] = useState(0n); const [claimedAmount, setClaimedAmount] = useState(0n); - const { address, chain } = useAccount(); + const { chain } = useAccount(); const subgraphChainId = getSubgraphChainId(streamConfig.network); - const currentValues = useQuery({ - queryKey: ['subgraph', subgraphChainId, address], - queryFn: async () => await fetchSubgraphData(subgraphChainId, address), - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const currentValues = useSubgraphInfo(subgraphChainId); const chainId = chain?.id; const { diff --git a/src/components/menu/RewardButtonWithMenu.tsx b/src/components/menu/RewardButtonWithMenu.tsx index 2acc23fadc..4b2e634a7f 100644 --- a/src/components/menu/RewardButtonWithMenu.tsx +++ b/src/components/menu/RewardButtonWithMenu.tsx @@ -1,7 +1,5 @@ import React, { FC, useEffect, useState } from 'react'; -import { useAccount } from 'wagmi'; import { FlexSpacer } from '@giveth/ui-design-system'; -import { useQuery } from '@tanstack/react-query'; import { MenuAndButtonContainer, BalanceButton, @@ -22,8 +20,7 @@ import { MenuContainer } from './Menu.sc'; import { ItemsProvider } from '@/context/Items.context'; import { SubgraphDataHelper } from '@/lib/subgraph/subgraphDataHelper'; import { formatWeiHelper } from '@/helpers/number'; -import { fetchSubgraphData } from '@/services/subgraph.service'; -import config from '@/configuration'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; interface IRewardButtonWithMenuProps extends IHeaderButtonProps {} @@ -100,13 +97,7 @@ export const RewardButtonWithMenu: FC = ({ }; const HeaderRewardButton = () => { - const { chain, address } = useAccount(); - const currentValues = useQuery({ - queryKey: ['subgraph', chain?.id, address], - queryFn: async () => await fetchSubgraphData(chain?.id, address), - enabled: !!chain, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const currentValues = useSubgraphInfo(); const sdh = new SubgraphDataHelper(currentValues.data); const givBalance = sdh.getGIVTokenBalance(); return ( diff --git a/src/components/menu/RewardItems.tsx b/src/components/menu/RewardItems.tsx index 4a379dfb1d..088f0a1bf9 100644 --- a/src/components/menu/RewardItems.tsx +++ b/src/components/menu/RewardItems.tsx @@ -10,7 +10,6 @@ import { useIntl } from 'react-intl'; import Image from 'next/image'; import Link from 'next/link'; import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import config from '@/configuration'; import useGIVTokenDistroHelper from '@/hooks/useGIVTokenDistroHelper'; import { formatWeiHelper } from '@/helpers/number'; @@ -37,7 +36,7 @@ import { setShowSwitchNetworkModal } from '@/features/modal/modal.slice'; import { getChainName } from '@/lib/network'; import { getNetworkConfig } from '@/helpers/givpower'; import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; export interface IRewardItemsProps { showWhatIsGIVstreamModal: boolean; @@ -55,17 +54,10 @@ export const RewardItems: FC = ({ const [givStreamLiquidPart, setGIVstreamLiquidPart] = useState(0n); const [flowRateNow, setFlowRateNow] = useState(0n); - const { address, chain } = useAccount(); - const currentValues = useQuery({ - queryKey: ['subgraph', chain?.id, address], - queryFn: async () => await fetchSubgraphData(chain?.id, address), - enabled: !!chain, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const { chainId } = useAccount(); + const currentValues = useSubgraphInfo(); const { givTokenDistroHelper } = useGIVTokenDistroHelper(); const dispatch = useAppDispatch(); - - const chainId = chain?.id; const sdh = new SubgraphDataHelper(currentValues.data); const tokenDistroBalance = sdh.getGIVTokenDistroBalance(); diff --git a/src/components/modals/Boost/BoostModal.tsx b/src/components/modals/Boost/BoostModal.tsx index 074f2235a8..26e1e6a24f 100644 --- a/src/components/modals/Boost/BoostModal.tsx +++ b/src/components/modals/Boost/BoostModal.tsx @@ -1,7 +1,6 @@ import { IconRocketInSpace32 } from '@giveth/ui-design-system'; import { FC, useState } from 'react'; import { useIntl } from 'react-intl'; -import { useQueries } from '@tanstack/react-query'; import { useAccount } from 'wagmi'; import { IModal } from '@/types/common'; import { Modal } from '../Modal'; @@ -12,8 +11,7 @@ import { BoostModalContainer } from './BoostModal.sc'; import BoostedInnerModal from './BoostedInnerModal'; import BoostInnerModal from './BoostInnerModal'; import { getTotalGIVpower } from '@/helpers/givpower'; -import config from '@/configuration'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataForAllChains'; interface IBoostModalProps extends IModal { projectId: string; @@ -31,15 +29,7 @@ const BoostModal: FC = ({ setShowModal, projectId }) => { const [percentage, setPercentage] = useState(0); const [state, setState] = useState(EBoostModalState.BOOSTING); const { address } = useAccount(); - const subgraphValues = useQueries({ - queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ - queryKey: ['subgraph', chain.id, address], - queryFn: async () => { - return await fetchSubgraphData(chain.id, address); - }, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - })), - }); + const subgraphValues = useFetchSubgraphDataForAllChains(); const givPower = getTotalGIVpower(subgraphValues, address); if (givPower.total.isZero()) { diff --git a/src/components/modals/GIVdropHarvestModal.tsx b/src/components/modals/GIVdropHarvestModal.tsx index f0ed5d0db5..617f0693e6 100644 --- a/src/components/modals/GIVdropHarvestModal.tsx +++ b/src/components/modals/GIVdropHarvestModal.tsx @@ -12,7 +12,6 @@ import BigNumber from 'bignumber.js'; import { captureException } from '@sentry/nextjs'; import { useAccount } from 'wagmi'; import { WriteContractReturnType } from 'viem'; -import { useQuery } from '@tanstack/react-query'; import { Modal } from './Modal'; import { ConfirmedInnerModal, @@ -41,8 +40,8 @@ import { IModal } from '@/types/common'; import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; import { useModalAnimation } from '@/hooks/useModalAnimation'; import { SubgraphDataHelper } from '@/lib/subgraph/subgraphDataHelper'; -import { fetchSubgraphData } from '@/services/subgraph.service'; import { useFetchGIVPrice } from '@/hooks/useGivPrice'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; enum ClaimState { UNKNOWN, @@ -80,14 +79,9 @@ export const GIVdropHarvestModal: FC = ({ const { isAnimating, closeModal } = useModalAnimation(setShowModal); const { givTokenDistroHelper } = useGIVTokenDistroHelper(); - const { address, chain } = useAccount(); - const currentValues = useQuery({ - queryKey: ['subgraph', chain?.id, address], - queryFn: async () => await fetchSubgraphData(chain?.id, address), - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const { address, chainId } = useAccount(); + const currentValues = useSubgraphInfo(); - const chainId = chain?.id; const sdh = new SubgraphDataHelper(currentValues.data); const givTokenDistroBalance = sdh.getGIVTokenDistroBalance(); const { data: givPrice } = useFetchGIVPrice(); diff --git a/src/components/modals/StakeLock/LockSlider.tsx b/src/components/modals/StakeLock/LockSlider.tsx index 89fc599b24..d6efc9a4e8 100644 --- a/src/components/modals/StakeLock/LockSlider.tsx +++ b/src/components/modals/StakeLock/LockSlider.tsx @@ -12,12 +12,10 @@ import { import styled from 'styled-components'; import { Dispatch, FC, SetStateAction, useState } from 'react'; import { useIntl } from 'react-intl'; -import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import { smallFormatDate } from '@/lib/helpers'; import { getUnlockDate } from '@/helpers/givpower'; import config from '@/configuration'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; import type { IGIVpower } from '@/types/subgraph'; const maxRound = 26; @@ -29,13 +27,7 @@ interface ILockSlider { const LockSlider: FC = ({ round, setRound }) => { const { formatMessage, locale } = useIntl(); const [isChanged, setIsChanged] = useState(false); - const { address } = useAccount(); - const gnosisValues = useQuery({ - queryKey: ['subgraph', config.GNOSIS_NETWORK_NUMBER, address], - queryFn: async () => - await fetchSubgraphData(config.GNOSIS_NETWORK_NUMBER, address), - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const gnosisValues = useSubgraphInfo(config.GNOSIS_NETWORK_NUMBER); const givpowerInfo = gnosisValues.data?.givpowerInfo as IGIVpower; const unlockDate = new Date(getUnlockDate(givpowerInfo, round)); return ( diff --git a/src/components/modals/StakeLock/LockingBrief.tsx b/src/components/modals/StakeLock/LockingBrief.tsx index 9d2161d5bd..cd62724ecf 100644 --- a/src/components/modals/StakeLock/LockingBrief.tsx +++ b/src/components/modals/StakeLock/LockingBrief.tsx @@ -1,12 +1,10 @@ import { brandColors, H5, Flex } from '@giveth/ui-design-system'; import styled from 'styled-components'; -import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import { formatWeiHelper } from '@/helpers/number'; import { smallFormatDate } from '@/lib/helpers'; import { getUnlockDate } from '@/helpers/givpower'; import config from '@/configuration'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; import type { FC } from 'react'; import type { IGIVpower } from '@/types/subgraph'; @@ -20,13 +18,7 @@ const LockingBrief: FC = ({ amount, onLocking = false, }) => { - const { address } = useAccount(); - const gnosisValues = useQuery({ - queryKey: ['subgraph', config.GNOSIS_NETWORK_NUMBER, address], - queryFn: async () => - await fetchSubgraphData(config.GNOSIS_NETWORK_NUMBER, address), - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const gnosisValues = useSubgraphInfo(config.GNOSIS_NETWORK_NUMBER); const givpowerInfo = gnosisValues.data?.givpowerInfo as IGIVpower; const unlockDate = new Date(getUnlockDate(givpowerInfo, round)); return ( diff --git a/src/components/modals/StakeLock/TotalGIVpowerBox.tsx b/src/components/modals/StakeLock/TotalGIVpowerBox.tsx index 57eade613b..86893863d0 100644 --- a/src/components/modals/StakeLock/TotalGIVpowerBox.tsx +++ b/src/components/modals/StakeLock/TotalGIVpowerBox.tsx @@ -9,27 +9,17 @@ import styled from 'styled-components'; import { useEffect, useState } from 'react'; import BigNumber from 'bignumber.js'; import { useAccount } from 'wagmi'; -import { useQueries } from '@tanstack/react-query'; import { formatWeiHelper } from '@/helpers/number'; import { WrappedSpinner } from '@/components/Spinner'; import { getTotalGIVpower } from '@/helpers/givpower'; import { getGIVpowerOnChain } from '@/lib/stakingPool'; -import config from '@/configuration'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataForAllChains'; const TotalGIVpowerBox = () => { const [totalGIVpower, setTotalGIVpower] = useState(); const { address, chain } = useAccount(); const chainId = chain?.id; - const subgraphValues = useQueries({ - queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ - queryKey: ['subgraph', chain.id, address], - queryFn: async () => { - return await fetchSubgraphData(chain.id, address); - }, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - })), - }); + const subgraphValues = useFetchSubgraphDataForAllChains(); useEffect(() => { async function fetchTotalGIVpower() { diff --git a/src/components/views/claim/cards/Govern.tsx b/src/components/views/claim/cards/Govern.tsx index 7fb9ebabc4..78d3f715c0 100644 --- a/src/components/views/claim/cards/Govern.tsx +++ b/src/components/views/claim/cards/Govern.tsx @@ -7,7 +7,6 @@ import { captureException } from '@sentry/nextjs'; import { useIntl } from 'react-intl'; import { formatEther } from 'viem'; import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import { APRRow, ArrowButton, @@ -35,7 +34,7 @@ import useGIVTokenDistroHelper from '@/hooks/useGIVTokenDistroHelper'; import { IClaimViewCardProps } from '../Claim.view'; import { WeiPerEther } from '@/lib/constants/constants'; import { InputWithUnit } from '@/components/input/InputWithUnit'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; const GovernCardContainer = styled(Card)` padding-left: 254px; @@ -109,15 +108,9 @@ const GovernCard: FC = ({ index }) => { const [earnEstimate, setEarnEstimate] = useState(0n); const [apr, setApr] = useState(null); - const { address, chain } = useAccount(); - const chainId = chain?.id; + const { chainId } = useAccount(); const { givTokenDistroHelper } = useGIVTokenDistroHelper(); - const gnosisValues = useQuery({ - queryKey: ['subgraph', config.GNOSIS_NETWORK_NUMBER, address], - queryFn: async () => - await fetchSubgraphData(config.GNOSIS_NETWORK_NUMBER, address), - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const gnosisValues = useSubgraphInfo(config.GNOSIS_NETWORK_NUMBER); useEffect(() => { let _stacked = 0; diff --git a/src/components/views/claim/cards/Stake.tsx b/src/components/views/claim/cards/Stake.tsx index 22588e8738..def9267a08 100644 --- a/src/components/views/claim/cards/Stake.tsx +++ b/src/components/views/claim/cards/Stake.tsx @@ -4,8 +4,6 @@ import styled from 'styled-components'; import BigNumber from 'bignumber.js'; import { H2, H5, Lead, Flex } from '@giveth/ui-design-system'; import { useIntl } from 'react-intl'; -import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import { APRRow, ArrowButton, @@ -34,7 +32,7 @@ import { getNowUnixMS } from '@/helpers/time'; import { IClaimViewCardProps } from '../Claim.view'; import { WeiPerEther } from '@/lib/constants/constants'; import { InputWithUnit } from '@/components/input/InputWithUnit'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useSubgraphInfo } from '@/hooks/useSubgraphInfo'; const InvestCardContainer = styled(Card)` &::before { @@ -95,21 +93,9 @@ const InvestCard: FC = ({ index }) => { const [earnEstimate, setEarnEstimate] = useState(0n); const [APR, setAPR] = useState(Zero); - const { address } = useAccount(); const { givTokenDistroHelper } = useGIVTokenDistroHelper(); - const gnosisValues = useQuery({ - queryKey: ['subgraph', config.GNOSIS_NETWORK_NUMBER, address], - queryFn: async () => - await fetchSubgraphData(config.GNOSIS_NETWORK_NUMBER, address), - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); - - const mainnetValues = useQuery({ - queryKey: ['subgraph', config.MAINNET_NETWORK_NUMBER, address], - queryFn: async () => - await fetchSubgraphData(config.MAINNET_NETWORK_NUMBER, address), - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const gnosisValues = useSubgraphInfo(config.GNOSIS_NETWORK_NUMBER); + const mainnetValues = useSubgraphInfo(config.MAINNET_NETWORK_NUMBER); useEffect(() => { if (totalAmount) { diff --git a/src/components/views/userProfile/ProfileOverviewTab.tsx b/src/components/views/userProfile/ProfileOverviewTab.tsx index b6c4e7fb1f..dbd42d7624 100644 --- a/src/components/views/userProfile/ProfileOverviewTab.tsx +++ b/src/components/views/userProfile/ProfileOverviewTab.tsx @@ -17,7 +17,6 @@ import { import { useIntl } from 'react-intl'; import { useAccount } from 'wagmi'; -import { useQueries } from '@tanstack/react-query'; import Routes from '@/lib/constants/Routes'; import { isUserRegistered } from '@/lib/helpers'; import { mediaQueries } from '@/lib/constants/constants'; @@ -36,9 +35,8 @@ import { useProfileContext } from '@/context/profile.context'; import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; import { useGeneralWallet } from '@/providers/generalWalletProvider'; import { QFDonorEligibilityCard } from '@/components/views/userProfile/QFDonorEligibilityCard'; -import config from '@/configuration'; -import { fetchSubgraphData } from '@/services/subgraph.service'; import { getNowUnixMS } from '@/helpers/time'; +import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataForAllChains'; interface IBtnProps extends IButtonProps { outline?: boolean; @@ -115,15 +113,7 @@ const ProfileOverviewTab: FC = () => { const { activeQFRound } = useAppSelector(state => state.general); const boostedProjectsCount = userData?.boostedProjectsCount ?? 0; const { address } = useAccount(); - const subgraphValues = useQueries({ - queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ - queryKey: ['subgraph', chain.id, address], - queryFn: async () => { - return await fetchSubgraphData(chain.id, address); - }, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - })), - }); + const subgraphValues = useFetchSubgraphDataForAllChains(); const givPower = getTotalGIVpower(subgraphValues, address); const { title, subtitle, buttons } = section; diff --git a/src/components/views/userProfile/boostedTab/EmptyPowerBoosting.tsx b/src/components/views/userProfile/boostedTab/EmptyPowerBoosting.tsx index 62c6266154..37f99a4e85 100644 --- a/src/components/views/userProfile/boostedTab/EmptyPowerBoosting.tsx +++ b/src/components/views/userProfile/boostedTab/EmptyPowerBoosting.tsx @@ -9,26 +9,16 @@ import { useIntl } from 'react-intl'; import Link from 'next/link'; import { FC } from 'react'; import { useAccount } from 'wagmi'; -import { useQueries } from '@tanstack/react-query'; import Routes from '@/lib/constants/Routes'; import { getTotalGIVpower } from '@/helpers/givpower'; -import config from '@/configuration'; -import { fetchSubgraphData } from '@/services/subgraph.service'; +import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataForAllChains'; interface IEmptyPowerBoosting { myAccount?: boolean; } export const EmptyPowerBoosting: FC = ({ myAccount }) => { const { address } = useAccount(); - const subgraphValues = useQueries({ - queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ - queryKey: ['subgraph', chain.id, address], - queryFn: async () => { - return await fetchSubgraphData(chain.id, address); - }, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - })), - }); + const subgraphValues = useFetchSubgraphDataForAllChains(); const givPower = getTotalGIVpower(subgraphValues, address); const { formatMessage } = useIntl(); diff --git a/src/components/views/userProfile/boostedTab/ProfileBoostedTab.tsx b/src/components/views/userProfile/boostedTab/ProfileBoostedTab.tsx index 35b5d0cdea..ae78aa4e7c 100644 --- a/src/components/views/userProfile/boostedTab/ProfileBoostedTab.tsx +++ b/src/components/views/userProfile/boostedTab/ProfileBoostedTab.tsx @@ -4,7 +4,6 @@ import { captureException } from '@sentry/nextjs'; import { Col, Row } from '@giveth/ui-design-system'; import { useAccount } from 'wagmi'; -import { useQueries } from '@tanstack/react-query'; import { IUserProfileView } from '../UserProfile.view'; import BoostsTable from './BoostsTable'; import { IPowerBoosting } from '@/apollo/types/types'; @@ -29,8 +28,7 @@ import { formatWeiHelper } from '@/helpers/number'; import InlineToast, { EToastType } from '@/components/toasts/InlineToast'; import { useFetchPowerBoostingInfo } from './useFetchPowerBoostingInfo'; import { useProfileContext } from '@/context/profile.context'; -import { fetchSubgraphData } from '@/services/subgraph.service'; -import config from '@/configuration'; +import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataForAllChains'; export const ProfileBoostedTab: FC = () => { const { user } = useProfileContext(); @@ -39,15 +37,7 @@ export const ProfileBoostedTab: FC = () => { const { address, chain } = useAccount(); const { userData } = useAppSelector(state => state.user); const boostedProjectsCount = userData?.boostedProjectsCount ?? 0; - const subgraphValues = useQueries({ - queries: config.CHAINS_WITH_SUBGRAPH.map(chain => ({ - queryKey: ['subgraph', chain.id, address], - queryFn: async () => { - return await fetchSubgraphData(chain.id, address); - }, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - })), - }); + const subgraphValues = useFetchSubgraphDataForAllChains(); const givPower = getTotalGIVpower(subgraphValues, address); const isZeroGivPower = givPower.total.isZero(); const dispatch = useAppDispatch(); diff --git a/src/hooks/useGIVTokenDistroHelper.ts b/src/hooks/useGIVTokenDistroHelper.ts index 32f367eddd..a61acd7525 100644 --- a/src/hooks/useGIVTokenDistroHelper.ts +++ b/src/hooks/useGIVTokenDistroHelper.ts @@ -1,11 +1,8 @@ import { useState, useEffect } from 'react'; import { AddressZero } from '@ethersproject/constants'; -import { useQuery } from '@tanstack/react-query'; -import { useAccount } from 'wagmi'; import { TokenDistroHelper } from '@/lib/contractHelper/TokenDistroHelper'; import { SubgraphDataHelper } from '@/lib/subgraph/subgraphDataHelper'; -import { fetchSubgraphData } from '@/services/subgraph.service'; -import config from '@/configuration'; +import { useSubgraphInfo } from './useSubgraphInfo'; export const defaultTokenDistroHelper = new TokenDistroHelper({ contractAddress: AddressZero, @@ -23,13 +20,7 @@ const useGIVTokenDistroHelper = (hold = false) => { const [givTokenDistroHelper, setGIVTokenDistroHelper] = useState(defaultTokenDistroHelper); const [isLoaded, setIsLoaded] = useState(false); - const { chain, address } = useAccount(); - const currentValues = useQuery({ - queryKey: ['subgraph', chain?.id, address], - queryFn: async () => await fetchSubgraphData(chain?.id, address), - enabled: !hold, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const currentValues = useSubgraphInfo(); useEffect(() => { const updateHelper = () => { diff --git a/src/hooks/useStakingPool.ts b/src/hooks/useStakingPool.ts index 4f7737c74a..e93cf16556 100644 --- a/src/hooks/useStakingPool.ts +++ b/src/hooks/useStakingPool.ts @@ -1,7 +1,5 @@ import { useEffect, useState } from 'react'; -import { useQuery } from '@tanstack/react-query'; -import { useAccount } from 'wagmi'; import { getGivStakingAPR, getLPStakingAPR, @@ -10,8 +8,7 @@ import { import { SimplePoolStakingConfig, StakingType } from '@/types/config'; import { APR, UserStakeInfo } from '@/types/poolInfo'; import { Zero } from '@/helpers/number'; -import { fetchSubgraphData } from '@/services/subgraph.service'; -import config from '@/configuration'; +import { useSubgraphInfo } from './useSubgraphInfo'; export interface IStakeInfo { apr: APR; @@ -30,14 +27,7 @@ export const useStakingPool = ( notStakedAmount: 0n, stakedAmount: 0n, }); - const { address } = useAccount(); - const currentValues = useQuery({ - queryKey: ['subgraph', poolStakingConfig.network, address], - queryFn: async () => - await fetchSubgraphData(poolStakingConfig.network, address), - enabled: !hold, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const currentValues = useSubgraphInfo(poolStakingConfig.network); useEffect(() => { const { network, type } = poolStakingConfig; diff --git a/src/hooks/useTokenDistroHelper.ts b/src/hooks/useTokenDistroHelper.ts index 1ad69f15ab..3f786a2897 100644 --- a/src/hooks/useTokenDistroHelper.ts +++ b/src/hooks/useTokenDistroHelper.ts @@ -1,11 +1,8 @@ import { useState, useEffect, useMemo } from 'react'; -import { useAccount } from 'wagmi'; -import { useQuery } from '@tanstack/react-query'; import { TokenDistroHelper } from '@/lib/contractHelper/TokenDistroHelper'; import { SubgraphDataHelper } from '@/lib/subgraph/subgraphDataHelper'; import { RegenStreamConfig } from '@/types/config'; -import { fetchSubgraphData } from '@/services/subgraph.service'; -import config from '@/configuration'; +import { useSubgraphInfo } from './useSubgraphInfo'; export const useTokenDistroHelper = ( poolNetwork: number, @@ -14,13 +11,7 @@ export const useTokenDistroHelper = ( ) => { const [tokenDistroHelper, setTokenDistroHelper] = useState(); - const { address } = useAccount(); - const currentValues = useQuery({ - queryKey: ['subgraph', poolNetwork, address], - queryFn: async () => await fetchSubgraphData(poolNetwork, address), - enabled: !hold, - staleTime: config.SUBGRAPH_POLLING_INTERVAL, - }); + const currentValues = useSubgraphInfo(poolNetwork); const sdh = useMemo( () => new SubgraphDataHelper(currentValues.data), [currentValues.data], From f8c556e241b8e5cd83aad3b5ecfa75284c614e6a Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 14:09:42 +0330 Subject: [PATCH 56/97] remove unused chain id --- src/components/controller/subgraph.ctrl.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/controller/subgraph.ctrl.tsx b/src/components/controller/subgraph.ctrl.tsx index f69f0098da..991f98597d 100644 --- a/src/components/controller/subgraph.ctrl.tsx +++ b/src/components/controller/subgraph.ctrl.tsx @@ -7,12 +7,12 @@ import { useFetchSubgraphDataForAllChains } from '@/hooks/useFetchSubgraphDataFo import { useInteractedBlockNumber } from '@/hooks/useInteractedBlockNumber'; const SubgraphController: React.FC = () => { - const { address, chain } = useAccount(); + const { address } = useAccount(); const queryClient = useQueryClient(); const pollingTimeoutsRef = useRef<{ [key: number]: NodeJS.Timeout }>({}); const refetchedChainsRef = useRef>(new Set()); useFetchSubgraphDataForAllChains(); - useInteractedBlockNumber(chain?.id); + useInteractedBlockNumber(); useEffect(() => { const handleEvent = ( From d1f2f53b5981aa3d3567e3a1001795c4b8e4960e Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 14:16:26 +0330 Subject: [PATCH 57/97] update chain id --- src/components/GIVeconomyPages/GIVstream.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/GIVeconomyPages/GIVstream.tsx b/src/components/GIVeconomyPages/GIVstream.tsx index cf1047a615..1d80656cbf 100644 --- a/src/components/GIVeconomyPages/GIVstream.tsx +++ b/src/components/GIVeconomyPages/GIVstream.tsx @@ -71,10 +71,8 @@ export const TabGIVstreamTop = () => { const [rewardLiquidPart, setRewardLiquidPart] = useState(0n); const [rewardStream, setRewardStream] = useState(0n); const { givTokenDistroHelper } = useGIVTokenDistroHelper(showModal); - const { chain } = useAccount(); + const { chainId } = useAccount(); const currentValues = useSubgraphInfo(); - - const chainId = chain?.id; const sdh = new SubgraphDataHelper(currentValues.data); const { allocatedTokens, claimed, givback } = sdh.getGIVTokenDistroBalance(); @@ -164,7 +162,7 @@ export const TabGIVstreamTop = () => { }; export const TabGIVstreamBottom = () => { - const { chain, address } = useAccount(); + const { chainId } = useAccount(); const { givTokenDistroHelper } = useGIVTokenDistroHelper(); const { formatMessage } = useIntl(); @@ -174,7 +172,6 @@ export const TabGIVstreamBottom = () => { const [streamAmount, setStreamAmount] = useState(0n); const currentValues = useSubgraphInfo(); - const chainId = chain?.id; const sdh = new SubgraphDataHelper(currentValues.data); const givTokenDistroBalance = sdh.getGIVTokenDistroBalance(); const increaseSecRef = useRef(null); From ab4262f0259f755c870b82e71124a3bfb61724a0 Mon Sep 17 00:00:00 2001 From: Cherik Date: Mon, 21 Oct 2024 14:19:53 +0330 Subject: [PATCH 58/97] disable reward card action on not sync --- src/components/RewardCard.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/RewardCard.tsx b/src/components/RewardCard.tsx index 45f7a228af..938ac801e6 100644 --- a/src/components/RewardCard.tsx +++ b/src/components/RewardCard.tsx @@ -25,6 +25,7 @@ import { INetworkIdWithChain } from './views/donate/common/common.types'; import { ChainType } from '@/types/config'; import { EVMWrongNetworkSwitchModal } from './modals/WrongNetworkInnerModal'; import { useFetchGIVPrice } from '@/hooks/useGivPrice'; +import { useSubgraphSyncInfo } from '@/hooks/useSubgraphSyncInfo'; interface IRewardCardProps { cardName: string; @@ -63,6 +64,7 @@ export const RewardCard: FC = ({ useState(false); const { data: givPrice } = useFetchGIVPrice(); const { givTokenDistroHelper } = useGIVTokenDistroHelper(); + const subgraphSyncedInfo = useSubgraphSyncInfo(network); useEffect(() => { const price = @@ -132,7 +134,10 @@ export const RewardCard: FC = ({ label={actionLabel} onClick={actionCb} buttonType='primary' - disabled={liquidAmount === 0n} + disabled={ + liquidAmount === 0n || + !subgraphSyncedInfo.isSynced + } /> ) : ( From ec3fa78f9fe0a92740bceea9c62876a98172b802 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 21 Oct 2024 14:45:42 +0200 Subject: [PATCH 59/97] moved headers to new item of the array --- next.config.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/next.config.js b/next.config.js index 115c0884dc..00bc518a80 100644 --- a/next.config.js +++ b/next.config.js @@ -150,6 +150,20 @@ const moduleExports = withBundleAnalyzer({ }, headers: async () => { return [ + { + source: '/:path*', + locale: false, + headers: [ + { + key: 'X-Frame-Options', + value: 'SAMEORIGIN', + }, + { + key: 'Content-Security-Policy', + value: "frame-ancestors 'self'", + }, + ], + }, { // Adding CORS headers for /manifest.json source: '/manifest.json', @@ -164,14 +178,6 @@ const moduleExports = withBundleAnalyzer({ key: 'Access-Control-Allow-Headers', value: 'X-Requested-With, content-type, Authorization', }, - { - key: 'X-Frame-Options', - value: 'SAMEORIGIN', - }, - { - key: 'Content-Security-Policy', - value: "frame-ancestors 'self'", - }, ], }, ]; From 85aa303bcc06d02569e7fde3dc03dafea3fb6d8f Mon Sep 17 00:00:00 2001 From: maryjaf Date: Tue, 22 Oct 2024 18:10:16 +0330 Subject: [PATCH 60/97] add sticky style add sticky style for passport banner --- src/components/PassportBanner.tsx | 276 +++++++++++++++--------------- 1 file changed, 136 insertions(+), 140 deletions(-) diff --git a/src/components/PassportBanner.tsx b/src/components/PassportBanner.tsx index e818aa5448..5cd2d684f0 100644 --- a/src/components/PassportBanner.tsx +++ b/src/components/PassportBanner.tsx @@ -111,171 +111,167 @@ export const PassportBannerData: IData = { icon: , }, }; - export const PassportBanner = () => { - const { info, updateState, fetchUserMBDScore, handleSign, refreshScore } = - usePassport(); - const { currentRound, passportState, passportScore, qfEligibilityState } = - info; - + const { info, updateState, fetchUserMBDScore, handleSign, refreshScore } = usePassport(); + const { currentRound, passportState, passportScore, qfEligibilityState } = info; const { formatMessage, locale } = useIntl(); const { connector } = useAccount(); const { isOnSolana, handleSingOutAndSignInWithEVM } = useGeneralWallet(); const [showModal, setShowModal] = useState(false); const [signWithWallet, setSignWithWallet] = useState(false); - + const isGSafeConnector = connector?.id === 'safe'; - + + // Check if the eligibility state or current round is not loaded yet + const isLoading = !qfEligibilityState || !currentRound; + + // Only render the banner when the data is available + if (isLoading) { + return null; // Or return a spinner or loading message if you'd like + } + return !isOnSolana ? ( - <> - - - - {PassportBannerData[qfEligibilityState].icon} - -

- {formatMessage( - { - id: PassportBannerData[qfEligibilityState] - .content, - }, - { - data: - qfEligibilityState === - EQFElegibilityState.NOT_STARTED && - currentRound - ? smallFormatDate( - new Date( - currentRound?.beginDate, - ), - ) - : undefined, - }, - )} - {currentRound && - qfEligibilityState === - EQFElegibilityState.RECHECK_ELIGIBILITY && ( - <> - {' '} - - {new Date(currentRound.endDate) - .toLocaleString(locale || 'en-US', { - day: 'numeric', - month: 'short', - }) - .replace(/,/g, '')} - - - )} -

-
- {qfEligibilityState === - EQFElegibilityState.CHECK_ELIGIBILITY && ( - fetchUserMBDScore()}> - - {formatMessage({ - id: 'qf_donor_eligibility.banner.link.check_eligibility', - })} - - - )} - {qfEligibilityState === - EQFElegibilityState.RECHECK_ELIGIBILITY && ( - setShowModal(true)}> - - {formatMessage({ - id: 'qf_donor_eligibility.banner.link.recheck_eligibility', - })} - - - )} - {qfEligibilityState === EQFElegibilityState.PROCESSING && ( - - {formatMessage({ - id: 'label.processing', - })} - - - )} - {qfEligibilityState === - EQFElegibilityState.MORE_INFO_NEEDED && ( - setShowModal(true)}> - - {formatMessage({ - id: 'label.add_more_info', - })} - - - )} - {qfEligibilityState === EQFElegibilityState.NOT_SIGNED && ( - setSignWithWallet(true)}> - - {formatMessage({ - id: 'label.sign_message', - })} - - - - )} -
- {showModal && ( - - )} - {signWithWallet && ( - { - setSignWithWallet(false); - }} - /> - )} - - ) : ( + <> + + + {PassportBannerData[qfEligibilityState].icon} +

+ {formatMessage( + { + id: PassportBannerData[qfEligibilityState].content, + }, + { + data: + qfEligibilityState === EQFElegibilityState.NOT_STARTED && + currentRound + ? smallFormatDate(new Date(currentRound?.beginDate)) + : undefined, + } + )} + {currentRound && qfEligibilityState === EQFElegibilityState.RECHECK_ELIGIBILITY && ( + <> + {' '} + + {new Date(currentRound.endDate) + .toLocaleString(locale || 'en-US', { + day: 'numeric', + month: 'short', + }) + .replace(/,/g, '')} + + + )} +

+
+ {qfEligibilityState === EQFElegibilityState.CHECK_ELIGIBILITY && ( + fetchUserMBDScore()}> + {formatMessage({ - id: 'label.to_activate_your_gitcoin_passport', + id: 'qf_donor_eligibility.banner.link.check_eligibility', })} -

- +
+
+ )} + {qfEligibilityState === EQFElegibilityState.RECHECK_ELIGIBILITY && ( + setShowModal(true)}> + {formatMessage({ - id: 'label.switch_to_evm', + id: 'qf_donor_eligibility.banner.link.recheck_eligibility', })} - + + + )} + {qfEligibilityState === EQFElegibilityState.PROCESSING && ( + + {formatMessage({ + id: 'label.processing', + })} + + + )} + {qfEligibilityState === EQFElegibilityState.MORE_INFO_NEEDED && ( + setShowModal(true)}> + + {formatMessage({ + id: 'label.add_more_info', + })} + + + )} + {qfEligibilityState === (EQFElegibilityState as any).NOT_SIGNED && ( + setSignWithWallet(true)}> + + {formatMessage({ + id: 'label.sign_message', + })} + + + + )}
+ {showModal && ( + + )} + {signWithWallet && ( + { + setSignWithWallet(false); + }} + /> + )} + + ) : ( + +

+ {formatMessage({ + id: 'label.to_activate_your_gitcoin_passport', + })} +

+ + {formatMessage({ + id: 'label.switch_to_evm', + })} + +
); -}; + }; + + interface IPassportBannerWrapperProps { $bgColor: EPBGState; } export const PassportBannerWrapper = styled(Flex)` - flex-direction: column; - background-color: ${props => bgColor[props.$bgColor]}; - padding: 16px; - align-items: center; - justify-content: center; - gap: 8px; - position: relative; - ${mediaQueries.tablet} { - flex-direction: row; - } + flex-direction: column; + background-color: ${props => bgColor[props.$bgColor]}; + padding: 16px; + align-items: center; + justify-content: center; + gap: 8px; + position: sticky; /* Change this to sticky */ + top: 0; /* This keeps it at the top as the user scrolls */ + z-index: 1000; /* Ensure it stays above other content */ + ${mediaQueries.tablet} { + flex-direction: row; + } `; + const IconWrapper = styled.div` width: 30px; `; From 9378cfe2a433e2db04519b102e4399d16f7d1d4c Mon Sep 17 00:00:00 2001 From: maryjaf Date: Wed, 23 Oct 2024 07:59:51 +0330 Subject: [PATCH 61/97] add prettier fixes add prettier fixes in passportBanner.tsx --- src/components/PassportBanner.tsx | 275 ++++++++++++++++-------------- 1 file changed, 144 insertions(+), 131 deletions(-) diff --git a/src/components/PassportBanner.tsx b/src/components/PassportBanner.tsx index 5cd2d684f0..70bdbe7037 100644 --- a/src/components/PassportBanner.tsx +++ b/src/components/PassportBanner.tsx @@ -112,166 +112,179 @@ export const PassportBannerData: IData = { }, }; export const PassportBanner = () => { - const { info, updateState, fetchUserMBDScore, handleSign, refreshScore } = usePassport(); - const { currentRound, passportState, passportScore, qfEligibilityState } = info; + const { info, updateState, fetchUserMBDScore, handleSign, refreshScore } = + usePassport(); + const { currentRound, passportState, passportScore, qfEligibilityState } = + info; const { formatMessage, locale } = useIntl(); const { connector } = useAccount(); const { isOnSolana, handleSingOutAndSignInWithEVM } = useGeneralWallet(); const [showModal, setShowModal] = useState(false); const [signWithWallet, setSignWithWallet] = useState(false); - + const isGSafeConnector = connector?.id === 'safe'; - + // Check if the eligibility state or current round is not loaded yet const isLoading = !qfEligibilityState || !currentRound; - + // Only render the banner when the data is available if (isLoading) { - return null; // Or return a spinner or loading message if you'd like + return null; // Or return a spinner or loading message if you'd like } - + return !isOnSolana ? ( - <> + <> + + + + {PassportBannerData[qfEligibilityState].icon} + +

+ {formatMessage( + { + id: PassportBannerData[qfEligibilityState] + .content, + }, + { + data: + qfEligibilityState === + EQFElegibilityState.NOT_STARTED && + currentRound + ? smallFormatDate( + new Date( + currentRound?.beginDate, + ), + ) + : undefined, + }, + )} + {currentRound && + qfEligibilityState === + EQFElegibilityState.RECHECK_ELIGIBILITY && ( + <> + {' '} + + {new Date(currentRound.endDate) + .toLocaleString(locale || 'en-US', { + day: 'numeric', + month: 'short', + }) + .replace(/,/g, '')} + + + )} +

+
+ {qfEligibilityState === + EQFElegibilityState.CHECK_ELIGIBILITY && ( + fetchUserMBDScore()}> + + {formatMessage({ + id: 'qf_donor_eligibility.banner.link.check_eligibility', + })} + + + )} + {qfEligibilityState === + EQFElegibilityState.RECHECK_ELIGIBILITY && ( + setShowModal(true)}> + + {formatMessage({ + id: 'qf_donor_eligibility.banner.link.recheck_eligibility', + })} + + + )} + {qfEligibilityState === EQFElegibilityState.PROCESSING && ( + + {formatMessage({ + id: 'label.processing', + })} + + + )} + {qfEligibilityState === + EQFElegibilityState.MORE_INFO_NEEDED && ( + setShowModal(true)}> + + {formatMessage({ + id: 'label.add_more_info', + })} + + + )} + {qfEligibilityState === + (EQFElegibilityState as any).NOT_SIGNED && ( + setSignWithWallet(true)}> + + {formatMessage({ + id: 'label.sign_message', + })} + + + + )} +
+ {showModal && ( + + )} + {signWithWallet && ( + { + setSignWithWallet(false); + }} + /> + )} + + ) : ( - - - {PassportBannerData[qfEligibilityState].icon} -

- {formatMessage( - { - id: PassportBannerData[qfEligibilityState].content, - }, - { - data: - qfEligibilityState === EQFElegibilityState.NOT_STARTED && - currentRound - ? smallFormatDate(new Date(currentRound?.beginDate)) - : undefined, - } - )} - {currentRound && qfEligibilityState === EQFElegibilityState.RECHECK_ELIGIBILITY && ( - <> - {' '} - - {new Date(currentRound.endDate) - .toLocaleString(locale || 'en-US', { - day: 'numeric', - month: 'short', - }) - .replace(/,/g, '')} - - - )} -

-
- {qfEligibilityState === EQFElegibilityState.CHECK_ELIGIBILITY && ( - fetchUserMBDScore()}> - - {formatMessage({ - id: 'qf_donor_eligibility.banner.link.check_eligibility', - })} - - - )} - {qfEligibilityState === EQFElegibilityState.RECHECK_ELIGIBILITY && ( - setShowModal(true)}> - {formatMessage({ - id: 'qf_donor_eligibility.banner.link.recheck_eligibility', + id: 'label.to_activate_your_gitcoin_passport', })} - - - )} - {qfEligibilityState === EQFElegibilityState.PROCESSING && ( - - {formatMessage({ - id: 'label.processing', - })} - - - )} - {qfEligibilityState === EQFElegibilityState.MORE_INFO_NEEDED && ( - setShowModal(true)}> - - {formatMessage({ - id: 'label.add_more_info', - })} - - - )} - {qfEligibilityState === (EQFElegibilityState as any).NOT_SIGNED && ( - setSignWithWallet(true)}> - +

+ {formatMessage({ - id: 'label.sign_message', + id: 'label.switch_to_evm', })} -
- -
- )} +
- {showModal && ( - - )} - {signWithWallet && ( - { - setSignWithWallet(false); - }} - /> - )} - - ) : ( - -

- {formatMessage({ - id: 'label.to_activate_your_gitcoin_passport', - })} -

- - {formatMessage({ - id: 'label.switch_to_evm', - })} - -
); - }; - - +}; interface IPassportBannerWrapperProps { $bgColor: EPBGState; } export const PassportBannerWrapper = styled(Flex)` - flex-direction: column; - background-color: ${props => bgColor[props.$bgColor]}; - padding: 16px; - align-items: center; - justify-content: center; - gap: 8px; - position: sticky; /* Change this to sticky */ - top: 0; /* This keeps it at the top as the user scrolls */ - z-index: 1000; /* Ensure it stays above other content */ - ${mediaQueries.tablet} { - flex-direction: row; - } + flex-direction: column; + background-color: ${props => bgColor[props.$bgColor]}; + padding: 16px; + align-items: center; + justify-content: center; + gap: 8px; + position: sticky; /* Change this to sticky */ + top: 0; /* This keeps it at the top as the user scrolls */ + z-index: 1000; /* Ensure it stays above other content */ + ${mediaQueries.tablet} { + flex-direction: row; + } `; - const IconWrapper = styled.div` width: 30px; `; From 6aa1880cc8ec984533b3dd44bd5de01e9eeadf2d Mon Sep 17 00:00:00 2001 From: maryjaf Date: Wed, 23 Oct 2024 13:42:05 +0330 Subject: [PATCH 62/97] edit z-index --- src/components/PassportBanner.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PassportBanner.tsx b/src/components/PassportBanner.tsx index 70bdbe7037..6cb7c7db5e 100644 --- a/src/components/PassportBanner.tsx +++ b/src/components/PassportBanner.tsx @@ -279,7 +279,7 @@ export const PassportBannerWrapper = styled(Flex)` gap: 8px; position: sticky; /* Change this to sticky */ top: 0; /* This keeps it at the top as the user scrolls */ - z-index: 1000; /* Ensure it stays above other content */ + z-index: 5; /* Ensure it stays above other content */ ${mediaQueries.tablet} { flex-direction: row; } From 9f67e61077cefeed013a4ed624521287ecda52b3 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Wed, 23 Oct 2024 22:04:58 +0200 Subject: [PATCH 63/97] removed stake together --- src/components/views/givfarm/GIVfarmBottom.tsx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/components/views/givfarm/GIVfarmBottom.tsx b/src/components/views/givfarm/GIVfarmBottom.tsx index 50dce7f30f..b2432c8251 100644 --- a/src/components/views/givfarm/GIVfarmBottom.tsx +++ b/src/components/views/givfarm/GIVfarmBottom.tsx @@ -32,7 +32,6 @@ import StakingPoolCard from '@/components/cards/StakingCards/BaseStakingCard/Bas import { RegenStreamSection } from '@/components/givfarm/RegenStreamSection'; import ToggleSwitch from '@/components/ToggleSwitch'; import { getNetworkConfig } from '@/helpers/givpower'; -import { StakeTogetherCard } from '@/components/cards/StakingCards/BaseStakingCard/StakeTogetherCard'; const renderPool = (pool: SimplePoolStakingConfig, id: number) => ( @@ -189,17 +188,7 @@ export const GIVfarmBottom = () => { toggleOnOff={setShowArchivedPools} /> - - {renderPools(chainId, showArchivedPools)} - - - - + {renderPools(chainId, showArchivedPools)} From e4698ad4a3108428a36c882a39fa8f2e9cd08355 Mon Sep 17 00:00:00 2001 From: Lovel George <36924734+lovelgeorge99@users.noreply.github.com> Date: Sat, 26 Oct 2024 04:37:21 +0530 Subject: [PATCH 64/97] added footer links to qacc --- lang/ca.json | 2 ++ lang/en.json | 2 ++ lang/es.json | 2 ++ next-env.d.ts | 2 +- src/components/Footer/Footer.tsx | 18 +++++++++++++++++- src/lib/constants/links.ts | 2 ++ 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lang/ca.json b/lang/ca.json index 7008153fa8..985cfd381e 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -98,6 +98,8 @@ "component.title.user_guides": "Guies d'Usuari", "component.title.we_hiring": "Estem contractant!", "component.title.what_is_giveth": "Què és Giveth?", + "component.title.qacc": "Q/acc", + "component.title.qacc_news": "Q/acc News", "component.title.with_your_donation": "amb la teva donació", "component.tooltip.donate_anonymously": "En marcar això, no considerarem la informació del teu perfil com a donant per a aquesta donació i no la mostrarem a les pàgines públiques.", "component.tooltip.donate_privately": "Si marques això, amagarem el teu nom i l'enllaç de la transacció de donació de la pàgina del projecte, i no mostrarem aquesta donació a la teva pàgina de perfil públic.", diff --git a/lang/en.json b/lang/en.json index eb8ac421ea..2ddfd1f966 100644 --- a/lang/en.json +++ b/lang/en.json @@ -97,6 +97,8 @@ "component.title.tos": "Terms of Use", "component.title.user_guides": "User Guides", "component.title.we_hiring": "We're Hiring!", + "component.title.qacc": "Q/acc", + "component.title.qacc_news": "Q/acc News", "component.title.what_is_giveth": "What is Giveth?", "component.title.with_your_donation": "with your Donation", "component.tooltip.donate_anonymously": "By checking this, we won't consider your profile information as a donor for this donation and won't show it on public pages.", diff --git a/lang/es.json b/lang/es.json index 9b6c0c3095..abb0ae51a7 100644 --- a/lang/es.json +++ b/lang/es.json @@ -706,6 +706,8 @@ "label.on": "en", "label.onboarding_guide": "Guía de incorporación", "label.one_million_giv": "1 Millón de GIV", + "component.title.qacc": "Q/acc", + "component.title.qacc_news": "Q/acc News", "label.one_round": "1 Ronda", "label.one_time_donation": "Donación Única", "label.on_gnosis_and": "en el red de Gnosis Chain y además, ¡el 80% de las tarifas de registro van al 'Matching Pool' de Giveth!", diff --git a/next-env.d.ts b/next-env.d.ts index a4a7b3f5cf..4f11a03dc6 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 7cbe06a31c..7da33574dd 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -142,6 +142,22 @@ const Footer = () => { + + + + {formatMessage({ + id: 'component.title.qacc', + })} + + + + + {formatMessage({ + id: 'component.title.qacc_news', + })} + + + @@ -225,7 +241,7 @@ const LeftContainer = styled(Flex)` gap: 0; ${mediaQueries.laptopL} { - gap: 0 72px; + gap: 0 0px; } `; diff --git a/src/lib/constants/links.ts b/src/lib/constants/links.ts index 03e55171ee..f1c6413628 100644 --- a/src/lib/constants/links.ts +++ b/src/lib/constants/links.ts @@ -56,6 +56,8 @@ const links = { SWAG: 'https://swag.giveth.io/', COMMONS_STACK: 'https://commonsstack.org/', RECRUITEE: 'https://giveth.recruitee.com/', + QACC: 'https://qacc.giveth.io/', + QACC_NEWS: 'https://qacc.giveth.io/news', JOINGIVFRENS: TYPEFORM + 'regenfarms', DISCORD_SUPPORT: 'https://discord.gg/TeWHtAjNQT', CALENDAR: From a2e672517f2fb9d37f4ebaf723a3c0f8c6be5eb9 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Fri, 8 Nov 2024 12:06:43 +0100 Subject: [PATCH 65/97] Feat/Allow project boost if project is vouched --- lang/ca.json | 1 + lang/en.json | 1 + lang/es.json | 1 + .../views/project/ProjectGIVbackToast.tsx | 25 ++++- .../projectActionCard/AdminActions.tsx | 103 ++++++++++-------- .../ProjectCardNotification.tsx | 45 ++++++++ 6 files changed, 128 insertions(+), 48 deletions(-) create mode 100644 src/components/views/project/projectActionCard/ProjectCardNotification.tsx diff --git a/lang/ca.json b/lang/ca.json index 7023c9de42..e0184160d2 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -1678,6 +1678,7 @@ "project.givback_toast.title.verified_public_2": " del valor de la teva donació!", "project.givback_toast.title.verified_public_3": "Rep recompenses de fins a {percent}%", "project.givback_toast.title.verified_public_not_eligible": "Impulsa aquest projecte amb GIVpower!", + "project.givback_toast.complete_eligibility": "Completa el teu formulari d’elegibilitat per als GIVbacks", "projects_all": "Tots els Projectes", "projects_all_desc": "SUPORT A PROJECTES GLOBALS DE BÉS PÚBLICS, SOSTENIBILITAT I REGENERACIÓ AMB CRYPTODONACIONS", "projects_art-and-culture": "Art i Cultura", diff --git a/lang/en.json b/lang/en.json index 552db6df57..f9e629f0f7 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1679,6 +1679,7 @@ "project.givback_toast.title.verified_public_2": " of your donation value!", "project.givback_toast.title.verified_public_3": "Get rewarded with up to {percent}%", "project.givback_toast.title.verified_public_not_eligible": "Boost this project with GIVpower!", + "project.givback_toast.complete_eligibility": "Complete your GIVbacks Eligibility form", "projects_all": "All Projects", "projects_all_desc": "SUPPORT GLOBAL PROJECTS IN PUBLIC GOODS, SUSTAINABILITY, AND REGENERATION WITH CRYPTO DONATIONS", "projects_art-and-culture": "Art & Culture", diff --git a/lang/es.json b/lang/es.json index 410ca22183..602043da5e 100644 --- a/lang/es.json +++ b/lang/es.json @@ -1679,6 +1679,7 @@ "project.givback_toast.title.verified_public_2": " del valor de tu donación!", "project.givback_toast.title.verified_public_3": "Recibe recompensas de hasta {percent}%", "project.givback_toast.title.verified_public_not_eligible": "Boostea este proyecto con GIVpower!", + "project.givback_toast.complete_eligibility": "Complete su formulario de elegibilidad para GIVbacks", "projects_all": "Todos los proyectos", "projects_all_desc": "APOYE PROYECTOS GLOBALES EN BIENES PÚBLICOS, SOSTENIBILIDAD Y REGENERACIÓN CON CRIPTODONACIONES", "projects_art-and-culture": "Arte & Cultura", diff --git a/src/components/views/project/ProjectGIVbackToast.tsx b/src/components/views/project/ProjectGIVbackToast.tsx index 62e26d268b..37829103ee 100644 --- a/src/components/views/project/ProjectGIVbackToast.tsx +++ b/src/components/views/project/ProjectGIVbackToast.tsx @@ -53,7 +53,7 @@ const ProjectGIVbackToast = () => { const isOwnerVerifiedNotEligible = isVerified && isAdmin && !isGivbackEligible; - const color = isOwnerGivbackEligible + let color = isOwnerGivbackEligible ? semanticColors.golden[600] : neutralColors.gray[900]; const { formatMessage } = useIntl(); @@ -227,6 +227,29 @@ const ProjectGIVbackToast = () => { }); icon = ; link = links.CANCELLED_PROJECTS_DOCS; + } else if (isOwnerVerifiedNotEligible) { + title = formatMessage( + { + id: `${useIntlTitle}verified_owner`, + }, + { + percent: givbackFactorPercent, + value: GIVBACKS_DONATION_QUALIFICATION_VALUE_USD, + }, + ); + description = formatMessage({ + id: `${useIntlDescription}verified_owner_not_eligible`, + }); + color = semanticColors.golden[600]; + icon = ; + link = links.GIVPOWER_DOC; + Button = ( + } + /> + ); } else { title = formatMessage({ id: `${useIntlTitle}non_verified_owner`, diff --git a/src/components/views/project/projectActionCard/AdminActions.tsx b/src/components/views/project/projectActionCard/AdminActions.tsx index d4c59c6b13..34b28d2577 100644 --- a/src/components/views/project/projectActionCard/AdminActions.tsx +++ b/src/components/views/project/projectActionCard/AdminActions.tsx @@ -31,6 +31,7 @@ import { EVerificationStatus } from '@/apollo/types/types'; import ClaimRecurringDonationModal from '../../userProfile/projectsTab/ClaimRecurringDonationModal'; import config from '@/configuration'; import { findAnchorContractAddress } from '@/helpers/superfluid'; +import { ProjectCardNotification } from './ProjectCardNotification'; interface IMobileActionsModalProps { setShowModal: (value: boolean) => void; @@ -163,55 +164,63 @@ export const AdminActions = () => { project={project} /> )} + ) : ( - setShowMobileActionsModal(true)} - > -
Project Actions
- - {showMobileActionsModal && ( - - {options.map(option => ( - - - {option.icon} -
{option.label}
-
-
- ))} - {showVerificationModal && ( - setShowVerificationModal(false)} - /> - )} - {deactivateModal && ( - - )} - {showShareModal && ( - - )} -
- )} - {showClaimModal && ( - - )} -
+ <> + setShowMobileActionsModal(true)} + > +
Project Actions
+ + {showMobileActionsModal && ( + + {options.map(option => ( + + + + {option.icon} + +
{option.label}
+
+
+ ))} + {showVerificationModal && ( + setShowVerificationModal(false)} + /> + )} + {deactivateModal && ( + + )} + {showShareModal && ( + + )} +
+ )} + {showClaimModal && ( + + )} +
+ + ); }; diff --git a/src/components/views/project/projectActionCard/ProjectCardNotification.tsx b/src/components/views/project/projectActionCard/ProjectCardNotification.tsx new file mode 100644 index 0000000000..b2154971af --- /dev/null +++ b/src/components/views/project/projectActionCard/ProjectCardNotification.tsx @@ -0,0 +1,45 @@ +import { useIntl } from 'react-intl'; +import styled from 'styled-components'; + +import { + semanticColors, + IconAlertCircle16, + Flex, +} from '@giveth/ui-design-system'; +import { useProjectContext } from '@/context/project.context'; + +export const ProjectCardNotification = () => { + const { formatMessage } = useIntl(); + const { isAdmin, projectData } = useProjectContext(); + + const isVerified = projectData?.verified; + const isGivbackEligible = projectData?.isGivbackEligible; + + const isOwnerVerifiedNotEligible = + isVerified && isAdmin && !isGivbackEligible; + + if (!isOwnerVerifiedNotEligible) { + return null; + } + + return ( + + + + {formatMessage({ + id: `project.givback_toast.complete_eligibility`, + })} + + + ); +}; + +const ProjectCardNotificationWrapper = styled(Flex)` + align-items: center; + padding-top: 12px; + font-size: 14px; + color: ${semanticColors.golden[600]}; + span { + margin-left: 8px; + } +`; From 1a3e4206ce17d74fdbacf0ad7838e09184c4443d Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 11 Nov 2024 10:21:15 +0100 Subject: [PATCH 66/97] started verification of the user email --- src/apollo/gql/gqlUser.ts | 6 ++++++ src/components/modals/EditUserModal.tsx | 24 +++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/apollo/gql/gqlUser.ts b/src/apollo/gql/gqlUser.ts index b3dfd670fa..f0dbc63949 100644 --- a/src/apollo/gql/gqlUser.ts +++ b/src/apollo/gql/gqlUser.ts @@ -256,3 +256,9 @@ export const FETCH_USERS_GIVPOWER_BY_ADDRESS = ` } } }`; + +export const SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW = gql` + mutation SendUserEmailConfirmationCodeFlow($email: String!) { + sendUserEmailConfirmationCodeFlow(email: $email) + } +`; diff --git a/src/components/modals/EditUserModal.tsx b/src/components/modals/EditUserModal.tsx index 341d6bdcad..f229549964 100644 --- a/src/components/modals/EditUserModal.tsx +++ b/src/components/modals/EditUserModal.tsx @@ -8,7 +8,10 @@ import { captureException } from '@sentry/nextjs'; import { useForm } from 'react-hook-form'; import { Modal } from './Modal'; import { client } from '@/apollo/apolloClient'; -import { UPDATE_USER } from '@/apollo/gql/gqlUser'; +import { + SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, + UPDATE_USER, +} from '@/apollo/gql/gqlUser'; import { IUser } from '@/apollo/types/types'; import { gToast, ToastType } from '../toasts'; import { @@ -88,6 +91,20 @@ const EditUserModal = ({ } }; + const testMe = async () => { + try { + const { data } = await client.mutate({ + mutation: SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, + variables: { + email: 'test£gmail.com', + }, + }); + console.log(data); + } catch (error) { + console.log(error); + } + }; + const onSubmit = async (formData: Inputs) => { setIsLoading(true); try { @@ -163,6 +180,11 @@ const EditUserModal = ({
+
+ +
{inputFields.map(field => ( Date: Mon, 11 Nov 2024 19:02:44 +0100 Subject: [PATCH 67/97] Disable USDC on recurring donation --- next-env.d.ts | 2 +- src/config/production.tsx | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/next-env.d.ts b/next-env.d.ts index 4f11a03dc6..a4a7b3f5cf 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/src/config/production.tsx b/src/config/production.tsx index 27c51ceb91..d394f547a6 100644 --- a/src/config/production.tsx +++ b/src/config/production.tsx @@ -522,21 +522,21 @@ const config: EnvConfig = { isSuperToken: true, coingeckoId: 'dai', }, - { - underlyingToken: { - decimals: 6, - id: '0x7f5c764cbc14f9669b88837ca1490cca17c31607', - name: 'USD Coin', - symbol: 'USDC', - coingeckoId: 'usd-coin', - }, - decimals: 18, - id: '0x8430f084b939208e2eded1584889c9a66b90562f', - name: 'Super USD Coin', - symbol: 'USDCx', - isSuperToken: true, - coingeckoId: 'usd-coin', - }, + // { + // underlyingToken: { + // decimals: 6, + // id: '0x7f5c764cbc14f9669b88837ca1490cca17c31607', + // name: 'USD Coin', + // symbol: 'USDC', + // coingeckoId: 'usd-coin', + // }, + // decimals: 18, + // id: '0x8430f084b939208e2eded1584889c9a66b90562f', + // name: 'Super USD Coin', + // symbol: 'USDCx', + // isSuperToken: true, + // coingeckoId: 'usd-coin', + // }, { underlyingToken: { decimals: 18, From 39bf6065801e19951c630fddac765c8c2ecf0dff Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 14 Nov 2024 14:57:22 +0100 Subject: [PATCH 68/97] stil testing --- src/components/modals/EditUserModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/modals/EditUserModal.tsx b/src/components/modals/EditUserModal.tsx index f229549964..a50855f6fd 100644 --- a/src/components/modals/EditUserModal.tsx +++ b/src/components/modals/EditUserModal.tsx @@ -96,7 +96,7 @@ const EditUserModal = ({ const { data } = await client.mutate({ mutation: SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, variables: { - email: 'test£gmail.com', + email: 'kkatusic@gmail.com', }, }); console.log(data); From cd0c808aeb6a579fdf98aa75f64ecc214ee9d80c Mon Sep 17 00:00:00 2001 From: kkatusic Date: Fri, 15 Nov 2024 15:28:24 +0100 Subject: [PATCH 69/97] Fix/Mobile search --- src/components/SearchInput.tsx | 65 ++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/components/SearchInput.tsx b/src/components/SearchInput.tsx index 50751654e7..06314630b1 100644 --- a/src/components/SearchInput.tsx +++ b/src/components/SearchInput.tsx @@ -42,36 +42,49 @@ export const SearchInput: FC = ({ setTerm, className }) => { setValue(event.target.value); } + function handleFormSubmit(inputValue: string) { + if (inputValue.length > 2) { + setTerm(inputValue); + } + } + const [inputRef] = useFocus(); return ( - - - {value.length > 0 ? ( - { - setValue(''); - setTerm(''); - }} - > - - - ) : ( - - - - )} - + { + e.preventDefault(); + handleFormSubmit(value); + }} + > + + + {value.length > 0 ? ( + { + setValue(''); + setTerm(''); + }} + > + + + ) : ( + + + + )} + + {value.length > 0 ? ( value.length > 2 ? ( From 89c774704435a0ef90e7527bf6e0567ab3b006d4 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Sat, 16 Nov 2024 18:13:07 +0100 Subject: [PATCH 70/97] created separated input for user email verification --- lang/ca.json | 7 +- lang/en.json | 9 +- lang/es.json | 3 + src/apollo/types/types.ts | 1 + src/components/InputUserEmailVerify.tsx | 443 ++++++++++++++++++++++++ src/components/modals/EditUserModal.tsx | 59 ++-- src/features/user/user.queries.ts | 1 + 7 files changed, 495 insertions(+), 28 deletions(-) create mode 100644 src/components/InputUserEmailVerify.tsx diff --git a/lang/ca.json b/lang/ca.json index 7023c9de42..1d185cc249 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -383,6 +383,9 @@ "label.eligible_networks_for_matching": "Xarxes aptes per a la concordança QF", "label.email": "correu electrònic", "label.email_address": "Adreça electrònica", + "label.email_verified": "Correu electrònic verificat", + "label.email_verify": "Verifica el correu electrònic", + "label.email_already_verified": "El teu correu electrònic ha estat verificat. Ara pots desar la informació del teu perfil.", "label.enable_change": "Habilita el canvi", "label.enable_recurring_donations": "Habilitar Donacions Recurrents", "label.ends_on": "acaba el", @@ -610,7 +613,7 @@ "label.loading": "Carregant", "label.loading_data": "Carregant Dades", "label.location": "Ubicació", - "label.location_optional": "ubicació (opcional)", + "label.location_optional": "Ubicació (opcional)", "label.locekd_giv": "GIV Bloquejat", "label.locked_for": "Bloquejat per", "label.locked_giv_details": "Detalls del GIV bloquejat", @@ -1204,7 +1207,7 @@ "label.wallet": "CARTERA", "label.wallet_connect": "Connexió de la cartera", "label.want_to_use_another_wallet": "Vols usar una altra cartera?", - "label.website_or_url": "lloc web o URL", + "label.website_or_url": "Lloc web o URL", "label.week": "setmana", "label.welcome_giver": "Benvingut, Giver", "label.welcome_to_the": "Benvingut a", diff --git a/lang/en.json b/lang/en.json index 552db6df57..45c6487c75 100644 --- a/lang/en.json +++ b/lang/en.json @@ -381,8 +381,11 @@ "label.elevate_projects": "Elevate Projects", "label.eligible_for_matching": "Eligible for Matching", "label.eligible_networks_for_matching": "Eligible networks for QF matching", - "label.email": "email", + "label.email": "Email", "label.email_address": "Email Address", + "label.email_verified": "Email Verified", + "label.email_verify": "Verify Email", + "label.email_already_verified": "Your email has been verified. You can now save your profile information.", "label.enable_change": "Enable Change", "label.enable_recurring_donations": "Enable Recurring Donations", "label.ends_on": "ends on", @@ -610,7 +613,7 @@ "label.loading": "Loading", "label.loading_data": "Loading Data", "label.location": "Location", - "label.location_optional": "location (optional)", + "label.location_optional": "Location (optional)", "label.locekd_giv": "Locked GIV", "label.locked_for": "Locked for", "label.locked_giv_details": "Locked GIV Details", @@ -1204,7 +1207,7 @@ "label.wallet": "WALLET", "label.wallet_connect": "Wallet Connect", "label.want_to_use_another_wallet": "Want to use another wallet?", - "label.website_or_url": "website or url", + "label.website_or_url": "Website or url", "label.week": "week", "label.welcome_giver": "Welcome, Giver", "label.welcome_to_the": "Welcome to the", diff --git a/lang/es.json b/lang/es.json index 410ca22183..9438eaaed5 100644 --- a/lang/es.json +++ b/lang/es.json @@ -383,6 +383,9 @@ "label.eligible_networks_for_matching": "Redes elegibles para la asignación de QF", "label.email": "Email", "label.email_address": "Dirección de Email", + "label.email_verified": "Correo electrónico verificado", + "label.email_verify": "Verificar correo electrónico", + "label.email_already_verified": "Tu correo electrónico ha sido verificado. Ahora puedes guardar la información de tu perfil.", "label.enable_change": "Ayuda al Cambio", "label.enable_recurring_donations": "Habilitar Donaciones Recurrentes", "label.ends_on": "termina el", diff --git a/src/apollo/types/types.ts b/src/apollo/types/types.ts index b461427456..5187903452 100644 --- a/src/apollo/types/types.ts +++ b/src/apollo/types/types.ts @@ -237,6 +237,7 @@ export interface IUser { wasReferred?: boolean; isReferrer?: boolean; chainvineId?: string; + isEmailVerified?: boolean; } export interface IPassportInfo { diff --git a/src/components/InputUserEmailVerify.tsx b/src/components/InputUserEmailVerify.tsx new file mode 100644 index 0000000000..a060bfbad5 --- /dev/null +++ b/src/components/InputUserEmailVerify.tsx @@ -0,0 +1,443 @@ +import { + GLink, + IIconProps, + neutralColors, + semanticColors, + SublineBold, + FlexCenter, + brandColors, + Flex, + IconEmptyCircle, + IconCheckCircleFilled, +} from '@giveth/ui-design-system'; +import React, { + forwardRef, + InputHTMLAttributes, + ReactElement, + useCallback, + useId, + useRef, + useState, +} from 'react'; +import styled, { css } from 'styled-components'; +import { useIntl } from 'react-intl'; +import { EInputValidation, IInputValidation } from '@/types/inputValidation'; +import InputStyled from './styled-components/Input'; +import { getTextWidth } from '@/helpers/text'; +import { + inputSizeToFontSize, + inputSizeToPaddingLeft, + inputSizeToVerticalPadding, +} from '@/helpers/styledComponents'; +import { Spinner } from './Spinner'; +import { useProfileContext } from '@/context/profile.context'; +import { SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW } from '@/apollo/gql/gqlUser'; +import { client } from '@/apollo/apolloClient'; +import type { + DeepRequired, + FieldError, + FieldErrorsImpl, + Merge, + RegisterOptions, + UseFormRegister, +} from 'react-hook-form'; +export enum InputSize { + SMALL, + MEDIUM, + LARGE, +} + +interface IInputLabelProps { + $required?: boolean; + $disabled?: boolean; +} + +interface IInput extends InputHTMLAttributes { + label?: string; + caption?: string; + isValidating?: boolean; + size?: InputSize; + LeftIcon?: ReactElement; + error?: ICustomInputError; + suffix?: ReactElement; +} + +interface ICustomInputError { + message?: string; +} + +interface IInputWithRegister extends IInput { + register: UseFormRegister; + registerName: string; + registerOptions?: RegisterOptions; + error?: + | FieldError + | undefined + | ICustomInputError + | Merge>>>; +} + +const InputSizeToLinkSize = (size: InputSize) => { + switch (size) { + case InputSize.SMALL: + return 'Tiny'; + case InputSize.MEDIUM: + return 'Small'; + case InputSize.LARGE: + return 'Medium'; + default: + return 'Small'; + } +}; + +type InputType = + | IInputWithRegister + | ({ + registerName?: never; + register?: never; + registerOptions?: never; + } & IInput); + +const InputUserEmailVerify = forwardRef( + (props, inputRef) => { + const { formatMessage } = useIntl(); + const { user } = useProfileContext(); + + const [email, setEmail] = useState(user.email); + const [verified, setVerified] = useState(user.isEmailVerified); + const [disableVerifyButton, setDisableVerifyButton] = useState( + !user.isEmailVerified && !user.email, + ); + const [isVerificationProcess, setIsVerificationProcess] = + useState(false); + + const { + label, + caption, + size = InputSize.MEDIUM, + disabled, + LeftIcon, + register, + registerName, + registerOptions = { required: false }, + error, + maxLength, + value, + isValidating, + suffix, + className, + ...rest + } = props; + const id = useId(); + const canvasRef = useRef(); + + const [validationStatus, setValidationStatus] = useState( + !error || isValidating + ? EInputValidation.NORMAL + : EInputValidation.ERROR, + ); + + // const inputRef = useRef(null); + + const calcLeft = useCallback(() => { + if ( + suffix && + !canvasRef.current && + typeof document !== 'undefined' + ) { + canvasRef.current = document.createElement('canvas'); + } + if (canvasRef.current) { + const width = getTextWidth( + value?.toString() || '', + `normal ${inputSizeToFontSize(size)}px Red Hat Text`, + canvasRef.current, + ); + return inputSizeToPaddingLeft(size, !!LeftIcon) + width; + } + return 15; + }, [suffix, value, size, LeftIcon]); + + const { ref = undefined, ...restRegProps } = + registerName && register + ? register(registerName, registerOptions) + : {}; + + // Setup label button on condition + let labelButton = verified + ? formatMessage({ + id: 'label.email_verified', + }) + : formatMessage({ + id: 'label.email_verify', + }); + + // Enable verification process "button" if email input value was empty and not verified yet + // and setup email if input value was changed and has more than 3 characters + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.value.length > 3) { + setEmail(e.target.value); + setDisableVerifyButton(false); + } else { + setDisableVerifyButton(true); + } + }; + + const verificationEmailHandler = async () => { + console.log({ email }); + try { + const { data } = await client.mutate({ + mutation: SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, + variables: { + email: 'kkatusic@gmail.com', + }, + }); + if (data.sendUserEmailConfirmationCodeFlow === 'EMAIL_EXIST') { + setValidationStatus(EInputValidation.WARNING); + setDisableVerifyButton(true); + } + } catch (error) { + console.log(error); + } + }; + + return ( + + {label && ( + + )} + + {LeftIcon && ( + + {LeftIcon} + + )} + { + ref !== undefined && ref(e); + if (inputRef) (inputRef as any).current = e; + }} + data-testid='styled-input' + onChange={handleInputChange} + /> + + {suffix} + + + + + {!isVerificationProcess && !verified && ( + + )} + {!isVerificationProcess && verified && ( + + )} + {!!isVerificationProcess && ( + + )} + {labelButton} + + + + + {isValidating && } + {maxLength && ( + + {value ? String(value)?.length : 0}/{maxLength} + + )} + + + {error?.message ? ( + + {error.message as string} + + ) : ( + + {verified && + formatMessage({ + id: 'label.email_already_verified', + })} + //hidden char + )} + + ); + }, +); + +InputUserEmailVerify.displayName = 'Input'; + +const Absolute = styled(FlexCenter)` + position: absolute; + right: 10px; + top: 0; + bottom: 0; +`; + +const CharLength = styled(SublineBold)` + display: flex; + justify-content: center; + align-items: center; + font-size: 12px; + background: ${neutralColors.gray[300]}; + color: ${neutralColors.gray[700]}; + font-weight: 500; + border-radius: 64px; + width: 52px; + height: 30px; + margin-right: 6px; +`; + +const InputContainer = styled.div` + flex: 1; +`; + +const InputLabel = styled(GLink)` + padding-bottom: 4px; + color: ${props => + props.$disabled ? neutralColors.gray[600] : neutralColors.gray[900]}; + &::after { + content: '*'; + display: ${props => (props.$required ? 'inline-block' : 'none')}; + padding: 0 4px; + color: ${semanticColors.punch[500]}; + } +`; + +const InputDesc = styled(GLink)` + padding-top: 4px; + color: ${neutralColors.gray[900]}; + display: block; +`; + +const InputValidation = styled(GLink)` + padding-top: 4px; + display: block; + color: ${props => { + switch (props.$validation) { + case EInputValidation.NORMAL: + return neutralColors.gray[900]; + case EInputValidation.WARNING: + return semanticColors.golden[600]; + case EInputValidation.ERROR: + return semanticColors.punch[500]; + case EInputValidation.SUCCESS: + return semanticColors.jade[500]; + default: + return neutralColors.gray[300]; + } + }}; +`; + +const InputWrapper = styled.div` + position: relative; + display: flex; +`; + +interface IInputWrapper { + $inputSize: InputSize; +} + +const LeftIconWrapper = styled.div` + position: absolute; + transform: translateY(-50%); + + border-right: 1px solid ${neutralColors.gray[400]}; + top: 50%; + left: 0; + overflow: hidden; + ${props => { + switch (props.$inputSize) { + case InputSize.SMALL: + return css` + width: 28px; + height: 16px; + padding-left: 8px; + `; + case InputSize.MEDIUM: + return css` + width: 36px; + height: 24px; + padding-top: 4px; + padding-left: 16px; + `; + case InputSize.LARGE: + return css` + width: 36px; + height: 24px; + padding-top: 4px; + padding-left: 16px; + `; + } + }} + padding-right: 4px; +`; + +const SuffixWrapper = styled.span` + position: absolute; + /* width: 16px; + height: 16px; */ +`; + +type VerifyInputButtonWrapperProps = { + $verified?: boolean; +}; + +const VerifyInputButtonWrapper = styled.button` + outline: none; + cursor: pointer; + background-color: ${({ $verified }) => + $verified ? 'transparent' : brandColors.giv[50]}; + border: 1px solid + ${({ $verified }) => + $verified ? semanticColors.jade[500] : brandColors.giv[50]}; + border-radius: 8px; + padding: 3px 8px; + span { + color: ${({ $verified }) => + $verified ? semanticColors.jade[500] : brandColors.giv[500]}; + font-size: 10px; + font-weight: 400; + line-height: 13.23px; + text-align: left; + } + svg { + color: ${({ $verified }) => + $verified ? semanticColors.jade[500] : brandColors.giv[500]}; + } + &:disabled { + opacity: 0.5; + } +`; + +export default InputUserEmailVerify; diff --git a/src/components/modals/EditUserModal.tsx b/src/components/modals/EditUserModal.tsx index a50855f6fd..ef25834db4 100644 --- a/src/components/modals/EditUserModal.tsx +++ b/src/components/modals/EditUserModal.tsx @@ -26,6 +26,7 @@ import { requiredOptions, validators } from '@/lib/constants/regex'; import { useModalAnimation } from '@/hooks/useModalAnimation'; import useUpload from '@/hooks/useUpload'; import { useGeneralWallet } from '@/providers/generalWalletProvider'; +import InputUserEmailVerify from '../InputUserEmailVerify'; interface IEditUserModal extends IModal { user: IUser; @@ -186,28 +187,36 @@ const EditUserModal = ({
- {inputFields.map(field => ( - - ))} - ", + "label.email_confirm_code": "Confirma el codi", "label.enable_change": "Habilita el canvi", "label.enable_recurring_donations": "Habilitar Donacions Recurrents", "label.ends_on": "acaba el", diff --git a/lang/en.json b/lang/en.json index 45c6487c75..da6ef53f22 100644 --- a/lang/en.json +++ b/lang/en.json @@ -386,6 +386,12 @@ "label.email_verified": "Email Verified", "label.email_verify": "Verify Email", "label.email_already_verified": "Your email has been verified. You can now save your profile information.", + "label.email_used": "This email address will be used to send you important communications.", + "label.email_used_another": "This email that has already been verified on another profile!", + "label.email_sent_to": "Verification code sent to {email}", + "label.email_please_verify": "Please Verify your email. Enter the confirmation code sent to your email.", + "label.email_get_resend": "Didn't get the email? Check your spam folder or ", + "label.email_confirm_code": "Confirm Code", "label.enable_change": "Enable Change", "label.enable_recurring_donations": "Enable Recurring Donations", "label.ends_on": "ends on", diff --git a/lang/es.json b/lang/es.json index 9438eaaed5..dd18a568a4 100644 --- a/lang/es.json +++ b/lang/es.json @@ -386,6 +386,12 @@ "label.email_verified": "Correo electrónico verificado", "label.email_verify": "Verificar correo electrónico", "label.email_already_verified": "Tu correo electrónico ha sido verificado. Ahora puedes guardar la información de tu perfil.", + "label.email_used": "Esta dirección de correo electrónico se utilizará para enviarte comunicaciones importantes.", + "label.email_used_another": "¡Este correo electrónico ya ha sido verificado en otro perfil!", + "label.email_sent_to": "Código de verificación enviado a {email}", + "label.email_please_verify": "Por favor, verifica tu correo electrónico. Ingresa el código de confirmación enviado a tu correo.", + "label.email_get_resend": "¿No recibiste el correo? Revisa tu carpeta de spam o ", + "label.email_confirm_code": "Confirmar código", "label.enable_change": "Ayuda al Cambio", "label.enable_recurring_donations": "Habilitar Donaciones Recurrentes", "label.ends_on": "termina el", diff --git a/src/apollo/gql/gqlUser.ts b/src/apollo/gql/gqlUser.ts index f0dbc63949..17b0d819f8 100644 --- a/src/apollo/gql/gqlUser.ts +++ b/src/apollo/gql/gqlUser.ts @@ -262,3 +262,9 @@ export const SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW = gql` sendUserEmailConfirmationCodeFlow(email: $email) } `; + +export const SEND_USER_CONFIRMATION_CODE_FLOW = gql` + mutation SendUserConfirmationCodeFlow($verifyCode: String!) { + sendUserConfirmationCodeFlow(verifyCode: $verifyCode) + } +`; diff --git a/src/components/InputUserEmailVerify.tsx b/src/components/InputUserEmailVerify.tsx index a060bfbad5..f46f7b301a 100644 --- a/src/components/InputUserEmailVerify.tsx +++ b/src/components/InputUserEmailVerify.tsx @@ -9,6 +9,7 @@ import { Flex, IconEmptyCircle, IconCheckCircleFilled, + IconAlertCircle, } from '@giveth/ui-design-system'; import React, { forwardRef, @@ -20,7 +21,7 @@ import React, { useState, } from 'react'; import styled, { css } from 'styled-components'; -import { useIntl } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import { EInputValidation, IInputValidation } from '@/types/inputValidation'; import InputStyled from './styled-components/Input'; import { getTextWidth } from '@/helpers/text'; @@ -31,7 +32,10 @@ import { } from '@/helpers/styledComponents'; import { Spinner } from './Spinner'; import { useProfileContext } from '@/context/profile.context'; -import { SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW } from '@/apollo/gql/gqlUser'; +import { + SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, + SEND_USER_CONFIRMATION_CODE_FLOW, +} from '@/apollo/gql/gqlUser'; import { client } from '@/apollo/apolloClient'; import type { DeepRequired, @@ -98,6 +102,10 @@ type InputType = registerOptions?: never; } & IInput); +interface IExtendedInputLabelProps extends IInputLabelProps { + $validation?: EInputValidation; +} + const InputUserEmailVerify = forwardRef( (props, inputRef) => { const { formatMessage } = useIntl(); @@ -110,10 +118,19 @@ const InputUserEmailVerify = forwardRef( ); const [isVerificationProcess, setIsVerificationProcess] = useState(false); + const [inputDescription, setInputDescription] = useState( + verified + ? formatMessage({ + id: 'label.email_already_verified', + }) + : formatMessage({ + id: 'label.email_used', + }), + ); + const codeInputRef = useRef(null); const { label, - caption, size = InputSize.MEDIUM, disabled, LeftIcon, @@ -137,6 +154,12 @@ const InputUserEmailVerify = forwardRef( : EInputValidation.ERROR, ); + const [validationCodeStatus, setValidationCodeStatus] = useState( + EInputValidation.SUCCESS, + ); + const [disableCodeVerifyButton, setDisableCodeVerifyButton] = + useState(true); + // const inputRef = useRef(null); const calcLeft = useCallback(() => { @@ -183,18 +206,66 @@ const InputUserEmailVerify = forwardRef( } }; + // Verification email handler, it will be called on button click + // It will send request to backend to check if email exists and if it's not verified yet + // or email is already exist on another user account + // If email isn't verified it will send email with verification code to user const verificationEmailHandler = async () => { - console.log({ email }); try { const { data } = await client.mutate({ mutation: SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, variables: { - email: 'kkatusic@gmail.com', + email: email, }, }); + if (data.sendUserEmailConfirmationCodeFlow === 'EMAIL_EXIST') { setValidationStatus(EInputValidation.WARNING); setDisableVerifyButton(true); + setInputDescription( + formatMessage({ + id: 'label.email_used_another', + }), + ); + } + + if ( + data.sendUserEmailConfirmationCodeFlow === + 'VERIFICATION_SENT' + ) { + setIsVerificationProcess(true); + } + } catch (error) { + console.log(error); + } + }; + + // Verification code handler, it will be called on button click + const handleInputCodeChange = ( + e: React.ChangeEvent, + ) => { + const value = e.target.value; + value.length >= 5 + ? setDisableCodeVerifyButton(false) + : setDisableCodeVerifyButton(true); + }; + + // Sent verification code to backend to check if it's correct + const handleButtonCodeChange = async () => { + try { + const { data } = await client.mutate({ + mutation: SEND_USER_CONFIRMATION_CODE_FLOW, + variables: { + verifyCode: codeInputRef.current?.value, + }, + }); + + if ( + data.sendUserConfirmationCodeFlow === 'VERIFICATION_SUCCESS' + ) { + setIsVerificationProcess(false); + setDisableCodeVerifyButton(true); + setVerified(true); } } catch (error) { console.log(error); @@ -209,6 +280,11 @@ const InputUserEmailVerify = forwardRef( $disabled={disabled} size={InputSizeToLinkSize(size)} $required={Boolean(registerOptions.required)} + $validation={ + isVerificationProcess + ? EInputValidation.ERROR + : undefined + } > {label} @@ -221,13 +297,16 @@ const InputUserEmailVerify = forwardRef( )} { @@ -245,27 +324,22 @@ const InputUserEmailVerify = forwardRef( > {suffix} - - - - {!isVerificationProcess && !verified && ( - - )} - {!isVerificationProcess && verified && ( - - )} - {!!isVerificationProcess && ( - - )} - {labelButton} - - - + {!isVerificationProcess && ( + + + + {!verified && } + {verified && } + {labelButton} + + + + )} {isValidating && } {maxLength && ( @@ -283,12 +357,84 @@ const InputUserEmailVerify = forwardRef( {error.message as string} ) : ( - - {verified && - formatMessage({ - id: 'label.email_already_verified', - })} - //hidden char + + {inputDescription} + + )} + {isVerificationProcess && ( + + + + {formatMessage( + { + id: 'label.email_sent_to', + }, + { email }, + )} + + + + + + + + {!verified && } + {verified && } + + {formatMessage({ + id: 'label.email_confirm_code', + })} + + + + + + + ( + + ), + }} + /> + + )} ); @@ -322,10 +468,12 @@ const InputContainer = styled.div` flex: 1; `; -const InputLabel = styled(GLink)` +const InputLabel = styled(GLink)` padding-bottom: 4px; color: ${props => - props.$disabled ? neutralColors.gray[600] : neutralColors.gray[900]}; + props.$validation === EInputValidation.ERROR + ? semanticColors.punch[600] + : neutralColors.gray[900]}; &::after { content: '*'; display: ${props => (props.$required ? 'inline-block' : 'none')}; @@ -334,9 +482,22 @@ const InputLabel = styled(GLink)` } `; -const InputDesc = styled(GLink)` +const InputDesc = styled(GLink)<{ validationStatus: EInputValidation }>` padding-top: 4px; - color: ${neutralColors.gray[900]}; + color: ${props => { + switch (props.validationStatus) { + case EInputValidation.NORMAL: + return neutralColors.gray[900]; + case EInputValidation.WARNING: + return semanticColors.golden[600]; + case EInputValidation.ERROR: + return semanticColors.punch[500]; + case EInputValidation.SUCCESS: + return semanticColors.jade[500]; + default: + return neutralColors.gray[300]; + } + }}; display: block; `; @@ -440,4 +601,41 @@ const VerifyInputButtonWrapper = styled.button` } `; +const ValidationCode = styled(Flex)` + flex-direction: column; + margin-top: 30px; + margin-bottom: 25px; +`; + +const EmailSentNotification = styled(Flex)` + width: 100%; + margin-bottom: 20px; + border: 1px solid ${brandColors.giv[200]}; + padding: 16px; + border-radius: 8px; + font-size: 12px; + font-weight: 400; + line-height: 15.88px; + text-align: left; + color: ${brandColors.giv[500]}; + svg { + color: ${brandColors.giv[500]}; + } +`; + +const InputCodeDesc = styled(GLink)` + padding-top: 4px; + font-size: 0.625rem; + line-height: 132%; + & button { + background: none; + border: none; + padding: 0; + color: ${brandColors.pinky[400]}; + font-size: 0.625rem; + line-height: 132%; + cursor: pointer; + } +`; + export default InputUserEmailVerify; From 77373cd7a24e692f007b1961fc072e0761cef736 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 19 Nov 2024 14:31:22 +0100 Subject: [PATCH 72/97] completed all verification process and restriction --- lang/ca.json | 2 + lang/en.json | 2 + lang/es.json | 2 + pages/account.tsx | 2 + pages/project/[projectIdSlug]/index.tsx | 2 - src/apollo/gql/gqlUser.ts | 7 +- src/components/InputUserEmailVerify.tsx | 34 ++- src/components/modals/EditUserModal.tsx | 24 +-- src/components/views/project/ProjectIndex.tsx | 3 + .../projectActionCard/AdminActions.tsx | 196 +++++++++++------- .../views/userProfile/VerifyEmailBanner.tsx | 59 ++++++ .../projectsTab/ProfileProjectsTab.tsx | 12 +- src/context/profile.context.tsx | 14 +- src/context/project.context.tsx | 5 + 14 files changed, 252 insertions(+), 112 deletions(-) create mode 100644 src/components/views/userProfile/VerifyEmailBanner.tsx diff --git a/lang/ca.json b/lang/ca.json index fda44e8977..71a9150fcf 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -392,6 +392,8 @@ "label.email_please_verify": "Si us plau, verifica el teu correu electrònic. Introdueix el codi de confirmació enviat al teu correu.", "label.email_get_resend": "No has rebut el correu? Revisa la teva carpeta de correu brossa o ", "label.email_confirm_code": "Confirma el codi", + "label.email_verify_banner": "Obté l'accés complet als teus comptes i projectes. Verifica la teva adreça de correu electrònic! ", + "label.email_actions_text": "Verifica el teu correu electrònic per gestionar els teus projectes!", "label.enable_change": "Habilita el canvi", "label.enable_recurring_donations": "Habilitar Donacions Recurrents", "label.ends_on": "acaba el", diff --git a/lang/en.json b/lang/en.json index da6ef53f22..39961f8327 100644 --- a/lang/en.json +++ b/lang/en.json @@ -392,6 +392,8 @@ "label.email_please_verify": "Please Verify your email. Enter the confirmation code sent to your email.", "label.email_get_resend": "Didn't get the email? Check your spam folder or ", "label.email_confirm_code": "Confirm Code", + "label.email_verify_banner": "Get the full access to your accounts and projects. Verify your email address! ", + "label.email_actions_text": "Verify your email to manage your projects!", "label.enable_change": "Enable Change", "label.enable_recurring_donations": "Enable Recurring Donations", "label.ends_on": "ends on", diff --git a/lang/es.json b/lang/es.json index dd18a568a4..1a12d5fbdc 100644 --- a/lang/es.json +++ b/lang/es.json @@ -392,6 +392,8 @@ "label.email_please_verify": "Por favor, verifica tu correo electrónico. Ingresa el código de confirmación enviado a tu correo.", "label.email_get_resend": "¿No recibiste el correo? Revisa tu carpeta de spam o ", "label.email_confirm_code": "Confirmar código", + "label.email_verify_banner": "Obtén acceso completo a tus cuentas y proyectos. ¡Verifica tu dirección de correo electrónico! ", + "label.email_actions_text": "¡Verifica tu correo electrónico para gestionar tus proyectos!", "label.enable_change": "Ayuda al Cambio", "label.enable_recurring_donations": "Habilitar Donaciones Recurrentes", "label.ends_on": "termina el", diff --git a/pages/account.tsx b/pages/account.tsx index 1d13e12c95..4714c88d4f 100644 --- a/pages/account.tsx +++ b/pages/account.tsx @@ -6,6 +6,7 @@ import WalletNotConnected from '@/components/WalletNotConnected'; import UserNotSignedIn from '@/components/UserNotSignedIn'; import UserProfileView from '@/components/views/userProfile/UserProfile.view'; import { ProfileProvider } from '@/context/profile.context'; +import VerifyEmailBanner from '@/components/views/userProfile/VerifyEmailBanner'; const AccountRoute = () => { const { isSignedIn, isEnabled, userData, isLoading } = useAppSelector( @@ -27,6 +28,7 @@ const AccountRoute = () => { {userData?.name} | Giveth + {!userData?.isEmailVerified && } diff --git a/pages/project/[projectIdSlug]/index.tsx b/pages/project/[projectIdSlug]/index.tsx index d4f4e58c07..3b701dd79f 100644 --- a/pages/project/[projectIdSlug]/index.tsx +++ b/pages/project/[projectIdSlug]/index.tsx @@ -10,8 +10,6 @@ import { ProjectProvider } from '@/context/project.context'; const ProjectRoute: FC = ({ project }) => { useReferral(); - console.log({ project }); - return ( diff --git a/src/apollo/gql/gqlUser.ts b/src/apollo/gql/gqlUser.ts index 17b0d819f8..975eb9da1b 100644 --- a/src/apollo/gql/gqlUser.ts +++ b/src/apollo/gql/gqlUser.ts @@ -264,7 +264,10 @@ export const SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW = gql` `; export const SEND_USER_CONFIRMATION_CODE_FLOW = gql` - mutation SendUserConfirmationCodeFlow($verifyCode: String!) { - sendUserConfirmationCodeFlow(verifyCode: $verifyCode) + mutation SendUserConfirmationCodeFlow( + $verifyCode: String! + $email: String! + ) { + sendUserConfirmationCodeFlow(verifyCode: $verifyCode, email: $email) } `; diff --git a/src/components/InputUserEmailVerify.tsx b/src/components/InputUserEmailVerify.tsx index f46f7b301a..4cc9845601 100644 --- a/src/components/InputUserEmailVerify.tsx +++ b/src/components/InputUserEmailVerify.tsx @@ -37,6 +37,7 @@ import { SEND_USER_CONFIRMATION_CODE_FLOW, } from '@/apollo/gql/gqlUser'; import { client } from '@/apollo/apolloClient'; +import { showToastError } from '@/lib/helpers'; import type { DeepRequired, FieldError, @@ -109,7 +110,7 @@ interface IExtendedInputLabelProps extends IInputLabelProps { const InputUserEmailVerify = forwardRef( (props, inputRef) => { const { formatMessage } = useIntl(); - const { user } = useProfileContext(); + const { user, updateUser } = useProfileContext(); const [email, setEmail] = useState(user.email); const [verified, setVerified] = useState(user.isEmailVerified); @@ -204,6 +205,13 @@ const InputUserEmailVerify = forwardRef( } else { setDisableVerifyButton(true); } + + // Check if user is changing email address + if (e.target.value !== user.email) { + setVerified(false); + } else { + setVerified(true); + } }; // Verification email handler, it will be called on button click @@ -234,6 +242,12 @@ const InputUserEmailVerify = forwardRef( 'VERIFICATION_SENT' ) { setIsVerificationProcess(true); + setValidationStatus(EInputValidation.NORMAL); + setInputDescription( + formatMessage({ + id: 'label.email_used', + }), + ); } } catch (error) { console.log(error); @@ -257,17 +271,29 @@ const InputUserEmailVerify = forwardRef( mutation: SEND_USER_CONFIRMATION_CODE_FLOW, variables: { verifyCode: codeInputRef.current?.value, + email: email, }, }); if ( data.sendUserConfirmationCodeFlow === 'VERIFICATION_SUCCESS' ) { + // Reset states setIsVerificationProcess(false); setDisableCodeVerifyButton(true); setVerified(true); + setValidationCodeStatus(EInputValidation.SUCCESS); + + // Update user data + updateUser({ + email: email, + isEmailVerified: true, + }); } } catch (error) { + if (error instanceof Error) { + showToastError(error.message); + } console.log(error); } }; @@ -359,7 +385,7 @@ const InputUserEmailVerify = forwardRef( ) : ( {inputDescription} @@ -482,10 +508,10 @@ const InputLabel = styled(GLink)` } `; -const InputDesc = styled(GLink)<{ validationStatus: EInputValidation }>` +const InputDesc = styled(GLink)<{ $validationstatus: EInputValidation }>` padding-top: 4px; color: ${props => { - switch (props.validationStatus) { + switch (props.$validationstatus) { case EInputValidation.NORMAL: return neutralColors.gray[900]; case EInputValidation.WARNING: diff --git a/src/components/modals/EditUserModal.tsx b/src/components/modals/EditUserModal.tsx index ef25834db4..64f7dead4f 100644 --- a/src/components/modals/EditUserModal.tsx +++ b/src/components/modals/EditUserModal.tsx @@ -8,10 +8,7 @@ import { captureException } from '@sentry/nextjs'; import { useForm } from 'react-hook-form'; import { Modal } from './Modal'; import { client } from '@/apollo/apolloClient'; -import { - SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, - UPDATE_USER, -} from '@/apollo/gql/gqlUser'; +import { UPDATE_USER } from '@/apollo/gql/gqlUser'; import { IUser } from '@/apollo/types/types'; import { gToast, ToastType } from '../toasts'; import { @@ -92,20 +89,6 @@ const EditUserModal = ({ } }; - const testMe = async () => { - try { - const { data } = await client.mutate({ - mutation: SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, - variables: { - email: 'kkatusic@gmail.com', - }, - }); - console.log(data); - } catch (error) { - console.log(error); - } - }; - const onSubmit = async (formData: Inputs) => { setIsLoading(true); try { @@ -181,11 +164,6 @@ const EditUserModal = ({
-
- -
{inputFields.map(field => { // We load different input components for email becasue validation is different diff --git a/src/components/views/project/ProjectIndex.tsx b/src/components/views/project/ProjectIndex.tsx index 0e9cc06347..dd2c8e00ff 100644 --- a/src/components/views/project/ProjectIndex.tsx +++ b/src/components/views/project/ProjectIndex.tsx @@ -49,6 +49,7 @@ import Routes from '@/lib/constants/Routes'; import { ChainType } from '@/types/config'; import { useAppSelector } from '@/features/hooks'; import { EndaomentProjectsInfo } from '@/components/views/project/EndaomentProjectsInfo'; +import VerifyEmailBanner from '../userProfile/VerifyEmailBanner'; const ProjectDonations = dynamic( () => import('./projectDonations/ProjectDonations.index'), @@ -84,6 +85,7 @@ const ProjectIndex: FC = () => { hasActiveQFRound, isCancelled, isAdmin, + isAdminEmailVerified, isLoading, } = useProjectContext(); @@ -134,6 +136,7 @@ const ProjectIndex: FC = () => { return ( + {!isAdminEmailVerified && } {hasActiveQFRound && !isOnSolana && } {title && `${title} |`} Giveth diff --git a/src/components/views/project/projectActionCard/AdminActions.tsx b/src/components/views/project/projectActionCard/AdminActions.tsx index d4c59c6b13..35abcfc87b 100644 --- a/src/components/views/project/projectActionCard/AdminActions.tsx +++ b/src/components/views/project/projectActionCard/AdminActions.tsx @@ -9,6 +9,7 @@ import { Flex, FlexCenter, IconArrowDownCircle16, + semanticColors, } from '@giveth/ui-design-system'; import React, { FC, useState } from 'react'; import { useIntl } from 'react-intl'; @@ -55,6 +56,8 @@ export const AdminActions = () => { const { switchChain } = useSwitchChain(); const chainId = chain?.id; + const { isAdminEmailVerified } = useProjectContext(); + const isVerificationDisabled = isGivbackEligible || verificationFormStatus === EVerificationStatus.SUBMITTED || @@ -132,86 +135,109 @@ export const AdminActions = () => { }; return !isMobile ? ( - - - {showVerificationModal && ( - setShowVerificationModal(false)} - /> - )} - {deactivateModal && ( - + <> + {!isAdminEmailVerified && ( + + {formatMessage({ + id: 'label.email_actions_text', + })} + )} - {showShareModal && ( - + - )} - {showClaimModal && ( - - )} - + {showVerificationModal && ( + setShowVerificationModal(false)} + /> + )} + {deactivateModal && ( + + )} + {showShareModal && ( + + )} + {showClaimModal && ( + + )} + + ) : ( - setShowMobileActionsModal(true)} - > -
Project Actions
- - {showMobileActionsModal && ( - - {options.map(option => ( - - - {option.icon} -
{option.label}
-
-
- ))} - {showVerificationModal && ( - setShowVerificationModal(false)} - /> - )} - {deactivateModal && ( - - )} - {showShareModal && ( - - )} -
- )} - {showClaimModal && ( - + <> + {!isAdminEmailVerified && ( + + {formatMessage({ + id: 'label.email_actions_text', + })} + )} -
+ setShowMobileActionsModal(true)} + $verified={isAdminEmailVerified} + > +
Project Actions
+ + {showMobileActionsModal && ( + + {options.map(option => ( + + + + {option.icon} + +
{option.label}
+
+
+ ))} + {showVerificationModal && ( + setShowVerificationModal(false)} + /> + )} + {deactivateModal && ( + + )} + {showShareModal && ( + + )} +
+ )} + {showClaimModal && ( + + )} +
+ ); }; @@ -232,18 +258,26 @@ const MobileActionsModal: FC = ({ ); }; -const Wrapper = styled.div` +interface WrapperProps { + $verified: boolean; +} + +const Wrapper = styled.div` order: 1; margin-bottom: 16px; + opacity: ${({ $verified }) => ($verified ? 1 : 0.5)}; + pointer-events: ${({ $verified }) => ($verified ? 'auto' : 'none')}; ${mediaQueries.tablet} { margin-bottom: 5px; order: unset; } `; -const MobileWrapper = styled(FlexCenter)` +const MobileWrapper = styled(FlexCenter)` padding: 10px 16px; background-color: ${neutralColors.gray[300]}; + opacity: ${({ $verified }) => ($verified ? 1 : 0.5)}; + pointer-events: ${({ $verified }) => ($verified ? 'auto' : 'none')}; border-radius: 8px; `; @@ -251,3 +285,9 @@ const MobileActionModalItem = styled(Flex)` padding: 16px 24px; border-bottom: ${neutralColors.gray[400]} 1px solid; `; + +const VerifyNotification = styled.div` + font-size: 14px; + text-align: center; + color: ${semanticColors.golden[600]}; +`; diff --git a/src/components/views/userProfile/VerifyEmailBanner.tsx b/src/components/views/userProfile/VerifyEmailBanner.tsx new file mode 100644 index 0000000000..ea992647a8 --- /dev/null +++ b/src/components/views/userProfile/VerifyEmailBanner.tsx @@ -0,0 +1,59 @@ +import styled from 'styled-components'; +import { useRouter } from 'next/router'; +import { brandColors, FlexCenter } from '@giveth/ui-design-system'; +import { FormattedMessage } from 'react-intl'; +import Routes from '@/lib/constants/Routes'; + +const VerifyEmailBanner = () => { + const router = useRouter(); + return ( + + + ( + + ), + }} + /> + + + ); +}; + +const PStyled = styled.div` + display: flex; + gap: 4px; + @media (max-width: 768px) { + flex-direction: column; + } + + & button { + background: none; + border: none; + padding: 0; + font-size: 16px; + color: ${brandColors.giv[500]}; + cursor: pointer; + } +`; + +const Wrapper = styled(FlexCenter)` + flex-wrap: wrap; + padding: 16px; + text-align: center; + gap: 4px; + background: ${brandColors.mustard[200]}; + z-index: 99; + position: sticky; +`; + +export default VerifyEmailBanner; diff --git a/src/components/views/userProfile/projectsTab/ProfileProjectsTab.tsx b/src/components/views/userProfile/projectsTab/ProfileProjectsTab.tsx index 388a42d693..f41d40c928 100644 --- a/src/components/views/userProfile/projectsTab/ProfileProjectsTab.tsx +++ b/src/components/views/userProfile/projectsTab/ProfileProjectsTab.tsx @@ -49,7 +49,7 @@ const ProfileProjectsTab: FC = () => { )} )} - + {!isLoading && data?.totalCount === 0 ? ( = () => { ); }; -export const ProjectsContainer = styled.div` +interface ProjectsContainerProps { + $verified: boolean; +} + +export const ProjectsContainer = styled.div` margin-bottom: 40px; + background-color: ${({ $verified }) => + $verified ? 'transparent' : '#f0f0f0'}; + opacity: ${({ $verified }) => ($verified ? 1 : 0.5)}; + pointer-events: ${({ $verified }) => ($verified ? 'auto' : 'none')}; `; export const Loading = styled(Flex)` diff --git a/src/context/profile.context.tsx b/src/context/profile.context.tsx index 51d4283cfe..2b5ebaafb4 100644 --- a/src/context/profile.context.tsx +++ b/src/context/profile.context.tsx @@ -12,12 +12,14 @@ interface ProfileContext { user: IUser; myAccount: boolean; givpowerBalance: string; + updateUser: (updatedUser: Partial) => void; } const ProfileContext = createContext({ user: {} as IUser, myAccount: false, givpowerBalance: '0', + updateUser: () => {}, }); ProfileContext.displayName = 'ProfileContext'; @@ -27,9 +29,18 @@ export const ProfileProvider = (props: { myAccount: boolean; children: ReactNode; }) => { - const { user, myAccount, children } = props; + const { user: initialUser, myAccount, children } = props; + const [user, setUser] = useState(initialUser); const [balance, setBalance] = useState('0'); + // Update user data + const updateUser = (updatedUser: Partial) => { + setUser(prevUser => ({ + ...prevUser, + ...updatedUser, + })); + }; + useEffect(() => { const fetchTotal = async () => { try { @@ -52,6 +63,7 @@ export const ProfileProvider = (props: { user, myAccount, givpowerBalance: balance, + updateUser, }} > {children} diff --git a/src/context/project.context.tsx b/src/context/project.context.tsx index 925bb815aa..0b2e95ecee 100644 --- a/src/context/project.context.tsx +++ b/src/context/project.context.tsx @@ -56,6 +56,7 @@ interface IProjectContext { isActive: boolean; isDraft: boolean; isAdmin: boolean; + isAdminEmailVerified: boolean; hasActiveQFRound: boolean; totalDonationsCount: number; isCancelled: boolean; @@ -73,6 +74,7 @@ const ProjectContext = createContext({ isActive: true, isDraft: false, isAdmin: false, + isAdminEmailVerified: false, hasActiveQFRound: false, totalDonationsCount: 0, isCancelled: false, @@ -110,6 +112,8 @@ export const ProjectProvider = ({ user?.walletAddress, ); + const isAdminEmailVerified = !!(isAdmin && user?.isEmailVerified); + const hasActiveQFRound = hasActiveRound(projectData?.qfRounds); const fetchProjectBySlug = useCallback(async () => { @@ -313,6 +317,7 @@ export const ProjectProvider = ({ isActive, isDraft, isAdmin, + isAdminEmailVerified, hasActiveQFRound, totalDonationsCount, isCancelled, From 681535cf8544f239f850e553f6f9851894c12ef3 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Wed, 20 Nov 2024 13:21:41 +0100 Subject: [PATCH 73/97] fixing small bugs bypassing first update of the user --- src/apollo/gql/gqlUser.ts | 2 ++ src/components/InputUserEmailVerify.tsx | 5 ++++- src/components/views/onboarding/InfoStep.tsx | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/apollo/gql/gqlUser.ts b/src/apollo/gql/gqlUser.ts index 975eb9da1b..d95f31deac 100644 --- a/src/apollo/gql/gqlUser.ts +++ b/src/apollo/gql/gqlUser.ts @@ -179,6 +179,7 @@ export const UPDATE_USER = gql` $firstName: String $avatar: String $newUser: Boolean + $isFirstUpdate: Boolean ) { updateUser( url: $url @@ -188,6 +189,7 @@ export const UPDATE_USER = gql` lastName: $lastName avatar: $avatar newUser: $newUser + isFirstUpdate: $isFirstUpdate ) } `; diff --git a/src/components/InputUserEmailVerify.tsx b/src/components/InputUserEmailVerify.tsx index 4cc9845601..4d05855d49 100644 --- a/src/components/InputUserEmailVerify.tsx +++ b/src/components/InputUserEmailVerify.tsx @@ -209,7 +209,7 @@ const InputUserEmailVerify = forwardRef( // Check if user is changing email address if (e.target.value !== user.email) { setVerified(false); - } else { + } else if (e.target.value !== user.email && user.isEmailVerified) { setVerified(true); } }; @@ -250,6 +250,9 @@ const InputUserEmailVerify = forwardRef( ); } } catch (error) { + if (error instanceof Error) { + showToastError(error.message); + } console.log(error); } }; diff --git a/src/components/views/onboarding/InfoStep.tsx b/src/components/views/onboarding/InfoStep.tsx index ceb5de5406..fc2bd04f8b 100644 --- a/src/components/views/onboarding/InfoStep.tsx +++ b/src/components/views/onboarding/InfoStep.tsx @@ -80,6 +80,7 @@ const InfoStep: FC = ({ setStep }) => { variables: { ...formData, newUser: true, + isFirstUpdate: true, }, }); if (response.updateUser) { From 035394a3f721a7caf81d5a4651e2c870a5b050ce Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 21 Nov 2024 12:21:19 +0100 Subject: [PATCH 74/97] adding verification process to the new user update form and removing bypassing --- src/apollo/gql/gqlUser.ts | 2 - src/components/views/onboarding/InfoStep.tsx | 350 ++++++++++++++++++- 2 files changed, 345 insertions(+), 7 deletions(-) diff --git a/src/apollo/gql/gqlUser.ts b/src/apollo/gql/gqlUser.ts index d95f31deac..975eb9da1b 100644 --- a/src/apollo/gql/gqlUser.ts +++ b/src/apollo/gql/gqlUser.ts @@ -179,7 +179,6 @@ export const UPDATE_USER = gql` $firstName: String $avatar: String $newUser: Boolean - $isFirstUpdate: Boolean ) { updateUser( url: $url @@ -189,7 +188,6 @@ export const UPDATE_USER = gql` lastName: $lastName avatar: $avatar newUser: $newUser - isFirstUpdate: $isFirstUpdate ) } `; diff --git a/src/components/views/onboarding/InfoStep.tsx b/src/components/views/onboarding/InfoStep.tsx index fc2bd04f8b..80b89534d8 100644 --- a/src/components/views/onboarding/InfoStep.tsx +++ b/src/components/views/onboarding/InfoStep.tsx @@ -1,10 +1,25 @@ -import { FC, useEffect, useState } from 'react'; +import { FC, useEffect, useRef, useState } from 'react'; import { useMutation } from '@apollo/client'; -import { H6, neutralColors, Col, Row } from '@giveth/ui-design-system'; +import { + H6, + neutralColors, + Col, + Row, + brandColors, + semanticColors, + IconAlertCircle, + Flex, + GLink, +} from '@giveth/ui-design-system'; import styled from 'styled-components'; import { captureException } from '@sentry/nextjs'; import { useForm } from 'react-hook-form'; -import { UPDATE_USER } from '@/apollo/gql/gqlUser'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { + SEND_USER_CONFIRMATION_CODE_FLOW, + SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, + UPDATE_USER, +} from '@/apollo/gql/gqlUser'; import { SkipOnboardingModal } from '@/components/modals/SkipOnboardingModal'; import { gToast, ToastType } from '@/components/toasts'; import { @@ -21,6 +36,10 @@ import { setShowSignWithWallet } from '@/features/modal/modal.slice'; import { fetchUserByAddress } from '@/features/user/user.thunks'; import { requiredOptions, validators } from '@/lib/constants/regex'; import { useGeneralWallet } from '@/providers/generalWalletProvider'; +import { client } from '@/apollo/apolloClient'; +import { showToastError } from '@/lib/helpers'; +import InputStyled from '@/components/styled-components/Input'; +import { EInputValidation } from '@/types/inputValidation'; export interface IUserInfo { email: string; @@ -38,7 +57,17 @@ enum EUserInfo { URL = 'url', } +interface IInputLabelProps { + $required?: boolean; + $disabled?: boolean; +} + +interface IExtendedInputLabelProps extends IInputLabelProps { + $validation?: EInputValidation; +} + const InfoStep: FC = ({ setStep }) => { + const { formatMessage } = useIntl(); const [isLoading, setIsLoading] = useState(false); const [updateUser] = useMutation(UPDATE_USER); const [showModal, setShowModal] = useState(false); @@ -47,6 +76,141 @@ const InfoStep: FC = ({ setStep }) => { const dispatch = useAppDispatch(); const { isSignedIn, userData } = useAppSelector(state => state.user); + // States for email verification + const { userData: currentDBUser } = useAppSelector(state => state.user); + const [verified, setVerified] = useState( + currentDBUser?.isEmailVerified || false, + ); + const [disableVerifyButton, setDisableVerifyButton] = useState( + !currentDBUser?.isEmailVerified && !currentDBUser?.email, + ); + const [disableCodeVerifyButton, setDisableCodeVerifyButton] = + useState(true); + const [email, setEmail] = useState(currentDBUser?.email || ''); + const [isVerificationProcess, setIsVerificationProcess] = useState(false); + const [validationStatus, setValidationStatus] = useState( + EInputValidation.NORMAL, + ); + const [inputDescription, setInputDescription] = useState( + verified + ? formatMessage({ + id: 'label.email_already_verified', + }) + : formatMessage({ + id: 'label.email_used', + }), + ); + const codeInputRef = useRef(null); + const [validationCodeStatus, setValidationCodeStatus] = useState( + EInputValidation.SUCCESS, + ); + + // Setup label button on condition + let labelButton = verified + ? formatMessage({ + id: 'label.email_verified', + }) + : formatMessage({ + id: 'label.email_verify', + }); + + // Enable verification process "button" if email input value was empty and not verified yet + // and setup email if input value was changed and has more than 3 characters + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.value.length > 3) { + setEmail(e.target.value); + setDisableVerifyButton(false); + } else { + setDisableVerifyButton(true); + } + + // Check if user is changing email address + if (e.target.value !== currentDBUser?.email) { + setVerified(false); + } else if ( + e.target.value !== currentDBUser.email && + currentDBUser.isEmailVerified + ) { + setVerified(true); + } + }; + + // Verification email handler, it will be called on button click + // It will send request to backend to check if email exists and if it's not verified yet + // or email is already exist on another user account + // If email isn't verified it will send email with verification code to user + const verificationEmailHandler = async () => { + try { + const { data } = await client.mutate({ + mutation: SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW, + variables: { + email: email, + }, + }); + + if (data.sendUserEmailConfirmationCodeFlow === 'EMAIL_EXIST') { + setValidationStatus(EInputValidation.WARNING); + setDisableVerifyButton(true); + showToastError( + formatMessage({ + id: 'label.email_used_another', + }), + ); + } + + if ( + data.sendUserEmailConfirmationCodeFlow === 'VERIFICATION_SENT' + ) { + setIsVerificationProcess(true); + setValidationStatus(EInputValidation.NORMAL); + setInputDescription( + formatMessage({ + id: 'label.email_used', + }), + ); + } + } catch (error) { + if (error instanceof Error) { + showToastError(error.message); + } + console.log(error); + } + }; + + // Verification code handler, it will be called on button click + const handleInputCodeChange = (e: React.ChangeEvent) => { + const value = e.target.value; + value.length >= 5 + ? setDisableCodeVerifyButton(false) + : setDisableCodeVerifyButton(true); + }; + + // Sent verification code to backend to check if it's correct + const handleButtonCodeChange = async () => { + try { + const { data } = await client.mutate({ + mutation: SEND_USER_CONFIRMATION_CODE_FLOW, + variables: { + verifyCode: codeInputRef.current?.value, + email: email, + }, + }); + + if (data.sendUserConfirmationCodeFlow === 'VERIFICATION_SUCCESS') { + // Reset states + setIsVerificationProcess(false); + setDisableCodeVerifyButton(true); + setVerified(true); + setValidationCodeStatus(EInputValidation.SUCCESS); + } + } catch (error) { + if (error instanceof Error) { + showToastError(error.message); + } + console.log(error); + } + }; + const { register, handleSubmit, @@ -80,7 +244,6 @@ const InfoStep: FC = ({ setStep }) => { variables: { ...formData, newUser: true, - isFirstUpdate: true, }, }); if (response.updateUser) { @@ -144,8 +307,87 @@ const InfoStep: FC = ({ setStep }) => { type='email' registerOptions={requiredOptions.email} error={errors.email} + caption={inputDescription} + onChange={handleInputChange} /> + {!isVerificationProcess && ( + + + {labelButton} + + + )} + {isVerificationProcess && ( + <> + + + + {formatMessage( + { + id: 'label.email_sent_to', + }, + { email }, + )} + + + + + + + ( + + ), + }} + /> + + + + + {formatMessage({ + id: 'label.email_confirm_code', + })} + + + + )} Where are you?
@@ -179,7 +421,7 @@ const InfoStep: FC = ({ setStep }) => { @@ -209,4 +451,102 @@ const SectionHeader = styled(H6)` border-bottom: 1px solid ${neutralColors.gray[400]}; `; +type VerifyInputButtonWrapperProps = { + $verified?: boolean; +}; + +const VerifyInputButtonWrapper = styled.button` + outline: none; + cursor: pointer; + margin-top: 24px; + background-color: ${({ $verified }) => + $verified ? semanticColors.jade[500] : brandColors.giv[500]}; + border: 1px solid + ${({ $verified }) => + $verified ? semanticColors.jade[500] : brandColors.giv[50]}; + border-radius: 8px; + padding: 20px 20px; + color: #ffffff; + font-size: 16px; + font-weight: 500; + line-height: 13.23px; + text-align: left; + &:hover { + opacity: 0.85; + } + &:disabled { + opacity: 0.5; + } +`; + +const VerifyCodeButtonWrapper = styled.button` + outline: none; + cursor: pointer; + margin-top: 48px; + background-color: ${({ $verified }) => + $verified ? semanticColors.jade[500] : brandColors.giv[500]}; + border: 1px solid + ${({ $verified }) => + $verified ? semanticColors.jade[500] : brandColors.giv[50]}; + border-radius: 8px; + padding: 20px 20px; + color: #ffffff; + font-size: 16px; + font-weight: 500; + line-height: 13.23px; + text-align: left; + &:hover { + opacity: 0.85; + } + &:disabled { + opacity: 0.5; + } +`; + +const EmailSentNotification = styled(Flex)` + width: 100%; + margin-top: 20px; + margin-bottom: 20px; + border: 1px solid ${brandColors.giv[200]}; + padding: 16px; + border-radius: 8px; + font-size: 1em; + font-weight: 400; + line-height: 15.88px; + text-align: left; + color: ${brandColors.giv[500]}; + svg { + color: ${brandColors.giv[500]}; + } +`; + +const InputLabel = styled(GLink)` + padding-bottom: 4px; + color: ${props => + props.$validation === EInputValidation.ERROR + ? semanticColors.punch[600] + : neutralColors.gray[900]}; + &::after { + content: '*'; + display: ${props => (props.$required ? 'inline-block' : 'none')}; + padding: 0 4px; + color: ${semanticColors.punch[500]}; + } +`; + +const InputCodeDesc = styled(GLink)` + padding-top: 4px; + font-size: 0.75rem; + line-height: 132%; + & button { + background: none; + border: none; + padding: 0; + color: ${brandColors.pinky[400]}; + font-size: 0.75rem; + line-height: 132%; + cursor: pointer; + } +`; + export default InfoStep; From bfc5521a3382a8e99241f65461820afda549cade Mon Sep 17 00:00:00 2001 From: kkatusic Date: Thu, 21 Nov 2024 14:16:06 +0100 Subject: [PATCH 75/97] fixing toast messages on user edit form --- lang/ca.json | 1 + lang/en.json | 1 + lang/es.json | 1 + src/components/views/onboarding/InfoStep.tsx | 23 ++++++++++++++++---- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lang/ca.json b/lang/ca.json index 71a9150fcf..ff79705c6c 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -394,6 +394,7 @@ "label.email_confirm_code": "Confirma el codi", "label.email_verify_banner": "Obté l'accés complet als teus comptes i projectes. Verifica la teva adreça de correu electrònic! ", "label.email_actions_text": "Verifica el teu correu electrònic per gestionar els teus projectes!", + "label.email_error_verify": "Error de verificació del correu electrònic", "label.enable_change": "Habilita el canvi", "label.enable_recurring_donations": "Habilitar Donacions Recurrents", "label.ends_on": "acaba el", diff --git a/lang/en.json b/lang/en.json index 39961f8327..13e9659b3a 100644 --- a/lang/en.json +++ b/lang/en.json @@ -394,6 +394,7 @@ "label.email_confirm_code": "Confirm Code", "label.email_verify_banner": "Get the full access to your accounts and projects. Verify your email address! ", "label.email_actions_text": "Verify your email to manage your projects!", + "label.email_error_verify": "Error verification email", "label.enable_change": "Enable Change", "label.enable_recurring_donations": "Enable Recurring Donations", "label.ends_on": "ends on", diff --git a/lang/es.json b/lang/es.json index 1a12d5fbdc..fc3a0c5389 100644 --- a/lang/es.json +++ b/lang/es.json @@ -394,6 +394,7 @@ "label.email_confirm_code": "Confirmar código", "label.email_verify_banner": "Obtén acceso completo a tus cuentas y proyectos. ¡Verifica tu dirección de correo electrónico! ", "label.email_actions_text": "¡Verifica tu correo electrónico para gestionar tus proyectos!", + "label.email_error_verify": "Error de verificación del correo electrónico", "label.enable_change": "Ayuda al Cambio", "label.enable_recurring_donations": "Habilitar Donaciones Recurrentes", "label.ends_on": "termina el", diff --git a/src/components/views/onboarding/InfoStep.tsx b/src/components/views/onboarding/InfoStep.tsx index 80b89534d8..dc0cc4cb9a 100644 --- a/src/components/views/onboarding/InfoStep.tsx +++ b/src/components/views/onboarding/InfoStep.tsx @@ -37,7 +37,6 @@ import { fetchUserByAddress } from '@/features/user/user.thunks'; import { requiredOptions, validators } from '@/lib/constants/regex'; import { useGeneralWallet } from '@/providers/generalWalletProvider'; import { client } from '@/apollo/apolloClient'; -import { showToastError } from '@/lib/helpers'; import InputStyled from '@/components/styled-components/Input'; import { EInputValidation } from '@/types/inputValidation'; @@ -151,10 +150,16 @@ const InfoStep: FC = ({ setStep }) => { if (data.sendUserEmailConfirmationCodeFlow === 'EMAIL_EXIST') { setValidationStatus(EInputValidation.WARNING); setDisableVerifyButton(true); - showToastError( + gToast( formatMessage({ id: 'label.email_used_another', }), + { + type: ToastType.DANGER, + title: formatMessage({ + id: 'label.email_error_verify', + }), + }, ); } @@ -171,7 +176,12 @@ const InfoStep: FC = ({ setStep }) => { } } catch (error) { if (error instanceof Error) { - showToastError(error.message); + gToast(error.message, { + type: ToastType.DANGER, + title: formatMessage({ + id: 'label.email_error_verify', + }), + }); } console.log(error); } @@ -205,7 +215,12 @@ const InfoStep: FC = ({ setStep }) => { } } catch (error) { if (error instanceof Error) { - showToastError(error.message); + gToast(error.message, { + type: ToastType.DANGER, + title: formatMessage({ + id: 'label.email_error_verify', + }), + }); } console.log(error); } From e8eaac8e91fa34645b23b67f174ab6c9e2cd6a11 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 25 Nov 2024 14:41:06 +0100 Subject: [PATCH 76/97] Fix/Email verification bugs --- lang/ca.json | 2 +- lang/en.json | 2 +- lang/es.json | 2 +- src/components/InputUserEmailVerify.tsx | 11 ++++++++++- src/components/modals/EditUserModal.tsx | 6 +++++- src/components/views/project/ProjectIndex.tsx | 2 +- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lang/ca.json b/lang/ca.json index ff79705c6c..ee05908859 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -390,7 +390,7 @@ "label.email_used_another": "Aquest correu electrònic ja ha estat verificat en un altre perfil!", "label.email_sent_to": "Codi de verificació enviat a {email}", "label.email_please_verify": "Si us plau, verifica el teu correu electrònic. Introdueix el codi de confirmació enviat al teu correu.", - "label.email_get_resend": "No has rebut el correu? Revisa la teva carpeta de correu brossa o ", + "label.email_get_resend": "No has rebut el correu electrònic? Revisa la teva carpeta de correu brossa o ", "label.email_confirm_code": "Confirma el codi", "label.email_verify_banner": "Obté l'accés complet als teus comptes i projectes. Verifica la teva adreça de correu electrònic! ", "label.email_actions_text": "Verifica el teu correu electrònic per gestionar els teus projectes!", diff --git a/lang/en.json b/lang/en.json index 13e9659b3a..031b61d825 100644 --- a/lang/en.json +++ b/lang/en.json @@ -390,7 +390,7 @@ "label.email_used_another": "This email that has already been verified on another profile!", "label.email_sent_to": "Verification code sent to {email}", "label.email_please_verify": "Please Verify your email. Enter the confirmation code sent to your email.", - "label.email_get_resend": "Didn't get the email? Check your spam folder or ", + "label.email_get_resend": "Didn't get the email? Check your spam folder or ", "label.email_confirm_code": "Confirm Code", "label.email_verify_banner": "Get the full access to your accounts and projects. Verify your email address! ", "label.email_actions_text": "Verify your email to manage your projects!", diff --git a/lang/es.json b/lang/es.json index fc3a0c5389..0ecd7837c8 100644 --- a/lang/es.json +++ b/lang/es.json @@ -390,7 +390,7 @@ "label.email_used_another": "¡Este correo electrónico ya ha sido verificado en otro perfil!", "label.email_sent_to": "Código de verificación enviado a {email}", "label.email_please_verify": "Por favor, verifica tu correo electrónico. Ingresa el código de confirmación enviado a tu correo.", - "label.email_get_resend": "¿No recibiste el correo? Revisa tu carpeta de spam o ", + "label.email_get_resend": "¿No recibiste el correo electrónico? Revisa tu carpeta de spam o ", "label.email_confirm_code": "Confirmar código", "label.email_verify_banner": "Obtén acceso completo a tus cuentas y proyectos. ¡Verifica tu dirección de correo electrónico! ", "label.email_actions_text": "¡Verifica tu correo electrónico para gestionar tus proyectos!", diff --git a/src/components/InputUserEmailVerify.tsx b/src/components/InputUserEmailVerify.tsx index 4d05855d49..37244252ed 100644 --- a/src/components/InputUserEmailVerify.tsx +++ b/src/components/InputUserEmailVerify.tsx @@ -96,11 +96,14 @@ const InputSizeToLinkSize = (size: InputSize) => { }; type InputType = - | IInputWithRegister + | (IInputWithRegister & { + verifiedSaveButton?: (verified: boolean) => void; + }) | ({ registerName?: never; register?: never; registerOptions?: never; + verifiedSaveButton?: (verified: boolean) => void; } & IInput); interface IExtendedInputLabelProps extends IInputLabelProps { @@ -209,8 +212,13 @@ const InputUserEmailVerify = forwardRef( // Check if user is changing email address if (e.target.value !== user.email) { setVerified(false); + props.verifiedSaveButton && props.verifiedSaveButton(false); } else if (e.target.value !== user.email && user.isEmailVerified) { setVerified(true); + props.verifiedSaveButton && props.verifiedSaveButton(true); + } else if (e.target.value === user.email && user.isEmailVerified) { + setVerified(true); + props.verifiedSaveButton && props.verifiedSaveButton(true); } }; @@ -285,6 +293,7 @@ const InputUserEmailVerify = forwardRef( setIsVerificationProcess(false); setDisableCodeVerifyButton(true); setVerified(true); + props.verifiedSaveButton && props.verifiedSaveButton(true); setValidationCodeStatus(EInputValidation.SUCCESS); // Update user data diff --git a/src/components/modals/EditUserModal.tsx b/src/components/modals/EditUserModal.tsx index 64f7dead4f..235995dcff 100644 --- a/src/components/modals/EditUserModal.tsx +++ b/src/components/modals/EditUserModal.tsx @@ -58,6 +58,7 @@ const EditUserModal = ({ const { walletAddress: address } = useGeneralWallet(); const [updateUser] = useMutation(UPDATE_USER); + const [verified, setVerified] = useState(user.isEmailVerified); const { isAnimating, closeModal } = useModalAnimation(setShowModal); const onSaveAvatar = async () => { @@ -191,6 +192,9 @@ const EditUserModal = ({ register={register} error={(errors as any)[field.name]} registerOptions={field.registerOptions} + {...(field.type === 'email' && { + verifiedSaveButton: setVerified, + })} /> ); })} @@ -199,7 +203,7 @@ const EditUserModal = ({ label={formatMessage({ id: 'label.save', })} - disabled={isLoading} + disabled={isLoading || !verified} type='submit' /> = () => { return ( - {!isAdminEmailVerified && } + {!isAdminEmailVerified && isAdmin && } {hasActiveQFRound && !isOnSolana && } {title && `${title} |`} Giveth From 3920020e1c110683c8b9b208bb3e36ded9366418 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 25 Nov 2024 17:37:35 +0100 Subject: [PATCH 77/97] updating actions cards --- .../views/project/projectActionCard/AdminActions.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/project/projectActionCard/AdminActions.tsx b/src/components/views/project/projectActionCard/AdminActions.tsx index 6ad468605b..70fdf1c25f 100644 --- a/src/components/views/project/projectActionCard/AdminActions.tsx +++ b/src/components/views/project/projectActionCard/AdminActions.tsx @@ -32,6 +32,7 @@ import { EVerificationStatus } from '@/apollo/types/types'; import ClaimRecurringDonationModal from '../../userProfile/projectsTab/ClaimRecurringDonationModal'; import config from '@/configuration'; import { findAnchorContractAddress } from '@/helpers/superfluid'; +import { ProjectCardNotification } from './ProjectCardNotification'; interface IMobileActionsModalProps { setShowModal: (value: boolean) => void; @@ -174,8 +175,8 @@ export const AdminActions = () => { project={project} /> )} - - + + ) : ( <> @@ -238,6 +239,7 @@ export const AdminActions = () => { /> )} + ); }; From 3041ecf4f690624b2063e0610fa14e71951a0e10 Mon Sep 17 00:00:00 2001 From: Mitch Oz Date: Mon, 25 Nov 2024 14:51:02 -0600 Subject: [PATCH 78/97] update banner text for email verification --- lang/ca.json | 2 +- lang/en.json | 2 +- lang/es.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lang/ca.json b/lang/ca.json index 7b4cdd8f92..a27ad5f828 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -392,7 +392,7 @@ "label.email_please_verify": "Si us plau, verifica el teu correu electrònic. Introdueix el codi de confirmació enviat al teu correu.", "label.email_get_resend": "No has rebut el correu electrònic? Revisa la teva carpeta de correu brossa o ", "label.email_confirm_code": "Confirma el codi", - "label.email_verify_banner": "Obté l'accés complet als teus comptes i projectes. Verifica la teva adreça de correu electrònic! ", + "label.email_verify_banner": " i verifica la propietat de la teva adreça de correu electrònic per recuperar l'accés als teus projectes.", "label.email_actions_text": "Verifica el teu correu electrònic per gestionar els teus projectes!", "label.email_error_verify": "Error de verificació del correu electrònic", "label.enable_change": "Habilita el canvi", diff --git a/lang/en.json b/lang/en.json index d5de6b3ddd..9150f71c69 100644 --- a/lang/en.json +++ b/lang/en.json @@ -392,7 +392,7 @@ "label.email_please_verify": "Please Verify your email. Enter the confirmation code sent to your email.", "label.email_get_resend": "Didn't get the email? Check your spam folder or ", "label.email_confirm_code": "Confirm Code", - "label.email_verify_banner": "Get the full access to your accounts and projects. Verify your email address! ", + "label.email_verify_banner": " & verify ownership of your email address to regain access to your projects.", "label.email_actions_text": "Verify your email to manage your projects!", "label.email_error_verify": "Error verification email", "label.enable_change": "Enable Change", diff --git a/lang/es.json b/lang/es.json index 6c1a78c378..8aa45bc4cc 100644 --- a/lang/es.json +++ b/lang/es.json @@ -392,7 +392,7 @@ "label.email_please_verify": "Por favor, verifica tu correo electrónico. Ingresa el código de confirmación enviado a tu correo.", "label.email_get_resend": "¿No recibiste el correo electrónico? Revisa tu carpeta de spam o ", "label.email_confirm_code": "Confirmar código", - "label.email_verify_banner": "Obtén acceso completo a tus cuentas y proyectos. ¡Verifica tu dirección de correo electrónico! ", + "label.email_verify_banner": " y verifica la propiedad de tu dirección de correo electrónico para recuperar el acceso a tus proyectos.", "label.email_actions_text": "¡Verifica tu correo electrónico para gestionar tus proyectos!", "label.email_error_verify": "Error de verificación del correo electrónico", "label.enable_change": "Ayuda al Cambio", From 304457bac2d00493952184e5fd0f94b4c66c0341 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Tue, 26 Nov 2024 15:25:37 +0100 Subject: [PATCH 79/97] Fixing bugs and adding new features --- lang/ca.json | 5 + lang/en.json | 8 ++ lang/es.json | 5 + src/components/Header/Header.tsx | 5 + src/components/InputUserEmailVerify.tsx | 4 +- src/components/controller/modal.ctrl.tsx | 10 ++ src/components/modals/VerifyEmailModal.tsx | 110 ++++++++++++++++++ .../views/project/ProjectGIVbackToast.tsx | 9 +- src/components/views/project/ProjectIndex.tsx | 6 +- src/components/views/project/ProjectTabs.tsx | 26 ++++- .../views/userProfile/UserProfile.view.tsx | 8 +- .../views/userProfile/VerifyEmailBanner.tsx | 9 +- src/features/modal/modal.slice.ts | 5 + 13 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 src/components/modals/VerifyEmailModal.tsx diff --git a/lang/ca.json b/lang/ca.json index ee05908859..c29e58af10 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -395,6 +395,11 @@ "label.email_verify_banner": "Obté l'accés complet als teus comptes i projectes. Verifica la teva adreça de correu electrònic! ", "label.email_actions_text": "Verifica el teu correu electrònic per gestionar els teus projectes!", "label.email_error_verify": "Error de verificació del correu electrònic", + "label.email_modal_verify_your": "Verifica la teva adreça de correu electrònic", + "label.email_modal_need_verify": "Hauràs de verificar la teva adreça de correu electrònic abans de poder crear un nou projecte.", + "label.email_modal_verifying": "Verificar la teva adreça de correu electrònic assegura que puguem comunicar-nos amb tu sobre qualsevol canvi important a la plataforma. La teva adreça de correu electrònic no es compartirà públicament.", + "label.email_modal_to_verifying": "Per verificar la teva adreça de correu electrònic, edita el teu perfil i actualitza el teu correu electrònic.", + "label.email_modal_button": "Actualitza el perfil", "label.enable_change": "Habilita el canvi", "label.enable_recurring_donations": "Habilitar Donacions Recurrents", "label.ends_on": "acaba el", diff --git a/lang/en.json b/lang/en.json index 031b61d825..3edd465357 100644 --- a/lang/en.json +++ b/lang/en.json @@ -395,6 +395,14 @@ "label.email_verify_banner": "Get the full access to your accounts and projects. Verify your email address! ", "label.email_actions_text": "Verify your email to manage your projects!", "label.email_error_verify": "Error verification email", + "label.email_modal_verify_your": "Verify your email address", + "label.email_modal_need_verify": "You'll need to verify your email address before being able to create a new project.", + "label.email_modal_verifying": "Verifying your email address ensures we can communicate with you about any important changes on the platform. Your email address will not be shared publicly.", + "label.email_modal_to_verifying": "To verify your email address edit your profile and update your email.", + "label.email_modal_button": "Update profile", + + + "label.enable_change": "Enable Change", "label.enable_recurring_donations": "Enable Recurring Donations", "label.ends_on": "ends on", diff --git a/lang/es.json b/lang/es.json index 0ecd7837c8..ad2a7b3fb1 100644 --- a/lang/es.json +++ b/lang/es.json @@ -395,6 +395,11 @@ "label.email_verify_banner": "Obtén acceso completo a tus cuentas y proyectos. ¡Verifica tu dirección de correo electrónico! ", "label.email_actions_text": "¡Verifica tu correo electrónico para gestionar tus proyectos!", "label.email_error_verify": "Error de verificación del correo electrónico", + "label.email_modal_verify_your": "Verifica tu dirección de correo electrónico", + "label.email_modal_need_verify": "Necesitarás verificar tu dirección de correo electrónico antes de poder crear un nuevo proyecto.", + "label.email_modal_verifying": "Verificar tu dirección de correo electrónico asegura que podamos comunicarnos contigo sobre cualquier cambio importante en la plataforma. Tu dirección de correo electrónico no se compartirá públicamente.", + "label.email_modal_to_verifying": "Para verificar tu dirección de correo electrónico, edita tu perfil y actualiza tu correo electrónico.", + "label.email_modal_button": "Actualizar perfil", "label.enable_change": "Ayuda al Cambio", "label.enable_recurring_donations": "Habilitar Donaciones Recurrentes", "label.ends_on": "termina el", diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 2ef881021f..2e73698ce5 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -35,6 +35,7 @@ import { ETheme } from '@/features/general/general.slice'; import { setShowCompleteProfile, setShowSearchModal, + setShowVerifyEmailModal, } from '@/features/modal/modal.slice'; import { slugToProjectView } from '@/lib/routeCreators'; import { useModalCallback } from '@/hooks/useModalCallback'; @@ -138,6 +139,10 @@ const Header: FC = () => { openWalletConnectModal(); } else if (!isSignedIn) { signInThenCreate(); + } else if (!isUserRegistered(userData)) { + dispatch(setShowCompleteProfile(true)); + } else if (!userData?.isEmailVerified) { + dispatch(setShowVerifyEmailModal(true)); } else if (isUserRegistered(userData)) { router.push(Routes.CreateProject); } else { diff --git a/src/components/InputUserEmailVerify.tsx b/src/components/InputUserEmailVerify.tsx index 37244252ed..39ca986626 100644 --- a/src/components/InputUserEmailVerify.tsx +++ b/src/components/InputUserEmailVerify.tsx @@ -367,7 +367,7 @@ const InputUserEmailVerify = forwardRef( @@ -635,7 +635,7 @@ const VerifyInputButtonWrapper = styled.button` $verified ? semanticColors.jade[500] : brandColors.giv[500]}; } &:disabled { - opacity: 0.5; + opacity: ${({ $verified }) => ($verified ? '1' : '0.5')}; } `; diff --git a/src/components/controller/modal.ctrl.tsx b/src/components/controller/modal.ctrl.tsx index 84a25bc2a8..17934e8d77 100644 --- a/src/components/controller/modal.ctrl.tsx +++ b/src/components/controller/modal.ctrl.tsx @@ -4,6 +4,7 @@ import WelcomeModal from '@/components/modals/WelcomeModal'; import { FirstWelcomeModal } from '@/components/modals/FirstWelcomeModal'; import { SignWithWalletModal } from '@/components/modals/SignWithWalletModal'; import { CompleteProfileModal } from '@/components/modals/CompleteProfileModal'; +import { VerifyEmailModal } from '@/components/modals/VerifyEmailModal'; import { useIsSafeEnvironment } from '@/hooks/useSafeAutoConnect'; import { useAppDispatch, useAppSelector } from '@/features/hooks'; import { @@ -13,6 +14,7 @@ import { setShowSearchModal, setShowSwitchNetworkModal, setShowWelcomeModal, + setShowVerifyEmailModal, } from '@/features/modal/modal.slice'; import { isUserRegistered } from '@/lib/helpers'; import { SearchModal } from '../modals/SearchModal'; @@ -27,6 +29,7 @@ const ModalController = () => { showWelcomeModal, showSearchModal, showSwitchNetwork, + showVerifyEmailModal, } = useAppSelector(state => state.modal); const { userData, isSignedIn } = useAppSelector(state => state.user); @@ -103,6 +106,13 @@ const ModalController = () => { } /> )} + {showVerifyEmailModal && ( + + dispatch(setShowVerifyEmailModal(state)) + } + /> + )} ); }; diff --git a/src/components/modals/VerifyEmailModal.tsx b/src/components/modals/VerifyEmailModal.tsx new file mode 100644 index 0000000000..b9b30e95d8 --- /dev/null +++ b/src/components/modals/VerifyEmailModal.tsx @@ -0,0 +1,110 @@ +import { FC } from 'react'; +import router from 'next/router'; +import styled from 'styled-components'; +import { + brandColors, + Button, + H5, + IconProfile, + Lead, +} from '@giveth/ui-design-system'; +import { useIntl } from 'react-intl'; + +import { Modal } from '@/components/modals/Modal'; +import Routes from '@/lib/constants/Routes'; +import { IModal } from '@/types/common'; +import { useAppSelector } from '@/features/hooks'; +import { ETheme } from '@/features/general/general.slice'; +import { useModalAnimation } from '@/hooks/useModalAnimation'; + +export const VerifyEmailModal: FC = ({ setShowModal }) => { + const theme = useAppSelector(state => state.general.theme); + const { isAnimating, closeModal } = useModalAnimation(setShowModal); + const { formatMessage } = useIntl(); + + const handleClick = () => { + router.push( + { + pathname: Routes.MyAccount, + query: { opencheck: 'true' }, + }, + undefined, + { shallow: true }, + ); + closeModal(); + }; + + return ( + } + headerTitle={formatMessage({ id: 'label.complete_your_profile' })} + headerTitlePosition='left' + > + + + {formatMessage({ + id: 'label.email_modal_verify_your', + })} + +
+ {formatMessage({ + id: 'label.email_modal_need_verify', + })} +
+
+
+ {formatMessage({ + id: 'label.email_modal_verifying', + })} +
+
+
+ {formatMessage({ + id: 'label.email_modal_to_verifying', + })} +
+ + +
+
+ ); +}; + +const Container = styled(Lead)` + max-width: 528px; + padding: 24px; + text-align: left; + color: ${brandColors.deep[900]}; +`; + +const OkButton = styled(Button)` + width: 300px; + height: 48px; + margin: 48px auto 0; +`; + +const SkipButton = styled(Button)` + width: 300px; + margin: 0 auto 0; + background: transparent !important; + color: ${brandColors.pinky[500]} !important; + &:hover { + background: transparent !important; + color: ${brandColors.pinky[300]} !important; + } +`; + +const Title = styled(H5)` + margin: 24px 0; + font-weight: 700; +`; diff --git a/src/components/views/project/ProjectGIVbackToast.tsx b/src/components/views/project/ProjectGIVbackToast.tsx index 62e26d268b..d7aeda77f7 100644 --- a/src/components/views/project/ProjectGIVbackToast.tsx +++ b/src/components/views/project/ProjectGIVbackToast.tsx @@ -39,7 +39,8 @@ import { GIVBACKS_DONATION_QUALIFICATION_VALUE_USD } from '@/lib/constants/const const ProjectGIVbackToast = () => { const [showBoost, setShowBoost] = useState(false); const [showVerification, setShowVerification] = useState(false); - const { projectData, isAdmin, activateProject } = useProjectContext(); + const { projectData, isAdmin, activateProject, isAdminEmailVerified } = + useProjectContext(); const verStatus = projectData?.verificationFormStatus; const projectStatus = projectData?.status.name; const isGivbackEligible = projectData?.isGivbackEligible; @@ -74,6 +75,7 @@ const ProjectGIVbackToast = () => { const handleBoostClick = () => { if (isSSRMode) return; + if (!isAdminEmailVerified) return; if (!isEnabled) { openConnectModal?.(); } else if (!isSignedIn) { @@ -329,7 +331,7 @@ const ProjectGIVbackToast = () => { return ( <> - + {icon}
@@ -405,7 +407,7 @@ const Content = styled(Flex)` } `; -const Wrapper = styled(Flex)` +const Wrapper = styled(Flex)<{ $isverified: boolean }>` justify-content: space-between; align-items: center; gap: 24px; @@ -417,6 +419,7 @@ const Wrapper = styled(Flex)` ${mediaQueries.laptopL} { flex-direction: row; } + opacity: ${({ $isverified }) => ($isverified ? '1' : '0.75')}; `; const InnerLink = styled.a` diff --git a/src/components/views/project/ProjectIndex.tsx b/src/components/views/project/ProjectIndex.tsx index 55acb70dbc..965b6c69fb 100644 --- a/src/components/views/project/ProjectIndex.tsx +++ b/src/components/views/project/ProjectIndex.tsx @@ -221,7 +221,11 @@ const ProjectIndex: FC = () => { {projectData && !isDraft && ( - + )} diff --git a/src/components/views/project/ProjectTabs.tsx b/src/components/views/project/ProjectTabs.tsx index 744aaf581b..9aeaf8686f 100644 --- a/src/components/views/project/ProjectTabs.tsx +++ b/src/components/views/project/ProjectTabs.tsx @@ -17,6 +17,7 @@ import { Shadow } from '@/components/styled-components/Shadow'; interface IProjectTabs { activeTab: number; slug: string; + verified?: boolean; } const badgeCount = (count?: number) => { @@ -24,7 +25,7 @@ const badgeCount = (count?: number) => { }; const ProjectTabs = (props: IProjectTabs) => { - const { activeTab, slug } = props; + const { activeTab, slug, verified } = props; const { projectData, totalDonationsCount, boostersData } = useProjectContext(); const { totalProjectUpdates } = projectData || {}; @@ -61,8 +62,22 @@ const ProjectTabs = (props: IProjectTabs) => { i.query ? `?tab=${i.query}` : '' }`} scroll={false} + onClick={e => { + if ( + !verified && + i.query === EProjectPageTabs.UPDATES + ) { + e.preventDefault(); // Prevent the link from navigating from unverified users + } + }} > - + {formatMessage({ id: i.title })} {badgeCount(i.badge) && ( ` display: flex; padding: 9px 24px; border-radius: 48px; @@ -117,6 +136,7 @@ const Tab = styled(P)` color: ${brandColors.pinky[500]}; background: ${neutralColors.gray[200]}; } + opacity: ${({ $unverified }) => ($unverified ? '0.5' : '1')}; `; const Wrapper = styled.div` diff --git a/src/components/views/userProfile/UserProfile.view.tsx b/src/components/views/userProfile/UserProfile.view.tsx index 9235c4b532..2fbd335488 100644 --- a/src/components/views/userProfile/UserProfile.view.tsx +++ b/src/components/views/userProfile/UserProfile.view.tsx @@ -47,6 +47,8 @@ import { useGeneralWallet } from '@/providers/generalWalletProvider'; export interface IUserProfileView {} const UserProfileView: FC = () => { + const router = useRouter(); + const [showModal, setShowModal] = useState(false); // follow this state to refresh user content on screen const [showUploadProfileModal, setShowUploadProfileModal] = useState(false); const [showIncompleteWarning, setShowIncompleteWarning] = useState(false); @@ -57,12 +59,16 @@ const UserProfileView: FC = () => { const [pfpData, setPfpData] = useState(); const { walletChainType, chain } = useGeneralWallet(); const { user, myAccount } = useProfileContext(); - const router = useRouter(); const pfpToken = useGiverPFPToken(user?.walletAddress, user?.avatar); const showCompleteProfile = user && !isUserRegistered(user) && showIncompleteWarning && myAccount; + // Update the modal state if the query changes + useEffect(() => { + setShowModal(!!router.query.opencheck); + }, [router.query.opencheck]); + useEffect(() => { if (user && !isUserRegistered(user) && myAccount) { setShowIncompleteWarning(true); diff --git a/src/components/views/userProfile/VerifyEmailBanner.tsx b/src/components/views/userProfile/VerifyEmailBanner.tsx index ea992647a8..0a3dfe9741 100644 --- a/src/components/views/userProfile/VerifyEmailBanner.tsx +++ b/src/components/views/userProfile/VerifyEmailBanner.tsx @@ -16,7 +16,14 @@ const VerifyEmailBanner = () => {