From 37cdbb8dbd79669a4a51ecbcd7dbb5eb5c979b1e Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Tue, 18 Oct 2022 19:21:03 -0700 Subject: [PATCH] Add backgrounded stream+messages title notifier. Closes #2208 --- .../TitleNotifier/TitleNotifier.tsx | 73 +++++++++++++++++++ web/components/layouts/Main.tsx | 2 + web/components/stores/ClientConfigStore.tsx | 11 ++- 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 web/components/TitleNotifier/TitleNotifier.tsx diff --git a/web/components/TitleNotifier/TitleNotifier.tsx b/web/components/TitleNotifier/TitleNotifier.tsx new file mode 100644 index 000000000..a28b9dcfc --- /dev/null +++ b/web/components/TitleNotifier/TitleNotifier.tsx @@ -0,0 +1,73 @@ +/** + * This component is responsible for updating the title of the page when + * different state changes occur. + * If the stream live state changes, or chat messages come in while the + * page is backgrounded, this component will update the title to reflect it. * + * @component + */ +import { FC, useEffect } from 'react'; +import { useRecoilValue } from 'recoil'; +import { serverStatusState, chatMessagesAtom } from '../stores/ClientConfigStore'; + +export const TitleNotifier: FC = () => { + const chatMessages = useRecoilValue(chatMessagesAtom); + const serverStatus = useRecoilValue(serverStatusState); + + let backgrounded = false; + let defaultTitle = ''; + + const onBlur = () => { + console.log('onBlur'); + backgrounded = true; + defaultTitle = document.title; + }; + + const onFocus = () => { + console.log('onFocus'); + backgrounded = false; + window.document.title = defaultTitle; + }; + + const listenForEvents = () => { + // Listen for events that should update the title + console.log('listenForEvents'); + window.addEventListener('blur', onBlur); + window.addEventListener('focus', onFocus); + }; + + useEffect(() => { + defaultTitle = window.document.title; + listenForEvents(); + + return () => { + window.removeEventListener('focus', onFocus); + window.removeEventListener('blur', onBlur); + }; + }, []); + + useEffect(() => { + const { online } = serverStatus; + + if (!backgrounded || !online) { + return; + } + + window.document.title = `💬 :: ${defaultTitle}`; + }, [chatMessages]); + + useEffect(() => { + if (!backgrounded) { + return; + } + + const { online } = serverStatus; + + if (online) { + window.document.title = ` 🟢 :: ${defaultTitle}`; + } else if (!online) { + window.document.title = ` 🔴 :: ${defaultTitle}`; + } + }, [serverStatusState]); + + return null; +}; diff --git a/web/components/layouts/Main.tsx b/web/components/layouts/Main.tsx index 4fba689ff..8e4924376 100644 --- a/web/components/layouts/Main.tsx +++ b/web/components/layouts/Main.tsx @@ -18,6 +18,7 @@ import { FatalErrorStateModal } from '../modals/FatalErrorStateModal/FatalErrorS import setupNoLinkReferrer from '../../utils/no-link-referrer'; import { ServerRenderedMetadata } from '../ServerRendered/ServerRenderedMetadata'; import { ServerRenderedHydration } from '../ServerRendered/ServerRenderedHydration'; +import { TitleNotifier } from '../TitleNotifier/TitleNotifier'; export const Main: FC = () => { const clientConfig = useRecoilValue(clientConfigStateAtom); @@ -77,6 +78,7 @@ export const Main: FC = () => { +
diff --git a/web/components/stores/ClientConfigStore.tsx b/web/components/stores/ClientConfigStore.tsx index a4cb5572b..33a9b7adf 100644 --- a/web/components/stores/ClientConfigStore.tsx +++ b/web/components/stores/ClientConfigStore.tsx @@ -329,6 +329,8 @@ export const ClientConfigStore: FC = () => { } }; + const handleChatNotification = () => {}; + // Read the config and status on initial load from a JSON string that lives // in window. This is placed there server-side and allows for fast initial // load times because we don't have to wait for the API calls to complete. @@ -339,7 +341,7 @@ export const ClientConfigStore: FC = () => { setClientConfig(config); } } catch (e) { - // console.error('Error parsing config hydration', e); + console.error('Error parsing config hydration', e); } try { @@ -348,7 +350,7 @@ export const ClientConfigStore: FC = () => { setServerStatus(status); } } catch (e) { - // console.error('error parsing status hydration', e); + console.error('error parsing status hydration', e); } }, []); @@ -364,6 +366,11 @@ export const ClientConfigStore: FC = () => { } }, [hasLoadedConfig, accessToken]); + // Notify about chat activity when backgrounded. + useEffect(() => { + handleChatNotification(); + }, [chatMessages]); + useEffect(() => { updateClientConfig(); handleUserRegistration();