mirror of
https://github.com/Radiquum/AniX.git
synced 2025-09-04 05:25:36 +05:00
anix/refactor: change Filter Fetch Function
This commit is contained in:
parent
56334893b4
commit
fd0ce8cb94
5 changed files with 536 additions and 73 deletions
|
@ -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
|
||||
|
|
516
app/api/utils.ts
516
app/api/utils.ts
|
@ -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 = {
|
||||
|
|
12
app/discovery/popular/page.tsx
Normal file
12
app/discovery/popular/page.tsx
Normal 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 />;
|
||||
}
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
26
app/pages/IndexFilters.tsx
Normal file
26
app/pages/IndexFilters.tsx
Normal 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 },
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue