// Copyright 2025 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useState, useCallback } from 'react'; import classNames from 'classnames'; import type { LocalizerType } from '../types/I18N'; import { toLogFormat } from '../types/errors'; import { formatFileSize } from '../util/formatFileSize'; import { SECOND } from '../util/durations'; import type { ValidationResultType as BackupValidationResultType } from '../services/backups'; import { SettingsRow, FlowingSettingsControl } from './PreferencesUtil'; import { Button, ButtonVariant } from './Button'; import { Spinner } from './Spinner'; import type { MessageCountBySchemaVersionType } from '../sql/Interface'; import type { MessageAttributesType } from '../model-types'; export function PreferencesInternal({ i18n, exportLocalBackup: doExportLocalBackup, validateBackup: doValidateBackup, getMessageCountBySchemaVersion, getMessageSampleForSchemaVersion, }: { i18n: LocalizerType; exportLocalBackup: () => Promise; validateBackup: () => Promise; getMessageCountBySchemaVersion: () => Promise; getMessageSampleForSchemaVersion: ( version: number ) => Promise>; }): JSX.Element { const [isExportPending, setIsExportPending] = useState(false); const [exportResult, setExportResult] = useState< BackupValidationResultType | undefined >(); const [messageCountBySchemaVersion, setMessageCountBySchemaVersion] = useState(); const [messageSampleForVersions, setMessageSampleForVersions] = useState<{ [schemaVersion: number]: Array; }>(); const [isValidationPending, setIsValidationPending] = useState(false); const [validationResult, setValidationResult] = useState< BackupValidationResultType | undefined >(); const validateBackup = useCallback(async () => { setIsValidationPending(true); setValidationResult(undefined); try { setValidationResult(await doValidateBackup()); } catch (error) { setValidationResult({ error: toLogFormat(error) }); } finally { setIsValidationPending(false); } }, [doValidateBackup]); const renderValidationResult = useCallback( ( backupResult: BackupValidationResultType | undefined ): JSX.Element | undefined => { if (backupResult == null) { return; } if ('result' in backupResult) { const { result: { totalBytes, stats, duration }, } = backupResult; let snapshotDirEl: JSX.Element | undefined; if ('snapshotDir' in backupResult.result) { snapshotDirEl = (

Backup path:

                {backupResult.result.snapshotDir}
              

); } return (
{snapshotDirEl}

Main file size: {formatFileSize(totalBytes)}

Duration: {Math.round(duration / SECOND)}s

              {JSON.stringify(stats, null, 2)}
            
); } const { error } = backupResult; return (
            {error}
          
); }, [] ); const exportLocalBackup = useCallback(async () => { setIsExportPending(true); setExportResult(undefined); try { setExportResult(await doExportLocalBackup()); } catch (error) { setExportResult({ error: toLogFormat(error) }); } finally { setIsExportPending(false); } }, [doExportLocalBackup]); return (
{i18n('icu:Preferences__internal__validate-backup--description')}
{renderValidationResult(validationResult)}
{i18n( 'icu:Preferences__internal__export-local-backup--description' )}
{renderValidationResult(exportResult)}
Check message schema versions
{messageCountBySchemaVersion ? (
              
                  {messageCountBySchemaVersion.map(
                    ({ schemaVersion, count }) => {
                      return (
                        
                          
                          {messageSampleForVersions?.[schemaVersion] ? (
                            
                          ) : null}
                        
                      );
                    }
                  )}
                
Schema version # Messages
{schemaVersion} {count}
{JSON.stringify( messageSampleForVersions[schemaVersion], null, 2 )}
) : null}
); }