@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.
- package/dist/components/notifications.d.ts.map +1 -1
- package/dist/components/notifications.js.map +1 -1
- package/dist/demo/container.d.ts +2 -0
- package/dist/demo/container.d.ts.map +1 -1
- package/dist/demo/container.js +2 -1
- package/dist/demo/container.js.map +1 -1
- package/dist/demo/index.d.ts +1 -0
- package/dist/demo/index.d.ts.map +1 -1
- package/dist/demo/index.js +1 -0
- package/dist/demo/index.js.map +1 -1
- package/dist/demo/mock-notifications-api.d.ts +12 -0
- package/dist/demo/mock-notifications-api.d.ts.map +1 -0
- package/dist/demo/mock-notifications-api.js +55 -0
- package/dist/demo/mock-notifications-api.js.map +1 -0
- package/dist/demo/server-on-load.d.ts +3 -0
- package/dist/demo/server-on-load.d.ts.map +1 -0
- package/dist/demo/server-on-load.js +83 -0
- package/dist/demo/server-on-load.js.map +1 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/notifications.stories.d.ts +1 -0
- package/dist/notifications.stories.d.ts.map +1 -1
- package/dist/stores/notifications.store.d.ts +3 -0
- package/dist/stores/notifications.store.d.ts.map +1 -1
- package/dist/stores/notifications.store.js +32 -4
- package/dist/stores/notifications.store.js.map +1 -1
- package/package.json +2 -2
- package/src/components/notifications.tsx +0 -1
- package/src/demo/container.tsx +9 -3
- package/src/demo/index.ts +1 -0
- package/src/demo/mock-notifications-api.ts +44 -0
- package/src/demo/server-on-load.tsx +88 -0
- package/src/index.ts +12 -11
- package/src/notifications.stories.tsx +2 -0
- package/src/stores/__tests__/notifications.store.test.ts +61 -8
- 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;
|
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
|
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"}
|
package/dist/demo/container.d.ts
CHANGED
@@ -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;
|
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"}
|
package/dist/demo/container.js
CHANGED
@@ -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 {
|
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"}
|
package/dist/demo/index.d.ts
CHANGED
package/dist/demo/index.d.ts.map
CHANGED
@@ -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"}
|
package/dist/demo/index.js
CHANGED
package/dist/demo/index.js.map
CHANGED
@@ -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 @@
|
|
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 {
|
2
|
-
export {
|
3
|
-
export {
|
4
|
-
export {
|
5
|
-
export {
|
6
|
-
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 { 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
|
package/dist/index.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
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
|
2
|
-
export { Notifications
|
3
|
-
export {
|
4
|
-
export { intercept
|
5
|
-
export {
|
6
|
-
export {
|
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 {
|
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":";;;
|
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;;
|
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
|
-
|
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(
|
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(
|
326
|
+
this.notificationsApi.changeIsReadProperty(id, true, notification.version);
|
300
327
|
}
|
301
328
|
}
|
302
|
-
this.notifications.delete(
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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 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
|
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": "
|
59
|
+
"gitHead": "83dda961540286419e84e47007a242eac9a2f0ac"
|
60
60
|
}
|
package/src/demo/container.tsx
CHANGED
@@ -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 {
|
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
@@ -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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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 {
|
15
|
-
export {
|
16
|
-
export {
|
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
|
-
|
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
|
-
|
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(
|
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(
|
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
|
}
|