Show error toast on database errors
This commit is contained in:
parent
c38e871ca9
commit
60f55f1749
7 changed files with 76 additions and 15 deletions
|
@ -804,6 +804,10 @@
|
|||
"messageformat": "Failed to send message with endorsements",
|
||||
"description": "An error popup when we attempted and failed to send a message using endorsements, only for internal users."
|
||||
},
|
||||
"icu:Toast--SQLError": {
|
||||
"messageformat": "Database error: please share your logs.",
|
||||
"description": "An error popup when a database method fails, for internal & beta users"
|
||||
},
|
||||
"icu:Toast--InvalidStorageServiceHeaders": {
|
||||
"messageformat": "Received invalid response from storage service. Please share your logs.",
|
||||
"description": "[Only shown to internal/beta users] An error popup when we noticed an invalid response (i.e. a web request response) from one of our servers"
|
||||
|
|
25
app/main.ts
25
app/main.ts
|
@ -1595,7 +1595,7 @@ const runSQLCorruptionHandler = async () => {
|
|||
// This is a glorified event handler. Normally, this promise never resolves,
|
||||
// but if there is a corruption error triggered by any query that we run
|
||||
// against the database - the promise will resolve and we will call
|
||||
// `onDatabaseError`.
|
||||
// `onDatabaseInitializationError`.
|
||||
const error = await sql.whenCorrupted();
|
||||
|
||||
getLogger().error(
|
||||
|
@ -1603,14 +1603,14 @@ const runSQLCorruptionHandler = async () => {
|
|||
`Restarting the application immediately. Error: ${error.message}`
|
||||
);
|
||||
|
||||
await onDatabaseError(error);
|
||||
await onDatabaseInitializationError(error);
|
||||
};
|
||||
|
||||
const runSQLReadonlyHandler = async () => {
|
||||
// This is a glorified event handler. Normally, this promise never resolves,
|
||||
// but if there is a corruption error triggered by any query that we run
|
||||
// against the database - the promise will resolve and we will call
|
||||
// `onDatabaseError`.
|
||||
// `onDatabaseInitializationError`.
|
||||
const error = await sql.whenReadonly();
|
||||
|
||||
getLogger().error(
|
||||
|
@ -1779,10 +1779,19 @@ async function initializeSQL(
|
|||
drop(runSQLCorruptionHandler());
|
||||
drop(runSQLReadonlyHandler());
|
||||
|
||||
sql.onUnknownSqlError(onUnknownSqlError);
|
||||
|
||||
return { ok: true, error: undefined };
|
||||
}
|
||||
|
||||
const onDatabaseError = async (error: Error) => {
|
||||
function onUnknownSqlError(error: Error) {
|
||||
getLogger().error('Unknown SQL Error:', Errors.toLogFormat(error));
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('sql-error');
|
||||
}
|
||||
}
|
||||
|
||||
const onDatabaseInitializationError = async (error: Error) => {
|
||||
// Prevent window from re-opening
|
||||
ready = false;
|
||||
|
||||
|
@ -1885,11 +1894,11 @@ const onDatabaseError = async (error: Error) => {
|
|||
});
|
||||
|
||||
if (confirmationButtonIndex === confirmDeleteAllDataButtonIndex) {
|
||||
getLogger().error('onDatabaseError: Deleting all data');
|
||||
getLogger().error('onDatabaseInitializationError: Deleting all data');
|
||||
await sql.removeDB();
|
||||
userConfig.remove();
|
||||
getLogger().error(
|
||||
'onDatabaseError: Requesting immediate restart after quit'
|
||||
'onDatabaseInitializationError: Requesting immediate restart after quit'
|
||||
);
|
||||
app.relaunch();
|
||||
}
|
||||
|
@ -1901,7 +1910,7 @@ const onDatabaseError = async (error: Error) => {
|
|||
);
|
||||
}
|
||||
|
||||
getLogger().error('onDatabaseError: Quitting application');
|
||||
getLogger().error('onDatabaseInitializationError: Quitting application');
|
||||
app.exit(1);
|
||||
};
|
||||
|
||||
|
@ -2278,7 +2287,7 @@ app.on('ready', async () => {
|
|||
if (sqlError) {
|
||||
getLogger().error('sql.initialize was unsuccessful; returning early');
|
||||
|
||||
await onDatabaseError(sqlError);
|
||||
await onDatabaseInitializationError(sqlError);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -152,6 +152,8 @@ function getToast(toastType: ToastType): AnyToast {
|
|||
return { toastType: ToastType.ReportedSpam };
|
||||
case ToastType.ReportedSpamAndBlocked:
|
||||
return { toastType: ToastType.ReportedSpamAndBlocked };
|
||||
case ToastType.SQLError:
|
||||
return { toastType: ToastType.SQLError };
|
||||
case ToastType.StickerPackInstallFailed:
|
||||
return { toastType: ToastType.StickerPackInstallFailed };
|
||||
case ToastType.StoryMuted:
|
||||
|
|
|
@ -506,6 +506,20 @@ export function renderToast({
|
|||
</Toast>
|
||||
);
|
||||
}
|
||||
if (toastType === ToastType.SQLError) {
|
||||
return (
|
||||
<Toast
|
||||
onClose={hideToast}
|
||||
toastAction={{
|
||||
label: i18n('icu:Toast__ActionLabel--SubmitLog'),
|
||||
onClick: onShowDebugLog,
|
||||
}}
|
||||
autoDismissDisabled
|
||||
>
|
||||
{i18n('icu:Toast--SQLError')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryMuted) {
|
||||
return (
|
||||
|
|
|
@ -92,6 +92,7 @@ type ResponseEntry<T> = {
|
|||
type KnownErrorResolverType = Readonly<{
|
||||
kind: SqliteErrorKind;
|
||||
resolve: (err: Error) => void;
|
||||
once?: boolean;
|
||||
}>;
|
||||
|
||||
type CreateWorkerResultType = Readonly<{
|
||||
|
@ -210,16 +211,31 @@ export class MainSQL {
|
|||
|
||||
public whenCorrupted(): Promise<Error> {
|
||||
const { promise, resolve } = explodePromise<Error>();
|
||||
this.#errorResolvers.push({ kind: SqliteErrorKind.Corrupted, resolve });
|
||||
this.#errorResolvers.push({
|
||||
kind: SqliteErrorKind.Corrupted,
|
||||
resolve,
|
||||
once: true,
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
public whenReadonly(): Promise<Error> {
|
||||
const { promise, resolve } = explodePromise<Error>();
|
||||
this.#errorResolvers.push({ kind: SqliteErrorKind.Readonly, resolve });
|
||||
this.#errorResolvers.push({
|
||||
kind: SqliteErrorKind.Readonly,
|
||||
resolve,
|
||||
once: true,
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
public onUnknownSqlError(callback: (error: Error) => void): void {
|
||||
this.#errorResolvers.push({
|
||||
kind: SqliteErrorKind.Unknown,
|
||||
resolve: callback,
|
||||
});
|
||||
}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
if (this.#onReady) {
|
||||
try {
|
||||
|
@ -375,16 +391,14 @@ export class MainSQL {
|
|||
}
|
||||
|
||||
#onError(errorKind: SqliteErrorKind, error: Error): void {
|
||||
if (errorKind === SqliteErrorKind.Unknown) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvers = new Array<(error: Error) => void>();
|
||||
this.#errorResolvers = this.#errorResolvers.filter(entry => {
|
||||
if (entry.kind === errorKind) {
|
||||
resolvers.push(entry.resolve);
|
||||
if (entry.once) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ export enum ToastType {
|
|||
ReactionFailed = 'ReactionFailed',
|
||||
ReportedSpam = 'ReportedSpam',
|
||||
ReportedSpamAndBlocked = 'ReportedSpamAndBlocked',
|
||||
SQLError = 'SQLError',
|
||||
StickerPackInstallFailed = 'StickerPackInstallFailed',
|
||||
StoryMuted = 'StoryMuted',
|
||||
StoryReact = 'StoryReact',
|
||||
|
@ -153,6 +154,7 @@ export type AnyToast =
|
|||
| { toastType: ToastType.ReportedSpam }
|
||||
| { toastType: ToastType.ReportedSpamAndBlocked }
|
||||
| { toastType: ToastType.StickerPackInstallFailed }
|
||||
| { toastType: ToastType.SQLError }
|
||||
| { toastType: ToastType.StoryMuted }
|
||||
| { toastType: ToastType.StoryReact }
|
||||
| { toastType: ToastType.StoryReply }
|
||||
|
|
|
@ -22,6 +22,8 @@ import { DataReader } from '../../sql/Client';
|
|||
import type { WindowsNotificationData } from '../../services/notifications';
|
||||
import { AggregatedStats } from '../../textsecure/WebsocketResources';
|
||||
import { UNAUTHENTICATED_CHANNEL_NAME } from '../../textsecure/SocketManager';
|
||||
import { isProduction } from '../../util/version';
|
||||
import { ToastType } from '../../types/Toast';
|
||||
|
||||
// It is important to call this as early as possible
|
||||
window.i18n = SignalContext.i18n;
|
||||
|
@ -437,6 +439,20 @@ ipc.on('show-release-notes', () => {
|
|||
}
|
||||
});
|
||||
|
||||
ipc.on('sql-error', () => {
|
||||
if (!window.reduxActions) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isProduction(window.getVersion())) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.reduxActions.toast.showToast({
|
||||
toastType: ToastType.SQLError,
|
||||
});
|
||||
});
|
||||
|
||||
ipc.on(
|
||||
'art-creator:uploadStickerPack',
|
||||
async (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue