@adobe/react-native-aepmessaging 7.2.1 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/RCTAEPMessaging.podspec +1 -1
  2. package/README.md +145 -16
  3. package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingConstants.java +3 -0
  4. package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingModule.java +103 -33
  5. package/babel.config.js +3 -0
  6. package/dist/module/Messaging.js +334 -0
  7. package/dist/module/Messaging.js.map +1 -0
  8. package/dist/module/index.js +30 -0
  9. package/dist/module/index.js.map +1 -0
  10. package/dist/module/models/ContentCard.js +24 -0
  11. package/dist/module/models/ContentCard.js.map +1 -0
  12. package/dist/{models → module/models}/HTMLProposition.js +8 -9
  13. package/dist/module/models/HTMLProposition.js.map +1 -0
  14. package/dist/module/models/InAppMessage.js +4 -0
  15. package/dist/module/models/InAppMessage.js.map +1 -0
  16. package/dist/module/models/JSONProposition.js +22 -0
  17. package/dist/module/models/JSONProposition.js.map +1 -0
  18. package/dist/module/models/Message.js +182 -0
  19. package/dist/module/models/Message.js.map +1 -0
  20. package/dist/module/models/MessagingDelegate.js +4 -0
  21. package/dist/module/models/MessagingDelegate.js.map +1 -0
  22. package/dist/module/models/MessagingEdgeEventType.js +24 -0
  23. package/dist/module/models/MessagingEdgeEventType.js.map +1 -0
  24. package/dist/module/models/MessagingProposition.js +57 -0
  25. package/dist/module/models/MessagingProposition.js.map +1 -0
  26. package/dist/module/models/MessagingPropositionItem.js +4 -0
  27. package/dist/module/models/MessagingPropositionItem.js.map +1 -0
  28. package/dist/module/models/PersonalizationSchema.js +26 -0
  29. package/dist/module/models/PersonalizationSchema.js.map +1 -0
  30. package/dist/module/models/PropositionItem.js +113 -0
  31. package/dist/module/models/PropositionItem.js.map +1 -0
  32. package/dist/module/models/ScopeDetails.js +2 -0
  33. package/dist/module/models/ScopeDetails.js.map +1 -0
  34. package/dist/{models/JSONProposition.js → module/models/index.js} +14 -12
  35. package/dist/module/models/index.js.map +1 -0
  36. package/dist/module/ui/components/Button/Button.js +57 -0
  37. package/dist/module/ui/components/Button/Button.js.map +1 -0
  38. package/dist/module/ui/components/Button/Button.spec.js +476 -0
  39. package/dist/module/ui/components/Button/Button.spec.js.map +1 -0
  40. package/dist/module/ui/components/ContentCardView/ContentCardView.js +257 -0
  41. package/dist/module/ui/components/ContentCardView/ContentCardView.js.map +1 -0
  42. package/dist/module/ui/components/ContentCardView/ContentCardView.spec.js +363 -0
  43. package/dist/module/ui/components/ContentCardView/ContentCardView.spec.js.map +1 -0
  44. package/dist/module/ui/components/DismissButton/DismissButton.js +70 -0
  45. package/dist/module/ui/components/DismissButton/DismissButton.js.map +1 -0
  46. package/dist/module/ui/components/DismissButton/DismissButton.spec.js +279 -0
  47. package/dist/module/ui/components/DismissButton/DismissButton.spec.js.map +1 -0
  48. package/dist/module/ui/components/FullScreenCenterView/FullScreenCenterView.js +34 -0
  49. package/dist/module/ui/components/FullScreenCenterView/FullScreenCenterView.js.map +1 -0
  50. package/dist/module/ui/components/Inbox/EmptyState.js +64 -0
  51. package/dist/module/ui/components/Inbox/EmptyState.js.map +1 -0
  52. package/dist/module/ui/components/Inbox/Inbox.js +235 -0
  53. package/dist/module/ui/components/Inbox/Inbox.js.map +1 -0
  54. package/dist/module/ui/components/Inbox/Inbox.spec.js +847 -0
  55. package/dist/module/ui/components/Inbox/Inbox.spec.js.map +1 -0
  56. package/dist/module/ui/components/Pagination/Pagination.js +176 -0
  57. package/dist/module/ui/components/Pagination/Pagination.js.map +1 -0
  58. package/dist/module/ui/components/Pagination/Pagination.spec.js +193 -0
  59. package/dist/module/ui/components/Pagination/Pagination.spec.js.map +1 -0
  60. package/dist/module/ui/components/UnreadIcon/UnreadIcon.js +184 -0
  61. package/dist/module/ui/components/UnreadIcon/UnreadIcon.js.map +1 -0
  62. package/dist/module/ui/components/UnreadIcon/UnreadIcon.spec.js +815 -0
  63. package/dist/module/ui/components/UnreadIcon/UnreadIcon.spec.js.map +1 -0
  64. package/dist/{models/ContentCard.js → module/ui/components/index.js} +12 -12
  65. package/dist/module/ui/components/index.js.map +1 -0
  66. package/dist/module/ui/hooks/index.js +18 -0
  67. package/dist/module/ui/hooks/index.js.map +1 -0
  68. package/dist/module/ui/hooks/useAspectRatio.js +33 -0
  69. package/dist/module/ui/hooks/useAspectRatio.js.map +1 -0
  70. package/dist/module/ui/hooks/useAspectRatio.spec.js +65 -0
  71. package/dist/module/ui/hooks/useAspectRatio.spec.js.map +1 -0
  72. package/dist/module/ui/hooks/useContentCardUI.js +51 -0
  73. package/dist/module/ui/hooks/useContentCardUI.js.map +1 -0
  74. package/dist/module/ui/hooks/useContentCardUI.spec.js +85 -0
  75. package/dist/module/ui/hooks/useContentCardUI.spec.js.map +1 -0
  76. package/dist/module/ui/hooks/useInbox.js +49 -0
  77. package/dist/module/ui/hooks/useInbox.js.map +1 -0
  78. package/dist/module/ui/hooks/useInbox.spec.js +93 -0
  79. package/dist/module/ui/hooks/useInbox.spec.js.map +1 -0
  80. package/dist/module/ui/hooks/useInboxSettings.js +26 -0
  81. package/dist/module/ui/hooks/useInboxSettings.js.map +1 -0
  82. package/dist/module/ui/hooks/useInboxSettings.spec.js +50 -0
  83. package/dist/module/ui/hooks/useInboxSettings.spec.js.map +1 -0
  84. package/dist/module/ui/index.js +10 -0
  85. package/dist/module/ui/index.js.map +1 -0
  86. package/dist/module/ui/providers/InboxProvider.js +27 -0
  87. package/dist/module/ui/providers/InboxProvider.js.map +1 -0
  88. package/dist/module/ui/theme/Theme.js +2 -0
  89. package/dist/module/ui/theme/Theme.js.map +1 -0
  90. package/dist/module/ui/theme/ThemeProvider.js +112 -0
  91. package/dist/module/ui/theme/ThemeProvider.js.map +1 -0
  92. package/dist/{models/InAppMessage.js → module/ui/theme/index.js} +6 -3
  93. package/dist/module/ui/theme/index.js.map +1 -0
  94. package/dist/module/ui/types/ContentViewEvent.js +2 -0
  95. package/dist/module/ui/types/ContentViewEvent.js.map +1 -0
  96. package/dist/module/ui/types/Templates.js +26 -0
  97. package/dist/module/ui/types/Templates.js.map +1 -0
  98. package/dist/{models/ScopeDetails.js → module/ui/types/index.js} +6 -3
  99. package/dist/module/ui/types/index.js.map +1 -0
  100. package/dist/module/ui/utils/generateCardHash.js +50 -0
  101. package/dist/module/ui/utils/generateCardHash.js.map +1 -0
  102. package/dist/module/ui/utils/generateCardHash.spec.js +103 -0
  103. package/dist/module/ui/utils/generateCardHash.spec.js.map +1 -0
  104. package/dist/module/ui/utils/inboxStorage.js +65 -0
  105. package/dist/module/ui/utils/inboxStorage.js.map +1 -0
  106. package/dist/module/ui/utils/inboxStorage.spec.js +123 -0
  107. package/dist/module/ui/utils/inboxStorage.spec.js.map +1 -0
  108. package/dist/module/ui/utils/index.js +5 -0
  109. package/dist/module/ui/utils/index.js.map +1 -0
  110. package/dist/{Messaging.d.ts → typescript/Messaging.d.ts} +23 -7
  111. package/dist/typescript/Messaging.d.ts.map +1 -0
  112. package/dist/{index.d.ts → typescript/index.d.ts} +4 -2
  113. package/dist/typescript/index.d.ts.map +1 -0
  114. package/dist/typescript/models/ContentCard.d.ts +57 -0
  115. package/dist/typescript/models/ContentCard.d.ts.map +1 -0
  116. package/dist/{models → typescript/models}/HTMLProposition.d.ts +1 -0
  117. package/dist/typescript/models/HTMLProposition.d.ts.map +1 -0
  118. package/dist/{models → typescript/models}/InAppMessage.d.ts +1 -0
  119. package/dist/typescript/models/InAppMessage.d.ts.map +1 -0
  120. package/dist/{models → typescript/models}/JSONProposition.d.ts +1 -0
  121. package/dist/typescript/models/JSONProposition.d.ts.map +1 -0
  122. package/dist/{models → typescript/models}/Message.d.ts +14 -0
  123. package/dist/typescript/models/Message.d.ts.map +1 -0
  124. package/dist/{models → typescript/models}/MessagingDelegate.d.ts +1 -0
  125. package/dist/typescript/models/MessagingDelegate.d.ts.map +1 -0
  126. package/dist/{models → typescript/models}/MessagingEdgeEventType.d.ts +1 -0
  127. package/dist/typescript/models/MessagingEdgeEventType.d.ts.map +1 -0
  128. package/dist/{models → typescript/models}/MessagingProposition.d.ts +1 -0
  129. package/dist/typescript/models/MessagingProposition.d.ts.map +1 -0
  130. package/dist/{models → typescript/models}/MessagingPropositionItem.d.ts +1 -0
  131. package/dist/typescript/models/MessagingPropositionItem.d.ts.map +1 -0
  132. package/dist/{models → typescript/models}/PersonalizationSchema.d.ts +2 -0
  133. package/dist/typescript/models/PersonalizationSchema.d.ts.map +1 -0
  134. package/dist/{models → typescript/models}/PropositionItem.d.ts +1 -0
  135. package/dist/typescript/models/PropositionItem.d.ts.map +1 -0
  136. package/dist/{models → typescript/models}/ScopeDetails.d.ts +1 -0
  137. package/dist/typescript/models/ScopeDetails.d.ts.map +1 -0
  138. package/dist/typescript/models/index.d.ts +11 -0
  139. package/dist/typescript/models/index.d.ts.map +1 -0
  140. package/dist/typescript/ui/components/Button/Button.d.ts +14 -0
  141. package/dist/typescript/ui/components/Button/Button.d.ts.map +1 -0
  142. package/dist/typescript/ui/components/Button/Button.spec.d.ts +2 -0
  143. package/dist/typescript/ui/components/Button/Button.spec.d.ts.map +1 -0
  144. package/dist/typescript/ui/components/ContentCardView/ContentCardView.d.ts +39 -0
  145. package/dist/typescript/ui/components/ContentCardView/ContentCardView.d.ts.map +1 -0
  146. package/dist/typescript/ui/components/ContentCardView/ContentCardView.spec.d.ts +2 -0
  147. package/dist/typescript/ui/components/ContentCardView/ContentCardView.spec.d.ts.map +1 -0
  148. package/dist/typescript/ui/components/DismissButton/DismissButton.d.ts +13 -0
  149. package/dist/typescript/ui/components/DismissButton/DismissButton.d.ts.map +1 -0
  150. package/dist/typescript/ui/components/DismissButton/DismissButton.spec.d.ts +2 -0
  151. package/dist/typescript/ui/components/DismissButton/DismissButton.spec.d.ts.map +1 -0
  152. package/dist/typescript/ui/components/FullScreenCenterView/FullScreenCenterView.d.ts +5 -0
  153. package/dist/typescript/ui/components/FullScreenCenterView/FullScreenCenterView.d.ts.map +1 -0
  154. package/dist/typescript/ui/components/Inbox/EmptyState.d.ts +19 -0
  155. package/dist/typescript/ui/components/Inbox/EmptyState.d.ts.map +1 -0
  156. package/dist/typescript/ui/components/Inbox/Inbox.d.ts +21 -0
  157. package/dist/typescript/ui/components/Inbox/Inbox.d.ts.map +1 -0
  158. package/dist/typescript/ui/components/Inbox/Inbox.spec.d.ts +2 -0
  159. package/dist/typescript/ui/components/Inbox/Inbox.spec.d.ts.map +1 -0
  160. package/dist/typescript/ui/components/Pagination/Pagination.d.ts +14 -0
  161. package/dist/typescript/ui/components/Pagination/Pagination.d.ts.map +1 -0
  162. package/dist/typescript/ui/components/Pagination/Pagination.spec.d.ts +2 -0
  163. package/dist/typescript/ui/components/Pagination/Pagination.spec.d.ts.map +1 -0
  164. package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.d.ts +14 -0
  165. package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.d.ts.map +1 -0
  166. package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.spec.d.ts +2 -0
  167. package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.spec.d.ts.map +1 -0
  168. package/dist/typescript/ui/components/index.d.ts +10 -0
  169. package/dist/typescript/ui/components/index.d.ts.map +1 -0
  170. package/dist/typescript/ui/hooks/index.d.ts +4 -0
  171. package/dist/typescript/ui/hooks/index.d.ts.map +1 -0
  172. package/dist/typescript/ui/hooks/useAspectRatio.d.ts +3 -0
  173. package/dist/typescript/ui/hooks/useAspectRatio.d.ts.map +1 -0
  174. package/dist/typescript/ui/hooks/useAspectRatio.spec.d.ts +2 -0
  175. package/dist/typescript/ui/hooks/useAspectRatio.spec.d.ts.map +1 -0
  176. package/dist/typescript/ui/hooks/useContentCardUI.d.ts +14 -0
  177. package/dist/typescript/ui/hooks/useContentCardUI.d.ts.map +1 -0
  178. package/dist/typescript/ui/hooks/useContentCardUI.spec.d.ts +2 -0
  179. package/dist/typescript/ui/hooks/useContentCardUI.spec.d.ts.map +1 -0
  180. package/dist/typescript/ui/hooks/useInbox.d.ts +12 -0
  181. package/dist/typescript/ui/hooks/useInbox.d.ts.map +1 -0
  182. package/dist/typescript/ui/hooks/useInbox.spec.d.ts +2 -0
  183. package/dist/typescript/ui/hooks/useInbox.spec.d.ts.map +1 -0
  184. package/dist/typescript/ui/hooks/useInboxSettings.d.ts +7 -0
  185. package/dist/typescript/ui/hooks/useInboxSettings.d.ts.map +1 -0
  186. package/dist/typescript/ui/hooks/useInboxSettings.spec.d.ts +2 -0
  187. package/dist/typescript/ui/hooks/useInboxSettings.spec.d.ts.map +1 -0
  188. package/dist/typescript/ui/index.d.ts +8 -0
  189. package/dist/typescript/ui/index.d.ts.map +1 -0
  190. package/dist/typescript/ui/providers/InboxProvider.d.ts +56 -0
  191. package/dist/typescript/ui/providers/InboxProvider.d.ts.map +1 -0
  192. package/dist/typescript/ui/theme/Theme.d.ts +44 -0
  193. package/dist/typescript/ui/theme/Theme.d.ts.map +1 -0
  194. package/dist/typescript/ui/theme/ThemeProvider.d.ts +21 -0
  195. package/dist/typescript/ui/theme/ThemeProvider.d.ts.map +1 -0
  196. package/dist/typescript/ui/theme/index.d.ts +3 -0
  197. package/dist/typescript/ui/theme/index.d.ts.map +1 -0
  198. package/dist/typescript/ui/types/ContentViewEvent.d.ts +9 -0
  199. package/dist/typescript/ui/types/ContentViewEvent.d.ts.map +1 -0
  200. package/dist/typescript/ui/types/Templates.d.ts +43 -0
  201. package/dist/typescript/ui/types/Templates.d.ts.map +1 -0
  202. package/dist/typescript/ui/types/index.d.ts +3 -0
  203. package/dist/typescript/ui/types/index.d.ts.map +1 -0
  204. package/dist/typescript/ui/utils/generateCardHash.d.ts +21 -0
  205. package/dist/typescript/ui/utils/generateCardHash.d.ts.map +1 -0
  206. package/dist/typescript/ui/utils/generateCardHash.spec.d.ts +2 -0
  207. package/dist/typescript/ui/utils/generateCardHash.spec.d.ts.map +1 -0
  208. package/dist/typescript/ui/utils/inboxStorage.d.ts +20 -0
  209. package/dist/typescript/ui/utils/inboxStorage.d.ts.map +1 -0
  210. package/dist/typescript/ui/utils/inboxStorage.spec.d.ts +2 -0
  211. package/dist/typescript/ui/utils/inboxStorage.spec.d.ts.map +1 -0
  212. package/dist/typescript/ui/utils/index.d.ts +3 -0
  213. package/dist/typescript/ui/utils/index.d.ts.map +1 -0
  214. package/ios/src/RCTAEPMessaging.mm +15 -0
  215. package/ios/src/RCTAEPMessaging.swift +61 -3
  216. package/ios/src/RCTAEPMessagingConstants.swift +4 -1
  217. package/jest.config.js +15 -0
  218. package/package.json +33 -5
  219. package/src/Messaging.ts +288 -32
  220. package/src/index.ts +3 -3
  221. package/src/models/ContentCard.ts +52 -27
  222. package/src/models/HTMLProposition.ts +1 -1
  223. package/src/models/JSONProposition.ts +1 -1
  224. package/src/models/Message.ts +50 -0
  225. package/src/models/PersonalizationSchema.ts +1 -0
  226. package/src/models/index.ts +22 -0
  227. package/src/ui/components/Button/Button.spec.tsx +496 -0
  228. package/src/ui/components/Button/Button.tsx +76 -0
  229. package/src/ui/components/ContentCardView/ContentCardView.spec.tsx +278 -0
  230. package/src/ui/components/ContentCardView/ContentCardView.tsx +400 -0
  231. package/src/ui/components/DismissButton/DismissButton.spec.tsx +314 -0
  232. package/src/ui/components/DismissButton/DismissButton.tsx +100 -0
  233. package/src/ui/components/FullScreenCenterView/FullScreenCenterView.tsx +32 -0
  234. package/src/ui/components/Inbox/EmptyState.tsx +89 -0
  235. package/src/ui/components/Inbox/Inbox.spec.tsx +478 -0
  236. package/src/ui/components/Inbox/Inbox.tsx +275 -0
  237. package/src/ui/components/Pagination/Pagination.spec.tsx +159 -0
  238. package/src/ui/components/Pagination/Pagination.tsx +222 -0
  239. package/src/ui/components/UnreadIcon/UnreadIcon.spec.tsx +878 -0
  240. package/src/ui/components/UnreadIcon/UnreadIcon.tsx +234 -0
  241. package/src/ui/components/index.ts +22 -0
  242. package/{dist/models/MessagingPropositionItem.js → src/ui/hooks/index.ts} +5 -4
  243. package/src/ui/hooks/useAspectRatio.spec.tsx +66 -0
  244. package/src/ui/hooks/useAspectRatio.tsx +39 -0
  245. package/src/ui/hooks/useContentCardUI.spec.tsx +82 -0
  246. package/src/ui/hooks/useContentCardUI.ts +48 -0
  247. package/src/ui/hooks/useInbox.spec.tsx +87 -0
  248. package/src/ui/hooks/useInbox.ts +46 -0
  249. package/src/ui/hooks/useInboxSettings.spec.tsx +41 -0
  250. package/src/ui/hooks/useInboxSettings.ts +24 -0
  251. package/src/ui/index.ts +7 -0
  252. package/src/ui/providers/InboxProvider.tsx +79 -0
  253. package/src/ui/theme/Theme.ts +57 -0
  254. package/src/ui/theme/ThemeProvider.tsx +120 -0
  255. package/src/ui/theme/index.ts +14 -0
  256. package/src/ui/types/ContentViewEvent.ts +20 -0
  257. package/src/ui/types/Templates.ts +77 -0
  258. package/src/ui/types/index.ts +14 -0
  259. package/src/ui/utils/generateCardHash.spec.tsx +86 -0
  260. package/src/ui/utils/generateCardHash.ts +59 -0
  261. package/src/ui/utils/inboxStorage.spec.tsx +136 -0
  262. package/src/ui/utils/inboxStorage.ts +64 -0
  263. package/src/ui/utils/index.ts +3 -0
  264. package/tutorials/ContentCardCustomizationGuide.md +661 -0
  265. package/tutorials/ContentCards.md +419 -0
  266. package/tutorials/In-App Messaging.md +31 -0
  267. package/tutorials/Inbox.md +515 -0
  268. package/tutorials/resources/image-only-template.png +0 -0
  269. package/tutorials/resources/large-image-template.png +0 -0
  270. package/tutorials/resources/small-image-template.png +0 -0
  271. package/dist/Messaging.js +0 -151
  272. package/dist/Messaging.js.map +0 -1
  273. package/dist/index.js +0 -34
  274. package/dist/index.js.map +0 -1
  275. package/dist/models/ContentCard.d.ts +0 -51
  276. package/dist/models/ContentCard.js.map +0 -1
  277. package/dist/models/HTMLProposition.js.map +0 -1
  278. package/dist/models/InAppMessage.js.map +0 -1
  279. package/dist/models/JSONProposition.js.map +0 -1
  280. package/dist/models/Message.js +0 -114
  281. package/dist/models/Message.js.map +0 -1
  282. package/dist/models/MessagingDelegate.js +0 -14
  283. package/dist/models/MessagingDelegate.js.map +0 -1
  284. package/dist/models/MessagingEdgeEventType.js +0 -24
  285. package/dist/models/MessagingEdgeEventType.js.map +0 -1
  286. package/dist/models/MessagingProposition.js +0 -59
  287. package/dist/models/MessagingProposition.js.map +0 -1
  288. package/dist/models/MessagingPropositionItem.js.map +0 -1
  289. package/dist/models/PersonalizationSchema.js +0 -25
  290. package/dist/models/PersonalizationSchema.js.map +0 -1
  291. package/dist/models/PropositionItem.js +0 -78
  292. package/dist/models/PropositionItem.js.map +0 -1
  293. package/dist/models/ScopeDetails.js.map +0 -1
