mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
exp(orbit): host merge, guest queue view, settings popover, i18n
Guest suggestions now auto-merge into the host's play queue at random positions inside the upcoming range, so they actually surface alongside host-picked tracks instead of piling up at the end. Per-item dedupe via addedBy:addedAt:trackId keys survives reshuffles. Guests get a read-only mirror of the session in place of their queue panel — live-card for the host's current track, suggestion list with submitter attribution, and a footer reminding them the host owns playback. Track metadata is resolved lazily and cached locally. New host-only settings popover (gear in the bar) with auto-approve and auto-shuffle toggles plus a "Shuffle now" action. Settings live in the OrbitState blob and push immediately to Navidrome; missing fields on older blobs default to "both on". 15-min shuffle now touches the real playerStore queue via a new shuffleUpcomingQueue action — previous behaviour only reshuffled the guest-facing suggestion history. A manual shuffle is available from the settings popover so the interval can be verified without waiting. Start-modal reworked into a single step: share-link is visible and copy-able from the start, auto-copied on Start if the host hasn't already hit Copy. Link carries a live name-derived slug (parser strips it, SID is authoritative). LAN/remote-server hint above the facts. Random session-name suggester pulls from three pattern pools for ~10k unique combinations. All user-visible Orbit strings moved to a dedicated orbit i18n namespace (en + de); other locales fall back to en via i18next. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '../store/orbitStore';
|
||||
import { leaveOrbitSession } from '../utils/orbit';
|
||||
|
||||
@@ -12,6 +13,7 @@ import { leaveOrbitSession } from '../utils/orbit';
|
||||
* "OK" cleans up the guest-side outbox + resets the local store.
|
||||
*/
|
||||
export default function OrbitExitModal() {
|
||||
const { t } = useTranslation();
|
||||
const phase = useOrbitStore(s => s.phase);
|
||||
const errorMessage = useOrbitStore(s => s.errorMessage);
|
||||
const role = useOrbitStore(s => s.role);
|
||||
@@ -22,12 +24,10 @@ export default function OrbitExitModal() {
|
||||
const isKicked = phase === 'error' && errorMessage === 'kicked';
|
||||
if (!isEnded && !isKicked) return null;
|
||||
|
||||
const title = isKicked
|
||||
? 'You were removed from the session'
|
||||
: 'The host ended the session';
|
||||
const body = isKicked
|
||||
? `@${hostName ?? 'host'} removed you from "${sessionName ?? 'the session'}".`
|
||||
: `"${sessionName ?? 'The session'}" has ended. Hope you had fun.`;
|
||||
const title = isKicked ? t('orbit.exitKickedTitle') : t('orbit.exitEndedTitle');
|
||||
const body = isKicked
|
||||
? t('orbit.exitKickedBody', { host: hostName ?? '', name: sessionName ?? '' })
|
||||
: t('orbit.exitEndedBody', { name: sessionName ?? '' });
|
||||
|
||||
const onOk = async () => {
|
||||
try {
|
||||
@@ -50,7 +50,7 @@ export default function OrbitExitModal() {
|
||||
<h3 id="orbit-exit-title" className="orbit-exit-modal__title">{title}</h3>
|
||||
<p className="orbit-exit-modal__body">{body}</p>
|
||||
<div className="orbit-exit-modal__actions">
|
||||
<button type="button" className="btn btn-primary" onClick={onOk}>OK</button>
|
||||
<button type="button" className="btn btn-primary" onClick={onOk}>{t('orbit.exitOk')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
|
||||
Reference in New Issue
Block a user