Add backup attachment restore idle state
This commit is contained in:
parent
56ccd02232
commit
8601baa7f5
13 changed files with 122 additions and 17 deletions
|
@ -4787,6 +4787,14 @@
|
|||
"messageformat": "Restore paused",
|
||||
"description": "Label indicating media (attachment) download progress has been paused (due to user interaction)"
|
||||
},
|
||||
"icu:BackupMediaDownloadProgress__title-idle": {
|
||||
"messageformat": "{currentSize} of {totalSize} restored",
|
||||
"description": "Label indicating media (attachment) download progress"
|
||||
},
|
||||
"icu:BackupMediaDownloadProgress__description-idle": {
|
||||
"messageformat": "Media will continue to download in the background",
|
||||
"description": "Description text when attachment download is idle, and will continue in the background"
|
||||
},
|
||||
"icu:BackupMediaDownloadProgress__button-pause": {
|
||||
"messageformat": "Pause transfer",
|
||||
"description": "Text for button to pause media (attachment) download after backup impor"
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
.BackupMediaDownloadProgress {
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 11px;
|
||||
padding-inline-end: 16px;
|
||||
|
@ -26,6 +25,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.BackupMediaDownloadProgress__icon {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.BackupMediaDownloadProgress__icon--complete {
|
||||
&::after {
|
||||
content: '';
|
||||
|
@ -46,6 +49,27 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.BackupMediaDownloadProgress__icon--idle {
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
@include light-theme {
|
||||
@include color-svg(
|
||||
'../images/icons/v3/backup/backup-bold.svg',
|
||||
$color-ultramarine
|
||||
);
|
||||
}
|
||||
@include dark-theme {
|
||||
@include color-svg(
|
||||
'../images/icons/v3/backup/backup-bold.svg',
|
||||
$color-ultramarine-light
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
button.BackupMediaDownloadProgress__button {
|
||||
@include button-reset;
|
||||
@include font-subtitle-bold;
|
||||
|
@ -86,7 +110,7 @@ button.BackupMediaDownloadProgress__button-close {
|
|||
width: 20px;
|
||||
height: 20px;
|
||||
@include light-theme {
|
||||
@include color-svg('../images/icons/v3/x/x.svg', $color-gray-75);
|
||||
@include color-svg('../images/icons/v3/x/x.svg', $color-gray-45);
|
||||
}
|
||||
@include dark-theme {
|
||||
@include color-svg('../images/icons/v3/x/x.svg', $color-gray-20);
|
||||
|
@ -102,7 +126,7 @@ button.BackupMediaDownloadProgress__button-close {
|
|||
min-height: 36px;
|
||||
}
|
||||
|
||||
.BackupMediaDownloadProgress__progressbar-hint {
|
||||
.BackupMediaDownloadProgress__description {
|
||||
@include font-subtitle;
|
||||
|
||||
@include light-theme {
|
||||
|
|
|
@ -45,6 +45,14 @@ export function Paused(args: PropsType): JSX.Element {
|
|||
return <BackupMediaDownloadProgress {...args} isPaused />;
|
||||
}
|
||||
|
||||
export function Idle(args: PropsType): JSX.Element {
|
||||
return <BackupMediaDownloadProgress {...args} isIdle />;
|
||||
}
|
||||
|
||||
export function PausedAndIdle(args: PropsType): JSX.Element {
|
||||
return <BackupMediaDownloadProgress {...args} isPaused isIdle />;
|
||||
}
|
||||
|
||||
export function Complete(args: PropsType): JSX.Element {
|
||||
return (
|
||||
<BackupMediaDownloadProgress {...args} downloadedBytes={args.totalBytes} />
|
||||
|
|
|
@ -14,6 +14,7 @@ export type PropsType = Readonly<{
|
|||
i18n: LocalizerType;
|
||||
downloadedBytes: number;
|
||||
totalBytes: number;
|
||||
isIdle: boolean;
|
||||
isPaused: boolean;
|
||||
handleCancel: VoidFunction;
|
||||
handleClose: VoidFunction;
|
||||
|
@ -25,6 +26,7 @@ export function BackupMediaDownloadProgress({
|
|||
i18n,
|
||||
downloadedBytes,
|
||||
totalBytes,
|
||||
isIdle,
|
||||
isPaused,
|
||||
handleCancel: handleConfirmedCancel,
|
||||
handleClose,
|
||||
|
@ -45,31 +47,57 @@ export function BackupMediaDownloadProgress({
|
|||
downloadedBytes / totalBytes
|
||||
);
|
||||
|
||||
const closeButton = (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClose}
|
||||
className="BackupMediaDownloadProgress__button-close"
|
||||
aria-label={i18n('icu:close')}
|
||||
/>
|
||||
);
|
||||
|
||||
let content: JSX.Element | undefined;
|
||||
let icon: JSX.Element | undefined;
|
||||
let actionButton: JSX.Element | undefined;
|
||||
if (fractionComplete === 1) {
|
||||
icon = <div className="BackupMediaDownloadProgress__icon--complete" />;
|
||||
icon = (
|
||||
<div className="BackupMediaDownloadProgress__icon BackupMediaDownloadProgress__icon--complete" />
|
||||
);
|
||||
content = (
|
||||
<>
|
||||
<div className="BackupMediaDownloadProgress__title">
|
||||
{i18n('icu:BackupMediaDownloadProgress__title-complete')}
|
||||
</div>
|
||||
<div className="BackupMediaDownloadProgress__progressbar-hint">
|
||||
<div className="BackupMediaDownloadProgress__description">
|
||||
{formatFileSize(downloadedBytes)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
actionButton = (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClose}
|
||||
className="BackupMediaDownloadProgress__button-close"
|
||||
aria-label={i18n('icu:close')}
|
||||
/>
|
||||
actionButton = closeButton;
|
||||
} else if (isIdle && !isPaused) {
|
||||
icon = (
|
||||
<div className="BackupMediaDownloadProgress__icon BackupMediaDownloadProgress__icon--idle" />
|
||||
);
|
||||
content = (
|
||||
<>
|
||||
<div className="BackupMediaDownloadProgress__title">
|
||||
{i18n('icu:BackupMediaDownloadProgress__title-idle', {
|
||||
currentSize: formatFileSize(downloadedBytes),
|
||||
totalSize: formatFileSize(totalBytes),
|
||||
})}
|
||||
</div>
|
||||
<div className="BackupMediaDownloadProgress__description">
|
||||
{i18n('icu:BackupMediaDownloadProgress__description-idle')}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
actionButton = closeButton;
|
||||
} else {
|
||||
icon = <ProgressCircle fractionComplete={fractionComplete} />;
|
||||
icon = (
|
||||
<div className="BackupMediaDownloadProgress__icon">
|
||||
<ProgressCircle fractionComplete={fractionComplete} />
|
||||
</div>
|
||||
);
|
||||
|
||||
if (isPaused) {
|
||||
content = (
|
||||
|
@ -94,7 +122,7 @@ export function BackupMediaDownloadProgress({
|
|||
{i18n('icu:BackupMediaDownloadProgress__title-in-progress')}
|
||||
</div>
|
||||
|
||||
<div className="BackupMediaDownloadProgress__progressbar-hint">
|
||||
<div className="BackupMediaDownloadProgress__description">
|
||||
{i18n('icu:BackupMediaDownloadProgress__progressbar-hint', {
|
||||
currentSize: formatFileSize(downloadedBytes),
|
||||
totalSize: formatFileSize(totalBytes),
|
||||
|
|
|
@ -139,6 +139,7 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => {
|
|||
},
|
||||
backupMediaDownloadProgress: {
|
||||
downloadBannerDismissed: false,
|
||||
isIdle: false,
|
||||
isPaused: false,
|
||||
totalBytes: 0,
|
||||
downloadedBytes: 0,
|
||||
|
|
|
@ -62,6 +62,7 @@ export type PropsType = {
|
|||
backupMediaDownloadProgress: {
|
||||
totalBytes: number;
|
||||
downloadedBytes: number;
|
||||
isIdle: boolean;
|
||||
isPaused: boolean;
|
||||
downloadBannerDismissed: boolean;
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
AttachmentSizeError,
|
||||
type AttachmentType,
|
||||
AttachmentVariant,
|
||||
mightBeOnBackupTier,
|
||||
} from '../types/Attachment';
|
||||
import { __DEPRECATED$getMessageById } from '../messages/getMessageById';
|
||||
import {
|
||||
|
@ -185,7 +186,14 @@ export class AttachmentDownloadManager extends JobManager<CoreAttachmentDownload
|
|||
size: attachment.size,
|
||||
ciphertextSize: getAttachmentCiphertextLength(attachment.size),
|
||||
attachment,
|
||||
source,
|
||||
// If the attachment does not have a backupLocator, we don't want to store it as a
|
||||
// "backup import" attachment, since it's really just a normal attachment that we'll
|
||||
// try to download from the transit tier (or it's an invalid attachment, etc.). We
|
||||
// may need to extend the attachment_downloads table in the future to better
|
||||
// differentiate source vs. location.
|
||||
source: mightBeOnBackupTier(attachment)
|
||||
? source
|
||||
: AttachmentDownloadSource.STANDARD,
|
||||
});
|
||||
|
||||
if (!parseResult.success) {
|
||||
|
@ -237,6 +245,13 @@ export class AttachmentDownloadManager extends JobManager<CoreAttachmentDownload
|
|||
messageIds
|
||||
);
|
||||
}
|
||||
|
||||
static async waitForIdle(callback?: VoidFunction): Promise<void> {
|
||||
await AttachmentDownloadManager.instance.waitForIdle();
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type DependenciesType = {
|
||||
|
@ -284,7 +299,7 @@ async function runDownloadAttachmentJob({
|
|||
};
|
||||
}
|
||||
|
||||
if (job.attachment.backupLocator?.mediaName) {
|
||||
if (mightBeOnBackupTier(job.attachment)) {
|
||||
const currentDownloadedSize =
|
||||
window.storage.get('backupMediaDownloadCompletedBytes') ?? 0;
|
||||
drop(
|
||||
|
|
|
@ -88,6 +88,7 @@ export abstract class JobManager<CoreJobType> {
|
|||
|
||||
this.enabled = true;
|
||||
await this.params.markAllJobsInactive();
|
||||
await this.maybeStartJobs();
|
||||
this.tick();
|
||||
}
|
||||
|
||||
|
@ -241,7 +242,7 @@ export abstract class JobManager<CoreJobType> {
|
|||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
if (nextJobs.length === 0) {
|
||||
if (nextJobs.length === 0 && this.activeJobs.size === 0) {
|
||||
if (this.idleCallbacks.length > 0) {
|
||||
const callbacks = this.idleCallbacks;
|
||||
this.idleCallbacks = [];
|
||||
|
|
|
@ -109,6 +109,7 @@ import { getRoomIdFromRootKey } from '../../util/callLinksRingrtc';
|
|||
import { loadAllAndReinitializeRedux } from '../allLoaders';
|
||||
import { resetBackupMediaDownloadProgress } from '../../util/backupMediaDownload';
|
||||
import { getEnvironment, isTestEnvironment } from '../../environment';
|
||||
import { drop } from '../../util/drop';
|
||||
|
||||
const MAX_CONCURRENCY = 10;
|
||||
|
||||
|
@ -339,6 +340,11 @@ export class BackupImportStream extends Writable {
|
|||
!isTestEnvironment(getEnvironment())
|
||||
) {
|
||||
await AttachmentDownloadManager.start();
|
||||
drop(
|
||||
AttachmentDownloadManager.waitForIdle(async () => {
|
||||
await window.storage.put('backupMediaDownloadIdle', true);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
done();
|
||||
|
|
|
@ -258,10 +258,12 @@ export const getBackupMediaDownloadProgress = createSelector(
|
|||
downloadedBytes: number;
|
||||
isPaused: boolean;
|
||||
downloadBannerDismissed: boolean;
|
||||
isIdle: boolean;
|
||||
} => ({
|
||||
totalBytes: state.backupMediaDownloadTotalBytes ?? 0,
|
||||
downloadedBytes: state.backupMediaDownloadCompletedBytes ?? 0,
|
||||
isPaused: state.backupMediaDownloadPaused ?? false,
|
||||
isIdle: state.backupMediaDownloadIdle ?? false,
|
||||
downloadBannerDismissed: state.backupMediaDownloadBannerDismissed ?? false,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -409,6 +409,15 @@ describe('AttachmentDownloadManager/JobManager', () => {
|
|||
idx % 2 === 0
|
||||
? AttachmentDownloadSource.BACKUP_IMPORT
|
||||
: AttachmentDownloadSource.STANDARD,
|
||||
digest: `digestFor${idx}`,
|
||||
attachment: {
|
||||
contentType: MIME.IMAGE_JPEG,
|
||||
size: 128,
|
||||
digest: `digestFor${idx}`,
|
||||
backupLocator: {
|
||||
mediaName: 'medianame',
|
||||
},
|
||||
},
|
||||
}));
|
||||
// make one of the backup job messages visible to test that code path as well
|
||||
downloadManager?.updateVisibleTimelineMessages(['message-0', 'message-1']);
|
||||
|
|
1
ts/types/Storage.d.ts
vendored
1
ts/types/Storage.d.ts
vendored
|
@ -147,6 +147,7 @@ export type StorageAccessType = {
|
|||
backupMediaDownloadCompletedBytes: number;
|
||||
backupMediaDownloadPaused: boolean;
|
||||
backupMediaDownloadBannerDismissed: boolean;
|
||||
backupMediaDownloadIdle: boolean;
|
||||
setBackupSignatureKey: boolean;
|
||||
lastReceivedAtCounter: number;
|
||||
preferredReactionEmoji: ReadonlyArray<string>;
|
||||
|
|
|
@ -17,6 +17,7 @@ export async function resetBackupMediaDownloadItems(): Promise<void> {
|
|||
window.storage.remove('backupMediaDownloadCompletedBytes'),
|
||||
window.storage.remove('backupMediaDownloadBannerDismissed'),
|
||||
window.storage.remove('backupMediaDownloadPaused'),
|
||||
window.storage.remove('backupMediaDownloadIdle'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue