mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add diff_status_change event to /chats/watch pubsub stream (#22419)
## Summary Adds a new `diff_status_change` event kind to the `/chats/watch` pubsub stream so the sidebar can update diff status (PR created, files changed, branch info) without a full page reload. ### Problem When a chat's diff status changes (e.g. PR created via GitHub, git branch pushed), the sidebar didn't update because: 1. The backend `publishChatPubsubEvent` didn't include diff status data 2. The frontend watch handler only merged `status`, `title`, and `updated_at` from events ### Solution A **notify-only** approach: a new `ChatEventKindDiffStatusChange` event kind tells the frontend "diff status changed for chat X" — the frontend then invalidates the relevant React Query cache entries to re-fetch. ### Backend changes - **`coderd/pubsub/chatevent.go`**: New `ChatEventKindDiffStatusChange = "diff_status_change"` constant - **`coderd/chatd/chatd.go`**: New `PublishDiffStatusChange(ctx, chatID)` method on `Server` - **`coderd/chats.go`**: New `publishChatDiffStatusEvent` helper. Published from: - `refreshWorkspaceChatDiffStatuses` — after each chat's diff status is refreshed via GitHub API - `storeChatGitRef` — after persisting git branch/origin info from workspace agent ### Frontend changes - **`AgentsPage.tsx`**: Handle `diff_status_change` event by invalidating `chatDiffStatusKey` and `chatDiffContentsKey` queries - **`ChatContext.ts`**: Remove redundant diff status invalidation that fired on every chat status change (the new event kind handles this properly)
This commit is contained in:
@@ -1430,6 +1430,24 @@ func (p *Server) publishChatPubsubEvent(chat database.Chat, kind coderdpubsub.Ch
|
||||
}
|
||||
}
|
||||
|
||||
// PublishDiffStatusChange broadcasts a diff_status_change event for
|
||||
// the given chat so that watching clients know to re-fetch the diff
|
||||
// status. This is called from the HTTP layer after the diff status
|
||||
// is updated in the database.
|
||||
func (p *Server) PublishDiffStatusChange(ctx context.Context, chatID uuid.UUID) error {
|
||||
if p.pubsub == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
chat, err := p.db.GetChatByID(ctx, chatID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get chat: %w", err)
|
||||
}
|
||||
|
||||
p.publishChatPubsubEvent(chat, coderdpubsub.ChatEventKindDiffStatusChange)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Server) publishError(chatID uuid.UUID, message string) {
|
||||
message = strings.TrimSpace(message)
|
||||
if message == "" {
|
||||
|
||||
@@ -1068,7 +1068,9 @@ func (api *API) storeChatGitRef(ctx context.Context, workspaceID, workspaceOwner
|
||||
slog.F("workspace_id", workspaceID),
|
||||
slog.Error(err),
|
||||
)
|
||||
continue
|
||||
}
|
||||
api.publishChatDiffStatusEvent(ctx, chat.ID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1109,6 +1111,7 @@ func (api *API) refreshWorkspaceChatDiffStatuses(ctx context.Context, workspaceI
|
||||
}
|
||||
|
||||
api.publishChatStatusEvent(ctx, chat.ID)
|
||||
api.publishChatDiffStatusEvent(ctx, chat.ID)
|
||||
}
|
||||
|
||||
return allHavePR
|
||||
@@ -1138,6 +1141,19 @@ func (api *API) publishChatStatusEvent(ctx context.Context, chatID uuid.UUID) {
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) publishChatDiffStatusEvent(ctx context.Context, chatID uuid.UUID) {
|
||||
if api.chatDaemon == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := api.chatDaemon.PublishDiffStatusChange(ctx, chatID); err != nil {
|
||||
api.Logger.Debug(ctx, "failed to publish chat diff status change",
|
||||
slog.F("chat_id", chatID),
|
||||
slog.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) resolveChatDiffContents(
|
||||
ctx context.Context,
|
||||
chat database.Chat,
|
||||
|
||||
@@ -39,8 +39,9 @@ type ChatEvent struct {
|
||||
type ChatEventKind string
|
||||
|
||||
const (
|
||||
ChatEventKindStatusChange ChatEventKind = "status_change"
|
||||
ChatEventKindTitleChange ChatEventKind = "title_change"
|
||||
ChatEventKindCreated ChatEventKind = "created"
|
||||
ChatEventKindDeleted ChatEventKind = "deleted"
|
||||
ChatEventKindStatusChange ChatEventKind = "status_change"
|
||||
ChatEventKindTitleChange ChatEventKind = "title_change"
|
||||
ChatEventKindCreated ChatEventKind = "created"
|
||||
ChatEventKindDeleted ChatEventKind = "deleted"
|
||||
ChatEventKindDiffStatusChange ChatEventKind = "diff_status_change"
|
||||
)
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { watchChat } from "api/api";
|
||||
import {
|
||||
chatDiffContentsKey,
|
||||
chatDiffStatusKey,
|
||||
chatKey,
|
||||
chatsKey,
|
||||
} from "api/queries/chats";
|
||||
import { chatKey, chatsKey } from "api/queries/chats";
|
||||
import type * as TypesGen from "api/typesGenerated";
|
||||
import { asRecord, asString } from "components/ai-elements/runtimeTypeUtils";
|
||||
import {
|
||||
@@ -614,7 +609,6 @@ export const useChatStore = (
|
||||
continue;
|
||||
}
|
||||
|
||||
const previousStatus = store.getSnapshot().chatStatus;
|
||||
store.setChatStatus(nextStatus);
|
||||
if (nextStatus === "pending" || nextStatus === "waiting") {
|
||||
store.clearStreamState();
|
||||
@@ -627,16 +621,6 @@ export const useChatStore = (
|
||||
status: nextStatus,
|
||||
updated_at: new Date().toISOString(),
|
||||
}));
|
||||
if (previousStatus !== nextStatus) {
|
||||
void Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: chatDiffStatusKey(chatID),
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: chatDiffContentsKey(chatID),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -681,7 +665,6 @@ export const useChatStore = (
|
||||
cancelScheduledStreamReset,
|
||||
chatID,
|
||||
clearChatErrorReason,
|
||||
queryClient,
|
||||
scheduleStreamReset,
|
||||
setChatErrorReason,
|
||||
store,
|
||||
|
||||
@@ -2,6 +2,8 @@ import { watchChats } from "api/api";
|
||||
import { getErrorMessage } from "api/errors";
|
||||
import {
|
||||
archiveChat,
|
||||
chatDiffContentsKey,
|
||||
chatDiffStatusKey,
|
||||
chatKey,
|
||||
chatModelConfigs,
|
||||
chatModels,
|
||||
@@ -291,6 +293,21 @@ const AgentsPage: FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chatEvent.kind === "diff_status_change") {
|
||||
void Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: chatsKey,
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: chatDiffStatusKey(updatedChat.id),
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: chatDiffContentsKey(updatedChat.id),
|
||||
}),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
queryClient.setQueryData(
|
||||
chatsKey,
|
||||
(prev: TypesGen.Chat[] | undefined) => {
|
||||
|
||||
Reference in New Issue
Block a user