aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-10-06 03:49:02 -0700
committerFuwn <[email protected]>2024-10-06 03:49:02 -0700
commitded5e5bf084ce424c05066ad9c6f418672345aac (patch)
treeb620bf9098ad4bb3139a0348cdb5270f2621ac5d
parentfeat(badges): move badge operations to graphql (diff)
downloaddue.moe-ded5e5bf084ce424c05066ad9c6f418672345aac.tar.xz
due.moe-ded5e5bf084ce424c05066ad9c6f418672345aac.zip
feat(badges): use preferences graphql api
-rw-r--r--src/graphql/user/resolvers.ts68
-rw-r--r--src/graphql/user/schema.graphql34
-rw-r--r--src/lib/User/BadgeWall/AWC.svelte68
-rw-r--r--src/lib/User/BadgeWall/Badges.svelte4
-rw-r--r--src/lib/User/BadgeWall/FallbackBadge.svelte4
-rw-r--r--src/lib/User/BadgeWall/badge.ts20
-rw-r--r--src/routes/user/[user]/badges/+page.gql14
-rw-r--r--src/routes/user/[user]/badges/+page.svelte56
-rw-r--r--src/routes/user/[user]/badges/+page.ts28
9 files changed, 190 insertions, 106 deletions
diff --git a/src/graphql/user/resolvers.ts b/src/graphql/user/resolvers.ts
index 1e5b9ec0..66e3f1a2 100644
--- a/src/graphql/user/resolvers.ts
+++ b/src/graphql/user/resolvers.ts
@@ -12,6 +12,7 @@ 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';
+import { getUserPreferences, setBiography, setCSS, setPinnedBadgeWallCategories, toggleHideAWCBadges, toggleHideMissingBadges, toggleHololiveStreamPinning, togglePinnedBadgeWallCategory, type UserPreferences } from '$lib/Database/SB/User/preferences';
type Context = RequestEvent<Partial<Record<string, string>>, string | null>;
@@ -30,7 +31,7 @@ const auth = async (context: Context) => {
});
};
-const authenticatedOperation = async (
+const authenticatedBadgesOperation = async (
context: Context,
operation: (identity: UserIdentity, authorised: boolean) => void
) => {
@@ -45,29 +46,43 @@ const authenticatedOperation = async (
return await getUserBadges(identity.id);
};
+const authenticatedPreferencesOperation = async (
+ context: Context,
+ operation: (identity: UserIdentity, authorised: boolean) => Promise<UserPreferences | null>
+) => {
+ const identity = await auth(context);
+
+ if (identity instanceof Error) return [];
+
+ const authorised = authorisedJson.includes(identity.id);
+
+ return operation(identity, authorised) as Promise<UserPreferences>
+};
+
export const resolvers: WithIndex<Resolvers> = {
Query: {
User: async (_, args) => {
return {
id: args.id,
- badges: (await getUserBadges(args.id)) as Badge[]
+ badges: (await getUserBadges(args.id)) as Badge[],
+ preferences: await getUserPreferences(args.id)
};
}
},
Mutation: {
shadowHideBadges: async (_, args, context) =>
- await authenticatedOperation(
+ await authenticatedBadgesOperation(
context,
async (_, authorised) => await setShadowHidden(args.userId, authorised)
),
shadowHideBadge: async (_, args, context) =>
- await authenticatedOperation(
+ await authenticatedBadgesOperation(
context,
async () =>
await setShadowHiddenBadge(args.id, args.state == null ? true : args.state)
),
hideBadge: async (_, args, context) =>
- await authenticatedOperation(context, async (identity) => {
+ await authenticatedBadgesOperation(context, async (identity) => {
const allBadges = await getUserBadges(identity.id);
const category = args.category || '';
@@ -89,7 +104,7 @@ export const resolvers: WithIndex<Resolvers> = {
);
}),
updateBadge: async (_, args, context) =>
- await authenticatedOperation(context, async (identity) => {
+ await authenticatedBadgesOperation(context, async (identity) => {
const badge = {
post: args.post || undefined,
image: args.image || undefined,
@@ -108,14 +123,49 @@ export const resolvers: WithIndex<Resolvers> = {
}
}),
deleteBadge: async (_, args, context) =>
- await authenticatedOperation(
+ await authenticatedBadgesOperation(
context,
async (identity) => await removeUserBadge(identity.id, args.id)
),
pruneUserBadges: async (_, context) =>
- await authenticatedOperation(
+ await authenticatedBadgesOperation(
context as Context,
async (identity) => await removeAllUserBadges(identity.id)
- )
+ ),
+ toggleHideMissingBadges: async (_, _args, context) =>
+ await authenticatedPreferencesOperation(
+ context as Context,
+ async (identity) => await toggleHideMissingBadges(identity.id)
+ ) as UserPreferences,
+ toggleHideAWCBadges: async (_, _args, context) =>
+ await authenticatedPreferencesOperation(
+ context as Context,
+ async (identity) => await toggleHideAWCBadges(identity.id)
+ ) as UserPreferences,
+ setBadgeWallCSS: async (_, args, context) =>
+ await authenticatedPreferencesOperation(
+ context as Context,
+ async (identity) => await setCSS(identity.id, args.css)
+ ) as UserPreferences,
+ togglePinnedBadgeWallCategory: async (_, args, context) =>
+ await authenticatedPreferencesOperation(
+ context as Context,
+ async (identity) => await togglePinnedBadgeWallCategory(identity.id, args.category)
+ ) as UserPreferences,
+ setPinnedBadgeWallCategories: async (_, args, context) =>
+ await authenticatedPreferencesOperation(
+ context as Context,
+ async (identity) => await setPinnedBadgeWallCategories(identity.id, args.categories)
+ ) as UserPreferences,
+ setBiography: async (_, args, context) =>
+ await authenticatedPreferencesOperation(
+ context as Context,
+ async (identity) => await setBiography(identity.id, args.biography.slice(0, 3000))
+ ) as UserPreferences,
+ togglePinnedHololiveStream: async (_, args, context) =>
+ await authenticatedPreferencesOperation(
+ context as Context,
+ async (identity) => await toggleHololiveStreamPinning(identity.id, args.stream)
+ ) as UserPreferences,
}
};
diff --git a/src/graphql/user/schema.graphql b/src/graphql/user/schema.graphql
index 1e54f866..d68e7fd9 100644
--- a/src/graphql/user/schema.graphql
+++ b/src/graphql/user/schema.graphql
@@ -19,23 +19,43 @@ type Mutation {
): [Badge]!
deleteBadge(id: Int!): [Badge]!
pruneUserBadges: [Badge]!
+ toggleHideMissingBadges: Preferences
+ toggleHideAWCBadges: Preferences
+ setBadgeWallCSS(css: String!): Preferences
+ togglePinnedBadgeWallCategory(category: String!): Preferences
+ setPinnedBadgeWallCategories(categories: [String!]!): Preferences
+ setBiography(biography: String!): Preferences
+ togglePinnedHololiveStream(stream: String!): Preferences
}
type User {
id: Int!
badges: [Badge]!
+ preferences: Preferences
}
type Badge {
- post: String
- image: String
+ post: String!
+ image: String!
description: String
- id: Int
- time: String
+ id: Int!
+ time: String!
category: String
- hidden: Boolean
+ hidden: Boolean!
source: String
designer: String
- shadow_hidden: Boolean
- click_count: Int
+ shadow_hidden: Boolean!
+ click_count: Int!
+}
+
+type Preferences {
+ created_at: String!
+ updated_at: String!
+ user_id: Int!
+ pinned_hololive_streams: [String]!
+ hide_missing_badges: Boolean!
+ biography: String
+ badge_wall_css: String!
+ hide_awc_badges: Boolean!
+ pinned_badge_wall_categories: [String]!
}
diff --git a/src/lib/User/BadgeWall/AWC.svelte b/src/lib/User/BadgeWall/AWC.svelte
index c2268510..ba8a22ea 100644
--- a/src/lib/User/BadgeWall/AWC.svelte
+++ b/src/lib/User/BadgeWall/AWC.svelte
@@ -1,14 +1,14 @@
<script lang="ts">
import type { AWCBadgesGroup } from '$lib/Data/awc';
- import type { UserPreferences } from '$lib/Database/SB/User/preferences';
import { cdn, thumbnail } from '$lib/Utility/image';
+ import type { Preferences } from '../../../graphql/$types';
import FallbackBadge from './FallbackBadge.svelte';
import './badges.css';
export let awcPromise: Promise<Response>;
export let categoryFilter: string | null;
export let isOwner: boolean;
- export let preferences: UserPreferences;
+ export let preferences: Preferences;
const awcBadgesGrouped = (awcResponse: string): AWCBadgesGroup[] => {
return Array.from(
@@ -60,39 +60,41 @@
</script>
{#await awcPromise then badges}
- {#await badges.clone().text() then text}
- {@const parsedBadges = awcBadgesGrouped(text)}
+ {#if badges}
+ {#await badges.clone().text() then text}
+ {@const parsedBadges = awcBadgesGrouped(text)}
- {#if parsedBadges.length > 0}
- {#each parsedBadges as group}
- <details open={categoryFilter || isOwner ? false : true}>
- <summary>
- Anime Watching Club <span class="opaque">|</span>
- {group.group}
- </summary>
+ {#if parsedBadges.length > 0}
+ {#each parsedBadges as group}
+ <details open={categoryFilter || isOwner ? false : true}>
+ <summary>
+ Anime Watching Club <span class="opaque">|</span>
+ {group.group}
+ </summary>
- <p />
+ <p />
- <div class="badges">
- {#each group.badges as badge, index}
- <div id={`badge-${index}`}>
- <FallbackBadge
- {badge}
- source={cdn(thumbnail(badge.image))}
- alternative={badge.description}
- fallback={thumbnail(badge.image)}
- hideOnError={preferences.hide_missing_badges}
- awc
- {index}
- {preferences}
- />
- </div>
- {/each}
- </div>
- </details>
+ <div class="badges">
+ {#each group.badges as badge, index}
+ <div id={`badge-${index}`}>
+ <FallbackBadge
+ {badge}
+ source={cdn(thumbnail(badge.image))}
+ alternative={badge.description}
+ fallback={thumbnail(badge.image)}
+ hideOnError={preferences.hide_missing_badges}
+ awc
+ {index}
+ {preferences}
+ />
+ </div>
+ {/each}
+ </div>
+ </details>
- <p />
- {/each}
- {/if}
- {/await}
+ <p />
+ {/each}
+ {/if}
+ {/await}
+ {/if}
{/await}
diff --git a/src/lib/User/BadgeWall/Badges.svelte b/src/lib/User/BadgeWall/Badges.svelte
index 127c98c8..c4da3118 100644
--- a/src/lib/User/BadgeWall/Badges.svelte
+++ b/src/lib/User/BadgeWall/Badges.svelte
@@ -6,14 +6,14 @@
import FallbackImage from '$lib/Image/FallbackImage.svelte';
import { cdn, thumbnail } from '$lib/Utility/image';
import FallbackBadge from './FallbackBadge.svelte';
- import type { UserPreferences } from '$lib/Database/SB/User/preferences';
+ import type { Preferences } from '../../../graphql/$types';
import type { IndexedBadge } from './badge';
export let ungroupedBadges: IndexedBadge[];
export let groupedBadges: [string, IndexedBadge[]][];
export let categoryFilter: string | null;
export let editMode: boolean;
- export let preferences: UserPreferences | undefined;
+ export let preferences: Preferences | undefined;
export let selectedBadge: IndexedBadge | undefined = undefined;
</script>
diff --git a/src/lib/User/BadgeWall/FallbackBadge.svelte b/src/lib/User/BadgeWall/FallbackBadge.svelte
index 2f0bdabd..0e690443 100644
--- a/src/lib/User/BadgeWall/FallbackBadge.svelte
+++ b/src/lib/User/BadgeWall/FallbackBadge.svelte
@@ -7,7 +7,7 @@
import { databaseTimeToDate } from '../../Utility/time';
import { cubicOut } from 'svelte/easing';
import { dev } from '$app/environment';
- import type { UserPreferences } from '$lib/Database/SB/User/preferences';
+ import type { Preferences } from '../../../graphql/$types';
export let source: string | null | undefined;
export let alternative: string | null | undefined;
@@ -21,7 +21,7 @@
export let selectedBadge: Badge | null = null;
export let awc = false;
export let index: number | null = null;
- export let preferences: UserPreferences | undefined;
+ export let preferences: Preferences | undefined;
let replaceCount = 0;
let badgeReference: HTMLImageElement;
diff --git a/src/lib/User/BadgeWall/badge.ts b/src/lib/User/BadgeWall/badge.ts
index 834c0362..0c15b9ca 100644
--- a/src/lib/User/BadgeWall/badge.ts
+++ b/src/lib/User/BadgeWall/badge.ts
@@ -1,18 +1,18 @@
-import type { Badge } from '$lib/Database/SB/User/badges';
+import type { Badge } from "../../../graphql/user/$types";
export interface IndexedBadge extends Badge {
- index: number;
+ index: number;
}
export const classifyDesignerName = (designer: string) => {
- let name = designer;
- const anilistUser = designer.match(/https?:\/\/anilist\.co\/user\/([^/]+)\/?/);
+ let name = designer;
+ const anilistUser = designer.match(/https?:\/\/anilist\.co\/user\/([^/]+)\/?/);
- if (anilistUser) {
- name = `@${anilistUser[1]}`;
- } else if (!designer.toLowerCase().startsWith('@')) {
- name = `@${designer}`;
- }
+ if (anilistUser) {
+ name = `@${anilistUser[1]}`;
+ } else if (!designer.toLowerCase().startsWith('@')) {
+ name = `@${designer}`;
+ }
- return name;
+ return name;
};
diff --git a/src/routes/user/[user]/badges/+page.gql b/src/routes/user/[user]/badges/+page.gql
index 44c35bd8..f7d3991e 100644
--- a/src/routes/user/[user]/badges/+page.gql
+++ b/src/routes/user/[user]/badges/+page.gql
@@ -1,4 +1,4 @@
-query UserBadges($id: Int!) {
+query BadgeWallUser($id: Int!) {
User(id: $id) {
badges {
post
@@ -13,5 +13,17 @@ query UserBadges($id: Int!) {
shadow_hidden
click_count
}
+
+ preferences {
+ created_at
+ updated_at
+ user_id
+ pinned_hololive_streams
+ hide_missing_badges
+ biography
+ badge_wall_css
+ hide_awc_badges
+ pinned_badge_wall_categories
+ }
}
}
diff --git a/src/routes/user/[user]/badges/+page.svelte b/src/routes/user/[user]/badges/+page.svelte
index c4d48454..f17bb790 100644
--- a/src/routes/user/[user]/badges/+page.svelte
+++ b/src/routes/user/[user]/badges/+page.svelte
@@ -2,11 +2,10 @@
import AWC from './../../../../lib/User/BadgeWall/AWC.svelte';
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 type { Badge } from '../../../../graphql/$types';
import { onDestroy, onMount } from 'svelte';
import HeadTitle from '$lib/Home/HeadTitle.svelte';
import { databaseTimeToDate, dateToInputTime, inputTimeToDatabaseTime } from '$lib/Utility/time';
- import root from '$lib/Utility/root';
import proxy from '$lib/Utility/proxy';
import locale from '$stores/locale';
import Skeleton from '$lib/Loading/Skeleton.svelte';
@@ -17,7 +16,6 @@
import SettingHint from '$lib/Settings/SettingHint.svelte';
import Popup from '$lib/Layout/Popup.svelte';
import { page } from '$app/stores';
- import type { UserPreferences } from '$lib/Database/SB/User/preferences';
import { browser } from '$app/environment';
import BadgePreview from '$lib/User/BadgeWall/BadgePreview.svelte';
import authorisedJson from '$lib/Data/Static/authorised.json';
@@ -26,10 +24,34 @@
import Badges from '$lib/User/BadgeWall/Badges.svelte';
import type { IndexedBadge } from '$lib/User/BadgeWall/badge';
import { graphql } from '$houdini';
+ import type { Preferences } from '../../../../graphql/user/$types';
export let data;
- $: ({ UserBadges } = data);
+ $: ({ BadgeWallUser } = data);
+ $: preferences = $BadgeWallUser.fetching
+ ? undefined
+ : ($BadgeWallUser.data?.User.preferences as Preferences);
+
+ $: if (browser && preferences && preferences.badge_wall_css) {
+ const sanitise = (css: string) =>
+ css
+ .replace(/\/\*[\s\S]*?\*\//g, '')
+ .replace(/<\/?[^>]+(>|$)/g, '')
+ .replace(
+ /(expression|javascript|vbscript|onerror|onload|onclick|onmouseover|onmouseout|onmouseup|onmousedown|onkeydown|onkeyup|onkeypress|onblur|onfocus|onsubmit|onreset|onselect|onchange|ondblclick):/gi,
+ ''
+ )
+ .replace(/(behaviour|behavior|moz-binding|content):/gi, '')
+ .replace(/\s+/g, ' ')
+ .trim();
+ const style = document.createElement('style');
+
+ style.dataset.badgeWall = 'true';
+ style.innerHTML = sanitise(preferences.badge_wall_css);
+
+ document.head.appendChild(style);
+ }
const updateBadgeQuery = graphql(`
mutation UpdateBadge(
@@ -157,7 +179,6 @@
let badger: Partial<User>;
let migrateMode = false;
let hideMode = false;
- let preferences: UserPreferences;
const authorised = authorisedJson.includes($identity.id);
let noticeDismissed = false;
@@ -189,27 +210,6 @@
}
awcPromise = fetch(proxy(`https://awc.moe/challenger/${badger.name}`));
- preferences = await (await fetch(root(`/api/preferences?id=${badger.id}`))).json();
-
- if (preferences && preferences.badge_wall_css) {
- const sanitise = (css: string) =>
- css
- .replace(/\/\*[\s\S]*?\*\//g, '')
- .replace(/<\/?[^>]+(>|$)/g, '')
- .replace(
- /(expression|javascript|vbscript|onerror|onload|onclick|onmouseover|onmouseout|onmouseup|onmousedown|onkeydown|onkeyup|onkeypress|onblur|onfocus|onsubmit|onreset|onselect|onchange|ondblclick):/gi,
- ''
- )
- .replace(/(behaviour|behavior|moz-binding|content):/gi, '')
- .replace(/\s+/g, ' ')
- .trim();
- const style = document.createElement('style');
-
- style.dataset.badgeWall = 'true';
- style.innerHTML = sanitise(preferences.badge_wall_css);
-
- document.head.appendChild(style);
- }
if (data.user && !isId) {
currentUserIdentity = userIdentity(data.user);
@@ -561,12 +561,12 @@
{:then identity}
{@const isOwner = identity && (isId ? identity.id : identity.name) === data.username}
- {#if $UserBadges.fetching || !$UserBadges.data}
+ {#if $BadgeWallUser.fetching || !$BadgeWallUser.data}
<Message message="Loading badges ..." />
<Skeleton grid={true} count={100} width="150px" height="170px" />
{:else}
- {@const ungroupedBadges = castBadgesToIndexedBadges($UserBadges.data.User.badges)}
+ {@const ungroupedBadges = castBadgesToIndexedBadges($BadgeWallUser.data.User.badges)}
{@const isBadgeSelected =
selectedBadge &&
selectedBadge !== undefined &&
diff --git a/src/routes/user/[user]/badges/+page.ts b/src/routes/user/[user]/badges/+page.ts
index 8b7204ad..bd13fdc4 100644
--- a/src/routes/user/[user]/badges/+page.ts
+++ b/src/routes/user/[user]/badges/+page.ts
@@ -1,20 +1,20 @@
-import { load_UserBadges } from '$houdini';
+import { load_BadgeWallUser } 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));
+ 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
- };
+ return {
+ ...(await load_BadgeWallUser({
+ event,
+ variables: {
+ id: userData.id
+ }
+ })),
+ username,
+ userData,
+ event
+ };
};