UNPKG

@selfcommunity/react-ui

Version:

React UI Components to integrate a Community created with SelfCommunity Platform.

192 lines (191 loc) • 8.87 kB
import { useCallback, useEffect, useReducer, useRef } from 'react'; import { LiveStreamApiClient } from '@selfcommunity/api-services'; import { useDisconnectButton, useParticipants } from '@livekit/components-react'; import { useLiveStream } from './LiveStreamProvider'; import { useSnackbar } from 'notistack'; import { useIntl } from 'react-intl'; import { useSCUser } from '@selfcommunity/react-core'; import { LIVE_CHECKING_INITIAL_DELAY_GUEST, LIVE_CHECKING_INITIAL_DELAY_HOST, LIVE_CHECKING_INTERVAL, WARNING_THRESHOLD_EXPIRING_SOON } from '../constants'; import { RoomEvent } from 'livekit-client'; const _INITIAL_STATE = { checkStarted: false, timeRemaining: 60, isExpiringSoonAloneInRoom: false, isExpiringSoonMissingHost: false, isExpiringSoonFewMinutesRemaining: false, isExpiredSoonAloneInRoom: false, isExpiredSoonMissingHost: false, isExpiredSoonFewMinutesRemaining: false }; const reducer = (state, action) => { switch (action.type) { case 'startChecking': return Object.assign(Object.assign({}, _INITIAL_STATE), { checkStarted: true }); case 'stopChecking': return Object.assign(Object.assign({}, _INITIAL_STATE), { checkStarted: false }); case 'reset': return Object.assign({}, _INITIAL_STATE); case 'isExpiringSoonAloneInRoom': return Object.assign(Object.assign({}, state), { isExpiringSoonAloneInRoom: action.value }); case 'isExpiringSoonMissingHost': return Object.assign(Object.assign({}, state), { isExpiringSoonMissingHost: action.value }); case 'isExpiringSoonFewMinutesRemaining': return Object.assign(Object.assign({}, state), { isExpiringSoonFewMinutesRemaining: action.value }); case 'isExpiredAloneInRoom': return Object.assign(Object.assign({}, state), { isExpiredAloneInRoom: action.value }); case 'isExpiredSoonMissingHost': return Object.assign(Object.assign({}, state), { isExpiredSoonMissingHost: action.value }); case 'isExpiredSoonFewMinutesRemaining': return Object.assign(Object.assign({}, state), { isExpiredSoonFewMinutesRemaining: action.value }); case 'timeRemaining': return Object.assign(Object.assign({}, state), { timeRemaining: action.value }); default: return Object.assign(Object.assign({}, state), { [action.type]: action.value }); } }; /** * Custom hook for monitoring livestream. * @param {number} warningThreshold * @param showWarnings * @param performDisconnect */ export function useLivestreamCheck(warningThreshold = WARNING_THRESHOLD_EXPIRING_SOON, showWarnings = true, performDisconnect = true) { // STATE const [state, dispatch] = useReducer(reducer, _INITIAL_STATE); const intervalRef = useRef(null); // HOOKS const scUserContext = useSCUser(); const participants = useParticipants({ updateOnlyOn: [ RoomEvent.ParticipantConnected, RoomEvent.ParticipantDisconnected, RoomEvent.ConnectionStateChanged, RoomEvent.Connected, RoomEvent.Disconnected, RoomEvent.TrackSubscribed, RoomEvent.TrackUnsubscribed ] }); const { liveStream } = useLiveStream(); const { buttonProps } = useDisconnectButton({}); const { enqueueSnackbar } = useSnackbar(); const __DEBUG = useRef(true); // INTL const intl = useIntl(); /** * fetchLivestreamStatus */ const fetchLivestreamStatus = () => { LiveStreamApiClient.getMonthlyDuration() .then((r) => { dispatch({ type: 'timeRemaining', value: r.remaining_minutes }); if (r.remaining_minutes > 0 && r.remaining_minutes <= warningThreshold) { if (!state.isExpiringSoonFewMinutesRemaining && !state.isExpiredSoonFewMinutesRemaining && liveStream.host.id === scUserContext.user.id && showWarnings) { __DEBUG && console.log('Warning: '); enqueueSnackbar(intl.formatMessage({ id: 'ui.liveStreamRoom.check.fewMinutesRemaining', defaultMessage: 'ui.liveStreamRoom.check.fewMinutesRemaining' }), { variant: 'warning', autoHideDuration: 30000 }); dispatch({ type: 'isExpiringSoonFewMinutesRemaining', value: true }); } } else if (r.remaining_minutes <= 0) { __DEBUG && console.log('Livestream expired'); dispatch({ type: 'isExpiredFewMinutesRemaining', value: true }); } else if (state.isExpiredFewMinutesRemaining) { dispatch({ type: 'isExpiringSoonFewMinutesRemaining', value: false }); } }) .catch((error) => { console.error('Error fetching live status:', error); }); }; /** * Check live */ const check = useCallback(() => { if (__DEBUG) { console.log('Checking live status'); console.log('Status: ', state); console.log('Checking participants...', participants.length); console.log(participants); } if (participants.length <= 1) { if (!state.isExpiringSoonAloneInRoom && !state.isExpiredAloneInRoom && showWarnings) { __DEBUG && console.log('Set expire soon: you are alone in the room'); enqueueSnackbar(intl.formatMessage({ id: 'ui.liveStreamRoom.check.youAreAloneInTheRoom', defaultMessage: 'ui.liveStreamRoom.check.youAreAloneInTheRoom' }), { variant: 'warning', autoHideDuration: 10000 }); state.isExpiringSoonAloneInRoom ? dispatch({ type: 'isExpiredAloneInRoom', value: true }) : dispatch({ type: 'isExpiringSoonAloneInRoom', value: true }); } else if (performDisconnect && (state.isExpiringSoonAloneInRoom || state.isExpiredAloneInRoom)) { // Leave the room __DEBUG && console.log('Leave the room: no participants'); buttonProps.onClick(); } return; } else if (state.isExpiringSoonAloneInRoom) { __DEBUG && console.log('Reset expire soon'); dispatch({ type: 'isExpiringSoonAloneInRoom', value: false }); } __DEBUG && console.log('Checking live speaker...'); const speaker = participants.find((pt) => { return pt.name === liveStream.host.username; }); if (!speaker) { if (!state.isExpiredSoonMissingHost && !state.isExpiringSoonMissingHost && liveStream.host.id !== scUserContext.user.id && showWarnings) { enqueueSnackbar(intl.formatMessage({ id: 'ui.liveStreamRoom.check.hostMissing', defaultMessage: 'ui.liveStreamRoom.check.hostMissing' }), { variant: 'warning', autoHideDuration: 10000 }); state.isExpiringSoonMissingHost ? dispatch({ type: 'isExpiredSoonMissingHost', value: true }) : dispatch({ type: 'isExpiringSoonMissingHost', value: true }); } else if (performDisconnect && (state.isExpiredSoonMissingHost || state.isExpiringSoonMissingHost)) { // Leave the room __DEBUG && console.log('Leave the room: no host'); buttonProps.onClick(); } } else if (state.isExpiringSoonMissingHost) { dispatch({ type: 'isExpiringSoonMissingHost', value: false }); } __DEBUG && console.log('Checking live status resources...'); fetchLivestreamStatus(); }, [state, buttonProps, participants]); /** * Check live status */ useEffect(() => { if (state.checkStarted) { intervalRef.current = setInterval(check, LIVE_CHECKING_INTERVAL * 60000); } return () => { intervalRef.current && clearInterval(intervalRef.current); }; }, [state, participants]); /** * Start the checking after a delay */ useEffect(() => { let _timeout; if (liveStream) { _timeout = setTimeout(() => { // Start the checking after 5 minutes dispatch({ type: 'startChecking' }); __DEBUG && console.log('Start checking'); }, (liveStream.host.id === scUserContext.user.id ? LIVE_CHECKING_INITIAL_DELAY_HOST : LIVE_CHECKING_INITIAL_DELAY_GUEST) * 60000); } return () => { _timeout && clearTimeout(_timeout); dispatch({ type: 'stopChecking' }); }; }, [liveStream]); return state; }