import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; import { AnimationController, GestureController, IonRange, ModalController, PopoverController } from '@ionic/angular'; import { ToastService } from 'src/app/services/toast.service'; import { ContactsPage } from '../new-group/contacts/contacts.page'; import { ChatOptionsFeaturesPage } from 'src/app/modals/chat-options-features/chat-options-features.page'; import { TimeService } from 'src/app/services/functions/time.service'; import { FileService } from 'src/app/services/functions/file.service'; import { ViewDocumentPage } from 'src/app/modals/view-document/view-document.page'; import { ThemeService } from 'src/app/services/theme.service'; import { ViewEventPage } from 'src/app/modals/view-event/view-event.page'; 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 { Subscription } from 'rxjs'; import { MessageRepositoryService } from 'src/app/module/chat/data/repository/message-respository.service' 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 { UserTypingServiceRepository } from 'src/app/module/chat/data/repository/user-typing-repository.service'; import { ChatServiceService } from 'src/app/module/chat/domain/chat-service.service'; import { EditMessagePage } from 'src/app/modals/edit-message/edit-message.page'; import { MessageEntity } from 'src/app/module/chat/domain/entity/message'; import { MemberTable } from 'src/app/module/chat/infra/database/dexie/schema/members'; import { TypingTable } from 'src/app/module/chat/infra/database/dexie/schema/typing'; import { MessageAttachmentFileType, MessageAttachmentSource } from 'src/app/module/chat/data/dto/message/messageOutputDTO'; 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'; @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; @Input() roomId: string; @Input() showMessages: string; @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; userName = ""; room: any = new Array(); roomName: any; isAdmin = true; roomCountDownDate: string; audioMimeType = '' textField = '' roomData$: DexieObservable roomStatus$: DexieObservable roomMessage$: DexieObservable roomMembers$: DexieObservable //userTyping$: DexieObservable userTyping$: TypingTable[] | undefined newMessagesStream!: Subscription selectedMessage: any = null; emojis: string[] = ['😊', '😂', '❤️', '👍', '😢']; // Add more emojis as needed totalMessage = 0 recordData:RecordingData messages: MessageEntity[] = [] messageReceiveSubject: Subscription messageDeleteSubject: Subscription messageUpdateSubject: Subscription messageSendSubject: Subscription messages1: {[key: string]: MessageEntity[]} = {} MessageAttachmentFileType = MessageAttachmentFileType MessageAttachmentFileSource = MessageAttachmentSource constructor( public popoverController: PopoverController, private modalController: ModalController, private animationController: AnimationController, private toastService: ToastService, private timeService: TimeService, private fileService: FileService, 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 messageRepositoryService: MessageRepositoryService, private userTypingServiceRepository: UserTypingServiceRepository, private chatServiceService: ChatServiceService, private CameraService: CameraService, private FilePickerWebService: FilePickerWebService, private FilePickerService: FilePickerService, private SpeakerService: SpeakerService, private RoomLocalRepository: RoomLocalRepository ) { // update this.checkAudioPermission() } ngOnChanges(changes: SimpleChanges): void { this.roomData$ = this.RoomLocalRepository.getRoomByIdLive(this.roomId) this.getMessages(); this.listenToIncomingMessage(); this.listenToDeleteMessage(); this.listenToUpdateMessage(); this.listenToSendMessage() // this.roomMessage$ = this.messageRepositoryService.getItemsLive(this.roomId) this.roomMembers$ = this.MemberListLocalRepository.getRoomMemberByIdLive(this.roomId) as any this.roomStatus$ = this.MemberListLocalRepository.allMemberOnline(this.roomId) this.chatServiceService.getRoomById(this.roomId) this.userTypingServiceRepository.getUserTypingLive().subscribe((e) => { const arrayNames = e.map(e => e.userName) this.userTyping$ = e as any const uniqueArray = [...new Set(arrayNames)]; (this.myInputRef.nativeElement as HTMLDivElement).innerHTML = '::'+ uniqueArray }) } async getMessages() { // dont remove this line this.messages1[this.roomId] = [] let messages = await this.messageRepositoryService.getItems(this.roomId) this.messages1[this.roomId] = [] this.messages1[this.roomId] = messages this.messages1[this.roomId].push(LastMessage) this.loadAttachment() } async loadAttachment() { for(const message of this.messages1[this.roomId]) { 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 } }) } } } } async onImageLoad(message: MessageEntity, index:number) { if(message.attachments[0].fileName == LastMessage.attachments[0].fileName) { this.scrollToBottom() setTimeout(() => { this.scrollToBottom(); }, 100) this.messages1[this.roomId].splice(index, 1); } } async onImageError() {} listenToIncomingMessage() { this.messageReceiveSubject?.unsubscribe(); this.messageReceiveSubject = this.chatServiceService.listenToIncomingMessage(this.roomId).subscribe(async (message) => { this.messages1[this.roomId].push(message as MessageEntity) 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 } } setTimeout(() => { this.scrollToBottomClicked() }, 100) }); } listenToDeleteMessage() { this.messageDeleteSubject?.unsubscribe(); this.messageDeleteSubject = this.chatServiceService.listenToDeleteMessage(this.roomId).subscribe((deleteMessage) => { console.log('delete class', deleteMessage); const index = this.messages1[this.roomId].findIndex(e => e?.id === deleteMessage.id); // Use triple equals for comparison if (index !== -1) { // Check if the item was found console.log('delete ==') this.messages1[this.roomId].splice(index, 1); // console.log('removed index', index); } else { // console.log('message not found'); } }); } listenToUpdateMessage() { this.messageUpdateSubject?.unsubscribe(); this.messageUpdateSubject = this.chatServiceService.listenToUpdateMessage(this.roomId).subscribe((updateMessage) => { 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].message = updateMessage.message this.messages1[this.roomId][index].reactions = updateMessage.reactions } else { // console.log('message not found'); } }); } listenToSendMessage() { 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 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++; } } else { // console.log('message not found'); } }); } toggleEmojiPicker(message: MessageEntity) { 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: MessageEntity, emoji: string) { // Logic to add reaction to the message console.log(`Reacting to message ${message.id} with emoji ${emoji.codePointAt(0).toString(16)}`); this.selectedMessage = null; // Close the picker after adding reaction this.chatServiceService.reactToMessage({ memberId: SessionStore.user.UserId, messageId: message.id, roomId: this.roomId, reaction: emoji, requestId: '' }) } sendReadAt() { this.messageRepositoryService.sendReadAt({roomId: this.roomId}).then((e) => { console.log(e) }) } sendTyping() { this.userTypingServiceRepository.addUserTyping(this.roomId) } async editMessage(message: MessageEntity) { const modal = await this.modalController.create({ component: EditMessagePage, cssClass: '', componentProps: { message: message.message, roomId: this.roomId, } }); modal.present() return modal.onDidDismiss().then((res) => { this.chatServiceService.updateMessage({ memberId: SessionStore.user.UserId, message: res.data.message, messageId: message.id, requestId: '', roomId: this.roomId }) }); } async checkAudioPermission() { const permissionStatus = await navigator.permissions.query({ name: 'microphone' } as any) this.audioPermissionStatus = permissionStatus.state permissionStatus.onchange = (data: any) => { // // } } ngOnInit() { this.scrollToBottom(); this.getChatMembers(); 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; } }); } load = () => { this.getChatMembers(); } doRefresh(ev: any) { this.load(); 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() { window.removeEventListener('scroll', this.scrollChangeCallback, true); } 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 } } async sendAudio(fileName) { const roomId = this.roomId //Converting base64 to blob const encodedData = this.audioRecordedDataUrl; const message = new MessageEntity(); message.roomId = this.roomId message.sentAt = new Date().toISOString() message.sender = { userPhoto: '', wxeMail: SessionStore.user.Email, wxFullName: SessionStore.user.FullName, wxUserId: SessionStore.user.UserId } 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) }] this.chatServiceService.sendMessage(message) this.messages1[this.roomId].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) this.deleteRecording(); } async deleteRecording() { this.allowTyping = true; this.lastAudioRecorded = ''; this.audioRecordedSafe = '' this.audioRecordedDataUrl = '' } showDateDuration(start: any) { return this.timeService.showDateDuration(start); } async goToEvent(event: any) { let classs; if (window.innerWidth < 701) { classs = 'modal modal-desktop' } else { classs = 'modal modal-desktop showAsideOptions' } const modal = await this.modalController.create({ component: ViewEventPage, componentProps: { eventId: event.id, CalendarId: event.calendarId }, cssClass: classs, }); modal.onDidDismiss().then((res) => { }); await modal.present(); } async sendMessage() { const message = new MessageEntity(); message.message = this.textField message.roomId = this.roomId message.sender = { userPhoto: '', wxeMail: SessionStore.user.Email, wxFullName: SessionStore.user.FullName, wxUserId: SessionStore.user.UserId } message.sentAt = new Date().toISOString() this.textField = '' this.messages1[this.roomId].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) const data = await this.chatServiceService.sendMessage(message) } async openViewDocumentModal(file: any) { let task = { serialNumber: '', taskStartDate: '', isEvent: true, workflowInstanceDataFields: { FolderID: '', Subject: file.Assunto, SourceSecFsID: file.ApplicationId, SourceType: 'DOC', SourceID: file.DocId, DispatchNumber: '' } } let doc = { "Id": "", "ParentId": "", "Source": 1, "ApplicationId": file.ApplicationId, "CreateDate": "", "Data": null, "Description": "", "Link": null, "SourceId": file.DocId, "SourceName": file.Assunto, "Stakeholders": "", } const modal = await this.modalController.create({ component: ViewDocumentPage, componentProps: { trustedUrl: '', file: { title: file.Assunto, url: '', title_link: '', }, Document: doc, applicationId: file.ApplicationId, docId: file.DocId, folderId: '', task: task }, cssClass: 'modal modal-desktop' }); await modal.present(); } getChatMembers() {} 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.roomId, members: [], isAdmin: this.isAdmin } }); await modal.present(); modal.onDidDismiss().then(res => { if (res.data == 'leave') { // this.getRoomInfo(); this.closeAllDesktopComponents.emit(); this.showEmptyContainer.emit(); // this.ChatSystemService.hidingRoom(this.roomId).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.roomId); } else if (res.data == 'addUser') { this.openGroupContactsPage(); } else {} }); } openGroupContactsPage() { this.openGroupContacts.emit(this.roomId); } async takePictureMobile() { 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 = new MessageEntity(); message.roomId = this.roomId message.sender = { userPhoto: '', wxeMail: SessionStore.user.Email, wxFullName: SessionStore.user.FullName, wxUserId: SessionStore.user.UserId } message.sentAt = new Date().toISOString() message.attachments = [{ file: compressedImage.value.split(',')[1], fileName: "foto", source: MessageAttachmentSource.Device, fileType: MessageAttachmentFileType.Image, mimeType: 'image/'+picture.value.format }] this.messages1[this.roomId].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) this.chatServiceService.sendMessage(message) } } } 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; if (data.selected) { // "title": res.data.selected.Assunto, // "description": res.data.selected.DocTypeDesc, const message = new MessageEntity(); message.message = this.textField message.roomId = this.roomId message.sentAt = new Date().toISOString() message.sender = { userPhoto: '', wxeMail: SessionStore.user.Email, wxFullName: SessionStore.user.FullName, wxUserId: SessionStore.user.UserId } message.attachments = [{ fileName: res.data.selected.Assunto, source: MessageAttachmentSource.Webtrix, fileType: MessageAttachmentFileType.Doc, applicationId: res.data.selected.ApplicationType, docId: res.data.selected.Id, }] this.messages1[this.roomId].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) this.chatServiceService.sendMessage(message) this.textField = '' } }); await modal.present(); } async pickPicture() { 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 = new MessageEntity(); message.roomId = this.roomId message.sentAt = new Date().toISOString() message.sender = { userPhoto: '', wxeMail: SessionStore.user.Email, wxFullName: SessionStore.user.FullName, wxUserId: SessionStore.user.UserId } message.attachments = [{ file: file.value.base64String, fileName: "foto", source: MessageAttachmentSource.Device, fileType: MessageAttachmentFileType.Image, mimeType: 'image/'+file.value.format, description: '' }] this.messages1[this.roomId].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) this.chatServiceService.sendMessage(message) } } } } messageDelete(message: MessageEntity) { // this.messageRepositoryService.sendMessageDelete() this.chatServiceService.messageDelete({ messageId: message.id, roomId: this.roomId, }) } 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 = new MessageEntity(); message.roomId = this.roomId message.sentAt = new Date().toISOString() message.sender = { userPhoto: '', wxeMail: SessionStore.user.Email, wxFullName: SessionStore.user.FullName, wxUserId: SessionStore.user.UserId } message.attachments = [{ file: fileBase64.value.split(',')[1], fileName: file.value.name, source: MessageAttachmentSource.Device, fileType: MessageAttachmentFileType.Doc, mimeType: file.value.type }] this.messages1[this.roomId].push(message) setTimeout(() => { this.scrollToBottomClicked() }, 100) this.chatServiceService.sendMessage(message) } } else { this.toastService._badRequest("Ficheiro inválido") } } } async _openChatOptions() { const roomId = this.roomId; 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: ChatOptionsFeaturesPage, cssClass: 'model profile-modal search-submodal', componentProps: { roomId: this.roomId, members: [], } }); modal.onDidDismiss().then(async (res) => { if (res['data'] == 'meeting') { //this.closeAllDesktopComponents.emit(); let data = { roomId: this.roomId, members: [] } this.openNewEventPage.emit(data); } else if (res['data'] == 'take-picture') { this.takePictureMobile() } else if (res['data'] == 'add-picture') { this.pickPicture() } else if (res['data'] == 'add-document') { this.addFile() } else if (res['data'] == 'documentoGestaoDocumental') { this.addFileWebtrix() this.showLoader = false; } }); await modal.present(); } 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, data: any): void { const link = document.createElement("a") link.href = `data:${data.type}';base64,${data.image_url}`; link.download = fileName link.click() link.remove() } viewDocument(file: any, url?: string) { if (file.type == "application/webtrix") { this.openViewDocumentModal(file); } else { let fullUrl = "https://www.tabularium.pt" + url; this.fileService.viewDocumentByUrl(fullUrl); } } openFile(pdfString, filename, type) { const blob = this.b64toBlob(pdfString, 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) { if (msg.file.type === "application/webtrix") { this.viewDocument(msg.file, msg.attachments.image_url) } else { if (!msg.attachments[0].image_url || msg.attachments[0].image_url === null || msg.attachments[0].image_url === '') { // this.downloadFileMsg(msg) } else { var str = msg.attachments[0].image_url str = str.substring(1, ((str.length) - 1)); if (this.platform.is('desktop') || this.platform.is('mobileweb')) { if (msg.file.type == "application/img") { const modal = await this.modalController.create({ component: ViewMediaPage, cssClass: 'modal modal-desktop', componentProps: { image: msg.attachments[0].image_url, type: msg.file.type, username: msg.u.name, _updatedAt: msg._updatedAt } }); modal.present(); } else { this.downloadFileFromBrowser(msg.attachments[0].title, msg.attachments[0],) } } else { this.openFile(msg.attachments[0].image_url, msg.attachments[0].title, msg.file.type); // this.downloadFileFromBrowser("file", str) } } } } 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) } }