feat: real-time user profile updates and propagate avatarColor to all Avatar callsites

Broadcast user_updated events over WebSocket when profile fields change,
updating members, DM participants, friends, and cached messages in real time.
Widen useVoiceParticipantMeta to return the full user object and add a
standalone avatarColor prop to Avatar so all ~16 callsites now resolve
the user's chosen gradient color instead of falling back to hash-based colors.
This commit is contained in:
Jannis Braun
2026-03-11 01:12:12 +01:00
parent 9255683d8c
commit 3790386a5f
16 changed files with 131 additions and 12 deletions
+44
View File
@@ -180,6 +180,50 @@ export async function userRoutes(app: FastifyInstance): Promise<void> {
});
}
// Broadcast user_updated for profile field changes
const profileFields = ['displayName', 'avatar', 'banner', 'accentColor', 'avatarColor', 'bio', 'customStatus'];
const hasProfileChange = profileFields.some(f => f in updateData);
if (hasProfileChange) {
const userUpdatedEvent = { type: 'user_updated' as const, user: sanitized };
const targetUserIds = new Set<string>();
// 1. Collect online users who share a space
const userSpaces = connectionManager.getUserSpaces(sanitized.id);
for (const [uid, spaceIds] of connectionManager.getUserSpaceEntries()) {
for (const spaceId of userSpaces) {
if (spaceIds.has(spaceId)) { targetUserIds.add(uid); break; }
}
}
// 2. Collect DM channel co-members
const dmMemberships = db.select().from(schema.dmMembers)
.where(eq(schema.dmMembers.userId, sanitized.id)).all();
for (const dm of dmMemberships) {
const coMembers = db.select().from(schema.dmMembers)
.where(eq(schema.dmMembers.dmChannelId, dm.dmChannelId)).all();
for (const m of coMembers) targetUserIds.add(m.userId);
}
// 3. Collect friends
const friendRows = db.select().from(schema.friends)
.where(or(
eq(schema.friends.userId, sanitized.id),
eq(schema.friends.friendId, sanitized.id),
)).all();
for (const fr of friendRows) {
targetUserIds.add(fr.userId === sanitized.id ? fr.friendId : fr.userId);
}
// 4. Include self (for other tabs/connections)
targetUserIds.add(sanitized.id);
// Send deduplicated — each user gets the event exactly once
for (const uid of targetUserIds) {
connectionManager.sendToUser(uid, userUpdatedEvent);
}
}
return reply.code(200).send(sanitized);
});