Update link & sync UI
This commit is contained in:
parent
1c933af6ce
commit
6f1d767c72
14 changed files with 293 additions and 119 deletions
|
@ -16,6 +16,9 @@ export default {
|
|||
},
|
||||
} satisfies ComponentMeta<Props>;
|
||||
|
||||
export function Spinning(args: Props): JSX.Element {
|
||||
return <ProgressBar {...args} fractionComplete={null} />;
|
||||
}
|
||||
export function Zero(args: Props): JSX.Element {
|
||||
return <ProgressBar {...args} />;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,16 @@ export function ProgressBar({
|
|||
fractionComplete,
|
||||
isRTL,
|
||||
}: {
|
||||
fractionComplete: number;
|
||||
fractionComplete: number | null;
|
||||
isRTL: boolean;
|
||||
}): JSX.Element {
|
||||
if (fractionComplete == null) {
|
||||
return (
|
||||
<div className="ProgressBar">
|
||||
<div className="ProgressBar__fill ProgressBar__fill--spinning" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="ProgressBar">
|
||||
<div
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import type { Meta, StoryFn } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
|
@ -73,11 +73,48 @@ const Template: StoryFn<PropsType> = (args: PropsType) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const NoBytes = Template.bind({});
|
||||
NoBytes.args = {
|
||||
backupStep: InstallScreenBackupStep.Download,
|
||||
currentBytes: undefined,
|
||||
totalBytes: undefined,
|
||||
export function FullFlow(): JSX.Element {
|
||||
const [backupStep, setBackupStep] = useState<InstallScreenBackupStep>(
|
||||
InstallScreenBackupStep.WaitForBackup
|
||||
);
|
||||
const [currentBytes, setCurrentBytes] = useState<number>(0);
|
||||
const [totalBytes, setTotalBytes] = useState<number>(0);
|
||||
const TOTAL_BYTES = 1024 * 1024;
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setBackupStep(InstallScreenBackupStep.Download);
|
||||
setCurrentBytes(0);
|
||||
setTotalBytes(TOTAL_BYTES);
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
setTimeout(() => {
|
||||
setCurrentBytes(TOTAL_BYTES / (4 - i));
|
||||
}, i * 900);
|
||||
}
|
||||
}, 1000);
|
||||
}, [TOTAL_BYTES]);
|
||||
|
||||
return (
|
||||
<InstallScreenBackupImportStep
|
||||
i18n={i18n}
|
||||
updates={DEFAULT_UPDATES}
|
||||
currentVersion="v6.0.0"
|
||||
OS="macOS"
|
||||
startUpdate={action('startUpdate')}
|
||||
forceUpdate={action('forceUpdate')}
|
||||
onCancel={action('onCancel')}
|
||||
onRetry={action('onRetry')}
|
||||
currentBytes={currentBytes}
|
||||
totalBytes={totalBytes}
|
||||
backupStep={backupStep}
|
||||
onRestartLink={action('onRestartLink')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const Waiting = Template.bind({});
|
||||
Waiting.args = {
|
||||
backupStep: InstallScreenBackupStep.WaitForBackup,
|
||||
};
|
||||
|
||||
export const Bytes = Template.bind({});
|
||||
|
|
|
@ -24,40 +24,50 @@ import { InstallScreenUpdateDialog } from './InstallScreenUpdateDialog';
|
|||
// We can't always use destructuring assignment because of the complexity of this props
|
||||
// type.
|
||||
|
||||
export type PropsType = Readonly<{
|
||||
i18n: LocalizerType;
|
||||
backupStep: InstallScreenBackupStep;
|
||||
currentBytes?: number;
|
||||
totalBytes?: number;
|
||||
error?: InstallScreenBackupError;
|
||||
onCancel: () => void;
|
||||
onRetry: () => void;
|
||||
onRestartLink: () => void;
|
||||
export type PropsType = Readonly<
|
||||
{
|
||||
i18n: LocalizerType;
|
||||
|
||||
// Updater UI
|
||||
updates: UpdatesStateType;
|
||||
currentVersion: string;
|
||||
OS: string;
|
||||
startUpdate: () => void;
|
||||
forceUpdate: () => void;
|
||||
}>;
|
||||
error?: InstallScreenBackupError;
|
||||
onCancel: () => void;
|
||||
onRetry: () => void;
|
||||
onRestartLink: () => void;
|
||||
|
||||
export function InstallScreenBackupImportStep({
|
||||
i18n,
|
||||
backupStep,
|
||||
currentBytes,
|
||||
totalBytes,
|
||||
error,
|
||||
onCancel,
|
||||
onRetry,
|
||||
onRestartLink,
|
||||
// Updater UI
|
||||
updates: UpdatesStateType;
|
||||
currentVersion: string;
|
||||
OS: string;
|
||||
startUpdate: () => void;
|
||||
forceUpdate: () => void;
|
||||
} & (
|
||||
| {
|
||||
backupStep: InstallScreenBackupStep.WaitForBackup;
|
||||
}
|
||||
| {
|
||||
backupStep:
|
||||
| InstallScreenBackupStep.Download
|
||||
| InstallScreenBackupStep.Process;
|
||||
currentBytes: number;
|
||||
totalBytes: number;
|
||||
}
|
||||
)
|
||||
>;
|
||||
|
||||
export function InstallScreenBackupImportStep(props: PropsType): JSX.Element {
|
||||
const {
|
||||
i18n,
|
||||
backupStep,
|
||||
error,
|
||||
onCancel,
|
||||
onRetry,
|
||||
onRestartLink,
|
||||
updates,
|
||||
currentVersion,
|
||||
OS,
|
||||
startUpdate,
|
||||
forceUpdate,
|
||||
} = props;
|
||||
|
||||
updates,
|
||||
currentVersion,
|
||||
OS,
|
||||
startUpdate,
|
||||
forceUpdate,
|
||||
}: PropsType): JSX.Element {
|
||||
const [isConfirmingCancel, setIsConfirmingCancel] = useState(false);
|
||||
const [isConfirmingSkip, setIsConfirmingSkip] = useState(false);
|
||||
|
||||
|
@ -92,52 +102,6 @@ export function InstallScreenBackupImportStep({
|
|||
setIsConfirmingSkip(false);
|
||||
}, [onRetry]);
|
||||
|
||||
let progress: JSX.Element;
|
||||
if (currentBytes != null && totalBytes != null) {
|
||||
const fractionComplete = roundFractionForProgressBar(
|
||||
currentBytes / totalBytes
|
||||
);
|
||||
|
||||
let hint: string;
|
||||
if (backupStep === InstallScreenBackupStep.Download) {
|
||||
hint = i18n('icu:BackupImportScreen__progressbar-hint', {
|
||||
currentSize: formatFileSize(currentBytes),
|
||||
totalSize: formatFileSize(totalBytes),
|
||||
fractionComplete,
|
||||
});
|
||||
} else if (backupStep === InstallScreenBackupStep.Process) {
|
||||
hint = i18n('icu:BackupImportScreen__progressbar-hint--processing');
|
||||
} else {
|
||||
throw missingCaseError(backupStep);
|
||||
}
|
||||
|
||||
progress = (
|
||||
<>
|
||||
<ProgressBar
|
||||
key={backupStep}
|
||||
fractionComplete={fractionComplete}
|
||||
isRTL={i18n.getLocaleDirection() === 'rtl'}
|
||||
/>
|
||||
<div className="InstallScreenBackupImportStep__progressbar-hint">
|
||||
{hint}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
progress = (
|
||||
<>
|
||||
<ProgressBar
|
||||
key={backupStep}
|
||||
fractionComplete={0}
|
||||
isRTL={i18n.getLocaleDirection() === 'rtl'}
|
||||
/>
|
||||
<div className="InstallScreenBackupImportStep__progressbar-hint">
|
||||
{i18n('icu:BackupImportScreen__progressbar-hint--preparing')}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const learnMoreLink = (parts: Array<string | JSX.Element>) => (
|
||||
<a href={SYNCING_MESSAGES_SECURITY_URL} rel="noreferrer" target="_blank">
|
||||
{parts}
|
||||
|
@ -209,13 +173,12 @@ export function InstallScreenBackupImportStep({
|
|||
return (
|
||||
<div className="InstallScreenBackupImportStep">
|
||||
<TitlebarDragArea />
|
||||
|
||||
<InstallScreenSignalLogo />
|
||||
<div className="InstallScreenBackupImportStep__content">
|
||||
<h3 className="InstallScreenBackupImportStep__title">
|
||||
{i18n('icu:BackupImportScreen__title')}
|
||||
</h3>
|
||||
{progress}
|
||||
<ProgressBarAndDescription {...props} />
|
||||
<div className="InstallScreenBackupImportStep__description">
|
||||
{i18n('icu:BackupImportScreen__description')}
|
||||
</div>
|
||||
|
@ -289,3 +252,76 @@ export function InstallScreenBackupImportStep({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type ProgressBarPropsType = Readonly<
|
||||
{
|
||||
i18n: LocalizerType;
|
||||
} & (
|
||||
| {
|
||||
backupStep: InstallScreenBackupStep.WaitForBackup;
|
||||
}
|
||||
| {
|
||||
backupStep:
|
||||
| InstallScreenBackupStep.Download
|
||||
| InstallScreenBackupStep.Process;
|
||||
currentBytes: number;
|
||||
totalBytes: number;
|
||||
}
|
||||
)
|
||||
>;
|
||||
|
||||
function ProgressBarAndDescription(props: ProgressBarPropsType): JSX.Element {
|
||||
const { backupStep, i18n } = props;
|
||||
if (backupStep === InstallScreenBackupStep.WaitForBackup) {
|
||||
return (
|
||||
<>
|
||||
<ProgressBar
|
||||
fractionComplete={null}
|
||||
isRTL={i18n.getLocaleDirection() === 'rtl'}
|
||||
/>
|
||||
<div className="InstallScreenBackupImportStep__progressbar-hint">
|
||||
{i18n('icu:BackupImportScreen__progressbar-hint--preparing')}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const { currentBytes, totalBytes } = props;
|
||||
|
||||
const fractionComplete = roundFractionForProgressBar(
|
||||
currentBytes / totalBytes
|
||||
);
|
||||
|
||||
if (backupStep === InstallScreenBackupStep.Download) {
|
||||
return (
|
||||
<>
|
||||
<ProgressBar
|
||||
fractionComplete={fractionComplete}
|
||||
isRTL={i18n.getLocaleDirection() === 'rtl'}
|
||||
/>
|
||||
<div className="InstallScreenBackupImportStep__progressbar-hint">
|
||||
{i18n('icu:BackupImportScreen__progressbar-hint', {
|
||||
currentSize: formatFileSize(currentBytes),
|
||||
totalSize: formatFileSize(totalBytes),
|
||||
fractionComplete,
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else if (backupStep === InstallScreenBackupStep.Process) {
|
||||
return (
|
||||
<>
|
||||
<ProgressBar
|
||||
fractionComplete={fractionComplete}
|
||||
isRTL={i18n.getLocaleDirection() === 'rtl'}
|
||||
/>
|
||||
<div className="InstallScreenBackupImportStep__progressbar-hint">
|
||||
{i18n('icu:BackupImportScreen__progressbar-hint--processing')}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
throw missingCaseError(backupStep);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@ import type { ReactElement } from 'react';
|
|||
import React from 'react';
|
||||
|
||||
export function InstallScreenSignalLogo(): ReactElement {
|
||||
return <div className="InstallScreenSignalLogo">Signal</div>;
|
||||
return <div className="InstallScreenSignalLogo" />;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue