@servicetitan/notifications 32.0.0 → 32.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +24 -0
- 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 +31 -8
- package/src/stores/notifications.store.ts +33 -0
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/components/notifications.tsx"],"names":[],"mappings":"AACA,OAAO,EAAY,UAAU,EAAmB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EACH,EAAE,EACF,iBAAiB,EAMpB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAK5D,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB,CAAC,EAAE,CAAC;IAC7D,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrD;AAED,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAWhD,CAAC;AAEF,OAAO,CAAC,MAAM,CAAC;IACX;;;OAGG;
|
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,UAqBhC;IAGF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAU/B;IAEF,OAAO,CAAC,0BAA0B;IAQlC,OAAO,CAAC,aAAa;IAuCrB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,QAAQ;IAchB,OAAO,CAAC,UAAU;CASrB"}
|
@@ -123,6 +123,7 @@ export class NotificationsStore {
|
|
123
123
|
toast.update(existing.toastId, this.getToastProps(clientNotification));
|
124
124
|
clientNotification.toastId = existing.toastId;
|
125
125
|
this.notifications.set(notification.id, clientNotification);
|
126
|
+
this.setTimer(clientNotification);
|
126
127
|
return;
|
127
128
|
}
|
128
129
|
toast.dismiss(existing.toastId);
|
@@ -130,6 +131,7 @@ export class NotificationsStore {
|
|
130
131
|
const method = this.getToastMethod(clientNotification);
|
131
132
|
clientNotification.toastId = toast[method](this.getToastProps(clientNotification));
|
132
133
|
this.notifications.set(notification.id, clientNotification);
|
134
|
+
this.setTimer(clientNotification);
|
133
135
|
}
|
134
136
|
getToastMethod({ payload: { status } }) {
|
135
137
|
switch(status){
|
@@ -150,6 +152,7 @@ export class NotificationsStore {
|
|
150
152
|
duration: action ? false : duration,
|
151
153
|
message,
|
152
154
|
onClose: ()=>this.handleClose(notification),
|
155
|
+
onDismiss: ()=>this.handleClose(notification),
|
153
156
|
progress,
|
154
157
|
title
|
155
158
|
};
|
@@ -216,12 +219,29 @@ export class NotificationsStore {
|
|
216
219
|
return Status.Info;
|
217
220
|
}
|
218
221
|
}
|
222
|
+
setTimer(notification) {
|
223
|
+
this.clearTimer(notification);
|
224
|
+
const { duration = 0 } = notification.payload;
|
225
|
+
if (duration <= 0) {
|
226
|
+
return;
|
227
|
+
}
|
228
|
+
this.timers.set(notification.id, window.setTimeout(()=>this.read(notification), duration));
|
229
|
+
}
|
230
|
+
clearTimer({ id }) {
|
231
|
+
const timer = this.timers.get(id);
|
232
|
+
if (!timer) {
|
233
|
+
return;
|
234
|
+
}
|
235
|
+
this.timers.delete(id);
|
236
|
+
window.clearTimeout(timer);
|
237
|
+
}
|
219
238
|
constructor(notificationsApi, notificationsChannel){
|
220
239
|
_define_property(this, "notificationsApi", void 0);
|
221
240
|
_define_property(this, "notificationsChannel", void 0);
|
222
241
|
_define_property(this, "index", void 0);
|
223
242
|
_define_property(this, "userId", void 0);
|
224
243
|
_define_property(this, "intervalId", void 0);
|
244
|
+
_define_property(this, "timers", void 0);
|
225
245
|
_define_property(this, "notifications", void 0);
|
226
246
|
_define_property(this, "closedNotifications", void 0);
|
227
247
|
_class_private_field_init(this, _navigate, {
|
@@ -260,6 +280,8 @@ export class NotificationsStore {
|
|
260
280
|
this.publisher.global_emitter.unbind(EVENT_NAME, this.onPublisherEvent);
|
261
281
|
}
|
262
282
|
window.clearInterval(this.intervalId);
|
283
|
+
Object.values(this.timers).forEach((timer)=>window.clearTimeout(timer));
|
284
|
+
this.timers.clear();
|
263
285
|
};
|
264
286
|
this.add = (options, preventDuplicates)=>{
|
265
287
|
const notification = {
|
@@ -289,6 +311,7 @@ export class NotificationsStore {
|
|
289
311
|
};
|
290
312
|
this.read = (notification)=>{
|
291
313
|
if (notification.id > 0) {
|
314
|
+
this.clearTimer(notification);
|
292
315
|
if (notification.status === ServerNotificationStatus.InProgress) {
|
293
316
|
this.closedNotifications.set(notification.id, {
|
294
317
|
status: notification.status,
|
@@ -312,6 +335,7 @@ export class NotificationsStore {
|
|
312
335
|
});
|
313
336
|
};
|
314
337
|
makeObservable(this);
|
338
|
+
this.timers = new Map();
|
315
339
|
}
|
316
340
|
}
|
317
341
|
_define_property(NotificationsStore, "interceptors", new Map());
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/stores/notifications.store.ts"],"sourcesContent":["import { toast, toastProps } from '@servicetitan/anvil2';\nimport { injectable, inject, optional } from '@servicetitan/react-ioc';\nimport { action, runInAction, comparer, makeObservable } from 'mobx';\n\nimport {\n DefaultServerNotificationPayload,\n NotificationsApi,\n Notification as ServerNotification,\n NotificationProcessStatus as ServerNotificationStatus,\n} from '../api/notifications.api';\nimport { DefaultNotificationOptions, Notification, Status, isLinkAction, isNewer } from '../common';\nimport { NotificationInterceptor } from '../intercept';\nimport { NotificationsChannel, NOTIFICATIONS_CHANNEL_TOKEN } from '../notifications-channel';\nimport { dateFromString } from '../utils/date-from-string';\n\nconst EVENT_NAME = 'NotificationEvent';\nconst CLIENT_NOTIFICATION_TYPE = 'ClientNotification';\nconst CLIENT_NOTIFICATION_VERSION = 1;\nconst DAY_INTERVAL = 24 * 60 * 60 * 1000;\n\ninterface ClosedNotification {\n status: ServerNotificationStatus;\n timestamp: number;\n}\n\ninterface ClientNotification extends Notification<DefaultNotificationOptions> {\n toastId?: string;\n}\n\ntype NavigateCallback = (path: string) => void;\n\ntype ToastMethod = 'info' | 'success' | 'warning' | 'danger';\n\n@injectable()\nexport class NotificationsStore {\n static readonly interceptors = new Map<string, NotificationInterceptor>();\n\n private index = -1;\n private userId = 0;\n private intervalId?: number;\n\n private readonly notifications = new Map<number, ClientNotification>();\n private readonly closedNotifications = new Map<number, ClosedNotification>();\n\n #navigate?: NavigateCallback;\n\n constructor(\n @inject(NotificationsApi) @optional() private readonly notificationsApi?: NotificationsApi,\n @inject(NOTIFICATIONS_CHANNEL_TOKEN)\n @optional()\n private readonly notificationsChannel?: NotificationsChannel\n ) {\n makeObservable(this);\n }\n\n static intercept(type: string, fn?: NotificationInterceptor) {\n if (fn) {\n NotificationsStore.interceptors.set(type, fn);\n } else {\n NotificationsStore.interceptors.delete(type);\n }\n }\n\n private get publisher() {\n return this.notificationsChannel?.();\n }\n\n get navigate(): NavigateCallback | undefined {\n return this.#navigate;\n }\n\n set navigate(fn: NavigateCallback) {\n this.#navigate = fn;\n }\n\n initialize = async (userId: number) => {\n this.userId = userId;\n\n if (this.publisher) {\n this.publisher.bind(EVENT_NAME, this.onPublisherEvent);\n }\n\n this.intervalId = window.setInterval(() => this.cleanupClosedNotifications(), DAY_INTERVAL);\n\n if (!this.notificationsApi) {\n return;\n }\n\n const notifications = (await this.notificationsApi.getNotifications(false)).data;\n\n runInAction(() => {\n for (const notification of notifications) {\n this.updateIfNewer(notification);\n }\n });\n };\n\n dispose = () => {\n if (this.publisher) {\n this.publisher.global_emitter.unbind(EVENT_NAME, this.onPublisherEvent);\n }\n\n window.clearInterval(this.intervalId);\n };\n\n @action\n add = (options: DefaultNotificationOptions, preventDuplicates?: boolean) => {\n const notification: ClientNotification = {\n id: this.index,\n userId: this.userId,\n type: CLIENT_NOTIFICATION_TYPE,\n status: ServerNotificationStatus.Info,\n isRead: false,\n createdOn: new Date(),\n modifiedOn: new Date(),\n version: CLIENT_NOTIFICATION_VERSION,\n payload: { ...options },\n };\n\n if (preventDuplicates) {\n if (\n [...this.notifications.values()].some(({ payload }) =>\n comparer.structural(payload, notification.payload)\n )\n ) {\n return;\n }\n this.notifications.set(notification.id, notification);\n }\n\n const method = this.getToastMethod(notification);\n notification.toastId = toast[method](this.getToastProps(notification));\n this.index -= 1;\n };\n\n @action\n read = (notification: Notification) => {\n if (notification.id > 0) {\n if (notification.status === ServerNotificationStatus.InProgress) {\n this.closedNotifications.set(notification.id, {\n status: notification.status,\n timestamp: Date.now(),\n });\n }\n\n if (this.notificationsApi) {\n this.notificationsApi.changeIsReadProperty(\n notification.id,\n true,\n notification.version\n );\n }\n }\n\n this.notifications.delete(notification.id);\n };\n\n @action\n private readonly onPublisherEvent = (notification: ServerNotification) => {\n if (notification.userId !== this.userId) {\n return;\n }\n\n this.updateIfNewer({\n ...notification,\n createdOn: dateFromString(notification.createdOn),\n modifiedOn: dateFromString(notification.modifiedOn),\n });\n };\n\n private cleanupClosedNotifications() {\n for (const [id, { timestamp }] of Array.from(this.closedNotifications)) {\n if (Date.now() - timestamp > DAY_INTERVAL) {\n this.closedNotifications.delete(id);\n }\n }\n }\n\n private updateIfNewer(notification: ServerNotification) {\n const closed = this.closedNotifications.get(notification.id);\n if (closed) {\n if (closed.status === notification.status) {\n return;\n }\n\n this.closedNotifications.delete(notification.id);\n }\n\n const clientNotification = this.toClientNotification(notification);\n if (!clientNotification) {\n return;\n }\n\n const existing = this.notifications.get(notification.id);\n if (existing) {\n if (!isNewer(existing, notification)) {\n return;\n }\n if (\n notification.status === existing.status ||\n notification.status === ServerNotificationStatus.InProgress\n ) {\n toast.update(existing.toastId!, this.getToastProps(clientNotification));\n clientNotification.toastId = existing.toastId;\n this.notifications.set(notification.id, clientNotification);\n return;\n }\n toast.dismiss(existing.toastId);\n }\n\n const method = this.getToastMethod(clientNotification);\n clientNotification.toastId = toast[method](this.getToastProps(clientNotification));\n this.notifications.set(notification.id, clientNotification);\n }\n\n private getToastMethod({ payload: { status } }: ClientNotification): ToastMethod {\n switch (status) {\n case Status.Success:\n return 'success';\n case Status.Error:\n return 'danger';\n case Status.Warning:\n return 'warning';\n default:\n return 'info';\n }\n }\n\n private getToastProps(notification: ClientNotification): toastProps {\n const { action, duration = 8000, message = '', progress, title } = notification.payload;\n return {\n actions: this.getToastActions(notification),\n duration: action ? false : duration,\n message,\n onClose: () => this.handleClose(notification),\n progress,\n title,\n };\n }\n\n private getToastActions(notification: ClientNotification): toastProps['actions'] | undefined {\n const { action } = notification.payload;\n if (!action) {\n return;\n }\n\n return {\n primary: {\n label: action.label,\n onClick: () => this.handleClick(notification),\n },\n };\n }\n\n private handleClick({ payload: { action }, toastId }: ClientNotification) {\n if (action) {\n if (isLinkAction(action)) {\n if (action.external) {\n window.open(action.link);\n } else {\n this.navigate?.(action.link);\n }\n } else {\n action.onClick();\n }\n }\n\n toast.dismiss(toastId);\n }\n\n private handleClose(notification: Notification) {\n this.read(notification);\n }\n\n private toClientNotification(\n serverNotification: ServerNotification\n ): ClientNotification | undefined {\n const result: Notification<DefaultServerNotificationPayload> = {\n ...serverNotification,\n payload: {\n ...JSON.parse(serverNotification.payload ?? '{}'),\n status: this.toClientStatus(serverNotification.status),\n },\n };\n\n const interceptor = NotificationsStore.interceptors.get(serverNotification.type);\n if (!interceptor) {\n return result;\n }\n\n const dismiss = () => {\n const toastId = this.notifications.get(result.id)?.toastId;\n if (toastId) {\n toast.dismiss(toastId);\n }\n };\n\n return interceptor(result, dismiss);\n }\n\n private toClientStatus(status: ServerNotificationStatus) {\n switch (status) {\n case ServerNotificationStatus.Success:\n return Status.Success;\n case ServerNotificationStatus.Failure:\n return Status.Error;\n default:\n return Status.Info;\n }\n }\n}\n"],"names":["toast","injectable","inject","optional","action","runInAction","comparer","makeObservable","NotificationsApi","NotificationProcessStatus","ServerNotificationStatus","Status","isLinkAction","isNewer","NotificationsChannel","NOTIFICATIONS_CHANNEL_TOKEN","dateFromString","EVENT_NAME","CLIENT_NOTIFICATION_TYPE","CLIENT_NOTIFICATION_VERSION","DAY_INTERVAL","NotificationsStore","intercept","type","fn","interceptors","set","delete","publisher","notificationsChannel","navigate","cleanupClosedNotifications","id","timestamp","Array","from","closedNotifications","Date","now","updateIfNewer","notification","closed","get","status","clientNotification","toClientNotification","existing","notifications","InProgress","update","toastId","getToastProps","dismiss","method","getToastMethod","payload","Success","Error","Warning","duration","message","progress","title","actions","getToastActions","onClose","handleClose","primary","label","onClick","handleClick","external","window","open","link","read","serverNotification","result","JSON","parse","toClientStatus","interceptor","Failure","Info","constructor","notificationsApi","index","userId","intervalId","initialize","dispose","add","onPublisherEvent","Map","bind","setInterval","getNotifications","data","global_emitter","unbind","clearInterval","options","preventDuplicates","isRead","createdOn","modifiedOn","version","values","some","structural","changeIsReadProperty"],"mappings":"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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 if (notification.id > 0) {\n this.clearTimer(notification);\n\n if (notification.status === ServerNotificationStatus.InProgress) {\n this.closedNotifications.set(notification.id, {\n status: notification.status,\n timestamp: Date.now(),\n });\n }\n\n if (this.notificationsApi) {\n this.notificationsApi.changeIsReadProperty(\n notification.id,\n true,\n notification.version\n );\n }\n }\n\n this.notifications.delete(notification.id);\n };\n\n @action\n private readonly onPublisherEvent = (notification: ServerNotification) => {\n if (notification.userId !== this.userId) {\n return;\n }\n\n this.updateIfNewer({\n ...notification,\n createdOn: dateFromString(notification.createdOn),\n modifiedOn: dateFromString(notification.modifiedOn),\n });\n };\n\n private cleanupClosedNotifications() {\n for (const [id, { timestamp }] of Array.from(this.closedNotifications)) {\n if (Date.now() - timestamp > DAY_INTERVAL) {\n this.closedNotifications.delete(id);\n }\n }\n }\n\n private updateIfNewer(notification: ServerNotification) {\n const closed = this.closedNotifications.get(notification.id);\n if (closed) {\n if (closed.status === notification.status) {\n return;\n }\n\n this.closedNotifications.delete(notification.id);\n }\n\n const clientNotification = this.toClientNotification(notification);\n if (!clientNotification) {\n return;\n }\n\n const existing = this.notifications.get(notification.id);\n if (existing) {\n if (!isNewer(existing, notification)) {\n return;\n }\n if (\n notification.status === existing.status ||\n notification.status === ServerNotificationStatus.InProgress\n ) {\n toast.update(existing.toastId!, this.getToastProps(clientNotification));\n clientNotification.toastId = existing.toastId;\n this.notifications.set(notification.id, clientNotification);\n this.setTimer(clientNotification);\n return;\n }\n toast.dismiss(existing.toastId);\n }\n\n const method = this.getToastMethod(clientNotification);\n clientNotification.toastId = toast[method](this.getToastProps(clientNotification));\n this.notifications.set(notification.id, clientNotification);\n this.setTimer(clientNotification);\n }\n\n private getToastMethod({ payload: { status } }: ClientNotification): ToastMethod {\n switch (status) {\n case Status.Success:\n return 'success';\n case Status.Error:\n return 'danger';\n case Status.Warning:\n return 'warning';\n default:\n return 'info';\n }\n }\n\n private getToastProps(notification: ClientNotification): toastProps {\n const { action, duration = 8000, message = '', progress, title } = notification.payload;\n return {\n actions: this.getToastActions(notification),\n duration: action ? false : duration,\n message,\n onClose: () => this.handleClose(notification),\n onDismiss: () => this.handleClose(notification),\n progress,\n title,\n };\n }\n\n private getToastActions(notification: ClientNotification): toastProps['actions'] | undefined {\n const { action } = notification.payload;\n if (!action) {\n return;\n }\n\n return {\n primary: {\n label: action.label,\n onClick: () => this.handleClick(notification),\n },\n };\n }\n\n private handleClick({ payload: { action }, toastId }: ClientNotification) {\n if (action) {\n if (isLinkAction(action)) {\n if (action.external) {\n window.open(action.link);\n } else {\n this.navigate?.(action.link);\n }\n } else {\n action.onClick();\n }\n }\n\n toast.dismiss(toastId);\n }\n\n private handleClose(notification: Notification) {\n this.read(notification);\n }\n\n private toClientNotification(\n serverNotification: ServerNotification\n ): ClientNotification | undefined {\n const result: Notification<DefaultServerNotificationPayload> = {\n ...serverNotification,\n payload: {\n ...JSON.parse(serverNotification.payload ?? '{}'),\n status: this.toClientStatus(serverNotification.status),\n },\n };\n\n const interceptor = NotificationsStore.interceptors.get(serverNotification.type);\n if (!interceptor) {\n return result;\n }\n\n const dismiss = () => {\n const toastId = this.notifications.get(result.id)?.toastId;\n if (toastId) {\n toast.dismiss(toastId);\n }\n };\n\n return interceptor(result, dismiss);\n }\n\n private toClientStatus(status: ServerNotificationStatus) {\n switch (status) {\n case ServerNotificationStatus.Success:\n return Status.Success;\n case ServerNotificationStatus.Failure:\n return Status.Error;\n default:\n return Status.Info;\n }\n }\n\n private setTimer(notification: ClientNotification) {\n this.clearTimer(notification);\n\n const { duration = 0 } = notification.payload;\n if (duration <= 0) {\n return;\n }\n\n this.timers.set(\n notification.id,\n window.setTimeout(() => this.read(notification), duration)\n );\n }\n\n private clearTimer({ id }: Notification) {\n const timer = this.timers.get(id);\n if (!timer) {\n return;\n }\n\n this.timers.delete(id);\n window.clearTimeout(timer);\n }\n}\n"],"names":["toast","injectable","inject","optional","action","runInAction","comparer","makeObservable","NotificationsApi","NotificationProcessStatus","ServerNotificationStatus","Status","isLinkAction","isNewer","NotificationsChannel","NOTIFICATIONS_CHANNEL_TOKEN","dateFromString","EVENT_NAME","CLIENT_NOTIFICATION_TYPE","CLIENT_NOTIFICATION_VERSION","DAY_INTERVAL","NotificationsStore","intercept","type","fn","interceptors","set","delete","publisher","notificationsChannel","navigate","cleanupClosedNotifications","id","timestamp","Array","from","closedNotifications","Date","now","updateIfNewer","notification","closed","get","status","clientNotification","toClientNotification","existing","notifications","InProgress","update","toastId","getToastProps","setTimer","dismiss","method","getToastMethod","payload","Success","Error","Warning","duration","message","progress","title","actions","getToastActions","onClose","handleClose","onDismiss","primary","label","onClick","handleClick","external","window","open","link","read","serverNotification","result","JSON","parse","toClientStatus","interceptor","Failure","Info","clearTimer","timers","setTimeout","timer","clearTimeout","constructor","notificationsApi","index","userId","intervalId","initialize","dispose","add","onPublisherEvent","Map","bind","setInterval","getNotifications","data","global_emitter","unbind","clearInterval","Object","values","forEach","clear","options","preventDuplicates","isRead","createdOn","modifiedOn","version","some","structural","changeIsReadProperty"],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@servicetitan/notifications",
|
3
|
-
"version": "32.
|
3
|
+
"version": "32.1.0",
|
4
4
|
"description": "",
|
5
5
|
"homepage": "https://docs.st.dev/docs/frontend/notifications-center",
|
6
6
|
"repository": {
|
@@ -56,5 +56,5 @@
|
|
56
56
|
"cli": {
|
57
57
|
"webpack": false
|
58
58
|
},
|
59
|
-
"gitHead": "
|
59
|
+
"gitHead": "2ac8e908d5461818254ed23184aff7fc7d384e83"
|
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,44 @@ 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
|
+
describe('when notification is closed', () => {
|
294
|
+
beforeEach(() => {
|
295
|
+
subject();
|
296
|
+
jest.mocked(toast.info).mock.calls[0][0].onClose?.({} as any);
|
297
|
+
});
|
298
|
+
|
299
|
+
itNotifiesApi();
|
300
|
+
});
|
301
|
+
|
302
|
+
describe('when notification is dismissed', () => {
|
303
|
+
beforeEach(() => {
|
304
|
+
subject();
|
305
|
+
jest.mocked(toast.info).mock.calls[0][0].onDismiss?.();
|
306
|
+
});
|
307
|
+
|
308
|
+
itNotifiesApi();
|
309
|
+
});
|
310
|
+
|
311
|
+
describe('when duration expires', () => {
|
312
|
+
const duration = 8000;
|
313
|
+
|
314
|
+
beforeEach(() => {
|
315
|
+
notification.payload = JSON.stringify({ ...options, duration });
|
316
|
+
subject();
|
317
|
+
jest.advanceTimersByTime(duration + 1);
|
318
|
+
});
|
319
|
+
|
320
|
+
itNotifiesApi();
|
298
321
|
});
|
299
322
|
|
300
323
|
describe('when notification has interceptor', () => {
|
@@ -38,6 +38,7 @@ export class NotificationsStore {
|
|
38
38
|
private index = -1;
|
39
39
|
private userId = 0;
|
40
40
|
private intervalId?: number;
|
41
|
+
private readonly timers: Map<number, number>;
|
41
42
|
|
42
43
|
private readonly notifications = new Map<number, ClientNotification>();
|
43
44
|
private readonly closedNotifications = new Map<number, ClosedNotification>();
|
@@ -51,6 +52,7 @@ export class NotificationsStore {
|
|
51
52
|
private readonly notificationsChannel?: NotificationsChannel
|
52
53
|
) {
|
53
54
|
makeObservable(this);
|
55
|
+
this.timers = new Map();
|
54
56
|
}
|
55
57
|
|
56
58
|
static intercept(type: string, fn?: NotificationInterceptor) {
|
@@ -101,6 +103,8 @@ export class NotificationsStore {
|
|
101
103
|
}
|
102
104
|
|
103
105
|
window.clearInterval(this.intervalId);
|
106
|
+
Object.values(this.timers).forEach(timer => window.clearTimeout(timer));
|
107
|
+
this.timers.clear();
|
104
108
|
};
|
105
109
|
|
106
110
|
@action
|
@@ -136,6 +140,8 @@ export class NotificationsStore {
|
|
136
140
|
@action
|
137
141
|
read = (notification: Notification) => {
|
138
142
|
if (notification.id > 0) {
|
143
|
+
this.clearTimer(notification);
|
144
|
+
|
139
145
|
if (notification.status === ServerNotificationStatus.InProgress) {
|
140
146
|
this.closedNotifications.set(notification.id, {
|
141
147
|
status: notification.status,
|
@@ -203,6 +209,7 @@ export class NotificationsStore {
|
|
203
209
|
toast.update(existing.toastId!, this.getToastProps(clientNotification));
|
204
210
|
clientNotification.toastId = existing.toastId;
|
205
211
|
this.notifications.set(notification.id, clientNotification);
|
212
|
+
this.setTimer(clientNotification);
|
206
213
|
return;
|
207
214
|
}
|
208
215
|
toast.dismiss(existing.toastId);
|
@@ -211,6 +218,7 @@ export class NotificationsStore {
|
|
211
218
|
const method = this.getToastMethod(clientNotification);
|
212
219
|
clientNotification.toastId = toast[method](this.getToastProps(clientNotification));
|
213
220
|
this.notifications.set(notification.id, clientNotification);
|
221
|
+
this.setTimer(clientNotification);
|
214
222
|
}
|
215
223
|
|
216
224
|
private getToastMethod({ payload: { status } }: ClientNotification): ToastMethod {
|
@@ -233,6 +241,7 @@ export class NotificationsStore {
|
|
233
241
|
duration: action ? false : duration,
|
234
242
|
message,
|
235
243
|
onClose: () => this.handleClose(notification),
|
244
|
+
onDismiss: () => this.handleClose(notification),
|
236
245
|
progress,
|
237
246
|
title,
|
238
247
|
};
|
@@ -308,4 +317,28 @@ export class NotificationsStore {
|
|
308
317
|
return Status.Info;
|
309
318
|
}
|
310
319
|
}
|
320
|
+
|
321
|
+
private setTimer(notification: ClientNotification) {
|
322
|
+
this.clearTimer(notification);
|
323
|
+
|
324
|
+
const { duration = 0 } = notification.payload;
|
325
|
+
if (duration <= 0) {
|
326
|
+
return;
|
327
|
+
}
|
328
|
+
|
329
|
+
this.timers.set(
|
330
|
+
notification.id,
|
331
|
+
window.setTimeout(() => this.read(notification), duration)
|
332
|
+
);
|
333
|
+
}
|
334
|
+
|
335
|
+
private clearTimer({ id }: Notification) {
|
336
|
+
const timer = this.timers.get(id);
|
337
|
+
if (!timer) {
|
338
|
+
return;
|
339
|
+
}
|
340
|
+
|
341
|
+
this.timers.delete(id);
|
342
|
+
window.clearTimeout(timer);
|
343
|
+
}
|
311
344
|
}
|