diff options
| author | Fuwn <[email protected]> | 2024-10-06 03:49:02 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2024-10-06 03:49:02 -0700 |
| commit | ded5e5bf084ce424c05066ad9c6f418672345aac (patch) | |
| tree | b620bf9098ad4bb3139a0348cdb5270f2621ac5d | |
| parent | feat(badges): move badge operations to graphql (diff) | |
| download | due.moe-ded5e5bf084ce424c05066ad9c6f418672345aac.tar.xz due.moe-ded5e5bf084ce424c05066ad9c6f418672345aac.zip | |
feat(badges): use preferences graphql api
| -rw-r--r-- | src/graphql/user/resolvers.ts | 68 | ||||
| -rw-r--r-- | src/graphql/user/schema.graphql | 34 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/AWC.svelte | 68 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/Badges.svelte | 4 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/FallbackBadge.svelte | 4 | ||||
| -rw-r--r-- | src/lib/User/BadgeWall/badge.ts | 20 | ||||
| -rw-r--r-- | src/routes/user/[user]/badges/+page.gql | 14 | ||||
| -rw-r--r-- | src/routes/user/[user]/badges/+page.svelte | 56 | ||||
| -rw-r--r-- | src/routes/user/[user]/badges/+page.ts | 28 |
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 + }; }; |