// 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 ( { shortcutModalOpen && } { gamepadModalOpen && } ); }; module.exports = withCoreSuspender(App);