mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
7a7a9f5e6b
111 of 122 top-level src/utils/ files move into 16 topic folders (audio, cache, cover, share, server, playback, playlist, deviceSync, waveform, mix, format, export, changelog, ui, perf, componentHelpers). True singletons with no cluster stay at the utils/ root. Pure file-move: a path-aware codemod rewrote 539 relative-import specifiers across 275 files; no logic touched. The hot-path coverage gate list (.github/frontend-hot-path-files.txt) is updated to the new paths for the 11 gated utils files — a mechanical consequence of the move, not a CI change. tsc is green.
128 lines
5.4 KiB
TypeScript
128 lines
5.4 KiB
TypeScript
import { useCallback } from 'react';
|
|
import { setRating } from '../api/subsonicStarRating';
|
|
import type { SubsonicAlbum, SubsonicArtist } from '../api/subsonicTypes';
|
|
import type { Track } from '../store/playerStoreTypes';
|
|
import { useAuthStore } from '../store/authStore';
|
|
import { showToast } from '../utils/ui/toast';
|
|
|
|
type RatingKind = 'song' | 'album' | 'artist';
|
|
|
|
interface Args {
|
|
type: string | null;
|
|
item: unknown;
|
|
userRatingOverrides: Record<string, number>;
|
|
setUserRatingOverride: (id: string, rating: number) => void;
|
|
entityRatingSupport: 'full' | 'track_only' | 'unknown';
|
|
t: (key: string) => string;
|
|
}
|
|
|
|
interface Result {
|
|
applySongRating: (songId: string, rating: number) => void;
|
|
applyAlbumRating: (album: SubsonicAlbum, rating: number) => void;
|
|
applyArtistRating: (artist: SubsonicArtist, rating: number) => void;
|
|
getRatingValueByKind: (kind: RatingKind, id: string) => number;
|
|
commitRatingByKind: (kind: RatingKind, id: string, rating: number) => void;
|
|
}
|
|
|
|
export function useContextMenuRating({
|
|
type, item, userRatingOverrides, setUserRatingOverride, entityRatingSupport, t,
|
|
}: Args): Result {
|
|
const setEntityRatingSupport = useAuthStore(s => s.setEntityRatingSupport);
|
|
const activeServerId = useAuthStore(s => s.activeServerId);
|
|
|
|
const applySongRating = useCallback((songId: string, rating: number) => {
|
|
setUserRatingOverride(songId, rating);
|
|
setRating(songId, rating).catch(() => {});
|
|
}, [setUserRatingOverride]);
|
|
|
|
const applyAlbumRating = useCallback((album: SubsonicAlbum, rating: number) => {
|
|
setUserRatingOverride(album.id, rating);
|
|
if (entityRatingSupport !== 'full') return;
|
|
setRating(album.id, rating).catch(err => {
|
|
if (activeServerId) setEntityRatingSupport(activeServerId, 'track_only');
|
|
showToast(
|
|
typeof err === 'string' ? err : err instanceof Error ? err.message : t('entityRating.saveFailed'),
|
|
4500,
|
|
'error',
|
|
);
|
|
});
|
|
}, [setUserRatingOverride, entityRatingSupport, activeServerId, setEntityRatingSupport, t]);
|
|
|
|
const applyArtistRating = useCallback((artist: SubsonicArtist, rating: number) => {
|
|
setUserRatingOverride(artist.id, rating);
|
|
if (entityRatingSupport !== 'full') return;
|
|
setRating(artist.id, rating).catch(err => {
|
|
if (activeServerId) setEntityRatingSupport(activeServerId, 'track_only');
|
|
showToast(
|
|
typeof err === 'string' ? err : err instanceof Error ? err.message : t('entityRating.saveFailed'),
|
|
4500,
|
|
'error',
|
|
);
|
|
});
|
|
}, [setUserRatingOverride, entityRatingSupport, activeServerId, setEntityRatingSupport, t]);
|
|
|
|
const getRatingValueByKind = useCallback((kind: RatingKind, id: string): number => {
|
|
if (kind === 'song' && (type === 'song' || type === 'album-song' || type === 'queue-item')) {
|
|
const song = item as Track;
|
|
if (song.id === id) return userRatingOverrides[id] ?? song.userRating ?? 0;
|
|
}
|
|
if (kind === 'album' && type === 'album') {
|
|
const album = item as SubsonicAlbum;
|
|
if (album.id === id) return userRatingOverrides[id] ?? album.userRating ?? 0;
|
|
}
|
|
if (kind === 'album' && type === 'multi-album') {
|
|
const albums = item as SubsonicAlbum[];
|
|
const compositeId = [...albums.map(a => a.id)].sort().join('\x1e');
|
|
if (id !== compositeId) return userRatingOverrides[id] ?? 0;
|
|
if (albums.length === 0) return 0;
|
|
const vals = albums.map(a => userRatingOverrides[a.id] ?? a.userRating ?? 0);
|
|
const first = vals[0];
|
|
return vals.every(v => v === first) ? first : 0;
|
|
}
|
|
if (kind === 'artist' && type === 'artist') {
|
|
const artist = item as SubsonicArtist;
|
|
if (artist.id === id) return userRatingOverrides[id] ?? artist.userRating ?? 0;
|
|
}
|
|
if (kind === 'artist' && type === 'multi-artist') {
|
|
const artists = item as SubsonicArtist[];
|
|
const compositeId = [...artists.map(a => a.id)].sort().join('\x1e');
|
|
if (id !== compositeId) return userRatingOverrides[id] ?? 0;
|
|
if (artists.length === 0) return 0;
|
|
const vals = artists.map(a => userRatingOverrides[a.id] ?? a.userRating ?? 0);
|
|
const first = vals[0];
|
|
return vals.every(v => v === first) ? first : 0;
|
|
}
|
|
return userRatingOverrides[id] ?? 0;
|
|
}, [type, item, userRatingOverrides]);
|
|
|
|
const commitRatingByKind = useCallback((kind: RatingKind, id: string, rating: number) => {
|
|
if (kind === 'song') {
|
|
applySongRating(id, rating);
|
|
return;
|
|
}
|
|
if (kind === 'album' && type === 'album') {
|
|
applyAlbumRating(item as SubsonicAlbum, rating);
|
|
return;
|
|
}
|
|
if (kind === 'album' && type === 'multi-album') {
|
|
const albums = item as SubsonicAlbum[];
|
|
const compositeId = [...albums.map(a => a.id)].sort().join('\x1e');
|
|
if (id !== compositeId) return;
|
|
for (const a of albums) applyAlbumRating(a, rating);
|
|
return;
|
|
}
|
|
if (kind === 'artist' && type === 'artist') {
|
|
applyArtistRating(item as SubsonicArtist, rating);
|
|
return;
|
|
}
|
|
if (kind === 'artist' && type === 'multi-artist') {
|
|
const artists = item as SubsonicArtist[];
|
|
const compositeId = [...artists.map(a => a.id)].sort().join('\x1e');
|
|
if (id !== compositeId) return;
|
|
for (const a of artists) applyArtistRating(a, rating);
|
|
}
|
|
}, [applySongRating, applyAlbumRating, applyArtistRating, type, item]);
|
|
|
|
return { applySongRating, applyAlbumRating, applyArtistRating, getRatingValueByKind, commitRatingByKind };
|
|
}
|