From 9fa2eb9e16708470976a5314996effd20a96a255 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:12:23 +0000 Subject: [PATCH] Add missing hooks and fix getImage to be synchronous Co-authored-by: leinelissen <10154841+leinelissen@users.noreply.github.com> --- src/store/music/hooks.ts | 80 +++++++++++++++++++++++++++++++--- src/utility/JellyfinApi/lib.ts | 17 +++----- 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/src/store/music/hooks.ts b/src/store/music/hooks.ts index c55354d..4dc9302 100644 --- a/src/store/music/hooks.ts +++ b/src/store/music/hooks.ts @@ -10,7 +10,8 @@ import { albums } from '@/store/db/schema/albums'; import { artists } from '@/store/db/schema/artists'; import { tracks } from '@/store/db/schema/tracks'; import { playlists } from '@/store/db/schema/playlists'; -import { eq, desc } from 'drizzle-orm'; +import { playlistTracks } from '@/store/db/schema/playlist-tracks'; +import { eq, desc, and, inArray } from 'drizzle-orm'; import { parseISO } from 'date-fns'; import { ALPHABET_LETTERS } from '@/CONSTANTS'; import type { SectionListData } from 'react-native'; @@ -27,14 +28,23 @@ export function useAlbums(sourceId: string) { return useMemo(() => { const albumsMap: Record = {}; const ids: string[] = []; + let lastRefreshed: Date | undefined; (data || []).forEach(album => { const enriched = enrichAlbum(album); albumsMap[enriched.Id] = enriched; ids.push(enriched.Id); + + // Track the oldest lastRefreshed date + if (enriched.lastRefreshed) { + const refreshDate = new Date(enriched.lastRefreshed); + if (!lastRefreshed || refreshDate < lastRefreshed) { + lastRefreshed = refreshDate; + } + } }); - return { albums: albumsMap, ids, error, isLoading: false }; + return { albums: albumsMap, ids, error, isLoading: false, lastRefreshed }; }, [data, error]); } @@ -116,14 +126,23 @@ export function useArtists(sourceId: string) { return useMemo(() => { const artistsMap: Record = {}; const ids: string[] = []; + let lastRefreshed: Date | undefined; (data || []).forEach(artist => { const enriched = enrichArtist(artist); artistsMap[enriched.Id] = enriched; ids.push(enriched.Id); + + // Track the oldest lastRefreshed date + if (enriched.lastRefreshed) { + const refreshDate = new Date(enriched.lastRefreshed); + if (!lastRefreshed || refreshDate < lastRefreshed) { + lastRefreshed = refreshDate; + } + } }); - return { artists: artistsMap, ids, error }; + return { artists: artistsMap, ids, error, isLoading: false, lastRefreshed }; }, [data, error]); } @@ -159,23 +178,34 @@ export function usePlaylists(sourceId: string) { return useMemo(() => { const playlistsMap: Record = {}; const ids: string[] = []; + let lastRefreshed: Date | undefined; (data || []).forEach(playlist => { const enriched = enrichPlaylist(playlist); playlistsMap[enriched.Id] = enriched; ids.push(enriched.Id); + + // Track the oldest lastRefreshed date + if (enriched.lastRefreshed) { + const refreshDate = new Date(enriched.lastRefreshed); + if (!lastRefreshed || refreshDate < lastRefreshed) { + lastRefreshed = refreshDate; + } + } }); - return { playlists: playlistsMap, ids, error }; + return { playlists: playlistsMap, ids, error, isLoading: false, lastRefreshed }; }, [data, error]); } /** * Get tracks by album */ -export function useTracksByAlbum(albumId: string) { +export function useTracksByAlbum(sourceId: string, albumId: string) { const { data, error } = useLiveQuery( - albumId ? db.select().from(tracks).where(eq(tracks.albumId, albumId)) : null + sourceId && albumId + ? db.select().from(tracks).where(and(eq(tracks.sourceId, sourceId), eq(tracks.albumId, albumId))) + : null ); return useMemo(() => { @@ -192,6 +222,42 @@ export function useTracksByAlbum(albumId: string) { }, [data, error]); } +/** + * Get tracks by playlist + */ +export function useTracksByPlaylist(sourceId: string, playlistId: string) { + const { data: relations, error: relError } = useLiveQuery( + sourceId && playlistId + ? db.select().from(playlistTracks).where(and(eq(playlistTracks.sourceId, sourceId), eq(playlistTracks.playlistId, playlistId))) + : null + ); + + const trackIds = useMemo(() => (relations || []).map(r => r.trackId), [relations]); + + const { data: tracksData, error: tracksError } = useLiveQuery( + trackIds.length > 0 + ? db.select().from(tracks).where(inArray(tracks.id, trackIds)) + : null + ); + + return useMemo(() => { + const tracksMap: Record = {}; + + // Create map for quick lookup + (tracksData || []).forEach(track => { + const enriched = enrichTrack(track); + tracksMap[enriched.Id] = enriched; + }); + + // Sort by position in playlist + const sortedIds = (relations || []) + .sort((a, b) => (a.position || 0) - (b.position || 0)) + .map(r => r.trackId); + + return { tracks: tracksMap, ids: sortedIds, error: relError || tracksError }; + }, [relations, tracksData, relError, tracksError]); +} + /** * Get all tracks for a source */ @@ -210,7 +276,7 @@ export function useTracks(sourceId: string) { ids.push(enriched.Id); }); - return { tracks: tracksMap, ids, error }; + return { tracks: tracksMap, ids, error, isLoading: false }; }, [data, error]); } diff --git a/src/utility/JellyfinApi/lib.ts b/src/utility/JellyfinApi/lib.ts index 3d7c927..945438c 100644 --- a/src/utility/JellyfinApi/lib.ts +++ b/src/utility/JellyfinApi/lib.ts @@ -131,11 +131,11 @@ function formatImageUri(ItemId: string | number, baseUri: string): string { /** * Retrieve an image URL for a given ItemId + * Note: This function is synchronous and does not check for downloaded images. + * Downloaded images should be handled separately if needed. */ -export async function getImage(item: string | number | Album | AlbumTrack | Playlist | ArtistItem | null, credentials?: Credentials): Promise { - // Either accept provided credentials, or retrieve them directly from the database - const creds = credentials ?? await getCredentials(); - const serverUri = creds?.uri; +export function getImage(item: string | number | Album | AlbumTrack | Playlist | ArtistItem | null, credentials?: Credentials): string | undefined { + const serverUri = credentials?.uri; if (!item || !serverUri) { return undefined; @@ -150,14 +150,7 @@ export async function getImage(item: string | number | Album | AlbumTrack | Play ? item.AlbumId || item.Id : item.Id; - // Check if we have a downloaded image for this item - const downloadEntity = await db.select().from(downloads).where(eq(downloads.id, itemId)).limit(1); - const metadata = downloadEntity[0]?.metadataJson ? JSON.parse(downloadEntity[0].metadataJson) : {}; - if (metadata.image) { - return metadata.image; - } - - // If no downloaded image, fall back to server URL + // Return server URL for the image if (typeof item === 'string' || typeof item === 'number') { if (__DEV__) { console.warn('useGetImage: supplied item is string or number. Please submit an item object instead.', { item });