mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
78a177b1bc
* fix(orbit): harden the guest outbox — recover lost suggestions, avoid a duplicate outbox Two outbox robustness issues on the poll-based, non-atomic playlist transport: - Lost-update: a track a guest appends to its outbox can be wiped by a concurrent host sweep-clear before the host recorded it, leaving the suggestion lost AND stuck on "waiting on host" forever (it only clears once it reaches the host's play queue). The playlist read-modify-write can't be made atomic, so recover: re-send a pending suggestion the host hasn't recorded (absent from state.queue) past a grace window — the host dedupes by (user, trackId), so it's idempotent — and give up + toast past a 45s window so the row stops hanging. New pendingResend planner module with unit tests; suggest + tick share ensureTrackInOutbox. - Duplicate outbox: a transient getPlaylists failure at join was swallowed to [], so the existing-outbox check missed and a second outbox was created. Distinguish "lookup failed" from "genuinely absent" and retry once before falling back to create. New i18n key for the give-up toast across all locales. * fix(orbit): reject non-http(s) schemes in share invites normalizeShareServerUrl only prepends http:// when the server string doesn't already start with "http", so an invite carrying a scheme like httpx:// or https-phish:// slips through unchanged. Reject anything whose parsed protocol isn't http(s) at parse time, before the known-server match. Add round-trip tests covering the accepted and rejected schemes. * fix(orbit): read exit-modal role fresh to avoid a stale keydown closure The exit modal's Enter/Escape handler binds once per open (deps [isOpen]) and closed over the role prop, so a role change while the modal was up would act on a stale value. Read role from the store inside the action instead, and drop the now-unused reactive subscription. * docs(changelog): consolidate Orbit fixes into one block, add 1159
92 lines
2.6 KiB
TypeScript
92 lines
2.6 KiB
TypeScript
/**
|
|
* Orbit — multi-user listen-together feature.
|
|
*
|
|
* This file is a re-export shim. Implementation lives under `utils/orbit/`:
|
|
*
|
|
* - `constants.ts` — cadence + TTL ms values.
|
|
* - `helpers.ts` — pure utilities (id gen, serialisation, key fns).
|
|
* - `stateMath.ts` — pure state transforms (shuffle, drift, sweep merge).
|
|
* - `remote.ts` — Navidrome playlist comment I/O.
|
|
* - `shareLink.ts` — invite magic-string encode/decode.
|
|
* - `host.ts` — host session lifecycle (start, end, settings, enqueue).
|
|
* - `moderation.ts` — host kicks / soft-removes / mutes.
|
|
* - `guest.ts` — guest join/leave + suggestion pipeline + host
|
|
* approve/decline reactions.
|
|
* - `sweep.ts` — host-side outbox sweep loop.
|
|
* - `cleanup.ts` — app-start orphan playlist sweep.
|
|
*
|
|
* Importers everywhere else in the codebase keep using `'../utils/orbit'`;
|
|
* this shim re-exports every public symbol unchanged.
|
|
*/
|
|
|
|
export {
|
|
ORBIT_HEARTBEAT_ALIVE_MS,
|
|
ORBIT_ORPHAN_TTL_MS,
|
|
ORBIT_REMOVED_TTL_MS,
|
|
ORBIT_SHUFFLE_INTERVAL_MS,
|
|
} from './orbit/constants';
|
|
export {
|
|
generateSessionId,
|
|
makeCoalescedRunner,
|
|
OrbitStateTooLarge,
|
|
serialiseOrbitState,
|
|
suggestionKey,
|
|
} from './orbit/helpers';
|
|
export { isOrbitPlaybackSyncActive } from './orbit/sessionActive';
|
|
export {
|
|
applyOutboxSnapshotsToState,
|
|
computeOrbitDriftMs,
|
|
effectiveShuffleIntervalMs,
|
|
maybeShuffleQueue,
|
|
patchOrbitState,
|
|
} from './orbit/stateMath';
|
|
export {
|
|
findSessionPlaylistId,
|
|
readOrbitState,
|
|
writeOrbitHeartbeat,
|
|
writeOrbitState,
|
|
} from './orbit/remote';
|
|
export {
|
|
readOrbitTransitionSettings,
|
|
applyOrbitTransitionSettings,
|
|
saveGuestTransitionsOnce,
|
|
restoreGuestTransitions,
|
|
} from './orbit/transitions';
|
|
export {
|
|
buildOrbitShareLink,
|
|
parseOrbitShareLink,
|
|
type OrbitShareLink,
|
|
} from './orbit/shareLink';
|
|
export {
|
|
endOrbitSession,
|
|
hostEnqueueToOrbit,
|
|
startOrbitSession,
|
|
triggerOrbitShuffleNow,
|
|
updateOrbitSettings,
|
|
type StartOrbitArgs,
|
|
} from './orbit/host';
|
|
export {
|
|
kickOrbitParticipant,
|
|
removeOrbitParticipant,
|
|
setOrbitSuggestionBlocked,
|
|
} from './orbit/moderation';
|
|
export {
|
|
approveOrbitSuggestion,
|
|
declineOrbitSuggestion,
|
|
ensureTrackInOutbox,
|
|
evaluateOrbitSuggestGate,
|
|
joinOrbitSession,
|
|
leaveOrbitSession,
|
|
OrbitJoinError,
|
|
OrbitSuggestBlockedError,
|
|
suggestOrbitTrack,
|
|
type OrbitSuggestGateReason,
|
|
} from './orbit/guest';
|
|
export {
|
|
forgetPendingSuggestion,
|
|
planPendingResends,
|
|
resetPendingResendState,
|
|
} from './orbit/pendingResend';
|
|
export { sweepGuestOutboxes } from './orbit/sweep';
|
|
export { cleanupOrphanedOrbitPlaylists } from './orbit/cleanup';
|