mirror of
https://code.equilibrium.co.ao/ITO/doneit-web.git
synced 2026-04-19 21:06:06 +00:00
improve profile reactiveness and action page performance
This commit is contained in:
@@ -100,6 +100,10 @@ import { environment } from 'src/environments/environment';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { calendarReducer } from './services/Repositorys/Agenda/agenda-memory-source.service';
|
||||
|
||||
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
|
||||
// import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
// import { AngularFireModule } from '@angular/fire';
|
||||
// import { AngularFireMessagingModule } from '@angular/fire/messaging';
|
||||
@@ -184,7 +188,10 @@ import { FirebaseX } from '@ionic-native/firebase-x/ngx'; */
|
||||
// options
|
||||
DeplomaOptionsPageModule,
|
||||
CreateProcessPageModule,
|
||||
ImageCropperModule
|
||||
ImageCropperModule,
|
||||
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
],
|
||||
entryComponents: [
|
||||
DiplomaOptionsPage,
|
||||
|
||||
@@ -362,7 +362,7 @@ const routes: Routes = [
|
||||
},
|
||||
|
||||
],
|
||||
canActivate: [AuthGuard]
|
||||
// canActivate: [AuthGuard]
|
||||
|
||||
},
|
||||
{
|
||||
@@ -376,7 +376,7 @@ const routes: Routes = [
|
||||
|
||||
},
|
||||
],
|
||||
canActivate: [AuthGuard]
|
||||
// canActivate: [AuthGuard]
|
||||
},
|
||||
{
|
||||
path: 'inactivity',
|
||||
@@ -386,7 +386,7 @@ const routes: Routes = [
|
||||
loadChildren: ()=> import('../pages/inactivity/inactivity.module').then(m => m.InactivityPageModule)
|
||||
},
|
||||
],
|
||||
canActivate: [InactivityGuard]
|
||||
// canActivate: [InactivityGuard]
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
@@ -396,7 +396,7 @@ const routes: Routes = [
|
||||
loadChildren: ()=> import('../pages/inactivity/inactivity.module').then(m => m.InactivityPageModule)
|
||||
},
|
||||
],
|
||||
canActivate: [InactivityGuard]
|
||||
// canActivate: [InactivityGuard]
|
||||
},
|
||||
{
|
||||
path: 'pin',
|
||||
@@ -406,7 +406,7 @@ const routes: Routes = [
|
||||
loadChildren: ()=> import('../pages/inactivity/inactivity.module').then(m => m.InactivityPageModule)
|
||||
},
|
||||
],
|
||||
canActivate: [InactivityGuard]
|
||||
// canActivate: [InactivityGuard]
|
||||
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { CameraSource } from "@capacitor/camera";
|
||||
|
||||
export interface ITakePictureParams {
|
||||
width?: number,
|
||||
height?: number,
|
||||
quality: number,
|
||||
source: CameraSource
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
|
||||
import { ITakePictureParams } from './adaptor';
|
||||
import { err, ok, Result } from 'neverthrow';
|
||||
import { TracingType, XTracerAsync } from 'src/app/services/monitoring/opentelemetry/tracer';
|
||||
import { error } from '../../services/Either/index';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CameraService {
|
||||
|
||||
constructor() { }
|
||||
|
||||
|
||||
async takePicture(data: ITakePictureParams, tracing?: TracingType): Promise<Result<string, any>> {
|
||||
try {
|
||||
tracing?.addEvent('take picture')
|
||||
const capturedImage = await Camera.getPhoto({
|
||||
...data,
|
||||
// allowEditing: true,
|
||||
resultType: CameraResultType.Base64,
|
||||
});
|
||||
tracing?.addEvent('end take picture')
|
||||
tracing.log('image log', {
|
||||
base64: capturedImage.base64String
|
||||
})
|
||||
return ok(capturedImage.base64String)
|
||||
} catch(error) {
|
||||
tracing?.log("camera error", {
|
||||
error
|
||||
})
|
||||
tracing?.hasError('capture image')
|
||||
return err(error)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -114,23 +114,23 @@ export class TokenInterceptor implements HttpInterceptor {
|
||||
}),
|
||||
catchError((error) => {
|
||||
console.log(error)
|
||||
SessionStore.user.Authorization = SessionStore.user.Authorization;
|
||||
SessionStore.user.RefreshToken = SessionStore.user.RefreshToken;
|
||||
SessionStore.setInativity(false)
|
||||
/* SessionStore.setUrlBeforeInactivity(this.router.url); */
|
||||
// SessionStore.user.Authorization = SessionStore.user.Authorization;
|
||||
// SessionStore.user.RefreshToken = SessionStore.user.RefreshToken;
|
||||
// SessionStore.setInativity(false)
|
||||
// /* SessionStore.setUrlBeforeInactivity(this.router.url); */
|
||||
|
||||
if (environment.production) {
|
||||
window.location.pathname = '/auth'
|
||||
} else {
|
||||
const pathBeforeGoOut = window.location.pathname
|
||||
console.log('Before auth',window.location.pathname)
|
||||
this.router.navigateByUrl('/auth', { replaceUrl: true }).then(() =>{
|
||||
if(pathBeforeGoOut != "/auth") {
|
||||
this.httpErrorHandle.httpsSucessMessagge('sessonExpired')
|
||||
}
|
||||
// if (environment.production) {
|
||||
// window.location.pathname = '/auth'
|
||||
// } else {
|
||||
// const pathBeforeGoOut = window.location.pathname
|
||||
// console.log('Before auth',window.location.pathname)
|
||||
// this.router.navigateByUrl('/auth', { replaceUrl: true }).then(() =>{
|
||||
// if(pathBeforeGoOut != "/auth") {
|
||||
// this.httpErrorHandle.httpsSucessMessagge('sessonExpired')
|
||||
// }
|
||||
|
||||
})
|
||||
}
|
||||
// })
|
||||
// }
|
||||
return of(false);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -365,7 +365,6 @@ export class DocumentSetUpMeetingPage implements OnInit {
|
||||
if(!isHttpError(value.error)) {
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico. #1')
|
||||
console.log(value.error)
|
||||
tracing.bugFlag()
|
||||
} else {
|
||||
this.httpErroHandle.httpStatusHandle(value.error.status)
|
||||
}
|
||||
@@ -379,7 +378,6 @@ export class DocumentSetUpMeetingPage implements OnInit {
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.setAttribute('no', 'this.selectedUserCalendar')
|
||||
tracing.bugFlag()
|
||||
}
|
||||
|
||||
laoder.remove();
|
||||
|
||||
@@ -8,12 +8,26 @@ import { EditProfilePageRoutingModule } from './edit-profile-routing.module';
|
||||
|
||||
import { EditProfilePage } from './edit-profile.page';
|
||||
|
||||
|
||||
|
||||
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
IonicModule,
|
||||
EditProfilePageRoutingModule
|
||||
EditProfilePageRoutingModule,
|
||||
|
||||
FontAwesomeModule,
|
||||
MatMenuModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
],
|
||||
declarations: [EditProfilePage]
|
||||
})
|
||||
|
||||
@@ -58,39 +58,46 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="d-flex align-center flex-column" (click)="takePicture()">
|
||||
<div class="d-flex justify-center align-center " >
|
||||
<div mat-icon-button [matMenuTriggerFor]="menu" aria-label="Example icon-button with a menu">
|
||||
<div class="d-flex align-center flex-column" >
|
||||
|
||||
<div *ngIf="profilePicture == '' ">
|
||||
<img
|
||||
<div *ngIf="profilePictureSubject == undefined ">
|
||||
<img
|
||||
|
||||
class="profile-pic" src="assets/images/theme/gov/icons-profile.svg">
|
||||
class="profile-pic" src="assets/images/theme/gov/icons-profile.svg">
|
||||
|
||||
<!-- <img *ngIf="SessionStore.user.RoleDescription == 'Presidente da República' " class="profile-pic"
|
||||
src='assets/images/presidente.png'>
|
||||
<img *ngIf="SessionStore.user.RoleDescription == 'Ministro e Director do Gabinete do PR' "
|
||||
class="profile-pic" src='assets/images/ministro.png'>
|
||||
<img *ngIf="SessionStore.user.RoleDescription == 'Secretário Geral' " class="profile-pic"
|
||||
src='assets/images/secretaria_geral.png'> -->
|
||||
</div>
|
||||
|
||||
<div *ngIf="profilePicture != '' ">
|
||||
<img class="profile-pic" src={{profilePicture}}>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- <ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="profile-pic"
|
||||
src="assets/images/icons-default-profile.svg"></ion-icon>
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="profile-pic"
|
||||
src="assets/images/theme/gov/icons-profile.svg"></ion-icon>
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'doneIt' " class="profile-pic"
|
||||
src="assets/images/theme/{{ThemeService.currentTheme}}/icons-profile.svg"></ion-icon> -->
|
||||
<div
|
||||
style="background: black;width: 50px;height: 50px;position: relative;top: -33px;left: 38px;border-radius: 55px;overflow: hidden;border: 5px solid white;">
|
||||
<img src="assets/images/camera.png">
|
||||
<!-- <img *ngIf="SessionStore.user.RoleDescription == 'Presidente da República' " class="profile-pic"
|
||||
src='assets/images/presidente.png'>
|
||||
<img *ngIf="SessionStore.user.RoleDescription == 'Ministro e Director do Gabinete do PR' "
|
||||
class="profile-pic" src='assets/images/ministro.png'>
|
||||
<img *ngIf="SessionStore.user.RoleDescription == 'Secretário Geral' " class="profile-pic"
|
||||
src='assets/images/secretaria_geral.png'> -->
|
||||
</div>
|
||||
|
||||
<div *ngIf="(profilePictureSubject | async) as calendarData">
|
||||
<img class="profile-pic" src={{calendarData.base64}}>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
*ngIf="camecaIcons"
|
||||
style="background: black;width: 50px;height: 50px;position: relative;margin-top: -33px;left: 38px;border-radius: 55px;overflow: hidden;border: 5px solid white;">
|
||||
<img src="assets/images/camera.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button (click)="uploadPicture(CameraSource.Camera)" mat-menu-item>
|
||||
<span>take picture</span>
|
||||
</button>
|
||||
<button (click)="uploadPicture(CameraSource.Photos)" mat-menu-item >
|
||||
<span>Ficheiro</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="profile-info">
|
||||
|
||||
@@ -6,12 +6,16 @@ import { SessionStore } from 'src/app/store/session.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { BackgroundService } from 'src/app/services/background.service';
|
||||
import { ThemeService } from 'src/app/services/theme.service';
|
||||
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
|
||||
import { Filesystem, Directory } from '@capacitor/filesystem';
|
||||
import { File } from '@awesome-cordova-plugins/file/ngx';
|
||||
import { CameraSource } from '@capacitor/camera';
|
||||
import { StorageService } from 'src/app/services/storage.service';
|
||||
import { AttachmentsService } from 'src/app/services/attachments.service';
|
||||
|
||||
import { CameraService } from 'src/app/infra/camera/camera.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
import { HttpErrorHandle } from 'src/app/services/http-error-handle.service';
|
||||
import { TracingType, XTracerAsync } from 'src/app/services/monitoring/opentelemetry/tracer';
|
||||
import { UserRepositoryService } from 'src/app/module/user/data/user-repository.service';
|
||||
import { isHttpError } from 'src/app/services/http.service';
|
||||
import { UserProfilePicture } from 'src/app/module/user/data/datasource/user-local-repository.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-profile',
|
||||
@@ -27,19 +31,28 @@ export class EditProfilePage implements OnInit {
|
||||
capturedImageTitle = '';
|
||||
profilePicture = "";
|
||||
|
||||
profilePictureSubject: Observable<UserProfilePicture>
|
||||
camecaIcons = false
|
||||
|
||||
constructor(private modalController: ModalController,
|
||||
private animationController: AnimationController,
|
||||
public platform: Platform,
|
||||
private BackgroundService: BackgroundService,
|
||||
public ThemeService: ThemeService,
|
||||
private file: File,
|
||||
private storageService: StorageService,
|
||||
private attachmentService: AttachmentsService
|
||||
private CameraService: CameraService,
|
||||
private toastService: ToastService,
|
||||
private httpErrorHandle: HttpErrorHandle,
|
||||
private UserRepositoryService: UserRepositoryService
|
||||
|
||||
) { }
|
||||
) {
|
||||
this.profilePictureSubject = this.UserRepositoryService.getProfilePictureLive() as any
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
setTimeout(() => {
|
||||
this.camecaIcons = true
|
||||
}, 100)
|
||||
this.getProfilpictureFromStorage();
|
||||
}
|
||||
getProfilpictureFromStorage() {
|
||||
@@ -54,27 +67,7 @@ export class EditProfilePage implements OnInit {
|
||||
this.profilePicture = "";
|
||||
})
|
||||
}
|
||||
/* getProfilpicture(guid) {
|
||||
console.log('Get picture ', guid.path)
|
||||
this.attachmentService.getUserProfilePhoto().subscribe(async (picture: any) => {
|
||||
|
||||
console.log('Get picture ', picture)
|
||||
this.storageService.store(this.SessionStore.user.RoleID.toString() + "guid", guid.path)
|
||||
this.storageService.store(this.SessionStore.user.RoleID.toString(), picture).then((value) => {
|
||||
this.profilePicture = picture
|
||||
this.SessionStore.user.UserPhoto = picture;
|
||||
|
||||
|
||||
console.log('picture saved')
|
||||
}).catch((error) => {
|
||||
console.log('picture not saved')
|
||||
});
|
||||
|
||||
}, ((error) => {
|
||||
console.log('Error get profile picture: ', error)
|
||||
}))
|
||||
|
||||
} */
|
||||
|
||||
|
||||
close() {
|
||||
this.modalController.dismiss();
|
||||
@@ -171,48 +164,65 @@ export class EditProfilePage implements OnInit {
|
||||
this.BackgroundService.paint();
|
||||
}
|
||||
|
||||
CameraSource = CameraSource
|
||||
@XTracerAsync({name:'edit-profile/takePicture', bugPrint: true, autoFinish: false})
|
||||
async uploadPicture(source: CameraSource, tracing?: TracingType) {
|
||||
|
||||
async takePicture() {
|
||||
const capturedImage = await Camera.getPhoto({
|
||||
const capturedImage = await this.CameraService.takePicture({
|
||||
width: 250,
|
||||
height: 250,
|
||||
quality: 100,
|
||||
// allowEditing: true,
|
||||
resultType: CameraResultType.Base64,
|
||||
source: CameraSource.Camera
|
||||
});
|
||||
source: source
|
||||
}, tracing)
|
||||
|
||||
this.capturedImage = capturedImage.base64String;
|
||||
var object = JSON.stringify({
|
||||
"ImageBase64": this.capturedImage
|
||||
if(capturedImage.isOk()) {
|
||||
|
||||
this.capturedImage = capturedImage.value;
|
||||
var object = {
|
||||
"ImageBase64": this.capturedImage
|
||||
}
|
||||
|
||||
tracing.addEvent('serialize image')
|
||||
|
||||
const guid = await this.UserRepositoryService.addUserProfilePhoto(object)
|
||||
|
||||
|
||||
if(guid.isOk()) {
|
||||
tracing.addEvent('upload image')
|
||||
|
||||
const base = await this.UserRepositoryService.getUserProfilePhoto(guid.value, tracing)
|
||||
|
||||
if(base.isOk()) {
|
||||
tracing.addEvent('download image')
|
||||
this.storageService.store(this.SessionStore.user.RoleID.toString(), 'data:image/jpeg;base64,'+base.value).then((value) => {
|
||||
tracing.addEvent('store image in')
|
||||
this.profilePicture = 'data:image/jpeg;base64,' + base.value;
|
||||
|
||||
tracing.setAttribute("picture.save", "true")
|
||||
tracing.finish()
|
||||
}).catch((error) => {
|
||||
tracing.setAttribute("picture.save", "false")
|
||||
tracing.finish()
|
||||
});
|
||||
} else {
|
||||
if(!isHttpError(base.error)) {
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico.')
|
||||
} else {
|
||||
this.httpErrorHandle.httpStatusHandle(base.error)
|
||||
}
|
||||
tracing.finish()
|
||||
}
|
||||
} else {
|
||||
|
||||
if(!isHttpError(guid.error)) {
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico.')
|
||||
} else {
|
||||
this.httpErrorHandle.httpStatusHandle(guid.error)
|
||||
}
|
||||
tracing.finish()
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
console.log('ATTACHME ', object)
|
||||
|
||||
this.attachmentService.addUserProfilePhoto(object).subscribe((guid) => {
|
||||
console.log('GUID ', guid)
|
||||
console.log(this.SessionStore.user.RoleID.toString())
|
||||
|
||||
//get user profile picture base64
|
||||
this.attachmentService.getUserProfilePhoto(guid).subscribe((base) => {
|
||||
console.log('before picture saved',base)
|
||||
this.storageService.store(this.SessionStore.user.RoleID.toString(), 'data:image/jpeg;base64,'+base).then((value) => {
|
||||
this.profilePicture = 'data:image/jpeg;base64,' + base;
|
||||
|
||||
|
||||
console.log('picture saved',value)
|
||||
}).catch((error) => {
|
||||
console.log('picture not saved')
|
||||
});
|
||||
|
||||
},(error) => {
|
||||
console.log('profile picture erro: ', error)
|
||||
})
|
||||
|
||||
/* this.getProfilpicture(guid); */
|
||||
}, ((error) => {
|
||||
console.log('Erro Upload profile picture ', error)
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
@@ -268,4 +278,3 @@ export class EditProfilePage implements OnInit {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
<div class="d-flex align-center flex-column">
|
||||
|
||||
<div *ngIf="profilePicture == ''">
|
||||
<div *ngIf="profilePictureSubject == undefined ">
|
||||
<img
|
||||
class="profile-pic" src="assets/images/theme/gov/icons-profile.svg">
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
src='assets/images/secretaria_geral.png'> -->
|
||||
</div>
|
||||
|
||||
<div *ngIf="profilePicture != ''" class="profile-pic">
|
||||
<div *ngIf="(profilePictureSubject | async) as calendarData"class="profile-pic">
|
||||
<img class="profile-pic"
|
||||
src={{profilePicture}}>
|
||||
src={{calendarData.base64}}>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ import { NotificationRepositoryService } from 'src/app/module/notification/data/
|
||||
import { Observable } from 'rxjs';
|
||||
import { NotificationTable } from 'src/app/module/notification/data/infra/db/notification.db';
|
||||
import { isHttpError } from 'src/app/services/http.service';
|
||||
import { UserRepositoryService } from 'src/app/module/user/data/user-repository.service';
|
||||
import { UserProfilePicture } from 'src/app/module/user/data/datasource/user-local-repository.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-profile',
|
||||
templateUrl: './profile.page.html',
|
||||
@@ -50,6 +53,7 @@ export class ProfilePage implements OnInit {
|
||||
notificationList$: Observable<NotificationTable[]>
|
||||
|
||||
objectRead = {}
|
||||
profilePictureSubject: Observable<UserProfilePicture>
|
||||
|
||||
constructor(
|
||||
private modalController: ModalController,
|
||||
@@ -64,10 +68,11 @@ export class ProfilePage implements OnInit {
|
||||
private authservice: AuthService,
|
||||
private agendaDataRepository: AgendaDataRepositoryService,
|
||||
private toastService: ToastService,
|
||||
private notificationRepositoryService: NotificationRepositoryService
|
||||
private notificationRepositoryService: NotificationRepositoryService,
|
||||
private UserRepositoryService: UserRepositoryService
|
||||
) {
|
||||
|
||||
|
||||
this.profilePictureSubject = this.UserRepositoryService.getProfilePictureLive() as any
|
||||
window['e'] = () => {
|
||||
console.log(
|
||||
this.zone.run(() => this.router.navigate(['/home/chat']))
|
||||
|
||||
@@ -137,7 +137,6 @@ export class ViewEventPage implements OnInit {
|
||||
} else {
|
||||
tracing.setAttribute('eventId', this.eventId)
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
if(!isHttpError(res.error)) {
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico. #13')
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { z } from 'zod';
|
||||
import { Dexie, EntityTable, liveQuery } from 'Dexie';
|
||||
|
||||
const UserProfilePicture = z.object({
|
||||
base64: z.string(),
|
||||
id: z.number().optional(),
|
||||
})
|
||||
|
||||
const UserTable = z.object({
|
||||
ChunksBase64: z.number(),
|
||||
});
|
||||
|
||||
export type UserProfilePicture = z.infer<typeof UserProfilePicture>
|
||||
|
||||
// Database declaration (move this to its own module also)
|
||||
const session = new Dexie('session') as Dexie & {
|
||||
profilePicture: EntityTable<UserProfilePicture, 'id'>;
|
||||
};
|
||||
|
||||
session.version(1).stores({
|
||||
profilePicture: '++id, base64'
|
||||
});
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserLocalRepositoryService {
|
||||
|
||||
constructor() { }
|
||||
|
||||
|
||||
async addProfilePicture(data: UserProfilePicture) {
|
||||
try {
|
||||
await session.transaction('rw', session.profilePicture, async () => {
|
||||
// Clear existing records from myTable
|
||||
await session.profilePicture.clear();
|
||||
await session.profilePicture.add(data);
|
||||
});
|
||||
console.log('Clear and add operations completed within transaction.');
|
||||
} catch (error) {
|
||||
console.error('Error performing transaction:', error, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getLiveProfilePicture() {
|
||||
return liveQuery(() =>
|
||||
session.profilePicture.orderBy('id').reverse().first()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserMemoryRepositoryService {
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpService } from 'src/app/services/http.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { IProfilePictureInputDTO } from '../dto/profilePictureInputDTO';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserRemoteRepositoryService {
|
||||
|
||||
constructor(
|
||||
private httpService: HttpService
|
||||
) { }
|
||||
|
||||
getUserProfilePhoto(guid: string) {
|
||||
const geturl = environment.apiURL + 'UserAuthentication/GetPhoto';
|
||||
|
||||
const params = {
|
||||
UserPhoto: guid
|
||||
}
|
||||
|
||||
return this.httpService.get<string>(`${geturl}`, params);
|
||||
}
|
||||
|
||||
addUserProfilePhoto(data: IProfilePictureInputDTO) {
|
||||
const geturl = environment.apiURL + 'UserAuthentication/AddPhoto';
|
||||
|
||||
let http = new HttpHeaders();
|
||||
|
||||
http = http.set('content-type', "application/json");
|
||||
http = http.set('accept', "application/json");
|
||||
let options = {
|
||||
headers: http
|
||||
};
|
||||
|
||||
return this.httpService.post<string>(`${geturl}`, data, options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const profilePictureInputDTOSchema = z.object({
|
||||
ImageBase64: z.string()
|
||||
})
|
||||
|
||||
// const profilePictureInputDTOSchema = z.string()
|
||||
|
||||
export type IProfilePictureInputDTO = z.infer<typeof profilePictureInputDTOSchema>
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UserRemoteRepositoryService } from './datasource/user-remote-repository.service';
|
||||
import { IProfilePictureInputDTO } from './dto/profilePictureInputDTO';
|
||||
import { UserLocalRepositoryService } from './datasource/user-local-repository.service';
|
||||
import { TracingType } from 'src/app/services/monitoring/opentelemetry/tracer';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserRepositoryService {
|
||||
|
||||
constructor(
|
||||
private remote: UserRemoteRepositoryService,
|
||||
private local: UserLocalRepositoryService
|
||||
) { }
|
||||
|
||||
async getUserProfilePhoto(guid: string , tracing?: TracingType) {
|
||||
const result = await this.remote.getUserProfilePhoto(guid)
|
||||
|
||||
if(result.isOk()) {
|
||||
this.local.addProfilePicture({base64: 'data:image/jpeg;base64,' + result.value})
|
||||
} else {
|
||||
tracing?.setAttribute("picture.upload", "false")
|
||||
tracing?.hasError("cant upload picture")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async addUserProfilePhoto(data: IProfilePictureInputDTO, tracing?: TracingType) {
|
||||
const result = await this.remote.addUserProfilePhoto(data)
|
||||
|
||||
|
||||
if(result.isOk()) {
|
||||
this.local.addProfilePicture({base64: 'data:image/jpeg;base64,' + data.ImageBase64})
|
||||
} else {
|
||||
tracing?.setAttribute("picture.upload", "false")
|
||||
tracing?.hasError("cant upload picture")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
getProfilePictureLive() {
|
||||
return this.local.getLiveProfilePicture()
|
||||
}
|
||||
}
|
||||
@@ -782,7 +782,6 @@ export class AgendaPage implements OnInit {
|
||||
}
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
load++
|
||||
if (load == (selectedCalendarIds).length) {
|
||||
@@ -801,7 +800,6 @@ export class AgendaPage implements OnInit {
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.setAttribute('error', 'selectedCalendar.wxUserId')
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico. #2')
|
||||
tracing.bugFlag()
|
||||
}
|
||||
|
||||
tracing.addEvent('load range end')
|
||||
|
||||
@@ -177,7 +177,6 @@ export class ViewEventPage implements OnInit {
|
||||
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
// this.toastService.badRequest('Este evento já não existe na sua agenda')
|
||||
this.RouteService.goBack();
|
||||
|
||||
@@ -125,13 +125,11 @@ export class ApproveEventPage implements OnInit {
|
||||
}
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
this.httpErrorHandle.httpStatusHandle(res.error)
|
||||
} else if(!isHttpError(res.error)) {
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico. #12')
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ export class EventListPage implements OnInit {
|
||||
} else {
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
if(!isHttpError(allEvents.error)) {
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico. #4')
|
||||
}
|
||||
|
||||
-2
@@ -395,7 +395,6 @@ export class BookMeetingModalPage implements OnInit {
|
||||
if(!isHttpError(value.error)) {
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico. #1')
|
||||
console.log(value.error)
|
||||
tracing.bugFlag()
|
||||
} else {
|
||||
this.httpErroHandle.httpStatusHandle(value.error.status)
|
||||
}
|
||||
@@ -409,7 +408,6 @@ export class BookMeetingModalPage implements OnInit {
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.setAttribute('no', 'this.selectedUserCalendar')
|
||||
tracing.bugFlag()
|
||||
}
|
||||
|
||||
if (this.task.FsId == '8') {
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<p>mais {{ PublicationFromMvService.form.Files.length - displayLimit }}</p>
|
||||
</ion-thumbnail>
|
||||
<ion-label class="pl-10">
|
||||
<p>{{capturedImageTitle}}</p>
|
||||
<!-- <p>{{capturedImageTitle}}</p> -->
|
||||
<p hidden>size</p>
|
||||
</ion-label>
|
||||
</div>
|
||||
|
||||
@@ -222,7 +222,7 @@ export class NewPublicationPage implements OnInit {
|
||||
source: CameraSource.Camera
|
||||
});
|
||||
this.capturedImage = 'data:image/jpeg;base64,' + capturedImage.base64String;
|
||||
this.capturedImageTitle = 'foto';
|
||||
this.capturedImageTitle = '';
|
||||
this.showCroppModal();
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,9 +22,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="post-item overflow-y-auto">
|
||||
<div class="post-item overflow-y-auto height-100 ">
|
||||
|
||||
<div style="width: 100%; height: 395px;">
|
||||
<div style="width: 100%;">
|
||||
<app-swiper
|
||||
[publicationList]=publication
|
||||
></app-swiper>
|
||||
|
||||
+1
-2
@@ -52,7 +52,6 @@ ion-toolbar {
|
||||
.div-title {
|
||||
/* padding: 0!important; */
|
||||
float: left;
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
.post-img {
|
||||
@@ -95,4 +94,4 @@ ion-toolbar {
|
||||
|
||||
.font-14-rem {
|
||||
font-size: rem(14) !important;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -23,6 +23,8 @@ import { PublicationHolderService } from 'src/app/services/publication/publicati
|
||||
})
|
||||
export class PublicationDetailPage implements OnInit {
|
||||
@ViewChild('videoPlayer', { static: true }) videoPlayer: ElementRef;
|
||||
@ViewChild('ScrollContainer', { static: true }) ScrollContainer: ElementRef;
|
||||
|
||||
showLoader: boolean;
|
||||
DocumentId: string;
|
||||
folderId: string;
|
||||
@@ -82,8 +84,6 @@ export class PublicationDetailPage implements OnInit {
|
||||
FileExtension: '',
|
||||
};
|
||||
|
||||
|
||||
|
||||
window['publicationEdit'] = () => {
|
||||
if(this.isComponentIsAlive == true) {
|
||||
this.getPublicationDetail()
|
||||
@@ -92,9 +92,10 @@ export class PublicationDetailPage implements OnInit {
|
||||
|
||||
}
|
||||
|
||||
OnDestroy() {
|
||||
ngOnDestroy() {
|
||||
this.isComponentIsAlive = false
|
||||
}
|
||||
|
||||
doRefresh(event) {
|
||||
this.getPublicationDetail();
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
|
||||
<ion-card *ngFor="let publication of publicationFolderService.publicationList[folderId] let i = index">
|
||||
<ion-card-content>
|
||||
|
||||
<div style="width: 100%; height: 395px;">
|
||||
<app-swiper
|
||||
[publicationList]=publication
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { HttpClient, HttpErrorResponse, HttpResponse } from '@angular/common/http';
|
||||
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ok, err, Result } from 'neverthrow';
|
||||
import { HttpParams } from '@angular/common/http';
|
||||
import { TracingType } from './monitoring/opentelemetry/tracer';
|
||||
|
||||
|
||||
export type HTTPOptions = {
|
||||
headers?: HttpHeaders
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@@ -10,10 +16,10 @@ export class HttpService {
|
||||
|
||||
constructor(private http:HttpClient) { }
|
||||
|
||||
async post<T>(url: string, body: any, tracing?: TracingType): Promise<Result<T, HttpErrorResponse>> {
|
||||
async post<T>(url: string, body: any, options:HTTPOptions ={}, tracing?: TracingType): Promise<Result<T, HttpErrorResponse>> {
|
||||
|
||||
try {
|
||||
const result = await this.http.post(url, body).toPromise()
|
||||
const result = await this.http.post(url, body, options).toPromise()
|
||||
return ok (result as T)
|
||||
} catch (e) {
|
||||
return err(e as HttpErrorResponse)
|
||||
@@ -42,11 +48,9 @@ export class HttpService {
|
||||
if(isHttpError(e)) {
|
||||
tracing?.setAttribute('status.code', e.status.toString())
|
||||
if (e.status == 400) {
|
||||
tracing?.bugFlag()
|
||||
tracing?.setAttribute('outcome', 'failed')
|
||||
}
|
||||
} else {
|
||||
tracing?.bugFlag()
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing?.setAttribute('map.error', 'true')
|
||||
tracing?.setAttribute('map.error.context', JSON.stringify(e))
|
||||
|
||||
@@ -84,4 +84,4 @@ function childSpanExample(parentContext) {
|
||||
}, 500);
|
||||
}
|
||||
|
||||
parentSpanExample()
|
||||
// parentSpanExample()
|
||||
|
||||
@@ -8,7 +8,9 @@ import { environment } from 'src/environments/environment';
|
||||
import { UseCaseCounter } from './matrix';
|
||||
import { openTelemetryLogging } from './logging';
|
||||
// import { context, propagation } from '@opentelemetry/api';
|
||||
|
||||
import {
|
||||
SpanStatus, SpanStatusCode
|
||||
} from '@opentelemetry/api';
|
||||
const tracerInstance = OpentelemetryAgendaProvider.getTracer('example-tracer-hole', '111', {})
|
||||
const tracerNotificationInstance = OpentelemetryNotificationProvider.getTracer('example-tracer-hole', '111', {})
|
||||
// const logger: ILoggerAdapter = new ColoredLoggerService()
|
||||
@@ -53,15 +55,16 @@ const createTracingInstance = ({bugPrint, name, module, autoFinish}): TracingTyp
|
||||
|
||||
const data = {
|
||||
event: {},
|
||||
tags: {}
|
||||
tags: {},
|
||||
status: {} as any,
|
||||
}
|
||||
|
||||
return {
|
||||
const returnObject = {
|
||||
span: span as any,
|
||||
tracer: tracerInstance,
|
||||
tracerId: requestId,
|
||||
attributes: SemanticAttributes,
|
||||
setStatus: (status: any) => {
|
||||
setStatus: (status: SpanStatus) => {
|
||||
span.setStatus(status);
|
||||
},
|
||||
addEvent: (context: string, message?: any, obj?: any) => {
|
||||
@@ -79,7 +82,7 @@ const createTracingInstance = ({bugPrint, name, module, autoFinish}): TracingTyp
|
||||
span.setAttribute(key, value);
|
||||
|
||||
if(key =='outcome' && value == 'failed') {
|
||||
span.setAttribute('error', 'true')
|
||||
returnObject.hasError('error')
|
||||
}
|
||||
},
|
||||
log(message: string, data: Object) {
|
||||
@@ -110,18 +113,25 @@ const createTracingInstance = ({bugPrint, name, module, autoFinish}): TracingTyp
|
||||
finish: () => {
|
||||
if(environment.apiURL != 'https://gdqas-api.oapr.gov.ao/api/') {
|
||||
span.end();
|
||||
UseCaseCounter.add(1, {user: SessionStore?.user?.FullName, outcome:data.tags['outcome'], usecase: name})
|
||||
UseCaseCounter.add(1, {user: SessionStore?.user?.FullName, outcome:data.tags['outcome'] || data.status?.code , usecase: name})
|
||||
}
|
||||
|
||||
if(bugPrint && data.tags['outcome'] == 'failed') {
|
||||
if(bugPrint && (data.tags['outcome'] == 'failed' || data.status?.code == SpanStatusCode.ERROR)) {
|
||||
console.error(name, data)
|
||||
}
|
||||
},
|
||||
bugFlag:() => {},
|
||||
hasError:(message: string) => {
|
||||
if(data.status?.code != SpanStatusCode.ERROR) {
|
||||
data.status = {code: SpanStatusCode.ERROR, message}
|
||||
span.setStatus({code: SpanStatusCode.ERROR, message})
|
||||
span.setAttribute('outcome','failed')
|
||||
}
|
||||
},
|
||||
createSpan: (name, parent?: any) => {
|
||||
return tracerInstance.startSpan(name, { root: false }, parent) as Span;
|
||||
}
|
||||
}
|
||||
return returnObject
|
||||
}
|
||||
|
||||
export function XTracerAsync({ name, bugPrint, module = null, autoFinish = true, daley =0 }) {
|
||||
@@ -221,7 +231,7 @@ export type TracingType = {
|
||||
getAttribute: (key: string) => string;
|
||||
LocalLogEvent: (name: string, attributesOrStartTime: any, obj?:any) => void;
|
||||
finish: () => void;
|
||||
bugFlag:() => void;
|
||||
hasError:(message: string) => void;
|
||||
createSpan:(name, parent?: any) => Span;
|
||||
};
|
||||
|
||||
|
||||
@@ -189,7 +189,6 @@ export class EditEventToApprovePage implements OnInit {
|
||||
|
||||
} else {
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
this.httpErroHalde.httpStatusHandle(res.error)
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,6 @@ export class EventListPage implements OnInit {
|
||||
} else {
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
if(!isHttpError(allEvents.error)) {
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico. #3')
|
||||
|
||||
@@ -200,7 +200,6 @@ export class ViewEventPage implements OnInit {
|
||||
}
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
this.viewEventDetailDismiss.emit({
|
||||
type: 'close'
|
||||
|
||||
@@ -191,7 +191,6 @@ export class EditEventToApproveComponent implements OnInit {
|
||||
tracing.setAttribute('outcome', 'success')
|
||||
} else {
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
this.httpErroHalde.httpStatusHandle(res.error)
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,6 @@ export class EventsToApprovePage implements OnInit {
|
||||
} else {
|
||||
|
||||
tracing.setAttribute('outcome', 'failed')
|
||||
tracing.bugFlag()
|
||||
|
||||
if(!isHttpError(allEvents.error)) {
|
||||
this.toastService._badRequest('Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico. #4')
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'default' " class="icon font-45-em" src='assets/images/icons-profile.svg'></ion-icon>
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="icon font-45-em" src='assets/images/theme/gov/icons-profile.svg'></ion-icon> -->
|
||||
|
||||
<div (click)="openProfile()" *ngIf="profilePicture == ''" class="profile-image" >
|
||||
<div (click)="openProfile()" *ngIf="profilePictureSubject == undefined " class="profile-image" >
|
||||
<!-- <img *ngIf="loggeduser.RoleDescription == 'Presidente da República' " class="profile-image"
|
||||
src='assets/images/presidente.png'>
|
||||
<img *ngIf="loggeduser.RoleDescription == 'Ministro e Director do Gabinete do PR' " class="profile-image"
|
||||
@@ -69,8 +69,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div (click)="openProfile()" *ngIf="profilePicture != ''" class="profile-image">
|
||||
<img class="profile-image image-prety" src={{profilePicture}}>
|
||||
<div (click)="openProfile()" *ngIf="(profilePictureSubject | async) as calendarData" class="profile-image">
|
||||
<img class="profile-image image-prety" src={{calendarData.base64}}>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'doneIt' " class="icon" src='assets/images/theme/doneIt/icons-profile.svg'></ion-icon>
|
||||
<ion-icon *ngIf="ThemeService.currentTheme == 'gov' " class="icon" src='assets/images/theme/gov/icons-profile.svg'></ion-icon> -->
|
||||
|
||||
<div *ngIf="profilePicture == ''" class="profile-image">
|
||||
<div *ngIf="profilePictureSubject == undefined " class="profile-image">
|
||||
<!-- <img *ngIf="loggeduser.RoleDescription == 'Presidente da República' " class="profile-image"
|
||||
src='assets/images/presidente.png'>
|
||||
<img *ngIf="loggeduser.RoleDescription == 'Ministro e Director do Gabinete do PR' " class="profile-image"
|
||||
@@ -239,8 +239,8 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="profilePicture != ''" class="profile-image">
|
||||
<img class="profile-image font-45-em image-prety" src={{profilePicture}}>
|
||||
<div *ngIf="(profilePictureSubject | async) as calendarData" class="profile-image">
|
||||
<img class="profile-image font-45-em image-prety" src={{calendarData.base64}}>
|
||||
</div>
|
||||
|
||||
<div class="profile-text" *ngIf="(notificationCount$ | async) as notificationCount ">
|
||||
|
||||
@@ -19,6 +19,9 @@ import { PublicationHolderService } from 'src/app/services/publication/publicati
|
||||
import { Cy } from 'cypress/enum'
|
||||
import { NotificationRepositoryService } from 'src/app/module/notification/data/notification-repository.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { UserRepositoryService } from 'src/app/module/user/data/user-repository.service';
|
||||
import { UserProfilePicture } from 'src/app/module/user/data/datasource/user-local-repository.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.page.html',
|
||||
@@ -57,6 +60,7 @@ export class HeaderPage implements OnInit {
|
||||
Cy = Cy
|
||||
|
||||
notificationCount$: Observable<number>
|
||||
profilePictureSubject: Observable<UserProfilePicture>
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
@@ -73,9 +77,10 @@ export class HeaderPage implements OnInit {
|
||||
public NotificationHolderService: NotificationHolderService,
|
||||
public HeaderSettingsService: HeaderSettingsService,
|
||||
public PublicationHolderService: PublicationHolderService,
|
||||
private notificationRepositoryService: NotificationRepositoryService
|
||||
private notificationRepositoryService: NotificationRepositoryService,
|
||||
private UserRepositoryService: UserRepositoryService
|
||||
) {
|
||||
|
||||
this.profilePictureSubject = this.UserRepositoryService.getProfilePictureLive() as any
|
||||
this.notificationCount$ = this.notificationRepositoryService.getNotificationLiveCount()
|
||||
|
||||
this.loggeduser = SessionStore.user;
|
||||
|
||||
+11
-6
@@ -24,7 +24,7 @@
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
|
||||
<div class="content-container">
|
||||
<div class="content-container" #ScrollContainer>
|
||||
<div *ngIf="publication.Title != ''">
|
||||
<ion-refresher name="refresher" slot="fixed" (ionRefresh)="doRefresh($event)">
|
||||
<ion-progress-bar type="indeterminate" *ngIf="showLoader"></ion-progress-bar>
|
||||
@@ -35,15 +35,20 @@
|
||||
<swiper-container navigation="true" [pagination]="{clickable: true, dynamicBullets: true }">
|
||||
<swiper-slide *ngFor="let files of publication.Files let k = index">
|
||||
<!-- <div (click)="openPreview(publication)"> -->
|
||||
<div>
|
||||
<img *ngIf="checkFileType.checkFileType(files.FileExtension ) == 'image'" alt="image" tappable
|
||||
src="{{'data:image/jpg;base64,' + files.FileBase64}}">
|
||||
<div >
|
||||
<img *ngIf="checkFileType.checkFileType(files.FileExtension ) == 'image' && files.FileBase64!=''" alt="sem image" tappable
|
||||
src="{{'data:image/jpg;base64,' + files.FileBase64}}"
|
||||
style="max-height:{{containerMaxHeight}}px"
|
||||
>
|
||||
|
||||
<video *ngIf="checkFileType.checkFileType(files.FileExtension ) == 'video'" controls="controls" preload="metadata"
|
||||
webkit-playsinline="webkit-playsinline">
|
||||
<video *ngIf="checkFileType.checkFileType(files.FileExtension ) == 'video' && files.FileBase64!=''" controls="controls" preload="metadata"
|
||||
webkit-playsinline="webkit-playsinline" style="max-height:{{containerMaxHeight}}px">
|
||||
<source [src]="files.FileBase64" type="video/mp4">
|
||||
</video>
|
||||
|
||||
<div *ngIf="files.FileBase64==''" style="height:{{containerMaxHeight}}px">
|
||||
sem contiudo
|
||||
</div>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper-container>
|
||||
|
||||
+10
-13
@@ -103,13 +103,7 @@ swiper-slide img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
max-height: 1000px;
|
||||
max-width: 868px;
|
||||
|
||||
min-height: 350px;
|
||||
min-width: 468px;
|
||||
object-fit: cover;
|
||||
object-fit: contain;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -121,15 +115,18 @@ swiper-slide video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
max-height: 1000px;
|
||||
max-width: 868px;
|
||||
|
||||
min-height: 350px;
|
||||
min-width: 468px;
|
||||
object-fit: cover;
|
||||
object-fit: contain;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
|
||||
swiper-slide {
|
||||
margin-bottom: -100%;
|
||||
}
|
||||
.swiper-slide-active {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
+46
-1
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
|
||||
import { ModalController } from '@ionic/angular';
|
||||
import { Publication } from 'src/app/models/publication';
|
||||
import { NewPublicationPage } from 'src/app/shared/publication/new-publication/new-publication.page';
|
||||
@@ -29,6 +29,9 @@ export class PublicationDetailPage implements OnInit {
|
||||
@Output() goBackToViewPublications = new EventEmitter();
|
||||
publicationPipe = new PublicationPipe()
|
||||
|
||||
containerMaxWidth = 0
|
||||
containerMaxHeight = 0
|
||||
|
||||
constructor(
|
||||
private modalController: ModalController,
|
||||
private publications:PublicationsService,
|
||||
@@ -56,6 +59,48 @@ export class PublicationDetailPage implements OnInit {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ViewChild('ScrollContainer', { static: true }) ScrollContainer: ElementRef;
|
||||
private observer: IntersectionObserver;
|
||||
|
||||
ngAfterViewInit() {
|
||||
// Create a callback function for the IntersectionObserver
|
||||
const callback = (entries: IntersectionObserverEntry[]) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
this.onElementVisible();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Create the IntersectionObserver instance
|
||||
this.observer = new IntersectionObserver(callback, {
|
||||
root: null, // Use the viewport as the root
|
||||
threshold: 0.1 // Trigger the callback when 10% of the element is visible
|
||||
});
|
||||
|
||||
// Start observing the target element
|
||||
this.observer.observe(this.ScrollContainer.nativeElement);
|
||||
}
|
||||
|
||||
onElementVisible() {
|
||||
console.log('Element is visible!');
|
||||
const e = this.ScrollContainer.nativeElement as HTMLDivElement
|
||||
|
||||
this.containerMaxHeight = e.clientHeight - 100;
|
||||
this.containerMaxWidth = e.clientWidth - 40
|
||||
|
||||
window['e'] = e;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
// Unobserve the element when the component is destroyed
|
||||
if (this.observer) {
|
||||
this.observer.unobserve(this.ScrollContainer.nativeElement);
|
||||
this.observer.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.getPublicationDetail();
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<ion-card *ngFor="let publication of publicationFolderService.publicationList[folderId] let i = index">
|
||||
<ion-card-content>
|
||||
|
||||
<div style="width: 100%; height: 395px;">
|
||||
<div style="width: 100%; height: 395px;overflow:hidden">
|
||||
<app-swiper
|
||||
[publicationList]=publication
|
||||
[navigation]="true"
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
</ion-toolbar>
|
||||
</ion-header> -->
|
||||
|
||||
<ion-content>
|
||||
<swiper-container #swipers [slidechange]="onSlideChange()" [navigation]="navigation" autoHeight="true">
|
||||
<div>
|
||||
<swiper-container id="C{{componentId}}" #swipers (click)="click()" [navigation]="navigation" autoHeight="true">
|
||||
<swiper-slide *ngFor="let files of publicationList.Files let k = index">
|
||||
<div >
|
||||
<img *ngIf="checkFileType.checkFileType(files.FileExtension ) == 'image'" class="post-img"
|
||||
[src]="'data:image/jpg;base64,' + files.FileBase64" loading="lazy">
|
||||
|
||||
<video #videoElement [appVisibility]="onVisibilityChange" *ngIf="checkFileType.checkFileType(files.FileExtension ) == 'video'" class="post-video" controls="controls" preload="none"
|
||||
<video #videoElements [appVisibility]="onVisibilityChange" *ngIf="checkFileType.checkFileType(files.FileExtension ) == 'video'" class="post-video" controls="controls" preload="none"
|
||||
playsinline webkit-playsinline="webkit-playsinline" (play)="stopvideoService.registerVideoWithEvent($event)"q>
|
||||
<source [src]="files.FileBase64" type="video/mp4">
|
||||
</video>
|
||||
@@ -26,7 +26,7 @@
|
||||
</swiper-container>
|
||||
|
||||
|
||||
</ion-content>
|
||||
</div>
|
||||
|
||||
<ion-footer>
|
||||
<div *ngIf="pagination && publicationList?.Files?.length > 1" class="dots-container">
|
||||
@@ -38,20 +38,3 @@
|
||||
</span>
|
||||
</div>
|
||||
</ion-footer>
|
||||
|
||||
|
||||
<!-- <ion-footer>
|
||||
<div *ngIf="publicationList.Files.length > 1" class="dots-container">
|
||||
<span *ngFor="let files of publicationList.Files; let k = index"
|
||||
[class.dotsSwiper]="true"
|
||||
[class.active-dot]="swiperIndex === k"
|
||||
[class.small-dot-after]="k === swiperIndex + 5"
|
||||
[class.small-dot-before]="k === swiperIndex - 5"
|
||||
[class.smaller-dot-after]="k === swiperIndex + 6"
|
||||
[class.smaller-dot-before]="k === swiperIndex - 6"
|
||||
[class.visibility-hiden-dot-after]="k === swiperIndex + 7"
|
||||
[class.visibility-hiden-dot-before]="k === swiperIndex - 7"
|
||||
(click)="goToSlide(k)">
|
||||
</span>
|
||||
</div>
|
||||
</ion-footer> -->
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, ElementRef, Input, OnInit, ViewChild, OnChanges, ViewChildren, QueryList } from '@angular/core';
|
||||
import { checkFileTypeService } from 'src/app/services/checkFileType.service';
|
||||
import { StopvideoService } from 'src/app/services/stopvideo.service';
|
||||
|
||||
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
@Component({
|
||||
selector: 'app-swiper',
|
||||
templateUrl: './swiper.page.html',
|
||||
@@ -11,35 +9,45 @@ import { StopvideoService } from 'src/app/services/stopvideo.service';
|
||||
})
|
||||
export class SwiperPage implements OnInit {
|
||||
|
||||
|
||||
@Input() publicationList: any;
|
||||
@Input() navigation: boolean;
|
||||
@Input() pagination: boolean;
|
||||
swiperIndex: number = 0;
|
||||
|
||||
@ViewChild('VideoManager') VideoManager;
|
||||
@ViewChild('videoElement') videoElements: ElementRef | undefined;
|
||||
@ViewChildren('videoElement') videoElements: QueryList<ElementRef>;
|
||||
@ViewChild('swipers')
|
||||
swiperRef: ElementRef | undefined;
|
||||
|
||||
showControls: boolean = false;
|
||||
previousIndex: number | undefined;
|
||||
|
||||
componentId = uuidv4()
|
||||
|
||||
constructor(
|
||||
public checkFileType: checkFileTypeService,
|
||||
public stopvideoService: StopvideoService,
|
||||
) {
|
||||
console.log('Income list',this.publicationList)
|
||||
}
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
var videos:NodeListOf<HTMLVideoElement> = document.querySelectorAll(`#C${this.componentId} video`);
|
||||
|
||||
try {
|
||||
// Pause each video
|
||||
videos.forEach(function (video) {
|
||||
video.load();
|
||||
})
|
||||
} catch(e) {}
|
||||
|
||||
}, 100)
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.onSlideChange()
|
||||
}
|
||||
|
||||
onSlideChange() {
|
||||
|
||||
console.log('onchage')
|
||||
if (this.swiperRef) {
|
||||
|
||||
const swiper = this.swiperRef.nativeElement.swiper;
|
||||
@@ -52,12 +60,13 @@ export class SwiperPage implements OnInit {
|
||||
this.previousIndex = this.swiperIndex;
|
||||
this.loadVideo()
|
||||
|
||||
} else {
|
||||
console.log(this.swiperIndex)
|
||||
}
|
||||
console.log(this.swiperIndex)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
goToSlide(index: number) {
|
||||
this.swiperIndex = this.swiperRef?.nativeElement.swiper.activeIndex;
|
||||
this.swiperRef?.nativeElement.swiper.slideTo(index);
|
||||
@@ -79,35 +88,45 @@ export class SwiperPage implements OnInit {
|
||||
video.pause();
|
||||
})
|
||||
|
||||
const video: HTMLVideoElement = this.videoElements.nativeElement;
|
||||
|
||||
video.pause()
|
||||
|
||||
/* this.videoElements.forEach(videoElement => {
|
||||
this.videoElements.forEach(videoElement => {
|
||||
// You can access the native HTML video element using videoElement.nativeElement
|
||||
|
||||
const video: HTMLVideoElement = videoElement.nativeElement;
|
||||
|
||||
video.pause()
|
||||
// Do something with each video element
|
||||
// console.log(video);
|
||||
}); */
|
||||
});
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
click() {
|
||||
this.onSlideChange()
|
||||
}
|
||||
//This method does the refresh of all video(it was implement beacouse of the erro displaing the controls of the video while swipe)
|
||||
//If you can find a better solution, please remove
|
||||
loadVideo() {
|
||||
var videos = document.querySelectorAll('video');
|
||||
|
||||
try {
|
||||
// Pause each video
|
||||
videos.forEach(function (video) {
|
||||
video.load();
|
||||
})
|
||||
|
||||
const video: HTMLVideoElement = this.videoElements.nativeElement;
|
||||
const file = this.publicationList.Files[this.swiperIndex]
|
||||
if(this.checkFileType.checkFileType(file.FileExtension ) == 'video') {
|
||||
|
||||
const video = document.querySelector(`#C${this.componentId} .swiper-slide-active video`) as HTMLVideoElement
|
||||
if(video) {
|
||||
|
||||
// video.load();
|
||||
video.removeAttribute('controls');
|
||||
setTimeout(() => {
|
||||
video.setAttribute('controls', 'controls');
|
||||
}, 10)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
video.load()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user