From 503620fe00fa05b658d2b1acb8f631899925a1a4 Mon Sep 17 00:00:00 2001 From: trevor-signal <131492920+trevor-signal@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:13:25 -0400 Subject: [PATCH] Update database error confirmation dialogs --- _locales/en/messages.json | 22 ++++++++++-- app/main.ts | 74 +++++++++++++++++++++++++++++--------- ts/sql/migrations/index.ts | 8 +++-- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 57c9c0c9f4..3946d4623e 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -109,11 +109,27 @@ }, "icu:databaseError": { "messageformat": "Database Error", - "description": "Shown in a popup if the database cannot start up properly" + "description": "Title of a popup if the database cannot start up properly" + }, + "icu:databaseError__detail": { + "messageformat": "A database error occurred. You can copy the error and contact Signal support to help fix the issue. If you need to use Signal right away, you can delete your data and restart.\n\nContact support by visiting: https://support.signal.org/hc/requests/new?desktop", + "description": "Description shown in a popup if the database cannot start up properly" }, "icu:deleteAndRestart": { - "messageformat": "Delete all data and restart", - "description": "Shown in a popup if the database cannot start up properly; allows user to delete database and restart" + "messageformat": "Delete data and restart", + "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?", + "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.", + "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": { + "messageformat": "The version of your database does not match this version of Signal. Make sure you are opening the newest version of Signal on your computer.", + "description": "Text in a popup shown if the app cannot start because the user started an older version of Signal" }, "icu:mainMenuFile": { "messageformat": "&File", diff --git a/app/main.ts b/app/main.ts index d0d197622f..550890fdb3 100644 --- a/app/main.ts +++ b/app/main.ts @@ -123,6 +123,7 @@ import { load as loadLocale } from './locale'; import type { LoggerType } from '../ts/types/Logging'; import { HourCyclePreference } from '../ts/types/I18N'; +import { DBVersionFromFutureError } from '../ts/sql/migrations'; const STICKER_CREATOR_PARTITION = 'sticker-creator'; @@ -1596,28 +1597,69 @@ const onDatabaseError = async (error: string) => { } mainWindow = undefined; + const { i18n } = getResolvedMessagesLocale(); + + let deleteAllDataButtonIndex: number | undefined; + let messageDetail: string; + + const buttons = [i18n('icu:copyErrorAndQuit')]; + const copyErrorAndQuitButtonIndex = 0; + + if (error.includes(DBVersionFromFutureError.name)) { + // 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 + messageDetail = i18n('icu:databaseError__startOldVersion'); + } else { + // Otherwise, this is some other kind of DB error, let's give them the option to + // delete. + messageDetail = i18n('icu:databaseError__detail'); + + buttons.push(i18n('icu:deleteAndRestart')); + deleteAllDataButtonIndex = 1; + } + const buttonIndex = dialog.showMessageBoxSync({ - buttons: [ - getResolvedMessagesLocale().i18n('icu:deleteAndRestart'), - getResolvedMessagesLocale().i18n('icu:copyErrorAndQuit'), - ], - defaultId: 1, - cancelId: 1, - detail: redactAll(error), - message: getResolvedMessagesLocale().i18n('icu:databaseError'), + buttons, + defaultId: copyErrorAndQuitButtonIndex, + cancelId: copyErrorAndQuitButtonIndex, + message: i18n('icu:databaseError'), + detail: messageDetail, noLink: true, type: 'error', }); - if (buttonIndex === 1) { + if (buttonIndex === copyErrorAndQuitButtonIndex) { clipboard.writeText(`Database startup error:\n\n${redactAll(error)}`); - } else { - await sql.removeDB(); - userConfig.remove(); - getLogger().error( - 'onDatabaseError: Requesting immediate restart after quit' - ); - app.relaunch(); + } else if ( + typeof deleteAllDataButtonIndex === 'number' && + buttonIndex === deleteAllDataButtonIndex + ) { + const confirmationButtons = [ + i18n('icu:cancel'), + i18n('icu:deleteAndRestart'), + ]; + const cancelButtonIndex = 0; + const confirmDeleteAllDataButtonIndex = 1; + const confirmationButtonIndex = dialog.showMessageBoxSync({ + buttons: confirmationButtons, + defaultId: cancelButtonIndex, + cancelId: cancelButtonIndex, + message: i18n('icu:databaseError__deleteDataConfirmation'), + detail: i18n('icu:databaseError__deleteDataConfirmation__detail'), + noLink: true, + type: 'warning', + }); + + if (confirmationButtonIndex === confirmDeleteAllDataButtonIndex) { + getLogger().error('onDatabaseError: Deleting all data'); + await sql.removeDB(); + userConfig.remove(); + getLogger().error( + 'onDatabaseError: Requesting immediate restart after quit' + ); + app.relaunch(); + } } getLogger().error('onDatabaseError: Quitting application'); diff --git a/ts/sql/migrations/index.ts b/ts/sql/migrations/index.ts index 3f77f808a1..8a04742119 100644 --- a/ts/sql/migrations/index.ts +++ b/ts/sql/migrations/index.ts @@ -2013,6 +2013,10 @@ export const SCHEMA_VERSIONS = [ updateToSchemaVersion950, ]; +export class DBVersionFromFutureError extends Error { + override name = 'DBVersionFromFutureError'; +} + export function updateSchema(db: Database, logger: LoggerType): void { const sqliteVersion = getSQLiteVersion(db); const sqlcipherVersion = getSQLCipherVersion(db); @@ -2029,9 +2033,9 @@ export function updateSchema(db: Database, logger: LoggerType): void { ); if (startingVersion > MAX_VERSION) { - throw new Error( + throw new DBVersionFromFutureError( `SQL: User version is ${startingVersion} but the expected maximum version ` + - `is ${MAX_VERSION}. Did you try to start an old version of Signal?` + `is ${MAX_VERSION}.` ); }