diff --git a/app/main.ts b/app/main.ts index d8d8109510..ffde56e734 100644 --- a/app/main.ts +++ b/app/main.ts @@ -832,6 +832,12 @@ async function createWindow() { // App dock icon bounce bounce.init(mainWindow); + mainWindow.on('hide', () => { + if (mainWindow && !windowState.shouldQuit()) { + mainWindow.webContents.send('set-media-playback-disabled', true); + } + }); + // Emitted when the window is about to be closed. // Note: We do most of our shutdown logic here because all windows are closed by // Electron before the app quits. @@ -937,6 +943,12 @@ async function createWindow() { } }); + mainWindow.on('show', () => { + if (mainWindow) { + mainWindow.webContents.send('set-media-playback-disabled', false); + } + }); + mainWindow.once('ready-to-show', async () => { getLogger().info('main window is ready-to-show'); diff --git a/ts/components/AvatarLightbox.tsx b/ts/components/AvatarLightbox.tsx index 2f8c05da84..aa0f745415 100644 --- a/ts/components/AvatarLightbox.tsx +++ b/ts/components/AvatarLightbox.tsx @@ -32,6 +32,7 @@ export function AvatarLightbox({ i18n={i18n} isViewOnce media={[]} + playbackDisabled={false} saveAttachment={noop} toggleForwardMessagesModal={noop} onMediaPlaybackStart={noop} diff --git a/ts/components/Lightbox.stories.tsx b/ts/components/Lightbox.stories.tsx index a6c55eb72c..6867acf310 100644 --- a/ts/components/Lightbox.stories.tsx +++ b/ts/components/Lightbox.stories.tsx @@ -66,6 +66,7 @@ const createProps = (overrideProps: Partial = {}): PropsType => { media, saveAttachment: action('saveAttachment'), selectedIndex, + playbackDisabled: false, toggleForwardMessagesModal: action('toggleForwardMessagesModal'), onMediaPlaybackStart: noop, onPrevAttachment: () => { diff --git a/ts/components/Lightbox.tsx b/ts/components/Lightbox.tsx index 529b49e435..424c3db300 100644 --- a/ts/components/Lightbox.tsx +++ b/ts/components/Lightbox.tsx @@ -36,6 +36,7 @@ export type PropsType = { i18n: LocalizerType; isViewOnce?: boolean; media: ReadonlyArray>; + playbackDisabled: boolean; saveAttachment: SaveAttachmentActionCreatorType; selectedIndex: number; toggleForwardMessagesModal: (messageIds: ReadonlyArray) => unknown; @@ -82,6 +83,7 @@ export function Lightbox({ saveAttachment, selectedIndex, toggleForwardMessagesModal, + playbackDisabled, onMediaPlaybackStart, onNextAttachment, onPrevAttachment, @@ -250,6 +252,16 @@ export function Lightbox({ } }, [videoElement, onMediaPlaybackStart]); + useEffect(() => { + if (!videoElement || videoElement.paused) { + return; + } + + if (playbackDisabled) { + videoElement.pause(); + } + }, [playbackDisabled, videoElement]); + useEffect(() => { const div = document.createElement('div'); document.body.appendChild(div); diff --git a/ts/state/ducks/lightbox.ts b/ts/state/ducks/lightbox.ts index b96164fab7..06b17adf32 100644 --- a/ts/state/ducks/lightbox.ts +++ b/ts/state/ducks/lightbox.ts @@ -49,11 +49,14 @@ export type LightboxStateType = hasPrevMessage: boolean; hasNextMessage: boolean; selectedIndex: number | undefined; + playbackDisabled: boolean; }; const CLOSE_LIGHTBOX = 'lightbox/CLOSE'; const SHOW_LIGHTBOX = 'lightbox/SHOW'; const SET_SELECTED_LIGHTBOX_INDEX = 'lightbox/SET_SELECTED_LIGHTBOX_INDEX'; +const SET_LIGHTBOX_PLAYBACK_DISABLED = + 'lightbox/SET_LIGHTBOX_PLAYBACK_DISABLED'; type CloseLightboxActionType = ReadonlyDeep<{ type: typeof CLOSE_LIGHTBOX; @@ -71,6 +74,11 @@ type ShowLightboxActionType = { }; }; +type SetLightboxPlaybackDisabledActionType = ReadonlyDeep<{ + type: typeof SET_LIGHTBOX_PLAYBACK_DISABLED; + payload: boolean; +}>; + type SetSelectedLightboxIndexActionType = ReadonlyDeep<{ type: typeof SET_SELECTED_LIGHTBOX_INDEX; payload: number; @@ -83,7 +91,8 @@ type LightboxActionType = | MessageDeletedActionType | MessageExpiredActionType | ShowLightboxActionType - | SetSelectedLightboxIndexActionType; + | SetSelectedLightboxIndexActionType + | SetLightboxPlaybackDisabledActionType; function closeLightbox(): ThunkAction< void, @@ -115,6 +124,28 @@ function closeLightbox(): ThunkAction< }; } +function setPlaybackDisabled( + playbackDisabled: boolean +): ThunkAction< + void, + RootStateType, + unknown, + SetLightboxPlaybackDisabledActionType +> { + return (dispatch, getState) => { + const { lightbox } = getState(); + + if (!lightbox.isShowingLightbox) { + return; + } + + dispatch({ + type: SET_LIGHTBOX_PLAYBACK_DISABLED, + payload: playbackDisabled, + }); + }; +} + function showLightboxWithMedia( selectedIndex: number | undefined, media: ReadonlyArray> @@ -340,6 +371,7 @@ function showLightbox(opts: { older.length > 0 && filterValidAttachments(older[0]).length > 0, hasNextMessage: newer.length > 0 && filterValidAttachments(newer[0]).length > 0, + playbackDisabled: false, }, }); }; @@ -481,6 +513,7 @@ export const actions = { showLightboxForPrevMessage, showLightboxForNextMessage, setSelectedLightboxIndex, + setPlaybackDisabled, }; export const useLightboxActions = (): BoundActionCreatorsMapObject< @@ -505,6 +538,7 @@ export function reducer( return { ...action.payload, isShowingLightbox: true, + playbackDisabled: false, }; } @@ -522,6 +556,17 @@ export function reducer( }; } + if (action.type === SET_LIGHTBOX_PLAYBACK_DISABLED) { + if (!state.isShowingLightbox) { + return state; + } + + return { + ...state, + playbackDisabled: action.payload, + }; + } + if ( action.type === MESSAGE_CHANGED || action.type === MESSAGE_DELETED || diff --git a/ts/state/selectors/lightbox.ts b/ts/state/selectors/lightbox.ts index 96e981708e..b9c8bf5173 100644 --- a/ts/state/selectors/lightbox.ts +++ b/ts/state/selectors/lightbox.ts @@ -46,3 +46,8 @@ export const getHasNextMessage = createSelector( getLightboxState, (state): boolean => state.isShowingLightbox && state.hasNextMessage ); + +export const getPlaybackDisabled = createSelector( + getLightboxState, + (state): boolean => state.isShowingLightbox && state.playbackDisabled +); diff --git a/ts/state/smart/Lightbox.tsx b/ts/state/smart/Lightbox.tsx index 89f44da33d..67e0ca1408 100644 --- a/ts/state/smart/Lightbox.tsx +++ b/ts/state/smart/Lightbox.tsx @@ -21,6 +21,7 @@ import { getMedia, getHasPrevMessage, getHasNextMessage, + getPlaybackDisabled, getSelectedIndex, shouldShowLightbox, } from '../selectors/lightbox'; @@ -50,6 +51,7 @@ export function SmartLightbox(): JSX.Element | null { const hasPrevMessage = useSelector(getHasPrevMessage); const hasNextMessage = useSelector(getHasNextMessage); const selectedIndex = useSelector(getSelectedIndex); + const playbackDisabled = useSelector(getPlaybackDisabled); const onPrevAttachment = useCallback(() => { if (selectedIndex <= 0) { @@ -93,6 +95,7 @@ export function SmartLightbox(): JSX.Element | null { i18n={i18n} isViewOnce={isViewOnce} media={media} + playbackDisabled={playbackDisabled} saveAttachment={saveAttachment} selectedIndex={selectedIndex || 0} toggleForwardMessagesModal={toggleForwardMessagesModal} diff --git a/ts/util/createIPCEvents.ts b/ts/util/createIPCEvents.ts index f5c326b260..55e1215cca 100644 --- a/ts/util/createIPCEvents.ts +++ b/ts/util/createIPCEvents.ts @@ -119,6 +119,7 @@ export type IPCEventsCallbacksType = { removeDarkOverlay: () => void; resetAllChatColors: () => void; resetDefaultChatColor: () => void; + setMediaPlaybackDisabled: (playbackDisabled: boolean) => void; showConversationViaNotification: (data: NotificationClickData) => void; showConversationViaSignalDotMe: ( kind: string, @@ -640,6 +641,13 @@ export function createIPCEvents( getMediaPermissions: window.IPC.getMediaPermissions, getMediaCameraPermissions: window.IPC.getMediaCameraPermissions, + setMediaPlaybackDisabled: (playbackDisabled: boolean) => { + window.reduxActions.lightbox.setPlaybackDisabled(playbackDisabled); + if (playbackDisabled) { + window.reduxActions.audioPlayer.pauseVoiceNotePlayer(); + } + }, + ...overrideEvents, }; } diff --git a/ts/windows/main/phase1-ipc.ts b/ts/windows/main/phase1-ipc.ts index a84666db51..44321cef2d 100644 --- a/ts/windows/main/phase1-ipc.ts +++ b/ts/windows/main/phase1-ipc.ts @@ -239,6 +239,16 @@ ipc.on('power-channel:lock-screen', () => { window.Whisper.events.trigger('powerMonitorLockScreen'); }); +ipc.on( + 'set-media-playback-disabled', + (_event: unknown, playbackDisabled: unknown) => { + const { setMediaPlaybackDisabled } = window.Events || {}; + if (setMediaPlaybackDisabled) { + setMediaPlaybackDisabled(Boolean(playbackDisabled)); + } + } +); + ipc.on('window:set-window-stats', (_event, stats) => { if (!window.reduxActions) { return;