From 8d34e079e8721e777dc82ec4e4a9765a7f2faa80 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Sun, 29 Mar 2026 17:42:55 +0800 Subject: [PATCH] feat(invite): show toast when invited to workspace member:added event now includes workspace_name. Frontend shows a toast notification when the current user is invited. Also clears stale member list on workspace switch. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/web/features/realtime/use-realtime-sync.ts | 6 ++++-- apps/web/features/workspace/store.ts | 2 +- apps/web/shared/types/events.ts | 1 + server/internal/handler/workspace.go | 6 +++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/web/features/realtime/use-realtime-sync.ts b/apps/web/features/realtime/use-realtime-sync.ts index 3d6e2162..8a16c5c2 100644 --- a/apps/web/features/realtime/use-realtime-sync.ts +++ b/apps/web/features/realtime/use-realtime-sync.ts @@ -96,11 +96,13 @@ export function useRealtimeSync(ws: WSClient | null) { }); const unsubMemberAdded = ws.on("member:added", (p) => { - const { member } = p as MemberAddedPayload; + const { member, workspace_name } = p as MemberAddedPayload; const myUserId = useAuthStore.getState().user?.id; if (member.user_id === myUserId) { - // I was invited to a new workspace — refresh workspace list useWorkspaceStore.getState().refreshWorkspaces(); + toast.info( + `You were invited to ${workspace_name ?? "a workspace"}`, + ); } }); diff --git a/apps/web/features/workspace/store.ts b/apps/web/features/workspace/store.ts index 529d03ed..5b3cbee6 100644 --- a/apps/web/features/workspace/store.ts +++ b/apps/web/features/workspace/store.ts @@ -96,7 +96,7 @@ export const useWorkspaceStore = create((set, get) => ({ // Clear stale data from other stores before switching useIssueStore.getState().setIssues([]); useInboxStore.getState().setItems([]); - set({ agents: [], skills: [] }); + set({ members: [], agents: [], skills: [] }); await hydrateWorkspace(workspaces, ws.id); }, diff --git a/apps/web/shared/types/events.ts b/apps/web/shared/types/events.ts index 954eac26..15991d07 100644 --- a/apps/web/shared/types/events.ts +++ b/apps/web/shared/types/events.ts @@ -121,6 +121,7 @@ export interface MemberUpdatedPayload { export interface MemberAddedPayload { member: MemberWithUser; workspace_id: string; + workspace_name?: string; } export interface MemberRemovedPayload { diff --git a/server/internal/handler/workspace.go b/server/internal/handler/workspace.go index b8560555..d8ce24f9 100644 --- a/server/internal/handler/workspace.go +++ b/server/internal/handler/workspace.go @@ -389,7 +389,11 @@ func (h *Handler) CreateMember(w http.ResponseWriter, r *http.Request) { slog.Info("member added", append(logger.RequestAttrs(r), "member_id", uuidToString(member.ID), "workspace_id", workspaceID, "email", email, "role", role)...) userID := requestUserID(r) - h.publish(protocol.EventMemberAdded, workspaceID, "member", userID, map[string]any{"member": memberWithUserResponse(member, user)}) + eventPayload := map[string]any{"member": memberWithUserResponse(member, user)} + if ws, err := h.Queries.GetWorkspace(r.Context(), parseUUID(workspaceID)); err == nil { + eventPayload["workspace_name"] = ws.Name + } + h.publish(protocol.EventMemberAdded, workspaceID, "member", userID, eventPayload) writeJSON(w, http.StatusCreated, memberWithUserResponse(member, user)) }