import { AfterViewInit, Component, ElementRef, EventEmitter, HostListener, Input, OnChanges, OnDestroy, OnInit, Output, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren } 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'; import { ViewDocumentPage } from 'src/app/modals/view-document/view-document.page'; import { ThemeService } from 'src/app/services/theme.service'; import { FileType } from 'src/app/models/fileType'; import { SearchPage } from 'src/app/pages/search/search.page'; import { CameraResultType } from '@capacitor/camera'; import { RecordingData } from 'capacitor-voice-recorder'; import { DomSanitizer } from '@angular/platform-browser'; import { Platform } from '@ionic/angular'; import { File } from '@awesome-cordova-plugins/file/ngx'; import { FileOpener } from '@awesome-cordova-plugins/file-opener/ngx'; import { SessionStore } from 'src/app/store/session.service'; 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 { Observable, Subscription } from 'rxjs'; import { RoomLocalRepository } from 'src/app/module/chat/data/repository/room/room-local-repository.service' import { MemberListLocalRepository } from 'src/app/module/chat/data/repository/member/member-list-local-repository.service' import { ChatServiceService } from 'src/app/module/chat/domain/chat-service.service'; import { EditMessagePage } from 'src/app/ui/chat/modal/edit-message/edit-message.page'; import { IMessageType, MessageAttachmentFileType, MessageAttachmentSource } from 'src/app/core/chat/entity/message'; import { JSFileToDataUrl } from 'src/app/utils/ToBase64'; import { CameraService } from 'src/app/infra/camera/camera.service' import { FilePickerWebService } from 'src/app/infra/file-picker/web/file-picker-web.service' import { FilePickerService } from 'src/app/infra/file-picker/file-picker.service' import { allowedDocExtension } from 'src/app/utils/allowedDocExtension'; import { SpeakerService, StartRecordingResultError, StopRecordingResultError } from 'src/app/infra/speaker/speaker.service' import { compressImageBase64 } from 'src/app/utils/imageCompressore'; import { ChatPopoverPage } from '../../modal/chat-popover/chat-popover.page'; import { LastMessage } from '../../utils/lastMessage'; import { UserTypingLocalRepository } from 'src/app/module/chat/data/repository/typing/user-typing-local-data-source.service'; import { UserTypingRemoteRepositoryService } from 'src/app/module/chat/data/repository/typing/user-typing-live-data-source.service'; import { RoomType } from "src/app/core/chat/entity/group"; import { tap } from 'rxjs/operators'; import { AlertController } from '@ionic/angular'; import { ViewOncesImagePage, ViewOncesImagePageInput } from '../../modal/view-onces/view-onces.page'; import { MemberTable } from 'src/app/infra/database/dexie/instance/chat/schema/members'; import { MessageTable } from 'src/app/infra/database/dexie/instance/chat/schema/message'; import { RoomTable } from 'src/app/infra/database/dexie/instance/chat/schema/room'; import { RoomViewModel } from '../../store/model/room'; import { MessageViewModal } from '../../store/model/message'; import { XBallon } from '../../utils/messageBallon'; import { whatsappDate } from 'src/app/ui/shared/utils/whatappdate'; import { IDBoolean } from 'src/app/infra/database/dexie/type'; import { Result } from 'neverthrow'; import { MessageOutPutDataDTO } from 'src/app/core/chat/repository/dto/messageOutputDTO'; import { v4 as uuidv4 } from 'uuid'; @Component({ selector: 'app-messages', templateUrl: './messages.page.html', styleUrls: ['./messages.page.scss'], }) export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy { showLoader: boolean; @ViewChild('scrollMe') private myScrollContainer: ElementRef; @ViewChild('message-item') messageContainer: ElementRef; dm: any; userPresence = ''; dmUsers: any; downloadProgess = 0; roomType!: RoomType RoomTypeEnum = RoomType @Input() _room!: RoomViewModel room!: RoomViewModel @Output() openNewEventPage: EventEmitter = new EventEmitter(); @Output() getDirectMessages: EventEmitter = new EventEmitter(); @Output() closeAllDesktopComponents: EventEmitter = new EventEmitter(); @Output() showEmptyContainer: EventEmitter = new EventEmitter(); @Output() openGroupContacts: EventEmitter = new EventEmitter(); @Output() openEditGroupPage: EventEmitter = new EventEmitter(); @Output() getGroups: EventEmitter = new EventEmitter(); scrollingOnce: boolean = true; private scrollChangeCallback: () => void; currentPosition: any; startPosition: number; mesageItemDropdownOptions: boolean = false; scrollToBottomBtn = false; longPressActive = false; downloadFile: string; showAvatar = true; recording = false; allowTyping = true; lastAudioRecorded = ''; audioRecordedSafe: any = ""; audioRecordedDataUrl: any = ""; audioDownloaded: any = ""; durationDisplay = ''; duration = 0; audioPermissionStatus: 'granted' | 'denied' | 'prompt' | null = null SessionStore = SessionStore audioPlay: Howl = null; isPlaying = false; audioProgress = 0; audioDuration = 0; audioTimer: any; @ViewChild('range', { static: false }) range: IonRange; @ViewChild('array') myInputRef!: ElementRef; isAdmin = true; roomCountDownDate: string; audioMimeType = '' textField = '' roomData$: Observable roomStatus$: DexieObservable roomMessage$: DexieObservable roomMembers$: Observable userTyping$: string[] newMessagesStream!: Subscription selectedMessage: any = null; emojis: string[] = ['😊', '😂', '❤️', '👍', '😢']; // Add more emojis as needed totalMessage = 0 recordData:RecordingData messages: MessageViewModal[] = [] messageReceiveSubject: Subscription messageDeleteSubject: Subscription messageUpdateSubject: Subscription messageSendSubject: Subscription messageTypingSubject: Subscription messageOnReconnectSubject: Subscription messageOnSetRoomId: Subscription messages1: {[key: string]: MessageViewModal[]} = {} MessageAttachmentFileType = MessageAttachmentFileType MessageAttachmentFileSource = MessageAttachmentSource IMessageType = IMessageType @ViewChild('imageModal') imageModal: TemplateRef; totalMembers = 0 members: MemberTable[] = [] date: {[key: string]: Object} = {} handleClickActive = true constructor( public popoverController: PopoverController, private modalController: ModalController, private animationController: AnimationController, private toastService: ToastService, private gestureController: GestureController, public ThemeService: ThemeService, private sanitiser: DomSanitizer, private file: File, private platform: Platform, private fileOpener: FileOpener, public p: PermissionService, private MemberListLocalRepository: MemberListLocalRepository, private chatServiceService: ChatServiceService, private CameraService: CameraService, private FilePickerWebService: FilePickerWebService, private FilePickerService: FilePickerService, private SpeakerService: SpeakerService, private RoomLocalRepository: RoomLocalRepository, private userTypingLocalRepository: UserTypingLocalRepository, private UserTypingRemoteRepositoryService: UserTypingRemoteRepositoryService, private alertController: AlertController, ) { // update this.checkAudioPermission() } ngOnChanges(changes: SimpleChanges): void { this.room = {...this._room} as any if(this.room.local == IDBoolean.false) { this.getMessages() this.subscribeToChanges() } else { this.getMessages() } this.roomData$ = this.RoomLocalRepository.getRoomByIdLive(this.room.$id) this.roomData$.subscribe(e => { if(e) { this.room = new RoomViewModel(e) this.roomType = e.roomType; } }) this.messageOnSetRoomId?.unsubscribe() if(this.room.local == IDBoolean.true) { this.messageOnSetRoomId = this.chatServiceService.roomDirectOnSetId({$roomId: this.room.$id}).subscribe((data) => { this.messageOnSetRoomId?.unsubscribe() this.room = new RoomViewModel(data) const hasMyMessage = this.messages1[this.room.$id].find(e => e.sender.wxUserId == SessionStore.user.UserId) if(!hasMyMessage) { setTimeout(() => { this.getMessages() }, 500) } else { this.listenToMessageLoadHistory() } setTimeout(() => { this.subscribeToChanges() }, 500) }) } } stop() { this.messageReceiveSubject?.unsubscribe(); this.messageDeleteSubject?.unsubscribe(); this.messageUpdateSubject?.unsubscribe(); this.messageSendSubject?.unsubscribe(); this.messageTypingSubject?.unsubscribe(); this.messageOnReconnectSubject?.unsubscribe(); this.messageOnSetRoomId?.unsubscribe(); this.newMessagesStream?.unsubscribe(); } subscribeToChanges() { this.listenToIncomingMessage(); this.listenToDeleteMessage(); this.listenToUpdateMessage(); this.listenToSendMessage(); this.roomMembers$ = this.MemberListLocalRepository.getRoomMemberByIdLive(this.room.id).pipe( tap((members) => { this.totalMembers = members.length this.members = members for(const member of members) { if(member.wxUserId == SessionStore.user.UserId) { this.isAdmin = member.isAdmin } } }) ) this.roomStatus$ = this.MemberListLocalRepository.allMemberOnline(this.room.id) this.messageTypingSubject?.unsubscribe() this.messageTypingSubject = this.userTypingLocalRepository.getUserTypingLiveByRoomId(this.room.id).subscribe((e) => { const arrayNames = e.filter((b)=> b.userId != SessionStore.user.UserId).map(e => e.userName) const uniqueArray = [...new Set(arrayNames)]; this.userTyping$ = uniqueArray }) as any this.updateRoomDetails() } updateRoomDetails() { this.chatServiceService.getRoomById(this.room.id) } removeBold() { if(!this.room?.local) { this.chatServiceService.removeBoldFromRoom({roomId: this.room.id}) } } @HostListener('document:click', ['$event']) handleClickOutside(event: Event) { if (!this.handleClickActive) return; const clickedInside = (event.target as HTMLElement).closest('.mat-menu-content'); if (!clickedInside) { this.selectedMessage = null; } } messageStatus(message: MessageViewModal) { if(this.allViewed(message)) { return 'allViewed' } else if(this.allReceived(message)) { return 'allReceived' } else if (message.messageHasId) { return 'enviado' } else { return 'enviar' } } allReceived(message: MessageViewModal) { return message.info.filter(e => typeof e.deliverAt == 'string').length == this.totalMembers } allViewed(message: MessageViewModal) { const totalMembers = this.members.filter((e) => message.sender.wxUserId != e.wxUserId ).length return message.info.filter(e => typeof e.readAt == 'string' && message.sender.wxUserId != e.memberId ).length == totalMembers } async getMessages() { // dont remove this line this.messages1[this.room.$id] = [] let messages = await this.chatServiceService.messageLocalGetById({ roomId: this.room.id, receiverId: this.room?.receiverId?.toString() }) if(messages.isOk()) { this.messages1[this.room.$id] = [] this.date = {} const allMessage = []; //console.time("mappingTime"); const sortMessages = messages.value.sort((a, b) => new Date(a.sentAt).getTime() - new Date(b.sentAt).getTime()) for(const message of sortMessages) { const date = whatsappDate(message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(message) allMessage.push(Ballon) } allMessage.push(new MessageViewModal(message)) } //console.timeEnd("mappingTime"); this.messages1[this.room.$id] this.messages1[this.room.$id] = allMessage // if(messages.length >= 1) { // this.messages1[this.room.$id].push(LastMessage) // } this.loadAttachment() setTimeout(() => { this.sendReadMessage() }, 1000) if(this.room.id) { this.listenToMessageLoadHistory() } } } listenToMessageLoadHistory() { this.messageOnReconnectSubject?.unsubscribe() this.messageOnReconnectSubject = this.chatServiceService.listenToMessageLoadHistory({roomId: this.room.id}).subscribe((messages) => { for(const message of messages.data.reverse()) { const found = this.messages1[this.room.$id].find((e) => e.id == message.id) if(!found) { const date = whatsappDate(message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(message) this.messages1[this.room.$id].push(Ballon) } const msg = new MessageViewModal(message as any) Object.assign(msg, message) this.messages1[this.room.$id].push(msg) } } }) } async loadAttachment() { for(const message of this.messages1[this.room.$id]) { if(message.hasAttachment && message.attachments[0].source != MessageAttachmentSource.Webtrix) { this.chatServiceService.getMessageAttachmentByMessageId(message).then((result)=> { if(result.isOk()) { message.attachments[0].safeFile = result.value if(result.value.startsWith('blob:http')) { message.attachments[0].blobURl = true } } }) } } } async onImageLoad(message: MessageViewModal, index:number) { if(message.attachments[0].fileName == LastMessage.attachments[0].fileName) { this.scrollToBottom() setTimeout(() => { this.scrollToBottom(); }, 100) this.messages1[this.room.$id].splice(index, 1); } } async onImageError() {} async viewOnce(event: Event, message: MessageViewModal, index:number) { const params: ViewOncesImagePageInput = { imageDataUrl: message.attachments[index].safeFile as any, } const modal = await this.modalController.create({ component: ViewOncesImagePage, cssClass: '', componentProps: params }); modal.present() return modal.onDidDismiss().then((res) => { this.messageDelete(message) }); } sendReadMessage() { if(this.room.local == IDBoolean.false) { for(const message of this.messages1[this.room.$id]) { if(!message.meSender()) { const me = message.haveSeen(message.info) if(!me) { // Logger.info('send read at, sender '+ message.sender.wxFullName+ ' '+ message.message +'message id'+ message.id) this.chatServiceService.sendReadAt({ memberId: SessionStore.user.UserId, messageId: message.id, requestId: uuidv4(), roomId: this.room.id }) } else { // console.log('no need', message ) } } } } else { console.log('dont read for this room') } } listenToIncomingMessage() { this.messageReceiveSubject?.unsubscribe(); this.messageReceiveSubject = this.chatServiceService.listenToIncomingMessage(this.room.id).subscribe(async (_message) => { const date = whatsappDate(_message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(_message) this.messages1[this.room.$id].push(Ballon) } const message = new MessageViewModal(_message) this.messages1[this.room.$id].push(new MessageViewModal(message)) if(message.hasAttachment) { const result = await this.chatServiceService.downloadMessageAttachmentByMessageId({ $messageId: message.id, id: message.attachments[0].id }) if(result.isOk()) { message.attachments[0].safeFile = result.value if((result.value as unknown as string).startsWith('blob:http')) { message.attachments[0].blobURl = true } else { message.attachments[0].blobURl = false } } } this.chatServiceService.sendReadAt({ memberId: SessionStore.user.UserId, messageId: message.id, requestId: '', roomId: this.room.id }) setTimeout(() => { this.scrollToBottomClicked() }, 100) setTimeout(() => { this.removeBold() }, 1000) }); } listenToDeleteMessage() { this.messageDeleteSubject?.unsubscribe(); this.messageDeleteSubject = this.chatServiceService.listenToDeleteMessage(this.room.id).subscribe((deleteMessage) => { console.log('delete class', deleteMessage); const index = this.messages1[this.room.$id].findIndex(e => typeof e?.id == 'string' && e?.id === deleteMessage.id || typeof e?.requestId == 'string' && e?.requestId == deleteMessage.requestId); try { console.log(this.messages1[this.room.$id][index]) this.messages1[this.room.$id][index].delete() } catch (e) { console.log('delete', e) } // if (index !== -1) { // Check if the item was found // console.log('delete ==') // this.messages1[this.room.$id].splice(index, 1); // // console.log('removed index', index); // } else { // // console.log('message not found'); // } }); } listenToUpdateMessage() { this.messageUpdateSubject?.unsubscribe(); // console.log('liste to update') this.messageUpdateSubject = this.chatServiceService.listenToUpdateMessage(this.room.id).subscribe((updateMessage) => { const index = this.messages1[this.room.$id].findIndex(e => e?.id === updateMessage.id); // Use triple equals for comparison if (index !== -1) { // Check if the item was found this.messages1[this.room.$id][index].info = updateMessage.info this.messages1[this.room.$id][index].message = updateMessage.message this.messages1[this.room.$id][index].reactions = updateMessage.reactions } else { // console.log('message not found'); } }); } listenToSendMessage() { this.messageSendSubject?.unsubscribe(); this.messageSendSubject = this.chatServiceService.listenToSendMessage(this.room.id).subscribe((updateMessage) => { const index = this.messages1[this.room.$id].findIndex(e => e?.requestId === updateMessage.requestId); // Use triple equals for comparison if (index !== -1) { // Check if the item was found this.messages1[this.room.$id][index].id = updateMessage.id this.messages1[this.room.$id][index].info = updateMessage.info let attachmentIndex = 0; for(const message of updateMessage.attachments) { this.messages1[this.room.$id][index].attachments[attachmentIndex].id = message.id attachmentIndex++; } } else { console.log('not found message to update ui'); } }); } onDisConnect() {} toggleEmojiPicker(message: MessageViewModal) { 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: MessageViewModal, 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.room.id, reaction: emoji, requestId: '' }) } sendReadAt() { // this.chatServiceService.messageMarkAsRead({roomId: this.room.$id}).then((e) => { // console.log(e) // }) } sendTyping() { if(this.room.local == IDBoolean.false) { this.UserTypingRemoteRepositoryService.sendTyping(this.room.id) } else { console.log('dont send') } } async editMessage(message: MessageViewModal) { const modal = await this.popoverController.create({ component: EditMessagePage, cssClass: 'edit-message', componentProps: { message: message.message, roomId: this.room.$id, } }); modal.present() return modal.onDidDismiss().then((res) => { if(res.data) { this.chatServiceService.updateMessage({ memberId: SessionStore.user.UserId, message: res.data.message, messageId: message.id, requestId: '', roomId: this.room.id }) } }); } async checkAudioPermission() { const permissionStatus = await navigator.permissions.query({ name: 'microphone' } as any) this.audioPermissionStatus = permissionStatus.state permissionStatus.onchange = (data: any) => { // // } } ngOnInit() { this.scrollToBottom(); this.deleteRecording(); } onPressingMessage() { const gesture = this.gestureController.create({ el: this.messageContainer.nativeElement, gestureName: 'long-press', onStart: ev => { this.longPressActive = true; }, onEnd: ev => { this.longPressActive = false; } }); } doRefresh(ev: any) { // ev.target.complete(); } scrollToBottom = () => { try { if (this.scrollingOnce) { this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight; //this.scrollingOnce = false; } } catch (err) { } } scrollToBottomClicked = () => { try { this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight; //this.scrollingOnce = false; } catch (err) { } } ngAfterViewInit() { // this.scrollChangeCallback = () => this.onContentScrolled(event); // window.addEventListener('scroll', this.scrollChangeCallback, true); } ngOnDestroy() { // this.stop() window.removeEventListener('scroll', this.scrollChangeCallback, true); this.handleClickActive = false; // Disable the listener before component destruction } onContentScrolled(e) { this.startPosition = e.srcElement.scrollTop; let scroll = e.srcElement.scrollTop; let windowHeight = e.srcElement.scrollHeight; let containerHeight = windowHeight - e.srcElement.clientHeight; if (scroll > this.currentPosition) { } else { this.scrollingOnce = false; } if ((containerHeight - 100) > scroll) { this.scrollToBottomBtn = true; } else { this.scrollToBottomBtn = false; } this.currentPosition = scroll; } calculateDuration() { if (!this.recording) { this.duration = 0; this.durationDisplay = ''; return; } this.duration += 1; const minutes = Math.floor(this.duration / 60); const seconds = (this.duration % 60).toString().padStart(2, '0'); this.durationDisplay = `${minutes}:${seconds}`; setTimeout(() => { this.calculateDuration(); }, 1000) } async startRecording() { const start = await this.SpeakerService.startRecording() if(start.isOk()) { this.recording = true; this.calculateDuration(); } else if(start.error == StartRecordingResultError.NoSpeaker) { this.toastService._badRequest('Este dispositivo não tem capacidade para gravação de áudio!'); } else if (start.error == StartRecordingResultError.NeedPermission) { this.toastService._badRequest('Para gravar uma mensagem de voz, permita o acesso do Gabinete Digital ao seu microfone.'); } else if(start.error == StartRecordingResultError.alreadyRecording) { } } async stopRecording() { this.deleteRecording(); this.allowTyping = false; const stop = await this.SpeakerService.stopRecording() if(stop.isOk()) { this.lastAudioRecorded = 'audio' this.recording = false; const recordData = stop.value this.recordData = recordData this.audioMimeType = recordData.value.mimeType if (recordData.value.recordDataBase64.includes('data:audio')) { console.log({recordData}) this.audioRecordedDataUrl = recordData.value.recordDataBase64 this.audioRecordedSafe = this.sanitiser.bypassSecurityTrustResourceUrl(recordData.value.recordDataBase64); } else if (recordData.value.mimeType && recordData?.value?.recordDataBase64) { console.log({recordData}) this.audioRecordedDataUrl = `data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}` this.audioRecordedSafe = this.sanitiser.bypassSecurityTrustResourceUrl(this.audioRecordedDataUrl); } } else if (stop.error == StopRecordingResultError.haventStartYet) { return } } createMessage() { const message = new MessageViewModal(); if(this.room.id) { message.roomId = this.room.id } message.sentAt = new Date().toISOString() message.sender = { userPhoto: '', wxeMail: SessionStore.user.Email, wxFullName: SessionStore.user.FullName, wxUserId: SessionStore.user.UserId } message.sentAt = new Date().toISOString() if(this.room.receiverId) { message.receiverId = this.room.receiverId } return message } async messageResult(result: Promise> ) { // let message = await result // if(message.isOk() && this.room.local == IDBoolean.true) { // this.room.local = IDBoolean.false; // console.log('enter') // // await this.chatServiceService.roomSetLocalToFalseById({ // // $roomId: this.room.$id, // // roomId: message.value.roomId // // }) // this.room.id = message.value.roomId // //this.subscribeToChanges() // } } async sendAudio(fileName) { const roomId = this.room.$id //Converting base64 to blob const encodedData = this.audioRecordedDataUrl; const message = this.createMessage(); message.attachments = [{ file: encodedData.split(',')[1], fileName: "audio", source: MessageAttachmentSource.Device, fileType: MessageAttachmentFileType.Audio, mimeType: this.audioMimeType, // 'audio/webm', safeFile: this.sanitiser.bypassSecurityTrustResourceUrl(this.audioRecordedDataUrl) }] const date = whatsappDate(message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(message) this.messages1[this.room.$id].push(Ballon) } this.messages1[this.room.$id].push(message) let sendMessage = this.chatServiceService.sendMessage(message, this.roomType) //this.messageResult(sendMessage) setTimeout(() => { this.scrollToBottomClicked() }, 100) this.deleteRecording(); } async deleteRecording() { this.allowTyping = true; this.lastAudioRecorded = ''; this.audioRecordedSafe = '' this.audioRecordedDataUrl = '' } async sendMessage() { if(this.textField.replace(/\s+/g, "") == "") { return true; } const message = this.createMessage(); message.message = this.textField this.textField = '' const date = whatsappDate(message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(message) this.messages1[this.room.$id].push(Ballon) } this.messages1[this.room.$id].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) let sendMessage = this.chatServiceService.sendMessage(message, this.roomType) //this.messageResult(sendMessage) } async openViewDocumentModal(msg: MessageViewModal) { console.log('passing', msg) let task = { serialNumber: '', taskStartDate: '', isEvent: true, workflowInstanceDataFields: { FolderID: '', Subject: msg.attachments[0].description, SourceSecFsID: msg.attachments[0].applicationId, SourceType: 'DOC', SourceID: msg.attachments[0].docId, DispatchNumber: '' } } let doc = { "Id": "", "ParentId": "", "Source": 1, "ApplicationId": msg.attachments[0].applicationId, "CreateDate": "", "Data": null, "Description": "", "Link": null, "SourceId": msg.attachments[0].docId, "SourceName": msg.attachments[0].description, "Stakeholders": "", } const modal = await this.modalController.create({ component: ViewDocumentPage, componentProps: { trustedUrl: '', file: { title: msg.attachments[0].description, url: '', title_link: '', }, Document: doc, applicationId: msg.attachments[0].applicationId, docId: msg.attachments[0].docId, folderId: '', task: task }, cssClass: 'modal modal-desktop' }); await modal.present(); } async addContacts() { const modal = await this.modalController.create({ component: ContactsPage, componentProps: {}, cssClass: 'contacts', backdropDismiss: false }); modal.onDidDismiss(); await modal.present(); } async _openMessagesOptions() { const enterAnimation = (baseEl: any) => { const backdropAnimation = this.animationController.create() .addElement(baseEl.querySelector('ion-backdrop')!) .fromTo('opacity', '0.01', 'var(--backdrop-opacity)'); const wrapperAnimation = this.animationController.create() .addElement(baseEl.querySelector('.modal-wrapper')!) .keyframes([ { offset: 0, opacity: '1', right: '-100%' }, { offset: 1, opacity: '1', right: '0px' } ]); return this.animationController.create() .addElement(baseEl) .easing('ease-out') .duration(500) .addAnimation([backdropAnimation, wrapperAnimation]); } const leaveAnimation = (baseEl: any) => { return enterAnimation(baseEl).direction('reverse'); } const modal = await this.modalController.create({ enterAnimation, leaveAnimation, component: ChatPopoverPage, cssClass: 'model search-submodal chat-option-aside', componentProps: { roomId: this.room.id, members: [], isAdmin: this.isAdmin, roomType: this.roomType } }); await modal.present(); modal.onDidDismiss().then(res => { if (res.data == 'leave') { // this.getRoomInfo(); this.closeAllDesktopComponents.emit(); this.showEmptyContainer.emit(); // this.ChatSystemService.hidingRoom(this.room.$id).catch((error) => console.error(error)); } else if (res.data == 'delete') { this.closeAllDesktopComponents.emit(); this.showEmptyContainer.emit(); } else if (res.data == 'cancel') { } else if (res.data == 'edit') { //this.closeAllDesktopComponents.emit(); this.openEditGroupPage.emit(this.room.$id); } else if (res.data == 'addUser') { this.openGroupContactsPage(); } else {} }); } openGroupContactsPage() { this.openGroupContacts.emit(this.room.id); } async takePictureMobile() { const oneShot = await this.askIfOneShot() const picture = await this.CameraService.takePicture({ cameraResultType: CameraResultType.DataUrl, quality: 90 }) if(picture.isOk()) { const file = picture.value const compressedImage = await compressImageBase64( file.dataUrl, 800, // maxWidth 800, // maxHeight 0.9 // quality ) if(compressedImage.isOk()) { const message = this.createMessage(); message.oneShot = oneShot message.attachments = [{ file: compressedImage.value.split(',')[1], fileName: "foto", source: MessageAttachmentSource.Device, fileType: MessageAttachmentFileType.Image, mimeType: 'image/'+picture.value.format }] const date = whatsappDate(message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(message) this.messages1[this.room.$id].push(Ballon) } this.messages1[this.room.$id].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) let sendMessage = this.chatServiceService.sendMessage(message, this.roomType) //this.messageResult(sendMessage) } } } async addFile() { this.addFileToChat(['.doc', '.docx', '.pdf'], MessageAttachmentFileType.Doc) } async addFileWebtrix() { const modal = await this.modalController.create({ component: SearchPage, cssClass: 'group-messages modal-desktop search-modal search-modal-to-desktop', componentProps: { type: 'AccoesPresidenciais & ArquivoDespachoElect', select: true, showSearchInput: true, } }); modal.onDidDismiss().then(async res => { const data = res.data; console.log(data) if (data.selected) { // "title": res.data.selected.Assunto, // "description": res.data.selected.DocTypeDesc, const message = this.createMessage(); message.message = this.textField message.attachments = [{ fileName: res.data.selected.Assunto, source: MessageAttachmentSource.Webtrix, fileType: MessageAttachmentFileType.Doc, applicationId: res.data.selected.ApplicationType, docId: parseInt(res.data.selected.Id) , description: res.data.selected.Assunto }] const date = whatsappDate(message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(message) this.messages1[this.room.$id].push(Ballon) } this.messages1[this.room.$id].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) let sendMessage = this.chatServiceService.sendMessage(message, this.roomType) //this.messageResult(sendMessage) this.textField = '' } }); await modal.present(); } askIfOneShot(): Promise { return new Promise(async (resolve, reject) => { const alert = await this.alertController.create({ header: 'Confirmar', message: 'Visualização unica?', buttons: [ { text: 'não', role: 'cancel', handler: () => { resolve(false) console.log('User clicked No'); // Handle No action here } }, { text: 'Sim', handler: () => { console.log('User clicked Yes'); resolve(true) // Handle Yes action here } } ] }); await alert.present(); }) } async pickPicture() { const oneShot = await this.askIfOneShot() const file = await this.FilePickerService.getPicture({ cameraResultType: CameraResultType.Base64 }) if(file.isOk()) { var base64 = 'data:image/jpeg;base64,' + file.value.base64String if (file.value.format == "jpeg" || file.value.format == "png" || file.value.format == "gif") { const compressedImage = await compressImageBase64( base64, 800, // maxWidth 800, // maxHeight 0.9 // quality ) if(compressedImage.isOk()) { const message = this.createMessage(); message.oneShot = oneShot message.attachments = [{ file: file.value.base64String, fileName: "foto", source: MessageAttachmentSource.Device, fileType: MessageAttachmentFileType.Image, mimeType: 'image/'+file.value.format, description: '' }] const date = whatsappDate(message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(message) this.messages1[this.room.$id].push(Ballon) } this.messages1[this.room.$id].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) let sendMessage = this.chatServiceService.sendMessage(message, this.roomType) //this.messageResult(sendMessage) } } } } messageDelete(message: MessageViewModal) { // this.messageRepositoryService.sendMessageDelete() this.chatServiceService.messageDelete({ messageId: message.id, roomId: this.room.id, }) } async addFileToChat(types: typeof FileType[], attachmentFileType:MessageAttachmentFileType) { const file = await this.FilePickerWebService.getFileFromDevice(types); if(file.isOk()) { const fileExtension = await allowedDocExtension(file.value.type) //if(fileExtension.isOk()) { console.log('FILE rigth?', file) let fileBase64 = await JSFileToDataUrl(file.value); if(fileBase64.isOk()) { const message = this.createMessage(); message.attachments = [{ file: fileBase64.value.split(',')[1], fileName: file.value.name, source: MessageAttachmentSource.Device, fileType: MessageAttachmentFileType.Doc, mimeType: file.value.type, description: file.value.name }] const date = whatsappDate(message.sentAt, false) if(!this.date[date]) { this.date[date] = true const Ballon = XBallon(message) this.messages1[this.room.$id].push(Ballon) } this.messages1[this.room.$id].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) let sendMessage = this.chatServiceService.sendMessage(message, this.roomType) //this.messageResult(sendMessage) } //} else { //this.toastService._badRequest("Ficheiro inválido") //} } } async audioPreview(msg) { if (!msg.attachments[0].title_link || msg.attachments[0].title_link === null || msg.attachments[0].title_link === '') { // this.downloadFileMsg(msg) } else { } } b64toBlob(b64Data, contentType) { contentType = contentType || ''; var sliceSize = 512; b64Data = b64Data.replace(/^[^,]+,/, ''); b64Data = b64Data.replace(/\s/g, ''); var byteCharacters = window.atob(b64Data); var byteArrays = []; for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { var slice = byteCharacters.slice(offset, offset + sliceSize); var byteNumbers = new Array(slice.length); for (var i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } var byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } var blob = new Blob(byteArrays, { type: contentType }); return blob; } downloadFileFromBrowser(fileName: string, msg: MessageViewModal): void { if(msg.attachments[0]?.blobURl) { // Create a temporary URL for the Blob const url = msg.attachments[0].safeFile; // Create an element with the download attribute const a = document.createElement('a'); a.href = url; a.download = fileName; // Set the desired file name a.click(); } else { const link = document.createElement("a") link.href = `data:${msg.attachments[0].mimeType}';base64,${msg.attachments[0].safeFile}`; link.download = fileName link.click() link.remove() } } viewDocument(msg: MessageViewModal) { this.openViewDocumentModal(msg); } openFile(blob: Blob, filename, type) { let pathFile = '' const fileName = filename const contentFile = blob if (this.platform.is('ios')) { pathFile = this.file.documentsDirectory } else { pathFile = this.file.externalRootDirectory } this.file .writeFile(pathFile, fileName, contentFile, { replace: true }) .then(success => { this.fileOpener .open(pathFile + fileName, type) .then(() => { }) .catch(e => console.error(e)) }) .catch(e => console.error(e)) } async openPreview(msg: MessageViewModal) { console.log(msg) if (msg.attachments[0].source === MessageAttachmentSource.Webtrix) { this.viewDocument(msg) } else { if (!msg.attachments[0].safeFile || msg.attachments[0].safeFile === null || msg.attachments[0].safeFile === '') { // this.downloadFileMsg(msg) } else { var str = msg.attachments[0].safeFile if (this.platform.is('desktop') || this.platform.is('mobileweb')) { if (msg.attachments[0].mimeType.includes('image')) { const modal = await this.modalController.create({ component: ViewMediaPage, cssClass: 'modal modal-desktop', componentProps: { image: msg.attachments[0].safeFile, type: msg.attachments[0].mimeType, username: msg.attachments[0].description, _updatedAt: msg.sentAt } }); modal.present(); } else { this.downloadFileFromBrowser(msg.attachments[0].description, msg) } } } } } start(track) { if (this.audioPlay) { this.audioPlay.stop(); } this.audioPlay = new Howl({ src: [track.changingThisBreaksApplicationSecurity], onplay: () => { this.isPlaying = true; this.updateProgress() }, onend: () => { this.isPlaying = false; clearTimeout(this.audioTimer) this.audioProgress = 0 }, }) this.audioPlay.play(); } togglePlayer(pause) { this.isPlaying = !pause; if (pause) { this.audioPlay.pause(); } else { this.audioPlay.play(); } } seek() { let newValue = +this.range.value; let duration = this.audioPlay.duration(); this.audioPlay.seek(duration * (newValue / 100)); } updateProgress() { let seek = this.audioPlay.seek(); this.audioProgress = (seek / this.audioPlay.duration()) * 100 || 0; this.audioTimer = setTimeout(() => { this.updateProgress() }, 1000) } closeModal(a: any) { } }