Git pull with conflits

This commit is contained in:
Eudes Inácio
2022-03-27 21:03:49 +01:00
80 changed files with 2615 additions and 1341 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ import { SqliteService } from 'src/app/services/sqlite.service';
import { BackgroundService } from 'src/app/services/background.service';
import { ScreenOrientation } from '@ionic-native/screen-orientation/ngx';
import { StorageService } from 'src/app/services/storage.service';
import { MessageModel } from './models/beast-orm'
const CUSTOM_DATE_FORMATS: NgxMatDateFormats = {
parse: {
+4 -10
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,7 +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 'node_modules_/@ionic-native/file/ngx';
import { MultipleDocumentsPicker } from '@awesome-cordova-plugins/multiple-document-picker/ngx';
@@ -57,13 +53,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';
@@ -112,10 +109,8 @@ import { FirebaseX } from '@ionic-native/firebase-x/ngx'; */
MatSelectModule,
MatDialogModule,
//
PdfViewerModule,
HammerModule,
CustomImageCachePageRoutingModule
CustomImageCachePageRoutingModule,
],
providers: [
@@ -127,7 +122,7 @@ import { FirebaseX } from '@ionic-native/firebase-x/ngx'; */
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
InAppBrowser,
Camera,
//File,
File,
WebView,
FilePath,
/* FCM,
@@ -136,7 +131,6 @@ import { FirebaseX } from '@ionic-native/firebase-x/ngx'; */
ChatService,
ScreenOrientation,
Network,
File,
SQLite,
CookieService,
ImagePicker,
@@ -120,7 +120,7 @@ export class ChatOptionsFeaturesPage implements OnInit {
}
this.chatService.sendMessage(body).subscribe(res=> {
console.log(res);
// console.log(res);
},(error) => {
});
@@ -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>
+44
View File
@@ -0,0 +1,44 @@
import { models } from 'beast-orm'
import { AESEncrypt } from '../services/aesencrypt.service'
const _AESEncrypt = new AESEncrypt()
const { ArrayField, JsonField} = models.indexedDB.fields
export class MessageModel extends models.Model {
channels = ArrayField()
mentions = ArrayField()
msg = models.CharField()
rid = models.CharField()
ts = models.CharField()
u = JsonField()
_id = models.CharField({unique:true})
_updatedAt = models.CharField()
messageSend = models.BooleanField()
offline = models.BooleanField()
viewed = ArrayField()
received = ArrayField()
localReference = models.CharField({blank:true})
attachments = ArrayField()
file = ArrayField()
}
export class DeleteMessageModel extends models.Model {
messageId = models.IntegerField()
rid = models.CharField()
ts = models.CharField()
u = JsonField()
needToReceiveBy = ArrayField()
}
models.register({
databaseName: 'chat-storage',
type: 'indexedDB',
version: 1,
models: [MessageModel, DeleteMessageModel]
})
+126 -121
View File
@@ -67,6 +67,7 @@ interface FirstUnread {
export interface Message {
customFields:any;
_id: string;
id: null | string;
rid: string;
msg: string;
ts: Ts;
@@ -81,7 +82,11 @@ export interface Message {
editedBy: EditedBy;
urls: any[];
temporaryData: object
localReference?: string
localReference?: string,
viewed: string[],
received: string[],
delate: boolean,
delateRequest: boolean
}
@@ -174,159 +179,159 @@ export interface chatHistory {
interface Ts {
$date: any;
$date: any;
}
interface U {
_id: string;
username: string;
name: string;
_id: string;
username: string;
name: string;
}
interface UpdatedAt {
$date: any;
$date: any;
}
interface Attachment {
ts: Date;
title_link_download: boolean;
ts: Date;
title_link_download: boolean;
}
export interface File {
type: string;
guid: string;
image_url: string;
subject: string;
start_date?: Date;
end_date?: Date;
venue: string;
id: string;
type: string;
guid: string;
image_url: string;
subject: string;
start_date?: Date;
end_date?: Date;
venue: string;
id: string;
}
interface EditedAt {
$date: number;
$date: number;
}
interface EditedBy {
_id: string;
username: string;
_id: string;
username: string;
}
interface Ts2 {
$date: number;
$date: number;
}
interface U2 {
_id: string;
username: string;
name: string;
_id: string;
username: string;
name: string;
}
interface UpdatedAt2 {
$date: number;
$date: number;
}
interface FirstUnread {
_id: string;
rid: string;
msg: string;
ts: Ts2;
u: U2;
_updatedAt: string;
mentions: any[];
channels: any[];
_id: string;
rid: string;
msg: string;
ts: Ts2;
u: U2;
_updatedAt: string;
mentions: any[];
channels: any[];
}
export interface Message {
customFields:any;
_id: string;
rid: string;
msg: string;
ts: Ts;
u: U;
t: string;
_updatedAt: '';
mentions: any[];
channels: any[];
attachments: Attachment[];
file: File;
editedAt: EditedAt;
editedBy: EditedBy;
urls: any[];
customFields:any;
_id: string;
rid: string;
msg: string;
ts: Ts;
u: U;
t: string;
_updatedAt: '';
mentions: any[];
channels: any[];
attachments: Attachment[];
file: File;
editedAt: EditedAt;
editedBy: EditedBy;
urls: any[];
}
export interface Lm {
$date: any;
$date: any;
}
export interface LastMessage {
_id: string;
rid: string;
msg: string;
ts: Ts;
u: U;
_updatedAt: UpdatedAt2;
mentions: any[];
channels: any[];
file: File;
attachments: Attachment[];
_id: string;
rid: string;
msg: string;
ts: Ts;
u: U;
_updatedAt: UpdatedAt2;
mentions: any[];
channels: any[];
file: File;
attachments: Attachment[];
}
export interface CustomFields {
}
export interface Update {
_id: string;
t: string;
usernames: string[];
usersCount: number;
uids: string[];
default: boolean;
ro: boolean;
sysMes: boolean;
_updatedAt: UpdatedAt;
lm: Lm;
lastMessage: LastMessage;
name: string;
fname: string;
u: U2;
customFields: CustomFields;
_id: string;
t: string;
usernames: string[];
usersCount: number;
uids: string[];
default: boolean;
ro: boolean;
sysMes: boolean;
_updatedAt: UpdatedAt;
lm: Lm;
lastMessage: LastMessage;
name: string;
fname: string;
u: U2;
customFields: CustomFields;
}
export interface DeletedAt {
$date: any;
$date: any;
}
export interface Remove {
_id: string;
_deletedAt: DeletedAt;
_id: string;
_deletedAt: DeletedAt;
}
export interface Result {
update: Update[];
remove: Remove[];
update: Update[];
remove: Remove[];
}
export interface Rooms {
msg: string;
id: string;
result: Result;
msg: string;
id: string;
result: Result;
}
export interface ChatMessage {
msg: string;
id: string;
result: Message
msg: string;
id: string;
result: Message
}
@@ -334,44 +339,44 @@ export interface ChatMessage {
export interface chatHistory {
msg: string;
id: string;
result: {
messages: Message[];
firstUnread: FirstUnread;
unreadNotLoaded: number;
};
msg: string;
id: string;
result: {
messages: Message[];
firstUnread: FirstUnread;
unreadNotLoaded: number;
};
}
export interface chatUser {
_id: string;
createdAt: Date;
emails: {
address: string;
verified: boolean;
}
type: string;
status: string;
active: boolean;
_updatedAt: Date;
roles: string[];
name: string;
lastLogin: Date;
statusConnection: string;
utcOffset: number;
username: string;
__rooms: string[];
requirePasswordChange?: boolean;
settings: {
preferences: {
language: string;
};
};
nickname: string;
statusText: string;
banners: any;
statusDefault: string;
language: string;
avatarOrigin: string;
avatarETag?: any;
_id: string;
createdAt: Date;
emails: {
address: string;
verified: boolean;
}
type: string;
status: 'online' | 'offline' | 'away' | 'busy' ;
active: boolean;
_updatedAt: Date;
roles: string[];
name: string;
lastLogin: Date;
statusConnection: string;
utcOffset: number;
username: string;
__rooms: string[];
requirePasswordChange?: boolean;
settings: {
preferences: {
language: string;
};
};
nickname: string;
statusText: string;
banners: any;
statusDefault: string;
language: string;
avatarOrigin: string;
avatarETag?: any;
}
+54 -4
View File
@@ -1,4 +1,54 @@
export interface Message {
author: string;
message: string;
}
export interface IncomingChatMessage {
msg: string;
collection: string;
id: string;
fields: {
args: {
_id: string;
rid: string;
localReference: string;
msg: string;
file?: any;
ts: {
$date: number;
};
u: {
_id: string;
username: string;
name: string;
};
_updatedAt: {
$date: number;
};
mentions: any[];
channels: any[];
} [];
}
eventName: string;
};
export interface ChatMessageInterface {
_id: string;
rid: string;
localReference: string;
msg: string;
file?: any;
ts: number;
u: {
_id: string;
username: string;
name: string;
};
_updatedAt: number;
mentions: any[];
channels: any[];
};
export interface falseTypingMethod{
method: 'viewMessage' | 'deleteMessage'
params: object
}
+12 -10
View File
@@ -69,16 +69,18 @@
<div *ngIf="room.lastMessage" class="item-description" [class.item-description-active]="room.id == idSelected">
<ion-label *ngIf="room.lastMessage && room.otherUserType == false">{{room.lastMessage.msg}}</ion-label>
<ion-label *ngIf="room.otherUserType == true">A escrever...</ion-label>
<ion-label *ngIf="room.otherUserType == true">está escrever...</ion-label>
<!-- <ion-label *ngIf="room.lastMessage.file">
<fa-icon *ngIf="room.lastMessage.file.type != 'application/meeting'" icon="file-alt" class="file-icon" [class.set-active-item-font-to-white]="room.id == idSelected"></fa-icon>
</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>
@@ -131,12 +132,13 @@
</div>
<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}} A 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 *ngIf="group.otherUserType == true">{{group.userThatIsTyping}} está escrever ...</div>
<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">
-5
View File
@@ -20,7 +20,6 @@ import { NewGroupPage } from './new-group/new-group.page';
import { Storage } from '@ionic/storage';
import { EditGroupPage } from 'src/app/shared/chat/edit-group/edit-group.page';
import * as Rx from "rxjs/Rx";
import { Message } from 'src/app/models/message.model';
import { Observable, Subject } from "rxjs/Rx";
import { NavigationStart, NavigationEnd, Router } from '@angular/router';
import { EventPerson } from 'src/app/models/eventperson.model';
@@ -143,10 +142,6 @@ export class ChatPage implements OnInit {
ngOnInit() {
console.log('Rooms INDIVIDUAIS',this.wsChatMethodsService._dm);
console.log(' ROOMS GROUP',this.wsChatMethodsService._group);
this.segment = "Contactos";
this.authService.userData$.subscribe((res: any) => {
@@ -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 */
@@ -63,7 +63,8 @@
<span class="time">{{msg.duration}}</span>
</div>
<div class="message">
<ion-label>{{msg.msg}}</ion-label>
<ion-label *ngIf="msg.delate == false">{{msg.msg}}</ion-label>
<ion-label *ngIf="msg.delate == true">{{msg.msg}}</ion-label>
{{last ? scrollToBottom() : ''}}
</div>
</div>
@@ -92,7 +93,7 @@
</div>
</div> -->
<div *ngIf="msg.file">
<div *ngIf="msg.file && msg.delate == false">
<div class='message-container incoming-{{msg.u.username!=loggedUser.me.username}}' class='message-container incoming-{{msg.u.username!=loggedUser.me.username}}' *ngIf="msg.t != 'r' && msg.t != 'ul' && msg.t != 'ru' && msg.file.type != 'application/meeting'" (press)="handlePress(msg._id)">
<div class="title">
<ion-label>{{msg.u.name ?? ""}}</ion-label>
@@ -106,7 +107,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 +118,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>
@@ -137,7 +141,9 @@
<ion-label class="info-meeting-medium"><ion-icon></ion-icon><ion-icon name="location-outline"></ion-icon> {{msg.file.venue}}</ion-label><br />
</div>
</div>
<div *ngIf="msg.file && msg.delate == true">
Apagou a mensagem
</div>
<div *ngIf="msg.t == 'r'" class="info-text">
<ion-label>Alterou o nome do grupo para "{{msg.msg.split('-').join(' ')}}"</ion-label><br />
@@ -187,28 +193,51 @@
</ion-content>
<ion-footer>
<div class="typing" *ngIf="wsChatMethodsService.getGroupRoom(roomId).otherUserType == true" >A escrever...</div>
<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 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;
@@ -357,5 +357,5 @@
.typing ngx-letters-avatar {
padding-left: 10px;
}
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,16 @@ 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;
showAvatar = true;
constructor(
private menu: MenuController,
private modalController: ModalController,
@@ -97,8 +110,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;
@@ -117,6 +130,14 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
this.scrollToBottomClicked()
}, 50)
this.showAvatar = false
setTimeout(() => {
this.scrollToBottomClicked()
this.showAvatar = true
}, 150)
}
ngOnInit() {
@@ -132,6 +153,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 +168,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 +209,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 +380,7 @@ export class GroupMessagesPage implements OnInit, AfterViewInit, OnDestroy {
close() {
this.modalController.dismiss();
this.deleteRecording();
}
doRefresh(ev: any) {
@@ -311,13 +424,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,
+51 -44
View File
@@ -17,6 +17,8 @@
<!-- <span><ion-icon class="{{users.status}}" name="ellipse"></ion-icon></span> -->
</div>
</div>
<!-- <div (click)="wsChatMethodsService.getDmRoom(roomId).deleteAll()" >delete all</div> -->
<div class="middle-container-options" *ngIf="showMessageOptions">
<fa-icon (click)="deleteMessage(selectedMsgId)" icon="trash" class="middle-container-options-icons"></fa-icon>
<!-- <ion-icon name="trash"></ion-icon> -->
@@ -56,24 +58,14 @@
<span class="time">{{msg.duration}}</span>
</div>
<div>
<ion-label>{{msg.msg}}</ion-label>
{{last ? scrollToBottom() : ''}}
</div>
</div>
<div (press)="handlePress(msg._id)" class='message-container incoming-{{msg.u.username!=loggedUser.me.username}}' (click)="openPreview(msg)" *ngIf="msg.msg !=''">
<div class="title">
<ion-label>{{msg.u.name}}</ion-label>
<span class="time">{{msg.duration}}</span>
</div>
<div>
<ion-label>{{msg.msg}}</ion-label>
<ion-label *ngIf="msg.delate == false">{{msg.msg}}</ion-label>
<ion-label *ngIf="msg.delate == true">{{msg.msg}}</ion-label>
{{last ? scrollToBottom() : ''}}
</div>
</div>
<div *ngIf="msg.file">
<div *ngIf="msg.file && msg.delate == false">
<div (press)="handlePress(msg._id)" class='message-container incoming-{{msg.u.username!=loggedUser.me.username}}' *ngIf="msg.file.type != 'application/meeting'">
<div class="title">
<ion-label>{{msg.u.name}}</ion-label>
@@ -87,8 +79,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 +90,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>
@@ -120,7 +115,7 @@
</div>
<div *ngIf="msg.file">
<div *ngIf="msg.file && msg.delate == false">
<div *ngIf="msg.file.type == 'application/meeting'" class="info-meeting">
<ion-label class="info-meeting-small">{{msg.u.name}} criou esta reunião</ion-label><br />
<button (click)="goToEvent(msg.file.id)" class="btn-no-color info-meeting-normal"><ion-label class="info-meeting-normal">{{msg.file.subject}}</ion-label></button><br />
@@ -129,6 +124,10 @@
</div>
{{last ? scrollToBottom() : ''}}
</div>
<div *ngIf="msg.file && msg.delate == true">
Apagou a mensagem
</div>
</div>
</div>
<ion-fab horizontal="end" vertical="bottom" slot="fixed">
@@ -162,44 +161,52 @@
</ion-toolbar>
</ion-footer> -->
<ion-footer>
<div class="typing" *ngIf="wsChatMethodsService.getDmRoom(roomId).otherUserType == true" >A escrever...</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> -->
<ion-footer >
<div class="typing" *ngIf="wsChatMethodsService.getDmRoom(roomId).otherUserType == true">
<ngx-letters-avatar
[avatarName]= "wsChatMethodsService.getGroupRoom(roomId).name"
[width]="30"
[circular]="true"
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)="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>
+15 -7
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{
@@ -318,5 +315,16 @@ display: block;
.typing ngx-letters-avatar {
padding-left: 10px;
}
padding-right: 5px;
}
button{
padding: 0px;
border: 0px;
}
button::-moz-focus-inner {
padding: 0px;
border: 0px;
}
+111 -49
View File
@@ -88,7 +88,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;
@@ -101,6 +105,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
fileSelected?: Blob;
pdfUrl?:string;
base64File: string;
downloadProgess: number;
constructor(
public popoverController: PopoverController,
@@ -129,6 +134,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
private multipleDocumentsPicker: MultipleDocumentsPicker,
private sant: DomSanitizer,
//private file: File
private sanitiser: DomSanitizer,
) {
this.loggedUser = authService.ValidatedUserChat['data'];
this.roomId = this.navParams.get('roomId');
@@ -140,6 +146,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)
@@ -154,15 +162,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() {
@@ -176,11 +183,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();
@@ -203,61 +213,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;
@@ -269,19 +291,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);
}
@@ -291,6 +302,7 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
close() {
this.modalController.dismiss();
this.deleteRecording();
}
load() {
@@ -367,6 +379,44 @@ 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();
}
viewDocument(msg: any, url?: string) {
console.log(msg)
if (msg.attachments.type == "application/webtrix") {
@@ -405,6 +455,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
}
@@ -464,8 +521,6 @@ export class MessagesPage implements OnInit, AfterViewInit, OnDestroy {
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;
});
}
@@ -956,6 +1011,11 @@ fileToBase64 = (blob,filename) => {
}
/* testeDownload(msg: MessageService) {
this.downloadFile = "";
this.AttachmentsService.downloadFileAndStore(msg.file.guid);
} */
downloadFileMsg(msg: MessageService) {
console.log('FILE TYPE', msg.file.type)
@@ -966,7 +1026,8 @@ fileToBase64 = (blob,filename) => {
var name = msg.file.guid;
if (event.type === HttpEventType.DownloadProgress) {
//this.downloadProgess = Math.round((100 * event.loaded) / event.total);
this.downloadProgess = Math.round((100 * event.loaded) / event.total);
console.log(this.downloadProgess)
console.log('FILE TYPE 33', msg.file.type)
} else if (event.type === HttpEventType.Response) {
if (msg.file.type == "application/img") {
@@ -1009,7 +1070,8 @@ fileToBase64 = (blob,filename) => {
console.log(msg);
if (!msg.attachments[0].image_url || msg.attachments[0].image_url === null || msg.attachments[0].image_url === '') {
this.downloadFileMsg(msg)
this.downloadFile(msg)
//this.testDownlod(msg)
} else {
const modal = await this.modalController.create({
-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]
@@ -2,107 +2,103 @@
<app-header></app-header>
</ion-header>
<ion-content id="main-content" class="container-wrapper">
<div class="main-content d-flex flex-column height-100" *ngIf="loadedEvent" >
<div class="pl-20 pr-20 text-center d-flex justify-center align-center" style="width:100%; height: 30px; background-color: var(--Event-approve-header-color); border-top-right-radius: 25px;border-top-left-radius: 25px;">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="right-icons font-28" src="assets/images/icons-received-event.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov'" class="right-icons font-28" src="assets/images/theme/gov/icons-received-event.svg"></ion-icon>
<!-- <ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="right-icons" src="assets/images/theme/gov/icons-received-event-selected.svg"></ion-icon> -->
<ion-icon *ngIf="ThemeService.currentTheme == 'tribunal' " class="right-icons font-28" src="assets/images/theme/tribunal/icons-received-event.svg"></ion-icon>
<div style="color: white;">
Evento Pendente de Aprovação
<div class="main-content height-100" *ngIf="loadedEvent" >
<div class="d-flex width-100">
<div class="pl-20 pr-20 text-center d-flex justify-center align-center" style="width:100%; height: 30px; background-color: var(--Event-approve-header-color); border-top-right-radius: 25px;border-top-left-radius: 25px;">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="right-icons font-28" src="assets/images/icons-received-event.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov'" class="right-icons font-28" src="assets/images/theme/gov/icons-received-event.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'tribunal' " class="right-icons font-28" src="assets/images/theme/tribunal/icons-received-event.svg"></ion-icon>
<div style="color: white;">
Evento Pendente de Aprovação
</div>
</div>
</div>
<div class="content d-flex flex-column width-100" *ngIf="loadedEvent.workflowInstanceDataFields">
<div class="header-content width-100 d-flex justify-space-between">
<div (click)="goBack()" class="header-icon-left cursor-pointer">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-arrow-arrow-left.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " slot="end" src='assets/images/theme/gov/icons-calendar-arrow-left.svg'></ion-icon>
</div>
<div class="header-title flex-grow-1 cursor-pointer" *ngIf="loadedEvent.workflowInstanceDataFields.Subject">
<label>{{loadedEvent.workflowInstanceDataFields.Subject}}</label>
</div>
<div (click)="editar(loadedEvent.serialNumber)" class="header-icon-right display-none-true">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-edit.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="edit" slot="end" src="assets/images/theme/gov/icons-edit.svg" ></ion-icon>
</div>
<div class="menu-ptions">
<button class="btn-no-color" (click)="openOptions(loadedEvent)">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-menu.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " src="assets/images/theme/gov/icons-menu.svg"></ion-icon>
</button>
</div>
</div>
<div class="upper-content d-flex flex-column">
<div class="content-location">
<p>
<span class="location">{{loadedEvent.workflowInstanceDataFields.Location}}</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Oficial MDGPR' " style="background-color: #ffb703;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Pessoal MDGPR' " style="background-color: #f05d5e;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Oficial PR' " style="background-color: #99e47b;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Pessoal PR' " style="background-color: #958bfc;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
</p>
</div>
<div class="content-details">
<ion-label>
<p>{{customDate}}</p>
<p *ngIf="toDateString(loadedEvent.workflowInstanceDataFields.StartDate) == toDateString(loadedEvent.workflowInstanceDataFields.EndDate)">das {{loadedEvent.workflowInstanceDataFields.StartDate | date: 'HH:mm'}} às {{loadedEvent.workflowInstanceDataFields.EndDate | date: 'HH:mm'}}</p>
<p *ngIf="toDateString(loadedEvent.workflowInstanceDataFields.StartDate) != toDateString(loadedEvent.workflowInstanceDataFields.EndDate)">{{loadedEvent.workflowInstanceDataFields.StartDate | date: 'd/M/yy' }} - {{ loadedEvent.workflowInstanceDataFields.StartDate | date: 'dd/M/yy'}} </p>
<p *ngIf="loadedEvent.workflowInstanceDataFields.OccurrenceType == '-1'">(Não se repete)</p>
<p *ngIf="loadedEvent.workflowInstanceDataFields.OccurrenceType != '-1'">Repete</p>
</ion-label>
</div>
</div>
<div class="line"></div>
<div class="overflow-y-auto">
<div class="middle-content">
<div *ngIf="loadedEvent.workflowInstanceDataFields.ParticipantsList">
<h5>Intervenientes</h5>
<div *ngFor="let att of loadedEvent.workflowInstanceDataFields.ParticipantsList">
<ion-label>{{att.Name}}</ion-label>
</div>
<div class="line"></div>
<div class="d-flex width-100">
<div class="content d-flex flex-column width-100" *ngIf="loadedEvent.workflowInstanceDataFields">
<div class="header-content width-100 d-flex justify-space-between">
<div (click)="goBack()" class="header-icon-left cursor-pointer">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-arrow-arrow-left.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " slot="end" src='assets/images/theme/gov/icons-calendar-arrow-left.svg'></ion-icon>
</div>
<div *ngIf="loadedEvent.workflowInstanceDataFields.Body">
<h5>Detalhes</h5>
<ion-item lines="none" class="ion-no-margin ion-no-padding">
<p [innerHTML]="loadedEvent.workflowInstanceDataFields.Body"></p>
</ion-item>
<div class="line"></div>
<div class="header-title flex-grow-1 cursor-pointer" *ngIf="loadedEvent.workflowInstanceDataFields.Subject">
<label>{{loadedEvent.workflowInstanceDataFields.Subject}}</label>
</div>
<div (click)="editar(loadedEvent.serialNumber)" class="header-icon-right display-none-true">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-edit.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="edit" slot="end" src="assets/images/theme/gov/icons-edit.svg" ></ion-icon>
</div>
<div class="menu-ptions">
<button class="btn-no-color" (click)="openOptions(loadedEvent)">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-menu.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " src="assets/images/theme/gov/icons-menu.svg"></ion-icon>
</button>
</div>
</div>
<div class="upper-content d-flex flex-column">
<div class="content-location">
<p>
<span class="location">{{loadedEvent.workflowInstanceDataFields.Location}}</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Oficial MDGPR' " style="background-color: #ffb703;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Pessoal MDGPR' " style="background-color: #f05d5e;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<div *ngIf="loadedEvent.Documents" class="bottom-content width-100">
<ion-list>
<h5>Documentos Anexados</h5>
<ion-item class="ion-no-margin ion-no-padding cursor-pointer"
*ngFor="let attachment of loadedEvent.Documents"
(click)="viewDocument(attachment.DocId, attachment)">
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Oficial PR' " style="background-color: #99e47b;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Pessoal PR' " style="background-color: #958bfc;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
</p>
</div>
<div class="content-details">
<ion-label>
<p *ngIf="attachment.Assunto" class="attach-title-item">{{ attachment.Assunto }}<span class="span-right color-red btn-size"><ion-icon hidden name="close"></ion-icon></span></p>
<p *ngIf="!attachment.Assunto" class="attach-title-item">{{ attachment.DocNumber }}<span class="span-right color-red btn-size"><ion-icon hidden name="close"></ion-icon></span></p>
<p><span class="span-left">{{attachment.Sender}}</span><span class="span-right">{{ attachment.DocDate | date: 'dd/MM/yy' }}</span></p>
<p>{{customDate}}</p>
<p *ngIf="toDateString(loadedEvent.workflowInstanceDataFields.StartDate) == toDateString(loadedEvent.workflowInstanceDataFields.EndDate)">das {{loadedEvent.workflowInstanceDataFields.StartDate | date: 'HH:mm'}} às {{loadedEvent.workflowInstanceDataFields.EndDate | date: 'HH:mm'}}</p>
<p *ngIf="toDateString(loadedEvent.workflowInstanceDataFields.StartDate) != toDateString(loadedEvent.workflowInstanceDataFields.EndDate)">{{loadedEvent.workflowInstanceDataFields.StartDate | date: 'd/M/yy' }} - {{ loadedEvent.workflowInstanceDataFields.StartDate | date: 'dd/M/yy'}} </p>
<p *ngIf="!loadedEvent.workflowInstanceDataFields.IsRecurring">(Não se repete)</p>
<p *ngIf="loadedEvent.workflowInstanceDataFields.IsRecurring">Repete</p>
</ion-label>
</ion-item>
</ion-list>
</div>
</div>
<div class="line"></div>
<div class="overflow-y-auto">
<div class="middle-content">
<div *ngIf="loadedEvent.workflowInstanceDataFields.ParticipantsList">
<h5>Intervenientes</h5>
<div *ngFor="let att of loadedEvent.workflowInstanceDataFields.ParticipantsList">
<ion-label>{{att.Name}}</ion-label>
</div>
<div class="line"></div>
</div>
<div *ngIf="loadedEvent.workflowInstanceDataFields.Body">
<h5>Detalhes</h5>
<ion-item lines="none" class="ion-no-margin ion-no-padding">
<p [innerHTML]="loadedEvent.workflowInstanceDataFields.Body"></p>
</ion-item>
<div class="line"></div>
</div>
</div>
<div *ngIf="loadedEvent.Documents" class="bottom-content width-100">
<ion-list>
<h5>Documentos Anexados</h5>
<ion-item class="ion-no-margin ion-no-padding cursor-pointer"
*ngFor="let attachment of loadedEvent.Documents"
(click)="viewDocument(attachment.DocId, attachment)">
<ion-label>
<p *ngIf="attachment.Assunto" class="attach-title-item">{{ attachment.Assunto }}<span class="span-right color-red btn-size"><ion-icon hidden name="close"></ion-icon></span></p>
<p *ngIf="!attachment.Assunto" class="attach-title-item">{{ attachment.DocNumber }}<span class="span-right color-red btn-size"><ion-icon hidden name="close"></ion-icon></span></p>
<p><span class="span-left">{{attachment.Sender}}</span><span class="span-right">{{ attachment.DocDate | date: 'dd/MM/yy' }}</span></p>
</ion-label>
</ion-item>
</ion-list>
</div>
</div>
</div>
</div>
<div *ngIf="loadedEvent" class="aside-right flex-column height-100">
<div class="aside-buttons">
@@ -114,6 +110,7 @@
<button (click)="rejeitar(loadedEvent.serialNumber)" full class="btn-delete" shape="round" >Rejeitar</button>
</div>
</div>
</div>
</div>
</ion-content>
@@ -17,7 +17,7 @@
<div class="title">
<ion-label>Gabinete Digital</ion-label>
</div>
<button *ngIf="hideRefreshBtn" class="btn-no-color btn-refresh" (click)="doRefresh($event)">
<button title="Atualizar" *ngIf="hideRefreshBtn" class="btn-no-color btn-refresh" (click)="doRefresh($event)">
<ion-icon class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
<div *ngIf="!hideRefreshBtn" class="title-icons">
@@ -25,11 +25,11 @@
<ion-label class="title ">Acções Presidenciais</ion-label>
</div>
<div class="div-icon">
<button class="btn-no-color" (click)="AddPublicationFolder()">
<button title="Adicionar nova ação presidencial" class="btn-no-color" (click)="AddPublicationFolder()">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " slot="icon-only" src='assets/images/icons-add.svg'></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " slot="icon-only" src='assets/images/theme/gov/icons-add.svg'></ion-icon>
</button>
<button *ngIf="hideRefreshBtn" class="btn-no-color" (click)="refreshing()">
<button title="Atualizar" *ngIf="hideRefreshBtn" class="btn-no-color" (click)="refreshing()">
<ion-icon slot="icon-only" class="title-icon font-awesome" name="reload-circle" title="Actualizar"></ion-icon>
</button>
</div>
+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);
}
}
-1
View File
@@ -44,7 +44,6 @@ export class AESEncrypt {
//Decrypting the string contained in cipherParams using the PBKDF2 key
var decrypted = CryptoJS.AES.decrypt(cipherParams, key128Bits1000Iterations, { mode: CryptoJS.mode.CBC, iv: iv, padding: CryptoJS.pad.Pkcs7 });
console.log('AES decrypt',decrypted.toString(CryptoJS.enc.Utf8));
return decrypted.toString(CryptoJS.enc.Utf8);
}
+28 -1
View File
@@ -5,6 +5,8 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { LoginUserRespose } from '../models/user.model';
import { SessionStore } from '../store/session.service';
import { File } from '@ionic-native/file/ngx';
import { Platform } from '@ionic/angular';
@Injectable({
providedIn: 'root'
@@ -14,9 +16,10 @@ export class AttachmentsService {
loggeduser: LoginUserRespose;
headers: HttpHeaders;
constructor(private http: HttpClient) {
constructor(private http: HttpClient, private platform: Platform, private file: File) {
this.loggeduser = SessionStore.user
this.headers = new HttpHeaders();
this.headers = this.headers.set('Authorization', SessionStore.user.BasicAuthKey);
}
@@ -60,6 +63,29 @@ export class AttachmentsService {
})
}
downloadFileAndStore(guid:any) {
var name = new Date().getTime();
const downloadPath = (
this.platform.is('android')
) ? this.file.externalDataDirectory : this.file.documentsDirectory;
let vm = this;
/** HttpClient - @angular/common/http */
this.http.get(
environment.apiURL +'objectserver/streamfiles?path='+guid,
{
responseType: 'arraybuffer',
}
).subscribe((fileBlob: Uint8Array) => {
/** File - @ionic-native/file/ngx */
vm.file.writeFile(downloadPath, "YourFileName.pdf", fileBlob, {replace: true});
console.log(downloadPath +"/"+"YourFileName.pdf")
console.log(fileBlob)
});
}
getAttachmentsBySerial(serialNumber: string): Observable<Attachment[]>{
let geturl = environment.apiURL + 'attachments/GetAttachments';
@@ -71,6 +97,7 @@ export class AttachmentsService {
headers: this.headers,
params: params
};
return this.http.get<Attachment[]>(`${geturl}`, options);
}
+15 -10
View File
@@ -51,7 +51,7 @@ export class AuthService {
if (SessionStore.exist) {
this.ValidatedUser = SessionStore.user
console.log('login', SessionStore.user.RochetChatUser, SessionStore.user.Password)
// console.log('login', SessionStore.user.RochetChatUser, SessionStore.user.Password)
this.loginToChatWs()
}
@@ -138,7 +138,7 @@ export class AuthService {
this.autoLoginChat(expirationDate.getTime() - date);
}
async autoLoginChat(expirationDate:number){
async autoLoginChat(expirationDate:number) {
setTimeout(()=>{
this.loginChat();
}, expirationDate)
@@ -148,14 +148,18 @@ export class AuthService {
setTimeout(()=>{
this.WsChatService.connect();
this.WsChatService.login().then((message) => {
console.log('rocket chat login successfully', message)
this.WsChatService.login().then((message: any) => {
SessionStore.user.RochetChatUserId = message.result.id
SessionStore.save()
this.WsChatService.setStatus('online')
}).catch((message)=>{
}).catch((message) => {
console.log('rocket chat login failed', message)
})
// before sending a message with a attachment
this.NfService.beforeSendAttachment = async (message: MessageService, room?: RoomService) => {
if(message.hasFile) {
@@ -165,14 +169,14 @@ export class AuthService {
try {
let guid: any = await this.AttachmentsService.uploadFile(formData).toPromise()
message.file.guid = guid.path
await this.storage.set(guid.path, message.file.image_url).then(() => {
console.log('add picture to chat IMAGE SAVED')
message.getFileFromDb()
});
// await this.storage.set(guid.path, message.file.image_url).then(() => {
// console.log('add picture to chat IMAGE SAVED')
// // message.getFileFromDb()
// });
return true
} catch(e) {
console.log(e)
console.log('failed to upload to server', e)
return false
}
@@ -225,6 +229,7 @@ export class AuthService {
return false
}
};
}, 1)
}
+2 -3
View File
@@ -6,8 +6,7 @@ import { StorageService } from './storage.service';
import { HttpClient, HttpHeaderResponse } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { Storage } from '@ionic/storage';
import { Message } from 'src/app/models/message.model';
import { Observable, Subject } from "rxjs/Rx";
@Injectable({
providedIn: 'root'
@@ -120,7 +119,7 @@ export class ChatService {
return this.http.get(environment.apiChatUrl+'im.history', opts);
}
sendMessage(body:any){
sendMessage(body:any) {
let opts = {
headers: this.headers,
}
+29 -2
View File
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { ChatService } from '../chat.service';
import { v4 as uuidv4 } from 'uuid'
@Injectable({
providedIn: 'root'
@@ -17,7 +18,7 @@ export class ChatMethodsService {
{
"rid": roomId,
"msg":"",
"file":{
"file": {
"type": "application/meeting",
"subject": data.subject,
"start_date": data.start,
@@ -29,4 +30,30 @@ export class ChatMethodsService {
}
this.chatService.sendMessage(body).subscribe(res=> {});
}
}
send({roomId, msg, attachments = null, file = null, localReference = null}) {
let body = {
"message":
{
rid: roomId,
localReference: localReference,
msg: msg,
attachments,
file
}
}
return this.chatService.sendMessage(body)
}
deleteMessage(body) {
return this.chatService.deleteMessage(body)
}
}
+153 -131
View File
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root'
@@ -10,161 +11,182 @@ export class ChatStorageService {
private storage: Storage,
) { }
/**
/**
* @description delete message in the DB. get all messages, delete then corresponding message and update the store
* @param id message ID
*/
private deleteMessageFromDb(messageId, roomId) {
this.storage.get('chatmsg' + roomId).then((messages: any = []) => {
async deleteMessageFromDb(messageId, roomId) {
if (environment.chatOffline) {
await this.storage.get('chatmsg' + roomId).then(async(messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
}
messages.forEach((message, index) => {
await messages.forEach( async (message, index) => {
if(message._id == messageId) {
messages.splice(index, 1)
}
})
this.storage.set('chatmsg' + roomId, messages).then((value) => {
console.log('MSG SAVED ON STORAGE', value)
// console.log('MSG SAVED ON STORAGE', value)
});
})
}
}
async updateMessageDB(ChatMessage, roomId, identificator) {
if (environment.chatOffline) {
await this.storage.get('chatmsg' + roomId).then(async(messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
}
let index;
const find = messages.find((message, _index)=> {
if(message?.localReference == ChatMessage?.localReference ||
message?._id == ChatMessage?._id) {
index = _index
return true
}
return false
})
if(find) {
messages[index] = Object.assign(messages[index], ChatMessage)
await this.storage.set('chatmsg' + roomId, messages)
} else {
// console.log('failed to update', identificator, ':',ChatMessage)
}
})
}
}
getMsgFromDB() {
async updateChat(history, roomId, identificator = '_id') {
if (environment.chatOffline) {
await this.storage.get('chatmsg' + roomId).then(async(messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
}
/* this.storage.get('chatmsg' + this.id).then((message) => {
console.log('ALL MESSAGE WEB', message)
message.forEach(message => {
if (message.file) {
if (message.file.guid) {
this.storage.get(message.file.guid).then((image) => {
//console.log('IMAGE FROM STORAGE', image)
message.file.image_url = image
});
}
}
let mmessage = this.fix_updatedAt(message)
console.log('FROM DB WEB', mmessage)
const wewMessage = new MessageService(this.storage)
wewMessage.setData(mmessage)
this.messages.push(wewMessage)
console.log('loadHistory 222', this.messages)
});
}) */
}
history.forEach( async(ChatMessage)=>{
let index;
const find = messages.find((message, _index)=> {
if(message[identificator]) {
if(message[identificator] == ChatMessage[identificator]) {
index = _index
return true
}
}
return false
})
if(find) {
messages[index] = Object.assign(messages[index], ChatMessage)
// if(messages[index].msg.includes('***********')) {
// console.log('storage update')
// console.log(JSON.stringify(messages[index]))
// console.log(JSON.stringify(ChatMessage))
// }
await this.storage.set('chatmsg' + roomId, messages)
} else {
console.log('failed to update', identificator)
}
})
async transformData(res) {
// this.mgsArray = [];
// res.forEach(async element => {
// if (element.file) {
// if (element.file.guid) {
// await this.storage.get(element.file.guid).then((image) => {
// let chatmsg = {
// _id: element._id,
// attachments: element.attachments,
// channels: element.channels,
// file: {
// guid: element.file.guid,
// image_url: image,
// type: element.file.type
// },
// mentions: element.mentions,
// msg: element.msg,
// rid: element.rid,
// ts: element.ts,
// u: element.u,
// _updatedAt: element._updatedAt,
// }
// this.mgsArray.push(chatmsg);
// })
// } else {
// let chatmsg = {
// _id: element._id,
// attachments: element.attachments,
// channels: element.channels,
// file: element.file,
// mentions: element.mentions,
// msg: element.msg,
// rid: element.rid,
// ts: element.ts,
// u: element.u,
// _updatedAt: element._updatedAt,
// }
// this.mgsArray.push(chatmsg)
// }
// } else {
// let chatmsg = {
// _id: element._id,
// attachments: element.attachments,
// channels: element.channels,
// mentions: element.mentions,
// msg: element.msg,
// rid: element.rid,
// ts: element.ts,
// u: element.u,
// _updatedAt: element._updatedAt,
// }
// this.mgsArray.push(chatmsg)
// }
// });
// await this.storage.remove('chatmsg').then(() => {
// console.log('MSG REMOVE FROM STORAGE')
// });
// await this.storage.set('chatmsg', this.mgsArray).then((value) => {
// console.log('MSG SAVED ON STORAGE', value)
// });
})
}
}
async addMessageDB(ChatMessage, roomId) {
if (environment.chatOffline) {
await this.storage.get('chatmsg' + roomId).then(async(messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
}
if(!ChatMessage._id && environment.chatOffline) {
getMsgFromDBMobile() {
// console.log('ALL MSG DBBB', this.id)
// this.sqlservice.getAllChatMSG(this.id).then((msg: any = []) => {
// let ad = [];
// ad = msg
// console.log('ALL MSG DBBB', ad.length)
// msg.map(element => {
// console.log('CHANNEL ELEMENT', element)
// let msgChat = {
// _id: element.Id,
// attachments: this.isJson(element.Attachments),
// channels: this.isJson(element.Channels),
// file: {
// guid: this.isJson(element.File).guid,
// image_url: this.isJson(element.image_url),
// type: this.isJson(element.File).type
// },
// mentions: this.isJson(element.Mentions),
// msg: element.Msg,
// rid: element.Rid,
// ts: element.Ts,
// u: this.isJson(element.U),
// _updatedAt: this.isJson(element.UpdatedAt),
// }
// let mmessage = this.fix_updatedAt(msgChat)
// console.log('FROM DB WEB', mmessage)
// const wewMessage = new MessageService(this.storage)
// wewMessage.setData(mmessage)
// this.messages.push(wewMessage)
// console.log('loadHistory 222', this.messages)
// });
// });
delete ChatMessage.temporaryData
messages.push(ChatMessage)
console.log('no ID')
await this.storage.set('chatmsg' + roomId, messages)
// console.log('add to DB', ChatMessage)
} else {
const find = messages.find((message)=> {
return message._id == ChatMessage._id
})
if(!find) {
delete ChatMessage.temporaryData
messages.push(ChatMessage)
await this.storage.set('chatmsg' + roomId, messages)
// console.log('add to DB', ChatMessage)
} else {
console.log('duplicate')
}
}
})
}
}
async addManyMessageDB(_ChatMessage: any[], roomId) {
if (environment.chatOffline) {
await this.storage.get('chatmsg' + roomId).then(async(messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
}
await _ChatMessage.forEach(async(ChatMessage)=>{
if(!ChatMessage._id && environment.chatOffline) {
delete ChatMessage.temporaryData
messages.push(ChatMessage)
// console.log('add to DB')
} else {
const find = messages.find((message)=> {
return message._id == ChatMessage._id
})
if(!find) {
delete ChatMessage.temporaryData
messages.push(ChatMessage)
// console.log('add to DB')
}
}
})
await this.storage.set('chatmsg' + roomId, messages)
})
}
}
}
+141 -82
View File
@@ -5,8 +5,12 @@ import { SessionStore } from 'src/app/store/session.service';
import { capitalizeTxt } from 'src/plugin/text'
import { NfService } from 'src/app/services/chat/nf.service'
import { WsChatService } from 'src/app/services/chat/ws-chat.service';
import { environment } from 'src/environments/environment';
import { showDateDuration } from 'src/plugin/showDateDuration';
import { ChatStorageService } from './chat-storage.service'
import { ChatMethodsService } from './chat-methods.service'
import { MessageModel, DeleteMessageModel } from '../../models/beast-orm'
import { AESEncrypt } from '../aesencrypt.service'
@Injectable({
providedIn: 'root'
})
@@ -26,7 +30,8 @@ export class MessageService {
}
t = ''
_id =''
_id = ''
id = '' // table id
_updatedAt
file
attachments
@@ -43,14 +48,22 @@ export class MessageService {
localReference = null
viewed = []
received = []
addToDb = false
messageSend = false
delate = false
delateRequest = false
constructor(private storage: Storage,
private NfService: NfService,
private WsChatService: WsChatService) {
private WsChatService: WsChatService,
private ChatStorageService: ChatStorageService,
private ChatMethodsService: ChatMethodsService,
private AESEncrypt: AESEncrypt) {
}
setData({customFields = {}, channels, mentions, msg ,rid ,ts, u, t, _id, _updatedAt, file, attachments, temporaryData, localReference}:Message) {
this.customFields = customFields
setData({customFields = {}, channels, mentions, msg ,rid ,ts, u, t, _id, id, _updatedAt, file, attachments, temporaryData, localReference , viewed = [], received = [], delate = false, delateRequest =false, }:Message) {
this.channels = channels || []
this.mentions = mentions || []
this.msg = msg || ""
@@ -64,10 +77,19 @@ export class MessageService {
this.attachments = attachments
this.temporaryData = temporaryData
this.localReference = localReference || null
this.id = id
this.delate = delate
this.delateRequest = delateRequest
this.viewed = [...new Set([...viewed,...this.viewed])];
this.received = [...new Set([...received,...this.received])];
if(!this.ts) {
this.offline = true
this.messageSend = false
} else {
this.messageSend = true
this.offline = false
}
@@ -79,18 +101,13 @@ export class MessageService {
}
}
// if(typeof(this.file?.type)) {
// this.hasFile = true
// }
if(this.hasFile) {
this.getFileFromDb()
// this.getFileFromDb()
if(this.file.type != 'application/webtrix') {
this.displayType = this.file.type.replace('application/','').toUpperCase()
}
}
this.calDateDuration()
}
@@ -101,37 +118,29 @@ export class MessageService {
return firstName + ' ' + lastName
}
getFileFromDb() {
// getFileFromDb() {
if(this.hasFile) {
if (this.file.guid) {
this.storage.get(this.file.guid).then((image) => {
if(image != null) {
this.file.image_url = image
}
});
}
}
}
// if(this.hasFile) {
// if (this.file.guid) {
// this.storage.get(this.file.guid).then((image) => {
// if(image != null) {
// this.file.image_url = image
// }
// });
// }
// }
// }
async send(): Promise<any> {
this.sendAttempt++;
if(!this.hasFile) {
this.WsChatService.send({roomId:this.rid, msg:this.msg, localReference: this.localReference}).then(({message, requestId}) => {
let ChatMessage = message.result
if (environment.chatOffline) {
// this.redefinedMessage(ChatMessage)
this.offline = false
}
const params = {roomId:this.rid, msg:this.msg, localReference: this.localReference}
await this.sendRequest(params)
return new Promise((resolve, reject)=>{
resolve(ChatMessage)
})
})
} else {
this.uploadingFile = true
@@ -149,26 +158,17 @@ export class MessageService {
this.temporaryData = {}
this.WsChatService.send({roomId:this.rid, msg: this.msg, attachments: this.attachments, file: this.file, localReference: this.localReference}).then(({message, requestId}) => {
const params = {roomId:this.rid, msg: this.msg, attachments: this.attachments, file: this.file, localReference: this.localReference}
await this.sendRequest(params)
console.log('message', message)
let ChatMessage = message.result
if (environment.chatOffline) {
// this.redefinedMessage(ChatMessage)
this.offline = false
}
return new Promise((resolve, reject)=>{
resolve(ChatMessage)
})
})
} else if(this.WsChatService.isLogin == false) {
this.WsChatService.registerCallback({
type: 'reConnect',
funx: async ()=> {
return await this.send()
this.send()
return true
}
})
@@ -185,11 +185,42 @@ export class MessageService {
}
redefinedMessage(ChatMessage) {
ChatMessage = this.NfService.fix_updatedAt(ChatMessage)
this.setData(ChatMessage)
async sendRequest(params) {
this.ChatMethodsService.send(params).subscribe(
(response: any) => {
const ChatMessage = response.message
this.messageSend = true
this.redefinedMessage(ChatMessage)
},
(error) => {
this.WsChatService.registerCallback({
type: 'reConnect',
funx: async ()=> {
this.WsChatService.send(params).then(({message, requestId}) => {
let ChatMessage = message.result
this.messageSend = true
this.redefinedMessage(ChatMessage)
})
return true
}
})
}
)
}
async redefinedMessage(ChatMessage , update = true) {
ChatMessage = this.NfService.fix_updatedAt(ChatMessage)
const message = this.getChatObj()
ChatMessage = Object.assign(message, ChatMessage)
this.setData(ChatMessage)
await this.save()
}
async downloadFileMsg() {
const result = await this.NfService.beforeSendAttachment(this)
@@ -203,47 +234,75 @@ export class MessageService {
this.duration = showDateDuration(date || this._updatedAt);
}
private messageReceptor() {
return this.u.username != SessionStore.user.RochetChatUser
async delateStatusFalse() {
this.delate = true
this.save()
}
receptorReceive() {
async delateDB() {
// alert('delete data')
const message = await MessageModel.get({_id: this._id})
await message.delete()
if(this.messageReceptor()) {
let newMessage = {
rid: this._id,
msg: this.msg,
attachments: this.attachments,
file: this.file,
localReference: this.localReference,
viewed: this.viewed.push('123'),
received: this.viewed.push('123'),
}
this.WsChatService.updateMessage(newMessage).then(()=>{
console.log('newMessage', newMessage)
})
}
isSenderIsNotMe(ChatMessage) {
return SessionStore.user.RochetChatUser != ChatMessage.u.username
}
messageOwnerById(id) {
return SessionStore.user.RochetChatUser != this.u.username
}
private getChatObj() {
return {
channels: this.channels,
mentions: this.mentions,
//msg: this.AESEncrypt.encrypt(this.msg, SessionStore.user.RochetChatUser),
msg:this.msg,
rid: this.rid,
ts: this.ts,
u: this.u,
_id: this._id,
id: this.id,
_updatedAt: this._updatedAt,
messageSend: this.messageSend,
offline: this.offline,
viewed: this.viewed,
received: this.received,
localReference: this.localReference,
attachments: this.attachments,
file: this.file,
delate: this.delate
}
}
receptorView() {
if(this.messageReceptor()) {
let newMessage = {
rid: this._id,
msg: this.msg,
attachments: this.attachments,
file: this.file,
localReference: this.localReference,
viewed: this.viewed.push('123'),
received: this.viewed.push('123'),
}
this.WsChatService.updateMessage(newMessage).then(()=>{
console.log('newMessage', newMessage)
})
async addMessageDB() {
if(!this.addToDb) {
this.addToDb= true
const message = this.getChatObj()
delete message.id
const createdMessage = await MessageModel.create(message)
this.id = createdMessage.id
}
}
async save() {
const message = this.getChatObj()
await MessageModel.update(message)
}
decryptMessage() {
try {
// this.msg = this.AESEncrypt.decrypt(this.msg, SessionStore.user.RochetChatUser)
} catch (error) {}
}
}
+468 -205
View File
@@ -16,6 +16,11 @@ import { environment } from 'src/environments/environment';
import { ChatService } from 'src/app/services/chat.service';
import { NfService } from 'src/app/services/chat/nf.service';
import { v4 as uuidv4 } from 'uuid'
import { ChatStorageService } from './chat-storage.service'
import { ChatMethodsService } from './chat-methods.service'
import { DeleteMessageModel, MessageModel } from '../../models/beast-orm'
import { AESEncrypt } from '../aesencrypt.service'
import { IncomingChatMessage, ChatMessageInterface, falseTypingMethod } from 'src/app/models/message.model';
@Injectable({
providedIn: 'root'
@@ -32,6 +37,7 @@ export class RoomService {
name = ''
_updatedAt = {}
hasLoadHistory = false
restoreFromOffline = false
duration = ''
isTyping = false
otherUserType = false
@@ -39,10 +45,10 @@ export class RoomService {
message = ''
lastMessageTxt = ''
userThatIsTyping = ''
private ToastService = ToastsService
mgsArray = [];
messagesLocalReference = []
members = []
u
scrollDown = () => { }
@@ -66,21 +72,88 @@ export class RoomService {
private NativeNotificationService: NativeNotificationService,
private sortService: SortService,
private chatService: ChatService,
private NfService: NfService
private NfService: NfService,
private ChatStorageService: ChatStorageService,
private ChatMethodsService: ChatMethodsService,
private AESEncrypt: AESEncrypt
) {
this.NativeNotificationService.askForPermission()
// this.restoreMessageFromDB()
this.WsChatService.getUserStatus((d) => {
const userId = d.fields.args[0][0]
const statusNum = d.fields.args[0][2]
const statusText = this.statusNumberToText(statusNum)
//
if(this.members?.map) {
const membersIds = this.members.map((user)=> user._id)
if(membersIds.includes(userId)) {
if(statusText != 'offline') {
this.deleteMessageToReceive(userId)
}
this.messages.forEach((message, index) => {
if(!message.messageOwnerById(userId)) {
if(!this.messages[index]?.received?.includes(userId)) {
if(this.messages[index]._id) {
try {
if(!this.messages[index].received.includes(userId)) {
this.messages[index].received.push(userId)
}
} catch(e) {
this.messages[index].received = [userId]
}
this.messages[index].save()
}
}
}
})
}
}
})
}
setData({ customFields = {}, id, name, t, lastMessage = new MessageService(this.storage, this.NfService, this.WsChatService), _updatedAt }) {
/**
* @description convert rocketchat statues num to readable string
* @param text
* @returns
*/
statusNumberToText(text) {
if(text == '0') {
return "offline"
}
else if(text == '1') {
return "online"
}
else if(text == '2') {
return "away"
}
else if(text == '3') {
return "busy"
}
}
setData({members, u, customFields = {}, id, name, t, lastMessage = new MessageService(this.storage, this.NfService, this.WsChatService, this.ChatStorageService, this.ChatMethodsService, this.AESEncrypt), _updatedAt }) {
this.customFields = customFields
this.id = id
this.name = name
this.t = t
this.lastMessage = lastMessage
this._updatedAt = _updatedAt
this.u = u
this.members = members
this.calDateDuration()
this.restoreMessageFromDB()
}
@@ -89,19 +162,25 @@ export class RoomService {
return SessionStore.user.RochetChatUser != ChatMessage.u.username
}
senderId(ChatMessage) {
return ChatMessage.u._id
}
receiveMessage() {
this.WsChatService.updateRoomEventss(
this.id,
"stream-room-messages",
(_ChatMessage) => {
console.log('recivemessage', _ChatMessage)
async (IncomingChatMessage:IncomingChatMessage) => {
let ChatMessage = _ChatMessage.fields.args[0]
ChatMessage = this.fix_updatedAt(ChatMessage)
let IncomingChatMessageArgs = IncomingChatMessage.fields.args[0]
let ChatMessage : ChatMessageInterface = this.fix_updatedAt(IncomingChatMessageArgs)
if(!this.messagesLocalReference.includes(ChatMessage.localReference)) {
const message = this.prepareMessage(ChatMessage)
const message = await this.prepareCreate({message: ChatMessage, save: true})
message.messageSend = true
this.lastMessage = message
this.calDateDuration(ChatMessage._updatedAt)
@@ -114,154 +193,150 @@ export class RoomService {
message: message.msg,
title: this.name
});
}
this.addMessageDB(ChatMessage)
message.addMessageDB()
setTimeout(()=>{
this.scrollDown()
}, 50)
}
} else {
this.messages.forEach((message, index)=> {
if(message.localReference == ChatMessage.localReference) {
const membersIds = this.members.map((user)=> user._id)
this.getAllUsers().forEach( async (users) => {
if(membersIds.includes(users._id)) {
if(users.status != 'offline') {
this.messages[index].received.push(users._id)
setTimeout(() => {
message.save()
}, 150)
}
}
});
}
})
}
}
)
this.WsChatService.receiveStreamNotifyRoom((message) => {
if(message.fields.eventName == this.id+'/'+'typing') {
this.userThatIsTyping = this.usernameToDisplayName(message.fields.args[0])
console.log(this.userThatIsTyping, 'this.userThatIsTyping')
this.isTyping = message.fields.args[1]
this.otherUserType = message.fields.args[1]
const args = message.fields.args
// alert(JSON.stringify(args))
if (typeof args[1] != 'object') {
this.userThatIsTyping = this.usernameToDisplayName(args[0])
console.log(this.userThatIsTyping, 'this.userThatIsTyping')
this.isTyping = args[1]
this.otherUserType = args[1]
this.readAllMessage()
// console.log(JSON.stringify(args))
// alert(JSON.stringify(args))
} else if(args[0]?.method == 'viewMessage' || args[1]?.method == 'viewMessage') {
this.readAllMessage()
} else if(args[0]?.method == 'deleteMessage' || args[1]?.method == 'deleteMessage') {
// alert('delete')
// console.log(args[0], 'receive delete message::()')
this.deleteMessage(args[1]?.method?._id)
} else {
// alert('miss')
}
} else if (message.fields.eventName == this.id+'/'+'deleteMessage') {}
})
}
async addMessageDB(ChatMessage) {
if (environment.chatOffline) {
this.storage.get('chatmsg' + this.id).then((messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
getRoomMembersIds(): string[] {
return this.members.map((user)=> user._id)
}
getAllMemberThatIsNotOffline(): string[] {
const membersIds = this.getRoomMembersIds()
const allChatUsers = this.getAllUsers()
const AllMemberThatIsNotOffline = []
for(let user of allChatUsers) {
if(membersIds.includes(user._id)) {
if(user.status != 'offline') {
AllMemberThatIsNotOffline.push(user._id)
}
}
}
return AllMemberThatIsNotOffline
}
getAllMemberThatIsOffline(): string[] {
const membersIds = this.getRoomMembersIds()
const allChatUsers = this.getAllUsers()
const AllMemberThatIsNotOffline = []
for(let user of allChatUsers) {
if(membersIds.includes(user._id)) {
if(user.status == 'offline') {
AllMemberThatIsNotOffline.push(user._id)
}
}
}
return AllMemberThatIsNotOffline
}
if(!ChatMessage._id && environment.chatOffline) {
async deleteMessageToReceive(userId) {
const allDeleteMessages = await DeleteMessageModel.filter({rid: this.id}).execute()
delete ChatMessage.temporaryData
messages.push(ChatMessage)
this.storage.set('chatmsg' + this.id, messages)
for(let message_ of allDeleteMessages) {
if(message_.needToReceiveBy.includes(userId)) {
message_.needToReceiveBy = message_.needToReceiveBy.filter((e)=> e != userId)
this.sendFalseTypingReadMessage('deleteMessage',{_id:message_.messageId})
if(message_.needToReceiveBy.length == 0) {
const deleteMessage = await DeleteMessageModel.get({messageId: message_.messageId})
await deleteMessage.delete()
} else {
const find = messages.find((message)=> {
return message._id == ChatMessage._id
})
if(!find) {
delete ChatMessage.temporaryData
messages.push(ChatMessage)
this.storage.set('chatmsg' + this.id, messages)
}
}
})
}
}
async updateMessageDB(ChatMessage, localReference) {
if (environment.chatOffline) {
this.storage.get('chatmsg' + this.id).then((messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
await DeleteMessageModel.update(message_)
}
let index;
const find = messages.find((message, _index)=> {
if(message.localReference) {
if(message?.localReference == ChatMessage?.localReference) {
index = _index
return true
}
}
return false
})
if(find) {
messages[index] = ChatMessage
this.storage.set('chatmsg' + this.id, messages)
}
})
}
}
}
}
async updateViewedMessage(id, userId) {
if (environment.chatOffline) {
this.storage.get('chatmsg' + this.id).then((messages: any = []) => {
if(!Array.isArray(messages)) {
messages = []
}
let index;
const find = messages.find((message, _index)=> {
if(message._id == id) {
index = _index
return true
}
return false
})
if(find) {
if(!messages[index].hasOwnProperty('viewed') || !Array.isArray(messages[index].viewed)) {
messages.viewed = []
}
messages.viewed.push(userId)
this.storage.set('chatmsg' + this.id, messages)
}
})
}
}
/**
* @description delete message in the DB. get all messages, delete then corresponding message and update the store
* @param id message ID
*/
private deleteMessageFromDb(id) {
if (environment.chatOffline) {
this.storage.get('chatmsg' + this.id).then((messages: any = []) => {
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)
});
})
}
}
async receiveMessageDelete() {
@@ -271,7 +346,6 @@ export class RoomService {
async (ChatMessage) => {
const DeletedMessageId = ChatMessage.fields.args[0]._id;
console.log(DeletedMessageId);
this.deleteMessage(DeletedMessageId)
}
@@ -283,23 +357,93 @@ export class RoomService {
* @description delete message in the view
* @param id message ID
*/
deleteMessage(id) {
this.messages.forEach((message, index) => {
async deleteMessage(id) {
await this.messages.forEach(async(message, index) => {
if(message._id == id) {
this.messages.splice(index, 1)
if (SessionStore.user.RochetChatUser == message.u.username) {
const allMemberThatIsOffline = this.getAllMemberThatIsOffline()
await DeleteMessageModel.create({
messageId: message._id,
rid: message.rid,
ts: message.ts,
u: message.u,
needToReceiveBy: allMemberThatIsOffline
})
}
message.delateStatusFalse()
message.delateDB()
this.deleteMessageFromDb(id)
//Get previous last message from room
const previousLastMessage = this.messages.slice(-1)[0];
this.lastMessage = previousLastMessage;
this.calDateDuration(previousLastMessage._updatedAt)
this.sortRoomList()
}
})
}
deleteAll() {
this.messages.forEach((message)=>{
if(message?._id) {
this.sendDeleteRequest(message._id)
}
})
}
async delateMessageToSendToOthers(userId) {
const deleteMessage = await DeleteMessageModel.all()
const toSend = deleteMessage.filter((DeleteMessage:string[])=> ! DeleteMessage.includes(userId))
}
async sendDeleteRequest(msgId) {
const message = this.messages.find((e)=>e._id = msgId)
message.delateStatusFalse()
this.ChatMethodsService.deleteMessage({_id:msgId, msgId:msgId, roomId:message.rid}).subscribe(
(response: any) => {
message.delateRequest = true
message.save()
this.deleteMessage(msgId)
},
(response) => {
if (response.error.error.startsWith('No message found with the id of')) {
// alert('not found')
this.deleteMessage(msgId)
} else {
// this.deleteMessage(DeletedMessageId)
this.WsChatService.registerCallback({
type: 'reConnect',
funx: async ()=> {
this.sendDeleteRequest(msgId)
return true
}
})
}
}
)
}
/**
* @description sen text message
*/
@@ -313,24 +457,19 @@ export class RoomService {
attachments,
file,
temporaryData,
localReference,
viewed: [],
received: []
localReference
}
const message: MessageService = this.prepareMessage(offlineChatMessage, environment.chatOffline)
/**
* @description redefine message offline data "offlineChatMessage" with live ChatMessage
*/
message.send().then((ChatMessage) => {
this.updateMessageDB(ChatMessage, localReference)
})
this.message= ''
const message: MessageService = await this.prepareCreate({message:offlineChatMessage, save: environment.chatOffline})
this.messagesLocalReference.push(localReference)
await message.addMessageDB()
message.send()
if (environment.chatOffline) {
this.messagesLocalReference.push(localReference)
this.addMessageDB(offlineChatMessage)
setTimeout(() => {
this.scrollDown()
@@ -341,29 +480,10 @@ export class RoomService {
this.sortRoomList()
}
this.message= ''
}
/**
*
* @param message
* @param ChatMessage
* @description when creating message we use offline data, then we need redefined with live data
*/
redefinedMessage (message: MessageService, ChatMessage) {
ChatMessage = this.fix_updatedAt(ChatMessage)
message.setData(ChatMessage)
if( new Date(this.lastMessage._updatedAt).getTime() < new Date(message._updatedAt).getTime()) {
this.lastMessage = message
this.calDateDuration(message._updatedAt)
}
this.sortRoomList()
}
sendTyping(text:string = this.message) {
@@ -386,6 +506,10 @@ export class RoomService {
this.typingWatch()
}
sendFalseTypingReadMessage(method,param: object) {
this.WsChatService.sendStreamNotifyRoom(this.id, SessionStore.user.RochetChatUser, 'typing', {method:method, params: param} as falseTypingMethod)
this.setTypingOff()
}
private typingWatch() {
setTimeout(()=>{
@@ -398,7 +522,7 @@ export class RoomService {
this.WsChatService.sendStreamNotifyRoom(this.id, SessionStore.user.RochetChatUser, 'typing', this.isTyping)
}
} else {
//console.log(now - this.lastTimeType)
}
}, 3000)
@@ -430,43 +554,48 @@ export class RoomService {
return JSON.parse(str);
}
async restoreMessageFromDB() {
if(environment.chatOffline) {
await this.storage.get('chatmsg' + this.id).then( async (messages = []) => {
if(!Array.isArray(messages)) {
messages = []
}
const messages = await MessageModel.filter({rid:this.id}).execute()
await messages.forEach( async (ChatMessage, index) => {
const wewMessage = this.prepareMessage(ChatMessage, false)
await messages.forEach( async (ChatMessage, index) => {
if(wewMessage.offline == false) {
this.prepareMessage(ChatMessage)
} else {
const offlineMessage = this.prepareMessage(ChatMessage)
const wewMessage = await this.simplePrepareMessage(ChatMessage)
if(wewMessage.offline == false) {
const message = await this.prepareMessageCreateIfNotExist_iD({message:ChatMessage})
message?.decryptMessage()
} else {
const offlineMessage = await this.prepareMessageCreateIfNotExist({message:ChatMessage})
if(offlineMessage) {
this.messagesLocalReference.push(offlineMessage.localReference)
offlineMessage.send().then((newChatMessage) => {
this.updateMessageDB(newChatMessage, ChatMessage.localReference)
})
offlineMessage?.decryptMessage()
offlineMessage.send()
}
});
}
setTimeout(()=> {
this.scrollDown()
}, 50)
if(wewMessage.delate && !wewMessage.offline && !wewMessage.delateRequest) {
this.sendDeleteRequest(wewMessage._id)
}
})
});
setTimeout(()=> {
this.scrollDown()
}, 50)
}
}
// runs onces only
async loadHistory({limit = 50, forceUpdate = false }) {
async loadHistory({limit = 1000, forceUpdate = false }) {
if(forceUpdate == false) {
if (this.hasLoadHistory) {
@@ -474,16 +603,29 @@ export class RoomService {
}
}
if(this.restoreFromOffline == false) {
this.restoreFromOffline = true
await this.restoreMessageFromDB()
}
await this.WsChatService.loadHistory(this.id, limit).then( async (chatHistory:chatHistory) => {
await chatHistory.result.messages.reverse().forEach( async (message) => {
console.log('load chatHistory', JSON.stringify(chatHistory))
this.prepareMessage(message)
// this.messages = this.sortService.sortDate(this.messages, '_updatedAt')
const messagesId = this.messages.map((message)=> message._id)
chatHistory.result.messages.reverse().forEach(async(message: any) => {
if (!messagesId.includes(message._id)) {
const messagesToSave = await this.prepareMessageCreateIfNotExist_iD({message: message});
if(messagesToSave) {
await messagesToSave.addMessageDB()
}
}
})
this.storage.set('chatmsg' + this.id, chatHistory.result.messages)
})
setTimeout(() => {
@@ -494,38 +636,156 @@ export class RoomService {
}
async readAllMessage() {
const membersIds = this.members.map((user)=> user._id)
await this.messages.forEach( async (message, index) => {
if(message._id) {
if(message.viewed.length == 0) {
this.messages[index].viewed = membersIds
this.messages[index].received = membersIds
await this.messages[index].save()
}
}
})
}
/**
* @description find or create message
* @param message
* @param save
* @returns
*/
prepareMessage(message, save = true): MessageService {
async prepareMessage({message, save = true, redefined = false}): Promise<MessageService> {
message = this.fix_updatedAt(message)
const wewMessage = new MessageService(this.storage, this.NfService, this.WsChatService)
const wewMessage = new MessageService(this.storage, this.NfService, this.WsChatService, this.ChatStorageService, this.ChatMethodsService, this.AESEncrypt)
wewMessage.setData(message)
wewMessage.loadHistory = this.hasLoadHistory
if(!message._id && environment.chatOffline && save) {
let foundIndex;
this.messages.push(wewMessage)
return wewMessage
}
const found = this.messages.find((MessageService) => {
const found = this.messages.find((MessageService, index) => {
if (MessageService._id == message._id) {
if(this.hasLoadHistory) console.log(`${MessageService._id} == ${message._id}`)
foundIndex = index
return true
} else {
return false
}
})
if (save && !found) {
this.messages.push(wewMessage)
if(save) {
if (!found) {
this.messages.push(wewMessage)
return wewMessage
}
} else if(foundIndex) {
return this.messages[foundIndex]
} else {
return wewMessage
}
}
async ChatMessageIsPresentInTheView(ChatMessage:ChatMessageInterface) {
let foundIndex;
const found = this.messages.find((MessageService, index) => {
if (MessageService._id == ChatMessage._id) {
foundIndex = index
return true
} else {
return false
}
})
if (foundIndex) {
return { found, foundIndex}
} else {
return false
}
}
/**
* @description find or create message
* @param message
* @param save
* @returns
*/
async prepareCreate({message, save = true}): Promise<MessageService> {
message = this.fix_updatedAt(message)
const wewMessage = new MessageService(this.storage, this.NfService, this.WsChatService, this.ChatStorageService, this.ChatMethodsService, this.AESEncrypt)
wewMessage.setData(message)
wewMessage.loadHistory = this.hasLoadHistory
this.messages.push(wewMessage)
return wewMessage
}
simplePrepareMessage(message) {
message = this.fix_updatedAt(message)
const wewMessage = new MessageService(this.storage, this.NfService, this.WsChatService, this.ChatStorageService, this.ChatMethodsService, this.AESEncrypt)
wewMessage.setData(message)
wewMessage.loadHistory = this.hasLoadHistory
return wewMessage
}
async prepareMessageCreateIfNotExist({message}) {
message = this.fix_updatedAt(message)
let foundIndex;
const found = this.messages.find((MessageService, index) => {
if (MessageService._id == message._id ||
MessageService.localReference == message.localReference ) {
foundIndex = index
return true
} else {
return false
}
})
if (!found) {
const wewMessage = this.simplePrepareMessage(message)
this.messages.push(wewMessage)
return wewMessage
} else {
return null
}
}
async prepareMessageCreateIfNotExist_iD({message}) {
message = this.fix_updatedAt(message)
const found = this.messages.find((MessageService, index) => {
if (MessageService._id == message._id ) {
return true
} else {
return false
}
})
if (!found) {
const wewMessage = this.simplePrepareMessage(message)
this.messages.push(wewMessage)
return wewMessage
} else {
return null
}
}
private calDateDuration(date = null) {
@@ -533,11 +793,10 @@ export class RoomService {
this._updatedAt = date || this._updatedAt
}
private fix_updatedAt(message) {
if (message.result) {
private fix_updatedAt(message): ChatMessageInterface {
if (message?.result) {
message.result._updatedAt = message.result._updatedAt['$date']
} else if(message._updatedAt) {
} else if(message?._updatedAt) {
if(message._updatedAt.hasOwnProperty('$date')) {
message._updatedAt = message._updatedAt['$date']
}
@@ -545,7 +804,6 @@ export class RoomService {
return message
}
usernameToDisplayName(username) {
const firstName = capitalizeTxt(username.split('.')[0])
@@ -553,4 +811,9 @@ export class RoomService {
return firstName + ' ' + lastName
}
sendReadMessage() {
this.WsChatService.readMessage(this.id)
this.sendFalseTypingReadMessage('viewMessage', {})
}
}
@@ -14,6 +14,12 @@ import { SortService } from '../functions/sort.service';
import { chatUser } from 'src/app/models/chatMethod';
import { NfService } from 'src/app/services/chat/nf.service'
import { ChangeProfileService } from '../change-profile.service';
import { UserSession } from 'src/app/models/user.model';
import { AuthService } from '../auth.service';
import { ChatStorageService } from './chat-storage.service'
import { ChatMethodsService } from './chat-methods.service'
import { AESEncrypt } from '../aesencrypt.service'
@Injectable({
providedIn: 'root'
})
@@ -33,6 +39,8 @@ export class WsChatMethodsService {
currentRoom: RoomService = null
users: chatUser[] = []
loggedUser: any;
constructor(
private WsChatService: WsChatService,
private storage: Storage,
@@ -43,8 +51,15 @@ export class WsChatMethodsService {
private ChatService: ChatService,
private NfService: NfService,
private changeProfileService: ChangeProfileService,
private chatService: ChatService,
private authService: AuthService,
private ChatStorageService: ChatStorageService,
private ChatMethodsService:ChatMethodsService,
private AESEncrypt: AESEncrypt
) {
this.loggedUser = authService.ValidatedUserChat['data'];
this.loadChat()
this.WsChatService.registerCallback({
@@ -79,39 +94,6 @@ export class WsChatMethodsService {
this.storage.remove('Rooms');
})
// this.WsChatService.registerCallback({
// type:'Onmessage',
// funx:(message) => {
// if(message.msg =='changed' && message.collection == "stream-room-messages") {
// if(message.fields.args[0].rid) {
// // new message
// const ChatMessage = message.fields.args[0]
// const messageId = ChatMessage.rid
// setTimeout(()=>{
// this.sortRoomList()
// }, 100)
// }
// } else if(message.msg =='changed' && message.collection == "stream-notify-room") {
// if(message.fields.eventName.includes('deleteMessage')) {
// // delete message
// const DeletedMessageId = message.fields.args[0]._id;
// setTimeout(()=>{
// this.sortRoomList()
// }, 100)
// } else if(message.fields.eventName.includes('typing')) {
// }
// }
// }
// })
}
private loadChat() {
@@ -144,7 +126,6 @@ export class WsChatMethodsService {
this.users = []
}
openRoom(roomId) {
if(this.currentRoom) {
@@ -161,12 +142,13 @@ export class WsChatMethodsService {
}
async restoreRooms() {
try {
const rooms = await this.storage.get('Rooms');
console.log('restore',rooms)
if(rooms) {
await rooms.result.update.forEach( async (roomData: room) => {
await this.prepareRoom(roomData);
@@ -183,14 +165,43 @@ export class WsChatMethodsService {
this.loadingWholeList = true
const rooms = await this.WsChatService.getRooms();
await this.storage.remove('Rooms');
await this.storage.set('Rooms', rooms);
console.log('rooms', rooms)
await rooms.result.update.forEach( async (roomData: room, index) => {
const roomId = this.getRoomId(roomData);
if(roomData.t == 'd') {
const res = await this.chatService.getMembers(roomId).toPromise();
const members = res['members'];
const users = members.filter(data => data.username != this.loggedUser.me.username);
rooms.result.update[index]['members'] = users
await this.prepareRoom(roomData);
} else {
if (roomData.t === 'p') {
const res = await this.chatService.getGroupMembers(roomId).toPromise()
const members = res['members'];
const users = members.filter(data => data.username != this.loggedUser.me.username);
rooms.result.update[index]['members'] = users
}
else {
const res = await this.chatService.getChannelMembers(roomId).toPromise()
const members = res['members'];
const users = members.filter(data => data.username != this.loggedUser.me.username);
rooms.result.update[index]['members'] = users
}
}
await rooms.result.update.forEach( async (roomData: room) => {
await this.prepareRoom(roomData);
});
console.log('save rooms', rooms)
await this.storage.set('Rooms', rooms);
this.sortRoomList()
this.loadingWholeList = false
}
@@ -199,6 +210,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()
}
@@ -247,11 +259,23 @@ export class WsChatMethodsService {
})
this.WsChatService.subStreamNotifyRoom(id, 'typing', false)
this.WsChatService.subStreamNotifyRoom(id, 'readMessage', false)
this.WsChatService.streamNotifyRoomDeleteMessage(id).then((subscription)=>{
//console.log('streamNotifyRoomDeleteMessage', subscription);
})
}
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,26 +287,31 @@ 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: new Date( roomData._updatedAt || roomData._updatedAt['$date']),
u : roomData.u || {},
members: roomData.members
}
let roomId = this.getRoomId(roomData)
// create room
if(!this.roomExist(roomId)) {
let room:RoomService = new RoomService(this.WsChatService, new MessageService(this.storage, this.NfService, this.WsChatService), this.storage, this.platform, this.sqlservice, this.NativeNotificationService, this.sortService, this.ChatService, this.NfService)
let room:RoomService = new RoomService(this.WsChatService, new MessageService(this.storage, this.NfService, this.WsChatService, this.ChatStorageService, this.ChatMethodsService, this.AESEncrypt), this.storage, this.platform, this.sqlservice, this.NativeNotificationService, this.sortService, this.ChatService, this.NfService , this.ChatStorageService, this.ChatMethodsService, this.AESEncrypt)
room.setData(setData)
room.receiveMessage()
room.getAllUsers = this.getUsers
room.receiveMessageDelete();
room.sortRoomList = this.sortRoomList
// create individual room
if(this.isIndividual(roomData)) {
@@ -300,7 +329,6 @@ export class WsChatMethodsService {
}
} else {
console.log('have!!!')
// in this case room is already present, therefor it will only be necessary,
// to redefine
@@ -336,16 +364,16 @@ export class WsChatMethodsService {
const username = d.fields.args[0][1]
const statusNum = d.fields.args[0][2]
const statusText = this.statusNumberToText(statusNum)
const user = this.getUserByName(username)
if(user) {
user.status = statusText
}
this.users.forEach((user, index) => {
if(user.username == username) {
this.users[index].status = statusText
}
})
})
}
getUserByName(username) {
@@ -398,9 +426,9 @@ export class WsChatMethodsService {
}
hidingRoom(id?) {
return this.WsChatService.hidingRoom(id).then(()=>{
// this.hideRoom(id)
// this.hideRoom(id)
})
}
@@ -460,9 +488,9 @@ export class WsChatMethodsService {
let _res = await this.ChatService.getAllUsers().toPromise()
let user = _res['users'].filter(data => data.username != SessionStore.user.RochetChatUser);
let users = _res['users'].filter(data => data.username != SessionStore.user.RochetChatUser);
user = user.sort((a,b) => {
users = users.sort((a,b) => {
if(a.name < b.name) {
return -1;
}
@@ -472,7 +500,11 @@ export class WsChatMethodsService {
return 0;
});
this.users = user
users.forEach((user, index) => {
// user[index].status = this.statusNumberToText(user[index].status)
})
this.users = users
}
getUserOfRoom(roomId){
+30 -5
View File
@@ -123,6 +123,30 @@ export class WsChatService {
});
}
readMessage(roomId) {
const requestId = uuidv4()
const message = {
"msg":"method",
"method":"readMessages",
"params": [roomId, []],
"id": requestId
}
this.ws.send({message, requestId})
return new Promise<Rooms>((resolve, reject) => {
this.ws.registerCallback({type:'Onmessage', funx:(message)=>{
if(message.id == requestId) { // same request send
resolve(message)
return true
}
}})
});
}
getUserOfRoom(roomId) {
const requestId = uuidv4()
@@ -174,6 +198,8 @@ export class WsChatService {
}]
}
console.log('send message to rocketchat ', message)
this.ws.send({message, requestId});
return new Promise((resolve, reject) => {
@@ -285,7 +311,7 @@ export class WsChatService {
});
}
joinRoom(){}
joinRoom() {}
deleteMessage(msgId) {
const requestId = uuidv4();
@@ -369,7 +395,7 @@ export class WsChatService {
}
subStreamNotifyRoom(roomId : string , event: 'typing' | 'deleteMessage', param: any) {
subStreamNotifyRoom(roomId : string , event: 'typing' | 'deleteMessage' | 'readMessage', param: any) {
const requestId = uuidv4()
@@ -496,7 +522,7 @@ export class WsChatService {
}
updateRoomEventss(roomId, collection:string, funx: Function, ) {
updateRoomEventss(roomId, collection:string, funx: Function, ) {
this.ws.registerCallback({
type:'Onmessage',
@@ -515,7 +541,6 @@ updateRoomEventss(roomId, collection:string, funx: Function, ) {
})
}
streamRoomMessages(roomId : string) {
const requestId = uuidv4()
@@ -703,7 +728,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>
@@ -27,114 +27,118 @@
</div>
</div>
<div class="content d-flex flex-column">
<div class="header-content width-100 d-flex justify-space-between">
<div (click)="close()" class="header-icon-left cursor-pointer">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-arrow-arrow-left.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " src="assets/images/theme/gov/icons-calendar-arrow-left.svg"></ion-icon>
</div>
<div class="header-title flex-grow-1 cursor-pointer">
<label>{{loadedEvent.workflowInstanceDataFields.Subject}}</label>
</div>
<div class="main-content d-flex height-100 width-100">
<div class="content d-flex flex-column">
<div (click)="editar(loadedEvent.serialNumber)" class="header-icon-right display-none-{{showAside}}">
<button class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-edit.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " src="assets/images/theme/gov/icons-edit.svg"></ion-icon>
</button>
</div>
<div (click)="rejectTask(loadedEvent.serialNumber)" class="header-icon-right display-none-{{showAside}}">
<button class="btn-no-color" >
<ion-icon class="delete" name="trash-sharp"></ion-icon>
</button>
</div>
</div>
<div class="upper-content d-flex flex-column">
<div class="content-location">
<p>
<span class="location">{{loadedEvent.workflowInstanceDataFields.Location}}</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Oficial MDGPR' " style="background-color: #ffb703;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Pessoal MDGPR' " style="background-color: #f05d5e;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Oficial PR' " style="background-color: #99e47b;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Pessoal PR' " style="background-color: #958bfc;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
</p>
</div>
<div class="content-details">
<ion-label>
<p>{{customDate}}</p>
<p *ngIf="toDateString(loadedEvent.workflowInstanceDataFields.StartDate) == toDateString(loadedEvent.workflowInstanceDataFields.EndDate)">das {{loadedEvent.workflowInstanceDataFields.StartDate | date: 'HH:mm'}} às {{loadedEvent.workflowInstanceDataFields.EndDate | date: 'HH:mm'}}</p>
<p *ngIf="toDateString(loadedEvent.workflowInstanceDataFields.StartDate) != toDateString(loadedEvent.workflowInstanceDataFields.EndDate)">{{loadedEvent.workflowInstanceDataFields.StartDate | date: 'd/M/yy' }} - {{ loadedEvent.workflowInstanceDataFields.StartDate | date: 'dd/M/yy'}} </p>
<p *ngIf="!loadedEvent.workflowInstanceDataFields.IsRecurring">(Não se repete)</p>
<p *ngIf="loadedEvent.workflowInstanceDataFields.IsRecurring">Repete</p>
</ion-label>
</div>
</div>
<div class="line"></div>
<div class="overflow-y-auto">
<div class="middle-content">
<div *ngIf="loadedEvent.workflowInstanceDataFields.ParticipantsList">
<h5>Intervenientes</h5>
<div *ngFor="let att of loadedEvent.workflowInstanceDataFields.ParticipantsList">
<ion-label>{{att.Name}}</ion-label>
</div>
<div class="line"></div>
<div class="header-content width-100 d-flex justify-space-between">
<div (click)="close()" class="header-icon-left cursor-pointer">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-arrow-arrow-left.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " src="assets/images/theme/gov/icons-calendar-arrow-left.svg"></ion-icon>
</div>
<div *ngIf="loadedEvent.workflowInstanceDataFields.Body">
<h5>Detalhes</h5>
<ion-item lines="none" class="ion-no-margin ion-no-padding">
<p [innerHTML]="loadedEvent.workflowInstanceDataFields.Body"></p>
</ion-item>
<div class="line"></div>
<div class="header-title flex-grow-1 cursor-pointer">
<label>{{loadedEvent.workflowInstanceDataFields.Subject}}</label>
</div>
<div (click)="editar(loadedEvent.serialNumber)" class="header-icon-right display-none-{{showAside}}">
<button class="btn-no-color">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/images/icons-edit.svg"></ion-icon>
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " src="assets/images/theme/gov/icons-edit.svg"></ion-icon>
</button>
</div>
<div (click)="rejectTask(loadedEvent.serialNumber)" class="header-icon-right display-none-{{showAside}}">
<button class="btn-no-color" >
<ion-icon class="delete" name="trash-sharp"></ion-icon>
</button>
</div>
</div>
<div *ngIf="loadedEvent.Documents" class="bottom-content width-100">
<ion-list>
<h5>Documentos Anexados </h5>
<ion-item class="ion-no-margin ion-no-padding cursor-pointer"
*ngFor="let attachment of loadedEvent.Documents"
(click)="viewDocument(attachment.DocId, attachment)">
<div class="upper-content d-flex flex-column">
<div class="content-location">
<p>
<span class="location">{{loadedEvent.workflowInstanceDataFields.Location}}</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Oficial MDGPR' " style="background-color: #ffb703;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Pessoal MDGPR' " style="background-color: #f05d5e;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Oficial PR' " style="background-color: #99e47b;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
<span class="event-type-{{loadedEvent.workflowInstanceDataFields.Agenda}}" *ngIf="loadedEvent.workflowDisplayName == 'Agenda Pessoal PR' " style="background-color: #958bfc;">
{{loadedEvent.workflowInstanceDataFields.Agenda}}
</span>
</p>
</div>
<div class="content-details">
<ion-label>
<p class="attach-title-item d-block">{{attachment.Assunto}}</p>
<p><span class="span-left">{{attachment.Sender}}</span><span class="span-right">{{ attachment.DocDate | date: 'dd-MM-yyyy HH:mm' }}</span></p>
<p>{{customDate}}</p>
<p *ngIf="toDateString(loadedEvent.workflowInstanceDataFields.StartDate) == toDateString(loadedEvent.workflowInstanceDataFields.EndDate)">das {{loadedEvent.workflowInstanceDataFields.StartDate | date: 'HH:mm'}} às {{loadedEvent.workflowInstanceDataFields.EndDate | date: 'HH:mm'}}</p>
<p *ngIf="toDateString(loadedEvent.workflowInstanceDataFields.StartDate) != toDateString(loadedEvent.workflowInstanceDataFields.EndDate)">{{loadedEvent.workflowInstanceDataFields.StartDate | date: 'd/M/yy' }} - {{ loadedEvent.workflowInstanceDataFields.StartDate | date: 'dd/M/yy'}} </p>
<p *ngIf="!loadedEvent.workflowInstanceDataFields.IsRecurring">(Não se repete)</p>
<p *ngIf="loadedEvent.workflowInstanceDataFields.IsRecurring">Repete</p>
</ion-label>
</ion-item>
</ion-list>
</div>
</div>
<div class="line"></div>
<div class="overflow-y-auto">
<div class="middle-content">
<div *ngIf="loadedEvent.workflowInstanceDataFields.ParticipantsList">
<h5>Intervenientes</h5>
<div *ngFor="let att of loadedEvent.workflowInstanceDataFields.ParticipantsList">
<ion-label>{{att.Name}}</ion-label>
</div>
<div class="line"></div>
</div>
<div *ngIf="loadedEvent.workflowInstanceDataFields.Body">
<h5>Detalhes</h5>
<ion-item lines="none" class="ion-no-margin ion-no-padding">
<p [innerHTML]="loadedEvent.workflowInstanceDataFields.Body"></p>
</ion-item>
<div class="line"></div>
</div>
</div>
<div *ngIf="loadedEvent.Documents" class="bottom-content width-100">
<ion-list>
<h5>Documentos Anexados </h5>
<ion-item class="ion-no-margin ion-no-padding cursor-pointer"
*ngFor="let attachment of loadedEvent.Documents"
(click)="viewDocument(attachment.DocId, attachment)">
<ion-label>
<p class="attach-title-item d-block">{{attachment.Assunto}}</p>
<p><span class="span-left">{{attachment.Sender}}</span><span class="span-right">{{ attachment.DocDate | date: 'dd-MM-yyyy HH:mm' }}</span></p>
</ion-label>
</ion-item>
</ion-list>
</div>
</div>
</div>
</div>
<div *ngIf="showAside" class="aside-right flex-column height-100 cursor-pointer">
<div class="aside-buttons">
<button hidden full class="btn-ok" shape="round" >Editar evento</button>
<button (click)="approveTask(loadedEvent.serialNumber)" full class="btn-ok" shape="round" >Aprovar</button>
<button (click)="emendTask(loadedEvent.serialNumber)" class="btn-cancel" shape="round" >Rever</button>
<div class="solid"></div>
<button full class="btn-cancel" shape="round" (click)="editar(loadedEvent.serialNumber)" >Editar</button>
<button (click)="rejectTask(loadedEvent.serialNumber)" full class="btn-delete" shape="round" >Rejeitar</button>
<div *ngIf="showAside" class="aside-right flex-column height-100 cursor-pointer">
<div class="aside-buttons">
<button hidden full class="btn-ok" shape="round" >Editar evento</button>
<button (click)="approveTask(loadedEvent.serialNumber)" full class="btn-ok" shape="round" >Aprovar</button>
<button (click)="emendTask(loadedEvent.serialNumber)" class="btn-cancel" shape="round" >Rever</button>
<div class="solid"></div>
<button full class="btn-cancel" shape="round" (click)="editar(loadedEvent.serialNumber)" >Editar</button>
<button (click)="rejectTask(loadedEvent.serialNumber)" full class="btn-delete" shape="round" >Rejeitar</button>
</div>
</div>
</div>
</div>
</ion-content>
@@ -3,7 +3,7 @@
<div class="header-title d-flex align-center justify-between width-100">
<div class="flex-grow-1">Eventos para Aprovação</div>
<div (click)="refreshing()">
<button class="btn-no-color" >
<button title="Atualizar" class="btn-no-color" >
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -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],
@@ -23,10 +23,7 @@
</ion-label>
</div>
</div>
<!-- <div *ngIf="room.customFields.countDownDate" class="d-flex align-items-center yellow-orange pl-10">
<i class="far fa-clock font-15" ></i>
<ion-label class="font-15 pl-10" color="warning">{{countDownDate()}}</ion-label>
</div> -->
</div>
</ion-toolbar>
</ion-header>
@@ -57,12 +54,13 @@
<span class="time">{{msg.duration}}</span>
</div>
<div class="message">
<ion-label>{{msg.msg}}</ion-label>
<ion-label *ngIf="msg.delate == false">{{msg.msg}}</ion-label>
<ion-label *ngIf="msg.delate == true">{{msg.msg}}</ion-label>
{{last ? scrollToBottom() : ''}}
</div>
</div>
<div *ngIf="msg.file">
<div *ngIf="msg.file && msg.delate == false">
<div class="message-item incoming-{{msg.u.username!=loggedUser.me.username}} max-width-45" *ngIf="msg.t != 'r' && msg.t != 'ul' && msg.t != 'au' && msg.t != 'ru' && msg.file.type != 'application/meeting'" >
<div *ngIf="msg.file.type != 'application/meeting'">
<div class="message-item-options d-flex justify-content-end">
@@ -72,6 +70,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 +81,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 +92,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>
@@ -114,35 +116,10 @@
<ion-label class="info-meeting-medium"><ion-icon></ion-icon><ion-icon name="location-outline"></ion-icon> {{msg.file.venue}}</ion-label><br />
</div>
</div>
<!-- <div class='message-item incoming-{{msg.u.username!=loggedUser.me.username}} max-width-45' *ngIf="msg.msg ==''">
<div *ngIf="msg.file.type == 'application/img'">
<div class="message-item-options d-flex justify-content-end">
<fa-icon [matMenuTriggerFor]="beforeMenu" icon="chevron-down" class="message-options-icon cursor-pointer">
</fa-icon>
<mat-menu #beforeMenu="matMenu" xPosition="before">
<button (click)="deleteMessage(msg._id)" class="menuButton">Apagar mensagem</button>
</mat-menu>
</div>
<div class="title">
<ion-label>{{msg.u.name}}</ion-label>
<span class="time">{{msg.duration}}</span>
</div>
<div>
<ion-label>{{msg.msg}}</ion-label>
<div *ngIf="msg.file" class="message-attachments">
<div>
<div (click)="openPreview(msg)">
<img *ngIf="msg.attachments[0].image_url" src="{{msg.attachments[0].image_url}}" alt="image">
</div>
</div>
</div>
{{last ? scrollToBottom() : ''}}
</div>
</div>
<div *ngIf="msg.file && msg.delate == true">
Apagou a mensagem
</div>
-->
<div *ngIf="msg.t == 'r'" class="info-text">
<ion-label>Alterou o nome do grupo para "{{msg.msg.split('-').join(' ')}}"</ion-label><br />
@@ -203,14 +180,27 @@
</ion-content>
<ion-footer>
<div class="typing" *ngIf="wsChatMethodsService.getGroupRoom(roomId).otherUserType == true" >A escrever...</div>
<!-- <div class="typing" *ngIf="wsChatMethodsService.getGroupRoom(roomId).otherUserType == true" >A escrever...</div> -->
<div class="typing" *ngIf="wsChatMethodsService.getGroupRoom(roomId).otherUserType == true">
{{ wsChatMethodsService.getGroupRoom(roomId).otherUserType }}
<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 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>
@@ -232,24 +222,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; */
@@ -287,5 +291,5 @@
.typing ngx-letters-avatar {
padding-left: 10px;
}
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,6 +88,16 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
pdfurl = "http://www.africau.edu/images/default/sample.pdf";
downloadFile: any;
showAvatar = false;
recording = false;
allowTyping = true;
storedFileNames = [];
lastAudioRecorded = '';
audioRecorded:any = "";
audioDownloaded:any = "";
durationDisplay = '';
duration = 0;
constructor(
public wsChatMethodsService: WsChatMethodsService,
@@ -107,6 +120,7 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
private CameraService: CameraService,
private toastService: ToastService,
private sanitiser: DomSanitizer,
) {
console.log('OnCONSTRUCTOR');
@@ -126,10 +140,15 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
this.wsChatMethodsService.openRoom(this.roomId)
this.wsChatMethodsService.getGroupRoom(this.roomId).scrollDown = this.scrollToBottomClicked
this.showAvatar = false
setTimeout(()=>{
this.scrollToBottomClicked()
this.showAvatar = true
}, 50)
this.deleteRecording();
}
ngOnInit() {
@@ -142,6 +161,9 @@ export class GroupMessagesPage implements OnInit, OnChanges, AfterViewInit, OnDe
}, 1000);
this.getChatMembers();
//this.getMessageDB();
VoiceRecorder.requestAudioRecordingPermission();
this.deleteRecording();
this.loadFiles();
}
@@ -225,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 => {
@@ -307,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);
@@ -838,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],
+73 -108
View File
@@ -14,10 +14,6 @@
</button>
</div>
</div>
<!-- <div *ngIf="frameUrl" class="width-100">
<iframe id="iframe" [src]="frameUrl" height="20%" width="100%" title="Iframe Example">
</iframe>
</div> -->
<div hidden class="header-bottom" (click)="addContacts()">
<div class="header-bottom-icon">
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " src="assets/icon/icons-user.svg"></ion-icon>
@@ -45,37 +41,41 @@
<fa-icon [matMenuTriggerFor]="beforeMenu" icon="chevron-down" class="message-options-icon cursor-pointer">
</fa-icon>
<mat-menu #beforeMenu="matMenu" xPosition="before">
<button (click)="deleteMessage(msg._id)" class="menuButton">Apagar mensagem</button>
<button (click)="deleteMessage(msg._id, msg)" class="menuButton">Apagar mensagem</button>
</mat-menu>
</div>
<div class="title">
<ion-label (click)="testEditMessage(msg)">{{msg.u.name}}</ion-label>
<ion-label>{{msg.u.name}}</ion-label>
<span class="time">{{msg.duration}}</span>
</div>
<div class="d-flex justify-space-between">
<ion-label class="flex-0">{{msg.msg}}</ion-label>
<ion-label class="float-status-all float-status">
<ion-icon *ngIf="!msg.offline && msg.viewed.length == 0" src="assets/images/check-double-solid.svg">
</ion-icon>
<ion-icon *ngIf="msg.offline && msg.viewed.length == 0" src="assets/images/check-solid.svg"></ion-icon>
<ion-icon *ngIf="msg.viewed.length == 1" src="assets/images/check-double-solid -viewed.svg"></ion-icon>
<ion-label *ngIf="msg.delate == false" class="flex-0">{{msg.msg}}</ion-label>
<ion-label *ngIf="msg.delate == true" class="flex-0">Apagou a mensagem</ion-label>
<ion-label class="float-status-all float-status" >
<ion-icon *ngIf="msg.messageSend == false" src="assets/images/clock-regular.svg"></ion-icon>
<ion-icon *ngIf="msg.messageSend == true && msg.received.length == 0" src="assets/images/check-solid.svg"></ion-icon>
<ion-icon *ngIf="msg.messageSend && msg.received.length >= 1 && msg.viewed.length == 0" src="assets/images/check-double-solid.svg"></ion-icon>
<ion-icon *ngIf="msg.viewed.length >= 1" src="assets/images/check-double-solid -viewed.svg"></ion-icon>
</ion-label>
{{last ? scrollToBottom() : ''}}
</div>
</div>
<div *ngIf="msg.file">
<div *ngIf="msg.file && msg.delate == false">
<div class='message-item incoming-{{msg.u.username!=loggedUser.me.username}} max-width-45'
*ngIf="msg.file.type != 'application/meeting'">
<div class="message-item-options d-flex justify-content-end">
<fa-icon [matMenuTriggerFor]="beforeMenu" icon="chevron-down" class="message-options-icon cursor-pointer">
</fa-icon>
<mat-menu #beforeMenu="matMenu" xPosition="before">
<button (click)="deleteMessage(msg._id)" class="menuButton">Apagar mensagem</button>
<button (click)="deleteMessage(msg._id, msg)" class="menuButton">Apagar mensagem</button>
</mat-menu>
</div>
<div class="title">
<ion-label>{{msg.u.name}} </ion-label>
<ion-label>{{msg.u.name}}</ion-label>
<span class="time">{{msg.duration}}</span>
</div>
<div>
@@ -83,17 +83,16 @@
<div *ngFor="let file of msg.attachments">
<div *ngIf="msg.file.type == 'application/img'" (click)="openPreview(msg)" dfsdvsvs>
<img src={{msg.attachments[0].image_url}} alt="image">
<ion-label class="float-status-image float-status-all">
<ion-icon *ngIf="!msg.offline && msg.viewed.length == 0" src="assets/images/check-double-solid.svg">
</ion-icon>
<ion-icon *ngIf="msg.offline && msg.viewed.length == 0" src="assets/images/check-solid.svg">
</ion-icon>
<ion-icon *ngIf="msg.viewed.length == 1" src="assets/images/check-double-solid -viewed.svg">
</ion-icon>
<ion-label class="float-status-all float-status" >
<ion-icon *ngIf="msg.messageSend == false" src="assets/images/clock-regular.svg"></ion-icon>
<ion-icon *ngIf="msg.messageSend == true && msg.received.length == 0" src="assets/images/check-solid.svg"></ion-icon>
<ion-icon *ngIf="msg.messageSend && msg.received.length >= 1 && msg.viewed.length == 0" src="assets/images/check-double-solid.svg"></ion-icon>
<ion-icon *ngIf="msg.viewed.length >= 1" src="assets/images/check-double-solid -viewed.svg"></ion-icon>
</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>
@@ -110,19 +109,21 @@
<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>
<ion-icon *ngIf="msg.offline && msg.viewed.length == 0" src="assets/images/check-solid.svg">
</ion-icon>
<ion-icon *ngIf="msg.viewed.length == 1" src="assets/images/check-double-solid -viewed.svg">
</ion-icon>
<ion-label class="float-status-all float-status" >
<ion-icon *ngIf="msg.messageSend == false" src="assets/images/clock-regular.svg"></ion-icon>
<ion-icon *ngIf="msg.messageSend == true && msg.received.length == 0" src="assets/images/check-solid.svg"></ion-icon>
<ion-icon *ngIf="msg.messageSend && msg.received.length >= 1 && msg.viewed.length == 0" src="assets/images/check-double-solid.svg"></ion-icon>
<ion-icon *ngIf="msg.viewed.length >= 1" src="assets/images/check-double-solid -viewed.svg"></ion-icon>
</ion-label>
</div>
</div>
@@ -147,56 +148,13 @@
</div>
</div>
<!-- <div class='message-item incoming-{{msg.u.username!=loggedUser.me.username}} max-width-45' *ngIf="msg.msg ==''">
<div *ngIf="msg.file">
<div *ngIf="msg.file.type == 'application/img'">
<div class="message-item-options d-flex justify-content-end">
<fa-icon [matMenuTriggerFor]="beforeMenu" icon="chevron-down" class="message-options-icon cursor-pointer">
</fa-icon>
<mat-menu #beforeMenu="matMenu" xPosition="before">
<button (click)="deleteMessage(msg._id)" class="menuButton">Apagar mensagem</button>
</mat-menu>
</div>
<div class="title">
<ion-label>{{msg.u.name}}</ion-label>
<span class="time">{{msg.duration}}</span>
</div>
<div>
<ion-label>{{msg.msg}}</ion-label>
<div *ngIf="msg.file" class="message-attachments">
<div>
<div (click)="openPreview(msg)">
File
<img *ngIf="msg.attachments[0].image_url" src="{{msg.attachments[0].image_url}}" alt="image">
</div>
</div>
</div>
{{last ? scrollToBottom() : ''}}
</div>
</div>
</div>
</div> -->
<div *ngIf="msg.file && msg.delate == true">
Apagou a mensagem
</div>
<!-- <div *ngIf="msg.file">
<div class="info-meeting" *ngIf="msg.file.type == 'application/meeting'">
<ion-label class="info-meeting-small">{{msg.u.name}} criou esta reunião</ion-label><br />
<button (click)="goToEvent(msg.file.id)" class="btn-no-color info-meeting-normal">
<ion-label class="info-meeting-normal">{{msg.file.subject}}</ion-label>
</button><br />
<ion-label class="info-meeting-medium">
<ion-icon name="calendar-outline"></ion-icon> De {{showDateDuration(msg.file.start_date)}} a
{{showDateDuration(msg.file.end_date)}}
</ion-label><br />
<ion-label class="info-meeting-medium">
<ion-icon></ion-icon>
<ion-icon name="location-outline"></ion-icon> {{msg.file.venue}}
</ion-label><br />
</div>
{{last ? scrollToBottom() : ''}}
</div> -->
</div>
</div>
<ion-fab horizontal="end" vertical="bottom" slot="fixed">
<ion-fab-button *ngIf="scrollToBottomBtn" (click)="scrollToBottomClicked()" color="light" size="small">
<ion-icon name="chevron-down"></ion-icon>
@@ -206,21 +164,24 @@
</ion-content>
<ion-footer>
<div class="typing">
<ngx-letters-avatar *ngIf="showAvatar" [avatarName]="wsChatMethodsService.getDmRoom(roomId).name" [width]="30"
[circular]="true" fontFamily="Open Sans"></ngx-letters-avatar>
A escrever...
<ion-footer (click)="wsChatMethodsService.getDmRoom(roomId).sendReadMessage()">
<div class="typing" *ngIf="wsChatMethodsService.getDmRoom(roomId).otherUserType == true" >
<ngx-letters-avatar *ngIf="showAvatar"
[avatarName]= "wsChatMethodsService.getDmRoom(roomId).name"
[width]="30"
[circular]="true"
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>
@@ -242,31 +203,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>
@@ -327,5 +327,5 @@ display: block;
.typing ngx-letters-avatar {
padding-left: 10px;
}
padding-right: 5px;
}
+164 -16
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: string;
massages: MessageService[] = []
showAvatar = true
showAvatar = true;
recording = false;
allowTyping = true;
storedFileNames = [];
lastAudioRecorded = '';
audioRecorded:any = "";
audioDownloaded:any = "";
durationDisplay = '';
duration = 0;
constructor(
public popoverController: PopoverController,
@@ -107,11 +118,11 @@ 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'];
}
ngOnChanges(changes: SimpleChanges): void {
this.wsChatMethodsService.getDmRoom(this.roomId).loadHistory({})
@@ -130,13 +141,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();
}
@@ -176,7 +191,6 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
}
scrollToBottomClicked = () => {
console.log('scroll')
try {
this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
//this.scrollingOnce = false;
@@ -209,6 +223,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,9 +354,52 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
this.wsChatMethodsService.getDmRoom(this.roomId).send({})
}
deleteMessage(msgId: string) {
const room = this.wsChatMethodsService.getDmRoom(this.roomId)
this.alertService.confirmDeleteMessage(msgId, room);
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, msg:MessageService) {
/* msg.delateStatusFalse()
this.wsChatMethodsService.getDmRoom(this.roomId).sendDeleteRequest(msgId) */
}
async viewDocument(msg: any, url?: string) {
@@ -333,14 +480,12 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
}
getChatMembers() {
console.log(this.roomId);
// console.log(this.roomId);
//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;
});
}
@@ -471,6 +616,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
temporaryData: formData,
attachments: [{
"title": file.path,
// "image_url": "",
//"image_url": 'data:image/jpeg;base64,' + file.base64String,
"text": "description",
"title_link_download": false,
@@ -600,6 +746,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
const roomId = this.roomId
const file: any = await this.fileService.getFileFromDevice(types);
console.log('Add file', file)
/* const imageData = await this.fileToBase64Service.convert(file).then((filee) => {
console.log('Add file', filee)
@@ -763,9 +910,10 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
ts: msg.attachments[0].ts
}
await this.storage.set(msg.file.guid, this.downloadFile).then(() => {
console.log('IMAGE SAVED')
});
// save the changes to the storage
//msg.save()
}
});
@@ -821,7 +969,7 @@ export class MessagesPage implements OnInit, OnChanges, AfterViewInit, OnDestroy
testEditMessage(msg: MessageService) {
msg.receptorReceive()
// msg.receptorReceive()
// alert('cool!')
}
}
@@ -3,7 +3,7 @@
<div class="title">
<div class="thetitle"><ion-label >Todas as tarefas</ion-label></div>
<div class="theicon">
<button class="btn-no-color" (click)="refreshing()">
<button title="Atualizar" class="btn-no-color" (click)="refreshing()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -6,7 +6,7 @@
<ion-label *ngIf="loggeduser.Profile =='PR'" >Despachos</ion-label>
</div>
<div class="theicon">
<button class="btn-no-color" (click)="doRefresh()">
<button title="Atualizar" class="btn-no-color" (click)="doRefresh()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -3,7 +3,7 @@
<div class="title">
<div class="thetitle"><ion-label >Despachos</ion-label></div>
<div class="theicon">
<button class="btn-no-color" (click)="refreshing()">
<button title="Atualizar" class="btn-no-color" (click)="refreshing()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -3,7 +3,7 @@
<div class="title">
<div class="thetitle"><ion-label >Diplomas</ion-label></div>
<div class="theicon">
<button class="btn-no-color" (click)="doRefresh()">
<button title="Atualizar" class="btn-no-color" (click)="doRefresh()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -3,7 +3,7 @@
<div class="title">
<div class="thetitle"><ion-label >Diplomas</ion-label></div>
<div class="theicon">
<button class="btn-no-color" (click)="doRefresh()">
<button title="Atualizar" class="btn-no-color" (click)="doRefresh()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -3,7 +3,7 @@
<div class="title width-100">
<div class="title-container d-flex justify-space-between">
<span class="text-center mt-0 aside-title px-20"><label>Eventos para Aprovação</label></span>
<button class="btn-no-color" (click)="doRefresh()">
<button title="Atualizar" title="Atualizar" class="btn-no-color" (click)="doRefresh()">
<ion-icon class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -3,7 +3,7 @@
<div class="title">
<div class="thetitle"><ion-label >Expediente</ion-label></div>
<div class="theicon">
<button class="btn-no-color" (click)="refreshing()">
<button title="Atualizar" class="btn-no-color" (click)="refreshing()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -3,7 +3,7 @@
<div class="title">
<div class="thetitle"><ion-label >Expediente</ion-label></div>
<div class="theicon">
<button class="btn-no-color" (click)="doRefresh()">
<button title="Atualizar" class="btn-no-color" (click)="doRefresh()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -3,7 +3,7 @@
<div class="title">
<div class="thetitle"><ion-label >Pedidos</ion-label></div>
<div class="theicon">
<button class="btn-no-color" (click)="doRefresh()">
<button title="Atualizar" class="btn-no-color" (click)="doRefresh()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
@@ -3,7 +3,7 @@
<div class="title">
<div class="thetitle"><ion-label >Pendentes</ion-label></div>
<div class="theicon">
<button class="btn-no-color" (click)="doRefresh()">
<button title="Atualizar" class="btn-no-color" (click)="doRefresh()">
<ion-icon slot="end" class="title-icon font-awesome" name="reload-circle"></ion-icon>
</button>
</div>
+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;
}
}
}
+1 -1
View File
@@ -88,7 +88,7 @@ class SessionService {
this.reset(new UserSession())
}
private save() {
save() {
localstoreService.set(this.keyName, {
user: this._user