2022-09-11 03:08:51 +02:00
|
|
|
import { FC, useEffect, useState } from 'react';
|
2022-05-26 05:38:40 +02:00
|
|
|
import { atom, selector, useRecoilState, useSetRecoilState } from 'recoil';
|
|
|
|
import { useMachine } from '@xstate/react';
|
2022-04-28 08:19:20 +02:00
|
|
|
import { makeEmptyClientConfig, ClientConfig } from '../../interfaces/client-config.model';
|
|
|
|
import ClientConfigService from '../../services/client-config-service';
|
2022-04-30 00:09:53 +02:00
|
|
|
import ChatService from '../../services/chat-service';
|
2022-05-03 02:45:22 +02:00
|
|
|
import WebsocketService from '../../services/websocket-service';
|
2022-04-28 23:36:05 +02:00
|
|
|
import { ChatMessage } from '../../interfaces/chat-message.model';
|
2022-10-11 01:26:09 +02:00
|
|
|
import { CurrentUser } from '../../interfaces/current-user';
|
2022-05-13 23:44:16 +02:00
|
|
|
import { ServerStatus, makeEmptyServerStatus } from '../../interfaces/server-status.model';
|
2022-05-26 05:38:40 +02:00
|
|
|
import appStateModel, {
|
|
|
|
AppStateEvent,
|
|
|
|
AppStateOptions,
|
|
|
|
makeEmptyAppState,
|
|
|
|
} from './application-state';
|
2022-05-26 22:52:04 +02:00
|
|
|
import { setLocalStorage, getLocalStorage } from '../../utils/localStorage';
|
2022-05-03 07:13:36 +02:00
|
|
|
import {
|
|
|
|
ConnectedClientInfoEvent,
|
2022-05-03 23:17:05 +02:00
|
|
|
MessageType,
|
2022-05-03 07:13:36 +02:00
|
|
|
ChatEvent,
|
2022-09-05 02:58:06 +02:00
|
|
|
MessageVisibilityEvent,
|
2022-05-12 08:31:31 +02:00
|
|
|
SocketEvent,
|
2022-05-03 07:13:36 +02:00
|
|
|
} from '../../interfaces/socket-events';
|
2022-10-19 05:40:57 +02:00
|
|
|
import { mergeMeta } from '../../utils/helpers';
|
2022-05-26 05:38:40 +02:00
|
|
|
import handleConnectedClientInfoMessage from './eventhandlers/connected-client-info-handler';
|
2022-05-13 23:44:16 +02:00
|
|
|
import ServerStatusService from '../../services/status-service';
|
2022-05-26 22:52:04 +02:00
|
|
|
import handleNameChangeEvent from './eventhandlers/handleNameChangeEvent';
|
2022-05-28 07:27:20 +02:00
|
|
|
import { DisplayableError } from '../../types/displayable-error';
|
2022-05-13 23:44:16 +02:00
|
|
|
|
2022-05-26 05:38:40 +02:00
|
|
|
const SERVER_STATUS_POLL_DURATION = 5000;
|
|
|
|
const ACCESS_TOKEN_KEY = 'accessToken';
|
|
|
|
|
2022-09-11 03:08:51 +02:00
|
|
|
let serverStatusRefreshPoll: ReturnType<typeof setInterval>;
|
|
|
|
|
2022-05-13 23:44:16 +02:00
|
|
|
// Server status is what gets updated such as viewer count, durations,
|
|
|
|
// stream title, online/offline state, etc.
|
|
|
|
export const serverStatusState = atom<ServerStatus>({
|
|
|
|
key: 'serverStatusState',
|
|
|
|
default: makeEmptyServerStatus(),
|
|
|
|
});
|
2022-04-26 08:10:07 +02:00
|
|
|
|
2022-04-28 08:19:20 +02:00
|
|
|
// The config that comes from the API.
|
2022-05-03 02:45:22 +02:00
|
|
|
export const clientConfigStateAtom = atom({
|
2022-04-26 08:10:07 +02:00
|
|
|
key: 'clientConfigState',
|
|
|
|
default: makeEmptyClientConfig(),
|
|
|
|
});
|
|
|
|
|
2022-10-11 01:26:09 +02:00
|
|
|
export const accessTokenAtom = atom<string>({
|
|
|
|
key: 'accessTokenAtom',
|
2022-06-25 06:30:54 +02:00
|
|
|
default: null,
|
|
|
|
});
|
|
|
|
|
2022-10-11 01:26:09 +02:00
|
|
|
export const currentUserAtom = atom<CurrentUser>({
|
|
|
|
key: 'currentUserAtom',
|
2022-04-30 00:09:53 +02:00
|
|
|
default: null,
|
2022-04-28 08:19:20 +02:00
|
|
|
});
|
|
|
|
|
2022-05-03 02:45:22 +02:00
|
|
|
export const chatMessagesAtom = atom<ChatMessage[]>({
|
2022-04-28 23:36:05 +02:00
|
|
|
key: 'chatMessages',
|
|
|
|
default: [] as ChatMessage[],
|
|
|
|
});
|
|
|
|
|
2022-08-21 01:13:31 +02:00
|
|
|
export const chatAuthenticatedAtom = atom<boolean>({
|
|
|
|
key: 'chatAuthenticatedAtom',
|
|
|
|
default: false,
|
|
|
|
});
|
|
|
|
|
2022-05-05 01:55:54 +02:00
|
|
|
export const websocketServiceAtom = atom<WebsocketService>({
|
|
|
|
key: 'websocketServiceAtom',
|
|
|
|
default: null,
|
2022-10-19 01:39:49 +02:00
|
|
|
dangerouslyAllowMutability: true,
|
2022-05-05 01:55:54 +02:00
|
|
|
});
|
|
|
|
|
2022-05-26 05:38:40 +02:00
|
|
|
export const appStateAtom = atom<AppStateOptions>({
|
|
|
|
key: 'appState',
|
|
|
|
default: makeEmptyAppState(),
|
|
|
|
});
|
|
|
|
|
2022-07-03 12:36:30 +02:00
|
|
|
export const isMobileAtom = atom<boolean | undefined>({
|
|
|
|
key: 'isMobileAtom',
|
|
|
|
default: undefined,
|
|
|
|
});
|
|
|
|
|
2022-05-26 05:38:40 +02:00
|
|
|
export const chatVisibleToggleAtom = atom<boolean>({
|
|
|
|
key: 'chatVisibilityToggleAtom',
|
|
|
|
default: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
export const isVideoPlayingAtom = atom<boolean>({
|
|
|
|
key: 'isVideoPlayingAtom',
|
|
|
|
default: false,
|
|
|
|
});
|
|
|
|
|
2022-05-28 07:27:20 +02:00
|
|
|
export const fatalErrorStateAtom = atom<DisplayableError>({
|
|
|
|
key: 'fatalErrorStateAtom',
|
|
|
|
default: null,
|
|
|
|
});
|
|
|
|
|
2022-06-02 23:23:51 +02:00
|
|
|
export const clockSkewAtom = atom<Number>({
|
|
|
|
key: 'clockSkewAtom',
|
|
|
|
default: 0.0,
|
|
|
|
});
|
|
|
|
|
2022-09-05 02:58:06 +02:00
|
|
|
export const removedMessageIdsAtom = atom<string[]>({
|
|
|
|
key: 'removedMessageIds',
|
|
|
|
default: [],
|
|
|
|
});
|
|
|
|
|
2022-05-26 05:38:40 +02:00
|
|
|
// Chat is visible if the user wishes it to be visible AND the required
|
|
|
|
// chat state is set.
|
|
|
|
export const isChatVisibleSelector = selector({
|
|
|
|
key: 'isChatVisibleSelector',
|
|
|
|
get: ({ get }) => {
|
|
|
|
const state: AppStateOptions = get(appStateAtom);
|
|
|
|
const userVisibleToggle: boolean = get(chatVisibleToggleAtom);
|
2022-10-11 01:26:09 +02:00
|
|
|
const accessToken: string = get(accessTokenAtom);
|
2022-05-26 05:38:40 +02:00
|
|
|
return accessToken && state.chatAvailable && userVisibleToggle;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-05-26 20:08:37 +02:00
|
|
|
export const isChatAvailableSelector = selector({
|
|
|
|
key: 'isChatAvailableSelector',
|
|
|
|
get: ({ get }) => {
|
|
|
|
const state: AppStateOptions = get(appStateAtom);
|
2022-10-11 01:26:09 +02:00
|
|
|
const accessToken: string = get(accessTokenAtom);
|
2022-05-26 20:08:37 +02:00
|
|
|
return accessToken && state.chatAvailable;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-05-26 05:38:40 +02:00
|
|
|
// We display in an "online/live" state as long as video is actively playing.
|
|
|
|
// Even during the time where technically the server has said it's no longer
|
|
|
|
// live, however the last few seconds of video playback is still taking place.
|
|
|
|
export const isOnlineSelector = selector({
|
|
|
|
key: 'isOnlineSelector',
|
|
|
|
get: ({ get }) => {
|
|
|
|
const state: AppStateOptions = get(appStateAtom);
|
|
|
|
const isVideoPlaying: boolean = get(isVideoPlayingAtom);
|
|
|
|
return state.videoAvailable || isVideoPlaying;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-09-05 02:58:06 +02:00
|
|
|
export const visibleChatMessagesSelector = selector<ChatMessage[]>({
|
|
|
|
key: 'visibleChatMessagesSelector',
|
|
|
|
get: ({ get }) => {
|
|
|
|
const messages: ChatMessage[] = get(chatMessagesAtom);
|
|
|
|
const removedIds: string[] = get(removedMessageIdsAtom);
|
|
|
|
return messages.filter(message => !removedIds.includes(message.id));
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-09-11 00:37:07 +02:00
|
|
|
export const ClientConfigStore: FC = () => {
|
2022-11-02 08:01:21 +01:00
|
|
|
const [, appStateSend, appStateService] = useMachine(appStateModel);
|
2022-10-11 01:26:09 +02:00
|
|
|
const [currentUser, setCurrentUser] = useRecoilState(currentUserAtom);
|
2022-08-21 01:13:31 +02:00
|
|
|
const setChatAuthenticated = useSetRecoilState<boolean>(chatAuthenticatedAtom);
|
2022-09-11 05:03:58 +02:00
|
|
|
const [clientConfig, setClientConfig] = useRecoilState<ClientConfig>(clientConfigStateAtom);
|
2022-11-13 19:47:28 +01:00
|
|
|
const [serverStatus, setServerStatus] = useRecoilState<ServerStatus>(serverStatusState);
|
2022-06-02 23:23:51 +02:00
|
|
|
const setClockSkew = useSetRecoilState<Number>(clockSkewAtom);
|
2022-05-03 22:01:50 +02:00
|
|
|
const [chatMessages, setChatMessages] = useRecoilState<ChatMessage[]>(chatMessagesAtom);
|
2022-05-03 07:13:36 +02:00
|
|
|
const [accessToken, setAccessToken] = useRecoilState<string>(accessTokenAtom);
|
2022-05-26 05:38:40 +02:00
|
|
|
const setAppState = useSetRecoilState<AppStateOptions>(appStateAtom);
|
2022-05-28 07:27:20 +02:00
|
|
|
const setGlobalFatalErrorMessage = useSetRecoilState<DisplayableError>(fatalErrorStateAtom);
|
2022-05-26 05:38:40 +02:00
|
|
|
const setWebsocketService = useSetRecoilState<WebsocketService>(websocketServiceAtom);
|
2022-09-05 02:58:06 +02:00
|
|
|
const [hiddenMessageIds, setHiddenMessageIds] = useRecoilState<string[]>(removedMessageIdsAtom);
|
2022-09-11 03:08:51 +02:00
|
|
|
const [hasLoadedStatus, setHasLoadedStatus] = useState(false);
|
|
|
|
const [hasLoadedConfig, setHasLoadedConfig] = useState(false);
|
2022-05-28 07:27:20 +02:00
|
|
|
|
2022-05-05 08:06:35 +02:00
|
|
|
let ws: WebsocketService;
|
2022-04-26 08:10:07 +02:00
|
|
|
|
2022-05-28 07:27:20 +02:00
|
|
|
const setGlobalFatalError = (title: string, message: string) => {
|
|
|
|
setGlobalFatalErrorMessage({
|
|
|
|
title,
|
|
|
|
message,
|
|
|
|
});
|
|
|
|
};
|
2022-05-26 05:38:40 +02:00
|
|
|
const sendEvent = (event: string) => {
|
2022-11-13 19:47:28 +01:00
|
|
|
console.debug('---- sending event:', event);
|
2022-05-26 05:38:40 +02:00
|
|
|
appStateSend({ type: event });
|
|
|
|
};
|
|
|
|
|
2022-04-26 08:10:07 +02:00
|
|
|
const updateClientConfig = async () => {
|
|
|
|
try {
|
|
|
|
const config = await ClientConfigService.getConfig();
|
|
|
|
setClientConfig(config);
|
2022-05-28 07:27:20 +02:00
|
|
|
setGlobalFatalErrorMessage(null);
|
2022-09-11 03:08:51 +02:00
|
|
|
setHasLoadedConfig(true);
|
2022-04-26 08:10:07 +02:00
|
|
|
} catch (error) {
|
2022-05-28 07:27:20 +02:00
|
|
|
setGlobalFatalError(
|
|
|
|
'Unable to reach Owncast server',
|
2022-09-11 03:08:51 +02:00
|
|
|
`Owncast cannot launch. Please make sure the Owncast server is running.`,
|
2022-05-28 07:27:20 +02:00
|
|
|
);
|
2022-04-26 08:10:07 +02:00
|
|
|
console.error(`ClientConfigService -> getConfig() ERROR: \n${error}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-13 23:44:16 +02:00
|
|
|
const updateServerStatus = async () => {
|
|
|
|
try {
|
|
|
|
const status = await ServerStatusService.getStatus();
|
|
|
|
setServerStatus(status);
|
2022-09-11 03:08:51 +02:00
|
|
|
setHasLoadedStatus(true);
|
2022-06-02 23:23:51 +02:00
|
|
|
const { serverTime } = status;
|
|
|
|
|
|
|
|
const clockSkew = new Date(serverTime).getTime() - Date.now();
|
|
|
|
setClockSkew(clockSkew);
|
2022-05-26 05:38:40 +02:00
|
|
|
|
2022-05-28 07:27:20 +02:00
|
|
|
setGlobalFatalErrorMessage(null);
|
2022-05-13 23:44:16 +02:00
|
|
|
} catch (error) {
|
2022-05-26 05:38:40 +02:00
|
|
|
sendEvent(AppStateEvent.Fail);
|
2022-05-28 07:27:20 +02:00
|
|
|
setGlobalFatalError(
|
|
|
|
'Unable to reach Owncast server',
|
2022-10-09 08:15:38 +02:00
|
|
|
`Owncast cannot launch. Please make sure the Owncast server is running.`,
|
2022-05-28 07:27:20 +02:00
|
|
|
);
|
2022-05-13 23:44:16 +02:00
|
|
|
console.error(`serverStatusState -> getStatus() ERROR: \n${error}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-03 02:45:22 +02:00
|
|
|
const handleUserRegistration = async (optionalDisplayName?: string) => {
|
2022-05-26 05:38:40 +02:00
|
|
|
const savedAccessToken = getLocalStorage(ACCESS_TOKEN_KEY);
|
|
|
|
if (savedAccessToken) {
|
|
|
|
setAccessToken(savedAccessToken);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-30 00:09:53 +02:00
|
|
|
try {
|
2022-05-26 05:38:40 +02:00
|
|
|
sendEvent(AppStateEvent.NeedsRegister);
|
2022-04-30 00:09:53 +02:00
|
|
|
const response = await ChatService.registerUser(optionalDisplayName);
|
2022-05-03 02:45:22 +02:00
|
|
|
console.log(`ChatService -> registerUser() response: \n${response}`);
|
2022-08-10 04:56:45 +02:00
|
|
|
const { accessToken: newAccessToken, displayName: newDisplayName, displayColor } = response;
|
2022-04-30 00:09:53 +02:00
|
|
|
if (!newAccessToken) {
|
|
|
|
return;
|
|
|
|
}
|
2022-05-26 05:38:40 +02:00
|
|
|
|
2022-05-03 02:45:22 +02:00
|
|
|
console.log('setting access token', newAccessToken);
|
2022-10-11 01:26:09 +02:00
|
|
|
setCurrentUser({
|
|
|
|
...currentUser,
|
|
|
|
displayName: newDisplayName,
|
|
|
|
displayColor,
|
|
|
|
});
|
2022-05-03 02:45:22 +02:00
|
|
|
setAccessToken(newAccessToken);
|
2022-05-26 05:38:40 +02:00
|
|
|
setLocalStorage(ACCESS_TOKEN_KEY, newAccessToken);
|
2022-04-30 00:09:53 +02:00
|
|
|
} catch (e) {
|
2022-05-26 05:38:40 +02:00
|
|
|
sendEvent(AppStateEvent.Fail);
|
2022-04-30 00:09:53 +02:00
|
|
|
console.error(`ChatService -> registerUser() ERROR: \n${e}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-26 07:51:17 +02:00
|
|
|
const resetAndReAuth = () => {
|
|
|
|
setLocalStorage(ACCESS_TOKEN_KEY, '');
|
2022-10-11 01:26:09 +02:00
|
|
|
setAccessToken(null);
|
2022-05-26 07:51:17 +02:00
|
|
|
handleUserRegistration();
|
|
|
|
};
|
|
|
|
|
2022-09-05 02:58:06 +02:00
|
|
|
const handleMessageVisibilityChange = (message: MessageVisibilityEvent) => {
|
|
|
|
const { ids, visible } = message;
|
|
|
|
if (visible) {
|
|
|
|
const updatedIds = hiddenMessageIds.filter(id => !ids.includes(id));
|
|
|
|
setHiddenMessageIds(updatedIds);
|
|
|
|
} else {
|
|
|
|
const updatedIds = [...hiddenMessageIds, ...ids];
|
|
|
|
setHiddenMessageIds(updatedIds);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-03 07:13:36 +02:00
|
|
|
const handleMessage = (message: SocketEvent) => {
|
|
|
|
switch (message.type) {
|
2022-05-26 07:51:17 +02:00
|
|
|
case MessageType.ERROR_NEEDS_REGISTRATION:
|
|
|
|
resetAndReAuth();
|
|
|
|
break;
|
2022-05-03 23:17:05 +02:00
|
|
|
case MessageType.CONNECTED_USER_INFO:
|
2022-06-25 06:30:54 +02:00
|
|
|
handleConnectedClientInfoMessage(
|
|
|
|
message as ConnectedClientInfoEvent,
|
2022-08-21 01:13:31 +02:00
|
|
|
setChatAuthenticated,
|
2022-10-11 01:26:09 +02:00
|
|
|
setCurrentUser,
|
2022-06-25 06:30:54 +02:00
|
|
|
);
|
2022-07-15 05:36:47 +02:00
|
|
|
setChatMessages(currentState => [...currentState, message as ChatEvent]);
|
2022-05-03 07:13:36 +02:00
|
|
|
break;
|
2022-05-03 23:17:05 +02:00
|
|
|
case MessageType.CHAT:
|
2022-06-29 20:50:56 +02:00
|
|
|
setChatMessages(currentState => [...currentState, message as ChatEvent]);
|
2022-05-03 07:13:36 +02:00
|
|
|
break;
|
2022-05-26 22:52:04 +02:00
|
|
|
case MessageType.NAME_CHANGE:
|
|
|
|
handleNameChangeEvent(message as ChatEvent, chatMessages, setChatMessages);
|
|
|
|
break;
|
2022-07-15 06:05:34 +02:00
|
|
|
case MessageType.USER_JOINED:
|
|
|
|
setChatMessages(currentState => [...currentState, message as ChatEvent]);
|
|
|
|
break;
|
2022-08-11 05:22:00 +02:00
|
|
|
case MessageType.SYSTEM:
|
|
|
|
setChatMessages(currentState => [...currentState, message as ChatEvent]);
|
|
|
|
break;
|
2022-10-19 04:44:42 +02:00
|
|
|
case MessageType.CHAT_ACTION:
|
|
|
|
setChatMessages(currentState => [...currentState, message as ChatEvent]);
|
|
|
|
break;
|
2022-09-05 02:58:06 +02:00
|
|
|
case MessageType.VISIBILITY_UPDATE:
|
|
|
|
handleMessageVisibilityChange(message as MessageVisibilityEvent);
|
|
|
|
break;
|
2022-05-03 07:13:36 +02:00
|
|
|
default:
|
|
|
|
console.error('Unknown socket message type: ', message.type);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-30 00:09:53 +02:00
|
|
|
const getChatHistory = async () => {
|
|
|
|
try {
|
|
|
|
const messages = await ChatService.getChatHistory(accessToken);
|
2022-06-29 20:50:56 +02:00
|
|
|
setChatMessages(currentState => [...currentState, ...messages]);
|
2022-04-30 00:09:53 +02:00
|
|
|
} catch (error) {
|
|
|
|
console.error(`ChatService -> getChatHistory() ERROR: \n${error}`);
|
|
|
|
}
|
2022-05-03 07:13:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const startChat = async () => {
|
|
|
|
try {
|
2022-10-19 05:40:57 +02:00
|
|
|
const { socketHostOverride } = clientConfig;
|
|
|
|
const host = socketHostOverride || window.location.toString();
|
|
|
|
ws = new WebsocketService(accessToken, '/ws', host);
|
2022-05-05 01:55:54 +02:00
|
|
|
ws.handleMessage = handleMessage;
|
|
|
|
setWebsocketService(ws);
|
2022-05-03 07:13:36 +02:00
|
|
|
} catch (error) {
|
|
|
|
console.error(`ChatService -> startChat() ERROR: \n${error}`);
|
|
|
|
}
|
2022-04-30 00:09:53 +02:00
|
|
|
};
|
|
|
|
|
2022-10-19 04:21:03 +02:00
|
|
|
const handleChatNotification = () => {};
|
|
|
|
|
2022-09-11 00:37:07 +02:00
|
|
|
// 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.
|
|
|
|
useEffect(() => {
|
|
|
|
try {
|
|
|
|
if ((window as any).configHydration) {
|
|
|
|
const config = JSON.parse((window as any).configHydration);
|
|
|
|
setClientConfig(config);
|
2022-10-25 06:26:03 +02:00
|
|
|
setHasLoadedConfig(true);
|
2022-09-11 00:37:07 +02:00
|
|
|
}
|
|
|
|
} catch (e) {
|
2022-10-19 04:21:03 +02:00
|
|
|
console.error('Error parsing config hydration', e);
|
2022-09-11 00:37:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
if ((window as any).statusHydration) {
|
|
|
|
const status = JSON.parse((window as any).statusHydration);
|
|
|
|
setServerStatus(status);
|
2022-10-25 06:26:03 +02:00
|
|
|
setHasLoadedStatus(true);
|
2022-09-11 00:37:07 +02:00
|
|
|
}
|
|
|
|
} catch (e) {
|
2022-10-19 04:21:03 +02:00
|
|
|
console.error('error parsing status hydration', e);
|
2022-09-11 00:37:07 +02:00
|
|
|
}
|
|
|
|
}, []);
|
|
|
|
|
2022-09-11 03:08:51 +02:00
|
|
|
useEffect(() => {
|
|
|
|
if (hasLoadedStatus && hasLoadedConfig) {
|
|
|
|
sendEvent(AppStateEvent.Loaded);
|
|
|
|
}
|
|
|
|
}, [hasLoadedStatus, hasLoadedConfig]);
|
|
|
|
|
2022-11-13 23:50:20 +01:00
|
|
|
useEffect(() => {
|
|
|
|
if (serverStatus.online) {
|
|
|
|
sendEvent(AppStateEvent.Online);
|
|
|
|
} else {
|
|
|
|
sendEvent(AppStateEvent.Offline);
|
|
|
|
}
|
|
|
|
}, [serverStatus]);
|
|
|
|
|
2022-09-11 05:03:58 +02:00
|
|
|
useEffect(() => {
|
|
|
|
if (!clientConfig.chatDisabled && accessToken && hasLoadedConfig) {
|
|
|
|
startChat();
|
|
|
|
}
|
|
|
|
}, [hasLoadedConfig, accessToken]);
|
|
|
|
|
2022-10-19 04:21:03 +02:00
|
|
|
// Notify about chat activity when backgrounded.
|
|
|
|
useEffect(() => {
|
|
|
|
handleChatNotification();
|
|
|
|
}, [chatMessages]);
|
|
|
|
|
2022-04-26 08:10:07 +02:00
|
|
|
useEffect(() => {
|
|
|
|
updateClientConfig();
|
2022-04-30 00:09:53 +02:00
|
|
|
handleUserRegistration();
|
2022-05-26 05:38:40 +02:00
|
|
|
updateServerStatus();
|
2022-09-11 03:08:51 +02:00
|
|
|
|
|
|
|
clearInterval(serverStatusRefreshPoll);
|
|
|
|
serverStatusRefreshPoll = setInterval(() => {
|
2022-05-13 23:44:16 +02:00
|
|
|
updateServerStatus();
|
2022-05-26 05:38:40 +02:00
|
|
|
}, SERVER_STATUS_POLL_DURATION);
|
2022-11-02 08:01:21 +01:00
|
|
|
|
|
|
|
return () => {
|
|
|
|
clearInterval(serverStatusRefreshPoll);
|
|
|
|
};
|
|
|
|
}, []);
|
2022-05-13 23:44:16 +02:00
|
|
|
|
|
|
|
useEffect(() => {
|
2022-05-03 02:45:22 +02:00
|
|
|
if (!accessToken) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-30 00:09:53 +02:00
|
|
|
getChatHistory();
|
|
|
|
}, [accessToken]);
|
|
|
|
|
2022-05-29 03:43:28 +02:00
|
|
|
useEffect(() => {
|
|
|
|
appStateService.onTransition(state => {
|
|
|
|
if (!state.changed) {
|
|
|
|
return;
|
|
|
|
}
|
2022-05-26 05:38:40 +02:00
|
|
|
|
2022-05-29 03:43:28 +02:00
|
|
|
const metadata = mergeMeta(state.meta) as AppStateOptions;
|
2022-05-26 05:38:40 +02:00
|
|
|
|
2022-08-23 04:23:06 +02:00
|
|
|
// console.debug('--- APP STATE: ', state.value);
|
|
|
|
// console.debug('--- APP META: ', metadata);
|
2022-05-26 05:38:40 +02:00
|
|
|
|
2022-05-29 03:43:28 +02:00
|
|
|
setAppState(metadata);
|
|
|
|
});
|
2022-11-02 08:01:21 +01:00
|
|
|
}, []);
|
2022-05-03 02:45:22 +02:00
|
|
|
|
2022-04-26 08:10:07 +02:00
|
|
|
return null;
|
2022-09-07 09:00:28 +02:00
|
|
|
};
|