@stack-spot/portal-components 2.27.1 → 2.27.2

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 (246) hide show
  1. package/CHANGELOG.md +635 -628
  2. package/dist/components/AnimatedHeight.d.ts +1 -1
  3. package/dist/components/AnimatedHeight.js +26 -26
  4. package/dist/components/AsyncContent.d.ts +1 -1
  5. package/dist/components/AsyncContent.js +1 -1
  6. package/dist/components/BannerWarning.d.ts +1 -1
  7. package/dist/components/BannerWarning.js +1 -1
  8. package/dist/components/Breadcrumb/index.d.ts +2 -2
  9. package/dist/components/Breadcrumb/index.js +1 -1
  10. package/dist/components/Breadcrumb/styled.js +31 -31
  11. package/dist/components/ButtonLoading.d.ts +1 -1
  12. package/dist/components/ButtonLoading.js +1 -1
  13. package/dist/components/ChatBot.d.ts +1 -1
  14. package/dist/components/ChatBot.js +1 -1
  15. package/dist/components/ContentValidateFilter.d.ts +1 -1
  16. package/dist/components/ContentValidateFilter.js +1 -1
  17. package/dist/components/FadingOverflow.d.ts +1 -1
  18. package/dist/components/FadingOverflow.js +69 -69
  19. package/dist/components/FileTreeView/More.d.ts +1 -1
  20. package/dist/components/FileTreeView/More.js +1 -1
  21. package/dist/components/FileTreeView/index.d.ts +1 -1
  22. package/dist/components/FileTreeView/index.js +1 -1
  23. package/dist/components/InfiniteScroll.d.ts +1 -1
  24. package/dist/components/InfiniteScroll.js +1 -1
  25. package/dist/components/InfoMaintenanceBanner.d.ts +1 -1
  26. package/dist/components/InfoMaintenanceBanner.js +2 -2
  27. package/dist/components/LazyMarkdown/BlockquoteMd.d.ts +1 -1
  28. package/dist/components/LazyMarkdown/BlockquoteMd.js +1 -1
  29. package/dist/components/LazyMarkdown/CodeViewer.d.ts +1 -1
  30. package/dist/components/LazyMarkdown/CodeViewer.js +76 -76
  31. package/dist/components/LazyMarkdown/Markdown.d.ts +1 -1
  32. package/dist/components/LazyMarkdown/Markdown.js +1 -1
  33. package/dist/components/LazyMarkdown/MarkdownButton.d.ts +1 -1
  34. package/dist/components/LazyMarkdown/MarkdownButton.js +1 -1
  35. package/dist/components/LazyMarkdown/Video.d.ts +1 -1
  36. package/dist/components/LazyMarkdown/Video.js +1 -1
  37. package/dist/components/LazyMarkdown/index.d.ts +1 -1
  38. package/dist/components/LazyMarkdown/index.js +1 -1
  39. package/dist/components/Placeholder.d.ts +7 -3
  40. package/dist/components/Placeholder.d.ts.map +1 -1
  41. package/dist/components/Placeholder.js +3 -3
  42. package/dist/components/Placeholder.js.map +1 -1
  43. package/dist/components/ScrollView.js +16 -16
  44. package/dist/components/Select/BadgeItem.d.ts +1 -1
  45. package/dist/components/Select/BadgeItem.js +1 -1
  46. package/dist/components/Select/ClearInput.d.ts +1 -1
  47. package/dist/components/Select/ClearInput.js +1 -1
  48. package/dist/components/Select/CloseItem.d.ts +1 -1
  49. package/dist/components/Select/CloseItem.js +1 -1
  50. package/dist/components/Select/CreatableSelect.js +1 -1
  51. package/dist/components/Select/CustomMenu.d.ts +1 -1
  52. package/dist/components/Select/CustomMenu.js +1 -1
  53. package/dist/components/Select/LabelItem.d.ts +1 -1
  54. package/dist/components/Select/LabelItem.js +1 -1
  55. package/dist/components/Select/MultiValue.d.ts +1 -1
  56. package/dist/components/Select/MultiValue.js +1 -1
  57. package/dist/components/Select/SelectInfiniteScroll.d.ts +1 -1
  58. package/dist/components/Select/SelectInfiniteScroll.js +1 -1
  59. package/dist/components/Select/SelectSearch.d.ts +1 -1
  60. package/dist/components/Select/SelectSearch.js +1 -1
  61. package/dist/components/SelectionList.d.ts +1 -1
  62. package/dist/components/SelectionList.js +61 -61
  63. package/dist/components/StatusCircle.d.ts +1 -1
  64. package/dist/components/StatusCircle.js +6 -6
  65. package/dist/components/Stepper/Navigation.js +4 -4
  66. package/dist/components/Stepper/Step.js +3 -3
  67. package/dist/components/Stepper/Stepper.js +6 -6
  68. package/dist/components/Stepper/headers.js +22 -22
  69. package/dist/components/Table/HeaderItem.js +1 -1
  70. package/dist/components/Table/SettingsVerticalMenu.d.ts +1 -1
  71. package/dist/components/Table/SettingsVerticalMenu.js +1 -1
  72. package/dist/components/Table/StyledLinkTable.d.ts +1 -1
  73. package/dist/components/Table/StyledLinkTable.js +5 -5
  74. package/dist/components/Table/TableData.d.ts +1 -1
  75. package/dist/components/Table/TableData.js +25 -25
  76. package/dist/components/TimelineSection.d.ts +1 -1
  77. package/dist/components/TimelineSection.js +14 -14
  78. package/dist/components/error/ErrorFeedback.d.ts +1 -1
  79. package/dist/components/error/ErrorFeedback.js +35 -35
  80. package/dist/components/error/NotFound.d.ts +1 -1
  81. package/dist/components/error/NotFound.js +1 -1
  82. package/dist/components/error/UnderMaintenance.d.ts +1 -1
  83. package/dist/components/error/UnderMaintenance.js +1 -1
  84. package/dist/components/form/Form/Form.d.ts +1 -1
  85. package/dist/components/form/Form/Form.js +1 -1
  86. package/dist/components/form/Form/FormGroup.d.ts +2 -2
  87. package/dist/components/form/Form/FormGroup.js +1 -1
  88. package/dist/components/form/SearchInput.d.ts +1 -1
  89. package/dist/components/form/SearchInput.js +1 -1
  90. package/dist/components/form/Select/CustomSelect.d.ts +1 -1
  91. package/dist/components/form/Select/CustomSelect.js +1 -1
  92. package/dist/components/form/Select/DetailedSelect.d.ts +1 -1
  93. package/dist/components/form/Select/DetailedSelect.js +1 -1
  94. package/dist/components/form/Select/Select.d.ts +1 -1
  95. package/dist/components/form/Select/Select.js +1 -1
  96. package/dist/components/form/Select/styled.js +161 -161
  97. package/dist/components/form/Select/utils.js +1 -1
  98. package/dist/components/notification/NotificationComponent.d.ts +1 -1
  99. package/dist/components/notification/NotificationComponent.js +54 -54
  100. package/dist/components/notification/NotificationItem.d.ts +1 -1
  101. package/dist/components/notification/NotificationItem.d.ts.map +1 -1
  102. package/dist/components/notification/NotificationItem.js +8 -2
  103. package/dist/components/notification/NotificationItem.js.map +1 -1
  104. package/dist/components/notification/NotificationList.d.ts +1 -1
  105. package/dist/components/notification/NotificationList.d.ts.map +1 -1
  106. package/dist/components/notification/NotificationList.js +44 -44
  107. package/dist/components/notification/NotificationList.js.map +1 -1
  108. package/dist/components/notification/NotificationPlaceholder.d.ts +1 -1
  109. package/dist/components/notification/NotificationPlaceholder.d.ts.map +1 -1
  110. package/dist/components/notification/NotificationPlaceholder.js +2 -2
  111. package/dist/components/notification/NotificationPlaceholder.js.map +1 -1
  112. package/dist/containers/NotificationsPage.d.ts +1 -1
  113. package/dist/containers/NotificationsPage.js +10 -10
  114. package/dist/context/anchor.d.ts +1 -1
  115. package/dist/context/anchor.js +1 -1
  116. package/dist/context/loading.d.ts +1 -1
  117. package/dist/context/loading.js +1 -1
  118. package/dist/context/notification/context.d.ts +1 -1
  119. package/dist/context/notification/context.js +1 -1
  120. package/dist/hooks/date.js +1 -1
  121. package/dist/hooks/service-now.js +28 -28
  122. package/dist/svg/AI.d.ts +1 -1
  123. package/dist/svg/AI.js +1 -1
  124. package/dist/svg/CS.d.ts +1 -1
  125. package/dist/svg/CS.js +1 -1
  126. package/dist/svg/EDP.d.ts +1 -1
  127. package/dist/svg/EDP.js +1 -1
  128. package/dist/svg/Forbidden.d.ts +1 -1
  129. package/dist/svg/Forbidden.js +1 -1
  130. package/dist/svg/GenericPlaceholder.d.ts +4 -2
  131. package/dist/svg/GenericPlaceholder.d.ts.map +1 -1
  132. package/dist/svg/GenericPlaceholder.js +2 -2
  133. package/dist/svg/GenericPlaceholder.js.map +1 -1
  134. package/dist/svg/HUB.d.ts +1 -1
  135. package/dist/svg/HUB.js +1 -1
  136. package/dist/svg/Logo.d.ts +1 -1
  137. package/dist/svg/Logo.js +1 -1
  138. package/dist/svg/MiniLogo.d.ts +1 -1
  139. package/dist/svg/MiniLogo.js +1 -1
  140. package/dist/svg/NotFound.d.ts +1 -1
  141. package/dist/svg/NotFound.js +1 -1
  142. package/dist/svg/ServerError.d.ts +1 -1
  143. package/dist/svg/ServerError.js +1 -1
  144. package/dist/svg/Unauthenticated.d.ts +1 -1
  145. package/dist/svg/Unauthenticated.js +1 -1
  146. package/package.json +80 -80
  147. package/readme.md +66 -66
  148. package/src/components/AnimatedHeight.tsx +174 -174
  149. package/src/components/AsyncContent.tsx +78 -78
  150. package/src/components/BannerWarning.tsx +91 -91
  151. package/src/components/Breadcrumb/index.tsx +76 -76
  152. package/src/components/Breadcrumb/styled.ts +37 -37
  153. package/src/components/ButtonLoading.tsx +29 -29
  154. package/src/components/ChatBot.tsx +82 -82
  155. package/src/components/ContentValidateFilter.tsx +15 -15
  156. package/src/components/FadingOverflow.tsx +265 -265
  157. package/src/components/FileTreeView/More.tsx +114 -114
  158. package/src/components/FileTreeView/index.tsx +186 -186
  159. package/src/components/InfiniteScroll.tsx +24 -24
  160. package/src/components/InfoMaintenanceBanner.tsx +29 -29
  161. package/src/components/LazyMarkdown/BlockquoteMd.tsx +107 -107
  162. package/src/components/LazyMarkdown/CodeViewer.tsx +161 -161
  163. package/src/components/LazyMarkdown/Markdown.tsx +122 -122
  164. package/src/components/LazyMarkdown/MarkdownButton.tsx +24 -24
  165. package/src/components/LazyMarkdown/Video.tsx +13 -13
  166. package/src/components/LazyMarkdown/index.tsx +21 -21
  167. package/src/components/Placeholder.tsx +123 -118
  168. package/src/components/ScrollView.tsx +57 -57
  169. package/src/components/Select/BadgeItem.tsx +58 -58
  170. package/src/components/Select/ClearInput.tsx +24 -24
  171. package/src/components/Select/CloseItem.tsx +38 -38
  172. package/src/components/Select/CreatableSelect.tsx +155 -155
  173. package/src/components/Select/CustomMenu.tsx +16 -16
  174. package/src/components/Select/LabelItem.tsx +8 -8
  175. package/src/components/Select/MultiValue.tsx +49 -49
  176. package/src/components/Select/SelectInfiniteScroll.tsx +82 -82
  177. package/src/components/Select/SelectSearch.tsx +195 -195
  178. package/src/components/Select/index.tsx +7 -7
  179. package/src/components/Select/types.ts +8 -8
  180. package/src/components/SelectionList.tsx +427 -427
  181. package/src/components/StatusCircle.tsx +67 -67
  182. package/src/components/Stepper/Navigation.tsx +97 -97
  183. package/src/components/Stepper/Step.tsx +30 -30
  184. package/src/components/Stepper/Stepper.tsx +113 -113
  185. package/src/components/Stepper/headers.tsx +64 -64
  186. package/src/components/Stepper/index.ts +3 -3
  187. package/src/components/Table/HeaderItem.tsx +52 -52
  188. package/src/components/Table/SettingsVerticalMenu.tsx +50 -50
  189. package/src/components/Table/StyledLinkTable.tsx +22 -22
  190. package/src/components/Table/TableData.tsx +251 -251
  191. package/src/components/Table/index.tsx +2 -2
  192. package/src/components/TimelineSection.tsx +66 -66
  193. package/src/components/error/ErrorFeedback.tsx +217 -217
  194. package/src/components/error/NotFound.tsx +24 -24
  195. package/src/components/error/UnderMaintenance.tsx +30 -30
  196. package/src/components/error/index.ts +4 -4
  197. package/src/components/form/Form/Form.tsx +101 -101
  198. package/src/components/form/Form/FormGroup.tsx +221 -221
  199. package/src/components/form/Form/index.ts +2 -2
  200. package/src/components/form/SearchInput.tsx +69 -69
  201. package/src/components/form/Select/CustomSelect.tsx +232 -232
  202. package/src/components/form/Select/DetailedSelect.tsx +85 -85
  203. package/src/components/form/Select/Select.tsx +67 -67
  204. package/src/components/form/Select/index.ts +4 -4
  205. package/src/components/form/Select/styled.ts +165 -165
  206. package/src/components/form/Select/types.ts +112 -112
  207. package/src/components/form/Select/utils.tsx +28 -28
  208. package/src/components/notification/NotificationComponent.tsx +340 -340
  209. package/src/components/notification/NotificationItem.tsx +345 -337
  210. package/src/components/notification/NotificationList.tsx +179 -178
  211. package/src/components/notification/NotificationPlaceholder.tsx +44 -43
  212. package/src/components/notification/types.ts +72 -72
  213. package/src/containers/NotificationsPage.tsx +119 -119
  214. package/src/context/anchor.tsx +37 -37
  215. package/src/context/loading.tsx +36 -36
  216. package/src/context/notification/LazyNotificationList.ts +103 -103
  217. package/src/context/notification/NotificationController.ts +104 -104
  218. package/src/context/notification/context.tsx +23 -23
  219. package/src/context/notification/hooks.ts +98 -98
  220. package/src/context/notification/types.ts +66 -66
  221. package/src/hooks/date.ts +31 -31
  222. package/src/hooks/keyboard.tsx +128 -128
  223. package/src/hooks/manual-render.tsx +10 -10
  224. package/src/hooks/service-now.tsx +233 -233
  225. package/src/hooks/text.tsx +30 -30
  226. package/src/hooks/title.tsx +28 -28
  227. package/src/hooks/use-effect-once.tsx +43 -43
  228. package/src/index.ts +19 -19
  229. package/src/notifications.ts +11 -11
  230. package/src/svg/AI.tsx +41 -41
  231. package/src/svg/CS.tsx +48 -48
  232. package/src/svg/EDP.tsx +31 -31
  233. package/src/svg/Forbidden.tsx +22 -22
  234. package/src/svg/GenericPlaceholder.tsx +20 -20
  235. package/src/svg/HUB.tsx +48 -48
  236. package/src/svg/Logo.tsx +16 -16
  237. package/src/svg/MiniLogo.tsx +12 -12
  238. package/src/svg/NotFound.tsx +16 -16
  239. package/src/svg/ServerError.tsx +33 -33
  240. package/src/svg/Unauthenticated.tsx +16 -16
  241. package/src/svg/index.ts +11 -11
  242. package/src/utils/accessibility.ts +135 -135
  243. package/src/utils/cookie.ts +73 -73
  244. package/src/utils/promise.ts +5 -5
  245. package/src/utils/read-file.ts +16 -16
  246. package/tsconfig.json +10 -10
