From 34e6d765df2e0bfe0872fc0740aa9bbaa76bf60d Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Sat, 23 Mar 2024 09:07:46 +0100 Subject: [PATCH 01/49] init --- package.json | 1 + .../countdowntimer/CountdownTimer.tsx | 315 ++++++++++++++++++ .../countdowntimer/CountdownTimerChip.tsx | 61 ++++ .../participantlist/ParticipantList.tsx | 10 +- src/components/topbar/TopBar.tsx | 2 + .../translated/translatedComponents.tsx | 5 + src/store/actions/countdownTimerActions.tsx | 116 +++++++ src/store/actions/roomActions.tsx | 4 + .../middlewares/countdownTimerMiddleware.tsx | 151 +++++++++ src/store/slices/countdownTimerSlice.tsx | 42 +++ src/store/store.tsx | 4 + tsconfig.json | 1 + yarn.lock | 5 + 13 files changed, 716 insertions(+), 1 deletion(-) create mode 100644 src/components/countdowntimer/CountdownTimer.tsx create mode 100644 src/components/countdowntimer/CountdownTimerChip.tsx create mode 100644 src/store/actions/countdownTimerActions.tsx create mode 100644 src/store/middlewares/countdownTimerMiddleware.tsx create mode 100644 src/store/slices/countdownTimerSlice.tsx diff --git a/package.json b/package.json index 66d9b782..1a55b35c 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "bowser": "^2.11.0", "debug": "^4.3.4", "dompurify": "^3.0.6", + "moment": "^2.29.4", "file-saver": "^2.0.5", "fscreen": "^1.2.0", "hark": "^1.2.3", diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx new file mode 100644 index 00000000..7a4a1357 --- /dev/null +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -0,0 +1,315 @@ +import React, { useEffect, useRef } from 'react'; +import { IconButton, Grid, Switch, TextField, styled } from '@mui/material'; +import { HighlightOff as HighlightOffIcon, Pause as PauseIcon, PlayArrow as PlayArrowIcon } from '@mui/icons-material'; +import { useAppDispatch, useAppSelector } from '../../store/hooks'; +// import { intl } from '../../utils/intlManager'; +import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; +import { countdownTimerActions as countdownTimerSlices } from '../../store/slices/countdownTimerSlice'; + +import moment from 'moment'; + +// const styles = (theme) => +// ({ +// root: +// { +// padding: theme.spacing(1), +// display: 'flex', +// flexWrap: 'wrap', +// marginRight: -theme.spacing(1), +// marginTop: -theme.spacing(1) +// }, +// container: +// { +// marginTop: theme.spacing(1), +// marginRight: theme.spacing(2), +// flexGrow: '1', +// justifyContent: 'space-between' +// }, +// textfield: +// { +// marginTop: theme.spacing(1), +// marginRight: theme.spacing(1), +// flexGrow: '1' +// }, +// button: +// { +// marginTop: theme.spacing(1), +// marginRight: theme.spacing(1), +// flexGrow: '1' +// } + +// }); + +interface CountdownTimerProps { + isEnabled: boolean; + isRunning: boolean; + left: string; + // classes: any; +} + +const Div = styled('div')(({ theme }) => ({ + padding: theme.spacing(1), + display: 'flex', + flexWrap: 'wrap', + marginRight: theme.spacing(1), + marginTop: theme.spacing(1) +})); + +const CountdownTimer = () : JSX.Element => { + // const intl = useIntl(); + const dispatch = useAppDispatch(); + const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); + const isRunning = useAppSelector((state) => state.countdownTimer.isRunning); + const left = useAppSelector((state) => state.countdownTimer.left); + + const inputRef = useRef(null); + + const handleFocus = () => { + + if (inputRef.current) { + inputRef.current.focus(); + } + + const timeout = setTimeout(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, 50); + + return () => { + clearTimeout(timeout); + }; + }; + + let _countdownTimerRef: NodeJS.Timeout; + + useEffect(() => { + // eslint-disable-next-line no-console + console.log(isRunning, left); + + if (isRunning === true) { + + if (_countdownTimerRef === undefined) { + _countdownTimerRef = setInterval(() => { + let leftUnix = moment(`1000-01-01 ${left}`).unix(); + const endUnix = moment('1000-01-01 00:00:00').unix(); + + leftUnix--; + + const leftString = moment.unix(leftUnix).format('HH:mm:ss'); + + dispatch(countdownTimerSlices.setCountdownTimer({ left: leftString })); + + if (leftUnix === endUnix) { + dispatch(countdownTimerActions.stopCountdownTimer()); + } + + }, 1000); + + // eslint-disable-next-line no-console + console.log(_countdownTimerRef); + } + + return () => { + clearInterval(_countdownTimerRef); + }; + } + }, [ isRunning, left ]); + // }, [ isRunning, left ]); + + return ( +
+ + + {/* TextField set time */} + { + dispatch(countdownTimerActions.setCountdownTimer(e.target.value)); + handleFocus(); + }} + onKeyPress={(e) => { + if (left !== '00:00:00') { + if (e.key === 'Enter') { + countdownTimerActions.startCountdownTimer(); + e.preventDefault(); + } + } + }} + /> + {/* /TextField set time */} + + + + {/* Button reset time */} + { + dispatch(countdownTimerActions.setCountdownTimer('00:00:00')); + handleFocus(); + }} + > + + + {/* /Button reset */} + + + {!isRunning ? + + {/* Button start countdown */} + { + dispatch(countdownTimerActions.startCountdownTimer()); + }} + > + + + {/* /Button start countdown */} + + : + + {/* Button stop countdown */} + { + dispatch(countdownTimerActions.stopCountdownTimer()); + handleFocus(); + }} + > + + + {/* /Button stop countdown */} + + } + + {/* Switch toggle show/hide */} + { + dispatch( + isEnabled ? + countdownTimerActions.disableCountdownTimer() + : countdownTimerActions.enableCountdownTimer() + ); + // dispatch(countdownTimerActions.toggleCountdownTimer(!isEnabled)); + handleFocus(); + }} + name='checkedB' + color='error' + size='small' + /> + {/* /Switch toggle show/hide */} + + +
+ ); +}; + +// const mapStateToProps = (state) => ({ +// isEnabled: state.room.countdownTimer.isEnabled, +// isRunning: state.room.countdownTimer.isRunning, +// left: state.room.countdownTimer.left +// }); + +// export default withRoomContext(connect( +// mapStateToProps, +// null, +// null, +// { +// areStatesEqual: (next, prev) => { +// return ( +// prev.isEnabled === next.room.countdownTimer.isEnabled, +// prev.isRunning === next.room.countdownTimer.isRunning, +// prev.left === next.room.countdownTimer.left +// ); +// } +// } +// )(withStyles(styles)(CountdownTimer))); +export default CountdownTimer; \ No newline at end of file diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx new file mode 100644 index 00000000..e6f7f1b5 --- /dev/null +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -0,0 +1,61 @@ +import React, { useRef } from 'react'; +import { Chip, styled } from '@mui/material'; +import { HighlightOff as HighlightOffIcon, Pause as PauseIcon } from '@mui/icons-material'; +import { useAppDispatch, useAppSelector } from '../../store/hooks'; +// import { intl } from '../../utils/intlManager'; +import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; +import { useTheme } from '@mui/material/styles'; + +// const Div = styled('div')(({ theme }) => ({ +// padding: theme.spacing(1), +// marginRight: theme.spacing(1), +// marginTop: theme.spacing(1) +// })); + +const CountdownTimerChip = () : JSX.Element => { + const dispatch = useAppDispatch(); + const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); + // const isRunning = useAppSelector((state) => state.countdownTimer.isRunning); + const left = useAppSelector((state) => state.countdownTimer.left); + const theme = useTheme(); + + return ( + <> + { isEnabled && + + } + + ); +}; + +// const mapStateToProps = (state) => ({ +// isEnabled: state.room.countdownTimer.isEnabled, +// isRunning: state.room.countdownTimer.isRunning, +// left: state.room.countdownTimer.left +// }); + +// export default withRoomContext(connect( +// mapStateToProps, +// null, +// null, +// { +// areStatesEqual: (next, prev) => { +// return ( +// prev.isEnabled === next.room.countdownTimer.isEnabled, +// prev.isRunning === next.room.countdownTimer.isRunning, +// prev.left === next.room.countdownTimer.left +// ); +// } +// } +// )(withStyles(styles)(CountdownTimer))); +export default CountdownTimerChip; \ No newline at end of file diff --git a/src/components/participantlist/ParticipantList.tsx b/src/components/participantlist/ParticipantList.tsx index e5b03df1..9a2726f3 100644 --- a/src/components/participantlist/ParticipantList.tsx +++ b/src/components/participantlist/ParticipantList.tsx @@ -8,13 +8,15 @@ import { breakoutRoomsSelector, inParentRoomSelector, parentParticipantListSelec import { permissions } from '../../utils/roles'; import { breakoutRoomsLabel, - participantsLabel + participantsLabel, + countdownTimerActionsLabel } from '../translated/translatedComponents'; import ListMe from './ListMe'; import ListModerator from './ListModerator'; import ListPeer from './ListPeer'; import BreakoutModerator from '../breakoutrooms/BreakoutModerator'; import ListBreakoutRoom from '../breakoutrooms/ListBreakoutRoom'; +import CountdownTimer from '../countdowntimer/CountdownTimer'; const ParticipantListDiv = styled(Box)(({ theme }) => ({ width: '100%', @@ -37,6 +39,12 @@ const ParticipantList = (): JSX.Element => { return ( + <> + + {countdownTimerActionsLabel()} + + + { isModerator && } { (breakoutsEnabled && (rooms.length > 0 || canCreateRooms)) && <> diff --git a/src/components/topbar/TopBar.tsx b/src/components/topbar/TopBar.tsx index 6393cc6a..ace2bcf2 100644 --- a/src/components/topbar/TopBar.tsx +++ b/src/components/topbar/TopBar.tsx @@ -14,6 +14,7 @@ import LeaveButton from '../textbuttons/LeaveButton'; import { formatDuration } from '../../utils/formatDuration'; import LogoutButton from '../controlbuttons/LogoutButton'; import RecordIcon from '../recordicon/RecordIcon'; +import CountdownTimerChip from '../countdowntimer/CountdownTimerChip'; interface TopBarProps { fullscreenEnabled: boolean; @@ -115,6 +116,7 @@ const TopBar = ({ fullscreenEnabled, fullscreen, onFullscreen }: TopBarProps): R { someoneIsRecording && } { fullscreenEnabled && } + { canLock && } { canPromote && lobbyPeersLength > 0 && } diff --git a/src/components/translated/translatedComponents.tsx b/src/components/translated/translatedComponents.tsx index 3f232414..59a078bf 100644 --- a/src/components/translated/translatedComponents.tsx +++ b/src/components/translated/translatedComponents.tsx @@ -767,3 +767,8 @@ export const roomServerConnectionError = (message: string): string => intl.forma id: 'svc.roomServerConnectionError', defaultMessage: `Room-server: ${message}` }); + +export const countdownTimerActionsLabel = (): string => intl.formatMessage({ + id: 'countdownTimer.actions', + defaultMessage: 'Countdown timer actions' +}); diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx new file mode 100644 index 00000000..6704518a --- /dev/null +++ b/src/store/actions/countdownTimerActions.tsx @@ -0,0 +1,116 @@ +import { Logger } from '../../utils/Logger'; +import { countdownTimerActions } from '../slices/countdownTimerSlice'; +import { AppThunk } from '../store'; + +const logger = new Logger('CountdownTimerActions'); + +/** + * This thunk action sends a chat message. + * + * @param message - Message to send. + * @returns {AppThunk>} Promise. + */ + +export const enableCountdownTimer = (): +AppThunk> => async ( + dispatch, + getState, + { signalingService } +): Promise => { + logger.debug('moderator:enableCountdownTimer()'); + + try { + await signalingService.sendRequest('moderator:enableCountdownTimer'); + + dispatch(countdownTimerActions.enableCountdownTimer()); + } catch (error) { + logger.error('moderator:enableCountdownTimer() [error:"%o"]', error); + } +}; + +export const disableCountdownTimer = (): +AppThunk> => async ( + dispatch, + getState, + { signalingService } +): Promise => { + logger.debug('moderator:disableCountdownTimer()'); + + try { + await signalingService.sendRequest('moderator:disableCountdownTimer'); + + // const peerId = getState().me.id; + + dispatch(countdownTimerActions.disableCountdownTimer()); + } catch (error) { + logger.error('moderator:disableCountdownTimer() [error:"%o"]', error); + } +}; + +// export const toggleCountdownTimer = (isEnabled : boolean): +// AppThunk> => async ( +// dispatch, +// getState, +// { signalingService } +// ): Promise => { +// logger.debug('moderator:toggleCountdownTimer()'); + +// try { +// await signalingService.sendRequest('moderator:toggleCountdownTimer', { isEnabled: isEnabled }); + +// // const peerId = getState().me.id; + +// dispatch(countdownTimerActions.toggleCountdownTimer(isEnabled)); +// } catch (error) { +// logger.error('moderator:toggleCountdownTimer() [error:"%o"]', error); +// } +// }; + +export const setCountdownTimer = (left : string): +AppThunk> => async ( + dispatch, + getState, + { signalingService } +): Promise => { + logger.debug('setCountdownTimer() [left:"%s"]', left); + + try { + await signalingService.sendRequest('moderator:setCountdownTimer', { left }); + + dispatch(countdownTimerActions.setCountdownTimer({ left, isRunning: false })); + } catch (error) { + logger.error('setCountdownTimer() [error:"%o"]', error); + } +}; + +export const startCountdownTimer = (): +AppThunk> => async ( + dispatch, + getState, + { signalingService } +): Promise => { + logger.debug('startCountdownTimer)'); + + try { + await signalingService.sendRequest('moderator:startCountdownTimer'); + + } catch (error) { + logger.error('startCountdownTimer() [error:"%o"]', error); + } +}; + +export const stopCountdownTimer = (): +AppThunk> => async ( + dispatch, + getState, + { signalingService } +): Promise => { + logger.debug('stopCountdownTimer()'); + + try { + await signalingService.sendRequest('moderator:stopCountdownTimer'); + + } catch (error) { + logger.error('stopCountdownTimer() [error:"%o"]', error); + } +}; \ No newline at end of file diff --git a/src/store/actions/roomActions.tsx b/src/store/actions/roomActions.tsx index 0105c66c..8fe66c38 100644 --- a/src/store/actions/roomActions.tsx +++ b/src/store/actions/roomActions.tsx @@ -11,6 +11,7 @@ import { initialRoomSession, roomSessionsActions } from '../slices/roomSessionsS import { getSignalingUrl } from '../../utils/signalingHelpers'; import { getTenantFromFqdn } from './managementActions'; import { Logger } from '../../utils/Logger'; +import { countdownTimerActions } from '../slices/countdownTimerSlice'; const logger = new Logger('RoomActions'); @@ -62,6 +63,7 @@ export const joinRoom = (): AppThunk> => async ( tracker, chatHistory, fileHistory, + countdownTimer, breakoutRooms, locked, lobbyPeers, @@ -80,6 +82,8 @@ export const joinRoom = (): AppThunk> => async ( dispatch(lobbyPeersActions.addPeers(lobbyPeers)); dispatch(roomSessionsActions.addMessages({ sessionId, messages: chatHistory })); dispatch(roomSessionsActions.addFiles({ sessionId, files: fileHistory })); + dispatch(countdownTimerActions.setCountdownTimer(countdownTimer)); + }); if (!getState().me.audioMuted) dispatch(updateMic()); diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx new file mode 100644 index 00000000..93e4eaa2 --- /dev/null +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -0,0 +1,151 @@ +import { Middleware } from '@reduxjs/toolkit'; +import { Logger } from '../../utils/Logger'; + +import { countdownTimerActions } from '../slices/countdownTimerSlice'; +import { signalingActions } from '../slices/signalingSlice'; +import { AppDispatch, MiddlewareOptions, RootState } from '../store'; +import moment from 'moment'; + +const logger = new Logger('ChatMiddleware'); + +const createCountdownTimerMiddleware = ({ + signalingService +}: MiddlewareOptions): Middleware => { + logger.debug('createChatMiddleware()'); + + const middleware: Middleware = ({ + dispatch, getState + }: { + dispatch: AppDispatch, + getState: () => RootState + }) => + (next) => (action) => { + + const countdownTimer = getState().countdownTimer; + let _countdownTimerRef : any; + + if (signalingActions.connect.match(action)) { + signalingService.on('notification', (notification) => { + try { + switch (notification.method) { + + case 'moderator:enableCountdownTimer': { + + dispatch(countdownTimerActions.enableCountdownTimer()); + + break; + } + + case 'moderator:disableCountdownTimer': { + + dispatch(countdownTimerActions.disableCountdownTimer()); + + break; + } + case 'moderator:startedCountdownTimer': { + + dispatch(countdownTimerActions.startCountdownTimer()); + + // clearInterval(_countdownTimerRef); + + // _countdownTimerRef = setInterval(() => { + // let left = moment(`1000-01-01 ${countdownTimer.left}`).unix(); + // const end = moment('1000-01-01 00:00:00').unix(); + + // left--; + + // const left2 = moment.unix(left).format('HH:mm:ss'); + + // dispatch(countdownTimerActions.setCountdownTimer({ left: left2 })); + + // // countdownTimer.left = moment.unix(left).format('HH:mm:ss'); + + // // if (left === end) { + // // // if (left === end || room.empty) { + // // clearInterval(_countdownTimerRef); + + // // dispatch(countdownTimerActions.stopCountdownTimer()); + // // dispatch(countdownTimerActions.setCountdownTimer({ left: '00:00:00' })); + + // // // countdownTimer.isRunning = false; + // // // countdownTimer.left = '00:00:00'; + + // // // room.notifyPeers('moderator:setCountdownTimer', { + // // // peerId: peer.id, + // // // left: countdownTimer.left, + // // // // isRunning: countdownTimer.isRunning + // // // }); + + // // // room.notifyPeers('moderator:stoppedCountdownTimer', { + // // // peerId: peer.id, + // // // isRunning: countdownTimer.isRunning + // // // }); + // // } + + // }, 1000); + + break; + } + + case 'moderator:stoppedCountdownTimer': { + // const { isEnabled } = notification.data; + + dispatch(countdownTimerActions.stopCountdownTimer()); + + break; + } + + // case 'moderator:toggleCountdownTimer': { + // const { isEnabled } = notification.data; + + // dispatch(countdownTimerActions.toggleCountdownTimer(isEnabled)); + + // // store.dispatch(requestActions.notify( + // // { + // // type : 'info', + // // text : intl.formatMessage({ + // // id : 'xxx', + // // defaultMessage : 'Countdown timer is updated' + // // }) + // // })); + + // break; + // } + + case 'moderator:settedCountdownTimer': { + + const { left, isRunning } = notification.data; + + dispatch(countdownTimerActions.setCountdownTimer( + { left, isRunning })); + + // if (arr.includes(left) && isRunning) { + // if (left == arr[0] && isRunning) { + // dispatch(countdownTimerActions.notify( + // { + // type: 'info', + // text: intl.formatMessage({ + // id: 'xxx', + // defaultMessage: 'Time is up' + // }) + // })); + + // this._soundNotification('countdownTimer'); + // } + + break; + } + } + } catch (error) { + logger.error('error on signalService "notification" event [error:%o]', error); + } + }); + } + + return next(action); + }; + + return middleware; +}; + +export default createCountdownTimerMiddleware; \ No newline at end of file diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx new file mode 100644 index 00000000..d7efadb3 --- /dev/null +++ b/src/store/slices/countdownTimerSlice.tsx @@ -0,0 +1,42 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; + +interface CountdownTimerState { + isEnabled: boolean, + left: string, + isRunning: boolean +} + +const initialState : CountdownTimerState = { + isEnabled: true, + left: '00:00:00', + isRunning: false +}; + +const countdownTimerSlice = createSlice({ + name: 'countdownTimer', + initialState, + reducers: { + enableCountdownTimer: ((state) => { + state.isEnabled = true; + }), + disableCountdownTimer: ((state) => { + state.isEnabled = false; + }), + // toggleCountdownTimer: ((state, action: PayloadAction) => { + // state.isEnabled = action.payload; + // }), + setCountdownTimer: ((state, action: PayloadAction) => { + state.left = action.payload.left; + // state.isRunning = action.payload.isRunning; + }), + startCountdownTimer: ((state) => { + state.isRunning = true; + }), + stopCountdownTimer: ((state) => { + state.isRunning = false; + }), + } +}); + +export const countdownTimerActions = countdownTimerSlice.actions; +export default countdownTimerSlice; \ No newline at end of file diff --git a/src/store/store.tsx b/src/store/store.tsx index 2676af0e..f4e30094 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -27,8 +27,10 @@ import createPeerMiddleware from './middlewares/peerMiddleware'; import createPermissionsMiddleware from './middlewares/permissionsMiddleware'; import createChatMiddleware from './middlewares/chatMiddleware'; import createNotificationMiddleware from './middlewares/notificationMiddleware'; +import createCountdownTimerMiddleware from './middlewares/countdownTimerMiddleware'; import roomSlice from './slices/roomSlice'; import meSlice from './slices/meSlice'; +import countdownTimerSlice from './slices/countdownTimerSlice'; import consumersSlice from './slices/consumersSlice'; import signalingSlice from './slices/signalingSlice'; import permissionsSlice from './slices/permissionsSlice'; @@ -124,6 +126,7 @@ const reducer = combineReducers({ settings: settingsSlice.reducer, signaling: signalingSlice.reducer, ui: uiSlice.reducer, + countdownTimer: countdownTimerSlice.reducer, }); const pReducer = persistReducer(persistConfig, reducer); @@ -148,6 +151,7 @@ export const store = configureStore({ createRoomMiddleware(middlewareOptions), createNotificationMiddleware(middlewareOptions), createEffectsMiddleware(middlewareOptions), + createCountdownTimerMiddleware(middlewareOptions), ...(edumeetConfig.reduxLoggingEnabled ? [ createLogger({ duration: true, timestamp: false, diff --git a/tsconfig.json b/tsconfig.json index 2d133cab..d6da654b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,7 @@ "noEmit": true, "jsx": "react-jsx", "types": [ "vite/client" ], + "sourceMap": true }, "include": ["src/**/*"] } diff --git a/yarn.lock b/yarn.lock index 76a46b4b..139bb9c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3304,6 +3304,11 @@ minimist@^1.2.5, minimist@^1.2.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +moment@^2.29.4: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" From b7fee64f20e56cad3823c5aca4f0e94a2737480f Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 12 Apr 2024 09:12:05 +0200 Subject: [PATCH 02/49] Adjust countdownTimerChip styles --- src/components/countdowntimer/CountdownTimerChip.tsx | 11 +++++------ src/components/topbar/TopBar.tsx | 6 ++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index e6f7f1b5..370c8211 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -5,6 +5,7 @@ import { useAppDispatch, useAppSelector } from '../../store/hooks'; // import { intl } from '../../utils/intlManager'; import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; import { useTheme } from '@mui/material/styles'; +import AvTimerIcon from '@mui/icons-material/AvTimer'; // const Div = styled('div')(({ theme }) => ({ // padding: theme.spacing(1), @@ -24,14 +25,12 @@ const CountdownTimerChip = () : JSX.Element => { { isEnabled && } /> } diff --git a/src/components/topbar/TopBar.tsx b/src/components/topbar/TopBar.tsx index ace2bcf2..ec32fc8f 100644 --- a/src/components/topbar/TopBar.tsx +++ b/src/components/topbar/TopBar.tsx @@ -116,15 +116,17 @@ const TopBar = ({ fullscreenEnabled, fullscreen, onFullscreen }: TopBarProps): R { someoneIsRecording && } { fullscreenEnabled && } - { canLock && } { canPromote && lobbyPeersLength > 0 && } { loginEnabled && (loggedIn ? : ) } - + + + + From cd92cfe0be1a5ad66ca9aae7f03c879c18ec0abb Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Mon, 15 Apr 2024 16:38:34 +0200 Subject: [PATCH 03/49] Clean component --- .../countdowntimer/CountdownTimer.tsx | 134 ++---------------- 1 file changed, 15 insertions(+), 119 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 7a4a1357..adf8ea13 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -5,48 +5,8 @@ import { useAppDispatch, useAppSelector } from '../../store/hooks'; // import { intl } from '../../utils/intlManager'; import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; import { countdownTimerActions as countdownTimerSlices } from '../../store/slices/countdownTimerSlice'; - import moment from 'moment'; -// const styles = (theme) => -// ({ -// root: -// { -// padding: theme.spacing(1), -// display: 'flex', -// flexWrap: 'wrap', -// marginRight: -theme.spacing(1), -// marginTop: -theme.spacing(1) -// }, -// container: -// { -// marginTop: theme.spacing(1), -// marginRight: theme.spacing(2), -// flexGrow: '1', -// justifyContent: 'space-between' -// }, -// textfield: -// { -// marginTop: theme.spacing(1), -// marginRight: theme.spacing(1), -// flexGrow: '1' -// }, -// button: -// { -// marginTop: theme.spacing(1), -// marginRight: theme.spacing(1), -// flexGrow: '1' -// } - -// }); - -interface CountdownTimerProps { - isEnabled: boolean; - isRunning: boolean; - left: string; - // classes: any; -} - const Div = styled('div')(({ theme }) => ({ padding: theme.spacing(1), display: 'flex', @@ -84,8 +44,6 @@ const CountdownTimer = () : JSX.Element => { let _countdownTimerRef: NodeJS.Timeout; useEffect(() => { - // eslint-disable-next-line no-console - console.log(isRunning, left); if (isRunning === true) { @@ -105,9 +63,6 @@ const CountdownTimer = () : JSX.Element => { } }, 1000); - - // eslint-disable-next-line no-console - console.log(_countdownTimerRef); } return () => { @@ -115,26 +70,16 @@ const CountdownTimer = () : JSX.Element => { }; } }, [ isRunning, left ]); - // }, [ isRunning, left ]); return ( -
- +
+ + + {/* TextField set time */} - {/* TextField set time */} { // })} inputRef={inputRef} autoFocus - sx={{ - // marginTop: theme.spacing(1), - // marginRight: theme.spacing(1), - flexGrow: '1' - }} - // className={classes.textfield} + sx={{ flexGrow: '1' }} variant='outlined' label='timer (hh:mm:ss)' disabled={!isEnabled || (isRunning && left !== '00:00:00')} @@ -173,17 +113,15 @@ const CountdownTimer = () : JSX.Element => { } }} /> - {/* /TextField set time */} + {/* Button reset time */} - {/* Button reset time */} { }} color='error' size='small' - disabled={ - !isEnabled || - ( - isRunning || - left === '00:00:00' - ) - } + disabled={ !isEnabled || (isRunning || left === '00:00:00') } onClick={() => { dispatch(countdownTimerActions.setCountdownTimer('00:00:00')); handleFocus(); @@ -205,23 +137,17 @@ const CountdownTimer = () : JSX.Element => { > - {/* /Button reset */} + {/* Button start/stop countdown */} {!isRunning ? - {/* Button start countdown */} { > - {/* /Button start countdown */} : - {/* Button stop countdown */} { > - {/* /Button stop countdown */} } + + {/* Switch toggle show/hide */} - {/* Switch toggle show/hide */} { color='error' size='small' /> - {/* /Switch toggle show/hide */}
); }; -// const mapStateToProps = (state) => ({ -// isEnabled: state.room.countdownTimer.isEnabled, -// isRunning: state.room.countdownTimer.isRunning, -// left: state.room.countdownTimer.left -// }); - -// export default withRoomContext(connect( -// mapStateToProps, -// null, -// null, -// { -// areStatesEqual: (next, prev) => { -// return ( -// prev.isEnabled === next.room.countdownTimer.isEnabled, -// prev.isRunning === next.room.countdownTimer.isRunning, -// prev.left === next.room.countdownTimer.left -// ); -// } -// } -// )(withStyles(styles)(CountdownTimer))); export default CountdownTimer; \ No newline at end of file From 980a6bc803e0d3153a50fd0c9a87db7bf5d74887 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Mon, 15 Apr 2024 20:49:39 +0200 Subject: [PATCH 04/49] Refactor component --- .../countdowntimer/CountdownTimer.tsx | 99 +++++++------------ 1 file changed, 34 insertions(+), 65 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index adf8ea13..8d47a506 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -2,10 +2,14 @@ import React, { useEffect, useRef } from 'react'; import { IconButton, Grid, Switch, TextField, styled } from '@mui/material'; import { HighlightOff as HighlightOffIcon, Pause as PauseIcon, PlayArrow as PlayArrowIcon } from '@mui/icons-material'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; -// import { intl } from '../../utils/intlManager'; +import { intl } from '../../utils/intlManager'; import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; import { countdownTimerActions as countdownTimerSlices } from '../../store/slices/countdownTimerSlice'; import moment from 'moment'; +import { + countdownTimerStartLabel, countdownTimerStopLabel, countdownTimerPauseLabel, + countdownTimerEnableLabel, countdownTimerDisableLabel, countdownTimerSetLabel } + from '../translated/translatedComponents'; const Div = styled('div')(({ theme }) => ({ padding: theme.spacing(1), @@ -47,7 +51,7 @@ const CountdownTimer = () : JSX.Element => { if (isRunning === true) { - if (_countdownTimerRef === undefined) { + if (_countdownTimerRef === undefined) { _countdownTimerRef = setInterval(() => { let leftUnix = moment(`1000-01-01 ${left}`).unix(); const endUnix = moment('1000-01-01 00:00:00').unix(); @@ -81,10 +85,7 @@ const CountdownTimer = () : JSX.Element => { {/* TextField set time */} { type='time' value={left} size='small' - InputLabelProps={{ - shrink: true - }} - inputProps={{ - step: '1' - }} + InputLabelProps={{ shrink: true }} + inputProps={{ step: '1' }} onChange={(e) => { dispatch(countdownTimerActions.setCountdownTimer(e.target.value)); handleFocus(); @@ -118,15 +115,8 @@ const CountdownTimer = () : JSX.Element => { {/* Button reset time */} { {/* Button start/stop countdown */} - {!isRunning ? - - { + + { + if (!isRunning) { dispatch(countdownTimerActions.startCountdownTimer()); - }} - > - - - - : - - { + } else { dispatch(countdownTimerActions.stopCountdownTimer()); handleFocus(); - }} - > - - - - } + } + }} + > + {!isRunning ? : } + + {/* Switch toggle show/hide */} { - dispatch( - isEnabled ? - countdownTimerActions.disableCountdownTimer() - : countdownTimerActions.enableCountdownTimer() + dispatch(isEnabled ? + countdownTimerActions.disableCountdownTimer() : + countdownTimerActions.enableCountdownTimer() ); // dispatch(countdownTimerActions.toggleCountdownTimer(!isEnabled)); handleFocus(); From f22addc5e582f9475ee2150d8e07e2f68ca1bd87 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Tue, 16 Apr 2024 13:28:50 +0200 Subject: [PATCH 05/49] Make bigger refactor --- .../countdowntimer/CountdownTimer.tsx | 2 + .../translated/translatedComponents.tsx | 30 ++++++++++++ src/store/actions/roomActions.tsx | 5 ++ src/translations/pl.json | 5 +- src/views/room/Room.tsx | 46 ++++++++++++++++++- 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 8d47a506..7eae5441 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -47,6 +47,7 @@ const CountdownTimer = () : JSX.Element => { let _countdownTimerRef: NodeJS.Timeout; + /* useEffect(() => { if (isRunning === true) { @@ -74,6 +75,7 @@ const CountdownTimer = () : JSX.Element => { }; } }, [ isRunning, left ]); + */ return (
diff --git a/src/components/translated/translatedComponents.tsx b/src/components/translated/translatedComponents.tsx index 59a078bf..ac535aa5 100644 --- a/src/components/translated/translatedComponents.tsx +++ b/src/components/translated/translatedComponents.tsx @@ -772,3 +772,33 @@ export const countdownTimerActionsLabel = (): string => intl.formatMessage({ id: 'countdownTimer.actions', defaultMessage: 'Countdown timer actions' }); + +export const countdownTimerStartLabel = (): string => intl.formatMessage({ + id: 'countdownTimer.start', + defaultMessage: 'Start' +}); + +export const countdownTimerStopLabel = (): string => intl.formatMessage({ + id: 'countdownTimer.stop', + defaultMessage: 'Stop' +}); + +export const countdownTimerPauseLabel = (): string => intl.formatMessage({ + id: 'countdownTimer.pause', + defaultMessage: 'Pause' +}); + +export const countdownTimerEnableLabel = (): string => intl.formatMessage({ + id: 'countdownTimer.enable', + defaultMessage: 'Enable' +}); + +export const countdownTimerDisableLabel = (): string => intl.formatMessage({ + id: 'countdownTimer.disable', + defaultMessage: 'Enable' +}); + +export const countdownTimerSetLabel = (): string => intl.formatMessage({ + id: 'countdownTimer.set', + defaultMessage: 'Set' +}); \ No newline at end of file diff --git a/src/store/actions/roomActions.tsx b/src/store/actions/roomActions.tsx index 8fe66c38..1ce99bf0 100644 --- a/src/store/actions/roomActions.tsx +++ b/src/store/actions/roomActions.tsx @@ -84,6 +84,11 @@ export const joinRoom = (): AppThunk> => async ( dispatch(roomSessionsActions.addFiles({ sessionId, files: fileHistory })); dispatch(countdownTimerActions.setCountdownTimer(countdownTimer)); + dispatch(countdownTimer.isRunning ? + countdownTimerActions.startCountdownTimer() : + countdownTimerActions.stopCountdownTimer() + ); + }); if (!getState().me.audioMuted) dispatch(updateMic()); diff --git a/src/translations/pl.json b/src/translations/pl.json index f9c93cc0..251f32cc 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -277,5 +277,8 @@ "svc.mgmtUnavailable": "Usługa zarządzania niedostępna", "svc.mediaNodeUnavailable": "Usługa węzła mediów niedostępna", "svc.mediaConnectionNodeError": "Usługa węzła mediów: błąd połączenia", - "svc.mediaConnectionNodeSuccess": "Usługa węzła mediów: połączono" + "svc.mediaConnectionNodeSuccess": "Usługa węzła mediów: połączono", + "tooltip.coundowntimer.start": "Start", + "tooltip.coundowntimer.stop": "Stop", + "tooltip.coundowntimer.pause": "Pause" } diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index 63150add..46e146c1 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -11,7 +11,11 @@ import ExtraVideoDialog from '../../components/extravideodialog/ExtraVideoDialog import Help from '../../components/helpdialog/HelpDialog'; import MainContent from '../../components/maincontent/MainContent'; import HelpButton from '../../components/controlbuttons/HelpButton'; -import { useNotifier } from '../../store/hooks'; +import { useNotifier, useAppSelector, useAppDispatch } from '../../store/hooks'; +import moment from 'moment'; + +import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; +import { countdownTimerActions as countdownTimerSlices } from '../../store/slices/countdownTimerSlice'; const Room = (): JSX.Element => { useNotifier(); @@ -37,6 +41,46 @@ const Room = (): JSX.Element => { const handleFullscreenChange = () => setFullscreen(fscreen.fullscreenElement !== null); + // ------------------------------------------------------------------------- + const left = useAppSelector((state) => state.countdownTimer.left); + const isRunning = useAppSelector((state) => state.countdownTimer.isRunning); + const dispatch = useAppDispatch(); + + useEffect(() => { + + // let _countdownTimerRef: NodeJS.Timeout | undefined = undefined; + let _countdownTimerRef: NodeJS.Timeout | null = null; + + if (isRunning && !_countdownTimerRef) { + + _countdownTimerRef = setInterval(() => { + let leftUnix = moment(`1000-01-01 ${left}`).unix(); + const endUnix = moment('1000-01-01 00:00:00').unix(); + + leftUnix--; + + const leftString = moment.unix(leftUnix).format('HH:mm:ss'); + + dispatch(countdownTimerSlices.setCountdownTimer({ left: leftString })); + + if (leftUnix === endUnix) { + dispatch(countdownTimerActions.stopCountdownTimer()); + } + + }, 1000); + } else if (_countdownTimerRef) clearInterval(_countdownTimerRef); + + return (): void => { + if (_countdownTimerRef) { + clearInterval(_countdownTimerRef); + _countdownTimerRef = null; + } + }; + + }, [ isRunning, left, dispatch ]); + + // ------------------------------------------------------------------------- + return ( <> From 688dc319d20b7de8e8981adbb3f1ad23fb51787a Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Tue, 16 Apr 2024 13:33:33 +0200 Subject: [PATCH 06/49] Comment code --- src/components/countdowntimer/CountdownTimer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 7eae5441..704a9d53 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -45,7 +45,7 @@ const CountdownTimer = () : JSX.Element => { }; }; - let _countdownTimerRef: NodeJS.Timeout; + // let _countdownTimerRef: NodeJS.Timeout; /* useEffect(() => { From f975ded66dd2faaff36f98f28c8fd734a3218d77 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Tue, 16 Apr 2024 14:09:41 +0200 Subject: [PATCH 07/49] Clean code --- .../countdowntimer/CountdownTimer.tsx | 59 ++++--------------- .../participantlist/ParticipantList.tsx | 6 +- .../translated/translatedComponents.tsx | 6 +- src/views/room/Room.tsx | 3 - 4 files changed, 17 insertions(+), 57 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 704a9d53..8158fe26 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -1,26 +1,22 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useRef } from 'react'; import { IconButton, Grid, Switch, TextField, styled } from '@mui/material'; import { HighlightOff as HighlightOffIcon, Pause as PauseIcon, PlayArrow as PlayArrowIcon } from '@mui/icons-material'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; -import { intl } from '../../utils/intlManager'; import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; -import { countdownTimerActions as countdownTimerSlices } from '../../store/slices/countdownTimerSlice'; -import moment from 'moment'; import { countdownTimerStartLabel, countdownTimerStopLabel, countdownTimerPauseLabel, countdownTimerEnableLabel, countdownTimerDisableLabel, countdownTimerSetLabel } from '../translated/translatedComponents'; -const Div = styled('div')(({ theme }) => ({ - padding: theme.spacing(1), +const CountdownTimerDiv = styled('div')(({ theme }) => ({ display: 'flex', - flexWrap: 'wrap', marginRight: theme.spacing(1), - marginTop: theme.spacing(1) + marginTop: theme.spacing(1), + flexDirection: 'column', + gap: theme.spacing(1), })); const CountdownTimer = () : JSX.Element => { - // const intl = useIntl(); const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); const isRunning = useAppSelector((state) => state.countdownTimer.isRunning); @@ -45,46 +41,14 @@ const CountdownTimer = () : JSX.Element => { }; }; - // let _countdownTimerRef: NodeJS.Timeout; - - /* - useEffect(() => { - - if (isRunning === true) { - - if (_countdownTimerRef === undefined) { - _countdownTimerRef = setInterval(() => { - let leftUnix = moment(`1000-01-01 ${left}`).unix(); - const endUnix = moment('1000-01-01 00:00:00').unix(); - - leftUnix--; - - const leftString = moment.unix(leftUnix).format('HH:mm:ss'); - - dispatch(countdownTimerSlices.setCountdownTimer({ left: leftString })); - - if (leftUnix === endUnix) { - dispatch(countdownTimerActions.stopCountdownTimer()); - } - - }, 1000); - } - - return () => { - clearInterval(_countdownTimerRef); - }; - } - }, [ isRunning, left ]); - */ - return ( -
+ - {/* TextField set time */} + {/* Set */} { /> - {/* Button reset time */} + {/* Reset */} { - {/* Button start/stop countdown */} + {/* Start/stop */} { - {/* Switch toggle show/hide */} + {/* enable/disable */} { countdownTimerActions.disableCountdownTimer() : countdownTimerActions.enableCountdownTimer() ); - // dispatch(countdownTimerActions.toggleCountdownTimer(!isEnabled)); handleFocus(); }} name='checkedB' @@ -175,7 +138,7 @@ const CountdownTimer = () : JSX.Element => { /> -
+ ); }; diff --git a/src/components/participantlist/ParticipantList.tsx b/src/components/participantlist/ParticipantList.tsx index 9a2726f3..3a434093 100644 --- a/src/components/participantlist/ParticipantList.tsx +++ b/src/components/participantlist/ParticipantList.tsx @@ -9,7 +9,7 @@ import { permissions } from '../../utils/roles'; import { breakoutRoomsLabel, participantsLabel, - countdownTimerActionsLabel + countdownTimerLabel } from '../translated/translatedComponents'; import ListMe from './ListMe'; import ListModerator from './ListModerator'; @@ -39,13 +39,13 @@ const ParticipantList = (): JSX.Element => { return ( + { isModerator && } <> - {countdownTimerActionsLabel()} + {countdownTimerLabel()} - { isModerator && } { (breakoutsEnabled && (rooms.length > 0 || canCreateRooms)) && <> diff --git a/src/components/translated/translatedComponents.tsx b/src/components/translated/translatedComponents.tsx index ac535aa5..c1c9fe43 100644 --- a/src/components/translated/translatedComponents.tsx +++ b/src/components/translated/translatedComponents.tsx @@ -768,9 +768,9 @@ export const roomServerConnectionError = (message: string): string => intl.forma defaultMessage: `Room-server: ${message}` }); -export const countdownTimerActionsLabel = (): string => intl.formatMessage({ - id: 'countdownTimer.actions', - defaultMessage: 'Countdown timer actions' +export const countdownTimerLabel = (): string => intl.formatMessage({ + id: 'countdownTimer', + defaultMessage: 'Countdown timer' }); export const countdownTimerStartLabel = (): string => intl.formatMessage({ diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index 46e146c1..7055c6b0 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -41,7 +41,6 @@ const Room = (): JSX.Element => { const handleFullscreenChange = () => setFullscreen(fscreen.fullscreenElement !== null); - // ------------------------------------------------------------------------- const left = useAppSelector((state) => state.countdownTimer.left); const isRunning = useAppSelector((state) => state.countdownTimer.isRunning); const dispatch = useAppDispatch(); @@ -79,8 +78,6 @@ const Room = (): JSX.Element => { }, [ isRunning, left, dispatch ]); - // ------------------------------------------------------------------------- - return ( <> From 9acb14a847fef3346c4b9303e9a42f449d85e193 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Tue, 16 Apr 2024 14:19:23 +0200 Subject: [PATCH 08/49] Fix start when Enter key pressed --- src/components/countdowntimer/CountdownTimer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 8158fe26..4e1d8fb8 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -67,10 +67,10 @@ const CountdownTimer = () : JSX.Element => { dispatch(countdownTimerActions.setCountdownTimer(e.target.value)); handleFocus(); }} - onKeyPress={(e) => { + onKeyDown={(e) => { if (left !== '00:00:00') { if (e.key === 'Enter') { - countdownTimerActions.startCountdownTimer(); + dispatch(countdownTimerActions.startCountdownTimer()); e.preventDefault(); } } From d68d0a3b174da4e05ea1db02fdd6aee30c7245d8 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Tue, 16 Apr 2024 15:17:30 +0200 Subject: [PATCH 09/49] Clean code --- .../countdowntimer/CountdownTimerChip.tsx | 41 ++----------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index 370c8211..8dedd0b2 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -1,24 +1,11 @@ -import React, { useRef } from 'react'; -import { Chip, styled } from '@mui/material'; -import { HighlightOff as HighlightOffIcon, Pause as PauseIcon } from '@mui/icons-material'; -import { useAppDispatch, useAppSelector } from '../../store/hooks'; -// import { intl } from '../../utils/intlManager'; -import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; -import { useTheme } from '@mui/material/styles'; +import React from 'react'; +import { Chip } from '@mui/material'; +import { useAppSelector } from '../../store/hooks'; import AvTimerIcon from '@mui/icons-material/AvTimer'; -// const Div = styled('div')(({ theme }) => ({ -// padding: theme.spacing(1), -// marginRight: theme.spacing(1), -// marginTop: theme.spacing(1) -// })); - const CountdownTimerChip = () : JSX.Element => { - const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); - // const isRunning = useAppSelector((state) => state.countdownTimer.isRunning); const left = useAppSelector((state) => state.countdownTimer.left); - const theme = useTheme(); return ( <> @@ -30,31 +17,11 @@ const CountdownTimerChip = () : JSX.Element => { }} label={left} size='small' - icon={} + icon={} /> } ); }; -// const mapStateToProps = (state) => ({ -// isEnabled: state.room.countdownTimer.isEnabled, -// isRunning: state.room.countdownTimer.isRunning, -// left: state.room.countdownTimer.left -// }); - -// export default withRoomContext(connect( -// mapStateToProps, -// null, -// null, -// { -// areStatesEqual: (next, prev) => { -// return ( -// prev.isEnabled === next.room.countdownTimer.isEnabled, -// prev.isRunning === next.room.countdownTimer.isRunning, -// prev.left === next.room.countdownTimer.left -// ); -// } -// } -// )(withStyles(styles)(CountdownTimer))); export default CountdownTimerChip; \ No newline at end of file From 82630712cbbd3a51c808ba707670b7c063d299d1 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Tue, 16 Apr 2024 15:23:52 +0200 Subject: [PATCH 10/49] Clean code --- src/store/actions/countdownTimerActions.tsx | 19 ------------------- .../middlewares/countdownTimerMiddleware.tsx | 17 ----------------- src/store/slices/countdownTimerSlice.tsx | 3 --- 3 files changed, 39 deletions(-) diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 6704518a..9e1b4403 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -47,25 +47,6 @@ AppThunk> => async ( } }; -// export const toggleCountdownTimer = (isEnabled : boolean): -// AppThunk> => async ( -// dispatch, -// getState, -// { signalingService } -// ): Promise => { -// logger.debug('moderator:toggleCountdownTimer()'); - -// try { -// await signalingService.sendRequest('moderator:toggleCountdownTimer', { isEnabled: isEnabled }); - -// // const peerId = getState().me.id; - -// dispatch(countdownTimerActions.toggleCountdownTimer(isEnabled)); -// } catch (error) { -// logger.error('moderator:toggleCountdownTimer() [error:"%o"]', error); -// } -// }; - export const setCountdownTimer = (left : string): AppThunk> => async ( dispatch, diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 93e4eaa2..95746e3e 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -95,23 +95,6 @@ const createCountdownTimerMiddleware = ({ break; } - // case 'moderator:toggleCountdownTimer': { - // const { isEnabled } = notification.data; - - // dispatch(countdownTimerActions.toggleCountdownTimer(isEnabled)); - - // // store.dispatch(requestActions.notify( - // // { - // // type : 'info', - // // text : intl.formatMessage({ - // // id : 'xxx', - // // defaultMessage : 'Countdown timer is updated' - // // }) - // // })); - - // break; - // } - case 'moderator:settedCountdownTimer': { const { left, isRunning } = notification.data; diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index d7efadb3..48782e86 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -22,9 +22,6 @@ const countdownTimerSlice = createSlice({ disableCountdownTimer: ((state) => { state.isEnabled = false; }), - // toggleCountdownTimer: ((state, action: PayloadAction) => { - // state.isEnabled = action.payload; - // }), setCountdownTimer: ((state, action: PayloadAction) => { state.left = action.payload.left; // state.isRunning = action.payload.isRunning; From f484dbabddd27b159c19a3e817a42ea8cdb3b80c Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Tue, 16 Apr 2024 19:23:04 +0200 Subject: [PATCH 11/49] Clean code --- .../middlewares/countdownTimerMiddleware.tsx | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 95746e3e..8abac486 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -4,7 +4,6 @@ import { Logger } from '../../utils/Logger'; import { countdownTimerActions } from '../slices/countdownTimerSlice'; import { signalingActions } from '../slices/signalingSlice'; import { AppDispatch, MiddlewareOptions, RootState } from '../store'; -import moment from 'moment'; const logger = new Logger('ChatMiddleware'); @@ -21,9 +20,6 @@ const createCountdownTimerMiddleware = ({ }) => (next) => (action) => { - const countdownTimer = getState().countdownTimer; - let _countdownTimerRef : any; - if (signalingActions.connect.match(action)) { signalingService.on('notification', (notification) => { try { @@ -46,49 +42,10 @@ const createCountdownTimerMiddleware = ({ dispatch(countdownTimerActions.startCountdownTimer()); - // clearInterval(_countdownTimerRef); - - // _countdownTimerRef = setInterval(() => { - // let left = moment(`1000-01-01 ${countdownTimer.left}`).unix(); - // const end = moment('1000-01-01 00:00:00').unix(); - - // left--; - - // const left2 = moment.unix(left).format('HH:mm:ss'); - - // dispatch(countdownTimerActions.setCountdownTimer({ left: left2 })); - - // // countdownTimer.left = moment.unix(left).format('HH:mm:ss'); - - // // if (left === end) { - // // // if (left === end || room.empty) { - // // clearInterval(_countdownTimerRef); - - // // dispatch(countdownTimerActions.stopCountdownTimer()); - // // dispatch(countdownTimerActions.setCountdownTimer({ left: '00:00:00' })); - - // // // countdownTimer.isRunning = false; - // // // countdownTimer.left = '00:00:00'; - - // // // room.notifyPeers('moderator:setCountdownTimer', { - // // // peerId: peer.id, - // // // left: countdownTimer.left, - // // // // isRunning: countdownTimer.isRunning - // // // }); - - // // // room.notifyPeers('moderator:stoppedCountdownTimer', { - // // // peerId: peer.id, - // // // isRunning: countdownTimer.isRunning - // // // }); - // // } - - // }, 1000); - break; } case 'moderator:stoppedCountdownTimer': { - // const { isEnabled } = notification.data; dispatch(countdownTimerActions.stopCountdownTimer()); @@ -102,20 +59,6 @@ const createCountdownTimerMiddleware = ({ dispatch(countdownTimerActions.setCountdownTimer( { left, isRunning })); - // if (arr.includes(left) && isRunning) { - // if (left == arr[0] && isRunning) { - // dispatch(countdownTimerActions.notify( - // { - // type: 'info', - // text: intl.formatMessage({ - // id: 'xxx', - // defaultMessage: 'Time is up' - // }) - // })); - - // this._soundNotification('countdownTimer'); - // } - break; } } From 8fdd479112bd0b48cae6951877890924e2196459 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Tue, 16 Apr 2024 19:39:27 +0200 Subject: [PATCH 12/49] Clean code --- src/components/countdowntimer/CountdownTimer.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 4e1d8fb8..b9706721 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -4,7 +4,7 @@ import { HighlightOff as HighlightOffIcon, Pause as PauseIcon, PlayArrow as Play import { useAppDispatch, useAppSelector } from '../../store/hooks'; import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; import { - countdownTimerStartLabel, countdownTimerStopLabel, countdownTimerPauseLabel, + countdownTimerStartLabel, countdownTimerStopLabel, countdownTimerEnableLabel, countdownTimerDisableLabel, countdownTimerSetLabel } from '../translated/translatedComponents'; @@ -48,7 +48,7 @@ const CountdownTimer = () : JSX.Element => { container wrap='nowrap' alignItems='center' > - {/* Set */} + {/* set */} { inputProps={{ step: '1' }} onChange={(e) => { dispatch(countdownTimerActions.setCountdownTimer(e.target.value)); - handleFocus(); }} onKeyDown={(e) => { if (left !== '00:00:00') { @@ -78,7 +77,7 @@ const CountdownTimer = () : JSX.Element => { /> - {/* Reset */} + {/* reset */} { disabled={ !isEnabled || (isRunning || left === '00:00:00') } onClick={() => { dispatch(countdownTimerActions.setCountdownTimer('00:00:00')); - handleFocus(); }} > - {/* Start/stop */} + {/* start/stop */} { {/* enable/disable */} { countdownTimerActions.disableCountdownTimer() : countdownTimerActions.enableCountdownTimer() ); - handleFocus(); }} - name='checkedB' color='error' size='small' /> From f9bd476ab6174111ad31817b5f6a5c198f541b92 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Wed, 17 Apr 2024 15:02:23 +0200 Subject: [PATCH 13/49] Fix delimiters --- src/store/slices/countdownTimerSlice.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index 48782e86..a5fa8484 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -1,9 +1,9 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface CountdownTimerState { - isEnabled: boolean, - left: string, - isRunning: boolean + isEnabled: boolean; + left: string; + isRunning: boolean; } const initialState : CountdownTimerState = { From 9eafd3f9d66333f39f0b4829f0fa428687a100fa Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 18 Apr 2024 10:15:29 +0200 Subject: [PATCH 14/49] Clean Code --- src/store/slices/countdownTimerSlice.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index a5fa8484..fe33b893 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -24,7 +24,6 @@ const countdownTimerSlice = createSlice({ }), setCountdownTimer: ((state, action: PayloadAction) => { state.left = action.payload.left; - // state.isRunning = action.payload.isRunning; }), startCountdownTimer: ((state) => { state.isRunning = true; From f55cdb848260f9eeb0b8b51bff6ab03d173a9785 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Wed, 24 Apr 2024 15:07:14 +0200 Subject: [PATCH 15/49] Standarize state --- src/store/slices/countdownTimerSlice.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index fe33b893..03f2f412 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -2,14 +2,14 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface CountdownTimerState { isEnabled: boolean; - left: string; isRunning: boolean; + left: string; } const initialState : CountdownTimerState = { isEnabled: true, + isRunning: false, left: '00:00:00', - isRunning: false }; const countdownTimerSlice = createSlice({ From 2648e9c955bb968f1702be2f3322fdefac3a0b0c Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Wed, 24 Apr 2024 15:55:04 +0200 Subject: [PATCH 16/49] Standarize names --- .../countdowntimer/CountdownTimer.tsx | 16 ++++++------ src/store/actions/countdownTimerActions.tsx | 26 +++++++++---------- src/store/actions/roomActions.tsx | 2 +- .../middlewares/countdownTimerMiddleware.tsx | 10 +++---- src/store/slices/countdownTimerSlice.tsx | 14 +++++----- src/views/room/Room.tsx | 6 ++--- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index b9706721..adfa3962 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -19,7 +19,7 @@ const CountdownTimerDiv = styled('div')(({ theme }) => ({ const CountdownTimer = () : JSX.Element => { const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); - const isRunning = useAppSelector((state) => state.countdownTimer.isRunning); + const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); const left = useAppSelector((state) => state.countdownTimer.left); const inputRef = useRef(null); @@ -57,7 +57,7 @@ const CountdownTimer = () : JSX.Element => { sx={{ flexGrow: '1' }} variant='outlined' label='timer (hh:mm:ss)' - disabled={!isEnabled || (isRunning && left !== '00:00:00')} + disabled={!isEnabled || (isStarted && left !== '00:00:00')} type='time' value={left} size='small' @@ -84,7 +84,7 @@ const CountdownTimer = () : JSX.Element => { sx={{ flexGrow: '1' }} color='error' size='small' - disabled={ !isEnabled || (isRunning || left === '00:00:00') } + disabled={ !isEnabled || (isStarted || left === '00:00:00') } onClick={() => { dispatch(countdownTimerActions.setCountdownTimer('00:00:00')); }} @@ -96,7 +96,7 @@ const CountdownTimer = () : JSX.Element => { {/* start/stop */} { size='small' disabled={!isEnabled || left === '00:00:00'} onClick={() => { - if (!isRunning) { + if (!isStarted) { dispatch(countdownTimerActions.startCountdownTimer()); } else { dispatch(countdownTimerActions.stopCountdownTimer()); @@ -113,20 +113,20 @@ const CountdownTimer = () : JSX.Element => { } }} > - {!isRunning ? : } + {!isStarted ? : } {/* enable/disable */} { dispatch(isEnabled ? countdownTimerActions.disableCountdownTimer() : diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 9e1b4403..50b2ab48 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -47,51 +47,51 @@ AppThunk> => async ( } }; -export const setCountdownTimer = (left : string): +export const startCountdownTimer = (): AppThunk> => async ( dispatch, getState, { signalingService } ): Promise => { - logger.debug('setCountdownTimer() [left:"%s"]', left); + logger.debug('startCountdownTimer)'); try { - await signalingService.sendRequest('moderator:setCountdownTimer', { left }); + await signalingService.sendRequest('moderator:startCountdownTimer'); - dispatch(countdownTimerActions.setCountdownTimer({ left, isRunning: false })); } catch (error) { - logger.error('setCountdownTimer() [error:"%o"]', error); + logger.error('startCountdownTimer() [error:"%o"]', error); } }; -export const startCountdownTimer = (): +export const stopCountdownTimer = (): AppThunk> => async ( dispatch, getState, { signalingService } ): Promise => { - logger.debug('startCountdownTimer)'); + logger.debug('stopCountdownTimer()'); try { - await signalingService.sendRequest('moderator:startCountdownTimer'); + await signalingService.sendRequest('moderator:stopCountdownTimer'); } catch (error) { - logger.error('startCountdownTimer() [error:"%o"]', error); + logger.error('stopCountdownTimer() [error:"%o"]', error); } }; -export const stopCountdownTimer = (): +export const setCountdownTimer = (left : string): AppThunk> => async ( dispatch, getState, { signalingService } ): Promise => { - logger.debug('stopCountdownTimer()'); + logger.debug('setCountdownTimer() [left:"%s"]', left); try { - await signalingService.sendRequest('moderator:stopCountdownTimer'); + await signalingService.sendRequest('moderator:setCountdownTimer', { left }); + dispatch(countdownTimerActions.setCountdownTimer({ left, isStarted: false })); } catch (error) { - logger.error('stopCountdownTimer() [error:"%o"]', error); + logger.error('setCountdownTimer() [error:"%o"]', error); } }; \ No newline at end of file diff --git a/src/store/actions/roomActions.tsx b/src/store/actions/roomActions.tsx index 1ce99bf0..60d2edcc 100644 --- a/src/store/actions/roomActions.tsx +++ b/src/store/actions/roomActions.tsx @@ -84,7 +84,7 @@ export const joinRoom = (): AppThunk> => async ( dispatch(roomSessionsActions.addFiles({ sessionId, files: fileHistory })); dispatch(countdownTimerActions.setCountdownTimer(countdownTimer)); - dispatch(countdownTimer.isRunning ? + dispatch(countdownTimer.isStarted ? countdownTimerActions.startCountdownTimer() : countdownTimerActions.stopCountdownTimer() ); diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 8abac486..4a813503 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -25,14 +25,14 @@ const createCountdownTimerMiddleware = ({ try { switch (notification.method) { - case 'moderator:enableCountdownTimer': { + case 'moderator:enabledCountdownTimer': { dispatch(countdownTimerActions.enableCountdownTimer()); break; } - case 'moderator:disableCountdownTimer': { + case 'moderator:disabledCountdownTimer': { dispatch(countdownTimerActions.disableCountdownTimer()); @@ -52,12 +52,12 @@ const createCountdownTimerMiddleware = ({ break; } - case 'moderator:settedCountdownTimer': { + case 'moderator:setCountdownTimer': { - const { left, isRunning } = notification.data; + const { left, isStarted } = notification.data; dispatch(countdownTimerActions.setCountdownTimer( - { left, isRunning })); + { left, isStarted })); break; } diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index 03f2f412..607cba5b 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -2,13 +2,13 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface CountdownTimerState { isEnabled: boolean; - isRunning: boolean; + isStarted: boolean; left: string; } const initialState : CountdownTimerState = { isEnabled: true, - isRunning: false, + isStarted: false, left: '00:00:00', }; @@ -22,14 +22,14 @@ const countdownTimerSlice = createSlice({ disableCountdownTimer: ((state) => { state.isEnabled = false; }), - setCountdownTimer: ((state, action: PayloadAction) => { - state.left = action.payload.left; - }), startCountdownTimer: ((state) => { - state.isRunning = true; + state.isStarted = true; }), stopCountdownTimer: ((state) => { - state.isRunning = false; + state.isStarted = false; + }), + setCountdownTimer: ((state, action: PayloadAction) => { + state.left = action.payload.left; }), } }); diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index 7055c6b0..1daa715c 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -42,7 +42,7 @@ const Room = (): JSX.Element => { const handleFullscreenChange = () => setFullscreen(fscreen.fullscreenElement !== null); const left = useAppSelector((state) => state.countdownTimer.left); - const isRunning = useAppSelector((state) => state.countdownTimer.isRunning); + const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); const dispatch = useAppDispatch(); useEffect(() => { @@ -50,7 +50,7 @@ const Room = (): JSX.Element => { // let _countdownTimerRef: NodeJS.Timeout | undefined = undefined; let _countdownTimerRef: NodeJS.Timeout | null = null; - if (isRunning && !_countdownTimerRef) { + if (isStarted && !_countdownTimerRef) { _countdownTimerRef = setInterval(() => { let leftUnix = moment(`1000-01-01 ${left}`).unix(); @@ -76,7 +76,7 @@ const Room = (): JSX.Element => { } }; - }, [ isRunning, left, dispatch ]); + }, [ isStarted, left, dispatch ]); return ( <> From 9c9531d8f33fa6e38835ef79d0fc4fe88a11579e Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Wed, 24 Apr 2024 22:22:54 +0200 Subject: [PATCH 17/49] Show participant list (for testing) --- src/store/slices/uiSlice.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/slices/uiSlice.tsx b/src/store/slices/uiSlice.tsx index 02faf3e9..a15a1bf9 100644 --- a/src/store/slices/uiSlice.tsx +++ b/src/store/slices/uiSlice.tsx @@ -36,7 +36,7 @@ const initialState: UiState = { extraAudioDialogOpen: false, currentSettingsTab: 'media', chatOpen: false, - participantListOpen: false, + participantListOpen: true, }; const uiSlice = createSlice({ From 2eae093d0694241b1b09d2adc5fc59b34e99a5ed Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 25 Apr 2024 00:34:32 +0200 Subject: [PATCH 18/49] Fix counting stopped at 00:00:01 --- src/views/room/Room.tsx | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index 1daa715c..21b2af77 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -47,14 +47,10 @@ const Room = (): JSX.Element => { useEffect(() => { - // let _countdownTimerRef: NodeJS.Timeout | undefined = undefined; - let _countdownTimerRef: NodeJS.Timeout | null = null; - - if (isStarted && !_countdownTimerRef) { + if (isStarted) { - _countdownTimerRef = setInterval(() => { + const _countdownTimerRef = setInterval(() => { let leftUnix = moment(`1000-01-01 ${left}`).unix(); - const endUnix = moment('1000-01-01 00:00:00').unix(); leftUnix--; @@ -62,19 +58,10 @@ const Room = (): JSX.Element => { dispatch(countdownTimerSlices.setCountdownTimer({ left: leftString })); - if (leftUnix === endUnix) { - dispatch(countdownTimerActions.stopCountdownTimer()); - } - }, 1000); - } else if (_countdownTimerRef) clearInterval(_countdownTimerRef); - - return (): void => { - if (_countdownTimerRef) { - clearInterval(_countdownTimerRef); - _countdownTimerRef = null; - } - }; + + return () => { clearInterval(_countdownTimerRef); }; + } }, [ isStarted, left, dispatch ]); From 59bce3eb321965608189a948026e21e7b9db66f2 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 25 Apr 2024 01:08:54 +0200 Subject: [PATCH 19/49] Fix opened TextField Date Picker at start (mobile) --- .eslintrc | 2 ++ src/components/countdowntimer/CountdownTimer.tsx | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 08d19288..86dd8a9d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,6 +20,8 @@ }, "ignorePatterns": ["**/config.js", "vite.config.ts", "src/vite-env.d.ts"], "rules": { + "@typescript-eslint/no-unused-vars": ["warn", { "vars": "all", "args": "after-used" }], + "@typescript-eslint/no-explicit-any": "warn", "array-bracket-spacing": [ 2, "always", { "objectsInArrays": true, "arraysInArrays": true diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index adfa3962..56bdc68a 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -7,6 +7,7 @@ import { countdownTimerStartLabel, countdownTimerStopLabel, countdownTimerEnableLabel, countdownTimerDisableLabel, countdownTimerSetLabel } from '../translated/translatedComponents'; +import { isMobileSelector } from '../../store/selectors'; const CountdownTimerDiv = styled('div')(({ theme }) => ({ display: 'flex', @@ -17,6 +18,7 @@ const CountdownTimerDiv = styled('div')(({ theme }) => ({ })); const CountdownTimer = () : JSX.Element => { + const isMobile = useAppSelector(isMobileSelector); const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); @@ -53,7 +55,7 @@ const CountdownTimer = () : JSX.Element => { Date: Thu, 25 Apr 2024 22:22:09 +0200 Subject: [PATCH 20/49] Add visual indicator --- .../countdowntimer/CountdownTimer.tsx | 12 ++--- .../countdowntimer/CountdownTimerChip.tsx | 53 ++++++++++++++----- src/store/actions/countdownTimerActions.tsx | 11 ++-- src/store/actions/roomActions.tsx | 1 + .../middlewares/countdownTimerMiddleware.tsx | 17 +++++- src/store/slices/countdownTimerSlice.tsx | 11 ++-- src/views/room/Room.tsx | 12 ++--- 7 files changed, 83 insertions(+), 34 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 56bdc68a..59a1c112 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -22,7 +22,7 @@ const CountdownTimer = () : JSX.Element => { const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); - const left = useAppSelector((state) => state.countdownTimer.left); + const timeLeft = useAppSelector((state) => state.countdownTimer.timeLeft); const inputRef = useRef(null); @@ -59,9 +59,9 @@ const CountdownTimer = () : JSX.Element => { sx={{ flexGrow: '1' }} variant='outlined' label='timer (hh:mm:ss)' - disabled={!isEnabled || (isStarted && left !== '00:00:00')} + disabled={!isEnabled || (isStarted && timeLeft !== '00:00:00')} type='time' - value={left} + value={timeLeft} size='small' InputLabelProps={{ shrink: true }} inputProps={{ step: '1' }} @@ -69,7 +69,7 @@ const CountdownTimer = () : JSX.Element => { dispatch(countdownTimerActions.setCountdownTimer(e.target.value)); }} onKeyDown={(e) => { - if (left !== '00:00:00') { + if (timeLeft !== '00:00:00') { if (e.key === 'Enter') { dispatch(countdownTimerActions.startCountdownTimer()); e.preventDefault(); @@ -86,7 +86,7 @@ const CountdownTimer = () : JSX.Element => { sx={{ flexGrow: '1' }} color='error' size='small' - disabled={ !isEnabled || (isStarted || left === '00:00:00') } + disabled={ !isEnabled || (isStarted || timeLeft === '00:00:00') } onClick={() => { dispatch(countdownTimerActions.setCountdownTimer('00:00:00')); }} @@ -105,7 +105,7 @@ const CountdownTimer = () : JSX.Element => { sx={{ flexGrow: '1' }} color='error' size='small' - disabled={!isEnabled || left === '00:00:00'} + disabled={!isEnabled || timeLeft === '00:00:00'} onClick={() => { if (!isStarted) { dispatch(countdownTimerActions.startCountdownTimer()); diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index 8dedd0b2..a71e2c5f 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -2,24 +2,51 @@ import React from 'react'; import { Chip } from '@mui/material'; import { useAppSelector } from '../../store/hooks'; import AvTimerIcon from '@mui/icons-material/AvTimer'; +import moment from 'moment'; -const CountdownTimerChip = () : JSX.Element => { +const CountdownTimerChip = (): JSX.Element => { const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); - const left = useAppSelector((state) => state.countdownTimer.left); + const timeLeft = useAppSelector((state) => state.countdownTimer.timeLeft); + const timeInit = useAppSelector((state) => state.countdownTimer.timeInit); + + const totalTime = moment.duration(timeInit); + const leftTime = moment.duration(timeLeft); + + const totalSeconds = totalTime.asSeconds(); + const leftSeconds = leftTime.asSeconds(); + const percentage = parseFloat(((leftSeconds / totalSeconds) * 100).toFixed(2)); + let indicatorColor: string; + const backgroundColor = 'rgba(128, 128, 128, 0.5)'; // Declare the 'backgroundColor' variable here + + switch (true) { + case percentage <= 100 && percentage >= 50: + indicatorColor = '#2E7A27'; + break; + case percentage < 50 && percentage >= 20: + indicatorColor = '#FFA500'; + break; + case percentage < 20: + indicatorColor = 'red'; + break; + default: + indicatorColor = backgroundColor; + } return ( <> - { isEnabled && - } - /> - } + {isEnabled && ( + } + /> + )} ); }; diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 50b2ab48..74e6328e 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -79,18 +79,21 @@ AppThunk> => async ( } }; -export const setCountdownTimer = (left : string): +export const setCountdownTimer = (timeLeft : string): AppThunk> => async ( dispatch, getState, { signalingService } ): Promise => { - logger.debug('setCountdownTimer() [left:"%s"]', left); + logger.debug('setCountdownTimer() [timeLeft:"%s"]', timeLeft); try { - await signalingService.sendRequest('moderator:setCountdownTimer', { left }); + await signalingService.sendRequest('moderator:setCountdownTimer', { timeLeft }); + + dispatch(countdownTimerActions.setCountdownTimer({ timeLeft, isStarted: false })); + + dispatch(countdownTimerActions.setCountdownTimerTimeInit({ timeLeft, isStarted: false })); - dispatch(countdownTimerActions.setCountdownTimer({ left, isStarted: false })); } catch (error) { logger.error('setCountdownTimer() [error:"%o"]', error); } diff --git a/src/store/actions/roomActions.tsx b/src/store/actions/roomActions.tsx index 60d2edcc..a0a67f05 100644 --- a/src/store/actions/roomActions.tsx +++ b/src/store/actions/roomActions.tsx @@ -83,6 +83,7 @@ export const joinRoom = (): AppThunk> => async ( dispatch(roomSessionsActions.addMessages({ sessionId, messages: chatHistory })); dispatch(roomSessionsActions.addFiles({ sessionId, files: fileHistory })); dispatch(countdownTimerActions.setCountdownTimer(countdownTimer)); + dispatch(countdownTimerActions.setCountdownTimerTimeInit(countdownTimer)); dispatch(countdownTimer.isStarted ? countdownTimerActions.startCountdownTimer() : diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 4a813503..4b66f0e9 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -54,10 +54,23 @@ const createCountdownTimerMiddleware = ({ case 'moderator:setCountdownTimer': { - const { left, isStarted } = notification.data; + const { timeLeft, isStarted } = notification.data; dispatch(countdownTimerActions.setCountdownTimer( - { left, isStarted })); + { timeLeft, isStarted })); + + dispatch(countdownTimerActions.setCountdownTimerTimeInit( + { timeLeft, isStarted })); + + break; + } + + case 'moderator:updatedCountdownTimer': { + + const { timeLeft, isStarted } = notification.data; + + dispatch(countdownTimerActions.setCountdownTimer( + { timeLeft, isStarted })); break; } diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index 607cba5b..1f4bbede 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -3,13 +3,15 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface CountdownTimerState { isEnabled: boolean; isStarted: boolean; - left: string; + timeInit: string; + timeLeft: string; } const initialState : CountdownTimerState = { isEnabled: true, isStarted: false, - left: '00:00:00', + timeInit: '00:00:00', + timeLeft: '00:00:00', }; const countdownTimerSlice = createSlice({ @@ -29,7 +31,10 @@ const countdownTimerSlice = createSlice({ state.isStarted = false; }), setCountdownTimer: ((state, action: PayloadAction) => { - state.left = action.payload.left; + state.timeLeft = action.payload.timeLeft; + }), + setCountdownTimerTimeInit: ((state, action: PayloadAction) => { + state.timeInit = action.payload.timeLeft; }), } }); diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index 21b2af77..6070dcea 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -41,7 +41,7 @@ const Room = (): JSX.Element => { const handleFullscreenChange = () => setFullscreen(fscreen.fullscreenElement !== null); - const left = useAppSelector((state) => state.countdownTimer.left); + const timeLeft = useAppSelector((state) => state.countdownTimer.timeLeft); const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); const dispatch = useAppDispatch(); @@ -50,20 +50,20 @@ const Room = (): JSX.Element => { if (isStarted) { const _countdownTimerRef = setInterval(() => { - let leftUnix = moment(`1000-01-01 ${left}`).unix(); + let timeLeftUnix = moment(`1000-01-01 ${timeLeft}`).unix(); - leftUnix--; + timeLeftUnix--; - const leftString = moment.unix(leftUnix).format('HH:mm:ss'); + const timeLeftString = moment.unix(timeLeftUnix).format('HH:mm:ss'); - dispatch(countdownTimerSlices.setCountdownTimer({ left: leftString })); + dispatch(countdownTimerSlices.setCountdownTimer({ timeLeft: timeLeftString })); }, 1000); return () => { clearInterval(_countdownTimerRef); }; } - }, [ isStarted, left, dispatch ]); + }, [ isStarted, timeLeft, dispatch ]); return ( <> From 5aa4626865554432200d5b2c5748fd3332c3e32d Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 25 Apr 2024 22:32:04 +0200 Subject: [PATCH 21/49] Fix indicator for new joining user --- src/store/actions/countdownTimerActions.tsx | 2 +- src/store/middlewares/countdownTimerMiddleware.tsx | 4 ++-- src/store/slices/countdownTimerSlice.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 74e6328e..4aaf03fa 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -92,7 +92,7 @@ AppThunk> => async ( dispatch(countdownTimerActions.setCountdownTimer({ timeLeft, isStarted: false })); - dispatch(countdownTimerActions.setCountdownTimerTimeInit({ timeLeft, isStarted: false })); + dispatch(countdownTimerActions.setCountdownTimerTimeInit({ timeInit: timeLeft, isStarted: false })); } catch (error) { logger.error('setCountdownTimer() [error:"%o"]', error); diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 4b66f0e9..4430d63b 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -54,13 +54,13 @@ const createCountdownTimerMiddleware = ({ case 'moderator:setCountdownTimer': { - const { timeLeft, isStarted } = notification.data; + const { timeLeft, timeInit, isStarted } = notification.data; dispatch(countdownTimerActions.setCountdownTimer( { timeLeft, isStarted })); dispatch(countdownTimerActions.setCountdownTimerTimeInit( - { timeLeft, isStarted })); + { timeInit, isStarted })); break; } diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index 1f4bbede..f328ebcd 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -34,7 +34,7 @@ const countdownTimerSlice = createSlice({ state.timeLeft = action.payload.timeLeft; }), setCountdownTimerTimeInit: ((state, action: PayloadAction) => { - state.timeInit = action.payload.timeLeft; + state.timeInit = action.payload.timeInit; }), } }); From 6ea647660961c0d3a7ae959d18a0c22728b46f62 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 25 Apr 2024 23:21:11 +0200 Subject: [PATCH 22/49] Fix color format --- src/components/countdowntimer/CountdownTimerChip.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index a71e2c5f..dbc2009a 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -26,7 +26,7 @@ const CountdownTimerChip = (): JSX.Element => { indicatorColor = '#FFA500'; break; case percentage < 20: - indicatorColor = 'red'; + indicatorColor = '#FF0000'; break; default: indicatorColor = backgroundColor; From abd9db2996db2e752336e7519a6be0b2465f6d63 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 25 Apr 2024 23:24:14 +0200 Subject: [PATCH 23/49] Standarize names --- src/components/countdowntimer/CountdownTimerChip.tsx | 4 ++-- src/store/actions/countdownTimerActions.tsx | 2 +- src/store/actions/roomActions.tsx | 2 +- src/store/middlewares/countdownTimerMiddleware.tsx | 6 +++--- src/store/slices/countdownTimerSlice.tsx | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index dbc2009a..c9269c5a 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -7,9 +7,9 @@ import moment from 'moment'; const CountdownTimerChip = (): JSX.Element => { const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); const timeLeft = useAppSelector((state) => state.countdownTimer.timeLeft); - const timeInit = useAppSelector((state) => state.countdownTimer.timeInit); + const timeSet = useAppSelector((state) => state.countdownTimer.timeSet); - const totalTime = moment.duration(timeInit); + const totalTime = moment.duration(timeSet); const leftTime = moment.duration(timeLeft); const totalSeconds = totalTime.asSeconds(); diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 4aaf03fa..57fc54fa 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -92,7 +92,7 @@ AppThunk> => async ( dispatch(countdownTimerActions.setCountdownTimer({ timeLeft, isStarted: false })); - dispatch(countdownTimerActions.setCountdownTimerTimeInit({ timeInit: timeLeft, isStarted: false })); + dispatch(countdownTimerActions.setCountdownTimerTimeSet({ timeSet: timeLeft, isStarted: false })); } catch (error) { logger.error('setCountdownTimer() [error:"%o"]', error); diff --git a/src/store/actions/roomActions.tsx b/src/store/actions/roomActions.tsx index a0a67f05..b97aa68a 100644 --- a/src/store/actions/roomActions.tsx +++ b/src/store/actions/roomActions.tsx @@ -83,7 +83,7 @@ export const joinRoom = (): AppThunk> => async ( dispatch(roomSessionsActions.addMessages({ sessionId, messages: chatHistory })); dispatch(roomSessionsActions.addFiles({ sessionId, files: fileHistory })); dispatch(countdownTimerActions.setCountdownTimer(countdownTimer)); - dispatch(countdownTimerActions.setCountdownTimerTimeInit(countdownTimer)); + dispatch(countdownTimerActions.setCountdownTimerTimeSet(countdownTimer)); dispatch(countdownTimer.isStarted ? countdownTimerActions.startCountdownTimer() : diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 4430d63b..3c5a99b9 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -54,13 +54,13 @@ const createCountdownTimerMiddleware = ({ case 'moderator:setCountdownTimer': { - const { timeLeft, timeInit, isStarted } = notification.data; + const { timeLeft, timeSet, isStarted } = notification.data; dispatch(countdownTimerActions.setCountdownTimer( { timeLeft, isStarted })); - dispatch(countdownTimerActions.setCountdownTimerTimeInit( - { timeInit, isStarted })); + dispatch(countdownTimerActions.setCountdownTimerTimeSet( + { timeSet, isStarted })); break; } diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index f328ebcd..dafe1bb5 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -3,14 +3,14 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface CountdownTimerState { isEnabled: boolean; isStarted: boolean; - timeInit: string; + timeSet: string; timeLeft: string; } const initialState : CountdownTimerState = { isEnabled: true, isStarted: false, - timeInit: '00:00:00', + timeSet: '00:00:00', timeLeft: '00:00:00', }; @@ -33,8 +33,8 @@ const countdownTimerSlice = createSlice({ setCountdownTimer: ((state, action: PayloadAction) => { state.timeLeft = action.payload.timeLeft; }), - setCountdownTimerTimeInit: ((state, action: PayloadAction) => { - state.timeInit = action.payload.timeInit; + setCountdownTimerTimeSet: ((state, action: PayloadAction) => { + state.timeSet = action.payload.timeSet; }), } }); From fff6d4bf2affa9dc8160618ca26d0f43bd1168ea Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 26 Apr 2024 10:51:03 +0200 Subject: [PATCH 24/49] Open/close timer panel when clicking on Chip --- src/components/countdowntimer/CountdownTimerChip.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index c9269c5a..427d6800 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -1,14 +1,20 @@ import React from 'react'; import { Chip } from '@mui/material'; -import { useAppSelector } from '../../store/hooks'; +import { useAppDispatch, useAppSelector } from '../../store/hooks'; +import { uiActions } from '../../store/slices/uiSlice'; import AvTimerIcon from '@mui/icons-material/AvTimer'; import moment from 'moment'; const CountdownTimerChip = (): JSX.Element => { + const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); const timeLeft = useAppSelector((state) => state.countdownTimer.timeLeft); const timeSet = useAppSelector((state) => state.countdownTimer.timeSet); + const participantListOpen = useAppSelector((state) => state.ui.participantListOpen); + + const openUsersTab = () => dispatch(uiActions.setUi({ participantListOpen: !participantListOpen })); + const totalTime = moment.duration(timeSet); const leftTime = moment.duration(timeLeft); @@ -45,6 +51,8 @@ const CountdownTimerChip = (): JSX.Element => { label={timeLeft} size="small" icon={} + onClick={() => openUsersTab()} + // on={participantListOpen} /> )} From ba680a8e12567dbdb42447b40256f59cd3fd1377 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 26 Apr 2024 10:57:50 +0200 Subject: [PATCH 25/49] Fromat code --- src/components/countdowntimer/CountdownTimerChip.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index 427d6800..ea2d0bb5 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -17,12 +17,12 @@ const CountdownTimerChip = (): JSX.Element => { const totalTime = moment.duration(timeSet); const leftTime = moment.duration(timeLeft); - const totalSeconds = totalTime.asSeconds(); const leftSeconds = leftTime.asSeconds(); const percentage = parseFloat(((leftSeconds / totalSeconds) * 100).toFixed(2)); + let indicatorColor: string; - const backgroundColor = 'rgba(128, 128, 128, 0.5)'; // Declare the 'backgroundColor' variable here + const backgroundColor: string = 'rgba(128, 128, 128, 0.5)'; // Declare the 'backgroundColor' variable here switch (true) { case percentage <= 100 && percentage >= 50: From 0aed7a81896993ffb872401a862d12efb195aea8 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 26 Apr 2024 11:12:29 +0200 Subject: [PATCH 26/49] Simplify code --- src/components/countdowntimer/CountdownTimerChip.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index ea2d0bb5..ec1278c2 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -15,11 +15,9 @@ const CountdownTimerChip = (): JSX.Element => { const openUsersTab = () => dispatch(uiActions.setUi({ participantListOpen: !participantListOpen })); - const totalTime = moment.duration(timeSet); - const leftTime = moment.duration(timeLeft); - const totalSeconds = totalTime.asSeconds(); - const leftSeconds = leftTime.asSeconds(); - const percentage = parseFloat(((leftSeconds / totalSeconds) * 100).toFixed(2)); + const timeSetSeconds = moment.duration(timeSet).asSeconds(); + const timeLeftSeconds = moment.duration(timeLeft).asSeconds(); + const percentage = parseFloat(((timeLeftSeconds / timeSetSeconds) * 100).toFixed(2)); let indicatorColor: string; const backgroundColor: string = 'rgba(128, 128, 128, 0.5)'; // Declare the 'backgroundColor' variable here From 53e612d313a756df05a3b6d23cd00a6f5115793f Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 26 Apr 2024 11:13:52 +0200 Subject: [PATCH 27/49] Format code --- .../countdowntimer/CountdownTimerChip.tsx | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index ec1278c2..14c59610 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -23,17 +23,10 @@ const CountdownTimerChip = (): JSX.Element => { const backgroundColor: string = 'rgba(128, 128, 128, 0.5)'; // Declare the 'backgroundColor' variable here switch (true) { - case percentage <= 100 && percentage >= 50: - indicatorColor = '#2E7A27'; - break; - case percentage < 50 && percentage >= 20: - indicatorColor = '#FFA500'; - break; - case percentage < 20: - indicatorColor = '#FF0000'; - break; - default: - indicatorColor = backgroundColor; + case percentage <= 100 && percentage >= 50: indicatorColor = '#2E7A27'; break; + case percentage < 50 && percentage >= 20: indicatorColor = '#FFA500'; break; + case percentage < 20: indicatorColor = '#FF0000'; break; + default: indicatorColor = backgroundColor; } return ( From 30ed7be8c31e6e3df490f0cf8a49f7c163a77c3f Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 26 Apr 2024 11:15:39 +0200 Subject: [PATCH 28/49] Clean unused --- src/components/countdowntimer/CountdownTimerChip.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index 14c59610..5e46d175 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -43,7 +43,6 @@ const CountdownTimerChip = (): JSX.Element => { size="small" icon={} onClick={() => openUsersTab()} - // on={participantListOpen} /> )} From a936e0d4ae4b3f52b5b358240f79f95bdcd2d653 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 26 Apr 2024 11:17:40 +0200 Subject: [PATCH 29/49] Standarize names --- src/components/countdowntimer/CountdownTimerChip.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index 5e46d175..61f2d7be 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -15,9 +15,9 @@ const CountdownTimerChip = (): JSX.Element => { const openUsersTab = () => dispatch(uiActions.setUi({ participantListOpen: !participantListOpen })); - const timeSetSeconds = moment.duration(timeSet).asSeconds(); - const timeLeftSeconds = moment.duration(timeLeft).asSeconds(); - const percentage = parseFloat(((timeLeftSeconds / timeSetSeconds) * 100).toFixed(2)); + const secondsSet = moment.duration(timeSet).asSeconds(); + const secondsLeft = moment.duration(timeLeft).asSeconds(); + const percentage = parseFloat(((secondsLeft / secondsSet) * 100).toFixed(2)); let indicatorColor: string; const backgroundColor: string = 'rgba(128, 128, 128, 0.5)'; // Declare the 'backgroundColor' variable here From 70905a9e69f68482a879284b6b240b1a6f6da256 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 26 Apr 2024 11:37:25 +0200 Subject: [PATCH 30/49] Clean unused --- src/views/room/Room.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index 6070dcea..3552fc75 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -14,7 +14,6 @@ import HelpButton from '../../components/controlbuttons/HelpButton'; import { useNotifier, useAppSelector, useAppDispatch } from '../../store/hooks'; import moment from 'moment'; -import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; import { countdownTimerActions as countdownTimerSlices } from '../../store/slices/countdownTimerSlice'; const Room = (): JSX.Element => { From 701b54462ad5bc542bb71fd0f4e67ad947691b67 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 27 Jun 2024 12:23:34 +0200 Subject: [PATCH 31/49] Fix name --- src/store/middlewares/countdownTimerMiddleware.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 3c5a99b9..ec854bd5 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -52,7 +52,7 @@ const createCountdownTimerMiddleware = ({ break; } - case 'moderator:setCountdownTimer': { + case 'moderator:hasSetCountdownTimer': { const { timeLeft, timeSet, isStarted } = notification.data; From cd6735def6db6719590c9c6fa53201045653dbe4 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 27 Jun 2024 14:43:14 +0200 Subject: [PATCH 32/49] Fix name --- src/components/countdowntimer/CountdownTimer.tsx | 12 ++++++------ src/components/countdowntimer/CountdownTimerChip.tsx | 6 +++--- src/store/actions/countdownTimerActions.tsx | 10 +++++----- src/store/middlewares/countdownTimerMiddleware.tsx | 8 ++++---- src/store/slices/countdownTimerSlice.tsx | 6 +++--- src/views/room/Room.tsx | 12 ++++++------ 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 59a1c112..242cae81 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -22,7 +22,7 @@ const CountdownTimer = () : JSX.Element => { const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); - const timeLeft = useAppSelector((state) => state.countdownTimer.timeLeft); + const remainingTime = useAppSelector((state) => state.countdownTimer.remainingTime); const inputRef = useRef(null); @@ -59,9 +59,9 @@ const CountdownTimer = () : JSX.Element => { sx={{ flexGrow: '1' }} variant='outlined' label='timer (hh:mm:ss)' - disabled={!isEnabled || (isStarted && timeLeft !== '00:00:00')} + disabled={!isEnabled || (isStarted && remainingTime !== '00:00:00')} type='time' - value={timeLeft} + value={remainingTime} size='small' InputLabelProps={{ shrink: true }} inputProps={{ step: '1' }} @@ -69,7 +69,7 @@ const CountdownTimer = () : JSX.Element => { dispatch(countdownTimerActions.setCountdownTimer(e.target.value)); }} onKeyDown={(e) => { - if (timeLeft !== '00:00:00') { + if (remainingTime !== '00:00:00') { if (e.key === 'Enter') { dispatch(countdownTimerActions.startCountdownTimer()); e.preventDefault(); @@ -86,7 +86,7 @@ const CountdownTimer = () : JSX.Element => { sx={{ flexGrow: '1' }} color='error' size='small' - disabled={ !isEnabled || (isStarted || timeLeft === '00:00:00') } + disabled={ !isEnabled || (isStarted || remainingTime === '00:00:00') } onClick={() => { dispatch(countdownTimerActions.setCountdownTimer('00:00:00')); }} @@ -105,7 +105,7 @@ const CountdownTimer = () : JSX.Element => { sx={{ flexGrow: '1' }} color='error' size='small' - disabled={!isEnabled || timeLeft === '00:00:00'} + disabled={!isEnabled || remainingTime === '00:00:00'} onClick={() => { if (!isStarted) { dispatch(countdownTimerActions.startCountdownTimer()); diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index 61f2d7be..6ba0b878 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -8,7 +8,7 @@ import moment from 'moment'; const CountdownTimerChip = (): JSX.Element => { const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); - const timeLeft = useAppSelector((state) => state.countdownTimer.timeLeft); + const remainingTime = useAppSelector((state) => state.countdownTimer.remainingTime); const timeSet = useAppSelector((state) => state.countdownTimer.timeSet); const participantListOpen = useAppSelector((state) => state.ui.participantListOpen); @@ -16,7 +16,7 @@ const CountdownTimerChip = (): JSX.Element => { const openUsersTab = () => dispatch(uiActions.setUi({ participantListOpen: !participantListOpen })); const secondsSet = moment.duration(timeSet).asSeconds(); - const secondsLeft = moment.duration(timeLeft).asSeconds(); + const secondsLeft = moment.duration(remainingTime).asSeconds(); const percentage = parseFloat(((secondsLeft / secondsSet) * 100).toFixed(2)); let indicatorColor: string; @@ -39,7 +39,7 @@ const CountdownTimerChip = (): JSX.Element => { background: `linear-gradient(to right, ${indicatorColor} ${percentage}%, ${backgroundColor} ${percentage}%)`, animation: `${percentage}% blink-animation 1s infinite`, }} - label={timeLeft} + label={remainingTime} size="small" icon={} onClick={() => openUsersTab()} diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 57fc54fa..9484d50c 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -79,20 +79,20 @@ AppThunk> => async ( } }; -export const setCountdownTimer = (timeLeft : string): +export const setCountdownTimer = (remainingTime : string): AppThunk> => async ( dispatch, getState, { signalingService } ): Promise => { - logger.debug('setCountdownTimer() [timeLeft:"%s"]', timeLeft); + logger.debug('setCountdownTimer() [remainingTime:"%s"]', remainingTime); try { - await signalingService.sendRequest('moderator:setCountdownTimer', { timeLeft }); + await signalingService.sendRequest('moderator:setCountdownTimer', { remainingTime }); - dispatch(countdownTimerActions.setCountdownTimer({ timeLeft, isStarted: false })); + dispatch(countdownTimerActions.setCountdownTimer({ remainingTime, isStarted: false })); - dispatch(countdownTimerActions.setCountdownTimerTimeSet({ timeSet: timeLeft, isStarted: false })); + dispatch(countdownTimerActions.setCountdownTimerTimeSet({ timeSet: remainingTime, isStarted: false })); } catch (error) { logger.error('setCountdownTimer() [error:"%o"]', error); diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index ec854bd5..b942d2ac 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -54,10 +54,10 @@ const createCountdownTimerMiddleware = ({ case 'moderator:hasSetCountdownTimer': { - const { timeLeft, timeSet, isStarted } = notification.data; + const { remainingTime, timeSet, isStarted } = notification.data; dispatch(countdownTimerActions.setCountdownTimer( - { timeLeft, isStarted })); + { remainingTime, isStarted })); dispatch(countdownTimerActions.setCountdownTimerTimeSet( { timeSet, isStarted })); @@ -67,10 +67,10 @@ const createCountdownTimerMiddleware = ({ case 'moderator:updatedCountdownTimer': { - const { timeLeft, isStarted } = notification.data; + const { remainingTime, isStarted } = notification.data; dispatch(countdownTimerActions.setCountdownTimer( - { timeLeft, isStarted })); + { remainingTime, isStarted })); break; } diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index dafe1bb5..2a303327 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -4,14 +4,14 @@ interface CountdownTimerState { isEnabled: boolean; isStarted: boolean; timeSet: string; - timeLeft: string; + remainingTime: string; } const initialState : CountdownTimerState = { isEnabled: true, isStarted: false, timeSet: '00:00:00', - timeLeft: '00:00:00', + remainingTime: '00:00:00', }; const countdownTimerSlice = createSlice({ @@ -31,7 +31,7 @@ const countdownTimerSlice = createSlice({ state.isStarted = false; }), setCountdownTimer: ((state, action: PayloadAction) => { - state.timeLeft = action.payload.timeLeft; + state.remainingTime = action.payload.remainingTime; }), setCountdownTimerTimeSet: ((state, action: PayloadAction) => { state.timeSet = action.payload.timeSet; diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index 3552fc75..d24a3b69 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -40,7 +40,7 @@ const Room = (): JSX.Element => { const handleFullscreenChange = () => setFullscreen(fscreen.fullscreenElement !== null); - const timeLeft = useAppSelector((state) => state.countdownTimer.timeLeft); + const remainingTime = useAppSelector((state) => state.countdownTimer.remainingTime); const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); const dispatch = useAppDispatch(); @@ -49,20 +49,20 @@ const Room = (): JSX.Element => { if (isStarted) { const _countdownTimerRef = setInterval(() => { - let timeLeftUnix = moment(`1000-01-01 ${timeLeft}`).unix(); + let remainingTimeUnix = moment(`1000-01-01 ${remainingTime}`).unix(); - timeLeftUnix--; + remainingTimeUnix--; - const timeLeftString = moment.unix(timeLeftUnix).format('HH:mm:ss'); + const remainingTimeString = moment.unix(remainingTimeUnix).format('HH:mm:ss'); - dispatch(countdownTimerSlices.setCountdownTimer({ timeLeft: timeLeftString })); + dispatch(countdownTimerSlices.setCountdownTimer({ remainingTime: remainingTimeString })); }, 1000); return () => { clearInterval(_countdownTimerRef); }; } - }, [ isStarted, timeLeft, dispatch ]); + }, [ isStarted, remainingTime, dispatch ]); return ( <> From e3058baf0be951c0b860c165d720e87fbf6c4ab9 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 27 Jun 2024 15:03:36 +0200 Subject: [PATCH 33/49] Fix name --- src/components/countdowntimer/CountdownTimerChip.tsx | 4 ++-- src/store/actions/countdownTimerActions.tsx | 2 +- src/store/actions/roomActions.tsx | 2 +- src/store/middlewares/countdownTimerMiddleware.tsx | 6 +++--- src/store/slices/countdownTimerSlice.tsx | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index 6ba0b878..e8e13b87 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -9,13 +9,13 @@ const CountdownTimerChip = (): JSX.Element => { const dispatch = useAppDispatch(); const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); const remainingTime = useAppSelector((state) => state.countdownTimer.remainingTime); - const timeSet = useAppSelector((state) => state.countdownTimer.timeSet); + const initialTime = useAppSelector((state) => state.countdownTimer.initialTime); const participantListOpen = useAppSelector((state) => state.ui.participantListOpen); const openUsersTab = () => dispatch(uiActions.setUi({ participantListOpen: !participantListOpen })); - const secondsSet = moment.duration(timeSet).asSeconds(); + const secondsSet = moment.duration(initialTime).asSeconds(); const secondsLeft = moment.duration(remainingTime).asSeconds(); const percentage = parseFloat(((secondsLeft / secondsSet) * 100).toFixed(2)); diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 9484d50c..a3259084 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -92,7 +92,7 @@ AppThunk> => async ( dispatch(countdownTimerActions.setCountdownTimer({ remainingTime, isStarted: false })); - dispatch(countdownTimerActions.setCountdownTimerTimeSet({ timeSet: remainingTime, isStarted: false })); + dispatch(countdownTimerActions.setCountdownTimerInitialTime({ initialTime: remainingTime, isStarted: false })); } catch (error) { logger.error('setCountdownTimer() [error:"%o"]', error); diff --git a/src/store/actions/roomActions.tsx b/src/store/actions/roomActions.tsx index b97aa68a..173ea320 100644 --- a/src/store/actions/roomActions.tsx +++ b/src/store/actions/roomActions.tsx @@ -83,7 +83,7 @@ export const joinRoom = (): AppThunk> => async ( dispatch(roomSessionsActions.addMessages({ sessionId, messages: chatHistory })); dispatch(roomSessionsActions.addFiles({ sessionId, files: fileHistory })); dispatch(countdownTimerActions.setCountdownTimer(countdownTimer)); - dispatch(countdownTimerActions.setCountdownTimerTimeSet(countdownTimer)); + dispatch(countdownTimerActions.setCountdownTimerInitialTime(countdownTimer)); dispatch(countdownTimer.isStarted ? countdownTimerActions.startCountdownTimer() : diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index b942d2ac..1bf485b3 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -54,13 +54,13 @@ const createCountdownTimerMiddleware = ({ case 'moderator:hasSetCountdownTimer': { - const { remainingTime, timeSet, isStarted } = notification.data; + const { remainingTime, initialTime, isStarted } = notification.data; dispatch(countdownTimerActions.setCountdownTimer( { remainingTime, isStarted })); - dispatch(countdownTimerActions.setCountdownTimerTimeSet( - { timeSet, isStarted })); + dispatch(countdownTimerActions.setCountdownTimerInitialTime( + { initialTime, isStarted })); break; } diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index 2a303327..c31503e8 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -3,14 +3,14 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface CountdownTimerState { isEnabled: boolean; isStarted: boolean; - timeSet: string; + initialTime: string; remainingTime: string; } const initialState : CountdownTimerState = { isEnabled: true, isStarted: false, - timeSet: '00:00:00', + initialTime: '00:00:00', remainingTime: '00:00:00', }; @@ -33,8 +33,8 @@ const countdownTimerSlice = createSlice({ setCountdownTimer: ((state, action: PayloadAction) => { state.remainingTime = action.payload.remainingTime; }), - setCountdownTimerTimeSet: ((state, action: PayloadAction) => { - state.timeSet = action.payload.timeSet; + setCountdownTimerInitialTime: ((state, action: PayloadAction) => { + state.initialTime = action.payload.initialTime; }), } }); From 96bc865788e1b41f36e836ed281cdbbe2e13f1fc Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 27 Jun 2024 19:17:52 +0200 Subject: [PATCH 34/49] tmp --- .../countdowntimer/CountdownTimer.tsx | 4 ++-- src/store/actions/countdownTimerActions.tsx | 13 +++++++------ src/store/actions/roomActions.tsx | 3 +-- .../middlewares/countdownTimerMiddleware.tsx | 19 +++++++------------ src/store/slices/countdownTimerSlice.tsx | 14 ++++++++++++-- src/views/room/Room.tsx | 2 +- 6 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 242cae81..fe920b7e 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -66,7 +66,7 @@ const CountdownTimer = () : JSX.Element => { InputLabelProps={{ shrink: true }} inputProps={{ step: '1' }} onChange={(e) => { - dispatch(countdownTimerActions.setCountdownTimer(e.target.value)); + dispatch(countdownTimerActions.setCountdownTimerInitialTime(e.target.value)); }} onKeyDown={(e) => { if (remainingTime !== '00:00:00') { @@ -88,7 +88,7 @@ const CountdownTimer = () : JSX.Element => { size='small' disabled={ !isEnabled || (isStarted || remainingTime === '00:00:00') } onClick={() => { - dispatch(countdownTimerActions.setCountdownTimer('00:00:00')); + dispatch(countdownTimerActions.setCountdownTimerInitialTime('00:00:00')); }} > diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index a3259084..41214040 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -79,20 +79,21 @@ AppThunk> => async ( } }; -export const setCountdownTimer = (remainingTime : string): +export const setCountdownTimerInitialTime = (time : string): AppThunk> => async ( dispatch, getState, { signalingService } ): Promise => { - logger.debug('setCountdownTimer() [remainingTime:"%s"]', remainingTime); + logger.debug('setCountdownTimerInitialTime() [time:"%s"]', time); try { - await signalingService.sendRequest('moderator:setCountdownTimer', { remainingTime }); + + signalingService.sendRequest('moderator:setCountdownTimerInitialTime', time); - dispatch(countdownTimerActions.setCountdownTimer({ remainingTime, isStarted: false })); - - dispatch(countdownTimerActions.setCountdownTimerInitialTime({ initialTime: remainingTime, isStarted: false })); + dispatch(countdownTimerActions.setCountdownTimerRemainingTime(time)); + + dispatch(countdownTimerActions.setCountdownTimerInitialTime(time)); } catch (error) { logger.error('setCountdownTimer() [error:"%o"]', error); diff --git a/src/store/actions/roomActions.tsx b/src/store/actions/roomActions.tsx index 173ea320..7a09c070 100644 --- a/src/store/actions/roomActions.tsx +++ b/src/store/actions/roomActions.tsx @@ -82,8 +82,7 @@ export const joinRoom = (): AppThunk> => async ( dispatch(lobbyPeersActions.addPeers(lobbyPeers)); dispatch(roomSessionsActions.addMessages({ sessionId, messages: chatHistory })); dispatch(roomSessionsActions.addFiles({ sessionId, files: fileHistory })); - dispatch(countdownTimerActions.setCountdownTimer(countdownTimer)); - dispatch(countdownTimerActions.setCountdownTimerInitialTime(countdownTimer)); + dispatch(countdownTimerActions.joinCountdownTimer(countdownTimer)); dispatch(countdownTimer.isStarted ? countdownTimerActions.startCountdownTimer() : diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 1bf485b3..0e0ce546 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -52,25 +52,20 @@ const createCountdownTimerMiddleware = ({ break; } - case 'moderator:hasSetCountdownTimer': { + case 'moderator:settedCountdownTimerInitialTime': { - const { remainingTime, initialTime, isStarted } = notification.data; - - dispatch(countdownTimerActions.setCountdownTimer( - { remainingTime, isStarted })); - - dispatch(countdownTimerActions.setCountdownTimerInitialTime( - { initialTime, isStarted })); + const time = notification.data; + dispatch(countdownTimerActions.setCountdownTimerInitialTime(time)); + break; } - case 'moderator:updatedCountdownTimer': { + case 'moderator:settedCountdownTimerRemainingTime': { - const { remainingTime, isStarted } = notification.data; + const time = notification.data; - dispatch(countdownTimerActions.setCountdownTimer( - { remainingTime, isStarted })); + dispatch(countdownTimerActions.setCountdownTimerRemainingTime(time)); break; } diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index c31503e8..93bcabb5 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -30,11 +30,21 @@ const countdownTimerSlice = createSlice({ stopCountdownTimer: ((state) => { state.isStarted = false; }), - setCountdownTimer: ((state, action: PayloadAction) => { - state.remainingTime = action.payload.remainingTime; + setCountdownTimerRemainingTime: ((state, action: PayloadAction) => { + + const time = action.payload; + + state.remainingTime = time; }), setCountdownTimerInitialTime: ((state, action: PayloadAction) => { + + const time = action.payload; + + state.initialTime = time; + }), + joinCountdownTimer: ((state, action: PayloadAction) => { state.initialTime = action.payload.initialTime; + state.remainingTime = action.payload.remainingTime; }), } }); diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index d24a3b69..fd638ff6 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -55,7 +55,7 @@ const Room = (): JSX.Element => { const remainingTimeString = moment.unix(remainingTimeUnix).format('HH:mm:ss'); - dispatch(countdownTimerSlices.setCountdownTimer({ remainingTime: remainingTimeString })); + dispatch(countdownTimerSlices.setCountdownTimerRemainingTime(remainingTimeString)); }, 1000); From f23cb282da3cbab0a1ec4bc850998c941e6c2a8c Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Wed, 3 Jul 2024 21:45:50 +0200 Subject: [PATCH 35/49] Manage mobile when format time is 00:00 --- src/components/countdowntimer/CountdownTimer.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index fe920b7e..942ded98 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -1,6 +1,7 @@ import React, { useRef } from 'react'; import { IconButton, Grid, Switch, TextField, styled } from '@mui/material'; import { HighlightOff as HighlightOffIcon, Pause as PauseIcon, PlayArrow as PlayArrowIcon } from '@mui/icons-material'; +import moment from 'moment'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; import { @@ -58,7 +59,7 @@ const CountdownTimer = () : JSX.Element => { autoFocus={!isMobile} sx={{ flexGrow: '1' }} variant='outlined' - label='timer (hh:mm:ss)' + label={(isMobile) ? 'timer (HH:mm)' : 'timer (HH:mm:ss)'} disabled={!isEnabled || (isStarted && remainingTime !== '00:00:00')} type='time' value={remainingTime} @@ -66,7 +67,11 @@ const CountdownTimer = () : JSX.Element => { InputLabelProps={{ shrink: true }} inputProps={{ step: '1' }} onChange={(e) => { - dispatch(countdownTimerActions.setCountdownTimerInitialTime(e.target.value)); + const time = (isMobile && moment(e.target.value, 'HH:mm', true).isValid()) + ? moment(`${e.target.value}:00`, 'HH:mm:ss').format('HH:mm:ss') + : moment(`${e.target.value}`, 'HH:mm:ss').format('HH:mm:ss'); + + dispatch(countdownTimerActions.setCountdownTimerInitialTime(time)); }} onKeyDown={(e) => { if (remainingTime !== '00:00:00') { From 696d74ceec4e842a5db735d63fd659bc3700894f Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Wed, 3 Jul 2024 23:28:58 +0200 Subject: [PATCH 36/49] Add Permission --- src/components/participantlist/ParticipantList.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/participantlist/ParticipantList.tsx b/src/components/participantlist/ParticipantList.tsx index 3a434093..e2d1bcce 100644 --- a/src/components/participantlist/ParticipantList.tsx +++ b/src/components/participantlist/ParticipantList.tsx @@ -39,13 +39,14 @@ const ParticipantList = (): JSX.Element => { return ( - { isModerator && } - <> + { isModerator && <> + {countdownTimerLabel()} + } { (breakoutsEnabled && (rooms.length > 0 || canCreateRooms)) && <> From 2b94d4da025430bba0a66490c959b5a92af33c2c Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 4 Jul 2024 00:43:38 +0200 Subject: [PATCH 37/49] Fix style --- src/components/countdowntimer/CountdownTimerChip.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index e8e13b87..61689f55 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -38,6 +38,7 @@ const CountdownTimerChip = (): JSX.Element => { backgroundColor: backgroundColor, background: `linear-gradient(to right, ${indicatorColor} ${percentage}%, ${backgroundColor} ${percentage}%)`, animation: `${percentage}% blink-animation 1s infinite`, + width: '86px', }} label={remainingTime} size="small" From ad9bce130cfc06ea9b250006d738d749ff617003 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 4 Jul 2024 12:29:16 +0200 Subject: [PATCH 38/49] Refactor code --- src/store/middlewares/countdownTimerMiddleware.tsx | 10 ++++++++++ src/store/slices/countdownTimerSlice.tsx | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 0e0ce546..2c465eb3 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -69,6 +69,16 @@ const createCountdownTimerMiddleware = ({ break; } + + case 'moderator:finishedCountdownTimer': { + + const isStarted = notification.data.isStarted; + const remainingTime = notification.data.remainingTime; + + dispatch(countdownTimerActions.finishCountdownTimer({ isStarted, remainingTime })); + + break; + } } } catch (error) { logger.error('error on signalService "notification" event [error:%o]', error); diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx index 93bcabb5..16ebc1c3 100644 --- a/src/store/slices/countdownTimerSlice.tsx +++ b/src/store/slices/countdownTimerSlice.tsx @@ -42,6 +42,11 @@ const countdownTimerSlice = createSlice({ state.initialTime = time; }), + finishCountdownTimer: ((state, action: PayloadAction) => { + + state.isStarted = action.payload.isStarted; + state.remainingTime = action.payload.remainingTime; + }), joinCountdownTimer: ((state, action: PayloadAction) => { state.initialTime = action.payload.initialTime; state.remainingTime = action.payload.remainingTime; From 96366ab192474fd7e1bfad8163df8845db647848 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 4 Jul 2024 12:31:06 +0200 Subject: [PATCH 39/49] Add sound notification when time's up --- public/config/config.example.js | 3 +++ public/sounds/notify-countdowntimer.mp3 | Bin 0 -> 49154 bytes .../middlewares/notificationMiddleware.tsx | 6 ++++++ src/utils/types.tsx | 5 ++++- 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 public/sounds/notify-countdowntimer.mp3 diff --git a/public/config/config.example.js b/public/config/config.example.js index c661a3d5..5b694e3b 100644 --- a/public/config/config.example.js +++ b/public/config/config.example.js @@ -131,6 +131,9 @@ var config = { 'raisedHand': { 'play': '/sounds/notify-hand.mp3' }, + 'finishedCountdownTimer': { + 'play': '/sounds/notify-countdowntimer.mp3' + }, 'default': { 'debounce': 5000, 'play': '/sounds/notify.mp3' diff --git a/public/sounds/notify-countdowntimer.mp3 b/public/sounds/notify-countdowntimer.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..bd366d0e3df7a2d6dbf578dac7f7f8d3d5a6a280 GIT binary patch literal 49154 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 literal 0 HcmV?d00001 diff --git a/src/store/middlewares/notificationMiddleware.tsx b/src/store/middlewares/notificationMiddleware.tsx index d1d3069e..069af104 100644 --- a/src/store/middlewares/notificationMiddleware.tsx +++ b/src/store/middlewares/notificationMiddleware.tsx @@ -3,6 +3,7 @@ import { lobbyPeersActions } from '../slices/lobbyPeersSlice'; import { peersActions } from '../slices/peersSlice'; import { MiddlewareOptions, RootState } from '../store'; import { roomSessionsActions } from '../slices/roomSessionsSlice'; +import { countdownTimerActions } from '../slices/countdownTimerSlice'; import { notificationsActions } from '../slices/notificationsSlice'; import { HTMLMediaElementWithSink } from '../../utils/types'; import { settingsActions } from '../slices/settingsSlice'; @@ -96,6 +97,11 @@ const createNotificationMiddleware = ({ playNotificationSounds('newPeer'); } + // Finished countdownTimer + if (countdownTimerActions.finishCountdownTimer.match(action)) { + playNotificationSounds('finishedCountdownTimer'); + } + if (settingsActions.setSelectedAudioOutputDevice.match(action) && action.payload) { attachAudioOutput(action.payload); } diff --git a/src/utils/types.tsx b/src/utils/types.tsx index 929c7b82..20dabb2b 100644 --- a/src/utils/types.tsx +++ b/src/utils/types.tsx @@ -69,6 +69,9 @@ export const defaultEdumeetConfig: EdumeetConfig = { 'raisedHand': { 'play': '/sounds/notify-hand.mp3' }, + 'finishedCountdownTimer': { + 'play': '/sounds/notify-countdowntimer.mp3' + }, 'default': { 'debounce': 5000, 'play': '/sounds/notify.mp3' @@ -156,7 +159,7 @@ export interface AudioPreset { opusMaxPlaybackRate: number; } -export type NotificationType = 'default' | 'chatMessage' | 'raisedHand'; +export type NotificationType = 'default' | 'chatMessage' | 'raisedHand' | 'finishedCountdownTimer'; export interface NotificationSound { play: string; From ad83f52cbf7a776c404a9a40bff6aace949a7c87 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 4 Jul 2024 13:16:20 +0200 Subject: [PATCH 40/49] Add text notification when time's up --- src/components/translated/translatedComponents.tsx | 5 +++++ src/store/middlewares/countdownTimerMiddleware.tsx | 7 +++++++ src/translations/en.json | 3 ++- src/translations/pl.json | 5 +++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/components/translated/translatedComponents.tsx b/src/components/translated/translatedComponents.tsx index c1c9fe43..b09dac5d 100644 --- a/src/components/translated/translatedComponents.tsx +++ b/src/components/translated/translatedComponents.tsx @@ -801,4 +801,9 @@ export const countdownTimerDisableLabel = (): string => intl.formatMessage({ export const countdownTimerSetLabel = (): string => intl.formatMessage({ id: 'countdownTimer.set', defaultMessage: 'Set' +}); + +export const countdownTimerFinishedLabel = (): string => intl.formatMessage({ + id: 'countdownTimer.finished', + defaultMessage: 'Time is up!' }); \ No newline at end of file diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 2c465eb3..b51952c1 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -4,6 +4,8 @@ import { Logger } from '../../utils/Logger'; import { countdownTimerActions } from '../slices/countdownTimerSlice'; import { signalingActions } from '../slices/signalingSlice'; import { AppDispatch, MiddlewareOptions, RootState } from '../store'; +import { notificationsActions } from '../slices/notificationsSlice'; +import { countdownTimerFinishedLabel } from '../../components/translated/translatedComponents'; const logger = new Logger('ChatMiddleware'); @@ -76,6 +78,11 @@ const createCountdownTimerMiddleware = ({ const remainingTime = notification.data.remainingTime; dispatch(countdownTimerActions.finishCountdownTimer({ isStarted, remainingTime })); + + dispatch(notificationsActions.enqueueNotification({ + message: countdownTimerFinishedLabel(), + options: { variant: 'info' } + })); break; } diff --git a/src/translations/en.json b/src/translations/en.json index 71bbf699..74110d85 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -278,5 +278,6 @@ "svc.mgmtUnavailable": "Management service: Unavailable", "svc.mediaNodeUnavailable": "Media-node service: Unavailable", "svc.mediaConnectionNodeError": "Media-node service: Connection error", - "svc.mediaConnectionNodeSuccess": "Media-Node service: Got connection" + "svc.mediaConnectionNodeSuccess": "Media-Node service: Got connection", + "countdownTimer.finished": "Time is up!" } diff --git a/src/translations/pl.json b/src/translations/pl.json index 251f32cc..e79add1a 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -280,5 +280,6 @@ "svc.mediaConnectionNodeSuccess": "Usługa węzła mediów: połączono", "tooltip.coundowntimer.start": "Start", "tooltip.coundowntimer.stop": "Stop", - "tooltip.coundowntimer.pause": "Pause" -} + "tooltip.coundowntimer.pause": "Pause", + "countdownTimer.finished": "Czas minął!" +} \ No newline at end of file From 95124096c70e9f8aa7d7ead3a679583f6c8ddc20 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 4 Jul 2024 15:12:13 +0200 Subject: [PATCH 41/49] Merge countdownTimerSlice with roomSlice --- .../countdowntimer/CountdownTimer.tsx | 6 +- .../countdowntimer/CountdownTimerChip.tsx | 6 +- src/store/actions/countdownTimerActions.tsx | 10 ++-- src/store/actions/roomActions.tsx | 7 +-- .../middlewares/countdownTimerMiddleware.tsx | 16 ++--- .../middlewares/notificationMiddleware.tsx | 4 +- src/store/slices/countdownTimerSlice.tsx | 58 ------------------- src/store/slices/roomSlice.tsx | 49 +++++++++++++++- src/store/store.tsx | 2 - src/views/room/Room.tsx | 8 +-- 10 files changed, 76 insertions(+), 90 deletions(-) delete mode 100644 src/store/slices/countdownTimerSlice.tsx diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index 942ded98..cfd54674 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -21,9 +21,9 @@ const CountdownTimerDiv = styled('div')(({ theme }) => ({ const CountdownTimer = () : JSX.Element => { const isMobile = useAppSelector(isMobileSelector); const dispatch = useAppDispatch(); - const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); - const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); - const remainingTime = useAppSelector((state) => state.countdownTimer.remainingTime); + const isEnabled = useAppSelector((state) => state.room.countdownTimer.isEnabled); + const isStarted = useAppSelector((state) => state.room.countdownTimer.isStarted); + const remainingTime = useAppSelector((state) => state.room.countdownTimer.remainingTime); const inputRef = useRef(null); diff --git a/src/components/countdowntimer/CountdownTimerChip.tsx b/src/components/countdowntimer/CountdownTimerChip.tsx index 61689f55..0c047408 100644 --- a/src/components/countdowntimer/CountdownTimerChip.tsx +++ b/src/components/countdowntimer/CountdownTimerChip.tsx @@ -7,9 +7,9 @@ import moment from 'moment'; const CountdownTimerChip = (): JSX.Element => { const dispatch = useAppDispatch(); - const isEnabled = useAppSelector((state) => state.countdownTimer.isEnabled); - const remainingTime = useAppSelector((state) => state.countdownTimer.remainingTime); - const initialTime = useAppSelector((state) => state.countdownTimer.initialTime); + const isEnabled = useAppSelector((state) => state.room.countdownTimer.isEnabled); + const remainingTime = useAppSelector((state) => state.room.countdownTimer.remainingTime); + const initialTime = useAppSelector((state) => state.room.countdownTimer.initialTime); const participantListOpen = useAppSelector((state) => state.ui.participantListOpen); diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 41214040..764830d5 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -1,5 +1,5 @@ import { Logger } from '../../utils/Logger'; -import { countdownTimerActions } from '../slices/countdownTimerSlice'; +import { roomActions } from '../slices/roomSlice'; import { AppThunk } from '../store'; const logger = new Logger('CountdownTimerActions'); @@ -22,7 +22,7 @@ AppThunk> => async ( try { await signalingService.sendRequest('moderator:enableCountdownTimer'); - dispatch(countdownTimerActions.enableCountdownTimer()); + dispatch(roomActions.enableCountdownTimer()); } catch (error) { logger.error('moderator:enableCountdownTimer() [error:"%o"]', error); } @@ -41,7 +41,7 @@ AppThunk> => async ( // const peerId = getState().me.id; - dispatch(countdownTimerActions.disableCountdownTimer()); + dispatch(roomActions.disableCountdownTimer()); } catch (error) { logger.error('moderator:disableCountdownTimer() [error:"%o"]', error); } @@ -91,9 +91,9 @@ AppThunk> => async ( signalingService.sendRequest('moderator:setCountdownTimerInitialTime', time); - dispatch(countdownTimerActions.setCountdownTimerRemainingTime(time)); + dispatch(roomActions.setCountdownTimerRemainingTime(time)); - dispatch(countdownTimerActions.setCountdownTimerInitialTime(time)); + dispatch(roomActions.setCountdownTimerInitialTime(time)); } catch (error) { logger.error('setCountdownTimer() [error:"%o"]', error); diff --git a/src/store/actions/roomActions.tsx b/src/store/actions/roomActions.tsx index 7a09c070..c9080af5 100644 --- a/src/store/actions/roomActions.tsx +++ b/src/store/actions/roomActions.tsx @@ -11,7 +11,6 @@ import { initialRoomSession, roomSessionsActions } from '../slices/roomSessionsS import { getSignalingUrl } from '../../utils/signalingHelpers'; import { getTenantFromFqdn } from './managementActions'; import { Logger } from '../../utils/Logger'; -import { countdownTimerActions } from '../slices/countdownTimerSlice'; const logger = new Logger('RoomActions'); @@ -82,11 +81,11 @@ export const joinRoom = (): AppThunk> => async ( dispatch(lobbyPeersActions.addPeers(lobbyPeers)); dispatch(roomSessionsActions.addMessages({ sessionId, messages: chatHistory })); dispatch(roomSessionsActions.addFiles({ sessionId, files: fileHistory })); - dispatch(countdownTimerActions.joinCountdownTimer(countdownTimer)); + dispatch(roomActions.joinCountdownTimer(countdownTimer)); dispatch(countdownTimer.isStarted ? - countdownTimerActions.startCountdownTimer() : - countdownTimerActions.stopCountdownTimer() + roomActions.startCountdownTimer() : + roomActions.stopCountdownTimer() ); }); diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index b51952c1..74d6031c 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -1,7 +1,7 @@ import { Middleware } from '@reduxjs/toolkit'; import { Logger } from '../../utils/Logger'; -import { countdownTimerActions } from '../slices/countdownTimerSlice'; +import { roomActions } from '../slices/roomSlice'; import { signalingActions } from '../slices/signalingSlice'; import { AppDispatch, MiddlewareOptions, RootState } from '../store'; import { notificationsActions } from '../slices/notificationsSlice'; @@ -29,27 +29,27 @@ const createCountdownTimerMiddleware = ({ case 'moderator:enabledCountdownTimer': { - dispatch(countdownTimerActions.enableCountdownTimer()); + dispatch(roomActions.enableCountdownTimer()); break; } case 'moderator:disabledCountdownTimer': { - dispatch(countdownTimerActions.disableCountdownTimer()); + dispatch(roomActions.disableCountdownTimer()); break; } case 'moderator:startedCountdownTimer': { - dispatch(countdownTimerActions.startCountdownTimer()); + dispatch(roomActions.startCountdownTimer()); break; } case 'moderator:stoppedCountdownTimer': { - dispatch(countdownTimerActions.stopCountdownTimer()); + dispatch(roomActions.stopCountdownTimer()); break; } @@ -58,7 +58,7 @@ const createCountdownTimerMiddleware = ({ const time = notification.data; - dispatch(countdownTimerActions.setCountdownTimerInitialTime(time)); + dispatch(roomActions.setCountdownTimerInitialTime(time)); break; } @@ -67,7 +67,7 @@ const createCountdownTimerMiddleware = ({ const time = notification.data; - dispatch(countdownTimerActions.setCountdownTimerRemainingTime(time)); + dispatch(roomActions.setCountdownTimerRemainingTime(time)); break; } @@ -77,7 +77,7 @@ const createCountdownTimerMiddleware = ({ const isStarted = notification.data.isStarted; const remainingTime = notification.data.remainingTime; - dispatch(countdownTimerActions.finishCountdownTimer({ isStarted, remainingTime })); + dispatch(roomActions.finishCountdownTimer({ isStarted, remainingTime })); dispatch(notificationsActions.enqueueNotification({ message: countdownTimerFinishedLabel(), diff --git a/src/store/middlewares/notificationMiddleware.tsx b/src/store/middlewares/notificationMiddleware.tsx index 069af104..a97b68c3 100644 --- a/src/store/middlewares/notificationMiddleware.tsx +++ b/src/store/middlewares/notificationMiddleware.tsx @@ -3,7 +3,7 @@ import { lobbyPeersActions } from '../slices/lobbyPeersSlice'; import { peersActions } from '../slices/peersSlice'; import { MiddlewareOptions, RootState } from '../store'; import { roomSessionsActions } from '../slices/roomSessionsSlice'; -import { countdownTimerActions } from '../slices/countdownTimerSlice'; +import { roomActions } from '../slices/roomSlice'; import { notificationsActions } from '../slices/notificationsSlice'; import { HTMLMediaElementWithSink } from '../../utils/types'; import { settingsActions } from '../slices/settingsSlice'; @@ -98,7 +98,7 @@ const createNotificationMiddleware = ({ } // Finished countdownTimer - if (countdownTimerActions.finishCountdownTimer.match(action)) { + if (roomActions.finishCountdownTimer.match(action)) { playNotificationSounds('finishedCountdownTimer'); } diff --git a/src/store/slices/countdownTimerSlice.tsx b/src/store/slices/countdownTimerSlice.tsx deleted file mode 100644 index 16ebc1c3..00000000 --- a/src/store/slices/countdownTimerSlice.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; - -interface CountdownTimerState { - isEnabled: boolean; - isStarted: boolean; - initialTime: string; - remainingTime: string; -} - -const initialState : CountdownTimerState = { - isEnabled: true, - isStarted: false, - initialTime: '00:00:00', - remainingTime: '00:00:00', -}; - -const countdownTimerSlice = createSlice({ - name: 'countdownTimer', - initialState, - reducers: { - enableCountdownTimer: ((state) => { - state.isEnabled = true; - }), - disableCountdownTimer: ((state) => { - state.isEnabled = false; - }), - startCountdownTimer: ((state) => { - state.isStarted = true; - }), - stopCountdownTimer: ((state) => { - state.isStarted = false; - }), - setCountdownTimerRemainingTime: ((state, action: PayloadAction) => { - - const time = action.payload; - - state.remainingTime = time; - }), - setCountdownTimerInitialTime: ((state, action: PayloadAction) => { - - const time = action.payload; - - state.initialTime = time; - }), - finishCountdownTimer: ((state, action: PayloadAction) => { - - state.isStarted = action.payload.isStarted; - state.remainingTime = action.payload.remainingTime; - }), - joinCountdownTimer: ((state, action: PayloadAction) => { - state.initialTime = action.payload.initialTime; - state.remainingTime = action.payload.remainingTime; - }), - } -}); - -export const countdownTimerActions = countdownTimerSlice.actions; -export default countdownTimerSlice; \ No newline at end of file diff --git a/src/store/slices/roomSlice.tsx b/src/store/slices/roomSlice.tsx index 09a1d374..02c4d135 100644 --- a/src/store/slices/roomSlice.tsx +++ b/src/store/slices/roomSlice.tsx @@ -5,6 +5,13 @@ export type RoomConnectionState = 'new' | 'lobby' | 'joined' | 'left'; export type RoomMode = 'P2P' | 'SFU'; export type VideoCodec = 'vp8' | 'vp9' | 'h264' | 'h265' | 'av1'; +interface CountdownTimerState { + isEnabled: boolean; + isStarted: boolean; + initialTime: string; + remainingTime: string; +} + export interface RoomState { headless?: boolean; logo?: string; @@ -36,9 +43,10 @@ export interface RoomState { audioCodec?: string; screenSharingCodec?: VideoCodec; screenSharingSimulcast?: boolean; + countdownTimer: CountdownTimerState; } -type RoomUpdate = Omit; +type RoomUpdate = Omit; const initialState: RoomState = { logo: edumeetConfig.theme.logo, @@ -56,6 +64,12 @@ const initialState: RoomState = { audioCodec: 'opus', screenSharingCodec: 'vp8', screenSharingSimulcast: edumeetConfig.simulcastSharing, + countdownTimer: { + isEnabled: true, + isStarted: false, + initialTime: '00:00:00', + remainingTime: '00:00:00', + }, }; const roomSlice = createSlice({ @@ -77,6 +91,39 @@ const roomSlice = createSlice({ ) => { state.state = action.payload; }), + enableCountdownTimer: ((state) => { + state.countdownTimer.isEnabled = true; + }), + disableCountdownTimer: ((state) => { + state.countdownTimer.isEnabled = false; + }), + startCountdownTimer: ((state) => { + state.countdownTimer.isStarted = true; + }), + stopCountdownTimer: ((state) => { + state.countdownTimer.isStarted = false; + }), + setCountdownTimerRemainingTime: ((state, action: PayloadAction) => { + + const time = action.payload; + + state.countdownTimer.remainingTime = time; + }), + setCountdownTimerInitialTime: ((state, action: PayloadAction) => { + + const time = action.payload; + + state.countdownTimer.initialTime = time; + }), + finishCountdownTimer: ((state, action: PayloadAction) => { + + state.countdownTimer.isStarted = action.payload.isStarted; + state.countdownTimer.remainingTime = action.payload.remainingTime; + }), + joinCountdownTimer: ((state, action: PayloadAction) => { + state.countdownTimer.initialTime = action.payload.initialTime; + state.countdownTimer.remainingTime = action.payload.remainingTime; + }), } }); diff --git a/src/store/store.tsx b/src/store/store.tsx index f4e30094..9a0d02ec 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -30,7 +30,6 @@ import createNotificationMiddleware from './middlewares/notificationMiddleware'; import createCountdownTimerMiddleware from './middlewares/countdownTimerMiddleware'; import roomSlice from './slices/roomSlice'; import meSlice from './slices/meSlice'; -import countdownTimerSlice from './slices/countdownTimerSlice'; import consumersSlice from './slices/consumersSlice'; import signalingSlice from './slices/signalingSlice'; import permissionsSlice from './slices/permissionsSlice'; @@ -126,7 +125,6 @@ const reducer = combineReducers({ settings: settingsSlice.reducer, signaling: signalingSlice.reducer, ui: uiSlice.reducer, - countdownTimer: countdownTimerSlice.reducer, }); const pReducer = persistReducer(persistConfig, reducer); diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index fd638ff6..6accef79 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -14,7 +14,7 @@ import HelpButton from '../../components/controlbuttons/HelpButton'; import { useNotifier, useAppSelector, useAppDispatch } from '../../store/hooks'; import moment from 'moment'; -import { countdownTimerActions as countdownTimerSlices } from '../../store/slices/countdownTimerSlice'; +import { roomActions as roomSlices } from '../../store/slices/roomSlice'; const Room = (): JSX.Element => { useNotifier(); @@ -40,8 +40,8 @@ const Room = (): JSX.Element => { const handleFullscreenChange = () => setFullscreen(fscreen.fullscreenElement !== null); - const remainingTime = useAppSelector((state) => state.countdownTimer.remainingTime); - const isStarted = useAppSelector((state) => state.countdownTimer.isStarted); + const remainingTime = useAppSelector((state) => state.room.countdownTimer.remainingTime); + const isStarted = useAppSelector((state) => state.room.countdownTimer.isStarted); const dispatch = useAppDispatch(); useEffect(() => { @@ -55,7 +55,7 @@ const Room = (): JSX.Element => { const remainingTimeString = moment.unix(remainingTimeUnix).format('HH:mm:ss'); - dispatch(countdownTimerSlices.setCountdownTimerRemainingTime(remainingTimeString)); + dispatch(roomSlices.setCountdownTimerRemainingTime(remainingTimeString)); }, 1000); From c8b6c0b96d6c0a22458ceb2fcb4b0fa52f4bb791 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 4 Jul 2024 23:23:31 +0200 Subject: [PATCH 42/49] Revert showing participant list (for testing) --- src/store/slices/uiSlice.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/slices/uiSlice.tsx b/src/store/slices/uiSlice.tsx index a15a1bf9..02faf3e9 100644 --- a/src/store/slices/uiSlice.tsx +++ b/src/store/slices/uiSlice.tsx @@ -36,7 +36,7 @@ const initialState: UiState = { extraAudioDialogOpen: false, currentSettingsTab: 'media', chatOpen: false, - participantListOpen: true, + participantListOpen: false, }; const uiSlice = createSlice({ From b94ed07baef1640a14d97322f3df43f1e93731ef Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 4 Jul 2024 23:24:52 +0200 Subject: [PATCH 43/49] Remove unused Label --- src/components/translated/translatedComponents.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/translated/translatedComponents.tsx b/src/components/translated/translatedComponents.tsx index b09dac5d..914aa0fa 100644 --- a/src/components/translated/translatedComponents.tsx +++ b/src/components/translated/translatedComponents.tsx @@ -783,11 +783,6 @@ export const countdownTimerStopLabel = (): string => intl.formatMessage({ defaultMessage: 'Stop' }); -export const countdownTimerPauseLabel = (): string => intl.formatMessage({ - id: 'countdownTimer.pause', - defaultMessage: 'Pause' -}); - export const countdownTimerEnableLabel = (): string => intl.formatMessage({ id: 'countdownTimer.enable', defaultMessage: 'Enable' From 8bacd7adf654f463b5484d0305388e11f6bacb82 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 5 Jul 2024 13:16:06 +0200 Subject: [PATCH 44/49] Update translations --- .../participantlist/ParticipantList.tsx | 4 ++-- .../translated/translatedComponents.tsx | 18 +++++++++--------- src/translations/en.json | 10 ++++++++-- src/translations/pl.json | 8 +++++++- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/components/participantlist/ParticipantList.tsx b/src/components/participantlist/ParticipantList.tsx index e2d1bcce..2204dca7 100644 --- a/src/components/participantlist/ParticipantList.tsx +++ b/src/components/participantlist/ParticipantList.tsx @@ -9,7 +9,7 @@ import { permissions } from '../../utils/roles'; import { breakoutRoomsLabel, participantsLabel, - countdownTimerLabel + countdownTimerTitleLabel } from '../translated/translatedComponents'; import ListMe from './ListMe'; import ListModerator from './ListModerator'; @@ -42,7 +42,7 @@ const ParticipantList = (): JSX.Element => { { isModerator && <> - {countdownTimerLabel()} + {countdownTimerTitleLabel()} diff --git a/src/components/translated/translatedComponents.tsx b/src/components/translated/translatedComponents.tsx index 914aa0fa..0e941504 100644 --- a/src/components/translated/translatedComponents.tsx +++ b/src/components/translated/translatedComponents.tsx @@ -768,37 +768,37 @@ export const roomServerConnectionError = (message: string): string => intl.forma defaultMessage: `Room-server: ${message}` }); -export const countdownTimerLabel = (): string => intl.formatMessage({ - id: 'countdownTimer', +export const countdownTimerTitleLabel = (): string => intl.formatMessage({ + id: 'label.countdownTimer.title', defaultMessage: 'Countdown timer' }); export const countdownTimerStartLabel = (): string => intl.formatMessage({ - id: 'countdownTimer.start', + id: 'label.countdownTimer.start', defaultMessage: 'Start' }); export const countdownTimerStopLabel = (): string => intl.formatMessage({ - id: 'countdownTimer.stop', + id: 'label.countdownTimer.stop', defaultMessage: 'Stop' }); export const countdownTimerEnableLabel = (): string => intl.formatMessage({ - id: 'countdownTimer.enable', + id: 'label.countdownTimer.enable', defaultMessage: 'Enable' }); export const countdownTimerDisableLabel = (): string => intl.formatMessage({ - id: 'countdownTimer.disable', - defaultMessage: 'Enable' + id: 'label.countdownTimer.disable', + defaultMessage: 'Disable' }); export const countdownTimerSetLabel = (): string => intl.formatMessage({ - id: 'countdownTimer.set', + id: 'label.countdownTimer.set', defaultMessage: 'Set' }); export const countdownTimerFinishedLabel = (): string => intl.formatMessage({ - id: 'countdownTimer.finished', + id: 'label.countdownTimer.finished', defaultMessage: 'Time is up!' }); \ No newline at end of file diff --git a/src/translations/en.json b/src/translations/en.json index 74110d85..690c1fdb 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -279,5 +279,11 @@ "svc.mediaNodeUnavailable": "Media-node service: Unavailable", "svc.mediaConnectionNodeError": "Media-node service: Connection error", "svc.mediaConnectionNodeSuccess": "Media-Node service: Got connection", - "countdownTimer.finished": "Time is up!" -} + "label.countdownTimer.title": "Countdown timer", + "label.countdownTimer.enable": "Enable", + "label.countdownTimer.disable": "Disable", + "label.countdownTimer.start": "Start", + "label.countdownTimer.stop": "Stop", + "label.countdownTimer.set": "Set", + "label.countdownTimer.finished": "Time is up!" +} \ No newline at end of file diff --git a/src/translations/pl.json b/src/translations/pl.json index e79add1a..531f198d 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -281,5 +281,11 @@ "tooltip.coundowntimer.start": "Start", "tooltip.coundowntimer.stop": "Stop", "tooltip.coundowntimer.pause": "Pause", - "countdownTimer.finished": "Czas minął!" + "label.countdownTimer.title": "Timer", + "label.countdownTimer.enable": "Włącz", + "label.countdownTimer.disable": "Wyłącz", + "label.countdownTimer.start": "Start", + "label.countdownTimer.stop": "Stop", + "label.countdownTimer.set": "Ustaw", + "label.countdownTimer.finished": "Czas minął!" } \ No newline at end of file From 43ae64af4419a46d106e72e79ea08d6db7f07ba3 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 5 Jul 2024 13:25:43 +0200 Subject: [PATCH 45/49] Restore original .eslintrc --- .eslintrc | 2 -- 1 file changed, 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index 86dd8a9d..08d19288 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,8 +20,6 @@ }, "ignorePatterns": ["**/config.js", "vite.config.ts", "src/vite-env.d.ts"], "rules": { - "@typescript-eslint/no-unused-vars": ["warn", { "vars": "all", "args": "after-used" }], - "@typescript-eslint/no-explicit-any": "warn", "array-bracket-spacing": [ 2, "always", { "objectsInArrays": true, "arraysInArrays": true From 40c5760d4bcbe37643a3e98f2645aa5cedede6db Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 5 Jul 2024 13:44:49 +0200 Subject: [PATCH 46/49] Temporary disable Eslint type error reporting --- src/store/slices/roomSlice.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/store/slices/roomSlice.tsx b/src/store/slices/roomSlice.tsx index 02c4d135..43183b38 100644 --- a/src/store/slices/roomSlice.tsx +++ b/src/store/slices/roomSlice.tsx @@ -103,24 +103,24 @@ const roomSlice = createSlice({ stopCountdownTimer: ((state) => { state.countdownTimer.isStarted = false; }), - setCountdownTimerRemainingTime: ((state, action: PayloadAction) => { + setCountdownTimerRemainingTime: ((state, action: PayloadAction) => { // eslint-disable-line @typescript-eslint/no-explicit-any const time = action.payload; state.countdownTimer.remainingTime = time; }), - setCountdownTimerInitialTime: ((state, action: PayloadAction) => { + setCountdownTimerInitialTime: ((state, action: PayloadAction) => { // eslint-disable-line @typescript-eslint/no-explicit-any const time = action.payload; state.countdownTimer.initialTime = time; }), - finishCountdownTimer: ((state, action: PayloadAction) => { + finishCountdownTimer: ((state, action: PayloadAction) => { // eslint-disable-line @typescript-eslint/no-explicit-any state.countdownTimer.isStarted = action.payload.isStarted; state.countdownTimer.remainingTime = action.payload.remainingTime; }), - joinCountdownTimer: ((state, action: PayloadAction) => { + joinCountdownTimer: ((state, action: PayloadAction) => { // eslint-disable-line @typescript-eslint/no-explicit-any state.countdownTimer.initialTime = action.payload.initialTime; state.countdownTimer.remainingTime = action.payload.remainingTime; }), From d17148a78f2d44ba1c83da2eb8cb7a29d2191dab Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Fri, 5 Jul 2024 13:45:02 +0200 Subject: [PATCH 47/49] Remove unused --- src/store/middlewares/countdownTimerMiddleware.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/middlewares/countdownTimerMiddleware.tsx b/src/store/middlewares/countdownTimerMiddleware.tsx index 74d6031c..3b3c0f97 100644 --- a/src/store/middlewares/countdownTimerMiddleware.tsx +++ b/src/store/middlewares/countdownTimerMiddleware.tsx @@ -15,7 +15,7 @@ const createCountdownTimerMiddleware = ({ logger.debug('createChatMiddleware()'); const middleware: Middleware = ({ - dispatch, getState + dispatch }: { dispatch: AppDispatch, getState: () => RootState From cff2b34f139b91de3baffdcd255a398140aec787 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 19 Sep 2024 18:18:54 +0200 Subject: [PATCH 48/49] Add missing comments --- src/store/actions/countdownTimerActions.tsx | 31 +++++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/store/actions/countdownTimerActions.tsx b/src/store/actions/countdownTimerActions.tsx index 764830d5..193e7618 100644 --- a/src/store/actions/countdownTimerActions.tsx +++ b/src/store/actions/countdownTimerActions.tsx @@ -5,12 +5,10 @@ import { AppThunk } from '../store'; const logger = new Logger('CountdownTimerActions'); /** - * This thunk action sends a chat message. + * This thunk action enables the countdown timer. * - * @param message - Message to send. * @returns {AppThunk>} Promise. */ - export const enableCountdownTimer = (): AppThunk> => async ( dispatch, @@ -28,6 +26,11 @@ AppThunk> => async ( } }; +/** + * This thunk action disables the countdown timer. + * + * @returns {AppThunk>} Promise. + */ export const disableCountdownTimer = (): AppThunk> => async ( dispatch, @@ -47,6 +50,13 @@ AppThunk> => async ( } }; +/** + * + * This thunk action starts the countdown timer. + * + * @returns {AppThunk>} Promise. + * + */ export const startCountdownTimer = (): AppThunk> => async ( dispatch, @@ -63,6 +73,13 @@ AppThunk> => async ( } }; +/** + * + * This thunk action stops the countdown timer. + * + * @returns {AppThunk>} Promise. + * + */ export const stopCountdownTimer = (): AppThunk> => async ( dispatch, @@ -79,6 +96,14 @@ AppThunk> => async ( } }; +/** + * + * This thunk action sets the countdown timer remaining time. + * + * @param time - Time to set. + * @returns {AppThunk>} Promise. + * + */ export const setCountdownTimerInitialTime = (time : string): AppThunk> => async ( dispatch, From 7f8f89070c1506e7104651ed5fb28377b20bcdd6 Mon Sep 17 00:00:00 2001 From: Roman Drozd Date: Thu, 19 Sep 2024 18:25:35 +0200 Subject: [PATCH 49/49] Standarize namespace --- src/components/countdowntimer/CountdownTimer.tsx | 16 ++++++++-------- src/views/room/Room.tsx | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/countdowntimer/CountdownTimer.tsx b/src/components/countdowntimer/CountdownTimer.tsx index cfd54674..03cf7680 100644 --- a/src/components/countdowntimer/CountdownTimer.tsx +++ b/src/components/countdowntimer/CountdownTimer.tsx @@ -3,7 +3,7 @@ import { IconButton, Grid, Switch, TextField, styled } from '@mui/material'; import { HighlightOff as HighlightOffIcon, Pause as PauseIcon, PlayArrow as PlayArrowIcon } from '@mui/icons-material'; import moment from 'moment'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; -import * as countdownTimerActions from '../../store/actions/countdownTimerActions'; +import { setCountdownTimerInitialTime, startCountdownTimer, stopCountdownTimer, disableCountdownTimer, enableCountdownTimer } from '../../store/actions/countdownTimerActions'; import { countdownTimerStartLabel, countdownTimerStopLabel, countdownTimerEnableLabel, countdownTimerDisableLabel, countdownTimerSetLabel } @@ -71,12 +71,12 @@ const CountdownTimer = () : JSX.Element => { ? moment(`${e.target.value}:00`, 'HH:mm:ss').format('HH:mm:ss') : moment(`${e.target.value}`, 'HH:mm:ss').format('HH:mm:ss'); - dispatch(countdownTimerActions.setCountdownTimerInitialTime(time)); + dispatch(setCountdownTimerInitialTime(time)); }} onKeyDown={(e) => { if (remainingTime !== '00:00:00') { if (e.key === 'Enter') { - dispatch(countdownTimerActions.startCountdownTimer()); + dispatch(startCountdownTimer()); e.preventDefault(); } } @@ -93,7 +93,7 @@ const CountdownTimer = () : JSX.Element => { size='small' disabled={ !isEnabled || (isStarted || remainingTime === '00:00:00') } onClick={() => { - dispatch(countdownTimerActions.setCountdownTimerInitialTime('00:00:00')); + dispatch(setCountdownTimerInitialTime('00:00:00')); }} > @@ -113,9 +113,9 @@ const CountdownTimer = () : JSX.Element => { disabled={!isEnabled || remainingTime === '00:00:00'} onClick={() => { if (!isStarted) { - dispatch(countdownTimerActions.startCountdownTimer()); + dispatch(startCountdownTimer()); } else { - dispatch(countdownTimerActions.stopCountdownTimer()); + dispatch(stopCountdownTimer()); handleFocus(); } }} @@ -136,8 +136,8 @@ const CountdownTimer = () : JSX.Element => { disabled={isStarted} onChange={() => { dispatch(isEnabled ? - countdownTimerActions.disableCountdownTimer() : - countdownTimerActions.enableCountdownTimer() + disableCountdownTimer() : + enableCountdownTimer() ); }} color='error' diff --git a/src/views/room/Room.tsx b/src/views/room/Room.tsx index 6accef79..7ac1ba65 100644 --- a/src/views/room/Room.tsx +++ b/src/views/room/Room.tsx @@ -14,7 +14,7 @@ import HelpButton from '../../components/controlbuttons/HelpButton'; import { useNotifier, useAppSelector, useAppDispatch } from '../../store/hooks'; import moment from 'moment'; -import { roomActions as roomSlices } from '../../store/slices/roomSlice'; +import { roomActions } from '../../store/slices/roomSlice'; const Room = (): JSX.Element => { useNotifier(); @@ -55,7 +55,7 @@ const Room = (): JSX.Element => { const remainingTimeString = moment.unix(remainingTimeUnix).format('HH:mm:ss'); - dispatch(roomSlices.setCountdownTimerRemainingTime(remainingTimeString)); + dispatch(roomActions.setCountdownTimerRemainingTime(remainingTimeString)); }, 1000);