mirror of
https://code.equilibrium.co.ao/ITO/doneit-web.git
synced 2026-04-19 04:57:52 +00:00
ITOTEAM-523 notification status
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import { z } from "zod";
|
||||
import { NotificationTable } from '../../infra/db/notification.db'
|
||||
|
||||
type Changes = {
|
||||
insert: NotificationTable[];
|
||||
update: NotificationTable[];
|
||||
remove: NotificationTable[];
|
||||
};
|
||||
|
||||
export function NotificationListChanges(
|
||||
localList: NotificationTable[],
|
||||
serverList: NotificationTable[]
|
||||
): Changes {
|
||||
const changes: Changes = { insert: [], update: [], remove: [] };
|
||||
|
||||
const localMap = new Map(localList.map(item => [item.notificationId, item]));
|
||||
const serverMap = new Map(serverList.map(item => [item.notificationId, item]));
|
||||
|
||||
// Detect new or updated items
|
||||
for (const [id, serverItem] of serverMap) {
|
||||
const localItem = localMap.get(id);
|
||||
if (!localItem) {
|
||||
changes.insert.push(serverItem);
|
||||
} else if (localItem.status !== serverItem.status) {
|
||||
changes.update.push(serverItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect deleted items
|
||||
for (const [id, localItem] of localMap) {
|
||||
if (!serverMap.has(id)) {
|
||||
changes.remove.push(localItem);
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FirebasePushNotificationService } from './firebase-push-notification.service';
|
||||
|
||||
describe('FirebasePushNotificationService', () => {
|
||||
let service: FirebasePushNotificationService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(FirebasePushNotificationService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlertController, Platform } from '@ionic/angular';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { ActionPerformed, PushNotifications, PushNotificationSchema } from '@capacitor/push-notifications';
|
||||
import { AngularFireMessaging } from '@angular/fire/messaging';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FirebasePushNotificationService {
|
||||
|
||||
isPushNotificationsAvailable = Capacitor.isPluginAvailable('PushNotifications');
|
||||
active = false
|
||||
|
||||
constructor(
|
||||
private platform: Platform,
|
||||
private afMessaging: AngularFireMessaging,
|
||||
) {}
|
||||
|
||||
|
||||
onReceiveBackground(callback: Function) {
|
||||
|
||||
if (this.platform.is('mobile')) {
|
||||
if (!this.isPushNotificationsAvailable) {
|
||||
return false
|
||||
}
|
||||
|
||||
PushNotifications.addListener('pushNotificationActionPerformed',
|
||||
(notification: ActionPerformed) => {
|
||||
this.active = true
|
||||
callback(notification)
|
||||
console.log('NOtification Listener Backgroud', notification)
|
||||
|
||||
}
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
|
||||
navigator.serviceWorker.onmessage = (event) => {
|
||||
console.log('Mensagem recebida do Service Worker:', event.data);
|
||||
let object = {
|
||||
notification: event.data
|
||||
}
|
||||
callback(object)
|
||||
if (event.data.notificationClicked) {
|
||||
console.log('Notificação push do Firebase clicada em segundo plano!');
|
||||
}
|
||||
};
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
onReceiveForeground(callback: Function) {
|
||||
|
||||
if (this.platform.is('mobile')) {
|
||||
|
||||
if (!this.isPushNotificationsAvailable) {
|
||||
return false
|
||||
}
|
||||
PushNotifications.addListener('pushNotificationReceived',
|
||||
(notification: PushNotificationSchema) => {
|
||||
this.active = true
|
||||
console.log('NOtification Listener', notification)
|
||||
callback(notification)
|
||||
}
|
||||
);
|
||||
|
||||
} else {
|
||||
this.afMessaging.messages.subscribe((notification) => {
|
||||
console.log('NOtification Listener', notification)
|
||||
callback(notification)
|
||||
// Handle the received message, e.g., show a notification
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LocalNotificationService } from './local-notification.service';
|
||||
|
||||
describe('LocalNotificationService', () => {
|
||||
let service: LocalNotificationService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(LocalNotificationService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NotificationDataSource, NotificationTable, NotificationTableSchema } from '../infra/db/notification.db';
|
||||
import { err, ok } from 'neverthrow';
|
||||
import { from } from 'rxjs';
|
||||
import { liveQuery } from 'Dexie';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LocalNotificationService {
|
||||
|
||||
constructor() { }
|
||||
|
||||
|
||||
async addNotification(data: NotificationTable) {
|
||||
// db.eve
|
||||
try {
|
||||
const result = await NotificationDataSource.notification.add(data)
|
||||
return ok(result)
|
||||
} catch (e) {
|
||||
return err(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async updateNotification(data: NotificationTable) {
|
||||
// db.eve
|
||||
try {
|
||||
const result = await NotificationDataSource.notification.update(data.notificationId, data)
|
||||
return ok(result)
|
||||
} catch (e) {
|
||||
return err(false)
|
||||
}
|
||||
}
|
||||
|
||||
async updateNotifications(data: NotificationTable[]) {
|
||||
// db.eve
|
||||
try {
|
||||
const result = await NotificationDataSource.notification.bulkUpdate(data.map(e => ({
|
||||
key: e.notificationId,
|
||||
changes: { status: e.status }
|
||||
})))
|
||||
return ok(result)
|
||||
} catch (e) {
|
||||
return err(false)
|
||||
}
|
||||
}
|
||||
|
||||
async addNotifications(notifications: NotificationTable[]) {
|
||||
// Validate each notification
|
||||
const failed = []
|
||||
const validNotifications = notifications.filter(notification => {
|
||||
const result = NotificationTableSchema.safeParse(notification);
|
||||
|
||||
if(!result.success) {
|
||||
failed.push(notification)
|
||||
}
|
||||
return result.success;
|
||||
});
|
||||
|
||||
// Add valid notifications to the database
|
||||
if (validNotifications.length > 0) {
|
||||
await NotificationDataSource.notification.bulkAdd(validNotifications);
|
||||
} else {
|
||||
console.log('No valid notifications to add.');
|
||||
}
|
||||
|
||||
console.log({failed})
|
||||
|
||||
return ok(failed)
|
||||
}
|
||||
|
||||
getNotificationLive() {
|
||||
return from(liveQuery( () => {
|
||||
return NotificationDataSource.notification.orderBy('createdAt').reverse().toArray()
|
||||
}))
|
||||
}
|
||||
|
||||
async getNotification() {
|
||||
return NotificationDataSource.notification.toArray()
|
||||
}
|
||||
|
||||
clear() {
|
||||
return NotificationDataSource.notification.clear()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RemoteNotificationService } from './remote-notification.service';
|
||||
|
||||
describe('RemoteNotificationService', () => {
|
||||
let service: RemoteNotificationService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(RemoteNotificationService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpService } from 'src/app/services/http.service';
|
||||
import { NotificationInputDTO } from '../dto/NotificationInputDTO';
|
||||
import { NotificationOutputDTO, NotificationOutputDTOSchema } from '../dto/NotificationOutputDTO';
|
||||
import { APIReturn } from 'src/app/services/decorator/api-validate-schema.decorator';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RemoteNotificationService {
|
||||
|
||||
private baseUrl = 'https://gdapi-dev.dyndns.info/stage/api/v2';
|
||||
|
||||
constructor(
|
||||
private httpService: HttpService
|
||||
) { }
|
||||
|
||||
|
||||
@APIReturn(NotificationOutputDTOSchema, 'Get/Notifications')
|
||||
async getNotification(queryParameter: NotificationInputDTO) {
|
||||
return await this.httpService.get<NotificationOutputDTO>(`${this.baseUrl}/Notifications`, queryParameter);
|
||||
}
|
||||
|
||||
|
||||
async notificationStatus(id: string) {
|
||||
return await this.httpService.patch<NotificationOutputDTO>(`${this.baseUrl}/Notifications/${id}/status`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const NotificationInputDTOSchema = z.object({
|
||||
userId: z.string(),
|
||||
PageNumber: z.string(),
|
||||
PageSize: z.string(),
|
||||
})
|
||||
|
||||
export type NotificationInputDTO = z.infer<typeof NotificationInputDTOSchema>;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const NotificationOutputDTOSchema = z.object({
|
||||
|
||||
success: z.boolean(),
|
||||
message: z.string(),
|
||||
data: z.object({
|
||||
total: z.number(),
|
||||
result: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
service: z.string(),
|
||||
title: z.string(),
|
||||
body: z.string(),
|
||||
object: z.string(),
|
||||
objectId: z.string(),
|
||||
folderId: z.string().nullable(),
|
||||
createdAt: z.string(),
|
||||
viewDate: z.string().nullable(),
|
||||
status: z.boolean(),
|
||||
startDate: z.string().nullable(),
|
||||
endDate: z.string().nullable(),
|
||||
bodyEvent: z.string().nullable(),
|
||||
location: z.string().optional().nullable(),
|
||||
})
|
||||
)
|
||||
|
||||
}),
|
||||
})
|
||||
|
||||
export type NotificationOutputDTO = z.infer<typeof NotificationOutputDTOSchema>;
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Dexie, EntityTable, liveQuery } from 'Dexie';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const NotificationTableSchema = z.object({
|
||||
notificationId: z.string().nullable(),
|
||||
title: z.string().optional().nullable(),
|
||||
service: z.string().nullable(),
|
||||
object: z.string().optional().nullable(),
|
||||
idObject: z.string().nullable(),
|
||||
folderId: z.string().optional().nullable(),
|
||||
dateInit: z.string().optional().nullable(),
|
||||
dateEnd: z.string().optional().nullable(),
|
||||
location: z.string().optional().nullable(),
|
||||
status: z.boolean().optional(),
|
||||
})
|
||||
export type NotificationTable = z.infer<typeof NotificationTableSchema>
|
||||
|
||||
// Database declaration (move this to its own module also)
|
||||
export const NotificationDataSource = new Dexie('NotificationDataSource') as Dexie & {
|
||||
notification: EntityTable<NotificationTable, 'notificationId'>;
|
||||
};
|
||||
|
||||
NotificationDataSource.version(1).stores({
|
||||
notification: 'notificationId, title, service, object, idObject, folderId, dateInit, dateEnd, location, createdAt'
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NotificationRepositoryService } from './notification-repository.service';
|
||||
|
||||
describe('NotificationRepositoryService', () => {
|
||||
let service: NotificationRepositoryService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(NotificationRepositoryService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NotificationInputDTO } from './dto/NotificationInputDTO';
|
||||
import { RemoteNotificationService } from './datasource/remote-notification.service'
|
||||
import { FirebasePushNotificationService } from './datasource/firebase-push-notification.service'
|
||||
import { LocalNotificationService } from './datasource/local-notification.service'
|
||||
import { SessionStore } from 'src/app/store/session.service';
|
||||
import { NotificationListMapper } from '../domain/mapper/notificationListMapper';
|
||||
import { NotificationListChanges } from './async/changes/notificationListChange';
|
||||
import { NotificationTable } from './infra/db/notification.db';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class NotificationRepositoryService {
|
||||
|
||||
constructor(
|
||||
private RemoteNotificationService: RemoteNotificationService,
|
||||
private FirebasePushNotificationService: FirebasePushNotificationService,
|
||||
private LocalNotificationService: LocalNotificationService
|
||||
) {
|
||||
|
||||
this.FirebasePushNotificationService.onReceiveForeground(async (data)=> {
|
||||
console.log('FirebasePushNotificationService', data)
|
||||
|
||||
this.init()
|
||||
|
||||
})
|
||||
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
async init() {
|
||||
const result = await this.getNotification({
|
||||
PageNumber: "1",
|
||||
PageSize: "50",
|
||||
userId: SessionStore.user.UserId.toString()
|
||||
})
|
||||
|
||||
if(result.isOk()) {
|
||||
console.log('notification-list', result.value.data.result)
|
||||
|
||||
if(result.value.data.result.length >= 1) {
|
||||
const localList = await this.LocalNotificationService.getNotification()
|
||||
const serverList = NotificationListMapper(result.value)
|
||||
const { insert, update } = NotificationListChanges(localList, serverList)
|
||||
|
||||
console.log({insert, update})
|
||||
this.LocalNotificationService.addNotifications(insert)
|
||||
|
||||
this.LocalNotificationService.updateNotifications(update)
|
||||
// this.LocalNotificationService.addNotifications.
|
||||
} else {
|
||||
this.LocalNotificationService.clear()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async getNotification(queryParameter: NotificationInputDTO) {
|
||||
return await this.RemoteNotificationService.getNotification(queryParameter)
|
||||
}
|
||||
|
||||
getNotificationLive() {
|
||||
return this.LocalNotificationService.getNotificationLive()
|
||||
}
|
||||
|
||||
async notificationStatus(item: NotificationTable) {
|
||||
await this.RemoteNotificationService.notificationStatus(item.notificationId)
|
||||
item.status = true
|
||||
this.LocalNotificationService.updateNotification(item)
|
||||
this
|
||||
this.init()
|
||||
return
|
||||
}
|
||||
|
||||
async localNotificationStatus(item: NotificationTable) {
|
||||
item.status = true
|
||||
this.LocalNotificationService.updateNotification(item)
|
||||
this.init()
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { NotificationTable } from "../../data/infra/db/notification.db";
|
||||
import { NotificationOutputDTO } from '../../data/dto/NotificationOutputDTO'
|
||||
|
||||
export function NotificationListMapper(NotificationOutputDTO: NotificationOutputDTO): NotificationTable[] {
|
||||
|
||||
return NotificationOutputDTO.data.result.map( e => (
|
||||
{
|
||||
notificationId: e.id,
|
||||
title: e.title,
|
||||
service: e.service,
|
||||
object: e.object,
|
||||
idObject: e.objectId,
|
||||
folderId: e.folderId,
|
||||
dateInit: e.startDate,
|
||||
dateEnd: e.endDate,
|
||||
createdAt: e.createdAt,
|
||||
status: e.status,
|
||||
location: e.location
|
||||
}
|
||||
))
|
||||
}
|
||||
Reference in New Issue
Block a user