mirror of
https://github.com/Boof2015/astra-mobile.git
synced 2026-08-20 04:30:54 +02:00
initial db rewrite
This commit is contained in:
@@ -13,12 +13,14 @@ import { useSettingsStore } from '@/stores/settingsStore';
|
||||
import { usePlayerUiStore } from '@/stores/playerUiStore';
|
||||
import { useSearchStore } from '@/stores/searchStore';
|
||||
import { useRemoteSourcesStore } from '@/stores/remoteSourcesStore';
|
||||
import { buildArtistDetail } from '@/library/artistDetail';
|
||||
import { dbTrackToTrack } from '@/library/trackAdapter';
|
||||
import {
|
||||
hasActiveNativePlaybackSession,
|
||||
restorePlaybackSession,
|
||||
restoreVirtualPlaybackContext,
|
||||
} from '@/audio/playbackController';
|
||||
import { AstraLibraryData } from '../../modules/astra-library-scanner';
|
||||
import type { DbTrack } from '@/types/library';
|
||||
import {
|
||||
installMobileSessionPersistence,
|
||||
readPersistedMobileSession,
|
||||
@@ -30,23 +32,51 @@ import {
|
||||
resolvePlaybackSession,
|
||||
shouldRestoreSavedRoute,
|
||||
stableHrefForRoute,
|
||||
validateRestoredHref,
|
||||
} from './sessionState';
|
||||
|
||||
interface SessionLifecycleProps {
|
||||
onReady: () => void;
|
||||
}
|
||||
|
||||
function validateSavedHref(href: string): string {
|
||||
const tracks = useLibraryStore.getState().tracks;
|
||||
return validateRestoredHref(href, {
|
||||
hasAlbum: (identityKey) => tracks.some((track) => track.album_identity_key === identityKey),
|
||||
hasArtist: (name, credit) => {
|
||||
const groupingMode = credit ? 'astra' : useSettingsStore.getState().artistGroupingMode;
|
||||
return buildArtistDetail(tracks, name, groupingMode).tracks.length > 0;
|
||||
},
|
||||
hasPlaylist: (id) => usePlaylistStore.getState().playlists.some((playlist) => playlist.id === id),
|
||||
});
|
||||
async function validateSavedHref(href: string): Promise<string> {
|
||||
const normalized = normalizeStableHref(href) ?? '/';
|
||||
const [pathname, query = ''] = normalized.split('?', 2);
|
||||
const albumMatch = pathname.match(/^\/library\/album\/([^/]+)$/);
|
||||
if (albumMatch) {
|
||||
const key = decodeURIComponent(albumMatch[1]);
|
||||
const result = await AstraLibraryData.getAlbumDetail<DbTrack, Record<string, unknown>>(
|
||||
key,
|
||||
null,
|
||||
1
|
||||
);
|
||||
return result.summary ? normalized : '/library';
|
||||
}
|
||||
const artistMatch = pathname.match(
|
||||
/^\/library\/artist\/([^/]+)(?:\/(?:albums|songs|appearances))?$/
|
||||
);
|
||||
if (artistMatch) {
|
||||
const name = decodeURIComponent(artistMatch[1]);
|
||||
const groupingMode = new URLSearchParams(query).get('credit') === '1'
|
||||
? 'astra'
|
||||
: useSettingsStore.getState().artistGroupingMode;
|
||||
const result = await AstraLibraryData.getArtistDetail<DbTrack, Record<string, unknown>>(
|
||||
name,
|
||||
groupingMode,
|
||||
'all',
|
||||
null,
|
||||
1
|
||||
);
|
||||
return result.summary ? normalized : '/library';
|
||||
}
|
||||
const playlistMatch = pathname.match(/^\/library\/playlist\/(favorites|\d+)$/);
|
||||
if (
|
||||
playlistMatch &&
|
||||
playlistMatch[1] !== 'favorites' &&
|
||||
!usePlaylistStore.getState().playlists.some((playlist) => playlist.id === Number(playlistMatch[1]))
|
||||
) {
|
||||
return '/library';
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/** Restores once, then owns stable-route tracking and session autosave. */
|
||||
@@ -100,19 +130,24 @@ export function SessionLifecycle({ onReady }: SessionLifecycleProps) {
|
||||
|
||||
const liveNativeSession = await hasActiveNativePlaybackSession();
|
||||
if (!cancelled && snapshot?.playback && !liveNativeSession) {
|
||||
const resolved = resolvePlaybackSession(
|
||||
snapshot.playback,
|
||||
useLibraryStore.getState().tracks
|
||||
);
|
||||
restorePlaybackSession(
|
||||
resolved
|
||||
? { ...resolved, tracks: resolved.tracks.map(dbTrackToTrack) }
|
||||
: null
|
||||
);
|
||||
const nativeContext = await AstraLibraryData.restorePlaybackContext<DbTrack>();
|
||||
if (nativeContext) {
|
||||
restoreVirtualPlaybackContext(nativeContext, snapshot.playback);
|
||||
} else {
|
||||
const resolved = resolvePlaybackSession(
|
||||
snapshot.playback,
|
||||
useLibraryStore.getState().tracks
|
||||
);
|
||||
restorePlaybackSession(
|
||||
resolved
|
||||
? { ...resolved, tracks: resolved.tracks.map(dbTrackToTrack) }
|
||||
: null
|
||||
);
|
||||
}
|
||||
}
|
||||
if (cancelled) return;
|
||||
|
||||
const stableHref = validateSavedHref(snapshot?.lastStableHref ?? '/');
|
||||
const stableHref = await validateSavedHref(snapshot?.lastStableHref ?? '/');
|
||||
setInitialStableHref(stableHref);
|
||||
if (shouldRestoreSavedRoute(initialPathname.current, initialUrl) && stableHref !== '/') {
|
||||
router.replace(stableHref as never);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { AppState } from 'react-native';
|
||||
import { openLibraryDb } from '@/db/database';
|
||||
import { getSetting, setSetting } from '@/db/queries';
|
||||
import { AstraLibraryData } from '../../modules/astra-library-scanner';
|
||||
import { getPlaybackSessionSnapshot } from '@/audio/playbackController';
|
||||
import { usePlayerStore } from '@/stores/playerStore';
|
||||
import { useQueueStore } from '@/stores/queueStore';
|
||||
@@ -14,7 +13,6 @@ import {
|
||||
type PlaybackSessionSnapshotV1,
|
||||
} from './sessionState';
|
||||
|
||||
const MOBILE_SESSION_SETTING_KEY = 'mobile_session_state_v1';
|
||||
const STRUCTURAL_SAVE_DEBOUNCE_MS = 250;
|
||||
const POSITION_SAVE_THROTTLE_MS = 2000;
|
||||
|
||||
@@ -23,13 +21,11 @@ let scheduleStructuralSave: (() => void) | null = null;
|
||||
let writeChain: Promise<void> = Promise.resolve();
|
||||
|
||||
export async function readPersistedMobileSession(): Promise<MobileSessionSnapshotV1 | null> {
|
||||
const db = await openLibraryDb();
|
||||
return parseMobileSessionSnapshot(await getSetting(db, MOBILE_SESSION_SETTING_KEY));
|
||||
return parseMobileSessionSnapshot(await AstraLibraryData.readMobileSession());
|
||||
}
|
||||
|
||||
async function writePersistedMobileSession(snapshot: MobileSessionSnapshotV1): Promise<void> {
|
||||
const db = await openLibraryDb();
|
||||
await setSetting(db, MOBILE_SESSION_SETTING_KEY, stringifyMobileSessionSnapshot(snapshot));
|
||||
await AstraLibraryData.writeMobileSession(stringifyMobileSessionSnapshot(snapshot));
|
||||
}
|
||||
|
||||
function enqueueSnapshotWrite(snapshot: MobileSessionSnapshotV1): Promise<void> {
|
||||
@@ -153,7 +149,10 @@ export function installMobileSessionPersistence(
|
||||
}
|
||||
});
|
||||
const appStateSubscription = AppState.addEventListener('change', (state) => {
|
||||
if (state === 'inactive' || state === 'background') saveNow();
|
||||
if (state === 'inactive' || state === 'background') {
|
||||
saveNow();
|
||||
void AstraLibraryData.flushUserSnapshot().catch(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
// Persist route validation and queue normalization from hydration. The
|
||||
|
||||
Reference in New Issue
Block a user