From 032eb916b3e1bb54dcdfbf31a1583de07257184e Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:23:56 -0500 Subject: [PATCH] Update key transparency error dialog Co-authored-by: Jamie <113370520+jamiebuilds-signal@users.noreply.github.com> --- _locales/en/messages.json | 16 +++ ts/axo/AxoAlertDialog.dom.stories.tsx | 4 +- ts/axo/AxoAlertDialog.dom.tsx | 8 +- ts/axo/AxoButton.dom.stories.tsx | 19 ++++ ts/axo/AxoButton.dom.tsx | 5 +- ts/axo/AxoCheckbox.dom.tsx | 5 +- ts/axo/AxoDialog.dom.tsx | 6 +- ts/axo/_tailwind-theme/colors.css | 2 +- .../KeyTransparencyErrorDialog.dom.tsx | 14 ++- .../KeyTransparencyErrorDialog.preload.tsx | 99 +++++++------------ 10 files changed, 99 insertions(+), 79 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 3b1d15057f..90b3021b22 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -862,14 +862,26 @@ "messageformat": "Automatic Key Verification is currently unavailable for your device. Submit debug log?", "description": "Key Transparency Error Dialog > Title" }, + "icu:KeyTransparencyErrorDialog__Title-v2": { + "messageformat": "Automatic Key Verification is currently unavailable for your device. Submit a report to help us improve?", + "description": "Key Transparency Error Dialog > Title" + }, "icu:KeyTransparencyErrorDialog__Description": { "messageformat": "Debug logs helps us diagnose and fix the issue, and do not contain identifying information.", "description": "Key Transparency Error Dialog > Description" }, + "icu:KeyTransparencyErrorDialog__Description-v2": { + "messageformat": "Including your debug logs help us diagnose and fix the issue, and do not contain identifying information. Sharing is optional.", + "description": "Key Transparency Error Dialog > Description" + }, "icu:KeyTransparencyErrorDialog__ShareDebugLog__Label": { "messageformat": "Share debug log", "description": "Key Transparency Error Dialog > Share debug log > Label" }, + "icu:KeyTransparencyErrorDialog__ShareDebugLog__Label-v2": { + "messageformat": "Include debug log", + "description": "Key Transparency Error Dialog > Share debug log > Label" + }, "icu:KeyTransparencyErrorDialog__ShareDebugLog__ViewButton": { "messageformat": "View", "description": "Key Transparency Error Dialog > Share debug log > View Button" @@ -878,6 +890,10 @@ "messageformat": "Submit", "description": "Primary button text in the dialog shown when an unexpected key transparency error occurs. Clicking it will open a support page" }, + "icu:KeyTransparencyErrorDialog__Cancel": { + "messageformat": "No thanks", + "description": "Cancel button text in the dialog shown when an unexpected key transparency error occurs. Clicking it will close the dialog" + }, "icu:KeyTransparencyOnboardingDialog__Title": { "messageformat": "Signal can now auto-verify encryption", "description": "Key Transparency Onboarding Dialog > Title" diff --git a/ts/axo/AxoAlertDialog.dom.stories.tsx b/ts/axo/AxoAlertDialog.dom.stories.tsx index c43640f1b4..e1a667b744 100644 --- a/ts/axo/AxoAlertDialog.dom.stories.tsx +++ b/ts/axo/AxoAlertDialog.dom.stories.tsx @@ -103,8 +103,8 @@ function Template(props: { {props.extraLongText ? EXAMPLE_ACTION_LONG : EXAMPLE_ACTION} diff --git a/ts/axo/AxoAlertDialog.dom.tsx b/ts/axo/AxoAlertDialog.dom.tsx index 6c6fa3ee41..e2646aa16f 100644 --- a/ts/axo/AxoAlertDialog.dom.tsx +++ b/ts/axo/AxoAlertDialog.dom.tsx @@ -352,6 +352,8 @@ export namespace AxoAlertDialog { | 'strong-destructive' | 'subtle-destructive'; + export type Arrow = 'next' | 'external-link'; + export type ActionProps = Readonly<{ /** * Visual style of the button. @@ -360,11 +362,11 @@ export namespace AxoAlertDialog { /** * Optional leading icon. */ - symbol?: AxoSymbol.Name; + symbol?: AxoSymbol.Name | null; /** * When `true`, shows a forward arrow on the trailing side. */ - arrow?: boolean; + arrow?: Arrow | null; /** * Called when the button is clicked. */ @@ -397,7 +399,7 @@ export namespace AxoAlertDialog { ); } + +export function Arrows(): ReactNode { + return ( +
+ + Collapse + + + Expand + + + Next + + + External Link + +
+ ); +} diff --git a/ts/axo/AxoButton.dom.tsx b/ts/axo/AxoButton.dom.tsx index 34db17ad5c..84cfcef3d9 100644 --- a/ts/axo/AxoButton.dom.tsx +++ b/ts/axo/AxoButton.dom.tsx @@ -65,7 +65,7 @@ export namespace AxoButton { * Note: Omitted 'prev' because arrow appears on trailing side, * back buttons should probably all use AxoIconButton. */ - export type Arrow = 'collapse' | 'expand' | 'next'; + export type Arrow = 'collapse' | 'expand' | 'next' | 'external-link'; const baseStyles = tw( 'relative inline-flex max-w-full items-center-safe justify-center-safe rounded-full', @@ -211,6 +211,7 @@ export namespace AxoButton { collapse: 'chevron-up', expand: 'chevron-down', next: 'chevron-[end]', + 'external-link': 'open', }); /** @testexport */ @@ -248,7 +249,7 @@ export namespace AxoButton { /** * Optional leading icon. */ - symbol?: AxoSymbol.Name; + symbol?: AxoSymbol.Name | null; /** * Optional trailing arrow icon. */ diff --git a/ts/axo/AxoCheckbox.dom.tsx b/ts/axo/AxoCheckbox.dom.tsx index e05d528459..10971709c3 100644 --- a/ts/axo/AxoCheckbox.dom.tsx +++ b/ts/axo/AxoCheckbox.dom.tsx @@ -104,13 +104,14 @@ export namespace AxoCheckbox { className={tw( RootStyles.get(props.variant), 'flex items-center justify-center', - 'border border-primary inset-shadow-on-color', + 'border border-secondary', 'data-[state=unchecked]:bg-control', + 'data-[state=unchecked]:enabled:inset-shadow-on-color', 'data-[state=unchecked]:enabled:active:bg-control-pressed', 'data-[state=checked]:bg-accent', 'data-[state=checked]:text-primary-oncolor', + 'data-[state=checked]:enabled:shadow-[0_1px_1px_var(--axo-shadow-color-elevation-1)]', 'data-[state=checked]:enabled:active:bg-accent-pressed', - 'data-disabled:border-secondary', 'data-[state=checked]:data-disabled:text-disabled-oncolor', 'outline-none keyboard-mode:focus:axo-focus-ring', 'overflow-hidden' diff --git a/ts/axo/AxoDialog.dom.tsx b/ts/axo/AxoDialog.dom.tsx index fdc733b2d0..5a7ea4601b 100644 --- a/ts/axo/AxoDialog.dom.tsx +++ b/ts/axo/AxoDialog.dom.tsx @@ -661,6 +661,8 @@ export namespace AxoDialog { | 'subtle-destructive' | 'subtle-secondary'; + export type Arrow = 'next' | 'external-link'; + export type ActionProps = Readonly<{ /** * Visual style of the button. @@ -673,7 +675,7 @@ export namespace AxoDialog { /** * When `true`, shows a forward arrow on the trailing side. */ - arrow?: boolean | null; + arrow?: Arrow | null; /** * When `true`, shows a loading spinner and prevents interaction. */ @@ -700,7 +702,7 @@ export namespace AxoDialog { { + onOpenChange(false); + }, [onOpenChange]); + const handleSubmit = useCallback(() => { onSubmit(shareDebugLog); }, [onSubmit, shareDebugLog]); @@ -35,13 +39,13 @@ export function KeyTransparencyErrorDialog(

- {i18n('icu:KeyTransparencyErrorDialog__Title')} + {i18n('icu:KeyTransparencyErrorDialog__Title-v2')}

@@ -53,7 +57,7 @@ export function KeyTransparencyErrorDialog( onCheckedChange={setShareDebugLog} /> + + {i18n('icu:KeyTransparencyErrorDialog__Cancel')} + diff --git a/ts/state/smart/KeyTransparencyErrorDialog.preload.tsx b/ts/state/smart/KeyTransparencyErrorDialog.preload.tsx index c5165d82e7..ec5fd877d7 100644 --- a/ts/state/smart/KeyTransparencyErrorDialog.preload.tsx +++ b/ts/state/smart/KeyTransparencyErrorDialog.preload.tsx @@ -1,29 +1,36 @@ // Copyright 2026 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { memo, useCallback, useState, useEffect, type JSX } from 'react'; +import { memo, useCallback, useState, type JSX } from 'react'; import { useSelector } from 'react-redux'; import { ipcRenderer } from 'electron'; -import lodash from 'lodash'; import { KeyTransparencyErrorDialog } from '../../components/KeyTransparencyErrorDialog.dom.tsx'; import { createSupportUrl } from '../../util/createSupportUrl.std.ts'; import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser.dom.ts'; -import { drop } from '../../util/drop.std.ts'; import { useGlobalModalActions } from '../ducks/globalModals.preload.ts'; import { getIntl } from '../selectors/user.std.ts'; -const { noop } = lodash; +async function uploadDebugLogs(): Promise { + try { + const logData = await ipcRenderer.invoke('fetch-log'); + const logs: string = await ipcRenderer.invoke( + 'DebugLogs.getLogs', + logData, + window.navigator.userAgent + ); + const debugLogUrl = await ipcRenderer.invoke('DebugLogs.upload', logs); + return debugLogUrl; + } catch { + // Ignore + return null; + } +} export const SmartKeyTransparencyErrorDialog = memo( function SmartKeyTransparencyErrorDialog(): JSX.Element | null { const i18n = useSelector(getIntl); const { hideKeyTransparencyErrorDialog } = useGlobalModalActions(); - const [request, setRequest] = useState< - | undefined - | Readonly<{ - shareDebugLog: boolean; - }> - >(); + const [submitting, setSubmitting] = useState(false); const handleOpenChange = useCallback( (open: boolean) => { @@ -34,62 +41,26 @@ export const SmartKeyTransparencyErrorDialog = memo( [hideKeyTransparencyErrorDialog] ); - const handleSubmit = useCallback((shareDebugLog: boolean) => { - setRequest({ shareDebugLog }); - }, []); + const handleSubmit = useCallback( + async (shareDebugLog: boolean) => { + setSubmitting(true); - useEffect(() => { - if (request === undefined) { - return noop; - } + let debugLogUrl: string | null = null; + if (shareDebugLog) { + debugLogUrl = await uploadDebugLogs(); + } - let canceled = false; + const supportURL = createSupportUrl({ + locale: window.SignalContext.getI18nLocale(), + query: debugLogUrl ? { kt: debugLogUrl } : undefined, + }); - drop( - (async () => { - const query: Record = { - kt: '', - }; - - if (request.shareDebugLog) { - try { - const logData = await ipcRenderer.invoke('fetch-log'); - const logs: string = await ipcRenderer.invoke( - 'DebugLogs.getLogs', - logData, - window.navigator.userAgent - ); - if (canceled) { - return; - } - query.debugLog = await ipcRenderer.invoke( - 'DebugLogs.upload', - logs - ); - if (canceled) { - return; - } - } catch { - // Ignore - } - } - - const supportURL = createSupportUrl({ - locale: window.SignalContext.getI18nLocale(), - query, - }); - - openLinkInWebBrowser(supportURL); - - setRequest(undefined); - hideKeyTransparencyErrorDialog(); - })() - ); - - return () => { - canceled = true; - }; - }, [request, hideKeyTransparencyErrorDialog]); + openLinkInWebBrowser(supportURL); + setSubmitting(false); + hideKeyTransparencyErrorDialog(); + }, + [hideKeyTransparencyErrorDialog] + ); return ( window.IPC.showDebugLog({ mode: 'close' })} onSubmit={handleSubmit} - isSubmitting={request !== undefined} + isSubmitting={submitting} /> ); }