Fix forward comment landing before forwarded content
The comment send was fired in parallel with the forward REST call, not chained after it -- confirmed live with a 2-source bulk forward, where the comment (plain WS publish, fast) landed before the forwarded messages (async endpoint, responds with status:"pending" before the messages are actually persisted server-side). Chains the comment send off the forward promise instead of starting both at once. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
4cd2b33c53
commit
95c0677b55
@@ -831,22 +831,34 @@ export function ConversationView() {
|
|||||||
// `{pub}` publishes (was N sources x M targets separate WS sends, each independently racing
|
// `{pub}` publishes (was N sources x M targets separate WS sends, each independently racing
|
||||||
// subscription/attachment; the server handles every pair in one request with real per-pair
|
// subscription/attachment; the server handles every pair in one request with real per-pair
|
||||||
// success/failure reporting instead of each WS publish failing silently on its own).
|
// success/failure reporting instead of each WS publish failing silently on its own).
|
||||||
if (sources.length > 0) {
|
//
|
||||||
forwardMessages(sources, targetTopics)
|
// The comment send is chained AFTER this promise settles, not fired in parallel -- confirmed
|
||||||
.then((res) => {
|
// live (2026-09-25) that firing both at once is a real, reproducible bug, not a theoretical one:
|
||||||
if (res.failed.length > 0) showError(undefined, `${res.failed.length} 条消息转发失败`);
|
// the endpoint's response comes back with per-item `status:"pending"`/`reason:
|
||||||
})
|
// "MESSAGE_PERSIST_PENDING"` (it acks the request before the forwarded messages are actually
|
||||||
.catch((err) => showError(err, '转发失败'));
|
// written server-side), while the comment's own sendText() goes over the plain WS publish path,
|
||||||
}
|
// which is fast enough to land FIRST. A 2-source bulk forward reordered exactly this way in a
|
||||||
// Comment stays on the OLD client-side path deliberately -- see forwardMessages()'s own comment
|
// live test before this fix (comment appeared above both forwarded messages). Chaining at least
|
||||||
// on why (preserves this app's existing "comment appears AFTER the forwarded messages" order,
|
// waits for the request round-trip before starting the comment send, which resolved the
|
||||||
// which the endpoint's own built-in comment field does not).
|
// reordering in the same live scenario after this fix -- see this function's own follow-up
|
||||||
|
// verification, not a guarantee for every possible load/latency condition given the endpoint's
|
||||||
|
// own async "pending" semantics.
|
||||||
|
const forwardDone =
|
||||||
|
sources.length > 0
|
||||||
|
? forwardMessages(sources, targetTopics)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.failed.length > 0) showError(undefined, `${res.failed.length} 条消息转发失败`);
|
||||||
|
})
|
||||||
|
.catch((err) => showError(err, '转发失败'))
|
||||||
|
: Promise.resolve();
|
||||||
if (comment) {
|
if (comment) {
|
||||||
for (const targetTopic of targetTopics) {
|
forwardDone.then(() => {
|
||||||
ensureTopicAttached(targetTopic)
|
for (const targetTopic of targetTopics) {
|
||||||
.then((target) => sendText(target, comment))
|
ensureTopicAttached(targetTopic)
|
||||||
.catch((err) => showError(err, '附言发送失败'));
|
.then((target) => sendText(target, comment))
|
||||||
}
|
.catch((err) => showError(err, '附言发送失败'));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
setBulkForwardOpen(false);
|
setBulkForwardOpen(false);
|
||||||
exitSelectMode();
|
exitSelectMode();
|
||||||
@@ -1229,22 +1241,27 @@ export function ConversationView() {
|
|||||||
<ForwardPickerModal
|
<ForwardPickerModal
|
||||||
onClose={() => setForwardMsg(null)}
|
onClose={() => setForwardMsg(null)}
|
||||||
onPick={(targetTopics, comment) => {
|
onPick={(targetTopics, comment) => {
|
||||||
if (typeof forwardMsg.seq === 'number') {
|
// Comment is chained AFTER the forward call settles, not fired in parallel -- see
|
||||||
forwardMessages([{ topic: topicId, seq: forwardMsg.seq }], targetTopics)
|
// `bulkForward`'s own comment above for why this matters (confirmed live: firing both
|
||||||
.then((res) => {
|
// at once lets the comment's plain WS send outrace the forward endpoint's own async
|
||||||
if (res.failed.length > 0) showError(undefined, `${res.failed.length} 个目标转发失败`);
|
// "pending" persistence, landing the comment BEFORE the forwarded content it's meant to
|
||||||
})
|
// follow).
|
||||||
.catch((err) => showError(err, '转发失败'));
|
const forwardDone =
|
||||||
}
|
typeof forwardMsg.seq === 'number'
|
||||||
// Comment stays on the OLD client-side path deliberately -- see forwardMessages()'s own
|
? forwardMessages([{ topic: topicId, seq: forwardMsg.seq }], targetTopics)
|
||||||
// comment in lib/api/messages.ts on why (preserves the existing "comment appears AFTER
|
.then((res) => {
|
||||||
// the forwarded message" order).
|
if (res.failed.length > 0) showError(undefined, `${res.failed.length} 个目标转发失败`);
|
||||||
|
})
|
||||||
|
.catch((err) => showError(err, '转发失败'))
|
||||||
|
: Promise.resolve();
|
||||||
if (comment) {
|
if (comment) {
|
||||||
for (const targetTopic of targetTopics) {
|
forwardDone.then(() => {
|
||||||
ensureTopicAttached(targetTopic)
|
for (const targetTopic of targetTopics) {
|
||||||
.then((target) => sendText(target, comment))
|
ensureTopicAttached(targetTopic)
|
||||||
.catch((err) => showError(err, '附言发送失败'));
|
.then((target) => sendText(target, comment))
|
||||||
}
|
.catch((err) => showError(err, '附言发送失败'));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
setForwardMsg(null);
|
setForwardMsg(null);
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user