import { Injectable } from '@angular/core'; import { Message } from 'src/app/models/chatMethod'; import { SessionStore } from 'src/app/store/session.service'; import { capitalizeTxt } from 'src/plugin/text'; import { NfService } from 'src/app/services/chat/nf.service'; import { RochetChatConnectorService } from 'src/app/services/chat/rochet-chat-connector.service'; import { showDateDuration } from 'src/plugin/showDateDuration'; import { ChatMethodsService } from './chat-methods.service'; import { MessageModel, attachments } from '../../models/beast-orm'; import { AESEncrypt } from '../aesencrypt.service'; import { HttpEventType } from '@angular/common/http'; import { AttachmentsService } from 'src/app/services/attachments.service'; import { NetworkServiceService , ConnectionStatus} from 'src/app/services/network-service.service'; import { ChatSystemService } from './chat-system.service'; import { v4 as uuidv4 } from 'uuid' import { NotificationsService } from 'src/app/services/notifications.service'; @Injectable({ providedIn: 'root' }) export class MessageService { customFields channels = [] mentions = [] msg = '' rid = '' ts = {} u = { name: '', username: '', _id: "" } t = '' _id = '' id = '' // table id _updatedAt file attachments displayType = '' temporaryData: any hasFile = false hasSendAttachment = false sendAttempt = 0 uploadingFile = false errorUploadingAttachment = false loadHistory = false from: 'Offline'|'History'|'stream'| 'send' duration = '' localReference = null viewed: string[] = [] received: string[]= [] addToDb = false messageSend = false delate = false delateRequest = false downloadLoader: boolean = false downloadAttachments = false; downloadAttachmentsTemp = 0; UploadAttachmentsTemp = 0; manualRetry = false origin: 'history' | 'stream' | 'local' messageModelInstance: MessageModel attachmentsModelData: any constructor( private NfService: NfService, private RochetChatConnectorService: RochetChatConnectorService, private ChatMethodsService: ChatMethodsService, private AESEncrypt: AESEncrypt, private AttachmentsService: AttachmentsService, private NetworkServiceService: NetworkServiceService, private ChatSystemService: ChatSystemService, private notificationService: NotificationsService) { } setData({customFields = {}, channels, mentions, msg ,rid ,ts, u, t, _id, id, _updatedAt, file, attachments, temporaryData, localReference = 'out-'+uuidv4() , viewed = [], received = [], delate = false, delateRequest =false, from, sendAttempt = 0, origin, attachmentsModelData, hasFile = false, UploadAttachmentsTemp = 0 }:Message) { this.channels = channels || [] this.mentions = mentions || [] this.msg = msg || "" this.rid = rid this.ts = ts this.u = u || { name: this.usernameToDisplayName(SessionStore.user.UserName), username: SessionStore.user.UserName, _id: ""} this.t = t this._id = _id || "" this._updatedAt = _updatedAt || new Date().getTime() this.file = file this.temporaryData = temporaryData this.localReference = localReference || null this.id = id this.delate = delate this.delateRequest = delateRequest this.sendAttempt = 0 this.origin = origin this.attachmentsModelData = attachmentsModelData this.UploadAttachmentsTemp = UploadAttachmentsTemp if(this.attachments?.length >= 1 && attachments?.length >= 1) { this.attachments[0] = Object.assign(this.attachments[0], attachments[0]) } else { this.attachments = attachments } this.viewed = [...new Set([...viewed,...this.viewed])]; this.received = [...new Set([...received,...this.received])]; if(!this.ts) { this.messageSend = false } else { this.messageSend = true } this.hasFile = hasFile if (this.instanceHasAttachment && !this.hasFile) { this.hasFile = true } if(this.hasFile) { if(this.file.type != 'application/webtrix') { this.displayType = this.file.type.replace('application/','').toUpperCase() } } if(!this.u.name && this.u.username) { const user = this.ChatSystemService.users.find((u)=> u.username == this.u.username) if(user) { this.u.name = user.name } else if( this.u.username == SessionStore.user.UserName) { this.u.name = SessionStore.user.FullName } else { } } this.calDateDuration() } get offline () { if(!this._id) { return true } if(!this.ts) { return true } return false } /** * Message is on the server */ get online() { return !this.offline } /** * if Message is already saved on database */ get save() { return this.id != '' } private usernameToDisplayName(username) { try { const firstName = capitalizeTxt(username.split('.')[0]) const lastName = capitalizeTxt(username.split('.')[1]) return firstName + ' ' + lastName } catch (error) { return username } } async send(): Promise { if(this.messageSend) { return new Promise((resolve, reject) => { resolve('solve') }) } this.sendAttempt++; this.manualRetry = false if(!this.hasFile) { const params = { roomId:this.rid, msg:this.msg, localReference: this.localReference } await this.sendRequest(params) } else { let uploadSuccessfully = await this.sendRequestAttachment() if(uploadSuccessfully) { const params = {roomId:this.rid, msg: this.msg, attachments: this.attachments, file: this.file, localReference: this.localReference} await this.sendRequest(params) } else if(this.RochetChatConnectorService.isLogin == false) { this.RochetChatConnectorService.registerCallback({ type: 'reConnect', funx: async () => { this.send().catch((error) =>{ console.error(error) }) return true } }) } else if(this.NetworkServiceService.getCurrentNetworkStatus() == ConnectionStatus.Offline) { this.RochetChatConnectorService.registerCallback({ type: 'reConnect', funx: async ()=> { await this.send().catch((error) => { console.error(error) }) return true } }) } else if (this.UploadAttachmentsTemp <= 3) { setTimeout(async () => { return await this.send() }, 3000) } else if (this.NetworkServiceService.getCurrentNetworkStatus() == ConnectionStatus.Online) { this.manualRetry = true } } } functionTimer = null; async sendRequestAttachment() { this.uploadingFile = true let uploadSuccessfully = false if(!this.instanceHasAttachmentBase64 && !this.temporaryData) { try { await this.getFileFromDB() } catch (error) {} } if(!this.instanceHasTemporaryData) { await this.generateTemporaryData() } if(this.hasSendAttachment == false) { try { uploadSuccessfully = await this.NfService.beforeSendAttachment(this) this.UploadAttachmentsTemp++ this.uploadingFile = false this.manualRetry = false this.errorUploadingAttachment = false this.hasSendAttachment = true this.saveChanges() } catch (error) { this.uploadingFile = false this.errorUploadingAttachment = true this.UploadAttachmentsTemp++ this.saveChanges() } } return uploadSuccessfully } async sendRequest(params) { if(params?.attachments) { if(params?.attachments[0]?.image_url) { delete params?.attachments[0]?.image_url } } if(this.NetworkServiceService.getCurrentNetworkStatus() == ConnectionStatus.Online) { if(this.msg == '') { if(this.sendAttempt >= 4) { this.RochetChatConnectorService.send(params).then( (ChatMessage: any) => { ChatMessage = ChatMessage.message.result clearTimeout(this.functionTimer); this.redefinedMessage(ChatMessage) } ).catch((error) => { clearTimeout(this.functionTimer); console.error(error) }) } } else { this.RochetChatConnectorService.send(params).then( (ChatMessage: any) => { ChatMessage = ChatMessage.message.result clearTimeout(this.functionTimer); this.redefinedMessage(ChatMessage) } ).catch((error) => { clearTimeout(this.functionTimer); console.error(error) }) } this.functionTimer = setTimeout(() => { this.RochetChatConnectorService.registerCallback({ type:'Onmessage', key:'ping-pong-message', funx:(message: any) => { if(message.msg == "ping") { if(this.sendAttempt >= 3) { this.manualRetry = true } else { this.send() } return true } this.saveChanges() } }) this.RochetChatConnectorService.wsSend({message:{msg:"pong"}, loginRequired: false}) }, 3000) } else { this.RochetChatConnectorService.registerCallback({ type: 'reConnect', funx: async ()=> { this.send().catch((error) =>{ console.error(error) }) return true } }) } } async redefinedMessage(ChatMessage , update = true) { ChatMessage = this.NfService.fix_updatedAt(ChatMessage) const message = this.getChatObj() this.manualRetry = false this.messageSend = true ChatMessage = Object.assign(message, ChatMessage) this.setData(ChatMessage) const roomObject = this.ChatSystemService.getRoomById(this.rid) const users = roomObject.getUsersByStatus('online') for(const user of users) { this.addReceived(user._id) } if(!roomObject.isGroup) { var memeberTosend = this.ChatSystemService.getRoomById(this.rid).membersExcludeMe this.notificationService.ChatSendMessageNotification(memeberTosend[0].username,ChatMessage.u.name,ChatMessage.msg,this.rid) } else { var memeberTosend = this.ChatSystemService.getRoomById(this.rid).membersExcludeMe var usersNames = []; for(let i = 0; i < memeberTosend.length; i++) { usersNames.push(memeberTosend[i].username) } this.notificationService.ChatSendMessageNotificationGrup(usersNames,ChatMessage.u.name,ChatMessage.msg,this.rid) } await this.saveChanges() } sendNotificaton downloadFileMsg() { this.downloadLoader = true; let downloadFile = ""; this.AttachmentsService.downloadFile(this.file.guid).subscribe(async (event) => { if (event.type === HttpEventType.DownloadProgress) { } else if (event.type === HttpEventType.Response) { if (this.file.type == "application/img") { downloadFile = 'data:image/jpeg;base64,' + btoa(new Uint8Array(event.body).reduce((data, byte) => data + String.fromCharCode(byte), '')); } else if (this.file.type != "application/img") { downloadFile = new Uint8Array(event.body).reduce((data, byte) => data + String.fromCharCode(byte), ''); } this.attachments[0] = { image_url: downloadFile, name: this.attachments[0].name, title: this.attachments[0].title, title_link: downloadFile, title_link_download: this.attachments[0].title_link_download, ts: this.attachments[0].ts } // save the changes to the storage this.saveChanges() this.addFileToDb() this.downloadLoader = false; this.downloadAttachments = true this.downloadAttachmentsTemp++; } }, ()=>{ // error this.downloadLoader = false; this.downloadAttachments = false this.downloadAttachmentsTemp++; }); } private calDateDuration(date = null) { this.duration = showDateDuration(date || this._updatedAt); } async delateStatusFalse() { this.delate = true this.saveChanges() } addViewed(id: string) { if(this.messageOwner()) { let found = this.viewed.find((UserId) => UserId == id) if(!found) { this.viewed.push(id) return true } } return false } addReceived(id: string) { if(this.messageOwner()) { let found = this.received.find((UserId) => UserId == id) if(!found) { this.received.push(id) return true } } return false } async delateDB() { if(!this.messageModelInstance) { this.messageModelInstance = await this.getRowInstance() } await this.messageModelInstance.delete() } isSenderIsNotMe(ChatMessage) { return SessionStore.user.ChatData.data.userId != ChatMessage.u._id } messageOwner() { return SessionStore.user.ChatData.data.userId == this.u._id } getChatObj() { return { channels: this.channels, mentions: this.mentions, // msg: this.AESEncrypt.encrypt(this.msg, SessionStore.user.UserName), sendAttempt: this.sendAttempt, msg:this.msg, rid: this.rid, ts: this.ts, u: this.u, _id: this._id, t: this.t, id: this.id, hasFile: this.hasFile, origin: this.origin, _updatedAt: this._updatedAt, messageSend: this.messageSend, offline: this.offline, viewed: this.viewed, received: this.received, localReference: this.localReference, attachments: this.attachments, file: this.file, delate: this.delate, UploadAttachmentsTemp: this.UploadAttachmentsTemp } } earlySave = false async addMessageDB() { if(!this.addToDb && this.save) { this.addToDb = true const message = this.getChatObj() if(this.instanceHasAttachment) { this.hasFile = true } delete message.id const createdMessage = await MessageModel.create(message) if(this.instanceHasAttachment) { this.addFileToDb() } this.messageModelInstance = createdMessage this.id = createdMessage.id if(this.earlySave) { this.saveChanges(); } } } async addFileToDb() { if(!this.messageModelInstance) { this.messageModelInstance = await this.getRowInstance() } const createdMessage: any = this.messageModelInstance try { let file = {} if(this.attachmentsModelData) { file = {image_url: this.attachmentsModelData?.fileBase64} } await attachments.create({messageId: createdMessage.id, attachments: this.attachments, file: Object.assign(this.file, file) }) } catch (error) { console.log(error) } } async getFileFromDB() { if(!this.messageModelInstance) { this.messageModelInstance = await this.getRowInstance() } const data = await this.messageModelInstance.getAttachments() console.log('data', data); this.attachments = data.attachments this.file = data.file } async generateTemporaryData () { const blob: any = await this.base64StringToBlob(this.file.image_url) const formData = new FormData(); formData.append("blobFile", blob); this.temporaryData = formData } get instanceHasAttachment() { if (this.file) { if(this.file.type) { if(typeof(this.file.type) == 'string') { return true } } } return false } get instanceHasAttachmentBase64() { if (this.file) { if(this.file.type) { if(this.file?.image_url) { return true } } } return false } get instanceHasTemporaryData() { if (!this.temporaryData) { return false } return true } base64StringToBlob(base64Data) { return new Promise((resolve, reject) => { fetch(base64Data) .then(res => resolve(res.blob())) .then(console.log) }) } async getRowInstance () { if(this.save) { const message = this.getChatObj() let a if(!message.id) { delete message.id } if (this.localReference) { a = await MessageModel.get({localReference: this.localReference}) } else if (this._id) { a = await MessageModel.get({_id: this._id}) } else if(this.id) { a = await MessageModel.get({id: this.id}) } return a } } async saveChanges() { if(!this.messageModelInstance) { this.messageModelInstance = await this.getRowInstance() } if(this.save && this.messageModelInstance) { const message = this.getChatObj() if(!message.id) { delete message.id } for( const [name, value] of Object.entries(message)) { try { this.messageModelInstance[name] = value } catch (error) {} } await this.messageModelInstance.save() } else { this.earlySave = true } } decryptMessage() { try { // this.msg = this.AESEncrypt.decrypt(this.msg, SessionStore.user.UserName) } catch (error) {} } UIdata() { this.incomingFromCurrentUser = this.u.username!= SessionStore.user.UserName this.hasMessage = this.msg !='' } incomingFromCurrentUser = false hasMessage = false }