From 93c019dc3089a5fa21f587e29aa5ca217c05b0e5 Mon Sep 17 00:00:00 2001 From: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com> Date: Mon, 19 Feb 2024 06:44:05 -0800 Subject: [PATCH] Ask for confirmation when closing app during call --- _locales/en/messages.json | 8 +++++ app/main.ts | 68 +++++++++++++++++++++++++++++++++++ ts/util/createIPCEvents.ts | 40 +++++++++++++++++++++ ts/windows/main/phase1-ipc.ts | 13 +++++++ 4 files changed, 129 insertions(+) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index cd9a77efc..5ab106026 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -5723,6 +5723,14 @@ "messageformat": "Would you like to discard these changes?", "description": "ConfirmationDialog text for discarding changes" }, + "icu:ConfirmationDialog__Title--in-call-close-requested": { + "messageformat": "Close Signal and end the call?", + "description": "ConfirmationDialog title when trying to close the app while in a call." + }, + "icu:ConfirmationDialog__Title--close-requested-not-now": { + "messageformat": "Not Now", + "description": "Confirmation dialog button to cancel closing the app, while in a call and trying to close the app." + }, "icu:ProfileEditor--edit-photo": { "messageformat": "Edit photo", "description": "Text of a button on profile editor that leads to the avatar editor" diff --git a/app/main.ts b/app/main.ts index 091dcfee4..8d9fbac23 100644 --- a/app/main.ts +++ b/app/main.ts @@ -862,6 +862,21 @@ async function createWindow() { // Prevent the shutdown e.preventDefault(); + // In certain cases such as during an active call, we ask the user to confirm close + // which includes shutdown, clicking X on MacOS or closing to tray. + let shouldClose = true; + try { + shouldClose = await maybeRequestCloseConfirmation(); + } catch (error) { + getLogger().warn( + 'Error while requesting close confirmation.', + Errors.toLogFormat(error) + ); + } + if (!shouldClose) { + return; + } + /** * if the user is in fullscreen mode and closes the window, not the * application, we need them leave fullscreen first before closing it to @@ -2057,6 +2072,59 @@ function setupMenu(options?: Partial) { }); } +async function maybeRequestCloseConfirmation(): Promise { + if (!mainWindow || !mainWindow.webContents) { + return true; + } + + getLogger().info( + 'maybeRequestCloseConfirmation: Checking to see if close confirmation is needed' + ); + const request = new Promise(resolveFn => { + let timeout: NodeJS.Timeout | undefined; + + if (!mainWindow) { + resolveFn(true); + return; + } + + ipc.once('received-close-confirmation', (_event, result) => { + getLogger().info('maybeRequestCloseConfirmation: Response received'); + + clearTimeoutIfNecessary(timeout); + resolveFn(result); + }); + + ipc.once('requested-close-confirmation', () => { + getLogger().info( + 'maybeRequestCloseConfirmation: Confirmation dialog shown, waiting for user.' + ); + clearTimeoutIfNecessary(timeout); + }); + + mainWindow.webContents.send('maybe-request-close-confirmation'); + + // Wait a short time then proceed. Normally the dialog should be + // shown right away. + timeout = setTimeout(() => { + getLogger().error( + 'maybeRequestCloseConfirmation: Response never received; continuing with close.' + ); + resolveFn(true); + }, 10 * 1000); + }); + + try { + return await request; + } catch (error) { + getLogger().error( + 'maybeRequestCloseConfirmation error:', + Errors.toLogFormat(error) + ); + return true; + } +} + async function requestShutdown() { if (!mainWindow || !mainWindow.webContents) { return; diff --git a/ts/util/createIPCEvents.ts b/ts/util/createIPCEvents.ts index 5eb2ddf11..879bec8e2 100644 --- a/ts/util/createIPCEvents.ts +++ b/ts/util/createIPCEvents.ts @@ -22,6 +22,7 @@ import type { AuthorizeArtCreatorDataType } from '../state/ducks/globalModals'; import { calling } from '../services/calling'; import { resolveUsernameByLinkBase64 } from '../services/username'; import { writeProfile } from '../services/writeProfile'; +import { isInCall as getIsInCall } from '../state/selectors/calling'; import { getConversationsWithCustomColorSelector } from '../state/selectors/conversations'; import { getCustomColors } from '../state/selectors/items'; import { themeChanged } from '../shims/themeChanged'; @@ -43,6 +44,7 @@ import { isValidE164 } from './isValidE164'; import { fromWebSafeBase64 } from './webSafeBase64'; import { getConversation } from './getConversation'; import { instance, PhoneNumberFormat } from './libphonenumberInstance'; +import { showConfirmationDialog } from './showConfirmationDialog'; type SentMediaQualityType = 'standard' | 'high'; type ThemeType = 'light' | 'dark' | 'system'; @@ -129,6 +131,7 @@ export type IPCEventsCallbacksType = { showGroupViaLink: (value: string) => Promise; showReleaseNotes: () => void; showStickerPack: (packId: string, key: string) => void; + maybeRequestCloseConfirmation: () => Promise; shutdown: () => Promise; unknownSignalLink: () => void; getCustomColors: () => Record; @@ -620,6 +623,43 @@ export function createIPCEvents( showUnknownSgnlLinkModal(); }, + maybeRequestCloseConfirmation: async (): Promise => { + const isInCall = getIsInCall(window.reduxStore.getState()); + if (!isInCall) { + return true; + } + + try { + await new Promise((resolve, reject) => { + showConfirmationDialog({ + dialogName: 'closeConfirmation', + onTopOfEverything: true, + cancelText: window.i18n( + 'icu:ConfirmationDialog__Title--close-requested-not-now' + ), + confirmStyle: 'negative', + title: window.i18n( + 'icu:ConfirmationDialog__Title--in-call-close-requested' + ), + okText: window.i18n('icu:close'), + reject: () => reject(), + resolve: () => resolve(), + }); + }); + log.info('Close confirmed by user.'); + if (isInCall) { + window.reduxActions.calling.hangUpActiveCall( + 'User confirmed in-call close.' + ); + } + + return true; + } catch { + log.info('Close cancelled by user.'); + return false; + } + }, + unknownSignalLink: () => { log.warn('unknownSignalLink: Showing error dialog'); showUnknownSgnlLinkModal(); diff --git a/ts/windows/main/phase1-ipc.ts b/ts/windows/main/phase1-ipc.ts index 6cfc7037b..46e9a9151 100644 --- a/ts/windows/main/phase1-ipc.ts +++ b/ts/windows/main/phase1-ipc.ts @@ -388,6 +388,19 @@ ipc.on('get-ready-for-shutdown', async () => { } }); +ipc.on('maybe-request-close-confirmation', async () => { + const { maybeRequestCloseConfirmation } = window.Events; + if (!maybeRequestCloseConfirmation) { + ipc.send('received-close-confirmation', true); + return; + } + + log.info('Requesting close confirmation.'); + ipc.send('requested-close-confirmation'); + const result = await maybeRequestCloseConfirmation(); + ipc.send('received-close-confirmation', result); +}); + ipc.on('show-release-notes', () => { const { showReleaseNotes } = window.Events; if (showReleaseNotes) {