diff --git a/src/app/services/Repositorys/chat/data-source/room/room-remote-data-source.service.ts b/src/app/services/Repositorys/chat/data-source/room/room-remote-data-source.service.ts index 8c473a11f..936724d89 100644 --- a/src/app/services/Repositorys/chat/data-source/room/room-remote-data-source.service.ts +++ b/src/app/services/Repositorys/chat/data-source/room/room-remote-data-source.service.ts @@ -7,6 +7,9 @@ import { RoomOutPutDTO } from '../../dto/room/roomOutputDTO'; import { AddMemberToRoomInputDTO, AddMemberToRoomInputDTOSchema } from '../../dto/room/addMemberToRoomInputDto'; import { ValidateSchema } from 'src/app/services/decorators/validate-schema.decorator'; import { DataSourceReturn } from '../../../type'; +import { RoomByIdInputDTO, RoomByIdInputDTOSchema } from '../../dto/room/roomByIdInputDTO'; +import { RoomByIdOutputDTO, RoomByIdOutputDTOSchema } from '../../dto/room/roomByIdOutputDTO'; +import { APIReturn } from 'src/app/services/decorators/api-validate-schema.decorator'; @Injectable({ providedIn: 'root' @@ -27,8 +30,10 @@ export class RoomRemoteDataSourceService { return await this.httpService.get(`${this.baseUrl}/Room`); } - async getRoom(id: string): Promise> { - return await this.httpService.get(`${this.baseUrl}/Room/${id}`); + @ValidateSchema(RoomByIdInputDTOSchema) + @APIReturn(RoomByIdOutputDTOSchema) + async getRoom(id: RoomByIdInputDTO): DataSourceReturn { + return await this.httpService.get(`${this.baseUrl}/Room/${id}`); } async updateRoom(id: string, room: any): Promise> { diff --git a/src/app/services/Repositorys/chat/data-source/room/rooom-local-data-source.service.ts b/src/app/services/Repositorys/chat/data-source/room/rooom-local-data-source.service.ts index bd1d5dab4..a9249eb78 100644 --- a/src/app/services/Repositorys/chat/data-source/room/rooom-local-data-source.service.ts +++ b/src/app/services/Repositorys/chat/data-source/room/rooom-local-data-source.service.ts @@ -1,12 +1,10 @@ import { Injectable } from '@angular/core'; -import { AddMemberToRoomInputDTO } from '../../dto/room/addMemberToRoomInputDto'; import { RoomListItemOutPutDTO, RoomListOutPutDTO } from '../../dto/room/roomListOutputDTO'; import { Dexie, EntityTable, liveQuery } from 'Dexie'; import { err, ok } from 'neverthrow'; import { Observable } from 'rxjs'; -import { RoomOutPutDTO } from '../../dto/room/roomOutputDTO'; import { z } from 'zod'; - +import { UserList } from '../../../contacts/data-source/contacts-data-source.service'; const tableSchema = z.object({ @@ -17,20 +15,34 @@ const tableSchema = z.object({ expirationDate: z.any(), roomType: z.any() }) + +const TableMemberListSchema = z.object({ + $roomIdUserId: z.string().optional(), + id: z.string(), + roomId: z.string(), + user: z.object({ + wxUserId: z.number(), + wxFullName: z.string(), + wxeMail: z.string(), + userPhoto: z.string().nullable(), + }), + joinAt: z.string(), +}) + export type TableRoom = z.infer +export type TableMemberList = z.infer // Database declaration (move this to its own module also) export const roomDataSource = new Dexie('FriendDatabase') as Dexie & { room: EntityTable; + memberList: EntityTable; }; - - roomDataSource.version(1).stores({ - room: 'id, createdBy, roomName, roomType, expirationDate' + room: 'id, createdBy, roomName, roomType, expirationDate', + memberList: '$roomIdUserId, id, user, joinAt, roomId', }); - @Injectable({ providedIn: 'root' }) @@ -40,7 +52,6 @@ export class RoomLocalDataSourceService { constructor() {} - async createRoom(data: TableRoom) { try { const result = await roomDataSource.room.add(data) @@ -70,6 +81,17 @@ export class RoomLocalDataSourceService { } } + + async addMember(data: TableMemberList) { + try { + data.$roomIdUserId = data.roomId + data.user.wxUserId + const result = await roomDataSource.memberList.add(data) + return ok(result) + } catch (e) { + return err(false) + } + } + async getRoomById(id: any) { try { const result = await roomDataSource.room.get(id) @@ -77,7 +99,6 @@ export class RoomLocalDataSourceService { } catch (e) { return err(false) } - } getItemsLive(): Observable { @@ -88,5 +109,9 @@ export class RoomLocalDataSourceService { return liveQuery(() => roomDataSource.room.get(id)) as any; } + getRoomMemberByIdLive(roomId: any): Observable { + return liveQuery(() => roomDataSource.memberList.where('roomId').equals(roomId).toArray()) as any; + } + } diff --git a/src/app/services/Repositorys/chat/dto/room/roomByIdInputDTO.ts b/src/app/services/Repositorys/chat/dto/room/roomByIdInputDTO.ts new file mode 100644 index 000000000..c8e15d6f3 --- /dev/null +++ b/src/app/services/Repositorys/chat/dto/room/roomByIdInputDTO.ts @@ -0,0 +1,6 @@ +import { z } from "zod"; + +export const RoomByIdInputDTOSchema = z.string() + + +export type RoomByIdInputDTO = z.infer diff --git a/src/app/services/Repositorys/chat/dto/room/roomByIdOutputDTO.ts b/src/app/services/Repositorys/chat/dto/room/roomByIdOutputDTO.ts new file mode 100644 index 000000000..461ef931f --- /dev/null +++ b/src/app/services/Repositorys/chat/dto/room/roomByIdOutputDTO.ts @@ -0,0 +1,31 @@ +import { z } from "zod"; + +const UserSchema = z.object({ + wxUserId: z.number(), + wxFullName: z.string(), + wxeMail: z.string(), + userPhoto: z.string().nullable(), +}); + +const MemberSchema = z.object({ + id: z.string(), + user: UserSchema, + joinAt: z.string(), +}); + +export const RoomByIdOutputDTOSchema = z.object({ + success: z.boolean(), + message: z.string(), + data: z.object({ + id: z.string(), + roomName: z.string(), + createdBy: UserSchema, + createdAt: z.string(), + expirationDate: z.string().nullable(), + roomType: z.number(), + members: z.array(MemberSchema), + }), +}); + + +export type RoomByIdOutputDTO = z.infer diff --git a/src/app/services/Repositorys/chat/repository/room-repository.service.ts b/src/app/services/Repositorys/chat/repository/room-repository.service.ts index 8bfbe584d..a96ff02d2 100644 --- a/src/app/services/Repositorys/chat/repository/room-repository.service.ts +++ b/src/app/services/Repositorys/chat/repository/room-repository.service.ts @@ -7,6 +7,7 @@ import { AddMemberToRoomInputDTO } from '../dto/room/addMemberToRoomInputDto'; import { RoomLocalDataSourceService } from '../data-source/room/rooom-local-data-source.service'; import { HttpErrorResponse } from '@angular/common/http'; import { ZodError } from 'zod'; +import { RoomByIdInputDTO } from '../dto/room/roomByIdInputDTO'; @Injectable({ providedIn: 'root' @@ -30,6 +31,18 @@ export class RoomRepositoryService { return result } + + async getRoomById(id: RoomByIdInputDTO) { + const result = await this.roomRemoteDataSourceService.getRoom(id) + + if(result.isOk()) { + for (const user of result.value.data.members) { + this.roomLocalDataSourceService.addMember({...user, roomId:id}) + } + + } + } + async create(data: RoomInputDTO) { const result = await this.roomRemoteDataSourceService.createRoom(data) @@ -56,4 +69,9 @@ export class RoomRepositoryService { getItemByIdLive(id: any) { return this.roomLocalDataSourceService.getRoomByIdLive(id) } + + + getRoomMemberByIdLive(roomId: any) { + return this.roomLocalDataSourceService.getRoomMemberByIdLive(roomId) + } } diff --git a/src/app/services/decorators/api-validate-schema.decorator.ts b/src/app/services/decorators/api-validate-schema.decorator.ts new file mode 100644 index 000000000..ecc1f468e --- /dev/null +++ b/src/app/services/decorators/api-validate-schema.decorator.ts @@ -0,0 +1,45 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { Result, err } from 'neverthrow'; +import { z, ZodError } from 'zod'; +import * as Sentry from '@sentry/capacitor'; + +export function APIReturn(schema: z.ZodTypeAny) { + return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { + const originalMethod = descriptor.value; + + descriptor.value = async function (...args: any[]) { + const result: Result = await originalMethod.apply(this, args); + + if(result.isOk()) { + try { + // Validate the result using the provided schema + schema.parse(result.value); + + } catch (error) { + if (error instanceof ZodError) { + // If validation fails, throw an error with the details + // + console.log('unexpected data structure') + // Capture the Zod validation error with additional context + Sentry.withScope((scope) => { + scope.setTag('APIReturn', 'user'); + scope.setContext('data', { data: result.value }); + Sentry.captureException(error); + }); + console.error('Validation failed:', error.errors); + } else { + console.log('failed to setup the connection successful') + // Throw any other unexpected errors + throw error; + } + } + } else { + console.log() + // result.error. + } + + return result; + } + return descriptor; + } +} diff --git a/src/app/shared/chat/messages/messages.page.html b/src/app/shared/chat/messages/messages.page.html index 3d2da595c..502db2cad 100644 --- a/src/app/shared/chat/messages/messages.page.html +++ b/src/app/shared/chat/messages/messages.page.html @@ -44,6 +44,12 @@ {{ roomMessage$ | async | json }} + members + + + {{ memberList | json }} + + diff --git a/src/app/shared/chat/messages/messages.page.ts b/src/app/shared/chat/messages/messages.page.ts index dd1e05268..58a7a1b5f 100644 --- a/src/app/shared/chat/messages/messages.page.ts +++ b/src/app/shared/chat/messages/messages.page.ts @@ -34,12 +34,12 @@ import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page'; import { ChatMessageDebuggingPage } from 'src/app/shared/popover/chat-message-debugging/chat-message-debugging.page'; import { PermissionService } from 'src/app/services/permission.service'; import { FileValidatorService } from "src/app/services/file/file-validator.service" -import { ChangeDetectorRef } from '@angular/core'; import { RoomRepositoryService } from 'src/app/services/Repositorys/chat/repository/room-repository.service'; -import { Observable } from 'rxjs'; import { RoomListItemOutPutDTO, RoomListOutPutDTO } from 'src/app/services/Repositorys/chat/dto/room/roomListOutputDTO'; import { MessageRepositoryService } from 'src/app/services/Repositorys/chat/repository/message-respository.service'; import { MessageInputDTO } from 'src/app/services/Repositorys/chat/dto/message/messageInputDtO'; +import { TableMemberList } from 'src/app/services/Repositorys/chat/data-source/room/rooom-local-data-source.service'; +import { Observable } from 'rxjs'; const IMAGE_DIR = 'stored-images'; @@ -111,6 +111,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy roomData$: Observable roomMessage$: Observable + roomMembers$: Observable constructor( public popoverController: PopoverController, @@ -143,7 +144,8 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy ngOnChanges(changes: SimpleChanges): void { this.roomData$ = this.roomRepositoryService.getItemByIdLive(this.roomId) this.roomMessage$ = this.messageRepositoryService.getItemsLive(this.roomId) - + this.roomMembers$ = this.roomRepositoryService.getRoomMemberByIdLive(this.roomId) + this.roomRepositoryService.getRoomById(this.roomId) this.messageRepositoryService.listAllMessagesByRoomId(this.roomId) }