mirror of
https://code.equilibrium.co.ao/ITO/doneit-web.git
synced 2026-04-19 21:06:06 +00:00
add chat to mobile
This commit is contained in:
@@ -3,7 +3,7 @@ import { HttpService } from 'src/app/services/http.service';
|
||||
import { MessageInputDTO, MessageInputDTOSchema } from '../../dto/message/messageInputDtO';
|
||||
import { ValidateSchema } from 'src/app/services/decorators/validate-schema.decorator';
|
||||
import { APIReturn } from 'src/app/services/decorators/api-validate-schema.decorator';
|
||||
import { MessageOutPutDTO, MessageOutPutDTOSchema } from '../../dto/message/messageOutputDTO';
|
||||
import { MessageOutPutDataDTOSchema, MessageOutPutDTO, MessageOutPutDTOSchema } from '../../dto/message/messageOutputDTO';
|
||||
import { DataSourceReturn } from 'src/app/services/Repositorys/type';
|
||||
|
||||
@Injectable({
|
||||
@@ -15,7 +15,7 @@ export class MessageRemoteDataSourceService {
|
||||
|
||||
constructor(private httpService: HttpService) {}
|
||||
|
||||
@APIReturn(MessageOutPutDTOSchema, 'get/Messages')
|
||||
@APIReturn(MessageOutPutDTOSchema, 'post/Messages')
|
||||
@ValidateSchema(MessageInputDTOSchema)
|
||||
async sendMessage(data: MessageInputDTO) {
|
||||
return await this.httpService.post<MessageOutPutDTO>(`${this.baseUrl}/Messages`, data);
|
||||
@@ -25,6 +25,7 @@ export class MessageRemoteDataSourceService {
|
||||
return await this.httpService.post<any>(`${this.baseUrl}/Messages/${id}/React`, reaction);
|
||||
}
|
||||
|
||||
@APIReturn(MessageOutPutDTOSchema, 'get/Messages')
|
||||
async getMessagesFromRoom(id: string): DataSourceReturn<MessageOutPutDTO> {
|
||||
return await this.httpService.get(`${this.baseUrl}/Room/${id}/Messages`);
|
||||
}
|
||||
|
||||
@@ -12,24 +12,23 @@ export enum MessageAttachmentFileType {
|
||||
Video
|
||||
}
|
||||
|
||||
const DataSchema = z.object({
|
||||
export const MessageOutPutDataDTOSchema = z.object({
|
||||
id: z.string(),
|
||||
roomId: z.string(),
|
||||
wxUserId: z.number(),
|
||||
sender: z.object({
|
||||
wxUserId: z.number(),
|
||||
wxFullName: z.string(),
|
||||
wxeMail: z.string(),
|
||||
userPhoto: z.string().optional()
|
||||
}).nullable(),
|
||||
message: z.string(),
|
||||
message: z.string().nullable(),
|
||||
messageType: z.number(),
|
||||
sentAt: z.string(),
|
||||
deliverAt: z.string().datetime().nullable(),
|
||||
canEdit: z.boolean(),
|
||||
oneShot: z.boolean(),
|
||||
requireUnlock: z.boolean(),
|
||||
requestId: z.string(),
|
||||
requestId: z.string().optional(),
|
||||
reactions: z.object({
|
||||
id: z.string(),
|
||||
reactedAt: z.string(),
|
||||
@@ -50,8 +49,8 @@ const DataSchema = z.object({
|
||||
export const MessageOutPutDTOSchema = z.object({
|
||||
success: z.boolean(),
|
||||
message: z.string(),
|
||||
data: DataSchema.array()
|
||||
data: MessageOutPutDataDTOSchema.array()
|
||||
});
|
||||
|
||||
export type MessageOutPutDataDTO = z.infer<typeof DataSchema>
|
||||
export type MessageOutPutDataDTO = z.infer<typeof MessageOutPutDataDTOSchema>
|
||||
export type MessageOutPutDTO = z.infer<typeof MessageOutPutDTOSchema>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { z } from 'zod';
|
||||
import { MessageRepositoryService } from '../../data/repository/message-respository.service';
|
||||
import { ValidateSchema } from 'src/app/services/decorators/validate-schema.decorator';
|
||||
import { SafeValidateSchema, ValidateSchema } from 'src/app/services/decorators/validate-schema.decorator';
|
||||
|
||||
export const MessageDeleteInputDTOSchema = z.object({
|
||||
requestId: z.string().optional(),
|
||||
@@ -20,7 +20,7 @@ export class MessageDeleteLiveUseCaseService {
|
||||
public repository: MessageRepositoryService
|
||||
) { }
|
||||
|
||||
@ValidateSchema(MessageDeleteInputDTOSchema)
|
||||
@SafeValidateSchema(MessageDeleteInputDTOSchema, 'MessageDeleteLiveUseCaseService')
|
||||
async execute(data: MessageDeleteInputDTO) {
|
||||
return this.repository.sendMessageDelete(data)
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,6 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MessageLocalDataSourceService } from '../../../data/data-source/message/message-local-data-source.service';
|
||||
import { MessageOutPutDataDTO } from '../../../data/dto/message/messageOutputDTO';
|
||||
import { MessageOutPutDataDTO, MessageOutPutDataDTOSchema } from '../../../data/dto/message/messageOutputDTO';
|
||||
import { SafeValidateSchema, ValidateSchema } from 'src/app/services/decorators/validate-schema.decorator';
|
||||
import { MessageInputDTOSchema } from '../../../data/dto/message/messageInputDtO';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -12,6 +14,7 @@ export class SocketMessageUpdateUseCaseService {
|
||||
) { }
|
||||
|
||||
|
||||
@SafeValidateSchema(MessageOutPutDataDTOSchema, 'SocketMessageUpdateUseCaseService')
|
||||
async execute(data: MessageOutPutDataDTO) {
|
||||
const result = await this.messageLocalDataSourceService.messageExist({id: data.id})
|
||||
|
||||
@@ -23,7 +26,7 @@ export class SocketMessageUpdateUseCaseService {
|
||||
// delete data.chatRoomId
|
||||
|
||||
if(result.isOk()) {
|
||||
console.log('message exist', result.value)
|
||||
console.log('message exist', result.value, incomingMessage)
|
||||
return this.messageLocalDataSourceService.update(result.value.$id, incomingMessage)
|
||||
} else {
|
||||
console.log('message else')
|
||||
|
||||
@@ -5,7 +5,7 @@ export const MessageTable = z.object({
|
||||
$id: z.number().optional(),
|
||||
id: z.string().optional(),
|
||||
roomId: z.string().uuid(),
|
||||
message: z.string(),
|
||||
message: z.string().nullable(),
|
||||
messageType: z.number(),
|
||||
canEdit: z.boolean(),
|
||||
oneShot: z.boolean(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ion-header class="ion-no-border" *ngIf="ChatSystemService.getDmRoom(roomId)">
|
||||
<ion-header class="ion-no-border" >
|
||||
<ion-toolbar class="header-toolbar">
|
||||
<div class="main-header">
|
||||
<div class="header-top">
|
||||
@@ -9,10 +9,10 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="middle-container" *ngIf="!showMessageOptions">
|
||||
<div class="middle">
|
||||
<ion-label class="title">{{ChatSystemService.getDmRoom(roomId).name}}</ion-label>
|
||||
<span><ion-icon *ngIf="RochetChatConnectorService.isLogin"
|
||||
class="{{ ChatSystemService.getDmRoom(this.roomId).online }}" name="ellipse"></ion-icon></span>
|
||||
<div class="middle" *ngIf="roomData$ | async as roomData">
|
||||
<ion-label class="title">{{ roomData.roomName }}</ion-label>
|
||||
<span *ngIf="roomStatus$ | async as roomStatus"><ion-icon *ngIf="roomStatus"
|
||||
class="online" name="ellipse"></ion-icon></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,219 +42,102 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content *ngIf="ChatSystemService.getDmRoom(roomId)">
|
||||
<div (click)="handleClick()" class="message-attachments messages" #scrollMe>
|
||||
<ion-content >
|
||||
|
||||
<div class="messages-list-item-wrapper container-width-100 file" (dragover)="onDragOver($event)"
|
||||
(dragleave)="onDragLeave($event)"
|
||||
*ngFor="let msg of ChatSystemService.getDmRoom(this.roomId).messages; let last = last"
|
||||
[class.messages-list-item-wrapper-active]="msg._id == selectedMsgId">
|
||||
<div (press)="handlePress(msg._id)"
|
||||
class='file message-container incoming-{{msg.u.username!=sessionStore.user.UserName}}' *ngIf="msg.msg !=''"
|
||||
[class.dateLabel]="msg.dateLabel">
|
||||
<div class="title file" *ngIf="!msg.dateLabel">
|
||||
<ion-label>{{msg.u.name}}</ion-label>
|
||||
<span class="time">{{msg.time}}</span>
|
||||
</div>
|
||||
<div class="messages height-100 width-100 d-flex flex-column" #scrollMe >
|
||||
|
||||
<div
|
||||
*ngFor="let message of (roomMessage$ | async)" class="messages-list-item-wrapper"
|
||||
[ngClass]="{'my-message': message.sender.wxUserId === sessionStore.user.UserId, 'other-message': message.sender.wxUserId !== sessionStore.user.UserId}"
|
||||
>
|
||||
<div class="message-container">
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<div class="file">
|
||||
|
||||
<pre *ngIf="msg.delate == false" class="message-box text ma-0 font-13-rem"
|
||||
style="font-size: 0.8125rem !important;">{{msg.msg}}</pre>
|
||||
<!-- <ion-label *ngIf="msg.delate == false" class="message-box">{{msg.msg}} </ion-label> -->
|
||||
<ion-label *ngIf="msg.delate == true" class="flex-0">Apagou a mensagem</ion-label>
|
||||
|
||||
<ion-label class="float-status-all float-status" *ngIf="msg.u.username==sessionStore.user.UserName">
|
||||
|
||||
<span *ngIf="msg.online == true && !msg.manualRetry && msg.viewed == 0" class="enviado pl-10">
|
||||
Enviado</span>
|
||||
<!-- <ion-icon *ngIf="msg.messageSend == false && !msg.manualRetry" src="assets/images/clock-regular.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.messageSend == true && msg.received.length == 0 && msg.viewed.length == 0" src="assets/images/check-solid.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.messageSend && msg.received.length >= 1 && msg.viewed.length == 0" src="assets/images/check-double-solid.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.viewed.length >= 1" src="assets/images/check-double-solid -viewed.svg"></ion-icon> -->
|
||||
<span class="lido pl-10" *ngIf="msg.viewed.length >= 1"> Lido</span>
|
||||
<div *ngIf="msg.manualRetry" class="try pl-10" (click)="msg.send()">Tentar</div>
|
||||
</ion-label>
|
||||
</div>
|
||||
{{last ? scrollToBottom() : ''}}
|
||||
{{ message.message }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="msg.file && msg.delate == false">
|
||||
<div (press)="handlePress(msg._id)"
|
||||
class='message-container incoming-{{msg.u.username!=sessionStore.user.UserName}}'
|
||||
*ngIf="msg.file.type != 'application/meeting'">
|
||||
<div class="title">
|
||||
<ion-label>{{msg.u.name}}</ion-label>
|
||||
<span class="time">{{msg.time}}</span>
|
||||
<div>
|
||||
<div class="message-item-options d-flex justify-content-end">
|
||||
<fa-icon [matMenuTriggerFor]="beforeMenu" icon="chevron-down" class="message-options-icon cursor-pointer"></fa-icon>
|
||||
<mat-menu #beforeMenu="matMenu" xPosition="before">
|
||||
<button (click)="messageDelete({messageId: message.id })" class="menuButton">Apagar mensagem</button>
|
||||
<button (click)="editMessage(message)" class="menuButton">Editar mensagem</button>
|
||||
<button (click)="toggleEmojiPicker(message)" class="menuButton">Reagir mensagem</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div>
|
||||
<div *ngIf="msg.attachments" class="message-attachments">
|
||||
<div *ngFor="let file of msg.attachments let i = index">
|
||||
<div class="file" *ngIf="msg.file.type == 'application/img'" (click)="openPreview(msg)">
|
||||
<div *ngIf="!msg.attachments[0].image_url">
|
||||
<ion-item class="add-attachment-bg-color" shape="round" lines="none" type="button">
|
||||
<ion-icon name="image" class="file-icon"></ion-icon>
|
||||
<ion-label>{{"Imagem"}}</ion-label>
|
||||
<ion-icon
|
||||
*ngIf="ThemeService.currentTheme == 'gov' && msg.downloadLoader == false && msg.uploadingFile == false && msg.downloadAttachmentsTemp == 0"
|
||||
class="icon-download" src="assets/icon/theme/{{ThemeService.currentTheme}}/icons-download.svg"
|
||||
slot="end"></ion-icon>
|
||||
<ion-icon *ngIf="( msg.downloadLoader == true || msg.uploadingFile == true )"
|
||||
class="icon-download" src="assets/gif/theme/{{ThemeService.currentTheme}}/Blocks-loader.svg"
|
||||
slot="end"></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="msg.downloadAttachments == false && msg.downloadAttachmentsTemp >= 1 && msg.downloadLoader == false"
|
||||
src="assets/images/retry-svgrepo-com.svg" class="icon-download font-12"> </ion-icon>
|
||||
</ion-item>
|
||||
</div>
|
||||
<img class="d-block width-100" *ngIf="msg.attachments[0].image_url"
|
||||
src="{{msg.attachments[0].image_url}}" alt="image">
|
||||
|
||||
<ion-label class="float-status-all float-status" *ngIf="msg.u.username==sessionStore.user.UserName">
|
||||
<span *ngIf="msg.online == true && !msg.manualRetry && msg.viewed == 0" class="enviado pl-10">
|
||||
Enviado</span>
|
||||
<!-- <ion-icon *ngIf="msg.messageSend == false && !msg.manualRetry" src="assets/images/clock-regular.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.messageSend == true && msg.received.length == 0 && msg.viewed.length == 0" src="assets/images/check-solid.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.messageSend && msg.received.length >= 1 && msg.viewed.length == 0" src="assets/images/check-double-solid.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.viewed.length >= 1" src="assets/images/check-double-solid -viewed.svg"></ion-icon> -->
|
||||
<span class="lido pl-10" *ngIf="msg.viewed.length >= 1"> Lido</span>
|
||||
<div *ngIf="msg.manualRetry" class="try pl-10" (click)="msg.send()">Tentar</div>
|
||||
</ion-label>
|
||||
|
||||
</div>
|
||||
<div *ngIf="msg.file.type != 'application/img'">
|
||||
<div class="file add-attachment-bg-color" *ngIf="msg.file.type != 'application/audio'">
|
||||
<div (click)="docIndex(i); openPreview(msg)" class="file-details add-ellipsis cursor-pointer"
|
||||
*ngIf="msg.file">
|
||||
|
||||
<div *ngIf="!msg.attachments[0].image_url">
|
||||
<ion-item class="add-attachment-bg-color" shape="round" lines="none" type="button">
|
||||
|
||||
<ion-icon *ngIf="msg.attachments[0].type != 'webtrix'" name="document"
|
||||
class="file-icon"></ion-icon>
|
||||
<ion-icon *ngIf="msg.attachments[0].type == 'webtrix'" src="assets/icon/webtrix.svg"
|
||||
class="file-icon"></ion-icon>
|
||||
<ion-label>{{ file.title}}</ion-label>
|
||||
<ion-icon
|
||||
*ngIf="ThemeService.currentTheme == 'default' && msg.attachments[0].type != 'webtrix' && !( msg.downloadLoader == true || msg.uploadingFile == true ) "
|
||||
class="icon-download" src="assets/icon/theme/default/icons-download.svg"
|
||||
slot="end"></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="ThemeService.currentTheme == 'gov' && msg.attachments[0].type != 'webtrix' && !( msg.downloadLoader == true || msg.uploadingFile == true ) "
|
||||
class="icon-download" src="assets/icon/theme/gov/icons-download.svg" slot="end"></ion-icon>
|
||||
<ion-icon *ngIf="( msg.downloadLoader == true || msg.uploadingFile == true )"
|
||||
class="icon-download" src="assets/gif/theme/{{ThemeService.currentTheme}}/Blocks-loader.svg"
|
||||
slot="end"></ion-icon>
|
||||
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
<div *ngIf="msg.attachments[0].image_url">
|
||||
<span *ngIf="msg.file.type">
|
||||
<fa-icon *ngIf="msg.file.type == 'application/pdf'" icon="file-pdf"
|
||||
class="pdf-icon"></fa-icon>
|
||||
<fa-icon
|
||||
*ngIf="msg.file.type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'"
|
||||
icon="file-word" class="word-icon"></fa-icon>
|
||||
<fa-icon *ngIf="msg.file.type == 'application/word'" icon="file-word" class="word-icon">
|
||||
</fa-icon>
|
||||
<fa-icon
|
||||
*ngIf="msg.file.type == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'"
|
||||
icon="file-word" class="excel-icon"></fa-icon>
|
||||
<ion-icon *ngIf="msg.file.type == 'application/webtrix'" src="assets/icon/webtrix.svg">
|
||||
</ion-icon>
|
||||
</span>
|
||||
<ion-label class="file-title">{{file.title}}</ion-label>
|
||||
</div>
|
||||
<!-- <ion-progress-bar [type]="'indeterminate'" *ngIf="downloadLoader"></ion-progress-bar> -->
|
||||
</div>
|
||||
</div>
|
||||
<div (click)="audioPreview(msg)" class="audio-contentainer"
|
||||
*ngIf="msg.file.type == 'application/audio' && !file.title_link">
|
||||
<ion-item class="add-attachment-bg-color" shape="round" lines="none" type="button">
|
||||
<ion-icon name="mic-outline" class="file-icon"></ion-icon>
|
||||
<ion-label>{{"Mensagem de voz"}}</ion-label>
|
||||
<ion-icon
|
||||
*ngIf="ThemeService.currentTheme == 'gov' && msg.downloadLoader == false && msg.uploadingFile == false && msg.downloadAttachmentsTemp == 0"
|
||||
class="icon-download" src="assets/icon/theme/{{ThemeService.currentTheme}}/icons-download.svg"
|
||||
slot="end"></ion-icon>
|
||||
<ion-icon *ngIf="( msg.downloadLoader == true || msg.uploadingFile == true )"
|
||||
class="icon-download" src="assets/gif/theme/{{ThemeService.currentTheme}}/Blocks-loader.svg"
|
||||
slot="end"></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="msg.downloadAttachments == false && msg.downloadAttachmentsTemp >= 1 && msg.downloadLoader == false"
|
||||
src="assets/images/retry-svgrepo-com.svg" class="icon-download font-12"> </ion-icon>
|
||||
</ion-item>
|
||||
</div>
|
||||
<div class="file audio-contentainer" *ngIf="msg.file.type == 'application/audio' && file.title_link">
|
||||
<audio [src]="file.title_link | safehtml" preload="metadata" class="d-flex width-100" controls
|
||||
controlsList="nodownload noplaybackrate"></audio>
|
||||
</div>
|
||||
<div class="file-details-optional add-attachment-bg-color">
|
||||
<ion-label *ngIf="msg.file && msg.file != ''">
|
||||
<span *ngIf="file.description">{{file.description}}</span>
|
||||
<span *ngIf="file.description && msg.file.type != 'application/webtrix'"> • </span>
|
||||
|
||||
</ion-label>
|
||||
|
||||
<ion-label class="float-status-all float-status" *ngIf="msg.u.username==sessionStore.user.UserName">
|
||||
|
||||
<span *ngIf="msg.online == true && !msg.manualRetry && msg.viewed == 0" class="enviado pl-10">
|
||||
Enviado</span>
|
||||
<!-- <ion-icon *ngIf="msg.messageSend == false && !msg.manualRetry" src="assets/images/clock-regular.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.messageSend == true && msg.received.length == 0 && msg.viewed.length == 0" src="assets/images/check-solid.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.messageSend && msg.received.length >= 1 && msg.viewed.length == 0" src="assets/images/check-double-solid.svg"></ion-icon> -->
|
||||
<!-- <ion-icon *ngIf="msg.viewed.length >= 1" src="assets/images/check-double-solid -viewed.svg"></ion-icon> -->
|
||||
<span class="lido pl-10" *ngIf="msg.viewed.length >= 1"> Lido</span>
|
||||
<div *ngIf="msg.manualRetry" class="try pl-10" (click)="msg.send()">Tentar</div>
|
||||
</ion-label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{last ? scrollToBottom() : ''}}
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="msg.file.type == 'application/meeting'" class="info-meeting">
|
||||
<ion-label class="info-meeting-small">{{msg.u.name}} criou esta reunião</ion-label><br />
|
||||
<button (click)="goToEvent(msg.file)" class="btn-no-color info-meeting-normal">
|
||||
<ion-label class="info-meeting-normal">{{msg.file.subject}}</ion-label>
|
||||
</button><br />
|
||||
<ion-label class="info-meeting-medium">
|
||||
<ion-icon name="calendar-outline"></ion-icon> De {{showDateDuration(msg.file.start_date)}} a
|
||||
{{showDateDuration(msg.file.end_date)}}
|
||||
</ion-label><br />
|
||||
<ion-label class="info-meeting-medium">
|
||||
<ion-icon></ion-icon>
|
||||
<ion-icon name="location-outline"></ion-icon> {{msg.file.venue}}
|
||||
</ion-label><br />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="text-align: end;">
|
||||
|
||||
<div *ngIf="msg.file && msg.delate == true">
|
||||
Apagou a mensagem
|
||||
<div *ngIf="message.messageStatus != 'send'" style="font-size: .6875rem;">A enviar</div>
|
||||
<div *ngIf="message.messageStatus == 'send'" style="font-size: .6875rem;">Enviado</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Emoji Picker -->
|
||||
<div *ngIf="selectedMessage === message" class="emoji-picker" [ngStyle]="{'bottom': '0', 'right': '0'}">
|
||||
<span *ngFor="let emoji of emojis" (click)="addReaction(message, emoji)" class="emoji-icon">
|
||||
{{ emoji }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- current emoji -->
|
||||
<div>
|
||||
<span *ngFor="let reaction of message.reactions" class="emoji-icon">
|
||||
{{ reaction.reaction }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!--
|
||||
<ion-fab horizontal="end" vertical="bottom" slot="fixed">
|
||||
<ion-fab-button *ngIf="scrollToBottomBtn" (click)="scrollToBottomClicked()" color="light" size="small">
|
||||
<ion-fab-button *ngIf="scrollToBottomBtn" color="light" size="small">
|
||||
<ion-icon name="chevron-down"></ion-icon>
|
||||
</ion-fab-button>
|
||||
</ion-fab> -->
|
||||
|
||||
|
||||
|
||||
<ion-fab horizontal="start" vertical="bottom" slot="fixed">
|
||||
|
||||
<div #array>
|
||||
|
||||
</div>
|
||||
</ion-fab>
|
||||
|
||||
|
||||
<!-- <div *ngIf="userTyping$?.typingList?.typingList">
|
||||
|
||||
<div
|
||||
*ngFor="let message of userTyping$.typingList.typingList " class="messages-list-item-wrapper"
|
||||
>
|
||||
{{ message.userName }}
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<!-- <div *ngIf="userTyping$" class="header-bottom-contacts" >
|
||||
|
||||
<div *ngFor="let typing of userTyping$; let i = index">
|
||||
{{ typing.userName }}<div *ngIf="i < userTyping$.length - 1">, </div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
|
||||
</ion-content>
|
||||
|
||||
<ion-footer *ngIf="ChatSystemService.getDmRoom(roomId)" (click)="ChatSystemService.getDmRoom(roomId).sendReadMessage()">
|
||||
<ion-footer>
|
||||
|
||||
<div class="typing" *ngIf="ChatSystemService.getDmRoom(roomId).otherUserType == true">
|
||||
<!-- <div class="typing" *ngIf="ChatSystemService.getDmRoom(roomId).otherUserType == true">
|
||||
<ngx-letters-avatar [avatarName]="ChatSystemService.getGroupRoom(roomId).name" [width]="30" [circular]="true"
|
||||
fontFamily="Roboto"></ngx-letters-avatar>
|
||||
está a escrever ...
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="width-100 pl-20 pr-20">
|
||||
<span *ngIf="!lastAudioRecorded">{{durationDisplay}}</span>
|
||||
<audio [src]="audioRecorded" class="d-flex width-100 mt-10 mb-10" *ngIf="lastAudioRecorded" controls
|
||||
@@ -277,8 +160,8 @@
|
||||
<div *ngIf="!recording && !lastAudioRecorded" class="type-message">
|
||||
<ion-textarea *ngIf="allowTyping" autocomplete="on" autocorrect="on" spellcheck="true" clearOnEdit="true"
|
||||
placeholder="Escrever uma mensagem" auto-grow class="message-input" rows="1"
|
||||
[(ngModel)]="ChatSystemService.getDmRoom(roomId).message"
|
||||
(ionChange)="ChatSystemService.getDmRoom(roomId).sendTyping()"></ion-textarea>
|
||||
[(ngModel)]="textField"
|
||||
(ionChange)="sendTyping()"></ion-textarea>
|
||||
</div>
|
||||
<div *ngIf="recording" class="d-flex align-items-center justify-content-center">
|
||||
<button (click)="stopRecording()" class="btn-no-color d-flex align-items-center justify-content-center">
|
||||
@@ -287,21 +170,21 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button #recordbtn *ngIf="!ChatSystemService.getDmRoom(roomId).message && !lastAudioRecorded"
|
||||
<button #recordbtn *ngIf="!textField && !lastAudioRecorded"
|
||||
(click)="startRecording()" class="btn-no-color">
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send"
|
||||
src="assets/icon/theme/default/icons-chat-record-audio.svg"></ion-icon>
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send"
|
||||
src="assets/icon/theme/gov/icons-chat-record-audio.svg"></ion-icon>
|
||||
</button>
|
||||
<button *ngIf="ChatSystemService.getDmRoom(roomId).message" class="btn-no-color" (click)="sendMessage()"
|
||||
<button *ngIf="textField" class="btn-no-color" (click)="sendMessage()"
|
||||
class="btn-no-color">
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send"
|
||||
src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send"
|
||||
src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
|
||||
</button>
|
||||
<button *ngIf="!ChatSystemService.getDmRoom(roomId).message && lastAudioRecorded"
|
||||
<button *ngIf="!textField && lastAudioRecorded"
|
||||
(click)="sendAudio(lastAudioRecorded)" class="btn-no-color">
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send"
|
||||
src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
|
||||
@@ -311,4 +194,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ion-footer>
|
||||
</ion-footer>
|
||||
|
||||
@@ -416,3 +416,81 @@ button::-moz-focus-inner {
|
||||
width: 111px !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.message-item-options {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.emoji-picker {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-ms-border-radius: 5px;
|
||||
-o-border-radius: 5px;
|
||||
}
|
||||
|
||||
.emoji-icon {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.messages {
|
||||
font-size: rem(13);
|
||||
font-family: Roboto;
|
||||
overflow: auto;
|
||||
|
||||
//set scroll do bottom
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// overflow-x: hidden;
|
||||
// overflow-y: auto;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// word-wrap: break-word;
|
||||
// -webkit-overflow-scrolling: touch;
|
||||
|
||||
.other-message,
|
||||
.my-message {
|
||||
|
||||
.message-container {
|
||||
padding: 15px 20px;
|
||||
margin: 10px 20px 10px 75px;
|
||||
background: var(--chat-incoming-msg-color);
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.other-message {
|
||||
display: flex;
|
||||
/* justify-content: flex-end; */
|
||||
align-items: start;
|
||||
flex-direction: column;
|
||||
// float: left;
|
||||
// Styles for incoming messages from other users
|
||||
justify-content: flex-start;
|
||||
.message-container {
|
||||
padding: 15px 20px;
|
||||
margin: 10px 75px 10px 20px;
|
||||
background: #ebebeb;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.my-message {
|
||||
|
||||
display: flex;
|
||||
/* justify-content: flex-end; */
|
||||
align-items: end;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,21 @@ import { RochetChatConnectorService } from 'src/app/services/chat/rochet-chat-co
|
||||
import { FileValidatorService } from "src/app/services/file/file-validator.service"
|
||||
import { sanitize } from "sanitize-filename-ts";
|
||||
import { FilePicker } from '@capawesome/capacitor-file-picker';
|
||||
//======
|
||||
import { Observable as DexieObservable } from 'Dexie';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { MessageRepositoryService } from 'src/app/module/chat/data/repository/message-respository.service'
|
||||
import { RoomRepositoryService } from 'src/app/module/chat/data/repository/room-repository.service'
|
||||
import { MessageTable } from 'src/app/module/chat/infra/database/dexie/schema/message';
|
||||
import { MessageInputDTO } from 'src/app/module/chat/data/dto/message/messageInputDtO';
|
||||
import { RoomListItemOutPutDTO } from 'src/app/module/chat/data/dto/room/roomListOutputDTO';
|
||||
import { UserTypingServiceRepository } from 'src/app/module/chat/data/repository/user-typing-repository.service';
|
||||
import { ChatServiceService } from 'src/app/module/chat/domain/chat-service.service';
|
||||
import { EditMessagePage } from 'src/app/modals/edit-message/edit-message.page';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { MessageEntity } from 'src/app/module/chat/domain/entity/message';
|
||||
import { MemberTable } from 'src/app/module/chat/infra/database/dexie/schema/members';
|
||||
import { TypingTable } from 'src/app/module/chat/infra/database/dexie/schema/typing';
|
||||
|
||||
|
||||
|
||||
@@ -104,6 +119,18 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
|
||||
audioPermissionStatus: 'granted' | 'denied' | 'prompt' | null = null
|
||||
sessionStore = SessionStore
|
||||
|
||||
roomData$: DexieObservable<RoomListItemOutPutDTO | undefined>
|
||||
roomStatus$: DexieObservable<Boolean >
|
||||
roomMessage$: DexieObservable<MessageTable[]>
|
||||
roomMembers$: DexieObservable<MemberTable[] | undefined>
|
||||
//userTyping$: DexieObservable<TypingTable[] | undefined>
|
||||
userTyping$: TypingTable[] | undefined
|
||||
newMessagesStream!: Subscription
|
||||
|
||||
selectedMessage: any = null;
|
||||
emojis: string[] = ['😊', '😂', '❤️', '👍', '😢']; // Add more emojis as needed
|
||||
textField = ''
|
||||
|
||||
constructor(
|
||||
public popoverController: PopoverController,
|
||||
private modalController: ModalController,
|
||||
@@ -124,31 +151,54 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
|
||||
private router: Router,
|
||||
public RochetChatConnectorService: RochetChatConnectorService,
|
||||
private FileValidatorService: FileValidatorService,
|
||||
///
|
||||
private roomRepositoryService: RoomRepositoryService,
|
||||
private messageRepositoryService: MessageRepositoryService,
|
||||
private userTypingServiceRepository: UserTypingServiceRepository,
|
||||
private chatServiceService: ChatServiceService
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
this.loggedUser = SessionStore.user.ChatData['data'];
|
||||
this.roomId = this.navParams.get('roomId');
|
||||
this.roomId = this.navParams.get('roomId');
|
||||
|
||||
|
||||
window.onresize = (event) => {
|
||||
if (window.innerWidth > 701) {
|
||||
this.modalController.dismiss();
|
||||
}
|
||||
}
|
||||
this.roomData$ = this.roomRepositoryService.getItemByIdLive(this.roomId)
|
||||
|
||||
this.roomMessage$ = this.messageRepositoryService.getItemsLive(this.roomId)
|
||||
|
||||
this.ChatSystemService.getDmRoom(this.roomId).loadHistory({})
|
||||
this.ChatSystemService.getDmRoom(this.roomId).scrollDown = this.scrollToBottomClicked
|
||||
this.ChatSystemService.openRoom(this.roomId)
|
||||
this.roomMembers$ = this.roomRepositoryService.getRoomMemberByIdLive(this.roomId) as any
|
||||
this.roomStatus$ = this.roomRepositoryService.getRoomStatus(this.roomId)
|
||||
this.roomRepositoryService.getRoomById(this.roomId)
|
||||
this.messageRepositoryService.listAllMessagesByRoomId(this.roomId)
|
||||
|
||||
this.userTypingServiceRepository.getUserTypingLive().subscribe((e) => {
|
||||
const arrayNames = e.map(e => e.userName)
|
||||
this.userTyping$ = e as any
|
||||
|
||||
const uniqueArray = [...new Set(arrayNames)];
|
||||
|
||||
//(this.myInputRef.nativeElement as HTMLDivElement).innerHTML = '::'+ uniqueArray
|
||||
})
|
||||
|
||||
this.newMessagesStream?.unsubscribe()
|
||||
this.newMessagesStream = this.messageRepositoryService.subscribeToNewMessages(this.roomId).subscribe((e) => {
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollToBottomClicked()
|
||||
}, 150)
|
||||
} catch (error) {
|
||||
//alert(error)
|
||||
}
|
||||
}, 200)
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollToBottomClicked()
|
||||
}, 500)
|
||||
|
||||
})
|
||||
|
||||
//this.userTyping$ = this.userTypingMemoryDataSource.select(state => state) as any
|
||||
|
||||
// let a = this.userTypingMemoryDataSource.select(state => state).subscribe((e) => {
|
||||
// this.userTyping$ = e as any
|
||||
// })
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1336,6 +1386,67 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
|
||||
return blob;
|
||||
}
|
||||
|
||||
|
||||
messageDelete({messageId}) {
|
||||
// this.messageRepositoryService.sendMessageDelete()
|
||||
console.log('messageId', messageId)
|
||||
this.chatServiceService.messageDelete({
|
||||
messageId: messageId,
|
||||
roomId: this.roomId,
|
||||
})
|
||||
}
|
||||
|
||||
async editMessage(message: any) {
|
||||
|
||||
const modal = await this.modalController.create({
|
||||
component: EditMessagePage,
|
||||
cssClass: '',
|
||||
componentProps: {
|
||||
message: message.message,
|
||||
roomId: this.roomId,
|
||||
}
|
||||
});
|
||||
|
||||
modal.present()
|
||||
|
||||
return modal.onDidDismiss().then((res) => {
|
||||
|
||||
this.chatServiceService.updateMessage({
|
||||
memberId: SessionStore.user.UserId,
|
||||
message: res.data.message,
|
||||
messageId: message.id,
|
||||
requestId: '',
|
||||
roomId: this.roomId
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
toggleEmojiPicker(message: any) {
|
||||
if (this.selectedMessage === message) {
|
||||
this.selectedMessage = null; // Close the picker if it's already open
|
||||
} else {
|
||||
this.selectedMessage = message; // Open the picker for the selected message
|
||||
}
|
||||
}
|
||||
|
||||
addReaction(message: any, emoji: string) {
|
||||
// Logic to add reaction to the message
|
||||
console.log(`Reacting to message ${message.id} with emoji ${emoji.codePointAt(0).toString(16)}`);
|
||||
this.selectedMessage = null; // Close the picker after adding reaction
|
||||
|
||||
this.chatServiceService.reactToMessage({
|
||||
memberId: SessionStore.user.UserId,
|
||||
messageId: message.id,
|
||||
roomId: this.roomId,
|
||||
reaction: emoji,
|
||||
requestId: ''
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
sendTyping() {
|
||||
this.userTypingServiceRepository.addUserTyping(this.roomId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ export function APIReturn(schema: z.ZodTypeAny, context: string) {
|
||||
if (error instanceof ZodError) {
|
||||
// If validation fails, throw an error with the details
|
||||
//
|
||||
ColoredLoggerService.error(error.errors, 'API unexpected data structure '+ context, schema._def.description)
|
||||
|
||||
ColoredLoggerService.error(error.errors, 'API unexpected data structure '+ context)
|
||||
console.error(result.value)
|
||||
// Capture the Zod validation error with additional context
|
||||
Sentry.withScope((scope) => {
|
||||
scope.setTag('APIReturn', 'user');
|
||||
|
||||
@@ -64,6 +64,7 @@ export function SafeValidateSchema(schema: Schema, context: string) {
|
||||
tracing?.setAttribute?.('map.error.schema-'+i, JSON.stringify(schema))
|
||||
}
|
||||
ColoredLoggerService.error(e.errors, 'socket unexpected data structure '+ context, schema._def.description)
|
||||
console.error(args[0])
|
||||
|
||||
}
|
||||
return originalMethod.apply(this, args);
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<div class="message-item-options d-flex justify-content-end">
|
||||
<fa-icon [matMenuTriggerFor]="beforeMenu" icon="chevron-down" class="message-options-icon cursor-pointer"></fa-icon>
|
||||
<mat-menu #beforeMenu="matMenu" xPosition="before">
|
||||
<button (click)="messageDelete({messageId: message.id })" class="menuButton">Apagar mensagem</button>
|
||||
<button (click)="messageDelete(message)" class="menuButton">Apagar mensagem</button>
|
||||
<button (click)="editMessage(message)" class="menuButton">Editar mensagem</button>
|
||||
<button (click)="toggleEmojiPicker(message)" class="menuButton">Reagir mensagem</button>
|
||||
</mat-menu>
|
||||
|
||||
@@ -993,7 +993,6 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
|
||||
messageDelete({messageId}) {
|
||||
// this.messageRepositoryService.sendMessageDelete()
|
||||
|
||||
this.chatServiceService.messageDelete({
|
||||
messageId: messageId,
|
||||
roomId: this.roomId,
|
||||
|
||||
Reference in New Issue
Block a user