anix/refactor: change Filter Fetch Function

This commit is contained in:
Kentai Radiquum 2025-08-25 19:40:51 +05:00
parent 56334893b4
commit fd0ce8cb94
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
5 changed files with 536 additions and 73 deletions

View file

@ -72,7 +72,7 @@ export const App = (props) => {
className={`${inter.className} overflow-x-hidden dark:bg-[#0d1117] dark:text-white`}
>
<NavBarPc setIsSettingModalOpen={setIsSettingModalOpen} />
<main className="container px-2 pt-4 pb-24 mx-auto sm:pb-0">
<main className="container px-2 pt-4 pb-24 mx-auto lg:pb-0">
{props.children}
</main>
<ChangelogModal

View file

@ -278,7 +278,8 @@ export function minutesToTime(min: number) {
`${minutes} ${numberDeclension(minutes, "минута", "минуты", "минут")}`
: "";
if (days > 0 && hours > 0 && minutes > 0) return `${dayDisplay}, ${hourDisplay}, ${minuteDisplay}`;
if (days > 0 && hours > 0 && minutes > 0)
return `${dayDisplay}, ${hourDisplay}, ${minuteDisplay}`;
if (days > 0 && hours > 0) return `${dayDisplay}, ${hourDisplay}`;
if (days > 0 && minutes > 0) return `${dayDisplay}, ${minuteDisplay}`;
if (hours > 0 && minutes > 0) return `${hourDisplay}, ${minuteDisplay}`;
@ -294,65 +295,480 @@ const StatusList: Record<string, null | number> = {
announce: 3,
};
export async function _FetchHomePageReleases(
status: string,
token: string | null,
page: string | number = 0
) {
let statusId: null | number = null;
let categoryId: null | number = null;
if (status == "films") {
categoryId = 2;
} else {
statusId = StatusList[status];
}
const body = {
country: null,
season: null,
sort: 0,
studio: null,
age_ratings: [],
category_id: categoryId,
end_year: null,
episode_duration_from: null,
episode_duration_to: null,
export const FilterCountry = ["Япония", "Китай", "Южная Корея"];
export const FilterCategoryIdToString: Record<number, string> = {
1: "Сериал",
2: "Полнометражный фильм",
3: "OVA",
4: "Дорама",
};
export const FilterGenre = {
uncategorized: {
name: "Нет категории",
genres: [
"авангард",
"гурман",
"драма",
"комедия",
"повседневность",
"приключения",
"романтика",
"сверхъестественное",
"спорт",
"тайна",
"триллер",
"ужасы",
"фантастика",
"фэнтези",
"экшен",
"эротика",
"этти",
],
},
audience: {
name: "Аудитория",
genres: [
"детское",
"дзёсей",
"сэйнэн",
"сёдзё",
"сёдзё-ай",
"сёнен",
"сёнен-ай",
],
},
theme: {
name: "Тематика",
genres: [
"CGDCT",
"антропоморфизм",
"боевые искусства",
"вампиры",
"взрослые персонажи",
"видеоигры",
"военное",
"выживание",
"гарем",
"гонки",
"городское фэнтези",
"гэг-юмор",
"детектив",
"жестокость",
"забота о детях",
"злодейка",
"игра с высокими ставками",
"идолы (жен.)",
"идолы (муж.)",
"изобразительное искусство",
"исполнительское искусство",
"исторический",
"исэкай",
"иясикэй",
"командный спорт",
"космос",
"кроссдрессинг",
"культура отаку",
"любовный многоугольник",
"магическая смена пола",
"махо-сёдзё",
"медицина",
"меха",
"мифология",
"музыка",
"образовательное",
"организованная преступность",
"пародия",
"питомцы",
"психологическое",
"путешествие во времени",
"работа",
"реверс-гарем",
"реинкарнация",
"романтический подтекст",
"самураи",
"спортивные единоборства",
"стратегические игры",
"супер сила",
"удостоено наград",
"хулиганы",
"школа",
"шоу-бизнес",
],
},
};
export const FilterProfileListIdToString: Record<number, string> = {
0: "Избранное",
1: "Смотрю",
2: "В планах",
3: "Просмотрено",
4: "Отложено",
5: "Брошено",
};
export const FilterStudio = [
"Неважно",
"A-1 Pictures",
"A.C.G.T",
"ACTAS, Inc",
"ACiD FiLM",
"AIC A.S.T.A",
"AIC PLUS",
"AIC Spirits",
"AIC",
"Animac",
"ANIMATE",
"Aniplex",
"ARMS",
"Artland",
"ARTMIC Studios",
"Asahi Production",
"Asia-Do",
"ASHI",
"Asread",
"Asmik Ace",
"Aubeck",
"BM Entertainment",
"Bandai Visua",
"Barnum Studio",
"Bee Train",
"BeSTACK",
"Blender Foundation",
"Bones",
"Brains Base",
"Bridge",
"Cinema Citrus",
"Chaos Project",
"Cherry Lips",
"David Production",
"Daume",
"Doumu",
"Dax International",
"DLE INC",
"Digital Frontier",
"Digital Works",
"Diomedea",
"DIRECTIONS Inc",
"Dogakobo",
"Dofus",
"Encourage Films",
"Feel",
"Fifth Avenue",
"Five Ways",
"Fuji TV",
"Foursome",
"GRAM Studio",
"G&G Entertainment",
"Gainax",
"GANSIS",
"Gathering",
"Gonzino",
"Gonzo",
"GoHands",
"Green Bunny",
"Group TAC",
"Hal Film Maker",
"Hasbro Studios",
"h.m.p",
"Himajin",
"Hoods Entertainment",
"Idea Factory",
"J.C.Staff",
"KANSAI",
"Kaname Production",
"Kitty Films",
"Knack",
"Kokusai Eigasha",
"KSS (студия)",
"Kyoto Animation",
"Lemon Heart",
"LMD",
"Madhouse Studios",
"Magic Bus",
"Manglobe Inc.",
"Manpuku Jinja",
"MAPPA",
"Milky",
"Minamimachi Bugyosho",
"Media Blasters",
"Mook Animation",
"Moonrock",
"MOVIC",
"Mushi Productions",
"Natural High",
"Nippon Animation",
"Nomad",
"Lerche",
"OB Planning",
"Office AO",
"Ordet",
"Oriental Light and Magic",
"OLM Inc.",
"P.A. Works",
"Palm Studio",
"Pastel",
"Phoenix Entertainment",
"Picture Magic",
"Pink",
"Pink Pineapple",
"Planet",
"Plum",
"PPM",
"Primastea",
"Production I.G",
"Project No.9",
"Radix",
"Rikuentai",
"Robot",
"Satelight",
"Seven",
"Seven Arcs",
"Shaft",
"Silver Link",
"Shinei Animation",
"Shogakukan Music & Digital Entertainment",
"Soft on Demand",
"Starchild Records",
"Studio 9 Maiami",
"Studio Tulip",
"Studio 4°C",
"Studio e.go!",
"Studio A.P.P.P",
"Studio Barcelona",
"Studio Blanc",
"Studio Comet",
"Studio Deen",
"Studio Fantasia",
"Studio Flag",
"Studio Gallop",
"Studio Ghibli",
"Studio Guts",
"Studio Gokumi",
"Studio Rikka",
"Studio Hibari",
"Studio Junio",
"Studio Khara",
"Studio Live",
"Studio Matrix",
"Studio Pierrot",
"Studio Egg",
"Sunrise",
"Synergy SP",
"Synergy Japan",
"Tatsunoko Production",
"Tele-Cartoon Japan",
"Telecom Animation Film",
"Tezuka Productions",
"The Answer Studio",
"TMS",
"TNK",
"Toei Animation",
"Tokyo Kids",
"TYO Animations",
"Transarts",
"Triangle Staff",
"Trinet Entertainment",
"Ufotable",
"Vega Entertainment",
"Victor Entertainment",
"Viewworks",
"White Fox",
"Wonder Farm",
"XEBEC-M2",
"Xebec",
"Yumeta Company",
"Zexcs",
"Zuiyo Eizo",
"8bit",
];
export const FilterSource = [
"Оригинал",
"Манга",
"Веб-манга",
"Енкома",
"Ранобэ",
"Новелла",
"Веб-новелла",
"Визуальная новелла",
"Игра",
"Карточная игра",
"Книга",
"Книга с картинками",
"Музыка",
"Радио",
"Более одного",
"Другое",
];
export const FilterYear = Array.from({ length: 200 }, (_, i) => 1900 + i); // 1900-2100 years around now
export const FilterSeasonIdToString = {
1: "Зима",
2: "Весна",
3: "Лето",
4: "Осень",
};
export const FilterEpisodeCount = [
{
name: "Неважно",
episodes_from: null,
episodes_to: null,
genres: [],
profile_list_exclusions: [],
start_year: null,
status_id: statusId,
types: [],
is_genres_exclude_mode_enabled: false,
};
},
{
name: "От 1 до 12",
episodes_from: 1,
episodes_to: 12,
},
{
name: "От 13 до 25",
episodes_from: 13,
episodes_to: 25,
},
{
name: "От 26 до 100",
episodes_from: 26,
episodes_to: 100,
},
{
name: "Больше 100",
episodes_from: 100,
episodes_to: null,
},
];
export const FilterEpisodeDuration = [
{
name: "Неважно",
episode_duration_from: null,
episode_duration_to: null,
},
{
name: "До 10 минут",
episode_duration_from: 1,
episode_duration_to: 10,
},
{
name: "До 30 минут",
episode_duration_from: 1,
episode_duration_to: 30,
},
{
name: "Более 30 минут",
episode_duration_from: 30,
episode_duration_to: null,
},
];
export const FilterStatusIdToString = {
1: "Вышел",
2: "Выходит",
3: "Анонс",
};
export const FilterAgeRatingToString = {
1: "0+",
2: "6+",
3: "12+",
4: "16+",
5: "18+",
};
export const FilterSortToString = {
0: "По дате добавления",
1: "По рейтингу",
2: "По годам",
3: "По популярности",
};
export type Filter = {
country: null | string;
category_id: null | number;
genres: string[];
is_genres_exclude_mode_enabled: boolean;
profile_list_exclusions: number[];
types: number[]; // fetched from /type/all
studio: string[];
source: null | string;
start_year: null | number;
end_year: null | number;
season: null | number;
episodes_from: null | number;
episodes_to: null | number;
episode_duration_from: null | number;
episode_duration_to: null | number;
status_id: null | number;
age_ratings: number[];
sort: number;
};
export const FilterDefault: Filter = {
country: null,
season: null,
sort: 0,
source: null,
studio: null,
age_ratings: [],
category_id: null,
end_year: null,
episode_duration_from: null,
episode_duration_to: null,
episodes_from: null,
episodes_to: null,
genres: [],
is_genres_exclude_mode_enabled: false,
profile_list_exclusions: [],
start_year: null,
status_id: null,
types: [],
};
export async function FetchFilter(
{
country,
category_id,
genres,
is_genres_exclude_mode_enabled,
profile_list_exclusions,
types,
studio,
source,
start_year,
end_year,
season,
episodes_from,
episodes_to,
episode_duration_from,
episode_duration_to,
status_id,
age_ratings,
sort,
}: Filter,
page: number,
token: null | string
) {
let url: string;
url = `${ENDPOINTS.filter}/${page}`;
if (token) {
url += `?token=${token}`;
}
const data: Object = fetch(url, {
method: "POST",
headers: HEADERS,
body: JSON.stringify(body),
})
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error("Error fetching data");
}
const { data, error } = await fetchDataViaPost(
url,
JSON.stringify({
country,
category_id,
genres,
is_genres_exclude_mode_enabled,
profile_list_exclusions,
types,
studio,
source,
start_year,
end_year,
season,
episodes_from,
episodes_to,
episode_duration_from,
episode_duration_to,
status_id,
age_ratings,
sort,
})
.then((data: Object) => {
return data;
})
.catch((error) => {
console.log(error);
return null;
});
return data;
);
return [data, error];
}
export const BookmarksList = {

View file

@ -0,0 +1,12 @@
import { DiscoverPage } from "#/pages/Discover";
export const metadata = {
title: "Обзор - Популярное",
description: "Популярные релизы",
};
export const dynamic = "force-static";
export default function Discover() {
return <DiscoverPage />;
}

View file

@ -3,15 +3,22 @@ import { ReleaseCourusel } from "#/components/ReleaseCourusel/ReleaseCourusel";
import { Spinner } from "#/components/Spinner/Spinner";
import { useUserStore } from "#/store/auth";
import { useState, useEffect } from "react";
import { _FetchHomePageReleases } from "#/api/utils";
import { FetchFilter } from "#/api/utils";
import { usePreferencesStore } from "#/store/preferences";
import { useRouter } from "next/navigation";
import {
ListAnnounce,
ListFilms,
ListFinished,
ListLast,
ListOngoing,
} from "./IndexFilters";
export function IndexPage() {
const token = useUserStore((state) => state.token);
const preferenceStore = usePreferencesStore();
const router = useRouter()
const router = useRouter();
const [isLoading, setIsLoading] = useState(true);
const [lastReleasesData, setLastReleasesData] = useState(null);
const [ongoingReleasesData, setOngoingReleasesData] = useState(null);
@ -21,7 +28,9 @@ export function IndexPage() {
useEffect(() => {
if (preferenceStore.params.skipToCategory.enabled) {
router.push(`/home/${preferenceStore.params.skipToCategory.homeCategory}`);
router.push(
`/home/${preferenceStore.params.skipToCategory.homeCategory}`
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@ -35,11 +44,19 @@ export function IndexPage() {
setAnnounceReleasesData(null);
setFilmsReleasesData(null);
const lastReleases = await _FetchHomePageReleases("last", token);
const ongoingReleases = await _FetchHomePageReleases("ongoing", token);
const finishedReleases = await _FetchHomePageReleases("finished", token);
const announceReleases = await _FetchHomePageReleases("announce", token);
const filmsReleases = await _FetchHomePageReleases("films", token);
const [lastReleases] = await FetchFilter(ListLast.filter, 0, token);
const [ongoingReleases] = await FetchFilter(ListOngoing.filter, 0, token);
const [announceReleases] = await FetchFilter(
ListAnnounce.filter,
0,
token
);
const [finishedReleases] = await FetchFilter(
ListFinished.filter,
0,
token
);
const [filmsReleases] = await FetchFilter(ListFilms.filter, 0, token);
setLastReleasesData(lastReleases);
setOngoingReleasesData(ongoingReleases);
@ -56,16 +73,12 @@ export function IndexPage() {
return (
<>
{lastReleasesData ? (
{lastReleasesData && (
<ReleaseCourusel
sectionTitle="Последние релизы"
showAllLink="/home/last"
content={lastReleasesData.content}
/>
) : (
<div className="flex items-center justify-center min-w-full min-h-screen">
<Spinner />
</div>
)}
{finishedReleasesData && (
<ReleaseCourusel
@ -95,15 +108,11 @@ export function IndexPage() {
content={filmsReleasesData.content}
/>
)}
{!isLoading &&
!lastReleasesData &&
!finishedReleasesData &&
!ongoingReleasesData &&
!announceReleasesData && (
<div className="flex items-center justify-center min-w-full min-h-screen">
<h1 className="text-2xl">Ошибка загрузки контента...</h1>
</div>
)}
{isLoading && (
<div className="flex items-center justify-center h-32 min-w-full">
<Spinner />
</div>
)}
</>
);
}

View file

@ -0,0 +1,26 @@
import { Filter, FilterDefault } from "#/api/utils";
export const ListLast = {
name: "Последнее",
filter: FilterDefault,
};
export const ListOngoing = {
name: "Онгоинги",
filter: { ...FilterDefault, status_id: 2 },
};
export const ListAnnounce = {
name: "Анонсы",
filter: { ...FilterDefault, status_id: 3 },
};
export const ListFinished = {
name: "Завершённые",
filter: { ...FilterDefault, status_id: 1 },
};
export const ListFilms = {
name: "Фильмы",
filter: { ...FilterDefault, category_id: 2, status_id: null },
};