mirror of
https://github.com/unraid/api.git
synced 2026-01-05 16:09:49 -06:00
feat: wrap Notifications in a GraphQL Node & implement notification overviews
This commit is contained in:
@@ -11,6 +11,8 @@ export enum PUBSUB_CHANNEL {
|
||||
DISPLAY = 'DISPLAY',
|
||||
INFO = 'INFO',
|
||||
NOTIFICATION = 'NOTIFICATION',
|
||||
NOTIFICATION_ADDED = 'NOTIFICATION_ADDED',
|
||||
NOTIFICATION_OVERVIEW = 'NOTIFICATION_OVERVIEW',
|
||||
OWNER = 'OWNER',
|
||||
SERVERS = 'SERVERS',
|
||||
VMS = 'VMS',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import * as Types from '@app/graphql/generated/api/types';
|
||||
|
||||
import { z } from 'zod'
|
||||
import { AccessUrl, AccessUrlInput, AllowedOriginInput, ApiKey, ApiKeyResponse, ArrayType, ArrayCapacity, ArrayDisk, ArrayDiskFsColor, ArrayDiskStatus, ArrayDiskType, ArrayPendingState, ArrayState, Baseboard, Capacity, Case, Cloud, CloudResponse, Config, ConfigErrorState, Connect, ConnectSignInInput, ConnectUserInfoInput, ContainerHostConfig, ContainerMount, ContainerPort, ContainerPortType, ContainerState, Devices, Disk, DiskFsType, DiskInterfaceType, DiskPartition, DiskSmartStatus, Display, Docker, DockerContainer, DockerNetwork, DynamicRemoteAccessStatus, DynamicRemoteAccessType, EnableDynamicRemoteAccessInput, Flash, Gpu, Importance, Info, InfoApps, InfoCpu, InfoMemory, KeyFile, Me, MemoryFormFactor, MemoryLayout, MemoryType, MinigraphStatus, MinigraphqlResponse, Mount, Network, Node, Notification, NotificationFilter, NotificationType, Os, Owner, ParityCheck, Partition, Pci, ProfileModel, Registration, RegistrationState, RelayResponse, RemoteAccess, Server, ServerStatus, Service, SetupRemoteAccessInput, Share, System, Temperature, Theme, URL_TYPE, UnassignedDevice, Uptime, Usb, User, UserAccount, Vars, Versions, VmDomain, VmState, Vms, WAN_ACCESS_TYPE, WAN_FORWARD_TYPE, Welcome, addApiKeyInput, addUserInput, arrayDiskInput, authenticateInput, deleteUserInput, mdState, registrationType, updateApikeyInput, usersInput } from '@app/graphql/generated/api/types'
|
||||
import { AccessUrl, AccessUrlInput, AllowedOriginInput, ApiKey, ApiKeyResponse, ArrayType, ArrayCapacity, ArrayDisk, ArrayDiskFsColor, ArrayDiskStatus, ArrayDiskType, ArrayPendingState, ArrayState, Baseboard, Capacity, Case, Cloud, CloudResponse, Config, ConfigErrorState, Connect, ConnectSignInInput, ConnectUserInfoInput, ContainerHostConfig, ContainerMount, ContainerPort, ContainerPortType, ContainerState, Devices, Disk, DiskFsType, DiskInterfaceType, DiskPartition, DiskSmartStatus, Display, Docker, DockerContainer, DockerNetwork, DynamicRemoteAccessStatus, DynamicRemoteAccessType, EnableDynamicRemoteAccessInput, Flash, Gpu, Importance, Info, InfoApps, InfoCpu, InfoMemory, KeyFile, Me, MemoryFormFactor, MemoryLayout, MemoryType, MinigraphStatus, MinigraphqlResponse, Mount, Network, Node, Notification, NotificationCounts, NotificationFilter, NotificationOverview, NotificationType, Notifications, NotificationsdataArgs, Os, Owner, ParityCheck, Partition, Pci, ProfileModel, Registration, RegistrationState, RelayResponse, RemoteAccess, Server, ServerStatus, Service, SetupRemoteAccessInput, Share, System, Temperature, Theme, URL_TYPE, UnassignedDevice, Uptime, Usb, User, UserAccount, Vars, Versions, VmDomain, VmState, Vms, WAN_ACCESS_TYPE, WAN_FORWARD_TYPE, Welcome, addApiKeyInput, addUserInput, arrayDiskInput, authenticateInput, deleteUserInput, mdState, registrationType, updateApikeyInput, usersInput } from '@app/graphql/generated/api/types'
|
||||
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||
|
||||
type Properties<T> = Required<{
|
||||
@@ -601,6 +601,16 @@ export function NotificationSchema(): z.ZodObject<Properties<Notification>> {
|
||||
})
|
||||
}
|
||||
|
||||
export function NotificationCountsSchema(): z.ZodObject<Properties<NotificationCounts>> {
|
||||
return z.object({
|
||||
__typename: z.literal('NotificationCounts').optional(),
|
||||
alert: z.number(),
|
||||
info: z.number(),
|
||||
total: z.number(),
|
||||
warning: z.number()
|
||||
})
|
||||
}
|
||||
|
||||
export function NotificationFilterSchema(): z.ZodObject<Properties<NotificationFilter>> {
|
||||
return z.object({
|
||||
importance: ImportanceSchema.nullish(),
|
||||
@@ -610,6 +620,29 @@ export function NotificationFilterSchema(): z.ZodObject<Properties<NotificationF
|
||||
})
|
||||
}
|
||||
|
||||
export function NotificationOverviewSchema(): z.ZodObject<Properties<NotificationOverview>> {
|
||||
return z.object({
|
||||
__typename: z.literal('NotificationOverview').optional(),
|
||||
archive: NotificationCountsSchema(),
|
||||
unread: NotificationCountsSchema()
|
||||
})
|
||||
}
|
||||
|
||||
export function NotificationsSchema(): z.ZodObject<Properties<Notifications>> {
|
||||
return z.object({
|
||||
__typename: z.literal('Notifications').optional(),
|
||||
data: z.array(NotificationSchema()),
|
||||
id: z.string(),
|
||||
overview: NotificationOverviewSchema()
|
||||
})
|
||||
}
|
||||
|
||||
export function NotificationsdataArgsSchema(): z.ZodObject<Properties<NotificationsdataArgs>> {
|
||||
return z.object({
|
||||
filter: NotificationFilterSchema()
|
||||
})
|
||||
}
|
||||
|
||||
export function OsSchema(): z.ZodObject<Properties<Os>> {
|
||||
return z.object({
|
||||
__typename: z.literal('Os').optional(),
|
||||
|
||||
@@ -761,7 +761,7 @@ export type Node = {
|
||||
id: Scalars['ID']['output'];
|
||||
};
|
||||
|
||||
export type Notification = {
|
||||
export type Notification = Node & {
|
||||
__typename?: 'Notification';
|
||||
description: Scalars['String']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
@@ -774,6 +774,14 @@ export type Notification = {
|
||||
type: NotificationType;
|
||||
};
|
||||
|
||||
export type NotificationCounts = {
|
||||
__typename?: 'NotificationCounts';
|
||||
alert: Scalars['Int']['output'];
|
||||
info: Scalars['Int']['output'];
|
||||
total: Scalars['Int']['output'];
|
||||
warning: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type NotificationFilter = {
|
||||
importance?: InputMaybe<Importance>;
|
||||
limit: Scalars['Int']['input'];
|
||||
@@ -781,11 +789,29 @@ export type NotificationFilter = {
|
||||
type?: InputMaybe<NotificationType>;
|
||||
};
|
||||
|
||||
export type NotificationOverview = {
|
||||
__typename?: 'NotificationOverview';
|
||||
archive: NotificationCounts;
|
||||
unread: NotificationCounts;
|
||||
};
|
||||
|
||||
export enum NotificationType {
|
||||
ARCHIVE = 'ARCHIVE',
|
||||
UNREAD = 'UNREAD'
|
||||
}
|
||||
|
||||
export type Notifications = Node & {
|
||||
__typename?: 'Notifications';
|
||||
data: Array<Notification>;
|
||||
id: Scalars['ID']['output'];
|
||||
overview: NotificationOverview;
|
||||
};
|
||||
|
||||
|
||||
export type NotificationsdataArgs = {
|
||||
filter: NotificationFilter;
|
||||
};
|
||||
|
||||
export type Os = {
|
||||
__typename?: 'Os';
|
||||
arch?: Maybe<Scalars['String']['output']>;
|
||||
@@ -928,7 +954,7 @@ export type Query = {
|
||||
/** Current user account */
|
||||
me?: Maybe<Me>;
|
||||
network?: Maybe<Network>;
|
||||
notifications: Array<Notification>;
|
||||
notifications: Notifications;
|
||||
online?: Maybe<Scalars['Boolean']['output']>;
|
||||
owner?: Maybe<Owner>;
|
||||
parityHistory?: Maybe<Array<Maybe<ParityCheck>>>;
|
||||
@@ -970,11 +996,6 @@ export type QuerydockerNetworksArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QuerynotificationsArgs = {
|
||||
filter: NotificationFilter;
|
||||
};
|
||||
|
||||
|
||||
export type QueryuserArgs = {
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
@@ -1124,6 +1145,7 @@ export type Subscription = {
|
||||
info: Info;
|
||||
me?: Maybe<Me>;
|
||||
notificationAdded: Notification;
|
||||
notificationsOverview: NotificationOverview;
|
||||
online: Scalars['Boolean']['output'];
|
||||
owner: Owner;
|
||||
parityHistory: ParityCheck;
|
||||
@@ -1638,7 +1660,7 @@ export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs
|
||||
|
||||
/** Mapping of interface types */
|
||||
export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = ResolversObject<{
|
||||
Node: ( ArrayType ) | ( Config ) | ( Connect ) | ( Docker ) | ( Info ) | ( Network ) | ( Service ) | ( Vars );
|
||||
Node: ( ArrayType ) | ( Config ) | ( Connect ) | ( Docker ) | ( Info ) | ( Network ) | ( Notification ) | ( Notifications ) | ( Service ) | ( Vars );
|
||||
UserAccount: ( Me ) | ( User );
|
||||
}>;
|
||||
|
||||
@@ -1711,8 +1733,11 @@ export type ResolversTypes = ResolversObject<{
|
||||
Network: ResolverTypeWrapper<Network>;
|
||||
Node: ResolverTypeWrapper<ResolversInterfaceTypes<ResolversTypes>['Node']>;
|
||||
Notification: ResolverTypeWrapper<Notification>;
|
||||
NotificationCounts: ResolverTypeWrapper<NotificationCounts>;
|
||||
NotificationFilter: NotificationFilter;
|
||||
NotificationOverview: ResolverTypeWrapper<NotificationOverview>;
|
||||
NotificationType: NotificationType;
|
||||
Notifications: ResolverTypeWrapper<Notifications>;
|
||||
Os: ResolverTypeWrapper<Os>;
|
||||
Owner: ResolverTypeWrapper<Owner>;
|
||||
ParityCheck: ResolverTypeWrapper<ParityCheck>;
|
||||
@@ -1815,7 +1840,10 @@ export type ResolversParentTypes = ResolversObject<{
|
||||
Network: Network;
|
||||
Node: ResolversInterfaceTypes<ResolversParentTypes>['Node'];
|
||||
Notification: Notification;
|
||||
NotificationCounts: NotificationCounts;
|
||||
NotificationFilter: NotificationFilter;
|
||||
NotificationOverview: NotificationOverview;
|
||||
Notifications: Notifications;
|
||||
Os: Os;
|
||||
Owner: Owner;
|
||||
ParityCheck: ParityCheck;
|
||||
@@ -2295,7 +2323,7 @@ export type NetworkResolvers<ContextType = Context, ParentType extends Resolvers
|
||||
}>;
|
||||
|
||||
export type NodeResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Node'] = ResolversParentTypes['Node']> = ResolversObject<{
|
||||
__resolveType: TypeResolveFn<'Array' | 'Config' | 'Connect' | 'Docker' | 'Info' | 'Network' | 'Service' | 'Vars', ParentType, ContextType>;
|
||||
__resolveType: TypeResolveFn<'Array' | 'Config' | 'Connect' | 'Docker' | 'Info' | 'Network' | 'Notification' | 'Notifications' | 'Service' | 'Vars', ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
}>;
|
||||
|
||||
@@ -2311,6 +2339,27 @@ export type NotificationResolvers<ContextType = Context, ParentType extends Reso
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
}>;
|
||||
|
||||
export type NotificationCountsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['NotificationCounts'] = ResolversParentTypes['NotificationCounts']> = ResolversObject<{
|
||||
alert?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
info?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
total?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
warning?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
}>;
|
||||
|
||||
export type NotificationOverviewResolvers<ContextType = Context, ParentType extends ResolversParentTypes['NotificationOverview'] = ResolversParentTypes['NotificationOverview']> = ResolversObject<{
|
||||
archive?: Resolver<ResolversTypes['NotificationCounts'], ParentType, ContextType>;
|
||||
unread?: Resolver<ResolversTypes['NotificationCounts'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
}>;
|
||||
|
||||
export type NotificationsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Notifications'] = ResolversParentTypes['Notifications']> = ResolversObject<{
|
||||
data?: Resolver<Array<ResolversTypes['Notification']>, ParentType, ContextType, RequireFields<NotificationsdataArgs, 'filter'>>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
overview?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
}>;
|
||||
|
||||
export type OsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Os'] = ResolversParentTypes['Os']> = ResolversObject<{
|
||||
arch?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
build?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
@@ -2448,7 +2497,7 @@ export type QueryResolvers<ContextType = Context, ParentType extends ResolversPa
|
||||
info?: Resolver<Maybe<ResolversTypes['Info']>, ParentType, ContextType>;
|
||||
me?: Resolver<Maybe<ResolversTypes['Me']>, ParentType, ContextType>;
|
||||
network?: Resolver<Maybe<ResolversTypes['Network']>, ParentType, ContextType>;
|
||||
notifications?: Resolver<Array<ResolversTypes['Notification']>, ParentType, ContextType, RequireFields<QuerynotificationsArgs, 'filter'>>;
|
||||
notifications?: Resolver<ResolversTypes['Notifications'], ParentType, ContextType>;
|
||||
online?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
||||
owner?: Resolver<Maybe<ResolversTypes['Owner']>, ParentType, ContextType>;
|
||||
parityHistory?: Resolver<Maybe<Array<Maybe<ResolversTypes['ParityCheck']>>>, ParentType, ContextType>;
|
||||
@@ -2543,6 +2592,7 @@ export type SubscriptionResolvers<ContextType = Context, ParentType extends Reso
|
||||
info?: SubscriptionResolver<ResolversTypes['Info'], "info", ParentType, ContextType>;
|
||||
me?: SubscriptionResolver<Maybe<ResolversTypes['Me']>, "me", ParentType, ContextType>;
|
||||
notificationAdded?: SubscriptionResolver<ResolversTypes['Notification'], "notificationAdded", ParentType, ContextType>;
|
||||
notificationsOverview?: SubscriptionResolver<ResolversTypes['NotificationOverview'], "notificationsOverview", ParentType, ContextType>;
|
||||
online?: SubscriptionResolver<ResolversTypes['Boolean'], "online", ParentType, ContextType>;
|
||||
owner?: SubscriptionResolver<ResolversTypes['Owner'], "owner", ParentType, ContextType>;
|
||||
parityHistory?: SubscriptionResolver<ResolversTypes['ParityCheck'], "parityHistory", ParentType, ContextType>;
|
||||
@@ -2898,6 +2948,9 @@ export type Resolvers<ContextType = Context> = ResolversObject<{
|
||||
Network?: NetworkResolvers<ContextType>;
|
||||
Node?: NodeResolvers<ContextType>;
|
||||
Notification?: NotificationResolvers<ContextType>;
|
||||
NotificationCounts?: NotificationCountsResolvers<ContextType>;
|
||||
NotificationOverview?: NotificationOverviewResolvers<ContextType>;
|
||||
Notifications?: NotificationsResolvers<ContextType>;
|
||||
Os?: OsResolvers<ContextType>;
|
||||
Owner?: OwnerResolvers<ContextType>;
|
||||
ParityCheck?: ParityCheckResolvers<ContextType>;
|
||||
|
||||
@@ -11,11 +11,12 @@ input NotificationFilter {
|
||||
}
|
||||
|
||||
type Query {
|
||||
notifications(filter: NotificationFilter!): [Notification!]!
|
||||
notifications: Notifications!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
notificationAdded: Notification!
|
||||
notificationsOverview: NotificationOverview!
|
||||
}
|
||||
|
||||
enum Importance {
|
||||
@@ -24,7 +25,13 @@ enum Importance {
|
||||
WARNING
|
||||
}
|
||||
|
||||
type Notification {
|
||||
type Notifications implements Node {
|
||||
id: ID!
|
||||
overview: NotificationOverview!
|
||||
data(filter: NotificationFilter!): [Notification!]!
|
||||
}
|
||||
|
||||
type Notification implements Node {
|
||||
id: ID!
|
||||
title: String!
|
||||
subject: String!
|
||||
@@ -37,9 +44,13 @@ type Notification {
|
||||
}
|
||||
|
||||
type NotificationOverview {
|
||||
unread: Int!
|
||||
archived: Int!
|
||||
critical: Int!
|
||||
unread: NotificationCounts!
|
||||
archive: NotificationCounts!
|
||||
}
|
||||
|
||||
type NotificationCounts {
|
||||
info: Int!
|
||||
warning: Int!
|
||||
alert: Int!
|
||||
total: Int!
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import { ConnectResolver } from './connect/connect.resolver';
|
||||
import { ConnectService } from './connect/connect.service';
|
||||
import { idPrefixPlugin } from '@app/unraid-api/graph/id-prefix-plugin';
|
||||
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ResolversModule,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import {type NotificationFilter} from '@app/graphql/generated/api/types';
|
||||
import {Args, Query, Resolver, Subscription} from '@nestjs/graphql';
|
||||
import {Args, Query, ResolveField, Resolver, Subscription} from '@nestjs/graphql';
|
||||
import {UseRoles} from 'nest-access-control';
|
||||
import {createSubscription, PUBSUB_CHANNEL} from '@app/core/pubsub';
|
||||
import {NotificationsService} from './notifications.service';
|
||||
import { getServerIdentifier } from '@app/core/utils/server-identifier';
|
||||
|
||||
@Resolver()
|
||||
@Resolver('Notifications')
|
||||
export class NotificationsResolver {
|
||||
constructor(readonly notificationsService: NotificationsService) {}
|
||||
|
||||
@@ -14,20 +15,43 @@ export class NotificationsResolver {
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
})
|
||||
public async notifications(
|
||||
public async notifications() {
|
||||
return {
|
||||
id: getServerIdentifier('notifications'),
|
||||
}
|
||||
}
|
||||
|
||||
@ResolveField()
|
||||
public async overview() {
|
||||
return await this.notificationsService.getOverview();
|
||||
}
|
||||
|
||||
|
||||
@ResolveField()
|
||||
public async data(
|
||||
@Args('filter')
|
||||
filters: NotificationFilter
|
||||
) {
|
||||
return await this.notificationsService.getNotifications(filters);
|
||||
}
|
||||
|
||||
@Subscription('notificationAdded')
|
||||
@Subscription()
|
||||
@UseRoles({
|
||||
resource: 'notifications',
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
})
|
||||
async notificationAdded() {
|
||||
return createSubscription(PUBSUB_CHANNEL.NOTIFICATION);
|
||||
return createSubscription(PUBSUB_CHANNEL.NOTIFICATION_ADDED);
|
||||
}
|
||||
|
||||
@Subscription()
|
||||
@UseRoles({
|
||||
resource: 'notifications',
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
})
|
||||
async notificationsOverview() {
|
||||
return createSubscription(PUBSUB_CHANNEL.NOTIFICATION_OVERVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,89 @@
|
||||
import { NotificationIni } from '@app/core/types/states/notification';
|
||||
import { parseConfig } from '@app/core/utils/misc/parse-config';
|
||||
import { NotificationSchema } from '@app/graphql/generated/api/operations';
|
||||
import {Importance, NotificationType, type Notification, NotificationFilter,} from '@app/graphql/generated/api/types';
|
||||
import { Importance, NotificationType, type Notification, NotificationFilter, NotificationOverview, } from '@app/graphql/generated/api/types';
|
||||
import { getters } from '@app/store';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { readdir } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { isFulfilled, isRejected } from '@app/utils';
|
||||
import { FSWatcher, watch } from 'chokidar';
|
||||
import { FileLoadStatus } from '@app/store/types';
|
||||
import { pubsub, PUBSUB_CHANNEL } from '@app/core/pubsub';
|
||||
|
||||
@Injectable()
|
||||
export class NotificationsService {
|
||||
private logger = new Logger(NotificationsService.name);
|
||||
private static watcher: FSWatcher | null = null;
|
||||
|
||||
private static overview: NotificationOverview = {
|
||||
unread: {
|
||||
alert: 0,
|
||||
info: 0,
|
||||
warning: 0,
|
||||
total: 0,
|
||||
},
|
||||
archive: {
|
||||
alert: 0,
|
||||
info: 0,
|
||||
warning: 0,
|
||||
total: 0,
|
||||
},
|
||||
};
|
||||
|
||||
constructor() {
|
||||
NotificationsService.watcher = this.getNotificationsWatcher();
|
||||
}
|
||||
|
||||
private getNotificationsWatcher() {
|
||||
const { notify, status } = getters.dynamix();
|
||||
if (status === FileLoadStatus.LOADED && notify?.path) {
|
||||
if (NotificationsService.watcher) {
|
||||
return NotificationsService.watcher;
|
||||
}
|
||||
|
||||
NotificationsService.watcher = watch(notify.path, {})
|
||||
.on('add', (path) => {
|
||||
void this.handleNotificationAdd(path).catch((e) => this.logger.error(e));
|
||||
})
|
||||
// Do we even want to listen to removals?
|
||||
.on('unlink', (path) => {
|
||||
void this.handleNotificationRemoval(path).catch((e) => this.logger.error(e));
|
||||
});
|
||||
|
||||
return NotificationsService.watcher;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async handleNotificationAdd(path: string) {
|
||||
// The path looks like /{notification base path}/{type}/{notification id}
|
||||
const type = path.includes('/unread/') ? NotificationType.UNREAD : NotificationType.ARCHIVE;
|
||||
this.logger.debug(`Adding ${type} Notification: ${path}`);
|
||||
const notification = await this.loadNotificationFile(path, NotificationType[type]);
|
||||
|
||||
NotificationsService.overview[type.toLowerCase()][notification.importance.toLowerCase()] += 1;
|
||||
NotificationsService.overview[type.toLowerCase()]['total'] += 1;
|
||||
|
||||
pubsub.publish(PUBSUB_CHANNEL.NOTIFICATION_ADDED, {
|
||||
notificationAdded: notification
|
||||
});
|
||||
|
||||
pubsub.publish(PUBSUB_CHANNEL.NOTIFICATION_OVERVIEW, {
|
||||
notificationsOverview: NotificationsService.overview
|
||||
});
|
||||
}
|
||||
|
||||
private async handleNotificationRemoval(path: string) {
|
||||
pubsub.publish(PUBSUB_CHANNEL.NOTIFICATION_OVERVIEW, {
|
||||
notificationsOverview: NotificationsService.overview
|
||||
});
|
||||
}
|
||||
|
||||
public async getOverview(): Promise<NotificationOverview> {
|
||||
return NotificationsService.overview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all notifications from the file system.
|
||||
@@ -54,23 +126,23 @@ export class NotificationsService {
|
||||
const results = await Promise.allSettled(fileReads);
|
||||
|
||||
return [
|
||||
results.filter(isFulfilled).map(result => result.value).filter((notification) => {
|
||||
if (importance && importance !== notification.importance) {
|
||||
return false;
|
||||
}
|
||||
results.filter(isFulfilled).map(result => result.value).filter((notification) => {
|
||||
if (importance && importance !== notification.importance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type && type !== notification.type) {
|
||||
return false;
|
||||
}
|
||||
if (type && type !== notification.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.timestamp ?? 0).getTime() -
|
||||
new Date(a.timestamp ?? 0).getTime()
|
||||
),
|
||||
results.filter(isRejected).map((result) => result.reason),
|
||||
return true;
|
||||
})
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.timestamp ?? 0).getTime() -
|
||||
new Date(a.timestamp ?? 0).getTime()
|
||||
),
|
||||
results.filter(isRejected).map((result) => result.reason),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user