From f85d0e6469e2026a34fe1c86622d5de0091db8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Fri, 6 Dec 2024 12:28:29 -0600 Subject: [PATCH] feat(always-on-top): Updates buttons for visitors. (#15369) * feat(always-on-top): Updates buttons for visitors. * squash: rename listener. * squash: Adds visitor to the conference joined event. * squash: fix comments and lint. * squash: fix comments. --- modules/API/external/external_api.js | 11 +++ react/features/always-on-top/Toolbar.tsx | 85 ++++++++++++++++++++++- react/features/external-api/middleware.ts | 4 +- react/features/toolbox/functions.any.ts | 4 +- 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/modules/API/external/external_api.js b/modules/API/external/external_api.js index eafcc4b79abd..0791d82c943d 100644 --- a/modules/API/external/external_api.js +++ b/modules/API/external/external_api.js @@ -398,6 +398,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter { this._participants = {}; this._myUserID = undefined; this._onStageParticipant = undefined; + this._iAmvisitor = undefined; this._setupListeners(); id++; } @@ -619,6 +620,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter { email: data.email, avatarURL: data.avatarURL }; + this._iAmvisitor = data.visitor; } // eslint-disable-next-line no-fallthrough @@ -1168,6 +1170,15 @@ export default class JitsiMeetExternalAPI extends EventEmitter { }); } + /** + * Returns whether we have joined as visitor in a meeting. + * + * @returns {boolean} - Returns true if we have joined as visitor. + */ + isVisitor() { + return this._iAmvisitor; + } + /** * Returns the avatar URL of a participant. * diff --git a/react/features/always-on-top/Toolbar.tsx b/react/features/always-on-top/Toolbar.tsx index 845e9fdddc3d..1065fe49fd6f 100644 --- a/react/features/always-on-top/Toolbar.tsx +++ b/react/features/always-on-top/Toolbar.tsx @@ -4,6 +4,8 @@ import AudioMuteButton from './AudioMuteButton'; import HangupButton from './HangupButton'; import VideoMuteButton from './VideoMuteButton'; +const { api } = window.alwaysOnTop; + /** * The type of the React {@code Component} props of {@link Toolbar}. */ @@ -25,12 +27,89 @@ interface IProps { onMouseOver: (e?: React.MouseEvent) => void; } +/** + * The type of the React {@code Component} state of {@link Toolbar}. + */ +interface IState { + + /** + * Whether audio button to be shown or not. + */ + showAudioButton: boolean; + + /** + * Whether video button to be shown or not. + */ + showVideoButton: boolean; +} + +type Props = Partial; + /** * Represents the toolbar in the Always On Top window. * * @augments Component */ -export default class Toolbar extends Component { +export default class Toolbar extends Component { + /** + * Initializes a new {@code Toolbar} instance. + * + * @param {IProps} props - The React {@code Component} props to initialize the new {@code Toolbar} instance with. + */ + constructor(props: Props) { + super(props); + + this.state = { + showAudioButton: true, + showVideoButton: true + }; + + this._videoConferenceJoinedListener = this._videoConferenceJoinedListener.bind(this); + } + + /** + * Sets listens for changing meetings while showing the toolbar. + * + * @inheritdoc + * @returns {void} + */ + componentDidMount() { + api.on('videoConferenceJoined', this._videoConferenceJoinedListener); + + this._videoConferenceJoinedListener(); + } + + /** + * Handles is visitor changes. + * + * @returns {void} + */ + _videoConferenceJoinedListener() { + // for electron clients that embed the api and are not updated + if (!api.isVisitor) { + console.warn('external API not updated'); + + return; + } + + const isNotVisitor = !api.isVisitor(); + + this.setState({ + showAudioButton: isNotVisitor, + showVideoButton: isNotVisitor + }); + } + + /** + * Removes all listeners. + * + * @inheritdoc + * @returns {void} + */ + componentWillUnmount() { + api.removeListener('videoConferenceJoined', this._videoConferenceJoinedListener); + } + /** * Implements React's {@link Component#render()}. * @@ -49,8 +128,8 @@ export default class Toolbar extends Component { className = { `toolbox-content-items always-on-top-toolbox ${className}` } onMouseOut = { onMouseOut } onMouseOver = { onMouseOver }> - - + { this.state.showAudioButton && } + { this.state.showVideoButton && } ); diff --git a/react/features/external-api/middleware.ts b/react/features/external-api/middleware.ts index bcef6ca8652f..25c7efa8ab74 100644 --- a/react/features/external-api/middleware.ts +++ b/react/features/external-api/middleware.ts @@ -29,6 +29,7 @@ import { getBaseUrl } from '../base/util/helpers'; import { appendSuffix } from '../display-name/functions'; import { SUBMIT_FEEDBACK_ERROR, SUBMIT_FEEDBACK_SUCCESS } from '../feedback/actionTypes'; import { SET_FILMSTRIP_VISIBLE } from '../filmstrip/actionTypes'; +import { iAmVisitor } from '../visitors/functions'; import './subscriber'; @@ -120,7 +121,8 @@ MiddlewareRegistry.register(store => next => action => { ), avatarURL: loadableAvatarUrl, breakoutRoom, - email + email, + visitor: iAmVisitor(state) } ); break; diff --git a/react/features/toolbox/functions.any.ts b/react/features/toolbox/functions.any.ts index dd1e985a563f..55519ea71683 100644 --- a/react/features/toolbox/functions.any.ts +++ b/react/features/toolbox/functions.any.ts @@ -2,6 +2,7 @@ import { IReduxState } from '../app/types'; import { isJwtFeatureEnabledStateless } from '../base/jwt/functions'; import { IGUMPendingState } from '../base/media/types'; import { IParticipantFeatures } from '../base/participants/types'; +import { iAmVisitor } from '../visitors/functions'; /** * Indicates if the audio mute button is disabled or not. @@ -13,7 +14,8 @@ export function isAudioMuteButtonDisabled(state: IReduxState) { const { available, muted, unmuteBlocked, gumPending } = state['features/base/media'].audio; const { startSilent } = state['features/base/config']; - return Boolean(!available || startSilent || (muted && unmuteBlocked) || gumPending !== IGUMPendingState.NONE); + return Boolean(!available || startSilent || (muted && unmuteBlocked) || gumPending !== IGUMPendingState.NONE + || iAmVisitor(state)); } /**