mirror of
https://github.com/stablyai/orca.git
synced 2026-10-27 00:02:48 +00:00
fix(mobile-native-chat): stop an unbounded send claiming an image turn already in the read
The image-preview pass runs before the rebase, so a send captured with no boundary matched any image turn the settled read carried — binding the user's freshly attached photo to an old one and retiring the bubble through landedImagePendingIds, which short-circuits the retirement path entirely. Pinning the tail in the rebase could not help: the claim was already made. Such an entry now waits one tick and claims against a real tail.
This commit is contained in:
@@ -25,6 +25,16 @@ function assistantTurn(id: string, text: string, timestamp: number): NativeChatM
|
||||
}
|
||||
}
|
||||
|
||||
function imageTurn(id: string, path: string, timestamp: number): NativeChatMessage {
|
||||
return {
|
||||
id,
|
||||
role: 'user',
|
||||
blocks: [{ type: 'text', text: `[Image: source: ${path}]` }],
|
||||
timestamp,
|
||||
source: 'transcript'
|
||||
}
|
||||
}
|
||||
|
||||
/** Texts of the optimistic bubbles the chat list actually renders. */
|
||||
function renderedPendingTexts(
|
||||
messages: NativeChatMessage[],
|
||||
@@ -299,6 +309,23 @@ describe('useMobileNativeChatDrafts glued pending sends', () => {
|
||||
expect(renderedPendingTexts(olderIdentical, state)).toEqual(['fix the', 'bug'])
|
||||
})
|
||||
|
||||
// The preview pass runs before the rebase, so an entry with no boundary
|
||||
// would bind the user's fresh photo to whatever image turn the read carries.
|
||||
it('does not bind a photo sent before the read to an image turn already in it', async () => {
|
||||
await mount([], false, false)
|
||||
act(() => {
|
||||
const origin = state?.captureSendOrigin('')
|
||||
if (origin) {
|
||||
state?.acceptSend(origin, '', ['file:///new.png'])
|
||||
}
|
||||
})
|
||||
|
||||
const olderImage = [imageTurn('m1', '/old/photo.png', 1000)]
|
||||
await update(olderImage)
|
||||
expect(state?.imagePreviewsByMessageId).toEqual({})
|
||||
expect(state?.pending.map((item) => item.images)).toEqual([['file:///new.png']])
|
||||
})
|
||||
|
||||
it('still retires them once their own glued row lands', async () => {
|
||||
await mount([], false, false)
|
||||
rapidSend('fix the', 'bug')
|
||||
|
||||
@@ -291,7 +291,13 @@ export function useMobileNativeChatDrafts(args: {
|
||||
if (pending.length === 0) {
|
||||
return
|
||||
}
|
||||
const landedImagePreviews = findLandedImagePreviewEchoes(messages, pending)
|
||||
// This pass runs before the rebase below, so a send captured with no
|
||||
// boundary would claim any image turn in the read — binding the user's fresh
|
||||
// photo to an old one. It waits a tick and claims against a real tail.
|
||||
const landedImagePreviews = findLandedImagePreviewEchoes(
|
||||
messages,
|
||||
pending.filter((item) => item.baselineResolved)
|
||||
)
|
||||
const landedImagePendingIds = new Set(landedImagePreviews.map((preview) => preview.pendingId))
|
||||
if (landedImagePreviews.length > 0) {
|
||||
setImagePreviewsBySession((previous) =>
|
||||
|
||||
Reference in New Issue
Block a user