@@ -0,0 +1,234 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific
10
+ language governing permissions and limitations under the License.
11
+ */
12
+ import { useEffect, useMemo, useState } from 'react';
13
+ import {
14
+ Image,
15
+ ImageProps,
16
+ ImageStyle,
17
+ StyleSheet,
18
+ View,
19
+ ViewProps,
20
+ ViewStyle,
21
+ } from 'react-native';
22
+ import useInboxSettings from '../../hooks/useInboxSettings';
23
+ import { SettingsPlacement } from '../../providers/InboxProvider';
24
+ import { useTheme } from '../../theme';
25
+
26
+ export interface UnreadIconProps extends ViewProps {
27
+ imageStyle?: ImageStyle;
28
+ containerStyle?: ViewStyle;
29
+ source?: ImageProps['source'];
30
+ darkSource?: ImageProps['source'];
31
+ size?: number;
32
+ position?: SettingsPlacement;
33
+ type?: 'dot' | 'image';
34
+ }
35
+
36
+ interface DotProps {
37
+ size: number;
38
+ backgroundColor?: string;
39
+ }
40
+
41
+ const Dot = ({ size, backgroundColor }: DotProps) => (
42
+ <View
43
+ style={[
44
+ styles.dot,
45
+ {
46
+ width: size,
47
+ height: size,
48
+ borderRadius: size / 2,
49
+ backgroundColor
50
+ }
51
+ ]}
52
+ />
53
+ );
54
+
55
+ const UnreadIcon = ({
56
+ imageStyle,
57
+ containerStyle,
58
+ source,
59
+ darkSource,
60
+ size = 20,
61
+ position = 'topright',
62
+ type = 'dot',
63
+ style,
64
+ ...props
65
+ }: UnreadIconProps) => {
66
+ const { colors, isDark } = useTheme();
67
+ const settings = useInboxSettings();
68
+ const [imageLoadError, setImageLoadError] = useState(false);
69
+
70
+ // Get unread indicator settings from context
71
+ const unreadSettings = settings?.content.unread_indicator;
72
+
73
+ // Use settings from context with fallbacks to props
74
+ const displayPosition = unreadSettings?.unread_icon?.placement ?? position;
75
+
76
+ const imageSource = unreadSettings?.unread_icon?.image?.url ?
77
+ { uri: unreadSettings.unread_icon.image.url } : source;
78
+ const darkImageSource = unreadSettings?.unread_icon?.image?.darkUrl ?
79
+ { uri: unreadSettings.unread_icon.image.darkUrl } : darkSource;
80
+
81
+ // Determine if we should render as image type (only if we have valid URLs)
82
+ const hasImageUrl = Boolean(
83
+ unreadSettings?.unread_icon?.image?.url ||
84
+ unreadSettings?.unread_icon?.image?.darkUrl ||
85
+ imageSource ||
86
+ darkImageSource
87
+ );
88
+ const renderType = hasImageUrl ? 'image' : type;
89
+
90
+ // Reset error state when image source changes
91
+ useEffect(() => {
92
+ setImageLoadError(false);
93
+ }, [imageSource, darkImageSource]);
94
+
95
+ const positionStyle = useMemo(() => {
96
+ switch (displayPosition) {
97
+ case 'topleft':
98
+ return styles.positionTopLeft;
99
+ case 'topright':
100
+ return styles.positionTopRight;
101
+ case 'bottomleft':
102
+ return styles.positionBottomLeft;
103
+ case 'bottomright':
104
+ return styles.positionBottomRight;
105
+ default:
106
+ return styles.positionTopRight;
107
+ }
108
+ }, [displayPosition]);
109
+
110
+ const finalImageSource = useMemo(() =>
111
+ isDark && darkImageSource ? darkImageSource : imageSource,
112
+ [isDark, darkImageSource, imageSource]
113
+ );
114
+
115
+ const content = useMemo(() => {
116
+ // Check if we should show dot instead of image based on URL availability
117
+ const isEmptyUrlForCurrentMode = () => {
118
+ const imageSettings = unreadSettings?.unread_icon?.image;
119
+ if (!imageSettings) return false;
120
+
121
+ if (isDark) {
122
+ // In dark mode, show dot if darkUrl is empty string or if both darkUrl doesn't exist and url is empty
123
+ return imageSettings.darkUrl === '' ||
124
+ (!imageSettings.darkUrl && imageSettings.url === '');
125
+ }
126
+
127
+ // In light mode, show dot if url is empty string
128
+ return imageSettings.url === '';
129
+ };
130
+
131
+ // If URL is explicitly empty string for current mode, show dot
132
+ if (isEmptyUrlForCurrentMode()) {
133
+ return <Dot size={size} backgroundColor={colors.dotColor} />;
134
+ }
135
+
136
+ // If image failed to load, fallback to dot
137
+ if (renderType === 'image' && imageLoadError) {
138
+ return <Dot size={size} backgroundColor={colors.dotColor} />;
139
+ }
140
+
141
+ if (renderType === 'image' && (imageSource || darkImageSource)) {
142
+ return (
143
+ <Image
144
+ source={finalImageSource}
145
+ style={[
146
+ styles.image,
147
+ { width: size, height: size },
148
+ imageStyle
149
+ ]}
150
+ resizeMode="contain"
151
+ onError={(error) => {
152
+ console.warn('Failed to load unread icon image:', error.nativeEvent.error);
153
+ setImageLoadError(true);
154
+ }}
155
+ />
156
+ );
157
+ }
158
+
159
+ // Default dot type
160
+ return <Dot size={size} backgroundColor={colors.dotColor} />;
161
+ }, [
162
+ isDark,
163
+ unreadSettings?.unread_icon?.image,
164
+ size,
165
+ colors.dotColor,
166
+ renderType,
167
+ imageLoadError,
168
+ imageSource,
169
+ darkImageSource,
170
+ finalImageSource,
171
+ imageStyle
172
+ ]);
173
+
174
+ return (
175
+ <View
176
+ style={[
177
+ styles.container,
178
+ positionStyle,
179
+ { minWidth: size, minHeight: size },
180
+ containerStyle,
181
+ style
182
+ ]}
183
+ {...props}
184
+ >
185
+ {content}
186
+ </View>
187
+ );
188
+ };
189
+
190
+ export default UnreadIcon;
191
+
192
+ const styles = StyleSheet.create({
193
+ container: {
194
+ position: 'absolute',
195
+ justifyContent: 'center',
196
+ alignItems: 'center',
197
+ },
198
+ positionTopLeft: {
199
+ top: 6,
200
+ left: 6,
201
+ },
202
+ positionTopRight: {
203
+ top: 6,
204
+ right: 6,
205
+ },
206
+ positionBottomLeft: {
207
+ bottom: 6,
208
+ left: 6,
209
+ },
210
+ positionBottomRight: {
211
+ bottom: 6,
212
+ right: 6,
213
+ },
214
+ dot: {
215
+ shadowColor: '#000',
216
+ shadowOffset: {
217
+ width: 0,
218
+ height: 1,
219
+ },
220
+ shadowOpacity: 0.22,
221
+ shadowRadius: 2.22,
222
+ elevation: 3,
223
+ },
224
+ image: {
225
+ shadowColor: '#000',
226
+ shadowOffset: {
227
+ width: 0,
228
+ height: 1,
229
+ },
230
+ shadowOpacity: 0.22,
231
+ shadowRadius: 2.22,
232
+ elevation: 3,
233
+ }
234
+ });
@@ -0,0 +1,22 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific
10
+ language governing permissions and limitations under the License.
11
+ */
12
+
13
+ export * from "./Button/Button";
14
+ export * from "./Inbox/Inbox";
15
+ export * from "./ContentCardView/ContentCardView";
16
+ export * from "../types/ContentViewEvent";
17
+ export * from "./Pagination/Pagination";
18
+ export * from "./UnreadIcon/UnreadIcon";
19
+ export { default as EmptyState } from "./Inbox/EmptyState";
20
+
21
+ export { ThemeProvider } from "../theme/ThemeProvider";
22
+ export type { Themes } from "../theme/Theme";
@@ -1,6 +1,5 @@
1
- "use strict";
2
1
  /*
3
- Copyright 2023 Adobe. All rights reserved.
2
+ Copyright 2026 Adobe. All rights reserved.
4
3
  This file is licensed to you under the Apache License, Version 2.0 (the
5
4
  "License"); you may not use this file except in compliance with the License.
6
5
  You may obtain a copy of the License at
@@ -10,5 +9,7 @@
10
9
  ANY KIND, either express or implied. See the License for the specific
11
10
  language governing permissions and limitations under the License.
12
11
  */
