diff --git a/src/features/Projects/pages/ExplorePage/ExplorePage.tsx b/src/features/Projects/pages/ExplorePage/ExplorePage.tsx index 85b93f3..284beff 100644 --- a/src/features/Projects/pages/ExplorePage/ExplorePage.tsx +++ b/src/features/Projects/pages/ExplorePage/ExplorePage.tsx @@ -1,208 +1,250 @@ - -import ErrorMessage from 'src/Components/Errors/ErrorMessage/ErrorMessage'; -import { useExplorePageQuery, useGetFiltersQuery } from 'src/graphql'; -import ProjectsGrid from './ProjectsGrid/ProjectsGrid'; -import Categories, { Category } from '../../Components/Categories/Categories'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import Header from './Header/Header'; -import Button from 'src/Components/Button/Button'; -import { useAppDispatch } from 'src/utils/hooks'; -import { openModal } from 'src/redux/features/modals.slice'; -import { createAction } from '@reduxjs/toolkit'; -import { useReduxEffect } from 'src/utils/hooks/useReduxEffect'; -import { NetworkStatus } from '@apollo/client'; -import { FiSliders } from 'react-icons/fi'; -import { HiOutlineChevronDoubleDown } from 'react-icons/hi' -import { ProjectsFilters } from './Filters/FiltersModal'; -import { useUpdateUrlWithFilters } from './helpers'; -import { withBasicProvider } from 'src/utils/helperFunctions'; -import { ProjectsFiltersProvider, useProjectsFilters } from './filters-context'; -import { setTheme } from 'src/redux/features/ui.slice'; -import OgTags from 'src/Components/OgTags/OgTags'; - +import ErrorMessage from "src/Components/Errors/ErrorMessage/ErrorMessage"; +import { useExplorePageQuery, useGetFiltersQuery } from "src/graphql"; +import ProjectsGrid from "./ProjectsGrid/ProjectsGrid"; +import Categories, { Category } from "../../Components/Categories/Categories"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import Header from "./Header/Header"; +import Button from "src/Components/Button/Button"; +import { useAppDispatch } from "src/utils/hooks"; +import { openModal } from "src/redux/features/modals.slice"; +import { createAction } from "@reduxjs/toolkit"; +import { useReduxEffect } from "src/utils/hooks/useReduxEffect"; +import { NetworkStatus } from "@apollo/client"; +import { FiSliders } from "react-icons/fi"; +import { HiOutlineChevronDoubleDown } from "react-icons/hi"; +import { ProjectsFilters } from "./Filters/FiltersModal"; +import { useUpdateUrlWithFilters } from "./helpers"; +import { withBasicProvider } from "src/utils/helperFunctions"; +import { ProjectsFiltersProvider, useProjectsFilters } from "./filters-context"; +import { setTheme } from "src/redux/features/ui.slice"; +import OgTags from "src/Components/OgTags/OgTags"; function ExplorePage() { + const dispatch = useAppDispatch(); - const dispatch = useAppDispatch(); + const [canFetchMore, setCanFetchMore] = useState(true); - const [canFetchMore, setCanFetchMore] = useState(true); - - const { filters, updateFilters } = useProjectsFilters(); - - useUpdateUrlWithFilters(filters) - const filtersQuery = useGetFiltersQuery(); - - const hiddenCategoriesIds = useMemo(() => { - if (filtersQuery.loading) return []; - return filtersQuery.data?.categoryList?.filter(c => c?.isHidden).map(c => c!.id!) ?? []; - }, [filtersQuery.data?.categoryList, filtersQuery.loading]) - - const { queryFilters, hasSearchFilters } = useMemo(() => createQueryFilters(filters, { - hiddenCategoriesIds - }), [filters, hiddenCategoriesIds]) - - - const { data, networkStatus, error, fetchMore } = useExplorePageQuery({ - variables: { - page: 1, - pageSize: PAGE_SIZE, - filter: queryFilters - }, - notifyOnNetworkStatusChange: true, - onCompleted: data => { - if ((data.projects?.length ?? 0) < PAGE_SIZE) setCanFetchMore(false); - }, - skip: filtersQuery.loading - }); - - - const onFiltersUpdated = useCallback(({ payload }: typeof UPDATE_FILTERS_ACTION) => { - updateFilters(payload) - }, [updateFilters]) - - useReduxEffect(onFiltersUpdated, UPDATE_FILTERS_ACTION.type); - - useEffect(() => { - dispatch(setTheme('light')) - }, [dispatch]) - - - useEffect(() => { - setCanFetchMore(true); - }, [filters]) - - - const openFilters = () => { - dispatch(openModal({ - Modal: "FiltersModal", - isPageModal: true, - props: { - callbackAction: { - type: UPDATE_FILTERS_ACTION.type, - payload: { - - } - }, - initFilters: { - ...filters - } - } - })) - } - - const selectCategoryTab = (category: Category | null) => { - updateFilters({ ...(filters ?? {}), categories: category ? [category] : undefined }) - } - - const clickFetchMore = () => { - fetchMore({ variables: { page: Math.floor((data?.projects?.length ?? 0) / PAGE_SIZE) + 1 } }) - .then(res => { - if (!res.data.projects || res.data.projects.length < PAGE_SIZE) setCanFetchMore(false); - }) - } - - if (error) { - return
- -
- } - - const isLoading = networkStatus === NetworkStatus.loading || networkStatus === NetworkStatus.refetch || networkStatus === NetworkStatus.setVariables || filtersQuery.loading; - const isLoadingMore = networkStatus === NetworkStatus.fetchMore; - const canLoadMore = !isLoading && !isLoadingMore && data?.projects && data.projects.length > 0 && canFetchMore; + const { filters, updateFilters } = useProjectsFilters(); + useUpdateUrlWithFilters(filters); + const filtersQuery = useGetFiltersQuery(); + const hiddenCategoriesIds = useMemo(() => { + if (filtersQuery.loading) return []; return ( - <> - c?.isHidden) + .map((c) => c!.id!) ?? [] + ); + }, [filtersQuery.data?.categoryList, filtersQuery.loading]); + + const { queryFilters, hasSearchFilters } = useMemo( + () => + createQueryFilters(filters, { + hiddenCategoriesIds, + }), + [filters, hiddenCategoriesIds] + ); + + const { data, networkStatus, error, fetchMore } = useExplorePageQuery({ + variables: { + page: 1, + pageSize: PAGE_SIZE, + filter: queryFilters, + }, + notifyOnNetworkStatusChange: true, + onCompleted: (data) => { + if ((data.projects?.length ?? 0) < PAGE_SIZE) setCanFetchMore(false); + }, + skip: filtersQuery.loading, + }); + + const onFiltersUpdated = useCallback( + ({ payload }: typeof UPDATE_FILTERS_ACTION) => { + updateFilters(payload); + }, + [updateFilters] + ); + + useReduxEffect(onFiltersUpdated, UPDATE_FILTERS_ACTION.type); + + useEffect(() => { + dispatch(setTheme("light")); + }, [dispatch]); + + useEffect(() => { + setCanFetchMore(true); + }, [filters]); + + const openFilters = () => { + dispatch( + openModal({ + Modal: "FiltersModal", + isPageModal: true, + props: { + callbackAction: { + type: UPDATE_FILTERS_ACTION.type, + payload: {}, + }, + initFilters: { + ...filters, + }, + }, + }) + ); + }; + + const selectCategoryTab = (category: Category | null) => { + updateFilters({ + ...(filters ?? {}), + categories: category ? [category] : undefined, + }); + }; + + const clickFetchMore = () => { + fetchMore({ + variables: { + page: Math.floor((data?.projects?.length ?? 0) / PAGE_SIZE) + 1, + }, + }).then((res) => { + if (!res.data.projects || res.data.projects.length < PAGE_SIZE) + setCanFetchMore(false); + }); + }; + + if (error) { + return ( +
+ +
+ ); + } + + const isLoading = + networkStatus === NetworkStatus.loading || + networkStatus === NetworkStatus.refetch || + networkStatus === NetworkStatus.setVariables || + filtersQuery.loading; + const isLoadingMore = networkStatus === NetworkStatus.fetchMore; + const canLoadMore = + !isLoading && + !isLoadingMore && + data?.projects && + data.projects.length > 0 && + canFetchMore; + + return ( + <> + +
+
+
+
+ selectCategoryTab(v)} /> -
-
-
-
selectCategoryTab(v)} />
- -
- p !== null) as any[] ?? []} - /> - {canLoadMore &&
- -
} -
- - ) +
+ +
+ p !== null) as any[]) ?? []} + /> + {canLoadMore && ( +
+ +
+ )} +
+ + ); } export default withBasicProvider(ProjectsFiltersProvider, ExplorePage); - -const UPDATE_FILTERS_ACTION = createAction>('PROJECTS_FILTERS_UPDATED')({}) +const UPDATE_FILTERS_ACTION = createAction>( + "PROJECTS_FILTERS_UPDATED" +)({}); const PAGE_SIZE = 28; type QueryFilter = Partial<{ - categoryId: object | null - tagId: object | null - yearFounded: number | null - dead: boolean | null - license: string | null -}> + categoryId: object | null; + tagId: object | null; + yearFounded: number | null; + dead: boolean | null; + license: string | null; +}>; +const createQueryFilters = ( + filters: Partial | null, + extraFilters: { hiddenCategoriesIds: string[] } +) => { + let filter: QueryFilter = {}; + let hasSearchFilters = false; -const createQueryFilters = (filters: Partial | null, extraFilters: { hiddenCategoriesIds: string[] }) => { - let filter: QueryFilter = {} - let hasSearchFilters = false; + const defaultFilters = { + status: "Published", + }; + if (filters?.categories) { + filter.categoryId = filters?.categories.map((c) => c.id!); + hasSearchFilters = true; + } else { + filter.categoryId = { + _nin: extraFilters.hiddenCategoriesIds, + }; + } - if (filters?.categories) { - filter.categoryId = filters?.categories.map(c => c.id!) - hasSearchFilters = true; - } else { - filter.categoryId = { - _nin: extraFilters.hiddenCategoriesIds - }; - } + if (filters?.tags) { + filter.tagId = { + _in: filters?.tags.map((t) => t.id), + }; + hasSearchFilters = true; + } + if (filters?.yearFounded) { + filter.yearFounded = Number(filters?.yearFounded); + hasSearchFilters = true; + } - if (filters?.tags) { - filter.tagId = { - _in: filters?.tags.map(t => t.id) - } - hasSearchFilters = true; - } + if (filters?.projectStatus) { + filter.dead = filters?.projectStatus === "alive" ? false : true; + hasSearchFilters = true; + } - if (filters?.yearFounded) { - filter.yearFounded = Number(filters?.yearFounded) - hasSearchFilters = true; - } + if (filters?.projectLicense) { + filter.license = filters?.projectLicense; + hasSearchFilters = true; + } - if (filters?.projectStatus) { - filter.dead = filters?.projectStatus === 'alive' ? false : true; - hasSearchFilters = true; - } + if (Object.keys(filter).length === 0) + return { queryFilters: null, hasSearchFilters }; - - if (filters?.projectLicense) { - filter.license = filters?.projectLicense - hasSearchFilters = true; - } - - if (Object.keys(filter).length === 0) - return { queryFilters: null, hasSearchFilters } - - return { queryFilters: filter, hasSearchFilters }; -} \ No newline at end of file + return { queryFilters: { ...defaultFilters, ...filter }, hasSearchFilters }; +};