diff --git a/src/App/App.js b/src/App/App.js index 92240aa0d..b6031be6e 100644 --- a/src/App/App.js +++ b/src/App/App.js @@ -21,6 +21,7 @@ const RouterWithProtectedRoutes = withCoreSuspender(withProtectedRoutes(Router)) const App = () => { const { i18n } = useTranslation(); const shell = useShell(); + const [windowHidden, setWindowHidden] = React.useState(false); const onPathNotMatch = React.useCallback(() => { return NotFound; }, []); @@ -98,6 +99,17 @@ const App = () => { services.chromecast.off('stateChanged', onChromecastStateChange); }; }, []); + + // Handle shell window visibility changed event + React.useEffect(() => { + const onWindowVisibilityChanged = (state) => { + setWindowHidden(state.visible === false && state.visibility === 0); + }; + + shell.on('win-visibility-changed', onWindowVisibilityChanged); + return () => shell.off('win-visibility-changed', onWindowVisibilityChanged); + }, []); + React.useEffect(() => { const onCoreEvent = ({ event, args }) => { switch (event) { @@ -105,9 +117,11 @@ const App = () => { if (args && args.settings && typeof args.settings.interfaceLanguage === 'string') { i18n.changeLanguage(args.settings.interfaceLanguage); } - if (args?.settings) { - shell.send('update-settings', args.settings); + + if (args?.settings?.quitOnClose && windowHidden) { + shell.send('quit'); } + break; } } @@ -117,8 +131,8 @@ const App = () => { i18n.changeLanguage(state.profile.settings.interfaceLanguage); } - if (state?.profile?.settings) { - shell.send('update-settings', state.profile.settings); + if (state?.profile?.settings?.quitOnClose && windowHidden) { + shell.send('quit'); } }; const onWindowFocus = () => { @@ -162,7 +176,7 @@ const App = () => { services.core.transport.off('CoreEvent', onCoreEvent); } }; - }, [initialized]); + }, [initialized, windowHidden]); return ( diff --git a/src/common/useShell.ts b/src/common/useShell.ts index 5e61bfe84..f4700d779 100644 --- a/src/common/useShell.ts +++ b/src/common/useShell.ts @@ -1,21 +1,71 @@ +import { useEffect } from 'react'; +import EventEmitter from 'eventemitter3'; + +const SHELL_EVENT_OBJECT = 'transport'; +const transport = globalThis?.qt?.webChannelTransport; +const events = new EventEmitter(); + +enum ShellEventType { + SIGNAL = 1, + INVOKE_METHOD = 6, +} + +type ShellEvent = { + id: number; + type: ShellEventType; + object: string; + args: string[]; +}; + + + const createId = () => Math.floor(Math.random() * 9999) + 1; const useShell = () => { - const transport = globalThis?.qt?.webChannelTransport; - const send = (method: string, ...args: (string | number)[]) => { - transport?.send(JSON.stringify({ - id: createId(), - type: 6, - object: 'transport', - method: 'onEvent', - args: [method, ...args], - })); + try { + transport?.send(JSON.stringify({ + id: createId(), + type: ShellEventType.INVOKE_METHOD, + object: SHELL_EVENT_OBJECT, + method: 'onEvent', + args: [method, ...args], + })); + } catch(e) { + console.error('Shell', 'Failed to send event', e); + } }; + const on = (name: string, listener: (arg: any) => void) => { + events.on(name, listener); + }; + + const off = (name: string, listener: (arg: any) => void) => { + events.off(name, listener); + }; + + useEffect(() => { + if (!transport) return; + + transport.onmessage = ({ data }) => { + try { + const { type, args } = JSON.parse(data) as ShellEvent; + + if (type === ShellEventType.SIGNAL) { + const [methodName, methodArg] = args; + events.emit(methodName, methodArg); + } + } catch (e) { + console.error('Shell', 'Failed to handle event', e); + } + }; + }, []); + return { active: !!transport, send, + on, + off, }; }; diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 5effeffd4..7a50d7432 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,7 +1,12 @@ /* eslint-disable no-var */ +type QtTransportMessage = { + data: string; +}; + interface QtTransport { send: (message: string) => void, + onmessage: (message: QtTransportMessage) => void, } interface Qt { @@ -12,4 +17,4 @@ declare global { var qt: Qt | undefined; } -export { }; +export {} \ No newline at end of file