mirror of
https://code.equilibrium.co.ao/ITO/doneit-web.git
synced 2026-04-18 20:47:54 +00:00
send direct message
This commit is contained in:
@@ -21,6 +21,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../sentry"
|
||||
},
|
||||
{
|
||||
"path": "../ionic6"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
|
||||
@@ -123,7 +123,7 @@ import { FirebaseX } from '@ionic-native/firebase-x/ngx'; */
|
||||
],
|
||||
beforeSend(event) {
|
||||
if (event.level === 'error') {
|
||||
console.log(event.exception.values[0].value)
|
||||
// console.log(event.exception.values[0].value)
|
||||
openTelemetryLogging.send({
|
||||
type: 'graylog',
|
||||
payload: {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
export interface sendReadAtInput {
|
||||
memberId: number,
|
||||
messageId:string,
|
||||
roomId: string,
|
||||
requestId: string
|
||||
}
|
||||
+199
-199
@@ -78,206 +78,206 @@ const routes: Routes = [
|
||||
],
|
||||
|
||||
},
|
||||
// {
|
||||
// path: 'agenda',
|
||||
// children: [
|
||||
// {
|
||||
// path:'',
|
||||
// loadChildren: () => AgendaPageModule
|
||||
// },
|
||||
// {
|
||||
// path:':eventId/:caller',
|
||||
// loadChildren: () => import('../pages/agenda/view-event/view-event.module').then( m => m.ViewEventPageModule),
|
||||
// },
|
||||
// {
|
||||
// path: 'eventId/:caller',
|
||||
// loadChildren: () => import('../pages/agenda/view-event/view-event.module').then( m => m.ViewEventPageModule),
|
||||
// },
|
||||
// {
|
||||
// path: 'edit-event',
|
||||
// loadChildren: () => import('../pages/agenda/edit-event/edit-event.module').then( m => m.EditEventPageModule)
|
||||
// },
|
||||
// {
|
||||
// path: 'emend-message-modal',
|
||||
// loadChildren: () => import('../pages/gabinete-digital/event-list/approve-event-modal/approve-event-modal.module').then( m => m.ApproveEventModalPageModule)
|
||||
// },
|
||||
// {
|
||||
// path: 'view-event',
|
||||
// loadChildren: () => import('../pages/agenda/view-event/view-event.module').then( m => m.ViewEventPageModule)
|
||||
// },
|
||||
// {
|
||||
// path:'event-list',
|
||||
// children: [
|
||||
// {
|
||||
// path:'',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/event-list/event-list.module').then(m => m.EventListPageModule)
|
||||
// },
|
||||
// {
|
||||
// path:'approve-event',
|
||||
// children : [
|
||||
// {
|
||||
// path:':serialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/event-list/approve-event/approve-event.module').then(m => m.ApproveEventPageModule)
|
||||
// },
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// ],
|
||||
{
|
||||
path: 'agenda',
|
||||
children: [
|
||||
{
|
||||
path:'',
|
||||
loadChildren: () => AgendaPageModule
|
||||
},
|
||||
{
|
||||
path:':eventId/:caller',
|
||||
loadChildren: () => import('../pages/agenda/view-event/view-event.module').then( m => m.ViewEventPageModule),
|
||||
},
|
||||
{
|
||||
path: 'eventId/:caller',
|
||||
loadChildren: () => import('../pages/agenda/view-event/view-event.module').then( m => m.ViewEventPageModule),
|
||||
},
|
||||
{
|
||||
path: 'edit-event',
|
||||
loadChildren: () => import('../pages/agenda/edit-event/edit-event.module').then( m => m.EditEventPageModule)
|
||||
},
|
||||
{
|
||||
path: 'emend-message-modal',
|
||||
loadChildren: () => import('../pages/gabinete-digital/event-list/approve-event-modal/approve-event-modal.module').then( m => m.ApproveEventModalPageModule)
|
||||
},
|
||||
{
|
||||
path: 'view-event',
|
||||
loadChildren: () => import('../pages/agenda/view-event/view-event.module').then( m => m.ViewEventPageModule)
|
||||
},
|
||||
{
|
||||
path:'event-list',
|
||||
children: [
|
||||
{
|
||||
path:'',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/event-list/event-list.module').then(m => m.EventListPageModule)
|
||||
},
|
||||
{
|
||||
path:'approve-event',
|
||||
children : [
|
||||
{
|
||||
path:':serialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/event-list/approve-event/approve-event.module').then(m => m.ApproveEventPageModule)
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
|
||||
// },
|
||||
// {
|
||||
// path: 'gabinete-digital',
|
||||
// children: [
|
||||
// {
|
||||
// path:'',
|
||||
// loadChildren: ()=> GabineteDigitalPageModule
|
||||
// },
|
||||
// {
|
||||
// path:'expediente',
|
||||
// children: [
|
||||
// {
|
||||
// path:'',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/expediente/expediente.module').then(m => m.ExpedientePageModule)
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/expediente/expediente-detail/expediente-detail.module').then(m => m.ExpedienteDetailPageModule)
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/expediente/expediente-detail/expediente-detail.module').then(m => m.ExpedienteDetailPageModule)
|
||||
// },
|
||||
// {
|
||||
// path:'events/:eventId/:caller',
|
||||
// loadChildren: ()=> import('../pages/events/event-detail/event-detail.module').then(m => m.EventDetailPageModule),
|
||||
// },
|
||||
// {
|
||||
// path:'expediente-task-modal',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/expediente/expedient-task-modal/expedient-task-modal.module').then(m => m.ExpedientTaskModalPageModule),
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'expedientes-pr',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/expedientes-pr/expedientes-pr.module').then(m => m.ExpedientesPrPageModule),
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/expedientes-pr/expediente-pr/expediente-pr.module').then(m => m.ExpedientePrPageModule),
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'despachos',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/despachos/despachos.module').then(m => m.DespachosPageModule),
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/despachos/despacho/despacho.module').then(m => m.DespachoPageModule),
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'despachos-pr',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/despachos-pr/despachos-pr.module').then(m => m.DespachosPrPageModule),
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/despachos-pr/despacho-pr/despacho-pr.module').then(m => m.DespachoPrPageModule),
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'pedidos',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/pedidos/pedidos.module').then(m => m.PedidosPageModule),
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/pedidos/pedido/pedido.module').then(m => m.PedidoPageModule),
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'diplomas',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/diplomas/diplomas.module').then(m => m.DiplomasPageModule),
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/diplomas/diploma/diploma.module').then(m => m.DiplomaPageModule),
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'diplomas-assinar',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/diplomas-assinar/diplomas-assinar.module').then(m => m.DiplomasAssinarPageModule),
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/diplomas-assinar/diploma-assinar/diploma-assinar.module').then(m => m.DiplomaAssinarPageModule),
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'diplomas-gerar',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/diplomas-gerar/diplomas-gerar.module').then(m => m.DiplomasGerarPageModule),
|
||||
// },
|
||||
// {
|
||||
// path:':SerialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/diplomas-gerar/diplomas-gerar/diplomas-gerar-routing.module').then(m => m.DiplomasGerarPageRoutingModule),
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path:'event-list',
|
||||
// children: [
|
||||
// {
|
||||
// path:'',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/event-list/event-list.module').then(m => m.EventListPageModule)
|
||||
// },
|
||||
// {
|
||||
// path:'approve-event',
|
||||
// children : [
|
||||
// {
|
||||
// path:':serialNumber/:caller',
|
||||
// loadChildren: ()=> import('../pages/gabinete-digital/event-list/approve-event/approve-event.module').then(m => m.ApproveEventPageModule)
|
||||
// },
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: 'events-to-approve',
|
||||
// children: [
|
||||
// {
|
||||
// path: '',
|
||||
// loadChildren: ()=> import('../shared/gabinete-digital/edit-event-to-approve/edit-event.module')
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ],
|
||||
// },
|
||||
},
|
||||
{
|
||||
path: 'gabinete-digital',
|
||||
children: [
|
||||
{
|
||||
path:'',
|
||||
loadChildren: ()=> GabineteDigitalPageModule
|
||||
},
|
||||
{
|
||||
path:'expediente',
|
||||
children: [
|
||||
{
|
||||
path:'',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/expediente/expediente.module').then(m => m.ExpedientePageModule)
|
||||
},
|
||||
{
|
||||
path:':SerialNumber',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/expediente/expediente-detail/expediente-detail.module').then(m => m.ExpedienteDetailPageModule)
|
||||
},
|
||||
{
|
||||
path:':SerialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/expediente/expediente-detail/expediente-detail.module').then(m => m.ExpedienteDetailPageModule)
|
||||
},
|
||||
{
|
||||
path:'events/:eventId/:caller',
|
||||
loadChildren: ()=> import('../pages/events/event-detail/event-detail.module').then(m => m.EventDetailPageModule),
|
||||
},
|
||||
{
|
||||
path:'expediente-task-modal',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/expediente/expedient-task-modal/expedient-task-modal.module').then(m => m.ExpedientTaskModalPageModule),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'expedientes-pr',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/expedientes-pr/expedientes-pr.module').then(m => m.ExpedientesPrPageModule),
|
||||
},
|
||||
{
|
||||
path:':SerialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/expedientes-pr/expediente-pr/expediente-pr.module').then(m => m.ExpedientePrPageModule),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'despachos',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/despachos/despachos.module').then(m => m.DespachosPageModule),
|
||||
},
|
||||
{
|
||||
path:':SerialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/despachos/despacho/despacho.module').then(m => m.DespachoPageModule),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'despachos-pr',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/despachos-pr/despachos-pr.module').then(m => m.DespachosPrPageModule),
|
||||
},
|
||||
{
|
||||
path:':SerialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/despachos-pr/despacho-pr/despacho-pr.module').then(m => m.DespachoPrPageModule),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'pedidos',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/pedidos/pedidos.module').then(m => m.PedidosPageModule),
|
||||
},
|
||||
{
|
||||
path:':SerialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/pedidos/pedido/pedido.module').then(m => m.PedidoPageModule),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'diplomas',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/diplomas/diplomas.module').then(m => m.DiplomasPageModule),
|
||||
},
|
||||
{
|
||||
path:':SerialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/diplomas/diploma/diploma.module').then(m => m.DiplomaPageModule),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'diplomas-assinar',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/diplomas-assinar/diplomas-assinar.module').then(m => m.DiplomasAssinarPageModule),
|
||||
},
|
||||
{
|
||||
path:':SerialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/diplomas-assinar/diploma-assinar/diploma-assinar.module').then(m => m.DiplomaAssinarPageModule),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'diplomas-gerar',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/diplomas-gerar/diplomas-gerar.module').then(m => m.DiplomasGerarPageModule),
|
||||
},
|
||||
{
|
||||
path:':SerialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/diplomas-gerar/diplomas-gerar/diplomas-gerar-routing.module').then(m => m.DiplomasGerarPageRoutingModule),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path:'event-list',
|
||||
children: [
|
||||
{
|
||||
path:'',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/event-list/event-list.module').then(m => m.EventListPageModule)
|
||||
},
|
||||
{
|
||||
path:'approve-event',
|
||||
children : [
|
||||
{
|
||||
path:':serialNumber/:caller',
|
||||
loadChildren: ()=> import('../pages/gabinete-digital/event-list/approve-event/approve-event.module').then(m => m.ApproveEventPageModule)
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'events-to-approve',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: ()=> import('../shared/gabinete-digital/edit-event-to-approve/edit-event.module')
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
children: [
|
||||
|
||||
@@ -43,8 +43,11 @@ export class TokenInterceptor implements HttpInterceptor {
|
||||
|
||||
return next.handle(request).pipe(
|
||||
catchError((error) => {
|
||||
console.log('interceptor ',error)
|
||||
if (error instanceof HttpErrorResponse && error.status === 401) {
|
||||
return this.handle401Error(request, next);
|
||||
} else if (error.url.includes('https://gdapi-dev.dyndns.info/stage/api/v2') && error.status === 0){
|
||||
return this.handle401Error(request, next);
|
||||
} else {
|
||||
return throwError(error);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export class ChatModule {
|
||||
this.typingCallback[id].pipe(
|
||||
switchMap(() => timer(2000)),
|
||||
).subscribe(() => {
|
||||
console.log('111111==============')
|
||||
// console.log('111111==============')
|
||||
// this.memoryDataSource.dispatch(removeUserTyping({data: {...e} as any}))
|
||||
this.localDataSource.removeUserTyping(e)
|
||||
})
|
||||
@@ -51,8 +51,6 @@ export class ChatModule {
|
||||
this.typingCallback[id].next()
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('e--', e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MessageTable } from "src/app/module/chat/infra/database/dexie/schema/message";
|
||||
import { RoomListItemOutPutDTO, RoomListOutPutDTO } from "../../../dto/room/roomListOutputDTO";
|
||||
|
||||
export function messageListDetermineChanges(serverList: any[], localList: any[]) {
|
||||
export function messageListDetermineChanges(serverList: MessageTable[], localList: MessageTable[]) {
|
||||
|
||||
localList = localList.filter(e => e.id)
|
||||
// Convert lists to dictionaries for easier comparison
|
||||
|
||||
@@ -34,7 +34,11 @@ export const MessageOutPutDataDTOSchema = z.object({
|
||||
reaction: z.string(),
|
||||
sender: z.object({}),
|
||||
}).array(),
|
||||
info: z.array(z.object({})),
|
||||
info: z.array(z.object({
|
||||
memberId: z.number(),
|
||||
readAt: z.string().nullable(),
|
||||
deliverAt: z.string().nullable()
|
||||
})),
|
||||
attachments: z.array(z.object({
|
||||
fileType: z.nativeEnum(MessageAttachmentFileType),
|
||||
source: z.nativeEnum(MessageAttachmentSource),
|
||||
|
||||
@@ -8,38 +8,41 @@ import { chatDatabase } from '../../infra/database/dexie/service';
|
||||
import { ok } from 'neverthrow';
|
||||
import { err, Result } from 'neverthrow';
|
||||
import { MemberListUPdateStatusInputDTO } from '../../domain/use-case/socket/member-list-update-status-use-case.service';
|
||||
import { MemberTable } from '../../infra/database/dexie/schema/members';
|
||||
import { MemberTable, MemberTableSchema } from '../../infra/database/dexie/schema/members';
|
||||
import { from } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MemberListLocalRepository extends DexieRepository<RoomTable> {
|
||||
export class MemberListLocalRepository extends DexieRepository<MemberTable> {
|
||||
|
||||
constructor() {
|
||||
super(chatDatabase.room, RoomTableSchema)
|
||||
super(chatDatabase.members, MemberTableSchema)
|
||||
}
|
||||
|
||||
// messageDataSource.message.hook('creating', (primKey, obj, trans) => {
|
||||
// // const newMessage = await trans.table('message').get(primKey);
|
||||
// this.messageSubject.next(obj);
|
||||
// // return newMessage
|
||||
// })
|
||||
|
||||
async directMember({roomId, currentUserId}) {
|
||||
try {
|
||||
let a = await chatDatabase.members.where('roomId')
|
||||
.equals(roomId)
|
||||
.and(message => message.wxUserId !== currentUserId)
|
||||
.first()
|
||||
|
||||
return ok(a)
|
||||
} catch (e) {
|
||||
return err(e)
|
||||
}
|
||||
}
|
||||
|
||||
async addMember(data: MemberTable) {
|
||||
try {
|
||||
data.$roomIdUserId = data.roomId + data.wxUserId
|
||||
const result = await chatDatabase.members.add(data)
|
||||
return ok(result)
|
||||
} catch (e) {
|
||||
return err(false)
|
||||
}
|
||||
data.$roomIdUserId = data.roomId + data.wxUserId
|
||||
return this.insert(data)
|
||||
}
|
||||
|
||||
async updateMemberRole(data: MemberTable) {
|
||||
try {
|
||||
const result = await chatDatabase.members.where({
|
||||
wxUserId:data.wxUserId,
|
||||
wxUserId: data.wxUserId,
|
||||
roomId: data.roomId,
|
||||
}).modify(data);
|
||||
|
||||
@@ -100,19 +103,11 @@ export class MemberListLocalRepository extends DexieRepository<RoomTable> {
|
||||
return liveQuery(() => chatDatabase.members.get($roomIdUserId)) as any;
|
||||
}
|
||||
|
||||
getItemsLive(): Observable<RoomListOutPutDTO[]> {
|
||||
return liveQuery(() => chatDatabase.room.toArray()) as any;
|
||||
}
|
||||
|
||||
getRoomByIdLive(id: any): Observable<RoomListItemOutPutDTO | undefined> {
|
||||
return liveQuery(() => chatDatabase.room.get(id)) as any;
|
||||
}
|
||||
|
||||
async getRoomMemberById(roomId: any) {
|
||||
return await chatDatabase.members.where({roomId}).toArray()
|
||||
}
|
||||
getRoomMemberByIdLive(roomId: any) {
|
||||
return liveQuery(() => chatDatabase.members.where({roomId}).toArray())
|
||||
return from (liveQuery(() => chatDatabase.members.where({roomId}).toArray()))
|
||||
}
|
||||
|
||||
getRoomMemberNoneAdminByIdLive(roomId: any) {
|
||||
|
||||
+34
@@ -19,6 +19,22 @@ interface msgObj {
|
||||
requestId: string;
|
||||
}
|
||||
|
||||
interface sendDeliverAt {
|
||||
memberId: number,
|
||||
messageId:string,
|
||||
roomId: string,
|
||||
requestId: string
|
||||
}
|
||||
|
||||
|
||||
export interface sendReadAt {
|
||||
memberId: number,
|
||||
messageId:string,
|
||||
roomId: string,
|
||||
requestId: string
|
||||
}
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@@ -48,6 +64,24 @@ export class MessageSocketRepositoryService {
|
||||
return result;
|
||||
}
|
||||
|
||||
async sendDeliverAt(data: sendDeliverAt) {
|
||||
const result = await this.socket.sendData<any>({
|
||||
method: 'DeliverAt',
|
||||
data: data as any,
|
||||
})
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async sendReadAt(data: sendReadAt) {
|
||||
const result = await this.socket.sendData<any>({
|
||||
method: 'ReadAt',
|
||||
data: data as any,
|
||||
})
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
listenToMessages() {
|
||||
return this.socket.getMessage()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ValidateSchema } from 'src/app/services/decorators/validate-schema.deco
|
||||
import { chatDatabase } from '../../infra/database/dexie/service';
|
||||
import { RoomTable, RoomTableSchema } from '../../infra/database/dexie/schema/room';
|
||||
import { DexieRepository } from 'src/app/infra/repository/dexie/dexie-repository.service';
|
||||
import { from } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -81,7 +82,7 @@ export class RoomLocalRepository extends DexieRepository<RoomTable> {
|
||||
|
||||
|
||||
getItemsLive(){
|
||||
return liveQuery(() => chatDatabase.room.toArray());
|
||||
return from (liveQuery(() => chatDatabase.room.toArray()));
|
||||
}
|
||||
|
||||
getRoomByIdLive(id: any) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { SendLocalMessagesUseCaseService } from './use-case/messages-send-offlin
|
||||
import { RemoveMemberUseCaseService } from './use-case/member/-use-case.service'
|
||||
import { AddMemberUseCaseService } from './use-case/member-add-use-case.service'
|
||||
import { UpdateRoomByIdUseCaseService } from './use-case/room-update-by-id-use-case.service'
|
||||
import { MessageMarkAsReadUseCaseService } from './use-case/message/message-mark-as-read-use-case.service'
|
||||
import { GetMessageAttachmentLocallyUseCaseService } from 'src/app/module/chat/domain/use-case/message-get-attachment-localy-use-case.service';
|
||||
import { GetRoomListUseCaseService } from 'src/app/module/chat/domain/use-case/room-get-list-use-case.service';
|
||||
import { filter } from 'rxjs/operators';
|
||||
@@ -36,6 +37,7 @@ import { UserRemoveListInputDTO } from '../data/dto/room/userRemoveListInputDTO'
|
||||
import { AddMemberToRoomInputDTO } from '../data/dto/room/addMemberToRoomInputDto';
|
||||
import { RoomUpdateInputDTO } from '../data/dto/room/roomUpdateInputDTO';
|
||||
import { RoomType } from "src/app/module/chat/domain/entity/group";
|
||||
import { sendReadAt } from "src/app/module/chat/data/repository/message/message-live-signalr-data-source.service";
|
||||
|
||||
export const InstanceId = uuidv4();
|
||||
|
||||
@@ -73,7 +75,8 @@ export class ChatServiceService {
|
||||
private UpdateRoomByIdUseCaseService: UpdateRoomByIdUseCaseService,
|
||||
private RemoveMemberUseCaseService: RemoveMemberUseCaseService,
|
||||
private MessageReadAtByIdUseCaseService: MessageReadAtByIdUseCaseService,
|
||||
private SendLocalMessagesUseCaseService: SendLocalMessagesUseCaseService
|
||||
private SendLocalMessagesUseCaseService: SendLocalMessagesUseCaseService,
|
||||
private MessageMarkAsReadUseCaseService: MessageMarkAsReadUseCaseService
|
||||
) {
|
||||
this.messageLiveSignalRDataSourceService.getMessageDelete()
|
||||
.pipe()
|
||||
@@ -102,7 +105,6 @@ export class ChatServiceService {
|
||||
})
|
||||
).subscribe(async (message) => {
|
||||
if(message?.id) {
|
||||
console.log('create message')
|
||||
this.SocketMessageCreateUseCaseService.execute(message)
|
||||
}
|
||||
})
|
||||
@@ -115,7 +117,6 @@ export class ChatServiceService {
|
||||
}
|
||||
})
|
||||
).subscribe(async (message) => {
|
||||
console.log('123', message)
|
||||
this.MemberListUpdateStatusUseCaseService.execute(message.data as any)
|
||||
})
|
||||
|
||||
@@ -213,6 +214,10 @@ export class ChatServiceService {
|
||||
|
||||
}
|
||||
|
||||
sendReadAt(sendReadAt: sendReadAt) {
|
||||
return this.MessageMarkAsReadUseCaseService.execute(sendReadAt)
|
||||
}
|
||||
|
||||
listenToIncomingMessage(roomId:string) {
|
||||
return this.ListenMessageByRoomIdNewUseCase.execute({roomId})
|
||||
}
|
||||
|
||||
@@ -14,12 +14,18 @@ export const MessageEntitySchema = z.object({
|
||||
oneShot: z.boolean(),
|
||||
sentAt: z.string().optional(),
|
||||
requireUnlock: z.boolean(),
|
||||
editedAt: z.string().nullable().optional(),
|
||||
sender: z.object({
|
||||
wxUserId: z.number(),
|
||||
wxFullName: z.string(),
|
||||
wxeMail: z.string(),
|
||||
userPhoto: z.string(),
|
||||
}),
|
||||
info: z.array(z.object({
|
||||
memberId: z.number(),
|
||||
readAt: z.string().nullable(),
|
||||
deliverAt: z.string().nullable()
|
||||
})).optional(),
|
||||
sending: z.boolean().optional(),
|
||||
attachments: z.array(z.object({
|
||||
fileType: z.nativeEnum(MessageAttachmentFileType),
|
||||
@@ -47,6 +53,11 @@ export class MessageEntity implements Message {
|
||||
oneShot: boolean = false
|
||||
sentAt: string
|
||||
requireUnlock: boolean = false
|
||||
info: {
|
||||
memberId?: number
|
||||
readAt?: string,
|
||||
deliverAt?: string
|
||||
}[] = []
|
||||
sender: {
|
||||
wxUserId: number,
|
||||
wxFullName: string,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RoomExpirationService {
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
@@ -14,8 +14,12 @@ export class ListenSendMessageUseCase {
|
||||
|
||||
execute({roomId}: {roomId: string}) {
|
||||
|
||||
console.log('reciee message')
|
||||
return this.MessageSocketRepositoryService.listenToMessages().pipe(
|
||||
filter((message) => message?.requestId?.startsWith(InstanceId) && message?.roomId == roomId),
|
||||
filter((message) => {
|
||||
console.log(message, roomId)
|
||||
return message?.requestId?.startsWith(InstanceId) && message?.roomId == roomId
|
||||
}),
|
||||
map(message => message)
|
||||
)
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ import { MessageTable } from '../../infra/database/dexie/schema/message';
|
||||
import { MessageMapper } from '../mapper/messageMapper';
|
||||
import { SignalRService } from '../../infra/socket/signal-r.service';
|
||||
import { RoomType } from "src/app/module/chat/domain/entity/group";
|
||||
|
||||
|
||||
import { TracingType, XTracerAsync } from 'src/app/services/monitoring/opentelemetry/tracer';
|
||||
import { MemberListLocalRepository } from 'src/app/module/chat/data/repository/member-list-local-repository.service'
|
||||
import { SessionStore } from 'src/app/store/session.service';
|
||||
const MessageInputUseCaseSchema = z.object({
|
||||
memberId: z.number(),
|
||||
roomId: z.string(),
|
||||
@@ -39,11 +40,13 @@ export class MessageCreateUseCaseService {
|
||||
private AttachmentLocalRepositoryService: AttachmentLocalDataSource,
|
||||
private messageLocalDataSourceService: MessageLocalDataSourceService,
|
||||
private messageLiveSignalRDataSourceService: SignalRService,
|
||||
private messageSocketRepositoryService: MessageSocketRepositoryService
|
||||
private messageSocketRepositoryService: MessageSocketRepositoryService,
|
||||
private MemberListLocalRepository: MemberListLocalRepository
|
||||
) { }
|
||||
|
||||
|
||||
async execute(message: MessageEntity, messageEnum: RoomType) {
|
||||
@XTracerAsync({name:'MessageCreateUseCaseService', module:'chat', bugPrint: true})
|
||||
async execute(message: MessageEntity, messageEnum: RoomType, tracing?: TracingType) {
|
||||
|
||||
const validation = zodSafeValidation<MessageEntity>(MessageEntitySchema, message)
|
||||
|
||||
@@ -93,13 +96,30 @@ export class MessageCreateUseCaseService {
|
||||
//====================
|
||||
message.sending = true
|
||||
|
||||
const DTO = MessageMapper.fromDomain(message, message.requestId)
|
||||
|
||||
let sendMessageResult: Result<MessageOutPutDataDTO, any>
|
||||
if(messageEnum == RoomType.Group) {
|
||||
const DTO = MessageMapper.fromDomain(message, message.requestId)
|
||||
sendMessageResult = await this.messageLiveSignalRDataSourceService.sendMessage<MessageOutPutDataDTO>(DTO)
|
||||
} else {
|
||||
sendMessageResult = await this.messageSocketRepositoryService.sendDirectMessage(DTO)
|
||||
|
||||
if(message.receiverId) {
|
||||
const DTO = MessageMapper.fromDomain(message, message.requestId)
|
||||
sendMessageResult = await this.messageSocketRepositoryService.sendDirectMessage(DTO)
|
||||
} else {
|
||||
const getRoomMembers = await this.MemberListLocalRepository.directMember({
|
||||
roomId:message.roomId,
|
||||
currentUserId: SessionStore.user.UserId
|
||||
})
|
||||
if(getRoomMembers.isOk()) {
|
||||
message.receiverId = getRoomMembers.value.wxUserId
|
||||
const DTO = MessageMapper.fromDomain(message, message.requestId)
|
||||
sendMessageResult = await this.messageSocketRepositoryService.sendDirectMessage(DTO)
|
||||
} else {
|
||||
console.log('not found direct users', getRoomMembers.error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// return this sendMessageResult
|
||||
@@ -136,6 +156,11 @@ export class MessageCreateUseCaseService {
|
||||
zodErrorList: validation.error.errors,
|
||||
data: message.attachments
|
||||
})
|
||||
} else {
|
||||
Logger.error('failed to send message, validation failed', {
|
||||
zodErrorList: validation.error.errors,
|
||||
data: message
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ import { z } from 'zod';
|
||||
import { SafeValidateSchema, ValidateSchema } from 'src/app/services/decorators/validate-schema.decorator';
|
||||
import { MessageRemoteDataSourceService } from '../../data/repository/message/message-remote-data-source.service';
|
||||
import { MessageSocketRepositoryService } from '../../data/repository/message/message-live-signalr-data-source.service';
|
||||
|
||||
|
||||
import { XTracerAsync, TracingType } from 'src/app/services/monitoring/opentelemetry/tracer';
|
||||
export const MessageDeleteInputDTOSchema = z.object({
|
||||
requestId: z.string().optional(),
|
||||
roomId: z.string(),
|
||||
@@ -22,8 +21,11 @@ export class MessageDeleteLiveUseCaseService {
|
||||
public repository: MessageSocketRepositoryService
|
||||
) { }
|
||||
|
||||
@SafeValidateSchema(MessageDeleteInputDTOSchema, 'MessageDeleteUseCaseService')
|
||||
async execute(data: MessageDeleteInputDTO) {
|
||||
@XTracerAsync({name:'MessageDeleteLiveUseCaseService', module:'chat', bugPrint: true})
|
||||
async execute(data: MessageDeleteInputDTO, tracing?: TracingType) {
|
||||
tracing.log('MessageDeleteLiveUseCaseService payload', {
|
||||
data: data
|
||||
})
|
||||
return this.repository.sendMessageDelete(data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MessageSocketRepositoryService, sendReadAt } from '../../../data/repository/message/message-live-signalr-data-source.service';
|
||||
import { XTracerAsync, TracingType } from 'src/app/services/monitoring/opentelemetry/tracer';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MessageMarkAsReadUseCaseService {
|
||||
|
||||
constructor(
|
||||
private MessageSocketRepositoryService: MessageSocketRepositoryService,
|
||||
) { }
|
||||
|
||||
|
||||
@XTracerAsync({name:'MessageMarkAsReadUseCaseService', module:'chat', bugPrint: true})
|
||||
async execute(sendReadAt: sendReadAt, tracing?: TracingType) {
|
||||
return this.MessageSocketRepositoryService.sendReadAt(sendReadAt)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { RoomRemoteDataSourceService } from '../../data/repository/room-remote-r
|
||||
import { RoomLocalRepository } from '../../data/repository/room-local-repository.service';
|
||||
import { captureAndReraiseAsync } from 'src/app/services/decorators/captureAndReraiseAsync';
|
||||
import { isHttpResponse } from 'src/app/services/http.service';
|
||||
|
||||
import { CronJobService } from 'src/app/utils/task-scheduler'
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@@ -14,6 +14,7 @@ export class GetRoomListUseCaseService {
|
||||
private roomRemoteDataSourceService: RoomRemoteDataSourceService,
|
||||
// private roomMemoryDataSourceService: Store<RoomRemoteDataSourceState>,
|
||||
private roomLocalDataSourceService: RoomLocalRepository,
|
||||
private CronJobService: CronJobService
|
||||
) { }
|
||||
|
||||
@captureAndReraiseAsync('RoomRepositoryService/list')
|
||||
@@ -28,6 +29,12 @@ export class GetRoomListUseCaseService {
|
||||
|
||||
for( const roomData of roomsToInsert) {
|
||||
this.roomLocalDataSourceService.createRoom(roomData.chatRoom)
|
||||
|
||||
if(roomData.chatRoom.expirationDate) {
|
||||
console.log('room expiration date schedule')
|
||||
this.CronJobService.createCronJob('remove expired room', new Date(roomData.chatRoom.expirationDate), this.execute)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for( const roomData of roomsToUpdate) {
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ export class SocketMessageCreateUseCaseService {
|
||||
private messageLocalDataSourceService: MessageLocalDataSourceService,
|
||||
) { }
|
||||
|
||||
@XTracerAsync({name:'Socket-Message-Create-UseCase', bugPrint: true})
|
||||
@XTracerAsync({name:'Socket-Message-Create-UseCase', module:'chat', bugPrint: true})
|
||||
async execute(input: any, tracing?: TracingType) {
|
||||
|
||||
ParamsValidation(MessageOutPutDataDTOSchema, input, tracing)
|
||||
|
||||
+5
-3
@@ -15,7 +15,7 @@ export class SocketMessageUpdateUseCaseService {
|
||||
) { }
|
||||
|
||||
|
||||
@XTracerAsync({name:'Socket-Message-Update-UseCase', bugPrint: true})
|
||||
@XTracerAsync({name:'Socket-Message-Update-UseCase', bugPrint: true, module:'chat',})
|
||||
async execute(data: MessageOutPutDataDTO, tracing?: TracingType) {
|
||||
|
||||
ParamsValidation(MessageOutPutDataDTOSchema, data, tracing)
|
||||
@@ -30,11 +30,13 @@ export class SocketMessageUpdateUseCaseService {
|
||||
|
||||
if(result.isOk()) {
|
||||
tracing?.addEvent("Message found")
|
||||
return await this.messageLocalDataSourceService.update(result.value.$id, incomingMessage)
|
||||
const updateResult = await this.messageLocalDataSourceService.update(result.value.$id, incomingMessage)
|
||||
tracing.setAttribute('outcome', 'success')
|
||||
return updateResult
|
||||
} else {
|
||||
tracing?.addEvent("Message not found")
|
||||
}
|
||||
|
||||
tracing.setAttribute('outcome', 'success')
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ import { MessageLocalDataSourceService } from '../../data/repository/message/mes
|
||||
import { messageListDetermineChanges } from '../../data/async/list/rooms/messageListChangedetector';
|
||||
import { MessageTable } from '../../infra/database/dexie/schema/message';
|
||||
import { MessageRemoteDataSourceService } from '../../data/repository/message/message-remote-data-source.service';
|
||||
import { MessageSocketRepositoryService } from '../../data/repository/message/message-live-signalr-data-source.service';
|
||||
import { ok } from 'neverthrow';
|
||||
import { RoomLocalRepository } from '../../data/repository/room-local-repository.service';
|
||||
import { SessionStore } from 'src/app/store/session.service';
|
||||
import { Logger } from 'src/app/services/logger/main/service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -14,6 +17,7 @@ export class SyncAllRoomMessagesService {
|
||||
constructor(
|
||||
private messageLocalDataSourceService: MessageLocalDataSourceService,
|
||||
private messageRemoteDataSourceService: MessageRemoteDataSourceService,
|
||||
private MessageSocketRepositoryService: MessageSocketRepositoryService,
|
||||
private roomLocalDataSourceService: RoomLocalRepository,
|
||||
) { }
|
||||
|
||||
@@ -35,15 +39,38 @@ export class SyncAllRoomMessagesService {
|
||||
|
||||
const { addedItems, changedItems, deletedItems } = messageListDetermineChanges(result.value.data, localResult)
|
||||
|
||||
|
||||
for(const message of changedItems) {
|
||||
let clone: MessageTable = message
|
||||
clone.roomId = room.id
|
||||
|
||||
this.messageLocalDataSourceService.findOrUpdate(clone)
|
||||
|
||||
const me = message.info.find(e => e.memberId == SessionStore.user.UserId)
|
||||
|
||||
if(!me) {
|
||||
this.MessageSocketRepositoryService.sendDeliverAt({
|
||||
memberId: SessionStore.user.UserId,
|
||||
messageId: message.id,
|
||||
roomId: message.roomId,
|
||||
requestId: "string"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for(const message of addedItems) {
|
||||
let clone: MessageTable = message
|
||||
clone.roomId = room.id
|
||||
|
||||
const me = message.info.find(e => e.memberId == SessionStore.user.UserId)
|
||||
if(!me) {
|
||||
this.MessageSocketRepositoryService.sendDeliverAt({
|
||||
memberId: SessionStore.user.UserId,
|
||||
messageId: message.id,
|
||||
roomId: message.roomId,
|
||||
requestId: "string"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +81,8 @@ export class SyncAllRoomMessagesService {
|
||||
this.messageLocalDataSourceService.deleteByMessageId(message.id)
|
||||
}
|
||||
|
||||
} else {
|
||||
Logger.error('failed to get room message '+room.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export const AttachmentTableSchema = z.object({
|
||||
docId: z.string().optional(),
|
||||
mimeType: z.string().optional(),
|
||||
id: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
description: z.string().optional()
|
||||
})
|
||||
|
||||
export type AttachmentTable = z.infer<typeof AttachmentTableSchema>
|
||||
|
||||
@@ -11,6 +11,7 @@ export const MessageTableSchema = z.object({
|
||||
canEdit: z.boolean(),
|
||||
oneShot: z.boolean(),
|
||||
sentAt: z.string().optional(),
|
||||
editedAt: z.string().nullable().optional(),
|
||||
requireUnlock: z.boolean(),
|
||||
sender: z.object({
|
||||
wxUserId: z.number(),
|
||||
@@ -26,7 +27,11 @@ export const MessageTableSchema = z.object({
|
||||
reaction: z.string(),
|
||||
sender: z.object({}),
|
||||
}).array().optional(),
|
||||
info: z.array(z.object({})).optional(),
|
||||
info: z.array(z.object({
|
||||
memberId: z.number(),
|
||||
readAt: z.string().nullable(),
|
||||
deliverAt: z.string().nullable()
|
||||
})).optional(),
|
||||
attachments: z.array(z.object({
|
||||
fileType: z.nativeEnum(MessageAttachmentFileType),
|
||||
source: z.nativeEnum(MessageAttachmentSource),
|
||||
|
||||
@@ -157,7 +157,7 @@ export class SignalRConnection {
|
||||
|
||||
const requestId = uuidv4()
|
||||
if(this.connectionStateSubject.value == true) {
|
||||
console.log('send typing', data)
|
||||
// console.log('send typing', data)
|
||||
|
||||
try {
|
||||
this.hubConnection.invoke("Typing", data)
|
||||
@@ -276,7 +276,6 @@ export class SignalRConnection {
|
||||
});
|
||||
|
||||
this.hubConnection.on('TypingMessage', (_typing: UserTypingDTO) => {
|
||||
console.log('Typing', _typing)
|
||||
this.typingSubject.next(_typing);
|
||||
this.sendDataSubject.next({
|
||||
method: 'ReceiveMessage',
|
||||
@@ -285,7 +284,6 @@ export class SignalRConnection {
|
||||
});
|
||||
|
||||
this.hubConnection.on('AvailableUsers', (data: any) => {
|
||||
console.log('AvailableUsers', data)
|
||||
this.typingSubject.next(data);
|
||||
this.sendDataSubject.next({
|
||||
method: 'AvailableUsers',
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
// import { DateUtils } from './date';
|
||||
|
||||
import { environment } from "src/environments/environment";
|
||||
import { openTelemetryLogging } from "../../monitoring/opentelemetry/logging";
|
||||
import { SessionStore } from "src/app/store/session.service";
|
||||
import { DeviceInfo } from "@capacitor/device";
|
||||
import { Device } from '@capacitor/device';
|
||||
|
||||
export type MessageType = {
|
||||
message: string;
|
||||
context?: string;
|
||||
@@ -15,6 +21,12 @@ function getCurrentTime() {
|
||||
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
|
||||
}
|
||||
|
||||
let device: DeviceInfo;
|
||||
|
||||
Device.getInfo().then(e => {
|
||||
device = e
|
||||
});
|
||||
|
||||
export class Logger {
|
||||
|
||||
constructor() {}
|
||||
@@ -25,6 +37,25 @@ export class Logger {
|
||||
'color: #00897B', // CSS Style
|
||||
Object.assign(obj, { createdAt: getCurrentTime(), message })
|
||||
);
|
||||
|
||||
|
||||
if(environment.apiURL != 'https://gdqas-api.oapr.gov.ao/api/') {
|
||||
openTelemetryLogging.send({
|
||||
type: 'graylog',
|
||||
spanContext: null,
|
||||
payload: {
|
||||
message: message,
|
||||
object: {
|
||||
...obj,
|
||||
spanId: null,
|
||||
name,
|
||||
user: SessionStore?.user?.FullName,
|
||||
device_name: device?.name || device?.model,
|
||||
commit_date: environment.version.lastCommitTime,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static debug(message: string, obj = {}): void {
|
||||
@@ -33,6 +64,24 @@ export class Logger {
|
||||
'color: #039BE5', // CSS Style
|
||||
Object.assign(obj, {createdAt: getCurrentTime(), message })
|
||||
);
|
||||
|
||||
if(environment.apiURL != 'https://gdqas-api.oapr.gov.ao/api/') {
|
||||
openTelemetryLogging.send({
|
||||
type: 'graylog',
|
||||
spanContext: null,
|
||||
payload: {
|
||||
message: message,
|
||||
object: {
|
||||
...obj,
|
||||
spanId: null,
|
||||
name,
|
||||
user: SessionStore?.user?.FullName,
|
||||
device_name: device?.name || device?.model,
|
||||
commit_date: environment.version.lastCommitTime,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static info(message: string, obj = {}): void {
|
||||
@@ -44,6 +93,24 @@ export class Logger {
|
||||
obj,
|
||||
'\n',
|
||||
);
|
||||
|
||||
if(environment.apiURL != 'https://gdqas-api.oapr.gov.ao/api/') {
|
||||
openTelemetryLogging.send({
|
||||
type: 'graylog',
|
||||
spanContext: null,
|
||||
payload: {
|
||||
message: message,
|
||||
object: {
|
||||
...obj,
|
||||
spanId: null,
|
||||
name,
|
||||
user: SessionStore?.user?.FullName,
|
||||
device_name: device?.name || device?.model,
|
||||
commit_date: environment.version.lastCommitTime,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static warn(message: string, obj = {}): void {
|
||||
@@ -55,6 +122,24 @@ export class Logger {
|
||||
obj,
|
||||
'\n',
|
||||
);
|
||||
|
||||
if(environment.apiURL != 'https://gdqas-api.oapr.gov.ao/api/') {
|
||||
openTelemetryLogging.send({
|
||||
type: 'graylog',
|
||||
spanContext: null,
|
||||
payload: {
|
||||
message: message,
|
||||
object: {
|
||||
...obj,
|
||||
spanId: null,
|
||||
name,
|
||||
user: SessionStore?.user?.FullName,
|
||||
device_name: device?.name || device?.model,
|
||||
commit_date: environment.version.lastCommitTime,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static error(message?: string, obj = {}): void {
|
||||
@@ -67,6 +152,24 @@ export class Logger {
|
||||
obj,
|
||||
'\n',
|
||||
);
|
||||
|
||||
if(environment.apiURL != 'https://gdqas-api.oapr.gov.ao/api/') {
|
||||
openTelemetryLogging.send({
|
||||
type: 'graylog',
|
||||
spanContext: null,
|
||||
payload: {
|
||||
message: message,
|
||||
object: {
|
||||
...obj,
|
||||
spanId: null,
|
||||
name,
|
||||
user: SessionStore?.user?.FullName,
|
||||
device_name: device?.name || device?.model,
|
||||
commit_date: environment.version.lastCommitTime,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fatal(error?: any, message?: string, context?: string): void {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
|
||||
import { Tracer, Span } from '@opentelemetry/sdk-trace-base';
|
||||
import { OpentelemetryAgendaProvider, OpentelemetryInterceptorProvider, OpentelemetryLogging, OpentelemetryNotificationProvider } from './opentelemetry';
|
||||
import { OpentelemetryAgendaProvider, OpentelemetryChatProvider, OpentelemetryInterceptorProvider, OpentelemetryLogging, OpentelemetryNotificationProvider } from './opentelemetry';
|
||||
import { Device, DeviceInfo } from '@capacitor/device';
|
||||
import { SessionStore } from 'src/app/store/session.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '@opentelemetry/api';
|
||||
const tracerInstance = OpentelemetryAgendaProvider.getTracer('example-tracer-hole', '111', {})
|
||||
const tracerNotificationInstance = OpentelemetryNotificationProvider.getTracer('example-tracer-hole', '111', {})
|
||||
|
||||
const tracerChat = OpentelemetryChatProvider.getTracer('OpentelemetryChatProvider','some' ,{})
|
||||
let device: DeviceInfo;
|
||||
|
||||
Device.getInfo().then(e => {
|
||||
@@ -45,6 +45,8 @@ const createTracingInstance = ({bugPrint, name, module, autoFinish}): TracingTyp
|
||||
|
||||
if(module == 'notification') {
|
||||
_tracerInstance = tracerNotificationInstance
|
||||
} else if (module == 'chat') {
|
||||
_tracerInstance = tracerChat
|
||||
} else {
|
||||
_tracerInstance = tracerInstance
|
||||
}
|
||||
@@ -184,6 +186,9 @@ export function XTracerAsync({ name, bugPrint, module = null, autoFinish = true,
|
||||
return result
|
||||
} catch (e) {
|
||||
tracing.setAttribute('catch', 'true')
|
||||
tracing.log("cath", {
|
||||
error: e
|
||||
})
|
||||
|
||||
if(autoFinish) {
|
||||
setTimeout(tracing.finish , daley)
|
||||
@@ -226,6 +231,9 @@ export function XTracer({ name, bugPrint, module, autoFinish = true, daley =0 })
|
||||
} catch (e) {
|
||||
|
||||
tracing.setAttribute('catch', 'true')
|
||||
tracing.log("cath", {
|
||||
error: e
|
||||
})
|
||||
|
||||
if(autoFinish) {
|
||||
setTimeout(tracing.finish , daley)
|
||||
|
||||
@@ -67,6 +67,9 @@
|
||||
</div>
|
||||
<!-- <div class="item-date font-13-em" [class.item-date-active]="room.id == idSelected">{{room.duration}}</div> -->
|
||||
</div>
|
||||
<div class="item-date font-13-em" [class.item-date-active]="room.id == idSelected">
|
||||
{{ expirationDate[room.id] !== null ? expirationDate[room.id] + ' seconds left' : 'No expiration' }}
|
||||
</div>
|
||||
<!-- <div *ngIf="room.lastMessage" class="item-description d-flex align-items-center" [class.item-description-active]="room.id ==idSelected">
|
||||
<div class="item-message font-13-em add-ellipsis white-space-nowrap" *ngIf="room.otherUserType == false"> {{room.lastMessage.msg}} </div>
|
||||
<div class="font-13-em" *ngIf="room.otherUserType == true">está escrever ...</div>
|
||||
@@ -101,6 +104,9 @@
|
||||
</div>
|
||||
<!-- <div class="item-date font-13-em" [class.item-date-active]="room.id == idSelected">{{room.duration}}</div> -->
|
||||
</div>
|
||||
<div class="item-date font-13-em" [class.item-date-active]="room.id == idSelected">
|
||||
{{ expirationDate[room.id] !== null ? expirationDate[room.id] + ' seconds left' : 'No expiration' }}
|
||||
</div>
|
||||
<!-- <div *ngIf="room.lastMessage" class="item-description d-flex align-items-center" [class.item-description-active]="room.id ==idSelected">
|
||||
<div class="item-message font-13-em add-ellipsis white-space-nowrap" *ngIf="room.otherUserType == false"> {{room.lastMessage.msg}} </div>
|
||||
<div class="font-13-em" *ngIf="room.otherUserType == true">está escrever ...</div>
|
||||
|
||||
@@ -21,6 +21,8 @@ import { EditGroupPage } from './modal/edit-group/edit-group.page';
|
||||
import { ChatServiceService } from 'src/app/module/chat/domain/chat-service.service'
|
||||
import { RoomLocalRepository } from 'src/app/module/chat/data/repository/room-local-repository.service'
|
||||
import { RoomTable } from 'src/app/module/chat/infra/database/dexie/schema/room';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { interval, Subscription } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-chat',
|
||||
@@ -53,7 +55,12 @@ export class ChatPage implements OnInit {
|
||||
routerSubscription
|
||||
|
||||
// count$: Observable<RoomRemoteDataSourceState>;
|
||||
items$!: DexieObservable<RoomTable[]>;
|
||||
//items$!: DexieObservable<RoomTable[]>;
|
||||
items$!: Observable<RoomTable[]>;
|
||||
private rooms: RoomTable[] = [];
|
||||
private subscription: Subscription;
|
||||
expirationDate = {}
|
||||
private intervalSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private modalController: ModalController,
|
||||
@@ -82,7 +89,28 @@ export class ChatPage implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
this.items$ = this.roomLocalDataSourceService.getItemsLive()
|
||||
this.items$ = this.roomLocalDataSourceService.getItemsLive().pipe(
|
||||
map(rooms => rooms.map(room => ({
|
||||
...room,
|
||||
minutesLeft: this.getSecondsLeft(room.expirationDate)
|
||||
}))),
|
||||
tap((roomList) => {
|
||||
this.rooms = roomList
|
||||
})
|
||||
);
|
||||
|
||||
// Create the interval observable
|
||||
const interval$ = interval(1000).pipe(
|
||||
tap(() => {
|
||||
for (const room of this.rooms) {
|
||||
this.expirationDate[room.id] = this.getSecondsLeft(room.expirationDate);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Subscribe to the interval observable
|
||||
this.intervalSubscription = interval$.subscribe();
|
||||
|
||||
this.ChatServiceService.getRoomList()
|
||||
|
||||
this.hideRefreshButton();
|
||||
@@ -103,10 +131,20 @@ export class ChatPage implements OnInit {
|
||||
|
||||
}
|
||||
|
||||
getSecondsLeft(expirationDate: any): number | null {
|
||||
if (!expirationDate) return null;
|
||||
const now = new Date().getTime();
|
||||
const expTime = new Date(expirationDate).getTime();
|
||||
return Math.max(Math.floor((expTime - now) / 1000), 0);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
// this.setStatus('offline');
|
||||
this.routerSubscription?.unsubscribe();
|
||||
// Unsubscribe from the interval to prevent memory leaks
|
||||
if (this.intervalSubscription) {
|
||||
this.intervalSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="main-header">
|
||||
<div class="title-content">
|
||||
<div class="back-icon cursor-pointer">
|
||||
<button class="btn-no-color" (click)="close()">
|
||||
<button class="btn-no-color" (click)="close(roomId)">
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " slot="end" src='assets/images/icons-arrow-arrow-left.svg'></ion-icon>
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " slot="end" src='assets/images/theme/gov/icons-calendar-arrow-left.svg'></ion-icon>
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { ModalController } from '@ionic/angular';
|
||||
import { GroupMessagesPage } from '../group-messages/group-messages.page';
|
||||
import { ThemeService } from 'src/app/services/theme.service'
|
||||
@@ -32,7 +32,7 @@ export class ContactsPage implements OnInit {
|
||||
@Output() backToChat: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() closeAllDesktopComponents: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
roomId= ''
|
||||
@Input() roomId: string;
|
||||
|
||||
constructor(
|
||||
private modalController: ModalController,
|
||||
@@ -142,7 +142,9 @@ export class ContactsPage implements OnInit {
|
||||
message.receiverId = user.wxUserId
|
||||
message.message = 'hello'
|
||||
|
||||
const result = await this.chatServiceService.sendMessage(message, RoomType.Group)
|
||||
const result = await this.chatServiceService.sendMessage(message, RoomType.Direct)
|
||||
|
||||
console.log('result', result);
|
||||
|
||||
if(result.isOk()) {
|
||||
this.close(result.value.roomId)
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</ion-list>
|
||||
</div>
|
||||
pppp
|
||||
|
||||
<div *ngFor="let userContainer of userContainer | keyvalue;" >
|
||||
|
||||
<div class="item-divider">
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
</ion-refresher>
|
||||
|
||||
<div class="main-content">
|
||||
------
|
||||
<ion-progress-bar class="position-absolute" type="indeterminate" *ngIf="loading"></ion-progress-bar>
|
||||
<ion-virtual-scroll [items]="userList" approxItemHeight="70px" [headerFn]="separateLetter">
|
||||
|
||||
|
||||
@@ -68,10 +68,11 @@
|
||||
|
||||
<div *ngIf="attachment.fileType == MessageAttachmentFileType.Image">
|
||||
<img
|
||||
[src]="attachment.safeFile"
|
||||
(load)="onImageLoad(message, messageIndex)"
|
||||
(error)="onImageError()">
|
||||
</div>
|
||||
*ngIf="attachment.oneShot != true"
|
||||
[src]="attachment.safeFile"
|
||||
(load)="onImageLoad(message, messageIndex)"
|
||||
(error)="onImageError()">
|
||||
</div>
|
||||
|
||||
<div *ngIf="attachment.fileType == MessageAttachmentFileType.Audio">
|
||||
<audio [src]="attachment.safeFile|safehtml" preload="metadata" class="flex-grow-1" controls controlsList="nodownload noplaybackrate"></audio>
|
||||
@@ -92,7 +93,6 @@
|
||||
|
||||
<ion-label>{{ attachment.fileName}}</ion-label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -112,8 +112,19 @@
|
||||
</div>
|
||||
<div style="text-align: end;">
|
||||
|
||||
<div *ngIf="message.messageStatus != 'send'" style="font-size: .6875rem;">A enviar</div>
|
||||
<div *ngIf="message.messageStatus == 'send'" style="font-size: .6875rem;">Enviado</div>
|
||||
<!-- <div *ngIf="messageStatus(message) == 'enviar'" style="font-size: .6875rem;">A enviar</div>
|
||||
<div *ngIf="messageStatus(message) == 'enviado'" style="font-size: .6875rem;">Enviado</div>
|
||||
<div *ngIf="messageStatus(message) == 'allReceived'" style="font-size: .6875rem;">allReceived</div>
|
||||
<div *ngIf="messageStatus(message) == 'allViewed'" style="font-size: .6875rem;">allViewed</div>
|
||||
-->
|
||||
|
||||
<div *ngIf="totalMembers != 0">
|
||||
<ion-icon *ngIf="messageStatus(message) == 'enviar'" src="assets/images/clock-regular.svg"></ion-icon>
|
||||
<ion-icon *ngIf="messageStatus(message) == 'enviado'" src="assets/images/check-solid.svg"></ion-icon>
|
||||
<ion-icon *ngIf="messageStatus(message) == 'allReceived'" src="assets/images/check-double-solid.svg"></ion-icon>
|
||||
<ion-icon *ngIf="messageStatus(message) == 'allViewed'" src="assets/images/check-double-solid -viewed.svg"></ion-icon>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -147,7 +158,7 @@
|
||||
|
||||
|
||||
<!-- <ion-footer (click)="ChatSystemService.getDmRoom(roomId).sendReadMessage()"> -->
|
||||
<ion-footer >
|
||||
<ion-footer (click)="sendReadMessage()">
|
||||
|
||||
<!-- <div class="typing" *ngIf="ChatSystemService.getDmRoom(roomId).otherUserType == true" >
|
||||
<ngx-letters-avatar *ngIf="showAvatar"
|
||||
|
||||
@@ -464,3 +464,26 @@ ion-footer {
|
||||
cursor: pointer;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal img {
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
}
|
||||
|
||||
.modal button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { AnimationController, GestureController, IonRange, ModalController, PopoverController } from '@ionic/angular';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
import { ContactsPage } from '../contacts/contacts.page';
|
||||
@@ -21,11 +21,10 @@ import { Howl } from 'howler';
|
||||
import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page';
|
||||
import { PermissionService } from 'src/app/services/permission.service';
|
||||
import { Observable as DexieObservable } from 'Dexie';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { RoomLocalRepository } from 'src/app/module/chat/data/repository/room-local-repository.service'
|
||||
import { MemberListLocalRepository } from 'src/app/module/chat/data/repository/member-list-local-repository.service'
|
||||
import { MessageTable } from 'src/app/module/chat/infra/database/dexie/schema/message';
|
||||
import { RoomListItemOutPutDTO } from 'src/app/module/chat/data/dto/room/roomListOutputDTO';
|
||||
import { ChatServiceService } from 'src/app/module/chat/domain/chat-service.service';
|
||||
import { EditMessagePage } from 'src/app/modals/edit-message/edit-message.page';
|
||||
import { MessageEntity } from 'src/app/module/chat/domain/entity/message';
|
||||
@@ -44,10 +43,10 @@ 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 { MessageLocalDataSourceService } from 'src/app/module/chat/data/repository/message/message-local-data-source.service';
|
||||
import { MessageRemoteDataSourceService } from 'src/app/module/chat/data/repository/message/message-remote-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";
|
||||
import { RoomTable } from 'src/app/module/chat/infra/database/dexie/schema/room';
|
||||
import { Logger } from 'src/app/services/logger/main/service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -121,7 +120,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
roomData$: DexieObservable<RoomTable | undefined>
|
||||
roomStatus$: DexieObservable<Boolean >
|
||||
roomMessage$: DexieObservable<MessageTable[]>
|
||||
roomMembers$: DexieObservable<MemberTable[] | undefined>
|
||||
roomMembers$: Observable<MemberTable[] | undefined>
|
||||
//userTyping$: DexieObservable<TypingTable[] | undefined>
|
||||
userTyping$: TypingTable[] | undefined
|
||||
newMessagesStream!: Subscription
|
||||
@@ -142,6 +141,9 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
MessageAttachmentFileType = MessageAttachmentFileType
|
||||
MessageAttachmentFileSource = MessageAttachmentSource
|
||||
|
||||
@ViewChild('imageModal') imageModal: TemplateRef<any>;
|
||||
totalMembers = 0
|
||||
|
||||
constructor(
|
||||
public popoverController: PopoverController,
|
||||
private modalController: ModalController,
|
||||
@@ -166,7 +168,6 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
private userTypingLocalRepository: UserTypingLocalRepository,
|
||||
private UserTypingRemoteRepositoryService: UserTypingRemoteRepositoryService,
|
||||
private messageLocalDataSourceService: MessageLocalDataSourceService,
|
||||
private MessageRemoteDataSourceService: MessageRemoteDataSourceService
|
||||
) {
|
||||
// update
|
||||
this.checkAudioPermission()
|
||||
@@ -178,7 +179,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
this.roomData$ = this.RoomLocalRepository.getRoomByIdLive(this.roomId)
|
||||
|
||||
this.roomData$.subscribe(e => {
|
||||
console.log(e)
|
||||
// console.log(e)
|
||||
if(e) {
|
||||
this.roomType = e.roomType
|
||||
}
|
||||
@@ -189,10 +190,15 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
this.listenToIncomingMessage();
|
||||
this.listenToDeleteMessage();
|
||||
this.listenToUpdateMessage();
|
||||
this.listenToSendMessage()
|
||||
this.listenToSendMessage();
|
||||
|
||||
// this.roomMessage$ = this.messageRepositoryService.getItemsLive(this.roomId)
|
||||
this.roomMembers$ = this.MemberListLocalRepository.getRoomMemberByIdLive(this.roomId) as any
|
||||
this.roomMembers$ = this.MemberListLocalRepository.getRoomMemberByIdLive(this.roomId).pipe(
|
||||
tap((members) => {
|
||||
this.totalMembers = members.length
|
||||
})
|
||||
)
|
||||
|
||||
this.roomStatus$ = this.MemberListLocalRepository.allMemberOnline(this.roomId)
|
||||
this.chatServiceService.getRoomById(this.roomId)
|
||||
|
||||
@@ -206,6 +212,25 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
})
|
||||
}
|
||||
|
||||
messageStatus(message: MessageEntity) {
|
||||
if(this.allViewed(message)) {
|
||||
return 'allViewed'
|
||||
} else if(this.allReceived(message)) {
|
||||
return 'allReceived'
|
||||
} else if (message.messageStatus == 'send') {
|
||||
return 'enviado'
|
||||
} else {
|
||||
return 'enviar'
|
||||
}
|
||||
}
|
||||
allReceived(message: MessageEntity) {
|
||||
return message.info.filter(e => typeof e.deliverAt == 'string').length == this.totalMembers
|
||||
}
|
||||
|
||||
allViewed(message: MessageEntity) {
|
||||
return message.info.filter(e => typeof e.readAt == 'string').length == this.totalMembers
|
||||
}
|
||||
|
||||
async getMessages() {
|
||||
|
||||
// dont remove this line
|
||||
@@ -226,7 +251,6 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
if(message.hasAttachment) {
|
||||
|
||||
if(message.$id) {
|
||||
console.log('message.$id', message.$id)
|
||||
this.chatServiceService.getMessageAttachmentByMessageId(message).then((result)=> {
|
||||
if(result.isOk()) {
|
||||
message.attachments[0].safeFile = result.value
|
||||
@@ -253,6 +277,26 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
async onImageError() {}
|
||||
|
||||
|
||||
sendReadMessage() {
|
||||
|
||||
Logger.info('send read at')
|
||||
|
||||
for(const message of this.messages1[this.roomId]) {
|
||||
|
||||
const me = message.info.find(e => e.memberId == SessionStore.user.UserId && typeof e.readAt == 'string')
|
||||
|
||||
if(!me) {
|
||||
this.chatServiceService.sendReadAt({
|
||||
memberId: SessionStore.user.UserId,
|
||||
messageId: message.id,
|
||||
requestId: '',
|
||||
roomId: this.roomId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
listenToIncomingMessage() {
|
||||
this.messageReceiveSubject?.unsubscribe();
|
||||
this.messageReceiveSubject = this.chatServiceService.listenToIncomingMessage(this.roomId).subscribe(async (message) => {
|
||||
@@ -302,7 +346,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
const index = this.messages1[this.roomId].findIndex(e => e?.id === updateMessage.id); // Use triple equals for comparison
|
||||
|
||||
if (index !== -1) { // Check if the item was found
|
||||
console.log('update ==')
|
||||
this.messages1[this.roomId][index].info = updateMessage.info
|
||||
this.messages1[this.roomId][index].message = updateMessage.message
|
||||
this.messages1[this.roomId][index].reactions = updateMessage.reactions
|
||||
} else {
|
||||
@@ -316,19 +360,18 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
this.messageSendSubject?.unsubscribe();
|
||||
|
||||
this.messageSendSubject = this.chatServiceService.listenToSendMessage(this.roomId).subscribe((updateMessage) => {
|
||||
console.log('update message', updateMessage);
|
||||
|
||||
const index = this.messages1[this.roomId].findIndex(e => e?.requestId === updateMessage.requestId); // Use triple equals for comparison
|
||||
|
||||
if (index !== -1) { // Check if the item was found
|
||||
console.log('update ==')
|
||||
|
||||
this.messages1[this.roomId][index].id = updateMessage.id
|
||||
this.messages1[this.roomId][index].info = updateMessage.info
|
||||
|
||||
let attachmentIndex = 0;
|
||||
|
||||
for(const message of updateMessage.attachments) {
|
||||
console.log('set attachmen id', message)
|
||||
|
||||
this.messages1[this.roomId][index].attachments[attachmentIndex].id = message.id
|
||||
attachmentIndex++;
|
||||
}
|
||||
@@ -365,9 +408,9 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
|
||||
|
||||
sendReadAt() {
|
||||
this.chatServiceService.messageMarkAsRead({roomId: this.roomId}).then((e) => {
|
||||
console.log(e)
|
||||
})
|
||||
// this.chatServiceService.messageMarkAsRead({roomId: this.roomId}).then((e) => {
|
||||
// console.log(e)
|
||||
// })
|
||||
}
|
||||
|
||||
sendTyping() {
|
||||
@@ -1240,6 +1283,11 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
|
||||
closeModal(a: any) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,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';
|
||||
|
||||
@Component({
|
||||
selector: 'app-new-group',
|
||||
@@ -124,7 +125,7 @@ export class NewGroupPage implements OnInit{
|
||||
roomName: this.groupName,
|
||||
createdBy: SessionStore.user.UserId,
|
||||
roomType: 0,
|
||||
expirationDate: this.expirationDate?.toISOString(),
|
||||
expirationDate: this.expirationDate?.toISOString() ? UDate.GetDateWithTimeZone(this.expirationDate) : null,
|
||||
members: []
|
||||
})
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.roomData$ = this.RoomLocalRepository.getRoomByIdLive(this.roomId)
|
||||
|
||||
this.roomData$.subscribe(e => {
|
||||
console.log(e)
|
||||
// console.log(e)
|
||||
if(e) {
|
||||
this.roomType = e.roomType
|
||||
}
|
||||
@@ -337,12 +337,13 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
|
||||
if (index !== -1) { // Check if the item was found
|
||||
|
||||
this.messages1[this.roomId][index].id = updateMessage.id
|
||||
this.messages1[this.roomId][index].info = updateMessage.info
|
||||
|
||||
let attachmentIndex = 0;
|
||||
|
||||
for(const message of updateMessage.attachments) {
|
||||
for(const attachment of updateMessage.attachments) {
|
||||
|
||||
this.messages1[this.roomId][index].attachments[attachmentIndex].id = message.id
|
||||
this.messages1[this.roomId][index].attachments[attachmentIndex].id = attachment.id
|
||||
attachmentIndex++;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { SafeResourceUrl } from "@angular/platform-browser";
|
||||
import { MessageAttachmentFileType, MessageAttachmentSource } from "src/app/module/chat/data/dto/message/messageOutputDTO";
|
||||
import { MessageEntity } from "src/app/module/chat/domain/entity/message";
|
||||
|
||||
export class MessageViewModal {
|
||||
$id: number
|
||||
id: string
|
||||
roomId?: string
|
||||
receiverId?: number
|
||||
message: string
|
||||
messageType: number = 0
|
||||
canEdit: boolean = false
|
||||
oneShot: boolean = false
|
||||
sentAt: string
|
||||
requireUnlock: boolean = false
|
||||
info: {
|
||||
memberId?: number
|
||||
readAt?: string,
|
||||
deliverAt?: string
|
||||
}[] = []
|
||||
sender: {
|
||||
wxUserId: number,
|
||||
wxFullName: string,
|
||||
wxeMail: string,
|
||||
userPhoto: string,
|
||||
}
|
||||
sending: boolean = false
|
||||
sendAttemp = 0
|
||||
|
||||
attachments: {
|
||||
safeFile?: SafeResourceUrl;
|
||||
fileType: MessageAttachmentFileType,
|
||||
source: MessageAttachmentSource,
|
||||
file?: string,
|
||||
fileName: string,
|
||||
applicationId?: number,
|
||||
docId?: string,
|
||||
mimeType?: string,
|
||||
description?: string
|
||||
id?: string
|
||||
}[] = []
|
||||
|
||||
reactions = []
|
||||
requestId: string
|
||||
status = ''
|
||||
|
||||
constructor(model: MessageEntity) {
|
||||
Object.assign(this, model)
|
||||
}
|
||||
|
||||
|
||||
messageStatus(totalMembers: number) {
|
||||
if(this.allViewed(totalMembers)) {
|
||||
this.status = 'allViewed'
|
||||
} else if(this.allReceived(totalMembers)) {
|
||||
this.status = 'allReceived'
|
||||
} else if (this.id) {
|
||||
this.status = 'enviado'
|
||||
} else {
|
||||
this.status = 'enviar'
|
||||
}
|
||||
}
|
||||
|
||||
allReceived(totalMembers: number) {
|
||||
return this.info.filter(e => typeof e.deliverAt == 'string').length == totalMembers
|
||||
}
|
||||
|
||||
allViewed(totalMembers: number) {
|
||||
return this.info.filter(e => typeof e.readAt == 'string').length == totalMembers
|
||||
}
|
||||
|
||||
get hasAttachment() {
|
||||
return this.attachments.length >= 1
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export class UDate {
|
||||
static GetDateWithTimeZone(Date: Date) {
|
||||
let date = Date;
|
||||
|
||||
const tzOffset = -date.getTimezoneOffset(); // in minutes
|
||||
const diff = tzOffset >= 0 ? '+' : '-';
|
||||
const pad = (n: number) => (n < 10 ? '0' : '') + n;
|
||||
|
||||
return date.getFullYear() +
|
||||
'-' + pad(date.getMonth() + 1) +
|
||||
'-' + pad(date.getDate()) +
|
||||
'T' + pad(date.getHours()) +
|
||||
':' + pad(date.getMinutes()) +
|
||||
':' + pad(date.getSeconds()) +
|
||||
diff + pad(Math.floor(Math.abs(tzOffset) / 60)) +
|
||||
':' + pad(Math.abs(tzOffset) % 60);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { timer, Subscription } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Interface representing a cron job.
|
||||
*/
|
||||
interface CronJob {
|
||||
name: string;
|
||||
timeExecute: Date;
|
||||
callbackFunction: () => void;
|
||||
subscription: Subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to manage cron jobs using RxJS. Allows scheduling, replacing, and
|
||||
* removing jobs that execute a callback function at a specified time.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CronJobService {
|
||||
/**
|
||||
* A map to store cron jobs with a key composed of the job name and execution time.
|
||||
*/
|
||||
private jobs: Map<string, CronJob> = new Map();
|
||||
|
||||
/**
|
||||
* Generates a unique key for a cron job based on its name and execution time.
|
||||
* @param name - The name of the cron job.
|
||||
* @param timeExecute - The time when the job should execute.
|
||||
* @returns A string key combining the name and timeExecute.
|
||||
*/
|
||||
private generateJobKey(name: string, timeExecute: Date): string {
|
||||
return `${name}_${timeExecute.getTime()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cron job or replaces an existing one if the job with the same
|
||||
* name and execution time already exists. The job will execute the provided
|
||||
* callback function at the specified time.
|
||||
*
|
||||
* @param name - The name of the cron job.
|
||||
* @param timeExecute - The time when the job should execute.
|
||||
* @param callbackFunction - The function to be called when the job executes.
|
||||
*/
|
||||
createCronJob(name: string, timeExecute: Date, callbackFunction: () => void) {
|
||||
const now = new Date();
|
||||
const delay = timeExecute.getTime() - now.getTime();
|
||||
|
||||
if (delay < 0) {
|
||||
console.error('Time to execute is in the past!');
|
||||
return;
|
||||
}
|
||||
|
||||
const jobKey = this.generateJobKey(name, timeExecute);
|
||||
|
||||
// If the job already exists, replace it
|
||||
if (this.jobs.has(jobKey)) {
|
||||
this.removeCronJob(name, timeExecute);
|
||||
}
|
||||
|
||||
// Create a new timer and store the subscription
|
||||
const subscription = timer(delay).subscribe(() => {
|
||||
callbackFunction();
|
||||
this.removeCronJob(name, timeExecute);
|
||||
});
|
||||
|
||||
this.jobs.set(jobKey, { name, timeExecute, callbackFunction, subscription });
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a cron job by its name and execution time.
|
||||
*
|
||||
* @param name - The name of the cron job.
|
||||
* @param timeExecute - The time when the job was supposed to execute.
|
||||
*/
|
||||
removeCronJob(name: string, timeExecute: Date) {
|
||||
const jobKey = this.generateJobKey(name, timeExecute);
|
||||
const job = this.jobs.get(jobKey);
|
||||
if (job) {
|
||||
job.subscription.unsubscribe();
|
||||
this.jobs.delete(jobKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all currently scheduled cron jobs.
|
||||
*
|
||||
* @returns An array of all scheduled cron jobs.
|
||||
*/
|
||||
getCronJobs(): CronJob[] {
|
||||
return Array.from(this.jobs.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all cron jobs with the specified name, regardless of their execution time.
|
||||
*
|
||||
* @param name - The name of the cron jobs to remove.
|
||||
*/
|
||||
removeAllJobsByName(name: string) {
|
||||
for (const [key, job] of this.jobs) {
|
||||
if (job.name === name) {
|
||||
job.subscription.unsubscribe();
|
||||
this.jobs.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user