diff --git a/backend/package.json b/backend/package.json
index 5ecb7b7..68bbcda 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "jama-backend",
- "version": "0.11.18",
+ "version": "0.11.19",
"description": "TeamChat backend server",
"main": "src/index.js",
"scripts": {
diff --git a/backend/src/routes/usergroups.js b/backend/src/routes/usergroups.js
index 20d6509..ba4106e 100644
--- a/backend/src/routes/usergroups.js
+++ b/backend/src/routes/usergroups.js
@@ -256,18 +256,34 @@ router.patch('/:id', authMiddleware, teamManagerMiddleware, async (req, res) =>
for (const uid of newIds) {
if (!currentSet.has(uid)) {
await exec(req.schema, 'INSERT INTO user_group_members (user_group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [ug.id, uid]);
- await addUser(req.schema, ug.dm_group_id, uid, req.user.id);
+ await addUserSilent(req.schema, ug.dm_group_id, uid);
addedUids.push(uid);
}
}
for (const uid of currentSet) {
if (!newIds.has(uid)) {
await exec(req.schema, 'DELETE FROM user_group_members WHERE user_group_id=$1 AND user_id=$2', [ug.id, uid]);
- await removeUser(req.schema, ug.dm_group_id, uid, req.user.id);
+ await exec(req.schema, 'DELETE FROM group_members WHERE group_id=$1 AND user_id=$2', [ug.dm_group_id, uid]);
+ io.in(R(schema,'user',uid)).socketsLeave(R(schema,'group',ug.dm_group_id));
+ io.to(R(schema,'user',uid)).emit('group:deleted', { groupId: ug.dm_group_id });
removedUids.push(uid);
}
}
+ // Notification rule: single user → named message; multiple users → one generic message
+ if (addedUids.length === 1) {
+ const u = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [addedUids[0]]);
+ await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${u?.display_name||u?.name||'A user'} has joined the conversation.`);
+ } else if (addedUids.length > 1) {
+ await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${addedUids.length} new members have joined the conversation.`);
+ }
+ if (removedUids.length === 1) {
+ const u = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [removedUids[0]]);
+ await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${u?.display_name||u?.name||'A user'} has been removed from the conversation.`);
+ } else if (removedUids.length > 1) {
+ await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${removedUids.length} members have been removed from the conversation.`);
+ }
+
// Propagate to multi-group DMs
const mgDms = await query(req.schema, `
SELECT mgd.id, mgd.dm_group_id FROM multi_group_dm_members mgdm
@@ -287,8 +303,18 @@ router.patch('/:id', authMiddleware, teamManagerMiddleware, async (req, res) =>
io.to(R(schema,'user',uid)).emit('group:deleted', { groupId: mg.dm_group_id });
}
}
- if (addedUids.length > 0) await postSysMsg(req.schema, mg.dm_group_id, req.user.id, `Members were added to group "${ug.name}" and have joined this conversation.`);
- if (removedUids.length > 0) await postSysMsg(req.schema, mg.dm_group_id, req.user.id, `Members were removed from group "${ug.name}" and have left this conversation.`);
+ if (addedUids.length === 1) {
+ const u = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [addedUids[0]]);
+ await postSysMsg(req.schema, mg.dm_group_id, req.user.id, `${u?.display_name||u?.name||'A user'} has joined this conversation.`);
+ } else if (addedUids.length > 1) {
+ await postSysMsg(req.schema, mg.dm_group_id, req.user.id, `${addedUids.length} new members have joined this conversation.`);
+ }
+ if (removedUids.length === 1) {
+ const u = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [removedUids[0]]);
+ await postSysMsg(req.schema, mg.dm_group_id, req.user.id, `${u?.display_name||u?.name||'A user'} has been removed from this conversation.`);
+ } else if (removedUids.length > 1) {
+ await postSysMsg(req.schema, mg.dm_group_id, req.user.id, `${removedUids.length} members have been removed from this conversation.`);
+ }
}
}
diff --git a/build.sh b/build.sh
index 40119d3..cbb52f6 100644
--- a/build.sh
+++ b/build.sh
@@ -13,7 +13,7 @@
# ─────────────────────────────────────────────────────────────
set -euo pipefail
-VERSION="${1:-0.11.18}"
+VERSION="${1:-0.11.19}"
ACTION="${2:-}"
REGISTRY="${REGISTRY:-}"
IMAGE_NAME="jama"
diff --git a/frontend/package.json b/frontend/package.json
index d9b7db3..ef9253c 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "jama-frontend",
- "version": "0.11.18",
+ "version": "0.11.19",
"private": true,
"scripts": {
"dev": "vite",
diff --git a/frontend/src/components/ChatWindow.jsx b/frontend/src/components/ChatWindow.jsx
index 372d7d1..ea947b1 100644
--- a/frontend/src/components/ChatWindow.jsx
+++ b/frontend/src/components/ChatWindow.jsx
@@ -253,7 +253,10 @@ export default function ChatWindow({ group, onBack, onGroupUpdated, onDirectMess
{group.is_readonly ? read-only : null}
{isDirect &&
Private message
}
- {!isDirect && group.type === 'private' && Private group
}
+ {!isDirect && group.type === 'public' && Public message
}
+ {!isDirect && group.type === 'private' && group.is_managed && !group.is_multi_group && Private user group
}
+ {!isDirect && group.type === 'private' && group.is_managed && group.is_multi_group && Private group
}
+ {!isDirect && group.type === 'private' && !group.is_managed && Private group
}