aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-10-06 01:41:44 -0700
committerFuwn <[email protected]>2024-10-06 01:41:44 -0700
commita84d9c9f47c7cd1b345d0283bef9f211a9727893 (patch)
tree554827840b67e8a0068e707fad973a8780f47957
parentfeat(graphql): add subtitles (diff)
downloaddue.moe-a84d9c9f47c7cd1b345d0283bef9f211a9727893.tar.xz
due.moe-a84d9c9f47c7cd1b345d0283bef9f211a9727893.zip
feat(badges): move badge operations to graphql
-rw-r--r--src/graphql/user/resolvers.ts125
-rw-r--r--src/graphql/user/schema.graphql19
-rw-r--r--src/lib/Database/SB/User/badges.ts148
-rw-r--r--src/routes/api/badges/+server.ts257
-rw-r--r--src/routes/user/[user]/+page.svelte2
-rw-r--r--src/routes/user/[user]/badges/+page.gql17
-rw-r--r--src/routes/user/[user]/badges/+page.server.ts5
-rw-r--r--src/routes/user/[user]/badges/+page.svelte853
-rw-r--r--src/routes/user/[user]/badges/+page.ts20
9 files changed, 831 insertions, 615 deletions
diff --git a/src/graphql/user/resolvers.ts b/src/graphql/user/resolvers.ts
index af71d384..1e5b9ec0 100644
--- a/src/graphql/user/resolvers.ts
+++ b/src/graphql/user/resolvers.ts
@@ -1,14 +1,121 @@
-import { getUserBadges } from '$lib/Database/SB/User/badges';
+import { userIdentity, type UserIdentity } from '$lib/Data/AniList/identity';
+import {
+ addUserBadge,
+ getUserBadges,
+ removeAllUserBadges,
+ removeUserBadge,
+ setShadowHidden,
+ setShadowHiddenBadge,
+ updateUserBadge
+} from '$lib/Database/SB/User/badges';
import type { WithIndex } from '../$types';
import type { Resolvers, Badge } from './$types';
+import authorisedJson from '$lib/Data/Static/authorised.json';
+import type { RequestEvent } from '@sveltejs/kit';
+
+type Context = RequestEvent<Partial<Record<string, string>>, string | null>;
+
+const auth = async (context: Context) => {
+ const userCookie = context.cookies.get('user');
+
+ if (!userCookie) return Error('Unauthorised');
+
+ const user = JSON.parse(userCookie);
+
+ return await userIdentity({
+ tokenType: user['token_type'],
+ expiresIn: user['expires_in'],
+ accessToken: user['access_token'],
+ refreshToken: user['refresh_token']
+ });
+};
+
+const authenticatedOperation = async (
+ context: Context,
+ operation: (identity: UserIdentity, authorised: boolean) => void
+) => {
+ const identity = await auth(context);
+
+ if (identity instanceof Error) return [];
+
+ const authorised = authorisedJson.includes(identity.id);
+
+ operation(identity, authorised);
+
+ return await getUserBadges(identity.id);
+};
export const resolvers: WithIndex<Resolvers> = {
- Query: {
- User: async (_, args /* , _context */) => {
- return {
- id: args.id,
- badges: (await getUserBadges(args.id)) as Badge[]
- };
- }
- }
+ Query: {
+ User: async (_, args) => {
+ return {
+ id: args.id,
+ badges: (await getUserBadges(args.id)) as Badge[]
+ };
+ }
+ },
+ Mutation: {
+ shadowHideBadges: async (_, args, context) =>
+ await authenticatedOperation(
+ context,
+ async (_, authorised) => await setShadowHidden(args.userId, authorised)
+ ),
+ shadowHideBadge: async (_, args, context) =>
+ await authenticatedOperation(
+ context,
+ async () =>
+ await setShadowHiddenBadge(args.id, args.state == null ? true : args.state)
+ ),
+ hideBadge: async (_, args, context) =>
+ await authenticatedOperation(context, async (identity) => {
+ const allBadges = await getUserBadges(identity.id);
+ const category = args.category || '';
+
+ await Promise.all(
+ allBadges
+ .filter((badge) => badge.category === category)
+ .map(async (badge) => {
+ await updateUserBadge(identity.id, badge.id as number, {
+ ...badge,
+ hidden:
+ allBadges
+ .filter((badge) => badge.category === category)
+ .filter((badge) => badge.hidden).length >
+ allBadges.filter((badge) => badge.category === category).length / 2
+ ? false
+ : true
+ });
+ })
+ );
+ }),
+ updateBadge: async (_, args, context) =>
+ await authenticatedOperation(context, async (identity) => {
+ const badge = {
+ post: args.post || undefined,
+ image: args.image || undefined,
+ description: args.description || null,
+ time: args.time || undefined,
+ category: args.category || null,
+ hidden: args.hidden || false,
+ source: args.source || null,
+ designer: args.designer || null
+ };
+
+ if ((await getUserBadges(identity.id)).find((badge) => badge.id === args.id)) {
+ await updateUserBadge(identity.id, args.id as number, badge);
+ } else {
+ await addUserBadge(identity.id, badge);
+ }
+ }),
+ deleteBadge: async (_, args, context) =>
+ await authenticatedOperation(
+ context,
+ async (identity) => await removeUserBadge(identity.id, args.id)
+ ),
+ pruneUserBadges: async (_, context) =>
+ await authenticatedOperation(
+ context as Context,
+ async (identity) => await removeAllUserBadges(identity.id)
+ )
+ }
};
diff --git a/src/graphql/user/schema.graphql b/src/graphql/user/schema.graphql
index 942ee72d..1e54f866 100644
--- a/src/graphql/user/schema.graphql
+++ b/src/graphql/user/schema.graphql
@@ -2,6 +2,25 @@ type Query {
User(id: Int!): User!
}
+type Mutation {
+ shadowHideBadges(userId: Int!): [Badge]!
+ shadowHideBadge(id: Int!, state: Boolean): [Badge]!
+ hideBadge(category: String): [Badge]!
+ updateBadge(
+ id: Int
+ post: String
+ image: String
+ description: String
+ time: String
+ category: String
+ hidden: Boolean
+ source: String
+ designer: String
+ ): [Badge]!
+ deleteBadge(id: Int!): [Badge]!
+ pruneUserBadges: [Badge]!
+}
+
type User {
id: Int!
badges: [Badge]!
diff --git a/src/lib/Database/SB/User/badges.ts b/src/lib/Database/SB/User/badges.ts
index 14f848c5..bea8f3e3 100644
--- a/src/lib/Database/SB/User/badges.ts
+++ b/src/lib/Database/SB/User/badges.ts
@@ -2,105 +2,101 @@ import { databaseTimeToDate } from '$lib/Utility/time';
import sb from '../../sb';
export interface Badge {
- post?: string;
- image?: string;
- description?: string | null;
- id?: number;
- time?: string;
- category?: string | null;
- hidden?: boolean;
- source?: string | null;
- designer?: string | null;
- shadow_hidden?: boolean;
- click_count?: number;
+ post?: string;
+ image?: string;
+ description?: string | null;
+ id?: number;
+ time?: string;
+ category?: string | null;
+ hidden?: boolean;
+ source?: string | null;
+ designer?: string | null;
+ shadow_hidden?: boolean;
+ click_count?: number;
}
export const getUserBadges = async (userId: number): Promise<Badge[]> => {
- const { data, error } = await sb.from('user_badges').select('*').eq('user_id', userId);
+ const { data, error } = await sb.from('user_badges').select('*').eq('user_id', userId);
- if (error) return [];
+ if (error) return [];
- return data.sort((a, b) =>
- databaseTimeToDate((a as Badge).time ?? '').getTime() >
- databaseTimeToDate((b as Badge).time ?? '').getTime()
- ? -1
- : 1
- ) as Badge[];
+ return data.sort((a, b) =>
+ databaseTimeToDate((a as Badge).time ?? '').getTime() >
+ databaseTimeToDate((b as Badge).time ?? '').getTime()
+ ? -1
+ : 1
+ ) as Badge[];
};
export const addUserBadge = async (userId: number, badge: Badge) => {
- const { post, image, description, time, category, hidden, source, designer } = badge;
-
- if (post === undefined || image === undefined) return;
-
- if (time) {
- await sb.from('user_badges').insert({
- user_id: userId,
- post,
- image,
- description,
- time,
- category,
- hidden,
- source,
- designer
- });
- } else {
- await sb
- .from('user_badges')
- .insert({ user_id: userId, post, image, description, category, hidden, source, designer });
- }
+ const { post, image, description, time, category, hidden, source, designer } = badge;
+
+ if (post === undefined || image === undefined) return;
+
+ if (time) {
+ await sb.from('user_badges').insert({
+ user_id: userId,
+ post,
+ image,
+ description,
+ time,
+ category,
+ hidden,
+ source,
+ designer
+ });
+ } else {
+ await sb
+ .from('user_badges')
+ .insert({ user_id: userId, post, image, description, category, hidden, source, designer });
+ }
};
export const removeUserBadge = async (userId: number, id: number) => {
- if (!isNaN(id)) await sb.from('user_badges').delete().eq('id', id).eq('user_id', userId);
+ if (!isNaN(id)) await sb.from('user_badges').delete().eq('id', id).eq('user_id', userId);
};
export const updateUserBadge = async (userId: number, id: number, badge: Badge) => {
- if (badge.post === undefined || badge.image === undefined) return;
-
- await sb
- .from('user_badges')
- .update({
- post: badge.post,
- image: badge.image,
- description: badge.description,
- category: badge.category,
- time: badge.time,
- hidden: badge.hidden,
- source: badge.source,
- designer: badge.designer
- })
- .eq('id', id)
- .eq('user_id', userId);
+ if (badge.post === undefined || badge.image === undefined) return;
+
+ await sb
+ .from('user_badges')
+ .update({
+ post: badge.post,
+ image: badge.image,
+ description: badge.description,
+ category: badge.category,
+ time: badge.time,
+ hidden: badge.hidden,
+ source: badge.source,
+ designer: badge.designer
+ })
+ .eq('id', id)
+ .eq('user_id', userId);
};
export const renameCategory = async (userId: number, oldName: string, newName: string) =>
- await sb
- .from('user_badges')
- .update({ category: newName })
- .eq('category', oldName)
- .eq('user_id', userId);
+ await sb
+ .from('user_badges')
+ .update({ category: newName })
+ .eq('category', oldName)
+ .eq('user_id', userId);
export const removeAllUserBadges = async (userId: number) =>
- await sb.from('user_badges').delete().eq('user_id', userId);
+ await sb.from('user_badges').delete().eq('user_id', userId);
export const migrateCategory = async (userId: number, oldName: string, newName: string) =>
- await sb
- .from('user_badges')
- .update({ category: newName })
- .eq('category', oldName)
- .eq('user_id', userId);
+ await sb
+ .from('user_badges')
+ .update({ category: newName })
+ .eq('category', oldName)
+ .eq('user_id', userId);
export const setShadowHidden = async (userId: number, shadowHide: boolean) =>
- await sb.from('user_badges').update({ shadow_hidden: shadowHide }).eq('user_id', userId);
+ await sb.from('user_badges').update({ shadow_hidden: shadowHide }).eq('user_id', userId);
-export const setShadowHiddenBadge = async (userId: number, id: number, shadowHide: boolean) =>
- await sb
- .from('user_badges')
- .update({ shadow_hidden: shadowHide })
- .eq('id', id)
- .eq('user_id', userId);
+export const setShadowHiddenBadge = async (id: number, shadowHide: boolean) =>
+ await sb.from('user_badges').update({ shadow_hidden: shadowHide }).eq('id', id);
export const incrementClickCount = async (id: number) =>
- await sb.rpc('user_badges_increment_click_count', { user_badge_id: id });
+ await sb.rpc('user_badges_increment_click_count', { user_badge_id: id });
diff --git a/src/routes/api/badges/+server.ts b/src/routes/api/badges/+server.ts
index dfe25e7e..e7a26320 100644
--- a/src/routes/api/badges/+server.ts
+++ b/src/routes/api/badges/+server.ts
@@ -1,150 +1,149 @@
import { userIdentity } from '$lib/Data/AniList/identity';
import {
- removeAllUserBadges,
- removeUserBadge,
- updateUserBadge,
- getUserBadges,
- addUserBadge,
- type Badge,
- migrateCategory,
- setShadowHidden,
- setShadowHiddenBadge,
- incrementClickCount
+ removeAllUserBadges,
+ removeUserBadge,
+ updateUserBadge,
+ getUserBadges,
+ addUserBadge,
+ type Badge,
+ migrateCategory,
+ setShadowHidden,
+ setShadowHiddenBadge,
+ incrementClickCount
} from '$lib/Database/SB/User/badges';
import authorisedJson from '$lib/Data/Static/authorised.json';
const unauthorised = new Response('Unauthorised', { status: 401 });
const badges = async (id: number) =>
- Response.json(await getUserBadges(id), {
- headers: {
- 'Access-Control-Allow-Origin': 'https://due.moe'
- }
- });
+ Response.json(await getUserBadges(id), {
+ headers: {
+ 'Access-Control-Allow-Origin': 'https://due.moe'
+ }
+ });
export const GET = async ({ url }) => {
- return await badges(Number(url.searchParams.get('id') || 0));
+ return await badges(Number(url.searchParams.get('id') || 0));
};
export const DELETE = async ({ url, cookies }) => {
- const userCookie = cookies.get('user');
+ const userCookie = cookies.get('user');
- if (!userCookie) return unauthorised;
+ if (!userCookie) return unauthorised;
- const user = JSON.parse(userCookie);
- const identity = await userIdentity({
- tokenType: user['token_type'],
- expiresIn: user['expires_in'],
- accessToken: user['access_token'],
- refreshToken: user['refresh_token']
- });
+ const user = JSON.parse(userCookie);
+ const identity = await userIdentity({
+ tokenType: user['token_type'],
+ expiresIn: user['expires_in'],
+ accessToken: user['access_token'],
+ refreshToken: user['refresh_token']
+ });
- if ((url.searchParams.get('prune') || 0) === 'true') {
- await removeAllUserBadges(identity.id);
- } else {
- await removeUserBadge(identity.id, Number(url.searchParams.get('id')));
- }
+ if ((url.searchParams.get('prune') || 0) === 'true') {
+ await removeAllUserBadges(identity.id);
+ } else {
+ await removeUserBadge(identity.id, Number(url.searchParams.get('id')));
+ }
- return await badges(identity.id);
+ return await badges(identity.id);
};
export const PUT = async ({ cookies, url, request }) => {
- if (url.searchParams.get('incrementClickCount') || undefined) {
- await incrementClickCount(Number(url.searchParams.get('incrementClickCount')));
-
- return new Response('Incremented', { status: 200 });
- }
-
- const userCookie = cookies.get('user');
-
- if (!userCookie) return unauthorised;
-
- const user = JSON.parse(userCookie);
- const identity = await userIdentity({
- tokenType: user['token_type'],
- expiresIn: user['expires_in'],
- accessToken: user['access_token'],
- refreshToken: user['refresh_token']
- });
- const authorised = authorisedJson.includes(identity.id);
-
- if (url.searchParams.get('shadowHide'))
- setShadowHidden(Number(url.searchParams.get('shadowHide')), authorised);
-
- if (url.searchParams.get('import') || undefined) {
- await Promise.all(
- (await request.json()).map(async (badge: Badge) => await addUserBadge(identity.id, badge))
- );
-
- return await badges(identity.id);
- } else if (url.searchParams.get('migrate') || undefined) {
- await migrateCategory(
- identity.id,
- url.searchParams.get('original') || '',
- url.searchParams.get('new') || ''
- );
-
- return await badges(identity.id);
- }
-
- if (url.searchParams.get('hide') || undefined) {
- const allBadges = await getUserBadges(identity.id);
-
- await Promise.all(
- allBadges
- .filter((badge) => badge.category === (url.searchParams.get('category') || ''))
- .map(async (badge) => {
- await updateUserBadge(identity.id, badge.id as number, {
- ...badge,
- hidden:
- allBadges
- .filter((badge) => badge.category === (url.searchParams.get('category') || ''))
- .filter((badge) => badge.hidden).length >
- allBadges.filter(
- (badge) => badge.category === (url.searchParams.get('category') || '')
- ).length /
- 2
- ? false
- : true
- });
- })
- );
-
- return await badges(identity.id);
- }
-
- if (url.searchParams.get('shadowHideBadge') || undefined) {
- if (!authorised) return unauthorised;
-
- await setShadowHiddenBadge(
- Number(url.searchParams.get('id')),
- Number(url.searchParams.get('shadowHideBadge')),
- url.searchParams.get('status') == 'true' ? false : true
- );
-
- return await badges(Number(url.searchParams.get('id')));
- }
-
- const badge = {
- post: url.searchParams.get('post') || undefined,
- image: url.searchParams.get('image') || undefined,
- description: url.searchParams.get('description') || null,
- time: url.searchParams.get('time') || undefined,
- category: url.searchParams.get('category') || null,
- hidden: url.searchParams.get('hidden') || false,
- source: url.searchParams.get('source') || null,
- designer: url.searchParams.get('designer') || null
- };
-
- if (
- (await getUserBadges(identity.id)).find(
- (badge) => Number(badge.id) === Number(url.searchParams.get('update'))
- )
- ) {
- await updateUserBadge(identity.id, Number(url.searchParams.get('update')), badge as Badge);
- } else {
- await addUserBadge(identity.id, badge as Badge);
- }
-
- return await badges(identity.id);
+ if (url.searchParams.get('incrementClickCount') || undefined) {
+ await incrementClickCount(Number(url.searchParams.get('incrementClickCount')));
+
+ return new Response('Incremented', { status: 200 });
+ }
+
+ const userCookie = cookies.get('user');
+
+ if (!userCookie) return unauthorised;
+
+ const user = JSON.parse(userCookie);
+ const identity = await userIdentity({
+ tokenType: user['token_type'],
+ expiresIn: user['expires_in'],
+ accessToken: user['access_token'],
+ refreshToken: user['refresh_token']
+ });
+ const authorised = authorisedJson.includes(identity.id);
+
+ if (url.searchParams.get('shadowHide'))
+ setShadowHidden(Number(url.searchParams.get('shadowHide')), authorised);
+
+ if (url.searchParams.get('import') || undefined) {
+ await Promise.all(
+ (await request.json()).map(async (badge: Badge) => await addUserBadge(identity.id, badge))
+ );
+
+ return await badges(identity.id);
+ } else if (url.searchParams.get('migrate') || undefined) {
+ await migrateCategory(
+ identity.id,
+ url.searchParams.get('original') || '',
+ url.searchParams.get('new') || ''
+ );
+
+ return await badges(identity.id);
+ }
+
+ if (url.searchParams.get('hide') || undefined) {
+ const allBadges = await getUserBadges(identity.id);
+
+ await Promise.all(
+ allBadges
+ .filter((badge) => badge.category === (url.searchParams.get('category') || ''))
+ .map(async (badge) => {
+ await updateUserBadge(identity.id, badge.id as number, {
+ ...badge,
+ hidden:
+ allBadges
+ .filter((badge) => badge.category === (url.searchParams.get('category') || ''))
+ .filter((badge) => badge.hidden).length >
+ allBadges.filter(
+ (badge) => badge.category === (url.searchParams.get('category') || '')
+ ).length /
+ 2
+ ? false
+ : true
+ });
+ })
+ );
+
+ return await badges(identity.id);
+ }
+
+ if (url.searchParams.get('shadowHideBadge') || undefined) {
+ if (!authorised) return unauthorised;
+
+ await setShadowHiddenBadge(
+ Number(url.searchParams.get('shadowHideBadge')),
+ url.searchParams.get('status') == 'true' ? false : true
+ );
+
+ return await badges(Number(url.searchParams.get('id')));
+ }
+
+ const badge = {
+ post: url.searchParams.get('post') || undefined,
+ image: url.searchParams.get('image') || undefined,
+ description: url.searchParams.get('description') || null,
+ time: url.searchParams.get('time') || undefined,
+ category: url.searchParams.get('category') || null,
+ hidden: url.searchParams.get('hidden') || false,
+ source: url.searchParams.get('source') || null,
+ designer: url.searchParams.get('designer') || null
+ };
+
+ if (
+ (await getUserBadges(identity.id)).find(
+ (badge) => Number(badge.id) === Number(url.searchParams.get('update'))
+ )
+ ) {
+ await updateUserBadge(identity.id, Number(url.searchParams.get('update')), badge as Badge);
+ } else {
+ await addUserBadge(identity.id, badge as Badge);
+ }
+
+ return await badges(identity.id);
};
diff --git a/src/routes/user/[user]/+page.svelte b/src/routes/user/[user]/+page.svelte
index 0369a920..14c304f3 100644
--- a/src/routes/user/[user]/+page.svelte
+++ b/src/routes/user/[user]/+page.svelte
@@ -2,7 +2,7 @@
import settings from '$stores/settings';
import ParallaxImage from '../../../lib/Image/ParallaxImage.svelte';
import { typeSchedule, type ParseResult } from '$lib/Hololive/hololive';
- import { type User } from '$lib/Data/AniList/user';
+ import type { User } from '$lib/Data/AniList/user';
import HeadTitle from '$lib/Home/HeadTitle.svelte';
import Message from '$lib/Loading/Message.svelte';
import { estimatedDayReading } from '$lib/Media/Manga/time';
diff --git a/src/routes/user/[user]/badges/+page.gql b/src/routes/user/[user]/badges/+page.gql
new file mode 100644
index 00000000..44c35bd8
--- /dev/null
+++ b/src/routes/user/[user]/badges/+page.gql
@@ -0,0 +1,17 @@
+query UserBadges($id: Int!) {
+ User(id: $id) {
+ badges {
+ post
+ image
+ description
+ id
+ time
+ category
+ hidden
+ source
+ designer
+ shadow_hidden
+ click_count
+ }
+ }
+}
diff --git a/src/routes/user/[user]/badges/+page.server.ts b/src/routes/user/[user]/badges/+page.server.ts
deleted file mode 100644
index f18892a7..00000000
--- a/src/routes/user/[user]/badges/+page.server.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export const load = async ({ params }) => {
- return {
- username: params.user
- };
-};
diff --git a/src/routes/user/[user]/badges/+page.svelte b/src/routes/user/[user]/badges/+page.svelte
index 499233ce..c4d48454 100644
--- a/src/routes/user/[user]/badges/+page.svelte
+++ b/src/routes/user/[user]/badges/+page.svelte
@@ -3,7 +3,6 @@
import { userIdentity } from '$lib/Data/AniList/identity';
import { user, type User } from '$lib/Data/AniList/user';
import type { Badge } from '$lib/Database/SB/User/badges';
- // import { domToBlob } from 'modern-screenshot';
import { onDestroy, onMount } from 'svelte';
import HeadTitle from '$lib/Home/HeadTitle.svelte';
import { databaseTimeToDate, dateToInputTime, inputTimeToDatabaseTime } from '$lib/Utility/time';
@@ -20,16 +19,118 @@
import { page } from '$app/stores';
import type { UserPreferences } from '$lib/Database/SB/User/preferences';
import { browser } from '$app/environment';
- // import { io } from 'socket.io-client';
import BadgePreview from '$lib/User/BadgeWall/BadgePreview.svelte';
import authorisedJson from '$lib/Data/Static/authorised.json';
import identity from '$stores/identity';
import '$lib/User/BadgeWall/badges.css';
import Badges from '$lib/User/BadgeWall/Badges.svelte';
import type { IndexedBadge } from '$lib/User/BadgeWall/badge';
+ import { graphql } from '$houdini';
export let data;
+ $: ({ UserBadges } = data);
+
+ const updateBadgeQuery = graphql(`
+ mutation UpdateBadge(
+ $id: Int
+ $post: String
+ $image: String
+ $description: String
+ $time: String
+ $category: String
+ $hidden: Boolean
+ $source: String
+ $designer: String
+ ) {
+ updateBadge(
+ id: $id
+ post: $post
+ image: $image
+ description: $description
+ time: $time
+ category: $category
+ hidden: $hidden
+ source: $source
+ designer: $designer
+ ) {
+ post
+ image
+ description
+ id
+ time
+ category
+ hidden
+ source
+ designer
+ shadow_hidden
+ click_count
+ }
+ }
+ `);
+
+ const pruneBadgesQuery = graphql(`
+ mutation PruneUserBadges {
+ pruneUserBadges {
+ post
+ image
+ description
+ id
+ time
+ category
+ hidden
+ source
+ designer
+ shadow_hidden
+ click_count
+ }
+ }
+ `);
+
+ const hideCategoryQuery = graphql(`
+ mutation HideCategory($category: String) {
+ hideBadge(category: $category) {
+ post
+ image
+ description
+ id
+ time
+ category
+ hidden
+ source
+ designer
+ shadow_hidden
+ click_count
+ }
+ }
+ `);
+
+ const deleteBadgeQuery = graphql(`
+ mutation DeleteBadge($id: Int!) {
+ deleteBadge(id: $id) {
+ post
+ image
+ description
+ id
+ time
+ category
+ hidden
+ source
+ designer
+ shadow_hidden
+ click_count
+ }
+ }
+ `);
+
+ const shadowHideBadgeQuery = graphql(`
+ mutation ShadowHideBadge($id: Int!, $state: Boolean) {
+ shadowHideBadge(id: $id, state: $state) {
+ id
+ }
+ }
+ `);
+
interface ImportImage {
link?: string;
image: string;
@@ -40,7 +141,6 @@
let currentUserIdentity: ReturnType<typeof userIdentity>;
let error: null | string;
// const socket = io();
- let badgesPromise: Promise<Response>;
let awcPromise: Promise<Response>;
// let dark = true;
// let transparent = false;
@@ -67,16 +167,12 @@
type GroupedBadges = { [key: string]: IndexedBadge[] };
- const getBadges = () => (badgesPromise = fetch(root(`/api/badges?id=${badger.id}`)));
-
const setShadowHide = () =>
- fetch(`/api/badges?shadowHide=${badger.id}`, {
- method: 'PUT'
- }).then(getBadges);
+ shadowHideBadgeQuery.mutate({
+ id: badger.id as number
+ });
onMount(async () => {
- // socket.on('badges', (message) => (badges = message));
-
if (browser && localStorage.getItem('badgeWallNoticeDismissed')) noticeDismissed = true;
badger = isId
@@ -92,7 +188,6 @@
return;
}
- badgesPromise = fetch(root(`/api/badges?id=${badger.id}`));
awcPromise = fetch(proxy(`https://awc.moe/challenger/${badger.name}`));
preferences = await (await fetch(root(`/api/preferences?id=${badger.id}`))).json();
@@ -118,8 +213,6 @@
if (data.user && !isId) {
currentUserIdentity = userIdentity(data.user);
-
- // socket.emit('badges', data.user);
} else {
currentUserIdentity = new Promise((resolve) =>
resolve({
@@ -181,32 +274,27 @@
return;
}
- badgesPromise = fetch(
- `/api/badges?image=${encodeURIComponent(imageURL.value)}&post=${encodeURIComponent(
- activityURL.value || '#'
- )}${
- description.value.length > 0 ? `&description=${encodeURIComponent(description.value)}` : ''
- }${category.value.length > 0 ? `&category=${encodeURIComponent(category.value)}` : ''}${
- time.valueAsDate
- ? `&time=${encodeURIComponent(inputTimeToDatabaseTime(time.valueAsDate))}`
- : ''
- }${
- selectedBadge && selectedBadge.id ? `&update=${encodeURIComponent(selectedBadge.id)}` : ''
- }&hidden=${hidden.value === 'Hidden'}${
- source.value.length > 0 ? `&source=${encodeURIComponent(source.value)}` : ''
- }${designer.value.length > 0 ? `&designer=${encodeURIComponent(designer.value)}` : ''}`,
- {
- method: 'PUT'
- }
- );
-
- error = null;
- imageURL.value = '';
- activityURL.value = '';
- description.value = '';
- category.value = '';
- hidden.value = 'Shown';
- selectedBadge = undefined;
+ updateBadgeQuery
+ .mutate({
+ id: selectedBadge?.id,
+ image: imageURL.value,
+ post: activityURL.value || '#',
+ description: description.value,
+ category: category.value,
+ time: time.valueAsDate ? inputTimeToDatabaseTime(time.valueAsDate) : undefined,
+ hidden: hidden.value === 'Hidden',
+ source: source.value,
+ designer: designer.value
+ })
+ .then(() => {
+ error = null;
+ imageURL.value = '';
+ activityURL.value = '';
+ description.value = '';
+ category.value = '';
+ hidden.value = 'Shown';
+ selectedBadge = undefined;
+ });
};
const removeAllBadges = () => {
@@ -223,9 +311,8 @@
}
selectedBadge = undefined;
- badgesPromise = fetch(root(`/api/badges?prune=true`), {
- method: 'DELETE'
- });
+
+ pruneBadgesQuery.mutate(null).then();
};
const removeBadge = (badge: Badge) => {
@@ -245,9 +332,12 @@
(document.querySelector(`#badge-${badge.id}`) as HTMLAnchorElement).style.display = 'none';
selectedBadge = undefined;
- badgesPromise = fetch(root(`/api/badges?id=${badge.id}`), {
- method: 'DELETE'
- });
+
+ deleteBadgeQuery
+ .mutate({
+ id: badge.id
+ })
+ .then();
};
const groupBadges = (badges: IndexedBadge[]) => {
@@ -369,7 +459,7 @@
});
const migrateCategory = () => {
- badgesPromise = fetch(
+ fetch(
`/api/badges?migrate=true&original=${encodeURIComponent(
(document.querySelector('#migrate_original') as HTMLInputElement).value
)}&new=${encodeURIComponent(
@@ -378,22 +468,15 @@
{
method: 'PUT'
}
- );
-
- migrateMode = false;
+ ).then(() => (migrateMode = false));
};
const hideCategory = () => {
- badgesPromise = fetch(
- `/api/badges?hide=true&category=${encodeURIComponent(
- (document.querySelector('#category_hide') as HTMLInputElement).value
- )}`,
- {
- method: 'PUT'
- }
- );
-
- hideMode = false;
+ hideCategoryQuery
+ .mutate({
+ category: (document.querySelector('#category_hide') as HTMLInputElement).value
+ })
+ .then(() => (hideMode = false));
};
// const exportBadges = (groupedBadges: [string, Badge[]][]) => {
@@ -455,12 +538,12 @@
const shadowHideBadge = () => {
if (!selectedBadge && !authorised) return;
- badgesPromise = fetch(
- `/api/badges?shadowHideBadge=${selectedBadge?.id}&status=${selectedBadge?.shadow_hidden}&id=${badger.id}`,
- {
- method: 'PUT'
- }
- );
+ shadowHideBadgeQuery
+ .mutate({
+ id: badger.id as number,
+ state: selectedBadge?.shadow_hidden as boolean
+ })
+ .then();
};
</script>
@@ -478,376 +561,356 @@
{:then identity}
{@const isOwner = identity && (isId ? identity.id : identity.name) === data.username}
- {#await badgesPromise}
+ {#if $UserBadges.fetching || !$UserBadges.data}
<Message message="Loading badges ..." />
<Skeleton grid={true} count={100} width="150px" height="170px" />
- {:then badgesResponse}
- {#if badgesResponse}
- {#await badgesResponse.clone().json()}
- <Message message="Parsing badges ..." />
-
- <Skeleton grid={true} count={100} width="150px" height="170px" />
- {:then ungroupedBadgesAny}
- {@const ungroupedBadges = castBadgesToIndexedBadges(ungroupedBadgesAny)}
- {@const isBadgeSelected =
- selectedBadge &&
- selectedBadge !== undefined &&
- selectedBadge.image &&
- selectedBadge.image !== undefined &&
- !editMode}
-
- <div id="badges">
- {#if preferences && !preferences.hide_awc_badges}
- <AWC {awcPromise} {categoryFilter} {isOwner} {preferences} />
+ {:else}
+ {@const ungroupedBadges = castBadgesToIndexedBadges($UserBadges.data.User.badges)}
+ {@const isBadgeSelected =
+ selectedBadge &&
+ selectedBadge !== undefined &&
+ selectedBadge.image &&
+ selectedBadge.image !== undefined &&
+ !editMode}
+
+ <div id="badges">
+ {#if preferences && !preferences.hide_awc_badges}
+ <AWC {awcPromise} {categoryFilter} {isOwner} {preferences} />
+ {/if}
+
+ {#if ungroupedBadges === null}
+ <Message message="Loading badges ..." />
+
+ <Skeleton grid={true} count={10} width="150px" height="170px" />
+ {:else}
+ {@const groupedBadges = Object.entries(
+ groupBadges(removeHiddenBadges(isOwner, ungroupedBadges))
+ )}
+
+ {#if isOwner || authorised}
+ {@const shadowHiddenCount = ungroupedBadges.filter(
+ (badge) => badge.shadow_hidden
+ ).length}
+ {@const shadowHidden = shadowHiddenCount > 0}
+
+ {#if shadowHidden}
+ <div class="card">
+ <b>Notice:</b> The Badge Wall overseer system has detected badges containing
+ AI-generated material on your wall. {shadowHiddenCount} of your badges have been shadow
+ hidden.
+ <p />
+ You may use the "Un-shadow Hide Badges" button to unhide these badges, from where you
+ will be required to use the hide feature to hide these badges from the public, while
+ allowing them to stay visible to you as the account holder.
+ </div>
+ {:else if !noticeDismissed}
+ <div class="card">
+ <b>Notice:</b> AniList has begun purging outbound links which contain AI-generated
+ material, this includes Badge Wall. If you have collected badges with AI-generated
+ elements, kindly use the hide feature to hide these badges from the public, while
+ allowing them to stay visible to you as the account holder.
+ <p />
+ Failure to comply with this request at your earliest convenience will result in the hiding
+ of all badges from your Badge Wall.
+ <p />
+ <button
+ on:click={() => {
+ noticeDismissed = true;
+
+ localStorage.setItem('badgeWallNoticeDismissed', 'true');
+ }}
+ >
+ Dismiss
+ </button>
+ </div>
{/if}
- {#if ungroupedBadges === null}
- <Message message="Loading badges ..." />
+ <p />
- <Skeleton grid={true} count={10} width="150px" height="170px" />
- {:else}
- {@const groupedBadges = Object.entries(
- groupBadges(removeHiddenBadges(isOwner, ungroupedBadges))
- )}
+ <div class="card">
+ {#if authorised}
+ <button on:click={setShadowHide}>Shadow Hide Badges</button>
+ {/if}
- {#if isOwner || authorised}
- {@const shadowHiddenCount = ungroupedBadges.filter(
- (badge) => badge.shadow_hidden
- ).length}
- {@const shadowHidden = shadowHiddenCount > 0}
+ {#if isOwner && authorised}
+ <span style="margin: 0 0.625rem;">•</span>
+ {/if}
+
+ {#if isOwner}
+ <button
+ on:click={() => {
+ selectedBadge = undefined;
+ editMode = !editMode;
+ }}
+ >
+ {editMode
+ ? $locale().user.badges.editMode.disable
+ : $locale().user.badges.editMode.enable}
+ </button>
+ <span style="margin: 0 0.625rem;">•</span>
+ <button
+ on:click={() => {
+ selectedBadge = undefined;
+ importMode = !importMode;
+ }}
+ >
+ {importMode
+ ? $locale().user.badges.importMode.disable
+ : $locale().user.badges.importMode.enable}
+ </button>
+ <span style="margin: 0 0.625rem;">•</span>
+ <button
+ on:click={() => {
+ selectedBadge = undefined;
+ migrateMode = !migrateMode;
+ }}
+ >
+ Migrate Category
+ </button>
+ <span style="margin: 0 0.625rem;">•</span>
+ <button
+ on:click={() => {
+ selectedBadge = undefined;
+ hideMode = !hideMode;
+ }}
+ >
+ Hide Category
+ </button>
+ <!-- <!-- <span style="margin: 0 0.625rem;">•</span> -->
+ <!-- <button on:click={() => exportBadges(groupedBadges)}>Export Badges</button> -->
{#if shadowHidden}
- <div class="card">
- <b>Notice:</b> The Badge Wall overseer system has detected badges containing
- AI-generated material on your wall. {shadowHiddenCount} of your badges have been
- shadow hidden.
- <p />
- You may use the "Un-shadow Hide Badges" button to unhide these badges, from where
- you will be required to use the hide feature to hide these badges from the public,
- while allowing them to stay visible to you as the account holder.
- </div>
- {:else if !noticeDismissed}
- <div class="card">
- <b>Notice:</b> AniList has begun purging outbound links which contain
- AI-generated material, this includes Badge Wall. If you have collected badges
- with AI-generated elements, kindly use the hide feature to hide these badges
- from the public, while allowing them to stay visible to you as the account
- holder.
- <p />
- Failure to comply with this request at your earliest convenience will result in the
- hiding of all badges from your Badge Wall.
- <p />
- <button
- on:click={() => {
- noticeDismissed = true;
-
- localStorage.setItem('badgeWallNoticeDismissed', 'true');
- }}
- >
- Dismiss
- </button>
- </div>
+ <span style="margin: 0 0.625rem;">•</span>
+ <button on:click={setShadowHide}>Un-shadow Hide Badges</button>
{/if}
- <p />
-
- <div class="card">
- {#if authorised}
- <button on:click={setShadowHide}>Shadow Hide Badges</button>
- {/if}
-
- {#if isOwner && authorised}
- <span style="margin: 0 0.625rem;">•</span>
- {/if}
-
- {#if isOwner}
- <button
- on:click={() => {
- selectedBadge = undefined;
- editMode = !editMode;
- }}
- >
- {editMode
- ? $locale().user.badges.editMode.disable
- : $locale().user.badges.editMode.enable}
- </button>
- <span style="margin: 0 0.625rem;">•</span>
- <button
- on:click={() => {
- selectedBadge = undefined;
- importMode = !importMode;
- }}
- >
- {importMode
- ? $locale().user.badges.importMode.disable
- : $locale().user.badges.importMode.enable}
- </button>
- <span style="margin: 0 0.625rem;">•</span>
- <button
- on:click={() => {
- selectedBadge = undefined;
- migrateMode = !migrateMode;
- }}
- >
- Migrate Category
- </button>
- <span style="margin: 0 0.625rem;">•</span>
- <button
- on:click={() => {
- selectedBadge = undefined;
- hideMode = !hideMode;
- }}
- >
- Hide Category
- </button>
- <!-- <!-- <span style="margin: 0 0.625rem;">•</span> -->
- <!-- <button on:click={() => exportBadges(groupedBadges)}>Export Badges</button> -->
-
- {#if shadowHidden}
- <span style="margin: 0 0.625rem;">•</span>
- <button on:click={setShadowHide}>Un-shadow Hide Badges</button>
- {/if}
-
- {#if editMode && isOwner}
- {@const groups = groupedBadges
- .map((group) => group[0])
- .filter((group) => group !== 'Uncategorised')}
- {@const designers = castAsStringArray([
- ...new Set(
- ungroupedBadges
- .map((badge) => badge.designer)
- .filter((designer) => designer !== undefined && designer !== null)
- .filter(
- (designer, index, array) =>
- array.indexOf(designer) === index && !array.includes(`@${designer}`)
- )
+ {#if editMode && isOwner}
+ {@const groups = groupedBadges
+ .map((group) => group[0])
+ .filter((group) => group !== 'Uncategorised')}
+ {@const designers = castAsStringArray([
+ ...new Set(
+ ungroupedBadges
+ .map((badge) => badge.designer)
+ .filter((designer) => designer !== undefined && designer !== null)
+ .filter(
+ (designer, index, array) =>
+ array.indexOf(designer) === index && !array.includes(`@${designer}`)
)
- ])}
+ )
+ ])}
- <p />
+ <p />
- {#if error}
- <p style="color: red;">{error}</p>
- {/if}
+ {#if error}
+ <p style="color: red;">{error}</p>
+ {/if}
+ <input
+ type="text"
+ placeholder={$locale().user.badges.editMode.imageURL}
+ name="image_url"
+ minlength="1"
+ maxlength="1000"
+ size="15"
+ value={selectedBadge ? selectedBadge.image : ''}
+ />
+ <input
+ type="text"
+ placeholder={$locale().user.badges.editMode.activityURL}
+ name="activity_url"
+ minlength="1"
+ maxlength="1000"
+ size="15"
+ value={selectedBadge
+ ? selectedBadge.post === '#'
+ ? ''
+ : selectedBadge.post
+ : ''}
+ />
+ <input
+ type="text"
+ placeholder={$locale().user.badges.editMode.description}
+ name="description"
+ minlength="1"
+ maxlength="1000"
+ size="15"
+ value={selectedBadge ? selectedBadge.description : ''}
+ />
+ <Dropdown
+ items={groups.map((group) => ({
+ name: group,
+ url: '#',
+ onClick: () => {
+ const category = document.querySelector('input[name="category"]');
+
+ if (category instanceof HTMLInputElement) category.value = group;
+ }
+ }))}
+ header={false}
+ center={false}
+ >
+ <span slot="title">
<input
type="text"
- placeholder={$locale().user.badges.editMode.imageURL}
- name="image_url"
- minlength="1"
- maxlength="1000"
- size="15"
- value={selectedBadge ? selectedBadge.image : ''}
- />
- <input
- type="text"
- placeholder={$locale().user.badges.editMode.activityURL}
- name="activity_url"
+ placeholder={$locale().user.badges.editMode.category}
+ name="category"
minlength="1"
maxlength="1000"
size="15"
value={selectedBadge
- ? selectedBadge.post === '#'
+ ? selectedBadge.category === 'Uncategorised'
? ''
- : selectedBadge.post
+ : selectedBadge.category
: ''}
+ list="categories"
/>
- <input
- type="text"
- placeholder={$locale().user.badges.editMode.description}
- name="description"
- minlength="1"
- maxlength="1000"
- size="15"
- value={selectedBadge ? selectedBadge.description : ''}
- />
- <Dropdown
- items={groups.map((group) => ({
- name: group,
- url: '#',
- onClick: () => {
- const category = document.querySelector('input[name="category"]');
-
- if (category instanceof HTMLInputElement) category.value = group;
- }
- }))}
- header={false}
- center={false}
- >
- <span slot="title">
- <input
- type="text"
- placeholder={$locale().user.badges.editMode.category}
- name="category"
- minlength="1"
- maxlength="1000"
- size="15"
- value={selectedBadge
- ? selectedBadge.category === 'Uncategorised'
- ? ''
- : selectedBadge.category
- : ''}
- list="categories"
- />
- </span>
- </Dropdown>
- <span style="float: right;">
+ </span>
+ </Dropdown>
+ <span style="float: right;">
+ <input
+ type="datetime-local"
+ value={selectedBadge && selectedBadge.time
+ ? dateToInputTime(databaseTimeToDate(selectedBadge.time))
+ : ''}
+ />
+ <small>Must be full date and time, defaults to now if any fields empty</small>
+ </span>
+
+ <p />
+
+ <div class="edit-row-2">
+ <input
+ type="text"
+ placeholder={$locale().user.badges.editMode.source}
+ name="source"
+ minlength="1"
+ maxlength="1000"
+ size="16"
+ value={selectedBadge ? selectedBadge.source : ''}
+ />
+ <Dropdown
+ items={designers.map((designer) => ({
+ name: designer,
+ url: '#',
+ onClick: () => {
+ const designerField = document.querySelector('input[name="designer"]');
+
+ if (designerField instanceof HTMLInputElement)
+ designerField.value = designer;
+ }
+ }))}
+ header={false}
+ center={false}
+ >
+ <span slot="title">
<input
- type="datetime-local"
- value={selectedBadge && selectedBadge.time
- ? dateToInputTime(databaseTimeToDate(selectedBadge.time))
- : ''}
+ type="text"
+ placeholder={$locale().user.badges.editMode.designer}
+ name="designer"
+ minlength="1"
+ maxlength="1000"
+ size="17"
+ value={selectedBadge ? selectedBadge.designer : ''}
/>
- <small
- >Must be full date and time, defaults to now if any fields empty</small
- >
</span>
-
- <p />
-
- <div class="edit-row-2">
+ </Dropdown>
+ <Dropdown
+ items={[false, true].map((hidden) => ({
+ name: hidden ? 'Hidden' : 'Shown',
+ url: '#',
+ onClick: () => {
+ const hiddenInput = document.querySelector('input[name="hidden"]');
+
+ if (hiddenInput instanceof HTMLInputElement)
+ hiddenInput.value = hidden ? 'Hidden' : 'Shown';
+ }
+ }))}
+ header={false}
+ center={false}
+ >
+ <span slot="title">
<input
type="text"
- placeholder={$locale().user.badges.editMode.source}
- name="source"
+ placeholder="Shown"
+ name="hidden"
minlength="1"
maxlength="1000"
- size="16"
- value={selectedBadge ? selectedBadge.source : ''}
+ size="15"
+ value={selectedBadge
+ ? selectedBadge.hidden
+ ? 'Hidden'
+ : 'Shown'
+ : 'Shown'}
/>
- <Dropdown
- items={designers.map((designer) => ({
- name: designer,
- url: '#',
- onClick: () => {
- const designerField =
- document.querySelector('input[name="designer"]');
-
- if (designerField instanceof HTMLInputElement)
- designerField.value = designer;
- }
- }))}
- header={false}
- center={false}
- >
- <span slot="title">
- <input
- type="text"
- placeholder={$locale().user.badges.editMode.designer}
- name="designer"
- minlength="1"
- maxlength="1000"
- size="17"
- value={selectedBadge ? selectedBadge.designer : ''}
- />
- </span>
- </Dropdown>
- <Dropdown
- items={[false, true].map((hidden) => ({
- name: hidden ? 'Hidden' : 'Shown',
- url: '#',
- onClick: () => {
- const hiddenInput = document.querySelector('input[name="hidden"]');
-
- if (hiddenInput instanceof HTMLInputElement)
- hiddenInput.value = hidden ? 'Hidden' : 'Shown';
- }
- }))}
- header={false}
- center={false}
- >
- <span slot="title">
- <input
- type="text"
- placeholder="Shown"
- name="hidden"
- minlength="1"
- maxlength="1000"
- size="15"
- value={selectedBadge
- ? selectedBadge.hidden
- ? 'Hidden'
- : 'Shown'
- : 'Shown'}
- />
- </span>
- </Dropdown>
- <button class="button-lined" on:click={submitBadge}
- >{selectedBadge
- ? $locale().user.badges.editMode.update
- : $locale().user.badges.editMode.add}</button
- >
- {#if selectedBadge}
- {$locale().user.badges.editMode.or}
- <button
- class="button-lined"
- on:click={() => {
- if (selectedBadge) removeBadge(selectedBadge);
- }}>{$locale().user.badges.editMode.delete}</button
- >
- {/if}
- </div>
+ </span>
+ </Dropdown>
+ <button class="button-lined" on:click={submitBadge}
+ >{selectedBadge
+ ? $locale().user.badges.editMode.update
+ : $locale().user.badges.editMode.add}</button
+ >
+ {#if selectedBadge}
+ {$locale().user.badges.editMode.or}
+ <button
+ class="button-lined"
+ on:click={() => {
+ if (selectedBadge) removeBadge(selectedBadge);
+ }}>{$locale().user.badges.editMode.delete}</button
+ >
{/if}
- {/if}
- </div>
+ </div>
+ {/if}
{/if}
+ </div>
+ {/if}
- <p />
-
- <Badges
- {ungroupedBadges}
- {groupedBadges}
- {categoryFilter}
- {editMode}
- {preferences}
- bind:selectedBadge
- />
- {/if}
- </div>
-
- {#if isBadgeSelected}
- <!-- {@const anyAdjacentBadgeExists =
+ <p />
+
+ <Badges
+ {ungroupedBadges}
+ {groupedBadges}
+ {categoryFilter}
+ {editMode}
+ {preferences}
+ bind:selectedBadge
+ />
+ {/if}
+ </div>
+
+ {#if isBadgeSelected}
+ <!-- {@const anyAdjacentBadgeExists =
adjacentBadgeExists(selectedBadge, ungroupedBadges, -1) ||
adjacentBadgeExists(selectedBadge, ungroupedBadges, 1)} -->
- <Popup
- fullscreen
- show={isBadgeSelected}
- onLeave={() => {
- selectedBadge = undefined;
- }}
- >
- <BadgePreview
- bind:selectedBadge
- onNext={() => setAdjacentCursor(ungroupedBadges, 1)}
- onPrevious={() => setAdjacentCursor(ungroupedBadges, -1)}
- hasNext={adjacentBadgeExists(selectedBadge, ungroupedBadges, 1) !== undefined}
- hasPrevious={adjacentBadgeExists(selectedBadge, ungroupedBadges, -1) !== undefined}
- />
-
- {#if authorised}
- <button on:click={shadowHideBadge}>
- {#if selectedBadge && selectedBadge.shadow_hidden}
- Un-shadow
- {:else}
- Shadow
- {/if} Hide Badge ({selectedBadge ? selectedBadge.id : 0})
- </button>
- {/if}
- </Popup>
+ <Popup
+ fullscreen
+ show={isBadgeSelected}
+ onLeave={() => {
+ selectedBadge = undefined;
+ }}
+ >
+ <BadgePreview
+ bind:selectedBadge
+ onNext={() => setAdjacentCursor(ungroupedBadges, 1)}
+ onPrevious={() => setAdjacentCursor(ungroupedBadges, -1)}
+ hasNext={adjacentBadgeExists(selectedBadge, ungroupedBadges, 1) !== undefined}
+ hasPrevious={adjacentBadgeExists(selectedBadge, ungroupedBadges, -1) !== undefined}
+ />
+
+ {#if authorised}
+ <button on:click={shadowHideBadge}>
+ {#if selectedBadge && selectedBadge.shadow_hidden}
+ Un-shadow
+ {:else}
+ Shadow
+ {/if} Hide Badge ({selectedBadge ? selectedBadge.id : 0})
+ </button>
{/if}
- {:catch}
- <Popup fullscreen locked>Could not parse badges</Popup>
- {/await}
- {:else}
- <Message message="Loading badges ..." />
-
- <Skeleton grid={true} count={100} width="150px" height="170px" />
+ </Popup>
{/if}
- {:catch}
- <Popup fullscreen locked>Could not fetch badges</Popup>
- {/await}
+ {/if}
{:catch}
<AnimeRateLimited>This user's badges could not be loaded.</AnimeRateLimited>
{/await}
diff --git a/src/routes/user/[user]/badges/+page.ts b/src/routes/user/[user]/badges/+page.ts
new file mode 100644
index 00000000..8b7204ad
--- /dev/null
+++ b/src/routes/user/[user]/badges/+page.ts
@@ -0,0 +1,20 @@
+import { load_UserBadges } from '$houdini';
+import { user } from '$lib/Data/AniList/user';
+import type { LoadEvent } from '@sveltejs/kit';
+
+export const load = async (event: LoadEvent) => {
+ const username = event.params.user as string;
+ const userData = await user(username, /^\d+$/.test(username));
+
+ return {
+ ...(await load_UserBadges({
+ event,
+ variables: {
+ id: userData.id
+ }
+ })),
+ username,
+ userData,
+ event
+ };
+};