diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 2a720c07f..0a5220eec 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -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"
diff --git a/stylesheets/components/BackupMediaDownloadProgress.scss b/stylesheets/components/BackupMediaDownloadProgress.scss
index 40ec849b9..bfc991e0c 100644
--- a/stylesheets/components/BackupMediaDownloadProgress.scss
+++ b/stylesheets/components/BackupMediaDownloadProgress.scss
@@ -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 {
diff --git a/ts/components/BackupMediaDownloadProgress.stories.tsx b/ts/components/BackupMediaDownloadProgress.stories.tsx
index bae36c8ac..b1b1aba40 100644
--- a/ts/components/BackupMediaDownloadProgress.stories.tsx
+++ b/ts/components/BackupMediaDownloadProgress.stories.tsx
@@ -45,6 +45,14 @@ export function Paused(args: PropsType): JSX.Element {
return ;
}
+export function Idle(args: PropsType): JSX.Element {
+ return ;
+}
+
+export function PausedAndIdle(args: PropsType): JSX.Element {
+ return ;
+}
+
export function Complete(args: PropsType): JSX.Element {
return (
diff --git a/ts/components/BackupMediaDownloadProgress.tsx b/ts/components/BackupMediaDownloadProgress.tsx
index ca9b44b0f..c74640c02 100644
--- a/ts/components/BackupMediaDownloadProgress.tsx
+++ b/ts/components/BackupMediaDownloadProgress.tsx
@@ -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 = (
+
+ );
+
let content: JSX.Element | undefined;
let icon: JSX.Element | undefined;
let actionButton: JSX.Element | undefined;
if (fractionComplete === 1) {
- icon =
;
+ icon = (
+
+ );
content = (
<>
{i18n('icu:BackupMediaDownloadProgress__title-complete')}
-
+
{formatFileSize(downloadedBytes)}
>
);
- actionButton = (
-
+ actionButton = closeButton;
+ } else if (isIdle && !isPaused) {
+ icon = (
+
);
+ content = (
+ <>
+
+ {i18n('icu:BackupMediaDownloadProgress__title-idle', {
+ currentSize: formatFileSize(downloadedBytes),
+ totalSize: formatFileSize(totalBytes),
+ })}
+
+
+ {i18n('icu:BackupMediaDownloadProgress__description-idle')}
+
+ >
+ );
+ actionButton = closeButton;
} else {
- icon =
;
+ icon = (
+
+ );
if (isPaused) {
content = (
@@ -94,7 +122,7 @@ export function BackupMediaDownloadProgress({
{i18n('icu:BackupMediaDownloadProgress__title-in-progress')}
-
+
{i18n('icu:BackupMediaDownloadProgress__progressbar-hint', {
currentSize: formatFileSize(downloadedBytes),
totalSize: formatFileSize(totalBytes),
diff --git a/ts/components/LeftPane.stories.tsx b/ts/components/LeftPane.stories.tsx
index fda271a2f..f84e84bc1 100644
--- a/ts/components/LeftPane.stories.tsx
+++ b/ts/components/LeftPane.stories.tsx
@@ -139,6 +139,7 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => {
},
backupMediaDownloadProgress: {
downloadBannerDismissed: false,
+ isIdle: false,
isPaused: false,
totalBytes: 0,
downloadedBytes: 0,
diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx
index e2d0229ab..e7860c387 100644
--- a/ts/components/LeftPane.tsx
+++ b/ts/components/LeftPane.tsx
@@ -62,6 +62,7 @@ export type PropsType = {
backupMediaDownloadProgress: {
totalBytes: number;
downloadedBytes: number;
+ isIdle: boolean;
isPaused: boolean;
downloadBannerDismissed: boolean;
};
diff --git a/ts/jobs/AttachmentDownloadManager.ts b/ts/jobs/AttachmentDownloadManager.ts
index 341058e3b..09c0c8390 100644
--- a/ts/jobs/AttachmentDownloadManager.ts
+++ b/ts/jobs/AttachmentDownloadManager.ts
@@ -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 {
+ 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(
diff --git a/ts/jobs/JobManager.ts b/ts/jobs/JobManager.ts
index 20fa294ec..c57b8113a 100644
--- a/ts/jobs/JobManager.ts
+++ b/ts/jobs/JobManager.ts
@@ -88,6 +88,7 @@ export abstract class JobManager {
this.enabled = true;
await this.params.markAllJobsInactive();
+ await this.maybeStartJobs();
this.tick();
}
@@ -241,7 +242,7 @@ export abstract class JobManager {
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 = [];
diff --git a/ts/services/backups/import.ts b/ts/services/backups/import.ts
index 582caf345..ed00eac97 100644
--- a/ts/services/backups/import.ts
+++ b/ts/services/backups/import.ts
@@ -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();
diff --git a/ts/state/selectors/items.ts b/ts/state/selectors/items.ts
index e9c79514f..2f5f97ae2 100644
--- a/ts/state/selectors/items.ts
+++ b/ts/state/selectors/items.ts
@@ -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,
})
);
diff --git a/ts/test-electron/services/AttachmentDownloadManager_test.ts b/ts/test-electron/services/AttachmentDownloadManager_test.ts
index 2bd84d9d3..3d5fd41a1 100644
--- a/ts/test-electron/services/AttachmentDownloadManager_test.ts
+++ b/ts/test-electron/services/AttachmentDownloadManager_test.ts
@@ -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']);
diff --git a/ts/types/Storage.d.ts b/ts/types/Storage.d.ts
index 18f69e6f2..1d4b0866c 100644
--- a/ts/types/Storage.d.ts
+++ b/ts/types/Storage.d.ts
@@ -147,6 +147,7 @@ export type StorageAccessType = {
backupMediaDownloadCompletedBytes: number;
backupMediaDownloadPaused: boolean;
backupMediaDownloadBannerDismissed: boolean;
+ backupMediaDownloadIdle: boolean;
setBackupSignatureKey: boolean;
lastReceivedAtCounter: number;
preferredReactionEmoji: ReadonlyArray;
diff --git a/ts/util/backupMediaDownload.ts b/ts/util/backupMediaDownload.ts
index 24599ead8..a7e352226 100644
--- a/ts/util/backupMediaDownload.ts
+++ b/ts/util/backupMediaDownload.ts
@@ -17,6 +17,7 @@ export async function resetBackupMediaDownloadItems(): Promise {
window.storage.remove('backupMediaDownloadCompletedBytes'),
window.storage.remove('backupMediaDownloadBannerDismissed'),
window.storage.remove('backupMediaDownloadPaused'),
+ window.storage.remove('backupMediaDownloadIdle'),
]);
}