@@ -1,104 +1,104 @@
1
- import { pull } from 'lodash'
2
- import { LazyNotificationList } from './LazyNotificationList'
3
- import { LoadNotificationsFilters, NotificationConfig, UnreadNotificationListener } from './types'
4
-
5
- const DEFAULT_POLLING_MS = 120000
6
-
7
- export class NotificationController {
8
- private lazyLists = new Map<number, LazyNotificationList>()
9
- readonly config: NotificationConfig
10
- private readStatusMap = new Map<string, boolean>()
11
- private nextId = 1
12
- private unreadNotification = false
13
- private lastUnreadVerification: Date | undefined
14
- private pollingTimeoutId: number | undefined
15
- private unreadNotificationListeners: UnreadNotificationListener[] = []
16
-
17
- constructor(config: NotificationConfig) {
18
- this.config = config
19
- config.pollingMS ??= DEFAULT_POLLING_MS
20
- config.notificationsPath ??= '/notifications'
21
- }
22
-
23
- private hasUnreadNotificationInMemory() {
24
- for (const [, read] of this.readStatusMap) {
25
- if (!read.valueOf()) return true
26
- }
27
- return false
28
- }
29
-
30
- private setUnreadNotification(value: boolean) {
31
- this.unreadNotification = value
32
- if (!value) {
33
- this.pollingTimeoutId = window.setTimeout(() => this.checkUnread(), this.config.pollingMS)
34
- }
35
- this.unreadNotificationListeners.forEach(l => l(value))
36
- }
37
-
38
- createLazyNotificationList(filters?: LoadNotificationsFilters): LazyNotificationList {
39
- const list = new LazyNotificationList({
40
- id: this.nextId++,
41
- filters,
42
- load: async (options) => {
43
- const result = await this.config.load(options)
44
- result.items.forEach((i) => {
45
- this.readStatusMap.set(i.id, i.committed)
46
- // updating the unread indicator, since it might be stale
47
- if (!i.committed) this.setUnreadNotification(true)
48
- })
49
- this.lazyLists.forEach(l => l !== list && list.update(this.readStatusMap))
50
- return result
51
- },
52
- })
53
- this.lazyLists.set(list.id, list)
54
- return list
55
- }
56
-
57
- destroyLazyNotificationList(id: number) {
58
- this.lazyLists.get(id)?.mute()
59
- this.lazyLists.delete(id)
60
- }
61
-
62
- async checkUnread() {
63
- window.clearInterval(this.pollingTimeoutId)
64
- this.setUnreadNotification(await this.config.checkForUnreadNotificationsSince(this.lastUnreadVerification))
65
- this.lastUnreadVerification = new Date()
66
- }
67
-
68
- async markAsCommitted(notificationId: string) {
69
- if (this.readStatusMap.get(notificationId)) return
70
- this.readStatusMap.set(notificationId, true)
71
- this.lazyLists.forEach(l => l.update(this.readStatusMap))
72
- try {
73
- await this.config.markAsCommitted(notificationId)
74
- // update the notification indicator: this is an inconsistency, we should actually ask the API, but since it's an expensive
75
- // operation in the backend, we won't, instead, we'll only check the notifications we have loaded in memory.
76
- if (!this.hasUnreadNotificationInMemory()) this.setUnreadNotification(false)
77
- } catch {
78
- this.readStatusMap.set(notificationId, false)
79
- this.lazyLists.forEach(l => l.update(this.readStatusMap))
80
- }
81
- }
82
-
83
- onUnreadNotificationChange(listener: UnreadNotificationListener) {
84
- this.unreadNotificationListeners.push(listener)
85
- return () => {
86
- pull(this.unreadNotificationListeners, listener)
87
- }
88
- }
89
-
90
- hasUnreadNotification() {
91
- return this.unreadNotification
92
- }
93
-
94
- reset() {
95
- this.lazyLists.clear()
96
- this.readStatusMap.clear()
97
- this.lastUnreadVerification = undefined
98
- this.unreadNotification = false
99
- }
100
-
101
- mute() {
102
- this.unreadNotificationListeners = []
103
- }
104
- }
1
+ import { pull } from 'lodash'
2
+ import { LazyNotificationList } from './LazyNotificationList'
3
+ import { LoadNotificationsFilters, NotificationConfig, UnreadNotificationListener } from './types'
4
+
5
+ const DEFAULT_POLLING_MS = 120000
6
+
7
+ export class NotificationController {
8
+ private lazyLists = new Map<number, LazyNotificationList>()
9
+ readonly config: NotificationConfig
10
+ private readStatusMap = new Map<string, boolean>()
11
+ private nextId = 1
12
+ private unreadNotification = false
13
+ private lastUnreadVerification: Date | undefined
14
+ private pollingTimeoutId: number | undefined
15
+ private unreadNotificationListeners: UnreadNotificationListener[] = []
16
+
17
+ constructor(config: NotificationConfig) {
18
+ this.config = config
19
+ config.pollingMS ??= DEFAULT_POLLING_MS
20
+ config.notificationsPath ??= '/notifications'
21
+ }
22
+
23
+ private hasUnreadNotificationInMemory() {
24
+ for (const [, read] of this.readStatusMap) {
25
+ if (!read.valueOf()) return true
26
+ }
27
+ return false
28
+ }
29
+
30
+ private setUnreadNotification(value: boolean) {
31
+ this.unreadNotification = value
32
+ if (!value) {
33
+ this.pollingTimeoutId = window.setTimeout(() => this.checkUnread(), this.config.pollingMS)
34
+ }
35
+ this.unreadNotificationListeners.forEach(l => l(value))
36
+ }
37
+
38
+ createLazyNotificationList(filters?: LoadNotificationsFilters): LazyNotificationList {
39
+ const list = new LazyNotificationList({
40
+ id: this.nextId++,
41
+ filters,
42
+ load: async (options) => {
43
+ const result = await this.config.load(options)
44
+ result.items.forEach((i) => {
45
+ this.readStatusMap.set(i.id, i.committed)
46
+ // updating the unread indicator, since it might be stale
47
+ if (!i.committed) this.setUnreadNotification(true)
48
+ })
49
+ this.lazyLists.forEach(l => l !== list && list.update(this.readStatusMap))
50
+ return result
51
+ },
52
+ })
53
+ this.lazyLists.set(list.id, list)
54
+ return list
55
+ }
56
+
57
+ destroyLazyNotificationList(id: number) {
58
+ this.lazyLists.get(id)?.mute()
59
+ this.lazyLists.delete(id)
60
+ }
61
+
62
+ async checkUnread() {
63
+ window.clearInterval(this.pollingTimeoutId)
64
+ this.setUnreadNotification(await this.config.checkForUnreadNotificationsSince(this.lastUnreadVerification))
65
+ this.lastUnreadVerification = new Date()
66
+ }
67
+
68
+ async markAsCommitted(notificationId: string) {
69
+ if (this.readStatusMap.get(notificationId)) return
70
+ this.readStatusMap.set(notificationId, true)
71
+ this.lazyLists.forEach(l => l.update(this.readStatusMap))
72
+ try {
73
+ await this.config.markAsCommitted(notificationId)
74
+ // update the notification indicator: this is an inconsistency, we should actually ask the API, but since it's an expensive
75
+ // operation in the backend, we won't, instead, we'll only check the notifications we have loaded in memory.
76
+ if (!this.hasUnreadNotificationInMemory()) this.setUnreadNotification(false)
77
+ } catch {
78
+ this.readStatusMap.set(notificationId, false)
79
+ this.lazyLists.forEach(l => l.update(this.readStatusMap))
80
+ }
81
+ }
82
+
83
+ onUnreadNotificationChange(listener: UnreadNotificationListener) {
84
+ this.unreadNotificationListeners.push(listener)
85
+ return () => {
86
+ pull(this.unreadNotificationListeners, listener)
87
+ }
88
+ }
89
+
90
+ hasUnreadNotification() {
91
+ return this.unreadNotification
92
+ }
93
+
94
+ reset() {
95
+ this.lazyLists.clear()
96
+ this.readStatusMap.clear()
97
+ this.lastUnreadVerification = undefined
98
+ this.unreadNotification = false
99
+ }
100
+
101
+ mute() {
102
+ this.unreadNotificationListeners = []
103
+ }
104
+ }
@@ -1,23 +1,23 @@
1
- import { createContext, useContext } from 'react'
2
- import { NotificationController } from './NotificationController'
3
-
4
- export interface Props {
5
- children: React.ReactNode,
6
- controller: NotificationController,
7
- }
8
-
9
- const context = createContext<NotificationController | undefined>(undefined)
10
-
11
- export const NotificationProvider = ({ children, controller }: Props) => (
12
- <context.Provider value={controller}>{children}</context.Provider>
13
- )
14
-
15
- export function useNotificationController() {
16
- const controller = useContext(context)
17
- if (!controller) {
18
- throw new Error(
19
- 'A NotificationController was requested, but no NotificationController is available in the React Context. Please, be sure to wrap your component in a NotificationProvider.',
20
- )
21
- }
22
- return controller
23
- }
1
+ import { createContext, useContext } from 'react'
2
+ import { NotificationController } from './NotificationController'
3
+
4
+ export interface Props {
5
+ children: React.ReactNode,
6
+ controller: NotificationController,
7
+ }
8
+
9
+ const context = createContext<NotificationController | undefined>(undefined)
10
+
11
+ export const NotificationProvider = ({ children, controller }: Props) => (
12
+ <context.Provider value={controller}>{children}</context.Provider>
13
+ )
14
+
15
+ export function useNotificationController() {
16
+ const controller = useContext(context)
17
+ if (!controller) {
18
+ throw new Error(
19
+ 'A NotificationController was requested, but no NotificationController is available in the React Context. Please, be sure to wrap your component in a NotificationProvider.',
20
+ )
21
+ }
22
+ return controller
23
+ }
@@ -1,98 +1,98 @@
1
- import { useLanguage } from '@stack-spot/portal-translate'
2
- import { useCallback, useRef, useState } from 'react'
3
- import { useManualRender } from '../../hooks/manual-render'
4
- import { useEffectOnce } from '../../hooks/use-effect-once'
5
- import { GetTenantNotificationsResponse } from '../../notifications'
6
- import { useNotificationController } from './context'
7
- import { LazyNotificationList } from './LazyNotificationList'
8
- import { LoadNotificationsFilters } from './types'
9
-
10
- export function useNotificationList(initialFilters: LoadNotificationsFilters = {}) {
11
- const { repaint } = useManualRender()
12
- const [status, setStatus] = useState<'startup' | 'idle' | 'error' | 'loading'>('startup')
13
- const error = useRef<any>()
14
- const controller = useNotificationController()
15
- const [filters, setFilters] = useState(initialFilters)
16
- const list = useRef<LazyNotificationList | undefined>()
17
-
18
- useEffectOnce(() => {
19
- async function start() {
20
- list.current = controller.createLazyNotificationList(initialFilters)
21
- list.current.subscribe(repaint)
22
- try {
23
- await list.current?.loadMore()
24
- setStatus('idle')
25
- } catch (e) {
26
- setStatus('error')
27
- error.current = e
28
- }
29
- }
30
- start()
31
- return () => {
32
- if (list.current) controller.destroyLazyNotificationList(list.current.id)
33
- }
34
- })
35
-
36
- const applyFilters = useCallback(async (newFilters: LoadNotificationsFilters) => {
37
- if (!list.current) return
38
- setStatus('loading')
39
- // since we're inside a useCallback, we don't have direct access to the current value of "filters". The only way we can access this
40
- // value is from the function "setFilters". But "setFilters" run async. For this reason, we wait the function to run and retrieve the
41
- // current value before continuing, hence the use of the await and promise below.
42
- const { next, prev } = await new Promise<{ next: LoadNotificationsFilters, prev: LoadNotificationsFilters }>((resolve) => {
43
- setFilters((filters) => {
44
- const prev = filters
45
- const next = { ...filters, ...newFilters }
46
- resolve({ prev, next })
47
- return next
48
- })
49
- })
50
- try {
51
- await list.current.applyFilters(next)
52
- setStatus('idle')
53
- error.current = undefined
54
- } catch (e) {
55
- setFilters(prev)
56
- if (error.current) {
57
- error.current = e
58
- setStatus('error')
59
- } else {
60
- setStatus('idle')
61
- }
62
- }
63
- }, [])
64
-
65
- return {
66
- status,
67
- items: list.current?.items ?? [],
68
- hasMore: list.current?.hasMore() ?? false,
69
- loadMore: () => list.current?.loadMore(),
70
- refresh: () => applyFilters(filters),
71
- applyFilters,
72
- filters,
73
- error: error.current,
74
- }
75
- }
76
-
77
- export function useGetNotificationTitleAndDescription({ content }: GetTenantNotificationsResponse) {
78
- const language = useLanguage()
79
- const { title, description } = content?.[language] || content?.en || {}
80
-
81
- return {
82
- title,
83
- description,
84
- }
85
- }
86
-
87
- export function useUnreadNotifications() {
88
- const [hasUnreadNotifications, setUnreadNotifications] = useState(false)
89
- const controller = useNotificationController()
90
-
91
- useEffectOnce(() => {
92
- const unsubscribe = controller.onUnreadNotificationChange(setUnreadNotifications)
93
- controller.checkUnread()
94
- return unsubscribe
95
- })
96
-
97
- return hasUnreadNotifications
98
- }
1
+ import { useLanguage } from '@stack-spot/portal-translate'
2
+ import { useCallback, useRef, useState } from 'react'
3
+ import { useManualRender } from '../../hooks/manual-render'
4
+ import { useEffectOnce } from '../../hooks/use-effect-once'
5
+ import { GetTenantNotificationsResponse } from '../../notifications'
6
+ import { useNotificationController } from './context'
7
+ import { LazyNotificationList } from './LazyNotificationList'
8
+ import { LoadNotificationsFilters } from './types'
9
+
10
+ export function useNotificationList(initialFilters: LoadNotificationsFilters = {}) {
11
+ const { repaint } = useManualRender()
12
+ const [status, setStatus] = useState<'startup' | 'idle' | 'error' | 'loading'>('startup')
13
+ const error = useRef<any>()
14
+ const controller = useNotificationController()
15
+ const [filters, setFilters] = useState(initialFilters)
16
+ const list = useRef<LazyNotificationList | undefined>()
17
+
18
+ useEffectOnce(() => {
19
+ async function start() {
20
+ list.current = controller.createLazyNotificationList(initialFilters)
21
+ list.current.subscribe(repaint)
22
+ try {
23
+ await list.current?.loadMore()
24
+ setStatus('idle')
25
+ } catch (e) {
26
+ setStatus('error')
27
+ error.current = e
28
+ }
29
+ }
30
+ start()
31
+ return () => {
32
+ if (list.current) controller.destroyLazyNotificationList(list.current.id)
33
+ }
34
+ })
35
+
36
+ const applyFilters = useCallback(async (newFilters: LoadNotificationsFilters) => {
37
+ if (!list.current) return
38
+ setStatus('loading')
39
+ // since we're inside a useCallback, we don't have direct access to the current value of "filters". The only way we can access this
40
+ // value is from the function "setFilters". But "setFilters" run async. For this reason, we wait the function to run and retrieve the
41
+ // current value before continuing, hence the use of the await and promise below.
42
+ const { next, prev } = await new Promise<{ next: LoadNotificationsFilters, prev: LoadNotificationsFilters }>((resolve) => {
43
+ setFilters((filters) => {
44
+ const prev = filters
45
+ const next = { ...filters, ...newFilters }
46
+ resolve({ prev, next })
47
+ return next
48
+ })
49
+ })
50
+ try {
51
+ await list.current.applyFilters(next)
52
+ setStatus('idle')
53
+ error.current = undefined
54
+ } catch (e) {
55
+ setFilters(prev)
56
+ if (error.current) {
57
+ error.current = e
58
+ setStatus('error')
59
+ } else {
60
+ setStatus('idle')
61
+ }
62
+ }
63
+ }, [])
64
+
65
+ return {
66
+ status,
67
+ items: list.current?.items ?? [],
68
+ hasMore: list.current?.hasMore() ?? false,
69
+ loadMore: () => list.current?.loadMore(),
70
+ refresh: () => applyFilters(filters),
71
+ applyFilters,
72
+ filters,
73
+ error: error.current,
74
+ }
75
+ }
76
+
77
+ export function useGetNotificationTitleAndDescription({ content }: GetTenantNotificationsResponse) {
78
+ const language = useLanguage()
79
+ const { title, description } = content?.[language] || content?.en || {}
80
+
81
+ return {
82
+ title,
83
+ description,
84
+ }
85
+ }
86
+
87
+ export function useUnreadNotifications() {
88
+ const [hasUnreadNotifications, setUnreadNotifications] = useState(false)
89
+ const controller = useNotificationController()
90
+
91
+ useEffectOnce(() => {
92
+ const unsubscribe = controller.onUnreadNotificationChange(setUnreadNotifications)
93
+ controller.checkUnread()
94
+ return unsubscribe
95
+ })
96
+
97
+ return hasUnreadNotifications
98
+ }
@@ -1,66 +1,66 @@
1
- import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
2
-
3
- export type NotificationPriority = 'HIGH' | 'MEDIUM' | 'LOW'
4
-
5
- export type NotificationContext = 'ACCOUNT' | 'STUDIO' | 'WORKSPACE'
6
-
7
- export interface LoadNotificationsFilters {
8
- committed?: boolean,
9
- criticality?: NotificationPriority,
10
- search?: string,
11
- context?: NotificationContext,
12
- size?: number,
13
- isBanner?: boolean,
14
- createdSince?: number,
15
- }
16
-
17
- export interface LoadNotificationsOptions extends LoadNotificationsFilters {
18
- page: number,
19
- isBanner?: boolean,
20
- }
21
-
22
- export type LazyNotificationListener = (notifications: GetTenantNotificationsResponse[], hasMore: boolean) => void
23
-
24
- export interface NotificationConfig {
25
- /**
26
- * Fetches the notifications according to the options passed as parameters.
27
- * @param options page, size and filters.
28
- * @returns a promise that resolves to a {@link ResponseModelGetTenantNotificationsResponse}.
29
- */
30
- load(options: LoadNotificationsOptions): Promise<ResponseModelGetTenantNotificationsResponse>,
31
- /**
32
- * Check for unread notifications since the date passed as parameter.
33
- * @param date the date to start the search from
34
- * @returns a promise that resolves to true if there are unread notifications or false otherwise.
35
- */
36
- checkForUnreadNotificationsSince(date?: Date): Promise<boolean>,
37
- /**
38
- * Marks the notification with the id passed as parameter as read (committed).
39
- * @param id the id of the notification.
40
- */
41
- markAsCommitted(id: string): Promise<void>,
42
- /**
43
- * How long (ms) we should wait before checking for unread notifications again.
44
- * @default 120000
45
- */
46
- pollingMS?: number,
47
- /**
48
- * Path to the notifications page.
49
- * @default '/notifications'
50
- */
51
- notificationsPath?: string,
52
- /**
53
- * A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
54
- *
55
- * Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
56
- *
57
- * @param id the id of the notification.
58
- */
59
- onClickAction?: (id: string) => void,
60
- /**
61
- * Function that is called whenever the "read more" button is clicked.
62
- */
63
- onClickViewNotification?: () => void,
64
- }
65
-
66
- export type UnreadNotificationListener = (hasUnreadNotification: boolean) => void
1
+ import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
2
+
3
+ export type NotificationPriority = 'HIGH' | 'MEDIUM' | 'LOW'
4
+
5
+ export type NotificationContext = 'ACCOUNT' | 'STUDIO' | 'WORKSPACE'
6
+
7
+ export interface LoadNotificationsFilters {
8
+ committed?: boolean,
9
+ criticality?: NotificationPriority,
10
+ search?: string,
11
+ context?: NotificationContext,
12
+ size?: number,
13
+ isBanner?: boolean,
14
+ createdSince?: number,
15
+ }
16
+
17
+ export interface LoadNotificationsOptions extends LoadNotificationsFilters {
18
+ page: number,
19
+ isBanner?: boolean,
20
+ }
21
+
22
+ export type LazyNotificationListener = (notifications: GetTenantNotificationsResponse[], hasMore: boolean) => void
23
+
24
+ export interface NotificationConfig {
25
+ /**
26
+ * Fetches the notifications according to the options passed as parameters.
27
+ * @param options page, size and filters.
28
+ * @returns a promise that resolves to a {@link ResponseModelGetTenantNotificationsResponse}.
29
+ */
30
+ load(options: LoadNotificationsOptions): Promise<ResponseModelGetTenantNotificationsResponse>,
31
+ /**
32
+ * Check for unread notifications since the date passed as parameter.
33
+ * @param date the date to start the search from
34
+ * @returns a promise that resolves to true if there are unread notifications or false otherwise.
35
+ */
36
+ checkForUnreadNotificationsSince(date?: Date): Promise<boolean>,
37
+ /**
38
+ * Marks the notification with the id passed as parameter as read (committed).
39
+ * @param id the id of the notification.
40
+ */
41
+ markAsCommitted(id: string): Promise<void>,
42
+ /**
43
+ * How long (ms) we should wait before checking for unread notifications again.
44
+ * @default 120000
45
+ */
46
+ pollingMS?: number,
47
+ /**
48
+ * Path to the notifications page.
49
+ * @default '/notifications'
50
+ */
51
+ notificationsPath?: string,
52
+ /**
53
+ * A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
54
+ *
55
+ * Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
56
+ *
57
+ * @param id the id of the notification.
58
+ */
59
+ onClickAction?: (id: string) => void,
60
+ /**
61
+ * Function that is called whenever the "read more" button is clicked.
62
+ */
63
+ onClickViewNotification?: () => void,
64
+ }
65
+
66
+ export type UnreadNotificationListener = (hasUnreadNotification: boolean) => void
package/src/hooks/date.ts CHANGED
@@ -1,31 +1,31 @@
1
- import { Language, useLanguage } from '@stack-spot/portal-translate'
2
- import { format } from 'date-fns'
3
- import { ptBR } from 'date-fns/locale'
4
-
5
- function formatDate(date: string, locale: Language, includeTime: boolean) {
6
- const time = includeTime ? ' HH:mm' : ''
7
- return locale === 'pt'
8
- ? format(new Date(date), `dd/MM/yyyy${time}`, { locale: ptBR })
9
- : format(new Date(date), `MMMM dd yyyy${time}`)
10
- }
11
-
12
- function formatSimpleDate(date: string, locale: Language) {
13
-
14
- return locale === 'pt'
15
- ? format(new Date(date), 'dd/MM/yyyy', { locale: ptBR })
16
- : format(new Date(date), 'MM/dd/yyyy')
17
- }
18
-
19
- /**
20
- * A utility for formatting dates.
21
- * @param date
22
- * @returns a date formatter object.
23
- */
24
- export function useDateFormatter() {
25
- const locale = useLanguage()
26
- return {
27
- formatDate: (date?: string) => date ? formatDate(date, locale, false) : undefined,
28
- formatDateWithTime: (date?: string) => date ? formatDate(date, locale, true) : undefined,
29
- formatSimpleDate: (date?: string) => date ? formatSimpleDate(date, locale) : undefined,
30
- }
31
- }
1
+ import { Language, useLanguage } from '@stack-spot/portal-translate'
2
+ import { format } from 'date-fns'
3
+ import { ptBR } from 'date-fns/locale'
4
+
5
+ function formatDate(date: string, locale: Language, includeTime: boolean) {
6
+ const time = includeTime ? ' HH:mm' : ''
7
+ return locale === 'pt'
8
+ ? format(new Date(date), `dd/MM/yyyy${time}`, { locale: ptBR })
9
+ : format(new Date(date), `MMMM dd yyyy${time}`)
10
+ }
11
+
12
+ function formatSimpleDate(date: string, locale: Language) {
13
+
14
+ return locale === 'pt'
15
+ ? format(new Date(date), 'dd/MM/yyyy', { locale: ptBR })
16
+ : format(new Date(date), 'MM/dd/yyyy')
17
+ }
18
+
19
+ /**
20
+ * A utility for formatting dates.
21
+ * @param date
22
+ * @returns a date formatter object.
23
+ */
24
+ export function useDateFormatter() {
25
+ const locale = useLanguage()
26
+ return {
27
+ formatDate: (date?: string) => date ? formatDate(date, locale, false) : undefined,
28
+ formatDateWithTime: (date?: string) => date ? formatDate(date, locale, true) : undefined,
29
+ formatSimpleDate: (date?: string) => date ? formatSimpleDate(date, locale) : undefined,
30
+ }
31
+ }