Add backup media download progress to settings pane
Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
parent
fd5b977190
commit
de847714e2
16 changed files with 355 additions and 189 deletions
|
@ -5565,11 +5565,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": {
|
||||
|
@ -5589,15 +5589,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": {
|
||||
|
@ -5613,21 +5613,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. <learnMoreLink>Learn more</learnMoreLink>.",
|
||||
"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"
|
||||
|
|
|
@ -138,4 +138,7 @@ button.BackupMediaDownloadProgress__button-close {
|
|||
|
||||
.BackupMediaDownloadCancelConfirmation {
|
||||
min-width: 440px;
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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<string | JSX.Element>) => (
|
||||
<a href={BACKUP_AND_RESTORE_SUPPORT_PAGE} rel="noreferrer" target="_blank">
|
||||
{parts}
|
||||
</a>
|
||||
);
|
||||
return (
|
||||
<ConfirmationDialog
|
||||
moduleClassName="BackupMediaDownloadCancelConfirmation"
|
||||
|
@ -35,7 +43,13 @@ export function BackupMediaDownloadCancelConfirmationDialog({
|
|||
onClose={handleDialogClose}
|
||||
title={i18n('icu:BackupMediaDownloadCancelConfirmation__title')}
|
||||
>
|
||||
{i18n('icu:BackupMediaDownloadCancelConfirmation__description')}
|
||||
<I18n
|
||||
id="icu:BackupMediaDownloadCancelConfirmation__description"
|
||||
i18n={i18n}
|
||||
components={{
|
||||
learnMoreLink,
|
||||
}}
|
||||
/>
|
||||
</ConfirmationDialog>
|
||||
);
|
||||
}
|
||||
|
|
128
ts/components/BackupMediaDownloadProgressSettings.tsx
Normal file
128
ts/components/BackupMediaDownloadProgressSettings.tsx
Normal file
|
@ -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 = (
|
||||
<Button
|
||||
onClick={handleResume}
|
||||
variant={ButtonVariant.Secondary}
|
||||
size={ButtonSize.Small}
|
||||
className="BackupMediaDownloadProgressSettings__button"
|
||||
>
|
||||
{i18n('icu:BackupMediaDownloadProgressSettings__button-resume')}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
title = i18n('icu:BackupMediaDownloadProgressSettings__title-in-progress');
|
||||
description = i18n(
|
||||
'icu:BackupMediaDownloadProgressSettings__progressbar-hint',
|
||||
{
|
||||
currentSize: formatFileSize(completedBytes),
|
||||
totalSize: formatFileSize(totalBytes),
|
||||
fractionComplete,
|
||||
}
|
||||
);
|
||||
actionButton = (
|
||||
<Button
|
||||
onClick={handlePause}
|
||||
variant={ButtonVariant.Secondary}
|
||||
size={ButtonSize.Small}
|
||||
className="BackupMediaDownloadProgressSettings__button"
|
||||
>
|
||||
{i18n('icu:BackupMediaDownloadProgressSettings__button-pause')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="BackupMediaDownloadProgressSettings">
|
||||
<div className="BackupMediaDownloadProgressSettings__content">
|
||||
<div className="BackupMediaDownloadProgressSettings__title">
|
||||
{title}
|
||||
</div>
|
||||
<div className="BackupMediaDownloadProgressSettings__ProgressBar">
|
||||
<ProgressBar fractionComplete={fractionComplete} isRTL={isRTL} />
|
||||
</div>
|
||||
<div className="BackupMediaDownloadProgressSettings__description">
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
<div className="BackupMediaDownloadProgressSettings__buttons">
|
||||
{actionButton}
|
||||
<Button
|
||||
onClick={() => setIsShowingCancelConfirmation(true)}
|
||||
variant={ButtonVariant.SecondaryDestructive}
|
||||
className="BackupMediaDownloadProgressSettings__button"
|
||||
size={ButtonSize.Small}
|
||||
>
|
||||
{i18n('icu:cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
{isShowingCancelConfirmation ? (
|
||||
<BackupMediaDownloadCancelConfirmationDialog
|
||||
i18n={i18n}
|
||||
handleConfirmCancel={handleCancel}
|
||||
handleDialogClose={() => setIsShowingCancelConfirmation(false)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -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 (
|
||||
<a
|
||||
key="signal-support"
|
||||
href={localizedSupportLink}
|
||||
href="https://support.signal.org/hc/requests/new?desktop"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
|
|
@ -415,6 +415,9 @@ export default {
|
|||
removeCustomColorOnConversations: action(
|
||||
'removeCustomColorOnConversations'
|
||||
),
|
||||
resumeBackupMediaDownload: action('resumeBackupMediaDownload'),
|
||||
pauseBackupMediaDownload: action('pauseBackupMediaDownload'),
|
||||
cancelBackupMediaDownload: action('cancelBackupMediaDownload'),
|
||||
resetAllChatColors: action('resetAllChatColors'),
|
||||
resetDefaultChatColor: action('resetDefaultChatColor'),
|
||||
savePreferredLeftPaneWidth: action('savePreferredLeftPaneWidth'),
|
||||
|
@ -528,6 +531,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,
|
||||
|
|
|
@ -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<string, CustomColorType>;
|
||||
defaultConversationColor: DefaultConversationColorType;
|
||||
|
@ -225,6 +230,8 @@ type PropsFunctionType = {
|
|||
getMessageSampleForSchemaVersion: (
|
||||
version: number
|
||||
) => Promise<Array<MessageAttributesType>>;
|
||||
resumeBackupMediaDownload: () => void;
|
||||
pauseBackupMediaDownload: () => void;
|
||||
getConversationsWithCustomColor: (colorId: string) => Array<ConversationType>;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
makeSyncRequest: () => unknown;
|
||||
|
@ -386,6 +393,10 @@ export function Preferences({
|
|||
availableMicrophones,
|
||||
availableSpeakers,
|
||||
backupFeatureEnabled,
|
||||
backupMediaDownloadStatus,
|
||||
pauseBackupMediaDownload,
|
||||
resumeBackupMediaDownload,
|
||||
cancelBackupMediaDownload,
|
||||
backupKeyViewed,
|
||||
backupSubscriptionStatus,
|
||||
backupLocalBackupsEnabled,
|
||||
|
@ -2160,6 +2171,10 @@ export function Preferences({
|
|||
accountEntropyPool={accountEntropyPool}
|
||||
backupKeyViewed={backupKeyViewed}
|
||||
backupSubscriptionStatus={backupSubscriptionStatus}
|
||||
backupMediaDownloadStatus={backupMediaDownloadStatus}
|
||||
cancelBackupMediaDownload={cancelBackupMediaDownload}
|
||||
pauseBackupMediaDownload={pauseBackupMediaDownload}
|
||||
resumeBackupMediaDownload={resumeBackupMediaDownload}
|
||||
cloudBackupStatus={cloudBackupStatus}
|
||||
i18n={i18n}
|
||||
locale={resolvedLocale}
|
||||
|
|
|
@ -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<string | undefined>;
|
||||
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 (
|
||||
<>
|
||||
<div className="Preferences--backups-summary__container">
|
||||
|
@ -484,12 +511,12 @@ function BackupsDetailsPage({
|
|||
})}
|
||||
</div>
|
||||
|
||||
{cloudBackupStatus ? (
|
||||
{cloudBackupStatus || shouldShowMediaProgress ? (
|
||||
<SettingsRow
|
||||
className="Preferences--backup-details"
|
||||
title={i18n('icu:Preferences--backup-details__header')}
|
||||
>
|
||||
{cloudBackupStatus.createdTimestamp ? (
|
||||
{cloudBackupStatus?.createdTimestamp ? (
|
||||
<div className="Preferences--backup-details__row">
|
||||
<label>{i18n('icu:Preferences--backup-created-at__label')}</label>
|
||||
<div
|
||||
|
@ -506,6 +533,17 @@ function BackupsDetailsPage({
|
|||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{shouldShowMediaProgress && backupMediaDownloadStatus ? (
|
||||
<div className="Preferences--backup-details__row">
|
||||
<BackupMediaDownloadProgressSettings
|
||||
{...backupMediaDownloadStatus}
|
||||
handleCancel={cancelBackupMediaDownload}
|
||||
handlePause={pauseBackupMediaDownload}
|
||||
handleResume={resumeBackupMediaDownload}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</SettingsRow>
|
||||
) : null}
|
||||
</>
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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');
|
||||
}}
|
||||
/>
|
||||
<PanelRow
|
||||
|
@ -536,9 +533,7 @@ export function ConversationDetails({
|
|||
label={i18n('icu:contactUs')}
|
||||
onClick={() => {
|
||||
openLinkInWebBrowser(
|
||||
getLocalizedUrl(
|
||||
'https://support.signal.org/hc/LOCALE/requests/new?desktop'
|
||||
)
|
||||
'https://support.signal.org/hc/requests/new?desktop'
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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> = 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<T extends string>(
|
||||
url: RequiresLocale<T>
|
||||
): string {
|
||||
const locale = window.SignalContext.getResolvedMessagesLocale();
|
||||
const supportLocale = mapToSupportLocale(locale);
|
||||
return url.replace('LOCALE', supportLocale);
|
||||
}
|
|
@ -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';
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue