From 3eecedb23fae5e23cf158df3f253a1fdbb16ed05 Mon Sep 17 00:00:00 2001 From: "peter.maquiran" Date: Wed, 17 Sep 2025 18:37:35 +0100 Subject: [PATCH] fix publication --- linkedin_content_calendar.md | 170 ++++++++++ src/app/app.module.ts | 91 ++--- .../actions-create-use-case.service.ts | 2 - .../actions-update-use-case.service.ts | 27 ++ .../publication-create-use-case.service.ts | 36 ++ ...n-files-delete-by-path-use-case.service.ts | 31 ++ .../publication-list-by-process-id.service.ts | 7 +- .../publication-update-use-case.service.ts | 37 +++ src/app/core/chat/entity/message.ts | 7 +- .../listen-message-by-roomId.service.ts | 2 +- .../room/room-bold-sync-use-case.service.ts | 2 - .../room/room-set-last-message.service.ts | 10 +- .../room-update-by-id-use-case.service.ts | 27 +- .../socket-message-delete-use-case.service.ts | 2 +- .../use-case/user-login-use-case.service.ts | 2 + .../crash-analytics/app-error-handler.ts | 52 +++ .../dexie/instance/chat/schema/message.ts | 3 +- src/app/infra/http/http.service.ts | 3 +- .../interceptors/token.interceptors.ts | 6 +- src/app/infra/open-replay/index.ts | 61 ++++ src/app/infra/socket/signalR/signalR.ts | 2 +- src/app/modals/view-media/view-media.page.ts | 24 +- src/app/module/actions/actions.module.ts | 20 ++ .../action-remote-repository.service.ts | 5 + .../publication-file-repository.service.ts | 16 +- .../publication-remote-repository.service.ts | 10 + .../list/rooms/messageListChangedetector.ts | 10 +- src/app/module/user/domain/user.service.ts | 8 +- .../diploma-assinar/diploma-assinar.page.ts | 4 +- .../pages/publications/publications.page.scss | 36 +- .../publication-detail.page.html | 22 -- .../monitoring/opentelemetry/opentelemetry.ts | 7 - src/app/services/notifications.service.ts | 46 +-- src/app/services/processes.service.ts | 48 ++- src/app/services/publications.service.ts | 4 +- .../services/socket-connection-mcr.service.ts | 312 +++--------------- .../event-details-documents-options.page.ts | 24 +- .../edit-action/edit-action.page.ts | 48 ++- .../new-action/new-action.page.html | 32 +- .../publication/new-action/new-action.page.ts | 2 - .../new-publication/new-publication.page.html | 57 ++-- .../new-publication/new-publication.page.ts | 266 +++++++-------- .../upload/publication-from-mv.service.ts | 39 +-- .../publication-detail.page.html | 6 +- .../view-publications.page.ts | 12 +- src/app/shared/swiper/swiper.page.html | 47 +-- src/app/shared/swiper/swiper.page.ts | 15 +- src/app/store/publication-folder.service.ts | 28 +- src/app/ui/agenda/agenda.page.ts | 5 +- src/app/ui/chat/chat.page.html | 17 +- src/app/ui/chat/chat.page.ts | 8 +- .../component/edit-group/edit-group.page.ts | 8 +- .../component/messages/messages.page.html | 11 +- .../chat/component/messages/messages.page.ts | 50 +-- .../chat/modal/edit-group/edit-group.page.ts | 9 + .../ui/chat/modal/messages/messages.page.ts | 8 + src/app/ui/chat/store/model/message.ts | 8 +- src/app/ui/chat/store/model/room.ts | 3 +- src/app/ui/chat/store/roomStore.ts | 30 +- .../shared/components/header/header.page.html | 5 +- .../shared/components/header/header.page.ts | 8 +- src/environments/environment.prod.ts | 2 +- version/git-version.ts | 12 +- web.config | 4 +- 64 files changed, 1043 insertions(+), 873 deletions(-) create mode 100644 linkedin_content_calendar.md create mode 100644 src/app/core/actions/use-case/actions-update-use-case.service.ts create mode 100644 src/app/core/actions/use-case/publication-create-use-case.service.ts create mode 100644 src/app/core/actions/use-case/publication-files-delete-by-path-use-case.service.ts create mode 100644 src/app/core/actions/use-case/publication-update-use-case.service.ts create mode 100644 src/app/infra/crash-analytics/app-error-handler.ts create mode 100644 src/app/infra/open-replay/index.ts create mode 100644 src/app/module/actions/actions.module.ts diff --git a/linkedin_content_calendar.md b/linkedin_content_calendar.md new file mode 100644 index 000000000..326a8d434 --- /dev/null +++ b/linkedin_content_calendar.md @@ -0,0 +1,170 @@ +# 📅 6-Month LinkedIn Content Calendar for Peter (Full-Stack Developer) + +This is a practical roadmap to grow your visibility and influence on LinkedIn. + +--- + +## 🌍 LinkedIn Fame Roadmap + +### Phase 1 – Foundation (Weeks 1–2) +- Optimize profile (photo, banner, headline, about, featured projects). +- Follow 100+ relevant devs, recruiters, and companies. + +### Phase 2 – Consistency (Weeks 3–6) +- Post 3–4 times per week. +- Share lessons, demos, explainers, and personal wins. +- Engage with other posts. + +### Phase 3 – Authority Building (Months 2–4) +- Write long-form articles (e.g., offline-first, clean architecture). +- Share diagrams, benchmarks, and tutorials. +- Start a content series. + +### Phase 4 – Expansion (Months 4–6) +- Collaborate with other devs. +- Speak in webinars or podcasts. +- Share across GitHub, Dev.to, Medium. + +### Phase 5 – Thought Leadership (Months 6+) +- Release libraries/tools. +- Do live coding sessions. +- Mentor juniors, share insights. + +--- + +# 📅 Content Calendar + +## Month 1 – Kickoff & Personal Branding +**Goal**: Introduce yourself and build recognition. + +- **Week 1**: + - Intro post: “I’m Peter, a full-stack dev passionate about scalable apps & clean architecture.” + - Share your tech stack (Angular, Node.js, Redis, RabbitMQ, OpenTelemetry, Nginx). + +- **Week 2**: + - Story: “How I solved a 5GB+ file upload challenge.” + - Quick explainer: “What is distributed tracing & why should devs care?” + +- **Week 3**: + - Share a coding tip from your daily work. + - Comment on trending dev posts. + +- **Week 4**: + - Failure story: “I misconfigured Nginx SSL once, here’s the lesson.” + - Diagram: “Reverse proxy explained in 30 seconds.” + +--- + +## Month 2 – Technical Authority +**Goal**: Show deep technical knowledge. + +- **Week 1**: + - Article: “Offline-first architecture in Angular.” + - LinkedIn summary post linking to article. + +- **Week 2**: + - Redis vs. DB caching. + - Code snippet with before/after perf gain. + +- **Week 3**: + - Diagram: RabbitMQ message flow. + - Debugging queue bottleneck tip. + +- **Week 4**: + - OpenTelemetry case: “Frontend + Backend performance tracing.” + - Engage with 10 influencers’ posts. + +--- + +## Month 3 – Visibility Boost +**Goal**: Grow network and reach. + +- **Week 1**: + - Series: “Clean Architecture in Practice (Part 1: Principles).” + - Share a GitHub repo demo. + +- **Week 2**: + - Poll: Biggest scaling pain? + - Humor/meme about dev life. + +- **Week 3**: + - Case study: Angular rendering optimization. + - Video: whiteboard explanation. + +- **Week 4**: + - Share someone else’s post with your perspective. + - Open-source contribution story. + +--- + +## Month 4 – Expansion +**Goal**: Gain recognition outside current network. + +- **Week 1**: + - Article: Scaling Angular apps with Nginx & Redis. + - Share in groups. + +- **Week 2**: + - Live mini-demo of debugging/tracing. + - Checklist: “5 things every scalable backend must have.” + +- **Week 3**: + - Share book/article you loved. + - Poll: “What do you log in production?” + +- **Week 4**: + - Failure/lesson post. + - Tutorial: Nginx SSL in 5 steps. + +--- + +## Month 5 – Community Building +**Goal**: Start building a following. + +- **Week 1**: + - Launch LinkedIn series/group: “Scalable Full-Stack Weekly.” + - Ask network for topic ideas. + +- **Week 2**: + - Monitoring with Prometheus + OpenTelemetry. + - Show a metrics dashboard screenshot. + +- **Week 3**: + - Article: Clean architecture for offline-first mobile apps. + - Share summary post. + +- **Week 4**: + - Journey post: “From beginner to distributed systems.” + - Engage with 20+ posts this week. + +--- + +## Month 6 – Thought Leadership +**Goal**: Become a go-to expert. + +- **Week 1**: + - Reflection post: “6 months of writing on LinkedIn.” + - Poll: Which post helped you most? + +- **Week 2**: + - Release a small tool/framework. + - Write article introducing it. + +- **Week 3**: + - Mentorship post: “If I started full-stack dev in 2025…” + - Share GitHub contributions/stats. + +- **Week 4**: + - Case study: “How I built a resilient offline-first app.” + - Reflection: invite more followers. + +--- + +# ⚡ Daily Engagement Habits +- Comment on 5 posts/day with value. +- Send 2–3 connection requests/day. +- Reply to all comments within 24h. + +--- + +✅ With consistency + authenticity, expect exponential growth by **Month 3–4**. diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 504f4df6b..ff810a55a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -64,19 +64,9 @@ import { CustomImageCachePageRoutingModule } from './services/file/custom-image- import { IonicImageLoaderModule } from 'ionic-image-loader-v5'; import { NgxExtendedPdfViewerModule } from 'ngx-extended-pdf-viewer'; import { FileOpener } from '@awesome-cordova-plugins/file-opener/ngx'; - - import { MatInputModule } from '@angular/material/input'; import { MatNativeDateModule } from '@angular/material/core'; - - -// The example is using Angular, Import '@sentry/vue' or '@sentry/react' when using a Sibling different than Angular. - import * as SentrySibling from '@sentry/angular'; -import * as Sentry from '@sentry/capacitor'; -// The e xample is using Angular, Import '@sentry/vue' or '@sentry/react' when using a Sibling different than Angular. -// For automatic instrumentation (highly recommended) -import { Integration } from '@sentry/types'; -import { BrowserTracing } from '@sentry/tracing'; +import * as SentrySibling from '@sentry/angular'; import { AngularFireModule } from '@angular/fire'; import { AngularFireMessagingModule } from '@angular/fire/messaging'; import { firebaseConfig } from '../firebase-config'; @@ -87,26 +77,26 @@ import { LoggingInterceptorService } from './services/logging-interceptor.servic import { PopupQuestionPipe } from './modals/popup-question.pipe'; import '@teamhive/capacitor-video-recorder'; import { tokenInterceptor } from './infra/monitoring/interceptors/token.interceptors'; - import { InputFilterDirective } from './services/directives/input-filter.directive'; import { DeplomaOptionsPageModule } from './shared/popover/deploma-options/deploma-options.module'; import { DiplomaOptionsPage } from './shared/popover/deploma-options/deploma-options.page'; import { ImageCropperModule } from 'ngx-image-cropper'; import { metricsInterceptor } from './infra/monitoring/interceptors/metter.interceptor'; - import {MatMenuModule} from '@angular/material/menu'; import {MatIconModule} from '@angular/material/icon'; import { ChatModule } from './module/chat/chat.module'; -import { openTelemetryLogging } from './services/monitoring/opentelemetry/logging'; - import { registerLocaleData } from '@angular/common'; import localePt from '@angular/common/locales/pt'; -import { LogsDatabase } from './infra/database/dexie/instance/logs/service'; import { UserModule } from './module/user/user.module'; import { Logger } from './services/logger/main/service'; -// Register the locale data registerLocaleData(localePt, 'pt'); +import * as Sentry from '@sentry/capacitor'; +import { Integration } from '@sentry/types'; +import { BrowserTracing } from '@sentry/tracing'; +import { LogsDatabase } from './infra/database/dexie/instance/logs/service'; +import { AppErrorHandler } from './infra/crash-analytics/app-error-handler'; + // Sentry.init( // { @@ -117,11 +107,7 @@ registerLocaleData(localePt, 'pt'); // // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring. // // We recommend adjusting this value in production. // tracesSampleRate: 1.0, -// integrations: [ -// new BrowserTracing({ -// tracingOrigins: ['localhost', 'https://gd-api.oapr.gov.ao/api/'], -// }) as Integration, -// ], +// debug: true, // logs to console // beforeSend(event) { // console.log('event.exception.values[0].value', event.exception.values[0].value); @@ -131,19 +117,17 @@ registerLocaleData(localePt, 'pt'); // console.log('event', event) // }) -// openTelemetryLogging.send({ -// level: 'info', -// message: event.exception.values[0].value, -// payload: { -// object: { -// sentry: true, -// error: event -// } -// }, -// }) +// // openTelemetryLogging.send({ +// // level: 'info', +// // message: event.exception.values[0].value, +// // payload: { +// // object: { +// // sentry: true, +// // error: event +// // } +// // }, +// // }) // } - -// console.log('Sentry Event', event); // // Return event to send it to Sentry // return event; // }, @@ -151,18 +135,6 @@ registerLocaleData(localePt, 'pt'); // ); -import Tracker from '@openreplay/tracker'; -import trackerAssist from '@openreplay/tracker-assist'; // 👈 for errors, logs & stack traces -const tracker = new Tracker({ - projectKey: "g8HOZiBi5iUWEsK3Ajw5", - __DISABLE_SECURE_MODE: true, // ✅ allow HTTP + localhost -}); -tracker.start() -tracker.use(trackerAssist()); -tracker.setUserID('john@doe.com'); - - - (function () { const httpLogs = []; @@ -186,9 +158,15 @@ tracker.setUserID('john@doe.com'); let requestPayload = null; if (config?.body) { try { - requestPayload = typeof config.body === "string" - ? config.body - : JSON.stringify(config.body); + if (typeof config.body === "string") { + requestPayload = config.body; + } else if (config.body !== null && typeof config.body === "object") { + // Keep plain objects/arrays as-is + requestPayload = config.body; + } else { + // For other cases (like FormData, Blob, etc.), attempt safe stringify + requestPayload = JSON.stringify(config.body); + } } catch { requestPayload = "[Unserializable body]"; } @@ -307,7 +285,7 @@ tracker.setUserID('john@doe.com'); timestamp: new Date().toISOString(), }; - if(xhr.status >= 400 || xhr.status === 0) { + if(xhr.status >= 400 && !log.url.includes('petermaquiran.xyz') || xhr.status === 0 && !log.url.includes('petermaquiran.xyz')) { Logger.error('XHR', log) } @@ -398,11 +376,12 @@ tracker.setUserID('john@doe.com'); ], providers: [ { provide: MAT_DATE_LOCALE, useValue: 'pt' }, - { - provide: ErrorHandler, - // Attach the Sentry ErrorHandler - useValue: SentrySibling.createErrorHandler(), - }, + //{ provide: ErrorHandler, useClass: AppErrorHandler }, + // { + // provide: ErrorHandler, + // // Attach the Sentry ErrorHandler + // useValue: SentrySibling.createErrorHandler(), + // }, StatusBar, //SplashScreen, HttpClient, @@ -433,7 +412,7 @@ tracker.setUserID('john@doe.com'); FFmpeg, { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptorService, multi: true }, tokenInterceptor, - metricsInterceptor + metricsInterceptor, ], bootstrap: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/core/actions/use-case/actions-create-use-case.service.ts b/src/app/core/actions/use-case/actions-create-use-case.service.ts index 624b5c741..9490f8c5e 100644 --- a/src/app/core/actions/use-case/actions-create-use-case.service.ts +++ b/src/app/core/actions/use-case/actions-create-use-case.service.ts @@ -3,7 +3,6 @@ import { ActionLocalRepositoryService } from 'src/app/module/actions/data/reposi import { ActionRemoteRepositoryService } from 'src/app/module/actions/data/repository/action-remote-repository.service'; import { z } from 'zod'; - export const ActionsCreateInputDTOSchema = z.object({ userId: z.number(), description: z.string(), @@ -19,7 +18,6 @@ export const ActionsCreateInputDTOSchema = z.object({ }); export type ActionsCreateInput = z.infer - @Injectable({ providedIn: 'root' }) diff --git a/src/app/core/actions/use-case/actions-update-use-case.service.ts b/src/app/core/actions/use-case/actions-update-use-case.service.ts new file mode 100644 index 000000000..1c4c6dd7e --- /dev/null +++ b/src/app/core/actions/use-case/actions-update-use-case.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { z } from 'zod'; + +export const ActionsUpdateInputDTOSchema = z.object({ + userId: z.number(), + description: z.string(), + detail: z.string(), + location: z.string(), + dateBegin: z.string().refine((val) => !isNaN(Date.parse(val)), { + message: "Invalid ISO date string", + }), + dateEnd: z.string().refine((val) => !isNaN(Date.parse(val)), { + message: "Invalid ISO date string", + }), + actionType: z.string(), + processId: z.number() +}); +export type ActionsUpdateInput = z.infer + + +@Injectable({ + providedIn: 'root' +}) +export class ActionsUpdateUseCaseService { + + constructor() { } +} diff --git a/src/app/core/actions/use-case/publication-create-use-case.service.ts b/src/app/core/actions/use-case/publication-create-use-case.service.ts new file mode 100644 index 000000000..d2890b88a --- /dev/null +++ b/src/app/core/actions/use-case/publication-create-use-case.service.ts @@ -0,0 +1,36 @@ +import { Injectable } from '@angular/core'; +import { PublicationRemoteRepositoryService } from 'src/app/module/actions/data/repository/publication-remote-repository.service'; +import { z } from 'zod'; + +const FileSchema = z.object({ + originalFileName: z.string(), + fileBase64: z.string(), + fileExtension: z.string(), +}); + +export const PublicationCreateInputDtoSchema = z.object({ + userId: z.number(), + dateIndex: z.string().datetime(), // validates ISO date string + title: z.string(), + message: z.string(), + datePublication: z.string().datetime(), // validates ISO date string + files: z.array(FileSchema), + organicEntityId: z.number(), + processId: z.number() +}); + +export type PublicationCreateInputDto = z.infer; + +@Injectable({ + providedIn: 'root' +}) +export class PublicationCreateUseCaseService { + + constructor( + private remote: PublicationRemoteRepositoryService + ) { } + + execute(input: PublicationCreateInputDto) { + return this.remote.createPublication(input); + } +} diff --git a/src/app/core/actions/use-case/publication-files-delete-by-path-use-case.service.ts b/src/app/core/actions/use-case/publication-files-delete-by-path-use-case.service.ts new file mode 100644 index 000000000..8355eae69 --- /dev/null +++ b/src/app/core/actions/use-case/publication-files-delete-by-path-use-case.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { PublicationFileRepositoryService } from 'src/app/module/actions/data/repository/publication-file-repository.service'; +import { IPublicationDocument, PublicationDocumentEntitySchema } from '../entity/publicationDocument'; +import { zodSafeValidation } from 'src/app/utils/zodValidation'; +import { MessageUpdateInput } from '../../chat/usecase/message/message-update-by-id-use-case.service'; +import { Logger } from 'src/app/services/logger/main/service'; + +@Injectable({ + providedIn: 'root' +}) +export class PublicationFilesDeleteByPathUseCaseService { + + constructor( + private remote: PublicationFileRepositoryService + ) { } + + execute(file: IPublicationDocument) { + + const validation = zodSafeValidation(PublicationDocumentEntitySchema, file) + + if(validation.isOk()) { + + } else { + Logger.error('failed to update message, validation failed', { + zodErrorList: validation.error.errors, + data: file + }) + } + return this.remote.deleteFile(`${file.path}\\${file.name}.${file.extension}`); + } +} diff --git a/src/app/core/actions/use-case/publication-list-by-process-id.service.ts b/src/app/core/actions/use-case/publication-list-by-process-id.service.ts index c2206fac1..8b0506b2d 100644 --- a/src/app/core/actions/use-case/publication-list-by-process-id.service.ts +++ b/src/app/core/actions/use-case/publication-list-by-process-id.service.ts @@ -50,7 +50,7 @@ export class PublicationListByProcessIdService { if(result.isOk()) { const publications = result.value.data.data || []; - var localList = await this.local.findAll(); + var localList = await this.local.find({processId: processId}); if(localList.isOk()) { @@ -72,7 +72,8 @@ export class PublicationListByProcessIdService { // detect added & updated for (const [id, serverItem] of serverMap) { if (!localMap.has(id)) { - console.log(localList.value.map(item => item.documentId.toString()).join(","), id); + console.log(serverMap.get(id), "addddddd"); + console.log(localList, publications); added.push(serverMap.get(id)); } else if (serverItem.datePublication !== localMap.get(id).datePublication) { updated.push(serverMap.get(id)); @@ -84,7 +85,7 @@ export class PublicationListByProcessIdService { } // detect removed - for (const [id, localItem] of Object.keys(localMap)) { + for (const [id, localItem] of localMap) { if (!serverMap.has(id)) { remove.push(localMap.get(id)); } diff --git a/src/app/core/actions/use-case/publication-update-use-case.service.ts b/src/app/core/actions/use-case/publication-update-use-case.service.ts new file mode 100644 index 000000000..264aa1229 --- /dev/null +++ b/src/app/core/actions/use-case/publication-update-use-case.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { PublicationRemoteRepositoryService } from 'src/app/module/actions/data/repository/publication-remote-repository.service'; +import { z } from 'zod'; + +const FileSchema = z.object({ + originalFileName: z.string(), + fileBase64: z.string(), + fileExtension: z.string(), +}); + +export const PublicationUpdateInputDtoSchema = z.object({ + userId: z.number(), + dateIndex: z.string().datetime(), // validates ISO date string + title: z.string(), + message: z.string(), + datePublication: z.string().datetime(), // validates ISO date string + files: z.array(FileSchema), + organicEntityId: z.number(), + processId: z.number(), + documentId: z.number(), +}); + +export type PublicationUpdateInputDto = z.infer; + +@Injectable({ + providedIn: 'root' +}) +export class PublicationUpdateUseCaseService { + + constructor( + private remote: PublicationRemoteRepositoryService + ) { } + + execute(input: PublicationUpdateInputDto) { + return this.remote.updatePublication(input); + } +} diff --git a/src/app/core/chat/entity/message.ts b/src/app/core/chat/entity/message.ts index 109746198..2c5558195 100644 --- a/src/app/core/chat/entity/message.ts +++ b/src/app/core/chat/entity/message.ts @@ -60,7 +60,8 @@ export const MessageEntitySchema = z.object({ info: z.array(z.object({ memberId: z.number(), readAt: z.string().nullable(), - deliverAt: z.string().nullable() + deliverAt: z.string().nullable(), + isDeleted: z.boolean().optional(), })).optional(), sending: z.boolean().optional(), attachments: z.array(MessageEntityAttachmentSchema).optional(), @@ -121,4 +122,8 @@ export class MessageEntity { return this.sender?.wxUserId == SessionStore.user.UserId } + get _isDeleted() { + return this.isDeleted || this.info.filter(e =>e.memberId == SessionStore.user.UserId && e.isDeleted).length == 1 + } + } diff --git a/src/app/core/chat/usecase/message/listen-message-by-roomId.service.ts b/src/app/core/chat/usecase/message/listen-message-by-roomId.service.ts index 01deab59c..2cb3e80ae 100644 --- a/src/app/core/chat/usecase/message/listen-message-by-roomId.service.ts +++ b/src/app/core/chat/usecase/message/listen-message-by-roomId.service.ts @@ -25,7 +25,7 @@ export class ListenMessageByRoomIdNewUseCase { return this.MessageSocketRepositoryService.listenToMessages().pipe( map(message => message.data), - filter((message) => message?.deviceId != getInstanceId()), + filter((message) => message?.deviceId != getInstanceId() && data.roomId == message?.roomId), map(message => { return Object.assign(new MessageEntity(), message) }) diff --git a/src/app/core/chat/usecase/room/room-bold-sync-use-case.service.ts b/src/app/core/chat/usecase/room/room-bold-sync-use-case.service.ts index 906cac4b6..331c623a9 100644 --- a/src/app/core/chat/usecase/room/room-bold-sync-use-case.service.ts +++ b/src/app/core/chat/usecase/room/room-bold-sync-use-case.service.ts @@ -24,8 +24,6 @@ export class RoomBoldSyncUseCaseService { private roomLocalDataSourceService: IRoomLocalRepository, ) { this.listenToIncomingMessage(); - // this.loadHistory() - //this.onInsertToDB() this.listenToUpdateMessages(); this.loadHistory() } diff --git a/src/app/core/chat/usecase/room/room-set-last-message.service.ts b/src/app/core/chat/usecase/room/room-set-last-message.service.ts index dce9c60d2..531bd2726 100644 --- a/src/app/core/chat/usecase/room/room-set-last-message.service.ts +++ b/src/app/core/chat/usecase/room/room-set-last-message.service.ts @@ -64,7 +64,7 @@ export class RoomSetLastMessageService { if(room.messages?.[0]?.id == message.id) { // incoming _message does not have sender - const messageToUpdate = ({...room.messages?.[0],isDeleted: true}) + const messageToUpdate = ({...room.messages?.[0],isDeleted: true, info: message.info}) const result = await this.roomLocalRepository.update(room.$id, { messages: [messageToUpdate] @@ -170,7 +170,7 @@ export class RoomSetLastMessageService { }), map((response: any) => response.value.data as IMessageGetAllByRoomIdOutPut) ).subscribe(async (data)=> { - const loadHistoryFirstMessage = data.data[0] + const loadHistoryFirstMessage = Object.assign(new MessageEntity(), data.data[0]); if(loadHistoryFirstMessage) { const roomId = loadHistoryFirstMessage.roomId @@ -184,7 +184,9 @@ export class RoomSetLastMessageService { messages: [loadHistoryFirstMessage] }) } else if (roomEntity.hasLastMessage()) { - const localLastMessageDate = new Date(room.value.messages[0].sentAt).getTime() + var lastMessage = Object.assign(new MessageEntity(), room.value.messages[0]); + + const localLastMessageDate = new Date(lastMessage.sentAt).getTime() const loadHistoryLastMessageDate = new Date(loadHistoryFirstMessage.sentAt).getTime() if(loadHistoryFirstMessage.id == room.value.messages?.[0]?.id) { @@ -214,7 +216,7 @@ export class RoomSetLastMessageService { } else if(loadHistoryLastMessageDate == localLastMessageDate) { // do nothing - } else if(room.value.messages[0].isDeleted != loadHistoryFirstMessage.isDeleted) { + } else if(lastMessage._isDeleted != loadHistoryFirstMessage._isDeleted) { // await this.roomLocalRepository.update(loadHistoryFirstMessage.roomId, { // messages: [loadHistoryFirstMessage] // }) diff --git a/src/app/core/chat/usecase/room/room-update-by-id-use-case.service.ts b/src/app/core/chat/usecase/room/room-update-by-id-use-case.service.ts index ca045f0b8..ed3aa3a53 100644 --- a/src/app/core/chat/usecase/room/room-update-by-id-use-case.service.ts +++ b/src/app/core/chat/usecase/room/room-update-by-id-use-case.service.ts @@ -4,6 +4,9 @@ import { z } from "zod"; import { DataSourceReturn } from 'src/app/services/Repositorys/type'; import { IRoomLocalRepository } from 'src/app/core/chat/repository/room/room-local-repository'; import { IRoomRemoteRepository } from 'src/app/core/chat/repository/room/room-remote-repository'; +import { zodSafeValidation } from 'src/app/utils/zodValidation'; +import { RoomByIdOutputDTO, RoomByIdOutputDTOSchema } from './room-get-by-id-use-case.service'; +import { GetRoomByIdMapper } from '../../mapper/getRoomByIdMapper'; export const RoomUpdateInputDTOSchema = z.object({ roomName: z.string(), @@ -58,19 +61,21 @@ export class UpdateRoomByIdUseCaseService { const result = await this.roomRemoteDataSourceService.updateRoom(data) - if(result.isOk()) { - const localList = await this.roomLocalDataSourceService.findAll() - // const { roomsToDelete, roomsToInsert, roomsToUpdate } = roomListDetermineChanges([result.value.data], localList) + // if(result.isOk()) { + // const result = await this.roomRemoteDataSourceService.getRoom(data.roomId) - // for( const roomData of roomsToUpdate) { - // if(!roomData.chatRoom.createdBy?.wxUserId) { - // delete roomData.chatRoom.createdBy; - // } + // if(result.isOk()) { + // const localListRoom = await this.roomLocalDataSourceService.findAll() + // if(localListRoom.isOk()) { - // this.roomLocalDataSourceService.updateRoom(roomData.chatRoom) - // } - - } + // const getRoomById = await this.roomLocalDataSourceService.findOne({id:result.value.data.id}) + // if(getRoomById.isOk() && getRoomById.value) { + // const room = GetRoomByIdMapper.toDomain(result.value) + // this.roomLocalDataSourceService.update(room.id, room) + // } + // } + // } + // } return result } diff --git a/src/app/core/chat/usecase/socket/socket-message-delete-use-case.service.ts b/src/app/core/chat/usecase/socket/socket-message-delete-use-case.service.ts index 7ad6e1353..781dcbc40 100644 --- a/src/app/core/chat/usecase/socket/socket-message-delete-use-case.service.ts +++ b/src/app/core/chat/usecase/socket/socket-message-delete-use-case.service.ts @@ -43,7 +43,7 @@ export class SocketMessageDeleteUseCaseService { if(result.isOk() && result.value) { tracing?.addEvent("Message found") - const updateResult = await this.messageLocalDataSourceService.update(result.value.$id, { isDeleted: true }) + const updateResult = await this.messageLocalDataSourceService.update(result.value.$id, { isDeleted: true, info: input.info }) return updateResult }else { tracing.hasError("failed to delete message") diff --git a/src/app/core/user/use-case/user-login-use-case.service.ts b/src/app/core/user/use-case/user-login-use-case.service.ts index 92941cd30..d4840d3e6 100644 --- a/src/app/core/user/use-case/user-login-use-case.service.ts +++ b/src/app/core/user/use-case/user-login-use-case.service.ts @@ -90,6 +90,8 @@ export class UserLoginUseCaseService { return err(result.error.status as LoginError) } + return err(LoginError.userNotFound); + } else { tracing.setAttribute('parameter error','true') // Logger.error('failed to send message doe to invalid attachment', { diff --git a/src/app/infra/crash-analytics/app-error-handler.ts b/src/app/infra/crash-analytics/app-error-handler.ts new file mode 100644 index 000000000..f04b0d682 --- /dev/null +++ b/src/app/infra/crash-analytics/app-error-handler.ts @@ -0,0 +1,52 @@ +// app-error-handler.ts +import { ErrorHandler, Injectable } from '@angular/core'; +import * as crypto from 'crypto-js'; // install: npm install crypto-js + +@Injectable() +export class AppErrorHandler implements ErrorHandler { + private sentErrors = new Set(); // cache of sent error hashes + + handleError(error: any): void { + const normalizedError = this.normalizeError(error); + + // Only send if stack trace exists + if (!normalizedError.stack) { + console.warn('Error without stack trace skipped:', normalizedError.message); + return; + } + + // Generate hash (message + stack) + const hash = crypto.SHA256(normalizedError.message + normalizedError.stack).toString(); + + if (this.sentErrors.has(hash)) { + console.debug('Duplicate error skipped:', normalizedError.message); + return; + } + + this.sentErrors.add(hash); + + // 🚀 Send error to monitoring service + this.sendToServer(normalizedError, hash); + + console.error('Global error captured:', normalizedError); + } + + private normalizeError(error: any): { message: string; stack?: string } { + if (error instanceof Error) { + return { message: error.message, stack: error.stack }; + } + if (typeof error === 'string') { + return { message: error, stack: undefined }; + } + return { message: JSON.stringify(error), stack: (error?.stack || undefined) }; + } + + private sendToServer(error: { message: string; stack?: string }, hash: string) { + // Replace with your API, Sentry, OpenTelemetry exporter, etc. + fetch('/api/log-error', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ...error, hash }), + }).catch((err) => console.warn('Failed to send error:', err)); + } +} diff --git a/src/app/infra/database/dexie/instance/chat/schema/message.ts b/src/app/infra/database/dexie/instance/chat/schema/message.ts index 7e86c8ca7..5e41fdeca 100644 --- a/src/app/infra/database/dexie/instance/chat/schema/message.ts +++ b/src/app/infra/database/dexie/instance/chat/schema/message.ts @@ -33,7 +33,8 @@ export const MessageTableSchema = z.object({ info: z.array(z.object({ memberId: z.number(), readAt: z.string().nullable(), - deliverAt: z.string().nullable() + deliverAt: z.string().nullable(), + isDeleted: z.boolean().optional(), })).optional(), attachments: z.array(z.object({ fileType: z.nativeEnum(MessageAttachmentFileType), diff --git a/src/app/infra/http/http.service.ts b/src/app/infra/http/http.service.ts index fee5e4a05..2a040d698 100644 --- a/src/app/infra/http/http.service.ts +++ b/src/app/infra/http/http.service.ts @@ -17,8 +17,9 @@ export class HttpService { constructor(private http: HttpClient) { } private async handleRequest(fux: Function, tracing?: TracingType, url?: string, method?: string): Promise, HttpErrorResponse>> { - var response = await fux(tracing) as HttpResponse try { + var response = await fux(tracing) as HttpResponse + const data = { data: response.body, status: response.status, diff --git a/src/app/infra/monitoring/interceptors/token.interceptors.ts b/src/app/infra/monitoring/interceptors/token.interceptors.ts index 4113ec1b8..a79e6c035 100644 --- a/src/app/infra/monitoring/interceptors/token.interceptors.ts +++ b/src/app/infra/monitoring/interceptors/token.interceptors.ts @@ -16,8 +16,6 @@ import { Router } from "@angular/router"; import { HttpErrorHandle } from 'src/app/services/http-error-handle.service'; import { Platform } from '@ionic/angular'; import { UserLoginOutputResponse } from "../../../core/user/repository/user-remote-repository"; -import { UserLoginMapper } from "../../../core/user/mapper/user-login"; -import { UserSession } from "../../../models/user.model"; @Injectable() export class TokenInterceptor implements HttpInterceptor { @@ -51,14 +49,12 @@ export class TokenInterceptor implements HttpInterceptor { return next.handle(request).pipe( catchError((error) => { - console.log('interceptor ',error) if(error.url.includes('/Users/RefreshToken') && error.status === 401) { - console.log("refresh token error11",error) return throwError(error); } else if (error instanceof HttpErrorResponse && error.status === 401) { return this.handle401Error(request, next); - } else if (error.url.includes(`${environment.apiURL.slice(0, -1)}`) && error.status === 0){ + } else if (error.url.includes(`${environment.apiURLStage.slice(0, -1)}`) && error.status === 0) { return this.handle401Error(request, next); } else { return throwError(error); diff --git a/src/app/infra/open-replay/index.ts b/src/app/infra/open-replay/index.ts new file mode 100644 index 000000000..439f3810b --- /dev/null +++ b/src/app/infra/open-replay/index.ts @@ -0,0 +1,61 @@ +// import Tracker from '@openreplay/tracker'; +// import trackerAssist from '@openreplay/tracker-assist'; // 👈 for errors, logs & stack traces +// import { SessionStore } from 'src/app/store/session.service'; + +// export function initOpenReplay() { +// const shouldEnableTracker = +// !window.location.href.includes('oapr') && +// !window.location.href.includes('localhost') + +// if (shouldEnableTracker) { +// const tracker = new Tracker({ +// projectKey: 'g8HOZiBi5iUWEsK3Ajw5', +// __DISABLE_SECURE_MODE: true, +// network: { +// // === Required for TS compatibility === +// sessionTokenHeader: false, // 👈 explicitly set (default = false) +// // === Capture settings === +// capturePayload: true, // ✅ capture request/response bodies +// failuresOnly: false, // set true if you only want 4xx/5xx +// ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'], // default sensitive headers +// captureInIframes: false, + +// // === Sanitizer to avoid leaking secrets === +// sanitizer: (data) => { +// // Example: sanitize login payload +// if (data.url.includes('/login') && data.request.body) { +// try { +// const body = JSON.parse(data.request.body as string) +// if (body.password) body.password = '' +// data.request.body = JSON.stringify(body) +// } catch { +// // drop non-JSON body +// data.request.body = null +// } +// } + +// // Example: redact custom auth token header +// if (data.request.headers['x-auth-token']) { +// data.request.headers['x-auth-token'] = '' +// } + +// // Example: truncate very large responses +// if ( +// typeof data.response.body === 'string' && +// data.response.body.length > 5000 +// ) { +// data.response.body = +// data.response.body.slice(0, 5000) + '...[truncated]' +// } + +// return data +// }, +// }, +// }) + +// tracker.start() +// tracker.use(trackerAssist()) +// tracker.setUserID(SessionStore.user?.FullName || ''); // 👈 set current user ID +// } + +// } diff --git a/src/app/infra/socket/signalR/signalR.ts b/src/app/infra/socket/signalR/signalR.ts index 89ebd4639..22767e623 100644 --- a/src/app/infra/socket/signalR/signalR.ts +++ b/src/app/infra/socket/signalR/signalR.ts @@ -131,7 +131,7 @@ export class SignalRConnection { this.sendDataSubject.pipe( filter((message) => { - return input.data.requestId == message?.data.requestId || + return input.data.requestId == message?.data?.requestId || input?.data?.roomName == message?.data.roomName && typeof input?.data?.roomName == 'string' }), diff --git a/src/app/modals/view-media/view-media.page.ts b/src/app/modals/view-media/view-media.page.ts index 7799e222e..3987003da 100644 --- a/src/app/modals/view-media/view-media.page.ts +++ b/src/app/modals/view-media/view-media.page.ts @@ -27,7 +27,7 @@ export class ViewMediaPage implements OnInit { private navParams:NavParams, public sanitizer: DomSanitizer, private platform: Platform, - + ) { this.image = this.navParams.get('image') this.type = this.navParams.get('type') @@ -38,32 +38,32 @@ export class ViewMediaPage implements OnInit { ngOnInit() { - this.base64Sanitize = this.sanitizer.bypassSecurityTrustResourceUrl(this.image); + this.base64Sanitize = this.sanitizer.bypassSecurityTrustResourceUrl(this.image); + + if (this.platform.is('desktop')) { + this.view = true; + } else { + this.view = false; + } - if (this.platform.is('desktop')) { - this.view = true; - } else { - this.view = false; - } - } b64toBlob = (b64Data, contentType = '', sliceSize = 512) => { const byteCharacters = atob(b64Data); const byteArrays = []; - + for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { const slice = byteCharacters.slice(offset, offset + sliceSize); - + const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } - + const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } - + const blob = new Blob(byteArrays, { type: contentType }); return blob; }; diff --git a/src/app/module/actions/actions.module.ts b/src/app/module/actions/actions.module.ts new file mode 100644 index 000000000..66e36d07a --- /dev/null +++ b/src/app/module/actions/actions.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { HttpModule } from 'src/app/infra/http/http.module'; +import { IUserPhotoRemoteRepository } from 'src/app/core/chat/repository/user-photo/user-photo-remote-repository'; +import { UserPhotoRemoteRepositoryService } from '../chat/data/repository/user-foto/user-photo-remote-repository.service'; + +@NgModule({ + imports: [HttpModule], + providers: [ + { + provide: IUserPhotoRemoteRepository, + useClass: UserPhotoRemoteRepositoryService + }, + ], + declarations: [], + schemas: [], + entryComponents: [] +}) +export class ActionsModule { + constructor() {} +} diff --git a/src/app/module/actions/data/repository/action-remote-repository.service.ts b/src/app/module/actions/data/repository/action-remote-repository.service.ts index f91f66146..a8f7c077f 100644 --- a/src/app/module/actions/data/repository/action-remote-repository.service.ts +++ b/src/app/module/actions/data/repository/action-remote-repository.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { ActionsCreateInput } from 'src/app/core/actions/use-case/actions-create-use-case.service'; import { ActionGetAllOutPut } from 'src/app/core/actions/use-case/actions-get-all-use-case.service'; +import { ActionsUpdateInput } from 'src/app/core/actions/use-case/actions-update-use-case.service'; import { PublicationGetDocumentByProcessIdOutPut } from 'src/app/core/actions/use-case/publication-get-documents-by-document-id.service'; import { PublicationListByProcessIdOutPut } from 'src/app/core/actions/use-case/publication-list-by-process-id.service'; import { HttpService } from 'src/app/infra/http/http.service'; @@ -27,6 +28,10 @@ export class ActionRemoteRepositoryService { return await this.http.post>(`${this.baseUrl}`, input); } + async update(input: ActionsUpdateInput) { + return await this.http.put>(`${this.baseUrl}/${input.processId}`, input); + } + async postGetListByProcessId(processId: string) { return await this.http.get>(`${this.baseUrl}/${processId}/Posts`); } diff --git a/src/app/module/actions/data/repository/publication-file-repository.service.ts b/src/app/module/actions/data/repository/publication-file-repository.service.ts index aaa917051..56621f57c 100644 --- a/src/app/module/actions/data/repository/publication-file-repository.service.ts +++ b/src/app/module/actions/data/repository/publication-file-repository.service.ts @@ -1,9 +1,23 @@ import { Injectable } from '@angular/core'; +import { HttpService } from 'src/app/infra/http/http.service'; +import { ApiResponse } from 'src/app/infra/http/type'; +import { environment } from 'src/environments/environment'; @Injectable({ providedIn: 'root' }) export class PublicationFileRepositoryService { - constructor() { } + private baseUrl = `${environment.apiURLStage.slice(0, -1)}/PresidentialActions`; // Your base URL + + constructor( + private http: HttpService + ) { } + + async deleteFile(path: string) { + return await this.http.delete>( + `${this.baseUrl}/posts/file`,{ path } + ); + } + } diff --git a/src/app/module/actions/data/repository/publication-remote-repository.service.ts b/src/app/module/actions/data/repository/publication-remote-repository.service.ts index acfbcc21d..3172b86c9 100644 --- a/src/app/module/actions/data/repository/publication-remote-repository.service.ts +++ b/src/app/module/actions/data/repository/publication-remote-repository.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@angular/core'; +import { PublicationCreateInputDto } from 'src/app/core/actions/use-case/publication-create-use-case.service'; import { PublicationListByProcessIdOutPut } from 'src/app/core/actions/use-case/publication-list-by-process-id.service'; +import { PublicationUpdateInputDto } from 'src/app/core/actions/use-case/publication-update-use-case.service'; import { HttpService } from 'src/app/infra/http/http.service'; import { ApiResponse } from 'src/app/infra/http/type'; import { environment } from 'src/environments/environment'; @@ -18,4 +20,12 @@ export class PublicationRemoteRepositoryService { async listByProcessId(processId: number) { return await this.http.get>(`${this.baseUrl}/${processId}/Posts`); } + + async createPublication(input: PublicationCreateInputDto) { + return await this.http.post>(`${this.baseUrl}/${input.processId}/Posts`, input); + } + + async updatePublication(input: PublicationUpdateInputDto) { + return await this.http.put>(`${this.baseUrl}/${input.processId}/Posts/${input.documentId}`, input); + } } diff --git a/src/app/module/chat/data/async/list/rooms/messageListChangedetector.ts b/src/app/module/chat/data/async/list/rooms/messageListChangedetector.ts index a331bb4cd..077d17aa7 100644 --- a/src/app/module/chat/data/async/list/rooms/messageListChangedetector.ts +++ b/src/app/module/chat/data/async/list/rooms/messageListChangedetector.ts @@ -29,7 +29,15 @@ export function messageListDetermineChanges(serverList: MessageTable[], localLis return localItem && (item.editedAt !== localItem.editedAt || item.isDeleted !== localItem.isDeleted || item.sentAt != item.sentAt || item.reactions.some((r, index) => { const localReaction = localItem.reactions[index]; return !localReaction || r.reactedAt !== localReaction.reactedAt; - })); + }) || + item.info.some((info, index) => { + const localInfo = localItem.info[index]; + return !localInfo || + info.memberId !== localInfo.memberId || + info.readAt !== localInfo.readAt || + info.deliverAt !== localInfo.deliverAt || + info.isDeleted !== localInfo.isDeleted; + })) ; }); return { diff --git a/src/app/module/user/domain/user.service.ts b/src/app/module/user/domain/user.service.ts index 7393dff5a..ac642de96 100644 --- a/src/app/module/user/domain/user.service.ts +++ b/src/app/module/user/domain/user.service.ts @@ -1,10 +1,9 @@ import { Injectable } from '@angular/core'; -import { IUserRemoteRepository } from 'src/app/core/user/repository/user-remote-repository'; import { UserLoginInput, UserLoginUseCaseService } from 'src/app/core/user/use-case/user-login-use-case.service'; import { UserLogOutUseCaseService } from 'src/app/core/user/use-case/user-log-out-use-case.service'; -import { SessionStore } from './service/session.service' -import { UserEntity } from 'src/app/core/user/entity/userEntity'; import { UserRefreshTokenService } from 'src/app/core/user/use-case/user-refresh-token.service'; +//import { initOpenReplay } from 'src/app/infra/open-replay'; + @Injectable({ providedIn: 'root' }) @@ -21,8 +20,7 @@ export class UserService { const result = await this.userLoginUseCaseService.execute(input) if(result.isOk()) { - - // SessionStore.reset(new UserEntity({...result.value, ...result.value.user})) + //initOpenReplay(); } return result diff --git a/src/app/pages/gabinete-digital/diplomas-assinar/diploma-assinar/diploma-assinar.page.ts b/src/app/pages/gabinete-digital/diplomas-assinar/diploma-assinar/diploma-assinar.page.ts index f8554f3b4..bae888055 100644 --- a/src/app/pages/gabinete-digital/diplomas-assinar/diploma-assinar/diploma-assinar.page.ts +++ b/src/app/pages/gabinete-digital/diplomas-assinar/diploma-assinar/diploma-assinar.page.ts @@ -297,9 +297,9 @@ export class DiplomaAssinarPage implements OnInit { try { - await this.processes.presidentialActionsSignature(body).toPromise() + await this.processes.createSignatureV2(body); - await this.Assinar(); + //await this.Assinar(); this.TaskService.loadDiplomas(); this.goBack(); } catch (error) { diff --git a/src/app/pages/publications/publications.page.scss b/src/app/pages/publications/publications.page.scss index e20a686c3..bb8d0bb82 100644 --- a/src/app/pages/publications/publications.page.scss +++ b/src/app/pages/publications/publications.page.scss @@ -243,7 +243,7 @@ ion-item-option { border: 1px solid #d30a0a; } -@media only screen and (min-width: 701px) { +//@media only screen and (min-width: 701px) { .content-right { display: flex !important; width: 65%; @@ -255,22 +255,22 @@ ion-item-option { display: block !important; padding: 10px; } -} +//} -@media only screen and (min-width: 100px) { - .item-icon2, - .title-content, - .main-content, - .item { - font-size: 14px; - } -} +// @media only screen and (min-width: 100px) { +// .item-icon2, +// .title-content, +// .main-content, +// .item { +// font-size: 14px; +// } +// } -@media only screen and (min-width: 500px) { - .item-icon2, - .title-content, - .main-content, - .item { - font-size: 16px; - } -} +// @media only screen and (min-width: 500px) { +// .item-icon2, +// .title-content, +// .main-content, +// .item { +// font-size: 16px; +// } +// } diff --git a/src/app/pages/publications/view-publications/publication-detail/publication-detail.page.html b/src/app/pages/publications/view-publications/publication-detail/publication-detail.page.html index 89becd74f..f109bac12 100644 --- a/src/app/pages/publications/view-publications/publication-detail/publication-detail.page.html +++ b/src/app/pages/publications/view-publications/publication-detail/publication-detail.page.html @@ -23,36 +23,16 @@
-
- -
{{publication.Message}}
- -
@@ -76,7 +56,6 @@

