From c499af62a4154aa9712232f4d53a041959021cf0 Mon Sep 17 00:00:00 2001 From: damencho Date: Mon, 26 Aug 2024 18:31:00 -0500 Subject: [PATCH 1/6] feat(shared-video): Shows confirmation dialog before playing video. --- lang/main.json | 2 + modules/API/API.js | 2 +- react/features/shared-video/actionTypes.ts | 19 ++++++++ react/features/shared-video/actions.any.ts | 40 +++++++++++++++-- react/features/shared-video/actions.native.ts | 34 ++++++++++++++ react/features/shared-video/actions.web.ts | 28 ++++++++++++ .../components/AbstractSharedVideoDialog.tsx | 4 +- .../web/ShareVideoConfirmDialog.tsx | 45 +++++++++++++++++++ react/features/shared-video/functions.ts | 20 ++++----- react/features/shared-video/middleware.any.ts | 34 +++++++++----- react/features/shared-video/reducer.ts | 19 +++++++- 11 files changed, 218 insertions(+), 29 deletions(-) create mode 100644 react/features/shared-video/components/web/ShareVideoConfirmDialog.tsx diff --git a/lang/main.json b/lang/main.json index 74aca7c5c42..401557058d0 100644 --- a/lang/main.json +++ b/lang/main.json @@ -439,6 +439,8 @@ "shareScreenWarningD2": "you need to stop audio sharing, start screen sharing and check the \"share audio\" option.", "shareScreenWarningH1": "If you want to share just your screen:", "shareScreenWarningTitle": "You need to stop audio sharing before sharing your screen", + "shareVideoConfirmPlay": "You’re about to open an external website. Do you want to continue?", + "shareVideoConfirmPlayTitle": "{{name}} has shared a video with you.", "shareVideoLinkError": "Please provide a correct video link.", "shareVideoTitle": "Share video", "shareYourScreen": "Share your screen", diff --git a/modules/API/API.js b/modules/API/API.js index 835d00a90c8..d4887e3d7a4 100644 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -547,7 +547,7 @@ function initCommands() { }, 'start-share-video': url => { sendAnalytics(createApiEvent('share.video.start')); - const id = extractYoutubeIdOrURL(url, APP.store.getState()['features/shared-video'].allowedUrlDomains); + const id = extractYoutubeIdOrURL(url); if (id) { APP.store.dispatch(playSharedVideo(id)); diff --git a/react/features/shared-video/actionTypes.ts b/react/features/shared-video/actionTypes.ts index 1cb641ad0cc..e96d4d280c8 100644 --- a/react/features/shared-video/actionTypes.ts +++ b/react/features/shared-video/actionTypes.ts @@ -19,6 +19,25 @@ export const SET_SHARED_VIDEO_STATUS = 'SET_SHARED_VIDEO_STATUS'; */ export const RESET_SHARED_VIDEO_STATUS = 'RESET_SHARED_VIDEO_STATUS'; +/** + * The type of the action which signals to mark that dialog for asking whether to load shared video is currently shown. + * + * { + * type: SET_DIALOG_IN_PROGRESS + * } + */ +export const SET_DIALOG_IN_PROGRESS = 'SET_DIALOG_IN_PROGRESS'; + +/** + * The type of the action which signals to mark that dialog for asking whether to load shared video is shown. + * + * { + * type: SET_DIALOG_SHOWN + * } + */ +export const SET_DIALOG_SHOWN = 'SET_DIALOG_SHOWN'; + + /** * The type of the action which signals to disable or enable the shared video * button. diff --git a/react/features/shared-video/actions.any.ts b/react/features/shared-video/actions.any.ts index 8d3cd3fade9..9588d9bd1b9 100644 --- a/react/features/shared-video/actions.any.ts +++ b/react/features/shared-video/actions.any.ts @@ -3,9 +3,42 @@ import { getCurrentConference } from '../base/conference/functions'; import { openDialog } from '../base/dialog/actions'; import { getLocalParticipant } from '../base/participants/functions'; -import { RESET_SHARED_VIDEO_STATUS, SET_ALLOWED_URL_DOMAINS, SET_SHARED_VIDEO_STATUS } from './actionTypes'; +import { + RESET_SHARED_VIDEO_STATUS, + SET_ALLOWED_URL_DOMAINS, SET_DIALOG_IN_PROGRESS, + SET_DIALOG_SHOWN, + SET_SHARED_VIDEO_STATUS +} from './actionTypes'; import { SharedVideoDialog } from './components'; -import { isSharedVideoEnabled, isURLAllowedForSharedVideo } from './functions'; +import { isSharedVideoEnabled } from './functions'; + +/** + * Marks dialog is in progress. + * + * @param {boolean} value - The value to set. + * @returns {{ + * type: SET_DIALOG_IN_PROGRESS, + * }} + */ +export function setDialogInProgress(value: boolean) { + return { + type: SET_DIALOG_IN_PROGRESS, + value + }; +} + +/** + * Marks that dialog was shown. + * + * @returns {{ + * type: SET_DIALOG_SHOWN, + * }} + */ +export function setDialogShown() { + return { + type: SET_DIALOG_SHOWN + }; +} /** * Resets the status of the shared video. @@ -90,8 +123,7 @@ export function stopSharedVideo() { */ export function playSharedVideo(videoUrl: string) { return (dispatch: IStore['dispatch'], getState: IStore['getState']) => { - if (!isSharedVideoEnabled(getState()) - || !isURLAllowedForSharedVideo(videoUrl, getState()['features/shared-video'].allowedUrlDomains, true)) { + if (!isSharedVideoEnabled(getState())) { return; } const conference = getCurrentConference(getState()); diff --git a/react/features/shared-video/actions.native.ts b/react/features/shared-video/actions.native.ts index 02b37d475df..2ff785ac990 100644 --- a/react/features/shared-video/actions.native.ts +++ b/react/features/shared-video/actions.native.ts @@ -1 +1,35 @@ +import i18n from 'i18next'; + +import { IStore } from '../app/types'; +import { openDialog } from '../base/dialog/actions'; +import ConfirmDialog from '../base/dialog/components/native/ConfirmDialog'; + +import { setDialogInProgress } from './actions.any'; + export * from './actions.any'; + +/** + * Shows a confirmation dialog whether to play the external video link. + * + * @param {string} actor - The actor's name. + * @param {Function} onSubmit - The function to execute when confirmed. + * + * @returns {Function} + */ +export function showConfirmPlayingDialog(actor: String, onSubmit: Function) { + return (dispatch: IStore['dispatch']) => { + dispatch(openDialog(ConfirmDialog, { + cancelLabel: 'dialog.Cancel', + confirmLabel: 'dialog.Ok', + descriptionKey: 'dialog.shareVideoConfirmPlay', + onCancel: () => dispatch(setDialogInProgress(false)), + onSubmit: () => { + dispatch(setDialogInProgress(false)); + onSubmit(); + }, + title: i18n.t('dialog.shareVideoConfirmPlayTitle', { + name: actor + }) + })); + }; +} diff --git a/react/features/shared-video/actions.web.ts b/react/features/shared-video/actions.web.ts index 769be0e9f30..4fd2977d42b 100644 --- a/react/features/shared-video/actions.web.ts +++ b/react/features/shared-video/actions.web.ts @@ -1,4 +1,9 @@ +import { IStore } from '../app/types'; +import { openDialog } from '../base/dialog/actions'; + import { SET_DISABLE_BUTTON } from './actionTypes'; +import { setDialogInProgress, setDialogShown } from './actions.any'; +import ShareVideoConfirmDialog from './components/web/ShareVideoConfirmDialog'; export * from './actions.any'; @@ -17,3 +22,26 @@ export function setDisableButton(disabled: boolean) { disabled }; } + +/** + * Shows a confirmation dialog whether to play the external video link. + * + * @param {string} actor - The actor's name. + * @param {Function} onSubmit - The function to execute when confirmed. + * + * @returns {Function} + */ +export function showConfirmPlayingDialog(actor: String, onSubmit: Function) { + return (dispatch: IStore['dispatch']) => { + dispatch(setDialogInProgress(true)); + dispatch(setDialogShown()); + dispatch(openDialog(ShareVideoConfirmDialog, { + actorName: actor, + onCancel: () => dispatch(setDialogInProgress(false)), + onSubmit: () => { + dispatch(setDialogInProgress(false)); + onSubmit(); + } + })); + }; +} diff --git a/react/features/shared-video/components/AbstractSharedVideoDialog.tsx b/react/features/shared-video/components/AbstractSharedVideoDialog.tsx index bbccfbf656e..ff4752f5615 100644 --- a/react/features/shared-video/components/AbstractSharedVideoDialog.tsx +++ b/react/features/shared-video/components/AbstractSharedVideoDialog.tsx @@ -53,9 +53,9 @@ export default class AbstractSharedVideoDialog extends Component < IProps, S * @returns {boolean} */ _onSetVideoLink(link: string) { - const { _allowedUrlDomains, onPostSubmit } = this.props; + const { onPostSubmit } = this.props; - const id = extractYoutubeIdOrURL(link, _allowedUrlDomains); + const id = extractYoutubeIdOrURL(link); if (!id) { return false; diff --git a/react/features/shared-video/components/web/ShareVideoConfirmDialog.tsx b/react/features/shared-video/components/web/ShareVideoConfirmDialog.tsx new file mode 100644 index 00000000000..5475feb35f6 --- /dev/null +++ b/react/features/shared-video/components/web/ShareVideoConfirmDialog.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { DialogProps } from '../../../base/dialog/constants'; +import Dialog from '../../../base/ui/components/web/Dialog'; + +interface IProps extends DialogProps { + + /** + * The name of the remote participant that shared the video. + */ + actorName: string; + + /** + * The function to execute when canceled. + */ + onCancel: () => void; + + /** + * The function to execute when confirmed. + */ + onSubmit: () => void; +} + +/** + * Dialog to confirm playing a video shared from a remote participant. + * + * @returns {JSX.Element} + */ +export default function ShareVideoConfirmDialog({ actorName, onCancel, onSubmit }: IProps): JSX.Element { + const { t } = useTranslation(); + + return ( + +
+ { t('dialog.shareVideoConfirmPlay') } +
+
+ ); +} diff --git a/react/features/shared-video/functions.ts b/react/features/shared-video/functions.ts index 0db7138f730..9f07438af7f 100644 --- a/react/features/shared-video/functions.ts +++ b/react/features/shared-video/functions.ts @@ -63,10 +63,9 @@ export function isVideoPlaying(stateful: IStateful): boolean { * Extracts a Youtube id or URL from the user input. * * @param {string} input - The user input. - * @param {Array} allowedUrlDomains - The allowed URL domains for shared video. * @returns {string|undefined} */ -export function extractYoutubeIdOrURL(input: string, allowedUrlDomains?: Array) { +export function extractYoutubeIdOrURL(input: string) { if (!input) { return; } @@ -77,15 +76,17 @@ export function extractYoutubeIdOrURL(input: string, allowedUrlDomains?: Array 0; + return !disableThirdPartyRequests; } /** diff --git a/react/features/shared-video/middleware.any.ts b/react/features/shared-video/middleware.any.ts index 3fc81e3ae17..3229cebe8a7 100644 --- a/react/features/shared-video/middleware.any.ts +++ b/react/features/shared-video/middleware.any.ts @@ -8,7 +8,7 @@ import { SET_CONFIG } from '../base/config/actionTypes'; import { MEDIA_TYPE } from '../base/media/constants'; import { PARTICIPANT_LEFT } from '../base/participants/actionTypes'; import { participantJoined, participantLeft, pinParticipant } from '../base/participants/actions'; -import { getLocalParticipant, getParticipantById } from '../base/participants/functions'; +import { getLocalParticipant, getParticipantById, getParticipantDisplayName } from '../base/participants/functions'; import { FakeParticipant } from '../base/participants/types'; import MiddlewareRegistry from '../base/redux/MiddlewareRegistry'; import { SET_DYNAMIC_BRANDING_DATA } from '../dynamic-branding/actionTypes'; @@ -17,8 +17,9 @@ import { RESET_SHARED_VIDEO_STATUS, SET_SHARED_VIDEO_STATUS } from './actionType import { resetSharedVideoStatus, setAllowedUrlDomians, - setSharedVideoStatus -} from './actions.any'; + setSharedVideoStatus, + showConfirmPlayingDialog +} from './actions'; import { DEFAULT_ALLOWED_URL_DOMAINS, PLAYBACK_STATUSES, @@ -53,18 +54,29 @@ MiddlewareRegistry.register(store => next => action => { from: string; muted: string; state: string; time: string; }; value: string; }) => { const state = getState(); - if (!isURLAllowedForSharedVideo(value, getState()['features/shared-video'].allowedUrlDomains, true)) { - logger.debug(`Shared Video: Received a not allowed URL ${value}`); - - return; - } - const { from } = attributes; const sharedVideoStatus = attributes.state; if (isSharingStatus(sharedVideoStatus)) { - handleSharingVideoStatus(store, value, attributes, conference); - } else if (sharedVideoStatus === 'stop') { + if (getState()['features/shared-video'].dialogInProgress) { + return; + } + + if (isURLAllowedForSharedVideo(value) || localParticipantId === from + || getState()['features/shared-video'].dialogShown) { + handleSharingVideoStatus(store, value, attributes, conference); + } else { + dispatch(showConfirmPlayingDialog(getParticipantDisplayName(getState(), from), () => { + handleSharingVideoStatus(store, value, attributes, conference); + + return true; // on mobile this is used to close the dialog + })); + } + + return; + } + + if (sharedVideoStatus === 'stop') { const videoParticipant = getParticipantById(state, value); dispatch(participantLeft(value, conference, { diff --git a/react/features/shared-video/reducer.ts b/react/features/shared-video/reducer.ts index f3bd9d9a408..31d5a4bdd27 100644 --- a/react/features/shared-video/reducer.ts +++ b/react/features/shared-video/reducer.ts @@ -3,17 +3,23 @@ import ReducerRegistry from '../base/redux/ReducerRegistry'; import { RESET_SHARED_VIDEO_STATUS, SET_ALLOWED_URL_DOMAINS, + SET_DIALOG_IN_PROGRESS, + SET_DIALOG_SHOWN, SET_DISABLE_BUTTON, SET_SHARED_VIDEO_STATUS } from './actionTypes'; import { DEFAULT_ALLOWED_URL_DOMAINS } from './constants'; const initialState = { - allowedUrlDomains: DEFAULT_ALLOWED_URL_DOMAINS + allowedUrlDomains: DEFAULT_ALLOWED_URL_DOMAINS, + dialogInProgress: false, + dialogShown: false }; export interface ISharedVideoState { allowedUrlDomains: Array; + dialogInProgress: boolean; + dialogShown: boolean; disabled?: boolean; muted?: boolean; ownerId?: string; @@ -36,6 +42,17 @@ ReducerRegistry.register('features/shared-video', ...initialState, allowedUrlDomains: state.allowedUrlDomains }; + case SET_DIALOG_IN_PROGRESS: { + return { + ...state, + dialogInProgress: action.value + }; + } + case SET_DIALOG_SHOWN: + return { + ...state, + dialogShown: true + }; case SET_SHARED_VIDEO_STATUS: return { ...state, From 0fc9a1791489b28c349b7102115fd78ad75ab81d Mon Sep 17 00:00:00 2001 From: Calin-Teodor Date: Tue, 27 Aug 2024 15:02:08 +0300 Subject: [PATCH 2/6] feat(shared-video/native): created ShareVideoConfirmDialog and unified actions --- react/features/shared-video/actions.any.ts | 23 +++++++++- react/features/shared-video/actions.native.ts | 34 -------------- react/features/shared-video/actions.web.ts | 28 ------------ .../shared-video/components/index.native.ts | 1 + .../shared-video/components/index.web.ts | 1 + .../native/ShareVideoConfirmDialog.tsx | 44 +++++++++++++++++++ react/features/shared-video/middleware.any.ts | 5 ++- 7 files changed, 72 insertions(+), 64 deletions(-) create mode 100644 react/features/shared-video/components/native/ShareVideoConfirmDialog.tsx diff --git a/react/features/shared-video/actions.any.ts b/react/features/shared-video/actions.any.ts index 9588d9bd1b9..7e835232856 100644 --- a/react/features/shared-video/actions.any.ts +++ b/react/features/shared-video/actions.any.ts @@ -9,9 +9,10 @@ import { SET_DIALOG_SHOWN, SET_SHARED_VIDEO_STATUS } from './actionTypes'; -import { SharedVideoDialog } from './components'; +import { ShareVideoConfirmDialog, SharedVideoDialog } from './components'; import { isSharedVideoEnabled } from './functions'; + /** * Marks dialog is in progress. * @@ -175,3 +176,23 @@ export function setAllowedUrlDomians(allowedUrlDomains: Array) { allowedUrlDomains }; } + +/** + * Shows a confirmation dialog whether to play the external video link. + * + * @param {string} actor - The actor's name. + * @param {Function} onSubmit - The function to execute when confirmed. + * + * @returns {Function} + */ +export function showConfirmPlayingDialog(actor: String, onSubmit: Function) { + return (dispatch: IStore['dispatch']) => { + dispatch(setDialogInProgress(true)); + dispatch(setDialogShown()); + dispatch(openDialog(ShareVideoConfirmDialog, { + actorName: actor, + onCancel: () => dispatch(setDialogInProgress(false)), + onSubmit: () => onSubmit() + })); + }; +} diff --git a/react/features/shared-video/actions.native.ts b/react/features/shared-video/actions.native.ts index 2ff785ac990..02b37d475df 100644 --- a/react/features/shared-video/actions.native.ts +++ b/react/features/shared-video/actions.native.ts @@ -1,35 +1 @@ -import i18n from 'i18next'; - -import { IStore } from '../app/types'; -import { openDialog } from '../base/dialog/actions'; -import ConfirmDialog from '../base/dialog/components/native/ConfirmDialog'; - -import { setDialogInProgress } from './actions.any'; - export * from './actions.any'; - -/** - * Shows a confirmation dialog whether to play the external video link. - * - * @param {string} actor - The actor's name. - * @param {Function} onSubmit - The function to execute when confirmed. - * - * @returns {Function} - */ -export function showConfirmPlayingDialog(actor: String, onSubmit: Function) { - return (dispatch: IStore['dispatch']) => { - dispatch(openDialog(ConfirmDialog, { - cancelLabel: 'dialog.Cancel', - confirmLabel: 'dialog.Ok', - descriptionKey: 'dialog.shareVideoConfirmPlay', - onCancel: () => dispatch(setDialogInProgress(false)), - onSubmit: () => { - dispatch(setDialogInProgress(false)); - onSubmit(); - }, - title: i18n.t('dialog.shareVideoConfirmPlayTitle', { - name: actor - }) - })); - }; -} diff --git a/react/features/shared-video/actions.web.ts b/react/features/shared-video/actions.web.ts index 4fd2977d42b..769be0e9f30 100644 --- a/react/features/shared-video/actions.web.ts +++ b/react/features/shared-video/actions.web.ts @@ -1,9 +1,4 @@ -import { IStore } from '../app/types'; -import { openDialog } from '../base/dialog/actions'; - import { SET_DISABLE_BUTTON } from './actionTypes'; -import { setDialogInProgress, setDialogShown } from './actions.any'; -import ShareVideoConfirmDialog from './components/web/ShareVideoConfirmDialog'; export * from './actions.any'; @@ -22,26 +17,3 @@ export function setDisableButton(disabled: boolean) { disabled }; } - -/** - * Shows a confirmation dialog whether to play the external video link. - * - * @param {string} actor - The actor's name. - * @param {Function} onSubmit - The function to execute when confirmed. - * - * @returns {Function} - */ -export function showConfirmPlayingDialog(actor: String, onSubmit: Function) { - return (dispatch: IStore['dispatch']) => { - dispatch(setDialogInProgress(true)); - dispatch(setDialogShown()); - dispatch(openDialog(ShareVideoConfirmDialog, { - actorName: actor, - onCancel: () => dispatch(setDialogInProgress(false)), - onSubmit: () => { - dispatch(setDialogInProgress(false)); - onSubmit(); - } - })); - }; -} diff --git a/react/features/shared-video/components/index.native.ts b/react/features/shared-video/components/index.native.ts index 699685be5d6..14eefdd4421 100644 --- a/react/features/shared-video/components/index.native.ts +++ b/react/features/shared-video/components/index.native.ts @@ -1,3 +1,4 @@ // @ts-ignore export { default as SharedVideoDialog } from './native/SharedVideoDialog'; export { default as SharedVideoButton } from './native/SharedVideoButton'; +export { default as ShareVideoConfirmDialog } from './native/ShareVideoConfirmDialog'; diff --git a/react/features/shared-video/components/index.web.ts b/react/features/shared-video/components/index.web.ts index 617f11369fa..af18c8ecb09 100644 --- a/react/features/shared-video/components/index.web.ts +++ b/react/features/shared-video/components/index.web.ts @@ -1,2 +1,3 @@ export { default as SharedVideoDialog } from './web/SharedVideoDialog'; export { default as SharedVideoButton } from './web/SharedVideoButton'; +export { default as ShareVideoConfirmDialog } from './web/ShareVideoConfirmDialog'; diff --git a/react/features/shared-video/components/native/ShareVideoConfirmDialog.tsx b/react/features/shared-video/components/native/ShareVideoConfirmDialog.tsx new file mode 100644 index 00000000000..e652b73baf0 --- /dev/null +++ b/react/features/shared-video/components/native/ShareVideoConfirmDialog.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import ConfirmDialog from '../../../base/dialog/components/native/ConfirmDialog'; +import { DialogProps } from '../../../base/dialog/constants'; + +interface IProps extends DialogProps { + + /** + * The name of the remote participant that shared the video. + */ + actorName: string; + + /** + * The function to execute when canceled. + */ + onCancel: () => void; + + /** + * The function to execute when confirmed. + */ + onSubmit: () => void; +} + +/** + * Dialog to confirm playing a video shared from a remote participant. + * + * @returns {JSX.Element} + */ +export default function ShareVideoConfirmDialog({ actorName, onCancel, onSubmit }: IProps): JSX.Element { + const { t } = useTranslation(); + + return ( + + ); +} diff --git a/react/features/shared-video/middleware.any.ts b/react/features/shared-video/middleware.any.ts index 3229cebe8a7..e5a5008fdfe 100644 --- a/react/features/shared-video/middleware.any.ts +++ b/react/features/shared-video/middleware.any.ts @@ -17,9 +17,10 @@ import { RESET_SHARED_VIDEO_STATUS, SET_SHARED_VIDEO_STATUS } from './actionType import { resetSharedVideoStatus, setAllowedUrlDomians, + setDialogInProgress, setSharedVideoStatus, showConfirmPlayingDialog -} from './actions'; +} from './actions.any'; import { DEFAULT_ALLOWED_URL_DOMAINS, PLAYBACK_STATUSES, @@ -67,6 +68,8 @@ MiddlewareRegistry.register(store => next => action => { handleSharingVideoStatus(store, value, attributes, conference); } else { dispatch(showConfirmPlayingDialog(getParticipantDisplayName(getState(), from), () => { + dispatch(setDialogInProgress(false)); + handleSharingVideoStatus(store, value, attributes, conference); return true; // on mobile this is used to close the dialog From 79ffd0245a47bbb54b99fbec1d5adbc82ac6e590 Mon Sep 17 00:00:00 2001 From: damencho Date: Tue, 27 Aug 2024 09:13:46 -0500 Subject: [PATCH 3/6] squash: Simplifies state and fixes stop and then start scenario. --- react/features/shared-video/actionTypes.ts | 15 ++------ react/features/shared-video/actions.any.ts | 36 +++++++------------ .../native/ShareVideoConfirmDialog.tsx | 8 +---- .../web/ShareVideoConfirmDialog.tsx | 8 +---- react/features/shared-video/middleware.any.ts | 11 +++--- react/features/shared-video/reducer.ts | 19 +++------- 6 files changed, 29 insertions(+), 68 deletions(-) diff --git a/react/features/shared-video/actionTypes.ts b/react/features/shared-video/actionTypes.ts index e96d4d280c8..10a981db12e 100644 --- a/react/features/shared-video/actionTypes.ts +++ b/react/features/shared-video/actionTypes.ts @@ -20,22 +20,13 @@ export const SET_SHARED_VIDEO_STATUS = 'SET_SHARED_VIDEO_STATUS'; export const RESET_SHARED_VIDEO_STATUS = 'RESET_SHARED_VIDEO_STATUS'; /** - * The type of the action which signals to mark that dialog for asking whether to load shared video is currently shown. + * The type of the action which marks that the user had confirmed to play video. * * { - * type: SET_DIALOG_IN_PROGRESS + * type: SET_CONFIRM_SHOW_VIDEO * } */ -export const SET_DIALOG_IN_PROGRESS = 'SET_DIALOG_IN_PROGRESS'; - -/** - * The type of the action which signals to mark that dialog for asking whether to load shared video is shown. - * - * { - * type: SET_DIALOG_SHOWN - * } - */ -export const SET_DIALOG_SHOWN = 'SET_DIALOG_SHOWN'; +export const SET_CONFIRM_SHOW_VIDEO = 'SET_CONFIRM_SHOW_VIDEO'; /** diff --git a/react/features/shared-video/actions.any.ts b/react/features/shared-video/actions.any.ts index 7e835232856..693f5044201 100644 --- a/react/features/shared-video/actions.any.ts +++ b/react/features/shared-video/actions.any.ts @@ -5,8 +5,8 @@ import { getLocalParticipant } from '../base/participants/functions'; import { RESET_SHARED_VIDEO_STATUS, - SET_ALLOWED_URL_DOMAINS, SET_DIALOG_IN_PROGRESS, - SET_DIALOG_SHOWN, + SET_ALLOWED_URL_DOMAINS, + SET_CONFIRM_SHOW_VIDEO, SET_SHARED_VIDEO_STATUS } from './actionTypes'; import { ShareVideoConfirmDialog, SharedVideoDialog } from './components'; @@ -14,33 +14,20 @@ import { isSharedVideoEnabled } from './functions'; /** - * Marks dialog is in progress. + * Marks that user confirmed or not to play video. * * @param {boolean} value - The value to set. * @returns {{ - * type: SET_DIALOG_IN_PROGRESS, + * type: SET_CONFIRM_SHOW_VIDEO, * }} */ -export function setDialogInProgress(value: boolean) { +export function setConfirmShowVideo(value: boolean) { return { - type: SET_DIALOG_IN_PROGRESS, + type: SET_CONFIRM_SHOW_VIDEO, value }; } -/** - * Marks that dialog was shown. - * - * @returns {{ - * type: SET_DIALOG_SHOWN, - * }} - */ -export function setDialogShown() { - return { - type: SET_DIALOG_SHOWN - }; -} - /** * Resets the status of the shared video. * @@ -187,12 +174,15 @@ export function setAllowedUrlDomians(allowedUrlDomains: Array) { */ export function showConfirmPlayingDialog(actor: String, onSubmit: Function) { return (dispatch: IStore['dispatch']) => { - dispatch(setDialogInProgress(true)); - dispatch(setDialogShown()); + // shows only one dialog at a time + dispatch(setConfirmShowVideo(false)); + dispatch(openDialog(ShareVideoConfirmDialog, { actorName: actor, - onCancel: () => dispatch(setDialogInProgress(false)), - onSubmit: () => onSubmit() + onSubmit: () => { + dispatch(setConfirmShowVideo(true)); + onSubmit(); + } })); }; } diff --git a/react/features/shared-video/components/native/ShareVideoConfirmDialog.tsx b/react/features/shared-video/components/native/ShareVideoConfirmDialog.tsx index e652b73baf0..68afb5b5453 100644 --- a/react/features/shared-video/components/native/ShareVideoConfirmDialog.tsx +++ b/react/features/shared-video/components/native/ShareVideoConfirmDialog.tsx @@ -11,11 +11,6 @@ interface IProps extends DialogProps { */ actorName: string; - /** - * The function to execute when canceled. - */ - onCancel: () => void; - /** * The function to execute when confirmed. */ @@ -27,7 +22,7 @@ interface IProps extends DialogProps { * * @returns {JSX.Element} */ -export default function ShareVideoConfirmDialog({ actorName, onCancel, onSubmit }: IProps): JSX.Element { +export default function ShareVideoConfirmDialog({ actorName, onSubmit }: IProps): JSX.Element { const { t } = useTranslation(); return ( @@ -35,7 +30,6 @@ export default function ShareVideoConfirmDialog({ actorName, onCancel, onSubmit cancelLabel = 'dialog.Cancel' confirmLabel = 'dialog.Ok' descriptionKey = 'dialog.shareVideoConfirmPlay' - onCancel = { onCancel } onSubmit = { onSubmit } title = { t('dialog.shareVideoConfirmPlayTitle', { name: actorName diff --git a/react/features/shared-video/components/web/ShareVideoConfirmDialog.tsx b/react/features/shared-video/components/web/ShareVideoConfirmDialog.tsx index 5475feb35f6..c479d208572 100644 --- a/react/features/shared-video/components/web/ShareVideoConfirmDialog.tsx +++ b/react/features/shared-video/components/web/ShareVideoConfirmDialog.tsx @@ -11,11 +11,6 @@ interface IProps extends DialogProps { */ actorName: string; - /** - * The function to execute when canceled. - */ - onCancel: () => void; - /** * The function to execute when confirmed. */ @@ -27,12 +22,11 @@ interface IProps extends DialogProps { * * @returns {JSX.Element} */ -export default function ShareVideoConfirmDialog({ actorName, onCancel, onSubmit }: IProps): JSX.Element { +export default function ShareVideoConfirmDialog({ actorName, onSubmit }: IProps): JSX.Element { const { t } = useTranslation(); return ( next => action => { const sharedVideoStatus = attributes.state; if (isSharingStatus(sharedVideoStatus)) { - if (getState()['features/shared-video'].dialogInProgress) { + // confirmShowVideo is undefined the first time we receive + // when confirmShowVideo is false we ignore everything except stop that resets it + if (getState()['features/shared-video'].confirmShowVideo === false) { return; } - if (isURLAllowedForSharedVideo(value) || localParticipantId === from - || getState()['features/shared-video'].dialogShown) { + if (isURLAllowedForSharedVideo(value) + || localParticipantId === from + || getState()['features/shared-video'].confirmShowVideo) { // if confirmed skip asking again handleSharingVideoStatus(store, value, attributes, conference); } else { dispatch(showConfirmPlayingDialog(getParticipantDisplayName(getState(), from), () => { - dispatch(setDialogInProgress(false)); handleSharingVideoStatus(store, value, attributes, conference); diff --git a/react/features/shared-video/reducer.ts b/react/features/shared-video/reducer.ts index 31d5a4bdd27..c3e98865f9a 100644 --- a/react/features/shared-video/reducer.ts +++ b/react/features/shared-video/reducer.ts @@ -3,23 +3,19 @@ import ReducerRegistry from '../base/redux/ReducerRegistry'; import { RESET_SHARED_VIDEO_STATUS, SET_ALLOWED_URL_DOMAINS, - SET_DIALOG_IN_PROGRESS, - SET_DIALOG_SHOWN, + SET_CONFIRM_SHOW_VIDEO, SET_DISABLE_BUTTON, SET_SHARED_VIDEO_STATUS } from './actionTypes'; import { DEFAULT_ALLOWED_URL_DOMAINS } from './constants'; const initialState = { - allowedUrlDomains: DEFAULT_ALLOWED_URL_DOMAINS, - dialogInProgress: false, - dialogShown: false + allowedUrlDomains: DEFAULT_ALLOWED_URL_DOMAINS }; export interface ISharedVideoState { allowedUrlDomains: Array; - dialogInProgress: boolean; - dialogShown: boolean; + confirmShowVideo?: boolean; disabled?: boolean; muted?: boolean; ownerId?: string; @@ -42,17 +38,12 @@ ReducerRegistry.register('features/shared-video', ...initialState, allowedUrlDomains: state.allowedUrlDomains }; - case SET_DIALOG_IN_PROGRESS: { + case SET_CONFIRM_SHOW_VIDEO: { return { ...state, - dialogInProgress: action.value + confirmShowVideo: action.value }; } - case SET_DIALOG_SHOWN: - return { - ...state, - dialogShown: true - }; case SET_SHARED_VIDEO_STATUS: return { ...state, From 161797ebef8b7a8478cd09de46a2031663162673 Mon Sep 17 00:00:00 2001 From: damencho Date: Tue, 27 Aug 2024 09:27:54 -0500 Subject: [PATCH 4/6] squash: Use constants everywhere. --- react/features/shared-video/actions.any.ts | 5 +++-- .../shared-video/components/native/YoutubeVideoManager.tsx | 2 +- react/features/shared-video/constants.ts | 5 +++++ react/features/shared-video/functions.ts | 4 +++- react/features/shared-video/middleware.any.ts | 4 +++- react/features/shared-video/middleware.web.ts | 4 ++-- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/react/features/shared-video/actions.any.ts b/react/features/shared-video/actions.any.ts index 693f5044201..ba0ca210e23 100644 --- a/react/features/shared-video/actions.any.ts +++ b/react/features/shared-video/actions.any.ts @@ -10,6 +10,7 @@ import { SET_SHARED_VIDEO_STATUS } from './actionTypes'; import { ShareVideoConfirmDialog, SharedVideoDialog } from './components'; +import { PLAYBACK_START, PLAYBACK_STATUSES } from './constants'; import { isSharedVideoEnabled } from './functions'; @@ -121,7 +122,7 @@ export function playSharedVideo(videoUrl: string) { dispatch(setSharedVideoStatus({ videoUrl, - status: 'start', + status: PLAYBACK_START, time: 0, ownerId: localParticipant?.id })); @@ -140,7 +141,7 @@ export function toggleSharedVideo() { const state = getState(); const { status = '' } = state['features/shared-video']; - if ([ 'playing', 'start', 'pause' ].includes(status)) { + if ([ PLAYBACK_STATUSES.PLAYING, PLAYBACK_START, PLAYBACK_STATUSES.PAUSED ].includes(status)) { dispatch(stopSharedVideo()); } else { dispatch(showSharedVideoDialog((id: string) => dispatch(playSharedVideo(id)))); diff --git a/react/features/shared-video/components/native/YoutubeVideoManager.tsx b/react/features/shared-video/components/native/YoutubeVideoManager.tsx index e4b9a78e70e..bdefe3d0a7a 100644 --- a/react/features/shared-video/components/native/YoutubeVideoManager.tsx +++ b/react/features/shared-video/components/native/YoutubeVideoManager.tsx @@ -130,7 +130,7 @@ class YoutubeVideoManager extends AbstractVideoManager { }); } - if (event === 'playing') { + if (event === PLAYBACK_STATUSES.PLAYING) { this.setState({ paused: false }, () => { diff --git a/react/features/shared-video/constants.ts b/react/features/shared-video/constants.ts index 2305a0848f6..1a07a2cb76b 100644 --- a/react/features/shared-video/constants.ts +++ b/react/features/shared-video/constants.ts @@ -29,6 +29,11 @@ export const PLAYBACK_STATUSES = { STOPPED: 'stop' }; +/** + * Playback start state. + */ +export const PLAYBACK_START = 'start'; + /** * The domain for youtube URLs. */ diff --git a/react/features/shared-video/functions.ts b/react/features/shared-video/functions.ts index 9f07438af7f..f29d47f1a8f 100644 --- a/react/features/shared-video/functions.ts +++ b/react/features/shared-video/functions.ts @@ -4,6 +4,8 @@ import { toState } from '../base/redux/functions'; import { ALLOW_ALL_URL_DOMAINS, + PLAYBACK_START, + PLAYBACK_STATUSES, VIDEO_PLAYER_PARTICIPANT_NAME, YOUTUBE_PLAYER_PARTICIPANT_NAME, YOUTUBE_URL_DOMAIN @@ -35,7 +37,7 @@ function getYoutubeId(url: string) { * @returns {boolean} */ export function isSharingStatus(status: string) { - return [ 'playing', 'pause', 'start' ].includes(status); + return [ PLAYBACK_STATUSES.PLAYING, PLAYBACK_STATUSES.PAUSED, PLAYBACK_START ].includes(status); } diff --git a/react/features/shared-video/middleware.any.ts b/react/features/shared-video/middleware.any.ts index f2ca2ed7f96..255ed9aba4d 100644 --- a/react/features/shared-video/middleware.any.ts +++ b/react/features/shared-video/middleware.any.ts @@ -22,6 +22,7 @@ import { } from './actions.any'; import { DEFAULT_ALLOWED_URL_DOMAINS, + PLAYBACK_START, PLAYBACK_STATUSES, SHARED_VIDEO, VIDEO_PLAYER_PARTICIPANT_NAME @@ -206,7 +207,8 @@ function handleSharingVideoStatus(store: IStore, videoUrl: string, const localParticipantId = getLocalParticipant(getState())?.id; const oldStatus = getState()['features/shared-video']?.status ?? ''; - if (state === 'start' || ![ 'playing', 'pause', 'start' ].includes(oldStatus)) { + if (state === PLAYBACK_START + || ![ PLAYBACK_STATUSES.PLAYING, PLAYBACK_STATUSES.PAUSED, PLAYBACK_START ].includes(oldStatus)) { const youtubeId = videoUrl.match(/http/) ? false : videoUrl; const avatarURL = youtubeId ? `https://img.youtube.com/vi/${youtubeId}/0.jpg` : ''; diff --git a/react/features/shared-video/middleware.web.ts b/react/features/shared-video/middleware.web.ts index f9f573f9e4d..5c3eb360504 100644 --- a/react/features/shared-video/middleware.web.ts +++ b/react/features/shared-video/middleware.web.ts @@ -3,7 +3,7 @@ import { getLocalParticipant } from '../base/participants/functions'; import MiddlewareRegistry from '../base/redux/MiddlewareRegistry'; import { setDisableButton } from './actions.web'; -import { SHARED_VIDEO } from './constants'; +import { PLAYBACK_STATUSES, SHARED_VIDEO } from './constants'; import { isSharedVideoEnabled } from './functions'; import './middleware.any'; @@ -25,7 +25,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { const { from } = attributes; const status = attributes.state; - if (status === 'playing') { + if (status === PLAYBACK_STATUSES.PLAYING) { if (localParticipantId !== from) { dispatch(setDisableButton(true)); } From 67c2b3650156c81ba1afeb1e8ab3f92be9c81bd1 Mon Sep 17 00:00:00 2001 From: damencho Date: Tue, 27 Aug 2024 09:55:21 -0500 Subject: [PATCH 5/6] squash: Use helper function. --- react/features/shared-video/middleware.any.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/react/features/shared-video/middleware.any.ts b/react/features/shared-video/middleware.any.ts index 255ed9aba4d..0f14998dcb1 100644 --- a/react/features/shared-video/middleware.any.ts +++ b/react/features/shared-video/middleware.any.ts @@ -207,8 +207,7 @@ function handleSharingVideoStatus(store: IStore, videoUrl: string, const localParticipantId = getLocalParticipant(getState())?.id; const oldStatus = getState()['features/shared-video']?.status ?? ''; - if (state === PLAYBACK_START - || ![ PLAYBACK_STATUSES.PLAYING, PLAYBACK_STATUSES.PAUSED, PLAYBACK_START ].includes(oldStatus)) { + if (state === PLAYBACK_START && !isSharingStatus(oldStatus)) { const youtubeId = videoUrl.match(/http/) ? false : videoUrl; const avatarURL = youtubeId ? `https://img.youtube.com/vi/${youtubeId}/0.jpg` : ''; From e72f8e670d126668c28ae86208ad4fa8e608926c Mon Sep 17 00:00:00 2001 From: damencho Date: Tue, 27 Aug 2024 10:02:05 -0500 Subject: [PATCH 6/6] squash: Ignore any command with not matching video URL. --- react/features/shared-video/middleware.any.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/react/features/shared-video/middleware.any.ts b/react/features/shared-video/middleware.any.ts index 0f14998dcb1..328f8c8c120 100644 --- a/react/features/shared-video/middleware.any.ts +++ b/react/features/shared-video/middleware.any.ts @@ -206,6 +206,14 @@ function handleSharingVideoStatus(store: IStore, videoUrl: string, const { dispatch, getState } = store; const localParticipantId = getLocalParticipant(getState())?.id; const oldStatus = getState()['features/shared-video']?.status ?? ''; + const oldVideoUrl = getState()['features/shared-video'].videoUrl; + + if (oldVideoUrl && oldVideoUrl !== videoUrl) { + logger.warn( + `User with id: ${localParticipantId} sent videoUrl: ${videoUrl} while we are playing: ${oldVideoUrl}`); + + return; + } if (state === PLAYBACK_START && !isSharingStatus(oldStatus)) { const youtubeId = videoUrl.match(/http/) ? false : videoUrl;