priviate group avatars update

This commit is contained in:
2026-03-28 12:02:57 -04:00
parent d7790bb7ef
commit eb3e45d88f
3 changed files with 43 additions and 6 deletions

View File

@@ -0,0 +1,5 @@
-- Migration 012: Add composite_members to groups for private group avatar composites
-- Stores up to 4 member previews (id, name, avatar) as a JSONB snapshot.
-- Only set for non-managed, non-direct private groups with 3+ members.
-- Updated only when a member is added and pre-add membership count was ≤3.
ALTER TABLE groups ADD COLUMN IF NOT EXISTS composite_members JSONB;

View File

@@ -13,6 +13,21 @@ function deleteImageFile(imageUrl) {
// Schema-aware room name helper
const R = (schema, type, id) => `${schema}:${type}:${id}`;
// Compute and store composite_members for a non-managed private group.
// Captures up to 4 current members (excluding deleted users), ordered by name.
async function computeAndStoreComposite(schema, groupId) {
const members = await query(schema,
`SELECT u.id, u.name, u.avatar FROM group_members gm
JOIN users u ON gm.user_id = u.id
WHERE gm.group_id = $1 AND u.name != 'Deleted User'
ORDER BY u.name LIMIT 4`,
[groupId]
);
await exec(schema, 'UPDATE groups SET composite_members=$1 WHERE id=$2',
[JSON.stringify(members), groupId]
);
}
module.exports = (io) => {
async function emitGroupNew(schema, io, groupId) {
@@ -202,6 +217,11 @@ router.post('/', authMiddleware, async (req, res) => {
await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [groupId, uid]);
}
}
// Generate composite avatar for non-managed private groups with 3+ members
const totalCount = await queryOne(req.schema, 'SELECT COUNT(*) AS cnt FROM group_members WHERE group_id=$1', [groupId]);
if (parseInt(totalCount.cnt) >= 3) {
await computeAndStoreComposite(req.schema, groupId);
}
}
await emitGroupNew(req.schema, io, groupId);
res.json({ group: await queryOne(req.schema, 'SELECT * FROM groups WHERE id=$1', [groupId]) });
@@ -246,6 +266,8 @@ router.post('/:id/members', authMiddleware, async (req, res) => {
if (group.owner_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'Only owner can add members' });
const targetUser = await queryOne(req.schema, 'SELECT is_default_admin FROM users WHERE id=$1', [userId]);
if (targetUser?.is_default_admin) return res.status(400).json({ error: 'Default admin cannot be added to private groups' });
// Capture pre-add count to decide if composite should regenerate
const preAddCount = await queryOne(req.schema, 'SELECT COUNT(*) AS cnt FROM group_members WHERE group_id=$1', [group.id]);
await exec(req.schema, 'INSERT INTO group_members (group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [group.id, userId]);
const addedUser = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [userId]);
const addedName = addedUser?.display_name || addedUser?.name || 'Unknown';
@@ -259,6 +281,14 @@ router.post('/:id/members', authMiddleware, async (req, res) => {
);
sysMsg.reactions = [];
io.to(R(req.schema,'group',group.id)).emit('message:new', sysMsg);
// Regenerate composite if pre-add count was ≤3 and group is non-managed private
if (!group.is_managed && !group.is_direct && parseInt(preAddCount.cnt) <= 3) {
const newTotal = parseInt(preAddCount.cnt) + 1;
if (newTotal >= 3) {
await computeAndStoreComposite(req.schema, group.id);
}
await emitGroupUpdated(req.schema, io, group.id);
}
io.in(R(req.schema,'user',userId)).socketsJoin(R(req.schema,'group',group.id));
io.to(R(req.schema,'user',userId)).emit('group:new', { group });
res.json({ success: true });