mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
2c0aef9538
Three factories for the actions that carry real logic (not just
`set({ field: v })` pass-through):
- `createSkipStarActions` — skip-to-1★ counter with threshold check
and per-`<activeServerId><trackId>` storage key. Disabling the
feature wipes the counter map so a re-enable doesn't resume from
stale partial counts. Crossing the threshold deletes the key so
the next session starts fresh.
- `createMusicLibraryActions` — `setMusicFolders` falls back to
`'all'` when the persisted filter points at a folder the server
no longer reports; `setMusicLibraryFilter` bumps
`musicLibraryFilterVersion` so subscribed pages refetch.
- `createPerServerCapabilityActions` — `setEntityRatingSupport`,
`setAudiomuseNavidromeEnabled`, `setSubsonicServerIdentity`,
`setInstantMixProbe`, `setAudiomuseNavidromeIssue`. Each branch
knows which neighbouring per-server maps to wipe when the input
invalidates them (identity not AudioMuse-eligible / probe empty /
audiomuse disabled).
Pure code-move. authStore.ts: 384 → 270 LOC (−114). All trivial-setter
and logic-bearing action bodies are now out of the store body; only
the persist config + `onRehydrateStorage` migration block remains as
a non-factory chunk (target for E.47).
94 lines
3.8 KiB
TypeScript
94 lines
3.8 KiB
TypeScript
import { isNavidromeAudiomuseSoftwareEligible } from '../utils/subsonicServerIdentity';
|
|
import type { AuthState } from './authStoreTypes';
|
|
|
|
type SetState = (
|
|
partial: Partial<AuthState> | ((state: AuthState) => Partial<AuthState>),
|
|
) => void;
|
|
|
|
/**
|
|
* Per-server capability flags learned from pings/probes:
|
|
*
|
|
* - `setEntityRatingSupport` — does setRating apply to album/artist
|
|
* ids on this server? Stored as `unknown`/`yes`/`no`.
|
|
* - `setAudiomuseNavidromeEnabled` — user opted in/out of the
|
|
* AudioMuse-AI Instant Mix path for this Navidrome. Disable also
|
|
* clears the related issue flag.
|
|
* - `setSubsonicServerIdentity` — server's identity from ping. If the
|
|
* ping reveals the server isn't AudioMuse-eligible (wrong type or
|
|
* too old), wipe the related caps for that id so the UI doesn't
|
|
* keep a stale toggle.
|
|
* - `setInstantMixProbe` — probe result for getSimilarSongs. If
|
|
* `empty`, wipe the related AudioMuse caps so the row hides.
|
|
* - `setAudiomuseNavidromeIssue` — set/clear the "current session
|
|
* saw a failure" flag.
|
|
*/
|
|
export function createPerServerCapabilityActions(set: SetState): Pick<
|
|
AuthState,
|
|
| 'setEntityRatingSupport'
|
|
| 'setAudiomuseNavidromeEnabled'
|
|
| 'setSubsonicServerIdentity'
|
|
| 'setInstantMixProbe'
|
|
| 'setAudiomuseNavidromeIssue'
|
|
> {
|
|
return {
|
|
setEntityRatingSupport: (serverId, level) =>
|
|
set(s => ({
|
|
entityRatingSupportByServer: { ...s.entityRatingSupportByServer, [serverId]: level },
|
|
})),
|
|
|
|
setAudiomuseNavidromeEnabled: (serverId, enabled) =>
|
|
set(s => {
|
|
const audiomuseNavidromeByServer = enabled
|
|
? { ...s.audiomuseNavidromeByServer, [serverId]: true }
|
|
: (() => {
|
|
const { [serverId]: _removed, ...rest } = s.audiomuseNavidromeByServer;
|
|
return rest;
|
|
})();
|
|
const { [serverId]: _issueRm, ...issueRest } = s.audiomuseNavidromeIssueByServer;
|
|
return { audiomuseNavidromeByServer, audiomuseNavidromeIssueByServer: issueRest };
|
|
}),
|
|
|
|
setSubsonicServerIdentity: (serverId, identity) =>
|
|
set(s => {
|
|
const subsonicServerIdentityByServer = { ...s.subsonicServerIdentityByServer, [serverId]: { ...identity } };
|
|
if (!isNavidromeAudiomuseSoftwareEligible(identity)) {
|
|
const { [serverId]: _a, ...audiomuseRest } = s.audiomuseNavidromeByServer;
|
|
const { [serverId]: _i, ...issueRest } = s.audiomuseNavidromeIssueByServer;
|
|
const { [serverId]: _p, ...probeRest } = s.instantMixProbeByServer;
|
|
return {
|
|
subsonicServerIdentityByServer,
|
|
audiomuseNavidromeByServer: audiomuseRest,
|
|
audiomuseNavidromeIssueByServer: issueRest,
|
|
instantMixProbeByServer: probeRest,
|
|
};
|
|
}
|
|
return { subsonicServerIdentityByServer };
|
|
}),
|
|
|
|
setInstantMixProbe: (serverId, result) =>
|
|
set(s => {
|
|
const instantMixProbeByServer = { ...s.instantMixProbeByServer, [serverId]: result };
|
|
if (result === 'empty') {
|
|
const { [serverId]: _a, ...audiomuseRest } = s.audiomuseNavidromeByServer;
|
|
const { [serverId]: _i, ...issueRest } = s.audiomuseNavidromeIssueByServer;
|
|
return {
|
|
instantMixProbeByServer,
|
|
audiomuseNavidromeByServer: audiomuseRest,
|
|
audiomuseNavidromeIssueByServer: issueRest,
|
|
};
|
|
}
|
|
return { instantMixProbeByServer };
|
|
}),
|
|
|
|
setAudiomuseNavidromeIssue: (serverId, hasIssue) =>
|
|
set(s =>
|
|
hasIssue
|
|
? { audiomuseNavidromeIssueByServer: { ...s.audiomuseNavidromeIssueByServer, [serverId]: true } }
|
|
: (() => {
|
|
const { [serverId]: _rm, ...rest } = s.audiomuseNavidromeIssueByServer;
|
|
return { audiomuseNavidromeIssueByServer: rest };
|
|
})(),
|
|
),
|
|
};
|
|
}
|