67c701f85a
- Volume propagé en direct sur l'élément audio (le slider ne faisait que persister) ; volume 0 respecté (?? 1) ; slider masqué sur iOS (can_set_volume) ; mute synchronisé store/élément, persistant entre pistes - Race condition loadedmetadata, USER_PROGRESS borné à 200 entrées, erreurs radio affichées avec bouton Réessayer - Mobile : barre de progression avec zone tactile 44 px (tap + scrub au doigt), boutons 48 px, contrôles wrap, listes paginées 50×50 + content-visibility - PWA : manifest + icônes 192/512 (any+maskable), service worker manuel (shell + pochettes, streams exclus), Media Session API (metadata + 4 handlers) - SEO : Seo.svelte (canonical, OG, Twitter), footer fédéré OKI + flag-bar - Mesuré puppeteer 390x844 : seek tactile OK, scrollWidth OK, SW actif
72 lines
1.6 KiB
TypeScript
72 lines
1.6 KiB
TypeScript
import clamp from 'just-clamp';
|
|
import { persisted } from 'svelte-local-storage-store';
|
|
|
|
export interface UserPreferences {
|
|
playback_rate: number;
|
|
volume: number;
|
|
muted: boolean;
|
|
}
|
|
|
|
/**
|
|
* The default user preferences
|
|
*/
|
|
const DEFAULT_USER_PREFERENCES: UserPreferences = {
|
|
playback_rate: 1,
|
|
volume: 1,
|
|
muted: false,
|
|
};
|
|
|
|
/**
|
|
* The user preferences store
|
|
*/
|
|
export const USER_PREFERENCES_STORE = persisted<UserPreferences>(
|
|
'USER_PREFERENCE',
|
|
DEFAULT_USER_PREFERENCES,
|
|
);
|
|
|
|
/**
|
|
* Sets the playback rate for the user
|
|
* @param value - The playback rate value
|
|
*/
|
|
const set_playback_rate = (value: number) => {
|
|
const playback_rate = clamp(value, 0.5, 5);
|
|
return USER_PREFERENCES_STORE.update((prefs) => ({
|
|
...prefs,
|
|
playback_rate,
|
|
}));
|
|
};
|
|
|
|
/**
|
|
* Sets the volume for the user
|
|
* @param value - The volume value
|
|
*/
|
|
const set_volume = (value: number) => {
|
|
const volume = clamp(value, 0, 1);
|
|
return USER_PREFERENCES_STORE.update((prefs) => ({ ...prefs, volume }));
|
|
};
|
|
|
|
/**
|
|
* Sets the muted state for the user. This is the single source of truth
|
|
* for mute: the audio element is kept in sync with it.
|
|
* @param value - The muted value
|
|
*/
|
|
const set_muted = (value: boolean) => {
|
|
return USER_PREFERENCES_STORE.update((prefs) => ({ ...prefs, muted: value }));
|
|
};
|
|
|
|
/**
|
|
* Clears user preferences store
|
|
*/
|
|
const clear_store = () => USER_PREFERENCES_STORE.set(DEFAULT_USER_PREFERENCES);
|
|
|
|
/**
|
|
* The user preferences object
|
|
*/
|
|
export const user_preferences = {
|
|
subscribe: USER_PREFERENCES_STORE.subscribe,
|
|
set_playback_rate,
|
|
set_volume,
|
|
set_muted,
|
|
clear: clear_store,
|
|
};
|