mirror of
https://github.com/owncast/owncast.git
synced 2024-10-10 19:16:02 +00:00
Display global error if server is unreachable
This commit is contained in:
parent
ce9d403269
commit
aae63e4e2c
@ -4,20 +4,28 @@ import {
|
|||||||
ClientConfigStore,
|
ClientConfigStore,
|
||||||
isChatAvailableSelector,
|
isChatAvailableSelector,
|
||||||
clientConfigStateAtom,
|
clientConfigStateAtom,
|
||||||
|
fatalErrorStateAtom,
|
||||||
} from '../stores/ClientConfigStore';
|
} from '../stores/ClientConfigStore';
|
||||||
import { Content, Header } from '../ui';
|
import { Content, Header } from '../ui';
|
||||||
import { ClientConfig } from '../../interfaces/client-config.model';
|
import { ClientConfig } from '../../interfaces/client-config.model';
|
||||||
|
import { DisplayableError } from '../../types/displayable-error';
|
||||||
|
import FatalErrorStateModal from '../modals/FatalErrorModal';
|
||||||
|
|
||||||
function Main() {
|
function Main() {
|
||||||
const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom);
|
const clientConfig = useRecoilValue<ClientConfig>(clientConfigStateAtom);
|
||||||
const { name, title } = clientConfig;
|
const { name, title } = clientConfig;
|
||||||
const isChatAvailable = useRecoilValue<boolean>(isChatAvailableSelector);
|
const isChatAvailable = useRecoilValue<boolean>(isChatAvailableSelector);
|
||||||
|
const fatalError = useRecoilValue<DisplayableError>(fatalErrorStateAtom);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ClientConfigStore />
|
<ClientConfigStore />
|
||||||
<Layout>
|
<Layout>
|
||||||
<Header name={title || name} chatAvailable={isChatAvailable} />
|
<Header name={title || name} chatAvailable={isChatAvailable} />
|
||||||
<Content />
|
<Content />
|
||||||
|
{fatalError && (
|
||||||
|
<FatalErrorStateModal title={fatalError.title} message={fatalError.message} />
|
||||||
|
)}
|
||||||
</Layout>
|
</Layout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
31
web/components/modals/FatalErrorModal.tsx
Normal file
31
web/components/modals/FatalErrorModal.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { Modal } from 'antd';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FatalErrorStateModal(props: Props) {
|
||||||
|
const { title, message } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={title}
|
||||||
|
visible
|
||||||
|
footer={null}
|
||||||
|
closable={false}
|
||||||
|
keyboard={false}
|
||||||
|
width={900}
|
||||||
|
centered
|
||||||
|
className="modal"
|
||||||
|
>
|
||||||
|
<style global jsx>{`
|
||||||
|
.modal .ant-modal-content,
|
||||||
|
.modal .ant-modal-header {
|
||||||
|
background-color: var(--warning-color);
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
<p>{message}</p>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -24,6 +24,7 @@ import handleChatMessage from './eventhandlers/handleChatMessage';
|
|||||||
import handleConnectedClientInfoMessage from './eventhandlers/connected-client-info-handler';
|
import handleConnectedClientInfoMessage from './eventhandlers/connected-client-info-handler';
|
||||||
import ServerStatusService from '../../services/status-service';
|
import ServerStatusService from '../../services/status-service';
|
||||||
import handleNameChangeEvent from './eventhandlers/handleNameChangeEvent';
|
import handleNameChangeEvent from './eventhandlers/handleNameChangeEvent';
|
||||||
|
import { DisplayableError } from '../../types/displayable-error';
|
||||||
|
|
||||||
const SERVER_STATUS_POLL_DURATION = 5000;
|
const SERVER_STATUS_POLL_DURATION = 5000;
|
||||||
const ACCESS_TOKEN_KEY = 'accessToken';
|
const ACCESS_TOKEN_KEY = 'accessToken';
|
||||||
@ -76,6 +77,11 @@ export const isVideoPlayingAtom = atom<boolean>({
|
|||||||
default: false,
|
default: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const fatalErrorStateAtom = atom<DisplayableError>({
|
||||||
|
key: 'fatalErrorStateAtom',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
|
|
||||||
// Chat is visible if the user wishes it to be visible AND the required
|
// Chat is visible if the user wishes it to be visible AND the required
|
||||||
// chat state is set.
|
// chat state is set.
|
||||||
export const isChatVisibleSelector = selector({
|
export const isChatVisibleSelector = selector({
|
||||||
@ -129,10 +135,17 @@ export function ClientConfigStore() {
|
|||||||
const [chatMessages, setChatMessages] = useRecoilState<ChatMessage[]>(chatMessagesAtom);
|
const [chatMessages, setChatMessages] = useRecoilState<ChatMessage[]>(chatMessagesAtom);
|
||||||
const [accessToken, setAccessToken] = useRecoilState<string>(accessTokenAtom);
|
const [accessToken, setAccessToken] = useRecoilState<string>(accessTokenAtom);
|
||||||
const setAppState = useSetRecoilState<AppStateOptions>(appStateAtom);
|
const setAppState = useSetRecoilState<AppStateOptions>(appStateAtom);
|
||||||
|
const setGlobalFatalErrorMessage = useSetRecoilState<DisplayableError>(fatalErrorStateAtom);
|
||||||
const setWebsocketService = useSetRecoilState<WebsocketService>(websocketServiceAtom);
|
const setWebsocketService = useSetRecoilState<WebsocketService>(websocketServiceAtom);
|
||||||
|
|
||||||
let ws: WebsocketService;
|
let ws: WebsocketService;
|
||||||
|
|
||||||
|
const setGlobalFatalError = (title: string, message: string) => {
|
||||||
|
setGlobalFatalErrorMessage({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
};
|
||||||
const sendEvent = (event: string) => {
|
const sendEvent = (event: string) => {
|
||||||
// console.log('---- sending event:', event);
|
// console.log('---- sending event:', event);
|
||||||
appStateSend({ type: event });
|
appStateSend({ type: event });
|
||||||
@ -143,7 +156,12 @@ export function ClientConfigStore() {
|
|||||||
const config = await ClientConfigService.getConfig();
|
const config = await ClientConfigService.getConfig();
|
||||||
setClientConfig(config);
|
setClientConfig(config);
|
||||||
sendEvent('LOADED');
|
sendEvent('LOADED');
|
||||||
|
setGlobalFatalErrorMessage(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
setGlobalFatalError(
|
||||||
|
'Unable to reach Owncast server',
|
||||||
|
`Owncast cannot launch. Please make sure the Owncast server is running. ${error}`,
|
||||||
|
);
|
||||||
console.error(`ClientConfigService -> getConfig() ERROR: \n${error}`);
|
console.error(`ClientConfigService -> getConfig() ERROR: \n${error}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -158,8 +176,13 @@ export function ClientConfigStore() {
|
|||||||
} else if (!status.online) {
|
} else if (!status.online) {
|
||||||
sendEvent(AppStateEvent.Offline);
|
sendEvent(AppStateEvent.Offline);
|
||||||
}
|
}
|
||||||
|
setGlobalFatalErrorMessage(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
sendEvent(AppStateEvent.Fail);
|
sendEvent(AppStateEvent.Fail);
|
||||||
|
setGlobalFatalError(
|
||||||
|
'Unable to reach Owncast server',
|
||||||
|
`Owncast cannot launch. Please make sure the Owncast server is running. ${error}`,
|
||||||
|
);
|
||||||
console.error(`serverStatusState -> getStatus() ERROR: \n${error}`);
|
console.error(`serverStatusState -> getStatus() ERROR: \n${error}`);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
21
web/stories/FatalErrorStateModal.stories.tsx
Normal file
21
web/stories/FatalErrorStateModal.stories.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||||
|
import FatalErrorStateModal from '../components/modals/FatalErrorModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'owncast/Modals/Global error state',
|
||||||
|
component: FatalErrorStateModal,
|
||||||
|
parameters: {},
|
||||||
|
} as ComponentMeta<typeof FatalErrorStateModal>;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const Template: ComponentStory<typeof FatalErrorStateModal> = args => (
|
||||||
|
<FatalErrorStateModal {...args} />
|
||||||
|
);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export const Example = Template.bind({});
|
||||||
|
Example.args = {
|
||||||
|
title: 'Example error title',
|
||||||
|
message: 'Example error message',
|
||||||
|
};
|
||||||
4
web/types/displayable-error.ts
Normal file
4
web/types/displayable-error.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface DisplayableError {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user