From bd3ab8d8cd87248fc7caeda3ef9dbfd33599a481 Mon Sep 17 00:00:00 2001 From: damencho Date: Tue, 25 Jun 2024 09:21:41 +0300 Subject: [PATCH] squash: Implements handling of live conference and queue service. --- lang/main.json | 4 +- package.json | 1 + react/features/base/config/configType.ts | 1 + react/features/base/config/configWhitelist.ts | 1 + react/features/base/config/reducer.ts | 1 + .../conference/components/web/Conference.tsx | 16 +- .../components/native/VisitorsList.tsx | 6 +- .../components/web/VisitorsList.tsx | 12 +- react/features/visitors/actionTypes.ts | 10 ++ react/features/visitors/actions.ts | 19 ++- .../components/native/VisitorsCountLabel.tsx | 12 +- .../components/native/VisitorsQueue.tsx | 8 + .../components/web/VisitorsCountLabel.tsx | 15 +- .../visitors/components/web/VisitorsQueue.tsx | 93 ++++++++++++ react/features/visitors/constants.ts | 4 + react/features/visitors/functions.any.ts | 11 ++ react/features/visitors/middleware.ts | 137 +++++++++++++++++- react/features/visitors/reducer.ts | 9 ++ react/features/visitors/websocket-client.ts | 127 ++++++++++++++++ .../mod_room_metadata_component.lua | 6 + .../mod_visitors_component.lua | 59 ++++++++ resources/prosody-plugins/token/util.lib.lua | 62 ++++++++ 22 files changed, 597 insertions(+), 17 deletions(-) create mode 100644 react/features/visitors/components/native/VisitorsQueue.tsx create mode 100644 react/features/visitors/components/web/VisitorsQueue.tsx create mode 100644 react/features/visitors/constants.ts create mode 100644 react/features/visitors/websocket-client.ts diff --git a/lang/main.json b/lang/main.json index 30499d0860..f09db6370c 100644 --- a/lang/main.json +++ b/lang/main.json @@ -1497,7 +1497,9 @@ "noVisitorLobby": "You cannot join while there is a lobby enabled for the meeting.", "notAllowedPromotion": "A participant needs to allow your request first.", "title": "You are a visitor in the meeting" - } + }, + "waiting": "*waiting", + "waitingMessage": "You'll join the meeting as soon as it is live!" }, "volumeSlider": "Volume slider", "welcomepage": { diff --git a/package.json b/package.json index abe5f5bc25..8ab2f2d233 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@react-navigation/material-top-tabs": "6.6.3", "@react-navigation/native": "6.1.7", "@react-navigation/stack": "6.3.17", + "@stomp/stompjs": "7.0.0", "@svgr/webpack": "6.3.1", "@tensorflow/tfjs-backend-wasm": "3.13.0", "@tensorflow/tfjs-core": "3.13.0", diff --git a/react/features/base/config/configType.ts b/react/features/base/config/configType.ts index 5c2bb58515..79761ef137 100644 --- a/react/features/base/config/configType.ts +++ b/react/features/base/config/configType.ts @@ -478,6 +478,7 @@ export interface IConfig { peopleSearchQueryTypes?: string[]; peopleSearchUrl?: string; preferBosh?: boolean; + preferVisitor?: boolean; preferredTranscribeLanguage?: string; prejoinConfig?: { enabled?: boolean; diff --git a/react/features/base/config/configWhitelist.ts b/react/features/base/config/configWhitelist.ts index e57f467301..631e7ebfe4 100644 --- a/react/features/base/config/configWhitelist.ts +++ b/react/features/base/config/configWhitelist.ts @@ -197,6 +197,7 @@ export default [ 'participantsPane', 'pcStatsInterval', 'preferBosh', + 'preferVisitor', 'prejoinConfig', 'prejoinPageEnabled', 'recordingService', diff --git a/react/features/base/config/reducer.ts b/react/features/base/config/reducer.ts index 55fd34f61a..00812289cd 100644 --- a/react/features/base/config/reducer.ts +++ b/react/features/base/config/reducer.ts @@ -80,6 +80,7 @@ export interface IConfigState extends IConfig { audio?: boolean; video?: boolean; }; + queueService: string; }; } diff --git a/react/features/conference/components/web/Conference.tsx b/react/features/conference/components/web/Conference.tsx index c357e61183..a443d3990d 100644 --- a/react/features/conference/components/web/Conference.tsx +++ b/react/features/conference/components/web/Conference.tsx @@ -30,6 +30,8 @@ import JitsiPortal from '../../../toolbox/components/web/JitsiPortal'; import Toolbox from '../../../toolbox/components/web/Toolbox'; import { LAYOUT_CLASSNAMES } from '../../../video-layout/constants'; import { getCurrentLayout } from '../../../video-layout/functions.any'; +import VisitorsQueue from '../../../visitors/components/web/VisitorsQueue'; +import { showVisitorsQueue } from '../../../visitors/functions.any'; import { init } from '../../actions.web'; import { maybeShowSuboptimalExperienceNotification } from '../../functions.web'; import { @@ -100,6 +102,11 @@ interface IProps extends AbstractProps, WithTranslation { */ _showPrejoin: boolean; + /** + * If visitors queue page is visible or not. + */ + _showVisitorsQueue: boolean; + dispatch: IStore['dispatch']; } @@ -206,6 +213,7 @@ class Conference extends AbstractConference { _overflowDrawer, _showLobby, _showPrejoin, + _showVisitorsQueue, t } = this.props; @@ -257,8 +265,9 @@ class Conference extends AbstractConference { - { _showPrejoin && } - { _showLobby && } + { (_showPrejoin && !_showVisitorsQueue) && } + { (_showLobby && !_showVisitorsQueue) && } + { _showVisitorsQueue && } @@ -402,7 +411,8 @@ function _mapStateToProps(state: IReduxState) { _overflowDrawer: overflowDrawer, _roomName: getConferenceNameForTitle(state), _showLobby: getIsLobbyVisible(state), - _showPrejoin: isPrejoinPageVisible(state) + _showPrejoin: isPrejoinPageVisible(state), + _showVisitorsQueue: showVisitorsQueue(state) }; } diff --git a/react/features/participants-pane/components/native/VisitorsList.tsx b/react/features/participants-pane/components/native/VisitorsList.tsx index 9c9a8c6265..c2f5d9fed2 100644 --- a/react/features/participants-pane/components/native/VisitorsList.tsx +++ b/react/features/participants-pane/components/native/VisitorsList.tsx @@ -14,6 +14,8 @@ import styles from './styles'; const VisitorsList = () => { const visitorsCount = useSelector((state: IReduxState) => state['features/visitors'].count || 0); + const isLive = useSelector( + (state: IReduxState) => state['features/base/conference'].metadata?.visitorsLive); const dispatch = useDispatch(); @@ -30,7 +32,9 @@ const VisitorsList = () => { let title = t('participantsPane.headings.visitors', { count: visitorsCount }); - if (requests.length > 0) { + if (isLive === false) { + title = `${title} ${t('visitors.waiting')}`; + } else if (requests.length > 0) { title += t('participantsPane.headings.visitorRequests', { count: requests.length }); } diff --git a/react/features/participants-pane/components/web/VisitorsList.tsx b/react/features/participants-pane/components/web/VisitorsList.tsx index 16a3857d86..70ac758482 100644 --- a/react/features/participants-pane/components/web/VisitorsList.tsx +++ b/react/features/participants-pane/components/web/VisitorsList.tsx @@ -67,8 +67,18 @@ const useStyles = makeStyles()(theme => { export default function VisitorsList() { const requests = useSelector(getPromotionRequests); const visitorsCount = useSelector((state: IReduxState) => state['features/visitors'].count || 0); + const isLive = useSelector( + (state: IReduxState) => state['features/base/conference'].metadata?.visitorsLive); const { t } = useTranslation(); + let visititorsHeading = t('participantsPane.headings.visitors', { count: visitorsCount }); + + if (isLive === false) { + let visitorsWaiting = t('visitors.waiting'); + + visititorsHeading = `${visititorsHeading} ${visitorsWaiting}`; + } + const { classes, cx } = useStyles(); const dispatch = useDispatch(); @@ -84,7 +94,7 @@ export default function VisitorsList() { <>
- { t('participantsPane.headings.visitors', { count: visitorsCount })} + { visititorsHeading } { requests.length > 0 && t('participantsPane.headings.visitorRequests', { count: requests.length }) }
diff --git a/react/features/visitors/actionTypes.ts b/react/features/visitors/actionTypes.ts index 4dfdbc6e31..59bca2ba0e 100644 --- a/react/features/visitors/actionTypes.ts +++ b/react/features/visitors/actionTypes.ts @@ -39,6 +39,16 @@ export const VISITOR_PROMOTION_REQUEST = 'VISITOR_PROMOTION_REQUEST'; */ export const CLEAR_VISITOR_PROMOTION_REQUEST = 'CLEAR_VISITOR_PROMOTION_REQUEST'; +/** + * The type of (redux) action which sets in visitor's queue. + * + * { + * type: SET_IN_VISITORS_QUEUE, + * value: boolean + * } + */ +export const SET_IN_VISITORS_QUEUE = 'SET_IN_VISITORS_QUEUE'; + /** * The type of (redux) action which sets visitor demote actor. * diff --git a/react/features/visitors/actions.ts b/react/features/visitors/actions.ts index 59052ac25b..425257f446 100644 --- a/react/features/visitors/actions.ts +++ b/react/features/visitors/actions.ts @@ -8,11 +8,13 @@ import { getLocalParticipant } from '../base/participants/functions'; import { CLEAR_VISITOR_PROMOTION_REQUEST, I_AM_VISITOR_MODE, + SET_IN_VISITORS_QUEUE, SET_VISITORS_SUPPORTED, SET_VISITOR_DEMOTE_ACTOR, UPDATE_VISITORS_COUNT, VISITOR_PROMOTION_REQUEST } from './actionTypes'; +import { VISITORS_LIVE_ID } from './constants'; import { IPromotionRequest } from './types'; /** @@ -150,6 +152,21 @@ export function setIAmVisitor(enabled: boolean) { }; } +/** + * Sets in visitor's queue. + * + * @param {boolean} value - The new value. + * @returns {{ + * type: SET_IN_VISITORS_QUEUE, + * }} + */ +export function setInVisitorsQueue(value: boolean) { + return { + type: SET_IN_VISITORS_QUEUE, + value + }; +} + /** * Sets visitor demote actor. * @@ -205,6 +222,6 @@ export function goLive() { return (_: IStore['dispatch'], getState: IStore['getState']) => { const { conference } = getState()['features/base/conference']; - conference?.getMetadataHandler().setMetadata('visitorsLive', true); + conference?.getMetadataHandler().setMetadata(VISITORS_LIVE_ID, true); }; } diff --git a/react/features/visitors/components/native/VisitorsCountLabel.tsx b/react/features/visitors/components/native/VisitorsCountLabel.tsx index 9477522356..104bf7871e 100644 --- a/react/features/visitors/components/native/VisitorsCountLabel.tsx +++ b/react/features/visitors/components/native/VisitorsCountLabel.tsx @@ -1,11 +1,12 @@ import React from 'react'; +import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { IReduxState } from '../../../app/types'; import { IconUsers } from '../../../base/icons/svg'; import Label from '../../../base/label/components/native/Label'; import BaseTheme from '../../../base/ui/components/BaseTheme.native'; -import { getVisitorsShortText, iAmVisitor } from '../../functions'; +import { getVisitorsShortText, iAmVisitor, isVisitorsLive } from '../../functions'; const styles = { raisedHandsCountLabel: { @@ -27,13 +28,20 @@ const VisitorsCountLabel = () => { const visitorsMode = useSelector((state: IReduxState) => iAmVisitor(state)); const visitorsCount = useSelector((state: IReduxState) => state['features/visitors'].count || 0); + const { t } = useTranslation(); + const isLive = useSelector(isVisitorsLive); + let visitorsWaiting = ''; + + if (isLive === false) { + visitorsWaiting = t('visitors.waiting'); + } return !visitorsMode && visitorsCount > 0 ? (