@peassoft/mnr-web-topline 3.0.1 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +105 -18
  2. package/dist/en/modules/keyboard-navigation/key-codes.d.ts +16 -18
  3. package/dist/en/modules/keyboard-navigation/key-codes.js +17 -18
  4. package/dist/en/modules/keyboard-navigation/vertical-menu.d.ts +1 -1
  5. package/dist/en/modules/keyboard-navigation/vertical-menu.js +8 -8
  6. package/dist/en/modules/local-db/actions/delete-all-data/index.js +2 -2
  7. package/dist/en/modules/local-db/actions/get-all-data/index.js +2 -2
  8. package/dist/en/modules/local-db/actions/get-is-user-known/index.d.ts +5 -0
  9. package/dist/en/modules/local-db/actions/get-is-user-known/index.js +33 -0
  10. package/dist/en/modules/local-db/actions/update-grant-token/index.js +3 -3
  11. package/dist/en/modules/local-db/actions/update-is-user-known/index.d.ts +5 -0
  12. package/dist/en/modules/local-db/actions/update-is-user-known/index.js +27 -0
  13. package/dist/en/modules/local-db/actions/update-refresh-token/index.js +3 -3
  14. package/dist/en/modules/local-db/actions/update-user/index.js +3 -3
  15. package/dist/en/modules/local-db/index.d.ts +2 -0
  16. package/dist/en/modules/local-db/index.js +3 -1
  17. package/dist/en/modules/local-db/init-db.d.ts +2 -1
  18. package/dist/en/modules/local-db/init-db.js +19 -10
  19. package/dist/en/modules/topline-service/index.d.ts +3 -1
  20. package/dist/en/modules/topline-service/index.js +6 -0
  21. package/dist/en/modules/topline-service/inner-service.d.ts +6 -1
  22. package/dist/en/modules/topline-service/inner-service.js +18 -0
  23. package/dist/en/modules/topline-service/types.d.ts +12 -0
  24. package/dist/en/modules/topline-service/types.js +4 -0
  25. package/dist/en/parts/login/actions/perform-login/index.d.ts +1 -15
  26. package/dist/en/parts/login/actions/perform-login/index.js +8 -17
  27. package/dist/en/parts/password-recovery/actions/save-password/index.d.ts +1 -15
  28. package/dist/en/parts/password-recovery/actions/save-password/index.js +8 -17
  29. package/dist/en/parts/shell/context.d.ts +1 -1
  30. package/dist/en/parts/shell/ui/logged-out-user-menu/index.js +2 -0
  31. package/dist/en/parts/shell/ui/loggeg-in-user-menu/index.js +2 -0
  32. package/dist/en/parts/shell/ui/shell/index.js +7 -3
  33. package/dist/en/parts/shell/ui/user-menu-item/index.d.ts +1 -0
  34. package/dist/en/parts/shell/ui/user-menu-item/index.js +2 -0
  35. package/dist/en/parts/signup/actions/perform-signup/index.d.ts +1 -15
  36. package/dist/en/parts/signup/actions/perform-signup/index.js +8 -17
  37. package/dist/en/shared/components/modal/index.js +2 -2
  38. package/dist/en/shared/procedures/process-response.d.ts +13 -0
  39. package/dist/en/shared/procedures/process-response.js +50 -0
  40. package/dist/en/topline.d.ts +1 -1
  41. package/dist/en/types/result.d.ts +9 -0
  42. package/dist/en/types/result.js +12 -0
  43. package/dist/ru/modules/keyboard-navigation/key-codes.d.ts +16 -18
  44. package/dist/ru/modules/keyboard-navigation/key-codes.js +17 -18
  45. package/dist/ru/modules/keyboard-navigation/vertical-menu.d.ts +1 -1
  46. package/dist/ru/modules/keyboard-navigation/vertical-menu.js +8 -8
  47. package/dist/ru/modules/local-db/actions/delete-all-data/index.js +2 -2
  48. package/dist/ru/modules/local-db/actions/get-all-data/index.js +2 -2
  49. package/dist/ru/modules/local-db/actions/get-is-user-known/index.d.ts +5 -0
  50. package/dist/ru/modules/local-db/actions/get-is-user-known/index.js +33 -0
  51. package/dist/ru/modules/local-db/actions/update-grant-token/index.js +3 -3
  52. package/dist/ru/modules/local-db/actions/update-is-user-known/index.d.ts +5 -0
  53. package/dist/ru/modules/local-db/actions/update-is-user-known/index.js +27 -0
  54. package/dist/ru/modules/local-db/actions/update-refresh-token/index.js +3 -3
  55. package/dist/ru/modules/local-db/actions/update-user/index.js +3 -3
  56. package/dist/ru/modules/local-db/index.d.ts +2 -0
  57. package/dist/ru/modules/local-db/index.js +3 -1
  58. package/dist/ru/modules/local-db/init-db.d.ts +2 -1
  59. package/dist/ru/modules/local-db/init-db.js +19 -10
  60. package/dist/ru/modules/topline-service/index.d.ts +3 -1
  61. package/dist/ru/modules/topline-service/index.js +6 -0
  62. package/dist/ru/modules/topline-service/inner-service.d.ts +6 -1
  63. package/dist/ru/modules/topline-service/inner-service.js +18 -0
  64. package/dist/ru/modules/topline-service/types.d.ts +12 -0
  65. package/dist/ru/modules/topline-service/types.js +4 -0
  66. package/dist/ru/parts/login/actions/perform-login/index.d.ts +1 -15
  67. package/dist/ru/parts/login/actions/perform-login/index.js +8 -17
  68. package/dist/ru/parts/password-recovery/actions/save-password/index.d.ts +1 -15
  69. package/dist/ru/parts/password-recovery/actions/save-password/index.js +8 -17
  70. package/dist/ru/parts/shell/context.d.ts +1 -1
  71. package/dist/ru/parts/shell/ui/logged-out-user-menu/index.js +2 -0
  72. package/dist/ru/parts/shell/ui/loggeg-in-user-menu/index.js +2 -0
  73. package/dist/ru/parts/shell/ui/shell/index.js +7 -3
  74. package/dist/ru/parts/shell/ui/user-menu-item/index.d.ts +1 -0
  75. package/dist/ru/parts/shell/ui/user-menu-item/index.js +2 -0
  76. package/dist/ru/parts/signup/actions/perform-signup/index.d.ts +1 -15
  77. package/dist/ru/parts/signup/actions/perform-signup/index.js +8 -17
  78. package/dist/ru/shared/components/modal/index.js +2 -2
  79. package/dist/ru/shared/procedures/process-response.d.ts +13 -0
  80. package/dist/ru/shared/procedures/process-response.js +50 -0
  81. package/dist/ru/topline.d.ts +1 -1
  82. package/dist/ru/types/result.d.ts +9 -0
  83. package/dist/ru/types/result.js +12 -0
  84. package/package.json +9 -3
  85. package/dist/en/shared/procedures/process-successful-response/index.d.ts +0 -30
  86. package/dist/en/shared/procedures/process-successful-response/index.js +0 -66
  87. package/dist/ru/shared/procedures/process-successful-response/index.d.ts +0 -30
  88. package/dist/ru/shared/procedures/process-successful-response/index.js +0 -66
