underpost 3.2.5 → 3.2.8

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 (138) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/CHANGELOG.md +251 -1
  3. package/CLI-HELP.md +26 -13
  4. package/Dockerfile +0 -4
  5. package/README.md +3 -3
  6. package/bin/build.js +13 -3
  7. package/bin/deploy.js +570 -1
  8. package/bin/file.js +5 -0
  9. package/conf.js +11 -2
  10. package/jsconfig.json +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  12. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  13. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  14. package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
  15. package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
  16. package/package.json +20 -11
  17. package/src/api/core/core.controller.js +10 -10
  18. package/src/api/core/core.service.js +10 -10
  19. package/src/api/default/default.controller.js +10 -10
  20. package/src/api/default/default.service.js +10 -10
  21. package/src/api/document/document.controller.js +12 -12
  22. package/src/api/document/document.model.js +10 -16
  23. package/src/api/file/file.controller.js +8 -8
  24. package/src/api/file/file.model.js +10 -10
  25. package/src/api/file/file.service.js +36 -36
  26. package/src/api/test/test.controller.js +8 -8
  27. package/src/api/test/test.service.js +8 -8
  28. package/src/api/user/guest.service.js +99 -0
  29. package/src/api/user/user.controller.js +6 -6
  30. package/src/api/user/user.model.js +8 -13
  31. package/src/api/user/user.service.js +3 -20
  32. package/src/cli/deploy.js +33 -30
  33. package/src/cli/fs.js +62 -5
  34. package/src/cli/image.js +43 -1
  35. package/src/cli/index.js +5 -1
  36. package/src/cli/release.js +57 -1
  37. package/src/cli/repository.js +35 -3
  38. package/src/cli/run.js +300 -35
  39. package/src/cli/ssh.js +1 -1
  40. package/src/cli/static.js +43 -115
  41. package/src/client/Default.index.js +21 -33
  42. package/src/client/components/core/404.js +4 -4
  43. package/src/client/components/core/500.js +4 -4
  44. package/src/client/components/core/Account.js +73 -60
  45. package/src/client/components/core/AgGrid.js +23 -33
  46. package/src/client/components/core/Alert.js +12 -13
  47. package/src/client/components/core/AppStore.js +1 -1
  48. package/src/client/components/core/Auth.js +20 -32
  49. package/src/client/components/core/Badge.js +7 -13
  50. package/src/client/components/core/BtnIcon.js +15 -17
  51. package/src/client/components/core/CalendarCore.js +42 -63
  52. package/src/client/components/core/Chat.js +13 -15
  53. package/src/client/components/core/ClientEvents.js +87 -0
  54. package/src/client/components/core/ColorPaletteElement.js +309 -0
  55. package/src/client/components/core/Content.js +17 -14
  56. package/src/client/components/core/Css.js +15 -71
  57. package/src/client/components/core/CssCore.js +12 -16
  58. package/src/client/components/core/D3Chart.js +4 -4
  59. package/src/client/components/core/Docs.js +60 -59
  60. package/src/client/components/core/DropDown.js +69 -91
  61. package/src/client/components/core/EventBus.js +92 -0
  62. package/src/client/components/core/EventsUI.js +14 -17
  63. package/src/client/components/core/FileExplorer.js +102 -234
  64. package/src/client/components/core/FullScreen.js +47 -75
  65. package/src/client/components/core/Input.js +24 -69
  66. package/src/client/components/core/Keyboard.js +25 -18
  67. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  68. package/src/client/components/core/LoadingAnimation.js +25 -31
  69. package/src/client/components/core/LogIn.js +41 -41
  70. package/src/client/components/core/LogOut.js +23 -14
  71. package/src/client/components/core/Modal.js +397 -176
  72. package/src/client/components/core/NotificationManager.js +14 -18
  73. package/src/client/components/core/Panel.js +54 -50
  74. package/src/client/components/core/PanelForm.js +25 -125
  75. package/src/client/components/core/Polyhedron.js +110 -214
  76. package/src/client/components/core/PublicProfile.js +39 -32
  77. package/src/client/components/core/Recover.js +52 -48
  78. package/src/client/components/core/Responsive.js +88 -32
  79. package/src/client/components/core/RichText.js +9 -18
  80. package/src/client/components/core/Router.js +24 -3
  81. package/src/client/components/core/SearchBox.js +37 -37
  82. package/src/client/components/core/SignUp.js +39 -30
  83. package/src/client/components/core/SocketIo.js +31 -2
  84. package/src/client/components/core/SocketIoHandler.js +6 -6
  85. package/src/client/components/core/ToggleSwitch.js +8 -20
  86. package/src/client/components/core/ToolTip.js +5 -17
  87. package/src/client/components/core/Translate.js +56 -59
  88. package/src/client/components/core/Validator.js +26 -16
  89. package/src/client/components/core/Wallet.js +15 -26
  90. package/src/client/components/core/Worker.js +140 -25
  91. package/src/client/components/core/windowGetDimensions.js +7 -7
  92. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  93. package/src/client/components/default/CssDefault.js +12 -12
  94. package/src/client/components/default/LogInDefault.js +6 -4
  95. package/src/client/components/default/LogOutDefault.js +6 -4
  96. package/src/client/components/default/RouterDefault.js +47 -0
  97. package/src/client/components/default/SettingsDefault.js +4 -4
  98. package/src/client/components/default/SignUpDefault.js +6 -4
  99. package/src/client/components/default/TranslateDefault.js +3 -3
  100. package/src/client/services/core/core.service.js +17 -49
  101. package/src/client/services/default/default.management.js +139 -242
  102. package/src/client/services/default/default.service.js +10 -16
  103. package/src/client/services/document/document.service.js +14 -19
  104. package/src/client/services/file/file.service.js +8 -13
  105. package/src/client/services/test/test.service.js +8 -13
  106. package/src/client/services/user/guest.service.js +79 -0
  107. package/src/client/services/user/user.management.js +5 -5
  108. package/src/client/services/user/user.service.js +14 -20
  109. package/src/client/ssr/body/404.js +3 -3
  110. package/src/client/ssr/body/500.js +3 -3
  111. package/src/client/ssr/body/CacheControl.js +5 -2
  112. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  113. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  114. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  115. package/src/client/ssr/offline/Maintenance.js +12 -11
  116. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  117. package/src/client/ssr/pages/Test.js +2 -2
  118. package/src/client/sw/core.sw.js +212 -0
  119. package/src/index.js +1 -1
  120. package/src/runtime/express/Dockerfile +4 -4
  121. package/src/runtime/lampp/Dockerfile +8 -7
  122. package/src/runtime/wp/Dockerfile +11 -17
  123. package/src/server/client-build-docs.js +45 -46
  124. package/src/server/client-build.js +334 -60
  125. package/src/server/client-formatted.js +47 -16
  126. package/src/server/conf.js +5 -4
  127. package/src/server/ipfs-client.js +232 -91
  128. package/src/server/process.js +13 -27
  129. package/src/server/start.js +6 -3
  130. package/src/server/valkey.js +134 -235
  131. package/tsconfig.docs.json +15 -0
  132. package/typedoc.json +20 -0
  133. package/jsdoc.json +0 -52
  134. package/src/client/components/core/ColorPalette.js +0 -5267
  135. package/src/client/components/core/JoyStick.js +0 -80
  136. package/src/client/components/default/RoutesDefault.js +0 -49
  137. package/src/client/sw/default.sw.js +0 -127
  138. package/src/client/sw/template.sw.js +0 -84
