@servicetitan/notifications 32.1.0 → 32.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.store.d.ts","sourceRoot":"","sources":["../../src/stores/notifications.store.ts"],"names":[],"mappings":"AAIA,OAAO,EAEH,gBAAgB,EAGnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,0BAA0B,EAAE,YAAY,EAAiC,MAAM,WAAW,CAAC;AACpG,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAA+B,MAAM,0BAA0B,CAAC;AAiB7F,KAAK,gBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAI/C,qBACa,kBAAkB;;IAce,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAGxE,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IAhB1C,MAAM,CAAC,QAAQ,CAAC,YAAY,uCAA8C;IAE1E,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAE7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IACvE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyC;gBAKlB,gBAAgB,CAAC,EAAE,gBAAgB,YAAA,EAGzE,oBAAoB,CAAC,EAAE,oBAAoB,YAAA;IAMhE,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,uBAAuB;IAQ3D,OAAO,KAAK,SAAS,GAEpB;IAED,IAAI,QAAQ,IAAI,gBAAgB,GAAG,SAAS,CAE3C;IAED,IAAI,QAAQ,CAAC,EAAE,EAAE,gBAAgB,EAEhC;IAED,UAAU,GAAU,QAAQ,MAAM,mBAoBhC;IAEF,OAAO,aAQL;IAGF,GAAG,GAAI,SAAS,0BAA0B,EAAE,oBAAoB,OAAO,UA2BrE;IAGF,IAAI,GAAI,cAAc,YAAY,UAqBhC;IAGF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAU/B;IAEF,OAAO,CAAC,0BAA0B;IAQlC,OAAO,CAAC,aAAa;IAuCrB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,QAAQ;IAchB,OAAO,CAAC,UAAU;CASrB"}
1
+ {"version":3,"file":"notifications.store.d.ts","sourceRoot":"","sources":["../../src/stores/notifications.store.ts"],"names":[],"mappings":"AAIA,OAAO,EAEH,gBAAgB,EAGnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,0BAA0B,EAAE,YAAY,EAAiC,MAAM,WAAW,CAAC;AACpG,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAA+B,MAAM,0BAA0B,CAAC;AAiB7F,KAAK,gBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAI/C,qBACa,kBAAkB;;IAce,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAGxE,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IAhB1C,MAAM,CAAC,QAAQ,CAAC,YAAY,uCAA8C;IAE1E,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAE7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IACvE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyC;gBAKlB,gBAAgB,CAAC,EAAE,gBAAgB,YAAA,EAGzE,oBAAoB,CAAC,EAAE,oBAAoB,YAAA;IAMhE,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,uBAAuB;IAQ3D,OAAO,KAAK,SAAS,GAEpB;IAED,IAAI,QAAQ,IAAI,gBAAgB,GAAG,SAAS,CAE3C;IAED,IAAI,QAAQ,CAAC,EAAE,EAAE,gBAAgB,EAEhC;IAED,UAAU,GAAU,QAAQ,MAAM,mBAoBhC;IAEF,OAAO,aAQL;IAGF,GAAG,GAAI,SAAS,0BAA0B,EAAE,oBAAoB,OAAO,UA2BrE;IAGF,IAAI,GAAI,cAAc,YAAY,UAsBhC;IAGF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAU/B;IAEF,OAAO,CAAC,0BAA0B;IAQlC,OAAO,CAAC,aAAa;IAuCrB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,QAAQ;IAchB,OAAO,CAAC,UAAU;CASrB"}
@@ -310,19 +310,23 @@ export class NotificationsStore {
310
310
  this.index -= 1;
311
311
  };
