fix(team): require revision notice before editing
This commit is contained in:
parent
7e0520cb4c
commit
86e700f031
2 changed files with 84 additions and 6 deletions
|
|
@ -733,12 +733,21 @@ export const MessagesPanel = memo(function MessagesPanel({
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleReviseMessage = useCallback(
|
const handleReviseMessage = useCallback(
|
||||||
(message: InboxMessage) => {
|
async (message: InboxMessage) => {
|
||||||
if (!isRevisableUserSentMessage(message, memberNames)) return;
|
if (!isRevisableUserSentMessage(message, memberNames)) return;
|
||||||
const originalMessageId = trimString(message.messageId);
|
const originalMessageId = trimString(message.messageId);
|
||||||
if (originalMessageId !== revisionMessageId) return;
|
if (originalMessageId !== revisionMessageId) return;
|
||||||
const recipient = trimString(message.to);
|
const recipient = trimString(message.to);
|
||||||
const originalText = getRevisableMessageText(message);
|
const originalText = getRevisableMessageText(message);
|
||||||
|
try {
|
||||||
|
await sendTeamMessage(teamName, {
|
||||||
|
member: recipient,
|
||||||
|
text: buildRevisionNoticeText(originalMessageId, originalText),
|
||||||
|
summary: `${REVISION_NOTICE_PREFIX} ${originalMessageId}`,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setRevisionRequest({
|
setRevisionRequest({
|
||||||
requestId: `${originalMessageId}:${Date.now()}`,
|
requestId: `${originalMessageId}:${Date.now()}`,
|
||||||
originalMessageId,
|
originalMessageId,
|
||||||
|
|
@ -747,11 +756,6 @@ export const MessagesPanel = memo(function MessagesPanel({
|
||||||
actionMode: message.actionMode,
|
actionMode: message.actionMode,
|
||||||
});
|
});
|
||||||
composerTextareaRef.current?.focus();
|
composerTextareaRef.current?.focus();
|
||||||
void sendTeamMessage(teamName, {
|
|
||||||
member: recipient,
|
|
||||||
text: buildRevisionNoticeText(originalMessageId, originalText),
|
|
||||||
summary: `${REVISION_NOTICE_PREFIX} ${originalMessageId}`,
|
|
||||||
}).catch(() => undefined);
|
|
||||||
},
|
},
|
||||||
[memberNames, revisionMessageId, sendTeamMessage, teamName]
|
[memberNames, revisionMessageId, sendTeamMessage, teamName]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -868,6 +868,80 @@ describe('MessagesPanel idle summary invariants', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not enter revision mode when the revision notice fails to send', async () => {
|
||||||
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
|
storeState.sendTeamMessage.mockRejectedValueOnce(new Error('send failed'));
|
||||||
|
const host = document.createElement('div');
|
||||||
|
document.body.appendChild(host);
|
||||||
|
const root = createRoot(host);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
storeState.teamMessagesByName['atlas-hq'] = {
|
||||||
|
canonicalMessages: [
|
||||||
|
makeMessage({
|
||||||
|
messageId: 'latest-user-send',
|
||||||
|
from: 'user',
|
||||||
|
to: 'bob',
|
||||||
|
source: 'user_sent',
|
||||||
|
timestamp: '2026-04-08T12:05:00.000Z',
|
||||||
|
text: 'raw transport text',
|
||||||
|
summary: 'restore this text',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
optimisticMessages: [],
|
||||||
|
feedRevision: 'rev-1',
|
||||||
|
nextCursor: null,
|
||||||
|
hasMore: false,
|
||||||
|
lastFetchedAt: Date.now(),
|
||||||
|
loadingHead: false,
|
||||||
|
loadingOlder: false,
|
||||||
|
headHydrated: true,
|
||||||
|
};
|
||||||
|
root.render(
|
||||||
|
React.createElement(MessagesPanel, {
|
||||||
|
teamName: 'atlas-hq',
|
||||||
|
position: 'sidebar',
|
||||||
|
onPositionChange: vi.fn(),
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
agentType: 'developer',
|
||||||
|
currentTaskId: null,
|
||||||
|
lastActiveAt: null,
|
||||||
|
messageCount: 0,
|
||||||
|
name: 'bob',
|
||||||
|
role: 'Developer',
|
||||||
|
status: 'idle',
|
||||||
|
taskCount: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tasks: [],
|
||||||
|
timeWindow: null,
|
||||||
|
pendingRepliesByMember: {},
|
||||||
|
onPendingReplyChange: vi.fn(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const editButtons = Array.from(host.querySelectorAll('button')).filter(
|
||||||
|
(button) => button.textContent === 'Edit message'
|
||||||
|
);
|
||||||
|
expect(editButtons).toHaveLength(1);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
editButtons[0].click();
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(storeState.sendTeamMessage).toHaveBeenCalledOnce();
|
||||||
|
expect(host.textContent).not.toContain('composer revision:latest-user-send:restore this text');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
root.unmount();
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('clears stale OpenCode runtime diagnostics once the member reply is visible', async () => {
|
it('clears stale OpenCode runtime diagnostics once the member reply is visible', async () => {
|
||||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
|
||||||
const host = document.createElement('div');
|
const host = document.createElement('div');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue