Skip to content

Commit bd2aaf7

Browse files
committed
anical first commit
1 parent b8ff0a4 commit bd2aaf7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+7026
-6039
lines changed

.graphqlconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"schema": "https://graphql.anilist.co",
3+
"documents": ["app/**/*.{ts,tsx}", "lib/**/*.{ts,tsx}"]
4+
}

app/anime/[id]/layout.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import NavBar from "@/components/main/NavBar";
2+
3+
export default function Layout({
4+
children,
5+
}: {
6+
children: React.ReactNode;
7+
}) {
8+
return (
9+
<main className="">
10+
<NavBar/>
11+
{children}
12+
</main>
13+
);
14+
}

app/anime/[id]/page.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Anilist } from '@/utils/anilist';
2+
import type { AnimeHeaderQuery, GetAnimeQuery } from '@/lib/types/anilist';
3+
import VideoIcon from '~icons/gravity-ui/video';
4+
import AnimeDetails, { ANIME_DETAILS } from '@/components/@HooKit/AnimeDetails';
5+
import gql from 'graphql-tag';
6+
7+
interface PageProps {
8+
params: {
9+
id: string;
10+
};
11+
}
12+
13+
const ANIME_HEADER = gql`
14+
query AnimeHeader($id: Int) {
15+
Media(id: $id, type: ANIME) {
16+
...AnimeDetails
17+
}
18+
}
19+
${ANIME_DETAILS}
20+
`
21+
22+
export default async function AnimePage({ params }: PageProps) {
23+
const data = await Anilist<GetAnimeQuery>(ANIME_HEADER, {
24+
id: parseInt((await params).id),
25+
}) as AnimeHeaderQuery;
26+
27+
return (
28+
<div>
29+
<AnimeDetails data={{
30+
coverImage: data.Media?.coverImage,
31+
description: data.Media?.description,
32+
genres: data.Media?.genres,
33+
title: data.Media?.title,
34+
format: data.Media?.format,
35+
reviews: data.Media?.reviews,
36+
studios: data.Media?.studios,
37+
favourites: data.Media?.favourites,
38+
meanScore: data.Media?.meanScore,
39+
staff: data.Media?.staff,
40+
}} />
41+
</div>
42+
);
43+
}

app/api/auth/callback/route.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { NextRequest } from 'next/server';
2+
import { cookies } from 'next/headers';
3+
import { fetchAniList } from '@/lib/auth';
4+
5+
export async function GET(request: NextRequest) {
6+
const searchParams = request.nextUrl.searchParams;
7+
const code = searchParams.get('code');
8+
9+
if (!code) {
10+
return new Response('No code provided', { status: 400 });
11+
}
12+
13+
try {
14+
const tokenResponse = await fetch('https://anilist.co/api/v2/oauth/token', {
15+
method: 'POST',
16+
headers: {
17+
'Content-Type': 'application/json',
18+
Accept: 'application/json',
19+
},
20+
body: JSON.stringify({
21+
grant_type: 'authorization_code',
22+
client_id: process.env.ANILIST_CLIENT_ID,
23+
client_secret: process.env.ANILIST_CLIENT_SECRET,
24+
redirect_uri: process.env.ANILIST_REDIRECT_URI,
25+
code: code,
26+
}),
27+
});
28+
29+
const data = await tokenResponse.json();
30+
31+
if (!tokenResponse.ok) {
32+
throw new Error(data.error || 'Failed to get token');
33+
}
34+
35+
// Set the cookie
36+
const cookieStore = await cookies();
37+
cookieStore.set('anilist_token', data.access_token, {
38+
httpOnly: true,
39+
secure: process.env.NODE_ENV === 'production',
40+
sameSite: 'lax',
41+
maxAge: 60 * 60 * 24 * 7 * 1, // 1 week
42+
});
43+
44+
const userData = await fetchAniList<any>(
45+
`query {
46+
Viewer {
47+
id
48+
name
49+
avatar {
50+
large
51+
}
52+
}
53+
}`,
54+
{},
55+
data.access_token
56+
);
57+
const viewer = userData.data.Viewer;
58+
cookieStore.set('user', JSON.stringify({name:viewer.name, id:viewer.id, avatar:viewer.avatar.large}), {
59+
httpOnly: true,
60+
secure: process.env.NODE_ENV === 'production',
61+
sameSite: 'lax',
62+
maxAge: 60 * 60 * 24 * 7 * 4, // 4 week
63+
});
64+
65+
return new Response(null, {
66+
status: 302,
67+
headers: {
68+
Location: '/profile',
69+
},
70+
});
71+
} catch (error) {
72+
console.error('Token exchange error:', error);
73+
return new Response('Authentication failed', { status: 500 });
74+
}
75+
}

app/api/image/route.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { load } from 'cheerio';
2+
3+
export async function GET(request: Request) {
4+
5+
const { searchParams } = new URL(request.url)
6+
const q = searchParams.get('q');
7+
if (!q) {
8+
return Response.json({ error: 'Query parameter is required' }, {
9+
status: 500
10+
});
11+
}
12+
const url = `https://www.google.com/search?q=${encodeURIComponent(q + " studio logo")}&tbm=isch&tbs=iar:s&sca_esv=9801acaddd243ef2&gbv=1&sei=bkoIZ_HiJ5-Li-gPusGmMA`;
13+
try {
14+
const response = await fetch(url, {
15+
cache: 'force-cache'
16+
});
17+
const html = await response.text();
18+
const $ = load(html);
19+
const firstImageUrl = $('.GpQGbf > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(1) > a:nth-child(1) > div:nth-child(1) > img:nth-child(1)').attr('src');
20+
if (firstImageUrl) {
21+
return Response.json({ url: firstImageUrl });
22+
} else {
23+
return Response.json({ error: 'No image found', url }, {
24+
status: 500
25+
});
26+
}
27+
28+
} catch (error) {
29+
return Response.json({ error: 'An error occurred while searching for images', details: error, url }, {
30+
status: 500
31+
});
32+
}
33+
}

app/globals.css

Lines changed: 195 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,203 @@
33
@tailwind utilities;
44

