feat: add lock while fading

This commit is contained in:
MAZE 2024-04-30 18:13:00 +03:30
parent c893e2a6ad
commit d9246b692b
8 changed files with 48 additions and 9 deletions

View file

@ -23,6 +23,8 @@ export function App() {
const favorites = useSoundStore(useShallow(state => state.getFavorites())); const favorites = useSoundStore(useShallow(state => state.getFavorites()));
const pause = useSoundStore(state => state.pause); const pause = useSoundStore(state => state.pause);
const lock = useSoundStore(state => state.lock);
const unlock = useSoundStore(state => state.unlock);
const favoriteSounds = useMemo(() => { const favoriteSounds = useMemo(() => {
const favoriteSounds = categories const favoriteSounds = categories
@ -56,13 +58,16 @@ export function App() {
useEffect(() => { useEffect(() => {
const unsubscribe = subscribe('fadeOut', (e: { duration: number }) => { const unsubscribe = subscribe('fadeOut', (e: { duration: number }) => {
lock();
setTimeout(() => { setTimeout(() => {
pause(); pause();
unlock();
}, e.duration); }, e.duration);
}); });
return unsubscribe; return unsubscribe;
}, [pause]); }, [pause, lock, unlock]);
const allCategories = useMemo(() => { const allCategories = useMemo(() => {
const favorites = []; const favorites = [];

View file

@ -12,14 +12,17 @@ export function PlayButton() {
const pause = useSoundStore(state => state.pause); const pause = useSoundStore(state => state.pause);
const toggle = useSoundStore(state => state.togglePlay); const toggle = useSoundStore(state => state.togglePlay);
const noSelected = useSoundStore(state => state.noSelected()); const noSelected = useSoundStore(state => state.noSelected());
const locked = useSoundStore(state => state.locked);
const showSnackbar = useSnackbar(); const showSnackbar = useSnackbar();
const handleToggle = useCallback(() => { const handleToggle = useCallback(() => {
if (locked) return;
if (noSelected) return showSnackbar('Please first select a sound to play.'); if (noSelected) return showSnackbar('Please first select a sound to play.');
toggle(); toggle();
}, [showSnackbar, toggle, noSelected]); }, [showSnackbar, toggle, noSelected, locked]);
useEffect(() => { useEffect(() => {
if (isPlaying && noSelected) pause(); if (isPlaying && noSelected) pause();

View file

@ -15,6 +15,7 @@ export function UnselectButton() {
const restoreHistory = useSoundStore(state => state.restoreHistory); const restoreHistory = useSoundStore(state => state.restoreHistory);
const hasHistory = useSoundStore(state => !!state.history); const hasHistory = useSoundStore(state => !!state.history);
const unselectAll = useSoundStore(state => state.unselectAll); const unselectAll = useSoundStore(state => state.unselectAll);
const locked = useSoundStore(state => state.locked);
const variants = { const variants = {
...mix(fade(), slideX(15)), ...mix(fade(), slideX(15)),
@ -22,9 +23,10 @@ export function UnselectButton() {
}; };
const handleToggle = useCallback(() => { const handleToggle = useCallback(() => {
if (locked) return;
if (hasHistory) restoreHistory(); if (hasHistory) restoreHistory();
else if (!noSelected) unselectAll(true); else if (!noSelected) unselectAll(true);
}, [hasHistory, noSelected, unselectAll, restoreHistory]); }, [hasHistory, noSelected, unselectAll, restoreHistory, locked]);
useEffect(() => { useEffect(() => {
const listener = (e: KeyboardEvent) => { const listener = (e: KeyboardEvent) => {

View file

@ -6,6 +6,14 @@ import { Item } from '../item';
export function Shuffle() { export function Shuffle() {
const shuffle = useSoundStore(state => state.shuffle); const shuffle = useSoundStore(state => state.shuffle);
const locked = useSoundStore(state => state.locked);
return <Item icon={<BiShuffle />} label="Shuffle Sounds" onClick={shuffle} />; return (
<Item
disabled={locked}
icon={<BiShuffle />}
label="Shuffle Sounds"
onClick={shuffle}
/>
);
} }

View file

@ -11,6 +11,7 @@ export function Range({ id, label }: RangeProps) {
const setVolume = useSoundStore(state => state.setVolume); const setVolume = useSoundStore(state => state.setVolume);
const volume = useSoundStore(state => state.sounds[id].volume); const volume = useSoundStore(state => state.sounds[id].volume);
const isSelected = useSoundStore(state => state.sounds[id].isSelected); const isSelected = useSoundStore(state => state.sounds[id].isSelected);
const locked = useSoundStore(state => state.locked);
return ( return (
<input <input
@ -22,8 +23,10 @@ export function Range({ id, label }: RangeProps) {
min={0} min={0}
type="range" type="range"
value={volume * 100} value={volume * 100}
onChange={e => isSelected && setVolume(id, Number(e.target.value) / 100)}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
onChange={e =>
!locked && isSelected && setVolume(id, Number(e.target.value) / 100)
}
/> />
); );
} }

View file

@ -32,18 +32,21 @@ export const Sound = forwardRef<HTMLDivElement, SoundProps>(function Sound(
const setVolume = useSoundStore(state => state.setVolume); const setVolume = useSoundStore(state => state.setVolume);
const volume = useSoundStore(state => state.sounds[id].volume); const volume = useSoundStore(state => state.sounds[id].volume);
const isSelected = useSoundStore(state => state.sounds[id].isSelected); const isSelected = useSoundStore(state => state.sounds[id].isSelected);
const locked = useSoundStore(state => state.locked);
const isLoading = useLoadingStore(state => state.loaders[src]); const isLoading = useLoadingStore(state => state.loaders[src]);
const sound = useSound(src, { loop: true, volume }); const sound = useSound(src, { loop: true, volume });
useEffect(() => { useEffect(() => {
if (locked) return;
if (isSelected && isPlaying && functional) { if (isSelected && isPlaying && functional) {
sound?.play(); sound?.play();
} else { } else {
sound?.pause(); sound?.pause();
} }
}, [isSelected, sound, isPlaying, functional]); }, [isSelected, sound, isPlaying, functional, locked]);
useEffect(() => { useEffect(() => {
if (hidden && isSelected) selectHidden(label); if (hidden && isSelected) selectHidden(label);
@ -51,19 +54,22 @@ export const Sound = forwardRef<HTMLDivElement, SoundProps>(function Sound(
}, [label, isSelected, hidden, selectHidden, unselectHidden]); }, [label, isSelected, hidden, selectHidden, unselectHidden]);
const _select = useCallback(() => { const _select = useCallback(() => {
if (locked) return;
select(id); select(id);
play(); play();
}, [select, play, id]); }, [select, play, id, locked]);
const _unselect = useCallback(() => { const _unselect = useCallback(() => {
if (locked) return;
unselect(id); unselect(id);
setVolume(id, 0.5); setVolume(id, 0.5);
}, [unselect, setVolume, id]); }, [unselect, setVolume, id, locked]);
const toggle = useCallback(() => { const toggle = useCallback(() => {
if (locked) return;
if (isSelected) _unselect(); if (isSelected) _unselect();
else _select(); else _select();
}, [isSelected, _select, _unselect]); }, [isSelected, _select, _unselect, locked]);
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
toggle(); toggle();

View file

@ -5,6 +5,7 @@ import type { SoundState } from './sound.state';
import { pickMany, random } from '@/helpers/random'; import { pickMany, random } from '@/helpers/random';
export interface SoundActions { export interface SoundActions {
lock: () => void;
override: (sounds: Record<string, number>) => void; override: (sounds: Record<string, number>) => void;
pause: () => void; pause: () => void;
play: () => void; play: () => void;
@ -14,6 +15,7 @@ export interface SoundActions {
shuffle: () => void; shuffle: () => void;
toggleFavorite: (id: string) => void; toggleFavorite: (id: string) => void;
togglePlay: () => void; togglePlay: () => void;
unlock: () => void;
unselect: (id: string) => void; unselect: (id: string) => void;
unselectAll: (pushToHistory?: boolean) => void; unselectAll: (pushToHistory?: boolean) => void;
} }
@ -25,6 +27,10 @@ export const createActions: StateCreator<
SoundActions SoundActions
> = (set, get) => { > = (set, get) => {
return { return {
lock() {
set({ locked: true });
},
override(newSounds) { override(newSounds) {
get().unselectAll(); get().unselectAll();
@ -111,6 +117,10 @@ export const createActions: StateCreator<
set({ isPlaying: !get().isPlaying }); set({ isPlaying: !get().isPlaying });
}, },
unlock() {
set({ locked: false });
},
unselect(id) { unselect(id) {
set({ set({
sounds: { sounds: {

View file

@ -14,6 +14,7 @@ export interface SoundState {
}; };
} | null; } | null;
isPlaying: boolean; isPlaying: boolean;
locked: boolean;
noSelected: () => boolean; noSelected: () => boolean;
sounds: { sounds: {
[id: string]: { [id: string]: {
@ -40,6 +41,7 @@ export const createState: StateCreator<
}, },
history: null, history: null,
isPlaying: false, isPlaying: false,
locked: false,
noSelected() { noSelected() {
const { sounds } = get(); const { sounds } = get();
const keys = Object.keys(sounds); const keys = Object.keys(sounds);