2023-02-27 01:54:28 +01:00
import { FC , useContext , useEffect , useState } from 'react' ;
2022-11-13 16:03:37 -08:00
import { atom , selector , useRecoilState , useSetRecoilState , RecoilEnv } from 'recoil' ;
2022-05-25 20:38:40 -07:00
import { useMachine } from '@xstate/react' ;
2022-04-27 23:19:20 -07:00
import { makeEmptyClientConfig , ClientConfig } from '../../interfaces/client-config.model' ;
2023-02-27 01:54:28 +01:00
import { ClientConfigServiceContext } from '../../services/client-config-service' ;
import { ChatServiceContext } from '../../services/chat-service' ;
2022-05-02 17:45:22 -07:00
import WebsocketService from '../../services/websocket-service' ;
2022-04-28 14:36:05 -07:00
import { ChatMessage } from '../../interfaces/chat-message.model' ;
2022-10-10 16:26:09 -07:00
import { CurrentUser } from '../../interfaces/current-user' ;
2022-05-13 14:44:16 -07:00
import { ServerStatus , makeEmptyServerStatus } from '../../interfaces/server-status.model' ;
2022-05-25 20:38:40 -07:00
import appStateModel , {
AppStateEvent ,
AppStateOptions ,
makeEmptyAppState ,
} from './application-state' ;
2022-05-26 13:52:04 -07:00
import { setLocalStorage , getLocalStorage } from '../../utils/localStorage' ;
2022-05-02 22:13:36 -07:00
import {
ConnectedClientInfoEvent ,
2022-05-03 14:17:05 -07:00
MessageType ,
2022-05-02 22:13:36 -07:00
ChatEvent ,
2022-09-04 17:58:06 -07:00
MessageVisibilityEvent ,
2022-05-11 23:31:31 -07:00
SocketEvent ,
2023-02-05 19:58:24 -08:00
FediverseEvent ,
2022-05-02 22:13:36 -07:00
} from '../../interfaces/socket-events' ;
2022-10-18 20:40:57 -07:00
import { mergeMeta } from '../../utils/helpers' ;
2022-05-25 20:38:40 -07:00
import handleConnectedClientInfoMessage from './eventhandlers/connected-client-info-handler' ;
2023-02-27 01:54:28 +01:00
import { ServerStatusServiceContext } from '../../services/status-service' ;
2022-05-26 13:52:04 -07:00
import handleNameChangeEvent from './eventhandlers/handleNameChangeEvent' ;
2022-05-27 22:27:20 -07:00
import { DisplayableError } from '../../types/displayable-error' ;
2022-05-13 14:44:16 -07:00
2022-11-13 16:03:37 -08:00
RecoilEnv . RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED = false ;
2022-05-25 20:38:40 -07:00
const SERVER_STATUS_POLL_DURATION = 5000 ;
const ACCESS_TOKEN_KEY = 'accessToken' ;
2022-09-10 18:08:51 -07:00
let serverStatusRefreshPoll : ReturnType < typeof setInterval > ;
2023-01-13 20:53:10 -08:00
let hasBeenModeratorNotified = false ;
2023-03-13 15:23:14 -07:00
let hasWebsocketDisconnected = false ;
2022-09-10 18:08:51 -07:00
2023-03-13 15:23:14 -07:00
const serverConnectivityError = ` Cannot connect to the Owncast service. Please check your internet connection and verify this Owncast server is running. ` ;
2023-01-17 19:21:24 -08:00
2022-05-13 14:44:16 -07: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-25 23:10:07 -07:00
2022-04-27 23:19:20 -07:00
// The config that comes from the API.
2022-05-02 17:45:22 -07:00
export const clientConfigStateAtom = atom ( {
2022-04-25 23:10:07 -07:00
key : 'clientConfigState' ,
default : makeEmptyClientConfig ( ) ,
} ) ;
2022-10-10 16:26:09 -07:00
export const accessTokenAtom = atom < string > ( {
key : 'accessTokenAtom' ,
2022-06-24 21:30:54 -07:00
default : null ,
} ) ;
2022-10-10 16:26:09 -07:00
export const currentUserAtom = atom < CurrentUser > ( {
key : 'currentUserAtom' ,
2022-04-29 15:09:53 -07:00
default : null ,
2022-04-27 23:19:20 -07:00
} ) ;
2022-05-02 17:45:22 -07:00
export const chatMessagesAtom = atom < ChatMessage [ ] > ( {
2022-04-28 14:36:05 -07:00
key : 'chatMessages' ,
default : [ ] as ChatMessage [ ] ,
} ) ;
2022-08-20 16:13:31 -07:00
export const chatAuthenticatedAtom = atom < boolean > ( {
key : 'chatAuthenticatedAtom' ,
default : false ,
} ) ;
2022-05-04 16:55:54 -07:00
export const websocketServiceAtom = atom < WebsocketService > ( {
key : 'websocketServiceAtom' ,
default : null ,
2022-10-18 16:39:49 -07:00
dangerouslyAllowMutability : true ,
2022-05-04 16:55:54 -07:00
} ) ;
2022-05-25 20:38:40 -07: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-25 20:38:40 -07:00
export const chatVisibleToggleAtom = atom < boolean > ( {
key : 'chatVisibilityToggleAtom' ,
default : true ,
} ) ;
export const isVideoPlayingAtom = atom < boolean > ( {
key : 'isVideoPlayingAtom' ,
default : false ,
} ) ;
2022-05-27 22:27:20 -07:00
export const fatalErrorStateAtom = atom < DisplayableError > ( {
key : 'fatalErrorStateAtom' ,
default : null ,
} ) ;
2022-06-02 14:23:51 -07:00
export const clockSkewAtom = atom < Number > ( {
key : 'clockSkewAtom' ,
default : 0.0 ,
} ) ;
2022-09-04 17:58:06 -07:00
export const removedMessageIdsAtom = atom < string [ ] > ( {
key : 'removedMessageIds' ,
default : [ ] ,
} ) ;
2023-03-13 15:23:14 -07:00
export const isChatAvailableSelector = selector ( {
key : 'isChatAvailableSelector' ,
2022-05-25 20:38:40 -07:00
get : ( { get } ) = > {
const state : AppStateOptions = get ( appStateAtom ) ;
2022-10-10 16:26:09 -07:00
const accessToken : string = get ( accessTokenAtom ) ;
2023-03-13 15:23:14 -07:00
return accessToken && state . chatAvailable && ! hasWebsocketDisconnected ;
2022-05-25 20:38:40 -07:00
} ,
} ) ;
2023-03-13 15:23:14 -07: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' ,
2022-05-26 11:08:37 -07:00
get : ( { get } ) = > {
const state : AppStateOptions = get ( appStateAtom ) ;
2023-03-13 15:23:14 -07:00
const userVisibleToggle : boolean = get ( chatVisibleToggleAtom ) ;
return state . chatAvailable && userVisibleToggle && ! hasWebsocketDisconnected ;
2022-05-26 11:08:37 -07:00
} ,
} ) ;
2022-05-25 20:38:40 -07: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-04 17:58:06 -07: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-10 15:37:07 -07:00
export const ClientConfigStore : FC = ( ) = > {
2023-02-27 01:54:28 +01:00
const ClientConfigService = useContext ( ClientConfigServiceContext ) ;
const ChatService = useContext ( ChatServiceContext ) ;
const ServerStatusService = useContext ( ServerStatusServiceContext ) ;
2022-11-13 16:03:37 -08:00
const [ appState , appStateSend , appStateService ] = useMachine ( appStateModel ) ;
2022-10-10 16:26:09 -07:00
const [ currentUser , setCurrentUser ] = useRecoilState ( currentUserAtom ) ;
2022-08-20 16:13:31 -07:00
const setChatAuthenticated = useSetRecoilState < boolean > ( chatAuthenticatedAtom ) ;
2022-09-10 20:03:58 -07:00
const [ clientConfig , setClientConfig ] = useRecoilState < ClientConfig > ( clientConfigStateAtom ) ;
2023-03-13 15:23:14 -07:00
const setServerStatus = useSetRecoilState < ServerStatus > ( serverStatusState ) ;
2022-06-02 14:23:51 -07:00
const setClockSkew = useSetRecoilState < Number > ( clockSkewAtom ) ;
2023-03-13 15:23:14 -07:00
const setChatMessages = useSetRecoilState < SocketEvent [ ] > ( chatMessagesAtom ) ;
2022-05-02 22:13:36 -07:00
const [ accessToken , setAccessToken ] = useRecoilState < string > ( accessTokenAtom ) ;
2022-05-25 20:38:40 -07:00
const setAppState = useSetRecoilState < AppStateOptions > ( appStateAtom ) ;
2022-05-27 22:27:20 -07:00
const setGlobalFatalErrorMessage = useSetRecoilState < DisplayableError > ( fatalErrorStateAtom ) ;
2022-05-25 20:38:40 -07:00
const setWebsocketService = useSetRecoilState < WebsocketService > ( websocketServiceAtom ) ;
2022-09-04 17:58:06 -07:00
const [ hiddenMessageIds , setHiddenMessageIds ] = useRecoilState < string [ ] > ( removedMessageIdsAtom ) ;
2022-09-10 18:08:51 -07:00
const [ hasLoadedConfig , setHasLoadedConfig ] = useState ( false ) ;
2022-05-27 22:27:20 -07:00
2022-05-04 23:06:35 -07:00
let ws : WebsocketService ;
2022-04-25 23:10:07 -07:00
2022-05-27 22:27:20 -07:00
const setGlobalFatalError = ( title : string , message : string ) = > {
setGlobalFatalErrorMessage ( {
title ,
message ,
} ) ;
} ;
2023-01-31 23:28:05 -08:00
const sendEvent = ( events : string [ ] ) = > {
2022-11-13 16:03:37 -08:00
// console.debug('---- sending event:', event);
2023-01-31 23:28:05 -08:00
appStateSend ( events ) ;
2022-05-25 20:38:40 -07:00
} ;
2022-11-13 16:03:37 -08:00
const handleStatusChange = ( status : ServerStatus ) = > {
if ( appState . matches ( 'loading' ) ) {
2023-01-31 23:28:05 -08:00
const events = [ AppStateEvent . Loaded ] ;
if ( status . online ) {
events . push ( AppStateEvent . Online ) ;
} else {
events . push ( AppStateEvent . Offline ) ;
}
sendEvent ( events ) ;
2022-11-13 16:03:37 -08:00
return ;
}
2022-11-20 13:27:37 -08:00
if ( status . online && appState . matches ( 'ready' ) ) {
2023-01-31 23:28:05 -08:00
sendEvent ( [ AppStateEvent . Online ] ) ;
2022-11-13 16:03:37 -08:00
} else if ( ! status . online && ! appState . matches ( 'ready.offline' ) ) {
2023-01-31 23:28:05 -08:00
sendEvent ( [ AppStateEvent . Offline ] ) ;
2022-11-13 16:03:37 -08:00
}
} ;
2022-04-25 23:10:07 -07:00
const updateClientConfig = async ( ) = > {
try {
const config = await ClientConfigService . getConfig ( ) ;
setClientConfig ( config ) ;
2022-05-27 22:27:20 -07:00
setGlobalFatalErrorMessage ( null ) ;
2022-09-10 18:08:51 -07:00
setHasLoadedConfig ( true ) ;
2022-04-25 23:10:07 -07:00
} catch ( error ) {
2023-01-17 19:21:24 -08:00
setGlobalFatalError ( 'Unable to reach Owncast server' , serverConnectivityError ) ;
2023-02-27 01:54:28 +01:00
console . error ( ` ClientConfigService -> getConfig() ERROR: \ n ` , error ) ;
2022-04-25 23:10:07 -07:00
}
} ;
2022-05-13 14:44:16 -07:00
const updateServerStatus = async ( ) = > {
try {
const status = await ServerStatusService . getStatus ( ) ;
2023-01-31 23:28:05 -08:00
handleStatusChange ( status ) ;
2022-05-13 14:44:16 -07:00
setServerStatus ( status ) ;
2023-01-31 23:28:05 -08:00
2022-06-02 14:23:51 -07:00
const { serverTime } = status ;
const clockSkew = new Date ( serverTime ) . getTime ( ) - Date . now ( ) ;
setClockSkew ( clockSkew ) ;
2022-05-25 20:38:40 -07:00
2022-05-27 22:27:20 -07:00
setGlobalFatalErrorMessage ( null ) ;
2022-05-13 14:44:16 -07:00
} catch ( error ) {
2023-01-31 23:28:05 -08:00
sendEvent ( [ AppStateEvent . Fail ] ) ;
2023-01-17 19:21:24 -08:00
setGlobalFatalError ( 'Unable to reach Owncast server' , serverConnectivityError ) ;
2023-02-27 01:54:28 +01:00
console . error ( ` serverStatusState -> getStatus() ERROR: \ n ` , error ) ;
2022-05-13 14:44:16 -07:00
}
} ;
2022-05-02 17:45:22 -07:00
const handleUserRegistration = async ( optionalDisplayName? : string ) = > {
2022-05-25 20:38:40 -07:00
const savedAccessToken = getLocalStorage ( ACCESS_TOKEN_KEY ) ;
if ( savedAccessToken ) {
setAccessToken ( savedAccessToken ) ;
2023-03-31 21:00:56 -07:00
2022-05-25 20:38:40 -07:00
return ;
}
2022-04-29 15:09:53 -07:00
try {
2023-01-31 23:28:05 -08:00
sendEvent ( [ AppStateEvent . NeedsRegister ] ) ;
2022-04-29 15:09:53 -07:00
const response = await ChatService . registerUser ( optionalDisplayName ) ;
2022-08-09 19:56:45 -07:00
const { accessToken : newAccessToken , displayName : newDisplayName , displayColor } = response ;
2022-04-29 15:09:53 -07:00
if ( ! newAccessToken ) {
return ;
}
2022-05-25 20:38:40 -07:00
2022-10-10 16:26:09 -07:00
setCurrentUser ( {
. . . currentUser ,
displayName : newDisplayName ,
displayColor ,
} ) ;
2022-05-02 17:45:22 -07:00
setAccessToken ( newAccessToken ) ;
2022-05-25 20:38:40 -07:00
setLocalStorage ( ACCESS_TOKEN_KEY , newAccessToken ) ;
2022-04-29 15:09:53 -07:00
} catch ( e ) {
2023-01-31 23:28:05 -08:00
sendEvent ( [ AppStateEvent . Fail ] ) ;
2022-04-29 15:09:53 -07:00
console . error ( ` ChatService -> registerUser() ERROR: \ n ${ e } ` ) ;
}
} ;
2022-05-25 22:51:17 -07:00
const resetAndReAuth = ( ) = > {
setLocalStorage ( ACCESS_TOKEN_KEY , '' ) ;
2022-10-10 16:26:09 -07:00
setAccessToken ( null ) ;
2023-03-31 21:00:56 -07:00
ws ? . shutdown ( ) ;
2022-05-25 22:51:17 -07:00
handleUserRegistration ( ) ;
} ;
2022-09-04 17:58:06 -07: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 ) ;
}
} ;
2023-03-13 15:23:14 -07:00
const handleSocketDisconnect = ( ) = > {
hasWebsocketDisconnected = true ;
} ;
const handleSocketConnected = ( ) = > {
hasWebsocketDisconnected = false ;
} ;
2022-05-02 22:13:36 -07:00
const handleMessage = ( message : SocketEvent ) = > {
switch ( message . type ) {
2022-05-25 22:51:17 -07:00
case MessageType . ERROR_NEEDS_REGISTRATION :
resetAndReAuth ( ) ;
break ;
2022-05-03 14:17:05 -07:00
case MessageType . CONNECTED_USER_INFO :
2022-06-24 21:30:54 -07:00
handleConnectedClientInfoMessage (
message as ConnectedClientInfoEvent ,
2022-08-20 16:13:31 -07:00
setChatAuthenticated ,
2022-10-10 16:26:09 -07:00
setCurrentUser ,
2022-06-24 21:30:54 -07:00
) ;
2023-03-03 21:54:01 -08:00
if ( message as ChatEvent ) {
const m = new ChatEvent ( message ) ;
if ( ! hasBeenModeratorNotified && m . user ? . isModerator ( ) ) {
setChatMessages ( currentState = > [ . . . currentState , message as ChatEvent ] ) ;
hasBeenModeratorNotified = true ;
}
2023-01-13 20:53:10 -08:00
}
2023-03-03 21:54:01 -08:00
2022-05-02 22:13:36 -07:00
break ;
2022-05-03 14:17:05 -07:00
case MessageType . CHAT :
2022-06-29 11:50:56 -07:00
setChatMessages ( currentState = > [ . . . currentState , message as ChatEvent ] ) ;
2022-05-02 22:13:36 -07:00
break ;
2022-05-26 13:52:04 -07:00
case MessageType . NAME_CHANGE :
2022-12-29 20:11:20 -08:00
handleNameChangeEvent ( message as ChatEvent , setChatMessages ) ;
2022-05-26 13:52:04 -07:00
break ;
2022-07-14 21:05:34 -07:00
case MessageType . USER_JOINED :
setChatMessages ( currentState = > [ . . . currentState , message as ChatEvent ] ) ;
break ;
2022-08-10 20:22:00 -07:00
case MessageType . SYSTEM :
setChatMessages ( currentState = > [ . . . currentState , message as ChatEvent ] ) ;
break ;
2022-10-18 19:44:42 -07:00
case MessageType . CHAT_ACTION :
setChatMessages ( currentState = > [ . . . currentState , message as ChatEvent ] ) ;
break ;
2023-02-05 19:58:24 -08:00
case MessageType . FEDIVERSE_ENGAGEMENT_FOLLOW :
setChatMessages ( currentState = > [ . . . currentState , message as FediverseEvent ] ) ;
break ;
case MessageType . FEDIVERSE_ENGAGEMENT_LIKE :
setChatMessages ( currentState = > [ . . . currentState , message as FediverseEvent ] ) ;
break ;
case MessageType . FEDIVERSE_ENGAGEMENT_REPOST :
setChatMessages ( currentState = > [ . . . currentState , message as FediverseEvent ] ) ;
break ;
2022-09-04 17:58:06 -07:00
case MessageType . VISIBILITY_UPDATE :
handleMessageVisibilityChange ( message as MessageVisibilityEvent ) ;
break ;
2023-03-13 15:23:14 -07:00
case MessageType . ERROR_USER_DISABLED :
console . log ( 'User has been disabled' ) ;
sendEvent ( [ AppStateEvent . ChatUserDisabled ] ) ;
break ;
2022-05-02 22:13:36 -07:00
default :
console . error ( 'Unknown socket message type: ' , message . type ) ;
}
} ;
2022-04-29 15:09:53 -07:00
const getChatHistory = async ( ) = > {
try {
const messages = await ChatService . getChatHistory ( accessToken ) ;
2023-03-13 15:23:14 -07:00
if ( messages ) {
setChatMessages ( currentState = > [ . . . currentState , . . . messages ] ) ;
}
2022-04-29 15:09:53 -07:00
} catch ( error ) {
console . error ( ` ChatService -> getChatHistory() ERROR: \ n ${ error } ` ) ;
}
2022-05-02 22:13:36 -07:00
} ;
const startChat = async ( ) = > {
try {
2023-03-31 21:00:56 -07:00
if ( ws ) {
ws ? . shutdown ( ) ;
setWebsocketService ( null ) ;
ws = null ;
}
2022-10-18 20:40:57 -07:00
const { socketHostOverride } = clientConfig ;
2023-01-21 23:17:11 -08:00
// Get a copy of the browser location without #fragments.
const l = window . location ;
l . hash = '' ;
const location = l . toString ( ) . replaceAll ( '#' , '' ) ;
const host = socketHostOverride || location ;
2022-10-18 20:40:57 -07:00
ws = new WebsocketService ( accessToken , '/ws' , host ) ;
2022-05-04 16:55:54 -07:00
ws . handleMessage = handleMessage ;
2023-03-13 15:23:14 -07:00
ws . socketDisconnected = handleSocketDisconnect ;
ws . socketConnected = handleSocketConnected ;
2022-05-04 16:55:54 -07:00
setWebsocketService ( ws ) ;
2022-05-02 22:13:36 -07:00
} catch ( error ) {
console . error ( ` ChatService -> startChat() ERROR: \ n ${ error } ` ) ;
2023-03-13 15:23:14 -07:00
sendEvent ( [ AppStateEvent . ChatUserDisabled ] ) ;
2022-05-02 22:13:36 -07:00
}
2022-04-29 15:09:53 -07:00
} ;
2022-09-10 15:37:07 -07: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-24 21:26:03 -07:00
setHasLoadedConfig ( true ) ;
2022-09-10 15:37:07 -07:00
}
} catch ( e ) {
2022-10-18 19:21:03 -07:00
console . error ( 'Error parsing config hydration' , e ) ;
2022-09-10 15:37:07 -07:00
}
try {
if ( ( window as any ) . statusHydration ) {
const status = JSON . parse ( ( window as any ) . statusHydration ) ;
setServerStatus ( status ) ;
2023-01-31 23:28:05 -08:00
handleStatusChange ( status ) ;
2022-09-10 15:37:07 -07:00
}
} catch ( e ) {
2022-10-18 19:21:03 -07:00
console . error ( 'error parsing status hydration' , e ) ;
2022-09-10 15:37:07 -07:00
}
} , [ ] ) ;
2022-09-10 18:08:51 -07:00
useEffect ( ( ) = > {
2023-03-31 21:00:56 -07:00
if ( clientConfig . chatDisabled ) {
return ;
}
if ( ! accessToken ) {
return ;
2022-09-10 20:03:58 -07:00
}
2023-03-31 21:00:56 -07:00
if ( ! hasLoadedConfig ) {
return ;
}
if ( ws ) {
return ;
}
startChat ( ) ;
2022-09-10 20:03:58 -07:00
} , [ hasLoadedConfig , accessToken ] ) ;
2022-04-25 23:10:07 -07:00
useEffect ( ( ) = > {
updateClientConfig ( ) ;
2022-04-29 15:09:53 -07:00
handleUserRegistration ( ) ;
2022-05-25 20:38:40 -07:00
updateServerStatus ( ) ;
2022-09-10 18:08:51 -07:00
clearInterval ( serverStatusRefreshPoll ) ;
serverStatusRefreshPoll = setInterval ( ( ) = > {
2022-05-13 14:44:16 -07:00
updateServerStatus ( ) ;
2022-05-25 20:38:40 -07:00
} , SERVER_STATUS_POLL_DURATION ) ;
2022-11-02 00:01:21 -07:00
return ( ) = > {
clearInterval ( serverStatusRefreshPoll ) ;
} ;
} , [ ] ) ;
2022-05-13 14:44:16 -07:00
useEffect ( ( ) = > {
2023-01-10 16:39:12 -08:00
if ( accessToken ) {
getChatHistory ( ) ;
2022-05-02 17:45:22 -07:00
}
2022-04-29 15:09:53 -07:00
} , [ accessToken ] ) ;
2022-05-28 18:43:28 -07:00
useEffect ( ( ) = > {
appStateService . onTransition ( state = > {
const metadata = mergeMeta ( state . meta ) as AppStateOptions ;
2022-05-25 20:38:40 -07:00
2022-08-22 19:23:06 -07:00
// console.debug('--- APP STATE: ', state.value);
// console.debug('--- APP META: ', metadata);
2022-05-25 20:38:40 -07:00
2022-05-28 18:43:28 -07:00
setAppState ( metadata ) ;
} ) ;
2022-11-02 00:01:21 -07:00
} , [ ] ) ;
2022-05-02 17:45:22 -07:00
2022-04-25 23:10:07 -07:00
return null ;
2022-09-07 09:00:28 +02:00
} ;