mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
refactor(player): E.31 — extract Last.fm love actions as factory (#595)
Four Last.fm-related actions move out of the playerStore create() body into `src/store/lastfmActions.ts` as a `createLastfmActions(set, get)` factory: - `toggleLastfmLove` — flip love + write through to the cache map - `setLastfmLoved` — force-set (used by external events) - `setLastfmLovedForSong` — write cache for an arbitrary title/artist - `syncLastfmLovedTracks` — startup bulk fetch + merge The store body uses `...createLastfmActions(set, get)` to inline them back into the actions map. First action-factory cut — sets the pattern for follow-up Phase E extractions of larger action groups (playback transport, queue mutators, etc.). Side-cleanup: 3 last.fm imports drop from playerStore (`lastfmLoveTrack`, `lastfmUnloveTrack`, `lastfmGetAllLovedTracks`) since they only fed the moved actions. playerStore 1550 → 1504 LOC.
This commit is contained in:
committed by
GitHub
parent
8dfe05fe7d
commit
2c5659b425
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
lastfmGetAllLovedTracks,
|
||||
lastfmLoveTrack,
|
||||
lastfmUnloveTrack,
|
||||
} from '../api/lastfm';
|
||||
import { useAuthStore } from './authStore';
|
||||
import type { PlayerState } from './playerStoreTypes';
|
||||
|
||||
type SetState = (
|
||||
partial: Partial<PlayerState> | ((state: PlayerState) => Partial<PlayerState>),
|
||||
) => void;
|
||||
type GetState = () => PlayerState;
|
||||
|
||||
/**
|
||||
* Four Last.fm love-related actions, factored out of the playerStore
|
||||
* `create()` body so the action set can be tested + reasoned about
|
||||
* separately:
|
||||
*
|
||||
* - `toggleLastfmLove` — flip the current track's love state on the
|
||||
* server, write through to the local cache map keyed by
|
||||
* `${title}::${artist}` so other queue rows showing the same song
|
||||
* update too.
|
||||
* - `setLastfmLoved` — force-set the boolean (used by the
|
||||
* `track:lastfm-loved` SSE-style event). Updates the cache when a
|
||||
* current track exists.
|
||||
* - `setLastfmLovedForSong` — write the cache for an arbitrary
|
||||
* title/artist pair (used by the QueuePanel love button on
|
||||
* not-yet-current tracks).
|
||||
* - `syncLastfmLovedTracks` — startup-time bulk fetch of the user's
|
||||
* loved-tracks list, merged into the local cache (local likes win
|
||||
* on conflict) plus a recompute of the current track's flag.
|
||||
*/
|
||||
export function createLastfmActions(set: SetState, get: GetState): Pick<
|
||||
PlayerState,
|
||||
'toggleLastfmLove' | 'setLastfmLoved' | 'setLastfmLovedForSong' | 'syncLastfmLovedTracks'
|
||||
> {
|
||||
return {
|
||||
toggleLastfmLove: () => {
|
||||
const { currentTrack, lastfmLoved } = get();
|
||||
const { lastfmSessionKey } = useAuthStore.getState();
|
||||
if (!currentTrack || !lastfmSessionKey) return;
|
||||
const newLoved = !lastfmLoved;
|
||||
const cacheKey = `${currentTrack.title}::${currentTrack.artist}`;
|
||||
set(s => ({ lastfmLoved: newLoved, lastfmLovedCache: { ...s.lastfmLovedCache, [cacheKey]: newLoved } }));
|
||||
if (newLoved) {
|
||||
lastfmLoveTrack(currentTrack, lastfmSessionKey);
|
||||
} else {
|
||||
lastfmUnloveTrack(currentTrack, lastfmSessionKey);
|
||||
}
|
||||
},
|
||||
|
||||
setLastfmLoved: (v) => {
|
||||
const { currentTrack } = get();
|
||||
if (currentTrack) {
|
||||
const cacheKey = `${currentTrack.title}::${currentTrack.artist}`;
|
||||
set(s => ({ lastfmLoved: v, lastfmLovedCache: { ...s.lastfmLovedCache, [cacheKey]: v } }));
|
||||
} else {
|
||||
set({ lastfmLoved: v });
|
||||
}
|
||||
},
|
||||
|
||||
syncLastfmLovedTracks: async () => {
|
||||
const { lastfmSessionKey, lastfmUsername } = useAuthStore.getState();
|
||||
if (!lastfmSessionKey || !lastfmUsername) return;
|
||||
const tracks = await lastfmGetAllLovedTracks(lastfmUsername, lastfmSessionKey);
|
||||
const newCache: Record<string, boolean> = {};
|
||||
for (const t of tracks) newCache[`${t.title}::${t.artist}`] = true;
|
||||
// Merge with existing cache (local likes take precedence)
|
||||
set(s => ({ lastfmLovedCache: { ...newCache, ...s.lastfmLovedCache } }));
|
||||
// Update current track's loved state if it's in the new cache
|
||||
const { currentTrack } = get();
|
||||
if (currentTrack) {
|
||||
const loved = newCache[`${currentTrack.title}::${currentTrack.artist}`] ?? false;
|
||||
set({ lastfmLoved: loved });
|
||||
}
|
||||
},
|
||||
|
||||
setLastfmLovedForSong: (title, artist, v) => {
|
||||
const cacheKey = `${title}::${artist}`;
|
||||
const isCurrentTrack = get().currentTrack?.title === title && get().currentTrack?.artist === artist;
|
||||
set(s => ({
|
||||
lastfmLovedCache: { ...s.lastfmLovedCache, [cacheKey]: v },
|
||||
...(isCurrentTrack ? { lastfmLoved: v } : {}),
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import i18n from '../i18n';
|
||||
import { buildStreamUrl, getPlayQueue, savePlayQueue, reportNowPlaying, getSong, getSimilarSongs2, getTopSongs, setRating } from '../api/subsonic';
|
||||
import { resolvePlaybackUrl } from '../utils/resolvePlaybackUrl';
|
||||
import { setDeferHotCachePrefetch } from '../utils/hotCacheGate';
|
||||
import { lastfmUpdateNowPlaying, lastfmLoveTrack, lastfmUnloveTrack, lastfmGetTrackLoved, lastfmGetAllLovedTracks } from '../api/lastfm';
|
||||
import { lastfmUpdateNowPlaying, lastfmGetTrackLoved } from '../api/lastfm';
|
||||
import { useAuthStore } from './authStore';
|
||||
import { useOfflineStore } from './offlineStore';
|
||||
import { useHotCacheStore } from './hotCacheStore';
|
||||
@@ -174,6 +174,7 @@ export {
|
||||
import type { PlayerState, Track } from './playerStoreTypes';
|
||||
export type { PlayerState, Track };
|
||||
import { applyQueueHistorySnapshot } from './applyQueueHistorySnapshot';
|
||||
import { createLastfmActions } from './lastfmActions';
|
||||
|
||||
|
||||
// ─── Module-level playback primitives ─────────────────────────────────────────
|
||||
@@ -257,54 +258,7 @@ export const usePlayerStore = create<PlayerState>()(
|
||||
},
|
||||
toggleFullscreen: () => set(state => ({ isFullscreenOpen: !state.isFullscreenOpen })),
|
||||
|
||||
toggleLastfmLove: () => {
|
||||
const { currentTrack, lastfmLoved } = get();
|
||||
const { lastfmSessionKey } = useAuthStore.getState();
|
||||
if (!currentTrack || !lastfmSessionKey) return;
|
||||
const newLoved = !lastfmLoved;
|
||||
const cacheKey = `${currentTrack.title}::${currentTrack.artist}`;
|
||||
set(s => ({ lastfmLoved: newLoved, lastfmLovedCache: { ...s.lastfmLovedCache, [cacheKey]: newLoved } }));
|
||||
if (newLoved) {
|
||||
lastfmLoveTrack(currentTrack, lastfmSessionKey);
|
||||
} else {
|
||||
lastfmUnloveTrack(currentTrack, lastfmSessionKey);
|
||||
}
|
||||
},
|
||||
|
||||
setLastfmLoved: (v) => {
|
||||
const { currentTrack } = get();
|
||||
if (currentTrack) {
|
||||
const cacheKey = `${currentTrack.title}::${currentTrack.artist}`;
|
||||
set(s => ({ lastfmLoved: v, lastfmLovedCache: { ...s.lastfmLovedCache, [cacheKey]: v } }));
|
||||
} else {
|
||||
set({ lastfmLoved: v });
|
||||
}
|
||||
},
|
||||
|
||||
syncLastfmLovedTracks: async () => {
|
||||
const { lastfmSessionKey, lastfmUsername } = useAuthStore.getState();
|
||||
if (!lastfmSessionKey || !lastfmUsername) return;
|
||||
const tracks = await lastfmGetAllLovedTracks(lastfmUsername, lastfmSessionKey);
|
||||
const newCache: Record<string, boolean> = {};
|
||||
for (const t of tracks) newCache[`${t.title}::${t.artist}`] = true;
|
||||
// Merge with existing cache (local likes take precedence)
|
||||
set(s => ({ lastfmLovedCache: { ...newCache, ...s.lastfmLovedCache } }));
|
||||
// Update current track's loved state if it's in the new cache
|
||||
const { currentTrack } = get();
|
||||
if (currentTrack) {
|
||||
const loved = newCache[`${currentTrack.title}::${currentTrack.artist}`] ?? false;
|
||||
set({ lastfmLoved: loved });
|
||||
}
|
||||
},
|
||||
|
||||
setLastfmLovedForSong: (title, artist, v) => {
|
||||
const cacheKey = `${title}::${artist}`;
|
||||
const isCurrentTrack = get().currentTrack?.title === title && get().currentTrack?.artist === artist;
|
||||
set(s => ({
|
||||
lastfmLovedCache: { ...s.lastfmLovedCache, [cacheKey]: v },
|
||||
...(isCurrentTrack ? { lastfmLoved: v } : {}),
|
||||
}));
|
||||
},
|
||||
...createLastfmActions(set, get),
|
||||
|
||||
toggleRepeat: () => set(state => {
|
||||
const modes = ['off', 'all', 'one'] as const;
|
||||
|
||||
Reference in New Issue
Block a user