@servicetitan/notifications 32.0.0 → 32.1.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.
Files changed (38) hide show
  1. package/dist/components/notifications.d.ts.map +1 -1
  2. package/dist/components/notifications.js.map +1 -1
  3. package/dist/demo/container.d.ts +2 -0
  4. package/dist/demo/container.d.ts.map +1 -1
  5. package/dist/demo/container.js +2 -1
  6. package/dist/demo/container.js.map +1 -1
  7. package/dist/demo/index.d.ts +1 -0
  8. package/dist/demo/index.d.ts.map +1 -1
  9. package/dist/demo/index.js +1 -0
  10. package/dist/demo/index.js.map +1 -1
  11. package/dist/demo/mock-notifications-api.d.ts +12 -0
  12. package/dist/demo/mock-notifications-api.d.ts.map +1 -0
  13. package/dist/demo/mock-notifications-api.js +55 -0
  14. package/dist/demo/mock-notifications-api.js.map +1 -0
  15. package/dist/demo/server-on-load.d.ts +3 -0
  16. package/dist/demo/server-on-load.d.ts.map +1 -0
  17. package/dist/demo/server-on-load.js +83 -0
  18. package/dist/demo/server-on-load.js.map +1 -0
  19. package/dist/index.d.ts +12 -6
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +6 -6
  22. package/dist/index.js.map +1 -1
  23. package/dist/notifications.stories.d.ts +1 -0
  24. package/dist/notifications.stories.d.ts.map +1 -1
  25. package/dist/stores/notifications.store.d.ts +3 -0
  26. package/dist/stores/notifications.store.d.ts.map +1 -1
  27. package/dist/stores/notifications.store.js +24 -0
  28. package/dist/stores/notifications.store.js.map +1 -1
  29. package/package.json +2 -2
  30. package/src/components/notifications.tsx +0 -1
  31. package/src/demo/container.tsx +9 -3
  32. package/src/demo/index.ts +1 -0
  33. package/src/demo/mock-notifications-api.ts +44 -0
  34. package/src/demo/server-on-load.tsx +88 -0
  35. package/src/index.ts +12 -11
  36. package/src/notifications.stories.tsx +2 -0
  37. package/src/stores/__tests__/notifications.store.test.ts +31 -8
  38. package/src/stores/notifications.store.ts +33 -0
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/components/notifications.tsx"],"names":[],"mappings":"AACA,OAAO,EAAY,UAAU,EAAmB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EACH,EAAE,EACF,iBAAiB,EAMpB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAK5D,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB,CAAC,EAAE,CAAC;IAC7D,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrD;AAED,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAWhD,CAAC;AAEF,OAAO,CAAC,MAAM,CAAC;IACX;;;OAGG;IAEH,IAAI,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;CACnD"}
1
+ {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/components/notifications.tsx"],"names":[],"mappings":"AACA,OAAO,EAAY,UAAU,EAAmB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EACH,EAAE,EACF,iBAAiB,EAMpB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAK5D,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB,CAAC,EAAE,CAAC;IAC7D,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrD;AAED,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAWhD,CAAC;AAEF,OAAO,CAAC,MAAM,CAAC;IACX;;;OAGG;IACH,IAAI,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;CACnD"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/notifications.tsx"],"sourcesContent":["import { Toaster } from '@servicetitan/anvil2';\nimport { Provider, interfaces, useDependencies } from '@servicetitan/react-ioc';\nimport {\n FC,\n PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\n\nimport { NotificationsApi } from '../api/notifications.api';\nimport { NotificationsStore } from '../stores/notifications.store';\nimport { useCompatibleNavigate } from '../utils/use-compatible-navigate';\nimport { NotificationsService } from '../notifications-service';\n\nexport interface NotificationsProps extends PropsWithChildren<{}> {\n apiService?: interfaces.Newable<NotificationsApi>;\n}\n\nexport const Notifications: FC<NotificationsProps> = ({ apiService, children }) => (\n <Provider\n singletons={[\n ...(apiService ? [{ provide: NotificationsApi, useClass: apiService }] : []),\n NotificationsStore,\n NotificationsService,\n ]}\n >\n <NotificationsUnwrapped />\n {children}\n </Provider>\n);\n\ndeclare global {\n /**\n * This variable ensures there is one and only one \\<Toaster />.\n * It contains the unique id of the instance that owns the Toaster.\n */\n // eslint-disable-next-line no-var\n var STNotificationsActiveId: string | undefined;\n}\n\nconst NotificationsUnwrapped: FC = () => {\n // Configure and clean up store\n\n const [store] = useDependencies(NotificationsStore);\n const navigate = useCompatibleNavigate();\n useEffect(() => {\n store.navigate = navigate;\n return () => {\n store.dispose();\n };\n }, [store, navigate]);\n\n // Use useSyncExternalStore to ensure we re-render when the active id changes\n\n const [thisId] = useState(() => makeId());\n const syncExternalStoreCallback = useRef<Function>();\n const subscribe = useCallback((callback: Function) => {\n syncExternalStoreCallback.current = callback;\n return () => (syncExternalStoreCallback.current = undefined);\n }, []);\n const activeId = useSyncExternalStore(subscribe, () => globalThis.STNotificationsActiveId);\n\n // Set and clean up active id\n\n useEffect(() => {\n if (globalThis.STNotificationsActiveId === undefined) {\n globalThis.STNotificationsActiveId = thisId;\n syncExternalStoreCallback.current?.();\n }\n return () => {\n if (globalThis.STNotificationsActiveId === thisId) {\n globalThis.STNotificationsActiveId = undefined;\n syncExternalStoreCallback.current?.();\n }\n };\n }, [syncExternalStoreCallback, thisId]);\n\n return activeId === thisId ? <Toaster id={'st-notifications-' + thisId} /> : null;\n};\n\nfunction makeId() {\n return Math.round(1e8 * Math.random()).toString(36);\n}\n"],"names":["Toaster","Provider","useDependencies","useCallback","useEffect","useRef","useState","useSyncExternalStore","NotificationsApi","NotificationsStore","useCompatibleNavigate","NotificationsService","Notifications","apiService","children","singletons","provide","useClass","NotificationsUnwrapped","store","navigate","dispose","thisId","makeId","syncExternalStoreCallback","subscribe","callback","current","undefined","activeId","globalThis","STNotificationsActiveId","id","Math","round","random","toString"],"mappings":";AAAA,SAASA,OAAO,QAAQ,uBAAuB;AAC/C,SAASC,QAAQ,EAAcC,eAAe,QAAQ,0BAA0B;AAChF,SAGIC,WAAW,EACXC,SAAS,EACTC,MAAM,EACNC,QAAQ,EACRC,oBAAoB,QACjB,QAAQ;AAEf,SAASC,gBAAgB,QAAQ,2BAA2B;AAC5D,SAASC,kBAAkB,QAAQ,gCAAgC;AACnE,SAASC,qBAAqB,QAAQ,mCAAmC;AACzE,SAASC,oBAAoB,QAAQ,2BAA2B;AAMhE,OAAO,MAAMC,gBAAwC,CAAC,EAAEC,UAAU,EAAEC,QAAQ,EAAE,iBAC1E,MAACb;QACGc,YAAY;eACJF,aAAa;gBAAC;oBAAEG,SAASR;oBAAkBS,UAAUJ;gBAAW;aAAE,GAAG,EAAE;YAC3EJ;YACAE;SACH;;0BAED,KAACO;YACAJ;;OAEP;AAWF,MAAMI,yBAA6B;IAC/B,+BAA+B;IAE/B,MAAM,CAACC,MAAM,GAAGjB,gBAAgBO;IAChC,MAAMW,WAAWV;IACjBN,UAAU;QACNe,MAAMC,QAAQ,GAAGA;QACjB,OAAO;YACHD,MAAME,OAAO;QACjB;IACJ,GAAG;QAACF;QAAOC;KAAS;IAEpB,6EAA6E;IAE7E,MAAM,CAACE,OAAO,GAAGhB,SAAS,IAAMiB;IAChC,MAAMC,4BAA4BnB;IAClC,MAAMoB,YAAYtB,YAAY,CAACuB;QAC3BF,0BAA0BG,OAAO,GAAGD;QACpC,OAAO,IAAOF,0BAA0BG,OAAO,GAAGC;IACtD,GAAG,EAAE;IACL,MAAMC,WAAWtB,qBAAqBkB,WAAW,IAAMK,WAAWC,uBAAuB;IAEzF,6BAA6B;IAE7B3B,UAAU;QACN,IAAI0B,WAAWC,uBAAuB,KAAKH,WAAW;gBAElDJ;YADAM,WAAWC,uBAAuB,GAAGT;aACrCE,qCAAAA,0BAA0BG,OAAO,cAAjCH,yDAAAA,wCAAAA;QACJ;QACA,OAAO;YACH,IAAIM,WAAWC,uBAAuB,KAAKT,QAAQ;oBAE/CE;gBADAM,WAAWC,uBAAuB,GAAGH;iBACrCJ,qCAAAA,0BAA0BG,OAAO,cAAjCH,yDAAAA,wCAAAA;YACJ;QACJ;IACJ,GAAG;QAACA;QAA2BF;KAAO;IAEtC,OAAOO,aAAaP,uBAAS,KAACtB;QAAQgC,IAAI,sBAAsBV;SAAa;AACjF;AAEA,SAASC;IACL,OAAOU,KAAKC,KAAK,CAAC,MAAMD,KAAKE,MAAM,IAAIC,QAAQ,CAAC;AACpD"}
1
+ {"version":3,"sources":["../../src/components/notifications.tsx"],"sourcesContent":["import { Toaster } from '@servicetitan/anvil2';\nimport { Provider, interfaces, useDependencies } from '@servicetitan/react-ioc';\nimport {\n FC,\n PropsWithChildren,\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\n\nimport { NotificationsApi } from '../api/notifications.api';\nimport { NotificationsStore } from '../stores/notifications.store';\nimport { useCompatibleNavigate } from '../utils/use-compatible-navigate';\nimport { NotificationsService } from '../notifications-service';\n\nexport interface NotificationsProps extends PropsWithChildren<{}> {\n apiService?: interfaces.Newable<NotificationsApi>;\n}\n\nexport const Notifications: FC<NotificationsProps> = ({ apiService, children }) => (\n <Provider\n singletons={[\n ...(apiService ? [{ provide: NotificationsApi, useClass: apiService }] : []),\n NotificationsStore,\n NotificationsService,\n ]}\n >\n <NotificationsUnwrapped />\n {children}\n </Provider>\n);\n\ndeclare global {\n /**\n * This variable ensures there is one and only one \\<Toaster />.\n * It contains the unique id of the instance that owns the Toaster.\n */\n var STNotificationsActiveId: string | undefined;\n}\n\nconst NotificationsUnwrapped: FC = () => {\n // Configure and clean up store\n\n const [store] = useDependencies(NotificationsStore);\n const navigate = useCompatibleNavigate();\n useEffect(() => {\n store.navigate = navigate;\n return () => {\n store.dispose();\n };\n }, [store, navigate]);\n\n // Use useSyncExternalStore to ensure we re-render when the active id changes\n\n const [thisId] = useState(() => makeId());\n const syncExternalStoreCallback = useRef<Function>();\n const subscribe = useCallback((callback: Function) => {\n syncExternalStoreCallback.current = callback;\n return () => (syncExternalStoreCallback.current = undefined);\n }, []);\n const activeId = useSyncExternalStore(subscribe, () => globalThis.STNotificationsActiveId);\n\n // Set and clean up active id\n\n useEffect(() => {\n if (globalThis.STNotificationsActiveId === undefined) {\n globalThis.STNotificationsActiveId = thisId;\n syncExternalStoreCallback.current?.();\n }\n return () => {\n if (globalThis.STNotificationsActiveId === thisId) {\n globalThis.STNotificationsActiveId = undefined;\n syncExternalStoreCallback.current?.();\n }\n };\n }, [syncExternalStoreCallback, thisId]);\n\n return activeId === thisId ? <Toaster id={'st-notifications-' + thisId} /> : null;\n};\n\nfunction makeId() {\n return Math.round(1e8 * Math.random()).toString(36);\n}\n"],"names":["Toaster","Provider","useDependencies","useCallback","useEffect","useRef","useState","useSyncExternalStore","NotificationsApi","NotificationsStore","useCompatibleNavigate","NotificationsService","Notifications","apiService","children","singletons","provide","useClass","NotificationsUnwrapped","store","navigate","dispose","thisId","makeId","syncExternalStoreCallback","subscribe","callback","current","undefined","activeId","globalThis","STNotificationsActiveId","id","Math","round","random","toString"],"mappings":";AAAA,SAASA,OAAO,QAAQ,uBAAuB;AAC/C,SAASC,QAAQ,EAAcC,eAAe,QAAQ,0BAA0B;AAChF,SAGIC,WAAW,EACXC,SAAS,EACTC,MAAM,EACNC,QAAQ,EACRC,oBAAoB,QACjB,QAAQ;AAEf,SAASC,gBAAgB,QAAQ,2BAA2B;AAC5D,SAASC,kBAAkB,QAAQ,gCAAgC;AACnE,SAASC,qBAAqB,QAAQ,mCAAmC;AACzE,SAASC,oBAAoB,QAAQ,2BAA2B;AAMhE,OAAO,MAAMC,gBAAwC,CAAC,EAAEC,UAAU,EAAEC,QAAQ,EAAE,iBAC1E,MAACb;QACGc,YAAY;eACJF,aAAa;gBAAC;oBAAEG,SAASR;oBAAkBS,UAAUJ;gBAAW;aAAE,GAAG,EAAE;YAC3EJ;YACAE;SACH;;0BAED,KAACO;YACAJ;;OAEP;AAUF,MAAMI,yBAA6B;IAC/B,+BAA+B;IAE/B,MAAM,CAACC,MAAM,GAAGjB,gBAAgBO;IAChC,MAAMW,WAAWV;IACjBN,UAAU;QACNe,MAAMC,QAAQ,GAAGA;QACjB,OAAO;YACHD,MAAME,OAAO;QACjB;IACJ,GAAG;QAACF;QAAOC;KAAS;IAEpB,6EAA6E;IAE7E,MAAM,CAACE,OAAO,GAAGhB,SAAS,IAAMiB;IAChC,MAAMC,4BAA4BnB;IAClC,MAAMoB,YAAYtB,YAAY,CAACuB;QAC3BF,0BAA0BG,OAAO,GAAGD;QACpC,OAAO,IAAOF,0BAA0BG,OAAO,GAAGC;IACtD,GAAG,EAAE;IACL,MAAMC,WAAWtB,qBAAqBkB,WAAW,IAAMK,WAAWC,uBAAuB;IAEzF,6BAA6B;IAE7B3B,UAAU;QACN,IAAI0B,WAAWC,uBAAuB,KAAKH,WAAW;gBAElDJ;YADAM,WAAWC,uBAAuB,GAAGT;aACrCE,qCAAAA,0BAA0BG,OAAO,cAAjCH,yDAAAA,wCAAAA;QACJ;QACA,OAAO;YACH,IAAIM,WAAWC,uBAAuB,KAAKT,QAAQ;oBAE/CE;gBADAM,WAAWC,uBAAuB,GAAGH;iBACrCJ,qCAAAA,0BAA0BG,OAAO,cAAjCH,yDAAAA,wCAAAA;YACJ;QACJ;IACJ,GAAG;QAACA;QAA2BF;KAAO;IAEtC,OAAOO,aAAaP,uBAAS,KAACtB;QAAQgC,IAAI,sBAAsBV;SAAa;AACjF;AAEA,SAASC;IACL,OAAOU,KAAKC,KAAK,CAAC,MAAMD,KAAKE,MAAM,IAAIC,QAAQ,CAAC;AACpD"}
@@ -1,5 +1,6 @@
1
1
  import { FC } from 'react';