@@ -1,7 +1,3 @@
1
- import stampit from 'stampit';
2
- import { getApiBaseUrl, getRequestCredentialsMode } from '../../../../modules/env/index.js';
3
- import request from '../../../../modules/request/index.js';
4
- import processSuccessfulResponse from '../../../../shared/procedures/process-successful-response/index.js';
5
1
  import type { ToplineUser } from '../../../../types/data.js';
6
2
  export type ReturnValue = {
7
3
  user: ToplineUser | null;
@@ -14,14 +10,4 @@ export type ReturnValue = {
14
10
  * - on network error or if backend responds with 5xx - "network";
15
11
  * - on login failure - "incorrect-credentials".
16
12
  */
17
- declare function performLogin(this: This, email: string, password: string): Promise<ReturnValue>;
18
- export type This = {
19
- getApiBaseUrl: typeof getApiBaseUrl;
20
- getRequestCredentialsMode: typeof getRequestCredentialsMode;
21
- request: typeof request;
22
- processSuccessfulResponse: typeof processSuccessfulResponse;
23
- performLogin: typeof performLogin;
24
- };
25
- export declare const PerformLogin: stampit.Stamp<This>;
26
- declare const _default: (email: string, password: string) => Promise<ReturnValue>;
27
- export default _default;
13
+ export default function performLogin(email: string, password: string): Promise<ReturnValue>;
@@ -1,7 +1,7 @@
1
- import stampit from 'stampit';
2
1
  import { getApiBaseUrl, getRequestCredentialsMode } from '../../../../modules/env/index.js';
3
2
  import request from '../../../../modules/request/index.js';
4
- import processSuccessfulResponse from '../../../../shared/procedures/process-successful-response/index.js';
3
+ import processResponse from '../../../../shared/procedures/process-response.js';
4
+ import { ToplineEventName } from '../../../../modules/topline-service/index.js';
5
5
  /**
6
6
  * Perform logging-in
7
7
  *
@@ -9,8 +9,8 @@ import processSuccessfulResponse from '../../../../shared/procedures/process-suc
9
9
  * - on network error or if backend responds with 5xx - "network";
10
10
  * - on login failure - "incorrect-credentials".
11
11
  */
12
- async function performLogin(email, password) {
13
- const url = `${this.getApiBaseUrl()}/public/login-with-credentials`;
12
+ export default async function performLogin(email, password) {
13
+ const url = `${getApiBaseUrl()}/public/login-with-credentials`;
14
14
  const options = {
15
15
  method: 'POST',
16
16
  headers: {
@@ -20,11 +20,11 @@ async function performLogin(email, password) {
20
20
  email,
21
21
  password
22
22
  }),
23
- credentials: this.getRequestCredentialsMode(),
23
+ credentials: getRequestCredentialsMode(),
24
24
  cache: 'no-store',
25
25
  redirect: 'error'
26
26
  };
27
- const response = await this.request(url, options, {
27
+ const response = await request(url, options, {
28
28
  maxRetries: 1
29
29
  });
30
30
  if (!response) {
@@ -34,7 +34,7 @@ async function performLogin(email, password) {
34
34
  };
35
35
  }
36
36
  if (response.status === 200) {
37
- return await this.processSuccessfulResponse(response, url);
37
+ return await processResponse(response, url, ToplineEventName.Login);
38
38
  }
39
39
  if (response.status === 401) {
40
40
  return {
@@ -48,13 +48,4 @@ async function performLogin(email, password) {
48
48
  user: null,
49
49
  error: 'network'
50
50
  };
51
- }
52
- export const PerformLogin = stampit().props({
53
- getApiBaseUrl,
54
- getRequestCredentialsMode,
55
- request,
56
- processSuccessfulResponse
57
- }).methods({
58
- performLogin
59
- });
60
- export default performLogin.bind(PerformLogin());
51
+ }
@@ -1,7 +1,3 @@
1
- import stampit from 'stampit';
2
- import { getApiBaseUrl, getRequestCredentialsMode } from '../../../../modules/env/index.js';
3
- import request from '../../../../modules/request/index.js';
4
- import processSuccessfulResponse from '../../../../shared/procedures/process-successful-response/index.js';
5
1
  import type { ToplineUser } from '../../../../types/data.js';
6
2
  export type ReturnValue = {
7
3
  user: ToplineUser | null;
@@ -14,14 +10,4 @@ export type ReturnValue = {
14
10
  * - on network error or if backend responds with 5xx - "network";
15
11
  * - on 403 Forbidden response - "no-claim".
16
12
  */
17
- declare function savePassword(this: This, email: string, password: string, secretCode: string): Promise<ReturnValue>;
18
- export type This = {
19
- getApiBaseUrl: typeof getApiBaseUrl;
20
- getRequestCredentialsMode: typeof getRequestCredentialsMode;
21
- request: typeof request;
22
- processSuccessfulResponse: typeof processSuccessfulResponse;
23
- savePassword: typeof savePassword;
24
- };
25
- export declare const SavePassword: stampit.Stamp<This>;
26
- declare const _default: (email: string, password: string, secretCode: string) => Promise<ReturnValue>;
27
- export default _default;
13
+ export default function savePassword(email: string, password: string, secretCode: string): Promise<ReturnValue>;
@@ -1,7 +1,7 @@
1
- import stampit from 'stampit';
2
1
  import { getApiBaseUrl, getRequestCredentialsMode } from '../../../../modules/env/index.js';
3
2
  import request from '../../../../modules/request/index.js';
4
- import processSuccessfulResponse from '../../../../shared/procedures/process-successful-response/index.js';
3
+ import processResponse from '../../../../shared/procedures/process-response.js';
4
+ import { ToplineEventName } from '../../../../modules/topline-service/index.js';
5
5
  /**
6
6
  * Save new password
7
7
  *
@@ -9,8 +9,8 @@ import processSuccessfulResponse from '../../../../shared/procedures/process-suc
9
9
  * - on network error or if backend responds with 5xx - "network";
10
10
  * - on 403 Forbidden response - "no-claim".
11
11
  */
12
- async function savePassword(email, password, secretCode) {
13
- const url = `${this.getApiBaseUrl()}/public/create-new-password`;
12
+ export default async function savePassword(email, password, secretCode) {
13
+ const url = `${getApiBaseUrl()}/public/create-new-password`;
14
14
  const options = {
15
15
  method: 'POST',
16
16
  headers: {
@@ -21,11 +21,11 @@ async function savePassword(email, password, secretCode) {
21
21
  password,
22
22
  secretCode
23
23
  }),
24
- credentials: this.getRequestCredentialsMode(),
24
+ credentials: getRequestCredentialsMode(),
25
25
  cache: 'no-store',
26
26
  redirect: 'error'
27
27
  };
28
- const response = await this.request(url, options, {
28
+ const response = await request(url, options, {
29
29
  expectedStatusCodes: [200, 403]
30
30
  });
31
31
  if (!response) {
@@ -35,7 +35,7 @@ async function savePassword(email, password, secretCode) {
35
35
  };
36
36
  }
37
37
  if (response.status === 200) {
38
- return await this.processSuccessfulResponse(response, url);
38
+ return await processResponse(response, url, ToplineEventName.Login);
39
39
  }
40
40
  if (response.status === 403) {
41
41
  return {
@@ -49,13 +49,4 @@ async function savePassword(email, password, secretCode) {
49
49
  user: null,
50
50
  error: 'network'
51
51
  };
52
- }
53
- export const SavePassword = stampit().props({
54
- getApiBaseUrl,
55
- getRequestCredentialsMode,
56
- request,
57
- processSuccessfulResponse
58
- }).methods({
59
- savePassword
60
- });
61
- export default savePassword.bind(SavePassword());
52
+ }
@@ -6,7 +6,7 @@ export type InnerToplineContext = {
6
6
  forceBlankHomeLink: boolean;
7
7
  user: ToplineUser | null;
8
8
  handleUserChange: (user: ToplineUser | null) => unknown;
9
- onReady: (toplineService: ToplineService, user: ToplineUser | null) => unknown;
9
+ onReady: (toplineService: ToplineService, user: ToplineUser | null, isUserKnown: boolean) => unknown;
10
10
  onError: (err: Error) => unknown;
11
11
  };
12
12
  declare const _default: React.Context<InnerToplineContext>;
@@ -29,12 +29,14 @@ export default function LoggedOutUserMenu(props) {
29
29
  label: "Log in" + '...',
30
30
  itemRef: menuItemRefs[0],
31
31
  tabIndex: currItemIdx === 0 ? 0 : -1,
32
+ testId: 'login_item',
32
33
  onKeyDown: handleKeyDown.bind(null, 'login'),
33
34
  onClick: handleSelect.bind(null, 'login')
34
35
  }), _jsx(UserMenuItem, {
35
36
  label: "Create account" + '...',
36
37
  itemRef: menuItemRefs[1],
37
38
  tabIndex: currItemIdx === 1 ? 0 : -1,
39
+ testId: 'signup_item',
38
40
  onKeyDown: handleKeyDown.bind(null, 'signup'),
39
41
  onClick: handleSelect.bind(null, 'signup')
40
42
  })]
@@ -29,12 +29,14 @@ export default function LoggedInUserMenu(props) {
29
29
  label: "My profile" + '...',
30
30
  itemRef: menuItemRefs[0],
31
31
  tabIndex: currItemIdx === 0 ? 0 : -1,
32
+ testId: 'profile_item',
32
33
  onKeyDown: handleKeyDown.bind(null, 'profile'),
33
34
  onClick: handleSelect.bind(null, 'profile')
34
35
  }), _jsx(UserMenuItem, {
35
36
  label: "Log out" + '',
36
37
  itemRef: menuItemRefs[1],
37
38
  tabIndex: currItemIdx === 1 ? 0 : -1,
39
+ testId: 'logout_item',
38
40
  onKeyDown: handleKeyDown.bind(null, 'logout'),
39
41
  onClick: handleSelect.bind(null, 'logout')
40
42
  })]
@@ -11,7 +11,7 @@ import { PasswordRecovery } from '../../../password-recovery/index.js';
11
11
  import { Profile } from '../../../profile/index.js';
12
12
  import ToplineContext from '../../context.js';
13
13
  import { globalRefs } from '../../../../modules/focus-marshal/index.js';
14
- import { initDb, getAllData } from '../../../../modules/local-db/index.js';
14
+ import { initDb, getAllData, getIsUserKnown } from '../../../../modules/local-db/index.js';
15
15
  import { logError } from '../../../../modules/logger/index.js';
16
16
  import { setGrantToken, setRefreshToken } from '../../../../modules/tokens/index.js';
17
17
  import { toplineService, innerToplineService, ToplineEventName, InnerToplineEventName } from '../../../../modules/topline-service/index.js';
@@ -70,6 +70,8 @@ export default function Shell() {
70
70
  const handleMoveToLogin = useCallback(() => setModalWindowOpen('login'), []);
71
71
  useEffect(() => {
72
72
  async function initData() {
73
+ innerToplineService.on(ToplineEventName.Login, handleUserChange);
74
+ innerToplineService.on(ToplineEventName.Signup, handleUserChange);
73
75
  innerToplineService.on(ToplineEventName.UserChange, handleUserChange);
74
76
  innerToplineService.on(ToplineEventName.Logout, () => handleUserChange(null));
75
77
  try {
@@ -78,7 +80,7 @@ export default function Shell() {
78
80
  if (e instanceof Error) {
79
81
  logError(e);
80
82
  }
81
- onReady(toplineService, null);
83
+ onReady(toplineService, null, true);
82
84
  return;
83
85
  }
84
86
  const {
@@ -89,7 +91,9 @@ export default function Shell() {
89
91
  handleUserChange(user);
90
92
  setGrantToken(grantToken);
91
93
  setRefreshToken(refreshToken);
92
- onReady(toplineService, user);
94
+ const isUserKnownResult = await getIsUserKnown();
95
+ const isUserKnown = isUserKnownResult.ok ? isUserKnownResult.value : true;
96
+ onReady(toplineService, user, isUserKnown);
93
97
  if (user) {
94
98
  await refreshUser(user);
95
99
  openWebsocket();
@@ -4,6 +4,7 @@ type Props = {
4
4
  itemRef: React.RefObject<HTMLDivElement | null>;
5
5
  tabIndex: number;
6
6
  onKeyDown: (e: React.KeyboardEvent) => unknown;
7
+ testId?: string;
7
8
  onClick: () => unknown;
8
9
  };
9
10
  export default function UserMenuItem(props: Props): JSX.Element;
@@ -4,6 +4,7 @@ export default function UserMenuItem(props) {
4
4
  label,
5
5
  itemRef,
6
6
  tabIndex,
7
+ testId,
7
8
  onKeyDown,
8
9
  onClick
9
10
  } = props;
@@ -12,6 +13,7 @@ export default function UserMenuItem(props) {
12
13
  className: 'topline_shell_userMenuItem',
13
14
  role: 'menuitem',
14
15
  tabIndex: tabIndex,
16
+ "data-testid": testId || undefined,
15
17
  onClick: onClick,
16
18
  onKeyDown: onKeyDown,
17
19
  children: label
@@ -1,7 +1,3 @@
1
- import stampit from 'stampit';
2
- import { getApiBaseUrl, getRequestCredentialsMode } from '../../../../modules/env/index.js';
3
- import request from '../../../../modules/request/index.js';
4
- import processSuccessfulResponse from '../../../../shared/procedures/process-successful-response/index.js';
5
1
  import type { ToplineUser } from '../../../../types/data.js';
6
2
  export type UserData = {
7
3
  email: string;
@@ -20,14 +16,4 @@ export type ReturnValue = {
20
16
  * - on network error or if backend responds with 5xx - "network";
21
17
  * - on 409 Conflict response - "user-exists".
22
18
  */
23
- declare function performSignup(this: This, userData: UserData): Promise<ReturnValue>;
24
- export type This = {
25
- getApiBaseUrl: typeof getApiBaseUrl;
26
- getRequestCredentialsMode: typeof getRequestCredentialsMode;
27
- request: typeof request;
28
- processSuccessfulResponse: typeof processSuccessfulResponse;
29
- performSignup: typeof performSignup;
30
- };
31
- export declare const PerformSignup: stampit.Stamp<This>;
32
- declare const _default: (userData: UserData) => Promise<ReturnValue>;
33
- export default _default;
19
+ export default function performSignup(userData: UserData): Promise<ReturnValue>;
@@ -1,7 +1,7 @@
1
- import stampit from 'stampit';
2
1
  import { getApiBaseUrl, getRequestCredentialsMode } from '../../../../modules/env/index.js';
3
2
  import request from '../../../../modules/request/index.js';
4
- import processSuccessfulResponse from '../../../../shared/procedures/process-successful-response/index.js';
3
+ import processResponse from '../../../../shared/procedures/process-response.js';
4
+ import { ToplineEventName } from '../../../../modules/topline-service/index.js';
5
5
  /**
6
6
  * Perform signing-up
7
7
  *
@@ -9,19 +9,19 @@ import processSuccessfulResponse from '../../../../shared/procedures/process-suc
9
9
  * - on network error or if backend responds with 5xx - "network";
10
10
  * - on 409 Conflict response - "user-exists".
11
11
  */
12
- async function performSignup(userData) {
13
- const url = `${this.getApiBaseUrl()}/public/signup`;
12
+ export default async function performSignup(userData) {
13
+ const url = `${getApiBaseUrl()}/public/signup`;
14
14
  const options = {
15
15
  method: 'POST',
16
16
  headers: {
17
17
  'Content-Type': 'application/json; charset=utf-8'
18
18
  },
19
19
  body: JSON.stringify(userData),
20
- credentials: this.getRequestCredentialsMode(),
20
+ credentials: getRequestCredentialsMode(),
21
21
  cache: 'no-store',
22
22
  redirect: 'error'
23
23
  };
24
- const response = await this.request(url, options, {
24
+ const response = await request(url, options, {
25
25
  expectedStatusCodes: [200, 409],
26
26
  maxRetries: 1
27
27
  });
@@ -32,7 +32,7 @@ async function performSignup(userData) {
32
32
  };
33
33
  }
34
34
  if (response.status === 200) {
35
- return await this.processSuccessfulResponse(response, url);
35
+ return await processResponse(response, url, ToplineEventName.Signup);
36
36
  }
37
37
  if (response.status === 409) {
38
38
  return {
@@ -46,13 +46,4 @@ async function performSignup(userData) {
46
46
  user: null,
47
47
  error: 'network'
48
48
  };
49
- }
50
- export const PerformSignup = stampit().props({
51
- getApiBaseUrl,
52
- getRequestCredentialsMode,
53
- request,
54
- processSuccessfulResponse
55
- }).methods({
56
- performSignup
57
- });
58
- export default performSignup.bind(PerformSignup());
49
+ }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect } from 'react';
3
- import keyCodes from '../../../modules/keyboard-navigation/key-codes.js';
3
+ import { Key } from '../../../modules/keyboard-navigation/key-codes.js';
4
4
  export default function Modal(props) {
5
5
  const {
6
6
  title,
@@ -8,7 +8,7 @@ export default function Modal(props) {
8
8
  } = props;
9
9
  useEffect(() => {
10
10
  const handleKeyDown = e => {
11
- if (e.which === keyCodes.ESC) {
11
+ if (e.key === Key.Escape) {
12
12
  onClose();
13
13
  }
14
14
  };
@@ -0,0 +1,13 @@
1
+ import { ToplineEventName } from '../../modules/topline-service/index.js';
2
+ import type { CustomResponse } from '../../modules/request/index.js';
3
+ import { type ToplineUser } from '../../types/data.js';
4
+ type EventName = ToplineEventName.Login | ToplineEventName.Signup;
5
+ type ReturnValue = {
6
+ user: ToplineUser | null;
7
+ error: 'network' | null;
8
+ };
9
+ /**
10
+ * Process successful response on a request that implies user has to be logged-in
11
+ */
12
+ export default function processResponse(response: CustomResponse, url: string, eventName: EventName): Promise<ReturnValue>;
13
+ export {};
@@ -0,0 +1,50 @@
1
+ import WebError from '@memnrev/web-error';
2
+ import { updateUser, updateGrantToken, updateRefreshToken, updateIsUserKnown } from '../../modules/local-db/index.js';
3
+ import { setGrantToken, setRefreshToken } from '../../modules/tokens/index.js';
4
+ import { openWebsocket } from '../../modules/websocket/index.js';
5
+ import { innerToplineService, ToplineEventName } from '../../modules/topline-service/index.js';
6
+ import { logError } from '../../modules/logger/index.js';
7
+ import { isToplineUser } from '../../types/data.js';
8
+ import { isObject } from '../../types/helpers.js';
9
+ /**
10
+ * Process successful response on a request that implies user has to be logged-in
11
+ */
12
+ export default async function processResponse(response, url, eventName) {
13
+ try {
14
+ const responseBody = await response.json();
15
+ if (isObject(responseBody) && 'user' in responseBody && isToplineUser(responseBody.user) && 'grantToken' in responseBody && typeof responseBody.grantToken === 'string' && 'refreshToken' in responseBody && typeof responseBody.refreshToken === 'string') {
16
+ const {
17
+ user,
18
+ grantToken,
19
+ refreshToken
20
+ } = responseBody;
21
+ await Promise.all([updateUser(user), updateGrantToken(grantToken), updateRefreshToken(refreshToken), updateIsUserKnown(true)]);
22
+ setGrantToken(grantToken);
23
+ setRefreshToken(refreshToken);
24
+ openWebsocket();
25
+ innerToplineService.emit(eventName, user);
26
+ return {
27
+ user,
28
+ error: null
29
+ };
30
+ }
31
+ logError(new WebError({
32
+ name: 'UnexpectedError'
33
+ }, `unexpected payload of response to request to ${url}`));
34
+ return {
35
+ user: null,
36
+ error: 'network'
37
+ };
38
+ } catch (e) {
39
+ logError(new WebError({
40
+ name: 'UnexpectedError',
41
+ ...(e instanceof Error ? {
42
+ cause: e
43
+ } : {})
44
+ }, `parsing 200 response of request to ${url} failed`));
45
+ return {
46
+ user: null,
47
+ error: 'network'
48
+ };
49
+ }
50
+ }
@@ -13,7 +13,7 @@ export type ToplineProps = {
13
13
  */
14
14
  forceBlankHomeLink?: boolean;
15
15
  /** Callback that is invoked after user is restored from a local DB */
16
- onReady?: (toplineService: ToplineService, user: ToplineUser | null) => unknown;
16
+ onReady?: (toplineService: ToplineService, user: ToplineUser | null, isUserKnown: boolean) => unknown;
17
17
  /**
18
18
  * Callback that is invoked if an unexpected error is caught by React's error boundary
19
19
  * component
@@ -0,0 +1,9 @@
1
+ export type Result<T, E> = {
2
+ ok: true;
3
+ value: T;
4
+ } | {
5
+ ok: false;
6
+ error: E;
7
+ };
8
+ export declare const Ok: <T>(data: T) => Result<T, never>;
9
+ export declare const Err: <E>(error: E) => Result<never, E>;
@@ -0,0 +1,12 @@
1
+ export const Ok = data => {
2
+ return {
3
+ ok: true,
4
+ value: data
5
+ };
6
+ };
7
+ export const Err = error => {
8
+ return {
9
+ ok: false,
10
+ error
11
+ };
12
+ };
@@ -1,18 +1,16 @@
1
- /**
2
- * Keyboard key codes
3
- */
4
- declare const keyCodes: Readonly<{
5
- TAB: 9;
6
- RETURN: 13;
7
- ESC: 27;
8
- SPACE: 32;
9
- PAGEUP: 33;
10
- PAGEDOWN: 34;
11
- END: 35;
12
- HOME: 36;
13
- LEFT: 37;
14
- UP: 38;
15
- RIGHT: 39;
16
- DOWN: 40;
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
- * Keyboard key codes
3
- */
4
- const keyCodes = Object.freeze({
5
- TAB: 9,
6
- RETURN: 13,
7
- ESC: 27,
8
- SPACE: 32,
9
- PAGEUP: 33,
10
- PAGEDOWN: 34,
11
- END: 35,
12
- HOME: 36,
13
- LEFT: 37,
14
- UP: 38,
15
- RIGHT: 39,
16
- DOWN: 40
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, 'which' | 'preventDefault' | 'stopPropagation'>;
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 keyCodes from './key-codes.js';
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.which) {
18
- case keyCodes.DOWN:
17
+ switch (e.key) {
18
+ case Key.Down:
19
19
  nextIndex = currIndex === itemsNum - 1 ? 0 : currIndex + 1;
20
20
  break;
21
- case keyCodes.UP:
21
+ case Key.Up:
22
22
  nextIndex = currIndex === 0 ? itemsNum - 1 : currIndex - 1;
23
23
  break;
24
- case keyCodes.HOME:
24
+ case Key.Home:
25
25
  nextIndex = 0;
26
26
  break;
27
- case keyCodes.END:
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.which === keyCodes.RETURN) {
37
+ if (e.key === Key.Enter) {
38
38
  onSelect(actionName);
39
39
  return;
40
40
  }
41
- if (e.which === keyCodes.ESC) {
41
+ if (e.key === Key.Escape) {
42
42
  onClose();
43
43
  return;
44
44
  }
@@ -1,4 +1,4 @@
1
- import { getDb, STORE_NAME } from '../../init-db.js';
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(STORE_NAME, 'readwrite').objectStore(STORE_NAME).clear();
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, STORE_NAME } from '../../init-db.js';
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(STORE_NAME).objectStore(STORE_NAME).getAll();
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,5 @@
1
+ import { type Result } from '../../../../types/result.js';
2
+ /**
3
+ * Retrieve the value of flag `isUserKnown`.
4
+ */
5
+ export default function getIsUserKnown(): Promise<Result<boolean, never>>;