@servicetitan/notifications 32.0.1 → 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.
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 +32 -4
  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 +61 -8
  38. package/src/stores/notifications.store.ts +42 -8
@@ -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,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"}
@@ -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 = {
@@ -288,18 +310,23 @@ export class NotificationsStore {
288
310
  this.index -= 1;
289
311
  };
290
312
  this.read = (notification)=>{
291
- if (notification.id > 0) {
313
+ const id = notification.id;
314
+ if (!this.notifications.get(id)) {
315
+ return;
316
+ }
317
+ if (id > 0) {
318
+ this.clearTimer(notification);
292
319
  if (notification.status === ServerNotificationStatus.InProgress) {
293
- this.closedNotifications.set(notification.id, {
320
+ this.closedNotifications.set(id, {
294
321
  status: notification.status,
295
322
  timestamp: Date.now()
296
323
  });
297
324
  }
298
325
  if (this.notificationsApi) {
299
- this.notificationsApi.changeIsReadProperty(notification.id, true, notification.version);
326
+ this.notificationsApi.changeIsReadProperty(id, true, notification.version);
300
327
  }
301
328
  }
302
- this.notifications.delete(notification.id);
329
+ this.notifications.delete(id);
303
330
  };
304
331
  this.onPublisherEvent = (notification)=>{
305
332
  if (notification.userId !== this.userId) {
@@ -312,6 +339,7 @@ export class NotificationsStore {
312
339
  });
313
340
  };
314
341
  makeObservable(this);
342
+ this.timers = new Map();
315
343
  }
316
344
  }
317
345
  _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":"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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.0.1",
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": "90707781decfde7cafdd4d246621498649ae0770"
59
+ "gitHead": "83dda961540286419e84e47007a242eac9a2f0ac"
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,74 @@ 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
+ 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
+
304
+ describe('when notification is closed', () => {
305
+ beforeEach(() => {
306
+ subject();
307
+ jest.mocked(toast.info).mock.calls[0][0].onClose?.({} as any);
308
+ });
309
+
310
+ itNotifiesApi();
311
+
312
+ itSendsNoNotificationWhenToastIsDismissed();
313
+ });
314
+
315
+ describe('when notification is dismissed', () => {
316
+ beforeEach(() => {
317
+ subject();
318
+ jest.mocked(toast.info).mock.calls[0][0].onDismiss?.();
319
+ });
320
+
321
+ itNotifiesApi();
322
+ });
323
+
324
+ describe('when toast has duration', () => {
325
+ const duration = 8000;
326
+
327
+ beforeEach(() => {
328
+ notification.payload = JSON.stringify({ ...options, duration });
329
+ subject();
330
+ });
331
+
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
+ });
298
351
  });
299
352
 
300
353
  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
@@ -135,24 +139,27 @@ export class NotificationsStore {
135
139
 
136
140
  @action
137
141
  read = (notification: Notification) => {
138
- if (notification.id > 0) {
142
+ const id = notification.id;
143
+ if (!this.notifications.get(id)) {
144
+ return;
145
+ }
146
+
147
+ if (id > 0) {
148
+ this.clearTimer(notification);
149
+
139
150
  if (notification.status === ServerNotificationStatus.InProgress) {
140
- this.closedNotifications.set(notification.id, {
151
+ this.closedNotifications.set(id, {
141
152
  status: notification.status,
142
153
  timestamp: Date.now(),
143
154
  });
144
155
  }
145
156
 
146
157
  if (this.notificationsApi) {
147
- this.notificationsApi.changeIsReadProperty(
148
- notification.id,
149
- true,
150
- notification.version
151
- );
158
+ this.notificationsApi.changeIsReadProperty(id, true, notification.version);
152
159
  }
153
160
  }
154
161
 
155
- this.notifications.delete(notification.id);
162
+ this.notifications.delete(id);
156
163
  };
157
164
 
158
165
  @action
@@ -203,6 +210,7 @@ export class NotificationsStore {
203
210
  toast.update(existing.toastId!, this.getToastProps(clientNotification));
204
211
  clientNotification.toastId = existing.toastId;
205
212
  this.notifications.set(notification.id, clientNotification);
213
+ this.setTimer(clientNotification);
206
214
  return;
207
215
  }
208
216
  toast.dismiss(existing.toastId);
@@ -211,6 +219,7 @@ export class NotificationsStore {
211
219
  const method = this.getToastMethod(clientNotification);
212
220
  clientNotification.toastId = toast[method](this.getToastProps(clientNotification));
213
221
  this.notifications.set(notification.id, clientNotification);
222
+ this.setTimer(clientNotification);
214
223
  }
215
224
 
216
225
  private getToastMethod({ payload: { status } }: ClientNotification): ToastMethod {
@@ -233,6 +242,7 @@ export class NotificationsStore {
233
242
  duration: action ? false : duration,
234
243
  message,
235
244
  onClose: () => this.handleClose(notification),
245
+ onDismiss: () => this.handleClose(notification),
236
246
  progress,
237
247
  title,
238
248
  };
@@ -308,4 +318,28 @@ export class NotificationsStore {
308
318
  return Status.Info;
309
319
  }
310
320
  }
321
+
322
+ private setTimer(notification: ClientNotification) {
323
+ this.clearTimer(notification);
324
+
325
+ const { duration = 0 } = notification.payload;
326
+ if (duration <= 0) {
327
+ return;
328
+ }
329
+
330
+ this.timers.set(
331
+ notification.id,
332
+ window.setTimeout(() => this.read(notification), duration)
333
+ );
334
+ }
335
+
336
+ private clearTimer({ id }: Notification) {
337
+ const timer = this.timers.get(id);
338
+ if (!timer) {
339
+ return;
340
+ }
341
+
342
+ this.timers.delete(id);
343
+ window.clearTimeout(timer);
344
+ }
311
345
  }