From f46dba48671e87ae551e01ba2d9d6a33f01209fe Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Sun, 31 May 2026 17:24:07 -0400 Subject: [PATCH 1/2] Use KeyboardStickyView for crypto input action bars on iOS Replaces KeyboardAvoidingView2 with KeyboardStickyView (react-native-keyboard-controller) for the bottom action bar on all four crypto input pages (encrypt, decrypt, sign, verify). The sticky view uses Reanimated to track keyboard height frame-by-frame on the UI thread, eliminating the layout-recalculation lag that caused the bar to trail the keyboard animation. --- shared/crypto/decrypt.tsx | 44 +++++++++++++------ shared/crypto/encrypt.tsx | 89 ++++++++++++++++++++++++--------------- shared/crypto/sign.tsx | 42 +++++++++++++----- shared/crypto/verify.tsx | 42 +++++++++++++----- 4 files changed, 148 insertions(+), 69 deletions(-) diff --git a/shared/crypto/decrypt.tsx b/shared/crypto/decrypt.tsx index 4990f4760f5a..70e101218a6f 100644 --- a/shared/crypto/decrypt.tsx +++ b/shared/crypto/decrypt.tsx @@ -5,6 +5,7 @@ import * as React from 'react' import * as T from '@/constants/types' import * as TestIDs from '@/tests/e2e/shared/test-ids' import {CryptoBanner, DragAndDrop, Input, InputActionsBar} from './input' +import {KeyboardStickyView} from 'react-native-keyboard-controller' import {CryptoOutput, CryptoOutputActionsBar, CryptoSignedSender} from './output' import { beginRun, @@ -138,6 +139,8 @@ export const DecryptInput = (_props: unknown) => { const {params} = useRoute() as RootRouteProps<'decryptTab'> const controller = useDecryptState(params) const navigateAppend = C.Router2.navigateAppend + const insets = Kb.useSafeAreaInsets() + const stickyOffset = React.useMemo(() => ({closed: -insets.bottom, opened: 0}), [insets.bottom]) const onRun = () => { const f = async () => { @@ -149,8 +152,31 @@ export const DecryptInput = (_props: unknown) => { C.ignorePromise(f()) } - const contents = ( - <> + if (!isMobile) { + return ( + + + + + ) + } + + return ( + { onSetInput={controller.setInput} onClearInput={controller.clearInput} /> - - ) - - return isMobile ? ( - - {contents} - - - ) : ( - - {contents} + + + ) } diff --git a/shared/crypto/encrypt.tsx b/shared/crypto/encrypt.tsx index dd2d8921ded4..517ec4b39b01 100644 --- a/shared/crypto/encrypt.tsx +++ b/shared/crypto/encrypt.tsx @@ -6,6 +6,7 @@ import * as T from '@/constants/types' import Recipients from './recipients' import {openURL} from '@/util/misc' import {CryptoBanner, DragAndDrop, Input, InputActionsBar} from './input' +import {KeyboardStickyView} from 'react-native-keyboard-controller' import {CryptoOutput, CryptoOutputActionsBar, CryptoSignedSender, OutputInfoBanner} from './output' import { type CommonOutputRouteParams, @@ -449,6 +450,8 @@ const EncryptInputBody = ({params}: {params?: EncryptRouteParams}) => { const blurCBRef = React.useRef(() => {}) const navigateAppend = C.Router2.navigateAppend const appendEncryptRecipientsBuilder = C.Router2.appendEncryptRecipientsBuilder + const insets = Kb.useSafeAreaInsets() + const stickyOffset = React.useMemo(() => ({closed: -insets.bottom, opened: 0}), [insets.bottom]) const onRun = () => { const f = async () => { @@ -460,32 +463,46 @@ const EncryptInputBody = ({params}: {params?: EncryptRouteParams}) => { C.ignorePromise(f()) } - const options = isMobile ? ( - - - - ) : ( - - ) + if (!isMobile) { + return ( + + + + + + + ) + } - const content = ( - <> + return ( + { onSetInput={controller.setInput} onClearInput={controller.clearInput} /> - {options} - - ) - - return isMobile ? ( - {content} - ) : ( - - {content} + + + + + ) } diff --git a/shared/crypto/sign.tsx b/shared/crypto/sign.tsx index 017d05c18a17..a9efba6dbbd8 100644 --- a/shared/crypto/sign.tsx +++ b/shared/crypto/sign.tsx @@ -6,6 +6,7 @@ import * as T from '@/constants/types' import * as TestIDs from '@/tests/e2e/shared/test-ids' import {openURL} from '@/util/misc' import {CryptoBanner, DragAndDrop, Input, InputActionsBar} from './input' +import {KeyboardStickyView} from 'react-native-keyboard-controller' import {CryptoOutput, CryptoOutputActionsBar, CryptoSignedSender, OutputInfoBanner} from './output' import { beginRun, @@ -147,6 +148,8 @@ export const SignInput = (_props: unknown) => { const controller = useSignState(params) const blurCBRef = React.useRef(() => {}) const navigateAppend = C.Router2.navigateAppend + const insets = Kb.useSafeAreaInsets() + const stickyOffset = React.useMemo(() => ({closed: -insets.bottom, opened: 0}), [insets.bottom]) const onRun = () => { const f = async () => { @@ -158,8 +161,31 @@ export const SignInput = (_props: unknown) => { C.ignorePromise(f()) } - const content = ( - <> + if (!isMobile) { + return ( + + + + + ) + } + + return ( + { onSetInput={controller.setInput} onClearInput={controller.clearInput} /> - {isMobile ? : null} - - ) - - return isMobile ? ( - {content} - ) : ( - - {content} + + + ) } diff --git a/shared/crypto/verify.tsx b/shared/crypto/verify.tsx index aaf851864728..9a0b68172b11 100644 --- a/shared/crypto/verify.tsx +++ b/shared/crypto/verify.tsx @@ -5,6 +5,7 @@ import * as React from 'react' import * as T from '@/constants/types' import * as TestIDs from '@/tests/e2e/shared/test-ids' import {CryptoBanner, DragAndDrop, Input, InputActionsBar} from './input' +import {KeyboardStickyView} from 'react-native-keyboard-controller' import {CryptoOutput, CryptoOutputActionsBar, CryptoSignedSender} from './output' import { beginRun, @@ -138,6 +139,8 @@ export const VerifyInput = (_props: unknown) => { const {params} = useRoute() as RootRouteProps<'verifyTab'> const controller = useVerifyState(params) const navigateAppend = C.Router2.navigateAppend + const insets = Kb.useSafeAreaInsets() + const stickyOffset = React.useMemo(() => ({closed: -insets.bottom, opened: 0}), [insets.bottom]) const onRun = () => { const f = async () => { @@ -149,8 +152,31 @@ export const VerifyInput = (_props: unknown) => { C.ignorePromise(f()) } - const content = ( - <> + if (!isMobile) { + return ( + + + + + ) + } + + return ( + { onSetInput={controller.setInput} onClearInput={controller.clearInput} /> - {isMobile ? : null} - - ) - - return isMobile ? ( - {content} - ) : ( - - {content} + + + ) } From 5692d3188a6531222c2a9b410b4553e648c0fac0 Mon Sep 17 00:00:00 2001 From: chrisnojima Date: Sun, 31 May 2026 17:32:26 -0400 Subject: [PATCH 2/2] try codegraph --- .claude/settings.json | 9 ++++++++- .gitignore | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.claude/settings.json b/.claude/settings.json index 27816aa5efb8..3a4e914bf1b0 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -14,7 +14,14 @@ "Bash(yarn tsc:*)", "Bash(yarn ios:pod*)", "Bash(yarn coverage:*)", - "Bash(yarn maestro*)" + "Bash(yarn maestro*)", + "mcp__codegraph__codegraph_search", + "mcp__codegraph__codegraph_context", + "mcp__codegraph__codegraph_callers", + "mcp__codegraph__codegraph_callees", + "mcp__codegraph__codegraph_impact", + "mcp__codegraph__codegraph_node", + "mcp__codegraph__codegraph_status" ] }, "hooks": { diff --git a/.gitignore b/.gitignore index a7e12fe22c49..e15aea4c0f76 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,5 @@ shared/tests/results/ CLAUDE.md .tsOuts docs +.codegraph +.mcp.json