312
312
  this.read = (notification)=>{
313
- if (notification.id > 0) {
313
+ const id = notification.id;
314
+ if (!this.notifications.get(id)) {
315
+ return;
316
+ }
317
+ if (id > 0) {
314
318
  this.clearTimer(notification);
315
319
  if (notification.status === ServerNotificationStatus.InProgress) {
316
- this.closedNotifications.set(notification.id, {
320
+ this.closedNotifications.set(id, {
317
321
  status: notification.status,
318
322
  timestamp: Date.now()
319
323
  });
320
324
  }
321
325
  if (this.notificationsApi) {
322
- this.notificationsApi.changeIsReadProperty(notification.id, true, notification.version);
326
+ this.notificationsApi.changeIsReadProperty(id, true, notification.version);
323
327
  }
324
328
  }
325
- this.notifications.delete(notification.id);
329
+ this.notifications.delete(id);
326
330
  };
327
331
  this.onPublisherEvent = (notification)=>{
328
332
  if (notification.userId !== this.userId) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/stores/notifications.store.ts"],"sourcesContent":["import { toast, toastProps } from '@servicetitan/anvil2';\nimport { injectable, inject, optional } from '@servicetitan/react-ioc';\nimport { action, runInAction, comparer, makeObservable } from 'mobx';\n\nimport {\n DefaultServerNotificationPayload,\n NotificationsApi,\n Notification as ServerNotification,\n NotificationProcessStatus as ServerNotificationStatus,\n} from '../api/notifications.api';\nimport { DefaultNotificationOptions, Notification, Status, isLinkAction, isNewer } from '../common';\nimport { NotificationInterceptor } from '../intercept';\nimport { NotificationsChannel, NOTIFICATIONS_CHANNEL_TOKEN } from '../notifications-channel';\nimport { dateFromString } from '../utils/date-from-string';\n\nconst EVENT_NAME = 'NotificationEvent';\nconst CLIENT_NOTIFICATION_TYPE = 'ClientNotification';\nconst CLIENT_NOTIFICATION_VERSION = 1;\nconst DAY_INTERVAL = 24 * 60 * 60 * 1000;\n\ninterface ClosedNotification {\n status: ServerNotificationStatus;\n timestamp: number;\n}\n\ninterface ClientNotification extends Notification<DefaultNotificationOptions> {\n toastId?: string;\n}\n\ntype NavigateCallback = (path: string) => void;\n\ntype ToastMethod = 'info' | 'success' | 'warning' | 'danger';\n\n@injectable()\nexport class NotificationsStore {\n static readonly interceptors = new Map<string, NotificationInterceptor>();\n\n private index = -1;\n private userId = 0;\n private intervalId?: number;\n private readonly timers: Map<number, number>;\n\n private readonly notifications = new Map<number, ClientNotification>();\n private readonly closedNotifications = new Map<number, ClosedNotification>();\n\n #navigate?: NavigateCallback;\n\n constructor(\n @inject(NotificationsApi) @optional() private readonly notificationsApi?: NotificationsApi,\n @inject(NOTIFICATIONS_CHANNEL_TOKEN)\n @optional()\n private readonly notificationsChannel?: NotificationsChannel\n ) {\n makeObservable(this);\n this.timers = new Map();\n }\n\n static intercept(type: string, fn?: NotificationInterceptor) {\n if (fn) {\n NotificationsStore.interceptors.set(type, fn);\n } else {\n NotificationsStore.interceptors.delete(type);\n }\n }\n\n private get publisher() {\n return this.notificationsChannel?.();\n }\n\n get navigate(): NavigateCallback | undefined {\n return this.#navigate;\n }\n\n set navigate(fn: NavigateCallback) {\n this.#navigate = fn;\n }\n\n initialize = async (userId: number) => {\n this.userId = userId;\n\n if (this.publisher) {\n this.publisher.bind(EVENT_NAME, this.onPublisherEvent);\n }\n\n this.intervalId = window.setInterval(() => this.cleanupClosedNotifications(), DAY_INTERVAL);\n\n if (!this.notificationsApi) {\n return;\n }\n\n const notifications = (await this.notificationsApi.getNotifications(false)).data;\n\n runInAction(() => {\n for (const notification of notifications) {\n this.updateIfNewer(notification);\n }\n });\n };\n\n dispose = () => {\n if (this.publisher) {\n this.publisher.global_emitter.unbind(EVENT_NAME, this.onPublisherEvent);\n }\n\n window.clearInterval(this.intervalId);\n Object.values(this.timers).forEach(timer => window.clearTimeout(timer));\n this.timers.clear();\n };\n\n @action\n add = (options: DefaultNotificationOptions, preventDuplicates?: boolean) => {\n const notification: ClientNotification = {\n id: this.index,\n userId: this.userId,\n type: CLIENT_NOTIFICATION_TYPE,\n status: ServerNotificationStatus.Info,\n isRead: false,\n createdOn: new Date(),\n modifiedOn: new Date(),\n version: CLIENT_NOTIFICATION_VERSION,\n payload: { ...options },\n };\n\n if (preventDuplicates) {\n if (\n [...this.notifications.values()].some(({ payload }) =>\n comparer.structural(payload, notification.payload)\n )\n ) {\n return;\n }\n this.notifications.set(notification.id, notification);\n }\n\n const method = this.getToastMethod(notification);\n notification.toastId = toast[method](this.getToastProps(notification));\n this.index -= 1;\n };\n\n @action\n read = (notification: Notification) => {\n if (notification.id > 0) {\n this.clearTimer(notification);\n\n if (notification.status === ServerNotificationStatus.InProgress) {\n this.closedNotifications.set(notification.id, {\n status: notification.status,\n timestamp: Date.now(),\n });\n }\n\n if (this.notificationsApi) {\n this.notificationsApi.changeIsReadProperty(\n notification.id,\n true,\n notification.version\n );\n }\n }\n\n this.notifications.delete(notification.id);\n };\n\n @action\n private readonly onPublisherEvent = (notification: ServerNotification) => {\n if (notification.userId !== this.userId) {\n return;\n }\n\n this.updateIfNewer({\n ...notification,\n createdOn: dateFromString(notification.createdOn),\n modifiedOn: dateFromString(notification.modifiedOn),\n });\n };\n\n private cleanupClosedNotifications() {\n for (const [id, { timestamp }] of Array.from(this.closedNotifications)) {\n if (Date.now() - timestamp > DAY_INTERVAL) {\n this.closedNotifications.delete(id);\n }\n }\n }\n\n private updateIfNewer(notification: ServerNotification) {\n const closed = this.closedNotifications.get(notification.id);\n if (closed) {\n if (closed.status === notification.status) {\n return;\n }\n\n this.closedNotifications.delete(notification.id);\n }\n\n const clientNotification = this.toClientNotification(notification);\n if (!clientNotification) {\n return;\n }\n\n const existing = this.notifications.get(notification.id);\n if (existing) {\n if (!isNewer(existing, notification)) {\n return;\n }\n if (\n notification.status === existing.status ||\n notification.status === ServerNotificationStatus.InProgress\n ) {\n toast.update(existing.toastId!, this.getToastProps(clientNotification));\n clientNotification.toastId = existing.toastId;\n this.notifications.set(notification.id, clientNotification);\n this.setTimer(clientNotification);\n return;\n }\n toast.dismiss(existing.toastId);\n }\n\n const method = this.getToastMethod(clientNotification);\n clientNotification.toastId = toast[method](this.getToastProps(clientNotification));\n this.notifications.set(notification.id, clientNotification);\n this.setTimer(clientNotification);\n }\n\n private getToastMethod({ payload: { status } }: ClientNotification): ToastMethod {\n switch (status) {\n case Status.Success:\n return 'success';\n case Status.Error:\n return 'danger';\n case Status.Warning:\n return 'warning';\n default:\n return 'info';\n }\n }\n\n private getToastProps(notification: ClientNotification): toastProps {\n const { action, duration = 8000, message = '', progress, title } = notification.payload;\n return {\n actions: this.getToastActions(notification),\n duration: action ? false : duration,\n message,\n onClose: () => this.handleClose(notification),\n onDismiss: () => this.handleClose(notification),\n progress,\n title,\n };\n }\n\n private getToastActions(notification: ClientNotification): toastProps['actions'] | undefined {\n const { action } = notification.payload;\n if (!action) {\n return;\n }\n\n return {\n primary: {\n label: action.label,\n onClick: () => this.handleClick(notification),\n },\n };\n }\n\n private handleClick({ payload: { action }, toastId }: ClientNotification) {\n if (action) {\n if (isLinkAction(action)) {\n if (action.external) {\n window.open(action.link);\n } else {\n this.navigate?.(action.link);\n }\n } else {\n action.onClick();\n }\n }\n\n toast.dismiss(toastId);\n }\n\n private handleClose(notification: Notification) {\n this.read(notification);\n }\n\n private toClientNotification(\n serverNotification: ServerNotification\n ): ClientNotification | undefined {\n const result: Notification<DefaultServerNotificationPayload> = {\n ...serverNotification,\n payload: {\n ...JSON.parse(serverNotification.payload ?? '{}'),\n status: this.toClientStatus(serverNotification.status),\n },\n };\n\n const interceptor = NotificationsStore.interceptors.get(serverNotification.type);\n if (!interceptor) {\n return result;\n }\n\n const dismiss = () => {\n const toastId = this.notifications.get(result.id)?.toastId;\n if (toastId) {\n toast.dismiss(toastId);\n }\n };\n\n return interceptor(result, dismiss);\n }\n\n private toClientStatus(status: ServerNotificationStatus) {\n switch (status) {\n case ServerNotificationStatus.Success:\n return Status.Success;\n case ServerNotificationStatus.Failure:\n return Status.Error;\n default:\n return Status.Info;\n }\n }\n\n private setTimer(notification: ClientNotification) {\n this.clearTimer(notification);\n\n const { duration = 0 } = notification.payload;\n if (duration <= 0) {\n return;\n }\n\n this.timers.set(\n notification.id,\n window.setTimeout(() => this.read(notification), duration)\n );\n }\n\n private clearTimer({ id }: Notification) {\n const timer = this.timers.get(id);\n if (!timer) {\n return;\n }\n\n this.timers.delete(id);\n window.clearTimeout(timer);\n }\n}\n"],"names":["toast","injectable","inject","optional","action","runInAction","comparer","makeObservable","NotificationsApi","NotificationProcessStatus","ServerNotificationStatus","Status","isLinkAction","isNewer","NotificationsChannel","NOTIFICATIONS_CHANNEL_TOKEN","dateFromString","EVENT_NAME","CLIENT_NOTIFICATION_TYPE","CLIENT_NOTIFICATION_VERSION","DAY_INTERVAL","NotificationsStore","intercept","type","fn","interceptors","set","delete","publisher","notificationsChannel","navigate","cleanupClosedNotifications","id","timestamp","Array","from","closedNotifications","Date","now","updateIfNewer","notification","closed","get","status","clientNotification","toClientNotification","existing","notifications","InProgress","update","toastId","getToastProps","setTimer","dismiss","method","getToastMethod","payload","Success","Error","Warning","duration","message","progress","title","actions","getToastActions","onClose","handleClose","onDismiss","primary","label","onClick","handleClick","external","window","open","link","read","serverNotification","result","JSON","parse","toClientStatus","interceptor","Failure","Info","clearTimer","timers","setTimeout","timer","clearTimeout","constructor","notificationsApi","index","userId","intervalId","initialize","dispose","add","onPublisherEvent","Map","bind","setInterval","getNotifications","data","global_emitter","unbind","clearInterval","Object","values","forEach","clear","options","preventDuplicates","isRead","createdOn","modifiedOn","version","some","structural","changeIsReadProperty"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/stores/notifications.store.ts"],"sourcesContent":["import { toast, toastProps } from '@servicetitan/anvil2';\nimport { injectable, inject, optional } from '@servicetitan/react-ioc';\nimport { action, runInAction, comparer, makeObservable } from 'mobx';\n\nimport {\n DefaultServerNotificationPayload,\n NotificationsApi,\n Notification as ServerNotification,\n NotificationProcessStatus as ServerNotificationStatus,\n} from '../api/notifications.api';\nimport { DefaultNotificationOptions, Notification, Status, isLinkAction, isNewer } from '../common';\nimport { NotificationInterceptor } from '../intercept';\nimport { NotificationsChannel, NOTIFICATIONS_CHANNEL_TOKEN } from '../notifications-channel';\nimport { dateFromString } from '../utils/date-from-string';\n\nconst EVENT_NAME = 'NotificationEvent';\nconst CLIENT_NOTIFICATION_TYPE = 'ClientNotification';\nconst CLIENT_NOTIFICATION_VERSION = 1;\nconst DAY_INTERVAL = 24 * 60 * 60 * 1000;\n\ninterface ClosedNotification {\n status: ServerNotificationStatus;\n timestamp: number;\n}\n\ninterface ClientNotification extends Notification<DefaultNotificationOptions> {\n toastId?: string;\n}\n\ntype NavigateCallback = (path: string) => void;\n\ntype ToastMethod = 'info' | 'success' | 'warning' | 'danger';\n\n@injectable()\nexport class NotificationsStore {\n static readonly interceptors = new Map<string, NotificationInterceptor>();\n\n private index = -1;\n private userId = 0;\n private intervalId?: number;\n private readonly timers: Map<number, number>;\n\n private readonly notifications = new Map<number, ClientNotification>();\n private readonly closedNotifications = new Map<number, ClosedNotification>();\n\n #navigate?: NavigateCallback;\n\n constructor(\n @inject(NotificationsApi) @optional() private readonly notificationsApi?: NotificationsApi,\n @inject(NOTIFICATIONS_CHANNEL_TOKEN)\n @optional()\n private readonly notificationsChannel?: NotificationsChannel\n ) {\n makeObservable(this);\n this.timers = new Map();\n }\n\n static intercept(type: string, fn?: NotificationInterceptor) {\n if (fn) {\n NotificationsStore.interceptors.set(type, fn);\n } else {\n NotificationsStore.interceptors.delete(type);\n }\n }\n\n private get publisher() {\n return this.notificationsChannel?.();\n }\n\n get navigate(): NavigateCallback | undefined {\n return this.#navigate;\n }\n\n set navigate(fn: NavigateCallback) {\n this.#navigate = fn;\n }\n\n initialize = async (userId: number) => {\n this.userId = userId;\n\n if (this.publisher) {\n this.publisher.bind(EVENT_NAME, this.onPublisherEvent);\n }\n\n this.intervalId = window.setInterval(() => this.cleanupClosedNotifications(), DAY_INTERVAL);\n\n if (!this.notificationsApi) {\n return;\n }\n\n const notifications = (await this.notificationsApi.getNotifications(false)).data;\n\n runInAction(() => {\n for (const notification of notifications) {\n this.updateIfNewer(notification);\n }\n });\n };\n\n dispose = () => {\n if (this.publisher) {\n this.publisher.global_emitter.unbind(EVENT_NAME, this.onPublisherEvent);\n }\n\n window.clearInterval(this.intervalId);\n Object.values(this.timers).forEach(timer => window.clearTimeout(timer));\n this.timers.clear();\n };\n\n @action\n add = (options: DefaultNotificationOptions, preventDuplicates?: boolean) => {\n const notification: ClientNotification = {\n id: this.index,\n userId: this.userId,\n type: CLIENT_NOTIFICATION_TYPE,\n status: ServerNotificationStatus.Info,\n isRead: false,\n createdOn: new Date(),\n modifiedOn: new Date(),\n version: CLIENT_NOTIFICATION_VERSION,\n payload: { ...options },\n };\n\n if (preventDuplicates) {\n if (\n [...this.notifications.values()].some(({ payload }) =>\n comparer.structural(payload, notification.payload)\n )\n ) {\n return;\n }\n this.notifications.set(notification.id, notification);\n }\n\n const method = this.getToastMethod(notification);\n notification.toastId = toast[method](this.getToastProps(notification));\n this.index -= 1;\n };\n\n @action\n read = (notification: Notification) => {\n const id = notification.id;\n if (!this.notifications.get(id)) {\n return;\n }\n\n if (id > 0) {\n this.clearTimer(notification);\n\n if (notification.status === ServerNotificationStatus.InProgress) {\n this.closedNotifications.set(id, {\n status: notification.status,\n timestamp: Date.now(),\n });\n }\n\n if (this.notificationsApi) {\n this.notificationsApi.changeIsReadProperty(id, true, notification.version);\n }\n }\n\n this.notifications.delete(id);\n };\n\n @action\n private readonly onPublisherEvent = (notification: ServerNotification) => {\n if (notification.userId !== this.userId) {\n return;\n }\n\n this.updateIfNewer({\n ...notification,\n createdOn: dateFromString(notification.createdOn),\n modifiedOn: dateFromString(notification.modifiedOn),\n });\n };\n\n private cleanupClosedNotifications() {\n for (const [id, { timestamp }] of Array.from(this.closedNotifications)) {\n if (Date.now() - timestamp > DAY_INTERVAL) {\n this.closedNotifications.delete(id);\n }\n }\n }\n\n private updateIfNewer(notification: ServerNotification) {\n const closed = this.closedNotifications.get(notification.id);\n if (closed) {\n if (closed.status === notification.status) {\n return;\n }\n\n this.closedNotifications.delete(notification.id);\n }\n\n const clientNotification = this.toClientNotification(notification);\n if (!clientNotification) {\n return;\n }\n\n const existing = this.notifications.get(notification.id);\n if (existing) {\n if (!isNewer(existing, notification)) {\n return;\n }\n if (\n notification.status === existing.status ||\n notification.status === ServerNotificationStatus.InProgress\n ) {\n toast.update(existing.toastId!, this.getToastProps(clientNotification));\n clientNotification.toastId = existing.toastId;\n this.notifications.set(notification.id, clientNotification);\n this.setTimer(clientNotification);\n return;\n }\n toast.dismiss(existing.toastId);\n }\n\n const method = this.getToastMethod(clientNotification);\n clientNotification.toastId = toast[method](this.getToastProps(clientNotification));\n this.notifications.set(notification.id, clientNotification);\n this.setTimer(clientNotification);\n }\n\n private getToastMethod({ payload: { status } }: ClientNotification): ToastMethod {\n switch (status) {\n case Status.Success:\n return 'success';\n case Status.Error:\n return 'danger';\n case Status.Warning:\n return 'warning';\n default:\n return 'info';\n }\n }\n\n private getToastProps(notification: ClientNotification): toastProps {\n const { action, duration = 8000, message = '', progress, title } = notification.payload;\n return {\n actions: this.getToastActions(notification),\n duration: action ? false : duration,\n message,\n onClose: () => this.handleClose(notification),\n onDismiss: () => this.handleClose(notification),\n progress,\n title,\n };\n }\n\n private getToastActions(notification: ClientNotification): toastProps['actions'] | undefined {\n const { action } = notification.payload;\n if (!action) {\n return;\n }\n\n return {\n primary: {\n label: action.label,\n onClick: () => this.handleClick(notification),\n },\n };\n }\n\n private handleClick({ payload: { action }, toastId }: ClientNotification) {\n if (action) {\n if (isLinkAction(action)) {\n if (action.external) {\n window.open(action.link);\n } else {\n this.navigate?.(action.link);\n }\n } else {\n action.onClick();\n }\n }\n\n toast.dismiss(toastId);\n }\n\n private handleClose(notification: Notification) {\n this.read(notification);\n }\n\n private toClientNotification(\n serverNotification: ServerNotification\n ): ClientNotification | undefined {\n const result: Notification<DefaultServerNotificationPayload> = {\n ...serverNotification,\n payload: {\n ...JSON.parse(serverNotification.payload ?? '{}'),\n status: this.toClientStatus(serverNotification.status),\n },\n };\n\n const interceptor = NotificationsStore.interceptors.get(serverNotification.type);\n if (!interceptor) {\n return result;\n }\n\n const dismiss = () => {\n const toastId = this.notifications.get(result.id)?.toastId;\n if (toastId) {\n toast.dismiss(toastId);\n }\n };\n\n return interceptor(result, dismiss);\n }\n\n private toClientStatus(status: ServerNotificationStatus) {\n switch (status) {\n case ServerNotificationStatus.Success:\n return Status.Success;\n case ServerNotificationStatus.Failure:\n return Status.Error;\n default:\n return Status.Info;\n }\n }\n\n private setTimer(notification: ClientNotification) {\n this.clearTimer(notification);\n\n const { duration = 0 } = notification.payload;\n if (duration <= 0) {\n return;\n }\n\n this.timers.set(\n notification.id,\n window.setTimeout(() => this.read(notification), duration)\n );\n }\n\n private clearTimer({ id }: Notification) {\n const timer = this.timers.get(id);\n if (!timer) {\n return;\n }\n\n this.timers.delete(id);\n window.clearTimeout(timer);\n }\n}\n"],"names":["toast","injectable","inject","optional","action","runInAction","comparer","makeObservable","NotificationsApi","NotificationProcessStatus","ServerNotificationStatus","Status","isLinkAction","isNewer","NotificationsChannel","NOTIFICATIONS_CHANNEL_TOKEN","dateFromString","EVENT_NAME","CLIENT_NOTIFICATION_TYPE","CLIENT_NOTIFICATION_VERSION","DAY_INTERVAL","NotificationsStore","intercept","type","fn","interceptors","set","delete","publisher","notificationsChannel","navigate","cleanupClosedNotifications","id","timestamp","Array","from","closedNotifications","Date","now","updateIfNewer","notification","closed","get","status","clientNotification","toClientNotification","existing","notifications","InProgress","update","toastId","getToastProps","setTimer","dismiss","method","getToastMethod","payload","Success","Error","Warning","duration","message","progress","title","actions","getToastActions","onClose","handleClose","onDismiss","primary","label","onClick","handleClick","external","window","open","link","read","serverNotification","result","JSON","parse","toClientStatus","interceptor","Failure","Info","clearTimer","timers","setTimeout","timer","clearTimeout","constructor","notificationsApi","index","userId","intervalId","initialize","dispose","add","onPublisherEvent","Map","bind","setInterval","getNotifications","data","global_emitter","unbind","clearInterval","Object","values","forEach","clear","options","preventDuplicates","isRead","createdOn","modifiedOn","version","some","structural","changeIsReadProperty"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/notifications",
3
- "version": "32.1.0",
3
+ "version": "32.2.0",
4
4
  "description": "",
5
5
  "homepage": "https://docs.st.dev/docs/frontend/notifications-center",
6
6
  "repository": {
@@ -56,5 +56,5 @@
56
56
  "cli": {
57
57
  "webpack": false
58
58
  },
59
- "gitHead": "2ac8e908d5461818254ed23184aff7fc7d384e83"
59
+ "gitHead": "83dda961540286419e84e47007a242eac9a2f0ac"
60
60
  }
@@ -290,6 +290,17 @@ describe(NotificationsStore.name, () => {
290
290
  });
291
291
  }
292
292
 
293
+ function itSendsNoNotificationWhenToastIsDismissed() {
294
+ test('sends no notification when toast is dismissed', () => {
295
+ const calls = jest.mocked(toast.info).mock.calls;
296
+ const previousCalls = calls.length;
297
+
298
+ calls[0][0].onDismiss!();
299
+
300
+ expect(api.changeIsReadProperty).toHaveBeenCalledTimes(previousCalls);
301
+ });
302
+ }
303
+
293
304
  describe('when notification is closed', () => {
294
305
  beforeEach(() => {
295
306
  subject();
@@ -297,6 +308,8 @@ describe(NotificationsStore.name, () => {
297
308
  });
298
309
 
299
310
  itNotifiesApi();
311
+
312
+ itSendsNoNotificationWhenToastIsDismissed();
300
313
  });
301
314
 
302
315
  describe('when notification is dismissed', () => {
@@ -308,16 +321,33 @@ describe(NotificationsStore.name, () => {
308
321
  itNotifiesApi();
309
322
  });
310
323
 
311
- describe('when duration expires', () => {
324
+ describe('when toast has duration', () => {
312
325
  const duration = 8000;
313
326
 
314
327
  beforeEach(() => {
315
328
  notification.payload = JSON.stringify({ ...options, duration });
316
329
  subject();
317
- jest.advanceTimersByTime(duration + 1);
318
330
  });
319
331
 
320
- itNotifiesApi();
332
+ describe('when duration expires', () => {
333
+ beforeEach(() => jest.advanceTimersByTime(duration + 1));
334
+
335
+ itNotifiesApi();
336
+
337
+ itSendsNoNotificationWhenToastIsDismissed();
338
+ });
339
+
340
+ describe('when notification is dismissed', () => {
341
+ beforeEach(() => jest.mocked(toast.info).mock.calls[0][0].onDismiss!());
342
+
343
+ itNotifiesApi();
344
+
345
+ test('sends no notification when duration expires', () => {
346
+ jest.advanceTimersByTime(duration + 1);
347
+
348
+ expect(api.changeIsReadProperty).toHaveBeenCalledTimes(1);
349
+ });
350
+ });
321
351
  });
322
352
 
323
353
  describe('when notification has interceptor', () => {
@@ -139,26 +139,27 @@ export class NotificationsStore {
139
139
 
140
140
  @action
141
141
  read = (notification: Notification) => {
142
- if (notification.id > 0) {
142
+ const id = notification.id;
143
+ if (!this.notifications.get(id)) {
144
+ return;
145
+ }
146
+
147
+ if (id > 0) {
143
148
  this.clearTimer(notification);
144
149
 
145
150
  if (notification.status === ServerNotificationStatus.InProgress) {
146
- this.closedNotifications.set(notification.id, {
151
+ this.closedNotifications.set(id, {
147
152
  status: notification.status,
148
153
  timestamp: Date.now(),
149
154
  });
150
155
  }
151
156
 
152
157
  if (this.notificationsApi) {
153
- this.notificationsApi.changeIsReadProperty(
154
- notification.id,
155
- true,
156
- notification.version
157
- );
158
+ this.notificationsApi.changeIsReadProperty(id, true, notification.version);
158
159
  }
159
160
  }
160
161
 
161
- this.notifications.delete(notification.id);
162
+ this.notifications.delete(id);
162
163
  };
163
164
 
164
165
  @action