signal-desktop/ts/util/longRunningTaskWrapper.tsx

91 lines
2.7 KiB
TypeScript
Raw Normal View History

2023-01-03 11:55:46 -08:00
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
2025-05-05 09:48:36 -07:00
import React, { StrictMode } from 'react';
import { createRoot, type Root } from 'react-dom/client';
2022-12-21 22:07:45 -05:00
import * as Errors from '../types/errors.js';
import { createLogger } from '../logging/log.js';
import { ProgressModal } from '../components/ProgressModal.js';
import { clearTimeoutIfNecessary } from './clearTimeoutIfNecessary.js';
import { sleep } from './sleep.js';
import { FunDefaultEnglishEmojiLocalizationProvider } from '../components/fun/FunEmojiLocalizationProvider.js';
import { AxoProvider } from '../axo/AxoProvider.js';
2025-06-16 11:59:31 -07:00
const log = createLogger('longRunningTaskWrapper');
export async function longRunningTaskWrapper<T>({
name,
idForLogging,
task,
suppressErrorDialog,
}: {
name: string;
idForLogging: string;
task: () => Promise<T>;
suppressErrorDialog?: boolean;
}): Promise<T> {
const idLog = `${name}/${idForLogging}`;
const ONE_SECOND = 1000;
const TWO_SECONDS = 2000;
let progressRoot: Root | undefined;
let spinnerStart;
let progressTimeout: NodeJS.Timeout | undefined = setTimeout(() => {
const progressNode = document.createElement('div');
2025-06-16 11:59:31 -07:00
log.info(`${idLog}: Creating spinner`);
progressRoot = createRoot(progressNode);
progressRoot.render(
2025-05-05 09:48:36 -07:00
<StrictMode>
<AxoProvider dir={window.i18n.getLocaleDirection()}>
<FunDefaultEnglishEmojiLocalizationProvider>
<ProgressModal i18n={window.i18n} />
</FunDefaultEnglishEmojiLocalizationProvider>
</AxoProvider>
2025-07-15 16:32:11 -07:00
</StrictMode>
2025-04-15 13:01:17 -07:00
);
spinnerStart = Date.now();
}, TWO_SECONDS);
// Note: any task we put here needs to have its own safety valve; this function will
// show a spinner until it's done
try {
2025-06-16 11:59:31 -07:00
log.info(`${idLog}: Starting task`);
const result = await task();
2025-06-16 11:59:31 -07:00
log.info(`${idLog}: Task completed successfully`);
clearTimeoutIfNecessary(progressTimeout);
progressTimeout = undefined;
if (progressRoot) {
const now = Date.now();
if (spinnerStart && now - spinnerStart < ONE_SECOND) {
log.info(
2025-06-16 11:59:31 -07:00
`${idLog}: Spinner shown for less than second, showing for another second`
);
2023-04-10 20:54:43 -07:00
await sleep(ONE_SECOND);
}
progressRoot.unmount();
progressRoot = undefined;
}
return result;
} catch (error) {
2025-06-16 11:59:31 -07:00
log.error(`${idLog}: Error!`, Errors.toLogFormat(error));
clearTimeoutIfNecessary(progressTimeout);
progressTimeout = undefined;
if (progressRoot) {
progressRoot.unmount();
progressRoot = undefined;
}
if (!suppressErrorDialog) {
2025-06-16 11:59:31 -07:00
log.info(`${idLog}: Showing error dialog`);
2022-12-21 22:07:45 -05:00
window.reduxActions.globalModals.showErrorModal({});
}
throw error;
}
}