From db40d18312fae54723d88964159e44a7e7d8cb6b Mon Sep 17 00:00:00 2001 From: Fuwn Date: Sun, 5 May 2024 01:07:58 -0700 Subject: feat(user): drag and drop pinned categories --- bun.lockb | Bin 173947 -> 174306 bytes package.json | 1 + src/lib/Database/userPreferences.ts | 14 +++++ src/routes/api/preferences/+server.ts | 13 ++++- src/routes/user/[user]/+page.svelte | 93 +++++++++++++++++++++++++++++++++- 5 files changed, 118 insertions(+), 3 deletions(-) diff --git a/bun.lockb b/bun.lockb index b9761c64..b4c00cc4 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index db12dfd7..da8e9ced 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "lz-string": "^1.5.0", "modern-screenshot": "^4.4.33", "rss-parser": "^3.13.0", + "sortablejs": "^1.15.2", "string-similarity": "^4.0.4", "svelte-i18n": "^4.0.0", "svelte-markdown": "^0.4.1", diff --git a/src/lib/Database/userPreferences.ts b/src/lib/Database/userPreferences.ts index 9f55e110..c3ef5c04 100644 --- a/src/lib/Database/userPreferences.ts +++ b/src/lib/Database/userPreferences.ts @@ -140,3 +140,17 @@ export const togglePinnedBadgeWallCategory = async (userId: number, category: st pinned_badge_wall_categories: pinnedCategories.join(',') }); }; + +export const setPinnedBadgeWallCategories = async (userId: number, categories: string) => { + const userPreferences = await getUserPreferences(userId); + + if (!userPreferences) return null; + + return await setUserPreferences(userId, { + updated_at: new Date().toISOString(), + pinned_hololive_streams: userPreferences.pinned_hololive_streams, + hide_missing_badges: userPreferences.hide_missing_badges, + badge_wall_css: userPreferences.badge_wall_css, + pinned_badge_wall_categories: categories + }); +}; diff --git a/src/routes/api/preferences/+server.ts b/src/routes/api/preferences/+server.ts index 2fe2ccfe..0291dd0c 100644 --- a/src/routes/api/preferences/+server.ts +++ b/src/routes/api/preferences/+server.ts @@ -5,7 +5,8 @@ import { setCSS, setBiography, toggleHideAWCBadges, - togglePinnedBadgeWallCategory + togglePinnedBadgeWallCategory, + setPinnedBadgeWallCategories } from '$lib/Database/userPreferences'; const unauthorised = new Response('Unauthorised', { status: 401 }); @@ -66,6 +67,16 @@ export const PUT = async ({ url, cookies, request }) => { } ); + if (url.searchParams.get('setCategories') !== null) + return Response.json( + await setPinnedBadgeWallCategories(userId, url.searchParams.get('setCategories') || ''), + { + headers: { + 'Access-Control-Allow-Origin': 'https://due.moe' + } + } + ); + if (url.searchParams.get('biography') !== null) return Response.json(await setBiography(userId, (await request.text()).slice(0, 3000)), { headers: { diff --git a/src/routes/user/[user]/+page.svelte b/src/routes/user/[user]/+page.svelte index 097007f0..3c443ae6 100644 --- a/src/routes/user/[user]/+page.svelte +++ b/src/routes/user/[user]/+page.svelte @@ -20,6 +20,7 @@ import SvelteMarkdown from 'svelte-markdown'; import MarkdownLink from '$lib/MarkdownLink.svelte'; import LinkedTooltip from '$lib/Tooltip/LinkedTooltip.svelte'; + import type { DragEventHandler } from 'svelte/elements'; export let data; @@ -27,6 +28,8 @@ let error = false; let schedule: ParseResult | undefined = undefined; let preferences: UserPreferences | undefined = undefined; + let draggedCategory: string | null = null; + let draggedOverCategory: string | null = null; $: displayBadges = (username: string, badges: number | string) => $locale({ @@ -42,6 +45,81 @@ .catch(() => (error = true)); }); + const handleDragStart = ( + event: DragEvent & { currentTarget: EventTarget & HTMLDivElement }, + category: string | null + ) => { + draggedCategory = category; + + if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'; + }; + + const handleDragOver = (event: any) => { + event.preventDefault(); + + event.dataTransfer.dropEffect = 'move'; + }; + + const handleDragEnter = ( + event: DragEvent & { currentTarget: EventTarget & HTMLDivElement }, + category: string | null + ) => { + event.preventDefault(); + + if (draggedCategory !== category && preferences && draggedCategory) { + draggedOverCategory = category; + + const categories = preferences.pinned_badge_wall_categories.split(','); + const draggedIndex = categories.indexOf(draggedCategory); + const targetIndex = categories.indexOf(category); + + categories.splice(draggedIndex, 1); + categories.splice(targetIndex, 0, draggedCategory); + + preferences.pinned_badge_wall_categories = categories.join(','); + } + }; + + const handleDragLeave = ( + event: DragEvent & { currentTarget: EventTarget & HTMLDivElement }, + category: string + ) => { + event.preventDefault(); + + if (draggedOverCategory === category && preferences && draggedCategory) { + draggedOverCategory = null; + + const categories = preferences.pinned_badge_wall_categories.split(','); + const draggedIndex = categories.indexOf(draggedCategory); + + categories.splice(draggedIndex, 1); + categories.splice(categories.indexOf(category) + 1, 0, draggedCategory); + + preferences.pinned_badge_wall_categories = categories.join(','); + } + }; + + const handleDrop = (event: { preventDefault: () => void }) => { + event.preventDefault(); + + if (userData && preferences) { + fetch( + root( + `/api/preferences?id=${userData.id}&setCategories=${preferences.pinned_badge_wall_categories}` + ), + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + } + } + ).then(refreshPreferences); + } + + draggedCategory = null; + draggedOverCategory = null; + }; + onMount(async () => { schedule = typeSchedule( parseScheduleHtml( @@ -248,10 +326,21 @@
{#each preferences.pinned_badge_wall_categories.split(',') as category} - +
handleDragStart(event, category)} + on:dragover={handleDragOver} + on:dragenter={(event) => handleDragEnter(event, category)} + on:dragleave={(event) => handleDragLeave(event, category)} + on:drop={handleDrop} + role="button" + tabindex="0" + > {category} + - +
{/each} -- cgit v1.2.3