mirror of
https://github.com/owncast/owncast.git
synced 2024-10-10 19:16:02 +00:00
Polish up the initial loading experience
This commit is contained in:
parent
5b29abd42d
commit
8d02f4068d
@ -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<typeof setInterval>;
|
||||
|
||||
// Server status is what gets updated such as viewer count, durations,
|
||||
// stream title, online/offline state, etc.
|
||||
export const serverStatusState = atom<ServerStatus>({
|
||||
@ -187,6 +189,8 @@ export const ClientConfigStore: FC = () => {
|
||||
const setGlobalFatalErrorMessage = useSetRecoilState<DisplayableError>(fatalErrorStateAtom);
|
||||
const setWebsocketService = useSetRecoilState<WebsocketService>(websocketServiceAtom);
|
||||
const [hiddenMessageIds, setHiddenMessageIds] = useRecoilState<string[]>(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]);
|
||||
|
@ -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 (
|
||||
<div>
|
||||
<AntContent className={styles.root}>
|
||||
<div className={styles.leftContent}>
|
||||
<Spin className={styles.loadingSpinner} size="large" spinning={appState.appLoading} />
|
||||
<Spin className={styles.loadingSpinner} size="large" spinning={appState.appLoading}>
|
||||
<AntContent className={styles.root}>
|
||||
<div className={styles.leftContent}>
|
||||
<div className={styles.topSection}>
|
||||
{online && <OwncastPlayer source="/hls/stream.m3u8" online={online} />}
|
||||
{!online && !appState.appLoading && (
|
||||
<OfflineBanner title={offlineTitle} text={offlineMessageBody} />
|
||||
)}
|
||||
<Statusbar
|
||||
online={online}
|
||||
lastConnectTime={lastConnectTime}
|
||||
lastDisconnectTime={lastDisconnectTime}
|
||||
viewerCount={viewerCount}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.midSection}>
|
||||
<div className={styles.buttonsLogoTitleSection}>
|
||||
<ActionButtonRow>
|
||||
{externalActionButtons}
|
||||
<FollowButton size="small" />
|
||||
<NotifyReminderPopup
|
||||
visible={showNotifyReminder}
|
||||
notificationClicked={() => setShowNotifyPopup(true)}
|
||||
notificationClosed={() => disableNotifyReminderPopup()}
|
||||
>
|
||||
<NotifyButton onClick={() => setShowNotifyPopup(true)} />
|
||||
</NotifyReminderPopup>
|
||||
</ActionButtonRow>
|
||||
|
||||
<div className={styles.topSection}>
|
||||
{online && <OwncastPlayer source="/hls/stream.m3u8" online={online} />}
|
||||
{!online && (
|
||||
<OfflineBanner
|
||||
<Modal
|
||||
title="Notify"
|
||||
visible={showNotifyPopup}
|
||||
afterClose={() => disableNotifyReminderPopup()}
|
||||
handleCancel={() => disableNotifyReminderPopup()}
|
||||
>
|
||||
<BrowserNotifyModal />
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
{isMobile && isChatVisible ? (
|
||||
<MobileContent
|
||||
name={name}
|
||||
text={
|
||||
offlineMessage || 'Please follow and ask to get notified when the stream is live.'
|
||||
}
|
||||
streamTitle={streamTitle}
|
||||
summary={summary}
|
||||
tags={tags}
|
||||
socialHandles={socialHandles}
|
||||
extraPageContent={extraPageContent}
|
||||
messages={messages}
|
||||
chatDisplayName={chatDisplayName}
|
||||
chatUserId={chatUserId}
|
||||
/>
|
||||
) : (
|
||||
<DesktopContent
|
||||
name={name}
|
||||
streamTitle={streamTitle}
|
||||
summary={summary}
|
||||
tags={tags}
|
||||
socialHandles={socialHandles}
|
||||
extraPageContent={extraPageContent}
|
||||
/>
|
||||
)}
|
||||
<Statusbar
|
||||
online={online}
|
||||
lastConnectTime={lastConnectTime}
|
||||
lastDisconnectTime={lastDisconnectTime}
|
||||
viewerCount={viewerCount}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.midSection}>
|
||||
<div className={styles.buttonsLogoTitleSection}>
|
||||
<ActionButtonRow>
|
||||
{externalActionButtons}
|
||||
<FollowButton size="small" />
|
||||
<NotifyReminderPopup
|
||||
visible={showNotifyReminder}
|
||||
notificationClicked={() => setShowNotifyPopup(true)}
|
||||
notificationClosed={() => disableNotifyReminderPopup()}
|
||||
>
|
||||
<NotifyButton onClick={() => setShowNotifyPopup(true)} />
|
||||
</NotifyReminderPopup>
|
||||
</ActionButtonRow>
|
||||
|
||||
<Modal
|
||||
title="Notify"
|
||||
visible={showNotifyPopup}
|
||||
afterClose={() => disableNotifyReminderPopup()}
|
||||
handleCancel={() => disableNotifyReminderPopup()}
|
||||
>
|
||||
<BrowserNotifyModal />
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
{isMobile && isChatVisible ? (
|
||||
<MobileContent
|
||||
name={name}
|
||||
streamTitle={streamTitle}
|
||||
summary={summary}
|
||||
tags={tags}
|
||||
socialHandles={socialHandles}
|
||||
extraPageContent={extraPageContent}
|
||||
messages={messages}
|
||||
chatDisplayName={chatDisplayName}
|
||||
chatUserId={chatUserId}
|
||||
/>
|
||||
) : (
|
||||
<DesktopContent
|
||||
name={name}
|
||||
streamTitle={streamTitle}
|
||||
summary={summary}
|
||||
tags={tags}
|
||||
socialHandles={socialHandles}
|
||||
extraPageContent={extraPageContent}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isChatVisible && !isMobile && <Sidebar />}
|
||||
</AntContent>
|
||||
{(!isMobile || !isChatVisible) && <Footer version={version} />}
|
||||
{isChatVisible && !isMobile && <Sidebar />}
|
||||
</AntContent>
|
||||
{(!isMobile || !isChatVisible) && <Footer version={version} />}
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -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<OfflineBannerProps> = ({ name, text }) => (
|
||||
export const OfflineBanner: FC<OfflineBannerProps> = ({ title, text }) => (
|
||||
<div className={styles.outerContainer}>
|
||||
<div className={styles.innerContainer}>
|
||||
<div className={styles.header}>{name} is currently offline.</div>
|
||||
<div className={styles.header}>{title}</div>
|
||||
<Divider />
|
||||
<div>{text}</div>
|
||||
|
||||
|
@ -26,7 +26,7 @@ export default function VideoEmbed() {
|
||||
<ClientConfigStore />
|
||||
<div className="video-embed">
|
||||
{online && <OwncastPlayer source="/hls/stream.m3u8" online={online} />}
|
||||
{!online && <OfflineBanner name={name} text="Stream is offline text goes here." />}{' '}
|
||||
{!online && <OfflineBanner title={name} text="Stream is offline text goes here." />}{' '}
|
||||
<Statusbar
|
||||
online={online}
|
||||
lastConnectTime={lastConnectTime}
|
||||
|
Loading…
x
Reference in New Issue
Block a user