2
2
  import { Notification } from '../api/notifications.api';
3
+ import { NotificationsProps } from '..';
3
4
  type EventCallback = (notification: Notification) => void;
4
5
  declare class Publisher {
5
6
  global_emitter: {
@@ -12,6 +13,7 @@ declare class Publisher {
12
13
  fire: (notification: Notification) => void;
13
14
  }
14
15
  interface ContainerProps {
16
+ apiService?: NotificationsProps['apiService'];
15
17
  children(publisher: Publisher): JSX.Element;
16
18
  }
17
19
  export declare const Container: FC<ContainerProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/demo/container.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsB,EAAE,EAAE,MAAM,OAAO,CAAC;AAI/C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD,KAAK,aAAa,GAAG,CAAC,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;AAE1D,cAAM,SAAS;IAEX,cAAc;;;MAGZ;IAEF,OAAO,CAAC,YAAY,CAAC,CAAgB;IAErC,IAAI,GAAI,IAAI,MAAM,EAAE,UAAU,aAAa,UAEzC;IAEF,MAAM,aAAY;IAElB,IAAI,GAAI,cAAc,YAAY,UAIhC;CACL;AAYD,UAAU,cAAc;IACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC;CAC/C;AAED,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CAkBxC,CAAC"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/demo/container.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsB,EAAE,EAAE,MAAM,OAAO,CAAC;AAI/C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAGH,kBAAkB,EAErB,MAAM,IAAI,CAAC;AAEZ,KAAK,aAAa,GAAG,CAAC,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;AAE1D,cAAM,SAAS;IAEX,cAAc;;;MAGZ;IAEF,OAAO,CAAC,YAAY,CAAC,CAAgB;IAErC,IAAI,GAAI,IAAI,MAAM,EAAE,UAAU,aAAa,UAEzC;IAEF,MAAM,aAAY;IAElB,IAAI,GAAI,cAAc,YAAY,UAIhC;CACL;AAYD,UAAU,cAAc;IACpB,UAAU,CAAC,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC;CAC/C;AAED,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CAkBxC,CAAC"}
@@ -43,7 +43,7 @@ const NotificationsInitializer = ()=>{
43
43
  ]);
44
44
  return null;
45
45
  };
