Skip to content

Commit 4988c78

Browse files
committed
fix(mship-resources): handle ADD/DELETE race and reorder during pending flush
- Track in-flight ADD promises so DELETE chains off finally(), preventing orphaned server rows when a user removes a resource before its POST resolves - Defer reorder PATCH until pending flush completes; emit with full local order - Clear new refs in reset paths
1 parent 3419c1e commit 4988c78

1 file changed

Lines changed: 55 additions & 12 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/home/hooks

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,8 @@ export function useChat(
15441544
const resourcesRef = useRef(resources)
15451545
resourcesRef.current = resources
15461546
const pendingPersistResourceKeysRef = useRef<Set<string>>(new Set())
1547+
const inFlightResourceAddsRef = useRef<Map<string, Promise<unknown>>>(new Map())
1548+
const reorderNeededAfterFlushRef = useRef(false)
15471549

15481550
// Derive the effective active resource ID — auto-selects the last resource when the stored ID is
15491551
// absent or no longer in the list, avoiding a separate Effect-based state correction loop.
@@ -1971,6 +1973,8 @@ export function useChat(
19711973
setResources([])
19721974
setActiveResourceId(null)
19731975
pendingPersistResourceKeysRef.current.clear()
1976+
inFlightResourceAddsRef.current.clear()
1977+
reorderNeededAfterFlushRef.current = false
19741978
resetEphemeralPreviewState()
19751979
setMessageQueue([])
19761980
clearQueueDispatchState()
@@ -1983,20 +1987,42 @@ export function useChat(
19831987
setTransportIdle,
19841988
])
19851989

1986-
const flushPendingResources = useCallback((chatId: string) => {
1990+
const flushPendingResources = useCallback(async (chatId: string) => {
19871991
const pendingKeys = pendingPersistResourceKeysRef.current
19881992
if (pendingKeys.size === 0) return
1993+
const flushPromises: Array<Promise<unknown>> = []
19891994
for (const resource of resourcesRef.current) {
1995+
if (resource.id === 'streaming-file') continue
19901996
const key = `${resource.type}:${resource.id}`
19911997
if (!pendingKeys.has(key)) continue
19921998
pendingKeys.delete(key)
1993-
requestJson(addMothershipChatResourceContract, { body: { chatId, resource } }).catch(
1994-
(err) => {
1999+
const promise = requestJson(addMothershipChatResourceContract, {
2000+
body: { chatId, resource },
2001+
})
2002+
.catch((err) => {
19952003
pendingPersistResourceKeysRef.current.add(key)
19962004
logger.warn('Failed to flush pending resource; will retry on next hydration', err)
1997-
}
1998-
)
2005+
})
2006+
.finally(() => {
2007+
inFlightResourceAddsRef.current.delete(key)
2008+
})
2009+
inFlightResourceAddsRef.current.set(key, promise)
2010+
flushPromises.push(promise)
19992011
}
2012+
if (flushPromises.length === 0) return
2013+
await Promise.allSettled(flushPromises)
2014+
if (!reorderNeededAfterFlushRef.current) return
2015+
reorderNeededAfterFlushRef.current = false
2016+
const localOrder = resourcesRef.current.filter(
2017+
(r) =>
2018+
r.id !== 'streaming-file' && !pendingPersistResourceKeysRef.current.has(`${r.type}:${r.id}`)
2019+
)
2020+
if (localOrder.length === 0) return
2021+
requestJson(reorderMothershipChatResourcesContract, {
2022+
body: { chatId, resources: localOrder },
2023+
}).catch((err) => {
2024+
logger.warn('Failed to sync resource order after flush', err)
2025+
})
20002026
}, [])
20012027

20022028
const adoptResolvedChatId = useCallback(
@@ -2046,12 +2072,17 @@ export function useChat(
20462072
const persistChatId = chatIdRef.current ?? selectedChatIdRef.current
20472073
const key = `${resource.type}:${resource.id}`
20482074
if (persistChatId) {
2049-
requestJson(addMothershipChatResourceContract, {
2075+
const promise = requestJson(addMothershipChatResourceContract, {
20502076
body: { chatId: persistChatId, resource },
2051-
}).catch((err) => {
2052-
pendingPersistResourceKeysRef.current.add(key)
2053-
logger.warn('Failed to persist resource; will retry on next hydration', err)
20542077
})
2078+
.catch((err) => {
2079+
pendingPersistResourceKeysRef.current.add(key)
2080+
logger.warn('Failed to persist resource; will retry on next hydration', err)
2081+
})
2082+
.finally(() => {
2083+
inFlightResourceAddsRef.current.delete(key)
2084+
})
2085+
inFlightResourceAddsRef.current.set(key, promise)
20552086
} else {
20562087
pendingPersistResourceKeysRef.current.add(key)
20572088
}
@@ -2064,23 +2095,33 @@ export function useChat(
20642095

20652096
const key = `${resourceType}:${resourceId}`
20662097
const wasPending = pendingPersistResourceKeysRef.current.delete(key)
2067-
if (wasPending) return
2098+
const inFlightAdd = inFlightResourceAddsRef.current.get(key)
2099+
if (wasPending && !inFlightAdd) return
20682100

20692101
const persistChatId = chatIdRef.current ?? selectedChatIdRef.current
2070-
if (persistChatId) {
2102+
if (!persistChatId) return
2103+
const fireDelete = () => {
20712104
requestJson(removeMothershipChatResourceContract, {
20722105
body: { chatId: persistChatId, resourceType, resourceId },
20732106
}).catch((err) => {
20742107
logger.warn('Failed to persist resource removal', err)
20752108
})
20762109
}
2110+
if (inFlightAdd) {
2111+
inFlightAdd.finally(fireDelete)
2112+
} else {
2113+
fireDelete()
2114+
}
20772115
}, [])
20782116

20792117
const reorderResources = useCallback((newOrder: MothershipResource[]) => {
20802118
setResources(newOrder)
2119+
const pendingKeys = pendingPersistResourceKeysRef.current
2120+
if (pendingKeys.size > 0) {
2121+
reorderNeededAfterFlushRef.current = true
2122+
}
20812123
const persistChatId = chatIdRef.current ?? selectedChatIdRef.current
20822124
if (!persistChatId) return
2083-
const pendingKeys = pendingPersistResourceKeysRef.current
20842125
const persistableResources = newOrder.filter(
20852126
(r) => r.id !== 'streaming-file' && !pendingKeys.has(`${r.type}:${r.id}`)
20862127
)
@@ -2220,6 +2261,8 @@ export function useChat(
22202261
setResources([])
22212262
setActiveResourceId(null)
22222263
pendingPersistResourceKeysRef.current.clear()
2264+
inFlightResourceAddsRef.current.clear()
2265+
reorderNeededAfterFlushRef.current = false
22232266
resetEphemeralPreviewState()
22242267
setMessageQueue([])
22252268
clearQueueDispatchState()

0 commit comments

Comments
 (0)