@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.
- package/RCTAEPMessaging.podspec +1 -1
- package/README.md +145 -16
- package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingConstants.java +3 -0
- package/android/src/main/java/com/adobe/marketing/mobile/reactnative/messaging/RCTAEPMessagingModule.java +103 -33
- package/babel.config.js +3 -0
- package/dist/module/Messaging.js +334 -0
- package/dist/module/Messaging.js.map +1 -0
- package/dist/module/index.js +30 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/models/ContentCard.js +24 -0
- package/dist/module/models/ContentCard.js.map +1 -0
- package/dist/{models → module/models}/HTMLProposition.js +8 -9
- package/dist/module/models/HTMLProposition.js.map +1 -0
- package/dist/module/models/InAppMessage.js +4 -0
- package/dist/module/models/InAppMessage.js.map +1 -0
- package/dist/module/models/JSONProposition.js +22 -0
- package/dist/module/models/JSONProposition.js.map +1 -0
- package/dist/module/models/Message.js +182 -0
- package/dist/module/models/Message.js.map +1 -0
- package/dist/module/models/MessagingDelegate.js +4 -0
- package/dist/module/models/MessagingDelegate.js.map +1 -0
- package/dist/module/models/MessagingEdgeEventType.js +24 -0
- package/dist/module/models/MessagingEdgeEventType.js.map +1 -0
- package/dist/module/models/MessagingProposition.js +57 -0
- package/dist/module/models/MessagingProposition.js.map +1 -0
- package/dist/module/models/MessagingPropositionItem.js +4 -0
- package/dist/module/models/MessagingPropositionItem.js.map +1 -0
- package/dist/module/models/PersonalizationSchema.js +26 -0
- package/dist/module/models/PersonalizationSchema.js.map +1 -0
- package/dist/module/models/PropositionItem.js +113 -0
- package/dist/module/models/PropositionItem.js.map +1 -0
- package/dist/module/models/ScopeDetails.js +2 -0
- package/dist/module/models/ScopeDetails.js.map +1 -0
- package/dist/{models/JSONProposition.js → module/models/index.js} +14 -12
- package/dist/module/models/index.js.map +1 -0
- package/dist/module/ui/components/Button/Button.js +57 -0
- package/dist/module/ui/components/Button/Button.js.map +1 -0
- package/dist/module/ui/components/Button/Button.spec.js +476 -0
- package/dist/module/ui/components/Button/Button.spec.js.map +1 -0
- package/dist/module/ui/components/ContentCardView/ContentCardView.js +257 -0
- package/dist/module/ui/components/ContentCardView/ContentCardView.js.map +1 -0
- package/dist/module/ui/components/ContentCardView/ContentCardView.spec.js +363 -0
- package/dist/module/ui/components/ContentCardView/ContentCardView.spec.js.map +1 -0
- package/dist/module/ui/components/DismissButton/DismissButton.js +70 -0
- package/dist/module/ui/components/DismissButton/DismissButton.js.map +1 -0
- package/dist/module/ui/components/DismissButton/DismissButton.spec.js +279 -0
- package/dist/module/ui/components/DismissButton/DismissButton.spec.js.map +1 -0
- package/dist/module/ui/components/FullScreenCenterView/FullScreenCenterView.js +34 -0
- package/dist/module/ui/components/FullScreenCenterView/FullScreenCenterView.js.map +1 -0
- package/dist/module/ui/components/Inbox/EmptyState.js +64 -0
- package/dist/module/ui/components/Inbox/EmptyState.js.map +1 -0
- package/dist/module/ui/components/Inbox/Inbox.js +235 -0
- package/dist/module/ui/components/Inbox/Inbox.js.map +1 -0
- package/dist/module/ui/components/Inbox/Inbox.spec.js +847 -0
- package/dist/module/ui/components/Inbox/Inbox.spec.js.map +1 -0
- package/dist/module/ui/components/Pagination/Pagination.js +176 -0
- package/dist/module/ui/components/Pagination/Pagination.js.map +1 -0
- package/dist/module/ui/components/Pagination/Pagination.spec.js +193 -0
- package/dist/module/ui/components/Pagination/Pagination.spec.js.map +1 -0
- package/dist/module/ui/components/UnreadIcon/UnreadIcon.js +184 -0
- package/dist/module/ui/components/UnreadIcon/UnreadIcon.js.map +1 -0
- package/dist/module/ui/components/UnreadIcon/UnreadIcon.spec.js +815 -0
- package/dist/module/ui/components/UnreadIcon/UnreadIcon.spec.js.map +1 -0
- package/dist/{models/ContentCard.js → module/ui/components/index.js} +12 -12
- package/dist/module/ui/components/index.js.map +1 -0
- package/dist/module/ui/hooks/index.js +18 -0
- package/dist/module/ui/hooks/index.js.map +1 -0
- package/dist/module/ui/hooks/useAspectRatio.js +33 -0
- package/dist/module/ui/hooks/useAspectRatio.js.map +1 -0
- package/dist/module/ui/hooks/useAspectRatio.spec.js +65 -0
- package/dist/module/ui/hooks/useAspectRatio.spec.js.map +1 -0
- package/dist/module/ui/hooks/useContentCardUI.js +51 -0
- package/dist/module/ui/hooks/useContentCardUI.js.map +1 -0
- package/dist/module/ui/hooks/useContentCardUI.spec.js +85 -0
- package/dist/module/ui/hooks/useContentCardUI.spec.js.map +1 -0
- package/dist/module/ui/hooks/useInbox.js +49 -0
- package/dist/module/ui/hooks/useInbox.js.map +1 -0
- package/dist/module/ui/hooks/useInbox.spec.js +93 -0
- package/dist/module/ui/hooks/useInbox.spec.js.map +1 -0
- package/dist/module/ui/hooks/useInboxSettings.js +26 -0
- package/dist/module/ui/hooks/useInboxSettings.js.map +1 -0
- package/dist/module/ui/hooks/useInboxSettings.spec.js +50 -0
- package/dist/module/ui/hooks/useInboxSettings.spec.js.map +1 -0
- package/dist/module/ui/index.js +10 -0
- package/dist/module/ui/index.js.map +1 -0
- package/dist/module/ui/providers/InboxProvider.js +27 -0
- package/dist/module/ui/providers/InboxProvider.js.map +1 -0
- package/dist/module/ui/theme/Theme.js +2 -0
- package/dist/module/ui/theme/Theme.js.map +1 -0
- package/dist/module/ui/theme/ThemeProvider.js +112 -0
- package/dist/module/ui/theme/ThemeProvider.js.map +1 -0
- package/dist/{models/InAppMessage.js → module/ui/theme/index.js} +6 -3
- package/dist/module/ui/theme/index.js.map +1 -0
- package/dist/module/ui/types/ContentViewEvent.js +2 -0
- package/dist/module/ui/types/ContentViewEvent.js.map +1 -0
- package/dist/module/ui/types/Templates.js +26 -0
- package/dist/module/ui/types/Templates.js.map +1 -0
- package/dist/{models/ScopeDetails.js → module/ui/types/index.js} +6 -3
- package/dist/module/ui/types/index.js.map +1 -0
- package/dist/module/ui/utils/generateCardHash.js +50 -0
- package/dist/module/ui/utils/generateCardHash.js.map +1 -0
- package/dist/module/ui/utils/generateCardHash.spec.js +103 -0
- package/dist/module/ui/utils/generateCardHash.spec.js.map +1 -0
- package/dist/module/ui/utils/inboxStorage.js +65 -0
- package/dist/module/ui/utils/inboxStorage.js.map +1 -0
- package/dist/module/ui/utils/inboxStorage.spec.js +123 -0
- package/dist/module/ui/utils/inboxStorage.spec.js.map +1 -0
- package/dist/module/ui/utils/index.js +5 -0
- package/dist/module/ui/utils/index.js.map +1 -0
- package/dist/{Messaging.d.ts → typescript/Messaging.d.ts} +23 -7
- package/dist/typescript/Messaging.d.ts.map +1 -0
- package/dist/{index.d.ts → typescript/index.d.ts} +4 -2
- package/dist/typescript/index.d.ts.map +1 -0
- package/dist/typescript/models/ContentCard.d.ts +57 -0
- package/dist/typescript/models/ContentCard.d.ts.map +1 -0
- package/dist/{models → typescript/models}/HTMLProposition.d.ts +1 -0
- package/dist/typescript/models/HTMLProposition.d.ts.map +1 -0
- package/dist/{models → typescript/models}/InAppMessage.d.ts +1 -0
- package/dist/typescript/models/InAppMessage.d.ts.map +1 -0
- package/dist/{models → typescript/models}/JSONProposition.d.ts +1 -0
- package/dist/typescript/models/JSONProposition.d.ts.map +1 -0
- package/dist/{models → typescript/models}/Message.d.ts +14 -0
- package/dist/typescript/models/Message.d.ts.map +1 -0
- package/dist/{models → typescript/models}/MessagingDelegate.d.ts +1 -0
- package/dist/typescript/models/MessagingDelegate.d.ts.map +1 -0
- package/dist/{models → typescript/models}/MessagingEdgeEventType.d.ts +1 -0
- package/dist/typescript/models/MessagingEdgeEventType.d.ts.map +1 -0
- package/dist/{models → typescript/models}/MessagingProposition.d.ts +1 -0
- package/dist/typescript/models/MessagingProposition.d.ts.map +1 -0
- package/dist/{models → typescript/models}/MessagingPropositionItem.d.ts +1 -0
- package/dist/typescript/models/MessagingPropositionItem.d.ts.map +1 -0
- package/dist/{models → typescript/models}/PersonalizationSchema.d.ts +2 -0
- package/dist/typescript/models/PersonalizationSchema.d.ts.map +1 -0
- package/dist/{models → typescript/models}/PropositionItem.d.ts +1 -0
- package/dist/typescript/models/PropositionItem.d.ts.map +1 -0
- package/dist/{models → typescript/models}/ScopeDetails.d.ts +1 -0
- package/dist/typescript/models/ScopeDetails.d.ts.map +1 -0
- package/dist/typescript/models/index.d.ts +11 -0
- package/dist/typescript/models/index.d.ts.map +1 -0
- package/dist/typescript/ui/components/Button/Button.d.ts +14 -0
- package/dist/typescript/ui/components/Button/Button.d.ts.map +1 -0
- package/dist/typescript/ui/components/Button/Button.spec.d.ts +2 -0
- package/dist/typescript/ui/components/Button/Button.spec.d.ts.map +1 -0
- package/dist/typescript/ui/components/ContentCardView/ContentCardView.d.ts +39 -0
- package/dist/typescript/ui/components/ContentCardView/ContentCardView.d.ts.map +1 -0
- package/dist/typescript/ui/components/ContentCardView/ContentCardView.spec.d.ts +2 -0
- package/dist/typescript/ui/components/ContentCardView/ContentCardView.spec.d.ts.map +1 -0
- package/dist/typescript/ui/components/DismissButton/DismissButton.d.ts +13 -0
- package/dist/typescript/ui/components/DismissButton/DismissButton.d.ts.map +1 -0
- package/dist/typescript/ui/components/DismissButton/DismissButton.spec.d.ts +2 -0
- package/dist/typescript/ui/components/DismissButton/DismissButton.spec.d.ts.map +1 -0
- package/dist/typescript/ui/components/FullScreenCenterView/FullScreenCenterView.d.ts +5 -0
- package/dist/typescript/ui/components/FullScreenCenterView/FullScreenCenterView.d.ts.map +1 -0
- package/dist/typescript/ui/components/Inbox/EmptyState.d.ts +19 -0
- package/dist/typescript/ui/components/Inbox/EmptyState.d.ts.map +1 -0
- package/dist/typescript/ui/components/Inbox/Inbox.d.ts +21 -0
- package/dist/typescript/ui/components/Inbox/Inbox.d.ts.map +1 -0
- package/dist/typescript/ui/components/Inbox/Inbox.spec.d.ts +2 -0
- package/dist/typescript/ui/components/Inbox/Inbox.spec.d.ts.map +1 -0
- package/dist/typescript/ui/components/Pagination/Pagination.d.ts +14 -0
- package/dist/typescript/ui/components/Pagination/Pagination.d.ts.map +1 -0
- package/dist/typescript/ui/components/Pagination/Pagination.spec.d.ts +2 -0
- package/dist/typescript/ui/components/Pagination/Pagination.spec.d.ts.map +1 -0
- package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.d.ts +14 -0
- package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.d.ts.map +1 -0
- package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.spec.d.ts +2 -0
- package/dist/typescript/ui/components/UnreadIcon/UnreadIcon.spec.d.ts.map +1 -0
- package/dist/typescript/ui/components/index.d.ts +10 -0
- package/dist/typescript/ui/components/index.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/index.d.ts +4 -0
- package/dist/typescript/ui/hooks/index.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/useAspectRatio.d.ts +3 -0
- package/dist/typescript/ui/hooks/useAspectRatio.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/useAspectRatio.spec.d.ts +2 -0
- package/dist/typescript/ui/hooks/useAspectRatio.spec.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/useContentCardUI.d.ts +14 -0
- package/dist/typescript/ui/hooks/useContentCardUI.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/useContentCardUI.spec.d.ts +2 -0
- package/dist/typescript/ui/hooks/useContentCardUI.spec.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/useInbox.d.ts +12 -0
- package/dist/typescript/ui/hooks/useInbox.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/useInbox.spec.d.ts +2 -0
- package/dist/typescript/ui/hooks/useInbox.spec.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/useInboxSettings.d.ts +7 -0
- package/dist/typescript/ui/hooks/useInboxSettings.d.ts.map +1 -0
- package/dist/typescript/ui/hooks/useInboxSettings.spec.d.ts +2 -0
- package/dist/typescript/ui/hooks/useInboxSettings.spec.d.ts.map +1 -0
- package/dist/typescript/ui/index.d.ts +8 -0
- package/dist/typescript/ui/index.d.ts.map +1 -0
- package/dist/typescript/ui/providers/InboxProvider.d.ts +56 -0
- package/dist/typescript/ui/providers/InboxProvider.d.ts.map +1 -0
- package/dist/typescript/ui/theme/Theme.d.ts +44 -0
- package/dist/typescript/ui/theme/Theme.d.ts.map +1 -0
- package/dist/typescript/ui/theme/ThemeProvider.d.ts +21 -0
- package/dist/typescript/ui/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/typescript/ui/theme/index.d.ts +3 -0
- package/dist/typescript/ui/theme/index.d.ts.map +1 -0
- package/dist/typescript/ui/types/ContentViewEvent.d.ts +9 -0
- package/dist/typescript/ui/types/ContentViewEvent.d.ts.map +1 -0
- package/dist/typescript/ui/types/Templates.d.ts +43 -0
- package/dist/typescript/ui/types/Templates.d.ts.map +1 -0
- package/dist/typescript/ui/types/index.d.ts +3 -0
- package/dist/typescript/ui/types/index.d.ts.map +1 -0
- package/dist/typescript/ui/utils/generateCardHash.d.ts +21 -0
- package/dist/typescript/ui/utils/generateCardHash.d.ts.map +1 -0
- package/dist/typescript/ui/utils/generateCardHash.spec.d.ts +2 -0
- package/dist/typescript/ui/utils/generateCardHash.spec.d.ts.map +1 -0
- package/dist/typescript/ui/utils/inboxStorage.d.ts +20 -0
- package/dist/typescript/ui/utils/inboxStorage.d.ts.map +1 -0
- package/dist/typescript/ui/utils/inboxStorage.spec.d.ts +2 -0
- package/dist/typescript/ui/utils/inboxStorage.spec.d.ts.map +1 -0
- package/dist/typescript/ui/utils/index.d.ts +3 -0
- package/dist/typescript/ui/utils/index.d.ts.map +1 -0
- package/ios/src/RCTAEPMessaging.mm +15 -0
- package/ios/src/RCTAEPMessaging.swift +61 -3
- package/ios/src/RCTAEPMessagingConstants.swift +4 -1
- package/jest.config.js +15 -0
- package/package.json +33 -5
- package/src/Messaging.ts +288 -32
- package/src/index.ts +3 -3
- package/src/models/ContentCard.ts +52 -27
- package/src/models/HTMLProposition.ts +1 -1
- package/src/models/JSONProposition.ts +1 -1
- package/src/models/Message.ts +50 -0
- package/src/models/PersonalizationSchema.ts +1 -0
- package/src/models/index.ts +22 -0
- package/src/ui/components/Button/Button.spec.tsx +496 -0
- package/src/ui/components/Button/Button.tsx +76 -0
- package/src/ui/components/ContentCardView/ContentCardView.spec.tsx +278 -0
- package/src/ui/components/ContentCardView/ContentCardView.tsx +400 -0
- package/src/ui/components/DismissButton/DismissButton.spec.tsx +314 -0
- package/src/ui/components/DismissButton/DismissButton.tsx +100 -0
- package/src/ui/components/FullScreenCenterView/FullScreenCenterView.tsx +32 -0
- package/src/ui/components/Inbox/EmptyState.tsx +89 -0
- package/src/ui/components/Inbox/Inbox.spec.tsx +478 -0
- package/src/ui/components/Inbox/Inbox.tsx +275 -0
- package/src/ui/components/Pagination/Pagination.spec.tsx +159 -0
- package/src/ui/components/Pagination/Pagination.tsx +222 -0
- package/src/ui/components/UnreadIcon/UnreadIcon.spec.tsx +878 -0
- package/src/ui/components/UnreadIcon/UnreadIcon.tsx +234 -0
- package/src/ui/components/index.ts +22 -0
- package/{dist/models/MessagingPropositionItem.js → src/ui/hooks/index.ts} +5 -4
- package/src/ui/hooks/useAspectRatio.spec.tsx +66 -0
- package/src/ui/hooks/useAspectRatio.tsx +39 -0
- package/src/ui/hooks/useContentCardUI.spec.tsx +82 -0
- package/src/ui/hooks/useContentCardUI.ts +48 -0
- package/src/ui/hooks/useInbox.spec.tsx +87 -0
- package/src/ui/hooks/useInbox.ts +46 -0
- package/src/ui/hooks/useInboxSettings.spec.tsx +41 -0
- package/src/ui/hooks/useInboxSettings.ts +24 -0
- package/src/ui/index.ts +7 -0
- package/src/ui/providers/InboxProvider.tsx +79 -0
- package/src/ui/theme/Theme.ts +57 -0
- package/src/ui/theme/ThemeProvider.tsx +120 -0
- package/src/ui/theme/index.ts +14 -0
- package/src/ui/types/ContentViewEvent.ts +20 -0
- package/src/ui/types/Templates.ts +77 -0
- package/src/ui/types/index.ts +14 -0
- package/src/ui/utils/generateCardHash.spec.tsx +86 -0
- package/src/ui/utils/generateCardHash.ts +59 -0
- package/src/ui/utils/inboxStorage.spec.tsx +136 -0
- package/src/ui/utils/inboxStorage.ts +64 -0
- package/src/ui/utils/index.ts +3 -0
- package/tutorials/ContentCardCustomizationGuide.md +661 -0
- package/tutorials/ContentCards.md +419 -0
- package/tutorials/In-App Messaging.md +31 -0
- package/tutorials/Inbox.md +515 -0
- package/tutorials/resources/image-only-template.png +0 -0
- package/tutorials/resources/large-image-template.png +0 -0
- package/tutorials/resources/small-image-template.png +0 -0
- package/dist/Messaging.js +0 -151
- package/dist/Messaging.js.map +0 -1
- package/dist/index.js +0 -34
- package/dist/index.js.map +0 -1
- package/dist/models/ContentCard.d.ts +0 -51
- package/dist/models/ContentCard.js.map +0 -1
- package/dist/models/HTMLProposition.js.map +0 -1
- package/dist/models/InAppMessage.js.map +0 -1
- package/dist/models/JSONProposition.js.map +0 -1
- package/dist/models/Message.js +0 -114
- package/dist/models/Message.js.map +0 -1
- package/dist/models/MessagingDelegate.js +0 -14
- package/dist/models/MessagingDelegate.js.map +0 -1
- package/dist/models/MessagingEdgeEventType.js +0 -24
- package/dist/models/MessagingEdgeEventType.js.map +0 -1
- package/dist/models/MessagingProposition.js +0 -59
- package/dist/models/MessagingProposition.js.map +0 -1
- package/dist/models/MessagingPropositionItem.js.map +0 -1
- package/dist/models/PersonalizationSchema.js +0 -25
- package/dist/models/PersonalizationSchema.js.map +0 -1
- package/dist/models/PropositionItem.js +0 -78
- package/dist/models/PropositionItem.js.map +0 -1
- 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
|
|
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
|
-
|
|
14
|
-
|
|
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
|
+
});
|