signal-desktop/ts/components/PreferencesBackups.tsx

215 lines
7.1 KiB
TypeScript
Raw Normal View History

2025-04-02 14:57:29 -04:00
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type {
BackupsSubscriptionType,
BackupStatusType,
} from '../types/backups';
import type { LocalizerType } from '../types/I18N';
import { formatTimestamp } from '../util/formatTimestamp';
import { formatFileSize } from '../util/formatFileSize';
import { SettingsRow } from './PreferencesUtil';
import { missingCaseError } from '../util/missingCaseError';
export function PreferencesBackups({
cloudBackupStatus,
backupSubscriptionStatus,
i18n,
locale,
}: {
cloudBackupStatus?: BackupStatusType;
backupSubscriptionStatus?: BackupsSubscriptionType;
i18n: LocalizerType;
locale: string;
}): JSX.Element {
return (
<>
<div className="Preferences--backups-summary__container">
{getBackupsSubscriptionSummary({
subscriptionStatus: backupSubscriptionStatus,
i18n,
locale,
})}
</div>
{cloudBackupStatus ? (
<SettingsRow
className="Preferences--backup-details"
title={i18n('icu:Preferences--backup-details__header')}
>
{cloudBackupStatus.createdAt ? (
<div className="Preferences--backup-details__row">
<label>{i18n('icu:Preferences--backup-created-at__label')}</label>
<div
id="Preferences--backup-details__value"
className="Preferences--backup-details__value"
>
{/* TODO (DESKTOP-8509) */}
{i18n('icu:Preferences--backup-created-by-phone')}
<span className="Preferences--backup-details__value-divider" />
{formatTimestamp(cloudBackupStatus.createdAt, {
dateStyle: 'medium',
timeStyle: 'short',
})}
</div>
</div>
) : null}
{cloudBackupStatus.mediaSize != null ||
cloudBackupStatus.protoSize != null}
<div className="Preferences--backup-details__row">
<label>
{i18n('icu:Preferences--backup-size__label')}{' '}
<div className="Preferences--backup-details__value">
{formatFileSize(
(cloudBackupStatus.mediaSize ?? 0) +
(cloudBackupStatus.protoSize ?? 0)
)}
</div>
</label>
</div>
</SettingsRow>
) : null}
</>
);
}
function getSubscriptionDetails({
i18n,
subscriptionStatus,
locale,
}: {
i18n: LocalizerType;
locale: string;
subscriptionStatus: BackupsSubscriptionType;
}): JSX.Element | null {
if (subscriptionStatus.status === 'active') {
return (
<>
{subscriptionStatus.cost ? (
<div className="Preferences--backups-summary__subscription-price">
{new Intl.NumberFormat(locale, {
style: 'currency',
currency: subscriptionStatus.cost.currencyCode,
currencyDisplay: 'narrowSymbol',
}).format(subscriptionStatus.cost.amount)}{' '}
/ month
</div>
) : null}
{subscriptionStatus.renewalDate ? (
<div className="Preferences--backups-summary__renewal-date">
{i18n('icu:Preferences--backup-plan__renewal-date', {
date: formatTimestamp(subscriptionStatus.renewalDate.getTime(), {
dateStyle: 'medium',
}),
})}
</div>
) : null}
</>
);
}
if (subscriptionStatus.status === 'pending-cancellation') {
return (
<>
<div className="Preferences--backups-summary__canceled">
{i18n('icu:Preferences--backup-plan__canceled')}
</div>
{subscriptionStatus.expiryDate ? (
<div className="Preferences--backups-summary__expiry-date">
{i18n('icu:Preferences--backup-plan__expiry-date', {
date: formatTimestamp(subscriptionStatus.expiryDate.getTime(), {
dateStyle: 'medium',
}),
})}
</div>
) : null}
</>
);
}
return null;
}
export function getBackupsSubscriptionSummary({
subscriptionStatus,
i18n,
locale,
}: {
locale: string;
subscriptionStatus?: BackupsSubscriptionType;
i18n: LocalizerType;
}): JSX.Element | null {
if (!subscriptionStatus) {
return null;
}
const { status } = subscriptionStatus;
switch (status) {
case 'active':
case 'pending-cancellation':
return (
<>
<div className="Preferences--backups-summary__status-container">
<div>
<div className="Preferences--backups-summary__type">
{i18n('icu:Preferences--backup-media-plan__description')}
</div>
<div className="Preferences--backups-summary__content">
{getSubscriptionDetails({ i18n, locale, subscriptionStatus })}
</div>
</div>
{subscriptionStatus.status === 'active' ? (
<div className="Preferences--backups-summary__icon Preferences--backups-summary__icon--active" />
) : (
<div className="Preferences--backups-summary__icon Preferences--backups-summary__icon--inactive" />
)}
</div>
<div className="Preferences--backups-summary__note">
{i18n('icu:Preferences--backup-media-plan__note')}
</div>
</>
);
case 'free':
return (
<>
<div className="Preferences--backups-summary__status-container">
<div>
<div className="Preferences--backups-summary__type">
{i18n('icu:Preferences--backup-messages-plan__description', {
mediaDayCount:
subscriptionStatus.mediaIncludedInBackupDurationDays,
})}
</div>
<div className="Preferences--backups-summary__content">
{i18n(
'icu:Preferences--backup-messages-plan__cost-description'
)}
</div>
</div>
<div className="Preferences--backups-summary__icon Preferences--backups-summary__icon--active" />
</div>
<div className="Preferences--backups-summary__note">
{i18n('icu:Preferences--backup-messages-plan__note')}
</div>
</>
);
case 'not-found':
case 'expired':
return (
<>
<div className="Preferences--backups-summary__status-container ">
<div className="Preferences--backups-summary__content">
{i18n('icu:Preferences--backup-plan-not-found__description')}
</div>
<div className="Preferences--backups-summary__icon Preferences--backups-summary__icon--inactive" />
</div>
<div className="Preferences--backups-summary__note">
<div className="Preferences--backups-summary__note">
{i18n('icu:Preferences--backup-plan__not-found__note')}
</div>
</div>
</>
);
default:
throw missingCaseError(status);
}
}