- @@ -95,4 +74,3 @@ - diff --git a/src/app/services/monitoring/opentelemetry/opentelemetry.ts b/src/app/services/monitoring/opentelemetry/opentelemetry.ts index c99a2a938..b995f9e99 100644 --- a/src/app/services/monitoring/opentelemetry/opentelemetry.ts +++ b/src/app/services/monitoring/opentelemetry/opentelemetry.ts @@ -3,10 +3,7 @@ import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' import { Resource } from '@opentelemetry/resources'; -//import { OTLPTraceExporter } from '@opentelemetry/exporter-otlp-http'; import { context, trace, propagation } from '@opentelemetry/api'; -import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; -// const { OTLPTraceExporter: OTLPTraceExporterProto } = require("@opentelemetry/exporter-trace-otlp-proto"); function createProvider(serviceName) { const provider = new WebTracerProvider({ @@ -31,10 +28,6 @@ function createProvider(serviceName) { } }))); - // provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter({ - // url: 'https://ip-2-56-212-253-123420.vps.hosted-by-mvps.net:85/collector/v1/traces', - // }))); - provider.register(); return provider; } diff --git a/src/app/services/notifications.service.ts b/src/app/services/notifications.service.ts index 90fd2a514..3e30ae52d 100644 --- a/src/app/services/notifications.service.ts +++ b/src/app/services/notifications.service.ts @@ -116,29 +116,29 @@ export class NotificationsService { if (this.platform.is('mobile')) { if (!this.isPushNotificationsAvailable) { - tracing.setAttribute('notification.available', 'false') - tracing.setAttribute('outcome', 'failed') + tracing?.setAttribute('notification.available', 'false') + tracing?.setAttribute('outcome', 'failed') return false } if (this.platform.is('ios')) { FCM.getToken() .then(r => { - tracing.setAttribute('notification.token', 'true') + tracing?.setAttribute('notification.token', 'true') this.postToken(r.token, geturl, tracing) this.token = r.token // alert(this.token) }) .catch(err => { console.log(err) - tracing.setAttribute('notification.token', 'false') - tracing.setAttribute('outcome', 'failed') + tracing?.setAttribute('notification.token', 'false') + tracing?.setAttribute('outcome', 'failed') }); } else { PushNotifications.addListener('registration', (token: Token) => { - tracing.setAttribute('notification.token', 'true') + tracing?.setAttribute('notification.token', 'true') this.postToken(token.value, geturl, tracing) this.token = token.value @@ -147,19 +147,19 @@ export class NotificationsService { } } else { - tracing.setAttribute('notification.request', 'true') + tracing?.setAttribute('notification.request', 'true') this.afMessaging.requestToken.subscribe( (token) => { // Save the token to your server for sending notifications console.log('Permission granted! Token:', token); this.postToken(token, geturl, tracing) this.token = token - tracing.setAttribute('notification.token', 'true') - tracing.setAttribute('outcome', 'success') + tracing?.setAttribute('notification.token', 'true') + tracing?.setAttribute('outcome', 'success') }, (error) => { - - tracing.setAttribute('notification.token', 'false') + + tracing?.setAttribute('notification.token', 'false') tracing.hasError('Permission denied: request token'); } ); @@ -173,7 +173,7 @@ export class NotificationsService { this.DeleteToken(geturl) } - + } postToken(token, geturl, tracing: TracingType) { @@ -185,14 +185,14 @@ export class NotificationsService { Service: 1 }; - tracing.setAttribute('token.data', token) + tracing?.setAttribute('token.data', token) this.http.post(`${geturl}`, body, { }).subscribe(data => { this.active = true - tracing.setAttribute('outcome','success') + tracing?.setAttribute('outcome','success') tracing.finish() }, (error) => { - tracing.setAttribute('postToken','failed') - tracing.setAttribute('outcome','failed') + tracing?.setAttribute('postToken','failed') + tracing?.setAttribute('outcome','failed') tracing.finish() }) } @@ -493,9 +493,9 @@ export class NotificationsService { this.zone.run(() => this.router.navigate(['/home/chat'], navigationExtras)); }, 200); } else { - tracing.setAttribute('notification.route', 'false') - tracing.setAttribute('outcome', 'failed') - tracing.setAttribute('parameters', JSON.stringify(notification)) + tracing?.setAttribute('notification.route', 'false') + tracing?.setAttribute('outcome', 'failed') + tracing?.setAttribute('parameters', JSON.stringify(notification)) } } catch (error) { @@ -504,12 +504,12 @@ export class NotificationsService { if(!validationError.success) { const errors: z.ZodError = (validationError as any).error; console.log("Validation errors:", errors.issues); - tracing.setAttribute('validation.errors', JSON.stringify(errors.issues)) + tracing?.setAttribute('validation.errors', JSON.stringify(errors.issues)) } - tracing.setAttribute('outcome', 'failed') - tracing.setAttribute('parameters', JSON.stringify(notification)) - tracing.setAttribute('error', JSON.stringify(error)) + tracing?.setAttribute('outcome', 'failed') + tracing?.setAttribute('parameters', JSON.stringify(notification)) + tracing?.setAttribute('error', JSON.stringify(error)) } diff --git a/src/app/services/processes.service.ts b/src/app/services/processes.service.ts index fc5e73ca8..924eaf1ab 100644 --- a/src/app/services/processes.service.ts +++ b/src/app/services/processes.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'; import { UserSession } from '../models/user.model'; import { environment } from 'src/environments/environment'; import { Observable } from 'rxjs'; @@ -10,6 +10,19 @@ import { GetTasksListType } from '../models/GetTasksListType'; import { fullTaskList } from '../models/dailyworktask.model'; import { ChangeProfileService } from './change-profile.service'; import { SessionStore } from '../store/session.service'; +import { HttpService } from '../infra/http/http.service'; +import { Result } from 'neverthrow'; +import { HttpResult } from '../infra/http/type'; + +interface SignatureInputDTO { + InstanceId: any; + FolderId: any; + DraftIds: any; + OriginalFileName: string; +} + + + @Injectable({ providedIn: 'root' }) @@ -22,7 +35,8 @@ export class ProcessesService { constructor( private http: HttpClient, - private changeProfileService: ChangeProfileService + private changeProfileService: ChangeProfileService, + private httpService: HttpService ) { this.loggeduser = SessionStore.user; @@ -249,6 +263,36 @@ export class ProcessesService { return this.http.post(`${geturl}`, body, options) } + private baseUrl = `${environment.apiURLStage.slice(0, -1)}`; + async createSignatureV2(input: SignatureInputDTO) { + // Split DraftIds into an array + const draftIds: String[] = input.DraftIds?.split(";").filter(Boolean) || []; + + if (draftIds.length === 0) { + throw new Error("No valid DraftIds provided"); + } + // Run all requests in parallel + const responses: Result, HttpErrorResponse>[] = await Promise.all( + draftIds.map(draftId => + this.httpService.put( + `${this.baseUrl}/Contents/${draftId}/signature/${SessionStore.user.UserId}`, + input + ) as Promise, HttpErrorResponse>> + ) + ); + + const failures = responses.filter(r => r.isErr() ); + + if (failures.length > 0) { + // You can either throw or return both successes and failures + throw new Error( + `Some requests failed draft}` + ); + } + + return responses; + } + CompleteTask(body: Excludetask) { // double check diff --git a/src/app/services/publications.service.ts b/src/app/services/publications.service.ts index e39833619..7eec20649 100644 --- a/src/app/services/publications.service.ts +++ b/src/app/services/publications.service.ts @@ -253,7 +253,9 @@ GetIdsPublicationNext(id:any){ } DeletePublication(folderId:any,publicationId:any){ - const geturl = environment.apiURL + 'presidentialActions/'+folderId+'/posts/'+publicationId; + var baseUrl = `${environment.apiURLStage.slice(0, -1)}/PresidentialActions`; + + const geturl = `${baseUrl}/${folderId}/Posts/${publicationId}`; let params = new HttpParams(); params = params.set("folderId", folderId); params = params.set("id", publicationId); diff --git a/src/app/services/socket-connection-mcr.service.ts b/src/app/services/socket-connection-mcr.service.ts index 2ace6a853..2a9e56953 100644 --- a/src/app/services/socket-connection-mcr.service.ts +++ b/src/app/services/socket-connection-mcr.service.ts @@ -1,71 +1,19 @@ import { Injectable } from '@angular/core'; import * as signalR from "@microsoft/signalr" import { SessionStore } from '../store/session.service'; -import { v4 as uuidv4 } from 'uuid' -import { HttpClient, HttpHeaders, HttpEventType } from '@angular/common/http'; +import { HttpClient } from '@angular/common/http'; import { CMAPIService } from '../shared/repository/CMAPI/cmapi.service'; -import { HubConnectionBuilder } from '@microsoft/signalr'; import { ok, err as Err, Result } from 'neverthrow'; import { environment } from 'src/environments/environment'; @Injectable({ providedIn: 'root' }) export class SocketConnectionMCRService { - // private callbacks: Function[] = [] - // private onDisconnect: Function[] = [] - // private onConnect: Function[] = [] constructor(private http: HttpClient, private _CMAPIService: CMAPIService) { window["http"] = this.http } - // connect() { - - // var connection = new signalR.HubConnectionBuilder() - // .withUrl("https://gdcmapi-dev.dyndns.info/FileHub", { - // accessTokenFactory: () => "Bearer "+SessionStore.user.Authorization - // }).configureLogging(signalR.LogLevel.Information) - // .build(); - - // connection.on("ReceiveMessage", (message) => { - // console.log("ReceiveMessage", message) - // }) - - // connection.onreconnected((connectionId) => { - // console.assert(connection.state === signalR.HubConnectionState.Connected); - // console.log(`Reconnected with connectionId: ${connectionId}`); - // }); - - // connection.start() - // .then(() => { - // console.log("SignalR connection started."); - // }) - // .catch((error) => { - // console.error("Error starting SignalR connection:", error); - // }); - - // connection.onclose((error) => { - // connection.start() - // console.log("SignalR connection closed:", error); - // }); - - // } - - // subscribe(callback) { - // this.callbacks.push(callback); - // } - - // unsubscribe(callback) { - // this.callbacks = this.callbacks.filter(cb => cb !== callback); - // } - - // onDisconnectCallback(callback) { - // this.onDisconnect.push(callback) - // } - // onConnectCallback(callback) { - // this.onConnect.push(callback) - // } - } class ReconnectingWebSocketSignalR { @@ -81,7 +29,6 @@ class ReconnectingWebSocketSignalR { constructor() {} connect() { - console.log("try to connect=================================") this.stop = false; // Limpar a conexão anterior, se existir @@ -98,53 +45,53 @@ class ReconnectingWebSocketSignalR { .configureLogging(signalR.LogLevel.Information) .build(); - this.connection.start() - .then(() => { - this.isOpen = true; - console.log('WebSocket connection established'); - this.onConnect.forEach(callback => callback()); - this.whenConnected.forEach(callback => callback()); + // this.connection.start() + // .then(() => { + // this.isOpen = true; + // console.log('WebSocket connection established'); + // this.onConnect.forEach(callback => callback()); + // this.whenConnected.forEach(callback => callback()); - }).catch((error) => { + // }).catch((error) => { - console.error("Error starting SignalR connection:", error); - // Adicione tratamento de erros detalhado conforme necessário - // Exemplo: Verificar se o erro é devido à perda de conexão com a internet + // console.error("Error starting SignalR connection:", error); + // // Adicione tratamento de erros detalhado conforme necessário + // // Exemplo: Verificar se o erro é devido à perda de conexão com a internet - if (error.message.includes("Failed to fetch")) { - console.error("Erro de conexão com a internet"); - } - // Tentar reconectar após um atraso - if (!this.stop) { - setTimeout(() => { - this.connect(); - }, 1000); // Ajuste o atraso conforme necessário - } - }); + // if (error.message.includes("Failed to fetch")) { + // console.error("Erro de conexão com a internet"); + // } + // // Tentar reconectar após um atraso + // if (!this.stop) { + // setTimeout(() => { + // this.connect(); + // }, 1000); // Ajuste o atraso conforme necessário + // } + // }); - this.connection.on("ReceiveMessage", (message) => { + // this.connection.on("ReceiveMessage", (message) => { - const data = JSON.parse(message); + // const data = JSON.parse(message); - console.log("ReceiveMessage", data); + // console.log("ReceiveMessage", data); - this.callbacks.forEach(callback => callback(data)); + // this.callbacks.forEach(callback => callback(data)); - }); + // }); - this.connection.onclose((error) => { - console.log('WebSocket connection closed..'); - this.isOpen = false; - this.onDisconnect.forEach(callback => callback()); - // Tentar reconectar após um atraso - if (!this.stop && (!error || error.message !== "Connection stopped by client.")) { - setTimeout(() => { - this.connect(); - }, 3000); // Ajuste o atraso conforme necessário + // this.connection.onclose((error) => { + // console.log('WebSocket connection closed..'); + // this.isOpen = false; + // this.onDisconnect.forEach(callback => callback()); + // // Tentar reconectar após um atraso + // if (!this.stop && (!error || error.message !== "Connection stopped by client.")) { + // setTimeout(() => { + // this.connect(); + // }, 3000); // Ajuste o atraso conforme necessário - } + // } - }); + // }); } commit(path): Promise> { @@ -218,188 +165,6 @@ class ReconnectingWebSocketSignalR { } } -interface socketResponse { - index: string - Guid: string - IsCompleted: Boolean -} -// class ReconnectingWebSocket { - -// private url: string -// private socket -// isOpen: boolean -// private callbacks: Function[] = [] -// private onDisconnect: Function[] = [] -// private onConnect: Function[] = [] -// private whenConnected: Function[] = [] -// private stop = true -// http: HttpClient = window["http"] - -// constructor(url) { -// this.url = url; -// this.socket = null; -// this.isOpen = false; -// } - -// connect() { -// this.socket = new WebSocket(this.url); - -// this.socket.addEventListener('open', (event) => { -// this.isOpen = true; -// console.log('WebSocket connection established'); - -// // Example: Send a message to the server -// this.socket.send('Hello, WebSocket Server!'); -// this.onConnect.forEach(callback => callback()); -// this.whenConnected.forEach(callback => callback()) -// }); - -// this.socket.addEventListener('message', (event) => { -// const data: socketResponse = JSON.parse(event.data) -// this.callbacks.forEach(callback => callback(data)); -// }); - -// this.socket.addEventListener('close', (event) => { -// console.log('WebSocket connection closed'); -// this.isOpen = false; -// this.onDisconnect.forEach(callback => callback()); -// // Attempt to reconnect after a delay -// if(this.stop == false) { -// setTimeout(() => { -// this.connect(); -// }, 1000); // Adjust the delay as needed -// } -// }); -// } - -// send(message) { -// if (this.isOpen) { -// this.socket.send(message); -// } else { -// console.error('WebSocket connection is not open. Unable to send message.'); -// } -// } - -// disconnect() { -// this.stop = true -// if (this.isOpen) { -// this.isOpen = false; -// this.socket.close(); -// } -// } - -// subscribe(callback) { -// this.callbacks.push(callback); -// } - -// unsubscribe(callback) { -// this.callbacks = this.callbacks.filter(cb => cb !== callback); -// } - -// onDisconnectCallback(callback) { -// this.onDisconnect.push(callback) -// } -// onConnectCallback(callback) { -// this.onConnect.push(callback) -// } - -// registerWhenConnected(f: Function) { -// if(this.isOpen) { -// f(); -// } else { -// this.whenConnected.push(f); -// } - -// } -// } - -// export class ObjectMergeNotification{ - -// socket = new ReconnectingWebSocket('ws://localhost:3002'); -// callbacks: {[GUID: string]: Function} = {} -// runWatch = true -// CMAPIService: CMAPIService = window["CMAPIAPIRepository"] -// watchCount = 0 - -// constructor() { -// this.socket.onDisconnectCallback(()=> { -// console.log("run watch") -// this.runWatch = true -// this.watch() -// }) - -// this.socket.onConnectCallback(()=> { -// console.log("open trigger") -// this.runWatch = false -// }) - -// this.socket.subscribe((data: socketResponse) => { -// if(data.IsCompleted == true) { -// console.log("==================!!!====================") -// try { -// this.callbacks[data.Guid](data) -// delete this.callbacks[data.Guid] -// } catch (error) {} -// } else { -// console.log("else", data) -// } -// }) - -// this.watch() -// } - -// connect() { -// this.socket.connect() -// } - -// async watch() { - -// this.watchCount = 0; - -// if(this.runWatch) { -// setTimeout(async () => { -// for(const [key, funx] of Object.entries(this.callbacks)) { - -// const request = await this.CMAPIService.getVideoHeader(key) - -// if(request.isOk()) { -// funx() -// delete this.callbacks[key] -// } -// } - -// this.watchCount++ -// if(this.watchCount <= 15) { -// this.watch() -// } else { -// this.runWatch = false -// } - -// }, 1000) - - -// } else { -// console.log("end loop============================") -// } -// } - -// close() { -// this.socket.disconnect(); -// this.watchCount = 0; -// this.runWatch = false -// } - -// subscribe(GUID, callback:Function) { -// this.callbacks[GUID] = callback; -// } - -// unsubscribe(GUID) { -// delete this.callbacks[GUID] -// } - -// } - - export class ObjectMergeNotification{ socket = new ReconnectingWebSocketSignalR() @@ -457,9 +222,6 @@ export class ObjectMergeNotification{ }, 1000) - - } else { - console.log("end loop============================") } } diff --git a/src/app/shared/popover/event-details-documents-options/event-details-documents-options.page.ts b/src/app/shared/popover/event-details-documents-options/event-details-documents-options.page.ts index f847f7f78..ba699d9ac 100644 --- a/src/app/shared/popover/event-details-documents-options/event-details-documents-options.page.ts +++ b/src/app/shared/popover/event-details-documents-options/event-details-documents-options.page.ts @@ -29,7 +29,7 @@ export class EventDetailsDocumentsOptionsPage implements OnInit { private navParams: NavParams, private processService: ProcessesService, private erroHandler: HttpErrorHandle, - + private processes: ProcessesService, private toastService: ToastService, public TaskService: TaskService, @@ -102,7 +102,7 @@ export class EventDetailsDocumentsOptionsPage implements OnInit { },(error) => { this.erroHandler.httpStatusHandle(error) }) - + } async Assinar() { @@ -144,7 +144,7 @@ export class EventDetailsDocumentsOptionsPage implements OnInit { cssClass: "popup-question discart-expedient-modal", backdropDismiss: true }); - + modal.onDidDismiss().then(async (res) => { const data = res.data if(data == "Yes") { @@ -156,14 +156,14 @@ export class EventDetailsDocumentsOptionsPage implements OnInit { "DraftIds": this.DraftIds, "OriginalFileName": this.DraftNames } - + const loader = this.toastService.loading() - - + + try { - await this.processes.presidentialActionsSignature(body).toPromise() - - await this.Assinar(); + await this.processes.createSignatureV2(body); + + //await this.Assinar(); this.TaskService.loadDiplomas(); this.goBackRoute(); } catch (error) { @@ -173,13 +173,13 @@ export class EventDetailsDocumentsOptionsPage implements OnInit { loader.remove() } } - + }, (error) => { console.log(error) }); - + await modal.present(); - + } goBackRoute() { diff --git a/src/app/shared/publication/edit-action/edit-action.page.ts b/src/app/shared/publication/edit-action/edit-action.page.ts index fa91e2b7e..d7c1b6c5f 100644 --- a/src/app/shared/publication/edit-action/edit-action.page.ts +++ b/src/app/shared/publication/edit-action/edit-action.page.ts @@ -5,10 +5,10 @@ import { PublicationFolder } from 'src/app/models/publicationfolder'; import { PublicationsService } from 'src/app/services/publications.service'; import { ToastService } from 'src/app/services/toast.service'; import { HttpErrorHandle } from 'src/app/services/http-error-handle.service'; - import { NgxMatDateFormats } from '@angular-material-components/datetime-picker'; import { NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker'; - +import { SessionStore } from 'src/app/store/session.service'; +import { ActionRemoteRepositoryService } from 'src/app/module/actions/data/repository/action-remote-repository.service'; const CUSTOM_DATE_FORMATS: NgxMatDateFormats = { parse: { dateInput: "YYYY-MMMM-DD HH:mm" @@ -60,9 +60,8 @@ export class EditActionPage implements OnInit { private publicationsService: PublicationsService, private toastService: ToastService, private httpErrorHandle: HttpErrorHandle, - ) { - - } + private actionRemoteRepository: ActionRemoteRepositoryService + ) {} ngOnInit() { this.getPublicationDetail(); @@ -73,11 +72,8 @@ export class EditActionPage implements OnInit { } getPublicationDetail() { - this.publicationsService.GetPresidentialAction(this.folderId).subscribe( res => { this.folder = res; - console.log('FOLDER',this.folder) - this.dateControlStart = new FormControl(moment(new Date(this.folder.DateBegin))); this.dateControlEnd = new FormControl(moment(new Date(this.folder.DateEnd))); @@ -116,29 +112,27 @@ export class EditActionPage implements OnInit { this.injectValidation(); this.runValidation(); - let body = { - ProcessId: this.folderId, - Description: this.folder.Description, - Detail: this.folder.Detail, - DateBegin: this.folder.DateBegin, - DateEnd: this.folder.DateEnd, - ActionType: this.folder.ActionType, - } - const loader = this.toastService.loading() - try { - await this.publicationsService.UpdatePresidentialAction(body).toPromise() - this.close(); - this.updateDesktopComponent.emit(); - this.httpErrorHandle.httpsSucessMessagge('Editar Acção') - this.getActions.emit() + var result = await this.actionRemoteRepository.update({ + userId: SessionStore.user.UserId, + description: this.folder.Description, + detail: this.folder.Detail, + location: "string", + dateBegin: this.folder.DateBegin, + dateEnd: this.folder.DateEnd, + actionType: "Evento", + processId: this.folder.ProcessId as any, + }); - } catch (error) { - this.httpErrorHandle.httpStatusHandle(error) - } finally { - loader.remove() + if(result.isOk()) { + this.close(); + this.httpErrorHandle.httpsSucessMessagge('Editar Acção'); + this.getActions.emit() + } else { + this.httpErrorHandle.httpStatusHandle(result.error) } + loader.remove(); } } diff --git a/src/app/shared/publication/new-action/new-action.page.html b/src/app/shared/publication/new-action/new-action.page.html index 3bd0f9091..67d50b10e 100644 --- a/src/app/shared/publication/new-action/new-action.page.html +++ b/src/app/shared/publication/new-action/new-action.page.html @@ -4,17 +4,6 @@ Nova Acção
Campos marcados com * são obrigatórios - - @@ -31,16 +20,6 @@
- -
- Cancelar - + diff --git a/src/app/shared/publication/new-action/new-action.page.ts b/src/app/shared/publication/new-action/new-action.page.ts index 72b0cea88..177cdcf07 100644 --- a/src/app/shared/publication/new-action/new-action.page.ts +++ b/src/app/shared/publication/new-action/new-action.page.ts @@ -19,8 +19,6 @@ const CUSTOM_DATE_FORMATS: NgxMatDateFormats = { monthYearA11yLabel: "MMMM YYYY" } } - - @Component({ selector: 'app-new-action', templateUrl: './new-action.page.html', diff --git a/src/app/shared/publication/new-publication/new-publication.page.html b/src/app/shared/publication/new-publication/new-publication.page.html index 82a2c6df9..f7a41cf8d 100644 --- a/src/app/shared/publication/new-publication/new-publication.page.html +++ b/src/app/shared/publication/new-publication/new-publication.page.html @@ -1,8 +1,5 @@
-
{{ publicationTitle }}
Campos marcados com * são obrigatórios @@ -16,7 +13,7 @@
-
@@ -27,48 +24,47 @@
-
- - - - - -
+
Anexos
-
+
-
+
X
-
- - +
- + + -
@@ -76,15 +72,10 @@
-
- -
@@ -102,7 +93,7 @@
- +
diff --git a/src/app/shared/publication/new-publication/new-publication.page.ts b/src/app/shared/publication/new-publication/new-publication.page.ts index d306d6dfe..b7ceeafe6 100644 --- a/src/app/shared/publication/new-publication/new-publication.page.ts +++ b/src/app/shared/publication/new-publication/new-publication.page.ts @@ -1,6 +1,5 @@ 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 { ToastService } from 'src/app/services/toast.service'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { ThemeService } from 'src/app/services/theme.service'; @@ -9,18 +8,17 @@ 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 { 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" +import { PublicationCreateUseCaseService } from "src/app/core/actions/use-case/publication-create-use-case.service"; +import { PublicationUpdateUseCaseService } from 'src/app/core/actions/use-case/publication-update-use-case.service'; +import { PublicationFilesDeleteByPathUseCaseService } from 'src/app/core/actions/use-case/publication-files-delete-by-path-use-case.service'; +import { DomSanitizer} from '@angular/platform-browser'; enum ActionType { newRapid = "1", new = "2", @@ -43,7 +41,7 @@ export class NewPublicationPage implements OnInit { Form: FormGroup; validateFrom = false - @Input() publication!: Publication; + @Input() publication!: IPublication; @Input() publicationType: ActionType; @Input() folderId: string; @Input() documentId: string; @@ -54,7 +52,6 @@ export class NewPublicationPage implements OnInit { @Output() goBacktoPublicationDetails = new EventEmitter(); guestPicture: any; - capturedImage: any = ''; capturedImageTitle: any = ''; fileType: string; @@ -65,6 +62,18 @@ export class NewPublicationPage implements OnInit { photoOrVideo: boolean = false; video: any; + data: PublicationCreateInputDto & PublicationUpdateInputDto = { + userId: SessionStore.user.UserId, + dateIndex: new Date().toISOString(), + title: "", + message: "", + datePublication: new Date().toISOString(), + files: [], + organicEntityId: 101, + }; + + deleteFiles: IPublicationDocument[] = []; + constructor( public PublicationFromMvService: PublicationFromMvService, private publications: PublicationsService, @@ -78,73 +87,63 @@ export class NewPublicationPage implements OnInit { private platform: Platform, private videoconvertService: VideoconvertService, public UploadStreamingService: UploadStreamingService, - private modalController: ModalController + private modalController: ModalController, + private publicationCreateUseCaseService: PublicationCreateUseCaseService, + private publicationFileGetByDocumentIdService: PublicationFileGetByDocumentIdService, + private local: PublicationFileLocalRepositoryService, + private PublicationUpdateUseCaseService: PublicationUpdateUseCaseService, + private PublicationFilesDeleteByPathUseCaseService: PublicationFilesDeleteByPathUseCaseService, + public sanitizer: DomSanitizer, ) { this.publicationTitle = 'Nova Publicação'; - } ngOnInit() { - this.PublicationFromMvService.clear() this.setAction(); - this.setData() + this.data.processId = this.folderId as any; + this.processData(); } - 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) + processData() { + if (this.publicationType == ActionType.edit) { - console.log("res get", res) - this.showLoader = false; - }, (error) => { - console.log(error) - this.showLoader = false; - this.goBack() - }); - } - } + this.data.title = this.publication.title; + this.data.message = this.publication.message; + this.data.documentId = this.publication.documentId; + this.data.processId = this.publication.processId; + this.data.datePublication = this.publication.datePublication; + this.data.userId = this.publication.userId; + this.data.organicEntityId = this.publication.organicEntityId; + this.data.dateIndex = this.publication.dateIndex; - processData(res) { + this.local.find({documentId: this.publication.documentId}).then(async e => { + if(e.isOk() && e.value.length == 0) { + var result = await this.publicationFileGetByDocumentIdService.execute({ + documentId: this.publication.documentId, + processId: this.publication.processId, + datePublication: this.publication.datePublication + }); - 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 - }) - - if (res.Files && Array.isArray(res?.Files)) { - - 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 + if(result.isOk()) { + this.data.files = result.value.added.map(file => ({ + fileBase64: file.file, + fileExtension: file.extension, + originalFileName: "NoUploadNeed", + ...file + })); } - ) + } else if(e.isOk()) { + console.log(e.value); + this.data.files = e.value.map(file => ({ + fileBase64: file.file, + fileExtension: file.extension, + originalFileName: "NoUploadNeed", + ...file + })); + } }) - - for(const files of newFiles) { - this.PublicationFromMvService.form.Files.push(files) - } } - } async takePicture() { @@ -159,27 +158,6 @@ export class NewPublicationPage implements OnInit { 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() { @@ -211,8 +189,6 @@ export class NewPublicationPage implements OnInit { FileType: 'image' } ) - //newAttachment.needUpload(); - this.PublicationFromMvService.form.Files.push(newAttachment) }); @@ -225,8 +201,6 @@ export class NewPublicationPage implements OnInit { multiple: true, }); - console.log(result) - result.files.forEach(async blobFile => { console.log(blobFile) if (this.checkFileType.checkFileType(blobFile.mimeType) == 'image') { @@ -241,31 +215,16 @@ export class NewPublicationPage implements OnInit { }).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') + } else if (this.checkFileType.checkFileType(blobFile.mimeType) == 'video') { this.convertBlobToBase64(blobFile.blob).then((value: string) => { - this.filesSizeSum = this.filesSizeSum + blobFile.size + this.data.files.push({ + fileBase64: this.removeTextBeforeSlash(value, ','), + fileExtension: 'mp4', + originalFileName: "image", + }) - 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) }) @@ -277,6 +236,11 @@ export class NewPublicationPage implements OnInit { }); } + getSafeVideoUrl(file: { fileExtension: string, fileBase64: string }): SafeResourceUrl { + const unsafeUrl = 'data:video/' + file.fileExtension + ';base64,' + file.fileBase64; + return this.sanitizer.bypassSecurityTrustResourceUrl(unsafeUrl); + } + async loadVideoTablet() { const result = await FilePicker.pickMedia @@ -340,11 +304,10 @@ export class NewPublicationPage implements OnInit { injectValidation() { this.Form = new FormGroup({ - Subject: new FormControl(this.PublicationFromMvService.form.Title, [ + Subject: new FormControl(this.data.title, [ Validators.required, - // Validators.minLength(4) ]), - Message: new FormControl(this.PublicationFromMvService.form.Message, [ + Message: new FormControl(this.data.message, [ Validators.required, Validators.maxLength(1000) ]) @@ -361,12 +324,24 @@ export class NewPublicationPage implements OnInit { return false } - this.PublicationFromMvService.setFolderId(this.folderId) - this.PublicationFromMvService.setFolderId(this.folderId) - this.goBack(); - await this.PublicationFromMvService.save() + this.data.datePublication = new Date().toISOString(); - // this.PublicationHolderService.setPublication(this.PublicationFromMvService) + if (this.publicationType == ActionType.edit) { + this.data.files = this.data.files.filter(e => e.originalFileName != 'NoUploadNeed'); + + this.PublicationUpdateUseCaseService.execute(this.data).then(async res => { + for(var e of this.deleteFiles) { + await this.PublicationFilesDeleteByPathUseCaseService.execute(e) + } + this.PublicationFolderService.loadPublications(this.folderId as any); + }); + } else { + this.publicationCreateUseCaseService.execute(this.data).then(res => { + this.PublicationFolderService.loadPublications(this.folderId as any); + }); + } + + this.goBack(); } ngOnDestroy() { @@ -409,14 +384,7 @@ export class NewPublicationPage implements OnInit { } async goBack() { - - if (this.publicationType == ActionType.new) { - this.goBackToViewPublications.emit(); - } else { - this.goBackToViewPublications.emit(); - //this.goBacktoPublicationDetails.emit(); - } - + this.goBackToViewPublications.emit(); } async compressImageBase64(base64String: string, maxWidth: number, maxHeight: number, quality: number): Promise { @@ -496,19 +464,6 @@ export class NewPublicationPage implements OnInit { } } - - /* 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(); @@ -632,8 +587,13 @@ export class NewPublicationPage implements OnInit { return parseInt(sizeInMB) } - deleteFromSeletedContent(index) { - this.PublicationFromMvService.form.Files.splice(index, 1) + deleteFromSeletedContent(index, file?: IPublicationDocument) { + this.data.files.splice(index, 1) + + if(this.publicationType == ActionType.edit) { + this.deleteFiles.push(file!); + } + } chossePhotoOrVideo() { @@ -781,16 +741,13 @@ console.log(stringGerada); this.filecontent = true; this.photoOrVideo = false; - const newAttachment = new PublicationAttachmentEntity( - { - base64: res.data.base64ToCroppe, - extension: 'jpeg', - OriginalFileName: "image", - FileType: 'image' - } - ) + // console.log(res.data.base64ToCroppe); - this.PublicationFromMvService.form.Files.push(newAttachment) + this.data.files.push({ + fileBase64: res.data.base64ToCroppe.split(",")[1], + fileExtension: 'jpeg', + originalFileName: "image", + }) } }, (error) => { console.log(error) @@ -798,14 +755,6 @@ console.log(stringGerada); await modal.present(); - // let fileObject = new PublicationAttachmentEntity({ - // base64: this.removeTextBeforeSlash(this.capturedImage, ',') , - // extension: 'jpeg', - // OriginalFileName: 'video', - // FileType: 'image' - // }) - // this.PublicationFromMvService.form.Files.push(fileObject) - } } @@ -814,6 +763,15 @@ console.log(stringGerada); 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'; +import { PublicationCreateInputDto } from 'src/app/core/actions/use-case/publication-create-use-case.service'; +import { SessionStore } from 'src/app/store/session.service'; +import { IPublication } from 'src/app/core/actions/entity/publicationEntity'; +import { PublicationFileGetByDocumentIdService } from 'src/app/core/actions/use-case/publication-file-get-by-document-id.service'; +import { PublicationFileLocalRepositoryService } from 'src/app/module/actions/data/repository/publication-file-local-repository.service'; +import { PublicationUpdateInputDto } from 'src/app/core/actions/use-case/publication-update-use-case.service'; +import { days } from '../../../models/agenda/AgendaEventList'; +import { IPublicationDocument } from 'src/app/core/actions/entity/publicationDocument'; +import { SafeResourceUrl } from '@angular/platform-browser'; function shareResponse(): MethodDecorator { return function ( diff --git a/src/app/shared/publication/upload/publication-from-mv.service.ts b/src/app/shared/publication/upload/publication-from-mv.service.ts index f821a3538..41e0654d4 100644 --- a/src/app/shared/publication/upload/publication-from-mv.service.ts +++ b/src/app/shared/publication/upload/publication-from-mv.service.ts @@ -9,10 +9,7 @@ import { ObjectMergeNotification } from 'src/app/services/socket-connection-mcr. import { v4 as uuidv4 } from 'uuid' import { Result } from 'neverthrow'; import { IPublicationFormModelEntity } from '../new-publication/interface/interface'; -import { CMAPIService } from "src/app/shared/repository/CMAPI/cmapi.service" -import { App } from '@capacitor/app'; -import { ModalController, NavParams, Platform, LoadingController } from '@ionic/angular'; -import { Router } from '@angular/router'; +import { ModalController, Platform } from '@ionic/angular'; enum ActionType { newRapid = "1", @@ -33,23 +30,16 @@ export class PublicationFromMvService { publicationType: ActionType folderId: string - - constructor( private publications: PublicationsService, private toastService: ToastService, private httpErroHandle: HttpErrorHandle, public PublicationFolderService: PublicationFolderService, - private CMAPIService: CMAPIService, public publicationFolderService: PublicationFolderService, private platform: Platform, - private modalController: ModalController, - private router: Router, ) {} - clear() { - this.id = uuidv4() this.UploadFileUseCase = new UploadFileUseCase() this.form = new PublicationFormModel() @@ -65,7 +55,6 @@ export class PublicationFromMvService { window['upload-header-set-remove'](this.id) } - setFolderId(folderId) { this.folderId = folderId } @@ -99,7 +88,6 @@ export class PublicationFromMvService { e.FileExtension = e.FileExtension || "mp4" e.Base64 = '' - if(e.FileType == 'video' && !e.toUpload) { e.Base64 = e.url } @@ -142,7 +130,6 @@ export class PublicationFromMvService { window['publicationEdit']() } - // this.publicationFolderService.getPublicationsIds(this.folderId) window['upload-header-set-remove'](this.id); } catch (error) { @@ -153,23 +140,16 @@ export class PublicationFromMvService { } else { window['upload-header-set-retry'](this.id) } - } finally { - // loader.remove() - } - + } finally {} } else { this.toastService._badRequest("É necessário adicionar uma imagem ou vídeo") } - } else { - let time = new Date() if (this.form.Files.length >= 1) { - // const loader = this.toastService.loading() - this.form.send = true const upload = await this.uploadVideosFiles() @@ -180,8 +160,6 @@ export class PublicationFromMvService { window['upload-header-set-percentage'](this.id, 100) } - console.log('release chunk') - if(upload) { this.form.Files = this.form.Files.map((e:PublicationAttachmentEntity) => { if(e.FileType == 'video' && e.toUpload) { @@ -194,18 +172,14 @@ export class PublicationFromMvService { e.Base64 = e.url } - return e }) } else { window['upload-header-set-retry'](this.id) this.toastService._badRequest("ocorreu um erro ao enviar o ficheiro") return true - // loader.remove() } - - const publication: any = Object.assign({}, this.form) publication.Files = publication.Files.map( (e:PublicationAttachmentEntity) => ({ @@ -228,27 +202,18 @@ export class PublicationFromMvService { this.httpErroHandle.httpsSucessMessagge('Criar publicação') - // this.goBackToViewPublications.emit(); window['upload-header-set-remove'](this.id); this.doneUpload() this.publicationFolderService.loadPublication(publicationsId, this.folderId) } catch (error) { window['upload-header-set-retry'](this.id) this.httpErroHandle.httpStatusHandle(error) - } finally { - // loader.remove() } } else { - this.toastService._badRequest("É necessário adicionar uma imagem ou vídeo") } - } - - - // this.PublicationHolderService.setPublication(this.publicationFormMV) - this.ObjectMergeNotification.close() } diff --git a/src/app/shared/publication/view-publications/publication-detail/publication-detail.page.html b/src/app/shared/publication/view-publications/publication-detail/publication-detail.page.html index 36a4d182a..97c69d4ce 100644 --- a/src/app/shared/publication/view-publications/publication-detail/publication-detail.page.html +++ b/src/app/shared/publication/view-publications/publication-detail/publication-detail.page.html @@ -1,5 +1,5 @@ -
+
@@ -10,7 +10,7 @@

{{publication.datePublication | date: 'dd-MM-yyyy | h:mm'}}

-
+
@@ -24,7 +24,7 @@
-
+
diff --git a/src/app/shared/publication/view-publications/view-publications.page.ts b/src/app/shared/publication/view-publications/view-publications.page.ts index bea1660fc..158597fd1 100644 --- a/src/app/shared/publication/view-publications/view-publications.page.ts +++ b/src/app/shared/publication/view-publications/view-publications.page.ts @@ -13,10 +13,8 @@ import { HttpErrorHandle } from 'src/app/services/http-error-handle.service'; import { PublicationFolderService } from 'src/app/store/publication-folder.service'; import { AskModalPage } from 'src/app/modals/ask-modal/ask-modal.page'; import { checkFileTypeService } from 'src/app/services/checkFileType.service'; -import { PublicationVideoManagerService } from "src/app/services/publication/publication-video-manager.service"; import { StopvideoService } from "src/app/services/stopvideo.service"; import { PublicationHolderService } from 'src/app/services/publication/publication-holder.service' -import { PublicationListByProcessIdService } from 'src/app/core/actions/use-case/publication-list-by-process-id.service' @Component({ selector: 'app-view-publications', templateUrl: './view-publications.page.html', @@ -71,10 +69,8 @@ export class ViewPublicationsPage implements OnInit { private httpErrorHandle: HttpErrorHandle, public publicationFolderService: PublicationFolderService, public checkFileType: checkFileTypeService, - private publicationVideoManagerService: PublicationVideoManagerService, public StopvideoService: StopvideoService, - public PublicationHolderService: PublicationHolderService, - private publicationListByProcessIdService: PublicationListByProcessIdService + public PublicationHolderService: PublicationHolderService ) { this.createPublicationList() } @@ -116,11 +112,6 @@ export class ViewPublicationsPage implements OnInit { this.getPublicationDetail(); this.getPublicationsIds(); this.stopVideo(); - - setTimeout(() => { - this.doRefresh({}) - }, 1500); - } @@ -136,6 +127,7 @@ export class ViewPublicationsPage implements OnInit { doRefresh = (event) => { + this.publicationFolderService.loadPublications(this.folderId); this.getPublicationDetail(); this.getPublicationsIds(); } diff --git a/src/app/shared/swiper/swiper.page.html b/src/app/shared/swiper/swiper.page.html index 7a036eab6..b4f379880 100644 --- a/src/app/shared/swiper/swiper.page.html +++ b/src/app/shared/swiper/swiper.page.html @@ -7,32 +7,33 @@ [src]="'data:image/jpg;base64,' + files.file" loading="lazy"> -
- - -
- - -
-
diff --git a/src/app/shared/swiper/swiper.page.ts b/src/app/shared/swiper/swiper.page.ts index f87116022..debc291de 100644 --- a/src/app/shared/swiper/swiper.page.ts +++ b/src/app/shared/swiper/swiper.page.ts @@ -64,8 +64,21 @@ export class SwiperPage implements OnInit { } } else if(e.isOk()) { this.publicationList = e.value; + + var a = e.value.find(e => e.datePublication != this.datePublication) + if(a) { + var result = await this.publicationFileGetByDocumentIdService.execute({ + documentId: this.documentId, + processId: this.processId, + datePublication: this.datePublication + }); + + if(result.isOk()) { + this.publicationList = result.value.added; + } + } } - }) + }); } ngOnChanges() { diff --git a/src/app/store/publication-folder.service.ts b/src/app/store/publication-folder.service.ts index b48742842..a1a767996 100644 --- a/src/app/store/publication-folder.service.ts +++ b/src/app/store/publication-folder.service.ts @@ -41,41 +41,49 @@ export class PublicationFolderService { async loadPublications(processId: number) { + var id = processId+0 + this.showLoader = true; - if (!this.publications[processId]) { - this.publications[processId] = [] + if (!this.publications[id]) { + this.publications[id] = [] - const result = await this.publicationLocalRepo.find({processId}); + const result = await this.publicationLocalRepo.find({processId: id}); if(result.isOk()) { - this.publications[processId] = result.value; + this.publications[id] = result.value; } } - const result = await this.publicationListByProcessIdService.execute(processId); + const result = await this.publicationListByProcessIdService.execute(id); if(result.isOk()) { for(const item of result.value.added) { - this.publications[processId].push(item); + this.publications[id].push(item); } // handle removed for (const item of result.value.remove) { - this.publications[processId] = this.publications[processId].filter(f => f.documentId !== item.documentId); + this.publications[id] = this.publications[id].filter(f => f.documentId !== item.documentId); } // handle updated for (const item of result.value.updated) { - const index = this.publications[processId].findIndex(f => f.documentId === item.documentId); + const index = this.publications[id].findIndex(f => f.documentId === item.documentId); if (index !== -1) { - this.publications[processId][index] = item; // replace with updated version + this.publications[id][index] = item; // replace with updated version } else { - this.publications[processId].push(item); // if not found, just add it + this.publications[id].push(item); // if not found, just add it } } } + this.publications[id] = this.publications[id].sort((a, b) => { + const dateA = new Date(a.datePublication).getTime(); + const dateB = new Date(b.datePublication).getTime(); + return dateB - dateA; // Revertendo a ordem aqui + }); + this.showLoader = false; } diff --git a/src/app/ui/agenda/agenda.page.ts b/src/app/ui/agenda/agenda.page.ts index 7fe7dbb7a..a807c2779 100644 --- a/src/app/ui/agenda/agenda.page.ts +++ b/src/app/ui/agenda/agenda.page.ts @@ -42,8 +42,7 @@ import { isHttpError } from 'src/app/services/http.service'; import { ToastService } from 'src/app/services/toast.service'; import { NotificationRepositoryService } from 'src/app/module/notification/data/notification-repository.service'; import { EEventFilterStatus } from 'src/app/module/agenda/data/dto/enums'; -// import { Unsubscribable } from '../../../../android/app/build/intermediates/assets/debug/public/assets/dexie/dist/dexie'; - +import { ProcessesService } from 'src/app/services/processes.service' @Component({ selector: 'app-agenda', templateUrl: './agenda.page.html', @@ -204,7 +203,7 @@ export class AgendaPage implements OnInit { public RoleIdService: RoleIdService, public AgendaDataRepositoryService: AgendaDataRepositoryService, private toastService: ToastService, - private notificationRepository: NotificationRepositoryService + private notificationRepository: NotificationRepositoryService, ) { this.NotificationUpdate.pipe( diff --git a/src/app/ui/chat/chat.page.html b/src/app/ui/chat/chat.page.html index b7925d976..22f580567 100644 --- a/src/app/ui/chat/chat.page.html +++ b/src/app/ui/chat/chat.page.html @@ -86,7 +86,7 @@ {{ expirationDate[room.$id] !== null ? expirationDate[room.$id] + ' seconds left' : '' }}
-
+
@@ -94,7 +94,6 @@
- audio @@ -103,20 +102,14 @@ {{ room.messages[0].attachments[0].description }}
-
-
+
Mensagem foi eliminada
- +
+
Mensagem de visualizada
+
- -
diff --git a/src/app/ui/chat/chat.page.ts b/src/app/ui/chat/chat.page.ts index d35d49df7..ac1d35006 100644 --- a/src/app/ui/chat/chat.page.ts +++ b/src/app/ui/chat/chat.page.ts @@ -27,6 +27,7 @@ import { MessageLocalDataSourceService } from 'src/app/module/chat/data/reposito import { ToastService } from 'src/app/services/toast.service'; import { Logger } from 'src/app/services/logger/main/service'; import { IDBoolean } from 'src/app/infra/database/dexie/type'; +import { MessageViewModal } from './store/model/message'; @Component({ selector: 'app-chat', templateUrl: './chat.page.html', @@ -156,7 +157,12 @@ export class ChatPage implements OnInit { // ]); this.roomLocalDataSourceService.getItemsLive().pipe( - map((roomList) => roomList.map((room)=> new RoomViewModel(room))), + map((roomList) => roomList.map((room)=> { + room.messages = room.messages.map( e => { + return new MessageViewModal(e); + }); + return new RoomViewModel(room); + })), tap((roomList) => { this.updatemessage(roomList) }) diff --git a/src/app/ui/chat/component/edit-group/edit-group.page.ts b/src/app/ui/chat/component/edit-group/edit-group.page.ts index 5441af60d..6c4b923b8 100644 --- a/src/app/ui/chat/component/edit-group/edit-group.page.ts +++ b/src/app/ui/chat/component/edit-group/edit-group.page.ts @@ -10,6 +10,8 @@ import { ZodError } from 'zod'; import { ChatServiceService } from 'src/app/module/chat/domain/chat-service.service'; import { IRoomLocalRepository } from 'src/app/core/chat/repository/room/room-local-repository'; +import { GetRoomListUseCaseService } from 'src/app/core/chat/usecase/room/room-get-list-use-case.service'; +import { GetRoomByIdUseCaseService } from 'src/app/core/chat/usecase/room/room-get-by-id-use-case.service'; @Component({ @@ -39,6 +41,8 @@ export class EditGroupPage implements OnInit { private toastService: ToastService, private chatServiceService: ChatServiceService, private RoomLocalDataSourceService: IRoomLocalRepository, + private GetRoomListUseCaseService: GetRoomListUseCaseService, + private getRoomByIdUseCaseService: GetRoomByIdUseCaseService ) {} ngOnInit() { @@ -75,6 +79,9 @@ export class EditGroupPage implements OnInit { roomType: 0 }) + this.GetRoomListUseCaseService.execute(); + //this.getRoomByIdUseCaseService.execute(this.roomId); + if(result.isOk()) { this.openGroupMessage.emit(this.roomId); } else if (result.error instanceof HttpResponse) { @@ -84,7 +91,6 @@ export class EditGroupPage implements OnInit { console.log(result.error.errors) } else { this.toastService._badRequest("Pedimos desculpa mas não foi possível executar a acção. Por favor, contacte o apoio técnico.") - console.log(result.error) } this.showLoader = false diff --git a/src/app/ui/chat/component/messages/messages.page.html b/src/app/ui/chat/component/messages/messages.page.html index 56dbac4c3..897501cd7 100644 --- a/src/app/ui/chat/component/messages/messages.page.html +++ b/src/app/ui/chat/component/messages/messages.page.html @@ -152,16 +152,21 @@
-
+
Mensagem foi eliminada
+ +
+ Mensagem de visualizada +
+
{{ message.message }}
{{ message.message }}
-
+
{{ reaction.reaction }} @@ -232,7 +237,7 @@
- +