From fc67ac8e7bfdc743ca8c60216a8e120e56d46964 Mon Sep 17 00:00:00 2001 From: trevor-signal <131492920+trevor-signal@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:30:13 -0400 Subject: [PATCH] Add backup media download progress to settings pane --- _locales/en/messages.json | 46 ++++-- .../BackupMediaDownloadProgress.scss | 3 + .../BackupMediaDownloadProgressSettings.scss | 31 +++++ stylesheets/manifest.scss | 1 + ...pMediaDownloadCancelConfirmationDialog.tsx | 16 ++- .../BackupMediaDownloadProgressSettings.tsx | 128 +++++++++++++++++ ts/components/DialogUpdate.tsx | 6 +- ts/components/Preferences.stories.tsx | 52 +++++++ ts/components/Preferences.tsx | 15 ++ ts/components/PreferencesBackups.tsx | 42 +++++- .../ChatSessionRefreshedNotification.tsx | 6 +- .../ConversationDetails.tsx | 9 +- ts/state/smart/Preferences.tsx | 25 +++- ts/types/backups.ts | 7 + ts/util/getLocalizedUrl.ts | 26 ---- ts/util/mapToSupportLocale.ts | 131 ------------------ 16 files changed, 355 insertions(+), 189 deletions(-) create mode 100644 stylesheets/components/BackupMediaDownloadProgressSettings.scss create mode 100644 ts/components/BackupMediaDownloadProgressSettings.tsx delete mode 100644 ts/util/getLocalizedUrl.ts delete mode 100644 ts/util/mapToSupportLocale.ts diff --git a/_locales/en/messages.json b/_locales/en/messages.json index c088012471..34ff8e46b8 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -5557,11 +5557,11 @@ "description": "Text of the retry button of the error modal in the backup import screen" }, "icu:BackupMediaDownloadProgress__title-in-progress": { - "messageformat": "Restoring media", + "messageformat": "Syncing media", "description": "Label next to a progress bar showing active media (attachment) download progress after restoring from backup" }, "icu:BackupMediaDownloadProgress__title-paused": { - "messageformat": "Restore paused", + "messageformat": "Syncing paused", "description": "Label indicating media (attachment) download progress has been paused (due to user interaction)" }, "icu:BackupMediaDownloadProgress__title-idle": { @@ -5581,15 +5581,15 @@ "description": "Description text when attachment download in progress but offline" }, "icu:BackupMediaDownloadProgress__button-pause": { - "messageformat": "Pause transfer", + "messageformat": "Pause", "description": "Text for button to pause media (attachment) download after backup impor" }, "icu:BackupMediaDownloadProgress__button-resume": { - "messageformat": "Resume transfer", + "messageformat": "Resume", "description": "Text for button to resume media (attachment) download after backup import" }, "icu:BackupMediaDownloadProgress__button-cancel": { - "messageformat": "Cancel transfer", + "messageformat": "Cancel syncing", "description": "Text for button to cancel (pause) media (attachment) download after backup import" }, "icu:BackupMediaDownloadProgress__button-more": { @@ -5605,21 +5605,45 @@ "description": "Hint under the progressbar showing media (attachment) download progress after restoring from backup" }, "icu:BackupMediaDownloadCancelConfirmation__title": { - "messageformat": "Cancel media transfer?", - "description": "Text for button to cancel (pause) media (attachment) download after backup import" + "messageformat": "Cancel media syncing?", + "description": "Text for button to cancel media (attachment) download after backup import" }, "icu:BackupMediaDownloadCancelConfirmation__description": { - "messageformat": "Your messages and media have not completed restoring. If you choose to cancel, you can transfer again from Settings.", - "description": "Text for button to cancel (pause) media (attachment) download after backup import" + "messageformat": "Your media has not completed syncing. If you choose to cancel, you can sync again by re-linking this device. Learn more.", + "description": "Text for button to cancel media (attachment) download after backup import" }, "icu:BackupMediaDownloadCancelConfirmation__button-continue": { - "messageformat": "Continue transfer", + "messageformat": "Continue syncing", "description": "Text for button to close confirmation dialog and continue media (attachment) download" }, "icu:BackupMediaDownloadCancelConfirmation__button-confirm-cancel": { - "messageformat": "Cancel transfer", + "messageformat": "Cancel syncing", "description": "Text for button to confirm cancellation of media (attachment) download" }, + "icu:BackupMediaDownloadProgressSettings__paused--title": { + "messageformat": "Syncing paused", + "description": "Label indicating media (attachment) download progress when the transfer has been paused" + }, + "icu:BackupMediaDownloadProgressSettings__paused--description": { + "messageformat": "Click “{resumeButtonText}” to continue syncing", + "description": "Shown below progress bar when the transfer has been paused. {resumeButtonText} will be 'icu:BackupMediaDownloadProgressSettings__button-resume'" + }, + "icu:BackupMediaDownloadProgressSettings__button-resume": { + "messageformat": "Resume", + "description": "Text for button to resume media (attachment) download after backup import" + }, + "icu:BackupMediaDownloadProgressSettings__button-pause": { + "messageformat": "Pause", + "description": "Text for button to pause media (attachment) download after backup import" + }, + "icu:BackupMediaDownloadProgressSettings__title-in-progress": { + "messageformat": "Syncing media", + "description": "Label next to a progress bar showing active media (attachment) download progress after restoring from backup" + }, + "icu:BackupMediaDownloadProgressSettings__progressbar-hint": { + "messageformat": "{currentSize} of {totalSize} ({fractionComplete, number, percent})...", + "description": "Hint under the progressbar in the backup import screen" + }, "icu:CriticalIdlePrimaryDeviceModal__title": { "messageformat": "Account action required", "description": "Title for modal shown when a user must use their idle primary device to retain their account" diff --git a/stylesheets/components/BackupMediaDownloadProgress.scss b/stylesheets/components/BackupMediaDownloadProgress.scss index a1a9af7a4c..72ae1c00d1 100644 --- a/stylesheets/components/BackupMediaDownloadProgress.scss +++ b/stylesheets/components/BackupMediaDownloadProgress.scss @@ -138,4 +138,7 @@ button.BackupMediaDownloadProgress__button-close { .BackupMediaDownloadCancelConfirmation { min-width: 440px; + a { + text-decoration: none; + } } diff --git a/stylesheets/components/BackupMediaDownloadProgressSettings.scss b/stylesheets/components/BackupMediaDownloadProgressSettings.scss new file mode 100644 index 0000000000..d2150432c3 --- /dev/null +++ b/stylesheets/components/BackupMediaDownloadProgressSettings.scss @@ -0,0 +1,31 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +@use '../mixins'; +@use '../variables'; + +.BackupMediaDownloadProgressSettings { + display: flex; + flex-direction: row; + align-items: center; + gap: 20px; + + &__buttons { + display: flex; + gap: 12px; + } +} + +.BackupMediaDownloadProgressSettings__content { + display: flex; + flex-direction: column; + justify-content: center; + gap: 5px; + flex: 1; +} + +.BackupMediaDownloadProgressSettings__description { + color: light-dark(variables.$color-gray-60, variables.$color-gray-25); + font-size: 12px; + line-height: 16px; +} diff --git a/stylesheets/manifest.scss b/stylesheets/manifest.scss index 252ed25d79..fbc8b1a698 100644 --- a/stylesheets/manifest.scss +++ b/stylesheets/manifest.scss @@ -33,6 +33,7 @@ @use 'components/AvatarTextEditor.scss'; @use 'components/BackfillFailureModal.scss'; @use 'components/BackupMediaDownloadProgress.scss'; +@use 'components/BackupMediaDownloadProgressSettings.scss'; @use 'components/BadgeCarouselIndex.scss'; @use 'components/BadgeDialog.scss'; @use 'components/BadgeSustainerInstructionsDialog.scss'; diff --git a/ts/components/BackupMediaDownloadCancelConfirmationDialog.tsx b/ts/components/BackupMediaDownloadCancelConfirmationDialog.tsx index 6cf1034184..aeb67f10eb 100644 --- a/ts/components/BackupMediaDownloadCancelConfirmationDialog.tsx +++ b/ts/components/BackupMediaDownloadCancelConfirmationDialog.tsx @@ -5,7 +5,10 @@ import React from 'react'; import { ConfirmationDialog } from './ConfirmationDialog'; import type { LocalizerType } from '../types/I18N'; +import { I18n } from './I18n'; +const BACKUP_AND_RESTORE_SUPPORT_PAGE = + 'https://support.signal.org/hc/articles/360007059752-Backup-and-Restore-Messages'; export function BackupMediaDownloadCancelConfirmationDialog({ i18n, handleConfirmCancel, @@ -15,6 +18,11 @@ export function BackupMediaDownloadCancelConfirmationDialog({ handleConfirmCancel: VoidFunction; handleDialogClose: VoidFunction; }): JSX.Element | null { + const learnMoreLink = (parts: Array) => ( + + {parts} + + ); return ( - {i18n('icu:BackupMediaDownloadCancelConfirmation__description')} + ); } diff --git a/ts/components/BackupMediaDownloadProgressSettings.tsx b/ts/components/BackupMediaDownloadProgressSettings.tsx new file mode 100644 index 0000000000..33a8d0b721 --- /dev/null +++ b/ts/components/BackupMediaDownloadProgressSettings.tsx @@ -0,0 +1,128 @@ +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React, { useState } from 'react'; + +import type { LocalizerType } from '../types/Util'; +import { formatFileSize } from '../util/formatFileSize'; +import { roundFractionForProgressBar } from '../util/numbers'; +import { ProgressBar } from './ProgressBar'; +import { Button, ButtonSize, ButtonVariant } from './Button'; +import { BackupMediaDownloadCancelConfirmationDialog } from './BackupMediaDownloadCancelConfirmationDialog'; + +export type PropsType = Readonly<{ + i18n: LocalizerType; + completedBytes: number; + totalBytes: number; + isPaused: boolean; + handleCancel: VoidFunction; + handleResume: VoidFunction; + handlePause: VoidFunction; +}>; + +export function BackupMediaDownloadProgressSettings({ + i18n, + completedBytes, + totalBytes, + isPaused, + handleCancel, + handleResume, + handlePause, +}: PropsType): JSX.Element | null { + const [isShowingCancelConfirmation, setIsShowingCancelConfirmation] = + useState(false); + const isRTL = i18n.getLocaleDirection() === 'rtl'; + if (totalBytes <= 0) { + return null; + } + + const fractionComplete = roundFractionForProgressBar( + completedBytes / totalBytes + ); + + const isCompleted = fractionComplete === 1; + + if (isCompleted) { + return null; + } + + let title: string; + let description: string; + let actionButton: JSX.Element | undefined; + + if (isPaused) { + title = i18n('icu:BackupMediaDownloadProgressSettings__paused--title'); + description = i18n( + 'icu:BackupMediaDownloadProgressSettings__paused--description', + { + resumeButtonText: i18n( + 'icu:BackupMediaDownloadProgressSettings__button-resume' + ), + } + ); + actionButton = ( + + ); + } else { + title = i18n('icu:BackupMediaDownloadProgressSettings__title-in-progress'); + description = i18n( + 'icu:BackupMediaDownloadProgressSettings__progressbar-hint', + { + currentSize: formatFileSize(completedBytes), + totalSize: formatFileSize(totalBytes), + fractionComplete, + } + ); + actionButton = ( + + ); + } + + return ( +
+
+
+ {title} +
+
+ +
+
+ {description} +
+
+
+ {actionButton} + +
+ {isShowingCancelConfirmation ? ( + setIsShowingCancelConfirmation(false)} + /> + ) : null} +
+ ); +} diff --git a/ts/components/DialogUpdate.tsx b/ts/components/DialogUpdate.tsx index d467778969..a5a0510989 100644 --- a/ts/components/DialogUpdate.tsx +++ b/ts/components/DialogUpdate.tsx @@ -10,20 +10,16 @@ import { PRODUCTION_DOWNLOAD_URL, BETA_DOWNLOAD_URL } from '../types/support'; import { I18n } from './I18n'; import { LeftPaneDialog } from './LeftPaneDialog'; import { formatFileSize } from '../util/formatFileSize'; -import { getLocalizedUrl } from '../util/getLocalizedUrl'; import type { LocalizerType } from '../types/Util'; import type { DismissOptions } from './LeftPaneDialog'; import type { WidthBreakpoint } from './_util'; function contactSupportLink(parts: ReactNode): JSX.Element { - const localizedSupportLink = getLocalizedUrl( - 'https://support.signal.org/hc/LOCALE/requests/new?desktop' - ); return ( diff --git a/ts/components/Preferences.stories.tsx b/ts/components/Preferences.stories.tsx index 273ea03f19..ef2c4b17ef 100644 --- a/ts/components/Preferences.stories.tsx +++ b/ts/components/Preferences.stories.tsx @@ -378,6 +378,9 @@ export default { removeCustomColorOnConversations: action( 'removeCustomColorOnConversations' ), + resumeBackupMediaDownload: action('resumeBackupMediaDownload'), + pauseBackupMediaDownload: action('pauseBackupMediaDownload'), + cancelBackupMediaDownload: action('cancelBackupMediaDownload'), resetAllChatColors: action('resetAllChatColors'), resetDefaultChatColor: action('resetDefaultChatColor'), savePreferredLeftPaneWidth: action('savePreferredLeftPaneWidth'), @@ -491,6 +494,55 @@ PNPDiscoverabilityDisabled.args = { page: Page.PNP, }; +export const BackupsMediaDownloadActive = Template.bind({}); +BackupsMediaDownloadActive.args = { + page: Page.BackupsDetails, + backupFeatureEnabled: true, + backupLocalBackupsEnabled: true, + cloudBackupStatus: { + protoSize: 100_000_000, + createdTimestamp: Date.now() - WEEK, + }, + backupSubscriptionStatus: { + status: 'active', + cost: { + amount: 22.99, + currencyCode: 'USD', + }, + renewalTimestamp: Date.now() + 20 * DAY, + }, + backupMediaDownloadStatus: { + completedBytes: 123_456_789, + totalBytes: 987_654_321, + isPaused: false, + isIdle: false, + }, +}; +export const BackupsMediaDownloadPaused = Template.bind({}); +BackupsMediaDownloadPaused.args = { + page: Page.BackupsDetails, + backupFeatureEnabled: true, + backupLocalBackupsEnabled: true, + cloudBackupStatus: { + protoSize: 100_000_000, + createdTimestamp: Date.now() - WEEK, + }, + backupSubscriptionStatus: { + status: 'active', + cost: { + amount: 22.99, + currencyCode: 'USD', + }, + renewalTimestamp: Date.now() + 20 * DAY, + }, + backupMediaDownloadStatus: { + completedBytes: 123_456_789, + totalBytes: 987_654_321, + isPaused: true, + isIdle: false, + }, +}; + export const BackupsPaidActive = Template.bind({}); BackupsPaidActive.args = { page: Page.Backups, diff --git a/ts/components/Preferences.tsx b/ts/components/Preferences.tsx index f619c50f39..2e87f989bb 100644 --- a/ts/components/Preferences.tsx +++ b/ts/components/Preferences.tsx @@ -74,6 +74,7 @@ import type { ThemeType, } from '../types/Util'; import type { + BackupMediaDownloadStatusType, BackupsSubscriptionType, BackupStatusType, } from '../types/backups'; @@ -118,6 +119,10 @@ export type PropsDataType = { localBackupFolder: string | undefined; cloudBackupStatus?: BackupStatusType; backupSubscriptionStatus: BackupsSubscriptionType; + backupMediaDownloadStatus?: BackupMediaDownloadStatusType; + pauseBackupMediaDownload: VoidFunction; + cancelBackupMediaDownload: VoidFunction; + resumeBackupMediaDownload: VoidFunction; blockedCount: number; customColors: Record; defaultConversationColor: DefaultConversationColorType; @@ -225,6 +230,8 @@ type PropsFunctionType = { getMessageSampleForSchemaVersion: ( version: number ) => Promise>; + resumeBackupMediaDownload: () => void; + pauseBackupMediaDownload: () => void; getConversationsWithCustomColor: (colorId: string) => Array; getPreferredBadge: PreferredBadgeSelectorType; makeSyncRequest: () => unknown; @@ -377,6 +384,10 @@ export function Preferences({ availableMicrophones, availableSpeakers, backupFeatureEnabled, + backupMediaDownloadStatus, + pauseBackupMediaDownload, + resumeBackupMediaDownload, + cancelBackupMediaDownload, backupKeyViewed, backupSubscriptionStatus, backupLocalBackupsEnabled, @@ -2154,6 +2165,10 @@ export function Preferences({ accountEntropyPool={accountEntropyPool} backupKeyViewed={backupKeyViewed} backupSubscriptionStatus={backupSubscriptionStatus} + backupMediaDownloadStatus={backupMediaDownloadStatus} + cancelBackupMediaDownload={cancelBackupMediaDownload} + pauseBackupMediaDownload={pauseBackupMediaDownload} + resumeBackupMediaDownload={resumeBackupMediaDownload} cloudBackupStatus={cloudBackupStatus} i18n={i18n} locale={resolvedLocale} diff --git a/ts/components/PreferencesBackups.tsx b/ts/components/PreferencesBackups.tsx index f6924c7557..85ed487ee5 100644 --- a/ts/components/PreferencesBackups.tsx +++ b/ts/components/PreferencesBackups.tsx @@ -5,6 +5,7 @@ import React, { useEffect, useState } from 'react'; import classNames from 'classnames'; import type { + BackupMediaDownloadStatusType, BackupsSubscriptionType, BackupStatusType, } from '../types/backups'; @@ -28,6 +29,7 @@ import type { PromptOSAuthResultType, } from '../util/os/promptOSAuthMain'; import { ConfirmationDialog } from './ConfirmationDialog'; +import { BackupMediaDownloadProgressSettings } from './BackupMediaDownloadProgressSettings'; export const SIGNAL_BACKUPS_LEARN_MORE_URL = 'https://support.signal.org/hc/articles/360007059752-Backup-and-Restore-Messages'; @@ -42,6 +44,10 @@ export function PreferencesBackups({ localBackupFolder, onBackupKeyViewedChange, pickLocalBackupFolder, + backupMediaDownloadStatus, + cancelBackupMediaDownload, + pauseBackupMediaDownload, + resumeBackupMediaDownload, page, promptOSAuth, refreshCloudBackupStatus, @@ -58,6 +64,10 @@ export function PreferencesBackups({ locale: string; onBackupKeyViewedChange: (keyViewed: boolean) => void; page: PreferencesBackupPage; + backupMediaDownloadStatus: BackupMediaDownloadStatusType | undefined; + cancelBackupMediaDownload: () => void; + pauseBackupMediaDownload: () => void; + resumeBackupMediaDownload: () => void; pickLocalBackupFolder: () => Promise; promptOSAuth: ( reason: PromptOSAuthReasonType @@ -90,6 +100,10 @@ export function PreferencesBackups({ i18n={i18n} cloudBackupStatus={cloudBackupStatus} backupSubscriptionStatus={backupSubscriptionStatus} + backupMediaDownloadStatus={backupMediaDownloadStatus} + cancelBackupMediaDownload={cancelBackupMediaDownload} + pauseBackupMediaDownload={pauseBackupMediaDownload} + resumeBackupMediaDownload={resumeBackupMediaDownload} locale={locale} /> ); @@ -468,12 +482,25 @@ function BackupsDetailsPage({ backupSubscriptionStatus, i18n, locale, + cancelBackupMediaDownload, + pauseBackupMediaDownload, + resumeBackupMediaDownload, + backupMediaDownloadStatus, }: { cloudBackupStatus?: BackupStatusType; backupSubscriptionStatus: BackupsSubscriptionType; i18n: LocalizerType; locale: string; + cancelBackupMediaDownload: () => void; + pauseBackupMediaDownload: () => void; + resumeBackupMediaDownload: () => void; + backupMediaDownloadStatus?: BackupMediaDownloadStatusType; }): JSX.Element { + const shouldShowMediaProgress = + backupMediaDownloadStatus && + backupMediaDownloadStatus.completedBytes < + backupMediaDownloadStatus.totalBytes; + return ( <>
@@ -484,12 +511,12 @@ function BackupsDetailsPage({ })}
- {cloudBackupStatus ? ( + {cloudBackupStatus || shouldShowMediaProgress ? ( - {cloudBackupStatus.createdTimestamp ? ( + {cloudBackupStatus?.createdTimestamp ? (
) : null} + {shouldShowMediaProgress && backupMediaDownloadStatus ? ( +
+ +
+ ) : null} ) : null} diff --git a/ts/components/conversation/ChatSessionRefreshedNotification.tsx b/ts/components/conversation/ChatSessionRefreshedNotification.tsx index d0f91e5f68..4c4733d661 100644 --- a/ts/components/conversation/ChatSessionRefreshedNotification.tsx +++ b/ts/components/conversation/ChatSessionRefreshedNotification.tsx @@ -10,7 +10,6 @@ import { Button, ButtonSize, ButtonVariant } from '../Button'; import { SystemMessage } from './SystemMessage'; import { ChatSessionRefreshedDialog } from './ChatSessionRefreshedDialog'; import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser'; -import { getLocalizedUrl } from '../../util/getLocalizedUrl'; type PropsHousekeepingType = { i18n: LocalizerType; @@ -34,9 +33,8 @@ export function ChatSessionRefreshedNotification( const wrappedContactSupport = useCallback(() => { setIsDialogOpen(false); - const url = getLocalizedUrl( - 'https://support.signal.org/hc/LOCALE/requests/new?desktop&chat_refreshed' - ); + const url = + 'https://support.signal.org/hc/requests/new?desktop&chat_refreshed'; openLinkInWebBrowser(url); }, [setIsDialogOpen]); diff --git a/ts/components/conversation/conversation-details/ConversationDetails.tsx b/ts/components/conversation/conversation-details/ConversationDetails.tsx index fafc24b12c..d5614a2924 100644 --- a/ts/components/conversation/conversation-details/ConversationDetails.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetails.tsx @@ -15,7 +15,6 @@ import type { SmartChooseGroupMembersModalPropsType } from '../../../state/smart import type { SmartConfirmAdditionsModalPropsType } from '../../../state/smart/ConfirmAdditionsModal'; import { assertDev } from '../../../util/assert'; import { getMutedUntilText } from '../../../util/getMutedUntilText'; -import { getLocalizedUrl } from '../../../util/getLocalizedUrl'; import type { LocalizerType, ThemeType } from '../../../types/Util'; import type { BadgeType } from '../../../badges/types'; @@ -521,9 +520,7 @@ export function ConversationDetails({ } label={i18n('icu:ConversationDetails--support-center')} onClick={() => { - openLinkInWebBrowser( - getLocalizedUrl('https://support.signal.org/hc/LOCALE') - ); + openLinkInWebBrowser('https://support.signal.org'); }} /> { openLinkInWebBrowser( - getLocalizedUrl( - 'https://support.signal.org/hc/LOCALE/requests/new?desktop' - ) + 'https://support.signal.org/hc/requests/new?desktop' ); }} /> diff --git a/ts/state/smart/Preferences.tsx b/ts/state/smart/Preferences.tsx index a9820765b3..b5652c14ae 100644 --- a/ts/state/smart/Preferences.tsx +++ b/ts/state/smart/Preferences.tsx @@ -79,6 +79,11 @@ import type { WidthBreakpoint } from '../../components/_util'; import { DialogType } from '../../types/Dialogs'; import { promptOSAuth } from '../../util/promptOSAuth'; import type { StateType } from '../reducer'; +import { + pauseBackupMediaDownload, + resumeBackupMediaDownload, + cancelBackupMediaDownload, +} from '../../util/backupMediaDownload'; const DEFAULT_NOTIFICATION_SETTING = 'message'; @@ -490,8 +495,15 @@ export function SmartPreferences(): JSX.Element | null { // Simple, one-way items - const { backupSubscriptionStatus, cloudBackupStatus, localBackupFolder } = - items; + const { + backupSubscriptionStatus, + cloudBackupStatus, + localBackupFolder, + backupMediaDownloadCompletedBytes, + backupMediaDownloadTotalBytes, + attachmentDownloadManagerIdled, + backupMediaDownloadPaused, + } = items; const defaultConversationColor = items.defaultConversationColor || DEFAULT_CONVERSATION_COLOR; const hasLinkPreviews = items.linkPreviews ?? false; @@ -712,6 +724,12 @@ export function SmartPreferences(): JSX.Element | null { backupFeatureEnabled={backupFeatureEnabled} backupKeyViewed={backupKeyViewed} backupSubscriptionStatus={backupSubscriptionStatus ?? { status: 'off' }} + backupMediaDownloadStatus={{ + completedBytes: backupMediaDownloadCompletedBytes ?? 0, + totalBytes: backupMediaDownloadTotalBytes ?? 0, + isPaused: Boolean(backupMediaDownloadPaused), + isIdle: Boolean(attachmentDownloadManagerIdled), + }} backupLocalBackupsEnabled={backupLocalBackupsEnabled} badge={badge} blockedCount={blockedCount} @@ -837,6 +855,9 @@ export function SmartPreferences(): JSX.Element | null { resetDefaultChatColor={resetDefaultChatColor} resolvedLocale={resolvedLocale} savePreferredLeftPaneWidth={savePreferredLeftPaneWidth} + resumeBackupMediaDownload={resumeBackupMediaDownload} + pauseBackupMediaDownload={pauseBackupMediaDownload} + cancelBackupMediaDownload={cancelBackupMediaDownload} selectedCamera={selectedCamera} selectedMicrophone={selectedMicrophone} selectedSpeaker={selectedSpeaker} diff --git a/ts/types/backups.ts b/ts/types/backups.ts index 3bf80b64cb..898a78cb0f 100644 --- a/ts/types/backups.ts +++ b/ts/types/backups.ts @@ -40,6 +40,13 @@ export type BackupStatusType = { protoSize?: number; }; +export type BackupMediaDownloadStatusType = { + totalBytes: number; + completedBytes: number; + isPaused: boolean; + isIdle: boolean; +}; + export type BackupsSubscriptionType = | { status: 'off' | 'not-found' | 'expired'; diff --git a/ts/util/getLocalizedUrl.ts b/ts/util/getLocalizedUrl.ts deleted file mode 100644 index 6c2c6771fa..0000000000 --- a/ts/util/getLocalizedUrl.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2025 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -import { mapToSupportLocale } from './mapToSupportLocale'; - -/** - * Ensures the provided string contains "LOCALE". - * If not, produces a readable TypeScript error. - */ -type RequiresLocale = T extends `${string}LOCALE${string}` - ? T - : `Error: The URL must contain "LOCALE" but got "${T}"`; - -/** - * Replaces "LOCALE" in a URL with the appropriate localized support locale. - * - * @param url The URL string containing "LOCALE" to be replaced - * @returns The URL with "LOCALE" replaced with the appropriate locale - */ -export function getLocalizedUrl( - url: RequiresLocale -): string { - const locale = window.SignalContext.getResolvedMessagesLocale(); - const supportLocale = mapToSupportLocale(locale); - return url.replace('LOCALE', supportLocale); -} diff --git a/ts/util/mapToSupportLocale.ts b/ts/util/mapToSupportLocale.ts deleted file mode 100644 index b3cf41032b..0000000000 --- a/ts/util/mapToSupportLocale.ts +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2021 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -export type SupportLocaleType = - | 'ar' - | 'de' - | 'en-us' - | 'es' - | 'fr' - | 'it' - | 'ja' - | 'pl' - | 'pt-br' - | 'ru' - | 'sq' - | 'zh-tw'; - -// See https://source.chromium.org/chromium/chromium/src/+/main:ui/base/l10n/l10n_util.cc -export type ElectronLocaleType = - | 'af' - | 'ar' - | 'bg' - | 'bn' - | 'ca' - | 'cs' - | 'cy' - | 'da' - | 'de' - | 'de-AT' - | 'de-CH' - | 'de-DE' - | 'de-LI' - | 'el' - | 'en' - | 'en-AU' - | 'en-CA' - | 'en-GB' - | 'en-GB-oxendict' - | 'en-IN' - | 'en-NZ' - | 'en-US' - | 'eo' - | 'es' - | 'es-419' - | 'et' - | 'eu' - | 'fa' - | 'fi' - | 'fr' - | 'fr-CA' - | 'fr-CH' - | 'fr-FR' - | 'he' - | 'hi' - | 'hr' - | 'hu' - | 'id' - | 'it' - | 'it-CH' - | 'it-IT' - | 'ja' - | 'km' - | 'kn' - | 'ko' - | 'lt' - | 'mk' - | 'mr' - | 'ms' - | 'nb' - | 'nl' - | 'nn' - | 'no' - | 'pl' - | 'pt-BR' - | 'pt-PT' - | 'ro' - | 'ru' - | 'sk' - | 'sl' - | 'sq' - | 'sr' - | 'sv' - | 'sw' - | 'ta' - | 'te' - | 'th' - | 'tr' - | 'uk' - | 'ur' - | 'vi' - | 'zh-CN' - | 'zh-HK' - | 'zh-TW'; - -export function mapToSupportLocale(ourLocale: string): SupportLocaleType { - if (ourLocale === 'ar') { - return ourLocale; - } - if (ourLocale === 'de') { - return ourLocale; - } - if (ourLocale === 'es') { - return ourLocale; - } - if (ourLocale === 'fr') { - return ourLocale; - } - if (ourLocale === 'it') { - return ourLocale; - } - if (ourLocale === 'ja') { - return ourLocale; - } - if (ourLocale === 'pl') { - return ourLocale; - } - if (ourLocale === 'pt-BR') { - return 'pt-br'; - } - if (ourLocale === 'ru') { - return ourLocale; - } - if (ourLocale === 'sq') { - return ourLocale; - } - if (ourLocale === 'zh-TW') { - return 'zh-tw'; - } - - return 'en-us'; -}