mirror of
https://github.com/Radiquum/AniX.git
synced 2025-09-03 21:15:35 +05:00
Merge pull request #16 from Radiquum/refactor__search
Some checks failed
Build and Publish 'anix-api-prox' to Docker Hub / publish (push) Waiting to run
Build and Publish 'anix' to Docker Hub / publish (push) Waiting to run
Build and Publish 'anix-player-parsers' to Docker Hub / publish (push) Has been cancelled
Some checks failed
Build and Publish 'anix-api-prox' to Docker Hub / publish (push) Waiting to run
Build and Publish 'anix' to Docker Hub / publish (push) Waiting to run
Build and Publish 'anix-player-parsers' to Docker Hub / publish (push) Has been cancelled
v3.8.0
This commit is contained in:
commit
e64118a2a1
29 changed files with 402 additions and 440 deletions
|
@ -18,7 +18,7 @@ app.use(function (req, res, next) {
|
||||||
res.header("Access-Control-Allow-Origin", req.headers.origin || "*");
|
res.header("Access-Control-Allow-Origin", req.headers.origin || "*");
|
||||||
res.header(
|
res.header(
|
||||||
"Access-Control-Allow-Headers",
|
"Access-Control-Allow-Headers",
|
||||||
"Origin, X-Requested-With, Content-Type, Accept, Sign"
|
"Origin, X-Requested-With, Content-Type, Accept, Sign, Allow, User-Agent, Api-Version"
|
||||||
);
|
);
|
||||||
res.header("Access-Control-Allow-Methods", "GET,HEAD,POST,OPTIONS");
|
res.header("Access-Control-Allow-Methods", "GET,HEAD,POST,OPTIONS");
|
||||||
next();
|
next();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export const corsHeaders = {
|
export const corsHeaders = {
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Access-Control-Allow-Headers":
|
"Access-Control-Allow-Headers":
|
||||||
"Origin, X-Requested-With, Content-Type, Accept, Sign",
|
"Origin, X-Requested-With, Content-Type, Accept, Sign, Allow, User-Agent, Api-Version",
|
||||||
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
|
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
|
||||||
"Cache-Control": "no-cache",
|
"Cache-Control": "no-cache",
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const CURRENT_APP_VERSION = "3.7.0";
|
export const CURRENT_APP_VERSION = "3.8.0";
|
||||||
import { env } from "next-runtime-env";
|
import { env } from "next-runtime-env";
|
||||||
|
|
||||||
const NEXT_PUBLIC_API_URL = env("NEXT_PUBLIC_API_URL") || null;
|
const NEXT_PUBLIC_API_URL = env("NEXT_PUBLIC_API_URL") || null;
|
||||||
|
@ -51,7 +51,14 @@ export const ENDPOINTS = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
filter: `${API_PREFIX}/filter`,
|
filter: `${API_PREFIX}/filter`,
|
||||||
search: `${API_URL}/search`,
|
search: {
|
||||||
|
profileList: `${API_PREFIX}/search/profile/list`,
|
||||||
|
profileHistory: `${API_PREFIX}/search/history`,
|
||||||
|
profileFavoriteCollection: `${API_PREFIX}/search/favoriteCollections`,
|
||||||
|
profileFavorites: `${API_PREFIX}/search/favorites`,
|
||||||
|
profiles: `${API_PREFIX}/search/profiles`,
|
||||||
|
releases: `${API_PREFIX}/search/releases`,
|
||||||
|
},
|
||||||
statistic: {
|
statistic: {
|
||||||
addHistory: `${API_PREFIX}/history/add`,
|
addHistory: `${API_PREFIX}/history/add`,
|
||||||
markWatched: `${API_PREFIX}/episode/watch`,
|
markWatched: `${API_PREFIX}/episode/watch`,
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import { NextResponse } from "next/server";
|
|
||||||
import { NextRequest } from "next/server";
|
|
||||||
import { fetchDataViaPost } from "../utils";
|
|
||||||
import { ENDPOINTS } from "../config";
|
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
|
||||||
const page = parseInt(request.nextUrl.searchParams.get("page")) || 0;
|
|
||||||
const query = decodeURI(request.nextUrl.searchParams.get("q")) || null;
|
|
||||||
const token = request.nextUrl.searchParams.get("token") || null;
|
|
||||||
|
|
||||||
const where = request.nextUrl.searchParams.get("where") || "releases";
|
|
||||||
const searchBy = parseInt(request.nextUrl.searchParams.get("searchBy")) || 0;
|
|
||||||
const list = parseInt(request.nextUrl.searchParams.get("list")) || null;
|
|
||||||
|
|
||||||
let url: URL;
|
|
||||||
|
|
||||||
if (where == "list") {
|
|
||||||
if (!list) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ message: "List ID required" },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!token) {
|
|
||||||
return NextResponse.json({ message: "token required" }, { status: 400 });
|
|
||||||
}
|
|
||||||
url = new URL(`${ENDPOINTS.search}/profile/list/${list}/${page}`);
|
|
||||||
} else if (where == "history") {
|
|
||||||
if (!token) {
|
|
||||||
return NextResponse.json({ message: "token required" }, { status: 400 });
|
|
||||||
}
|
|
||||||
url = new URL(`${ENDPOINTS.search}/history/${page}`);
|
|
||||||
} else if (where == "favorites") {
|
|
||||||
if (!token) {
|
|
||||||
return NextResponse.json({ message: "token required" }, { status: 400 });
|
|
||||||
}
|
|
||||||
url = new URL(`${ENDPOINTS.search}/favorites/${page}`);
|
|
||||||
} else if (where == "collections") {
|
|
||||||
if (!token) {
|
|
||||||
return NextResponse.json({ message: "token required" }, { status: 400 });
|
|
||||||
}
|
|
||||||
url = new URL(`${ENDPOINTS.search}/favoriteCollections/${page}`);
|
|
||||||
} else if (where == "profiles") {
|
|
||||||
url = new URL(`${ENDPOINTS.search}/profiles/${page}`);
|
|
||||||
} else {
|
|
||||||
url = new URL(`${ENDPOINTS.search}/releases/${page}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token) {
|
|
||||||
url.searchParams.set("token", token);
|
|
||||||
}
|
|
||||||
const body = { query, searchBy };
|
|
||||||
|
|
||||||
const { data, error } = await fetchDataViaPost(
|
|
||||||
url.toString(),
|
|
||||||
JSON.stringify(body),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
if (error) {
|
|
||||||
return new Response(JSON.stringify(error), {
|
|
||||||
status: 500,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(JSON.stringify(data), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -2,6 +2,8 @@ export const metadata = {
|
||||||
title: "Закладки",
|
title: "Закладки",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
import { BookmarksPage } from "#/pages/Bookmarks";
|
import { BookmarksPage } from "#/pages/Bookmarks";
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
return <BookmarksPage />;
|
return <BookmarksPage />;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ViewCollectionPage } from "#/pages/ViewCollection";
|
import { ViewCollectionPage } from "#/pages/ViewCollection";
|
||||||
import { fetchDataViaGet } from "#/api/utils";
|
import { fetchDataViaGet } from "#/api/utils";
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
export const dynamic = "force-static";
|
import { API_URL } from "#/api/config";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params },
|
{ params },
|
||||||
|
@ -9,7 +9,7 @@ export async function generateMetadata(
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
const id = params.id;
|
const id = params.id;
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/collection/${id}`
|
`${API_URL}/collection/${id}`
|
||||||
);
|
);
|
||||||
const previousOG = (await parent).openGraph;
|
const previousOG = (await parent).openGraph;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ export const metadata = {
|
||||||
description: "Создание новой коллекции",
|
description: "Создание новой коллекции",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Collections() {
|
export default function Collections() {
|
||||||
return <CreateCollectionPage />;
|
return <CreateCollectionPage />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ export const metadata = {
|
||||||
description: "Просмотр избранных коллекций",
|
description: "Просмотр избранных коллекций",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Collections() {
|
export default function Collections() {
|
||||||
return <CollectionsFullPage type="favorites" title="Избранные коллекции" />;
|
return <CollectionsFullPage type="favorites" title="Избранные коллекции" />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ export const metadata = {
|
||||||
description: "Просмотр и управление коллекциями",
|
description: "Просмотр и управление коллекциями",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Collections() {
|
export default function Collections() {
|
||||||
return <CollectionsPage />;
|
return <CollectionsPage />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const RelatedSection = (props: any) => {
|
||||||
<div className="flex items-center justify-center p-4">
|
<div className="flex items-center justify-center p-4">
|
||||||
{props.images.map((item, index) => {
|
{props.images.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<div key={`related-img-${index}`} className="w-[100px] lg:w-[300px] aspect-[9/12] even:scale-110 shadow-md even:shadow-lg even:z-30 origin-center first:[transform:translateX(25%)] last:[transform:translateX(-25%)]">
|
<div key={`related-img-${index}`} className="w-[100px] lg:w-[300px] aspect-[9/12] even:scale-110 shadow-md even:[box-shadow:_0px_0px_16px_black;] even:z-30 origin-center first:[transform:translateX(25%)] last:[transform:translateX(-25%)] rounded-lg overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
fill={true}
|
fill={true}
|
||||||
src={item}
|
src={item}
|
||||||
|
|
|
@ -4,6 +4,8 @@ export const metadata = {
|
||||||
|
|
||||||
import { FavoritesPage } from "#/pages/Favorites";
|
import { FavoritesPage } from "#/pages/Favorites";
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
return <FavoritesPage />;
|
return <FavoritesPage />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ export const metadata = {
|
||||||
|
|
||||||
import { HistoryPage } from "#/pages/History";
|
import { HistoryPage } from "#/pages/History";
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
return <HistoryPage />;
|
return <HistoryPage />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ export const metadata = {
|
||||||
description: "Вход в аккаунт anixart",
|
description: "Вход в аккаунт anixart",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
return <LoginPage />;
|
return <LoginPage />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ export const metadata = {
|
||||||
|
|
||||||
import { MenuPage } from "#/pages/MobileMenuPage";
|
import { MenuPage } from "#/pages/MobileMenuPage";
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
return <MenuPage />;
|
return <MenuPage />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { IndexPage } from "./pages/Index";
|
import { IndexPage } from "./pages/Index";
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Index() {
|
export default function Index() {
|
||||||
return <IndexPage />;
|
return <IndexPage />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,138 +1,258 @@
|
||||||
"use client";
|
"use client";
|
||||||
import useSWRInfinite from "swr/infinite";
|
|
||||||
import { ReleaseSection } from "#/components/ReleaseSection/ReleaseSection";
|
|
||||||
import { RelatedSection } from "#/components/RelatedSection/RelatedSection";
|
|
||||||
import { Spinner } from "#/components/Spinner/Spinner";
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useScrollPosition } from "#/hooks/useScrollPosition";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import { useUserStore } from "../store/auth";
|
import { useRouter } from "next/navigation";
|
||||||
import { Button, Dropdown, DropdownItem, Modal, ModalBody, ModalFooter, ModalHeader } from "flowbite-react";
|
|
||||||
import { CollectionsSection } from "#/components/CollectionsSection/CollectionsSection";
|
import { Dropdown, DropdownItem } from "flowbite-react";
|
||||||
|
import { useUserStore } from "#/store/auth";
|
||||||
|
import { ENDPOINTS } from "#/api/config";
|
||||||
|
import { tryCatchAPI } from "#/api/utils";
|
||||||
|
import useSWRInfinite from "swr/infinite";
|
||||||
|
import { Spinner } from "#/components/Spinner/Spinner";
|
||||||
|
import { ReleaseSection } from "#/components/ReleaseSection/ReleaseSection";
|
||||||
import { UserSection } from "#/components/UserSection/UserSection";
|
import { UserSection } from "#/components/UserSection/UserSection";
|
||||||
import { useSWRfetcher } from "#/api/utils";
|
import { CollectionsSection } from "#/components/CollectionsSection/CollectionsSection";
|
||||||
|
import { useScrollPosition } from "#/hooks/useScrollPosition";
|
||||||
|
import { RelatedSection } from "#/components/RelatedSection/RelatedSection";
|
||||||
|
|
||||||
const ListsMapping = {
|
const postFetcher = async (url: string, payload: string) => {
|
||||||
watching: {
|
const { data, error } = await tryCatchAPI(
|
||||||
name: "Смотрю",
|
fetch(url, {
|
||||||
id: 1,
|
method: "POST",
|
||||||
},
|
headers: {
|
||||||
planned: {
|
"Api-Version": "v2",
|
||||||
name: "В планах",
|
"Content-Type": "application/json",
|
||||||
id: 2,
|
},
|
||||||
},
|
body: payload,
|
||||||
watched: {
|
})
|
||||||
name: "Просмотрено",
|
);
|
||||||
id: 3,
|
|
||||||
},
|
if (error) {
|
||||||
delayed: {
|
throw error;
|
||||||
name: "Отложено",
|
}
|
||||||
id: 4,
|
return data;
|
||||||
},
|
|
||||||
abandoned: {
|
|
||||||
name: "Заброшено",
|
|
||||||
id: 5,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TagMapping = {
|
const whereMapping = [
|
||||||
name: {
|
{
|
||||||
name: "Названию",
|
id: "releases",
|
||||||
id: 0,
|
label: "Релизах",
|
||||||
|
auth: false,
|
||||||
},
|
},
|
||||||
studio: {
|
{
|
||||||
name: "Студии",
|
id: "profiles",
|
||||||
id: 1,
|
label: "Профилях",
|
||||||
|
auth: false,
|
||||||
},
|
},
|
||||||
director: {
|
{
|
||||||
name: "Режиссёру",
|
id: "list",
|
||||||
id: 2,
|
label: "Списках",
|
||||||
|
auth: true,
|
||||||
},
|
},
|
||||||
author: {
|
{
|
||||||
name: "Автору",
|
id: "history",
|
||||||
id: 3,
|
label: "Истории",
|
||||||
|
auth: true,
|
||||||
},
|
},
|
||||||
tag: {
|
{
|
||||||
name: "Тегу",
|
id: "favorites",
|
||||||
id: 4,
|
label: "Избранном",
|
||||||
|
auth: true,
|
||||||
},
|
},
|
||||||
};
|
{
|
||||||
|
id: "collections",
|
||||||
|
label: "Коллекциях",
|
||||||
|
auth: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const WhereMapping = {
|
const searchByMapping = {
|
||||||
releases: "Релизах",
|
releases: [
|
||||||
list: "Списках",
|
{
|
||||||
history: "Истории",
|
id: "name",
|
||||||
favorites: "Избранном",
|
label: "Названию",
|
||||||
collections: "Коллекциях",
|
value: 0,
|
||||||
profiles: "Профилях",
|
},
|
||||||
|
{
|
||||||
|
id: "studio",
|
||||||
|
label: "Студии",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "director",
|
||||||
|
label: "Режиссёру",
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "author",
|
||||||
|
label: "Автору",
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tag",
|
||||||
|
label: "Тегу",
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
id: "watching",
|
||||||
|
label: "Смотрю",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "planned",
|
||||||
|
label: "В планах",
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "watched",
|
||||||
|
label: "Просмотрено",
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "delayed",
|
||||||
|
label: "Отложено",
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "abandoned",
|
||||||
|
label: "Заброшено",
|
||||||
|
value: 5,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
none: [{ id: "none", label: "Нет", value: 0 }],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SearchPage() {
|
export function SearchPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [query, setQuery] = useState(searchParams.get("q") || "");
|
|
||||||
const [searchVal, setSearchVal] = useState(searchParams.get("q") || "");
|
|
||||||
const [where, setWhere] = useState(searchParams.get("where") || "releases");
|
|
||||||
const [searchBy, setSearchBy] = useState(
|
|
||||||
searchParams.get("searchBy") || "name"
|
|
||||||
);
|
|
||||||
const [list, setList] = useState(searchParams.get("list") || "watching");
|
|
||||||
const [filtersModalOpen, setFiltersModalOpen] = useState(false);
|
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const [query, setQuery] = useState(searchParams.get("query") || "");
|
||||||
|
const [params, setParams] = useState(null);
|
||||||
|
const [content, setContent] = useState(null);
|
||||||
|
|
||||||
|
const [HeaderH, setHeaderH] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const queryParams = searchParams.get("params");
|
||||||
|
|
||||||
|
if (queryParams) {
|
||||||
|
try {
|
||||||
|
setParams(JSON.parse(queryParams));
|
||||||
|
} catch (e) {
|
||||||
|
setParams({
|
||||||
|
where: "releases",
|
||||||
|
searchBy: "name",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setParams({
|
||||||
|
where: "releases",
|
||||||
|
searchBy: "name",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window) {
|
||||||
|
setHeaderH(document.querySelector("header").clientHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!params) return;
|
||||||
|
|
||||||
|
const url = new URL(`/search`, window.location.origin);
|
||||||
|
url.searchParams.set("query", query);
|
||||||
|
url.searchParams.set("params", JSON.stringify(params));
|
||||||
|
router.replace(url.toString());
|
||||||
|
setContent(null);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setContent(null);
|
||||||
|
|
||||||
|
const url = new URL(`/search`, window.location.origin);
|
||||||
|
url.searchParams.set("query", query);
|
||||||
|
url.searchParams.set("params", JSON.stringify(params));
|
||||||
|
router.replace(url.toString());
|
||||||
|
}, [query]);
|
||||||
|
|
||||||
const getKey = (pageIndex: number, previousPageData: any) => {
|
const getKey = (pageIndex: number, previousPageData: any) => {
|
||||||
if (where == "releases") {
|
if (!params) return null;
|
||||||
if (previousPageData && !previousPageData.releases.length) return null;
|
if (!query) return null;
|
||||||
} else {
|
|
||||||
if (previousPageData && !previousPageData.content.length) return null;
|
if (previousPageData) {
|
||||||
|
if (params.where == "releases") {
|
||||||
|
if (!previousPageData.releases.length) return null;
|
||||||
|
} else {
|
||||||
|
if (!previousPageData.content.length) return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL("/api/search", window.location.origin);
|
let url = null;
|
||||||
url.searchParams.set("page", pageIndex.toString());
|
switch (params.where) {
|
||||||
|
case "releases":
|
||||||
|
url = `${ENDPOINTS.search.releases}/${pageIndex}`;
|
||||||
|
break;
|
||||||
|
case "profiles":
|
||||||
|
url = `${ENDPOINTS.search.profiles}/${pageIndex}`;
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
const list = searchByMapping[params.where].find(
|
||||||
|
(item) => item.id == params.searchBy
|
||||||
|
);
|
||||||
|
if (!list) break;
|
||||||
|
url = `${ENDPOINTS.search.profileList}/${list.value}/${pageIndex}`;
|
||||||
|
break;
|
||||||
|
case "history":
|
||||||
|
url = `${ENDPOINTS.search.profileHistory}/${pageIndex}`;
|
||||||
|
break;
|
||||||
|
case "favorites":
|
||||||
|
url = `${ENDPOINTS.search.profileFavorites}/${pageIndex}`;
|
||||||
|
break;
|
||||||
|
case "collections":
|
||||||
|
url = `${ENDPOINTS.search.profileFavoriteCollection}/${pageIndex}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (userStore.token) {
|
if (userStore.token) {
|
||||||
url.searchParams.set("token", userStore.token);
|
url += `?token=${userStore.token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (where) {
|
let searchBy = null;
|
||||||
url.searchParams.set("where", where);
|
const _sbym = searchByMapping[params.where];
|
||||||
|
if (_sbym) {
|
||||||
|
searchBy = _sbym.find((item) => item.id == params.searchBy).value;
|
||||||
|
} else {
|
||||||
|
searchBy = searchByMapping["none"][0].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (where == "list" && list && ListsMapping.hasOwnProperty(list)) {
|
return [url, JSON.stringify({ query, searchBy })];
|
||||||
url.searchParams.set("list", ListsMapping[list].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
url.searchParams.set("searchBy", TagMapping[searchBy].id);
|
|
||||||
|
|
||||||
if (query) {
|
|
||||||
url.searchParams.set("q", query);
|
|
||||||
return url.toString();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data, error, isLoading, size, setSize } = useSWRInfinite(
|
const { data, error, isLoading, size, setSize, mutate } = useSWRInfinite(
|
||||||
getKey,
|
getKey,
|
||||||
useSWRfetcher,
|
([url, payload]) => postFetcher(url, payload),
|
||||||
{ initialSize: 2, revalidateFirstPage: false }
|
{ initialSize: 2 }
|
||||||
);
|
);
|
||||||
|
|
||||||
const [content, setContent] = useState(null);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
let allReleases = [];
|
let _content = [];
|
||||||
if (where == "releases") {
|
if (params.where == "releases") {
|
||||||
for (let i = 0; i < data.length; i++) {
|
for (let i = 0; i < data.length; i++) {
|
||||||
allReleases.push(...data[i].releases);
|
_content.push(...data[i].releases);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < data.length; i++) {
|
for (let i = 0; i < data.length; i++) {
|
||||||
allReleases.push(...data[i].content);
|
_content.push(...data[i].content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setContent(allReleases);
|
setContent(_content);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
@ -145,52 +265,22 @@ export function SearchPage() {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [scrollPosition]);
|
}, [scrollPosition]);
|
||||||
|
|
||||||
function _executeSearch(value: string) {
|
if (!params) return <></>;
|
||||||
const Params = new URLSearchParams(window.location.search);
|
|
||||||
Params.set("q", value);
|
|
||||||
const url = new URL(`/search?${Params.toString()}`, window.location.origin);
|
|
||||||
setContent(null);
|
|
||||||
setQuery(value);
|
|
||||||
router.push(url.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (searchVal && searchVal.length % 4 == 1) {
|
|
||||||
_executeSearch(searchVal.trim());
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [searchVal]);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return (
|
|
||||||
<main className="flex items-center justify-center min-h-screen">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<h1 className="text-2xl font-bold">Ошибка</h1>
|
|
||||||
<p className="text-lg">
|
|
||||||
Произошла ошибка поиска. Попробуйте обновить страницу или зайдите
|
|
||||||
позже.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div
|
||||||
<form
|
className="sticky top-0 sm:top-[var(--header-height)] z-50 flex flex-wrap w-full gap-2 bg-black bg-opacity-25 py-2 px-2 rounded-lg backdrop-blur-sm"
|
||||||
className="flex-1 max-w-full mx-auto"
|
style={{ "--header-height": `${HeaderH}px` } as React.CSSProperties}
|
||||||
onSubmit={(e) => {
|
>
|
||||||
e.preventDefault();
|
<div className="flex flex-col flex-1 w-full lg:flex-row">
|
||||||
_executeSearch(searchVal.trim());
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<label
|
<label
|
||||||
htmlFor="default-search"
|
htmlFor="default-search"
|
||||||
className="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white"
|
className="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white"
|
||||||
>
|
>
|
||||||
Search
|
Search
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative w-full">
|
||||||
<div className="absolute inset-y-0 flex items-center pointer-events-none start-0 ps-3">
|
<div className="absolute inset-y-0 flex items-center pointer-events-none start-0 ps-3">
|
||||||
<svg
|
<svg
|
||||||
className="w-4 h-4 text-gray-500 dark:text-gray-400"
|
className="w-4 h-4 text-gray-500 dark:text-gray-400"
|
||||||
|
@ -214,229 +304,110 @@ export function SearchPage() {
|
||||||
className="block w-full p-4 text-sm text-gray-900 border border-gray-300 rounded-lg ps-10 bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
className="block w-full p-4 text-sm text-gray-900 border border-gray-300 rounded-lg ps-10 bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||||
placeholder="Поиск аниме..."
|
placeholder="Поиск аниме..."
|
||||||
required
|
required
|
||||||
value={searchVal}
|
value={query}
|
||||||
onChange={(e) => setSearchVal(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="text-white absolute end-2.5 bottom-2.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
|
||||||
>
|
|
||||||
Поиск
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<div className="flex gap-2 mt-2 lg:ml-2 lg:mt-0">
|
||||||
<Button
|
<div className="flex justify-between flex-1 gap-4">
|
||||||
color="light"
|
<Dropdown
|
||||||
size="xl"
|
size="xl"
|
||||||
onClick={() => setFiltersModalOpen(true)}
|
label={`Искать в: ${whereMapping.find((item) => item.id == params.where).label}`}
|
||||||
>
|
color="light"
|
||||||
Фильтры
|
className="w-full lg:w-fit"
|
||||||
</Button>
|
>
|
||||||
|
{whereMapping.map((item) => {
|
||||||
|
return item.auth && !userStore.isAuth ?
|
||||||
|
<></>
|
||||||
|
: <DropdownItem
|
||||||
|
onClick={() =>
|
||||||
|
searchByMapping[item.id] ?
|
||||||
|
setParams({
|
||||||
|
where: item.id,
|
||||||
|
searchBy: searchByMapping[item.id][0].id,
|
||||||
|
})
|
||||||
|
: setParams({ where: item.id, searchBy: "none" })
|
||||||
|
}
|
||||||
|
key={`filter--where--${item.id}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</DropdownItem>;
|
||||||
|
})}
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
{searchByMapping[params.where] ?
|
||||||
|
<div className="flex justify-between flex-1 gap-4">
|
||||||
|
<Dropdown
|
||||||
|
size="xl"
|
||||||
|
label={`Искать по: ${
|
||||||
|
params.searchBy == "none" ?
|
||||||
|
searchByMapping.none[0].label
|
||||||
|
: searchByMapping[params.where].find(
|
||||||
|
(item) => item.id == params.searchBy
|
||||||
|
).label
|
||||||
|
}`}
|
||||||
|
color="light"
|
||||||
|
className="w-full lg:w-fit"
|
||||||
|
>
|
||||||
|
{searchByMapping[params.where].map((item) => {
|
||||||
|
return (
|
||||||
|
<DropdownItem
|
||||||
|
onClick={() =>
|
||||||
|
setParams({
|
||||||
|
where: params.where,
|
||||||
|
searchBy: item.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
key={`filter--where--${params.where}--searchBy--${item.id}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</DropdownItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
: <></>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">
|
|
||||||
|
<div>
|
||||||
|
{error ?
|
||||||
|
<div className="flex flex-col justify-between w-full p-4 border border-red-200 rounded-md md:flex-row bg-red-50 dark:bg-red-700 dark:border-red-600">
|
||||||
|
<div className="mb-4 md:mb-0 md:me-4">
|
||||||
|
<p>Произошла ошибка поиска</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: <></>}
|
||||||
|
|
||||||
{data && data[0].related && <RelatedSection {...data[0].related} />}
|
{data && data[0].related && <RelatedSection {...data[0].related} />}
|
||||||
{content ?
|
{content ?
|
||||||
content.length > 0 ?
|
content.length > 0 ?
|
||||||
<>
|
params.where == "profiles" ?
|
||||||
{where == "collections" ?
|
<UserSection content={content} />
|
||||||
<CollectionsSection
|
: params.where == "collections" ?
|
||||||
sectionTitle="Найденные Коллекции"
|
<CollectionsSection content={content} />
|
||||||
content={content}
|
: <ReleaseSection content={content} />
|
||||||
/>
|
|
||||||
: where == "profiles" ?
|
|
||||||
<UserSection
|
|
||||||
sectionTitle="Найденные Пользователи"
|
|
||||||
content={content}
|
|
||||||
/>
|
|
||||||
: <ReleaseSection
|
|
||||||
sectionTitle="Найденные релизы"
|
|
||||||
content={content}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
: <div className="flex flex-col items-center justify-center min-w-full gap-4 mt-12 text-xl">
|
: <div className="flex flex-col items-center justify-center min-w-full gap-4 mt-12 text-xl">
|
||||||
<span className="w-24 h-24 iconify-color twemoji--crying-cat"></span>
|
<span className="w-24 h-24 iconify-color twemoji--crying-cat"></span>
|
||||||
<p>Странно, аниме не найдено, попробуйте другой запрос...</p>
|
<p>Странно, аниме не найдено, попробуйте другой запрос...</p>
|
||||||
</div>
|
</div>
|
||||||
: isLoading && (
|
|
||||||
<div className="flex items-center justify-center min-w-full min-h-screen">
|
: <></>}
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{!content && !isLoading && !query && (
|
{!content && !isLoading && !query && (
|
||||||
<div className="flex flex-col items-center justify-center min-w-full gap-2 mt-12 text-xl">
|
<div className="flex flex-col items-center justify-center min-w-full gap-2 mt-12 text-xl">
|
||||||
<span className="w-16 h-16 iconify mdi--arrow-up animate-bounce"></span>
|
<span className="w-16 h-16 iconify mdi--arrow-up animate-bounce"></span>
|
||||||
<p>Введите ваш запрос что-бы найти любимый тайтл</p>
|
<p>Введите ваш запрос что-бы найти любимый тайтл</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
{(
|
{isLoading ?
|
||||||
data &&
|
<div className="flex items-center justify-center w-full h-16">
|
||||||
data.length > 1 &&
|
<Spinner />
|
||||||
(where == "releases" ?
|
|
||||||
data[data.length - 1].releases.length == 25
|
|
||||||
: data[data.length - 1].content.length == 25)
|
|
||||||
) ?
|
|
||||||
<Button
|
|
||||||
className="w-full"
|
|
||||||
color={"light"}
|
|
||||||
onClick={() => setSize(size + 1)}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="w-6 h-6 iconify mdi--plus-circle "></span>
|
|
||||||
<span className="text-lg">Загрузить ещё</span>
|
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
: ""}
|
||||||
: ""}
|
</div>
|
||||||
<FiltersModal
|
</div>
|
||||||
isOpen={filtersModalOpen}
|
|
||||||
setIsOpen={setFiltersModalOpen}
|
|
||||||
where={where}
|
|
||||||
setWhere={setWhere}
|
|
||||||
list={list}
|
|
||||||
setList={setList}
|
|
||||||
isAuth={userStore.isAuth}
|
|
||||||
searchBy={searchBy}
|
|
||||||
setSearchBy={setSearchBy}
|
|
||||||
setContent={setContent}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FiltersModal = (props: {
|
|
||||||
isOpen: boolean;
|
|
||||||
setIsOpen: any;
|
|
||||||
where: string;
|
|
||||||
setWhere: any;
|
|
||||||
list: string;
|
|
||||||
setList: any;
|
|
||||||
isAuth: boolean;
|
|
||||||
searchBy: string;
|
|
||||||
setSearchBy: any;
|
|
||||||
setContent: any;
|
|
||||||
}) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const [where, setWhere] = useState(props.where);
|
|
||||||
const [list, setList] = useState(props.list);
|
|
||||||
const [searchBy, setSearchBy] = useState(props.searchBy);
|
|
||||||
|
|
||||||
function _cancel() {
|
|
||||||
setWhere(props.where);
|
|
||||||
setList(props.list);
|
|
||||||
setSearchBy(props.searchBy);
|
|
||||||
props.setIsOpen(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _accept() {
|
|
||||||
const Params = new URLSearchParams(window.location.search);
|
|
||||||
|
|
||||||
if (props.where != where) {
|
|
||||||
Params.set("where", where);
|
|
||||||
props.setWhere(where);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (where == "list") {
|
|
||||||
Params.set("list", list);
|
|
||||||
props.setList(list);
|
|
||||||
} else {
|
|
||||||
Params.delete("list");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!["profiles", "collections"].includes(where)) {
|
|
||||||
Params.set("searchBy", searchBy);
|
|
||||||
props.setSearchBy(searchBy);
|
|
||||||
} else {
|
|
||||||
Params.delete("searchBy");
|
|
||||||
props.setSearchBy("name");
|
|
||||||
}
|
|
||||||
|
|
||||||
props.setContent(null);
|
|
||||||
|
|
||||||
const url = new URL(`/search?${Params.toString()}`, window.location.origin);
|
|
||||||
router.push(url.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal show={props.isOpen} onClose={() => _cancel()}>
|
|
||||||
<ModalHeader>Фильтры</ModalHeader>
|
|
||||||
<ModalBody>
|
|
||||||
<div className="my-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<p className="font-bold dark:text-white">Искать в</p>
|
|
||||||
<Dropdown label={WhereMapping[where]} color="blue">
|
|
||||||
{Object.keys(WhereMapping).map((item) => {
|
|
||||||
if (
|
|
||||||
["list", "history", "collections", "favorites"].includes(
|
|
||||||
item
|
|
||||||
) &&
|
|
||||||
!props.isAuth
|
|
||||||
) {
|
|
||||||
return <></>;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<DropdownItem
|
|
||||||
onClick={() => setWhere(item)}
|
|
||||||
key={`where--${item}`}
|
|
||||||
>
|
|
||||||
{WhereMapping[item]}
|
|
||||||
</DropdownItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{props.isAuth && where == "list" && ListsMapping.hasOwnProperty(list) ?
|
|
||||||
<div className="my-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<p className="font-bold dark:text-white">Список</p>
|
|
||||||
<Dropdown label={ListsMapping[list].name} color="blue">
|
|
||||||
{Object.keys(ListsMapping).map((item) => {
|
|
||||||
return (
|
|
||||||
<DropdownItem
|
|
||||||
onClick={() => setList(item)}
|
|
||||||
key={`list--${item}`}
|
|
||||||
>
|
|
||||||
{ListsMapping[item].name}
|
|
||||||
</DropdownItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
: ""}
|
|
||||||
{!["profiles", "collections"].includes(where) ?
|
|
||||||
<div className="my-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<p className="font-bold dark:text-white">Искать по</p>
|
|
||||||
<Dropdown label={TagMapping[searchBy].name} color="blue">
|
|
||||||
{Object.keys(TagMapping).map((item) => {
|
|
||||||
return (
|
|
||||||
<DropdownItem
|
|
||||||
onClick={() => setSearchBy(item)}
|
|
||||||
key={`tag--${item}`}
|
|
||||||
>
|
|
||||||
{TagMapping[item].name}
|
|
||||||
</DropdownItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
: ""}
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<div className="flex justify-end w-full gap-2">
|
|
||||||
<Button color="red" onClick={() => _cancel()}>
|
|
||||||
Отменить
|
|
||||||
</Button>
|
|
||||||
<Button color="blue" onClick={() => _accept()}>
|
|
||||||
Применить
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { BookmarksCategoryPage } from "#/pages/BookmarksCategory";
|
import { BookmarksCategoryPage } from "#/pages/BookmarksCategory";
|
||||||
import { fetchDataViaGet } from "#/api/utils";
|
import { fetchDataViaGet } from "#/api/utils";
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
export const dynamic = 'force-static';
|
import { API_URL } from "#/api/config";
|
||||||
|
|
||||||
const SectionTitleMapping = {
|
const SectionTitleMapping = {
|
||||||
watching: "Смотрю",
|
watching: "Смотрю",
|
||||||
|
@ -17,7 +17,7 @@ export async function generateMetadata(
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
const id: string = params.id;
|
const id: string = params.id;
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/profile/${id}`
|
`${API_URL}/profile/${id}`
|
||||||
);
|
);
|
||||||
const previousOG = (await parent).openGraph;
|
const previousOG = (await parent).openGraph;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { BookmarksPage } from "#/pages/Bookmarks";
|
import { BookmarksPage } from "#/pages/Bookmarks";
|
||||||
import { fetchDataViaGet } from "#/api/utils";
|
import { fetchDataViaGet } from "#/api/utils";
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
export const dynamic = "force-static";
|
import { API_URL } from "#/api/config";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params },
|
{ params },
|
||||||
|
@ -9,7 +9,7 @@ export async function generateMetadata(
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
const id: string = params.id;
|
const id: string = params.id;
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/profile/${id}`
|
`${API_URL}/profile/${id}`
|
||||||
);
|
);
|
||||||
const previousOG = (await parent).openGraph;
|
const previousOG = (await parent).openGraph;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { CollectionsFullPage } from "#/pages/CollectionsFull";
|
import { CollectionsFullPage } from "#/pages/CollectionsFull";
|
||||||
import { fetchDataViaGet } from "#/api/utils";
|
import { fetchDataViaGet } from "#/api/utils";
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
export const dynamic = "force-static";
|
import { API_URL } from "#/api/config";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params },
|
{ params },
|
||||||
|
@ -9,7 +9,7 @@ export async function generateMetadata(
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
const id: string = params.id;
|
const id: string = params.id;
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/profile/${id}`
|
`${API_URL}/profile/${id}`
|
||||||
);
|
);
|
||||||
const previousOG = (await parent).openGraph;
|
const previousOG = (await parent).openGraph;
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ export async function generateMetadata(
|
||||||
|
|
||||||
export default async function Collections({ params }) {
|
export default async function Collections({ params }) {
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/profile/${params.id}`
|
`${API_URL}/profile/${params.id}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ProfilePage } from "#/pages/Profile";
|
import { ProfilePage } from "#/pages/Profile";
|
||||||
import { fetchDataViaGet } from "#/api/utils";
|
import { fetchDataViaGet } from "#/api/utils";
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
export const dynamic = "force-static";
|
import { API_URL } from "#/api/config";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params },
|
{ params },
|
||||||
|
@ -9,7 +9,7 @@ export async function generateMetadata(
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
const id: string = params.id;
|
const id: string = params.id;
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/profile/${id}`
|
`${API_URL}/profile/${id}`
|
||||||
);
|
);
|
||||||
const previousOG = (await parent).openGraph;
|
const previousOG = (await parent).openGraph;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { RelatedPage } from "#/pages/Related";
|
import { RelatedPage } from "#/pages/Related";
|
||||||
import { fetchDataViaGet } from "#/api/utils";
|
import { fetchDataViaGet } from "#/api/utils";
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
export const dynamic = 'force-static';
|
import { API_URL } from "#/api/config";
|
||||||
|
|
||||||
const _getData = async (url: string) => {
|
const _getData = async (url: string) => {
|
||||||
const { data, error } = await fetchDataViaGet(url);
|
const { data, error } = await fetchDataViaGet(url);
|
||||||
|
@ -12,7 +12,7 @@ export async function generateMetadata({ params }, parent: ResolvingMetadata): P
|
||||||
const id:string = params.id;
|
const id:string = params.id;
|
||||||
const previousOG = (await parent).openGraph;
|
const previousOG = (await parent).openGraph;
|
||||||
|
|
||||||
const [ related, relatedError ] = await _getData(`https://api.anixart.tv/related/${id}/0`);
|
const [ related, relatedError ] = await _getData(`${API_URL}/related/${id}/0`);
|
||||||
if (relatedError || related.content.length == 0) {
|
if (relatedError || related.content.length == 0) {
|
||||||
return {
|
return {
|
||||||
title: "Ошибка",
|
title: "Ошибка",
|
||||||
|
@ -20,7 +20,7 @@ export async function generateMetadata({ params }, parent: ResolvingMetadata): P
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const [ firstRelease, firstReleaseError ] = await _getData(`https://api.anixart.tv/release/${related.content[0].id}`);
|
const [ firstRelease, firstReleaseError ] = await _getData(`${API_URL}/release/${related.content[0].id}`);
|
||||||
if (firstReleaseError) {
|
if (firstReleaseError) {
|
||||||
return {
|
return {
|
||||||
title: "Ошибка",
|
title: "Ошибка",
|
||||||
|
@ -46,7 +46,7 @@ export async function generateMetadata({ params }, parent: ResolvingMetadata): P
|
||||||
|
|
||||||
export default async function Related({ params }) {
|
export default async function Related({ params }) {
|
||||||
const id: string = params.id;
|
const id: string = params.id;
|
||||||
const [ related, relatedError ] = await _getData(`https://api.anixart.tv/related/${id}/0`);
|
const [ related, relatedError ] = await _getData(`${API_URL}/related/${id}/0`);
|
||||||
if (relatedError || related.content.length == 0) {
|
if (relatedError || related.content.length == 0) {
|
||||||
return <main className="flex items-center justify-center min-h-screen">
|
return <main className="flex items-center justify-center min-h-screen">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
@ -56,7 +56,7 @@ export default async function Related({ params }) {
|
||||||
</main>
|
</main>
|
||||||
};
|
};
|
||||||
|
|
||||||
const [ firstRelease, firstReleaseError ] = await _getData(`https://api.anixart.tv/release/${related.content[0].id}`);
|
const [ firstRelease, firstReleaseError ] = await _getData(`${API_URL}/release/${related.content[0].id}`);
|
||||||
if (firstReleaseError) {
|
if (firstReleaseError) {
|
||||||
return <main className="flex items-center justify-center min-h-screen">
|
return <main className="flex items-center justify-center min-h-screen">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { CollectionsFullPage } from "#/pages/CollectionsFull";
|
import { CollectionsFullPage } from "#/pages/CollectionsFull";
|
||||||
import { fetchDataViaGet } from "#/api/utils";
|
import { fetchDataViaGet } from "#/api/utils";
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
export const dynamic = "force-static";
|
import { API_URL } from "#/api/config";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params },
|
{ params },
|
||||||
|
@ -9,7 +9,7 @@ export async function generateMetadata(
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
const id = params.id;
|
const id = params.id;
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/release/${id}`
|
`${API_URL}/release/${id}`
|
||||||
);
|
);
|
||||||
const previousOG = (await parent).openGraph;
|
const previousOG = (await parent).openGraph;
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ export async function generateMetadata(
|
||||||
|
|
||||||
export default async function Collections({ params }) {
|
export default async function Collections({ params }) {
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/release/${params.id}`
|
`${API_URL}/release/${params.id}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ReleasePage } from "#/pages/Release";
|
import { ReleasePage } from "#/pages/Release";
|
||||||
import { fetchDataViaGet } from "#/api/utils";
|
import { fetchDataViaGet } from "#/api/utils";
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
export const dynamic = "force-static";
|
import { API_URL } from "#/api/config";
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params },
|
{ params },
|
||||||
|
@ -9,7 +9,7 @@ export async function generateMetadata(
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
const id = params.id;
|
const id = params.id;
|
||||||
const { data, error } = await fetchDataViaGet(
|
const { data, error } = await fetchDataViaGet(
|
||||||
`https://api.anixart.tv/release/${id}`
|
`${API_URL}/release/${id}`
|
||||||
);
|
);
|
||||||
const previousOG = (await parent).openGraph;
|
const previousOG = (await parent).openGraph;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ export const metadata = {
|
||||||
description: "Поиск аниме релизов",
|
description: "Поиск аниме релизов",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
|
|
||||||
export default function Search() {
|
export default function Search() {
|
||||||
return <SearchPage />;
|
return <SearchPage />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
"dev-with-services": "node ./run-all.dev.js",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
|
|
|
@ -9,7 +9,7 @@ app.use(function (req, res, next) {
|
||||||
res.header("Access-Control-Allow-Origin", req.headers.origin || "*");
|
res.header("Access-Control-Allow-Origin", req.headers.origin || "*");
|
||||||
res.header(
|
res.header(
|
||||||
"Access-Control-Allow-Headers",
|
"Access-Control-Allow-Headers",
|
||||||
"Origin, X-Requested-With, Content-Type, Accept"
|
"Origin, X-Requested-With, Content-Type, Accept, Allow, User-Agent"
|
||||||
);
|
);
|
||||||
res.header("Access-Control-Allow-Methods", "GET,HEAD,POST,OPTIONS");
|
res.header("Access-Control-Allow-Methods", "GET,HEAD,POST,OPTIONS");
|
||||||
next();
|
next();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export const corsHeaders = {
|
export const corsHeaders = {
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
|
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Allow, User-Agent",
|
||||||
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
|
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
|
||||||
"Cache-Control": "no-cache",
|
"Cache-Control": "no-cache",
|
||||||
};
|
};
|
||||||
|
|
8
public/changelog/3.8.0.md
Normal file
8
public/changelog/3.8.0.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# 3.8.0
|
||||||
|
|
||||||
|
## Изменено
|
||||||
|
|
||||||
|
- Фильтры на странице поиска были перемещены рядом с полем ввода
|
||||||
|
- ТЕХ: механизм запросов поиска стал работать так-же как и остальные запросы
|
||||||
|
- Динамическая метадата (пред просмотра) снова включена
|
||||||
|
- ТЕХ: генераторы метадаты (пред просмотра) используют ссылку API (API_URL) с конфига
|
29
run-all.dev.js
Normal file
29
run-all.dev.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
const { spawn } = require("child_process");
|
||||||
|
|
||||||
|
const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm";
|
||||||
|
|
||||||
|
const client = spawn(npm, ["run", "dev"], { shell: true });
|
||||||
|
const parser = spawn(npm, ["run", "serve"], { shell: true, cwd: "./player-parser" });
|
||||||
|
const proxy = spawn(npm, ["run", "serve"], { shell: true, cwd: "./api-prox" });
|
||||||
|
|
||||||
|
const clientInfo = "\x1b[36m[client]\x1b[0m";
|
||||||
|
const parserInfo = "\x1b[33m[parser]\x1b[0m";
|
||||||
|
const proxyInfo = "\x1b[31m[proxy]\x1b[0m";
|
||||||
|
|
||||||
|
console.log(`${clientInfo} CMD: ${client.spawnargs.toString()}`);
|
||||||
|
console.log(`${clientInfo} PID: ${client.pid}`);
|
||||||
|
console.log(`${parserInfo} CMD: ${parser.spawnargs.toString()}`);
|
||||||
|
console.log(`${parserInfo} PID: ${parser.pid}`);
|
||||||
|
console.log(`${proxyInfo} CMD: ${proxy.spawnargs.toString()}`);
|
||||||
|
console.log(`${proxyInfo} PID: ${proxy.pid}`);
|
||||||
|
console.log(`\n`);
|
||||||
|
|
||||||
|
client.stdout.on("data", (data) =>
|
||||||
|
console.log(clientInfo, data.toString())
|
||||||
|
);
|
||||||
|
parser.stdout.on("data", (data) =>
|
||||||
|
console.log(parserInfo, data.toString())
|
||||||
|
);
|
||||||
|
proxy.stdout.on("data", (data) =>
|
||||||
|
console.log(proxyInfo, data.toString())
|
||||||
|
);
|
Loading…
Add table
Add a link
Reference in a new issue