diff --git a/.env.example b/.env.example index 3967f84..06640ca 100644 --- a/.env.example +++ b/.env.example @@ -10,7 +10,7 @@ PROJECT_NAME=jama # Image version to run (set by build.sh, or use 'latest') -JAMA_VERSION=0.9.28 +JAMA_VERSION=0.9.29 # App port — the host port Docker maps to the container PORT=3000 diff --git a/backend/package.json b/backend/package.json index 5477107..d102e21 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "jama-backend", - "version": "0.9.28", + "version": "0.9.29", "description": "TeamChat backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/routes/usergroups.js b/backend/src/routes/usergroups.js index 31bdce1..0e965a2 100644 --- a/backend/src/routes/usergroups.js +++ b/backend/src/routes/usergroups.js @@ -18,11 +18,17 @@ function postSysMsg(db, groupId, userId, content) { if (msg) { msg.reactions = []; io.to(`group:${groupId}`).emit('message:new', msg); } } -function addUserToDmGroup(db, dmGroupId, userId, actorId) { +// Add user silently (no system message) — used during initial group creation +function addUserToDmGroupSilent(db, dmGroupId, userId) { db.prepare("INSERT OR IGNORE INTO group_members (group_id, user_id, joined_at) VALUES (?, ?, datetime('now'))").run(dmGroupId, userId); io.in(`user:${userId}`).socketsJoin(`group:${dmGroupId}`); const dmGroup = db.prepare('SELECT * FROM groups WHERE id = ?').get(dmGroupId); io.to(`user:${userId}`).emit('group:new', { group: dmGroup }); +} + +// Add user with system message — used when adding to existing group +function addUserToDmGroup(db, dmGroupId, userId, actorId) { + addUserToDmGroupSilent(db, dmGroupId, userId); const u = db.prepare('SELECT name, display_name FROM users WHERE id = ?').get(userId); postSysMsg(db, dmGroupId, actorId, `${u?.display_name || u?.name || 'A user'} has joined the conversation.`); } @@ -90,11 +96,9 @@ router.post('/multigroup', authMiddleware, adminMiddleware, (req, res) => { for (const uid of uids) { if (!addedUsers.has(uid)) { addedUsers.add(uid); - addUserToDmGroup(db, dmGroupId, uid, req.user.id); + addUserToDmGroupSilent(db, dmGroupId, uid); } } - const ug = db.prepare('SELECT name FROM user_groups WHERE id = ?').get(ugId); - if (ug) postSysMsg(db, dmGroupId, req.user.id, `Group "${ug.name}" has been added to this conversation.`); } const dm = db.prepare('SELECT * FROM multi_group_dms WHERE id = ?').get(mgId); @@ -199,7 +203,7 @@ router.post('/', authMiddleware, adminMiddleware, (req, res) => { const validIds = Array.isArray(memberIds) ? memberIds.map(Number).filter(Boolean) : []; for (const uid of validIds) { db.prepare("INSERT OR IGNORE INTO user_group_members (user_group_id, user_id) VALUES (?, ?)").run(ugId, uid); - addUserToDmGroup(db, dmGroupId, uid, req.user.id); + addUserToDmGroupSilent(db, dmGroupId, uid); } const group = db.prepare('SELECT * FROM user_groups WHERE id = ?').get(ugId); res.json({ group }); diff --git a/build.sh b/build.sh index b363269..3c55cef 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.9.28}" +VERSION="${1:-0.9.29}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="jama" diff --git a/frontend/package.json b/frontend/package.json index 8534ccf..ad5d9f8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jama-frontend", - "version": "0.9.28", + "version": "0.9.29", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/components/GroupManagerModal.jsx b/frontend/src/components/GroupManagerModal.jsx index 3e346ca..9359d90 100644 --- a/frontend/src/components/GroupManagerModal.jsx +++ b/frontend/src/components/GroupManagerModal.jsx @@ -191,7 +191,7 @@ function AllGroupsTab({ allUsers, onRefresh }) { } // ── Direct Messages tab ─────────────────────────────────────────────────────── -function DirectMessagesTab({ allUserGroups, onRefresh }) { +function DirectMessagesTab({ allUserGroups, onRefresh, refreshKey }) { const toast = useToast(); const [dms, setDms] = useState([]); const [selected, setSelected] = useState(null); @@ -203,8 +203,9 @@ function DirectMessagesTab({ allUserGroups, onRefresh }) { const [showDelete, setShowDelete] = useState(false); const load = useCallback(() => - api.getMultiGroupDms().then(({ dms }) => setDms(dms)).catch(() => {}), []); - useEffect(() => { load(); }, [load]); + api.getMultiGroupDms().then(({ dms }) => setDms(dms || [])).catch(e => console.error('multigroup load error:', e)), []); + // Reload whenever parent refreshes (e.g. after user group changes that affect membership) + useEffect(() => { load(); }, [load, refreshKey]); const clearSelection = () => { setSelected(null); setDmName(''); setGroupIds(new Set()); setSavedGroupIds(new Set()); setShowDelete(false); @@ -355,7 +356,7 @@ export default function GroupManagerModal({ onClose }) {