From c9b50620fefc85b116bcb5826f6e536256e4484c Mon Sep 17 00:00:00 2001 From: Peter Maquiran Date: Fri, 23 Aug 2024 11:10:52 +0100 Subject: [PATCH] validate user permision on chat --- src/app/module/chat/chat.module.ts | 14 ++-- .../chat/data/dto/typing/typingInputDTO.ts | 2 +- .../room/room-socket-repository.service.ts | 29 ++++++++ .../user-typing-live-data-source.service.ts | 38 +++++++++++ .../user-typing-local-data-source.service.ts | 15 +++-- .../user-typing-live-data-source.service.ts | 25 ------- .../use-case/room-create-use-case.service.ts | 25 ++++--- .../use-case/typing-send-use-case.service.ts | 2 +- .../infra/database/dexie/schema/typing.ts | 7 +- .../chat/infra/socket/signal-r.service.ts | 3 +- src/app/module/chat/infra/socket/signalR.ts | 67 +++++++++++++------ src/app/module/chat/tsconfig.json | 25 ++++++- .../group-contacts/group-contacts.page.ts | 1 - .../messages/contacts/contacts.page.ts | 2 +- .../component/messages/messages.page.html | 10 +-- .../chat/component/messages/messages.page.ts | 36 +++++++--- .../component/new-group/new-group.page.ts | 8 ++- .../modal/chat-popover/chat-popover.page.html | 8 +-- .../ui/chat/modal/messages/messages.page.ts | 4 +- src/app/ui/chat/requirements.readme.md | 31 +++++++++ 20 files changed, 253 insertions(+), 99 deletions(-) create mode 100644 src/app/module/chat/data/repository/typing/user-typing-live-data-source.service.ts rename src/app/module/chat/data/repository/{ => typing}/user-typing-local-data-source.service.ts (66%) delete mode 100644 src/app/module/chat/data/repository/user-typing-live-data-source.service.ts create mode 100644 src/app/ui/chat/requirements.readme.md diff --git a/src/app/module/chat/chat.module.ts b/src/app/module/chat/chat.module.ts index e30ad4a9e..54acacf38 100644 --- a/src/app/module/chat/chat.module.ts +++ b/src/app/module/chat/chat.module.ts @@ -4,7 +4,8 @@ import { ChatServiceService } from 'src/app/module/chat/domain/chat-service.serv import { skip, switchMap } from 'rxjs/operators'; import { SessionStore } from 'src/app/store/session.service'; import { Subject, timer } from 'rxjs'; -import { UserTypingLocalRepository } from './data/repository/user-typing-local-data-source.service'; +import { UserTypingLocalRepository } from './data/repository/typing/user-typing-local-data-source.service'; +import { UserTypingRemoteRepositoryService } from './data/repository/typing/user-typing-live-data-source.service'; import { RoomService } from 'src/app/module/chat/domain/service/room.service' @NgModule({ imports: [], @@ -22,6 +23,7 @@ export class ChatModule { private ChatServiceService: ChatServiceService, private signalR: SignalRService, private localDataSource: UserTypingLocalRepository, + private UserTypingRemoteRepositoryService: UserTypingRemoteRepositoryService, private RoomService: RoomService ) { @@ -31,8 +33,7 @@ export class ChatModule { } async listenToTyping() { - this.signalR.getTyping().subscribe(async (e) => { - if(e?.roomId) { + this.UserTypingRemoteRepositoryService.listenToTyping().subscribe(async(e) => { // this.memoryDataSource.dispatch(removeUserTyping({data: {...e} as any})) // this.memoryDataSource.dispatch(addUserTyping({data: {...e} as any})) @@ -52,9 +53,8 @@ export class ChatModule { } else { this.typingCallback[id].next() } - - } }) + } async syncMessage() { @@ -62,14 +62,14 @@ export class ChatModule { connection.pipe( skip(1) // Skip the first value - ).subscribe((value)=> { + ).subscribe((value: boolean)=> { if(value) { // on reconnect this.ChatServiceService.chatSync(); } }); - connection.subscribe((value) => { + connection.subscribe((value: boolean) => { if(value) { // on connect // this.ChatServiceService.sendLocalMessages() diff --git a/src/app/module/chat/data/dto/typing/typingInputDTO.ts b/src/app/module/chat/data/dto/typing/typingInputDTO.ts index 3c97ad2e3..057c6dc68 100644 --- a/src/app/module/chat/data/dto/typing/typingInputDTO.ts +++ b/src/app/module/chat/data/dto/typing/typingInputDTO.ts @@ -3,7 +3,7 @@ import { z } from "zod" export const UserTypingDTOSchema = z.object({ requestId: z.string(), roomId: z.string(), - userId: z.string(), + userId: z.number(), userName: z.string() }) export type UserTypingDTO = z.infer diff --git a/src/app/module/chat/data/repository/room/room-socket-repository.service.ts b/src/app/module/chat/data/repository/room/room-socket-repository.service.ts index 4c30f1264..25c7557de 100644 --- a/src/app/module/chat/data/repository/room/room-socket-repository.service.ts +++ b/src/app/module/chat/data/repository/room/room-socket-repository.service.ts @@ -3,11 +3,27 @@ import { SignalRService } from '../../../infra/socket/signal-r.service'; import { filter, map } from 'rxjs/operators'; import { z } from 'zod'; import { SocketMessage } from '../../../infra/socket/signalR'; +import { RoomInputDTO } from '../../dto/room/roomInputDTO'; +import { RoomOutPutDTO } from '../../dto/room/roomOutputDTO'; +import { v4 as uuidv4 } from 'uuid' const listenToDeleteRoomInputSchema = z.object({ roomId: z.string() }) + +export const RoomSocketOutPutDTOSchema = z.object({ + id: z.string(), + roomName: z.string(), + createdBy: z.any().nullable(), + createdAt: z.string(), + expirationDate: z.string().nullable(), + roomType: z.any() +}); + +export type RoomSocketOutPutDTO = z.infer + + export type ListenToDeleteRoomInput = z.infer @Injectable({ @@ -19,6 +35,19 @@ export class RoomSocketRepositoryService { private socket: SignalRService ) { } + + async CreateGroup(data: RoomInputDTO) { + const result = await this.socket.sendData({ + method: 'CreateGroup', + data: { + ...data, + requestId: uuidv4() + } as any, + }) + + return result; + } + listenToCreateRoom() { return this.socket.getData().pipe( filter((data) => data?.method == 'UserAddGroup') diff --git a/src/app/module/chat/data/repository/typing/user-typing-live-data-source.service.ts b/src/app/module/chat/data/repository/typing/user-typing-live-data-source.service.ts new file mode 100644 index 000000000..74ddc065c --- /dev/null +++ b/src/app/module/chat/data/repository/typing/user-typing-live-data-source.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@angular/core'; +import { SignalRService } from '../../../infra/socket/signal-r.service'; +import { SessionStore } from 'src/app/store/session.service'; +import { filter, map } from 'rxjs/operators'; +import { SocketMessage } from '../../../infra/socket/signalR'; +import { UserTypingDTO } from '../../dto/typing/typingInputDTO'; + +@Injectable({ + providedIn: 'root' +}) +export class UserTypingRemoteRepositoryService { + + constructor( + private socket: SignalRService + ) { } + + sendTyping(roomId: string) { + return this.socket.sendData({ + method: 'Typing', + data: { + roomId, + UserName:SessionStore.user.FullName, + userId:SessionStore.user.UserId, + requestId: '', + }, + }) + } + + + listenToTyping() { + return this.socket.getData().pipe( + filter((e) : e is SocketMessage=> e?.method == 'TypingMessage' + ), + map((e)=> e.data) + ) + } + +} diff --git a/src/app/module/chat/data/repository/user-typing-local-data-source.service.ts b/src/app/module/chat/data/repository/typing/user-typing-local-data-source.service.ts similarity index 66% rename from src/app/module/chat/data/repository/user-typing-local-data-source.service.ts rename to src/app/module/chat/data/repository/typing/user-typing-local-data-source.service.ts index fef77c0db..bc4fe900a 100644 --- a/src/app/module/chat/data/repository/user-typing-local-data-source.service.ts +++ b/src/app/module/chat/data/repository/typing/user-typing-local-data-source.service.ts @@ -2,8 +2,8 @@ import { Injectable } from '@angular/core'; import { z } from 'zod'; import { Dexie, EntityTable, liveQuery, Observable } from 'Dexie'; import { err, ok } from 'neverthrow'; -import { chatDatabase } from '../../infra/database/dexie/service'; -import { TypingTable } from '../../infra/database/dexie/schema/typing'; +import { chatDatabase } from '../../../infra/database/dexie/service'; +import { TypingTable } from '../../../infra/database/dexie/schema/typing'; @@ -28,7 +28,7 @@ export class UserTypingLocalRepository { async addUserTyping(data: TypingTable) { - data.id = data.chatRoomId + '@' + data.userName + data.id = data.roomId + '@' + data.userName try { const result = await chatDatabase.typing.add(data) return ok(result) @@ -39,7 +39,7 @@ export class UserTypingLocalRepository { async removeUserTyping(data: TypingTable) { - const id = data.chatRoomId + '@' + data.userName + const id = data.roomId + '@' + data.userName try { const result = await chatDatabase.typing.delete(id) return ok(result) @@ -53,4 +53,11 @@ export class UserTypingLocalRepository { return liveQuery(() => chatDatabase.typing.toArray()); } + getUserTypingLiveByRoomId(roomId: string) { + return liveQuery(() => chatDatabase.typing + .where('roomId') + .equals(roomId) + .sortBy('id') + ); + } } diff --git a/src/app/module/chat/data/repository/user-typing-live-data-source.service.ts b/src/app/module/chat/data/repository/user-typing-live-data-source.service.ts deleted file mode 100644 index 7786c4d1d..000000000 --- a/src/app/module/chat/data/repository/user-typing-live-data-source.service.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from '@angular/core'; -import { SignalRService } from '../../infra/socket/signal-r.service'; -import { SessionStore } from 'src/app/store/session.service'; - -@Injectable({ - providedIn: 'root' -}) -export class UserTypingRemoteRepositoryService { - - constructor( - private socket: SignalRService - ) { } - - sendTyping(roomId) { - return this.socket.sendData({ - method: 'Typing', - data: { - roomId, - UserName:SessionStore.user.FullName, - userId:SessionStore.user.UserId - }, - }) - } - -} diff --git a/src/app/module/chat/domain/use-case/room-create-use-case.service.ts b/src/app/module/chat/domain/use-case/room-create-use-case.service.ts index e4c99793d..449730e96 100644 --- a/src/app/module/chat/domain/use-case/room-create-use-case.service.ts +++ b/src/app/module/chat/domain/use-case/room-create-use-case.service.ts @@ -2,9 +2,11 @@ import { Injectable } from '@angular/core'; import { create } from 'domain'; import { SessionStore } from 'src/app/store/session.service'; import { RoomRemoteDataSourceService } from '../../data/repository/room/room-remote-repository.service'; +import { RoomSocketRepositoryService } from '../../data/repository/room/room-socket-repository.service'; import { captureAndReraiseAsync } from 'src/app/services/decorators/captureAndReraiseAsync'; import { RoomInputDTO } from '../../data/dto/room/roomInputDTO'; import { RoomLocalRepository } from '../../data/repository/room/room-local-repository.service'; +import { TracingType, XTracerAsync } from 'src/app/services/monitoring/opentelemetry/tracer'; @Injectable({ providedIn: 'root' @@ -14,31 +16,36 @@ export class CreateRoomUseCaseService { constructor( private roomRemoteDataSourceService: RoomRemoteDataSourceService, private roomLocalDataSourceService: RoomLocalRepository, + private RoomSocketRepositoryService: RoomSocketRepositoryService ) { } - @captureAndReraiseAsync('RoomRepositoryService/create') - async execute(data: RoomInputDTO) { + @XTracerAsync({name:'room-create-use-case.service', module:'chat', bugPrint: true, waitNThrow: 5000}) + async execute(data: RoomInputDTO, tracing?: TracingType) { - const result = await this.roomRemoteDataSourceService.createRoom(data) + const result = await this.RoomSocketRepositoryService.CreateGroup(data) + // const result = await this.roomRemoteDataSourceService.createRoom(data) + if(result.isOk()) { - if(!result.value.data.createdBy) { + console.log(result.value) + if(!result?.value?.createdBy) { let dataObject; - result.value.data.createdBy = { + result.value.createdBy = { wxeMail: SessionStore.user.Email, wxFullName: SessionStore.user.FullName, wxUserId: SessionStore.user.UserId, } - - dataObject = result.value.data + dataObject = result.value } - - const localResult = await this.roomLocalDataSourceService.createRoom(result.value.data) + const localResult = await this.roomLocalDataSourceService.createRoom(result.value) return localResult.map(e => result.value) + } else { + tracing.hasError("socket close"); + console.log(result.error) } return result diff --git a/src/app/module/chat/domain/use-case/typing-send-use-case.service.ts b/src/app/module/chat/domain/use-case/typing-send-use-case.service.ts index f72f0fcc3..ed8e052bc 100644 --- a/src/app/module/chat/domain/use-case/typing-send-use-case.service.ts +++ b/src/app/module/chat/domain/use-case/typing-send-use-case.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { SignalRService } from '../../infra/socket/signal-r.service'; import { SessionStore } from 'src/app/store/session.service'; -import { UserTypingRemoteRepositoryService } from '../../data/repository/user-typing-live-data-source.service'; +import { UserTypingRemoteRepositoryService } from '../../data/repository/typing/user-typing-live-data-source.service'; @Injectable({ providedIn: 'root' diff --git a/src/app/module/chat/infra/database/dexie/schema/typing.ts b/src/app/module/chat/infra/database/dexie/schema/typing.ts index 7f13af779..50ac388d7 100644 --- a/src/app/module/chat/infra/database/dexie/schema/typing.ts +++ b/src/app/module/chat/infra/database/dexie/schema/typing.ts @@ -3,12 +3,11 @@ import { EntityTable } from 'Dexie'; export const TypingTableSchema = z.object({ id: z.string().optional(), - userId: z.string().optional(), + userId: z.number().optional(), userName: z.string(), - chatRoomId: z.string(), - entryDate: z.string() + roomId: z.string(), }) export type TypingTable = z.infer export type DexieTypingsTable = EntityTable; -export const TypingTableColumn = 'id, userId, userName, chatRoomId, entryDate' +export const TypingTableColumn = 'id, userId, userName, roomId, entryDate' diff --git a/src/app/module/chat/infra/socket/signal-r.service.ts b/src/app/module/chat/infra/socket/signal-r.service.ts index f7342d068..ab8e02490 100644 --- a/src/app/module/chat/infra/socket/signal-r.service.ts +++ b/src/app/module/chat/infra/socket/signal-r.service.ts @@ -5,7 +5,6 @@ import { SignalRConnection, SocketMessage } from './signalR'; import { Plugins } from '@capacitor/core'; import { UserTypingDTO } from '../../data/dto/typing/typingInputDTO'; import { MessageOutPutDataDTO } from '../../data/dto/message/messageOutputDTO'; -import { MessageDeleteInputDTO } from '../../data/dto/message/messageDeleteInputDTO'; import { z } from 'zod'; import { filter, map, switchMap } from 'rxjs/operators'; import { Result } from 'neverthrow'; @@ -27,7 +26,7 @@ export type ISignalRInput = z.infer; providedIn: 'root' }) export class SignalRService { - private connection: SignalRConnection; + private connection!: SignalRConnection; private connectingSubject: BehaviorSubject = new BehaviorSubject(null); private sendDataSubject: BehaviorSubject<{method: string, data: any}> = new BehaviorSubject<{method: string, data: any}>(null); diff --git a/src/app/module/chat/infra/socket/signalR.ts b/src/app/module/chat/infra/socket/signalR.ts index 6f1676de2..31915ecd9 100644 --- a/src/app/module/chat/infra/socket/signalR.ts +++ b/src/app/module/chat/infra/socket/signalR.ts @@ -16,6 +16,11 @@ export interface SocketMessage { data: T } +export enum EnumSocketError { + catch = 1, + close +} + export class SignalRConnection { private hubConnection: signalR.HubConnection; @@ -73,9 +78,16 @@ export class SignalRConnection { this.disconnectSubject.next(true) this.pendingRequests.forEach((_, requestId) => { - const { reject } = this.pendingRequests.get(requestId); - reject(err(false)); - this.pendingRequests.delete(requestId); + const data = this.pendingRequests.get(requestId); + + if(data) { + const { reject } = data + reject(err({ + type: EnumSocketError.close + })); + this.pendingRequests.delete(requestId); + } + }); if(this.reconnect) { @@ -111,16 +123,25 @@ export class SignalRConnection { return new Promise((resolve, reject) => { if(this.connectionStateSubject.value == true) { + try { + this.pendingRequests.set(input.data.requestId, { resolve, reject: (data: any) => resolve(data) }); + + this.hubConnection.invoke(input.method, input.data) - this.hubConnection.invoke(input.method, input.data) + this.sendDataSubject.pipe( + filter((message) => input.data.requestId == message?.data.requestId), + first() + ).subscribe(value => { + resolve(ok(value.data as unknown as T)) + console.log('Received valid value:', value); + }); + + } catch(error) { + resolve(err({ + type: EnumSocketError.catch + })) + } - this.sendDataSubject.pipe( - filter((message) => input.data.requestId == message?.data.requestId), - first() - ).subscribe(value => { - resolve(ok(value.data as unknown as T)) - console.log('Received valid value:', value); - }); } else { this.sendLaterSubject.next({method: 'SendMessage', args: input}) @@ -134,10 +155,11 @@ export class SignalRConnection { const methods = ['ReceiveMessage', 'TypingMessage', 'AvailableUsers', 'ReadAt', 'DeleteMessage', 'UpdateMessage', 'GroupAddedMembers', - 'GroupDeletedMembers'] + 'GroupDeletedMembers', 'UserAddGroup'] for(const method of methods) { - this.hubConnection.on(method, (message: MessageOutPutDataDTO) => { + this.hubConnection.on(method, (message: any) => { + console.log({message}) this.messageSubject.next(message); this.sendDataSubject.next({ method: method, @@ -147,11 +169,6 @@ export class SignalRConnection { } } - public getMessages() { - return this.messageSubject.asObservable() - } - - public getConnectionState(): Observable { return this.connectionStateSubject.asObservable(); } @@ -160,8 +177,6 @@ export class SignalRConnection { return this.disconnectSubject.asObservable(); } - - public getData() { return this.sendDataSubject.asObservable() } @@ -174,6 +189,18 @@ export class SignalRConnection { .then(() => { console.log('Connection closed by user'); this.connectionStateSubject.next(false); + this.pendingRequests.forEach((_, requestId) => { + const data = this.pendingRequests.get(requestId); + + if(data) { + const { reject } = data + reject(err({ + type: EnumSocketError.close + })); + this.pendingRequests.delete(requestId); + } + + }); }) .catch(err => console.log('Error while closing connection: ' + err)); } diff --git a/src/app/module/chat/tsconfig.json b/src/app/module/chat/tsconfig.json index f36dbb47c..360475c49 100644 --- a/src/app/module/chat/tsconfig.json +++ b/src/app/module/chat/tsconfig.json @@ -1,8 +1,29 @@ { + "compileOnSave": false, "compilerOptions": { "strict": true, - "target": "ES2020", - "module": "CommonJS" + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "skipLibCheck": true, + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "module": "es2020", + "moduleResolution": "node", + "importHelpers": true, + "target": "es2017", + //"target": "es5", + "lib": [ + "es2018", + "dom" + ], + "inlineSources": true, + "sourceRoot": "/" + }, + "angularCompilerOptions": { + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true }, "include": [ "./**/*.ts" // Include all TypeScript files in the current directory and subdirectories diff --git a/src/app/ui/chat/component/group-messages/group-contacts/group-contacts.page.ts b/src/app/ui/chat/component/group-messages/group-contacts/group-contacts.page.ts index 3ab2de666..a4efb7bfd 100644 --- a/src/app/ui/chat/component/group-messages/group-contacts/group-contacts.page.ts +++ b/src/app/ui/chat/component/group-messages/group-contacts/group-contacts.page.ts @@ -60,7 +60,6 @@ export class GroupContactsPage implements OnInit { if(addMembers.isOk()) { - console.log('addMembers', addMembers) // this.addContacts(this.roomId); this.openGroupMessage.emit(this.roomId); this.chatServiceService.getRoomById(this.roomId); diff --git a/src/app/ui/chat/component/messages/contacts/contacts.page.ts b/src/app/ui/chat/component/messages/contacts/contacts.page.ts index 49417da07..d8777f790 100644 --- a/src/app/ui/chat/component/messages/contacts/contacts.page.ts +++ b/src/app/ui/chat/component/messages/contacts/contacts.page.ts @@ -167,7 +167,7 @@ export class ContactsPage implements OnInit { if(result.isOk()) { - this.openMessage.emit(result.value.data.id); + this.openMessage.emit(result.value.id); } else if(result.error instanceof HttpErrorResponse) { this.httpErrorHandle.httpStatusHandle(result.error) diff --git a/src/app/ui/chat/component/messages/messages.page.html b/src/app/ui/chat/component/messages/messages.page.html index be438a18b..03b0992ec 100644 --- a/src/app/ui/chat/component/messages/messages.page.html +++ b/src/app/ui/chat/component/messages/messages.page.html @@ -8,7 +8,7 @@
- - +
diff --git a/src/app/ui/chat/component/messages/messages.page.ts b/src/app/ui/chat/component/messages/messages.page.ts index 506bd3b9c..909e8c835 100644 --- a/src/app/ui/chat/component/messages/messages.page.ts +++ b/src/app/ui/chat/component/messages/messages.page.ts @@ -40,8 +40,8 @@ import { SpeakerService, StartRecordingResultError, StopRecordingResultError } f import { compressImageBase64 } from 'src/app/utils/imageCompressore'; import { ChatPopoverPage } from '../../modal/chat-popover/chat-popover.page'; import { LastMessage } from '../../utils/lastMessage'; -import { UserTypingLocalRepository } from 'src/app/module/chat/data/repository/user-typing-local-data-source.service'; -import { UserTypingRemoteRepositoryService } from 'src/app/module/chat/data/repository/user-typing-live-data-source.service'; +import { UserTypingLocalRepository } from 'src/app/module/chat/data/repository/typing/user-typing-local-data-source.service'; +import { UserTypingRemoteRepositoryService } from 'src/app/module/chat/data/repository/typing/user-typing-live-data-source.service'; import { MessageLocalDataSourceService } from 'src/app/module/chat/data/repository/message/message-local-data-source.service'; import { RoomType } from "src/app/module/chat/domain/entity/group"; import { RoomTable } from 'src/app/module/chat/infra/database/dexie/schema/room'; @@ -67,6 +67,8 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy downloadProgess = 0; roomType!: RoomType + RoomTypeEnum = RoomType + @Input() roomId: string; @Input() showMessages: string; @@ -137,6 +139,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy messageDeleteSubject: Subscription messageUpdateSubject: Subscription messageSendSubject: Subscription + messageTypingSubject: Subscription messages1: {[key: string]: MessageEntity[]} = {} MessageAttachmentFileType = MessageAttachmentFileType @@ -177,7 +180,6 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy ngOnChanges(changes: SimpleChanges): void { - this.roomData$ = this.RoomLocalRepository.getRoomByIdLive(this.roomId) this.roomData$.subscribe(e => { @@ -198,20 +200,34 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy this.roomMembers$ = this.MemberListLocalRepository.getRoomMemberByIdLive(this.roomId).pipe( tap((members) => { this.totalMembers = members.length + for(const member of members) { + if(member.wxUserId == SessionStore.user.UserId) { + this.isAdmin = member.isAdmin + } + } }) ) this.roomStatus$ = this.MemberListLocalRepository.allMemberOnline(this.roomId) this.chatServiceService.getRoomById(this.roomId) - - this.userTypingLocalRepository.getUserTypingLive().subscribe((e) => { - const arrayNames = e.map(e => e.userName) + + this.messageTypingSubject?.unsubscribe() + this.messageTypingSubject = this.userTypingLocalRepository.getUserTypingLiveByRoomId(this.roomId).subscribe((e) => { + const arrayNames = e.filter((b)=> b.userId != SessionStore.user.UserId).map(e => e.userName) this.userTyping$ = e as any const uniqueArray = [...new Set(arrayNames)]; - (this.myInputRef.nativeElement as HTMLDivElement).innerHTML = '::'+ uniqueArray - }) + console.log({uniqueArray}) + + if(uniqueArray.length >= 1) { + (this.myInputRef.nativeElement as HTMLDivElement).innerHTML = uniqueArray + '...' + } else { + (this.myInputRef.nativeElement as HTMLDivElement).innerHTML = '' + } + + }) as any + } messageStatus(message: MessageEntity) { @@ -242,7 +258,9 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy this.messages1[this.roomId] = [] this.messages1[this.roomId] = messages - this.messages1[this.roomId].push(LastMessage) + if(messages.length >= 1) { + this.messages1[this.roomId].push(LastMessage) + } this.loadAttachment() setTimeout(() => { diff --git a/src/app/ui/chat/component/new-group/new-group.page.ts b/src/app/ui/chat/component/new-group/new-group.page.ts index 18ee20c04..88475a0b8 100644 --- a/src/app/ui/chat/component/new-group/new-group.page.ts +++ b/src/app/ui/chat/component/new-group/new-group.page.ts @@ -11,6 +11,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorHandle } from 'src/app/services/http-error-handle.service' import { ChatServiceService } from 'src/app/module/chat/domain/chat-service.service' import { UDate } from 'src/app/utils/date'; +import { ToastService } from 'src/app/services/toast.service'; @Component({ selector: 'app-new-group', @@ -53,7 +54,8 @@ export class NewGroupPage implements OnInit{ private RouteService: RouteService, private viewContainerRef: ViewContainerRef, private httpErrorHandle: HttpErrorHandle, - private ChatServiceService: ChatServiceService + private ChatServiceService: ChatServiceService, + private toastService: ToastService, ) { this.loggedUserChat = SessionStore.user.ChatData['data']; @@ -130,9 +132,11 @@ export class NewGroupPage implements OnInit{ }) if(result.isOk()) { - this.addGroupMessage.emit(result.value.data.id); + this.addGroupMessage.emit(result.value.id); } else if(result.error instanceof HttpErrorResponse) { this.httpErrorHandle.httpStatusHandle(result.error) + } else { + this.toastService._badRequest('Por favor, contacta um administrador.'); } } diff --git a/src/app/ui/chat/modal/chat-popover/chat-popover.page.html b/src/app/ui/chat/modal/chat-popover/chat-popover.page.html index 365ca9263..f8f62105a 100644 --- a/src/app/ui/chat/modal/chat-popover/chat-popover.page.html +++ b/src/app/ui/chat/modal/chat-popover/chat-popover.page.html @@ -19,13 +19,13 @@
- + - - +
- + diff --git a/src/app/ui/chat/modal/messages/messages.page.ts b/src/app/ui/chat/modal/messages/messages.page.ts index 2f00d5772..91b2e3c3f 100644 --- a/src/app/ui/chat/modal/messages/messages.page.ts +++ b/src/app/ui/chat/modal/messages/messages.page.ts @@ -42,8 +42,8 @@ import { allowedDocExtension } from 'src/app/utils/allowedDocExtension'; import { JSFileToDataUrl } from 'src/app/utils/ToBase64'; import { RoomLocalRepository } from 'src/app/module/chat/data/repository/room/room-local-repository.service' import { MemberListLocalRepository } from 'src/app/module/chat/data/repository/member-list-local-repository.service' -import { UserTypingLocalRepository } from 'src/app/module/chat/data/repository/user-typing-local-data-source.service'; -import { UserTypingRemoteRepositoryService } from 'src/app/module/chat/data/repository/user-typing-live-data-source.service'; +import { UserTypingLocalRepository } from 'src/app/module/chat/data/repository/typing/user-typing-local-data-source.service'; +import { UserTypingRemoteRepositoryService } from 'src/app/module/chat/data/repository/typing/user-typing-live-data-source.service'; import { MessageLocalDataSourceService } from 'src/app/module/chat/data/repository/message/message-local-data-source.service'; import { MessageEnum } from 'src/app/module/chat/domain/use-case/message-create-use-case.service'; import { RoomType } from "src/app/module/chat/domain/entity/group"; diff --git a/src/app/ui/chat/requirements.readme.md b/src/app/ui/chat/requirements.readme.md new file mode 100644 index 000000000..eb491636c --- /dev/null +++ b/src/app/ui/chat/requirements.readme.md @@ -0,0 +1,31 @@ +# Gabinete Digital Chat + +## Descrição + +Este modulo que permite aos utilizadores criar grupos, enviar mensagens em tempo real e visualizar detalhes do grupo. O administrador do grupo é destacado na lista de membros, proporcionando uma forma clara e eficaz de gerir a comunicação em grupos. + +## Funcionalidades + + +- **Exibição de Detalhes do Grupo**: Visualização de informações como nome do grupo, lista de membros e destaque do administrador. + +## Requisitos + +### Requisitos Funcionais + +- Exibição de detalhes do grupo e destaque do administrador. + +### Requisitos Não Funcionais + +- **Usabilidade** + - A interface do utilizador deve ser intuitiva e fácil de navegar. + - Os detalhes do grupo e a identificação do administrador devem ser claramente visíveis e de fácil acesso. + + +## Utilização + + +- **Gestão de Grupos**: O administrador do grupo pode adicionar ou remover membros e alterar os detalhes do grupo. \ No newline at end of file