13
- Object.defineProperty(exports, "__esModule", { value: true });
14
- //# sourceMappingURL=MessagingPropositionItem.js.map
12
+
13
+ export * from './useContentCardUI';
14
+ export * from './useInbox';
15
+ export * from './useInboxSettings';
@@ -0,0 +1,66 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { renderHook, waitFor } from '@testing-library/react-native';
14
+ import { Image } from 'react-native';
15
+ import useAspectRatio from './useAspectRatio';
16
+
17
+ describe('useAspectRatio', () => {
18
+ afterEach(() => {
19
+ jest.restoreAllMocks();
20
+ });
21
+
22
+ it('returns 1 when uri is undefined', () => {
23
+ const { result } = renderHook(() => useAspectRatio());
24
+ expect(result.current).toBe(1);
25
+ });
26
+
27
+ it('returns width/height when Image.getSize succeeds', async () => {
28
+ jest.spyOn(Image, 'getSize').mockImplementation((_uri, success) => {
29
+ success(400, 100);
30
+ });
31
+
32
+ const { result } = renderHook(() => useAspectRatio('https://example.com/a.png'));
33
+
34
+ await waitFor(() => {
35
+ expect(result.current).toBe(4);
36
+ });
37
+ });
38
+
39
+ it('returns 1 when Image.getSize reports height 0', async () => {
40
+ jest.spyOn(Image, 'getSize').mockImplementation((_uri, success) => {
41
+ success(400, 0);
42
+ });
43
+
44
+ const { result } = renderHook(() => useAspectRatio('https://example.com/zero.png'));
45
+
46
+ await waitFor(() => {
47
+ expect(result.current).toBe(1);
48
+ });
49
+ });
50
+
51
+ it('returns 1 when Image.getSize fails', async () => {
52
+ const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
53
+ jest.spyOn(Image, 'getSize').mockImplementation((_uri, _s, failure) => {
54
+ failure?.(new Error('bad'));
55
+ });
56
+
57
+ const { result } = renderHook(() => useAspectRatio('https://example.com/b.png'));
58
+
59
+ await waitFor(() => {
60
+ expect(result.current).toBe(1);
61
+ });
62
+
63
+ expect(logSpy).toHaveBeenCalled();
64
+ logSpy.mockRestore();
65
+ });
66
+ });
@@ -0,0 +1,39 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific
10
+ language governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { useEffect, useState } from 'react';
14
+ import { Image } from 'react-native';
15
+
16
+ function useAspectRatio(uri?: string) {
17
+ const [imageAspectRatio, setImageAspectRatio] = useState<number>(1);
18
+
19
+ useEffect(() => {
20
+ if (!uri) {
21
+ return;
22
+ }
23
+
24
+ Image.getSize(
25
+ uri,
26
+ (width, height) => {
27
+ setImageAspectRatio(height > 0 ? width / height : 1);
28
+ },
29
+ (error) => {
30
+ console.log('Error getting image size:', error);
31
+ setImageAspectRatio(1);
32
+ }
33
+ );
34
+ }, [uri]);
35
+
36
+ return imageAspectRatio;
37
+ }
38
+
39
+ export default useAspectRatio;
@@ -0,0 +1,82 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { act, renderHook, waitFor } from '@testing-library/react-native';
14
+ import Messaging from '../../Messaging';
15
+ import { useContentCardUI } from './useContentCardUI';
16
+
17
+ jest.mock('../../Messaging', () => ({
18
+ __esModule: true,
19
+ default: {
20
+ updatePropositionsForSurfaces: jest.fn().mockResolvedValue(undefined),
21
+ getContentCardUI: jest.fn(),
22
+ },
23
+ }));
24
+
25
+ describe('useContentCardUI', () => {
26
+ const template = { id: 'c1', type: 'SmallImage', data: {} };
27
+
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+ jest.mocked(Messaging.updatePropositionsForSurfaces).mockResolvedValue(undefined);
31
+ jest.mocked(Messaging.getContentCardUI).mockResolvedValue([template] as any);
32
+ });
33
+
34
+ it('fetches content cards on mount and exposes refetch', async () => {
35
+ const { result } = renderHook(() => useContentCardUI('card-surface'));
36
+
37
+ await waitFor(() => {
38
+ expect(result.current.isLoading).toBe(false);
39
+ });
40
+
41
+ expect(Messaging.updatePropositionsForSurfaces).toHaveBeenCalledWith(['card-surface']);
42
+ expect(Messaging.getContentCardUI).toHaveBeenCalledWith('card-surface');
43
+ expect(result.current.content).toEqual([template]);
44
+ expect(result.current.error).toBeNull();
45
+
46
+ jest.mocked(Messaging.getContentCardUI).mockResolvedValue([] as any);
47
+ await act(async () => {
48
+ await result.current.refetch();
49
+ });
50
+
51
+ expect(Messaging.getContentCardUI).toHaveBeenCalledTimes(2);
52
+ expect(result.current.content).toEqual([]);
53
+ });
54
+
55
+ it('clears content and sets error when getContentCardUI throws', async () => {
56
+ const err = new Error('fetch failed');
57
+ const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
58
+ jest.mocked(Messaging.getContentCardUI).mockRejectedValueOnce(err);
59
+
60
+ const { result } = renderHook(() => useContentCardUI('x'));
61
+
62
+ await waitFor(() => {
63
+ expect(result.current.isLoading).toBe(false);
64
+ });
65
+
66
+ expect(result.current.content).toEqual([]);
67
+ expect(result.current.error).toBe(err);
68
+ errorSpy.mockRestore();
69
+ });
70
+
71
+ it('refetches when surface changes', async () => {
72
+ const { rerender } = renderHook(({ surface }: { surface: string }) => useContentCardUI(surface), {
73
+ initialProps: { surface: 's1' },
74
+ });
75
+
76
+ await waitFor(() => expect(Messaging.getContentCardUI).toHaveBeenCalledWith('s1'));
77
+
78
+ rerender({ surface: 's2' });
79
+
80
+ await waitFor(() => expect(Messaging.getContentCardUI).toHaveBeenCalledWith('s2'));
81
+ });
82
+ });
@@ -0,0 +1,48 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific
10
+ language governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { useCallback, useEffect, useState } from 'react';
14
+ import Messaging from '../../Messaging';
15
+ import { ContentTemplate } from '../types/Templates';
16
+
17
+ /**
18
+ * @experimental
19
+ * Hook to fetch content card UI for a given surface via `Messaging.getContentCardUI`.
20
+ * @param surface - The surface to fetch the content card UI for.
21
+ * @returns An object containing the content card UI, error, loading state, and a refetch function.
22
+ */
23
+ export const useContentCardUI = (surface: string) => {
24
+ const [content, setContent] = useState<ContentTemplate[]>([]);
25
+ const [error, setError] = useState<any>(null);
26
+ const [isLoading, setIsLoading] = useState(false);
27
+
28
+ const fetchContent = useCallback(async () => {
29
+ try {
30
+ setIsLoading(true);
31
+ await Messaging.updatePropositionsForSurfaces([surface]);
32
+ const content = await Messaging.getContentCardUI(surface);
33
+ setContent(content);
34
+ } catch (error) {
35
+ console.error(error);
36
+ setContent([]);
37
+ setError(error);
38
+ } finally {
39
+ setIsLoading(false);
40
+ }
41
+ }, [surface]);
42
+
43
+ useEffect(() => {
44
+ fetchContent();
45
+ }, [surface, fetchContent]);
46
+
47
+ return { content, error, isLoading, refetch: fetchContent };
48
+ };
@@ -0,0 +1,87 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { act, renderHook, waitFor } from '@testing-library/react-native';
14
+ import Messaging from '../../Messaging';
15
+ import { useInbox } from './useInbox';
16
+
17
+ jest.mock('../../Messaging', () => ({
18
+ __esModule: true,
19
+ default: {
20
+ updatePropositionsForSurfaces: jest.fn().mockResolvedValue(undefined),
21
+ getInbox: jest.fn(),
22
+ },
23
+ }));
24
+
25
+ const mockSettings = {
26
+ content: {
27
+ heading: { content: 'Inbox' },
28
+ layout: { orientation: 'vertical' as const },
29
+ capacity: 10,
30
+ },
31
+ };
32
+
33
+ describe('useInbox', () => {
34
+ beforeEach(() => {
35
+ jest.clearAllMocks();
36
+ jest.mocked(Messaging.updatePropositionsForSurfaces).mockResolvedValue(undefined);
37
+ jest.mocked(Messaging.getInbox).mockResolvedValue(mockSettings as any);
38
+ });
39
+
40
+ it('fetches inbox settings on mount and exposes refetch', async () => {
41
+ const { result } = renderHook(() => useInbox('my-surface'));
42
+
43
+ await waitFor(() => {
44
+ expect(result.current.isLoading).toBe(false);
45
+ });
46
+
47
+ expect(Messaging.updatePropositionsForSurfaces).toHaveBeenCalledWith(['my-surface']);
48
+ expect(Messaging.getInbox).toHaveBeenCalledWith('my-surface');
49
+ expect(result.current.settings).toEqual(mockSettings);
50
+ expect(result.current.error).toBeNull();
51
+
52
+ jest.mocked(Messaging.getInbox).mockResolvedValue({ ...mockSettings, activityId: 'x' } as any);
53
+ await act(async () => {
54
+ await result.current.refetch();
55
+ });
56
+
57
+ expect(Messaging.getInbox).toHaveBeenCalledTimes(2);
58
+ });
59
+
60
+ it('sets error when getInbox throws', async () => {
61
+ const err = new Error('no inbox');
62
+ const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
63
+ jest.mocked(Messaging.getInbox).mockRejectedValueOnce(err);
64
+
65
+ const { result } = renderHook(() => useInbox('bad-surface'));
66
+
67
+ await waitFor(() => {
68
+ expect(result.current.isLoading).toBe(false);
69
+ });
70
+
71
+ expect(result.current.error).toBe(err);
72
+ expect(result.current.settings).toBeNull();
73
+ errorSpy.mockRestore();
74
+ });
75
+
76
+ it('refetches when surface changes', async () => {
77
+ const { rerender } = renderHook(({ surface }: { surface: string }) => useInbox(surface), {
78
+ initialProps: { surface: 'a' },
79
+ });
80
+
81
+ await waitFor(() => expect(Messaging.getInbox).toHaveBeenCalledWith('a'));
82
+
83
+ rerender({ surface: 'b' });
84
+
85
+ await waitFor(() => expect(Messaging.getInbox).toHaveBeenCalledWith('b'));
86
+ });
87
+ });
@@ -0,0 +1,46 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific
10
+ language governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { useCallback, useEffect, useState } from "react";
14
+ import Messaging from "../../Messaging";
15
+ import { InboxSettings } from "../providers/InboxProvider";
16
+
17
+ /**
18
+ * @experimental
19
+ * Fetches inbox UI settings for a surface via `Messaging.getInbox`.
20
+ */
21
+ export function useInbox(surface: string) {
22
+ const [settings, setSettings] = useState<InboxSettings | null>(null);
23
+ const [error, setError] = useState<any>(null);
24
+ const [isLoading, setIsLoading] = useState(false);
25
+
26
+ const fetchInbox = useCallback(async () => {
27
+ try {
28
+ setIsLoading(true);
29
+ setError(null);
30
+ await Messaging.updatePropositionsForSurfaces([surface]);
31
+ const settings = await Messaging.getInbox(surface);
32
+ setSettings(settings);
33
+ } catch (error) {
34
+ setError(error);
35
+ console.error('error', error);
36
+ } finally {
37
+ setIsLoading(false);
38
+ }
39
+ }, [surface]);
40
+
41
+ useEffect(() => {
42
+ fetchInbox();
43
+ }, [surface, fetchInbox]);
44
+
45
+ return { settings, error, isLoading, refetch: fetchInbox };
46
+ }
@@ -0,0 +1,41 @@
1
+ /*
2
+ Copyright 2026 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the
4
+ "License"); you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
7
+ or agreed to in writing, software distributed under the License is
8
+ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF
9
+ ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { renderHook } from '@testing-library/react-native';
14
+ import React from 'react';
15
+ import InboxProvider from '../providers/InboxProvider';
16
+ import useInboxSettings from './useInboxSettings';
17
+
18
+ const inboxSettings = {
19
+ content: {
20
+ heading: { content: 'Heading' },
21
+ layout: { orientation: 'vertical' as const },
22
+ capacity: 8,
23
+ },
24
+ };
25
+
26
+ describe('useInboxSettings', () => {
27
+ it('returns null when used outside InboxProvider', () => {
28
+ const { result } = renderHook(() => useInboxSettings());
29
+ expect(result.current).toBeNull();
30
+ });
31
+
32
+ it('returns settings from InboxProvider context', () => {
33
+ const { result } = renderHook(() => useInboxSettings(), {
34
+ wrapper: ({ children }) => (
35
+ <InboxProvider settings={inboxSettings as any}>{children}</InboxProvider>
36
+ ),
37
+ });
38
+
39
+ expect(result.current).toEqual(inboxSettings);
40
+ });
41
+ });