Bypass nudge for encrypt and decrypt, remove phase-transition toast

Encrypt and decrypt operations now call the signer directly without
nudge/timeout/retry wrapping. The sign operation already provides
the user-facing nudge when approval is needed, so the encrypt nudge
was redundant noise. Phase-transition toast and related constants
removed as dead code.
This commit is contained in:
Chad Curtis
2026-03-28 09:43:43 -05:00
parent 2189f5e7c4
commit 7a49e9646c
+3 -47
View File
@@ -2,7 +2,7 @@ import type { NostrEvent, NostrSigner } from '@nostrify/types';
import { createElement } from 'react';
import { toast } from '@/hooks/useToast';
import { androidResume } from '@/lib/androidResume';
import { NudgeToastContent, PhaseToastContent } from '@/components/SignerToastContent';
import { NudgeToastContent } from '@/components/SignerToastContent';
// ---------------------------------------------------------------------------
// Constants
@@ -11,28 +11,10 @@ import { NudgeToastContent, PhaseToastContent } from '@/components/SignerToastCo
/** Show the nudge toast after this delay if a signer op is still pending. */
const NUDGE_DELAY_MS = 4_000;
/** Longer delay for decrypt operations — auto-approve is common and nudging
* too early sends the user to the signer with nothing to approve. */
const NUDGE_DELAY_DECRYPT_MS = 10_000;
/** Hard timeout — reject the op entirely after this long with no response. */
const HARD_TIMEOUT_MS = 45_000;
/**
* Event kinds whose content is encrypted by the user's signer before signing.
* A signEvent for one of these kinds immediately after a nip44 encrypt is
* treated as the second phase of the same operation (encrypt-then-sign), and
* a phase-transition toast is shown so the user knows a second approval is
* coming.
*
* Only kinds where Ditto calls `user.signer.nip44.encrypt()` then immediately
* `createEvent()` qualify — DM gift-wraps use ephemeral random signers and
* are excluded.
*/
const ENCRYPTED_CONTENT_KINDS = new Set([
10000, // Mute list (NIP-51, private items encrypted to self)
30078, // App settings (NIP-78, content encrypted to self)
]);
/** Max number of automatic retries on Android foreground resume. */
const MAX_RETRIES = 2;
@@ -174,16 +156,6 @@ function showSuccessToast(opType: OpType): void {
toast({ title: `${verb} approved`, duration: 3000, variant: 'success' });
}
function showPhaseTransitionToast(signKind: number | undefined): void {
const android = isAndroid();
const label = signKind !== undefined ? KIND_LABELS[signKind] : undefined;
const signDesc = label ? `approve ${label} signing` : 'approve signing';
const message = `Encryption approved — now ${signDesc} in your signer app.`;
const description = createElement(PhaseToastContent, { message, android });
toast({ title: 'Step 1 complete', description, duration: 8000 });
}
// ---------------------------------------------------------------------------
// Core: run a signer operation with nudge + retry logic
@@ -244,7 +216,7 @@ async function runWithNudge<T>(op: () => Promise<T>, opts: RunOpts): Promise<Run
// --- Nudge timer ---
let dismissNudge: (() => void) | undefined;
const delay = opType === 'decrypt' ? NUDGE_DELAY_DECRYPT_MS : NUDGE_DELAY_MS;
const delay = NUDGE_DELAY_MS;
const nudgeTimer = setTimeout(() => {
nudgeFired = true;
const handle = showNudgeToast({
@@ -344,11 +316,6 @@ export function signerWithNudge(
signer: NostrSigner,
isBunkerConnected?: () => boolean,
): NostrSigner {
// Multi-phase state: set to true when a nip44 encrypt completes with the
// nudge shown. Cleared on the next signEvent. Used to detect encrypt-then-sign
// flows for kinds whose content is encrypted by the user's signer.
let pendingEncryptNudge = false;
/** Run an op and return just the value (discarding nudge metadata). */
function run<T>(op: () => Promise<T>, kind: number | undefined, opType: OpType): Promise<T> {
return runWithNudge(op, { kind, opType, isBunkerConnected }).then((r) => r.value);
@@ -358,13 +325,6 @@ export function signerWithNudge(
getPublicKey: () => run(() => signer.getPublicKey(), undefined, 'sign'),
signEvent: (event: NostrEvent) => {
// Show a phase-transition toast when signing an event whose content was
// just encrypted by the user's signer and the nudge was shown for that
// encrypt. Only fires for kinds we know use encrypt-then-sign.
if (pendingEncryptNudge && ENCRYPTED_CONTENT_KINDS.has(event.kind)) {
showPhaseTransitionToast(event.kind);
}
pendingEncryptNudge = false;
return run(() => signer.signEvent(event), event.kind, 'sign');
},
};
@@ -381,11 +341,7 @@ export function signerWithNudge(
}) {
return {
encrypt: (pubkey: string, plaintext: string) =>
runWithNudge(() => crypto.encrypt(pubkey, plaintext), { kind: undefined, opType: 'encrypt', isBunkerConnected })
.then(({ value, nudgeFired }) => {
pendingEncryptNudge = nudgeFired;
return value;
}),
crypto.encrypt(pubkey, plaintext),
decrypt: (pubkey: string, ciphertext: string) =>
crypto.decrypt(pubkey, ciphertext),
};