feat: add user preferences for online status and read receipts with real-time updates
- Introduced `showOnlineStatus` and `showReadReceipts` fields in the user schema and API. - Integrated real-time status broadcasting in `UsersService` via `EventsGateway`. - Updated repository and frontend user types to align with new fields. - Enhanced user update handling to support dynamic preference changes for online status.
This commit is contained in:
@@ -36,6 +36,8 @@ export const users = pgTable(
|
||||
// Sécurité
|
||||
twoFactorSecret: pgpEncrypted("two_factor_secret"),
|
||||
isTwoFactorEnabled: boolean("is_two_factor_enabled").notNull().default(false),
|
||||
showOnlineStatus: boolean("show_online_status").notNull().default(true),
|
||||
showReadReceipts: boolean("show_read_receipts").notNull().default(true),
|
||||
|
||||
// RGPD & Conformité
|
||||
termsVersion: varchar("terms_version", { length: 16 }), // Version des CGU acceptées
|
||||
|
||||
@@ -47,6 +47,8 @@ export class UsersRepository {
|
||||
bio: users.bio,
|
||||
status: users.status,
|
||||
isTwoFactorEnabled: users.isTwoFactorEnabled,
|
||||
showOnlineStatus: users.showOnlineStatus,
|
||||
showReadReceipts: users.showReadReceipts,
|
||||
createdAt: users.createdAt,
|
||||
updatedAt: users.updatedAt,
|
||||
})
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { forwardRef, Module } from "@nestjs/common";
|
||||
import { AuthModule } from "../auth/auth.module";
|
||||
import { MediaModule } from "../media/media.module";
|
||||
import { RealtimeModule } from "../realtime/realtime.module";
|
||||
import { S3Module } from "../s3/s3.module";
|
||||
import { UsersRepository } from "./repositories/users.repository";
|
||||
import { UsersController } from "./users.controller";
|
||||
import { UsersService } from "./users.service";
|
||||
|
||||
@Module({
|
||||
imports: [forwardRef(() => AuthModule), MediaModule, S3Module],
|
||||
imports: [
|
||||
forwardRef(() => AuthModule),
|
||||
MediaModule,
|
||||
S3Module,
|
||||
forwardRef(() => RealtimeModule),
|
||||
],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService, UsersRepository],
|
||||
exports: [UsersService, UsersRepository],
|
||||
|
||||
@@ -20,6 +20,7 @@ import { ConfigService } from "@nestjs/config";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { RbacService } from "../auth/rbac.service";
|
||||
import { MediaService } from "../media/media.service";
|
||||
import { EventsGateway } from "../realtime/events.gateway";
|
||||
import { S3Service } from "../s3/s3.service";
|
||||
import { UsersRepository } from "./repositories/users.repository";
|
||||
import { UsersService } from "./users.service";
|
||||
@@ -49,6 +50,7 @@ describe("UsersService", () => {
|
||||
|
||||
const mockRbacService = {
|
||||
getUserRoles: jest.fn(),
|
||||
assignRoleToUser: jest.fn(),
|
||||
};
|
||||
|
||||
const mockMediaService = {
|
||||
@@ -65,6 +67,11 @@ describe("UsersService", () => {
|
||||
get: jest.fn(),
|
||||
};
|
||||
|
||||
const mockEventsGateway = {
|
||||
isUserOnline: jest.fn(),
|
||||
broadcastStatus: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
@@ -77,6 +84,7 @@ describe("UsersService", () => {
|
||||
{ provide: MediaService, useValue: mockMediaService },
|
||||
{ provide: S3Service, useValue: mockS3Service },
|
||||
{ provide: ConfigService, useValue: mockConfigService },
|
||||
{ provide: EventsGateway, useValue: mockEventsGateway },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { RbacService } from "../auth/rbac.service";
|
||||
import type { IMediaService } from "../common/interfaces/media.interface";
|
||||
import type { IStorageService } from "../common/interfaces/storage.interface";
|
||||
import { MediaService } from "../media/media.service";
|
||||
import { EventsGateway } from "../realtime/events.gateway";
|
||||
import { S3Service } from "../s3/s3.service";
|
||||
import { UpdateUserDto } from "./dto/update-user.dto";
|
||||
import { UsersRepository } from "./repositories/users.repository";
|
||||
@@ -27,6 +28,8 @@ export class UsersService {
|
||||
private readonly rbacService: RbacService,
|
||||
@Inject(MediaService) private readonly mediaService: IMediaService,
|
||||
@Inject(S3Service) private readonly s3Service: IStorageService,
|
||||
@Inject(forwardRef(() => EventsGateway))
|
||||
private readonly eventsGateway: EventsGateway,
|
||||
) {}
|
||||
|
||||
private async clearUserCache(username?: string) {
|
||||
@@ -137,6 +140,9 @@ export class UsersService {
|
||||
|
||||
const { role, ...userData } = data;
|
||||
|
||||
// On récupère l'utilisateur actuel avant mise à jour pour comparer les préférences
|
||||
const oldUser = await this.usersRepository.findOne(uuid);
|
||||
|
||||
const result = await this.usersRepository.update(uuid, userData);
|
||||
|
||||
if (role) {
|
||||
@@ -145,6 +151,21 @@ export class UsersService {
|
||||
|
||||
if (result[0]) {
|
||||
await this.clearUserCache(result[0].username);
|
||||
|
||||
// Gérer le changement de préférence de statut en ligne
|
||||
if (
|
||||
data.showOnlineStatus !== undefined &&
|
||||
data.showOnlineStatus !== oldUser?.showOnlineStatus
|
||||
) {
|
||||
const isOnline = this.eventsGateway.isUserOnline(uuid);
|
||||
if (isOnline) {
|
||||
if (data.showOnlineStatus) {
|
||||
this.eventsGateway.broadcastStatus(uuid, "online");
|
||||
} else {
|
||||
this.eventsGateway.broadcastStatus(uuid, "offline");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ export interface User {
|
||||
role?: "user" | "admin" | "moderator";
|
||||
status?: "active" | "verification" | "suspended" | "pending" | "deleted";
|
||||
twoFactorEnabled?: boolean;
|
||||
showOnlineStatus?: boolean;
|
||||
showReadReceipts?: boolean;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user