mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: update sidebar diff status on WebSocket events (#23116)
## Problem The sidebar diff status (PR icon, +additions/-deletions, file count) was not updating in real-time. Users had to reload the page to see changes. Two root causes: 1. **Frontend**: The `diff_status_change` WebSocket handler in `AgentsPage.tsx` had an early `return` (line 398) that skipped `updateInfiniteChatsCache`, so the sidebar's cache was never updated. Even for other event types, the cache merge only spread `status` and `title` — never `diff_status`. 2. **Server**: `publishChatPubsubEvent` in `chatd.go` constructed a minimal `Chat` payload without `DiffStatus`, so even if the frontend consumed the event, `updatedChat.diff_status` would be `undefined`. ## Fix ### Server (`coderd/chatd/chatd.go`) - `publishChatPubsubEvent` now accepts an optional `*codersdk.ChatDiffStatus` parameter; when non-nil it's set on the outgoing `Chat` payload. - `PublishDiffStatusChange` fetches the diff status from the DB, converts it, and passes it through. - Added `convertDBChatDiffStatus` (mirrors `coderd/chats.go`'s converter to avoid circular import). - All other callers pass `nil`. ### Frontend (`site/src/pages/AgentsPage/AgentsPage.tsx`) - Removed the early `return` so `diff_status_change` events fall through to the cache update logic. - Added `isDiffStatusEvent` flag and spread `diff_status` into both the infinite chats cache (sidebar) and the individual chat cache.
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
||||
agentproto "github.com/coder/coder/v2/agent/proto"
|
||||
"github.com/coder/coder/v2/coderd/chatd/chatprompt"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/externalauth/gitprovider"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/rbac/policy"
|
||||
"github.com/coder/coder/v2/coderd/render"
|
||||
@@ -1164,3 +1165,86 @@ func nullInt64Ptr(v sql.NullInt64) *int64 {
|
||||
value := v.Int64
|
||||
return &value
|
||||
}
|
||||
|
||||
// ChatDiffStatus converts a database.ChatDiffStatus to a
|
||||
// codersdk.ChatDiffStatus. When status is nil an empty value
|
||||
// containing only the chatID is returned.
|
||||
func ChatDiffStatus(chatID uuid.UUID, status *database.ChatDiffStatus) codersdk.ChatDiffStatus {
|
||||
result := codersdk.ChatDiffStatus{
|
||||
ChatID: chatID,
|
||||
}
|
||||
if status == nil {
|
||||
return result
|
||||
}
|
||||
|
||||
result.ChatID = status.ChatID
|
||||
if status.Url.Valid {
|
||||
u := strings.TrimSpace(status.Url.String)
|
||||
if u != "" {
|
||||
result.URL = &u
|
||||
}
|
||||
}
|
||||
if result.URL == nil {
|
||||
// Try to build a branch URL from the stored origin.
|
||||
// Since this function does not have access to the API
|
||||
// instance, we construct a GitHub provider directly as
|
||||
// a best-effort fallback.
|
||||
// TODO: This uses the default github.com API base URL,
|
||||
// so branch URLs for GitHub Enterprise instances will
|
||||
// be incorrect. To fix this, this function would need
|
||||
// access to the external auth configs.
|
||||
gp := gitprovider.New("github", "", nil)
|
||||
if gp != nil {
|
||||
if owner, repo, _, ok := gp.ParseRepositoryOrigin(status.GitRemoteOrigin); ok {
|
||||
branchURL := gp.BuildBranchURL(owner, repo, status.GitBranch)
|
||||
if branchURL != "" {
|
||||
result.URL = &branchURL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if status.PullRequestState.Valid {
|
||||
pullRequestState := strings.TrimSpace(status.PullRequestState.String)
|
||||
if pullRequestState != "" {
|
||||
result.PullRequestState = &pullRequestState
|
||||
}
|
||||
}
|
||||
result.PullRequestTitle = status.PullRequestTitle
|
||||
result.PullRequestDraft = status.PullRequestDraft
|
||||
result.ChangesRequested = status.ChangesRequested
|
||||
result.Additions = status.Additions
|
||||
result.Deletions = status.Deletions
|
||||
result.ChangedFiles = status.ChangedFiles
|
||||
if status.AuthorLogin.Valid {
|
||||
result.AuthorLogin = &status.AuthorLogin.String
|
||||
}
|
||||
if status.AuthorAvatarUrl.Valid {
|
||||
result.AuthorAvatarURL = &status.AuthorAvatarUrl.String
|
||||
}
|
||||
if status.BaseBranch.Valid {
|
||||
result.BaseBranch = &status.BaseBranch.String
|
||||
}
|
||||
if status.HeadBranch.Valid {
|
||||
result.HeadBranch = &status.HeadBranch.String
|
||||
}
|
||||
if status.PrNumber.Valid {
|
||||
result.PRNumber = &status.PrNumber.Int32
|
||||
}
|
||||
if status.Commits.Valid {
|
||||
result.Commits = &status.Commits.Int32
|
||||
}
|
||||
if status.Approved.Valid {
|
||||
result.Approved = &status.Approved.Bool
|
||||
}
|
||||
if status.ReviewerCount.Valid {
|
||||
result.ReviewerCount = &status.ReviewerCount.Int32
|
||||
}
|
||||
if status.RefreshedAt.Valid {
|
||||
refreshedAt := status.RefreshedAt.Time
|
||||
result.RefreshedAt = &refreshedAt
|
||||
}
|
||||
staleAt := status.StaleAt
|
||||
result.StaleAt = &staleAt
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user