55
:root {
6-
--background: #ffffff;
7-
--foreground: #171717;
6+
--card-width: 12em;
7+
--card-height: 18em;
8+
}
9+
10+
h1 {
11+
@apply text-3xl font-bold
12+
}
13+
14+
h2 {
15+
@apply text-xl font-semibold
16+
}
17+
18+
.grid-list {
19+
@apply 2xl:grid-cols-7 xl:grid-cols-6 lg:grid-cols-4 md:grid-cols-3 grid-cols-2
20+
}
21+
22+
.z-2 {
23+
z-index: 2;
24+
}
25+
26+
li {
27+
@apply mr-1
28+
}
29+
30+
.join li {
31+
margin: 0 !important;
32+
}
33+
34+
.primary {
35+
@apply bg-primary text-primary-content;
36+
}
37+
38+
.secondary {
39+
@apply bg-secondary text-secondary-content;
40+
}
41+
42+
.accent {
43+
@apply bg-accent text-accent-content;
44+
}
45+
46+
.error {
47+
@apply bg-error text-error-content;
48+
}
49+
50+
.info {
51+
@apply bg-info text-info-content;
52+
}
53+
54+
.success {
55+
@apply bg-success text-success-content;
56+
}
57+
58+
.base {
59+
@apply bg-base-100 text-base-content;
60+
}
61+
62+
.base-content {
63+
@apply bg-base-content text-base-100;
64+
}
65+
66+
.flex-center {
67+
@apply flex items-center justify-center
68+
}
69+
70+
.flex-center-col {
71+
@apply flex flex-col items-center justify-center
72+
}
73+
74+
.flex-center-row {
75+
@apply flex flex-row items-center justify-center
76+
}
77+
78+
.scroll-shadows {
79+
max-height: 150px;
80+
overflow: auto;
81+
82+
background:
83+
/* Shadow Cover TOP */
84+
linear-gradient(oklch(var(--b2)) 30%,
85+
rgba(255, 255, 255, 0)) center top,
86+
87+
/* Shadow Cover BOTTOM */
88+
linear-gradient(rgba(255, 255, 255, 0),
89+
oklch(var(--b2)) 70%) center bottom,
90+
91+
/* Shadow TOP */
92+
radial-gradient(farthest-side at 50% 0,
93+
oklch(var(--b1)),
94+
#ff000000) center top,
95+
96+
/* Shadow BOTTOM */
97+
radial-gradient(farthest-side at 50% 100%,
98+
oklch(var(--b1)),
99+
#ff000000) center bottom;
100+
101+
background-repeat: no-repeat;
102+
background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;
103+
background-attachment: local, local, scroll, scroll;
104+
}
105+
106+
.inline-1 {
107+
display: -webkit-box;
108+
-webkit-line-clamp: 1;
109+
-webkit-box-orient: vertical;
110+
overflow: hidden;
111+
}
112+
113+
.grid-rows-min {
114+
grid-auto-rows: min-content;
115+
}
116+
117+
.skeleton-b1-b2 {
118+
background-color: var(--fallback-b1, oklch(var(--b1)/var(--tw-bg-opacity)));
119+
background-image: linear-gradient(105deg,
120+
transparent 0%,
121+
transparent 40%,
122+
var(--fallback-b2, oklch(var(--b2)/1)) 50%,
123+
transparent 60%,
124+
transparent 100%);
125+
}
126+
127+
.skeleton-b1-b3 {
128+
background-color: var(--fallback-b1, oklch(var(--b1)/var(--tw-bg-opacity)));
129+
background-image: linear-gradient(105deg,
130+
transparent 0%,
131+
transparent 40%,
132+
var(--fallback-b3, oklch(var(--b3)/1)) 50%,
133+
transparent 60%,
134+
transparent 100%);
135+
}
136+
137+
.skeleton-b2-b1 {
138+
background-color: var(--fallback-b2, oklch(var(--b2)/var(--tw-bg-opacity)));
139+
background-image: linear-gradient(105deg,
140+
transparent 0%,
141+
transparent 40%,
142+
var(--fallback-b1, oklch(var(--b1)/1)) 50%,
143+
transparent 60%,
144+
transparent 100%);
145+
}
146+
147+
.full {
148+
@apply w-full h-full
149+
}
150+
151+
152+
/******************
153+
** INLINE STYLES **
154+
******************/
155+
.card-width {
156+
width: var(--card-width);
157+
}
158+
159+
.card-height {
160+
height: var(--card-height);
161+
}
162+
163+
.inline-text {
164+
overflow: hidden;
165+
-o-text-overflow: ellipsis;
166+
text-overflow: ellipsis;
167+
display: -webkit-box;
168+
-webkit-line-clamp: 2;
169+
-webkit-box-orient: vertical;
8170
}
9171

10-
@media (prefers-color-scheme: dark) {
11-
:root {
12-
--background: #0a0a0a;
13-
--foreground: #ededed;
14-
}
172+
.inline-text-1 {
173+
overflow: hidden;
174+
-o-text-overflow: ellipsis;
175+
text-overflow: ellipsis;
176+
display: -webkit-box;
177+
-webkit-line-clamp: 1;
178+
-webkit-box-orient: vertical;
179+
}
180+
.inline-text-2 {
181+
overflow: hidden;
182+
-o-text-overflow: ellipsis;
183+
text-overflow: ellipsis;
184+
display: -webkit-box;
185+
-webkit-line-clamp: 2 !important;
186+
-webkit-box-orient: vertical;
15187
}
16188

17-
body {
18-
color: var(--foreground);
19-
background: var(--background);
20-
font-family: Arial, Helvetica, sans-serif;
189+
.inline-text-3 {
190+
overflow: hidden;
191+
-o-text-overflow: ellipsis;
192+
text-overflow: ellipsis;
193+
display: -webkit-box;
194+
-webkit-line-clamp: 3;
195+
-webkit-box-orient: vertical;
21196
}
197+
198+
.inline-text-auto {
199+
overflow: hidden;
200+
-o-text-overflow: ellipsis;
201+
text-overflow: ellipsis;
202+
display: -webkit-box;
203+
-webkit-line-clamp: 1;
204+
-webkit-box-orient: vertical;
205+
}

0 commit comments

Comments
 (0)