mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 00:02:29 +00:00
fix(mobile): keep the live text's actions sheet on the block it was opened for
On Android the sheet opened by a long press on the live reasoning was a flag gated on a live block: it vanished when the block ended, mid Select text, and the stale flag reopened it unprompted on the next block. The sheet now holds the message it was opened for.
This commit is contained in:
@@ -74,4 +74,50 @@ describe('MobileNativeChatLiveLine on Android', () => {
|
||||
act(() => sheet!.props.onClose())
|
||||
expect(byType('MessageActionsSheet')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('keeps the sheet through the block ending, and never opens one for the next by itself', () => {
|
||||
const line = (reasoning: NativeChatLiveReasoning | null) =>
|
||||
createElement(MobileNativeChatLiveLine, {
|
||||
line: { thinking: true, activityText: null, reasoning, reasoningExpanded: true },
|
||||
onToggleReasoning: vi.fn(),
|
||||
fontScale: 1
|
||||
})
|
||||
act(() => {
|
||||
renderer = create(line(block))
|
||||
})
|
||||
act(() => byType('ReasoningBody')[0]!.props.onLongPress())
|
||||
// The block ends while the reader copies or selects it.
|
||||
act(() => renderer!.update(line(null)))
|
||||
expect(byType('MessageActionsSheet').map((sheet) => sheet.props.message)).toEqual([
|
||||
block.message
|
||||
])
|
||||
act(() => byType('MessageActionsSheet')[0]!.props.onClose())
|
||||
const next: NativeChatLiveReasoning = {
|
||||
message: { ...block.message, id: 'r-2' },
|
||||
markdown: 'Next thought'
|
||||
}
|
||||
act(() => renderer!.update(line(next)))
|
||||
expect(byType('MessageActionsSheet')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('opens no sheet for the next block when the first ends with the sheet open', () => {
|
||||
const line = (reasoning: NativeChatLiveReasoning | null) =>
|
||||
createElement(MobileNativeChatLiveLine, {
|
||||
line: { thinking: true, activityText: null, reasoning, reasoningExpanded: true },
|
||||
onToggleReasoning: vi.fn(),
|
||||
fontScale: 1
|
||||
})
|
||||
act(() => {
|
||||
renderer = create(line(block))
|
||||
})
|
||||
act(() => byType('ReasoningBody')[0]!.props.onLongPress())
|
||||
act(() => renderer!.update(line(null)))
|
||||
const next: NativeChatLiveReasoning = {
|
||||
message: { ...block.message, id: 'r-2' },
|
||||
markdown: 'Next thought'
|
||||
}
|
||||
act(() => renderer!.update(line(next)))
|
||||
// Still the first block's sheet, the one the reader opened; none for r-2.
|
||||
expect(byType('MessageActionsSheet').map((sheet) => sheet.props.message.id)).toEqual(['r-1'])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-nati
|
||||
import { ChevronRight } from 'lucide-react-native'
|
||||
import { nativeChatReasoningDisclosureKey } from '../../../src/shared/native-chat-reasoning-row'
|
||||
import { formatNativeChatActiveTurnLabel } from '../../../src/shared/native-chat-turn-status'
|
||||
import type { NativeChatMessage } from '../../../src/shared/native-chat-types'
|
||||
import { INLINE_TEXT_SELECTION } from '../components/inline-text-selection'
|
||||
import { colors, spacing, typography } from '../theme/mobile-theme'
|
||||
import { MobileNativeChatMessageActionsSheet } from './MobileNativeChatMessageActionsSheet'
|
||||
@@ -28,8 +29,10 @@ export function MobileNativeChatLiveLine({
|
||||
const open = reasoning !== null && reasoningExpanded
|
||||
const label = formatNativeChatActiveTurnLabel(line)
|
||||
// Android has no inline selection; the finished row's long-press sheet copies the live text too.
|
||||
const [actionsOpen, setActionsOpen] = useState(false)
|
||||
const openActions = useCallback(() => setActionsOpen(true), [])
|
||||
// It holds the block it opened for, so it outlives that block ending and never reopens by itself.
|
||||
const [actionsFor, setActionsFor] = useState<NativeChatMessage | null>(null)
|
||||
const liveMessage = reasoning?.message ?? null
|
||||
const openActions = useCallback(() => setActionsFor(liveMessage), [liveMessage])
|
||||
return (
|
||||
<View>
|
||||
{/* One element for every state of the line, so TalkBack hears each new label; the body
|
||||
@@ -70,10 +73,10 @@ export function MobileNativeChatLiveLine({
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
{reasoning && actionsOpen ? (
|
||||
{actionsFor ? (
|
||||
<MobileNativeChatMessageActionsSheet
|
||||
message={reasoning.message}
|
||||
onClose={() => setActionsOpen(false)}
|
||||
message={actionsFor}
|
||||
onClose={() => setActionsFor(null)}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user