Ask for confirmation when closing app during call
This commit is contained in:
parent
c772f2abc5
commit
93c019dc30
4 changed files with 129 additions and 0 deletions
|
@ -5723,6 +5723,14 @@
|
||||||
"messageformat": "Would you like to discard these changes?",
|
"messageformat": "Would you like to discard these changes?",
|
||||||
"description": "ConfirmationDialog text for discarding 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": {
|
"icu:ProfileEditor--edit-photo": {
|
||||||
"messageformat": "Edit photo",
|
"messageformat": "Edit photo",
|
||||||
"description": "Text of a button on profile editor that leads to the avatar editor"
|
"description": "Text of a button on profile editor that leads to the avatar editor"
|
||||||
|
|
68
app/main.ts
68
app/main.ts
|
@ -862,6 +862,21 @@ async function createWindow() {
|
||||||
// Prevent the shutdown
|
// Prevent the shutdown
|
||||||
e.preventDefault();
|
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
|
* if the user is in fullscreen mode and closes the window, not the
|
||||||
* application, we need them leave fullscreen first before closing it to
|
* application, we need them leave fullscreen first before closing it to
|
||||||
|
@ -2057,6 +2072,59 @@ function setupMenu(options?: Partial<CreateTemplateOptionsType>) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function maybeRequestCloseConfirmation(): Promise<boolean> {
|
||||||
|
if (!mainWindow || !mainWindow.webContents) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLogger().info(
|
||||||
|
'maybeRequestCloseConfirmation: Checking to see if close confirmation is needed'
|
||||||
|
);
|
||||||
|
const request = new Promise<boolean>(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() {
|
async function requestShutdown() {
|
||||||
if (!mainWindow || !mainWindow.webContents) {
|
if (!mainWindow || !mainWindow.webContents) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -22,6 +22,7 @@ import type { AuthorizeArtCreatorDataType } from '../state/ducks/globalModals';
|
||||||
import { calling } from '../services/calling';
|
import { calling } from '../services/calling';
|
||||||
import { resolveUsernameByLinkBase64 } from '../services/username';
|
import { resolveUsernameByLinkBase64 } from '../services/username';
|
||||||
import { writeProfile } from '../services/writeProfile';
|
import { writeProfile } from '../services/writeProfile';
|
||||||
|
import { isInCall as getIsInCall } from '../state/selectors/calling';
|
||||||
import { getConversationsWithCustomColorSelector } from '../state/selectors/conversations';
|
import { getConversationsWithCustomColorSelector } from '../state/selectors/conversations';
|
||||||
import { getCustomColors } from '../state/selectors/items';
|
import { getCustomColors } from '../state/selectors/items';
|
||||||
import { themeChanged } from '../shims/themeChanged';
|
import { themeChanged } from '../shims/themeChanged';
|
||||||
|
@ -43,6 +44,7 @@ import { isValidE164 } from './isValidE164';
|
||||||
import { fromWebSafeBase64 } from './webSafeBase64';
|
import { fromWebSafeBase64 } from './webSafeBase64';
|
||||||
import { getConversation } from './getConversation';
|
import { getConversation } from './getConversation';
|
||||||
import { instance, PhoneNumberFormat } from './libphonenumberInstance';
|
import { instance, PhoneNumberFormat } from './libphonenumberInstance';
|
||||||
|
import { showConfirmationDialog } from './showConfirmationDialog';
|
||||||
|
|
||||||
type SentMediaQualityType = 'standard' | 'high';
|
type SentMediaQualityType = 'standard' | 'high';
|
||||||
type ThemeType = 'light' | 'dark' | 'system';
|
type ThemeType = 'light' | 'dark' | 'system';
|
||||||
|
@ -129,6 +131,7 @@ export type IPCEventsCallbacksType = {
|
||||||
showGroupViaLink: (value: string) => Promise<void>;
|
showGroupViaLink: (value: string) => Promise<void>;
|
||||||
showReleaseNotes: () => void;
|
showReleaseNotes: () => void;
|
||||||
showStickerPack: (packId: string, key: string) => void;
|
showStickerPack: (packId: string, key: string) => void;
|
||||||
|
maybeRequestCloseConfirmation: () => Promise<boolean>;
|
||||||
shutdown: () => Promise<void>;
|
shutdown: () => Promise<void>;
|
||||||
unknownSignalLink: () => void;
|
unknownSignalLink: () => void;
|
||||||
getCustomColors: () => Record<string, CustomColorType>;
|
getCustomColors: () => Record<string, CustomColorType>;
|
||||||
|
@ -620,6 +623,43 @@ export function createIPCEvents(
|
||||||
showUnknownSgnlLinkModal();
|
showUnknownSgnlLinkModal();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
maybeRequestCloseConfirmation: async (): Promise<boolean> => {
|
||||||
|
const isInCall = getIsInCall(window.reduxStore.getState());
|
||||||
|
if (!isInCall) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise<void>((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: () => {
|
unknownSignalLink: () => {
|
||||||
log.warn('unknownSignalLink: Showing error dialog');
|
log.warn('unknownSignalLink: Showing error dialog');
|
||||||
showUnknownSgnlLinkModal();
|
showUnknownSgnlLinkModal();
|
||||||
|
|
|
@ -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', () => {
|
ipc.on('show-release-notes', () => {
|
||||||
const { showReleaseNotes } = window.Events;
|
const { showReleaseNotes } = window.Events;
|
||||||
if (showReleaseNotes) {
|
if (showReleaseNotes) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue