Dolls with friends
This commit is contained in:
@@ -190,7 +190,9 @@ describe('StateGateway', () => {
|
||||
data: { user: { keycloakSub: 'test-sub' } },
|
||||
};
|
||||
|
||||
await gateway.handleDisconnect(mockClient as unknown as AuthenticatedSocket);
|
||||
await gateway.handleDisconnect(
|
||||
mockClient as unknown as AuthenticatedSocket,
|
||||
);
|
||||
|
||||
expect(mockLoggerLog).toHaveBeenCalledWith(
|
||||
`Client id: ${mockClient.id} disconnected (user: test-sub)`,
|
||||
@@ -203,7 +205,9 @@ describe('StateGateway', () => {
|
||||
data: {},
|
||||
};
|
||||
|
||||
await gateway.handleDisconnect(mockClient as unknown as AuthenticatedSocket);
|
||||
await gateway.handleDisconnect(
|
||||
mockClient as unknown as AuthenticatedSocket,
|
||||
);
|
||||
|
||||
expect(mockLoggerLog).toHaveBeenCalledWith(
|
||||
`Client id: ${mockClient.id} disconnected (user: unknown)`,
|
||||
@@ -213,22 +217,28 @@ describe('StateGateway', () => {
|
||||
it('should remove socket if it matches', async () => {
|
||||
const mockClient: MockSocket = {
|
||||
id: 'client1',
|
||||
data: {
|
||||
data: {
|
||||
user: { keycloakSub: 'test-sub' },
|
||||
userId: 'user-id',
|
||||
friends: new Set(['friend-1']),
|
||||
},
|
||||
};
|
||||
|
||||
(mockUserSocketService.getSocket as jest.Mock).mockResolvedValue('client1');
|
||||
(mockUserSocketService.getSocket as jest.Mock).mockResolvedValue(
|
||||
'client1',
|
||||
);
|
||||
(mockUserSocketService.getFriendsSockets as jest.Mock).mockResolvedValue([
|
||||
{ userId: 'friend-1', socketId: 'friend-socket-id' }
|
||||
{ userId: 'friend-1', socketId: 'friend-socket-id' },
|
||||
]);
|
||||
|
||||
await gateway.handleDisconnect(mockClient as unknown as AuthenticatedSocket);
|
||||
await gateway.handleDisconnect(
|
||||
mockClient as unknown as AuthenticatedSocket,
|
||||
);
|
||||
|
||||
expect(mockUserSocketService.getSocket).toHaveBeenCalledWith('user-id');
|
||||
expect(mockUserSocketService.removeSocket).toHaveBeenCalledWith('user-id');
|
||||
expect(mockUserSocketService.removeSocket).toHaveBeenCalledWith(
|
||||
'user-id',
|
||||
);
|
||||
expect(mockServer.to).toHaveBeenCalledWith('friend-socket-id');
|
||||
});
|
||||
});
|
||||
@@ -277,7 +287,9 @@ describe('StateGateway', () => {
|
||||
};
|
||||
|
||||
// Mock getFriendsSockets to return empty array
|
||||
(mockUserSocketService.getFriendsSockets as jest.Mock).mockResolvedValue([]);
|
||||
(mockUserSocketService.getFriendsSockets as jest.Mock).mockResolvedValue(
|
||||
[],
|
||||
);
|
||||
|
||||
const data: CursorPositionDto = { x: 100, y: 200 };
|
||||
|
||||
|
||||
@@ -26,6 +26,13 @@ import type {
|
||||
UnfriendedEvent,
|
||||
} from '../../friends/events/friend.events';
|
||||
|
||||
import { DollEvents } from '../../dolls/events/doll.events';
|
||||
import type {
|
||||
DollCreatedEvent,
|
||||
DollUpdatedEvent,
|
||||
DollDeletedEvent,
|
||||
} from '../../dolls/events/doll.events';
|
||||
|
||||
const WS_EVENT = {
|
||||
CURSOR_REPORT_POSITION: 'cursor-report-position',
|
||||
FRIEND_REQUEST_RECEIVED: 'friend-request-received',
|
||||
@@ -34,6 +41,9 @@ const WS_EVENT = {
|
||||
UNFRIENDED: 'unfriended',
|
||||
FRIEND_CURSOR_POSITION: 'friend-cursor-position',
|
||||
FRIEND_DISCONNECTED: 'friend-disconnected',
|
||||
FRIEND_DOLL_CREATED: 'friend-doll-created',
|
||||
FRIEND_DOLL_UPDATED: 'friend-doll-updated',
|
||||
FRIEND_DOLL_DELETED: 'friend-doll-deleted',
|
||||
} as const;
|
||||
|
||||
@WebSocketGateway({
|
||||
@@ -135,8 +145,9 @@ export class StateGateway
|
||||
const friends = client.data.friends;
|
||||
if (friends) {
|
||||
const friendIds = Array.from(friends);
|
||||
const friendSockets = await this.userSocketService.getFriendsSockets(friendIds);
|
||||
|
||||
const friendSockets =
|
||||
await this.userSocketService.getFriendsSockets(friendIds);
|
||||
|
||||
for (const { socketId } of friendSockets) {
|
||||
this.io.to(socketId).emit(WS_EVENT.FRIEND_DISCONNECTED, {
|
||||
userId: userId,
|
||||
@@ -188,16 +199,15 @@ export class StateGateway
|
||||
const friends = client.data.friends;
|
||||
if (friends) {
|
||||
const friendIds = Array.from(friends);
|
||||
const friendSockets = await this.userSocketService.getFriendsSockets(friendIds);
|
||||
const friendSockets =
|
||||
await this.userSocketService.getFriendsSockets(friendIds);
|
||||
|
||||
for (const { socketId } of friendSockets) {
|
||||
const payload = {
|
||||
userId: currentUserId,
|
||||
position: data,
|
||||
};
|
||||
this.io
|
||||
.to(socketId)
|
||||
.emit(WS_EVENT.FRIEND_CURSOR_POSITION, payload);
|
||||
this.io.to(socketId).emit(WS_EVENT.FRIEND_CURSOR_POSITION, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,7 +264,9 @@ export class StateGateway
|
||||
}
|
||||
|
||||
// 2. Update cache for the user who accepted the request (friendRequest.receiverId)
|
||||
const receiverSocketId = await this.userSocketService.getSocket(friendRequest.receiverId);
|
||||
const receiverSocketId = await this.userSocketService.getSocket(
|
||||
friendRequest.receiverId,
|
||||
);
|
||||
if (receiverSocketId) {
|
||||
const receiverSocket = this.io.sockets.sockets.get(
|
||||
receiverSocketId,
|
||||
@@ -318,4 +330,61 @@ export class StateGateway
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OnEvent(DollEvents.DOLL_CREATED)
|
||||
async handleDollCreated(payload: DollCreatedEvent) {
|
||||
const { userId, doll } = payload;
|
||||
const friendSockets = await this.userSocketService.getFriendsSockets([
|
||||
userId,
|
||||
]);
|
||||
|
||||
for (const { socketId } of friendSockets) {
|
||||
this.io.to(socketId).emit(WS_EVENT.FRIEND_DOLL_CREATED, {
|
||||
friendId: userId,
|
||||
doll: {
|
||||
id: doll.id,
|
||||
name: doll.name,
|
||||
configuration: doll.configuration,
|
||||
createdAt: doll.createdAt,
|
||||
updatedAt: doll.updatedAt,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@OnEvent(DollEvents.DOLL_UPDATED)
|
||||
async handleDollUpdated(payload: DollUpdatedEvent) {
|
||||
const { userId, doll } = payload;
|
||||
const friendSockets = await this.userSocketService.getFriendsSockets([
|
||||
userId,
|
||||
]);
|
||||
|
||||
for (const { socketId } of friendSockets) {
|
||||
this.io.to(socketId).emit(WS_EVENT.FRIEND_DOLL_UPDATED, {
|
||||
friendId: userId,
|
||||
doll: {
|
||||
id: doll.id,
|
||||
name: doll.name,
|
||||
configuration: doll.configuration,
|
||||
createdAt: doll.createdAt,
|
||||
updatedAt: doll.updatedAt,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@OnEvent(DollEvents.DOLL_DELETED)
|
||||
async handleDollDeleted(payload: DollDeletedEvent) {
|
||||
const { userId, dollId } = payload;
|
||||
const friendSockets = await this.userSocketService.getFriendsSockets([
|
||||
userId,
|
||||
]);
|
||||
|
||||
for (const { socketId } of friendSockets) {
|
||||
this.io.to(socketId).emit(WS_EVENT.FRIEND_DOLL_DELETED, {
|
||||
friendId: userId,
|
||||
dollId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,9 @@ export class UserSocketService {
|
||||
return !!socketId;
|
||||
}
|
||||
|
||||
async getFriendsSockets(friendIds: string[]): Promise<{ userId: string; socketId: string }[]> {
|
||||
async getFriendsSockets(
|
||||
friendIds: string[],
|
||||
): Promise<{ userId: string; socketId: string }[]> {
|
||||
if (friendIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
@@ -84,7 +86,7 @@ export class UserSocketService {
|
||||
const results = await pipeline.exec();
|
||||
|
||||
const sockets: { userId: string; socketId: string }[] = [];
|
||||
|
||||
|
||||
if (results) {
|
||||
results.forEach((result, index) => {
|
||||
const [err, socketId] = result;
|
||||
@@ -95,7 +97,10 @@ export class UserSocketService {
|
||||
}
|
||||
return sockets;
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to batch get friend sockets from Redis', error);
|
||||
this.logger.error(
|
||||
'Failed to batch get friend sockets from Redis',
|
||||
error,
|
||||
);
|
||||
// Fallback to local implementation
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user