mirror of
https://github.com/remvze/moodist.git
synced 2025-12-19 01:44:15 +00:00
114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
import { useMemo, useEffect } from 'react';
|
|
import { useShallow } from 'zustand/react/shallow';
|
|
import { BiSolidHeart } from 'react-icons/bi/index';
|
|
import { Howler } from 'howler';
|
|
import { I18nextProvider } from 'react-i18next';
|
|
import i18n from '@/i18n';
|
|
|
|
import { useSoundStore } from '@/stores/sound';
|
|
|
|
import { Container } from '@/components/container';
|
|
import { StoreConsumer } from '@/components/store-consumer';
|
|
import { Buttons } from '@/components/buttons';
|
|
import { Categories } from '@/components/categories';
|
|
import { SharedModal } from '@/components/modals/shared';
|
|
import { Toolbar } from '@/components/toolbar';
|
|
import { SnackbarProvider } from '@/contexts/snackbar';
|
|
import { MediaControls } from '@/components/media-controls';
|
|
|
|
import { sounds } from '@/data/sounds';
|
|
import { FADE_OUT } from '@/constants/events';
|
|
|
|
import type { Sound, Category as CategoryType } from '@/data/types';
|
|
import { subscribe } from '@/lib/event';
|
|
|
|
interface AppProps {
|
|
locale: string;
|
|
}
|
|
|
|
export function App({ locale }: AppProps) {
|
|
if (locale && i18n.language !== locale) {
|
|
i18n.changeLanguage(locale);
|
|
}
|
|
const categoriesData = useMemo(() => sounds.categories, []);
|
|
|
|
const favorites = useSoundStore(useShallow(state => state.getFavorites()));
|
|
const pause = useSoundStore(state => state.pause);
|
|
const lock = useSoundStore(state => state.lock);
|
|
const unlock = useSoundStore(state => state.unlock);
|
|
|
|
const favoriteSounds = useMemo(() => {
|
|
const allFlatSounds = categoriesData
|
|
.map(category => category.sounds)
|
|
.flat();
|
|
const favoriteSoundsData = allFlatSounds.filter(sound =>
|
|
favorites.includes(sound.id),
|
|
);
|
|
|
|
return favorites
|
|
.map(favoriteId =>
|
|
favoriteSoundsData.find(sound => sound.id === favoriteId),
|
|
)
|
|
.filter((s): s is Sound => s !== undefined);
|
|
}, [favorites, categoriesData]);
|
|
|
|
useEffect(() => {
|
|
const onChange = () => {
|
|
const { ctx } = Howler;
|
|
|
|
if (ctx && !document.hidden) {
|
|
setTimeout(() => {
|
|
ctx.resume();
|
|
}, 100);
|
|
}
|
|
};
|
|
|
|
document.addEventListener('visibilitychange', onChange, false);
|
|
|
|
return () => document.removeEventListener('visibilitychange', onChange);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const unsubscribe = subscribe(FADE_OUT, (e: { duration: number }) => {
|
|
lock();
|
|
|
|
setTimeout(() => {
|
|
pause();
|
|
unlock();
|
|
}, e.duration);
|
|
});
|
|
|
|
return unsubscribe;
|
|
}, [pause, lock, unlock]);
|
|
|
|
const allCategories = useMemo(() => {
|
|
const favs: CategoryType[] = [];
|
|
if (favoriteSounds.length) {
|
|
favs.push({
|
|
icon: <BiSolidHeart />,
|
|
id: 'favorites',
|
|
sounds: favoriteSounds,
|
|
titleKey: 'sounds.favorites.title',
|
|
});
|
|
}
|
|
return [...favs, ...categoriesData];
|
|
}, [favoriteSounds, categoriesData]);
|
|
|
|
return (
|
|
<I18nextProvider i18n={i18n}>
|
|
<SnackbarProvider>
|
|
<StoreConsumer>
|
|
<MediaControls />
|
|
<Container>
|
|
<div id="app" />
|
|
<Buttons />
|
|
<Categories categories={allCategories} />
|
|
</Container>
|
|
|
|
<Toolbar />
|
|
<SharedModal />
|
|
</StoreConsumer>
|
|
</SnackbarProvider>
|
|
</I18nextProvider>
|
|
);
|
|
}
|