From 75c6c70397570fb00739f7953f5049e4eb1cb1c7 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Wed, 26 Nov 2025 01:14:22 +0000 Subject: [PATCH 1/3] Move related artists inference work to API --- .../routes/users/[slug]/artists/+server.ts | 37 +++++++++++++++ .../users/[slug]/collections/+server.ts | 3 +- dashboard/src/routes/+layout.server.ts | 45 +++---------------- 3 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 api/src/routes/users/[slug]/artists/+server.ts diff --git a/api/src/routes/users/[slug]/artists/+server.ts b/api/src/routes/users/[slug]/artists/+server.ts new file mode 100644 index 0000000..45530b3 --- /dev/null +++ b/api/src/routes/users/[slug]/artists/+server.ts @@ -0,0 +1,37 @@ +import { TABLES } from '../../../../../../shared/config'; +import { supabase } from '$lib/server/supabase'; +import { json } from '@sveltejs/kit'; +import type { ArtistHydrated } from '../../../../../../shared/types/hydrated'; + +export async function GET({ params }) { + const userId = params.slug; + + const { data: relatedArtistsData, error } = await supabase + .from(TABLES.artistMembers) + .select('artist_id') + .eq('user_id', userId); + + if (error) { + console.error('Error fetching user artists:', error); + return json({ error: 'Failed to fetch user artists' }, { status: 500 }); + } + + const { + data: connectedArtists, + error: artistsError + }: { + data: ArtistHydrated[] | null; + error: Error | null; + } = await supabase + .from(TABLES.artistsRich) + .select('*') + .in( + 'id', + relatedArtistsData.map((u) => u.artist_id) + ); + if (artistsError) { + console.error('Error fetching connected artists:', artistsError); + return json({ error: 'Failed to fetch connected artists' }, { status: 500 }); + } + return json(connectedArtists); +} diff --git a/api/src/routes/users/[slug]/collections/+server.ts b/api/src/routes/users/[slug]/collections/+server.ts index 4627f51..63e85bb 100644 --- a/api/src/routes/users/[slug]/collections/+server.ts +++ b/api/src/routes/users/[slug]/collections/+server.ts @@ -1,8 +1,9 @@ import { handlePostgrestQuery, supabase } from '$lib/server/supabase'; import { TABLES } from '../../../../../../shared/config/index'; +import type { CollectionHydrated } from '../../../../../../shared/types/hydrated'; export async function GET({ params }) { - return handlePostgrestQuery( + return handlePostgrestQuery( async () => supabase.from(TABLES.collectionsRich).select('*').eq('user_id', params.slug), { errorMessage: 'Failed to fetch collections' } ); diff --git a/dashboard/src/routes/+layout.server.ts b/dashboard/src/routes/+layout.server.ts index 8e066de..3818892 100644 --- a/dashboard/src/routes/+layout.server.ts +++ b/dashboard/src/routes/+layout.server.ts @@ -3,9 +3,10 @@ import { supabase } from '$lib/server/supabase'; import type { StreamLog } from '../../../shared/types/core'; import { sortReleasesByDate } from '../../../shared/utils'; import type { LayoutServerLoad } from './$types'; -import type { Artist, Track } from '../../../shared/types/core'; -import type { ReleaseHydrated } from '../../../shared/types/hydrated'; +import type { Track } from '../../../shared/types/core'; +import type { ArtistHydrated, ReleaseHydrated } from '../../../shared/types/hydrated'; import { TABLES } from '../../../shared/config'; +import { API_BASE, REQUEST_HEADER_BOILERPLATE } from '$lib/config'; export const load: LayoutServerLoad = async ({ locals: { safeGetSession }, cookies }) => { const { session, user } = await safeGetSession(); @@ -24,38 +25,10 @@ export const load: LayoutServerLoad = async ({ locals: { safeGetSession }, cooki const userID = session.user.id; - const { - data: userData, - error: userError - }: { - data: { artist_id: string }[] | null; - error: Error | null; - } = await supabase.from(TABLES.artistMembers).select('artist_id').eq('user_id', userID); - - if (userError || !userData) { - console.error('Error fetching user data:', userError); - return fail(500, { error: 'Failed to fetch user data' }); - } - - // Get all artist IDs for the user - const { - data: connectedArtists, - error: artistsError - }: { - data: Artist[] | null; - error: Error | null; - } = await supabase - .from(TABLES.artists) - .select('*') - .in( - 'id', - userData.map((u) => u.artist_id) - ); - - if (artistsError || !connectedArtists) { - console.error('Error fetching artists:', artistsError); - return fail(500, { error: 'Failed to fetch artists' }); - } + const connectedArtists: ArtistHydrated[] = await fetch(`${API_BASE}/users/${userID}/artists`, { + method: 'GET', + headers: REQUEST_HEADER_BOILERPLATE + }).then((res) => res.json()); const { data: songs, @@ -73,10 +46,6 @@ export const load: LayoutServerLoad = async ({ locals: { safeGetSession }, cooki error: Error | null; } = await supabase.from(TABLES.releasesRich).select('*'); - if (artistsError || !connectedArtists) { - console.error('Error fetching artists:', artistsError); - return fail(500, { error: 'Failed to fetch artists' }); - } if (songsError || !songs) { console.error('Error fetching songs:', songsError); return fail(500, { error: 'Failed to fetch songs' }); From a4a8f37800653be4c14cdd08b2429268c592d702 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Wed, 26 Nov 2025 01:36:04 +0000 Subject: [PATCH 2/3] Artist streams endpoint and remote function --- api/src/routes/artists/[slug]/streams/+server.ts | 10 ++++++++++ .../lib/components/views/stats/StatsView.svelte | 5 ++++- .../src/lib/remote-functions/stats.remote.ts | 14 ++++++++++++++ dashboard/src/routes/+layout.server.ts | 16 +--------------- dashboard/src/routes/+layout.ts | 3 +-- dashboard/src/routes/+page.svelte | 5 +---- shared/types/core.ts | 2 -- 7 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 api/src/routes/artists/[slug]/streams/+server.ts create mode 100644 dashboard/src/lib/remote-functions/stats.remote.ts diff --git a/api/src/routes/artists/[slug]/streams/+server.ts b/api/src/routes/artists/[slug]/streams/+server.ts new file mode 100644 index 0000000..93954b4 --- /dev/null +++ b/api/src/routes/artists/[slug]/streams/+server.ts @@ -0,0 +1,10 @@ +import { handlePostgrestQuery, supabase } from '$lib/server/supabase'; +import { TABLES } from '../../../../../../shared/config'; +import type { StreamLog } from '../../../../../../shared/types/core'; + +export async function GET({ params }) { + return handlePostgrestQuery( + async () => await supabase.from(TABLES.streams).select('*').eq('artist_id', params.slug), + { errorMessage: 'Failed to fetch streams for artist' } + ); +} diff --git a/dashboard/src/lib/components/views/stats/StatsView.svelte b/dashboard/src/lib/components/views/stats/StatsView.svelte index 402d309..dafda42 100644 --- a/dashboard/src/lib/components/views/stats/StatsView.svelte +++ b/dashboard/src/lib/components/views/stats/StatsView.svelte @@ -1,7 +1,10 @@ @@ -44,7 +41,7 @@ {:else if dashboardState.activeSection === 'profile'} {:else if dashboardState.activeSection === 'stats'} - + {/if} {:else}
Select an artist to manage their releases and songs.
diff --git a/shared/types/core.ts b/shared/types/core.ts index a56a414..8b4f812 100644 --- a/shared/types/core.ts +++ b/shared/types/core.ts @@ -1,5 +1,3 @@ -import type { ReleaseHydrated } from './hydrated'; - export interface User { first_name: string; tokens_balance: number; From 633a61cc79d7207c74dd41a5ede012cd1440a3b4 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Tue, 2 Dec 2025 01:31:15 +0000 Subject: [PATCH 3/3] Less horrible artist data fetching --- .../routes/artists/[slug]/tracks/+server.ts | 10 +++++ .../components/views/stats/StatsView.svelte | 5 +-- .../src/lib/remote-functions/artist.remote.ts | 35 +++++++++++++++- .../src/lib/remote-functions/stats.remote.ts | 2 +- dashboard/src/routes/+layout.server.ts | 40 +------------------ dashboard/src/routes/+layout.svelte | 1 - dashboard/src/routes/+layout.ts | 4 +- dashboard/src/routes/+page.svelte | 28 +++++++++---- 8 files changed, 70 insertions(+), 55 deletions(-) create mode 100644 api/src/routes/artists/[slug]/tracks/+server.ts diff --git a/api/src/routes/artists/[slug]/tracks/+server.ts b/api/src/routes/artists/[slug]/tracks/+server.ts new file mode 100644 index 0000000..e663880 --- /dev/null +++ b/api/src/routes/artists/[slug]/tracks/+server.ts @@ -0,0 +1,10 @@ +import { handlePostgrestQuery, supabase } from '$lib/server/supabase'; +import { TABLES } from '../../../../../../shared/config'; +import type { Track } from '../../../../../../shared/types/core'; + +export async function GET({ params }) { + return handlePostgrestQuery( + async () => await supabase.from(TABLES.tracks).select().eq('artist_id', params.slug), + { errorMessage: 'Failed to fetch tracks for artist' } + ); +} diff --git a/dashboard/src/lib/components/views/stats/StatsView.svelte b/dashboard/src/lib/components/views/stats/StatsView.svelte index dafda42..402d309 100644 --- a/dashboard/src/lib/components/views/stats/StatsView.svelte +++ b/dashboard/src/lib/components/views/stats/StatsView.svelte @@ -1,10 +1,7 @@ diff --git a/dashboard/src/routes/+layout.ts b/dashboard/src/routes/+layout.ts index 5cd7997..6603396 100644 --- a/dashboard/src/routes/+layout.ts +++ b/dashboard/src/routes/+layout.ts @@ -29,8 +29,6 @@ export const load: LayoutLoad = async ({ fetch, data, depends }) => { supabase, session: data.session ?? null, user: data.user, - artists: data.artists, - releases: data.releases, - songs: data.songs + artists: data.artists }; }; diff --git a/dashboard/src/routes/+page.svelte b/dashboard/src/routes/+page.svelte index f68464e..75324aa 100644 --- a/dashboard/src/routes/+page.svelte +++ b/dashboard/src/routes/+page.svelte @@ -6,6 +6,8 @@ import type { ReleaseHydrated } from '../../../shared/types/hydrated'; import StatsView from '$lib/components/views/stats/StatsView.svelte'; import MusicView from '$lib/components/views/music/MusicView.svelte'; + import { getArtistReleases, getArtistTracks } from '$lib/remote-functions/artist.remote'; + import { getArtistStreams } from '$lib/remote-functions/stats.remote'; let { data @@ -21,12 +23,24 @@ let activeArtist: Artist | null = $derived( data.artists.find((artist) => artist.id === dashboardState.activeArtist?.id) || null ); - let activeArtistSongs = $derived( - data.songs.filter((song) => song.artist_id === activeArtist?.id) - ); - let activeArtistReleases = $derived( - data.releases.filter((release) => release.artist_id === activeArtist?.id) - ); + let activeArtistStreams: StreamLog[] = $state([]); + let activeArtistSongs: Track[] = $state([]); + let activeArtistReleases: ReleaseHydrated[] = $state([]); + + $effect(() => { + const fetchArtistData = async () => { + if (activeArtist) { + activeArtistStreams = await getArtistStreams(activeArtist.id); + activeArtistSongs = await getArtistTracks(activeArtist.id); + activeArtistReleases = await getArtistReleases(activeArtist.id); + } else { + activeArtistStreams = []; + activeArtistSongs = []; + activeArtistReleases = []; + } + }; + fetchArtistData(); + }); @@ -41,7 +55,7 @@ {:else if dashboardState.activeSection === 'profile'} {:else if dashboardState.activeSection === 'stats'} - + {/if} {:else}
Select an artist to manage their releases and songs.