This commit is contained in:
Eudes Inácio
2022-03-21 10:45:03 +01:00
50 changed files with 1153 additions and 402 deletions
+4 -12
View File
@@ -12,18 +12,15 @@ import { HttpClientModule } from '@angular/common/http';
import { InAppBrowser } from '@ionic-native/in-app-browser/ngx';
//import { File } from '@ionic-native/File/ngx';
import { WebView } from '@ionic-native/ionic-webview/ngx';
import { FilePath } from '@ionic-native/file-path/ngx';
import { Camera } from '@ionic-native/camera/ngx';
import { IonicStorageModule } from '@ionic/storage';
//
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CalendarModule, DateAdapter } from 'angular-calendar';
import { adapterFactory } from 'angular-calendar/date-adapters/date-fns';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@@ -35,10 +32,6 @@ import {MatDatepickerModule} from '@angular/material/datepicker';
import {MAT_DATE_FORMATS, MAT_DATE_LOCALE} from '@angular/material/core';
import { NgxMatDateFormats, NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker';
import { Network } from '@ionic-native/network/ngx';
import { File } from '@ionic-native/file/ngx';
import {
NgxMatDatetimePickerModule,
NgxMatNativeDateModule,
@@ -56,13 +49,14 @@ import { far } from '@fortawesome/free-regular-svg-icons'
import { fab } from '@fortawesome/free-brands-svg-icons'
import { ScreenOrientation } from '@ionic-native/screen-orientation/ngx';
import { PdfViewerModule } from 'ng2-pdf-viewer';
import { SQLite } from '@ionic-native/sqlite/ngx';
import { CookieService } from 'ngx-cookie-service';
import { ImagePicker } from '@ionic-native/image-picker/ngx';
import { MediaCapture } from '@ionic-native/media-capture/ngx';
import { Media } from '@ionic-native/media/ngx';
import { File } from '@ionic-native/file/ngx';
import { StreamingMedia } from '@ionic-native/streaming-media/ngx';
import { PhotoViewer } from '@ionic-native/photo-viewer/ngx';
import {NgxImageCompressService} from 'ngx-image-compress';
@@ -111,10 +105,8 @@ import { FirebaseX } from '@ionic-native/firebase-x/ngx'; */
MatSelectModule,
MatDialogModule,
//
PdfViewerModule,
HammerModule,
CustomImageCachePageRoutingModule
CustomImageCachePageRoutingModule,
],
providers: [
@@ -126,7 +118,7 @@ import { FirebaseX } from '@ionic-native/firebase-x/ngx'; */
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
InAppBrowser,
Camera,
//File,
File,
WebView,
FilePath,
/* FCM,
@@ -7,7 +7,6 @@ import { IonicModule } from '@ionic/angular';
import { DocumentViewerPageRoutingModule } from './document-viewer-routing.module';
import { DocumentViewerPage } from './document-viewer.page';
import { PdfViewerModule } from 'ng2-pdf-viewer';
@NgModule({
@@ -17,7 +16,6 @@ import { PdfViewerModule } from 'ng2-pdf-viewer';
IonicModule,
DocumentViewerPageRoutingModule,
//
PdfViewerModule
],
declarations: [DocumentViewerPage]
})
@@ -1,9 +1,25 @@
<ion-content class=" bg-blue">
<div class="main-content width-100 overflow-y-auto height-100">
<div class="profile-header width-100">
<div class="div-logo d-md-none">
<div class="logo height-fit-content">
<img class="img" src='assets/images/logo-no-bg.png' alt='logo'>
<div class="div-logo d-md-none width-40">
<div class="logo-icon">
<img *ngIf="ThemeService.currentTheme == 'default' " src='assets/images/logo-no-bg.png' alt='logo'>
<img *ngIf="ThemeService.currentTheme == 'gov' " src='assets/images/theme/gov/governoangola_A.png' alt='logo'>
<img *ngIf="ThemeService.currentTheme == 'tribunal' " src='assets/images/theme/tribunal/tribunal-constitucional.png' alt='logo'>
</div>
<div *ngIf="ThemeService.currentTheme == 'gov'" class="logo-description d-flex align-center justify-content-center">
<div class="logo-description-content">
<p class="logo-description-text">Presidente da República</p>
<div class="add-line"></div>
<p class="logo-description-text tp-5">GABINETE DIGITAL</p>
</div>
</div>
<div *ngIf="ThemeService.currentTheme == 'default'" class="logo-description d-flex align-center justify-content-center">
<div class="logo-description-content">
<p class="logo-description-text color-white">Presidente da República</p>
<div class="add-line-white"></div>
<p class="logo-description-text tp-5 color-white">GABINETE DIGITAL</p>
</div>
</div>
</div>
<div class="btn-close d-flex cursor-pointer" (click)="close()">
@@ -10,18 +10,68 @@
.div-logo{
background: transparent;
width: calc(100% - 40px) !important;
width: em(140px);
justify-content: center;
display: flex;
color: black;
overflow: auto;
float: left;
.logo{
width: 140px;
.img{
.logo-icon{
width: 25.33%;
overflow: auto;
img{
width: 100%;
margin: 0px auto;
}
}
.logo-description{
width: 74.67%;
margin: 0 auto;
overflow: auto;
font-size: 8.5px;
font-family: Bahnschrift;
.logo-description-content{
width: 100%;
.logo-description-text{
font-weight: 700;
text-align: center;
width: 100%;
margin: 0 !important;
padding: 0 !important;
}
.add-line{
width: 100%;
border-bottom: 1px solid #000;
margin-bottom: 2.5px !important;
padding: 0 !important;
}
.add-line-white{
width: 100%;
border-bottom: 1px solid #fff;
margin-bottom: 2.5px !important;
padding: 0 !important;
}
}
.color-white{
color: #fff !important;
}
.add-botton-border{
border-bottom: 1px solid #000;
}
.add-botton-border-white{
border-bottom: 1px solid #fff;
}
}
}
}
.btn-close{
+23 -7
View File
@@ -1,8 +1,24 @@
<ion-header class=" bg-blue ion-no-border">
<div class="profile-header width-100">
<div class="div-logo d-md-none">
<div class="logo height-fit-content">
<img class="img" src='assets/images/logo-no-bg.png' alt='logo'>
<div class="div-logo d-md-none width-40">
<div class="logo-icon">
<img *ngIf="ThemeService.currentTheme == 'default' " src='assets/images/logo-no-bg.png' alt='logo'>
<img *ngIf="ThemeService.currentTheme == 'gov' " src='assets/images/theme/gov/governoangola_A.png' alt='logo'>
<img *ngIf="ThemeService.currentTheme == 'tribunal' " src='assets/images/theme/tribunal/tribunal-constitucional.png' alt='logo'>
</div>
<div *ngIf="ThemeService.currentTheme == 'gov'" class="logo-description d-flex align-center justify-content-center">
<div class="logo-description-content">
<p class="logo-description-text">Presidente da República</p>
<div class="add-line"></div>
<p class="logo-description-text tp-5">GABINETE DIGITAL</p>
</div>
</div>
<div *ngIf="ThemeService.currentTheme == 'default'" class="logo-description d-flex align-center justify-content-center">
<div class="logo-description-content">
<p class="logo-description-text color-white">Presidente da República</p>
<div class="add-line-white"></div>
<p class="logo-description-text tp-5 color-white">GABINETE DIGITAL</p>
</div>
</div>
</div>
<div class="btn-close d-flex cursor-pointer" (click)="close()">
@@ -13,7 +29,7 @@
</ion-header>
<ion-header class=" bg-blue ion-no-border">
<div class="profile-content">
<div class="profile-title d-flex justify-content-center width-100">
<ion-label >{{loggeduser.RoleDescription}}</ion-label>
</div>
@@ -37,7 +53,7 @@
<div class="d-flex flex-column height-100 overflow-y-auto">
<div class="notifications-content height-100">
<ion-list>
<div class="item cursor-pointer ion-no-padding ion-no-margin" lines="none"
*ngFor = "let item of notificationdata; let i = index"
@@ -66,9 +82,9 @@
</div>
</div>
</ion-list>
</div>
</div>
</ion-content>
+55 -5
View File
@@ -4,20 +4,70 @@
padding: 20px 20px;
border: 0 !important;
.div-logo {
.div-logo{
background: transparent;
width: calc(100% - 40px) !important;
width: em(140px);
justify-content: center;
display: flex;
color: black;
overflow: auto;
float: left;
.logo {
width: 140px;
.img {
.logo-icon{
width: 25.33%;
overflow: auto;
img{
width: 100%;
margin: 0px auto;
}
}
.logo-description{
width: 74.67%;
margin: 0 auto;
overflow: auto;
font-size: 8.5px;
font-family: Bahnschrift;
.logo-description-content{
width: 100%;
.logo-description-text{
font-weight: 700;
text-align: center;
width: 100%;
margin: 0 !important;
padding: 0 !important;
}
.add-line{
width: 100%;
border-bottom: 1px solid #000;
margin-bottom: 2.5px !important;
padding: 0 !important;
}
.add-line-white{
width: 100%;
border-bottom: 1px solid #fff;
margin-bottom: 2.5px !important;
padding: 0 !important;
}
}
.color-white{
color: #fff !important;
}
.add-botton-border{
border-bottom: 1px solid #000;
}
.add-botton-border-white{
border-bottom: 1px solid #fff;
}
}
}
}
.btn-close {
@@ -8,7 +8,6 @@ import { ViewMediaPageRoutingModule } from './view-media-routing.module';
import { ViewMediaPage } from './view-media.page';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { PdfViewerModule } from 'ng2-pdf-viewer';
@NgModule({
imports: [
@@ -17,7 +16,6 @@ import { PdfViewerModule } from 'ng2-pdf-viewer';
IonicModule,
FontAwesomeModule,
ViewMediaPageRoutingModule,
PdfViewerModule
],
declarations: [ViewMediaPage]
})
@@ -24,13 +24,11 @@
<img src="{{image}}">
</div>
<div *ngIf="type == 'application/pdf'">
<pdf-viewer [src]="image" [render-text]="true" [original-size]="false" [zoom]="0.5" style="display: block;">
</pdf-viewer>
</div>
</div>
</ion-slide>
</ion-slides>
</div>
</div>
</ion-content>
+10 -8
View File
@@ -75,10 +75,12 @@
</ion-label> -->
<div *ngIf="room.lastMessage.file">
<fa-icon *ngIf="room.lastMessage.file.type != 'application/meeting' && room.lastMessage.file.type != 'application/img'" icon="file-alt" class="file-icon" [class.set-active-item-font-to-white]="room.id == idSelected"></fa-icon>
<fa-icon *ngIf="room.lastMessage.file.type != 'application/meeting' && room.lastMessage.file.type != 'application/img' && room.lastMessage.file.type != 'application/audio'" icon="file-alt" class="file-icon" [class.set-active-item-font-to-white]="room.id == idSelected"></fa-icon>
<fa-icon *ngIf="room.lastMessage.file.type == 'application/audio'" icon="file-audio" class="file-icon" [class.set-active-item-font-to-white]="room.id == idSelected"></fa-icon>
<span *ngIf="room.lastMessage.file.type == 'application/audio'"> audio </span>
<fa-icon *ngIf="room.lastMessage.file.type == 'application/meeting'" icon="calendar-alt" class="file-icon" [class.set-active-item-font-to-white]="room.id == idSelected"></fa-icon>
<span> {{room.lastMessage.file.name || room.lastMessage.file.subject }}</span>
<span> {{room.lastMessage.file.name || room.lastMessage.file.subject}}</span>
</div>
<ion-label *ngIf="room.lastMessage.attachments">
@@ -120,8 +122,7 @@
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' && group.id != idSelected " class="icon" slot="start" src="assets/images/theme/gov/icons-chat-group-chat-40.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' && group.id == idSelected " class="icon" slot="start" src="assets/images/theme/gov/icons-chat-group-chat-40-hover.svg"></ion-icon>
</div>
<div
(click)="openGroupMessagesPage(group.id)" class="item-content flex-grow-1 cursor-pointer">
<div (click)="openGroupMessagesPage(group.id)" class="item-content flex-grow-1 cursor-pointer">
<div class="item-title-time">
<div class="item-title" [class.item-title-active]="group.id ==idSelected">
<ion-label>{{group.name.split('-').join(' ')}}</ion-label>
@@ -132,11 +133,12 @@
<div *ngIf="group.lastMessage" class="item-description d-flex align-items-center" [class.item-description-active]="group.id ==idSelected">
<div class="item-message" *ngIf="group.otherUserType == false">{{group.lastMessage.u.name}}: {{group.lastMessage.msg}} </div>
<div *ngIf="group.otherUserType == true">{{group.userThatIsTyping}} está escrever ...</div>
<div class="item-files add-ellipsis" *ngIf="group.file">
<fa-icon *ngIf="group.lastMessage.file.type != 'application/meeting'" icon="file-alt" class="file-icon" [class.set-active-item-font-to-white]="group.id == idSelected"></fa-icon>
<div class="item-files add-ellipsis" *ngIf="group.lastMessage.file">
<fa-icon *ngIf="group.lastMessage.file.type != 'application/meeting' && group.lastMessage.file.type != 'application/audio'" icon="file-alt" class="file-icon" [class.set-active-item-font-to-white]="group.id == idSelected"></fa-icon>
<fa-icon *ngIf="group.lastMessage.file.type == 'application/audio'" icon="file-audio" class="file-icon" [class.set-active-item-font-to-white]="group.id == idSelected"></fa-icon>
<span *ngIf="group.lastMessage.file.type == 'application/audio'" class="item-files-title"> audio </span>
<fa-icon *ngIf="group.lastMessage.file.type == 'application/meeting'" icon="calendar-alt" class="file-icon" [class.set-active-item-font-to-white]="group.id == idSelected"></fa-icon>
<span class="item-files-title"> {{group.lastMessage.file.name || group.file.subject}}</span>
<span *ngIf="group.lastMessage.file.type != 'application/audio'" class="item-files-title"> {{ group.lastMessage.attachments[0].title }}</span>
</div>
<div class="item-files" *ngIf="group.attachments">
<div *ngIf="group.value.lastMessage.attachments[0].image_url">
@@ -16,6 +16,7 @@ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { ImageCropperModule } from 'ngx-image-cropper';
import { AngularCropperjsModule } from 'angular-cropperjs';
import { LettersAvatarModule } from "ngx-letters-avatar";
import { PipesModule } from 'src/app/pipes/pipes.module';
@NgModule({
imports: [
@@ -26,7 +27,8 @@ import { LettersAvatarModule } from "ngx-letters-avatar";
GroupMessagesPageRoutingModule,
ChatPopoverPageModule,
BtnModalDismissPageModule,
LettersAvatarModule
LettersAvatarModule,
PipesModule,
/* ImageCropperModule,
AngularCropperjsModule */
@@ -106,7 +106,7 @@
<ion-icon *ngIf="msg.attachments[0].image_url == null" name="download-outline"></ion-icon>
</div>
<div *ngIf="msg.file.type != 'application/img'">
<div class="file">
<div class="file add-attachment-bg-color" *ngIf="msg.file.type != 'application/audio'">
<div (click)="openPreview(msg)" class="file-details add-ellipsis cursor-pointer" *ngIf="msg.file">
<span *ngIf="msg.file.type">
<fa-icon *ngIf="msg.file.type == 'application/pdf'" icon="file-pdf" class="pdf-icon"></fa-icon>
@@ -117,11 +117,14 @@
<ion-label class="file-title">{{file.title}}</ion-label>
</div>
</div>
<div class="file-details-optional">
<ion-label *ngIf="msg.file">
<div *ngIf="msg.file.type == 'application/audio'">
<audio [src]="file.title_link|safehtml" preload="metadata" class="d-flex width-100" controls controlsList="nodownload noplaybackrate"></audio>
</div>
<div class="file-details-optional add-attachment-bg-color">
<ion-label *ngIf="msg.file && msg.file != ''">
<span *ngIf="file.description">{{file.description}}</span>
<span *ngIf="file.description && msg.file.type != 'application/webtrix'"></span>
<span *ngIf="msg.file.type != 'application/webtrix'">{{msg.displayType}}</span>
<span *ngIf="msg.file.type != 'application/webtrix' && msg.file.type != 'application/audio'">{{msg.displayType}}</span>
</ion-label>
</div>
</div>
@@ -190,32 +193,47 @@
<div class="typing" >
<ngx-letters-avatar
[avatarName]= "wsChatMethodsService.getGroupRoom(roomId).name"
[width]="30"
[width]="30"
[circular]="true"
fontFamily="Roboto"></ngx-letters-avatar>
{{ wsChatMethodsService.getGroupRoom(roomId).userThatIsTyping }} está a escrever...
</div>
<div class="width-100 pl-20 pr-20">
<span *ngIf="!lastAudioRecorded">{{durationDisplay}}</span>
<audio [src]="audioRecorded" class="d-flex width-100 mt-10 mb-10" *ngIf="lastAudioRecorded" controls controlsList="nodownload noplaybackrate"></audio>
</div>
<div class="container width-100 d-flex">
<div>
<button class="btn-no-color" (click)="openChatOptions()">
<button *ngIf="!recording && !lastAudioRecorded && allowTyping" class="btn-no-color" (click)="openChatOptions()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-options" src="assets/images/icons-add.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-options" src="assets/images/theme/gov/icons-add.svg"></ion-icon>
</button>
<button *ngIf="recording || lastAudioRecorded || !allowTyping" class="btn-no-color" (click)="deleteRecording()">
<fa-icon class="icon-size-27" icon="trash"></fa-icon>
</button>
</div>
<div class="message-box width-80">
<ion-item class="ion-no-padding type-message" lines="none">
<ion-textarea autocomplete="on" autocorrect="on" spellcheck="true" clearOnEdit="true" placeholder="Escrever uma mensagem" auto-grow class="message-input" rows="1" [(ngModel)]="wsChatMethodsService.getGroupRoom(roomId).message" (ionChange)="wsChatMethodsService.getGroupRoom(roomId).sendTyping()"></ion-textarea>
<button hidden class="btn-no-color">
<ion-icon slot="end" src="assets/icon/icons-chat-mic.svg"></ion-icon>
<div *ngIf="!recording && !lastAudioRecorded" class="type-message">
<ion-textarea *ngIf="allowTyping" autocomplete="on" autocorrect="on" spellcheck="true" clearOnEdit="true" placeholder="Escrever uma mensagem" auto-grow class="message-input" rows="1" [(ngModel)]="wsChatMethodsService.getGroupRoom(roomId).message" (ionChange)="wsChatMethodsService.getGroupRoom(roomId).sendTyping()"></ion-textarea>
</div>
<div *ngIf="recording" class="d-flex align-items-center justify-content-center">
<button (click)="stopRecording()" class="btn-no-color d-flex align-items-center justify-content-center">
<ion-icon class="icon-size-45" name="stop-circle-outline" color="danger"></ion-icon>
</button>
</ion-item>
</div>
</div>
<div>
<button #recordbtn *ngIf="!wsChatMethodsService.getGroupRoom(roomId).message && !lastAudioRecorded" (click)="startRecording()" class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/default/icons-chat-record-audio.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-record-audio.svg"></ion-icon>
</button>
<button *ngIf="wsChatMethodsService.getGroupRoom(roomId).message" class="btn-no-color" (click)="sendMessage()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
</button>
<button *ngIf="!wsChatMethodsService.getGroupRoom(roomId).message" class="btn-no-color">
<button *ngIf="!wsChatMethodsService.getGroupRoom(roomId).message && lastAudioRecorded" class="btn-no-color" (click)="sendAudio(lastAudioRecorded)">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
</button>
@@ -177,7 +177,7 @@
-webkit-overflow-scrolling: touch;
.messages-list-item-wrapper{
overflow: auto;
overflow: hidden;
}
.messages-list-item-wrapper-active{
background: #e6f6ff75 !important;
@@ -358,4 +358,4 @@
.typing ngx-letters-avatar {
padding-right: 5px;
}
}
@@ -32,6 +32,9 @@ import { Storage } from '@ionic/storage';
import { CameraService } from 'src/app/services/camera.service';
import { SearchPage } from 'src/app/pages/search/search.page';
import { ProcessesService } from 'src/app/services/processes.service';
import { VoiceRecorder, VoiceRecorderPlugin, RecordingData, GenericResponse, CurrentRecordingStatus } from 'capacitor-voice-recorder';
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'app-group-messages',
@@ -74,6 +77,15 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('scrollMe') private myScrollContainer: ElementRef;
recording = false;
allowTyping = true;
storedFileNames = [];
lastAudioRecorded = '';
audioRecorded:any = "";
audioDownloaded:any = "";
durationDisplay = '';
duration = 0;
constructor(
private menu: MenuController,
private modalController: ModalController,
@@ -97,8 +109,8 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
private AttachmentsService: AttachmentsService,
private storage: Storage,
private processesService: ProcessesService,
private CameraService: CameraService,
private sanitiser: DomSanitizer,
) {
this.loggedUserChat = authService.ValidatedUserChat['data'];
this.isGroupCreated = true;
@@ -132,6 +144,8 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
this.wsChatMethodsService.getUserOfRoom(this.roomId).then((value) => {
console.log('MEMBER', value)
})
VoiceRecorder.requestAudioRecordingPermission();
//this.loadFiles();
}
setStatus(status: string) {
@@ -145,19 +159,8 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
}
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 = "";
const room = this.wsChatMethodsService.getGroupRoom(this.roomId)
this.alertService.confirmDeleteMessage(msgId, room);
}
ngAfterViewInit() {
@@ -197,6 +200,106 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
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 getFile(fileName?:any){
const audioFile = await Filesystem.readFile({
path: fileName,
directory: Directory.Data
})
//console.log(audioFile);
const base64sound = audioFile.data;
//Converting base64 to blob
const base64 = await fetch(base64sound);
//console.log(base64);
const base64Response = await fetch(`data:audio/ogg;base64,${base64sound}`);
//console.log(base64Response);
this.audioRecorded = base64Response.url;
console.log(this.audioRecorded);
}
async loadFiles() {
this.storage.get('fileName').then((fileName) => {
this.lastAudioRecorded = fileName;
})
this.storage.get('recordData').then((recordData) => {
console.log(recordData);
if(recordData.value.recordDataBase64.includes('data:audio')){
this.audioRecorded = this.sanitiser.bypassSecurityTrustResourceUrl(recordData.value.recordDataBase64);
}
else{
this.audioRecorded = this.sanitiser.bypassSecurityTrustResourceUrl(`data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}`);
}
});
}
startRecording() {
console.log('Recording');
if (this.recording) {
return;
}
this.recording = true;
VoiceRecorder.startRecording();
this.calculateDuration();
}
stopRecording() {
this.deleteRecording();
this.allowTyping = false;
console.log('Stop');
if (!this.recording) {
return;
}
this.recording = false;
VoiceRecorder.stopRecording().then(async (result: RecordingData) => {
console.log(result);
this.recording = false;
if (result.value && result.value.recordDataBase64) {
const recordData = result.value.recordDataBase64;
//console.log(recordData);
const fileName = new Date().getTime() + ".mp3";
//Save file
this.storage.set('fileName',fileName);
this.storage.set('recordData',result).then(() => {
console.log('Audio recorded saved');
})
}
})
setTimeout(async () => {
this.loadFiles();
}, 1000);
}
async deleteRecording(){
this.storage.remove('fileName');
this.storage.remove('recordData');
this.allowTyping = true;
this.lastAudioRecorded = '';
this.loadFiles();
}
async goToEvent(eventId: any) {
let classs;
@@ -268,6 +371,7 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
close() {
this.modalController.dismiss();
this.deleteRecording();
}
doRefresh(ev: any) {
@@ -311,13 +415,48 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
return i;
}
sendMessage() {
this.wsChatMethodsService.getGroupRoom(this.roomId).send({})
}
async sendAudio(fileName) {
const roomId = this.roomId
this.storage.get('recordData').then((recordData) => {
console.log(recordData);
if(recordData.value.recordDataBase64.includes('data:audio')){
this.audioRecorded = recordData.value.recordDataBase64;
}
else{
this.audioRecorded = `data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}`;
}
});
//Converting base64 to blob
const base64Response = await fetch(this.audioRecorded);
const blob = await base64Response.blob();
const formData = new FormData();
formData.append("blobFile", blob);
this.wsChatMethodsService.getGroupRoom(roomId).send({
file: {
"type": "application/audio",
/* "guid": '', */
},
attachments: [{
"title": fileName ,
"title_link": this.audioRecorded,
"title_link_download": true,
"type": "file"
}],
temporaryData: formData
})
this.deleteRecording();
}
async openOptions() {
const modal = await this.popoverController.create({
component: ChatPopoverPage,
@@ -12,6 +12,7 @@ import { BtnModalDismissPage } from 'src/app/shared/btn-modal-dismiss/btn-modal-
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { MatMenuModule } from '@angular/material/menu';
import { LettersAvatarModule } from "ngx-letters-avatar";
import { PipesModule } from 'src/app/pipes/pipes.module';
@NgModule({
imports: [
@@ -21,7 +22,8 @@ import { LettersAvatarModule } from "ngx-letters-avatar";
FontAwesomeModule,
MessagesPageRoutingModule,
MatMenuModule,
LettersAvatarModule
LettersAvatarModule,
PipesModule,
],
declarations: [MessagesPage]
})
+29 -28
View File
@@ -87,8 +87,8 @@
<ion-icon *ngIf="msg.attachments[0].image_url == null" name="download-outline"></ion-icon>
</div>
<div *ngIf="msg.file.type != 'application/img'">
<div class="file">
<div (click)="docIndex(i); openPreview(msg)" class="file-details add-ellipsis cursor-pointer" *ngIf="msg.file">
<div class="file add-attachment-bg-color" *ngIf="msg.file.type != 'application/audio'">
<div (click)="docIndex(i); viewDocument(msg, file.title_link)" class="file-details add-ellipsis cursor-pointer" *ngIf="msg.file">
<span *ngIf="msg.file.type">
<fa-icon *ngIf="msg.file.type == 'application/pdf'" icon="file-pdf" class="pdf-icon"></fa-icon>
<fa-icon *ngIf="msg.file.type == 'application/word'" icon="file-word" class="word-icon"></fa-icon>
@@ -98,11 +98,14 @@
<ion-label class="file-title">{{file.title}}</ion-label>
</div>
</div>
<div class="file-details-optional">
<div *ngIf="msg.file.type == 'application/audio'">
<audio [src]="file.title_link|safehtml" preload="metadata" class="d-flex width-100" controls controlsList="nodownload noplaybackrate"></audio>
</div>
<div class="file-details-optional add-attachment-bg-color">
<ion-label *ngIf="msg.file && msg.file != ''">
<span *ngIf="file.description">{{file.description}}</span>
<span *ngIf="file.description && msg.file.type != 'application/webtrix'"></span>
<span *ngIf="msg.file.type != 'application/webtrix'">{{msg.displayType}}</span>
<span *ngIf="msg.file.type != 'application/webtrix' && msg.file.type != 'application/audio'">{{msg.displayType}}</span>
</ion-label>
</div>
</div>
@@ -173,43 +176,41 @@
fontFamily="Roboto"></ngx-letters-avatar>
está a escrever ...
</div>
<div class="width-100 pl-20 pr-20">
<span *ngIf="!lastAudioRecorded">{{durationDisplay}}</span>
<audio [src]="audioRecorded" class="d-flex width-100 mt-10 mb-10" *ngIf="lastAudioRecorded" controls controlsList="nodownload noplaybackrate"></audio>
</div>
<ion-list hidden>
<ion-item (click)="playFile(storedFileNames)">
{{storedFileNames}}
</ion-item>
<ion-item (click)="playFile(storedFileNames)">
{{storedFileNames}}
</ion-item>
</ion-list>
<audio hidden controls>
<!-- <source src="https://www.tabularium.pt/file-upload/5g6DkyMC4MHcuaDyp/Audio%20record.mp3" type="audio/mpeg"> -->
</audio>
<!-- <button (click)="startRecording()">Start Recording</button>
<button (click)="stopRecording()">Stop Recording</button> -->
<div class="container width-100 d-flex">
<div>
<button class="btn-no-color" (click)="openChatOptions()">
<button *ngIf="!recording && !lastAudioRecorded && allowTyping" class="btn-no-color" (click)="openChatOptions()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-options" src="assets/images/icons-add.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-options" src="assets/images/theme/gov/icons-add.svg"></ion-icon>
</button>
<button *ngIf="recording || lastAudioRecorded || !allowTyping" class="btn-no-color" (click)="deleteRecording()">
<fa-icon class="icon-size-27" icon="trash"></fa-icon>
</button>
</div>
<div class="width-70">
<ion-item class="ion-no-padding ion-no-margin type-message" lines="none">
<ion-textarea autocomplete="on" autocorrect="on" spellcheck="true" *ngIf="!recording" clearOnEdit="true" placeholder="Escrever uma mensagem" auto-grow class="message-input" rows="1" [(ngModel)]="wsChatMethodsService.getDmRoom(roomId).message" (ionChange)="wsChatMethodsService.getDmRoom(roomId).sendTyping()"></ion-textarea>
<ion-textarea autocomplete="on" spellcheck="true" *ngIf="recording" clearOnEdit="true" placeholder="Escrever uma mensagem" auto-grow class="message-input" rows="1" [(ngModel)]="durationDisplay"></ion-textarea>
<button hidden #recordbtn class="btn-no-color" (click)="notImplemented()">
<ion-icon slot="end" src="assets/icon/icons-chat-mic.svg"></ion-icon>
<div class="width-70 message-container">
<div *ngIf="!recording && !lastAudioRecorded" class="type-message">
<ion-textarea *ngIf="allowTyping" autocomplete="on" autocorrect="on" spellcheck="true" clearOnEdit="true" placeholder="Escrever uma mensagem" auto-grow class="message-input" rows="1" [(ngModel)]="wsChatMethodsService.getDmRoom(roomId).message" (ionChange)="wsChatMethodsService.getDmRoom(roomId).sendTyping()"></ion-textarea>
</div>
<div *ngIf="recording" class="d-flex align-items-center justify-content-center">
<button (click)="stopRecording()" class="btn-no-color d-flex align-items-center justify-content-center">
<ion-icon class="icon-size-45" name="stop-circle-outline" color="danger"></ion-icon>
</button>
</ion-item>
</div>
</div>
<div>
<button *ngIf="wsChatMethodsService.getDmRoom(roomId).message" class="btn-no-color" (click)="sendMessage()">
<button #recordbtn *ngIf="!wsChatMethodsService.getDmRoom(roomId).message && !lastAudioRecorded" (click)="startRecording()" class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/default/icons-chat-record-audio.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-record-audio.svg"></ion-icon>
</button>
<button *ngIf="wsChatMethodsService.getDmRoom(roomId).message" class="btn-no-color" (click)="sendMessage()" class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
</button>
<button *ngIf="!wsChatMethodsService.getDmRoom(roomId).message" class="btn-no-color">
<button *ngIf="!wsChatMethodsService.getDmRoom(roomId).message && lastAudioRecorded" (click)="sendAudio(lastAudioRecorded)" class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
</button>
+14 -6
View File
@@ -56,9 +56,6 @@
width: calc(100% - 25px);
text-align: right;
/* ion-icon{
font-size: 25px;
} */
.middle-container-options-icons{
color: #0782c9;
font-size: 23px;
@@ -117,6 +114,7 @@
}
}
}
ion-content{
.welcome-text{
/* width: 322px; */
@@ -148,7 +146,7 @@
-webkit-overflow-scrolling: touch;
.messages-list-item-wrapper{
overflow: auto;
overflow: hidden;
}
.messages-list-item-wrapper-active{
background: #e6f6ff75 !important;
@@ -228,7 +226,6 @@
justify-content: center;
justify-content: space-evenly;
align-items: center;
}
.chat-icon-options{
@@ -319,4 +316,15 @@ display: block;
.typing ngx-letters-avatar {
padding-right: 5px;
}
}
button{
padding: 0px;
border: 0px;
}
button::-moz-focus-inner {
padding: 0px;
border: 0px;
}
+115 -53
View File
@@ -20,7 +20,7 @@ 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 { Directory, Encoding, FilesystemDirectory } from '@capacitor/filesystem';
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';
@@ -40,13 +40,10 @@ import { SearchPage } from 'src/app/pages/search/search.page';
import { Storage } from '@ionic/storage';
import { FileToBase64Service } from 'src/app/services/file/file-to-base64.service';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { Plugins } from '@capacitor/core';
import { fromByteArray } from 'base64-js';
import { DomSanitizer } from '@angular/platform-browser';
/* import {Plugins} from '@capacitor/core';
const { Filesystem } = Plugins;
const IMAGE_DIR = 'stored-images';
const { Filesystem } = Plugins; */
@Component({
selector: 'app-messages',
@@ -87,7 +84,11 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
LoadedDocument: any = null;
recording = false;
allowTyping = true;
storedFileNames = [];
lastAudioRecorded = '';
audioRecorded:any = "";
audioDownloaded:any = "";
durationDisplay = '';
duration = 0;
@ViewChild('recordbtn', { read: ElementRef }) recordBtn: ElementRef;
@@ -120,6 +121,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
private processesService: ProcessesService,
private storage: Storage,
private fileToBase64Service: FileToBase64Service,
private sanitiser: DomSanitizer,
) {
this.loggedUser = authService.ValidatedUserChat['data'];
this.roomId = this.navParams.get('roomId');
@@ -131,6 +133,8 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
}
};
console.log(this.wsChatMethodsService.getDmRoom(this.roomId).loadHistory({}));
this.wsChatMethodsService.getDmRoom(this.roomId).loadHistory({})
this.wsChatMethodsService.getDmRoom(this.roomId).scrollDown = this.scrollToBottomClicked
this.wsChatMethodsService.openRoom(this.roomId)
@@ -145,15 +149,14 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
this.wsChatMethodsService.getUserOfRoom(this.roomId).then((value) => {
console.log('MEMBER', value)
})
//this.loadFiles();
VoiceRecorder.requestAudioRecordingPermission();
this.getChatMembers();
Filesystem.mkdir({
/* Filesystem.mkdir({
path: IMAGE_DIR,
directory: Directory.Data,
recursive: true
});
}); */
}
ngAfterViewInit() {
@@ -167,11 +170,14 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
onStart: ev => {
Haptics.impact({ style: ImpactStyle.Light })
this.startRecording();
this.calculateDuration();
//this.calculateDuration();
},
onEnd: ev => {
Haptics.impact({ style: ImpactStyle.Light })
this.stopRecording();
/* setTimeout(() => {
this.loadFiles();
}, 500); */
}
}, true);
longpress.enable();
@@ -194,61 +200,73 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
}
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);
this.storage.get('fileName').then((fileName) => {
this.lastAudioRecorded = fileName;
})
this.storage.get('recordData').then((recordData) => {
console.log(recordData);
if(recordData.value.recordDataBase64.includes('data:audio')){
this.audioRecorded = this.sanitiser.bypassSecurityTrustResourceUrl(recordData.value.recordDataBase64);
}
else{
this.audioRecorded = this.sanitiser.bypassSecurityTrustResourceUrl(`data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}`);
}
});
}
startRecording() {
console.log('Recording');
if (this.recording) {
return;
}
this.recording = true;
VoiceRecorder.startRecording();
VoiceRecorder.startRecording()
.then((result: GenericResponse) => console.log(result.value))
.catch(error => console.log(error));
this.calculateDuration();
}
stopRecording() {
this.deleteRecording();
this.allowTyping = false;
console.log('Stop');
if (!this.recording) {
return;
}
this.recording = false;
VoiceRecorder.stopRecording().then(async (result: RecordingData) => {
console.log(result);
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,
//console.log(recordData);
const fileName = new Date().getTime() + ".mp3";
//Save file
this.storage.set('fileName',fileName);
this.storage.set('recordData',result).then(() => {
console.log('Audio recorded saved');
})
}
})
setTimeout(async () => {
this.loadFiles();
}, 1000);
}
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();
async deleteRecording(){
this.storage.remove('fileName');
this.storage.remove('recordData');
this.allowTyping = true;
this.lastAudioRecorded = '';
this.loadFiles();
}
handlePress(id?: string) {
this.selectedMsgId = id;
this.showMessageOptions = true;
@@ -260,19 +278,8 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
}
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 = "";
const room = this.wsChatMethodsService.getDmRoom(this.roomId)
this.alertService.confirmDeleteMessage(msgId, room);
}
@@ -282,6 +289,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
close() {
this.modalController.dismiss();
this.deleteRecording();
}
load() {
@@ -358,6 +366,53 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
this.wsChatMethodsService.getDmRoom(this.roomId).send({})
}
async sendAudio(fileName) {
const roomId = this.roomId
this.storage.get('recordData').then((recordData) => {
console.log(recordData);
if(recordData.value.recordDataBase64.includes('data:audio')){
this.audioRecorded = recordData.value.recordDataBase64;
}
else{
this.audioRecorded = `data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}`;
}
});
//Converting base64 to blob
const base64Response = await fetch(this.audioRecorded);
const blob = await base64Response.blob();
const formData = new FormData();
formData.append("blobFile", blob);
this.wsChatMethodsService.getDmRoom(roomId).send({
file: {
"type": "application/audio",
/* "guid": '', */
},
attachments: [{
"title": fileName ,
"title_link": this.audioRecorded,
"title_link_download": true,
"type": "file"
}],
temporaryData: formData
})
this.deleteRecording();
}
blobToBase64 = blob => {
const reader = new FileReader();
reader.readAsDataURL(blob);
return new Promise(resolve => {
reader.onloadend = () => {
resolve(reader.result);
};
});
};
viewDocument(msg: any, url?: string) {
console.log(msg)
if (msg.attachments.type == "application/webtrix") {
@@ -396,6 +451,13 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
}
}
/* playSound(url?:any){
alert('here')
console.log(url);
//this.audioDownloaded = this.sanitiser.bypassSecurityTrustResourceUrl(url);
this.audioDownloaded = url;
} */
docIndex(index: number) {
this.dicIndex = index
}
@@ -840,7 +902,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
if (msg.file.type == "application/img") {
this.downloadFile = 'data:image/jpeg;base64,' + btoa(new Uint8Array(event.body).reduce((data, byte) => data + String.fromCharCode(byte), ''));
} else {
//console.log('TRY THIS LIBRARY ',fromByteArray(event.body));
//console.log('TRY THIS LIBRARY ',fromByteArray(event.body));
this.downloadFile = event.body;
}
-2
View File
@@ -5,7 +5,6 @@ import { IonicModule } from '@ionic/angular';
import { EventsPageRoutingModule } from './events-routing.module';
import { EventsPage } from './events.page';
import { HeaderPageModule } from 'src/app/shared/header/header.module';
import { PdfViewerModule } from 'ng2-pdf-viewer';
@NgModule({
imports: [
@@ -15,7 +14,6 @@ import { PdfViewerModule } from 'ng2-pdf-viewer';
EventsPageRoutingModule,
HeaderPageModule,
//
PdfViewerModule
],
declarations: [EventsPage],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
+3 -2
View File
@@ -6,11 +6,12 @@ import { EventPipe } from './event.pipe';
import { PublicationPipe } from './publication.pipe';
import { ExpedienteTaskPipe } from './expediente-task.pipe';
import { ParticipantsPipe } from './participants.pipe';
import { SafehtmlPipe } from './safehtml.pipe';
@NgModule({
declarations: [FilterPipe, SearchDocumentPipe, CustomTaskPipe, EventPipe, PublicationPipe, ExpedienteTaskPipe, ParticipantsPipe],
exports: [FilterPipe],
declarations: [FilterPipe, SearchDocumentPipe, CustomTaskPipe, EventPipe, PublicationPipe, ExpedienteTaskPipe, ParticipantsPipe, SafehtmlPipe],
exports: [FilterPipe, SafehtmlPipe],
imports: []
})
export class PipesModule { }
+8
View File
@@ -0,0 +1,8 @@
import { SafehtmlPipe } from './safehtml.pipe';
describe('SafehtmlPipe', () => {
it('create an instance', () => {
const pipe = new SafehtmlPipe();
expect(pipe).toBeTruthy();
});
});
+14
View File
@@ -0,0 +1,14 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({
name: 'safehtml'
})
export class SafehtmlPipe implements PipeTransform {
constructor(private sanitiser: DomSanitizer){}
transform(html): unknown {
return this.sanitiser.bypassSecurityTrustResourceUrl(html);
}
}
+2
View File
@@ -167,6 +167,8 @@ export class AuthService {
message.file.guid = guid.path
await this.storage.set(guid.path, message.file.image_url).then(() => {
console.log('add picture to chat IMAGE SAVED')
console.log(message.attachments);
message.getFileFromDb()
});
+27 -25
View File
@@ -99,29 +99,31 @@ export class RoomService {
let ChatMessage = _ChatMessage.fields.args[0]
ChatMessage = this.fix_updatedAt(ChatMessage)
if(!this.messagesLocalReference.includes(ChatMessage.localReference)) {
const message = this.prepareMessage(ChatMessage)
this.lastMessage = message
this.calDateDuration(ChatMessage._updatedAt)
if (message.t == 'r') {
this.name = message.msg
}
if(this.isSenderIsNotMe(ChatMessage)) {
this.NativeNotificationService.sendNotificationChat({
message: message.msg,
title: this.name
});
}
this.addMessageDB(ChatMessage)
setTimeout(()=>{
this.scrollDown()
}, 50)
}, 50)
//Sort list of messages from recent to old
this.sortRoomList()
}
@@ -175,7 +177,7 @@ export class RoomService {
}
async updateMessageDB(ChatMessage, localReference) {
if (environment.chatOffline) {
if (environment.chatOffline) {
this.storage.get('chatmsg' + this.id).then((messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
@@ -197,14 +199,14 @@ export class RoomService {
messages[index] = ChatMessage
this.storage.set('chatmsg' + this.id, messages)
}
})
}
}
async updateViewedMessage(id, userId) {
if (environment.chatOffline) {
if (environment.chatOffline) {
this.storage.get('chatmsg' + this.id).then((messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
@@ -212,12 +214,12 @@ export class RoomService {
let index;
const find = messages.find((message, _index)=> {
if(message._id == id) {
index = _index
return true
}
return false
})
@@ -231,7 +233,7 @@ export class RoomService {
this.storage.set('chatmsg' + this.id, messages)
}
})
}
@@ -247,15 +249,15 @@ export class RoomService {
if(!Array.isArray(messages)) {
messages = []
}
messages.forEach((message, index) => {
if(message._id == id) {
messages.splice(index, 1)
}
})
this.storage.set('chatmsg' + this.id, messages).then((value) => {
console.log('MSG SAVED ON STORAGE', value)
});
@@ -332,7 +334,7 @@ export class RoomService {
if (environment.chatOffline) {
this.messagesLocalReference.push(localReference)
this.addMessageDB(offlineChatMessage)
setTimeout(() => {
this.scrollDown()
}, 150)
@@ -347,9 +349,9 @@ export class RoomService {
/**
*
* @param message
* @param ChatMessage
*
* @param message
* @param ChatMessage
* @description when creating message we use offline data, then we need redefined with live data
*/
redefinedMessage (message: MessageService, ChatMessage) {
@@ -497,9 +499,9 @@ export class RoomService {
/**
* @description find or create message
* @param message
* @param save
* @returns
* @param message
* @param save
* @returns
*/
prepareMessage(message, save = true): MessageService {
message = this.fix_updatedAt(message)
@@ -515,7 +517,7 @@ export class RoomService {
const found = this.messages.find((MessageService) => {
if (MessageService._id == message._id) {
if(this.hasLoadHistory) console.log(`${MessageService._id} == ${message._id}`)
if(this.hasLoadHistory) /* console.log(`${MessageService._id} == ${message._id}`) */
return true
} else {
return false
@@ -199,6 +199,7 @@ export class WsChatMethodsService {
* @description sort room list by last message date
*/
sortRoomList =() => {
this._dm = this.sortService.sortDate(this._dm,'_updatedAt').reverse()
this._group = this.sortService.sortDate(this._group,'_updatedAt').reverse()
}
@@ -252,6 +253,16 @@ export class WsChatMethodsService {
})
}
private fix_updatedAt(message) {
if (message.result) {
message.result._updatedAt = message.result._updatedAt['$date']
} else if(message._updatedAt) {
if(message._updatedAt.hasOwnProperty('$date')) {
message._updatedAt = message._updatedAt['$date']
}
}
return message
}
/**
* @description create a representation of an room in these instance this.dm, this.group ...
@@ -263,13 +274,16 @@ export class WsChatMethodsService {
/**
* @description data used to define or create room
*/
roomData = this.fix_updatedAt(roomData)
const setData = {
customFields: roomData.customFields,
id: this.getRoomId(roomData),
name: this.getRoomName(roomData),
t: roomData.t,
lastMessage: this.getRoomLastMessage(roomData),
_updatedAt: new Date(roomData._updatedAt['$date'])
_updatedAt: roomData._updatedAt
}
let roomId = this.getRoomId(roomData)
@@ -282,7 +296,7 @@ export class WsChatMethodsService {
room.getAllUsers = this.getUsers
room.receiveMessageDelete();
room.sortRoomList = this.sortRoomList
// create individual room
if(this.isIndividual(roomData)) {
@@ -398,9 +412,9 @@ export class WsChatMethodsService {
}
hidingRoom(id?) {
return this.WsChatService.hidingRoom(id).then(()=>{
// this.hideRoom(id)
// this.hideRoom(id)
})
}
+1 -1
View File
@@ -703,7 +703,7 @@ updateRoomEventss(roomId, collection:string, funx: Function, ) {
this.wsMsgQueue[requestId] = {message, requestId, loginRequired}
} else {
let messageStr = JSON.stringify(message)
// console.log('messageStr', messageStr)
console.log('messageStr', messageStr)
this.socket.send(messageStr)
}
-2
View File
@@ -5,7 +5,6 @@ import { IonicModule } from '@ionic/angular';
import { EventsPageRoutingModule } from './events-routing.module';
import { EventsPage } from './events.page';
import { HeaderPageModule } from 'src/app/shared/header/header.module';
import { PdfViewerModule } from 'ng2-pdf-viewer';
@NgModule({
imports: [
@@ -15,7 +14,6 @@ import { PdfViewerModule } from 'ng2-pdf-viewer';
EventsPageRoutingModule,
HeaderPageModule,
//
PdfViewerModule
],
declarations: [EventsPage],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
-5
View File
@@ -113,9 +113,4 @@
</div>
</div>
<!-- <pdf-viewer [src]="pdfSrc"
[render-text]="true"
style="display: block;"
></pdf-viewer> -->
</ion-content>
@@ -11,10 +11,10 @@ import { SharedModule } from 'src/app/shared/shared.module';
import { ChatPopoverPageModule } from '../../popover/chat-popover/chat-popover.module';
import { NewEventPageModule } from '../../agenda/new-event/new-event.module';
import { PdfViewerModule } from 'ng2-pdf-viewer';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import {MatMenuModule} from '@angular/material/menu';
import { LettersAvatarModule } from "ngx-letters-avatar";
import { PipesModule } from 'src/app/pipes/pipes.module';
@NgModule({
imports: [
@@ -22,11 +22,11 @@ import { LettersAvatarModule } from "ngx-letters-avatar";
FormsModule,
IonicModule,
FontAwesomeModule,
PdfViewerModule,
ChatPopoverPageModule,
GroupMessagesPageRoutingModule,
MatMenuModule,
LettersAvatarModule
LettersAvatarModule,
PipesModule,
//
],
exports: [GroupMessagesPage],
@@ -72,6 +72,7 @@
</mat-menu>
</div>
<div class="title">
<ion-label>{{msg.u.name}}</ion-label>
<span class="time">{{msg.duration}}</span>
</div>
<div class="message">
@@ -82,7 +83,7 @@
<ion-icon *ngIf="msg.attachments[0].image_url == null" name="download-outline"></ion-icon>
</div>
<div *ngIf="msg.file.type != 'application/img'">
<div class="file">
<div *ngIf="msg.file.type != 'application/audio'" class="file add-attachment-bg-color">
<div (click)="openPreview(msg)" class="file-details add-ellipsis cursor-pointer" *ngIf="msg.file">
<span *ngIf="msg.file.type">
<fa-icon *ngIf="msg.file.type == 'application/pdf'" icon="file-pdf" class="pdf-icon"></fa-icon>
@@ -93,11 +94,14 @@
<ion-label class="file-title">{{file.title}}</ion-label>
</div>
</div>
<div class="file-details-optional">
<div class="audio-contentainer" *ngIf="msg.file.type == 'application/audio'">
<audio [src]="file.title_link|safehtml" preload="metadata" controls controlsList="nodownload noplaybackrate"></audio>
</div>
<div class="file-details-optional add-attachment-bg-color">
<ion-label *ngIf="msg.file">
<span *ngIf="file.description">{{file.description}}</span>
<span *ngIf="file.description && msg.file.type != 'application/webtrix'"></span>
<span *ngIf="msg.file.type != 'application/webtrix'">{{msg.displayType}}</span>
<span *ngIf="msg.file.type != 'application/webtrix' && msg.file.type != 'application/audio'">{{msg.displayType}}</span>
</ion-label>
</div>
</div>
@@ -205,22 +209,23 @@
<ion-footer>
<!-- <div class="typing" *ngIf="wsChatMethodsService.getGroupRoom(roomId).otherUserType == true" >A escrever...</div> -->
<div class="typing" *ngIf="wsChatMethodsService.getGroupRoom(roomId).otherUserType == true">
<!-- <div class="typing" *ngIf="wsChatMethodsService.getGroupRoom(roomId).otherUserType == true">
<ngx-letters-avatar *ngIf="showAvatar"
[avatarName]= "wsChatMethodsService.getGroupRoom(roomId).name"
[width]="30"
[circular]="true"
fontFamily="Roboto"></ngx-letters-avatar>
{{ wsChatMethodsService.getGroupRoom(roomId).userThatIsTyping }} está a escrever...
</div>
</div> -->
<div class="width-100 pl-20 pr-20">
<span *ngIf="!lastAudioRecorded">{{durationDisplay}}</span>
<audio [src]="audioRecorded" class="d-flex width-100 mt-10 mb-10" *ngIf="lastAudioRecorded" controls controlsList="nodownload noplaybackrate"></audio>
</div>
<div class="container width-100 d-flex">
<div>
<!-- <button class="btn-no-color" (click)="openSendGroupMessageOptions()">
<ion-icon class="chat-icon-options" src="assets/images/icons-add.svg"></ion-icon>
</button> -->
<ion-fab horizontal="start" vertical="bottom" slot="fixed">
<ion-fab *ngIf="!recording && !lastAudioRecorded && allowTyping" horizontal="start" vertical="bottom" slot="fixed">
<ion-fab-button color="light" size="small">
<ion-icon name="add"></ion-icon>
</ion-fab-button>
@@ -242,24 +247,34 @@
</ion-fab-button>
</ion-fab-list>
</ion-fab>
<button *ngIf="recording || lastAudioRecorded || !allowTyping" class="btn-delete-recording btn-no-color" (click)="deleteRecording()">
<fa-icon class="icon-size-27" icon="trash"></fa-icon>
</button>
</div>
<div class="width-100">
<ion-item class="ion-no-padding type-message" lines="none">
<div *ngIf="!recording && !lastAudioRecorded" class="type-message">
<ion-textarea autocomplete="on" autocorrect="on" spellcheck="true" (keyup.enter)="sendMessage()" clearOnEdit="true" placeholder="Escrever uma mensagem" class="message-input" rows="1" [(ngModel)]="wsChatMethodsService.getGroupRoom(roomId).message" (ionChange)="wsChatMethodsService.getGroupRoom(roomId).sendTyping()"></ion-textarea>
<button hidden class="btn-no-color">
<ion-icon slot="end" src="assets/icon/icons-chat-mic.svg"></ion-icon>
</div>
<div *ngIf="recording" class="d-flex align-items-center justify-content-center">
<button (click)="stopRecording()" class="btn-no-color d-flex align-items-center justify-content-center">
<ion-icon class="icon-size-45" name="stop-circle-outline" color="danger"></ion-icon>
</button>
</ion-item>
</div>
</div>
<div class="btn-send">
<button #recordbtn *ngIf="!wsChatMethodsService.getGroupRoom(roomId).message && !lastAudioRecorded" (click)="startRecording()" class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/default/icons-chat-record-audio.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-record-audio.svg"></ion-icon>
</button>
<button *ngIf="wsChatMethodsService.getGroupRoom(roomId).message" class="btn-no-color" (click)="sendMessage()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
</button>
<button title="Enviar Mensagem" *ngIf="!wsChatMethodsService.getGroupRoom(roomId).message" class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<button *ngIf="!wsChatMethodsService.getGroupRoom(roomId).message && lastAudioRecorded" class="btn-no-color" (click)="sendAudio(lastAudioRecorded)">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
</button>
</div>
</div>
@@ -80,6 +80,10 @@
}
}
}
.btn-delete-recording{
margin-left: 20px !important;
}
ion-content{
.welcome-text{
/* width: 322px; */
@@ -288,4 +292,4 @@
.typing ngx-letters-avatar {
padding-right: 5px;
}
}
@@ -33,6 +33,9 @@ import { element } from 'protractor';
import { FileType } from 'src/app/models/fileType';
import { ToastService } from 'src/app/services/toast.service';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { VoiceRecorder, VoiceRecorderPlugin, RecordingData, GenericResponse, CurrentRecordingStatus } from 'capacitor-voice-recorder';
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
import { DomSanitizer } from '@angular/platform-browser';
/*
import * as pdfjsLib from 'pdfjs-dist';
@@ -85,7 +88,16 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
pdfurl = "http://www.africau.edu/images/default/sample.pdf";
downloadFile: any;
showAvatar = false
showAvatar = false;
recording = false;
allowTyping = true;
storedFileNames = [];
lastAudioRecorded = '';
audioRecorded:any = "";
audioDownloaded:any = "";
durationDisplay = '';
duration = 0;
constructor(
public wsChatMethodsService: WsChatMethodsService,
@@ -108,6 +120,7 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
private CameraService: CameraService,
private toastService: ToastService,
private sanitiser: DomSanitizer,
) {
console.log('OnCONSTRUCTOR');
@@ -134,6 +147,8 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
this.showAvatar = true
}, 50)
this.deleteRecording();
}
ngOnInit() {
@@ -146,6 +161,9 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
}, 1000);
this.getChatMembers();
//this.getMessageDB();
VoiceRecorder.requestAudioRecordingPermission();
this.deleteRecording();
this.loadFiles();
}
@@ -229,12 +247,100 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
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 getFile(fileName?:any){
const audioFile = await Filesystem.readFile({
path: fileName,
directory: Directory.Data
})
const base64sound = audioFile.data;
const base64Response = await fetch(`data:audio/ogg;base64,${base64sound}`);
this.audioRecorded = base64Response.url;
}
async loadFiles() {
this.storage.get('fileName').then((fileName) => {
this.lastAudioRecorded = fileName;
})
this.storage.get('recordData').then((recordData) => {
console.log(recordData);
if(recordData.value.recordDataBase64.includes('data:audio')){
this.audioRecorded = this.sanitiser.bypassSecurityTrustResourceUrl(recordData.value.recordDataBase64);
}
else{
this.audioRecorded = this.sanitiser.bypassSecurityTrustResourceUrl(`data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}`);
}
});
}
startRecording() {
console.log('Recording');
if (this.recording) {
return;
}
this.recording = true;
VoiceRecorder.startRecording();
this.calculateDuration();
}
stopRecording() {
this.deleteRecording();
this.allowTyping = false;
console.log('Stop');
if (!this.recording) {
return;
}
this.recording = false;
VoiceRecorder.stopRecording().then(async (result: RecordingData) => {
console.log(result);
this.recording = false;
if (result.value && result.value.recordDataBase64) {
const recordData = result.value.recordDataBase64;
//console.log(recordData);
const fileName = new Date().getTime() + ".mp3";
//Save file
this.storage.set('fileName',fileName);
this.storage.set('recordData',result).then(() => {
console.log('Audio recorded saved');
})
}
})
setTimeout(async () => {
this.loadFiles();
}, 1000);
}
async deleteRecording(){
this.storage.remove('fileName');
this.storage.remove('recordData');
this.allowTyping = true;
this.lastAudioRecorded = '';
this.loadFiles();
}
ngOnDestroy() {
window.removeEventListener('scroll', this.scrollChangeCallback, true);
}
async getChatMembers() {
//return await this.chatService.getMembers(roomId).toPromise();
this.chatService.getAllUsers().subscribe(res => {
@@ -311,6 +417,43 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
this.wsChatMethodsService.getGroupRoom(this.roomId).send({})
}
async sendAudio(fileName) {
const roomId = this.roomId
this.storage.get('recordData').then((recordData) => {
console.log(recordData);
if(recordData.value.recordDataBase64.includes('data:audio')){
this.audioRecorded = recordData.value.recordDataBase64;
}
else{
this.audioRecorded = `data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}`;
}
});
//Converting base64 to blob
const base64Response = await fetch(this.audioRecorded);
const blob = await base64Response.blob();
const formData = new FormData();
formData.append("blobFile", blob);
this.wsChatMethodsService.getGroupRoom(roomId).send({
file: {
"type": "application/audio",
/* "guid": '', */
},
attachments: [{
"title": fileName ,
"title_link": this.audioRecorded,
"title_link_download": true,
"type": "file"
}],
temporaryData: formData
})
this.deleteRecording();
}
deleteMessage(msgId: string) {
const room = this.wsChatMethodsService.getGroupRoom(this.roomId)
this.alertService.confirmDeleteMessage(msgId, room);
@@ -842,10 +985,10 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
if (msg.file.type == "application/img") {
this.downloadFile = 'data:image/jpeg;base64,' + btoa(new Uint8Array(event.body).reduce((data, byte) => data + String.fromCharCode(byte), ''));
} else if (msg.file.type === 'application/pdf') {
this.downloadFile = event.body;
}
msg.attachments[0] = {
image_url: this.downloadFile,
name: msg.attachments[0].name,
@@ -12,7 +12,7 @@ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { MatButtonModule } from '@angular/material/button';
import {MatMenuModule} from '@angular/material/menu';
import { LettersAvatarModule } from "ngx-letters-avatar";
import { PdfViewerModule } from 'ng2-pdf-viewer';
import { PipesModule } from 'src/app/pipes/pipes.module';
@NgModule({
imports: [
@@ -24,7 +24,7 @@ import { PdfViewerModule } from 'ng2-pdf-viewer';
MatButtonModule,
MatMenuModule,
LettersAvatarModule,
PdfViewerModule
PipesModule,
],
exports: [MessagesPage],
+33 -26
View File
@@ -89,7 +89,7 @@
</ion-label> -->
</div>
<div *ngIf="msg.file.type != 'application/img'">
<div class="file">
<div *ngIf="msg.file.type != 'application/audio'" class="file add-attachment-bg-color">
<div (click)="openPreview(msg)" class="file-details add-ellipsis cursor-pointer" *ngIf="msg.file">
<span *ngIf="msg.file.type">
<fa-icon *ngIf="msg.file.type == 'application/pdf'" icon="file-pdf" class="pdf-icon"></fa-icon>
@@ -106,11 +106,14 @@
<ion-label class="file-title">{{file.title}}</ion-label>
</div>
</div>
<div class="file-details-optional">
<div class="audio-contentainer" *ngIf="msg.file.type == 'application/audio'">
<audio [src]="file.title_link|safehtml" preload="metadata" class="flex-grow-1" controls controlsList="nodownload noplaybackrate"></audio>
</div>
<div class="file-details-optional add-attachment-bg-color">
<ion-label *ngIf="msg.file">
<span *ngIf="file.description">{{file.description}}</span>
<span *ngIf="file.description && msg.file.type != 'application/webtrix'"></span>
<span *ngIf="msg.file.type != 'application/webtrix'">{{msg.displayType}}</span>
<span *ngIf="msg.file.type != 'application/webtrix' && msg.file.type != 'application/audio'">{{msg.displayType}}</span>
</ion-label>
<!-- <ion-label class="float-status-webtrix float-status-all">
<ion-icon *ngIf="!msg.offline && msg.viewed.length == 0" src="assets/images/check-double-solid.svg"></ion-icon>
@@ -209,14 +212,14 @@
fontFamily="Roboto"></ngx-letters-avatar>
está a escrever...
</div>
<div class="width-100 pl-20 pr-20">
<span *ngIf="!lastAudioRecorded">{{durationDisplay}}</span>
<audio [src]="audioRecorded" class="d-flex width-100 mt-10 mb-10" *ngIf="lastAudioRecorded" controls controlsList="nodownload noplaybackrate"></audio>
</div>
<div class="container width-100 d-flex">
<div>
<!-- <button class="btn-no-color" (click)="openSendMessageOptions()">
<ion-icon class="chat-icon-options" src="assets/images/icons-add.svg"></ion-icon>
</button> -->
<ion-fab horizontal="start" vertical="bottom" slot="fixed">
<ion-fab *ngIf="!recording && !lastAudioRecorded && allowTyping" horizontal="start" vertical="bottom" slot="fixed">
<ion-fab-button color="light" size="small">
<ion-icon name="add"></ion-icon>
</ion-fab-button>
@@ -238,31 +241,35 @@
</ion-fab-button>
</ion-fab-list>
</ion-fab>
<button *ngIf="recording || lastAudioRecorded || !allowTyping" class="btn-no-color" (click)="deleteRecording()">
<fa-icon class="icon-size-27" icon="trash"></fa-icon>
</button>
</div>
<div class="width-100">
<ion-item class="ion-no-padding type-message" lines="none">
<ion-textarea (keyup.enter)="sendMessage()" clearOnEdit="true" placeholder="Escrever uma mensagem"
class="message-input" rows="1" [(ngModel)]="wsChatMethodsService.getDmRoom(roomId).message"
(ionChange)="wsChatMethodsService.getDmRoom(roomId).sendTyping()"></ion-textarea>
<button hidden class="btn-no-color">
<ion-icon slot="end" src="assets/icon/icons-chat-mic.svg"></ion-icon>
<div *ngIf="!recording && !lastAudioRecorded" class="type-message">
<ion-textarea *ngIf="allowTyping" (keyup.enter)="sendMessage()" clearOnEdit="true" placeholder="Escrever uma mensagem" class="message-input" rows="1" [(ngModel)]="wsChatMethodsService.getDmRoom(roomId).message" (ionChange)="wsChatMethodsService.getDmRoom(roomId).sendTyping()"></ion-textarea>
</div>
<div *ngIf="recording" class="d-flex align-items-center justify-content-center">
<button (click)="stopRecording()" class="btn-no-color d-flex align-items-center justify-content-center">
<ion-icon class="icon-size-45" name="stop-circle-outline" color="danger"></ion-icon>
</button>
</ion-item>
</div>
</div>
<div>
<button *ngIf="wsChatMethodsService.getDmRoom(roomId).message" class="btn-no-color" (click)="sendMessage()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send"
src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send"
src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<button #recordbtn *ngIf="!wsChatMethodsService.getDmRoom(roomId).message && !lastAudioRecorded" (click)="startRecording()" class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/default/icons-chat-record-audio.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-record-audio.svg"></ion-icon>
</button>
<button *ngIf="!wsChatMethodsService.getDmRoom(roomId).message" class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send"
src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send"
src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<button *ngIf="wsChatMethodsService.getDmRoom(roomId).message" class="btn-no-color" (click)="sendMessage()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
</button>
<button *ngIf="!wsChatMethodsService.getDmRoom(roomId).message && lastAudioRecorded" class="btn-no-color" (click)="sendAudio(lastAudioRecorded)">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="chat-icon-send" src="assets/icon/theme/gov/icons-chat-send.svg"></ion-icon>
</button>
</div>
</div>
</ion-footer>
</ion-footer>
@@ -328,4 +328,4 @@ display: block;
.typing ngx-letters-avatar {
padding-right: 5px;
}
}
+151 -4
View File
@@ -19,7 +19,6 @@ import { ThemeService } from 'src/app/services/theme.service'
import { ViewMediaPage } from 'src/app/modals/view-media/view-media.page';
import { SqliteService } from 'src/app/services/sqlite.service';
import { StorageService } from 'src/app/services/storage.service';
import { Directory, Filesystem } from '@capacitor/filesystem';
import { ViewEventPage } from 'src/app/modals/view-event/view-event.page';
import { Storage } from '@ionic/storage';
import { WsChatMethodsService } from 'src/app/services/chat/ws-chat-methods.service'
@@ -34,6 +33,9 @@ import { ProcessesService } from 'src/app/services/processes.service';
import { FileToBase64Service } from 'src/app/services/file/file-to-base64.service';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { DocumentViewer, DocumentViewerOptions } from '@ionic-native/document-viewer';
import { VoiceRecorder, VoiceRecorderPlugin, RecordingData, GenericResponse, CurrentRecordingStatus } from 'capacitor-voice-recorder';
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
import { DomSanitizer } from '@angular/platform-browser';
const IMAGE_DIR = 'stored-images';
@Component({
@@ -78,7 +80,16 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
downloadFile: any;
massages: MessageService[] = []
showAvatar = true
showAvatar = true;
recording = false;
allowTyping = true;
storedFileNames = [];
lastAudioRecorded = '';
audioRecorded:any = "";
audioDownloaded:any = "";
durationDisplay = '';
duration = 0;
constructor(
public popoverController: PopoverController,
@@ -107,6 +118,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
private CameraService: CameraService,
private processesService: ProcessesService,
private fileToBase64Service: FileToBase64Service,
private sanitiser: DomSanitizer,
) {
this.loggedUser = authService.ValidatedUserChat['data'];
@@ -130,13 +142,17 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
this.showAvatar = true
}, 150)
this.deleteRecording()
}
ngOnInit() {
this.scrollToBottom();
this.getChatMembers();
VoiceRecorder.requestAudioRecordingPermission();
this.deleteRecording();
this.loadFiles();
}
@@ -209,6 +225,96 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
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 getFile(fileName?:any){
const audioFile = await Filesystem.readFile({
path: fileName,
directory: Directory.Data
})
const base64sound = audioFile.data;
const base64Response = await fetch(`data:audio/ogg;base64,${base64sound}`);
this.audioRecorded = base64Response.url;
}
async loadFiles() {
this.storage.get('fileName').then((fileName) => {
this.lastAudioRecorded = fileName;
})
this.storage.get('recordData').then((recordData) => {
console.log(recordData);
if(recordData.value.recordDataBase64.includes('data:audio')){
this.audioRecorded = this.sanitiser.bypassSecurityTrustResourceUrl(recordData.value.recordDataBase64);
}
else{
this.audioRecorded = this.sanitiser.bypassSecurityTrustResourceUrl(`data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}`);
}
});
}
startRecording() {
console.log('Recording');
if (this.recording) {
return;
}
this.recording = true;
VoiceRecorder.startRecording();
this.calculateDuration();
}
stopRecording() {
this.deleteRecording();
this.allowTyping = false;
console.log('Stop');
if (!this.recording) {
return;
}
this.recording = false;
VoiceRecorder.stopRecording().then(async (result: RecordingData) => {
console.log(result);
this.recording = false;
if (result.value && result.value.recordDataBase64) {
const recordData = result.value.recordDataBase64;
//console.log(recordData);
const fileName = new Date().getTime() + ".mp3";
//Save file
this.storage.set('fileName',fileName);
this.storage.set('recordData',result).then(() => {
console.log('Audio recorded saved');
})
}
})
setTimeout(async () => {
this.loadFiles();
}, 1000);
}
async deleteRecording(){
this.storage.remove('fileName');
this.storage.remove('recordData');
this.allowTyping = true;
this.lastAudioRecorded = '';
this.loadFiles();
}
ngOnDestroy() {
this.checktimeOut = false;
window.removeEventListener('scroll', this.scrollChangeCallback, true);
@@ -250,6 +356,47 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
this.wsChatMethodsService.getDmRoom(this.roomId).send({})
}
async sendAudio(fileName) {
const roomId = this.roomId
let audioFile;
this.storage.get('recordData').then((recordData) => {
console.log(recordData);
audioFile = recordData;
if(recordData.value.recordDataBase64.includes('data:audio')){
this.audioRecorded = recordData.value.recordDataBase64;
}
else{
this.audioRecorded = `data:${recordData.value.mimeType};base64,${recordData.value.recordDataBase64}`;
}
});
//Converting base64 to blob
const base64Response = await fetch(this.audioRecorded);
const blob = await base64Response.blob();
const formData = new FormData();
formData.append("blobFile", blob);
this.wsChatMethodsService.getDmRoom(roomId).send({
file: {
"type": "application/audio",
/* "guid": '', */
"msDuration":audioFile.value.msDuration,
"mimeType":audioFile.value.mimeType,
},
attachments: [{
"title": fileName ,
"title_link": this.audioRecorded,
"title_link_download": true,
"type": "audio"
}],
temporaryData: formData
})
this.deleteRecording();
}
deleteMessage(msgId: string) {
const room = this.wsChatMethodsService.getDmRoom(this.roomId)
this.alertService.confirmDeleteMessage(msgId, room);
@@ -749,7 +896,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
if (msg.file.type == "application/img") {
this.downloadFile = 'data:image/jpeg;base64,' + btoa(new Uint8Array(event.body).reduce((data, byte) => data + String.fromCharCode(byte), ''));
} else if (msg.file.type === 'application/pdf') {
this.downloadFile = event.body;
}
+8 -1
View File
@@ -22,13 +22,20 @@
<img *ngIf="ThemeService.currentTheme == 'gov' " src='assets/images/theme/gov/governoangola_A.png' alt='logo'>
<img *ngIf="ThemeService.currentTheme == 'tribunal' " src='assets/images/theme/tribunal/tribunal-constitucional.png' alt='logo'>
</div>
<div class="logo-description d-flex align-center justify-content-center">
<div *ngIf="ThemeService.currentTheme == 'gov'" class="logo-description d-flex align-center justify-content-center">
<div class="logo-description-content">
<p class="logo-description-text">Presidente da República</p>
<div class="add-line"></div>
<p class="logo-description-text tp-5">GABINETE DIGITAL</p>
</div>
</div>
<div *ngIf="ThemeService.currentTheme == 'default'" class="logo-description d-flex align-center justify-content-center">
<div class="logo-description-content">
<p class="logo-description-text color-white">Presidente da República</p>
<div class="add-line-white"></div>
<p class="logo-description-text tp-5 color-white">GABINETE DIGITAL</p>
</div>
</div>
</div>
<div title="Perfil" class="div-profile cursor-pointer" (click)="openProfile()">
+14
View File
@@ -49,12 +49,26 @@
margin-bottom: 2.5px !important;
padding: 0 !important;
}
.add-line-white{
width: 100%;
border-bottom: 1px solid #fff;
margin-bottom: 2.5px !important;
padding: 0 !important;
}
}
.color-white{
color: #fff !important;
}
.add-botton-border{
border-bottom: 1px solid #000;
}
.add-botton-border-white{
border-bottom: 1px solid #fff;
}
}
}