import { AfterViewChecked, AfterViewInit, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router' import { GestureController, Gesture, ModalController, NavParams, PopoverController, IonSlides } from '@ionic/angular'; import { map } from 'rxjs/operators'; import { ViewDocumentPage } from 'src/app/modals/view-document/view-document.page'; import { EventPerson } from 'src/app/models/eventperson.model'; import { ExpedientTaskModalPageNavParamsTask } from 'src/app/models/ExpedientTaskModalPage'; import { SearchDocumentDetails, SearchFolderDetails } from 'src/app/models/search-document'; import { ContactsPage } from 'src/app/pages/chat/messages/contacts/contacts.page'; import { AlertService } from 'src/app/services/alert.service'; import { AuthService } from 'src/app/services/auth.service'; import { ChatService } from 'src/app/services/chat.service'; import { FileService } from 'src/app/services/functions/file.service'; import { ProcessesService } from 'src/app/services/processes.service'; import { ToastService } from 'src/app/services/toast.service'; import { NewEventPage } from 'src/app/shared/agenda/new-event/new-event.page'; import { ChatOptionsPopoverPage } from 'src/app/shared/popover/chat-options-popover/chat-options-popover.page'; import { MessagesOptionsPage } from 'src/app/shared/popover/messages-options/messages-options.page'; import { ChatMessageStore } from 'src/app/store/chat/chat-message.service'; import { ChatUserStorage } from 'src/app/store/chat/chat-user.service'; import { environment } from 'src/environments/environment'; import { ThemeService } from 'src/app/services/theme.service' import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'; import { VoiceRecorder, VoiceRecorderPlugin, RecordingData, GenericResponse, CurrentRecordingStatus } from 'capacitor-voice-recorder'; import { Haptics, ImpactStyle } from '@capacitor/haptics'; import { PreviewCameraPage } from 'src/app/modals/preview-camera/preview-camera.page'; // import { SocialSharing } from '@ionic-native/social-sharing/ngx'; import { Share } from '@capacitor/share'; const IMAGE_DIR = 'stored-images'; @Component({ selector: 'app-messages', templateUrl: './messages.page.html', styleUrls: ['./messages.page.scss'], }) export class MessagesPage implements OnInit, AfterViewInit, OnDestroy { showLoader: boolean; @ViewChild('scrollMe') private myScrollContainer: ElementRef; /* @ViewChild('messageContainer') messageContainer: ElementRef; */ @ViewChild('rectangle') private rectangle: ElementRef; canvas: any ctx: any loggedUser: any; message = ''; messages:any; userPresence=''; dmUsers:any; roomId:string; el:any; members:any; scrollingOnce:boolean = true; chatMessageStore = ChatMessageStore; chatUserStorage = ChatUserStorage; private scrollChangeCallback: () => void; currentPosition: any; startPosition: number; scrollToBottomBtn = false; attendees: EventPerson[] = []; longPressActive = false; showMessageOptions = false; selectedMsgId:string; dicIndex = 0; task: ExpedientTaskModalPageNavParamsTask; LoadedDocument:any = null; recording = false; storedFileNames = []; durationDisplay = ''; duration = 0; @ViewChild('recordbtn', { read: ElementRef }) recordBtn: ElementRef; myAudio: any; constructor( public popoverController: PopoverController, private modalController: ModalController, private navParams: NavParams, private chatService: ChatService, private authService: AuthService, private alertService: AlertService, private toastService: ToastService, private route: Router, private activatedRoute: ActivatedRoute, private fileService: FileService, private gestureController: GestureController, private processes: ProcessesService, public ThemeService: ThemeService, private changeDetectorRef: ChangeDetectorRef, // private socialSharing: SocialSharing ) { this.loggedUser = authService.ValidatedUserChat['data']; this.roomId = this.navParams.get('roomId'); window.onresize = (event) => { if( window.innerWidth > 701){ this.modalController.dismiss(); } }; } ngOnInit() { this.load(); this.setStatus('online'); this.loadFiles(); VoiceRecorder.requestAudioRecordingPermission(); Filesystem.mkdir({ path: IMAGE_DIR, directory: Directory.Data, recursive: true }); } ngAfterViewInit() { this.scrollChangeCallback = () => this.onContentScrolled(event); window.addEventListener('scroll', this.scrollChangeCallback, true); const longpress = this.gestureController.create({ el: this.recordBtn.nativeElement, threshold: 0, gestureName: 'long-press', onStart: ev => { Haptics.impact({ style: ImpactStyle.Light }) this.startRecording(); this.calculateDuration(); }, onEnd: ev =>{ Haptics.impact({ style: ImpactStyle.Light }) this.stopRecording(); } }, true); longpress.enable(); } 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 loadFiles(){ Filesystem.readdir({ path: '', directory: Directory.Data }).then(result =>{ console.log(result); const temp:any[] = result.files.reverse(); this.storedFileNames = temp[0]; console.log(this.storedFileNames); }) } startRecording(){ if(this.recording){ return; } this.recording = true; VoiceRecorder.startRecording(); } stopRecording(){ if(!this.recording){ return; } this.recording = false; VoiceRecorder.stopRecording().then(async (result: RecordingData) =>{ this.recording = false; if(result.value && result.value.recordDataBase64){ const recordData = result.value.recordDataBase64; console.log(recordData); this.myAudio = recordData; const fileName = new Date().getTime() + ".wav"; await Filesystem.writeFile({ path: fileName, directory: Directory.Data, data: recordData, }) } }) } async playFile(fileName?:any){ const audioFile = await Filesystem.readFile({ path: fileName, directory: Directory.Data }) console.log(audioFile); const base64sound = audioFile.data; const audioRef = new Audio(`data:audio/aac;base64,${base64sound}`) audioRef.oncanplaythrough = () => audioRef.play(); audioRef.load(); } handlePress(id?:string){ this.selectedMsgId = id; this.showMessageOptions = true; } handleClick(){ this.showMessageOptions = false; this.selectedMsgId = ""; } deleteMessage(msgId:string){ let body = { "roomId": this.roomId, "msgId": msgId, "asUser": false, } if(msgId){ this.alertService.confirmDeleteMessage(body); } else{ this.toastService.badRequest('Não foi possível apagar'); } this.showMessageOptions = false; this.selectedMsgId = ""; } setStatus(status:string){ let body = { message: '', status: status, } this.chatService.setUserStatus(body).subscribe(res => { console.log(res); }) } notImplemented(){ this.alertService.presentAlert('Funcionalidade em desenvolvimento'); } close(){ this.modalController.dismiss(); } load(){ this.serverLongPull(); this.getChatMembers(); } doRefresh(ev:any){ this.load(); ev.target.complete(); } scrollToBottom(): void { try { if(this.scrollingOnce){ this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight; //this.scrollingOnce = false; } } catch(err) { } } scrollToBottomClicked(): void { try { this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight; } catch(err) { } } 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) { //alert('BOTTOM'); } else { //alert('UP'); this.scrollingOnce = false; } if((containerHeight - 100) > scroll){ this.scrollToBottomBtn = true; } else{ this.scrollToBottomBtn = false; } this.currentPosition = scroll; } ngOnDestroy() { window.removeEventListener('scroll', this.scrollChangeCallback, true); } sendMessage() { let body = { "message": { "rid": this.roomId, "msg": this.message } } this.chatService.sendMessage(body).subscribe(res=> { //this.loadMessages(); this.scrollingOnce = true; }); this.message = ""; } loadMessages() { this.showLoader = true; const roomId = this.roomId; this.chatService.getRoomMessages(this.roomId).subscribe(res => { console.log(res); this.messages = res['messages'].reverse(); this.chatMessageStore.add(roomId, this.messages) console.log(this.messages); this.showLoader = false; }) } viewDocument(file:any, url?:string){ console.log(file); if(file.type == "application/webtrix") { this.openViewDocumentModal(file); } else{ let fullUrl = "https://www.tabularium.pt" + url; this.fileService.viewDocumentByUrl(fullUrl); } } docIndex(index: number){ this.dicIndex = index } 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() { this.showLoader = true; this.chatService.getMembers(this.roomId).subscribe(res=> { this.members = res['members']; this.dmUsers = res['members'].filter(data => data.username != this.loggedUser.me.username) console.log(res); console.log(this.dmUsers); this.showLoader = false; }); } showDateDuration(start:any){ let end; end = new Date(); start = new Date(start); let customizedDate; const totalSeconds = Math.floor((end - (start))/1000);; const totalMinutes = Math.floor(totalSeconds/60); const totalHours = Math.floor(totalMinutes/60); const totalDays = Math.floor(totalHours/24); const hours = totalHours - ( totalDays * 24 ); const minutes = totalMinutes - ( totalDays * 24 * 60 ) - ( hours * 60 ); const seconds = totalSeconds - ( totalDays * 24 * 60 * 60 ) - ( hours * 60 * 60 ) - ( minutes * 60 ); if(totalDays == 0){ if(start.getDate() == new Date().getDate()){ let time = start.getHours() + ":" + this.addZero(start.getUTCMinutes()); return time; } else{ return 'Ontem'; } } else{ let date = start.getDate() + "/" + (start.getMonth()+1) + "/" + start.getFullYear(); return date; } } addZero(i) { if (i < 10) { i = "0" + i; } return i; } async openMessagesOptions(ev?: any) { const popover = await this.popoverController.create({ component: MessagesOptionsPage, componentProps: { roomId: this.roomId, }, cssClass: 'messages-options', event: ev, translucent: true, }); return await popover.present(); } async addContacts(){ const modal = await this.modalController.create({ component: ContactsPage, componentProps: {}, cssClass: 'contacts', backdropDismiss: false }); await modal.present(); modal.onDidDismiss(); } async bookMeeting() { this.attendees = this.members.map((val)=>{ return { Name: val.name, EmailAddress: val.username+"@"+environment.domain, IsRequired: "true", } }); console.log(this.attendees); this.popoverController.dismiss(); if( window.innerWidth <= 1024){ const modal = await this.modalController.create({ component: NewEventPage, componentProps:{ attendees: this.attendees, }, cssClass: 'modal modal-desktop', backdropDismiss: false }); await modal.present(); modal.onDidDismiss().then((data) => { if(data){ } }); } } async openChatOptions(ev?: any) { const roomId = this.roomId console.log(this.members); const popover = await this.popoverController.create({ component: ChatOptionsPopoverPage, cssClass: 'chat-options-popover', event: ev, componentProps: { room: this.roomId, members: this.members, eventSelectedDate: new Date(), }, translucent: true }); await popover.present(); popover.onDidDismiss().then((res)=>{ console.log(res['data']); if(res['data'] == 'meeting'){ this.bookMeeting(); } else if(res['data'] == 'take-picture'){ this.fileService.addCameraPictureToChat(roomId); //this.loadPicture(); } else if(res['data'] == 'add-picture'){ this.fileService.addPictureToChatMobile(roomId); //this.loadPicture(); } else if(res['data'] == 'add-document'){ this.fileService.addDocumentToChat(this.roomId); //this.loadDocument(); } else if(res['data'] == 'documentoGestaoDocumental'){ this.fileService.addDocGestaoDocumentalToChat(this.roomId); //this.addDocGestaoDocumental(); } this.loadMessages(); }); } async serverLongPull() { const roomId = this.roomId /* this.chatService.getRoomMessages(roomId).subscribe(res=>{ console.log(res); }) */ this.chatService.getRoomMessages(roomId).subscribe(async res => { console.log("Chat message",res) if (res == 502) { // Connection timeout // happens when the synchro was pending for too long // let's reconnect await this.serverLongPull(); } else if (res != 200) { // Show Error //showMessage(response.statusText); //this.loadMessages() this.messages = res['messages'].reverse(); this.chatMessageStore.add(roomId, this.messages) //console.log(this.messages); // Reconnect in one second if(this.route.url != "/home/chat"){ console.log("Timer message stop") } else { //Check if modal is opened if(document.querySelector('.isMessagesChatOpened')){ await new Promise(resolve => setTimeout(resolve, 5000)); await this.serverLongPull(); //console.log('Timer message running') } } } else { // Got message //let message = await response.text(); //this.loadMessages() await this.serverLongPull(); } }); } sliderOpts = { zoom: false, slidesPerView: 1.5, spaceBetween: 20, centeredSlides: true }; zoomActive = false; zoomScale = 1; sliderZoomOpts = { allowSlidePrev: false, allowSlideNext: false, zoom: { maxRatio: 5 }, on: { zoomChange: (scale, imageEl, slideEl) => { this.zoomActive = true; this.zoomScale = scale/5; this.changeDetectorRef.detectChanges(); } } } async touchEnd(zoomslides: IonSlides, card) { // Zoom back to normal const slider = await zoomslides.getSwiper(); const zoom = slider.zoom; zoom.out(); // Card back to normal card.el.style['z-index'] = 9; this.zoomActive = false; this.changeDetectorRef.detectChanges(); } touchStart(card) { // Make card appear above backdrop card.el.style['z-index'] = 11; } async openPreview(msg) { const modal = await this.modalController.create({ component: PreviewCameraPage, cssClass: 'modal modal-desktop', componentProps: { image: msg.attachments[0].image_url, username: msg.u.name, _updatedAt: msg._updatedAt, } }); modal.present(); } imageSize(img){ var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); canvas.width=300 canvas.height=234 ctx.drawImage(img.attachments[0].image_url, 0, 0, 300, 234); document.body.appendChild(canvas); } getPicture(img){ var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); canvas.width=300 canvas.height=234 ctx.drawImage(img.attachments[0].image_url, 0, 0, 300, 234); document.body.appendChild(canvas); } // async ShareEmail(msg){ // // Check if sharing via email is supported // await Share.share({ // title: msg.u.username, // text: msg._updatedAt, // url: msg.attachments[0].image_url, // dialogTitle: 'Share with buddies', // }); // } }