mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-05-10 15:30:40 +00:00
212 lines
8 KiB
JavaScript
212 lines
8 KiB
JavaScript
// Copyright (C) 2017-2023 Smart code 203358507
|
|
|
|
require('spatial-navigation-polyfill');
|
|
const React = require('react');
|
|
const { useTranslation } = require('react-i18next');
|
|
const { useCore } = require('stremio/core');
|
|
const { Router } = require('stremio-router');
|
|
const { Shell, Chromecast, KeyboardShortcuts, ServicesProvider, GamepadProvider } = require('stremio/services');
|
|
const { NotFound } = require('stremio/routes');
|
|
const { FullscreenProvider, PlatformProvider, ToastProvider, TooltipProvider, ShortcutsProvider, CONSTANTS, useShell, useBinaryState, useProfile, withCoreSuspender, onFileDrop } = require('stremio/common');
|
|
const ServicesToaster = require('./ServicesToaster');
|
|
const DeepLinkHandler = require('./DeepLinkHandler');
|
|
const SearchParamsHandler = require('./SearchParamsHandler');
|
|
const { default: UpdaterBanner } = require('./UpdaterBanner');
|
|
const { default: ShortcutsModal } = require('./ShortcutsModal');
|
|
const { default: GamepadModal } = require('./GamepadModal');
|
|
const withProtectedRoutes = require('./withProtectedRoutes');
|
|
const routerViewsConfig = require('./routerViewsConfig');
|
|
const styles = require('./styles');
|
|
|
|
const RouterWithProtectedRoutes = withProtectedRoutes(Router);
|
|
|
|
const App = () => {
|
|
const core = useCore();
|
|
const profile = useProfile();
|
|
const { i18n } = useTranslation();
|
|
const shell = useShell();
|
|
const [gamepadSupportEnabled, setGamepadSupportEnabled] = React.useState(false);
|
|
const onPathNotMatch = React.useCallback(() => {
|
|
return NotFound;
|
|
}, []);
|
|
const services = React.useMemo(() => {
|
|
return {
|
|
shell: new Shell(),
|
|
chromecast: new Chromecast(),
|
|
keyboardShortcuts: new KeyboardShortcuts(),
|
|
};
|
|
}, []);
|
|
const [shortcutModalOpen,, closeShortcutsModal, toggleShortcutModal] = useBinaryState(false);
|
|
const [gamepadModalOpen,, closeGamepadModal, toggleGamepadModal] = useBinaryState(false);
|
|
|
|
const onShortcut = React.useCallback((name) => {
|
|
switch (name) {
|
|
case 'shortcuts':
|
|
toggleShortcutModal();
|
|
break;
|
|
case 'gamepadGuide':
|
|
toggleGamepadModal();
|
|
break;
|
|
}
|
|
}, [toggleShortcutModal, toggleGamepadModal]);
|
|
|
|
onFileDrop(['application/x-bittorrent'], (file, buffer) => {
|
|
core.transport.dispatch({
|
|
action: 'StreamingServer',
|
|
args: {
|
|
action: 'CreateTorrent',
|
|
args: Array.from(new Uint8Array(buffer))
|
|
}
|
|
});
|
|
});
|
|
|
|
React.useEffect(() => {
|
|
let prevPath = window.location.hash.slice(1);
|
|
const onLocationHashChange = () => {
|
|
core.transport.analytics({
|
|
event: 'LocationPathChanged',
|
|
args: { prevPath }
|
|
});
|
|
prevPath = window.location.hash.slice(1);
|
|
};
|
|
window.addEventListener('hashchange', onLocationHashChange);
|
|
return () => {
|
|
window.removeEventListener('hashchange', onLocationHashChange);
|
|
};
|
|
}, []);
|
|
|
|
React.useEffect(() => {
|
|
const onChromecastStateChange = () => {
|
|
if (services.chromecast.active) {
|
|
services.chromecast.transport.setOptions({
|
|
receiverApplicationId: CONSTANTS.CHROMECAST_RECEIVER_APP_ID,
|
|
autoJoinPolicy: chrome.cast.AutoJoinPolicy.PAGE_SCOPED,
|
|
resumeSavedSession: false,
|
|
language: null,
|
|
androidReceiverCompatible: true
|
|
});
|
|
}
|
|
};
|
|
services.chromecast.on('stateChanged', onChromecastStateChange);
|
|
services.shell.start();
|
|
services.chromecast.start();
|
|
services.keyboardShortcuts.start();
|
|
window.services = services;
|
|
return () => {
|
|
services.shell.stop();
|
|
services.chromecast.stop();
|
|
services.keyboardShortcuts.stop();
|
|
services.chromecast.off('stateChanged', onChromecastStateChange);
|
|
};
|
|
}, []);
|
|
|
|
// Handle shell events
|
|
React.useEffect(() => {
|
|
const onOpenMedia = (data) => {
|
|
try {
|
|
const { protocol, hostname, pathname, searchParams } = new URL(data);
|
|
if (protocol === CONSTANTS.PROTOCOL) {
|
|
if (hostname.length) {
|
|
const transportUrl = `https://${hostname}${pathname}`;
|
|
window.location.href = `#/addons?addon=${encodeURIComponent(transportUrl)}`;
|
|
} else {
|
|
window.location.href = `#${pathname}?${searchParams.toString()}`;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to open media:', e);
|
|
}
|
|
};
|
|
|
|
shell.on('open-media', onOpenMedia);
|
|
|
|
return () => {
|
|
shell.off('open-media', onOpenMedia);
|
|
};
|
|
}, []);
|
|
|
|
React.useEffect(() => {
|
|
if (typeof profile.settings?.interfaceLanguage === 'string') {
|
|
i18n.changeLanguage(profile.settings.interfaceLanguage);
|
|
}
|
|
|
|
if (typeof profile.settings?.gamepadSupport === 'boolean') {
|
|
setGamepadSupportEnabled(profile.settings.gamepadSupport);
|
|
}
|
|
|
|
if (profile.settings?.quitOnClose && shell.windowClosed) {
|
|
shell.send('quit');
|
|
}
|
|
}, [profile.settings, shell.windowClosed]);
|
|
|
|
React.useEffect(() => {
|
|
const onWindowFocus = () => {
|
|
core.transport.dispatch({
|
|
action: 'Ctx',
|
|
args: {
|
|
action: 'PullAddonsFromAPI'
|
|
}
|
|
});
|
|
core.transport.dispatch({
|
|
action: 'Ctx',
|
|
args: {
|
|
action: 'PullUserFromAPI',
|
|
args: {}
|
|
}
|
|
});
|
|
core.transport.dispatch({
|
|
action: 'Ctx',
|
|
args: {
|
|
action: 'SyncLibraryWithAPI'
|
|
}
|
|
});
|
|
core.transport.dispatch({
|
|
action: 'Ctx',
|
|
args: {
|
|
action: 'PullNotifications'
|
|
}
|
|
});
|
|
};
|
|
|
|
onWindowFocus();
|
|
window.addEventListener('focus', onWindowFocus);
|
|
|
|
return () => {
|
|
window.removeEventListener('focus', onWindowFocus);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<ServicesProvider services={services}>
|
|
<PlatformProvider>
|
|
<ToastProvider className={styles['toasts-container']}>
|
|
<TooltipProvider className={styles['tooltip-container']}>
|
|
<GamepadProvider enabled={gamepadSupportEnabled} onGuide={toggleGamepadModal}>
|
|
<ShortcutsProvider onShortcut={onShortcut}>
|
|
<FullscreenProvider>
|
|
{
|
|
shortcutModalOpen && <ShortcutsModal onClose={closeShortcutsModal}/>
|
|
}
|
|
{
|
|
gamepadModalOpen && <GamepadModal onClose={closeGamepadModal}/>
|
|
}
|
|
<ServicesToaster />
|
|
<DeepLinkHandler />
|
|
<SearchParamsHandler />
|
|
<UpdaterBanner className={styles['updater-banner-container']} />
|
|
<RouterWithProtectedRoutes
|
|
className={styles['router']}
|
|
viewsConfig={routerViewsConfig}
|
|
onPathNotMatch={onPathNotMatch}
|
|
/>
|
|
</FullscreenProvider>
|
|
</ShortcutsProvider>
|
|
</GamepadProvider>
|
|
</TooltipProvider>
|
|
</ToastProvider>
|
|
</PlatformProvider>
|
|
</ServicesProvider>
|
|
);
|
|
};
|
|
|
|
module.exports = withCoreSuspender(App);
|