From dddb57c98b388921e7595ab6bac331e041238979 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Fri, 25 Sep 2026 02:22:46 +0200 Subject: [PATCH] Add coauthor invitations tab with accept and reject --- .../library/LibraryInvitationsSection.svelte | 92 +++++++++++++++++++ .../lib/components/ui/primitives/Tabs.svelte | 11 +++ .../routes/(app)/library/+layout.server.ts | 9 ++ .../src/routes/(app)/library/+layout.svelte | 9 +- .../(app)/library/invitations/+page.server.ts | 25 +++++ .../(app)/library/invitations/+page.svelte | 7 ++ 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 web-player/src/lib/components/music/library/LibraryInvitationsSection.svelte create mode 100644 web-player/src/routes/(app)/library/+layout.server.ts create mode 100644 web-player/src/routes/(app)/library/invitations/+page.server.ts create mode 100644 web-player/src/routes/(app)/library/invitations/+page.svelte diff --git a/web-player/src/lib/components/music/library/LibraryInvitationsSection.svelte b/web-player/src/lib/components/music/library/LibraryInvitationsSection.svelte new file mode 100644 index 0000000..cc81289 --- /dev/null +++ b/web-player/src/lib/components/music/library/LibraryInvitationsSection.svelte @@ -0,0 +1,92 @@ + + +{#if error} + {error} +{/if} + +{#if visible.length > 0} + +{:else} + +{/if} diff --git a/web-player/src/lib/components/ui/primitives/Tabs.svelte b/web-player/src/lib/components/ui/primitives/Tabs.svelte index 681006a..3248908 100644 --- a/web-player/src/lib/components/ui/primitives/Tabs.svelte +++ b/web-player/src/lib/components/ui/primitives/Tabs.svelte @@ -3,6 +3,7 @@ href: string; label: string; active: boolean; + count?: number; } interface Props { @@ -51,6 +52,16 @@ : 'text-fg-2 hover:text-fg'}" > {tab.label} + {#if tab.count} + + {tab.count} + + {/if} {/each} diff --git a/web-player/src/routes/(app)/library/+layout.server.ts b/web-player/src/routes/(app)/library/+layout.server.ts new file mode 100644 index 0000000..fe20059 --- /dev/null +++ b/web-player/src/routes/(app)/library/+layout.server.ts @@ -0,0 +1,9 @@ +import type { LayoutServerLoad } from './$types'; +import { apiFor } from '$lib/server/api'; +import { requireUser } from '$lib/server/guards'; +import { loadInvitationCount } from '$lib/server/coauthors'; + +export const load: LayoutServerLoad = async ({ locals, url, fetch }) => { + requireUser(locals, url); + return { invitationCount: await loadInvitationCount(apiFor({ fetch, locals })) }; +}; diff --git a/web-player/src/routes/(app)/library/+layout.svelte b/web-player/src/routes/(app)/library/+layout.svelte index 897784d..b100151 100644 --- a/web-player/src/routes/(app)/library/+layout.svelte +++ b/web-player/src/routes/(app)/library/+layout.svelte @@ -24,7 +24,7 @@ import PageHeader from '$lib/components/ui/layout/PageHeader.svelte'; import Tabs from '$lib/components/ui/primitives/Tabs.svelte'; - let { children } = $props(); + let { data, children } = $props(); const pathname = $derived(page.url.pathname.replace(/\/$/, '')); const current = $derived( @@ -35,7 +35,12 @@ ) ?? SECTIONS[0] ); const tabs = $derived( - SECTIONS.map(({ href, label }) => ({ href, label, active: href === current.href })) + SECTIONS.map(({ href, label }) => ({ + href, + label, + active: href === current.href, + count: href === '/library/invitations' ? data.invitationCount : undefined + })) ); diff --git a/web-player/src/routes/(app)/library/invitations/+page.server.ts b/web-player/src/routes/(app)/library/invitations/+page.server.ts new file mode 100644 index 0000000..cfe3d6d --- /dev/null +++ b/web-player/src/routes/(app)/library/invitations/+page.server.ts @@ -0,0 +1,25 @@ +import type { PageServerLoad, Actions } from './$types'; +import { fail } from '@sveltejs/kit'; +import { apiFor } from '$lib/server/api'; +import { requireUser } from '$lib/server/guards'; +import { formString } from '$lib/server/forms/fields'; +import { tagged } from '$lib/server/actions/tagged'; +import { loadInvitations, respondToInvitation } from '$lib/server/coauthors'; + +export const load: PageServerLoad = async ({ locals, url, fetch }) => { + requireUser(locals, url); + return { invitations: await loadInvitations(apiFor({ fetch, locals })) }; +}; + +function respond(decision: 'accept' | 'reject') { + return tagged(decision, async ({ api, form }) => { + const invitationId = formString(form, 'invitationId'); + if (!invitationId) return fail(400, { message: 'Falta la invitación.' }); + return respondToInvitation(api, invitationId, decision); + }); +} + +export const actions: Actions = { + accept: respond('accept'), + reject: respond('reject') +}; diff --git a/web-player/src/routes/(app)/library/invitations/+page.svelte b/web-player/src/routes/(app)/library/invitations/+page.svelte new file mode 100644 index 0000000..bc4e00c --- /dev/null +++ b/web-player/src/routes/(app)/library/invitations/+page.svelte @@ -0,0 +1,7 @@ + + +