fix(presence): broadcast presence_update to friends + DM members + space members
Six WS sites that previously broadcast presence_update to spaces only now use collectProfileBroadcastTargetIds (the same recipient set as user_updated): - ws/handler.ts finalizeDisconnect (offline) - ws/handler.ts auth path (online) - ws/events.ts handlePresenceUpdate (manual idle/dnd/online) - ws/events.ts handleActivityUpdate (rich activity changes) - routes/users.ts showActivity-toggle clear - routes/users.ts status PATCH Friends with no shared space + DM-only co-members now see each other's online/offline transitions live, matching user_updated semantics. Federated stub presence broadcasts (Task B3) use the same helper, so cross-instance recipients are uniform. Updates one assertion in social.federated.test.ts that asserted the old snowflake-style stub username (now realname-based per A1).
This commit is contained in:
@@ -169,9 +169,12 @@ describe('POST /api/social/requests — federated branch (happy path)', () => {
|
||||
expect(sentEvent).toBeDefined();
|
||||
expect(sentEvent![0]).toBe(CALLER_ID);
|
||||
expect(sentEvent![1].request.id).toBe(body.requestId);
|
||||
// homeUserId identifies the target; username is the canonical stub form (<homeUserId>@<host>).
|
||||
// homeUserId identifies the target; username is the realname-based stub form
|
||||
// (<lookup.username>@<host>) since resolveOrCreateReplicatedUser now uses the
|
||||
// username hint from the wire profile snapshot. Falls back to <homeUserId>@<host>
|
||||
// only when no hint is available.
|
||||
expect(sentEvent![1].request.user.homeUserId).toBe('remote-alice');
|
||||
expect(sentEvent![1].request.user.username).toBe('remote-alice@orbit.test');
|
||||
expect(sentEvent![1].request.user.username).toBe('alice@orbit.test');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -436,16 +436,14 @@ export async function userRoutes(app: FastifyInstance): Promise<void> {
|
||||
connectionManager.setUserShowActivity(request.userId, showActivity);
|
||||
if (!showActivity) {
|
||||
connectionManager.clearUserActivities(request.userId);
|
||||
const userSpaces = connectionManager.getUserSpaces(request.userId);
|
||||
const clearPayload = {
|
||||
type: 'presence_update' as const,
|
||||
userId: request.userId,
|
||||
status: connectionManager.getUserStatus(request.userId),
|
||||
activities: [] as Activity[],
|
||||
};
|
||||
for (const spaceId of userSpaces) {
|
||||
connectionManager.sendToSpace(spaceId, clearPayload, request.userId);
|
||||
}
|
||||
const clearTargets = collectProfileBroadcastTargetIds(request.userId);
|
||||
for (const uid of clearTargets) connectionManager.sendToUser(uid, clearPayload);
|
||||
connectionManager.sendToUser(request.userId, clearPayload);
|
||||
|
||||
// S2S: project the cleared-activities snapshot to all active peers.
|
||||
@@ -498,19 +496,14 @@ export async function userRoutes(app: FastifyInstance): Promise<void> {
|
||||
|
||||
// Broadcast presence update if status changed
|
||||
if (status !== undefined) {
|
||||
const userSpaces = connectionManager.getUserSpaces(sanitized.id);
|
||||
for (const spaceId of userSpaces) {
|
||||
connectionManager.sendToSpace(spaceId, {
|
||||
type: 'presence_update',
|
||||
userId: sanitized.id,
|
||||
status: status,
|
||||
}, sanitized.id);
|
||||
}
|
||||
connectionManager.sendToUser(sanitized.id, {
|
||||
type: 'presence_update',
|
||||
const statusPayload = {
|
||||
type: 'presence_update' as const,
|
||||
userId: sanitized.id,
|
||||
status: status,
|
||||
});
|
||||
};
|
||||
const statusTargets = collectProfileBroadcastTargetIds(sanitized.id);
|
||||
for (const uid of statusTargets) connectionManager.sendToUser(uid, statusPayload);
|
||||
connectionManager.sendToUser(sanitized.id, statusPayload);
|
||||
}
|
||||
|
||||
// Broadcast user_updated for profile field changes
|
||||
|
||||
Reference in New Issue
Block a user