From 8d02f4068d872f86b7b365208e2524342ed152d2 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sat, 10 Sep 2022 18:08:51 -0700 Subject: [PATCH] Polish up the initial loading experience --- web/components/stores/ClientConfigStore.tsx | 23 ++- web/components/ui/Content/Content.tsx | 135 +++++++++--------- .../ui/OfflineBanner/OfflineBanner.tsx | 6 +- web/pages/embed/video/index.tsx | 2 +- 4 files changed, 90 insertions(+), 76 deletions(-) diff --git a/web/components/stores/ClientConfigStore.tsx b/web/components/stores/ClientConfigStore.tsx index 79e130737..21821e865 100644 --- a/web/components/stores/ClientConfigStore.tsx +++ b/web/components/stores/ClientConfigStore.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect } from 'react'; +import { FC, useEffect, useState } from 'react'; import { atom, selector, useRecoilState, useSetRecoilState } from 'recoil'; import { useMachine } from '@xstate/react'; import { makeEmptyClientConfig, ClientConfig } from '../../interfaces/client-config.model'; @@ -29,6 +29,8 @@ import { DisplayableError } from '../../types/displayable-error'; const SERVER_STATUS_POLL_DURATION = 5000; const ACCESS_TOKEN_KEY = 'accessToken'; +let serverStatusRefreshPoll: ReturnType; + // Server status is what gets updated such as viewer count, durations, // stream title, online/offline state, etc. export const serverStatusState = atom({ @@ -187,6 +189,8 @@ export const ClientConfigStore: FC = () => { const setGlobalFatalErrorMessage = useSetRecoilState(fatalErrorStateAtom); const setWebsocketService = useSetRecoilState(websocketServiceAtom); const [hiddenMessageIds, setHiddenMessageIds] = useRecoilState(removedMessageIdsAtom); + const [hasLoadedStatus, setHasLoadedStatus] = useState(false); + const [hasLoadedConfig, setHasLoadedConfig] = useState(false); let ws: WebsocketService; @@ -205,12 +209,12 @@ export const ClientConfigStore: FC = () => { try { const config = await ClientConfigService.getConfig(); setClientConfig(config); - sendEvent('LOADED'); setGlobalFatalErrorMessage(null); + setHasLoadedConfig(true); } catch (error) { setGlobalFatalError( 'Unable to reach Owncast server', - `Owncast cannot launch. Please make sure the Owncast server is running. ${error}`, + `Owncast cannot launch. Please make sure the Owncast server is running.`, ); console.error(`ClientConfigService -> getConfig() ERROR: \n${error}`); } @@ -220,6 +224,7 @@ export const ClientConfigStore: FC = () => { try { const status = await ServerStatusService.getStatus(); setServerStatus(status); + setHasLoadedStatus(true); const { serverTime } = status; const clockSkew = new Date(serverTime).getTime() - Date.now(); @@ -332,12 +337,10 @@ export const ClientConfigStore: FC = () => { }; const startChat = async () => { - sendEvent(AppStateEvent.Loading); try { ws = new WebsocketService(accessToken, '/ws'); ws.handleMessage = handleMessage; setWebsocketService(ws); - sendEvent(AppStateEvent.Loaded); } catch (error) { console.error(`ChatService -> startChat() ERROR: \n${error}`); } @@ -366,11 +369,19 @@ export const ClientConfigStore: FC = () => { } }, []); + useEffect(() => { + if (hasLoadedStatus && hasLoadedConfig) { + sendEvent(AppStateEvent.Loaded); + } + }, [hasLoadedStatus, hasLoadedConfig]); + useEffect(() => { updateClientConfig(); handleUserRegistration(); updateServerStatus(); - setInterval(() => { + + clearInterval(serverStatusRefreshPoll); + serverStatusRefreshPoll = setInterval(() => { updateServerStatus(); }, SERVER_STATUS_POLL_DURATION); }, [appState]); diff --git a/web/components/ui/Content/Content.tsx b/web/components/ui/Content/Content.tsx index 96a9a62ba..d3e531fb1 100644 --- a/web/components/ui/Content/Content.tsx +++ b/web/components/ui/Content/Content.tsx @@ -170,79 +170,82 @@ export const Content: FC = () => { window.addEventListener('resize', checkIfMobile); }, []); + let offlineMessageBody = + !appState.appLoading && 'Please follow and ask to get notified when the stream is live.'; + if (offlineMessage && !appState.appLoading) { + offlineMessageBody = offlineMessage; + } + + const offlineTitle = !appState.appLoading && `${name} is currently offline`; + return (
- -
- + + +
+
+ {online && } + {!online && !appState.appLoading && ( + + )} + +
+
+
+ + {externalActionButtons} + + setShowNotifyPopup(true)} + notificationClosed={() => disableNotifyReminderPopup()} + > + setShowNotifyPopup(true)} /> + + -
- {online && } - {!online && ( - disableNotifyReminderPopup()} + handleCancel={() => disableNotifyReminderPopup()} + > + + +
+
+ {isMobile && isChatVisible ? ( + + ) : ( + )} -
-
-
- - {externalActionButtons} - - setShowNotifyPopup(true)} - notificationClosed={() => disableNotifyReminderPopup()} - > - setShowNotifyPopup(true)} /> - - - - disableNotifyReminderPopup()} - handleCancel={() => disableNotifyReminderPopup()} - > - - -
-
- {isMobile && isChatVisible ? ( - - ) : ( - - )} -
- {isChatVisible && !isMobile && } -
- {(!isMobile || !isChatVisible) &&
} + {isChatVisible && !isMobile && } + + {(!isMobile || !isChatVisible) &&
} +
); }; diff --git a/web/components/ui/OfflineBanner/OfflineBanner.tsx b/web/components/ui/OfflineBanner/OfflineBanner.tsx index eb2f2104f..43c93cfe0 100644 --- a/web/components/ui/OfflineBanner/OfflineBanner.tsx +++ b/web/components/ui/OfflineBanner/OfflineBanner.tsx @@ -4,14 +4,14 @@ import { FC } from 'react'; import styles from './OfflineBanner.module.scss'; export type OfflineBannerProps = { - name: string; + title: string; text: string; }; -export const OfflineBanner: FC = ({ name, text }) => ( +export const OfflineBanner: FC = ({ title, text }) => (
-
{name} is currently offline.
+
{title}
{text}
diff --git a/web/pages/embed/video/index.tsx b/web/pages/embed/video/index.tsx index 03638ba32..c94d1fb7c 100644 --- a/web/pages/embed/video/index.tsx +++ b/web/pages/embed/video/index.tsx @@ -26,7 +26,7 @@ export default function VideoEmbed() {
{online && } - {!online && }{' '} + {!online && }{' '}