diff --git a/_locales/en/messages.json b/_locales/en/messages.json index a65852ea8..6f97885a9 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -111,6 +111,14 @@ "messageformat": "Unknown group", "description": "Shown as the name of a group if we don't have any information about it" }, + "icu:cantOpenSignalError": { + "messageformat": "Can’t Open Signal", + "description": "Title of a popup if the app cannot start up properly" + }, + "icu:cantOpenSignalError__detail": { + "messageformat": "To help fix the issue, follow the recovery guide on the support page or contact Signal support to help fix the issue.\n\nIf you need to use Signal right away, you can delete your data and relink this desktop. You will have the option to transfer message history from your phone.", + "description": "Description shown in a popup if the app cannot start up properly" + }, "icu:databaseError": { "messageformat": "Database Error", "description": "Title of a popup if the database cannot start up properly" @@ -124,11 +132,11 @@ "description": "Text of a button shown in a popup if the database cannot start up properly; allows user to delete all data in their database and restart" }, "icu:databaseError__deleteDataConfirmation": { - "messageformat": "Permanently delete all data?", + "messageformat": "Permanently Delete All Data", "description": "Header of a confirmation popup shown if the database cannot start up properly and the user selects 'delete data and restart'" }, "icu:databaseError__deleteDataConfirmation__detail": { - "messageformat": "All of your message history and media will be permanently deleted from this device. You will be able to use Signal on this device after relinking it. This will not delete any data from your phone.", + "messageformat": "All of your message history and media will be permanently deleted from this device. This will not delete any data from your phone.\n\nYou will be able to use Signal on this device after relinking it. You will have the option to transfer message history from your phone.", "description": "Description of a confirmation popup shown if the database cannot start up properly and the user selects 'delete data and restart'" }, "icu:databaseError__startOldVersion": { diff --git a/app/main.ts b/app/main.ts index 0ba2e7ced..c04038a63 100644 --- a/app/main.ts +++ b/app/main.ts @@ -123,6 +123,7 @@ import { SafeStorageBackendChangeError } from '../ts/types/SafeStorageBackendCha import { LINUX_PASSWORD_STORE_FLAGS } from '../ts/util/linuxPasswordStoreFlags'; import { getOwn } from '../ts/util/getOwn'; import { safeParseLoose, safeParseUnknown } from '../ts/util/schemas'; +import { getAppErrorIcon } from '../ts/util/getAppErrorIcon'; const animationSettings = systemPreferences.getAnimationSettings(); @@ -1814,12 +1815,14 @@ const onDatabaseError = async (error: Error) => { const { i18n } = getResolvedMessagesLocale(); + let copyErrorAndQuitButtonIndex: number; let deleteAllDataButtonIndex: number | undefined; + let goToSupportPageButtonIndex: number | undefined; + let defaultButtonId: number; + let messageTitle: string; let messageDetail: string; - const buttons = [i18n('icu:copyErrorAndQuit')]; - const copyErrorAndQuitButtonIndex = 0; - const SIGNAL_SUPPORT_LINK = 'https://support.signal.org/error'; + const buttons = []; // Note that this error is thrown by the worker process and thus instanceof // check won't work. @@ -1827,13 +1830,18 @@ const onDatabaseError = async (error: Error) => { // If the DB version is too new, the user likely opened an older version of Signal, // and they would almost never want to delete their data as a result, so we don't show // that option + messageTitle = i18n('icu:databaseError'); messageDetail = i18n('icu:databaseError__startOldVersion'); + buttons.push(i18n('icu:copyErrorAndQuit')); + copyErrorAndQuitButtonIndex = 0; + defaultButtonId = copyErrorAndQuitButtonIndex; } else if (error instanceof SafeStorageBackendChangeError) { const { currentBackend, previousBackend } = error; const previousBackendFlag = getOwn( LINUX_PASSWORD_STORE_FLAGS, previousBackend ); + messageTitle = i18n('icu:databaseError'); messageDetail = previousBackendFlag ? i18n('icu:databaseError__safeStorageBackendChangeWithPreviousFlag', { currentBackend, @@ -1844,27 +1852,32 @@ const onDatabaseError = async (error: Error) => { currentBackend, previousBackend, }); + buttons.push(i18n('icu:copyErrorAndQuit')); + copyErrorAndQuitButtonIndex = 0; + defaultButtonId = copyErrorAndQuitButtonIndex; } else { - // Otherwise, this is some other kind of DB error, let's give them the option to - // delete. - messageDetail = i18n( - 'icu:databaseError__detail', - { link: SIGNAL_SUPPORT_LINK }, - { bidi: 'strip' } - ); - + // Otherwise, this is some other kind of DB error, most likely broken safeStorage key. + // Let's give them the option to delete and show them the support guide. + messageTitle = i18n('icu:cantOpenSignalError'); + messageDetail = i18n('icu:cantOpenSignalError__detail'); + buttons.push(i18n('icu:goToSupportPage')); + goToSupportPageButtonIndex = 0; + // Delete button should be the hardest to click buttons.push(i18n('icu:deleteAndRestart')); deleteAllDataButtonIndex = 1; + buttons.push(i18n('icu:copyErrorAndQuit')); + copyErrorAndQuitButtonIndex = 2; + defaultButtonId = goToSupportPageButtonIndex; } const buttonIndex = dialog.showMessageBoxSync({ buttons, - defaultId: copyErrorAndQuitButtonIndex, + defaultId: defaultButtonId, cancelId: copyErrorAndQuitButtonIndex, - message: i18n('icu:databaseError'), + message: messageTitle, detail: messageDetail, + icon: getAppErrorIcon(), noLink: true, - type: 'error', }); if (buttonIndex === copyErrorAndQuitButtonIndex) { @@ -1889,8 +1902,8 @@ const onDatabaseError = async (error: Error) => { cancelId: cancelButtonIndex, message: i18n('icu:databaseError__deleteDataConfirmation'), detail: i18n('icu:databaseError__deleteDataConfirmation__detail'), + icon: getAppErrorIcon(), noLink: true, - type: 'warning', }); if (confirmationButtonIndex === confirmDeleteAllDataButtonIndex) { @@ -1902,6 +1915,12 @@ const onDatabaseError = async (error: Error) => { ); app.relaunch(); } + } else if (buttonIndex === goToSupportPageButtonIndex) { + drop( + shell.openExternal( + 'https://support.signal.org/hc/articles/9045714156314-Can-t-Open-Signal-Desktop' + ) + ); } getLogger().error('onDatabaseError: Quitting application'); diff --git a/images/app-icon-with-error.png b/images/app-icon-with-error.png new file mode 100644 index 000000000..f73082a79 Binary files /dev/null and b/images/app-icon-with-error.png differ diff --git a/ts/util/getAppErrorIcon.ts b/ts/util/getAppErrorIcon.ts new file mode 100644 index 000000000..41187f4d6 --- /dev/null +++ b/ts/util/getAppErrorIcon.ts @@ -0,0 +1,17 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { nativeImage } from 'electron'; +import type { NativeImage } from 'electron'; +import { join } from 'path'; + +export function getAppErrorIcon(): NativeImage { + const iconPath = join( + __dirname, + '..', + '..', + 'images', + 'app-icon-with-error.png' + ); + return nativeImage.createFromPath(iconPath); +}