feat: integrate user preferences for online status in WebSocket gateway
- Added `UsersService` to manage user preferences in `EventsGateway`. - Enhanced online/offline broadcasting to respect user `showOnlineStatus` preference. - Updated `handleTyping` and `check_status` to verify user preferences before emitting events. - Abstracted status broadcasting logic into `broadcastStatus`.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { JwtService } from "../crypto/services/jwt.service";
|
||||
import { UsersService } from "../users/users.service";
|
||||
import { EventsGateway } from "./events.gateway";
|
||||
|
||||
describe("EventsGateway", () => {
|
||||
@@ -15,12 +16,17 @@ describe("EventsGateway", () => {
|
||||
get: jest.fn().mockReturnValue("secret-password-32-chars-long-!!!"),
|
||||
};
|
||||
|
||||
const mockUsersService = {
|
||||
findOne: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
EventsGateway,
|
||||
{ provide: JwtService, useValue: mockJwtService },
|
||||
{ provide: ConfigService, useValue: mockConfigService },
|
||||
{ provide: UsersService, useValue: mockUsersService },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { forwardRef, Inject, Logger } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import {
|
||||
ConnectedSocket,
|
||||
@@ -14,6 +14,7 @@ import { getIronSession } from "iron-session";
|
||||
import { Server, Socket } from "socket.io";
|
||||
import { getSessionOptions, SessionData } from "../auth/session.config";
|
||||
import { JwtService } from "../crypto/services/jwt.service";
|
||||
import { UsersService } from "../users/users.service";
|
||||
|
||||
@WebSocketGateway({
|
||||
transports: ["websocket"],
|
||||
@@ -63,6 +64,8 @@ export class EventsGateway
|
||||
constructor(
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly configService: ConfigService,
|
||||
@Inject(forwardRef(() => UsersService))
|
||||
private readonly usersService: UsersService,
|
||||
) {}
|
||||
|
||||
afterInit(_server: Server) {
|
||||
@@ -106,9 +109,15 @@ export class EventsGateway
|
||||
|
||||
// Gérer le statut en ligne
|
||||
const userId = payload.sub as string;
|
||||
|
||||
if (!this.onlineUsers.has(userId)) {
|
||||
this.onlineUsers.set(userId, new Set());
|
||||
this.server.emit("user_status", { userId, status: "online" });
|
||||
|
||||
// Vérifier les préférences de l'utilisateur
|
||||
const user = await this.usersService.findOne(userId);
|
||||
if (user?.showOnlineStatus) {
|
||||
this.broadcastStatus(userId, "online");
|
||||
}
|
||||
}
|
||||
this.onlineUsers.get(userId)?.add(client.id);
|
||||
|
||||
@@ -119,19 +128,31 @@ export class EventsGateway
|
||||
}
|
||||
}
|
||||
|
||||
handleDisconnect(client: Socket) {
|
||||
async handleDisconnect(client: Socket) {
|
||||
const userId = client.data.user?.sub;
|
||||
if (userId && this.onlineUsers.has(userId)) {
|
||||
const sockets = this.onlineUsers.get(userId);
|
||||
sockets?.delete(client.id);
|
||||
if (sockets?.size === 0) {
|
||||
this.onlineUsers.delete(userId);
|
||||
this.server.emit("user_status", { userId, status: "offline" });
|
||||
|
||||
const user = await this.usersService.findOne(userId);
|
||||
if (user?.showOnlineStatus) {
|
||||
this.broadcastStatus(userId, "offline");
|
||||
}
|
||||
}
|
||||
}
|
||||
this.logger.log(`Client disconnected: ${client.id}`);
|
||||
}
|
||||
|
||||
broadcastStatus(userId: string, status: "online" | "offline") {
|
||||
this.server.emit("user_status", { userId, status });
|
||||
}
|
||||
|
||||
isUserOnline(userId: string): boolean {
|
||||
return this.onlineUsers.has(userId);
|
||||
}
|
||||
|
||||
@SubscribeMessage("join_content")
|
||||
handleJoinContent(
|
||||
@ConnectedSocket() client: Socket,
|
||||
@@ -151,13 +172,20 @@ export class EventsGateway
|
||||
}
|
||||
|
||||
@SubscribeMessage("typing")
|
||||
handleTyping(
|
||||
async handleTyping(
|
||||
@ConnectedSocket() client: Socket,
|
||||
@MessageBody() data: { recipientId: string; isTyping: boolean },
|
||||
) {
|
||||
const userId = client.data.user?.sub;
|
||||
if (!userId) return;
|
||||
|
||||
// Optionnel: vérifier si l'utilisateur autorise le statut en ligne avant d'émettre "typing"
|
||||
// ou si on considère que typing est une interaction directe qui outrepasse le statut.
|
||||
// Instagram affiche "Typing..." même si le statut en ligne est désactivé si on est dans le chat.
|
||||
// Mais par souci de cohérence avec "showOnlineStatus", on peut le vérifier.
|
||||
const user = await this.usersService.findOne(userId);
|
||||
if (!user?.showOnlineStatus) return;
|
||||
|
||||
this.server.to(`user:${data.recipientId}`).emit("user_typing", {
|
||||
userId,
|
||||
isTyping: data.isTyping,
|
||||
@@ -165,13 +193,19 @@ export class EventsGateway
|
||||
}
|
||||
|
||||
@SubscribeMessage("check_status")
|
||||
handleCheckStatus(
|
||||
async handleCheckStatus(
|
||||
@ConnectedSocket() _client: Socket,
|
||||
@MessageBody() userId: string,
|
||||
) {
|
||||
const isOnline = this.onlineUsers.has(userId);
|
||||
if (!isOnline) return { userId, status: "offline" };
|
||||
|
||||
const user = await this.usersService.findOne(userId);
|
||||
if (!user?.showOnlineStatus) return { userId, status: "offline" };
|
||||
|
||||
return {
|
||||
userId,
|
||||
status: this.onlineUsers.has(userId) ? "online" : "offline",
|
||||
status: "online",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user