From 09ddb71e15405d59d1b6fc61bfbf7dd564c3d666 Mon Sep 17 00:00:00 2001 From: Radiquum Date: Thu, 28 Aug 2025 21:37:47 +0500 Subject: [PATCH] anix/feat: add discovery filter page --- .../Discovery/Modal/FiltersModal.tsx | 14 +- app/discovery/filter/page.tsx | 12 ++ app/pages/DiscoverFilter.tsx | 145 ++++++++++++++++++ app/pages/Search.tsx | 2 +- 4 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 app/discovery/filter/page.tsx create mode 100644 app/pages/DiscoverFilter.tsx diff --git a/app/components/Discovery/Modal/FiltersModal.tsx b/app/components/Discovery/Modal/FiltersModal.tsx index c0069d9..f65aa4e 100644 --- a/app/components/Discovery/Modal/FiltersModal.tsx +++ b/app/components/Discovery/Modal/FiltersModal.tsx @@ -39,9 +39,15 @@ type ModalProps = { isOpen: boolean; setIsOpen: (value: boolean) => void; filter?: Filter; + setFilter?: (filter: Filter) => void; }; -export const FiltersModal = ({ isOpen, setIsOpen, filter }: ModalProps) => { +export const FiltersModal = ({ + isOpen, + setIsOpen, + filter, + setFilter, +}: ModalProps) => { const userStore = useUserStore(); const router = useRouter(); @@ -75,7 +81,11 @@ export const FiltersModal = ({ isOpen, setIsOpen, filter }: ModalProps) => { function saveFilter() { const _filter = JSON.stringify(newFilter); - router.push(`/discovery/filter?filter=${_filter}`); + if (setFilter) { + setFilter(newFilter); + } else { + router.push(`/discovery/filter?filter=${_filter}`); + } setIsOpen(false); } diff --git a/app/discovery/filter/page.tsx b/app/discovery/filter/page.tsx new file mode 100644 index 0000000..a7144c4 --- /dev/null +++ b/app/discovery/filter/page.tsx @@ -0,0 +1,12 @@ +import { DiscoverFilterPage } from "#/pages/DiscoverFilter"; + +export const metadata = { + title: "Фильтр", + description: "Поиск по фильтру", +}; + +export const dynamic = "force-static"; + +export default function Discover() { + return ; +} diff --git a/app/pages/DiscoverFilter.tsx b/app/pages/DiscoverFilter.tsx new file mode 100644 index 0000000..3de2435 --- /dev/null +++ b/app/pages/DiscoverFilter.tsx @@ -0,0 +1,145 @@ +"use client"; + +import { ENDPOINTS } from "#/api/config"; +import { FilterDefault, tryCatchAPI } from "#/api/utils"; +import { FiltersModal } from "#/components/Discovery/Modal/FiltersModal"; +import { ReleaseSection } from "#/components/ReleaseSection/ReleaseSection"; +import { Spinner } from "#/components/Spinner/Spinner"; +import { useScrollPosition } from "#/hooks/useScrollPosition"; +import { useUserStore } from "#/store/auth"; +import { Button } from "flowbite-react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; +import useSWRInfinite from "swr/infinite"; + +const postFetcher = async (url: string, payload: string) => { + const { data, error } = await tryCatchAPI( + fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: payload, + }) + ); + + if (error) { + throw error; + } + return data; +}; + +export const DiscoverFilterPage = () => { + const userStore = useUserStore(); + const searchParams = useSearchParams(); + const router = useRouter(); + const [filter, setFilter] = useState(null); + const [content, setContent] = useState(null); + const [FooterH, setFooterH] = useState(null); + const [FiltersModalOpen, setFiltersModalOpen] = useState(false); + + useEffect(() => { + const queryParams = searchParams.get("filter"); + if (queryParams) { + try { + const _filter = JSON.parse(queryParams); + if (Object.keys(_filter).length != Object.keys(FilterDefault).length) { + setFilter(FilterDefault); + } else { + setFilter(_filter); + } + } catch (e) { + setFilter(FilterDefault); + } + } else { + setFilter(FilterDefault); + } + + if (window) { + setFooterH(document.querySelector("footer").clientHeight + 16); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + setContent(null); + const url = new URL(`/discovery/filter`, window.location.origin); + url.searchParams.set("filter", JSON.stringify(filter)); + router.replace(url.toString()); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filter]); + + const getKey = (pageIndex: number, previousPageData: any) => { + if (!filter) return null; + + if (previousPageData && !previousPageData.content.length) return null; + + let url = `${ENDPOINTS.filter}/${pageIndex}`; + if (userStore.token) { + url += `?token=${userStore.token}`; + } + return [url, JSON.stringify(filter)]; + }; + + const { data, error, isLoading, size, setSize } = useSWRInfinite( + getKey, + ([url, payload]) => postFetcher(url, payload), + { initialSize: 2 } + ); + + useEffect(() => { + if (data) { + let _content = []; + for (let i = 0; i < data.length; i++) { + _content.push(...data[i].content); + } + setContent(_content); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data]); + + const scrollPosition = useScrollPosition(); + useEffect(() => { + if (scrollPosition >= 98 && scrollPosition <= 99) { + setSize(size + 1); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [scrollPosition]); + + if (!filter) return <>; + + return ( +
+ {error ? +
+
+

Произошла ошибка фильтра

+
+
+ : <>} + {content ? + + : <>} + {isLoading ? +
+ +
+ : ""} + + +
+ ); +}; diff --git a/app/pages/Search.tsx b/app/pages/Search.tsx index 6647906..0f58262 100644 --- a/app/pages/Search.tsx +++ b/app/pages/Search.tsx @@ -243,7 +243,7 @@ export function SearchPage() { return [url, JSON.stringify({ query, searchBy })]; }; - const { data, error, isLoading, size, setSize, mutate } = useSWRInfinite( + const { data, error, isLoading, size, setSize } = useSWRInfinite( getKey, ([url, payload]) => postFetcher(url, payload), { initialSize: 2 }