@@ -1,12 +1,22 @@
1
1
  import { cap, getId } from './CommonJs.js';
2
-
3
- const Keyboard = {
4
- ActiveKey: {},
5
- Event: {},
6
- Init: async function () {
2
+ import { KeyboardEventType, keyboardEvents } from './ClientEvents.js';
3
+ class Keyboard {
4
+ static ActiveKey = {};
5
+ static Event = {};
6
+ static onPressed(listener, options = {}) {
7
+ return keyboardEvents.on(KeyboardEventType.pressed, listener, options);
8
+ }
9
+ static offPressed(key) {
10
+ return keyboardEvents.off(key);
11
+ }
12
+ static hasPressedListener(key) {
13
+ return keyboardEvents.has(key);
14
+ }
15
+ static async instance() {
7
16
  const callBackTime = 45;
8
17
  window.onkeydown = (e = new KeyboardEvent()) => {
9
- this.ActiveKey[e.key] = true;
18
+ Keyboard.ActiveKey[e.key] = true;
19
+ keyboardEvents.emit(KeyboardEventType.pressed, { key: e.key, activeKeys: { ...Keyboard.ActiveKey }, event: e });
10
20
  // e.composedPath()
11
21
  // if (['Tab'].includes(e.key)) {
12
22
  // e.preventDefault();
@@ -15,24 +25,23 @@ const Keyboard = {
15
25
  // }
16
26
  };
17
27
  window.onkeyup = (e = new KeyboardEvent()) => {
18
- delete this.ActiveKey[e.key];
28
+ delete Keyboard.ActiveKey[e.key];
19
29
  };
20
30
  setInterval(() => {
21
- Object.keys(this.Event).map((key) => {
22
- Object.keys(this.ActiveKey).map((activeKey) => {
23
- if (activeKey in this.Event[key]) this.Event[key][activeKey]();
31
+ Object.keys(Keyboard.Event).map((key) => {
32
+ Object.keys(Keyboard.ActiveKey).map((activeKey) => {
33
+ if (activeKey in Keyboard.Event[key]) Keyboard.Event[key][activeKey]();
24
34
  });
25
35
  });
26
36
  }, callBackTime);
27
- },
28
- instanceMultiPressKeyTokens: {},
29
- instanceMultiPressKey: (options = { keys: [], id, timePressDelay, eventCallBack: () => {} }) => {
37
+ }
38
+ static instanceMultiPressKeyTokens = {};
39
+ static instanceMultiPressKey = (options = { keys: [], id, timePressDelay, eventCallBack: () => {} }) => {
30
40
  if (typeof options.keys[0] === 'string') options.keys[0] = [options.keys[0]];
31
41
  if (!options.id) options.id = getId(Keyboard.instanceMultiPressKeyTokens, 'key-press-');
32
42
  if (!options.timePressDelay) options.timePressDelay = 500;
33
43
  const { id, timePressDelay, keys, eventCallBack } = options;
34
44
  Keyboard.instanceMultiPressKeyTokens[id] = { ...options };
35
-
36
45
  let indexCombined = -1;
37
46
  for (const combinedKeys of keys) {
38
47
  indexCombined++;
@@ -58,7 +67,6 @@ const Keyboard = {
58
67
  }
59
68
  },
60
69
  };
61
-
62
70
  Keyboard.Event[`instanceMultiPressKey-${id}-${privateIndexCombined}`][key] = multiPressKey[key].trigger;
63
71
  Keyboard.Event[`instanceMultiPressKey-${id}-${privateIndexCombined}`][key.toLowerCase()] =
64
72
  multiPressKey[key].trigger;
@@ -67,7 +75,6 @@ const Keyboard = {
67
75
  Keyboard.Event[`instanceMultiPressKey-${id}-${privateIndexCombined}`][cap(key)] = multiPressKey[key].trigger;
68
76
  }
69
77
  }
70
- },
71
- };
72
-
78
+ };
79
+ }
73
80
  export { Keyboard };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Mobile virtual keyboard avoidance utility.
3
+ *
4
+ * On mobile browsers, when the software keyboard appears it reduces
5
+ * `window.visualViewport.height`. Without compensation, the keyboard
6
+ * can cover modals and form inputs so the user cannot see what they type.
7
+ *
8
+ * This module attaches to `window.visualViewport` resize/scroll events and
9
+ * translates a target element upward by the amount of space the keyboard has
10
+ * consumed. A debounce is applied so that rapid `resize` events (common on
11
+ * Android) do not cause visual jitter.
12
+ *
13
+ * Usage:
14
+ * ```js
15
+ * import { initKeyboardAvoidance } from './KeyboardAvoidance.js';
16
+ *
17
+ * // Attach when a modal opens:
18
+ * const cleanup = initKeyboardAvoidance(document.querySelector('.my-modal'));
19
+ *
20
+ * // Detach when the modal closes:
21
+ * cleanup();
22
+ * ```
23
+ *
24
+ * @module src/client/components/core/KeyboardAvoidance.js
25
+ * @namespace KeyboardAvoidanceModule
26
+ */
27
+
28
+ /**
29
+ * Minimum keyboard height in pixels below which we consider the keyboard
30
+ * to be hidden. Values smaller than this are treated as OS chrome
31
+ * (address bar resize, etc.) and ignored.
32
+ *
33
+ * @constant {number}
34
+ * @memberof KeyboardAvoidanceModule
35
+ */
36
+ const MIN_KEYBOARD_HEIGHT_PX = 50;
37
+
38
+ /**
39
+ * Debounce delay in milliseconds applied to the `resize` handler.
40
+ * Prevents excessive style recalculations when the keyboard animates in.
41
+ *
42
+ * @constant {number}
43
+ * @memberof KeyboardAvoidanceModule
44
+ */
45
+ const DEBOUNCE_MS = 32;
46
+
47
+ /**
48
+ * Attaches `visualViewport` listeners to translate `targetEl` when the
49
+ * mobile software keyboard appears.
50
+ *
51
+ * The function is a no-op in environments that do not support the
52
+ * `visualViewport` API (desktop browsers, some older mobile browsers).
53
+ *
54
+ * @param {HTMLElement} targetEl - The element to translate (typically a
55
+ * modal container or form wrapper).
56
+ * @param {object} [options]
57
+ * @param {number} [options.debounceMs=32] - Debounce delay in ms.
58
+ * @param {number} [options.minKeyboardPx=50] - Minimum offset to treat
59
+ * as a real keyboard appearance.
60
+ * @param {string} [options.transitionStyle='transform 0.15s ease-out'] -
61
+ * CSS transition applied to `targetEl` during keyboard animations.
62
+ * @returns {function(): void} Cleanup function — call when the element is
63
+ * unmounted or the modal closes to remove the event listeners.
64
+ *
65
+ * @memberof KeyboardAvoidanceModule
66
+ *
67
+ * @example
68
+ * // Inside a modal's open() method:
69
+ * this._kbCleanup = initKeyboardAvoidance(this._el);
70
+ *
71
+ * // Inside the modal's close() method:
72
+ * if (this._kbCleanup) { this._kbCleanup(); this._kbCleanup = null; }
73
+ */
74
+ export function initKeyboardAvoidance(
75
+ targetEl,
76
+ {
77
+ debounceMs = DEBOUNCE_MS,
78
+ minKeyboardPx = MIN_KEYBOARD_HEIGHT_PX,
79
+ transitionStyle = 'transform 0.15s ease-out',
80
+ } = {},
81
+ ) {
82
+ // Feature-detect visualViewport. When unavailable, return a no-op cleanup.
83
+ if (!window.visualViewport || !targetEl) return () => {};
84
+
85
+ // Apply transition so movement feels natural.
86
+ const previousTransition = targetEl.style.transition;
87
+ targetEl.style.transition = transitionStyle;
88
+
89
+ let debounceTimer = null;
90
+
91
+ /**
92
+ * Computes the vertical offset caused by the software keyboard and
93
+ * applies a CSS translateY correction to `targetEl`.
94
+ *
95
+ * @returns {void}
96
+ * @memberof KeyboardAvoidanceModule
97
+ */
98
+ const update = () => {
99
+ const offset = window.innerHeight - window.visualViewport.height - window.visualViewport.offsetTop;
100
+
101
+ if (offset > minKeyboardPx) {
102
+ // Keyboard is visible — push the element up.
103
+ targetEl.style.transform = `translateY(-${Math.round(offset)}px)`;
104
+ } else {
105
+ // Keyboard is hidden — restore original position.
106
+ targetEl.style.transform = '';
107
+ }
108
+ };
109
+
110
+ /**
111
+ * Debounced wrapper for `update`.
112
+ * @returns {void}
113
+ */
114
+ const onViewportChange = () => {
115
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
116
+ debounceTimer = setTimeout(update, debounceMs);
117
+ };
118
+
119
+ window.visualViewport.addEventListener('resize', onViewportChange);
120
+ window.visualViewport.addEventListener('scroll', onViewportChange);
121
+
122
+ // Run once immediately to handle the case where the keyboard was already
123
+ // open when this function was called.
124
+ update();
125
+
126
+ /**
127
+ * Cleanup function. Removes all listeners and restores the element's
128
+ * original styles.
129
+ *
130
+ * @returns {void}
131
+ */
132
+ return () => {
133
+ if (debounceTimer !== null) {
134
+ clearTimeout(debounceTimer);
135
+ debounceTimer = null;
136
+ }
137
+ window.visualViewport.removeEventListener('resize', onViewportChange);
138
+ window.visualViewport.removeEventListener('scroll', onViewportChange);
139
+ // Restore previous styles.
140
+ targetEl.style.transform = '';
141
+ targetEl.style.transition = previousTransition;
142
+ };
143
+ }
144
+
145
+ export default initKeyboardAvoidance;
@@ -4,17 +4,15 @@ import { darkTheme, renderCssAttr, subThemeManager } from './Css.js';
4
4
  import { loggerFactory } from './Logger.js';
5
5
  import { append, htmls, s } from './VanillaJs.js';
6
6
  import { getProxyPath } from './Router.js';
7
-
8
7
  const logger = loggerFactory(import.meta);
9
-
10
- const LoadingAnimation = {
11
- bar: {
8
+ class LoadingAnimation {
9
+ static bar = {
12
10
  tokens: {},
13
11
  getId: (id) => `bar-progress-${id.slice(1)}`,
14
12
  play: async function (container) {
15
- const id = this.getId(container);
13
+ const id = LoadingAnimation.bar.getId(container);
16
14
  const idEvent = s4() + s4() + s4();
17
- this.tokens[container] = `${idEvent}`;
15
+ LoadingAnimation.bar.tokens[container] = `${idEvent}`;
18
16
  // diagonal-bar-background-animation
19
17
  // #6d68ff #790079
20
18
  append(
@@ -39,28 +37,27 @@ const LoadingAnimation = {
39
37
  { time: 5000, value: 1 },
40
38
  ])
41
39
  setTimeout(() => {
42
- if (this.tokens[container] === idEvent && s(`.${id}`)) {
40
+ if (LoadingAnimation.bar.tokens[container] === idEvent && s(`.${id}`)) {
43
41
  s(`.${id}`).style.left = `-${frame.value}%`;
44
42
  // const percentageRender = html`${100 - frame.value}%`;
45
43
  }
46
44
  }, frame.time);
47
45
  },
48
46
  stop: function (container) {
49
- const id = this.getId(container);
50
- delete this.tokens[container];
47
+ const id = LoadingAnimation.bar.getId(container);
48
+ delete LoadingAnimation.bar.tokens[container];
51
49
  if (!s(`.${id}`)) return;
52
50
  s(`.${id}`).style.left = '0%';
53
51
  s(`.${id}`).style.opacity = 1;
54
52
  setTimeout(() => (s(`.${id}`).style.opacity = 0));
55
53
  setTimeout(() => s(`.${id}`).remove(), 400);
56
54
  },
57
- },
58
- spinner: {
55
+ };
56
+ static spinner = {
59
57
  getId: (id) => `spinner-progress-${id.slice(1)}`,
60
58
  play: async function (container, spinner, options = { append: '', prepend: '' }) {
61
59
  if (!s(container)) return;
62
- const id = this.getId(container);
63
-
60
+ const id = LoadingAnimation.spinner.getId(container);
64
61
  let render;
65
62
  switch (spinner) {
66
63
  case 'dual-ring':
@@ -71,11 +68,9 @@ const LoadingAnimation = {
71
68
  render = html`<div class="lds-dual-ring-mini"></div>`;
72
69
  break;
73
70
  }
74
-
75
71
  const style = {
76
72
  'text-align': 'center',
77
73
  };
78
-
79
74
  append(
80
75
  container,
81
76
  html`
@@ -93,30 +88,30 @@ const LoadingAnimation = {
93
88
  if (label) label.classList.add('hide');
94
89
  },
95
90
  stop: function (container) {
96
- const id = this.getId(container);
91
+ const id = LoadingAnimation.spinner.getId(container);
97
92
  if (!s(`.${id}`)) return;
98
93
  s(`.${id}`).remove();
99
94
  const label = BtnIcon.findLabel(s(container));
100
95
  if (label) label.classList.remove('hide');
101
96
  },
102
- },
103
- img: {
97
+ };
98
+ static img = {
104
99
  tokens: {},
105
100
  load: function ({ key, src, classes, style }) {
106
- this.tokens[key] = { src, classes, style };
101
+ LoadingAnimation.img.tokens[key] = { src, classes, style };
107
102
  },
108
103
  play: function (container, key) {
109
104
  append(
110
105
  container,
111
106
  html`<img
112
- ${this.tokens[key].classes ? `class="${this.tokens[key].classes}"` : ''}
113
- ${this.tokens[key].style ? `style="${this.tokens[key].style}"` : ''}
114
- src="${getProxyPath()}${this.tokens[key].src}"
107
+ ${LoadingAnimation.img.tokens[key].classes ? `class="${LoadingAnimation.img.tokens[key].classes}"` : ''}
108
+ ${LoadingAnimation.img.tokens[key].style ? `style="${LoadingAnimation.img.tokens[key].style}"` : ''}
109
+ src="${getProxyPath()}${LoadingAnimation.img.tokens[key].src}"
115
110
  />`,
116
111
  );
117
112
  },
118
- },
119
- barLevel: {
113
+ };
114
+ static barLevel = {
120
115
  append: () => {
121
116
  if (Array.from(sa('.ssr-loading-bar-block')).length >= 5) return;
122
117
  s(`.ssr-blink-bar`).classList.remove('ssr-blink-bar');
@@ -125,17 +120,16 @@ const LoadingAnimation = {
125
120
  clear: () => {
126
121
  htmls('.ssr-loading-bar', html`<div class="ssr-loading-bar-block ssr-blink-bar"></div>`);
127
122
  },
128
- },
129
- removeSplashScreen: function (backgroundContainer, callBack) {
123
+ };
124
+ static removeSplashScreen(backgroundContainer, callBack) {
130
125
  if (s(`.clean-cache-container`)) s(`.clean-cache-container`).style.display = 'none';
131
126
  if (!backgroundContainer) backgroundContainer = '.ssr-background';
132
127
  if (s(backgroundContainer)) {
133
128
  s(backgroundContainer).style.display = 'none';
134
129
  if (callBack) callBack();
135
130
  }
136
- },
137
-
138
- RenderCurrentSrcLoad: function (event) {
131
+ }
132
+ static RenderCurrentSrcLoad(event) {
139
133
  if (s(`.ssr-loading-info`)) {
140
134
  let nameSrcLoad = event.data.path;
141
135
  if (nameSrcLoad) {
@@ -153,6 +147,6 @@ const LoadingAnimation = {
153
147
  );
154
148
  }
155
149
  }
156
- },
157
- };
150
+ }
151
+ }
158
152
  export { LoadingAnimation };
@@ -7,15 +7,14 @@ import { EventsUI } from './EventsUI.js';
7
7
  import { Input } from './Input.js';
8
8
  import { loggerFactory } from './Logger.js';
9
9
  import { NotificationManager } from './NotificationManager.js';
10
+ import { AuthEventType, authLoginEvents } from './ClientEvents.js';
10
11
  import { Translate } from './Translate.js';
11
12
  import { Validator } from './Validator.js';
12
13
  import { htmls, s } from './VanillaJs.js';
13
14
  import { WebhookProvider } from './Webhook.js';
14
-
15
15
  const logger = loggerFactory(import.meta);
16
-
17
- const LogIn = {
18
- Scope: {
16
+ class LogIn {
17
+ static Scope = {
19
18
  user: {
20
19
  main: {
21
20
  model: {
@@ -23,13 +22,22 @@ const LogIn = {
23
22
  },
24
23
  },
25
24
  },
26
- },
27
- Event: {},
28
- Trigger: async function (options) {
25
+ };
26
+ static Event = {};
27
+ static onLogin(listener, options = {}) {
28
+ return authLoginEvents.on(AuthEventType.login, listener, options);
29
+ }
30
+ static offLogin(key) {
31
+ return authLoginEvents.off(key);
32
+ }
33
+ static hasLoginListener(key) {
34
+ return authLoginEvents.has(key);
35
+ }
36
+ static async Trigger(options) {
29
37
  const { user } = options;
30
- if (user) this.Scope.user.main.model.user = { ...this.Scope.user.main.model.user, ...user };
31
-
32
- for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
38
+ if (user) LogIn.Scope.user.main.model.user = { ...LogIn.Scope.user.main.model.user, ...user };
39
+ await authLoginEvents.emit(AuthEventType.login, options);
40
+ for (const eventKey of Object.keys(LogIn.Event)) await LogIn.Event[eventKey](options);
33
41
  if (!user || user.role === 'guest') return;
34
42
  await WebhookProvider.register({ user });
35
43
  if (s(`.session`))
@@ -56,15 +64,14 @@ const LogIn = {
56
64
  }
57
65
  </style>`,
58
66
  );
59
- if (!this.Scope.user.main.model.user.profileImage) {
67
+ if (!LogIn.Scope.user.main.model.user.profileImage) {
60
68
  // Try to load profile image only if profileImageId exists
61
- if (!this.Scope.user.main.model.user.profileImage && user?.profileImageId) {
69
+ if (!LogIn.Scope.user.main.model.user.profileImage && user?.profileImageId) {
62
70
  try {
63
71
  const resultFile = await FileService.get({ id: user.profileImageId });
64
72
  if (resultFile && resultFile.status === 'success' && resultFile.data[0]) {
65
73
  const imageData = resultFile.data[0];
66
74
  let imageSrc = null;
67
-
68
75
  try {
69
76
  // Handle new metadata-only format
70
77
  if (!imageData.data?.data && imageData._id) {
@@ -76,9 +83,8 @@ const LogIn = {
76
83
  const imageFile = new File([imageBlob], imageData.name, { type: imageData.mimetype });
77
84
  imageSrc = URL.createObjectURL(imageFile);
78
85
  }
79
-
80
86
  if (imageSrc) {
81
- this.Scope.user.main.model.user.profileImage = {
87
+ LogIn.Scope.user.main.model.user.profileImage = {
82
88
  resultFile,
83
89
  imageData,
84
90
  imageSrc,
@@ -97,8 +103,8 @@ const LogIn = {
97
103
  html`<div class="abs center top-box-profile-img-container">
98
104
  <img
99
105
  class="abs center top-box-profile-img"
100
- ${this.Scope.user.main.model.user.profileImage
101
- ? `src="${this.Scope.user.main.model.user.profileImage.imageSrc}"`
106
+ ${LogIn.Scope.user.main.model.user.profileImage
107
+ ? `src="${LogIn.Scope.user.main.model.user.profileImage.imageSrc}"`
102
108
  : `src="${getApiBaseUrl({
103
109
  id: 'assets/avatar',
104
110
  endpoint: 'user',
@@ -107,15 +113,14 @@ const LogIn = {
107
113
  </div>`,
108
114
  );
109
115
  }
110
- },
111
- Render: async function () {
116
+ }
117
+ static async instance() {
112
118
  setTimeout(async () => {
113
119
  const formData = [
114
120
  { model: 'email', id: `log-in-email`, rules: [{ type: 'isEmpty' }, { type: 'isEmail' }] },
115
121
  { model: 'password', id: `log-in-password`, rules: [{ type: 'isEmpty' }] },
116
122
  ];
117
123
  const validators = await Validator.instance(formData);
118
-
119
124
  EventsUI.onClick(`.btn-log-in`, async (e) => {
120
125
  e.preventDefault();
121
126
  const { errorMessage } = await validators();
@@ -125,88 +130,83 @@ const LogIn = {
125
130
  if ('model' in inputData) body[inputData.model] = s(`.${inputData.id}`).value;
126
131
  }
127
132
  const result = await UserService.post({ id: 'auth', body });
128
-
129
133
  if (result.status === 'error' && result.message.match('attempts')) {
130
134
  htmls(`.login-attempt-warn-value`, result.message.split(':')[1]);
131
135
  s(`.login-attempt-warn-container`).classList.remove('hide');
132
136
  } else s(`.login-attempt-warn-container`).classList.add('hide');
133
-
134
137
  if (result.status === 'error' && result.message.match('locked')) {
135
138
  htmls(`.login-attempt-warn-value0`, result.message.split(':')[1]);
136
139
  s(`.login-attempt-warn-container0`).classList.remove('hide');
137
140
  } else s(`.login-attempt-warn-container0`).classList.add('hide');
138
-
139
141
  if (result.status === 'success') await Auth.sessionIn(result);
140
142
  NotificationManager.Push({
141
- html: result.status === 'success' ? Translate.Render(`${result.status}-user-log-in`) : result.message,
143
+ html: result.status === 'success' ? Translate.instance(`${result.status}-user-log-in`) : result.message,
142
144
  status: result.status,
143
145
  });
144
146
  });
145
147
  s(`.btn-log-in-forgot-password`).onclick = () => {
146
148
  s(`.main-btn-recover`).click();
147
149
  };
148
-
149
150
  s(`.btn-log-in-i-not-have-account`).onclick = () => {
150
151
  s(`.main-btn-sign-up`).click();
151
152
  };
152
153
  });
153
154
  return html`
154
155
  <div class="in">
155
- ${await BtnIcon.Render({
156
+ ${await BtnIcon.instance({
156
157
  class: 'in section-mp form-button btn-log-in-i-not-have-account',
157
- label: html`<i class="fas fa-user-plus"></i> ${Translate.Render(`i-not-have-account`)}
158
+ label: html`<i class="fas fa-user-plus"></i> ${Translate.instance(`i-not-have-account`)}
158
159
  <br />
159
- ${Translate.Render(`sign-up`)}`,
160
+ ${Translate.instance(`sign-up`)}`,
160
161
  type: 'button',
161
162
  })}
162
163
  </div>
163
164
  <form class="in">
164
165
  <div class="in">
165
- ${await Input.Render({
166
+ ${await Input.instance({
166
167
  id: `log-in-email`,
167
168
  type: 'email',
168
- label: html`<i class="fa-solid fa-envelope"></i> ${Translate.Render('email')}`,
169
+ label: html`<i class="fa-solid fa-envelope"></i> ${Translate.instance('email')}`,
169
170
  containerClass: 'inl section-mp width-mini-box input-container',
170
171
  placeholder: true,
171
172
  autocomplete: 'email',
172
173
  })}
173
174
  </div>
174
175
  <div class="in">
175
- ${await Input.Render({
176
+ ${await Input.instance({
176
177
  id: `log-in-password`,
177
178
  type: 'password',
178
179
  autocomplete: 'new-password',
179
- label: html`<i class="fa-solid fa-lock"></i> ${Translate.Render('password')}`,
180
+ label: html`<i class="fa-solid fa-lock"></i> ${Translate.instance('password')}`,
180
181
  containerClass: 'inl section-mp width-mini-box input-container',
181
182
  placeholder: true,
182
183
  })}
183
184
  </div>
184
185
  <div class="in">
185
- ${await BtnIcon.Render({
186
+ ${await BtnIcon.instance({
186
187
  class: 'in section-mp form-button btn-log-in-forgot-password',
187
- label: html`<i class="fas fa-question-circle"></i> ${Translate.Render(`forgot-password`)}`,
188
+ label: html`<i class="fas fa-question-circle"></i> ${Translate.instance(`forgot-password`)}`,
188
189
  type: 'button',
189
190
  })}
190
191
  </div>
191
192
  <div class="in section-mp form-button login-attempt-warn-container hide">
192
- <i class="fa-solid fa-triangle-exclamation"></i> ${Translate.Render('login-attempts-remaining')}
193
+ <i class="fa-solid fa-triangle-exclamation"></i> ${Translate.instance('login-attempts-remaining')}
193
194
  <span style="color: #ed9d0f" class="login-attempt-warn-value"></span>
194
195
  </div>
195
196
  <div class="in section-mp form-button login-attempt-warn-container0 hide">
196
- <i class="fa-solid fa-triangle-exclamation"></i> ${Translate.Render('account-locked-try-again-in')}
197
+ <i class="fa-solid fa-triangle-exclamation"></i> ${Translate.instance('account-locked-try-again-in')}
197
198
  <span style="color: #ed9d0f" class="login-attempt-warn-value0"></span>
198
199
  </div>
199
200
 
200
201
  <div class="in">
201
- ${await BtnIcon.Render({
202
+ ${await BtnIcon.instance({
202
203
  class: 'in section-mp form-button btn-log-in',
203
- label: Translate.Render('log-in'),
204
+ label: Translate.instance('log-in'),
204
205
  type: 'submit',
205
206
  })}
206
207
  </div>
207
208
  </form>
208
209
  `;
209
- },
210
- };
211
-
210
+ }
211
+ }
212
212
  export { LogIn };
@@ -1,16 +1,26 @@
1
1
  import { Auth } from './Auth.js';
2
2
  import { BtnIcon } from './BtnIcon.js';
3
+ import { AuthEventType, authLogoutEvents } from './ClientEvents.js';
3
4
  import { LogIn } from './LogIn.js';
4
5
  import { Translate } from './Translate.js';
5
6
  import { htmls, s } from './VanillaJs.js';
6
7
  import { WebhookProvider } from './Webhook.js';
7
8
  import { NotificationManager } from './NotificationManager.js';
8
-
9
- const LogOut = {
10
- Event: {},
11
- Trigger: async function (options) {
9
+ class LogOut {
10
+ static Event = {};
11
+ static onLogout(listener, options = {}) {
12
+ return authLogoutEvents.on(AuthEventType.logout, listener, options);
13
+ }
14
+ static offLogout(key) {
15
+ return authLogoutEvents.off(key);
16
+ }
17
+ static hasLogoutListener(key) {
18
+ return authLogoutEvents.has(key);
19
+ }
20
+ static async Trigger(options) {
12
21
  await WebhookProvider.unregister();
13
- for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
22
+ await authLogoutEvents.emit(AuthEventType.logout, options);
23
+ for (const eventKey of Object.keys(LogOut.Event)) await LogOut.Event[eventKey](options);
14
24
  if (s(`.session`))
15
25
  htmls(
16
26
  `.session`,
@@ -35,29 +45,28 @@ const LogOut = {
35
45
  }
36
46
  </style>`,
37
47
  );
38
- },
39
- Render: async function () {
48
+ }
49
+ static async instance() {
40
50
  setTimeout(() => {
41
51
  s('.btn-log-out').onclick = async (e) => {
42
52
  e.preventDefault();
43
53
  await Auth.sessionOut();
44
54
  NotificationManager.Push({
45
- html: Translate.Render(`success-logout`),
55
+ html: Translate.instance(`success-logout`),
46
56
  status: 'success',
47
57
  });
48
58
  };
49
59
  });
50
- // Translate.Render('confirm-logout')
60
+ // Translate.instance('confirm-logout')
51
61
  return html` <form class="in">
52
62
  <div class="in">
53
- ${await BtnIcon.Render({
63
+ ${await BtnIcon.instance({
54
64
  class: 'inl section-mp btn-custom btn-log-out',
55
- label: html`<i class="fa-solid fa-power-off"></i> ${Translate.Render('log-out')}`,
65
+ label: html`<i class="fa-solid fa-power-off"></i> ${Translate.instance('log-out')}`,
56
66
  type: 'submit',
57
67
  })}
58
68
  </div>
59
69
  </form>`;
60
- },
61
- };
62
-
70
+ }
71
+ }
63
72
  export { LogOut };