import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { PublicationsService } from 'src/app/services/publications.service'; import { Publication } from 'src/app/models/publication'; import { PhotoService } from 'src/app/services/photo.service'; import { ToastService } from 'src/app/services/toast.service'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { ThemeService } from 'src/app/services/theme.service'; import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'; import { HttpErrorHandle } from 'src/app/services/http-error-handle.service'; import { PublicationFolderService } from 'src/app/store/publication-folder.service'; import { FilePicker } from '@capawesome/capacitor-file-picker'; import { checkFileTypeService } from 'src/app/services/checkFileType.service'; import { FileValidatorService } from "src/app/services/file/file-validator.service"; import { MiddlewareServiceService } from "src/app/shared/API/middleware/middleware-service.service"; import { LakefsRepositoryService } from '../../repository/lakefs/lakefs-repository.service'; import { SocketConnectionMCRService } from "src/app/services/socket-connection-mcr.service" import { CMAPIService } from '../../repository/CMAPI/cmapi.service'; import { environment } from 'src/environments/environment'; import { CaptureImageOptions, MediaCapture } from '@awesome-cordova-plugins/media-capture/ngx'; import { Directory, Filesystem, FilesystemDirectory } from '@capacitor/filesystem'; import { ModalController, Platform } from '@ionic/angular'; import { PublicationAttachmentEntity } from '../upload/upload-streaming.service'; import { VideoconvertService } from 'src/app/services/videoconvert.service'; import { PublicationHolderService } from 'src/app/services/publication/publication-holder.service' import { PublicationFromMvService } from "src/app/shared/publication/upload/publication-from-mv.service" import { UploadStreamingService } from "src/app/shared/publication/upload/upload-streaming.service" enum ActionType { newRapid = "1", new = "2", edit = "3" } @Component({ selector: 'app-new-publication', templateUrl: './new-publication.page.html', styleUrls: ['./new-publication.page.scss'], }) export class NewPublicationPage implements OnInit { showLoader: boolean; publicationTitle: string; imgUrl: any; Defaultimage: any = ''; Form: FormGroup; validateFrom = false @Input() publication!: Publication; @Input() publicationType: ActionType; @Input() folderId: string; @Input() documentId: string; @Output() closeDesktopComponent = new EventEmitter(); @Output() openPublicationDetails = new EventEmitter(); @Output() goBackToViewPublications = new EventEmitter(); @Output() goBacktoPublicationDetails = new EventEmitter(); guestPicture: any; capturedImage: any = ''; capturedImageTitle: any = ''; fileType: string; filecontent: boolean; captureContent: any; displayLimit = 4; filesSizeSum = 0; photoOrVideo: boolean = false; video: any; constructor( public PublicationFromMvService: PublicationFromMvService, public photoService: PhotoService, private publications: PublicationsService, private toastService: ToastService, public ThemeService: ThemeService, private httpErroHandle: HttpErrorHandle, public PublicationFolderService: PublicationFolderService, public checkFileType: checkFileTypeService, private mediaCapture: MediaCapture, private httpErrorHandle: HttpErrorHandle, private platform: Platform, private FileValidatorService: FileValidatorService, private MiddlewareServiceService: MiddlewareServiceService, private LakefsRepositoryService: LakefsRepositoryService, private SocketConnectionMCRService: SocketConnectionMCRService, private videoconvertService: VideoconvertService, public UploadStreamingService: UploadStreamingService, private modalController: ModalController ) { this.publicationTitle = 'Nova Publicação'; } ngOnInit() { this.PublicationFromMvService.clear() this.setAction(); this.setData() } setData() { if(this.publication) { this.processData(this.publication) } else { this.getPublicationDetail() } } getPublicationDetail() { if (this.publicationType != ActionType.new) { this.showLoader = true; this.publications.GetPublicationWithArrayOfFilesById(this.documentId).subscribe(res => { this.processData(res) console.log("res get", res) this.showLoader = false; }, (error) => { console.log(error) this.showLoader = false; this.goBack() }); } } processData(res) { console.log("res process", res) this.PublicationFromMvService.form.Files = [] this.PublicationFromMvService.form.setData({ DateIndex: res.DateIndex, DocumentId: res.DocumentId, ProcessId: res.ProcessId, Title: res.Title, Message: res.Message, DatePublication: res.DatePublication }) const newFiles: PublicationAttachmentEntity[] = res.Files.map(e => { return new PublicationAttachmentEntity( { base64: e.FileBase64, extension: e.FileExtension, OriginalFileName: e.OriginalFileName, FileType: this.checkFileType.checkFileType(e.FileExtension) as any } ) }) for(const files of newFiles) { this.PublicationFromMvService.form.Files.push(files) } } async takePicture() { const capturedImage = await Camera.getPhoto({ quality: 50, // allowEditing: true, resultType: CameraResultType.Base64, source: CameraSource.Camera }); this.capturedImage = 'data:image/jpeg;base64,' + capturedImage.base64String; this.capturedImageTitle = 'foto'; this.showCroppModal() /* if(validation.isOk) { */ /* const compressedImage = await this.compressImageBase64( this.capturedImage, 800, // maxWidth 800, // maxHeight 0.9 // quality ).then((picture) => { this.photoOrVideo = false; const FileExtension = this.removeTextBeforeSlash('jpeg', '/') const newAttachment = new PublicationAttachmentEntity( { base64: picture, extension: FileExtension, OriginalFileName: "foto", FileType: 'image' } ) this.PublicationFromMvService.form.Files.push(newAttachment) }); */ } async laodPicture() { const capturedImage = await Camera.getPhoto({ quality: 90, resultType: CameraResultType.Base64, source: CameraSource.Photos }); this.fileType = capturedImage.format console.log(this.fileType) this.capturedImage = 'data:image/jpeg;base64,' + capturedImage.base64String; this.capturedImageTitle = 'foto'; const compressedImage = await this.compressImageBase64( this.capturedImage, 800, // maxWidth 800, // maxHeight 0.9 // quality ).then((picture) => { const FileExtension = this.removeTextBeforeSlash('jpeg', '/') const newAttachment = new PublicationAttachmentEntity( { base64: picture, extension: FileExtension, OriginalFileName: "foto", FileType: 'image' } ) //newAttachment.needUpload(); this.PublicationFromMvService.form.Files.push(newAttachment) }); } async loadVideo() { const result = await FilePicker.pickMedia ({ multiple: true, }); console.log(result) result.files.forEach(async blobFile => { console.log(blobFile) if (this.checkFileType.checkFileType(blobFile.mimeType) == 'image') { this.convertBlobToBase64(blobFile.blob).then((value: string) => { this.filesSizeSum = this.filesSizeSum + blobFile.size this.capturedImage = 'data:image/jpeg;base64,' + this.removeTextBeforeSlash(value, ','), this.showCroppModal(); this.filecontent = true; }).catch((erro) => { console.log(erro) }) } else if (this.checkFileType.checkFileType(blobFile.mimeType) == 'video'){ let convertedVideo = await this.videoconvertService.convertVideoWeb(blobFile.blob,"src/assets/videos/","output",'mp4') this.convertBlobToBase64(blobFile.blob).then((value: string) => { this.filesSizeSum = this.filesSizeSum + blobFile.size const FileExtension = this.removeTextBeforeSlash(blobFile.mimeType, '/') const file = new File([blobFile.blob], blobFile.name); const newAttachment = new PublicationAttachmentEntity( { base64: this.removeTextBeforeSlash(value, ','), extension: 'mp4', blobFile: file, FileType: this.checkFileType.checkFileType(FileExtension) as any, OriginalFileName: 'load video' } ) newAttachment.needUpload() this.PublicationFromMvService.form.Files.push(newAttachment) this.filecontent = true; }).catch((erro) => { console.log(erro) }) } else { this.httpErroHandle.validationMessagge('filetype'); } }); } async loadVideoTablet() { const result = await FilePicker.pickMedia ({ multiple: true, }); console.log(result.files) result.files.forEach(element => { this.filesSizeSum = this.filesSizeSum + element.size if (this.checkFileType.checkFileType(element.mimeType) == 'image') { let resultUrl = decodeURIComponent(element.path); try { Filesystem.readFile({ path: resultUrl }) .then(async (content) => { console.log(result) this.capturedImage = 'data:image/jpeg;base64,'+content.data this.showCroppModal() }) .catch((err) => console.error(err)); } catch (error) { console.log('upload video error: ', error) } } else if (this.checkFileType.checkFileType(element.mimeType) == 'video'){ let resultUrl = decodeURIComponent(element.path); try { Filesystem.readFile({ path: resultUrl }) .then(async (content) => { console.log(result) console.log('load video tablet base64', content) this.filecontent = true; let fileObject = new PublicationAttachmentEntity({ base64: content.data, extension: this.removeTextBeforeSlash(element.mimeType, '/'), OriginalFileName: 'video', FileType: this.checkFileType.checkFileType( this.removeTextBeforeSlash(element.mimeType, '/')) as any }) fileObject.needUpload() this.PublicationFromMvService.form.Files.push(fileObject) }) .catch((err) => console.error(err)); } catch (error) { console.log('upload video error: ', error) } } else { this.httpErroHandle.validationMessagge('filetype'); } }); }; runValidation() { this.validateFrom = true } injectValidation() { this.Form = new FormGroup({ Subject: new FormControl(this.PublicationFromMvService.form.Title, [ Validators.required, // Validators.minLength(4) ]), Message: new FormControl(this.PublicationFromMvService.form.Message, [ Validators.required, Validators.maxLength(1000) ]) }) } async save() { this.injectValidation() this.runValidation() if (this.Form.invalid) { return false } this.PublicationFromMvService.setFolderId(this.folderId) this.PublicationFromMvService.setFolderId(this.folderId) this.goBack(); await this.PublicationFromMvService.save() // this.PublicationHolderService.setPublication(this.PublicationFromMvService) } ngOnDestroy() { if(!this.PublicationFromMvService.form.send) { this.PublicationFromMvService.cancel() } } close() { this.PublicationFromMvService.form.cancel = true if(this.PublicationFromMvService.form.send == false) { this.PublicationFromMvService.cancel() this.PublicationFromMvService.ObjectMergeNotification.close() } this.goBack(); } clear() { this.PublicationFromMvService.form.Files = []; } deletePublicationImage() { this.PublicationFromMvService.form.Files = [] } setAction() { if (this.publicationType == ActionType.newRapid) { this.publicationTitle = 'Nova Publicação Rápida'; } else if (this.publicationType == ActionType.new) { this.publicationTitle = 'Nova Publicação'; } else if (this.publicationType == ActionType.edit) { this.publicationTitle = 'Editar Publicação'; } this.PublicationFromMvService.publicationType = this.publicationType } async goBack() { if (this.publicationType == ActionType.new) { this.goBackToViewPublications.emit(); } else { this.goBackToViewPublications.emit(); //this.goBacktoPublicationDetails.emit(); } } async compressImageBase64(base64String: string, maxWidth: number, maxHeight: number, quality: number): Promise { return new Promise((resolve, reject) => { const image = new (window as any).Image(); image.src = base64String; image.onload = async () => { const canvas = document.createElement('canvas'); let newWidth = image.width; let newHeight = image.height; if (newWidth > maxWidth) { newHeight *= maxWidth / newWidth; newWidth = maxWidth; } if (newHeight > maxHeight) { newWidth *= maxHeight / newHeight; newHeight = maxHeight; } canvas.width = newWidth; canvas.height = newHeight; const context = canvas.getContext('2d'); context?.drawImage(image, 0, 0, newWidth, newHeight); const compressedBase64 = canvas.toDataURL('image/jpeg', quality); resolve(compressedBase64); }; image.onerror = (error) => { reject(error); }; }); } convertBlobToBase64(blob: Blob) { try { return new Promise((resolve, reject) => { const chunkSize = 5000000; // 5 MB const fileSize = blob.size; const chunks = Math.ceil(fileSize / chunkSize); let currentChunk = 0; const base64Chunks = []; const reader = new FileReader(); reader.onload = function () { base64Chunks.push(reader.result); currentChunk++; if (currentChunk < chunks) { loadNextChunk(); } else { const base64Data = base64Chunks.join(""); resolve(base64Data); } }; reader.onerror = function (error) { reject(error); }; function loadNextChunk() { const start = currentChunk * chunkSize; const end = Math.min(start + chunkSize, fileSize); const chunk = blob.slice(start, end); reader.readAsDataURL(chunk); } loadNextChunk(); }); } catch (error) { console.log(error); } } /* convertBlobToBase64(blob: Blob) { console.log('Convert blob ',blob) return new Promise((resolve, reject) => { const reader = new FileReader; reader.onerror = reject; reader.onload = () => { resolve(reader.result) } reader.readAsDataURL(blob) },) } */ getBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result); reader.onerror = error => reject(error); }); } removeTextBeforeSlash(inputString, mark) { if (inputString.includes(mark)) { const parts = inputString.split(mark); return parts.length > 1 ? parts[1] : inputString; } else { return inputString; } } async compressVideoBase64(base64String: string, maxWidth: number, maxHeight: number, quality: number): Promise { console.log(base64String) return new Promise(async (resolve, reject) => { try { // Decode the base64 video string to an ArrayBuffer const trimmedBase64 = base64String.trim(); const videoBuffer = this.base64ToArrayBuffer(this.removeTextBeforeSlash(trimmedBase64, ',')); // Create a Blob from the ArrayBuffer const videoBlob = new Blob([videoBuffer], { type: 'video/mp4' }); // Create an object URL from the Blob const videoObjectUrl = URL.createObjectURL(videoBlob); // Create a video element const video = document.createElement('video'); video.src = videoObjectUrl; // Wait for the video to load metadata video.addEventListener('loadedmetadata', async () => { const canvas = document.createElement('canvas'); let newWidth = video.videoWidth; let newHeight = video.videoHeight; if (newWidth > maxWidth) { newHeight *= maxWidth / newWidth; newWidth = maxWidth; } if (newHeight > maxHeight) { newWidth *= maxHeight / newHeight; newHeight = maxHeight; } canvas.width = newWidth; canvas.height = newHeight; const context = canvas.getContext('2d'); // Start continuous rendering function render() { context?.drawImage(video, 0, 0, newWidth, newHeight); requestAnimationFrame(render); } render(); // Convert the canvas to a Blob canvas.toBlob(async (blob) => { if (blob) { // Read the Blob as an ArrayBuffer const compressedVideoBuffer = await this.readBlobAsArrayBuffer(blob); // Convert the ArrayBuffer back to base64 const compressedBase64 = this.arrayBufferToBase64(compressedVideoBuffer); resolve(compressedBase64); } else { reject('Error creating compressed video blob.'); } }, 'video/mp4', 0.5); }); } catch (error) { reject(error); } }); } private base64ToArrayBuffer(base64: string): ArrayBuffer { const binaryString = window.atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; ++i) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; } private async readBlobAsArrayBuffer(blob: Blob): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => { if (reader.result instanceof ArrayBuffer) { resolve(reader.result); } else { reject('Error reading blob as ArrayBuffer.'); } }; reader.readAsArrayBuffer(blob); }); } private arrayBufferToBase64(buffer: ArrayBuffer): string { const binary = String.fromCharCode(...new Uint8Array(buffer)); return btoa(binary); } fileSizeToMB(sizeInBytes) { var sizeInMB = (sizeInBytes / (1024 * 1024)).toFixed(2); console.log(sizeInMB + 'MB'); return parseInt(sizeInMB) } deleteFromSeletedContent(index) { this.PublicationFromMvService.form.Files.splice(index, 1) } chossePhotoOrVideo() { this.photoOrVideo = !this.photoOrVideo } async startVideoRecording() { try { let options: CaptureImageOptions = { limit: 1 } const data: any = await this.mediaCapture.captureVideo(options) this.video = data[0]; console.log(data) data.forEach(async element => { this.filesSizeSum = this.filesSizeSum + element.size if (this.fileSizeToMB(this.filesSizeSum) <= 20) { if(this.platform.is('ios')) { this.recordevideoIos(element.fullPath) } else { this.recordVideoPc(element.fullPath) } } else { if (this.PublicationFromMvService.form.Files.length === 0) this.filesSizeSum = 0 this.httpErrorHandle.validationMessagge('filessize') } }); } catch (error) { console.log('record video error: ', error) } } checkTableDivice() { if (this.platform.is('tablet')) return true; } checkDesktop() { if (this.platform.is('desktop')) return true; } async recordevideoIos(fullPath) { try { const directory = await Filesystem.getUri({ directory: Directory.Cache, path: '', }); const stringGerada = this.gerarStringAleatoria(); console.log(stringGerada); this.videoconvertService.convertVideo(fullPath,directory.uri,stringGerada,'mp4'); Filesystem.readFile({ path: `${directory.uri}${stringGerada}.mp4`}) .then(async (content) => { this.filecontent = true; console.log('', content) let fileObject = new PublicationAttachmentEntity({ base64: 'data:video/mp4;base64,'+content.data, extension: 'mp4', OriginalFileName: 'record', FileType: 'video' } ) /* fileObject.needUpload() */ this.PublicationFromMvService.form.Files.push(fileObject) }) .catch((erro) => console.error('read converted video erro ', erro)); } catch (error) { console.log('record video ios erro, ', error) } } async recordVideoPc(fullPath) { try { const savedFile = await Filesystem.copy({ from: fullPath, // directory prop removed, Capacitor parses filename for us to: "video.mp4", toDirectory: FilesystemDirectory.Data }); console.log(savedFile.uri) Filesystem.readFile({ path: savedFile.uri }) .then(async (content) => { this.filecontent = true; console.log('', content) let fileObject; if(this.platform.is('desktop')) { fileObject = new PublicationAttachmentEntity({ base64: content.data, extension: 'mp4', OriginalFileName: 'record', FileType: 'video' }) } else { fileObject = new PublicationAttachmentEntity({ base64: 'data:video/mp4;base64,'+content.data, extension: 'mp4', OriginalFileName: 'record', FileType: 'video' }) } /* fileObject.needUpload() */ this.PublicationFromMvService.form.Files.push(fileObject) }) .catch((err) => console.error(err)); } catch (error) { } } gerarStringAleatoria() { const caracteres = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let stringAleatoria = ''; for (let i = 0; i < 4; i++) { const indiceAleatorio = Math.floor(Math.random() * caracteres.length); stringAleatoria += caracteres.charAt(indiceAleatorio); } return stringAleatoria; } async showCroppModal() { const modal = await this.modalController.create({ component: CropImagePage, componentProps: { base64ToCroppe: this.capturedImage }, cssClass: 'modal modal-desktop' }); modal.onDidDismiss().then((res) => { if (res) { this.capturedImage = res.data this.filecontent = true; this.photoOrVideo = false; const newAttachment = new PublicationAttachmentEntity( { base64: res.data.base64ToCroppe, extension: 'jpeg', OriginalFileName: "image", FileType: 'image' } ) this.PublicationFromMvService.form.Files.push(newAttachment) } }, (error) => { console.log(error) }); await modal.present(); // let fileObject = new PublicationAttachmentEntity({ // base64: this.removeTextBeforeSlash(this.capturedImage, ',') , // extension: 'jpeg', // OriginalFileName: 'video', // FileType: 'image' // }) // this.PublicationFromMvService.form.Files.push(fileObject) } } import { Observable, of, Subject } from 'rxjs'; import { tap, switchMap, delay, map } from 'rxjs/operators'; import { CropImagePage } from 'src/app/modals/crop-image/crop-image.page'; function shareResponse(): MethodDecorator { return function ( target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor ): PropertyDescriptor { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]): Observable { // Create a subject to broadcast the response const responseSubject = new Subject(); // Use switchMap to ensure only one subscription is active at a time return responseSubject.pipe( switchMap(() => { // If no ongoing execution, trigger the original method if (!this.isExecuting) { this.isExecuting = true; // Call the original method const result = originalMethod.apply(this, args); // Convert the result to an observable if it's not already const resultObservable = result instanceof Observable ? result : of(result); // Tap into the result to broadcast it to all subscribers return resultObservable.pipe( tap((response) => { responseSubject.next(response); this.isExecuting = false; }) ); } else { // If ongoing execution, return an empty observable return of(); } }) ); }; return descriptor; }; } class ApiService { private isExecuting = false; @shareResponse() fetchData(): Observable { // Simulate fetching data locally (replace it with your own data source) const localData: number[] = [1, 2, 3, 4, 5]; return of(localData.reduce((sum, value) => sum + value, 0)); } } const apiService = new ApiService(); // Example usage apiService.fetchData().subscribe((result) => console.log('Subscriber 1:', result)); // This subscriber will receive the same result without triggering a new execution apiService.fetchData().subscribe((result) => console.log('Subscriber 2:', result));