mirror of
https://github.com/leinelissen/jellyfin-audio-player.git
synced 2026-09-02 21:03:11 +03:00
fix: simplify hooks
This commit is contained in:
+14
-32
@@ -1,47 +1,29 @@
|
||||
/**
|
||||
* Database-backed hooks for albums
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useLiveQuery } from '@/store/live-queries';
|
||||
import { db } from '@/store';
|
||||
import albums from './entity';
|
||||
import { and, eq, desc } from 'drizzle-orm';
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
|
||||
export function useAlbums(sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(albums).where(eq(albums.sourceId, sourceId))
|
||||
: db.select().from(albums)
|
||||
return useLiveQuery(
|
||||
db.query.albums.findMany({
|
||||
where: sourceId ? eq(albums.sourceId, sourceId) : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: (data || []),
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
export function useAlbum([sourceId, id]: [sourceId: string, id: string]) {
|
||||
const { data, error } = useLiveQuery(
|
||||
db.select()
|
||||
.from(albums)
|
||||
.where(and(eq(albums.sourceId, sourceId), eq(albums.id, id)))
|
||||
.limit(1)
|
||||
return useLiveQuery(
|
||||
db.query.albums.findFirst({
|
||||
where: and(eq(albums.sourceId, sourceId), eq(albums.id, id))
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: data?.[0],
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
export function useRecentAlbums(limit: number = 24) {
|
||||
const { data, error } = useLiveQuery(
|
||||
db.select().from(albums).orderBy(desc(albums.dateCreated)).limit(limit)
|
||||
return useLiveQuery(
|
||||
db.query.albums.findMany({
|
||||
orderBy: (album, { desc }) => [desc(album.dateCreated)],
|
||||
limit,
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: (data || []),
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,20 @@
|
||||
/**
|
||||
* Database-backed hooks for artists
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useLiveQuery } from '@/store/live-queries';
|
||||
import { db } from '@/store';
|
||||
import artists from './entity';
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
|
||||
export function useArtists(sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(artists).where(eq(artists.sourceId, sourceId))
|
||||
: db.select().from(artists)
|
||||
return useLiveQuery(
|
||||
db.query.artists.findMany({
|
||||
where: sourceId ? eq(artists.sourceId, sourceId) : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: data ?? [],
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
export function useArtist([sourceId, id]: [sourceId: string, id: string]) {
|
||||
const { data, error } = useLiveQuery(
|
||||
db.select()
|
||||
.from(artists)
|
||||
.where(and(eq(artists.sourceId, sourceId), eq(artists.id, id)))
|
||||
.limit(1)
|
||||
return useLiveQuery(
|
||||
db.query.artists.findFirst({
|
||||
where: and(eq(artists.sourceId, sourceId), eq(artists.id, id)),
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: data?.[0],
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
@@ -1,43 +1,20 @@
|
||||
/**
|
||||
* Database-backed hooks for downloads data
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useLiveQuery } from '@/store/live-queries';
|
||||
import { db } from '@/store';
|
||||
import downloads from './entity';
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
import type { Download } from './types';
|
||||
|
||||
export function useDownloads(sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(downloads).where(eq(downloads.sourceId, sourceId))
|
||||
: db.select().from(downloads)
|
||||
return useLiveQuery(
|
||||
db.query.downloads.findMany({
|
||||
where: sourceId ? eq(downloads.sourceId, sourceId) : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
return {
|
||||
data: data ?? [],
|
||||
error
|
||||
};
|
||||
}
|
||||
|
||||
export function useDownload([sourceId, trackId]: [sourceId: string, trackId: string]) {
|
||||
const { data, error } = useLiveQuery(
|
||||
db.select()
|
||||
.from(downloads)
|
||||
.where(and(eq(downloads.sourceId, sourceId), eq(downloads.id, trackId)))
|
||||
.limit(1)
|
||||
return useLiveQuery(
|
||||
db.query.downloads.findFirst({
|
||||
where: and(eq(downloads.sourceId, sourceId), eq(downloads.id, trackId)),
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: data?.[0],
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
export function useIsDownloaded([sourceId, trackId]: [sourceId: string, trackId: string]): boolean {
|
||||
const { data } = useDownload([sourceId, trackId]);
|
||||
return data?.isComplete === true;
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import migrations from './database/migrations/migrations.js';
|
||||
|
||||
// Import all schema tables
|
||||
import sources from './sources/entity';
|
||||
import appSettings from './settings/entity.js';
|
||||
import settings from './settings/entity';
|
||||
import sleepTimer from './sleep-timer/entity';
|
||||
import artists from './artists/entity';
|
||||
import albums from './albums/entity';
|
||||
@@ -22,7 +22,7 @@ import syncCursors from './sync-cursors/entity';
|
||||
// Combined schema for drizzle
|
||||
const schema = {
|
||||
sources,
|
||||
appSettings,
|
||||
settings,
|
||||
sleepTimer,
|
||||
artists,
|
||||
albums,
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
/**
|
||||
* Database-backed hooks for music data
|
||||
* These replace Redux selectors with live database queries
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useLiveQuery } from '@/store/live-queries';
|
||||
import { db } from '@/store';
|
||||
import albums from '@/store/albums/entity';
|
||||
import artists from '@/store/artists/entity';
|
||||
import tracks from '@/store/tracks/entity';
|
||||
import playlists from '@/store/playlists/entity';
|
||||
import playlistTracks from '@/store/playlist-tracks/entity';
|
||||
import { and, eq, desc, inArray } from 'drizzle-orm';
|
||||
import { ALPHABET_LETTERS } from '@/CONSTANTS';
|
||||
import type { Album, AlbumTrack, MusicArtist, Playlist } from './types';
|
||||
|
||||
type AlbumSection = { label: string; data: string[][] };
|
||||
type ArtistSection = { label: string; data: MusicArtist[] };
|
||||
|
||||
const createAlbumSection = (label: string): AlbumSection => ({ label, data: [[]] });
|
||||
const createArtistSection = (label: string): ArtistSection => ({ label, data: [] });
|
||||
|
||||
/**
|
||||
* Get all albums (from all sources)
|
||||
*/
|
||||
export function useAlbums(sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(albums).where(eq(albums.sourceId, sourceId))
|
||||
: db.select().from(albums)
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const albumsMap: Record<string, Album> = {};
|
||||
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, lastRefreshed };
|
||||
}, [data, error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent albums (sorted by date created, from all sources)
|
||||
*/
|
||||
export function useRecentAlbums(amount: number = 24, sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(albums).where(eq(albums.sourceId, sourceId)).orderBy(desc(albums.dateCreated)).limit(amount)
|
||||
: db.select().from(albums).orderBy(desc(albums.dateCreated)).limit(amount)
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const albumsMap: Record<string, Album> = {};
|
||||
const ids: string[] = [];
|
||||
|
||||
(data || []).forEach(album => {
|
||||
const enriched = enrichAlbum(album);
|
||||
albumsMap[enriched.Id] = enriched;
|
||||
ids.push(enriched.Id);
|
||||
});
|
||||
|
||||
return { albums: albumsMap, ids, error };
|
||||
}, [data, error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get albums sorted by alphabet with sections
|
||||
*/
|
||||
export function useAlbumsByAlphabet() {
|
||||
const { albums: albumsMap, ids } = useAlbums();
|
||||
|
||||
return useMemo(() => {
|
||||
// Sort by album artist
|
||||
const sorted = [...ids].sort((a, b) => {
|
||||
const albumA = albumsMap[a];
|
||||
const albumB = albumsMap[b];
|
||||
if ((!albumA && !albumB) || (!albumA?.AlbumArtist && !albumB?.AlbumArtist)) {
|
||||
return 0;
|
||||
} else if (!albumA || !albumA.AlbumArtist) {
|
||||
return 1;
|
||||
} else if (!albumB || !albumB.AlbumArtist) {
|
||||
return -1;
|
||||
}
|
||||
return albumA.AlbumArtist.localeCompare(albumB.AlbumArtist);
|
||||
});
|
||||
|
||||
// Split into alphabet sections
|
||||
const sections = ALPHABET_LETTERS.split('').map(createAlbumSection);
|
||||
|
||||
sorted.forEach((id) => {
|
||||
const album = albumsMap[id];
|
||||
const letter = album?.AlbumArtist?.toUpperCase().charAt(0);
|
||||
const index = letter ? ALPHABET_LETTERS.indexOf(letter) : 26;
|
||||
|
||||
const section = sections[index >= 0 ? index : 26];
|
||||
const row = section.data.length - 1;
|
||||
|
||||
section.data[row].push(id);
|
||||
|
||||
if (section.data[row].length >= 2) {
|
||||
section.data.push([]);
|
||||
}
|
||||
});
|
||||
|
||||
return sections;
|
||||
}, [albumsMap, ids]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all artists (from all sources)
|
||||
*/
|
||||
export function useArtists(sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(artists).where(eq(artists.sourceId, sourceId))
|
||||
: db.select().from(artists)
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const artistsMap: Record<string, MusicArtist> = {};
|
||||
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, isLoading: false, lastRefreshed };
|
||||
}, [data, error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get artists sorted by alphabet with sections
|
||||
*/
|
||||
export function useArtistsByAlphabet() {
|
||||
const { artists: artistsMap } = useArtists();
|
||||
|
||||
return useMemo(() => {
|
||||
const artistsList = Object.values(artistsMap);
|
||||
const sections = ALPHABET_LETTERS.split('').map(createArtistSection);
|
||||
|
||||
artistsList.forEach((artist) => {
|
||||
const letter = artist.Name.toUpperCase().charAt(0);
|
||||
const index = letter ? ALPHABET_LETTERS.indexOf(letter) : 26;
|
||||
const section = sections[index >= 0 ? index : 26];
|
||||
section.data.push(artist);
|
||||
});
|
||||
|
||||
return sections;
|
||||
}, [artistsMap]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all playlists (from all sources)
|
||||
*/
|
||||
export function usePlaylists(sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(playlists).where(eq(playlists.sourceId, sourceId))
|
||||
: db.select().from(playlists)
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const playlistsMap: Record<string, Playlist> = {};
|
||||
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, isLoading: false, lastRefreshed };
|
||||
}, [data, error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tracks by album
|
||||
*/
|
||||
export function useTracksByAlbum([sourceId, albumId]: [sourceId: string, albumId: string]) {
|
||||
const { data, error } = useLiveQuery(
|
||||
db.select()
|
||||
.from(tracks)
|
||||
.where(and(eq(tracks.sourceId, sourceId), eq(tracks.albumId, albumId)))
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const tracksMap: Record<string, AlbumTrack> = {};
|
||||
const ids: string[] = [];
|
||||
|
||||
(data || []).forEach(track => {
|
||||
const enriched = enrichTrack(track);
|
||||
tracksMap[enriched.Id] = enriched;
|
||||
ids.push(enriched.Id);
|
||||
});
|
||||
|
||||
return { tracks: tracksMap, ids, error };
|
||||
}, [data, error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tracks by playlist
|
||||
*/
|
||||
export function useTracksByPlaylist([sourceId, playlistId]: [sourceId: string, playlistId: string]) {
|
||||
const { data: relations, error: relError } = useLiveQuery(
|
||||
db.select()
|
||||
.from(playlistTracks)
|
||||
.where(and(eq(playlistTracks.sourceId, sourceId), eq(playlistTracks.playlistId, playlistId)))
|
||||
);
|
||||
|
||||
const trackIds = useMemo(() => (relations || []).map(r => r.trackId), [relations]);
|
||||
|
||||
const { data: tracksData, error: tracksError } = useLiveQuery(
|
||||
trackIds.length > 0
|
||||
? db.select()
|
||||
.from(tracks)
|
||||
.where(and(eq(tracks.sourceId, sourceId), inArray(tracks.id, trackIds)))
|
||||
: null
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const tracksMap: Record<string, AlbumTrack> = {};
|
||||
|
||||
// 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 (from all sources)
|
||||
*/
|
||||
export function useTracks(sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(tracks).where(eq(tracks.sourceId, sourceId))
|
||||
: db.select().from(tracks)
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const tracksMap: Record<string, AlbumTrack> = {};
|
||||
const ids: string[] = [];
|
||||
|
||||
(data || []).forEach(track => {
|
||||
const enriched = enrichTrack(track);
|
||||
tracksMap[enriched.Id] = enriched;
|
||||
ids.push(enriched.Id);
|
||||
});
|
||||
|
||||
return { tracks: tracksMap, ids, error, isLoading: false };
|
||||
}, [data, error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper functions to enrich database rows with full type information
|
||||
*/
|
||||
|
||||
function enrichAlbum(dbAlbum: any): Album {
|
||||
const metadata = dbAlbum.metadataJson ? JSON.parse(dbAlbum.metadataJson) : {};
|
||||
return {
|
||||
Id: dbAlbum.id,
|
||||
Name: dbAlbum.name,
|
||||
ProductionYear: dbAlbum.productionYear,
|
||||
IsFolder: dbAlbum.isFolder,
|
||||
AlbumArtist: dbAlbum.albumArtist,
|
||||
DateCreated: dbAlbum.dateCreated ? new Date(dbAlbum.dateCreated).toISOString() : new Date().toISOString(),
|
||||
lastRefreshed: dbAlbum.lastRefreshed,
|
||||
...metadata,
|
||||
};
|
||||
}
|
||||
|
||||
function enrichArtist(dbArtist: any): MusicArtist {
|
||||
const metadata = dbArtist.metadataJson ? JSON.parse(dbArtist.metadataJson) : {};
|
||||
return {
|
||||
Id: dbArtist.id,
|
||||
Name: dbArtist.name,
|
||||
IsFolder: dbArtist.isFolder,
|
||||
...metadata,
|
||||
};
|
||||
}
|
||||
|
||||
function enrichTrack(dbTrack: any): AlbumTrack {
|
||||
const metadata = dbTrack.metadataJson ? JSON.parse(dbTrack.metadataJson) : {};
|
||||
const lyrics = dbTrack.lyrics ? JSON.parse(dbTrack.lyrics) : undefined;
|
||||
|
||||
return {
|
||||
Id: dbTrack.id,
|
||||
Name: dbTrack.name,
|
||||
AlbumId: dbTrack.albumId,
|
||||
Album: dbTrack.album,
|
||||
AlbumArtist: dbTrack.albumArtist,
|
||||
ProductionYear: dbTrack.productionYear,
|
||||
IndexNumber: dbTrack.indexNumber,
|
||||
ParentIndexNumber: dbTrack.parentIndexNumber,
|
||||
HasLyrics: dbTrack.hasLyrics,
|
||||
RunTimeTicks: dbTrack.runTimeTicks,
|
||||
Lyrics: lyrics,
|
||||
...metadata,
|
||||
};
|
||||
}
|
||||
|
||||
function enrichPlaylist(dbPlaylist: any): Playlist {
|
||||
const metadata = dbPlaylist.metadataJson ? JSON.parse(dbPlaylist.metadataJson) : {};
|
||||
return {
|
||||
Id: dbPlaylist.id,
|
||||
Name: dbPlaylist.name,
|
||||
CanDelete: dbPlaylist.canDelete,
|
||||
ChildCount: dbPlaylist.childCount,
|
||||
lastRefreshed: dbPlaylist.lastRefreshed,
|
||||
...metadata,
|
||||
};
|
||||
}
|
||||
@@ -2,35 +2,23 @@
|
||||
* Database-backed hooks for playlists
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useLiveQuery } from '@/store/live-queries';
|
||||
import { db } from '@/store';
|
||||
import playlists from './entity';
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
|
||||
export function usePlaylists(sourceId?: string) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(playlists).where(eq(playlists.sourceId, sourceId))
|
||||
: db.select().from(playlists)
|
||||
return useLiveQuery(
|
||||
db.query.playlists.findMany({
|
||||
where: sourceId ? eq(playlists.sourceId, sourceId) : undefined,
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: data ?? [],
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
export function usePlaylist([sourceId, id]: [sourceId: string, id: string]) {
|
||||
const { data, error } = useLiveQuery(
|
||||
db.select()
|
||||
.from(playlists)
|
||||
.where(and(eq(playlists.sourceId, sourceId), eq(playlists.id, id)))
|
||||
.limit(1)
|
||||
return useLiveQuery(
|
||||
db.query.playlists.findFirst({
|
||||
where: and(eq(playlists.sourceId, sourceId), eq(playlists.id, id)),
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: data?.[0],
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
@@ -2,21 +2,17 @@
|
||||
* Database-backed hooks for search queries
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useLiveQuery } from '@/store/live-queries';
|
||||
import { db } from '@/store';
|
||||
import searchQueries from './entity';
|
||||
import { eq, desc } from 'drizzle-orm';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export function useSearchQueries(sourceId?: string, limit?: number) {
|
||||
const { data, error } = useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(searchQueries).where(eq(searchQueries.sourceId, sourceId)).orderBy(desc(searchQueries.timestamp)).limit(limit || 100)
|
||||
: db.select().from(searchQueries).orderBy(desc(searchQueries.timestamp)).limit(limit || 100)
|
||||
return useLiveQuery(
|
||||
db.query.searchQueries.findMany({
|
||||
where: sourceId ? eq(searchQueries.sourceId, sourceId) : undefined,
|
||||
orderBy: (query, { desc }) => [desc(query.timestamp)],
|
||||
limit: limit || 100,
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: data ?? [],
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
|
||||
/**
|
||||
* App settings - global application settings (single row, id=1)
|
||||
*/
|
||||
const settings = sqliteTable('app_settings', {
|
||||
const settings = sqliteTable('settings', {
|
||||
id: integer('id').primaryKey().$default(() => 1),
|
||||
bitrate: integer('bitrate').notNull(),
|
||||
isOnboardingComplete: integer('is_onboarding_complete', { mode: 'boolean' }).notNull(),
|
||||
|
||||
@@ -8,11 +8,9 @@ import { eq } from 'drizzle-orm';
|
||||
import settings from './entity';
|
||||
|
||||
export function useAppSettings() {
|
||||
const { data, error } = useLiveQuery(
|
||||
db.select().from(settings)
|
||||
.where(eq(settings.id, 1))
|
||||
.limit(1)
|
||||
return useLiveQuery(
|
||||
db.query.settings.findFirst({
|
||||
where: eq(settings.id, 1),
|
||||
})
|
||||
);
|
||||
|
||||
return { data: data?.[0], error };
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* Sleep Timer Database Operations
|
||||
*
|
||||
* Replaces Redux store with direct database operations for sleep timer.
|
||||
*/
|
||||
|
||||
import { db } from '../database/client';
|
||||
import sleepTimer from './entity';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { invalidateTable } from '../live-queries';
|
||||
|
||||
const SLEEP_TIMER_ID = 1;
|
||||
|
||||
/**
|
||||
* Get the current sleep timer date
|
||||
*/
|
||||
export async function getSleepTimerDate(): Promise<number | null> {
|
||||
const result = await db
|
||||
.select()
|
||||
.from(sleepTimer)
|
||||
.where(eq(sleepTimer.id, SLEEP_TIMER_ID))
|
||||
.limit(1);
|
||||
|
||||
return result[0]?.date ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sleep timer date
|
||||
*/
|
||||
export async function setSleepTimerDate(date: Date | null): Promise<void> {
|
||||
const now = Date.now();
|
||||
const dateValue = date?.getTime() ?? null;
|
||||
|
||||
await db
|
||||
.insert(sleepTimer)
|
||||
.values({
|
||||
id: SLEEP_TIMER_ID,
|
||||
date: dateValue,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: [sleepTimer.id],
|
||||
set: {
|
||||
date: dateValue,
|
||||
updatedAt: now,
|
||||
},
|
||||
});
|
||||
|
||||
// Invalidate to trigger live query updates
|
||||
invalidateTable('sleep_timer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the sleep timer
|
||||
*/
|
||||
export async function clearSleepTimer(): Promise<void> {
|
||||
await setSleepTimerDate(null);
|
||||
}
|
||||
@@ -1,20 +1,12 @@
|
||||
/**
|
||||
* Database-backed hooks for sleep timer
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useLiveQuery } from '@/store/live-queries';
|
||||
import { db } from '@/store';
|
||||
import sleepTimer from './entity';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export function useSleepTimer() {
|
||||
const { data, error } = useLiveQuery(
|
||||
db.select().from(sleepTimer).where(eq(sleepTimer.id, 1)).limit(1)
|
||||
return useLiveQuery(
|
||||
db.query.sleepTimer.findFirst({
|
||||
where: eq(sleepTimer.id, 1),
|
||||
})
|
||||
);
|
||||
|
||||
return useMemo(() => ({
|
||||
data: data?.[0],
|
||||
error,
|
||||
}), [data, error]);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* Database-backed hooks for tracks with download joins
|
||||
*/
|
||||
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
import { useLiveQuery } from '@/store/live-queries';
|
||||
import { db } from '@/store';
|
||||
@@ -9,17 +5,16 @@ import tracks from './entity';
|
||||
|
||||
export function useTracks(sourceId?: string) {
|
||||
return useLiveQuery(
|
||||
sourceId
|
||||
? db.select().from(tracks).where(eq(tracks.sourceId, sourceId))
|
||||
: db.select().from(tracks)
|
||||
db.query.tracks.findMany({
|
||||
where: sourceId ? eq(tracks.sourceId, sourceId) : undefined,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function useTrack([sourceId, id]: [sourceId: string, id: string]) {
|
||||
return useLiveQuery(
|
||||
db.select()
|
||||
.from(tracks)
|
||||
.where(and(eq(tracks.sourceId, sourceId), eq(tracks.id, id)))
|
||||
.limit(1)
|
||||
db.query.tracks.findFirst({
|
||||
where: and(eq(tracks.sourceId, sourceId), eq(tracks.id, id)),
|
||||
})
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user