From a69f52d02f767fa98a3434a1035d68a150decc3a Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Mon, 4 May 2026 15:22:12 -0500 Subject: [PATCH] Improvements to local backup UI Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com> --- fonts/mono-special/MonoSpecial-Regular.woff2 | Bin 5220 -> 5328 bytes ts/components/PreferencesBackups.dom.tsx | 10 +- ts/components/PreferencesLocalBackups.dom.tsx | 120 ++++++++++++------ ts/textsecure/Storage.preload.ts | 3 +- ts/types/Nav.std.ts | 2 + ts/types/PreferencesBackupPage.std.ts | 4 + 6 files changed, 99 insertions(+), 40 deletions(-) diff --git a/fonts/mono-special/MonoSpecial-Regular.woff2 b/fonts/mono-special/MonoSpecial-Regular.woff2 index b9208c4075ee87a22ad6fc72024c590d1ffa246b..76e93176565c4d60e040780c67862a35afe91019 100644 GIT binary patch literal 5328 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 diff --git a/ts/components/PreferencesBackups.dom.tsx b/ts/components/PreferencesBackups.dom.tsx index 2e2e8d53a1..ca9d3c68d1 100644 --- a/ts/components/PreferencesBackups.dom.tsx +++ b/ts/components/PreferencesBackups.dom.tsx @@ -38,6 +38,8 @@ export const SIGNAL_BACKUPS_LEARN_MORE_URL = const LOCAL_BACKUPS_PAGES = new Set([ SettingsPage.LocalBackups, + SettingsPage.LocalBackupsSetupKey, + SettingsPage.LocalBackupsSetupFolder, SettingsPage.LocalBackupsKeyReference, ]); @@ -291,13 +293,17 @@ export function PreferencesBackups({ disabled={isAuthPending} onClick={async () => { if (isLocalBackupsSetup) { - setSettingsLocation({ page: SettingsPage.LocalBackups }); + setSettingsLocation({ + page: SettingsPage.LocalBackups, + }); } else { try { setIsAuthPending(true); const result = await promptOSAuth('enable-backups'); if (result === 'success' || result === 'unsupported') { - setSettingsLocation({ page: SettingsPage.LocalBackups }); + setSettingsLocation({ + page: SettingsPage.LocalBackupsSetupFolder, + }); } } finally { setIsAuthPending(false); diff --git a/ts/components/PreferencesLocalBackups.dom.tsx b/ts/components/PreferencesLocalBackups.dom.tsx index f2edb5a53e..4f68d0b854 100644 --- a/ts/components/PreferencesLocalBackups.dom.tsx +++ b/ts/components/PreferencesLocalBackups.dom.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import type { ChangeEvent, JSX, MouseEvent } from 'react'; -import { useCallback, useEffect, useMemo, useState, useRef } from 'react'; +import { useCallback, useMemo, useState, useRef } from 'react'; import lodash from 'lodash'; import classNames from 'classnames'; @@ -85,24 +85,33 @@ export function PreferencesLocalBackups({ const [isShowingBackupKeyChangedModal, setIsShowingBackupKeyChangedModal] = useState(false); - if (!localBackupFolder) { + if (settingsLocation.page === SettingsPage.LocalBackupsSetupFolder) { + if (localBackupFolder) { + setSettingsLocation({ page: SettingsPage.LocalBackups }); + } return ( { + const folder = await pickLocalBackupFolder(); + if (folder) { + setSettingsLocation({ page: SettingsPage.LocalBackupsSetupKey }); + } + }} /> ); } - const isReferencingBackupKey = - settingsLocation.page === SettingsPage.LocalBackupsKeyReference; - if (!previouslyViewedBackupKeyHash || isReferencingBackupKey) { + if ( + settingsLocation.page === SettingsPage.LocalBackupsSetupKey || + settingsLocation.page === SettingsPage.LocalBackupsKeyReference + ) { return ( { @@ -116,6 +125,16 @@ export function PreferencesLocalBackups({ ); } + if (!localBackupFolder && !isDisablePending) { + setSettingsLocation({ page: SettingsPage.LocalBackupsSetupFolder }); + return null; + } + + if (!previouslyViewedBackupKeyHash && !isDisablePending) { + setSettingsLocation({ page: SettingsPage.LocalBackupsSetupKey }); + return null; + } + async function showKeyReferenceWithAuth() { setAuthError(undefined); @@ -218,7 +237,11 @@ export function PreferencesLocalBackups({ openFileInFolder(localBackupFolder)} + onClick={() => + localBackupFolder + ? openFileInFolder(localBackupFolder) + : undefined + } > {showInFolderText} @@ -557,13 +580,10 @@ function LocalBackupsBackupKeyViewer({ ); const isStepViewOrReference = step === 'view' || step === 'reference'; - const backupKeyForDisplay = useMemo(() => { - return backupKey - .replace(/\s/g, '') - .replace(/.{4}(?=.)/g, '$& ') - .toUpperCase(); - }, [backupKey]); - + const backupKeyForDisplay = useMemo( + () => formatBackupKeyForDisplay(backupKey, { convertAmbiguousChars: true }), + [backupKey] + ); const onCopyBackupKey = useCallback( async function handleCopyBackupKey(e: MouseEvent) { e.preventDefault(); @@ -645,8 +665,10 @@ function LocalBackupsBackupKeyViewer({ {step === 'caution' && (
setIsBackupKeyConfirmed(isValid)} isStepViewOrReference={isStepViewOrReference} @@ -715,13 +738,29 @@ function LocalBackupsBackupKeyViewer({ ); } +function formatBackupKeyForDisplay( + backupKey: string, + { convertAmbiguousChars }: { convertAmbiguousChars: boolean } +): string { + const spacedAndUppercase = backupKey + .toUpperCase() + .replace(/\s/g, '') + .replace(/.{4}(?=.)/g, '$& '); + + if (convertAmbiguousChars) { + return spacedAndUppercase.replace(/O/g, '#').replace(/0/g, '='); + } + + return spacedAndUppercase; +} + function LocalBackupsBackupKeyTextarea({ - backupKey, + backupKeyForDisplay, i18n, onValidate, isStepViewOrReference, }: { - backupKey: string; + backupKeyForDisplay: string; i18n: LocalizerType; onValidate: (isValid: boolean) => void; isStepViewOrReference: boolean; @@ -729,30 +768,39 @@ function LocalBackupsBackupKeyTextarea({ const backupKeyTextareaRef = useRef(null); const [backupKeyInput, setBackupKeyInput] = useState(''); - useEffect(() => { - if (backupKeyTextareaRef.current) { - backupKeyTextareaRef.current.focus(); - } - }, [backupKeyTextareaRef, isStepViewOrReference]); - - const backupKeyNoSpaces = useMemo(() => { - return backupKey.replace(/\s/g, ''); - }, [backupKey]); - const handleTextareaChange = useCallback( (ev: ChangeEvent) => { - const { value } = ev.target; - const valueUppercaseNoSpaces = value.replace(/\s/g, '').toUpperCase(); - const valueForUI = valueUppercaseNoSpaces.replace(/.{4}(?=.)/g, '$& '); - setBackupKeyInput(valueForUI); - onValidate(valueUppercaseNoSpaces === backupKeyNoSpaces); + const { selectionStart, value } = ev.target; + + setBackupKeyInput( + formatBackupKeyForDisplay(value, { convertAmbiguousChars: false }) + ); + + const currentCharIndex = value + .slice(0, selectionStart) + .replace(/\s/g, '').length; + const newCaretIndex = + currentCharIndex + Math.max(0, Math.floor((currentCharIndex - 1) / 4)); + + requestAnimationFrame(() => { + backupKeyTextareaRef.current?.setSelectionRange( + newCaretIndex, + newCaretIndex + ); + }); + + onValidate( + formatBackupKeyForDisplay(value, { convertAmbiguousChars: true }) === + backupKeyForDisplay + ); }, - [backupKeyNoSpaces, onValidate] + [backupKeyForDisplay, onValidate] ); return (