From d6a08c6264305baaf9cef47b4d6796ba1dd7373d Mon Sep 17 00:00:00 2001 From: Peter Maquiran Date: Fri, 30 Aug 2024 12:41:50 +0100 Subject: [PATCH] set bold --- package-lock.json | 11 ++ package.json | 3 +- src/app/core/chat/entity/distribution.ts | 30 +++++ src/app/core/chat/entity/group.ts | 4 +- src/app/core/chat/mapper/getRoomListMapper.ts | 2 +- .../repository/bold/bold-local-repository.ts | 7 ++ .../distribution-local-repository.ts | 9 ++ ...essage-load-history-by-roomId-use-case.ts} | 0 .../dexie/instance/chat/schema/bold.ts | 11 ++ .../instance/chat/schema/destribution.ts | 10 +- src/app/infra/database/dexie/service.ts | 8 +- .../dexie/dexie-repository.service.ts | 22 ++++ src/app/module/chat/chat.module.ts | 21 +++- .../rooms/distributionListChangedetector.ts | 29 +++++ .../list/rooms/roomByIdChangeDetector.ts | 33 +++++ .../list/rooms/roomListChangeDetector.ts | 4 +- .../repository/bold/bold-local-repository.ts | 20 +++ .../destribution-local-repository.ts | 14 +++ src/app/module/chat/domain/adapter.ts | 2 +- .../chat/domain/chat-service.service.ts | 8 +- .../chat/domain/service/bold.service.spec.ts | 16 +++ .../chat/domain/service/bold.service.ts | 37 ++++++ .../service/distribution.service.spec.ts | 16 +++ .../domain/service/distribution.service.ts | 87 +++++++++++++ .../bold-remove-by-room-id.service.spec.ts | 16 +++ .../bold/bold-remove-by-room-id.service.ts | 33 +++++ .../room/room-get-by-id-use-case.service.ts | 118 +++++++++--------- .../room/room-get-list-use-case.service.ts | 4 +- src/app/ui/chat/chat.module.ts | 5 + src/app/ui/chat/chat.page.html | 4 +- src/app/ui/chat/chat.page.ts | 22 +++- .../chat/component/messages/messages.page.ts | 5 +- src/app/utils/entity.ts | 34 +++++ 33 files changed, 558 insertions(+), 87 deletions(-) create mode 100644 src/app/core/chat/entity/distribution.ts create mode 100644 src/app/core/chat/repository/bold/bold-local-repository.ts create mode 100644 src/app/core/chat/repository/distribution/distribution-local-repository.ts rename src/app/core/chat/usecase/message/{http-listen-to-message-load-history-use-case.ts => http-listen-to-message-load-history-by-roomId-use-case.ts} (100%) create mode 100644 src/app/infra/database/dexie/instance/chat/schema/bold.ts create mode 100644 src/app/module/chat/data/async/list/rooms/distributionListChangedetector.ts create mode 100644 src/app/module/chat/data/async/list/rooms/roomByIdChangeDetector.ts create mode 100644 src/app/module/chat/data/repository/bold/bold-local-repository.ts create mode 100644 src/app/module/chat/data/repository/destribution/destribution-local-repository.ts create mode 100644 src/app/module/chat/domain/service/bold.service.spec.ts create mode 100644 src/app/module/chat/domain/service/bold.service.ts create mode 100644 src/app/module/chat/domain/service/distribution.service.spec.ts create mode 100644 src/app/module/chat/domain/service/distribution.service.ts create mode 100644 src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service.spec.ts create mode 100644 src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service.ts create mode 100644 src/app/utils/entity.ts diff --git a/package-lock.json b/package-lock.json index b2f72e1ff..2db955158 100644 --- a/package-lock.json +++ b/package-lock.json @@ -142,6 +142,7 @@ "cross-env": "^7.0.3", "crypto-js": "^4.0.0", "date-fns": "^2.17.0", + "deep-object-diff": "^1.1.9", "depd": "^2.0.0", "dexie": "^4.0.7", "dompurify": "^3.0.6", @@ -14652,6 +14653,11 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, + "node_modules/deep-object-diff": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz", + "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -55393,6 +55399,11 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, + "deep-object-diff": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz", + "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==" + }, "deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", diff --git a/package.json b/package.json index ace32ee00..793dde3c4 100644 --- a/package.json +++ b/package.json @@ -161,6 +161,7 @@ "cross-env": "^7.0.3", "crypto-js": "^4.0.0", "date-fns": "^2.17.0", + "deep-object-diff": "^1.1.9", "depd": "^2.0.0", "dexie": "^4.0.7", "dompurify": "^3.0.6", @@ -330,4 +331,4 @@ "url": "git+https://Kayaya@bitbucket.org/equilibriumito/gabinete-digital.git" }, "license": "ISC" -} \ No newline at end of file +} diff --git a/src/app/core/chat/entity/distribution.ts b/src/app/core/chat/entity/distribution.ts new file mode 100644 index 000000000..e3d005ae8 --- /dev/null +++ b/src/app/core/chat/entity/distribution.ts @@ -0,0 +1,30 @@ +import { object, z } from 'zod'; +import { MessageEntitySchema } from './message'; +import { BaseEntity } from 'src/app/utils/entity'; + +const DistributionEntitySchema = z.object({ + $messageIdMemberId: z.string().optional(), + messageId: z.string(), + memberId: z.number(), + readAt: z.string().nullable(), + deliverAt: z.string().nullable(), + roomId: z.string(), +}) + +export type IDistribution = z.infer + + +export class DistributionEntity extends BaseEntity(DistributionEntitySchema) implements IDistribution{ + + roomId: typeof DistributionEntitySchema._input.roomId + readAt: typeof DistributionEntitySchema._input.readAt + messageId: typeof DistributionEntitySchema._input.messageId + memberId: typeof DistributionEntitySchema._input.memberId + deliverAt: typeof DistributionEntitySchema._input.deliverAt + + constructor(data: IDistribution) { + super(); + Object.assign(this, data) + } + +} \ No newline at end of file diff --git a/src/app/core/chat/entity/group.ts b/src/app/core/chat/entity/group.ts index a3cc6f351..ca81c9323 100644 --- a/src/app/core/chat/entity/group.ts +++ b/src/app/core/chat/entity/group.ts @@ -1,4 +1,5 @@ import { SessionStore } from "src/app/store/session.service"; +import { BaseEntity } from "src/app/utils/entity"; import { z } from "zod" export enum RoomType { @@ -37,7 +38,7 @@ export const RoomEntitySchema = z.object({ export type IRoomEntity = z.infer -export class RoomEntity implements IRoomEntity{ +export class RoomEntity extends BaseEntity(RoomEntitySchema) implements IRoomEntity{ id: typeof RoomEntitySchema._input.id roomName: typeof RoomEntitySchema._input.roomName @@ -48,6 +49,7 @@ export class RoomEntity implements IRoomEntity{ members: typeof RoomEntitySchema._input.members constructor(data: IRoomEntity) { + super(); Object.assign(this, data) if(data.roomType == RoomType.Direct) { this.setName() diff --git a/src/app/core/chat/mapper/getRoomListMapper.ts b/src/app/core/chat/mapper/getRoomListMapper.ts index 71a64a7c8..2e0583e43 100644 --- a/src/app/core/chat/mapper/getRoomListMapper.ts +++ b/src/app/core/chat/mapper/getRoomListMapper.ts @@ -14,7 +14,7 @@ export class GetRoomListMapper{ id: roomData.chatRoom.id, roomName: roomData.chatRoom.roomName, roomType: roomData.chatRoom.roomType, - members: [roomData.chatRoom.user1, roomData.chatRoom.user2].filter((e) => e?.wxUserId).map((b) => ({ + members: [ roomData.chatRoom.user2, roomData.chatRoom.user1].filter((e) => e?.wxUserId).map((b) => ({ id: '', isAdmin: false, joinAt: '', diff --git a/src/app/core/chat/repository/bold/bold-local-repository.ts b/src/app/core/chat/repository/bold/bold-local-repository.ts new file mode 100644 index 000000000..f34a691f5 --- /dev/null +++ b/src/app/core/chat/repository/bold/bold-local-repository.ts @@ -0,0 +1,7 @@ +import { BoldTable } from "src/app/infra/database/dexie/instance/chat/schema/bold"; +import { DexieRepository } from "src/app/infra/repository/dexie/dexie-repository.service"; + + +export abstract class IBoldLocalRepository extends DexieRepository implements IBoldLocalRepository { + +} \ No newline at end of file diff --git a/src/app/core/chat/repository/distribution/distribution-local-repository.ts b/src/app/core/chat/repository/distribution/distribution-local-repository.ts new file mode 100644 index 000000000..fc57f24f2 --- /dev/null +++ b/src/app/core/chat/repository/distribution/distribution-local-repository.ts @@ -0,0 +1,9 @@ +import { DistributionTable, DistributionTableSchema } from "src/app/infra/database/dexie/instance/chat/schema/destribution"; +import { chatDatabase } from "src/app/infra/database/dexie/service"; +import { DexieRepository } from "src/app/infra/repository/dexie/dexie-repository.service"; + + + +export abstract class IDistributionLocalRepository extends DexieRepository implements IDistributionLocalRepository { + +} \ No newline at end of file diff --git a/src/app/core/chat/usecase/message/http-listen-to-message-load-history-use-case.ts b/src/app/core/chat/usecase/message/http-listen-to-message-load-history-by-roomId-use-case.ts similarity index 100% rename from src/app/core/chat/usecase/message/http-listen-to-message-load-history-use-case.ts rename to src/app/core/chat/usecase/message/http-listen-to-message-load-history-by-roomId-use-case.ts diff --git a/src/app/infra/database/dexie/instance/chat/schema/bold.ts b/src/app/infra/database/dexie/instance/chat/schema/bold.ts new file mode 100644 index 000000000..9f2ec07c0 --- /dev/null +++ b/src/app/infra/database/dexie/instance/chat/schema/bold.ts @@ -0,0 +1,11 @@ +import { EntityTable } from 'Dexie'; +import { z } from 'zod'; + +export const BoldTableSchema = z.object({ + roomId: z.string(), + bold: z.number() +}) + +export type BoldTable = z.infer +export type DexieBoldTable = EntityTable; +export const BoldTableColumn = 'roomId, bold' \ No newline at end of file diff --git a/src/app/infra/database/dexie/instance/chat/schema/destribution.ts b/src/app/infra/database/dexie/instance/chat/schema/destribution.ts index 9998d9e98..e717259f5 100644 --- a/src/app/infra/database/dexie/instance/chat/schema/destribution.ts +++ b/src/app/infra/database/dexie/instance/chat/schema/destribution.ts @@ -2,12 +2,14 @@ import { EntityTable } from 'Dexie'; import { z } from 'zod'; export const DistributionTableSchema = z.object({ - $id: z.string(), + $messageIdMemberId: z.string().optional(), + messageId: z.string(), memberId: z.number(), readAt: z.string().nullable(), - deliverAt: z.string().nullable() + deliverAt: z.string().nullable(), + roomId: z.string(), }) export type DistributionTable = z.infer -export type DexieDistributionTable = EntityTable; -export const DistributionTableColumn = '++$id, messageId, memberId, readAt, deliverAt' \ No newline at end of file +export type DexieDistributionTable = EntityTable; +export const DistributionTableColumn = '$messageIdMemberId, messageId, memberId, readAt, deliverAt, roomId' \ No newline at end of file diff --git a/src/app/infra/database/dexie/service.ts b/src/app/infra/database/dexie/service.ts index 3d69e6ad2..c39742712 100644 --- a/src/app/infra/database/dexie/service.ts +++ b/src/app/infra/database/dexie/service.ts @@ -6,6 +6,8 @@ import { DexieRoomsTable, RoomTableColumn } from 'src/app/infra/database/dexie/i import { DexieTypingsTable, TypingTableColumn } from 'src/app/infra/database/dexie/instance/chat/schema/typing'; import { MessageEntity } from 'src/app/core/chat/entity/message'; import { AttachmentTableColumn, DexieAttachmentsTableSchema } from 'src/app/infra/database/dexie/instance/chat/schema/attachment'; +import { DexieDistributionTable, DistributionTable, DistributionTableColumn } from './instance/chat/schema/destribution'; +import { BoldTableColumn, DexieBoldTable } from './instance/chat/schema/bold'; // import FDBFactory from 'fake-indexeddb/lib/FDBFactory'; // import FDBKeyRange from 'fake-indexeddb/lib/FDBKeyRange'; @@ -20,6 +22,8 @@ export const chatDatabase = new Dexie('chat-database-infra',{ room: DexieRoomsTable, typing: DexieTypingsTable, attachment: DexieAttachmentsTableSchema, + distribution: DexieDistributionTable, + bold: DexieBoldTable }; chatDatabase.version(1).stores({ @@ -27,7 +31,9 @@ chatDatabase.version(1).stores({ members: MemberTableColumn, room: RoomTableColumn, typing: TypingTableColumn, - attachment: AttachmentTableColumn + attachment: AttachmentTableColumn, + distribution: DistributionTableColumn, + bold:BoldTableColumn }); chatDatabase.message.mapToClass(MessageEntity) diff --git a/src/app/infra/repository/dexie/dexie-repository.service.ts b/src/app/infra/repository/dexie/dexie-repository.service.ts index 71ba3f10e..32ddd8694 100644 --- a/src/app/infra/repository/dexie/dexie-repository.service.ts +++ b/src/app/infra/repository/dexie/dexie-repository.service.ts @@ -77,6 +77,28 @@ export class DexieRepository implements IDexieRepository { } } + async updateMany(updatedDocument: Partial[]) { + + const schema = this.ZodSchema.array() + const dataValidation = schema.safeParse(updatedDocument) + + if(dataValidation.success) { + try { + const updatedCount = await this.table.bulkPut(dataValidation.data); + return ok(updatedCount); + } catch (error) { + return err(new Error('Failed to update document: ' + error.message)); + } + } else { + Logger.error(`dexie.js failed to update many into ${this.table.name}, invalid data`, { + data: document, + zodError: dataValidation.error.issues + }); + return err((dataValidation as unknown as ZodError)) + } + } + + async delete(id: any): Promise> { try { await this.table.delete(id); diff --git a/src/app/module/chat/chat.module.ts b/src/app/module/chat/chat.module.ts index 98ace80e5..5752e01cf 100644 --- a/src/app/module/chat/chat.module.ts +++ b/src/app/module/chat/chat.module.ts @@ -10,7 +10,7 @@ import { RoomService } from 'src/app/module/chat/domain/service/room.service' import { HttpListenToMessageLoadHistoryAdapter } from './domain/adapter'; import { ISignalRService } from 'src/app/infra/socket/adapter'; import { HttpModule } from 'src/app/infra/http/http.module'; -import { HttpListenToMessageLoadHistoryUseCase } from 'src/app/core/chat/usecase/message/http-listen-to-message-load-history-use-case'; +import { HttpListenToMessageLoadHistoryUseCase } from 'src/app/core/chat/usecase/message/http-listen-to-message-load-history-by-roomId-use-case'; import { IMessageLocalRepository } from 'src/app/core/chat/repository/message/message-local-repository'; import { MessageLocalDataSourceService } from './data/repository/message/message-local-data-source.service'; import { MessageRemoteDataSourceService } from './data/repository/message/message-remote-data-source.service'; @@ -31,6 +31,12 @@ import { IAttachmentLocalRepository } from 'src/app/core/chat/repository/typing/ import { AttachmentLocalDataSource } from './data/repository/attachment/attachment-local-repository.service'; import { IAttachmentRemoteRepository } from 'src/app/core/chat/repository/attachment/attachment-remote-repository'; import { AttachmentRemoteDataSourceService } from './data/repository/attachment/attachment-remote-repository.service'; +import { IDistributionLocalRepository } from 'src/app/core/chat/repository/distribution/distribution-local-repository'; +import { DistributionLocalRepository } from './data/repository/destribution/destribution-local-repository'; +import { DistributionService } from './domain/service/distribution.service' +import { BoldLocalRepository } from './data/repository/bold/bold-local-repository'; +import { IBoldLocalRepository } from 'src/app/core/chat/repository/bold/bold-local-repository'; +import { BoldService } from 'src/app/module/chat/domain/service/bold.service' @NgModule({ imports: [HttpModule], providers: [ @@ -86,6 +92,15 @@ import { AttachmentRemoteDataSourceService } from './data/repository/attachment/ provide: IAttachmentRemoteRepository, useClass: AttachmentRemoteDataSourceService }, + // + { + provide: IDistributionLocalRepository, + useClass: DistributionLocalRepository + }, + { + provide: IBoldLocalRepository, + useClass: BoldLocalRepository + } ], declarations: [], schemas: [], @@ -101,7 +116,9 @@ export class ChatModule { private signalR: SignalRService, private localDataSource: UserTypingLocalRepository, private UserTypingRemoteRepositoryService: UserTypingRemoteRepositoryService, - private RoomService: RoomService + private RoomService: RoomService, + private DistributionService: DistributionService, + private BoldService: BoldService ) { this.RoomService.init() diff --git a/src/app/module/chat/data/async/list/rooms/distributionListChangedetector.ts b/src/app/module/chat/data/async/list/rooms/distributionListChangedetector.ts new file mode 100644 index 000000000..03c25d800 --- /dev/null +++ b/src/app/module/chat/data/async/list/rooms/distributionListChangedetector.ts @@ -0,0 +1,29 @@ + +import { DistributionTable } from "src/app/infra/database/dexie/instance/chat/schema/destribution"; +import { DistributionOutPutDTO } from "src/app/module/chat/domain/service/distribution.service"; + +export function distributionListDetermineChanges(____serverDistributions: DistributionOutPutDTO[], localDistributions: DistributionTable[], messageId: string) { + + const serverDistribution = ____serverDistributions.map( e=> { + return { + ...e, + $messageIdMemberId: messageId + e.memberId + } + }) + + const serverDistributionMap = new Map(serverDistribution.map(distribution => [distribution.$messageIdMemberId, distribution])); + const localDistributionMap = new Map(localDistributions.map(distribution => [distribution.$messageIdMemberId, distribution])); + + const distributionToInsert = serverDistribution.filter(distribution => !localDistributionMap.has(distribution.$messageIdMemberId)); + const distributionToUpdate = serverDistribution.filter(distribution => { + const localDistribution = localDistributionMap.get(distribution.$messageIdMemberId); + return localDistribution && ( + distribution.deliverAt !== localDistribution.deliverAt || + distribution.readAt !== localDistribution.readAt + ) + }); + + const distributionToDelete = localDistributions.filter(distribution => !serverDistributionMap.has(distribution.$messageIdMemberId)); + + return { distributionToInsert, distributionToUpdate, distributionToDelete }; +} diff --git a/src/app/module/chat/data/async/list/rooms/roomByIdChangeDetector.ts b/src/app/module/chat/data/async/list/rooms/roomByIdChangeDetector.ts new file mode 100644 index 000000000..8807d3f26 --- /dev/null +++ b/src/app/module/chat/data/async/list/rooms/roomByIdChangeDetector.ts @@ -0,0 +1,33 @@ +import { RoomTable } from "src/app/infra/database/dexie/instance/chat/schema/room"; +import { RoomByIdOutputDTO } from "src/app/module/chat/domain/use-case/room/room-get-by-id-use-case.service"; +import { RoomListItemOutPutDTO } from "src/app/module/chat/domain/use-case/room/room-get-list-use-case.service"; + +export function roomByIdDetermineChanges(serverResponse: RoomByIdOutputDTO, localRooms: RoomTable[]) { + + const localRoomMap = new Map(localRooms.map(room => [room.id, room])); + + let roomsToInsert:RoomByIdOutputDTO | undefined; + let roomsToUpdate: RoomByIdOutputDTO | undefined; + + const fond = localRooms.filter(room => !localRoomMap.has(serverResponse.data.id)); + if(!fond) { + roomsToInsert = serverResponse + } + + const needToUpdate = localRooms.filter(room => { + const localRoom = localRoomMap.get(room.id); + return localRoom && ( + room.roomName !== serverResponse.data.roomName || + room.createdBy.wxUserId !== serverResponse.data.createdBy.wxUserId || + room.createdAt !== serverResponse.data.createdAt || + room.expirationDate !== serverResponse.data.expirationDate // || + // room.chatRoom.roomType !== localRoom.roomType + ); + }); + + if(needToUpdate) { + roomsToUpdate = serverResponse + } + + return { roomsToInsert, roomsToUpdate }; +} diff --git a/src/app/module/chat/data/async/list/rooms/roomListChangeDetector.ts b/src/app/module/chat/data/async/list/rooms/roomListChangeDetector.ts index 673a275d2..b74d2b3e5 100644 --- a/src/app/module/chat/data/async/list/rooms/roomListChangeDetector.ts +++ b/src/app/module/chat/data/async/list/rooms/roomListChangeDetector.ts @@ -1,3 +1,4 @@ +import { RoomType } from "src/app/core/chat/entity/group"; import { RoomTable } from "src/app/infra/database/dexie/instance/chat/schema/room"; import { RoomListItemOutPutDTO } from "src/app/module/chat/domain/use-case/room/room-get-list-use-case.service"; @@ -8,8 +9,9 @@ export function roomListDetermineChanges(serverRooms: RoomListItemOutPutDTO[], l const roomsToInsert = serverRooms.filter(room => !localRoomMap.has(room.chatRoom.id)); const roomsToUpdate = serverRooms.filter(room => { const localRoom = localRoomMap.get(room.chatRoom.id); + return localRoom && ( - room.chatRoom.roomName !== localRoom.roomName || + room.chatRoom.roomName !== localRoom.roomName && room.chatRoom.roomType == RoomType.Group || room.chatRoom.createdBy.wxUserId !== localRoom.createdBy.wxUserId || room.chatRoom.createdAt !== localRoom.createdAt || room.chatRoom.expirationDate !== localRoom.expirationDate // || diff --git a/src/app/module/chat/data/repository/bold/bold-local-repository.ts b/src/app/module/chat/data/repository/bold/bold-local-repository.ts new file mode 100644 index 000000000..343b826fd --- /dev/null +++ b/src/app/module/chat/data/repository/bold/bold-local-repository.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { from } from "rxjs"; +import { DexieRepository } from "src/app/infra/repository/dexie/dexie-repository.service"; +import { Dexie, EntityTable, liveQuery, Observable } from 'Dexie'; +import { BoldTable, BoldTableSchema } from "src/app/infra/database/dexie/instance/chat/schema/bold"; +import { chatDatabase } from "src/app/infra/database/dexie/service"; +import { IBoldLocalRepository } from "src/app/core/chat/repository/bold/bold-local-repository"; + +@Injectable({ + providedIn: 'root' +}) +export class BoldLocalRepository extends DexieRepository implements IBoldLocalRepository { + constructor() { + super(chatDatabase.bold, BoldTableSchema) + } + + listen() { + return from(liveQuery(() => chatDatabase.bold.toArray())) + } +} \ No newline at end of file diff --git a/src/app/module/chat/data/repository/destribution/destribution-local-repository.ts b/src/app/module/chat/data/repository/destribution/destribution-local-repository.ts new file mode 100644 index 000000000..70abfeb63 --- /dev/null +++ b/src/app/module/chat/data/repository/destribution/destribution-local-repository.ts @@ -0,0 +1,14 @@ +import { DistributionTable, DistributionTableSchema } from "src/app/infra/database/dexie/instance/chat/schema/destribution"; +import { chatDatabase } from "src/app/infra/database/dexie/service"; +import { DexieRepository } from "src/app/infra/repository/dexie/dexie-repository.service"; + + +export class DistributionLocalRepository extends DexieRepository { + constructor() { + super(chatDatabase.distribution, DistributionTableSchema) + + chatDatabase.distribution.hook("creating", function (primKey, obj, transaction) { + obj.$messageIdMemberId = `${obj.messageId}${obj.memberId}` + }); + } +} \ No newline at end of file diff --git a/src/app/module/chat/domain/adapter.ts b/src/app/module/chat/domain/adapter.ts index baf0f67ee..031a88344 100644 --- a/src/app/module/chat/domain/adapter.ts +++ b/src/app/module/chat/domain/adapter.ts @@ -2,7 +2,7 @@ import { HttpErrorResponse } from "@angular/common/http"; import { Result } from "neverthrow"; import { Observable } from "rxjs"; import { MessageEntity } from "src/app/core/chat/entity/message"; -import { HttpListenToMessageLoadHistoryUseCaseInput } from "src/app/core/chat/usecase/message/http-listen-to-message-load-history-use-case"; +import { HttpListenToMessageLoadHistoryUseCaseInput } from "src/app/core/chat/usecase/message/http-listen-to-message-load-history-by-roomId-use-case"; import { HttpResult } from "src/app/infra/http/type"; import { UseCase } from "src/app/utils/use-case-interface"; import { IMessageGetAllByRoomIdOutPut } from 'src/app/core/chat/usecase/message/message-get-all-by-room-Id' diff --git a/src/app/module/chat/domain/chat-service.service.ts b/src/app/module/chat/domain/chat-service.service.ts index 09f0a6df3..5c0585217 100644 --- a/src/app/module/chat/domain/chat-service.service.ts +++ b/src/app/module/chat/domain/chat-service.service.ts @@ -36,10 +36,10 @@ import { MessageAttachmentByMessageIdInput, MessageAttachmentByMessageIdUseCase import { AddMemberToRoomInputDTO, AddMemberToRoomInputDTOSchema } from '../domain/use-case/member/member-add-use-case.service'; import { RoomType } from "src/app/core/chat/entity/group"; import { HttpListenToMessageLoadHistoryAdapter } from './adapter' -import { HttpListenToMessageLoadHistoryUseCaseInput } from 'src/app/core/chat/usecase/message/http-listen-to-message-load-history-use-case'; +import { HttpListenToMessageLoadHistoryUseCaseInput } from 'src/app/core/chat/usecase/message/http-listen-to-message-load-history-by-roomId-use-case'; import { MessageSocketRepositoryService } from 'src/app/module/chat/data/repository/message/message-live-signalr-data-source.service' import { MessageMarkAsReadInput } from "src/app/module/chat/domain/use-case/message/message-mark-as-read-use-case.service"; - +import { BoldRemoveByRoomIdInput, BoldRemoveByRoomIdService } from 'src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service' export const InstanceId = uuidv4(); @Injectable({ @@ -80,6 +80,7 @@ export class ChatServiceService { private MessageMarkAllMessageAsReadByRoomIdService: MessageMarkAllMessageAsReadByRoomIdService, private HttpListenToMessageLoadHistory: HttpListenToMessageLoadHistoryAdapter, private MessageSocketRepositoryService: MessageSocketRepositoryService, + private BoldRemoveByRoomIdService: BoldRemoveByRoomIdService ) { this.MessageSocketRepositoryService.listenToDeleteMessages() .pipe() @@ -182,6 +183,9 @@ export class ChatServiceService { // return this.GetMessageAttachmentLocallyUseCaseService.execute(input) // } + removeBoldFromRoom(input: BoldRemoveByRoomIdInput) { + return this.BoldRemoveByRoomIdService.execute(input) + } listenToMessageLoadHistory(input: HttpListenToMessageLoadHistoryUseCaseInput) { return this.HttpListenToMessageLoadHistory.execute(input) diff --git a/src/app/module/chat/domain/service/bold.service.spec.ts b/src/app/module/chat/domain/service/bold.service.spec.ts new file mode 100644 index 000000000..6ac95d9a2 --- /dev/null +++ b/src/app/module/chat/domain/service/bold.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { BoldService } from './bold.service'; + +describe('BoldService', () => { + let service: BoldService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(BoldService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/module/chat/domain/service/bold.service.ts b/src/app/module/chat/domain/service/bold.service.ts new file mode 100644 index 000000000..d8497224c --- /dev/null +++ b/src/app/module/chat/domain/service/bold.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { filter, map } from 'rxjs/operators'; +import { IMessageSocketRepository } from 'src/app/core/chat/repository/message/message-socket-repository'; +import { InstanceId } from '../chat-service.service'; +import { MessageEntity } from 'src/app/core/chat/entity/message'; +import { IBoldLocalRepository } from 'src/app/core/chat/repository/bold/bold-local-repository'; + +@Injectable({ + providedIn: 'root' +}) +export class BoldService { + + constructor( + private MessageSocketRepositoryService: IMessageSocketRepository, + private boldLocalRepository: IBoldLocalRepository + ) { + this.listenToIncomingMessage(); + } + + listenToIncomingMessage() { + return this.MessageSocketRepositoryService.listenToMessages().pipe( + filter((message) => !message?.requestId?.startsWith(InstanceId)), + map(message => Object.assign(new MessageEntity(), message)) + ).subscribe(async (message) => { + + const result = await this.boldLocalRepository.findOne({roomId: message.roomId}) + + if(result.isOk() && !result.value) { + const result = await this.boldLocalRepository.insert({roomId: message.roomId, bold: 1}) + } else if(result.isOk() && result.value.bold == 0) { + const result = await this.boldLocalRepository.update(message.roomId, {bold: 1}) + } + + }); + + } +} diff --git a/src/app/module/chat/domain/service/distribution.service.spec.ts b/src/app/module/chat/domain/service/distribution.service.spec.ts new file mode 100644 index 000000000..d2662a6d2 --- /dev/null +++ b/src/app/module/chat/domain/service/distribution.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { DistributionService } from './distribution.service'; + +describe('DistributionService', () => { + let service: DistributionService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DistributionService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/module/chat/domain/service/distribution.service.ts b/src/app/module/chat/domain/service/distribution.service.ts new file mode 100644 index 000000000..e2dbc5b6f --- /dev/null +++ b/src/app/module/chat/domain/service/distribution.service.ts @@ -0,0 +1,87 @@ +import { Injectable } from '@angular/core'; +import { filter, map } from 'rxjs/operators'; +import { IMessageGetAllByRoomIdOutPut } from 'src/app/core/chat/usecase/message/message-get-all-by-room-Id'; +import { HttpAdapter } from 'src/app/infra/http/adapter'; +import { IDistributionLocalRepository } from 'src/app/core/chat/repository/distribution/distribution-local-repository' +import { z } from 'zod'; +import { distributionListDetermineChanges } from '../../data/async/list/rooms/distributionListChangedetector'; +import { DistributionEntity } from 'src/app/core/chat/entity/distribution'; + +export const DistributionOutPutDTOSchema = z.object({ + $messageIdMemberId: z.string(), + memberId: z.number(), + readAt: z.string().nullable(), + deliverAt: z.string().nullable() +}) +export type DistributionOutPutDTO = z.infer + +@Injectable({ + providedIn: 'root' +}) +export class DistributionService { + + constructor( + private http: HttpAdapter, + private distributionLocalRepository: IDistributionLocalRepository + ) { + // this.listenToLoadHistory() + } + + listenToLoadHistory() { + return this.http.listen().pipe( + filter((response)=> { + if(response?.isOk()) { + return response.value.url.endsWith('Messages') + } + + return false + }), + map((response: any) => response.value.data as IMessageGetAllByRoomIdOutPut) + ) + .subscribe(async (data) => { + + const localList = await this.distributionLocalRepository.find({roomId: data.data[0].roomId}) + + data.data.map(async (message) => { + const serverList = message.info + + if(localList.isOk() && localList.value) { + + // const localListForCurrentMessage = localList.value.filter((e) => e.messageId == message.id) + + // console.log('localListForCurrentMessage', localListForCurrentMessage) + + // const { distributionToInsert, distributionToUpdate, distributionToDelete } = await distributionListDetermineChanges(serverList, localListForCurrentMessage, message.id) + // console.log({distributionToInsert, distributionToUpdate, distributionToDelete}) + + // const map = distributionToInsert.map((e) => { + // e. + // }) + + // this.distributionLocalRepository.insertMany(distributionToInsert) + + // const distributionEntityToInsert = distributionToInsert.map((ee)=> { + // return new DistributionEntity({ + // roomId: message.roomId, + // deliverAt: ee.deliverAt, + // memberId: ee.memberId, + // messageId: message.roomId, + // readAt: ee.readAt, + // }) + + // console.log('create') + // }) + + // if(distributionEntityToInsert.length >= 0) { + // // console.log('distributionEntityToInsert', distributionEntityToInsert) + // //this.distributionLocalRepository.insertMany(distributionEntityToInsert) + // } + + + } + + }) + + }) + } +} diff --git a/src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service.spec.ts b/src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service.spec.ts new file mode 100644 index 000000000..5765c451f --- /dev/null +++ b/src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { BoldRemoveByRoomIdService } from './bold-remove-by-room-id.service'; + +describe('BoldRemoveByRoomIdService', () => { + let service: BoldRemoveByRoomIdService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(BoldRemoveByRoomIdService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service.ts b/src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service.ts new file mode 100644 index 000000000..1c8550323 --- /dev/null +++ b/src/app/module/chat/domain/use-case/bold/bold-remove-by-room-id.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { IBoldLocalRepository } from 'src/app/core/chat/repository/bold/bold-local-repository'; +import { TracingType, XTracerAsync } from 'src/app/services/monitoring/opentelemetry/tracer'; +import { zodSafeValidation } from 'src/app/utils/zodValidation'; +import { z } from 'zod'; + + +const BoldRemoveByRoomIdInputSchema = z.object({ + roomId: z.string() +}) +export type BoldRemoveByRoomIdInput = z.infer +@Injectable({ + providedIn: 'root' +}) +export class BoldRemoveByRoomIdService { + + constructor( + private boldLocalRepository: IBoldLocalRepository + ) { } + + @XTracerAsync({name:'BoldRemoveByRoomIdService', module:'chat', bugPrint: true}) + async execute(input: BoldRemoveByRoomIdInput, tracing?: TracingType) { + + const validation = zodSafeValidation(BoldRemoveByRoomIdInputSchema, input) + + if(validation.isOk()) { + return await this.boldLocalRepository.delete(input.roomId) + } else { + tracing.hasError("invalid parameter") + } + + } +} diff --git a/src/app/module/chat/domain/use-case/room/room-get-by-id-use-case.service.ts b/src/app/module/chat/domain/use-case/room/room-get-by-id-use-case.service.ts index 272f73b67..7bd4fde67 100644 --- a/src/app/module/chat/domain/use-case/room/room-get-by-id-use-case.service.ts +++ b/src/app/module/chat/domain/use-case/room/room-get-by-id-use-case.service.ts @@ -10,8 +10,11 @@ import { IRoomRemoteRepository } from 'src/app/core/chat/repository/room/room-re import { IMemberLocalRepository } from 'src/app/core/chat/repository/member/member-local-repository'; import { IRoomLocalRepository } from 'src/app/core/chat/repository/room/room-local-repository'; import { GetRoomByIdMapper } from 'src/app/core/chat/mapper/getRoomByIdMapper'; -import { RoomEntity } from 'src/app/core/chat/entity/group'; +import { RoomEntity, RoomType } from 'src/app/core/chat/entity/group'; import { GetRoomListMapper } from 'src/app/core/chat/mapper/getRoomListMapper'; +import { roomByIdDetermineChanges } from '../../../data/async/list/rooms/roomByIdChangeDetector'; +import { diff, addedDiff, deletedDiff, updatedDiff, detailedDiff } from 'deep-object-diff'; +import { zodSafeValidation } from 'src/app/utils/zodValidation'; const UserSchema = z.object({ wxUserId: z.number(), @@ -56,7 +59,6 @@ export class GetRoomByIdUseCaseService { constructor( private roomRemoteDataSourceService: IRoomRemoteRepository, - // private roomMemoryDataSourceService: Store, private roomLocalDataSourceService: IRoomLocalRepository, private MemberListLocalRepository: IMemberLocalRepository ) { } @@ -64,71 +66,63 @@ export class GetRoomByIdUseCaseService { @captureAndReraiseAsync('RoomRepositoryService/getRoomById') async execute(id: RoomByIdInputDTO) { const result = await this.roomRemoteDataSourceService.getRoom(id) - + if(result.isOk()) { + const validData = zodSafeValidation(RoomByIdOutputDTOSchema, result.value) - const localListRoom = await this.roomLocalDataSourceService.findAll() - if(localListRoom.isOk()) { - const object = { - chatRoom: result.value.data - } - const { roomsToInsert, roomsToUpdate } = roomListDetermineChanges([object], localListRoom.value) - - if(roomsToUpdate) { - - const roomsToUpdateEntity = GetRoomListMapper.toDomain(roomsToUpdate) - for( const room of roomsToUpdateEntity) { - this.roomLocalDataSourceService.update(room.id, room) - if(room.expirationDate) { - console.log('room expiration date schedule') - // this.CronJobService.createCronJob('remove expired room', new Date(room.expirationDate), this.execute) - } - } - } - - if(roomsToInsert) { - - const roomsToInsertEntity = GetRoomListMapper.toDomain(roomsToInsert) - for( const room of roomsToInsertEntity) { - this.roomLocalDataSourceService.insert(room) - if(room.expirationDate) { - console.log('room expiration date schedule') - // this.CronJobService.createCronJob('remove expired room', new Date(room.expirationDate), this.execute) - } - } - } - - // for( const roomData of roomsToUpdate) { - // const room = new RoomEntity({ - // createdAt: result.value.data.createdAt, - // createdBy: result.value.data.createdBy, - // expirationDate: result.value.data.expirationDate, - // id: result.value.data.id, - // members: result.value.data.members, - // roomName: result.value.data.roomName, - // roomType: result.value.data.roomType, - // }) + if(validData.isOk()) { + const localListRoom = await this.roomLocalDataSourceService.findAll() + if(localListRoom.isOk()) { - // this.roomLocalDataSourceService.update(room.id, room) - // } - - // ============================ - const localList = await this.MemberListLocalRepository.getRoomMemberById(id) - - const { membersToInsert, membersToUpdate, membersToDelete } = roomMemberListDetermineChanges(result.value.data.members, localList, id) - - for (const user of membersToInsert) { - await this.MemberListLocalRepository.addMember(MemberListMapper(user, id)) + const getRoomById = await this.roomLocalDataSourceService.findOne({id:validData.value.data.id}) + if(getRoomById.isOk() && getRoomById.value) { + console.log(validData.value) + const room = GetRoomByIdMapper.toDomain(validData.value) + + const added: Partial = addedDiff(getRoomById.value, room); + const deleted: Partial = deletedDiff(getRoomById.value, room); + const updated: Partial = updatedDiff(getRoomById.value, room); + + delete added.members + if(room.roomType == RoomType.Direct) { + delete updated.roomName + } + + if(Object.keys(added).length >= 1 || Object.keys(updated).length >= 1) { + console.log('added', added); + console.log('deleted', deleted); + console.log('updated', updated); + this.roomLocalDataSourceService.update(room.id, room) + } + + } else if (getRoomById.isOk() && !getRoomById.value) { + console.log(validData.value) + const room = GetRoomByIdMapper.toDomain(validData.value) + this.roomLocalDataSourceService.insert(room) + } + + + // ============================ + const localList = await this.MemberListLocalRepository.getRoomMemberById(id) + + const { membersToInsert, membersToUpdate, membersToDelete } = roomMemberListDetermineChanges(validData.value.data.members, localList, id) + + for (const user of membersToInsert) { + await this.MemberListLocalRepository.addMember(MemberListMapper(user, id)) + } + + for (const user of membersToUpdate) { + await this.MemberListLocalRepository.updateMemberRole(MemberListMapper(user, id)) + } + + for(const user of membersToDelete) { + await this.MemberListLocalRepository.removeMemberFromRoom(user.$roomIdUserId) + } } + } else { - for (const user of membersToUpdate) { - await this.MemberListLocalRepository.updateMemberRole(MemberListMapper(user, id)) - } - - for(const user of membersToDelete) { - await this.MemberListLocalRepository.removeMemberFromRoom(user.$roomIdUserId) - } } + } else if (isHttpResponse(result.error) ) { @@ -140,4 +134,4 @@ export class GetRoomByIdUseCaseService { return result } -} +} \ No newline at end of file diff --git a/src/app/module/chat/domain/use-case/room/room-get-list-use-case.service.ts b/src/app/module/chat/domain/use-case/room/room-get-list-use-case.service.ts index d2c5abced..45d4e3671 100644 --- a/src/app/module/chat/domain/use-case/room/room-get-list-use-case.service.ts +++ b/src/app/module/chat/domain/use-case/room/room-get-list-use-case.service.ts @@ -80,9 +80,7 @@ export class GetRoomListUseCaseService { } const roomsToUpdateEntity = GetRoomListMapper.toDomain(roomsToUpdate) - for( const room of roomsToUpdateEntity) { - this.roomLocalDataSourceService.update(room.id, room) - } + this.roomLocalDataSourceService.updateMany(roomsToUpdateEntity) for( const room of roomsToDelete) { this.roomLocalDataSourceService.delete(room.id) diff --git a/src/app/ui/chat/chat.module.ts b/src/app/ui/chat/chat.module.ts index 8593b0b8a..96719d6e5 100644 --- a/src/app/ui/chat/chat.module.ts +++ b/src/app/ui/chat/chat.module.ts @@ -21,6 +21,8 @@ import { MessagesPageModule } from './component/messages/messages.module'; import { ContactsPageModule } from './component/contacts/contacts.module'; import { NewGroupPageModule } from './component/new-group/new-group.module'; import { ChatModule } from 'src/app/module/chat/chat.module'; +import { IBoldLocalRepository } from 'src/app/core/chat/repository/bold/bold-local-repository'; +import { BoldLocalRepository } from 'src/app/module/chat/data/repository/bold/bold-local-repository'; @NgModule({ imports: [ @@ -42,6 +44,9 @@ import { ChatModule } from 'src/app/module/chat/chat.module'; ImageCropperModule, AttendeeModalPageModule, ChatModule + ], + providers: [ + ], declarations: [ ChatPage diff --git a/src/app/ui/chat/chat.page.html b/src/app/ui/chat/chat.page.html index d47f996ea..4b8b34d9c 100644 --- a/src/app/ui/chat/chat.page.html +++ b/src/app/ui/chat/chat.page.html @@ -61,7 +61,7 @@
-
+
{{room.roomName}}
@@ -99,7 +99,7 @@
-
+
{{room.roomName}}
diff --git a/src/app/ui/chat/chat.page.ts b/src/app/ui/chat/chat.page.ts index 54af094f1..8c16c6489 100644 --- a/src/app/ui/chat/chat.page.ts +++ b/src/app/ui/chat/chat.page.ts @@ -23,7 +23,8 @@ import { map, tap } from 'rxjs/operators'; import { interval, Subscription } from 'rxjs'; import { RoomTable } from 'src/app/infra/database/dexie/instance/chat/schema/room'; import { RoomType } from 'src/app/core/chat/entity/group'; - +import { BoldLocalRepository } from 'src/app/module/chat/data/repository/bold/bold-local-repository' +import { BoldTable } from 'src/app/infra/database/dexie/instance/chat/schema/bold'; @Component({ selector: 'app-chat', templateUrl: './chat.page.html', @@ -64,6 +65,8 @@ export class ChatPage implements OnInit { RoomType = RoomType + boldTable: {[key: string]: BoldTable} = {} + constructor( private modalController: ModalController, private timeService: TimeService, @@ -73,18 +76,27 @@ export class ChatPage implements OnInit { public RouteService: RouteService, private ChatServiceService: ChatServiceService, private roomLocalDataSourceService: RoomLocalRepository, - private chatServiceService: ChatServiceService + private boldLocalRepository: BoldLocalRepository ) { - window.onresize = (event) => { if (window.innerWidth > 701) { this.modalController.dismiss(); } - }; + } this.showLoader = true; + this.boldLocalRepository.listen().subscribe((data) => { + const distributionObject: {[key: string]: BoldTable} = {}; + + for (const distribution of data) { + distributionObject[distribution.roomId] = distribution; + } + + this.boldTable = distributionObject + }) + } // Fetch all items using useLiveQuery @@ -127,7 +139,7 @@ export class ChatPage implements OnInit { this.routerSubscription = this.router.events.subscribe((event) => { if (event instanceof NavigationEnd && event.url.startsWith('/home/chat')) { this.routeCheck() - this.ChatServiceService.getRoomList() + // this.ChatServiceService.getRoomList() } }); diff --git a/src/app/ui/chat/component/messages/messages.page.ts b/src/app/ui/chat/component/messages/messages.page.ts index e48066420..58f6b28b4 100644 --- a/src/app/ui/chat/component/messages/messages.page.ts +++ b/src/app/ui/chat/component/messages/messages.page.ts @@ -275,7 +275,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy }) as any - + this.chatServiceService.removeBoldFromRoom({roomId: this.roomId}) } messageStatus(message: MessageEntity) { @@ -422,7 +422,10 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy setTimeout(() => { this.scrollToBottomClicked() }, 100) + + this.chatServiceService.removeBoldFromRoom({roomId: this.roomId}) }); + } listenToDeleteMessage() { diff --git a/src/app/utils/entity.ts b/src/app/utils/entity.ts new file mode 100644 index 000000000..330a43e85 --- /dev/null +++ b/src/app/utils/entity.ts @@ -0,0 +1,34 @@ +import { v4 as uuidv4 } from 'uuid'; +import { ZodSchema, ZodType } from 'zod'; + + + +// export interface IEntity { +// id: string; + +// createdAt: Date; + +// updatedAt: Date; + +// deletedAt?: Date; +// } + +export const BaseEntity = (schema: ZodSchema) => { + abstract class Entity { + readonly id: string; + + readonly createdAt: Date; + + readonly updatedAt: Date; + + deletedAt?: Date; + + static nameOf = (name: keyof T) => name; + + validate(entity: T): ZodType { + return schema.parse(entity); + } + } + + return Entity; +};