46
- export const Container = ({ children })=>{
46
+ export const Container = ({ apiService, children })=>{
47
47
  const publisherInstance = useMemo(()=>new Publisher(), []);
48
48
  return /*#__PURE__*/ _jsx(Provider, {
49
49
  singletons: [
@@ -53,6 +53,7 @@ export const Container = ({ children })=>{
53
53
  }
54
54
  ],
55
55
  children: /*#__PURE__*/ _jsxs(Notifications, {
56
+ apiService: apiService,
56
57
  children: [
57
58
  /*#__PURE__*/ _jsx(NotificationsInitializer, {}),
58
59
  children(publisherInstance)
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/demo/container.tsx"],"sourcesContent":["import { useEffect, useMemo, FC } from 'react';\n\nimport { Provider, useDependencies } from '@servicetitan/react-ioc';\n\nimport { Notification } from '../api/notifications.api';\nimport { NOTIFICATIONS_CHANNEL_TOKEN, Notifications, NotificationsService } from '..';\n\ntype EventCallback = (notification: Notification) => void;\n\nclass Publisher {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n global_emitter = {\n bind: () => {},\n unbind: () => {},\n };\n\n private subscription?: EventCallback;\n\n bind = (_0: string, callback: EventCallback) => {\n this.subscription = callback;\n };\n\n unbind = () => {};\n\n fire = (notification: Notification) => {\n if (this.subscription) {\n this.subscription(notification);\n }\n };\n}\n\nconst NotificationsInitializer: FC = () => {\n const [notifications] = useDependencies(NotificationsService);\n\n useEffect(() => {\n notifications.initialize();\n }, [notifications]);\n\n return null;\n};\n\ninterface ContainerProps {\n children(publisher: Publisher): JSX.Element;\n}\n\nexport const Container: FC<ContainerProps> = ({ children }) => {\n const publisherInstance = useMemo(() => new Publisher(), []);\n\n return (\n <Provider\n singletons={[\n {\n provide: NOTIFICATIONS_CHANNEL_TOKEN,\n useValue: () => publisherInstance,\n },\n ]}\n >\n <Notifications>\n <NotificationsInitializer />\n {children(publisherInstance)}\n </Notifications>\n </Provider>\n );\n};\n"],"names":["useEffect","useMemo","Provider","useDependencies","NOTIFICATIONS_CHANNEL_TOKEN","Notifications","NotificationsService","Publisher","global_emitter","bind","unbind","subscription","_0","callback","fire","notification","NotificationsInitializer","notifications","initialize","Container","children","publisherInstance","singletons","provide","useValue"],"mappings":";;;;;;;;;;;;;;AAAA,SAASA,SAAS,EAAEC,OAAO,QAAY,QAAQ;AAE/C,SAASC,QAAQ,EAAEC,eAAe,QAAQ,0BAA0B;AAGpE,SAASC,2BAA2B,EAAEC,aAAa,EAAEC,oBAAoB,QAAQ,KAAK;AAItF,MAAMC;;QACF,gEAAgE;QAChEC,uBAAAA,kBAAiB;YACbC,MAAM,KAAO;YACbC,QAAQ,KAAO;QACnB;QAEA,uBAAQC,gBAAR,KAAA;QAEAF,uBAAAA,QAAO,CAACG,IAAYC;YAChB,IAAI,CAACF,YAAY,GAAGE;QACxB;QAEAH,uBAAAA,UAAS,KAAO;QAEhBI,uBAAAA,QAAO,CAACC;YACJ,IAAI,IAAI,CAACJ,YAAY,EAAE;gBACnB,IAAI,CAACA,YAAY,CAACI;YACtB;QACJ;;AACJ;AAEA,MAAMC,2BAA+B;IACjC,MAAM,CAACC,cAAc,GAAGd,gBAAgBG;IAExCN,UAAU;QACNiB,cAAcC,UAAU;IAC5B,GAAG;QAACD;KAAc;IAElB,OAAO;AACX;AAMA,OAAO,MAAME,YAAgC,CAAC,EAAEC,QAAQ,EAAE;IACtD,MAAMC,oBAAoBpB,QAAQ,IAAM,IAAIM,aAAa,EAAE;IAE3D,qBACI,KAACL;QACGoB,YAAY;YACR;gBACIC,SAASnB;gBACToB,UAAU,IAAMH;YACpB;SACH;kBAED,cAAA,MAAChB;;8BACG,KAACW;gBACAI,SAASC;;;;AAI1B,EAAE"}
1
+ {"version":3,"sources":["../../src/demo/container.tsx"],"sourcesContent":["import { useEffect, useMemo, FC } from 'react';\n\nimport { Provider, useDependencies } from '@servicetitan/react-ioc';\n\nimport { Notification } from '../api/notifications.api';\nimport {\n NOTIFICATIONS_CHANNEL_TOKEN,\n Notifications,\n NotificationsProps,\n NotificationsService,\n} from '..';\n\ntype EventCallback = (notification: Notification) => void;\n\nclass Publisher {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n global_emitter = {\n bind: () => {},\n unbind: () => {},\n };\n\n private subscription?: EventCallback;\n\n bind = (_0: string, callback: EventCallback) => {\n this.subscription = callback;\n };\n\n unbind = () => {};\n\n fire = (notification: Notification) => {\n if (this.subscription) {\n this.subscription(notification);\n }\n };\n}\n\nconst NotificationsInitializer: FC = () => {\n const [notifications] = useDependencies(NotificationsService);\n\n useEffect(() => {\n notifications.initialize();\n }, [notifications]);\n\n return null;\n};\n\ninterface ContainerProps {\n apiService?: NotificationsProps['apiService'];\n children(publisher: Publisher): JSX.Element;\n}\n\nexport const Container: FC<ContainerProps> = ({ apiService, children }) => {\n const publisherInstance = useMemo(() => new Publisher(), []);\n\n return (\n <Provider\n singletons={[\n {\n provide: NOTIFICATIONS_CHANNEL_TOKEN,\n useValue: () => publisherInstance,\n },\n ]}\n >\n <Notifications apiService={apiService}>\n <NotificationsInitializer />\n {children(publisherInstance)}\n </Notifications>\n </Provider>\n );\n};\n"],"names":["useEffect","useMemo","Provider","useDependencies","NOTIFICATIONS_CHANNEL_TOKEN","Notifications","NotificationsService","Publisher","global_emitter","bind","unbind","subscription","_0","callback","fire","notification","NotificationsInitializer","notifications","initialize","Container","apiService","children","publisherInstance","singletons","provide","useValue"],"mappings":";;;;;;;;;;;;;;AAAA,SAASA,SAAS,EAAEC,OAAO,QAAY,QAAQ;AAE/C,SAASC,QAAQ,EAAEC,eAAe,QAAQ,0BAA0B;AAGpE,SACIC,2BAA2B,EAC3BC,aAAa,EAEbC,oBAAoB,QACjB,KAAK;AAIZ,MAAMC;;QACF,gEAAgE;QAChEC,uBAAAA,kBAAiB;YACbC,MAAM,KAAO;YACbC,QAAQ,KAAO;QACnB;QAEA,uBAAQC,gBAAR,KAAA;QAEAF,uBAAAA,QAAO,CAACG,IAAYC;YAChB,IAAI,CAACF,YAAY,GAAGE;QACxB;QAEAH,uBAAAA,UAAS,KAAO;QAEhBI,uBAAAA,QAAO,CAACC;YACJ,IAAI,IAAI,CAACJ,YAAY,EAAE;gBACnB,IAAI,CAACA,YAAY,CAACI;YACtB;QACJ;;AACJ;AAEA,MAAMC,2BAA+B;IACjC,MAAM,CAACC,cAAc,GAAGd,gBAAgBG;IAExCN,UAAU;QACNiB,cAAcC,UAAU;IAC5B,GAAG;QAACD;KAAc;IAElB,OAAO;AACX;AAOA,OAAO,MAAME,YAAgC,CAAC,EAAEC,UAAU,EAAEC,QAAQ,EAAE;IAClE,MAAMC,oBAAoBrB,QAAQ,IAAM,IAAIM,aAAa,EAAE;IAE3D,qBACI,KAACL;QACGqB,YAAY;YACR;gBACIC,SAASpB;gBACTqB,UAAU,IAAMH;YACpB;SACH;kBAED,cAAA,MAACjB;YAAce,YAAYA;;8BACvB,KAACJ;gBACAK,SAASC;;;;AAI1B,EAAE"}
@@ -7,4 +7,5 @@ export * from './prevent-duplicates';
7
7
  export * from './multiline-message';
8
8
  export * from './server-default';
9
9
  export * from './server-custom';
10
+ export * from './server-on-load';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/demo/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/demo/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
@@ -7,5 +7,6 @@ export * from './prevent-duplicates';
7
7
  export * from './multiline-message';
8
8
  export * from './server-default';
9
9
  export * from './server-custom';
10
+ export * from './server-on-load';
10
11
 
11
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/demo/index.ts"],"sourcesContent":["export * from './basic';\nexport * from './status-variations';\nexport * from './duration';\nexport * from './action-button';\nexport * from './progress';\nexport * from './prevent-duplicates';\nexport * from './multiline-message';\nexport * from './server-default';\nexport * from './server-custom';\n"],"names":[],"mappings":"AAAA,cAAc,UAAU;AACxB,cAAc,sBAAsB;AACpC,cAAc,aAAa;AAC3B,cAAc,kBAAkB;AAChC,cAAc,aAAa;AAC3B,cAAc,uBAAuB;AACrC,cAAc,sBAAsB;AACpC,cAAc,mBAAmB;AACjC,cAAc,kBAAkB"}
1
+ {"version":3,"sources":["../../src/demo/index.ts"],"sourcesContent":["export * from './basic';\nexport * from './status-variations';\nexport * from './duration';\nexport * from './action-button';\nexport * from './progress';\nexport * from './prevent-duplicates';\nexport * from './multiline-message';\nexport * from './server-default';\nexport * from './server-custom';\nexport * from './server-on-load';\n"],"names":[],"mappings":"AAAA,cAAc,UAAU;AACxB,cAAc,sBAAsB;AACpC,cAAc,aAAa;AAC3B,cAAc,kBAAkB;AAChC,cAAc,aAAa;AAC3B,cAAc,uBAAuB;AACrC,cAAc,sBAAsB;AACpC,cAAc,mBAAmB;AACjC,cAAc,kBAAkB;AAChC,cAAc,mBAAmB"}
@@ -0,0 +1,12 @@
1
+ import { AxiosPromise, CancelToken } from 'axios';
2
+ import { Notification, NotificationsApi } from '../api/notifications.api';
3
+ export declare class MockNotificationsApi extends NotificationsApi {
4
+ private readonly localStorageKey;
5
+ add(notification: Notification): void;
6
+ getNotification(id: number, _cancelToken?: CancelToken): AxiosPromise<Notification>;
7
+ getNotifications(_read: boolean, _cancelToken?: CancelToken): AxiosPromise<Notification[]>;
8
+ changeIsReadProperty(id: number, read: boolean, _version: number, _cancelToken?: CancelToken): AxiosPromise<void>;
9
+ private get notifications();
10
+ private set notifications(value);
11
+ }
12
+ //# sourceMappingURL=mock-notifications-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-notifications-api.d.ts","sourceRoot":"","sources":["../../src/demo/mock-notifications-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE1E,qBAAa,oBAAqB,SAAQ,gBAAgB;IACtD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwC;IAExE,GAAG,CAAC,YAAY,EAAE,YAAY;IAI9B,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,WAAW,GAAG,YAAY,CAAC,YAAY,CAAC;IAInF,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,WAAW,GAAG,YAAY,CAAC,YAAY,EAAE,CAAC;IAI1F,oBAAoB,CAChB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,WAAW,GAC3B,YAAY,CAAC,IAAI,CAAC;IAOrB,OAAO,KAAK,aAAa,GAIxB;IAED,OAAO,KAAK,aAAa,QAMxB;CACJ"}
@@ -0,0 +1,55 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ import { NotificationsApi } from '../api/notifications.api';
15
+ export class MockNotificationsApi extends NotificationsApi {
16
+ add(notification) {
17
+ this.notifications = [
18
+ ...this.notifications,
19
+ notification
20
+ ];
21
+ }
22
+ getNotification(id, _cancelToken) {
23
+ return Promise.resolve({
24
+ data: this.notifications.find((el)=>el.id === id)
25
+ });
26
+ }
27
+ getNotifications(_read, _cancelToken) {
28
+ return Promise.resolve({
29
+ data: this.notifications
30
+ });
31
+ }
32
+ changeIsReadProperty(id, read, _version, _cancelToken) {
33
+ if (read) {
34
+ this.notifications = this.notifications.filter((el)=>el.id !== id);
35
+ }
36
+ return Promise.resolve({});
37
+ }
38
+ get notifications() {
39
+ const value = localStorage.getItem(this.localStorageKey);
40
+ const result = value ? JSON.parse(value) : [];
41
+ return result;
42
+ }
43
+ set notifications(notifications) {
44
+ if (notifications.length) {
45
+ localStorage.setItem(this.localStorageKey, JSON.stringify(notifications));
46
+ } else {
47
+ localStorage.removeItem(this.localStorageKey);
48
+ }
49
+ }
50
+ constructor(...args){
51
+ super(...args), _define_property(this, "localStorageKey", 'MockNotificationsApi.notifications');
52
+ }
53
+ }
54
+
55
+ //# sourceMappingURL=mock-notifications-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/demo/mock-notifications-api.ts"],"sourcesContent":["import { AxiosPromise, CancelToken } from 'axios';\nimport { Notification, NotificationsApi } from '../api/notifications.api';\n\nexport class MockNotificationsApi extends NotificationsApi {\n private readonly localStorageKey = 'MockNotificationsApi.notifications';\n\n add(notification: Notification) {\n this.notifications = [...this.notifications, notification];\n }\n\n getNotification(id: number, _cancelToken?: CancelToken): AxiosPromise<Notification> {\n return Promise.resolve({ data: this.notifications.find(el => el.id === id) } as any);\n }\n\n getNotifications(_read: boolean, _cancelToken?: CancelToken): AxiosPromise<Notification[]> {\n return Promise.resolve({ data: this.notifications } as any);\n }\n\n changeIsReadProperty(\n id: number,\n read: boolean,\n _version: number,\n _cancelToken?: CancelToken\n ): AxiosPromise<void> {\n if (read) {\n this.notifications = this.notifications.filter(el => el.id !== id);\n }\n return Promise.resolve({} as any);\n }\n\n private get notifications() {\n const value = localStorage.getItem(this.localStorageKey);\n const result: Notification[] = value ? JSON.parse(value) : [];\n return result;\n }\n\n private set notifications(notifications: Notification[]) {\n if (notifications.length) {\n localStorage.setItem(this.localStorageKey, JSON.stringify(notifications));\n } else {\n localStorage.removeItem(this.localStorageKey);\n }\n }\n}\n"],"names":["NotificationsApi","MockNotificationsApi","add","notification","notifications","getNotification","id","_cancelToken","Promise","resolve","data","find","el","getNotifications","_read","changeIsReadProperty","read","_version","filter","value","localStorage","getItem","localStorageKey","result","JSON","parse","length","setItem","stringify","removeItem"],"mappings":";;;;;;;;;;;;;AACA,SAAuBA,gBAAgB,QAAQ,2BAA2B;AAE1E,OAAO,MAAMC,6BAA6BD;IAGtCE,IAAIC,YAA0B,EAAE;QAC5B,IAAI,CAACC,aAAa,GAAG;eAAI,IAAI,CAACA,aAAa;YAAED;SAAa;IAC9D;IAEAE,gBAAgBC,EAAU,EAAEC,YAA0B,EAA8B;QAChF,OAAOC,QAAQC,OAAO,CAAC;YAAEC,MAAM,IAAI,CAACN,aAAa,CAACO,IAAI,CAACC,CAAAA,KAAMA,GAAGN,EAAE,KAAKA;QAAI;IAC/E;IAEAO,iBAAiBC,KAAc,EAAEP,YAA0B,EAAgC;QACvF,OAAOC,QAAQC,OAAO,CAAC;YAAEC,MAAM,IAAI,CAACN,aAAa;QAAC;IACtD;IAEAW,qBACIT,EAAU,EACVU,IAAa,EACbC,QAAgB,EAChBV,YAA0B,EACR;QAClB,IAAIS,MAAM;YACN,IAAI,CAACZ,aAAa,GAAG,IAAI,CAACA,aAAa,CAACc,MAAM,CAACN,CAAAA,KAAMA,GAAGN,EAAE,KAAKA;QACnE;QACA,OAAOE,QAAQC,OAAO,CAAC,CAAC;IAC5B;IAEA,IAAYL,gBAAgB;QACxB,MAAMe,QAAQC,aAAaC,OAAO,CAAC,IAAI,CAACC,eAAe;QACvD,MAAMC,SAAyBJ,QAAQK,KAAKC,KAAK,CAACN,SAAS,EAAE;QAC7D,OAAOI;IACX;IAEA,IAAYnB,cAAcA,aAA6B,EAAE;QACrD,IAAIA,cAAcsB,MAAM,EAAE;YACtBN,aAAaO,OAAO,CAAC,IAAI,CAACL,eAAe,EAAEE,KAAKI,SAAS,CAACxB;QAC9D,OAAO;YACHgB,aAAaS,UAAU,CAAC,IAAI,CAACP,eAAe;QAChD;IACJ;;QAvCG,gBACH,uBAAiBA,mBAAkB;;AAuCvC"}
@@ -0,0 +1,3 @@
1
+ import { FC } from 'react';
2
+ export declare const ServerOnLoadExample: FC;
3
+ //# sourceMappingURL=server-on-load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-on-load.d.ts","sourceRoot":"","sources":["../../src/demo/server-on-load.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAW,MAAM,OAAO,CAAC;AAuBpC,eAAO,MAAM,mBAAmB,EAAE,EA+DjC,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Flex } from '@servicetitan/anvil2';
3
+ import { useMemo } from 'react';
4
+ import { NotificationProcessStatus } from '../api/notifications.api';
5
+ import { Container } from './container';
6
+ import { MockNotificationsApi } from './mock-notifications-api';
7
+ const BASIC = "This toast disappears automatically.\n\nIt won't reappear when the page is reloaded, unless the reload happens before the toast disappears.";
8
+ const WITH_ACTION = 'This toast does not disappear automatically.\n\nUntil the user clicks the action or closes it, the toast reappears whenever the page is reloaded.';
9
+ const WITH_ACTION_AND_DURATION = "This toast does not disappear automatically.\n\nHowever, it won't reappear when the page is reloaded, unless the reload happens before the toast duration expires.";
10
+ const action = {
11
+ label: 'Click me!',
12
+ link: '/table-master-detail',
13
+ external: true
14
+ };
15
+ export const ServerOnLoadExample = ()=>{
16
+ const api = useMemo(()=>new MockNotificationsApi(), []);
17
+ const handleAdd = (payload)=>{
18
+ const date = new Date();
19
+ api.add({
20
+ id: date.getTime(),
21
+ userId: 0,
22
+ type: 'PendingServerNotification',
23
+ status: NotificationProcessStatus.Info,
24
+ isRead: false,
25
+ version: 1,
26
+ createdOn: date,
27
+ modifiedOn: date,
28
+ payload: JSON.stringify(payload)
29
+ });
30
+ window.location.reload();
31
+ };
32
+ return /*#__PURE__*/ _jsx(Container, {
33
+ apiService: MockNotificationsApi,
34
+ children: ()=>/*#__PURE__*/ _jsxs(Flex, {
35
+ direction: "column",
36
+ gap: "4",
37
+ children: [
38
+ /*#__PURE__*/ _jsxs(Flex, {
39
+ direction: "row",
40
+ gap: "2",
41
+ children: [
42
+ /*#__PURE__*/ _jsx(Button, {
43
+ onClick: ()=>{
44
+ handleAdd({
45
+ title: 'Server Notification',
46
+ message: BASIC
47
+ });
48
+ },
49
+ children: "Basic"
50
+ }),
51
+ /*#__PURE__*/ _jsx(Button, {
52
+ onClick: ()=>{
53
+ handleAdd({
54
+ title: 'Server Notification (With Action)',
55
+ message: WITH_ACTION,
56
+ action
57
+ });
58
+ },
59
+ children: "With Action"
60
+ }),
61
+ /*#__PURE__*/ _jsx(Button, {
62
+ onClick: ()=>{
63
+ handleAdd({
64
+ title: 'Server Notification (With Action and Duration)',
65
+ message: WITH_ACTION_AND_DURATION,
66
+ action,
67
+ duration: 8000
68
+ });
69
+ },
70
+ children: "With Action and Duration"
71
+ })
72
+ ]
73
+ }),
74
+ /*#__PURE__*/ _jsx(Button, {
75
+ onClick: ()=>window.location.reload(),
76
+ children: "Reload Page"
77
+ })
78
+ ]
79
+ })
80
+ });
81
+ };
82
+
83
+ //# sourceMappingURL=server-on-load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/demo/server-on-load.tsx"],"sourcesContent":["import { Button, Flex } from '@servicetitan/anvil2';\nimport { FC, useMemo } from 'react';\n\nimport {\n DefaultServerNotificationPayload,\n NotificationProcessStatus,\n} from '../api/notifications.api';\nimport { Container } from './container';\nimport { MockNotificationsApi } from './mock-notifications-api';\nimport { LinkAction } from '../common';\n\nconst BASIC =\n \"This toast disappears automatically.\\n\\nIt won't reappear when the page is reloaded, unless the reload happens before the toast disappears.\";\nconst WITH_ACTION =\n 'This toast does not disappear automatically.\\n\\nUntil the user clicks the action or closes it, the toast reappears whenever the page is reloaded.';\nconst WITH_ACTION_AND_DURATION =\n \"This toast does not disappear automatically.\\n\\nHowever, it won't reappear when the page is reloaded, unless the reload happens before the toast duration expires.\";\n\nconst action: LinkAction = {\n label: 'Click me!',\n link: '/table-master-detail',\n external: true,\n};\n\nexport const ServerOnLoadExample: FC = () => {\n const api = useMemo(() => new MockNotificationsApi(), []);\n\n const handleAdd = (payload: DefaultServerNotificationPayload) => {\n const date = new Date();\n api.add({\n id: date.getTime(),\n userId: 0,\n type: 'PendingServerNotification',\n status: NotificationProcessStatus.Info,\n isRead: false,\n version: 1,\n createdOn: date,\n modifiedOn: date,\n payload: JSON.stringify(payload),\n });\n window.location.reload();\n };\n\n return (\n <Container apiService={MockNotificationsApi}>\n {() => (\n <Flex direction=\"column\" gap=\"4\">\n <Flex direction=\"row\" gap=\"2\">\n <Button\n onClick={() => {\n handleAdd({\n title: 'Server Notification',\n message: BASIC,\n });\n }}\n >\n Basic\n </Button>\n <Button\n onClick={() => {\n handleAdd({\n title: 'Server Notification (With Action)',\n message: WITH_ACTION,\n action,\n });\n }}\n >\n With Action\n </Button>\n <Button\n onClick={() => {\n handleAdd({\n title: 'Server Notification (With Action and Duration)',\n message: WITH_ACTION_AND_DURATION,\n action,\n duration: 8000,\n });\n }}\n >\n With Action and Duration\n </Button>\n </Flex>\n <Button onClick={() => window.location.reload()}>Reload Page</Button>\n </Flex>\n )}\n </Container>\n );\n};\n"],"names":["Button","Flex","useMemo","NotificationProcessStatus","Container","MockNotificationsApi","BASIC","WITH_ACTION","WITH_ACTION_AND_DURATION","action","label","link","external","ServerOnLoadExample","api","handleAdd","payload","date","Date","add","id","getTime","userId","type","status","Info","isRead","version","createdOn","modifiedOn","JSON","stringify","window","location","reload","apiService","direction","gap","onClick","title","message","duration"],"mappings":";AAAA,SAASA,MAAM,EAAEC,IAAI,QAAQ,uBAAuB;AACpD,SAAaC,OAAO,QAAQ,QAAQ;AAEpC,SAEIC,yBAAyB,QACtB,2BAA2B;AAClC,SAASC,SAAS,QAAQ,cAAc;AACxC,SAASC,oBAAoB,QAAQ,2BAA2B;AAGhE,MAAMC,QACF;AACJ,MAAMC,cACF;AACJ,MAAMC,2BACF;AAEJ,MAAMC,SAAqB;IACvBC,OAAO;IACPC,MAAM;IACNC,UAAU;AACd;AAEA,OAAO,MAAMC,sBAA0B;IACnC,MAAMC,MAAMZ,QAAQ,IAAM,IAAIG,wBAAwB,EAAE;IAExD,MAAMU,YAAY,CAACC;QACf,MAAMC,OAAO,IAAIC;QACjBJ,IAAIK,GAAG,CAAC;YACJC,IAAIH,KAAKI,OAAO;YAChBC,QAAQ;YACRC,MAAM;YACNC,QAAQrB,0BAA0BsB,IAAI;YACtCC,QAAQ;YACRC,SAAS;YACTC,WAAWX;YACXY,YAAYZ;YACZD,SAASc,KAAKC,SAAS,CAACf;QAC5B;QACAgB,OAAOC,QAAQ,CAACC,MAAM;IAC1B;IAEA,qBACI,KAAC9B;QAAU+B,YAAY9B;kBAClB,kBACG,MAACJ;gBAAKmC,WAAU;gBAASC,KAAI;;kCACzB,MAACpC;wBAAKmC,WAAU;wBAAMC,KAAI;;0CACtB,KAACrC;gCACGsC,SAAS;oCACLvB,UAAU;wCACNwB,OAAO;wCACPC,SAASlC;oCACb;gCACJ;0CACH;;0CAGD,KAACN;gCACGsC,SAAS;oCACLvB,UAAU;wCACNwB,OAAO;wCACPC,SAASjC;wCACTE;oCACJ;gCACJ;0CACH;;0CAGD,KAACT;gCACGsC,SAAS;oCACLvB,UAAU;wCACNwB,OAAO;wCACPC,SAAShC;wCACTC;wCACAgC,UAAU;oCACd;gCACJ;0CACH;;;;kCAIL,KAACzC;wBAAOsC,SAAS,IAAMN,OAAOC,QAAQ,CAACC,MAAM;kCAAI;;;;;AAKrE,EAAE"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,13 @@
1
- export { NotificationsApi, NotificationProcessStatus, DefaultServerNotificationPayload, } from './api/notifications.api';
2
- export { Notifications, NotificationsProps } from './components/notifications';
3
- export { DefaultNotificationOptions, LinkAction, FunctionAction, Notification, Status, } from './common';
4
- export { intercept, NotificationInterceptor } from './intercept';
5
- export { NotificationsChannel, NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';
6
- export { INotificationsService, NotificationsService } from './notifications-service';
1
+ export type { DefaultServerNotificationPayload } from './api/notifications.api';
2
+ export { NotificationsApi, NotificationProcessStatus } from './api/notifications.api';
3
+ export type { NotificationsProps } from './components/notifications';
4
+ export { Notifications } from './components/notifications';
5
+ export type { DefaultNotificationOptions, LinkAction, FunctionAction, Notification, } from './common';
6
+ export { Status } from './common';
7
+ export type { NotificationInterceptor } from './intercept';
8
+ export { intercept } from './intercept';
9
+ export type { NotificationsChannel } from './notifications-channel';
10
+ export { NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';
11
+ export type { INotificationsService } from './notifications-service';
12
+ export { NotificationsService } from './notifications-service';
7
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,gBAAgB,EAChB,yBAAyB,EACzB,gCAAgC,GACnC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EACH,0BAA0B,EAC1B,UAAU,EACV,cAAc,EACd,YAAY,EACZ,MAAM,GACT,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAC5F,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACtF,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,YAAY,EACR,0BAA0B,EAC1B,UAAU,EACV,cAAc,EACd,YAAY,GACf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- export { NotificationsApi, NotificationProcessStatus, DefaultServerNotificationPayload } from './api/notifications.api';
2
- export { Notifications, NotificationsProps } from './components/notifications';
3
- export { DefaultNotificationOptions, LinkAction, FunctionAction, Notification, Status } from './common';
4
- export { intercept, NotificationInterceptor } from './intercept';
5
- export { NotificationsChannel, NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';
6
- export { INotificationsService, NotificationsService } from './notifications-service';
1
+ export { NotificationsApi, NotificationProcessStatus } from './api/notifications.api';
2
+ export { Notifications } from './components/notifications';
3
+ export { Status } from './common';
4
+ export { intercept } from './intercept';
5
+ export { NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';
6
+ export { NotificationsService } from './notifications-service';
7
7
 
8
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n NotificationsApi,\n NotificationProcessStatus,\n DefaultServerNotificationPayload,\n} from './api/notifications.api';\nexport { Notifications, NotificationsProps } from './components/notifications';\nexport {\n DefaultNotificationOptions,\n LinkAction,\n FunctionAction,\n Notification,\n Status,\n} from './common';\nexport { intercept, NotificationInterceptor } from './intercept';\nexport { NotificationsChannel, NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';\nexport { INotificationsService, NotificationsService } from './notifications-service';\n"],"names":["NotificationsApi","NotificationProcessStatus","DefaultServerNotificationPayload","Notifications","NotificationsProps","DefaultNotificationOptions","LinkAction","FunctionAction","Notification","Status","intercept","NotificationInterceptor","NotificationsChannel","NOTIFICATIONS_CHANNEL_TOKEN","INotificationsService","NotificationsService"],"mappings":"AAAA,SACIA,gBAAgB,EAChBC,yBAAyB,EACzBC,gCAAgC,QAC7B,0BAA0B;AACjC,SAASC,aAAa,EAAEC,kBAAkB,QAAQ,6BAA6B;AAC/E,SACIC,0BAA0B,EAC1BC,UAAU,EACVC,cAAc,EACdC,YAAY,EACZC,MAAM,QACH,WAAW;AAClB,SAASC,SAAS,EAAEC,uBAAuB,QAAQ,cAAc;AACjE,SAASC,oBAAoB,EAAEC,2BAA2B,QAAQ,0BAA0B;AAC5F,SAASC,qBAAqB,EAAEC,oBAAoB,QAAQ,0BAA0B"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type { DefaultServerNotificationPayload } from './api/notifications.api';\nexport { NotificationsApi, NotificationProcessStatus } from './api/notifications.api';\nexport type { NotificationsProps } from './components/notifications';\nexport { Notifications } from './components/notifications';\nexport type {\n DefaultNotificationOptions,\n LinkAction,\n FunctionAction,\n Notification,\n} from './common';\nexport { Status } from './common';\nexport type { NotificationInterceptor } from './intercept';\nexport { intercept } from './intercept';\nexport type { NotificationsChannel } from './notifications-channel';\nexport { NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';\nexport type { INotificationsService } from './notifications-service';\nexport { NotificationsService } from './notifications-service';\n"],"names":["NotificationsApi","NotificationProcessStatus","Notifications","Status","intercept","NOTIFICATIONS_CHANNEL_TOKEN","NotificationsService"],"mappings":"AACA,SAASA,gBAAgB,EAAEC,yBAAyB,QAAQ,0BAA0B;AAEtF,SAASC,aAAa,QAAQ,6BAA6B;AAO3D,SAASC,MAAM,QAAQ,WAAW;AAElC,SAASC,SAAS,QAAQ,cAAc;AAExC,SAASC,2BAA2B,QAAQ,0BAA0B;AAEtE,SAASC,oBAAoB,QAAQ,0BAA0B"}
@@ -11,4 +11,5 @@ export declare const PreventDuplicates: () => import("react/jsx-runtime").JSX.El
11
11
  export declare const MultilineMessage: () => import("react/jsx-runtime").JSX.Element;
12
12
  export declare const ServerDefault: () => import("react/jsx-runtime").JSX.Element;
13
13
  export declare const ServerCustom: () => import("react/jsx-runtime").JSX.Element;
14
+ export declare const ServerOnLoad: () => import("react/jsx-runtime").JSX.Element;
14
15
  //# sourceMappingURL=notifications.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.stories.d.ts","sourceRoot":"","sources":["../src/notifications.stories.tsx"],"names":[],"mappings":";;;AAgBA,wBAEE;AAEF,eAAO,MAAM,KAAK,+CAAoC,CAAC;AACvD,eAAO,MAAM,gBAAgB,+CAAqD,CAAC;AACnF,eAAO,MAAM,QAAQ,+CAAoC,CAAC;AAC1D,eAAO,MAAM,YAAY,+CAA6C,CAAC;AACvE,eAAO,MAAM,QAAQ,+CAAoC,CAAC;AAC1D,eAAO,MAAM,iBAAiB,+CAAuD,CAAC;AACtF,eAAO,MAAM,gBAAgB,+CAAqD,CAAC;AACnF,eAAO,MAAM,aAAa,+CAA+C,CAAC;AAC1E,eAAO,MAAM,YAAY,+CAA6C,CAAC"}
1
+ {"version":3,"file":"notifications.stories.d.ts","sourceRoot":"","sources":["../src/notifications.stories.tsx"],"names":[],"mappings":";;;AAiBA,wBAEE;AAEF,eAAO,MAAM,KAAK,+CAAoC,CAAC;AACvD,eAAO,MAAM,gBAAgB,+CAAqD,CAAC;AACnF,eAAO,MAAM,QAAQ,+CAAoC,CAAC;AAC1D,eAAO,MAAM,YAAY,+CAA6C,CAAC;AACvE,eAAO,MAAM,QAAQ,+CAAoC,CAAC;AAC1D,eAAO,MAAM,iBAAiB,+CAAuD,CAAC;AACtF,eAAO,MAAM,gBAAgB,+CAAqD,CAAC;AACnF,eAAO,MAAM,aAAa,+CAA+C,CAAC;AAC1E,eAAO,MAAM,YAAY,+CAA6C,CAAC;AACvE,eAAO,MAAM,YAAY,+CAA8C,CAAC"}
@@ -11,6 +11,7 @@ export declare class NotificationsStore {
11
11
  private index;
12
12
  private userId;
13
13
  private intervalId?;
14
+ private readonly timers;
14
15
  private readonly notifications;
15
16
  private readonly closedNotifications;
16
17
  constructor(notificationsApi?: NotificationsApi | undefined, notificationsChannel?: NotificationsChannel | undefined);
@@ -32,6 +33,8 @@ export declare class NotificationsStore {
32
33
  private handleClose;
33
34
  private toClientNotification;
34
35
  private toClientStatus;
36
+ private setTimer;
37
+ private clearTimer;
35
38
  }
36
39
  export {};
37
40
  //# sourceMappingURL=notifications.store.d.ts.map
@@ -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;;IAae,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAGxE,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IAf1C,MAAM,CAAC,QAAQ,CAAC,YAAY,uCAA8C;IAE1E,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,UAAU,CAAC,CAAS;IAE5B,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;IAKhE,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,aAML;IAGF,GAAG,GAAI,SAAS,0BAA0B,EAAE,oBAAoB,OAAO,UA2BrE;IAGF,IAAI,GAAI,cAAc,YAAY,UAmBhC;IAGF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAU/B;IAEF,OAAO,CAAC,0BAA0B;IAQlC,OAAO,CAAC,aAAa;IAqCrB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,cAAc;CAUzB"}
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"}
@@ -123,6 +123,7 @@ export class NotificationsStore {
123
123
  toast.update(existing.toastId, this.getToastProps(clientNotification));
124
124
  clientNotification.toastId = existing.toastId;
125
125
  this.notifications.set(notification.id, clientNotification);
126
+ this.setTimer(clientNotification);
126
127
  return;
127
128
  }
128
129
  toast.dismiss(existing.toastId);
@@ -130,6 +131,7 @@ export class NotificationsStore {
130
131
  const method = this.getToastMethod(clientNotification);
131
132
  clientNotification.toastId = toast[method](this.getToastProps(clientNotification));
132
133
  this.notifications.set(notification.id, clientNotification);
134
+ this.setTimer(clientNotification);
133
135
  }
134
136
  getToastMethod({ payload: { status } }) {
135
137
  switch(status){
@@ -150,6 +152,7 @@ export class NotificationsStore {
150
152
  duration: action ? false : duration,
151
153
  message,
152
154
  onClose: ()=>this.handleClose(notification),
155
+ onDismiss: ()=>this.handleClose(notification),
153
156
  progress,
154
157
  title
155
158
  };
@@ -216,12 +219,29 @@ export class NotificationsStore {
216
219
  return Status.Info;
217
220
  }
218
221
  }
222
+ setTimer(notification) {
223
+ this.clearTimer(notification);
224
+ const { duration = 0 } = notification.payload;
225
+ if (duration <= 0) {
226
+ return;
227
+ }
228
+ this.timers.set(notification.id, window.setTimeout(()=>this.read(notification), duration));
229
+ }
230
+ clearTimer({ id }) {
231
+ const timer = this.timers.get(id);
232
+ if (!timer) {
233
+ return;
234
+ }
235
+ this.timers.delete(id);
236
+ window.clearTimeout(timer);
237
+ }
219
238
  constructor(notificationsApi, notificationsChannel){
220
239
  _define_property(this, "notificationsApi", void 0);
221
240
  _define_property(this, "notificationsChannel", void 0);
222
241
  _define_property(this, "index", void 0);
223
242
  _define_property(this, "userId", void 0);
224
243
  _define_property(this, "intervalId", void 0);
244
+ _define_property(this, "timers", void 0);
225
245
  _define_property(this, "notifications", void 0);
226
246
  _define_property(this, "closedNotifications", void 0);
227
247
  _class_private_field_init(this, _navigate, {
@@ -260,6 +280,8 @@ export class NotificationsStore {
260
280
  this.publisher.global_emitter.unbind(EVENT_NAME, this.onPublisherEvent);
261
281
  }
262
282
  window.clearInterval(this.intervalId);
283
+ Object.values(this.timers).forEach((timer)=>window.clearTimeout(timer));
284
+ this.timers.clear();
263
285
  };
264
286
  this.add = (options, preventDuplicates)=>{
265
287
  const notification = {
@@ -289,6 +311,7 @@ export class NotificationsStore {
289
311
  };
290
312
  this.read = (notification)=>{
291
313
  if (notification.id > 0) {
314
+ this.clearTimer(notification);
292
315
  if (notification.status === ServerNotificationStatus.InProgress) {
293
316
  this.closedNotifications.set(notification.id, {
294
317
  status: notification.status,
@@ -312,6 +335,7 @@ export class NotificationsStore {
312
335
  });
313
336
  };
314
337
  makeObservable(this);
338
+ this.timers = new Map();
315
339
  }
316
340
  }
317
341
  _define_property(NotificationsStore, "interceptors", new Map());
@@ -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\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 }\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 };\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 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 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 }\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 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"],"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","dismiss","method","getToastMethod","payload","Success","Error","Warning","duration","message","progress","title","actions","getToastActions","onClose","handleClose","primary","label","onClick","handleClick","external","window","open","link","read","serverNotification","result","JSON","parse","toClientStatus","interceptor","Failure","Info","constructor","notificationsApi","index","userId","intervalId","initialize","dispose","add","onPublisherEvent","Map","bind","setInterval","getNotifications","data","global_emitter","unbind","clearInterval","options","preventDuplicates","isRead","createdOn","modifiedOn","version","values","some","structural","changeIsReadProperty"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAASA,KAAK,QAAoB,uBAAuB;AACzD,SAASC,UAAU,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,0BAA0B;AACvE,SAASC,MAAM,EAAEC,WAAW,EAAEC,QAAQ,EAAEC,cAAc,QAAQ,OAAO;AAErE,SAEIC,gBAAgB,EAEhBC,6BAA6BC,wBAAwB,QAClD,2BAA2B;AAClC,SAAmDC,MAAM,EAAEC,YAAY,EAAEC,OAAO,QAAQ,YAAY;AAEpG,SAASC,oBAAoB,EAAEC,2BAA2B,QAAQ,2BAA2B;AAC7F,SAASC,cAAc,QAAQ,4BAA4B;AAE3D,MAAMC,aAAa;AACnB,MAAMC,2BAA2B;AACjC,MAAMC,8BAA8B;AACpC,MAAMC,eAAe,KAAK,KAAK,KAAK;IA0BhC;AAVJ,OAAO,MAAMC;IAqBT,OAAOC,UAAUC,IAAY,EAAEC,EAA4B,EAAE;QACzD,IAAIA,IAAI;YACJH,mBAAmBI,YAAY,CAACC,GAAG,CAACH,MAAMC;QAC9C,OAAO;YACHH,mBAAmBI,YAAY,CAACE,MAAM,CAACJ;QAC3C;IACJ;IAEA,IAAYK,YAAY;YACb,4BAAA;QAAP,QAAO,6BAAA,CAAA,QAAA,IAAI,EAACC,oBAAoB,cAAzB,iDAAA,gCAAA;IACX;IAEA,IAAIC,WAAyC;QACzC,gCAAO,IAAI,EAAC;IAChB;IAEA,IAAIA,SAASN,EAAoB,EAAE;uCAC1B,WAAYA;IACrB;IAiGQO,6BAA6B;QACjC,KAAK,MAAM,CAACC,IAAI,EAAEC,SAAS,EAAE,CAAC,IAAIC,MAAMC,IAAI,CAAC,IAAI,CAACC,mBAAmB,EAAG;YACpE,IAAIC,KAAKC,GAAG,KAAKL,YAAYb,cAAc;gBACvC,IAAI,CAACgB,mBAAmB,CAACT,MAAM,CAACK;YACpC;QACJ;IACJ;IAEQO,cAAcC,YAAgC,EAAE;QACpD,MAAMC,SAAS,IAAI,CAACL,mBAAmB,CAACM,GAAG,CAACF,aAAaR,EAAE;QAC3D,IAAIS,QAAQ;YACR,IAAIA,OAAOE,MAAM,KAAKH,aAAaG,MAAM,EAAE;gBACvC;YACJ;YAEA,IAAI,CAACP,mBAAmB,CAACT,MAAM,CAACa,aAAaR,EAAE;QACnD;QAEA,MAAMY,qBAAqB,IAAI,CAACC,oBAAoB,CAACL;QACrD,IAAI,CAACI,oBAAoB;YACrB;QACJ;QAEA,MAAME,WAAW,IAAI,CAACC,aAAa,CAACL,GAAG,CAACF,aAAaR,EAAE;QACvD,IAAIc,UAAU;YACV,IAAI,CAACjC,QAAQiC,UAAUN,eAAe;gBAClC;YACJ;YACA,IACIA,aAAaG,MAAM,KAAKG,SAASH,MAAM,IACvCH,aAAaG,MAAM,KAAKjC,yBAAyBsC,UAAU,EAC7D;gBACEhD,MAAMiD,MAAM,CAACH,SAASI,OAAO,EAAG,IAAI,CAACC,aAAa,CAACP;gBACnDA,mBAAmBM,OAAO,GAAGJ,SAASI,OAAO;gBAC7C,IAAI,CAACH,aAAa,CAACrB,GAAG,CAACc,aAAaR,EAAE,EAAEY;gBACxC;YACJ;YACA5C,MAAMoD,OAAO,CAACN,SAASI,OAAO;QAClC;QAEA,MAAMG,SAAS,IAAI,CAACC,cAAc,CAACV;QACnCA,mBAAmBM,OAAO,GAAGlD,KAAK,CAACqD,OAAO,CAAC,IAAI,CAACF,aAAa,CAACP;QAC9D,IAAI,CAACG,aAAa,CAACrB,GAAG,CAACc,aAAaR,EAAE,EAAEY;IAC5C;IAEQU,eAAe,EAAEC,SAAS,EAAEZ,MAAM,EAAE,EAAsB,EAAe;QAC7E,OAAQA;YACJ,KAAKhC,OAAO6C,OAAO;gBACf,OAAO;YACX,KAAK7C,OAAO8C,KAAK;gBACb,OAAO;YACX,KAAK9C,OAAO+C,OAAO;gBACf,OAAO;YACX;gBACI,OAAO;QACf;IACJ;IAEQP,cAAcX,YAAgC,EAAc;QAChE,MAAM,EAAEpC,MAAM,EAAEuD,WAAW,IAAI,EAAEC,UAAU,EAAE,EAAEC,QAAQ,EAAEC,KAAK,EAAE,GAAGtB,aAAae,OAAO;QACvF,OAAO;YACHQ,SAAS,IAAI,CAACC,eAAe,CAACxB;YAC9BmB,UAAUvD,SAAS,QAAQuD;YAC3BC;YACAK,SAAS,IAAM,IAAI,CAACC,WAAW,CAAC1B;YAChCqB;YACAC;QACJ;IACJ;IAEQE,gBAAgBxB,YAAgC,EAAqC;QACzF,MAAM,EAAEpC,MAAM,EAAE,GAAGoC,aAAae,OAAO;QACvC,IAAI,CAACnD,QAAQ;YACT;QACJ;QAEA,OAAO;YACH+D,SAAS;gBACLC,OAAOhE,OAAOgE,KAAK;gBACnBC,SAAS,IAAM,IAAI,CAACC,WAAW,CAAC9B;YACpC;QACJ;IACJ;IAEQ8B,YAAY,EAAEf,SAAS,EAAEnD,MAAM,EAAE,EAAE8C,OAAO,EAAsB,EAAE;QACtE,IAAI9C,QAAQ;YACR,IAAIQ,aAAaR,SAAS;gBACtB,IAAIA,OAAOmE,QAAQ,EAAE;oBACjBC,OAAOC,IAAI,CAACrE,OAAOsE,IAAI;gBAC3B,OAAO;wBACH,gBAAA;qBAAA,iBAAA,CAAA,QAAA,IAAI,EAAC5C,QAAQ,cAAb,qCAAA,oBAAA,OAAgB1B,OAAOsE,IAAI;gBAC/B;YACJ,OAAO;gBACHtE,OAAOiE,OAAO;YAClB;QACJ;QAEArE,MAAMoD,OAAO,CAACF;IAClB;IAEQgB,YAAY1B,YAA0B,EAAE;QAC5C,IAAI,CAACmC,IAAI,CAACnC;IACd;IAEQK,qBACJ+B,kBAAsC,EACR;YAIRA;QAHtB,MAAMC,SAAyD;YAC3D,GAAGD,kBAAkB;YACrBrB,SAAS;gBACL,GAAGuB,KAAKC,KAAK,CAACH,CAAAA,8BAAAA,mBAAmBrB,OAAO,cAA1BqB,yCAAAA,8BAA8B,KAAK;gBACjDjC,QAAQ,IAAI,CAACqC,cAAc,CAACJ,mBAAmBjC,MAAM;YACzD;QACJ;QAEA,MAAMsC,cAAc5D,mBAAmBI,YAAY,CAACiB,GAAG,CAACkC,mBAAmBrD,IAAI;QAC/E,IAAI,CAAC0D,aAAa;YACd,OAAOJ;QACX;QAEA,MAAMzB,UAAU;gBACI;YAAhB,MAAMF,WAAU,0BAAA,IAAI,CAACH,aAAa,CAACL,GAAG,CAACmC,OAAO7C,EAAE,eAAhC,8CAAA,wBAAmCkB,OAAO;YAC1D,IAAIA,SAAS;gBACTlD,MAAMoD,OAAO,CAACF;YAClB;QACJ;QAEA,OAAO+B,YAAYJ,QAAQzB;IAC/B;IAEQ4B,eAAerC,MAAgC,EAAE;QACrD,OAAQA;YACJ,KAAKjC,yBAAyB8C,OAAO;gBACjC,OAAO7C,OAAO6C,OAAO;YACzB,KAAK9C,yBAAyBwE,OAAO;gBACjC,OAAOvE,OAAO8C,KAAK;YACvB;gBACI,OAAO9C,OAAOwE,IAAI;QAC1B;IACJ;IAvQAC,YACI,AAAuDC,gBAAmC,EAC1F,AAEiBxD,oBAA2C,CAC9D;;;QAdF,uBAAQyD,SAAR,KAAA;QACA,uBAAQC,UAAR,KAAA;QACA,uBAAQC,cAAR,KAAA;QAEA,uBAAiBzC,iBAAjB,KAAA;QACA,uBAAiBX,uBAAjB,KAAA;QAEA,gCAAA;;mBAAA,KAAA;;QA+BAqD,uBAAAA,cAAAA,KAAAA;QAsBAC,uBAAAA,WAAAA,KAAAA;QAQA,uBACAC,OADA,KAAA;QA8BA,uBACAhB,QADA,KAAA;QAsBA,uBACiBiB,oBADjB,KAAA;aA9G2DP,mBAAAA;aAGtCxD,uBAAAA;aAbbyD,QAAQ,CAAC;aACTC,SAAS;aAGAxC,gBAAgB,IAAI8C;aACpBzD,sBAAsB,IAAIyD;aAiC3CJ,aAAa,OAAOF;YAChB,IAAI,CAACA,MAAM,GAAGA;YAEd,IAAI,IAAI,CAAC3D,SAAS,EAAE;gBAChB,IAAI,CAACA,SAAS,CAACkE,IAAI,CAAC7E,YAAY,IAAI,CAAC2E,gBAAgB;YACzD;YAEA,IAAI,CAACJ,UAAU,GAAGhB,OAAOuB,WAAW,CAAC,IAAM,IAAI,CAAChE,0BAA0B,IAAIX;YAE9E,IAAI,CAAC,IAAI,CAACiE,gBAAgB,EAAE;gBACxB;YACJ;YAEA,MAAMtC,gBAAgB,AAAC,CAAA,MAAM,IAAI,CAACsC,gBAAgB,CAACW,gBAAgB,CAAC,MAAK,EAAGC,IAAI;YAEhF5F,YAAY;gBACR,KAAK,MAAMmC,gBAAgBO,cAAe;oBACtC,IAAI,CAACR,aAAa,CAACC;gBACvB;YACJ;QACJ;aAEAkD,UAAU;YACN,IAAI,IAAI,CAAC9D,SAAS,EAAE;gBAChB,IAAI,CAACA,SAAS,CAACsE,cAAc,CAACC,MAAM,CAAClF,YAAY,IAAI,CAAC2E,gBAAgB;YAC1E;YAEApB,OAAO4B,aAAa,CAAC,IAAI,CAACZ,UAAU;QACxC;aAGAG,MAAM,CAACU,SAAqCC;YACxC,MAAM9D,eAAmC;gBACrCR,IAAI,IAAI,CAACsD,KAAK;gBACdC,QAAQ,IAAI,CAACA,MAAM;gBACnBhE,MAAML;gBACNyB,QAAQjC,yBAAyByE,IAAI;gBACrCoB,QAAQ;gBACRC,WAAW,IAAInE;gBACfoE,YAAY,IAAIpE;gBAChBqE,SAASvF;gBACToC,SAAS;oBAAE,GAAG8C,OAAO;gBAAC;YAC1B;YAEA,IAAIC,mBAAmB;gBACnB,IACI;uBAAI,IAAI,CAACvD,aAAa,CAAC4D,MAAM;iBAAG,CAACC,IAAI,CAAC,CAAC,EAAErD,OAAO,EAAE,GAC9CjD,SAASuG,UAAU,CAACtD,SAASf,aAAae,OAAO,IAEvD;oBACE;gBACJ;gBACA,IAAI,CAACR,aAAa,CAACrB,GAAG,CAACc,aAAaR,EAAE,EAAEQ;YAC5C;YAEA,MAAMa,SAAS,IAAI,CAACC,cAAc,CAACd;YACnCA,aAAaU,OAAO,GAAGlD,KAAK,CAACqD,OAAO,CAAC,IAAI,CAACF,aAAa,CAACX;YACxD,IAAI,CAAC8C,KAAK,IAAI;QAClB;aAGAX,OAAO,CAACnC;YACJ,IAAIA,aAAaR,EAAE,GAAG,GAAG;gBACrB,IAAIQ,aAAaG,MAAM,KAAKjC,yBAAyBsC,UAAU,EAAE;oBAC7D,IAAI,CAACZ,mBAAmB,CAACV,GAAG,CAACc,aAAaR,EAAE,EAAE;wBAC1CW,QAAQH,aAAaG,MAAM;wBAC3BV,WAAWI,KAAKC,GAAG;oBACvB;gBACJ;gBAEA,IAAI,IAAI,CAAC+C,gBAAgB,EAAE;oBACvB,IAAI,CAACA,gBAAgB,CAACyB,oBAAoB,CACtCtE,aAAaR,EAAE,EACf,MACAQ,aAAakE,OAAO;gBAE5B;YACJ;YAEA,IAAI,CAAC3D,aAAa,CAACpB,MAAM,CAACa,aAAaR,EAAE;QAC7C;aAGiB4D,mBAAmB,CAACpD;YACjC,IAAIA,aAAa+C,MAAM,KAAK,IAAI,CAACA,MAAM,EAAE;gBACrC;YACJ;YAEA,IAAI,CAAChD,aAAa,CAAC;gBACf,GAAGC,YAAY;gBACfgE,WAAWxF,eAAewB,aAAagE,SAAS;gBAChDC,YAAYzF,eAAewB,aAAaiE,UAAU;YACtD;QACJ;QApHIlG,eAAe,IAAI;IACvB;AAiQJ;AAnRI,iBADSc,oBACOI,gBAAe,IAAIoE"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/notifications",
3
- "version": "32.0.0",
3
+ "version": "32.1.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": "511315cbee77cc7c60f20d0d86d24f69779c85ed"
59
+ "gitHead": "2ac8e908d5461818254ed23184aff7fc7d384e83"
60
60
  }
@@ -37,7 +37,6 @@ declare global {
37
37
  * This variable ensures there is one and only one \<Toaster />.
38
38
  * It contains the unique id of the instance that owns the Toaster.
39
39
  */
40
- // eslint-disable-next-line no-var
41
40
  var STNotificationsActiveId: string | undefined;
42
41
  }
43
42
 
@@ -3,7 +3,12 @@ import { useEffect, useMemo, FC } from 'react';
3
3
  import { Provider, useDependencies } from '@servicetitan/react-ioc';
4
4
 
5
5
  import { Notification } from '../api/notifications.api';
6
- import { NOTIFICATIONS_CHANNEL_TOKEN, Notifications, NotificationsService } from '..';
6
+ import {
7
+ NOTIFICATIONS_CHANNEL_TOKEN,
8
+ Notifications,
9
+ NotificationsProps,
10
+ NotificationsService,
11
+ } from '..';
7
12
 
8
13
  type EventCallback = (notification: Notification) => void;
9
14
 
@@ -40,10 +45,11 @@ const NotificationsInitializer: FC = () => {
40
45
  };
41
46
 
42
47
  interface ContainerProps {
48
+ apiService?: NotificationsProps['apiService'];
43
49
  children(publisher: Publisher): JSX.Element;
44
50
  }
45
51
 
46
- export const Container: FC<ContainerProps> = ({ children }) => {
52
+ export const Container: FC<ContainerProps> = ({ apiService, children }) => {
47
53
  const publisherInstance = useMemo(() => new Publisher(), []);
48
54
 
49
55
  return (
@@ -55,7 +61,7 @@ export const Container: FC<ContainerProps> = ({ children }) => {
55
61
  },
56
62
  ]}
57
63
  >
58
- <Notifications>
64
+ <Notifications apiService={apiService}>
59
65
  <NotificationsInitializer />
60
66
  {children(publisherInstance)}
61
67
  </Notifications>
package/src/demo/index.ts CHANGED
@@ -7,3 +7,4 @@ export * from './prevent-duplicates';
7
7
  export * from './multiline-message';
8
8
  export * from './server-default';
9
9
  export * from './server-custom';
10
+ export * from './server-on-load';
@@ -0,0 +1,44 @@
1
+ import { AxiosPromise, CancelToken } from 'axios';
2
+ import { Notification, NotificationsApi } from '../api/notifications.api';
3
+
4
+ export class MockNotificationsApi extends NotificationsApi {
5
+ private readonly localStorageKey = 'MockNotificationsApi.notifications';
6
+
7
+ add(notification: Notification) {
8
+ this.notifications = [...this.notifications, notification];
9
+ }
10
+
11
+ getNotification(id: number, _cancelToken?: CancelToken): AxiosPromise<Notification> {
12
+ return Promise.resolve({ data: this.notifications.find(el => el.id === id) } as any);
13
+ }
14
+
15
+ getNotifications(_read: boolean, _cancelToken?: CancelToken): AxiosPromise<Notification[]> {
16
+ return Promise.resolve({ data: this.notifications } as any);
17
+ }
18
+
19
+ changeIsReadProperty(
20
+ id: number,
21
+ read: boolean,
22
+ _version: number,
23
+ _cancelToken?: CancelToken
24
+ ): AxiosPromise<void> {
25
+ if (read) {
26
+ this.notifications = this.notifications.filter(el => el.id !== id);
27
+ }
28
+ return Promise.resolve({} as any);
29
+ }
30
+
31
+ private get notifications() {
32
+ const value = localStorage.getItem(this.localStorageKey);
33
+ const result: Notification[] = value ? JSON.parse(value) : [];
34
+ return result;
35
+ }
36
+
37
+ private set notifications(notifications: Notification[]) {
38
+ if (notifications.length) {
39
+ localStorage.setItem(this.localStorageKey, JSON.stringify(notifications));
40
+ } else {
41
+ localStorage.removeItem(this.localStorageKey);
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,88 @@
1
+ import { Button, Flex } from '@servicetitan/anvil2';
2
+ import { FC, useMemo } from 'react';
3
+
4
+ import {
5
+ DefaultServerNotificationPayload,
6
+ NotificationProcessStatus,
7
+ } from '../api/notifications.api';
8
+ import { Container } from './container';
9
+ import { MockNotificationsApi } from './mock-notifications-api';
10
+ import { LinkAction } from '../common';
11
+
12
+ const BASIC =
13
+ "This toast disappears automatically.\n\nIt won't reappear when the page is reloaded, unless the reload happens before the toast disappears.";
14
+ const WITH_ACTION =
15
+ 'This toast does not disappear automatically.\n\nUntil the user clicks the action or closes it, the toast reappears whenever the page is reloaded.';
16
+ const WITH_ACTION_AND_DURATION =
17
+ "This toast does not disappear automatically.\n\nHowever, it won't reappear when the page is reloaded, unless the reload happens before the toast duration expires.";
18
+
19
+ const action: LinkAction = {
20
+ label: 'Click me!',
21
+ link: '/table-master-detail',
22
+ external: true,
23
+ };
24
+
25
+ export const ServerOnLoadExample: FC = () => {
26
+ const api = useMemo(() => new MockNotificationsApi(), []);
27
+
28
+ const handleAdd = (payload: DefaultServerNotificationPayload) => {
29
+ const date = new Date();
30
+ api.add({
31
+ id: date.getTime(),
32
+ userId: 0,
33
+ type: 'PendingServerNotification',
34
+ status: NotificationProcessStatus.Info,
35
+ isRead: false,
36
+ version: 1,
37
+ createdOn: date,
38
+ modifiedOn: date,
39
+ payload: JSON.stringify(payload),
40
+ });
41
+ window.location.reload();
42
+ };
43
+
44
+ return (
45
+ <Container apiService={MockNotificationsApi}>
46
+ {() => (
47
+ <Flex direction="column" gap="4">
48
+ <Flex direction="row" gap="2">
49
+ <Button
50
+ onClick={() => {
51
+ handleAdd({
52
+ title: 'Server Notification',
53
+ message: BASIC,
54
+ });
55
+ }}
56
+ >
57
+ Basic
58
+ </Button>
59
+ <Button
60
+ onClick={() => {
61
+ handleAdd({
62
+ title: 'Server Notification (With Action)',
63
+ message: WITH_ACTION,
64
+ action,
65
+ });
66
+ }}
67
+ >
68
+ With Action
69
+ </Button>
70
+ <Button
71
+ onClick={() => {
72
+ handleAdd({
73
+ title: 'Server Notification (With Action and Duration)',
74
+ message: WITH_ACTION_AND_DURATION,
75
+ action,
76
+ duration: 8000,
77
+ });
78
+ }}
79
+ >
80
+ With Action and Duration
81
+ </Button>
82
+ </Flex>
83
+ <Button onClick={() => window.location.reload()}>Reload Page</Button>
84
+ </Flex>
85
+ )}
86
+ </Container>
87
+ );
88
+ };
package/src/index.ts CHANGED
@@ -1,16 +1,17 @@
1
- export {
2
- NotificationsApi,
3
- NotificationProcessStatus,
4
- DefaultServerNotificationPayload,
5
- } from './api/notifications.api';
6
- export { Notifications, NotificationsProps } from './components/notifications';
7
- export {
1
+ export type { DefaultServerNotificationPayload } from './api/notifications.api';
2
+ export { NotificationsApi, NotificationProcessStatus } from './api/notifications.api';
3
+ export type { NotificationsProps } from './components/notifications';
4
+ export { Notifications } from './components/notifications';
5
+ export type {
8
6
  DefaultNotificationOptions,
9
7
  LinkAction,
10
8
  FunctionAction,
11
9
  Notification,
12
- Status,
13
10
  } from './common';
14
- export { intercept, NotificationInterceptor } from './intercept';
15
- export { NotificationsChannel, NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';
16
- export { INotificationsService, NotificationsService } from './notifications-service';
11
+ export { Status } from './common';
12
+ export type { NotificationInterceptor } from './intercept';
13
+ export { intercept } from './intercept';
14
+ export type { NotificationsChannel } from './notifications-channel';
15
+ export { NOTIFICATIONS_CHANNEL_TOKEN } from './notifications-channel';
16
+ export type { INotificationsService } from './notifications-service';
17
+ export { NotificationsService } from './notifications-service';
@@ -12,6 +12,7 @@ import {
12
12
  MultilineMessageExample,
13
13
  ServerDefaultExample,
14
14
  ServerCustomExample,
15
+ ServerOnLoadExample,
15
16
  } from './demo';
16
17
 
17
18
  export default {
@@ -27,6 +28,7 @@ export const PreventDuplicates = wrap('Prevent Duplicates', PreventDuplicatesExa
27
28
  export const MultilineMessage = wrap('Multiline Message', MultilineMessageExample);
28
29
  export const ServerDefault = wrap('Server Default', ServerDefaultExample);
29
30
  export const ServerCustom = wrap('Server Custom', ServerCustomExample);
31
+ export const ServerOnLoad = wrap('Server On Load', ServerOnLoadExample);
30
32
 
31
33
  function wrap(name: string, Component: FC) {
32
34
  return () => (
@@ -280,21 +280,44 @@ describe(NotificationsStore.name, () => {
280
280
  });
281
281
  });
282
282
 
283
- describe('when notification is closed', () => {
284
- const setup = () => {
285
- subject();
286
- jest.mocked(toast.info).mock.calls[0][0].onClose?.({} as any);
287
- };
288
-
283
+ function itNotifiesApi() {
289
284
  test('notifies api', () => {
290
- setup();
291
-
292
285
  expect(api.changeIsReadProperty).toHaveBeenCalledWith(
293
286
  notification.id,
294
287
  true,
295
288
  notification.version
296
289
  );
297
290
  });
291
+ }
292
+
293
+ describe('when notification is closed', () => {
294
+ beforeEach(() => {
295
+ subject();
296
+ jest.mocked(toast.info).mock.calls[0][0].onClose?.({} as any);
297
+ });
298
+
299
+ itNotifiesApi();
300
+ });
301
+
302
+ describe('when notification is dismissed', () => {
303
+ beforeEach(() => {
304
+ subject();
305
+ jest.mocked(toast.info).mock.calls[0][0].onDismiss?.();
306
+ });
307
+
308
+ itNotifiesApi();
309
+ });
310
+
311
+ describe('when duration expires', () => {
312
+ const duration = 8000;
313
+
314
+ beforeEach(() => {
315
+ notification.payload = JSON.stringify({ ...options, duration });
316
+ subject();
317
+ jest.advanceTimersByTime(duration + 1);
318
+ });
319
+
320
+ itNotifiesApi();
298
321
  });
299
322
 
300
323
  describe('when notification has interceptor', () => {
@@ -38,6 +38,7 @@ export class NotificationsStore {
38
38
  private index = -1;
39
39
  private userId = 0;
40
40
  private intervalId?: number;
41
+ private readonly timers: Map<number, number>;
41
42
 
42
43
  private readonly notifications = new Map<number, ClientNotification>();
43
44
  private readonly closedNotifications = new Map<number, ClosedNotification>();
@@ -51,6 +52,7 @@ export class NotificationsStore {
51
52
  private readonly notificationsChannel?: NotificationsChannel
52
53
  ) {
53
54
  makeObservable(this);
55
+ this.timers = new Map();
54
56
  }
55
57
 
56
58
  static intercept(type: string, fn?: NotificationInterceptor) {
@@ -101,6 +103,8 @@ export class NotificationsStore {
101
103
  }
102
104
 
103
105
  window.clearInterval(this.intervalId);
106
+ Object.values(this.timers).forEach(timer => window.clearTimeout(timer));
107
+ this.timers.clear();
104
108
  };
105
109
 
106
110
  @action
@@ -136,6 +140,8 @@ export class NotificationsStore {
136
140
  @action
137
141
  read = (notification: Notification) => {
138
142
  if (notification.id > 0) {
143
+ this.clearTimer(notification);
144
+
139
145
  if (notification.status === ServerNotificationStatus.InProgress) {
140
146
  this.closedNotifications.set(notification.id, {
141
147
  status: notification.status,
@@ -203,6 +209,7 @@ export class NotificationsStore {
203
209
  toast.update(existing.toastId!, this.getToastProps(clientNotification));
204
210
  clientNotification.toastId = existing.toastId;
205
211
  this.notifications.set(notification.id, clientNotification);
212
+ this.setTimer(clientNotification);
206
213
  return;
207
214
  }
208
215
  toast.dismiss(existing.toastId);
@@ -211,6 +218,7 @@ export class NotificationsStore {
211
218
  const method = this.getToastMethod(clientNotification);
212
219
  clientNotification.toastId = toast[method](this.getToastProps(clientNotification));
213
220
  this.notifications.set(notification.id, clientNotification);
221
+ this.setTimer(clientNotification);
214
222
  }
215
223
 
216
224
  private getToastMethod({ payload: { status } }: ClientNotification): ToastMethod {
@@ -233,6 +241,7 @@ export class NotificationsStore {
233
241
  duration: action ? false : duration,
234
242
  message,
235
243
  onClose: () => this.handleClose(notification),
244
+ onDismiss: () => this.handleClose(notification),
236
245
  progress,
237
246
  title,
238
247
  };
@@ -308,4 +317,28 @@ export class NotificationsStore {
308
317
  return Status.Info;
309
318
  }
310
319
  }
320
+
321
+ private setTimer(notification: ClientNotification) {
322
+ this.clearTimer(notification);
323
+
324
+ const { duration = 0 } = notification.payload;
325
+ if (duration <= 0) {
326
+ return;
327
+ }
328
+
329
+ this.timers.set(
330
+ notification.id,
331
+ window.setTimeout(() => this.read(notification), duration)
332
+ );
333
+ }
334
+
335
+ private clearTimer({ id }: Notification) {
336
+ const timer = this.timers.get(id);
337
+ if (!timer) {
338
+ return;
339
+ }
340
+
341
+ this.timers.delete(id);
342
+ window.clearTimeout(timer);
343
+ }
311
344
  }