Resumable backup import
This commit is contained in:
parent
3d8aaf0a5a
commit
8ef149e3a8
17 changed files with 498 additions and 33 deletions
|
@ -8,14 +8,18 @@ import classNames from 'classnames';
|
|||
import type { ViewStoryActionCreatorType } from '../state/ducks/stories';
|
||||
import type { VerificationTransport } from '../types/VerificationTransport';
|
||||
import { ThemeType } from '../types/Util';
|
||||
import { AppViewType } from '../state/ducks/app';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import { type AppStateType, AppViewType } from '../state/ducks/app';
|
||||
import { SmartInstallScreen } from '../state/smart/InstallScreen';
|
||||
import { StandaloneRegistration } from './StandaloneRegistration';
|
||||
import { BackupImportScreen } from './BackupImportScreen';
|
||||
import { usePageVisibility } from '../hooks/usePageVisibility';
|
||||
import { useReducedMotion } from '../hooks/useReducedMotion';
|
||||
|
||||
type PropsType = {
|
||||
appView: AppViewType;
|
||||
i18n: LocalizerType;
|
||||
state: AppStateType;
|
||||
openInbox: () => void;
|
||||
getCaptchaToken: () => Promise<string>;
|
||||
registerSingleDevice: (
|
||||
|
@ -49,7 +53,8 @@ type PropsType = {
|
|||
};
|
||||
|
||||
export function App({
|
||||
appView,
|
||||
i18n,
|
||||
state,
|
||||
getCaptchaToken,
|
||||
hasSelectedStoryData,
|
||||
isFullScreen,
|
||||
|
@ -70,9 +75,9 @@ export function App({
|
|||
}: PropsType): JSX.Element {
|
||||
let contents;
|
||||
|
||||
if (appView === AppViewType.Installer) {
|
||||
if (state.appView === AppViewType.Installer) {
|
||||
contents = <SmartInstallScreen />;
|
||||
} else if (appView === AppViewType.Standalone) {
|
||||
} else if (state.appView === AppViewType.Standalone) {
|
||||
const onComplete = () => {
|
||||
window.IPC.removeSetupMenuItems();
|
||||
openInbox();
|
||||
|
@ -87,8 +92,14 @@ export function App({
|
|||
uploadProfile={uploadProfile}
|
||||
/>
|
||||
);
|
||||
} else if (appView === AppViewType.Inbox) {
|
||||
} else if (state.appView === AppViewType.Inbox) {
|
||||
contents = renderInbox();
|
||||
} else if (state.appView === AppViewType.Blank) {
|
||||
contents = undefined;
|
||||
} else if (state.appView === AppViewType.BackupImport) {
|
||||
contents = <BackupImportScreen i18n={i18n} {...state} />;
|
||||
} else {
|
||||
throw missingCaseError(state);
|
||||
}
|
||||
|
||||
// This are here so that themes are properly applied to anything that is
|
||||
|
|
32
ts/components/BackupImportScreen.stories.tsx
Normal file
32
ts/components/BackupImportScreen.stories.tsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { Meta, StoryFn } from '@storybook/react';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
import type { PropsType } from './BackupImportScreen';
|
||||
import { BackupImportScreen } from './BackupImportScreen';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
export default {
|
||||
title: 'Components/BackupImportScreen',
|
||||
} satisfies Meta<PropsType>;
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
const Template: StoryFn<PropsType> = (args: PropsType) => (
|
||||
<BackupImportScreen {...args} i18n={i18n} />
|
||||
);
|
||||
|
||||
export const NoBytes = Template.bind({});
|
||||
NoBytes.args = {
|
||||
currentBytes: undefined,
|
||||
totalBytes: undefined,
|
||||
};
|
||||
|
||||
export const Bytes = Template.bind({});
|
||||
Bytes.args = {
|
||||
currentBytes: 500,
|
||||
totalBytes: 1024,
|
||||
};
|
85
ts/components/BackupImportScreen.tsx
Normal file
85
ts/components/BackupImportScreen.tsx
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { formatFileSize } from '../util/formatFileSize';
|
||||
import { TitlebarDragArea } from './TitlebarDragArea';
|
||||
import { InstallScreenSignalLogo } from './installScreen/InstallScreenSignalLogo';
|
||||
|
||||
// We can't always use destructuring assignment because of the complexity of this props
|
||||
// type.
|
||||
|
||||
export type PropsType = Readonly<{
|
||||
i18n: LocalizerType;
|
||||
currentBytes?: number;
|
||||
totalBytes?: number;
|
||||
}>;
|
||||
|
||||
export function BackupImportScreen({
|
||||
i18n,
|
||||
currentBytes,
|
||||
totalBytes,
|
||||
}: PropsType): JSX.Element {
|
||||
let percentage = 0;
|
||||
let progress: JSX.Element;
|
||||
if (currentBytes != null && totalBytes != null) {
|
||||
percentage = Math.max(0, Math.min(1, currentBytes / totalBytes));
|
||||
if (percentage > 0 && percentage <= 0.01) {
|
||||
percentage = 0.01;
|
||||
} else if (percentage >= 0.99 && percentage < 1) {
|
||||
percentage = 0.99;
|
||||
} else {
|
||||
percentage = Math.round(percentage * 100) / 100;
|
||||
}
|
||||
|
||||
progress = (
|
||||
<>
|
||||
<div className="BackupImportScreen__progressbar">
|
||||
<div
|
||||
className="BackupImportScreen__progressbar__fill"
|
||||
style={{ transform: `translateX(${(percentage - 1) * 100}%)` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="BackupImportScreen__progressbar-hint">
|
||||
{i18n('icu:BackupImportScreen__progressbar-hint', {
|
||||
currentSize: formatFileSize(currentBytes),
|
||||
totalSize: formatFileSize(totalBytes),
|
||||
fractionComplete: percentage,
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
progress = (
|
||||
<>
|
||||
<div className="BackupImportScreen__progressbar" />
|
||||
<div className="BackupImportScreen__progressbar-hint BackupImportScreen__progressbar-hint--hidden">
|
||||
{i18n('icu:BackupImportScreen__progressbar-hint', {
|
||||
currentSize: '',
|
||||
totalSize: '',
|
||||
fractionComplete: 0,
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="BackupImportScreen">
|
||||
<TitlebarDragArea />
|
||||
|
||||
<InstallScreenSignalLogo />
|
||||
|
||||
<div className="BackupImportScreen__content">
|
||||
<h3 className="BackupImportScreen__title">
|
||||
{i18n('icu:BackupImportScreen__title')}
|
||||
</h3>
|
||||
{progress}
|
||||
<div className="BackupImportScreen__description">
|
||||
{i18n('icu:BackupImportScreen__description')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue