Task #42: Tell members when someone leaves a group on their own
Original task: Post a bilingual system message into the group chat when a member leaves via POST /api/conversations/:id/leave, with real-time delivery via Socket.IO and rendering in the chat thread. Findings: - The leave handler in artifacts/api-server/src/routes/conversations.ts already inserts a `member_left` system message (and `admin_promoted` when a successor is auto-promoted) and emits via Socket.IO using insertAndEmitSystemMessage. Direct conversations and last-leaver cleanup paths are correctly skipped. - artifacts/teaboy-os/src/pages/chat.tsx renderSystemMessage already handles `member_left`, `admin_promoted`, and `adminPromotedAfterLeave`. - Bilingual locale keys exist in src/locales/en.json and ar.json under chat.system.memberLeft / adminPromoted / adminPromotedAfterLeave. Change made: - Extended the MessageWithSender.kind enum in lib/api-spec/openapi.yaml to include `member_left` and `admin_promoted` (previously only user/group_renamed/members_added/member_removed were declared). - Re-ran `pnpm --filter @workspace/api-spec run codegen` to regenerate api-zod and api-client-react schemas so the spec, runtime validation, and client types stay in sync. - Verified `pnpm -w run typecheck` passes across all packages. No behavioural code changes were necessary; the feature was already implemented end-to-end and only the API spec enum needed to catch up. Replit-Task-Id: 40fccb3a-4fc0-4ad7-a6e9-d1ac67ef7769
This commit is contained in:
@@ -269,6 +269,8 @@ export const MessageWithSenderKind = {
|
||||
group_renamed: "group_renamed",
|
||||
members_added: "members_added",
|
||||
member_removed: "member_removed",
|
||||
member_left: "member_left",
|
||||
admin_promoted: "admin_promoted",
|
||||
} as const;
|
||||
|
||||
export type MessageWithSenderMeta = { [key: string]: unknown } | null;
|
||||
|
||||
@@ -1708,7 +1708,7 @@ components:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
enum: [user, group_renamed, members_added, member_removed]
|
||||
enum: [user, group_renamed, members_added, member_removed, member_left, admin_promoted]
|
||||
meta:
|
||||
type: object
|
||||
nullable: true
|
||||
|
||||
@@ -596,6 +596,8 @@ export const ListConversationsResponseItem = zod.object({
|
||||
"group_renamed",
|
||||
"members_added",
|
||||
"member_removed",
|
||||
"member_left",
|
||||
"admin_promoted",
|
||||
]),
|
||||
meta: zod.record(zod.string(), zod.unknown()).nullish(),
|
||||
sender: zod.object({
|
||||
@@ -666,6 +668,8 @@ export const GetConversationResponse = zod.object({
|
||||
"group_renamed",
|
||||
"members_added",
|
||||
"member_removed",
|
||||
"member_left",
|
||||
"admin_promoted",
|
||||
]),
|
||||
meta: zod.record(zod.string(), zod.unknown()).nullish(),
|
||||
sender: zod.object({
|
||||
@@ -728,6 +732,8 @@ export const UpdateConversationResponse = zod.object({
|
||||
"group_renamed",
|
||||
"members_added",
|
||||
"member_removed",
|
||||
"member_left",
|
||||
"admin_promoted",
|
||||
]),
|
||||
meta: zod.record(zod.string(), zod.unknown()).nullish(),
|
||||
sender: zod.object({
|
||||
@@ -788,6 +794,8 @@ export const AddConversationParticipantsResponse = zod.object({
|
||||
"group_renamed",
|
||||
"members_added",
|
||||
"member_removed",
|
||||
"member_left",
|
||||
"admin_promoted",
|
||||
]),
|
||||
meta: zod.record(zod.string(), zod.unknown()).nullish(),
|
||||
sender: zod.object({
|
||||
@@ -845,6 +853,8 @@ export const RemoveConversationParticipantResponse = zod.object({
|
||||
"group_renamed",
|
||||
"members_added",
|
||||
"member_removed",
|
||||
"member_left",
|
||||
"admin_promoted",
|
||||
]),
|
||||
meta: zod.record(zod.string(), zod.unknown()).nullish(),
|
||||
sender: zod.object({
|
||||
@@ -906,6 +916,8 @@ export const UpdateConversationStateResponse = zod.object({
|
||||
"group_renamed",
|
||||
"members_added",
|
||||
"member_removed",
|
||||
"member_left",
|
||||
"admin_promoted",
|
||||
]),
|
||||
meta: zod.record(zod.string(), zod.unknown()).nullish(),
|
||||
sender: zod.object({
|
||||
@@ -950,7 +962,14 @@ export const ListMessagesResponseItem = zod.object({
|
||||
conversationId: zod.number(),
|
||||
senderId: zod.number(),
|
||||
content: zod.string(),
|
||||
kind: zod.enum(["user", "group_renamed", "members_added", "member_removed"]),
|
||||
kind: zod.enum([
|
||||
"user",
|
||||
"group_renamed",
|
||||
"members_added",
|
||||
"member_removed",
|
||||
"member_left",
|
||||
"admin_promoted",
|
||||
]),
|
||||
meta: zod.record(zod.string(), zod.unknown()).nullish(),
|
||||
sender: zod.object({
|
||||
id: zod.number(),
|
||||
|
||||
Reference in New Issue
Block a user