@peassoft/mnr-web-topline 3.0.0 → 4.0.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/README.md +105 -18
- package/dist/css/index.css +2 -2
- package/dist/en/modules/keyboard-navigation/key-codes.d.ts +16 -18
- package/dist/en/modules/keyboard-navigation/key-codes.js +17 -18
- package/dist/en/modules/keyboard-navigation/vertical-menu.d.ts +1 -1
- package/dist/en/modules/keyboard-navigation/vertical-menu.js +8 -8
- package/dist/en/modules/local-db/actions/delete-all-data/index.js +2 -2
- package/dist/en/modules/local-db/actions/get-all-data/index.js +2 -2
- package/dist/en/modules/local-db/actions/get-is-user-known/index.d.ts +5 -0
- package/dist/en/modules/local-db/actions/get-is-user-known/index.js +33 -0
- package/dist/en/modules/local-db/actions/update-grant-token/index.js +3 -3
- package/dist/en/modules/local-db/actions/update-is-user-known/index.d.ts +5 -0
- package/dist/en/modules/local-db/actions/update-is-user-known/index.js +27 -0
- package/dist/en/modules/local-db/actions/update-refresh-token/index.js +3 -3
- package/dist/en/modules/local-db/actions/update-user/index.js +3 -3
- package/dist/en/modules/local-db/index.d.ts +2 -0
- package/dist/en/modules/local-db/index.js +3 -1
- package/dist/en/modules/local-db/init-db.d.ts +2 -1
- package/dist/en/modules/local-db/init-db.js +19 -10
- package/dist/en/modules/topline-service/index.d.ts +3 -1
- package/dist/en/modules/topline-service/index.js +6 -0
- package/dist/en/modules/topline-service/inner-service.d.ts +6 -1
- package/dist/en/modules/topline-service/inner-service.js +18 -0
- package/dist/en/modules/topline-service/types.d.ts +12 -0
- package/dist/en/modules/topline-service/types.js +4 -0
- package/dist/en/parts/login/actions/perform-login/index.d.ts +1 -15
- package/dist/en/parts/login/actions/perform-login/index.js +8 -17
- package/dist/en/parts/password-recovery/actions/save-password/index.d.ts +1 -15
- package/dist/en/parts/password-recovery/actions/save-password/index.js +8 -17
- package/dist/en/parts/profile/ui/password-change-confirmation/index.js +1 -0
- package/dist/en/parts/profile/ui/password-change-form/use-state-with-validation-reset.js +1 -1
- package/dist/en/parts/shell/context.d.ts +1 -1
- package/dist/en/parts/shell/ui/logged-out-user-menu/index.js +2 -0
- package/dist/en/parts/shell/ui/loggeg-in-user-menu/index.js +2 -0
- package/dist/en/parts/shell/ui/shell/index.js +11 -5
- package/dist/en/parts/shell/ui/user-menu-item/index.d.ts +1 -0
- package/dist/en/parts/shell/ui/user-menu-item/index.js +2 -0
- package/dist/en/parts/signup/actions/perform-signup/index.d.ts +1 -15
- package/dist/en/parts/signup/actions/perform-signup/index.js +8 -17
- package/dist/en/shared/components/modal/index.js +2 -2
- package/dist/en/shared/procedures/process-response.d.ts +13 -0
- package/dist/en/shared/procedures/process-response.js +53 -0
- package/dist/en/topline.d.ts +1 -1
- package/dist/en/types/result.d.ts +9 -0
- package/dist/en/types/result.js +12 -0
- package/dist/ru/modules/keyboard-navigation/key-codes.d.ts +16 -18
- package/dist/ru/modules/keyboard-navigation/key-codes.js +17 -18
- package/dist/ru/modules/keyboard-navigation/vertical-menu.d.ts +1 -1
- package/dist/ru/modules/keyboard-navigation/vertical-menu.js +8 -8
- package/dist/ru/modules/local-db/actions/delete-all-data/index.js +2 -2
- package/dist/ru/modules/local-db/actions/get-all-data/index.js +2 -2
- package/dist/ru/modules/local-db/actions/get-is-user-known/index.d.ts +5 -0
- package/dist/ru/modules/local-db/actions/get-is-user-known/index.js +33 -0
- package/dist/ru/modules/local-db/actions/update-grant-token/index.js +3 -3
- package/dist/ru/modules/local-db/actions/update-is-user-known/index.d.ts +5 -0
- package/dist/ru/modules/local-db/actions/update-is-user-known/index.js +27 -0
- package/dist/ru/modules/local-db/actions/update-refresh-token/index.js +3 -3
- package/dist/ru/modules/local-db/actions/update-user/index.js +3 -3
- package/dist/ru/modules/local-db/index.d.ts +2 -0
- package/dist/ru/modules/local-db/index.js +3 -1
- package/dist/ru/modules/local-db/init-db.d.ts +2 -1
- package/dist/ru/modules/local-db/init-db.js +19 -10
- package/dist/ru/modules/topline-service/index.d.ts +3 -1
- package/dist/ru/modules/topline-service/index.js +6 -0
- package/dist/ru/modules/topline-service/inner-service.d.ts +6 -1
- package/dist/ru/modules/topline-service/inner-service.js +18 -0
- package/dist/ru/modules/topline-service/types.d.ts +12 -0
- package/dist/ru/modules/topline-service/types.js +4 -0
- package/dist/ru/parts/login/actions/perform-login/index.d.ts +1 -15
- package/dist/ru/parts/login/actions/perform-login/index.js +8 -17
- package/dist/ru/parts/password-recovery/actions/save-password/index.d.ts +1 -15
- package/dist/ru/parts/password-recovery/actions/save-password/index.js +8 -17
- package/dist/ru/parts/profile/ui/password-change-confirmation/index.js +1 -0
- package/dist/ru/parts/profile/ui/password-change-form/use-state-with-validation-reset.js +1 -1
- package/dist/ru/parts/shell/context.d.ts +1 -1
- package/dist/ru/parts/shell/ui/logged-out-user-menu/index.js +2 -0
- package/dist/ru/parts/shell/ui/loggeg-in-user-menu/index.js +2 -0
- package/dist/ru/parts/shell/ui/shell/index.js +11 -5
- package/dist/ru/parts/shell/ui/user-menu-item/index.d.ts +1 -0
- package/dist/ru/parts/shell/ui/user-menu-item/index.js +2 -0
- package/dist/ru/parts/signup/actions/perform-signup/index.d.ts +1 -15
- package/dist/ru/parts/signup/actions/perform-signup/index.js +8 -17
- package/dist/ru/shared/components/modal/index.js +2 -2
- package/dist/ru/shared/procedures/process-response.d.ts +13 -0
- package/dist/ru/shared/procedures/process-response.js +53 -0
- package/dist/ru/topline.d.ts +1 -1
- package/dist/ru/types/result.d.ts +9 -0
- package/dist/ru/types/result.js +12 -0
- package/package.json +10 -4
- package/dist/en/shared/procedures/process-successful-response/index.d.ts +0 -30
- package/dist/en/shared/procedures/process-successful-response/index.js +0 -66
- package/dist/ru/shared/procedures/process-successful-response/index.d.ts +0 -30
- package/dist/ru/shared/procedures/process-successful-response/index.js +0 -66
package/README.md
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
# @peassoft/mnr-web-topline
|
|
2
2
|
|
|
3
|
-
Topline widget for
|
|
3
|
+
Topline widget for Memorize'n'Revise web applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```shell
|
|
8
|
+
npm i @peassoft/mnr-web-topline
|
|
9
|
+
```
|
|
4
10
|
|
|
5
11
|
## Usage Example
|
|
6
12
|
|
|
7
13
|
```jsx
|
|
8
|
-
import { useCallback } from 'react';
|
|
14
|
+
import { useCallback, type JSX } from 'react';
|
|
9
15
|
import WebTopline, {
|
|
10
|
-
ToplineService,
|
|
11
|
-
ToplineUser,
|
|
16
|
+
type ToplineService,
|
|
17
|
+
type ToplineUser,
|
|
18
|
+
ToplineEventName,
|
|
12
19
|
} from '@peassoft/mnr-web-topline';
|
|
13
20
|
|
|
14
21
|
export default function Topline(): JSX.Element {
|
|
15
22
|
const handleReady = useCallback(
|
|
16
|
-
|
|
17
|
-
toplineService
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Use toplineService and toplineUser somehow else
|
|
23
|
+
(toplineService: ToplineService, toplineUser: ToplineUser | null, isUserKnown: boolean) => {
|
|
24
|
+
toplineService.on(ToplineEventName.Login, user => {/*...*/});
|
|
25
|
+
toplineService.on(ToplineEventName.Signup, user => {/*...*/});
|
|
26
|
+
toplineService.on(ToplineEventName.Logout, () => {/*...*/});
|
|
27
|
+
toplineService.on(ToplineEventName.UserChange, user => {/*...*/});
|
|
28
|
+
toplineService.on(ToplineEventName.SyncNotification, syncId => {/*...*/});
|
|
29
|
+
|
|
30
|
+
// Use `toplineUser`...
|
|
31
|
+
|
|
32
|
+
// Use `isUserKnown` e.g. to determine whether to fetch the default set of data.
|
|
29
33
|
},
|
|
30
34
|
[],
|
|
31
35
|
);
|
|
@@ -41,4 +45,87 @@ export default function Topline(): JSX.Element {
|
|
|
41
45
|
|
|
42
46
|
## API Reference
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
### Props
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
type ToplineProps = {
|
|
52
|
+
/** User to use in SSR */
|
|
53
|
+
ssrUser?: ToplineUser | null;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* If set to `true`, the link to the home page opens the home page in a separate window.
|
|
57
|
+
*
|
|
58
|
+
* For use in PWA.
|
|
59
|
+
*/
|
|
60
|
+
forceBlankHomeLink?: boolean;
|
|
61
|
+
|
|
62
|
+
/** Callback that is invoked after user is restored from a local DB */
|
|
63
|
+
onReady?: (
|
|
64
|
+
toplineService: ToplineService,
|
|
65
|
+
user: ToplineUser | null,
|
|
66
|
+
isUserKnown: boolean,
|
|
67
|
+
) => unknown;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Callback that is invoked if an unexpected error is caught by React's error boundary
|
|
71
|
+
* component
|
|
72
|
+
*
|
|
73
|
+
* This callback invokation will almost always indicate that the user management system
|
|
74
|
+
* is broken or in an undefined state. Thus, the hosting app should not try to recover
|
|
75
|
+
* from this error, but take reasonable measures depending on the situation.
|
|
76
|
+
*/
|
|
77
|
+
onError?: (err: Error) => unknown;
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `onReady` Callback Function Parameters
|
|
82
|
+
|
|
83
|
+
* `toplineService: ToplineService` - An object containing functionality of the topline (see below).
|
|
84
|
+
|
|
85
|
+
* `user: ToplineUser | null` - User data as it's restored from the local DB. Later, topline will make an attempt to fetch fresh user data from the server which may result in the following scenarios:
|
|
86
|
+
|
|
87
|
+
* User data fetched is identical to the local data: Nothing will happen.
|
|
88
|
+
|
|
89
|
+
* User data fetched defers from the local data: `ToplineEventName.UserChange` event will be emitted on the `toplineService`.
|
|
90
|
+
|
|
91
|
+
* Request is not authenticated: `ToplineEventName.Logout` event will be emitted on the `toplineService`.
|
|
92
|
+
|
|
93
|
+
* Request fails (e.g. no Internet connection): Nothing will happen.
|
|
94
|
+
|
|
95
|
+
* `isUserKnown: boolean` - Flag that if set to `true` indicates that the current browser once been used for logging in Memorize'n'Revise. The `false` value is possible when `user` value is `null`.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
### `ToplineService`
|
|
100
|
+
|
|
101
|
+
#### Events
|
|
102
|
+
|
|
103
|
+
* `ToplineService.on(ToplineEventName.Login, (user: ToplineUser) => unknown)` - Emitted after user has successfully logged in (possibly, after password restoration procedure).
|
|
104
|
+
|
|
105
|
+
* `ToplineService.on(ToplineEventName.Signup, (user: ToplineUser) => unknown)` - Emitted after user has successfully created a new account and is now logged-in.
|
|
106
|
+
|
|
107
|
+
* `ToplineService.on(ToplineEventName.Logout, () => unknown)` - Emitted when:
|
|
108
|
+
|
|
109
|
+
* user has explicitely logged out;
|
|
110
|
+
|
|
111
|
+
* a sync notification is received about user's log-out in another application instance;
|
|
112
|
+
|
|
113
|
+
* initialization sync request resulted in status code 401.
|
|
114
|
+
|
|
115
|
+
* `ToplineService.on(ToplineEventName.UserChange, (user: ToplineUser) => unknown)` - Emitted when:
|
|
116
|
+
|
|
117
|
+
* a sync notification is received about user's data having been changed in another application instance;
|
|
118
|
+
|
|
119
|
+
* initialization sync returns different user data as of stored in the local DB.
|
|
120
|
+
|
|
121
|
+
* `ToplineService.on(ToplineEventName.SyncNotification, (syncId: string) => unknown)` - Emitted when a notification about sync is received. `syncId` is supposed to be used by applications to filter out syncs which are initiated by the application itself.
|
|
122
|
+
|
|
123
|
+
#### Methods
|
|
124
|
+
|
|
125
|
+
* `ToplineService.grantToken(): string | null` - Get current `grantToken` to use in a request which requires authorization.
|
|
126
|
+
|
|
127
|
+
* `ToplineService.upgradeGrantToken(): Promise<string | null>` - Upgrade `grantToken` with `refreshToken`. Supposed to be used after a request that used an expired `grantToken` resulted in status code `401`.
|
|
128
|
+
|
|
129
|
+
* `ToplineService.logIn(): void` - Command to open logging-in form.
|
|
130
|
+
|
|
131
|
+
* `ToplineService.createAccount(): void` - Command to open creating account form.
|
package/dist/css/index.css
CHANGED
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
|
|
103
103
|
.topline_shell_userMenu {
|
|
104
104
|
position: absolute;
|
|
105
|
-
z-index:
|
|
105
|
+
z-index: 2;
|
|
106
106
|
bottom: 0;
|
|
107
107
|
right: 0;
|
|
108
108
|
width: 300px;
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
|
|
204
204
|
.topline_c_modal_overlay {
|
|
205
205
|
position: fixed;
|
|
206
|
-
z-index:
|
|
206
|
+
z-index: 2;
|
|
207
207
|
top: 0;
|
|
208
208
|
bottom: 0;
|
|
209
209
|
left: 0;
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}>;
|
|
18
|
-
export default keyCodes;
|
|
1
|
+
export declare enum Key {
|
|
2
|
+
Tab = "Tab",
|
|
3
|
+
Enter = "Enter",
|
|
4
|
+
Escape = "Escape",
|
|
5
|
+
Space = " ",
|
|
6
|
+
PageUp = "PageUp",
|
|
7
|
+
PageDown = "PageDown",
|
|
8
|
+
End = "End",
|
|
9
|
+
Home = "Home",
|
|
10
|
+
Left = "ArrowLeft",
|
|
11
|
+
Up = "ArrowUp",
|
|
12
|
+
Right = "ArrowRight",
|
|
13
|
+
Down = "ArrowDown",
|
|
14
|
+
F2 = "F2",
|
|
15
|
+
F10 = "F10"
|
|
16
|
+
}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
export default keyCodes;
|
|
1
|
+
export var Key;
|
|
2
|
+
(function (Key) {
|
|
3
|
+
Key["Tab"] = "Tab";
|
|
4
|
+
Key["Enter"] = "Enter";
|
|
5
|
+
Key["Escape"] = "Escape";
|
|
6
|
+
Key["Space"] = " ";
|
|
7
|
+
Key["PageUp"] = "PageUp";
|
|
8
|
+
Key["PageDown"] = "PageDown";
|
|
9
|
+
Key["End"] = "End";
|
|
10
|
+
Key["Home"] = "Home";
|
|
11
|
+
Key["Left"] = "ArrowLeft";
|
|
12
|
+
Key["Up"] = "ArrowUp";
|
|
13
|
+
Key["Right"] = "ArrowRight";
|
|
14
|
+
Key["Down"] = "ArrowDown";
|
|
15
|
+
Key["F2"] = "F2";
|
|
16
|
+
Key["F10"] = "F10";
|
|
17
|
+
})(Key || (Key = {}));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ToplineAction } from '../../types/app.js';
|
|
2
2
|
export type ProcessKeyDownParams = {
|
|
3
3
|
currIndex: number;
|
|
4
|
-
e: Pick<React.KeyboardEvent, '
|
|
4
|
+
e: Pick<React.KeyboardEvent, 'key' | 'preventDefault' | 'stopPropagation'>;
|
|
5
5
|
setCurrItemIdx: (idx: number) => unknown;
|
|
6
6
|
menuItemRefs: React.RefObject<HTMLDivElement | null>[];
|
|
7
7
|
actionName: ToplineAction;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Key } from './key-codes.js';
|
|
2
2
|
/**
|
|
3
3
|
* Process pressed key for vertical menu
|
|
4
4
|
*/
|
|
@@ -14,17 +14,17 @@ export function processKeyDown(params) {
|
|
|
14
14
|
} = params;
|
|
15
15
|
const itemsNum = menuItemRefs.length;
|
|
16
16
|
let nextIndex = -Infinity;
|
|
17
|
-
switch (e.
|
|
18
|
-
case
|
|
17
|
+
switch (e.key) {
|
|
18
|
+
case Key.Down:
|
|
19
19
|
nextIndex = currIndex === itemsNum - 1 ? 0 : currIndex + 1;
|
|
20
20
|
break;
|
|
21
|
-
case
|
|
21
|
+
case Key.Up:
|
|
22
22
|
nextIndex = currIndex === 0 ? itemsNum - 1 : currIndex - 1;
|
|
23
23
|
break;
|
|
24
|
-
case
|
|
24
|
+
case Key.Home:
|
|
25
25
|
nextIndex = 0;
|
|
26
26
|
break;
|
|
27
|
-
case
|
|
27
|
+
case Key.End:
|
|
28
28
|
nextIndex = itemsNum - 1;
|
|
29
29
|
}
|
|
30
30
|
if (nextIndex > -Infinity) {
|
|
@@ -34,11 +34,11 @@ export function processKeyDown(params) {
|
|
|
34
34
|
menuItemRefs[nextIndex]?.current?.focus();
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
|
-
if (e.
|
|
37
|
+
if (e.key === Key.Enter) {
|
|
38
38
|
onSelect(actionName);
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
if (e.
|
|
41
|
+
if (e.key === Key.Escape) {
|
|
42
42
|
onClose();
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDb,
|
|
1
|
+
import { getDb, MAIN_STORE_NAME } from '../../init-db.js';
|
|
2
2
|
import createRequestError from '../../create-request-error.js';
|
|
3
3
|
import { logError } from '../../../logger/index.js';
|
|
4
4
|
/**
|
|
@@ -11,7 +11,7 @@ export default function deleteAllData() {
|
|
|
11
11
|
resolve();
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
-
const req = db.transaction(
|
|
14
|
+
const req = db.transaction(MAIN_STORE_NAME, 'readwrite').objectStore(MAIN_STORE_NAME).clear();
|
|
15
15
|
req.onsuccess = () => resolve();
|
|
16
16
|
req.onerror = () => {
|
|
17
17
|
logError(createRequestError('deleteAllData'));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDb,
|
|
1
|
+
import { getDb, MAIN_STORE_NAME } from '../../init-db.js';
|
|
2
2
|
import createRequestError from '../../create-request-error.js';
|
|
3
3
|
import { logError } from '../../../logger/index.js';
|
|
4
4
|
/**
|
|
@@ -15,7 +15,7 @@ export default function getAllData() {
|
|
|
15
15
|
});
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
const req = db.transaction(
|
|
18
|
+
const req = db.transaction(MAIN_STORE_NAME).objectStore(MAIN_STORE_NAME).getAll();
|
|
19
19
|
req.onsuccess = function () {
|
|
20
20
|
const recordset = this.result;
|
|
21
21
|
let user = null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getDb, PERSISTENT_STORE_NAME } from '../../init-db.js';
|
|
2
|
+
import createRequestError from '../../create-request-error.js';
|
|
3
|
+
import { logError } from '../../../logger/index.js';
|
|
4
|
+
import { Ok } from '../../../../types/result.js';
|
|
5
|
+
/**
|
|
6
|
+
* Retrieve the value of flag `isUserKnown`.
|
|
7
|
+
*/
|
|
8
|
+
export default function getIsUserKnown() {
|
|
9
|
+
return new Promise(resolve => {
|
|
10
|
+
const db = getDb();
|
|
11
|
+
if (!db) {
|
|
12
|
+
// If something is wrong with the DB, we would be better off returning "true"
|
|
13
|
+
// to prevent presenting the user the default set of content when it might
|
|
14
|
+
// be unreasonable.
|
|
15
|
+
resolve(Ok(true));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const req = db.transaction(PERSISTENT_STORE_NAME).objectStore(PERSISTENT_STORE_NAME).get('isUserKnown');
|
|
19
|
+
req.onsuccess = function () {
|
|
20
|
+
const record = this.result;
|
|
21
|
+
if (record) {
|
|
22
|
+
resolve(Ok(record.isUserKnown));
|
|
23
|
+
} else {
|
|
24
|
+
resolve(Ok(false));
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
req.onerror = () => {
|
|
28
|
+
logError(createRequestError('getIsUserKnown'));
|
|
29
|
+
// For reasoning for returning "true" see comment on the "db" instance absence above.
|
|
30
|
+
resolve(Ok(true));
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDb,
|
|
1
|
+
import { getDb, MAIN_STORE_NAME } from '../../init-db.js';
|
|
2
2
|
import createRequestError from '../../create-request-error.js';
|
|
3
3
|
import { logError } from '../../../logger/index.js';
|
|
4
4
|
/**
|
|
@@ -11,13 +11,13 @@ export default function updateGrantToken(grantToken) {
|
|
|
11
11
|
resolve();
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
-
const transaction = db.transaction(
|
|
14
|
+
const transaction = db.transaction(MAIN_STORE_NAME, 'readwrite');
|
|
15
15
|
transaction.oncomplete = () => resolve();
|
|
16
16
|
transaction.onerror = () => {
|
|
17
17
|
logError(createRequestError('updateGrantToken'));
|
|
18
18
|
resolve();
|
|
19
19
|
};
|
|
20
|
-
const objectStore = transaction.objectStore(
|
|
20
|
+
const objectStore = transaction.objectStore(MAIN_STORE_NAME);
|
|
21
21
|
objectStore.put({
|
|
22
22
|
entityType: 'grantToken',
|
|
23
23
|
grantToken
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getDb, PERSISTENT_STORE_NAME } from '../../init-db.js';
|
|
2
|
+
import createRequestError from '../../create-request-error.js';
|
|
3
|
+
import { logError } from '../../../logger/index.js';
|
|
4
|
+
import { Ok } from '../../../../types/result.js';
|
|
5
|
+
/**
|
|
6
|
+
* Update the value of flag `isUserKnown`.
|
|
7
|
+
*/
|
|
8
|
+
export default function updateIsUserKnown(value) {
|
|
9
|
+
return new Promise(resolve => {
|
|
10
|
+
const db = getDb();
|
|
11
|
+
if (!db) {
|
|
12
|
+
resolve(Ok(undefined));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const transaction = db.transaction(PERSISTENT_STORE_NAME, 'readwrite');
|
|
16
|
+
transaction.oncomplete = () => resolve(Ok(undefined));
|
|
17
|
+
transaction.onerror = () => {
|
|
18
|
+
logError(createRequestError('updateRefreshToken'));
|
|
19
|
+
resolve(Ok(undefined));
|
|
20
|
+
};
|
|
21
|
+
const objectStore = transaction.objectStore(PERSISTENT_STORE_NAME);
|
|
22
|
+
objectStore.put({
|
|
23
|
+
entityType: 'isUserKnown',
|
|
24
|
+
isUserKnown: value
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDb,
|
|
1
|
+
import { getDb, MAIN_STORE_NAME } from '../../init-db.js';
|
|
2
2
|
import createRequestError from '../../create-request-error.js';
|
|
3
3
|
import { logError } from '../../../logger/index.js';
|
|
4
4
|
/**
|
|
@@ -11,13 +11,13 @@ export default function updateRefreshToken(refreshToken) {
|
|
|
11
11
|
resolve();
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
-
const transaction = db.transaction(
|
|
14
|
+
const transaction = db.transaction(MAIN_STORE_NAME, 'readwrite');
|
|
15
15
|
transaction.oncomplete = () => resolve();
|
|
16
16
|
transaction.onerror = () => {
|
|
17
17
|
logError(createRequestError('updateRefreshToken'));
|
|
18
18
|
resolve();
|
|
19
19
|
};
|
|
20
|
-
const objectStore = transaction.objectStore(
|
|
20
|
+
const objectStore = transaction.objectStore(MAIN_STORE_NAME);
|
|
21
21
|
objectStore.put({
|
|
22
22
|
entityType: 'refreshToken',
|
|
23
23
|
refreshToken
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDb,
|
|
1
|
+
import { getDb, MAIN_STORE_NAME } from '../../init-db.js';
|
|
2
2
|
import createRequestError from '../../create-request-error.js';
|
|
3
3
|
import { logError } from '../../../logger/index.js';
|
|
4
4
|
/**
|
|
@@ -11,13 +11,13 @@ export default function updateUser(user) {
|
|
|
11
11
|
resolve();
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
-
const transaction = db.transaction(
|
|
14
|
+
const transaction = db.transaction(MAIN_STORE_NAME, 'readwrite');
|
|
15
15
|
transaction.oncomplete = () => resolve();
|
|
16
16
|
transaction.onerror = () => {
|
|
17
17
|
logError(createRequestError('updateUser'));
|
|
18
18
|
resolve();
|
|
19
19
|
};
|
|
20
|
-
const objectStore = transaction.objectStore(
|
|
20
|
+
const objectStore = transaction.objectStore(MAIN_STORE_NAME);
|
|
21
21
|
objectStore.put({
|
|
22
22
|
entityType: 'user',
|
|
23
23
|
user
|
|
@@ -4,3 +4,5 @@ export { default as deleteAllData } from './actions/delete-all-data/index.js';
|
|
|
4
4
|
export { default as updateUser } from './actions/update-user/index.js';
|
|
5
5
|
export { default as updateGrantToken } from './actions/update-grant-token/index.js';
|
|
6
6
|
export { default as updateRefreshToken } from './actions/update-refresh-token/index.js';
|
|
7
|
+
export { default as getIsUserKnown } from './actions/get-is-user-known/index.js';
|
|
8
|
+
export { default as updateIsUserKnown } from './actions/update-is-user-known/index.js';
|
|
@@ -3,4 +3,6 @@ export { default as getAllData } from './actions/get-all-data/index.js';
|
|
|
3
3
|
export { default as deleteAllData } from './actions/delete-all-data/index.js';
|
|
4
4
|
export { default as updateUser } from './actions/update-user/index.js';
|
|
5
5
|
export { default as updateGrantToken } from './actions/update-grant-token/index.js';
|
|
6
|
-
export { default as updateRefreshToken } from './actions/update-refresh-token/index.js';
|
|
6
|
+
export { default as updateRefreshToken } from './actions/update-refresh-token/index.js';
|
|
7
|
+
export { default as getIsUserKnown } from './actions/get-is-user-known/index.js';
|
|
8
|
+
export { default as updateIsUserKnown } from './actions/update-is-user-known/index.js';
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import WebError from '@memnrev/web-error';
|
|
2
2
|
const DB_NAME = 'mnr_topline';
|
|
3
|
-
const DB_VERSION =
|
|
4
|
-
|
|
3
|
+
const DB_VERSION = 2;
|
|
4
|
+
// Main store data is deleted when a user logs out.
|
|
5
|
+
export const MAIN_STORE_NAME = 'main';
|
|
6
|
+
// Persistent store is not cleaned up when a user logs out.
|
|
7
|
+
export const PERSISTENT_STORE_NAME = 'persistent';
|
|
5
8
|
let _db = null;
|
|
6
9
|
/**
|
|
7
10
|
* Get IndexedDb database object
|
|
@@ -33,14 +36,20 @@ export function initDb() {
|
|
|
33
36
|
resolve();
|
|
34
37
|
};
|
|
35
38
|
openDbReq.onupgradeneeded = function (e) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
db.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const db = this.result;
|
|
40
|
+
switch (e.oldVersion) {
|
|
41
|
+
case 0:
|
|
42
|
+
db.createObjectStore(MAIN_STORE_NAME, {
|
|
43
|
+
keyPath: 'entityType'
|
|
44
|
+
});
|
|
45
|
+
db.createObjectStore(PERSISTENT_STORE_NAME, {
|
|
46
|
+
keyPath: 'entityType'
|
|
47
|
+
});
|
|
48
|
+
break;
|
|
49
|
+
case 1:
|
|
50
|
+
db.createObjectStore(PERSISTENT_STORE_NAME, {
|
|
51
|
+
keyPath: 'entityType'
|
|
52
|
+
});
|
|
44
53
|
}
|
|
45
54
|
};
|
|
46
55
|
openDbReq.onblocked = function () {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type LogoutListener, type UserChangeListener, type SyncNotificationListener, ToplineEventName } from './types.js';
|
|
1
|
+
import { type LoginListener, type SignupListener, type LogoutListener, type UserChangeListener, type SyncNotificationListener, ToplineEventName } from './types.js';
|
|
2
2
|
export { ToplineEventName, InnerToplineEventName } from './types.js';
|
|
3
3
|
export { default as innerToplineService, type InnerService } from './inner-service.js';
|
|
4
4
|
/**
|
|
@@ -13,6 +13,8 @@ declare class _ToplineService {
|
|
|
13
13
|
/** Upgrade grant token with refresh token */
|
|
14
14
|
upgradeGrantToken(): Promise<string | null>;
|
|
15
15
|
/** Register an event listener */
|
|
16
|
+
on(eventName: ToplineEventName.Login, cb: LoginListener): this;
|
|
17
|
+
on(eventName: ToplineEventName.Signup, cb: SignupListener): this;
|
|
16
18
|
on(eventName: ToplineEventName.Logout, cb: LogoutListener): this;
|
|
17
19
|
on(eventName: ToplineEventName.UserChange, cb: UserChangeListener): this;
|
|
18
20
|
on(eventName: ToplineEventName.SyncNotification, cb: SyncNotificationListener): this;
|
|
@@ -17,6 +17,12 @@ class _ToplineService {
|
|
|
17
17
|
}
|
|
18
18
|
on(eventName, cb) {
|
|
19
19
|
switch (eventName) {
|
|
20
|
+
case ToplineEventName.Login:
|
|
21
|
+
this.#innerSrvs.on(eventName, cb);
|
|
22
|
+
break;
|
|
23
|
+
case ToplineEventName.Signup:
|
|
24
|
+
this.#innerSrvs.on(eventName, cb);
|
|
25
|
+
break;
|
|
20
26
|
case ToplineEventName.Logout:
|
|
21
27
|
this.#innerSrvs.on(eventName, cb);
|
|
22
28
|
break;
|
|
@@ -4,7 +4,7 @@ import { getApiBaseUrl } from '../env/index.js';
|
|
|
4
4
|
import request from '../request/index.js';
|
|
5
5
|
import { logError } from '../logger/index.js';
|
|
6
6
|
import type { ToplineUser } from '../../types/data.js';
|
|
7
|
-
import { type LogoutListener, type UserChangeListener, type SyncNotificationListener, type InnerListenerCallback, ToplineEventName, InnerToplineEventName } from './types.js';
|
|
7
|
+
import { type LoginListener, type SignupListener, type LogoutListener, type UserChangeListener, type SyncNotificationListener, type InnerListenerCallback, ToplineEventName, InnerToplineEventName } from './types.js';
|
|
8
8
|
export type Deps = {
|
|
9
9
|
getRefreshToken: typeof getRefreshToken;
|
|
10
10
|
setGrantToken: typeof setGrantToken;
|
|
@@ -21,13 +21,18 @@ export declare class InnerService {
|
|
|
21
21
|
/** Get grant token */
|
|
22
22
|
grantToken(): string | null;
|
|
23
23
|
/** Register an event listener */
|
|
24
|
+
on(eventName: ToplineEventName.Login, cb: LoginListener): void;
|
|
25
|
+
on(eventName: ToplineEventName.Signup, cb: SignupListener): void;
|
|
24
26
|
on(eventName: ToplineEventName.Logout, cb: LogoutListener): void;
|
|
25
27
|
on(eventName: ToplineEventName.UserChange, cb: UserChangeListener): void;
|
|
26
28
|
on(eventName: ToplineEventName.SyncNotification, cb: SyncNotificationListener): void;
|
|
27
29
|
/** Emit an outer event */
|
|
30
|
+
emit(eventName: ToplineEventName.Login, payload: ToplineUser): void;
|
|
31
|
+
emit(eventName: ToplineEventName.Signup, payload: ToplineUser): void;
|
|
28
32
|
emit(eventName: ToplineEventName.Logout): void;
|
|
29
33
|
emit(eventName: ToplineEventName.UserChange, payload: ToplineUser): void;
|
|
30
34
|
emit(eventName: ToplineEventName.SyncNotification, payload: string): void;
|
|
35
|
+
emit(eventName: ToplineEventName, payload?: ToplineUser | string): void;
|
|
31
36
|
/** Upgrade grant token with refresh token */
|
|
32
37
|
upgradeGrantToken(): Promise<string | null>;
|
|
33
38
|
/**
|
|
@@ -38,6 +38,18 @@ export class InnerService {
|
|
|
38
38
|
}
|
|
39
39
|
on(eventName, cb) {
|
|
40
40
|
switch (eventName) {
|
|
41
|
+
case ToplineEventName.Login:
|
|
42
|
+
this.#listeners.add({
|
|
43
|
+
eventName,
|
|
44
|
+
cb: cb
|
|
45
|
+
});
|
|
46
|
+
break;
|
|
47
|
+
case ToplineEventName.Signup:
|
|
48
|
+
this.#listeners.add({
|
|
49
|
+
eventName,
|
|
50
|
+
cb: cb
|
|
51
|
+
});
|
|
52
|
+
break;
|
|
41
53
|
case ToplineEventName.Logout:
|
|
42
54
|
this.#listeners.add({
|
|
43
55
|
eventName,
|
|
@@ -67,6 +79,12 @@ export class InnerService {
|
|
|
67
79
|
this.#listeners.forEach(listener => {
|
|
68
80
|
if (eventName === listener.eventName) {
|
|
69
81
|
switch (listener.eventName) {
|
|
82
|
+
case ToplineEventName.Login:
|
|
83
|
+
listener.cb.call(this, payload);
|
|
84
|
+
break;
|
|
85
|
+
case ToplineEventName.Signup:
|
|
86
|
+
listener.cb.call(this, payload);
|
|
87
|
+
break;
|
|
70
88
|
case ToplineEventName.Logout:
|
|
71
89
|
listener.cb.call(this);
|
|
72
90
|
break;
|
|
@@ -5,6 +5,10 @@ import type { ToplineUser } from '../../types/data.js';
|
|
|
5
5
|
* @public
|
|
6
6
|
*/
|
|
7
7
|
export declare enum ToplineEventName {
|
|
8
|
+
/** User has been logged in */
|
|
9
|
+
Login = "login",
|
|
10
|
+
/** User has been signed up */
|
|
11
|
+
Signup = "signup",
|
|
8
12
|
/** User has been logged out */
|
|
9
13
|
Logout = "logout",
|
|
10
14
|
/** User data changed */
|
|
@@ -12,10 +16,18 @@ export declare enum ToplineEventName {
|
|
|
12
16
|
/** A sync notification has been received */
|
|
13
17
|
SyncNotification = "syncNotification"
|
|
14
18
|
}
|
|
19
|
+
export type LoginListener = (user: ToplineUser) => unknown;
|
|
20
|
+
export type SignupListener = (user: ToplineUser) => unknown;
|
|
15
21
|
export type LogoutListener = () => unknown;
|
|
16
22
|
export type UserChangeListener = (user: ToplineUser) => unknown;
|
|
17
23
|
export type SyncNotificationListener = (syncId: string) => unknown;
|
|
18
24
|
export type ToplineEventListener = {
|
|
25
|
+
eventName: ToplineEventName.Login;
|
|
26
|
+
cb: LoginListener;
|
|
27
|
+
} | {
|
|
28
|
+
eventName: ToplineEventName.Signup;
|
|
29
|
+
cb: SignupListener;
|
|
30
|
+
} | {
|
|
19
31
|
eventName: ToplineEventName.Logout;
|
|
20
32
|
cb: LogoutListener;
|
|
21
33
|
} | {
|