From 547a477b1a95a39f60108b7a834038bb26d2887d Mon Sep 17 00:00:00 2001 From: kkatusic Date: Wed, 5 Jun 2024 00:06:46 +0200 Subject: [PATCH 01/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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 1986731339924e0dfbdd34e59028eb92b9323a71 Mon Sep 17 00:00:00 2001 From: kkatusic Date: Mon, 7 Oct 2024 16:50:17 +0200 Subject: [PATCH 22/26] 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 23/26] 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 24/26] 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 25/26] 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 26/26] 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';