// Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useEffect, useState } from 'react'; import classNames from 'classnames'; import type { BackupMediaDownloadStatusType, BackupsSubscriptionType, BackupStatusType, } from '../types/backups.node.js'; import type { LocalizerType } from '../types/I18N.std.js'; import { SettingsControl as Control, FlowingSettingsControl as FlowingControl, LightIconLabel, SettingsRow, } from './PreferencesUtil.dom.js'; import type { SettingsLocation } from '../types/Nav.std.js'; import { SettingsPage } from '../types/Nav.std.js'; import { I18n } from './I18n.dom.js'; import { PreferencesLocalBackups } from './PreferencesLocalBackups.dom.js'; import type { ShowToastAction } from '../state/ducks/toast.preload.js'; import type { PromptOSAuthReasonType, PromptOSAuthResultType, } from '../util/os/promptOSAuthMain.main.js'; import { AxoButton } from '../axo/AxoButton.dom.js'; import { BackupLevel } from '../services/backups/types.std.js'; import { BackupsDetailsPage, renderSubscriptionDetails, } from './PreferencesBackupDetails.dom.js'; import type { LocalBackupExportMetadata } from '../types/LocalExport.std.js'; export const SIGNAL_BACKUPS_LEARN_MORE_URL = 'https://support.signal.org/hc/articles/360007059752-Backup-and-Restore-Messages'; const LOCAL_BACKUPS_PAGES = new Set([ SettingsPage.LocalBackups, SettingsPage.LocalBackupsKeyReference, SettingsPage.LocalBackupsSetupFolder, SettingsPage.LocalBackupsSetupKey, ]); function isLocalBackupsPage(page: SettingsPage) { return LOCAL_BACKUPS_PAGES.has(page); } export function PreferencesBackups({ accountEntropyPool, backupFreeMediaDays, backupKeyViewed, backupSubscriptionStatus, backupTier, cloudBackupStatus, i18n, isLocalBackupsEnabled, lastLocalBackup, locale, localBackupFolder, onBackupKeyViewedChange, openFileInFolder, osName, pickLocalBackupFolder, disableLocalBackups, backupMediaDownloadStatus, cancelBackupMediaDownload, pauseBackupMediaDownload, resumeBackupMediaDownload, settingsLocation, promptOSAuth, refreshCloudBackupStatus, refreshBackupSubscriptionStatus, setSettingsLocation, showToast, startLocalBackupExport, }: { accountEntropyPool: string | undefined; backupFreeMediaDays: number; backupKeyViewed: boolean; backupSubscriptionStatus: BackupsSubscriptionType; backupTier: BackupLevel | null; cloudBackupStatus?: BackupStatusType; localBackupFolder: string | undefined; i18n: LocalizerType; isLocalBackupsEnabled: boolean; lastLocalBackup: LocalBackupExportMetadata | undefined; locale: string; onBackupKeyViewedChange: (keyViewed: boolean) => void; openFileInFolder: (path: string) => void; osName: 'linux' | 'macos' | 'windows' | undefined; settingsLocation: SettingsLocation; backupMediaDownloadStatus: BackupMediaDownloadStatusType | undefined; cancelBackupMediaDownload: () => void; disableLocalBackups: ({ deleteExistingBackups, }: { deleteExistingBackups: boolean; }) => Promise; pauseBackupMediaDownload: () => void; resumeBackupMediaDownload: () => void; pickLocalBackupFolder: () => Promise; promptOSAuth: ( reason: PromptOSAuthReasonType ) => Promise; refreshCloudBackupStatus: () => void; refreshBackupSubscriptionStatus: () => void; setSettingsLocation: (settingsLocation: SettingsLocation) => void; showToast: ShowToastAction; startLocalBackupExport: () => void; }): React.JSX.Element | null { const [isAuthPending, setIsAuthPending] = useState(false); useEffect(() => { if (settingsLocation.page === SettingsPage.Backups) { refreshBackupSubscriptionStatus(); } else if (settingsLocation.page === SettingsPage.BackupsDetails) { refreshBackupSubscriptionStatus(); refreshCloudBackupStatus(); } }, [ settingsLocation.page, refreshBackupSubscriptionStatus, refreshCloudBackupStatus, ]); if (!isLocalBackupsEnabled && isLocalBackupsPage(settingsLocation.page)) { setSettingsLocation({ page: SettingsPage.Backups }); return null; } if (settingsLocation.page === SettingsPage.BackupsDetails) { if (backupTier == null) { setSettingsLocation({ page: SettingsPage.Backups }); return null; } return ( ); } if (isLocalBackupsPage(settingsLocation.page)) { return ( ); } const learnMoreLink = (parts: Array) => ( {parts} ); const isLocalBackupsSetup = localBackupFolder && backupKeyViewed; function renderRemoteBackups() { return ( <> {backupTier == null ? ( {i18n('icu:Preferences--signal-backups')}{' '}
} right={null} />
) : (
setSettingsLocation({ page: SettingsPage.BackupsDetails }) } > {i18n('icu:Preferences__button--manage')}
)} ); } function renderLocalBackups() { return (
{ if (!isLocalBackupsSetup) { try { setIsAuthPending(true); const result = await promptOSAuth('enable-backups'); if (result !== 'success' && result !== 'unsupported') { return; } } finally { setIsAuthPending(false); } } setSettingsLocation({ page: SettingsPage.LocalBackups }); }} > {isLocalBackupsSetup ? i18n('icu:Preferences__button--manage') : i18n('icu:Preferences__button--set-up')}
); } return ( <>
{i18n('icu:Preferences--backup-section-description')}
{renderRemoteBackups()} {isLocalBackupsEnabled ? renderLocalBackups() : null} ); } export function renderPaidBackupsSummary({ subscriptionStatus, i18n, locale, }: { locale: string; subscriptionStatus: BackupsSubscriptionType; i18n: LocalizerType; }): React.JSX.Element | null { return (
{i18n('icu:Preferences--backup-media-plan__description')}
{renderSubscriptionDetails({ i18n, locale, subscriptionStatus })}
); } export function renderFreeBackupsSummary({ backupFreeMediaDays, i18n, }: { backupFreeMediaDays: number; i18n: LocalizerType; }): React.JSX.Element | null { return (
{i18n('icu:Preferences--backup-messages-plan__description', { mediaDayCount: backupFreeMediaDays, })}
{i18n('icu:Preferences--backup-messages-plan__cost-description')}
); }