From 3204df3da3306d635d708d3daf9b1782d52fbd87 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 13 Oct 2025 17:21:17 -0700 Subject: [PATCH 1/4] refactor: restructure IterableEmbeddedBanner component and update imports for better organization --- example/src/components/Embedded/Embedded.tsx | 2 +- .../components/IterableEmbeddedBanner.tsx | 19 --- .../IterableEmbeddedBanner.styles.ts | 87 ++++++++++++++ .../IterableEmbeddedBanner.tsx | 111 ++++++++++++++++++ .../IterableEmbeddedBanner/index.ts | 1 + .../components/IterableEmbeddedView.tsx | 2 +- src/embedded/components/index.ts | 2 +- 7 files changed, 202 insertions(+), 22 deletions(-) delete mode 100644 src/embedded/components/IterableEmbeddedBanner.tsx create mode 100644 src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.styles.ts create mode 100644 src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.tsx create mode 100644 src/embedded/components/IterableEmbeddedBanner/index.ts diff --git a/example/src/components/Embedded/Embedded.tsx b/example/src/components/Embedded/Embedded.tsx index e90c4de43..1b0f8ce53 100644 --- a/example/src/components/Embedded/Embedded.tsx +++ b/example/src/components/Embedded/Embedded.tsx @@ -16,7 +16,7 @@ export const Embedded = () => { IterableEmbeddedMessage[] >([]); const [selectedViewType, setSelectedViewType] = - useState(IterableEmbeddedViewType.Notification); + useState(IterableEmbeddedViewType.Banner); const syncEmbeddedMessages = useCallback(() => { Iterable.embeddedManager.syncMessages(); diff --git a/src/embedded/components/IterableEmbeddedBanner.tsx b/src/embedded/components/IterableEmbeddedBanner.tsx deleted file mode 100644 index 56b4ca32b..000000000 --- a/src/embedded/components/IterableEmbeddedBanner.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { View, Text } from 'react-native'; - -import type { IterableEmbeddedComponentProps } from '../types/IterableEmbeddedComponentProps'; - -export const IterableEmbeddedBanner = ({ - config, - message, - onButtonClick = () => {}, -}: IterableEmbeddedComponentProps) => { - console.log(`🚀 > IterableEmbeddedBanner > config:`, config); - console.log(`🚀 > IterableEmbeddedBanner > message:`, message); - console.log(`🚀 > IterableEmbeddedBanner > onButtonClick:`, onButtonClick); - - return ( - - IterableEmbeddedBanner - - ); -}; diff --git a/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.styles.ts b/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.styles.ts new file mode 100644 index 000000000..551f50068 --- /dev/null +++ b/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.styles.ts @@ -0,0 +1,87 @@ +import { StyleSheet } from 'react-native'; +import { + embeddedMediaImageBackgroundColors, + embeddedMediaImageBorderColors, +} from '../../constants/embeddedViewDefaults'; + +export const IMAGE_HEIGHT = 70; +export const IMAGE_WIDTH = 70; + +export const styles = StyleSheet.create({ + body: { + alignSelf: 'stretch', + fontSize: 14, + fontWeight: '400', + lineHeight: 20, + }, + bodyContainer: { + alignItems: 'center', + alignSelf: 'stretch', + display: 'flex', + flexDirection: 'row', + paddingTop: 4, + }, + button: { + borderRadius: 32, + gap: 8, + }, + buttonContainer: { + alignItems: 'flex-start', + alignSelf: 'stretch', + display: 'flex', + flexDirection: 'row', + gap: 12, + width: '100%', + }, + buttonText: { + fontSize: 14, + fontWeight: '400', + lineHeight: 20, + paddingHorizontal: 12, + paddingVertical: 8, + }, + container: { + alignItems: 'flex-start', + borderStyle: 'solid', + boxShadow: + '0 1px 1px 0 rgba(0, 0, 0, 0.06), 0 0 2px 0 rgba(0, 0, 0, 0.06), 0 0 1px 0 rgba(0, 0, 0, 0.08)', + display: 'flex', + flexDirection: 'column', + gap: 16, + justifyContent: 'center', + padding: 16, + width: '100%', + }, + mediaContainer: { + alignItems: 'flex-start', + alignSelf: 'stretch', + display: 'flex', + flexDirection: 'row', + }, + mediaImage: { + backgroundColor: embeddedMediaImageBackgroundColors.banner, + borderColor: embeddedMediaImageBorderColors.banner, + borderRadius: 6, + borderStyle: 'solid', + borderWidth: 1, + height: IMAGE_HEIGHT, + paddingHorizontal: 0, + paddingVertical: 0, + width: IMAGE_WIDTH, + }, + textContainer: { + alignSelf: 'center', + display: 'flex', + flexDirection: 'column', + flexGrow: 1, + flexShrink: 1, + gap: 4, + width: '100%', + }, + title: { + fontSize: 16, + fontWeight: '700', + lineHeight: 16, + paddingBottom: 4, + }, +}); diff --git a/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.tsx b/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.tsx new file mode 100644 index 000000000..3b410d475 --- /dev/null +++ b/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.tsx @@ -0,0 +1,111 @@ +import { + Image, + Text, + TouchableOpacity, + View, + type TextStyle, + type ViewStyle, + PixelRatio, +} from 'react-native'; + +import { IterableEmbeddedViewType } from '../../enums'; +import { useEmbeddedView } from '../../hooks/useEmbeddedView'; +import type { IterableEmbeddedComponentProps } from '../../types/IterableEmbeddedComponentProps'; +import { + styles, + IMAGE_HEIGHT, + IMAGE_WIDTH, +} from './IterableEmbeddedBanner.styles'; + +/** + * TODO: figure out how default action works. + */ + +export const IterableEmbeddedBanner = ({ + config, + message, + onButtonClick = () => {}, +}: IterableEmbeddedComponentProps) => { + const { parsedStyles, media, handleButtonClick } = useEmbeddedView( + IterableEmbeddedViewType.Banner, + { message, config, onButtonClick } + ); + + const buttons = message.elements?.buttons ?? []; + + return ( + + {/* eslint-disable-next-line react-native/no-inline-styles */} + + + + {message.elements?.title} + + + {message.elements?.body} + + + {media.shouldShow && ( + + {media.caption + + )} + + {buttons.length > 0 && ( + + {buttons.map((button, index) => { + const backgroundColor = + index === 0 + ? parsedStyles.primaryBtnBackgroundColor + : parsedStyles.secondaryBtnBackgroundColor; + const textColor = + index === 0 + ? parsedStyles.primaryBtnTextColor + : parsedStyles.secondaryBtnTextColor; + return ( + handleButtonClick(button)} + key={button.id} + > + + {button.title} + + + ); + })} + + )} + + ); +}; diff --git a/src/embedded/components/IterableEmbeddedBanner/index.ts b/src/embedded/components/IterableEmbeddedBanner/index.ts new file mode 100644 index 000000000..bd574a288 --- /dev/null +++ b/src/embedded/components/IterableEmbeddedBanner/index.ts @@ -0,0 +1 @@ +export * from './IterableEmbeddedBanner'; diff --git a/src/embedded/components/IterableEmbeddedView.tsx b/src/embedded/components/IterableEmbeddedView.tsx index 770e4a777..fa76f584f 100644 --- a/src/embedded/components/IterableEmbeddedView.tsx +++ b/src/embedded/components/IterableEmbeddedView.tsx @@ -4,7 +4,7 @@ import { IterableEmbeddedViewType } from '../enums/IterableEmbeddedViewType'; import { IterableEmbeddedBanner } from './IterableEmbeddedBanner'; import { IterableEmbeddedCard } from './IterableEmbeddedCard'; -import { IterableEmbeddedNotification } from './IterableEmbeddedNotification/IterableEmbeddedNotification'; +import { IterableEmbeddedNotification } from './IterableEmbeddedNotification'; import type { IterableEmbeddedComponentProps } from '../types/IterableEmbeddedComponentProps'; /** diff --git a/src/embedded/components/index.ts b/src/embedded/components/index.ts index edf3ab6a0..22725d80c 100644 --- a/src/embedded/components/index.ts +++ b/src/embedded/components/index.ts @@ -1,4 +1,4 @@ -export * from './IterableEmbeddedBanner'; +export * from './IterableEmbeddedBanner/IterableEmbeddedBanner'; export * from './IterableEmbeddedCard'; export * from './IterableEmbeddedNotification/IterableEmbeddedNotification'; export * from './IterableEmbeddedView'; From b3aef75df8e6d528ba04da4f43715588a1fbc283 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 13 Oct 2025 17:22:41 -0700 Subject: [PATCH 2/4] feat: add message click handling to IterableEmbeddedBanner component --- .../IterableEmbeddedBanner.tsx | 159 ++++++++++-------- 1 file changed, 86 insertions(+), 73 deletions(-) diff --git a/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.tsx b/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.tsx index 3b410d475..194e17e0a 100644 --- a/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.tsx +++ b/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.tsx @@ -6,6 +6,7 @@ import { type TextStyle, type ViewStyle, PixelRatio, + Pressable, } from 'react-native'; import { IterableEmbeddedViewType } from '../../enums'; @@ -25,87 +26,99 @@ export const IterableEmbeddedBanner = ({ config, message, onButtonClick = () => {}, + onMessageClick = () => {}, }: IterableEmbeddedComponentProps) => { - const { parsedStyles, media, handleButtonClick } = useEmbeddedView( - IterableEmbeddedViewType.Banner, - { message, config, onButtonClick } - ); + const { parsedStyles, media, handleButtonClick, handleMessageClick } = + useEmbeddedView(IterableEmbeddedViewType.Banner, { + message, + config, + onButtonClick, + onMessageClick, + }); const buttons = message.elements?.buttons ?? []; return ( - - {/* eslint-disable-next-line react-native/no-inline-styles */} - - - - {message.elements?.title} - - - {message.elements?.body} - + handleMessageClick()}> + + {} + + + + {message.elements?.title} + + + {message.elements?.body} + + + {media.shouldShow && ( + + {media.caption + + )} - {media.shouldShow && ( - - {media.caption + {buttons.length > 0 && ( + + {buttons.map((button, index) => { + const backgroundColor = + index === 0 + ? parsedStyles.primaryBtnBackgroundColor + : parsedStyles.secondaryBtnBackgroundColor; + const textColor = + index === 0 + ? parsedStyles.primaryBtnTextColor + : parsedStyles.secondaryBtnTextColor; + return ( + handleButtonClick(button)} + key={button.id} + > + + {button.title} + + + ); + })} )} - {buttons.length > 0 && ( - - {buttons.map((button, index) => { - const backgroundColor = - index === 0 - ? parsedStyles.primaryBtnBackgroundColor - : parsedStyles.secondaryBtnBackgroundColor; - const textColor = - index === 0 - ? parsedStyles.primaryBtnTextColor - : parsedStyles.secondaryBtnTextColor; - return ( - handleButtonClick(button)} - key={button.id} - > - - {button.title} - - - ); - })} - - )} - + ); }; From b781e90e8a243d7116e6fb75990eae2fd352b7ec Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 13 Oct 2025 17:27:19 -0700 Subject: [PATCH 3/4] fix: update IMAGE_HEIGHT and IMAGE_WIDTH for platform-specific styling in IterableEmbeddedBanner --- .../IterableEmbeddedBanner.styles.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.styles.ts b/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.styles.ts index 551f50068..09b97619f 100644 --- a/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.styles.ts +++ b/src/embedded/components/IterableEmbeddedBanner/IterableEmbeddedBanner.styles.ts @@ -1,11 +1,12 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Platform } from 'react-native'; import { embeddedMediaImageBackgroundColors, embeddedMediaImageBorderColors, } from '../../constants/embeddedViewDefaults'; -export const IMAGE_HEIGHT = 70; -export const IMAGE_WIDTH = 70; +// See https://support.iterable.com/hc/en-us/articles/23230946708244-Out-of-the-Box-Views-for-Embedded-Messages#banners +export const IMAGE_HEIGHT = Platform.OS === 'android' ? 80 : 100; +export const IMAGE_WIDTH = Platform.OS === 'android' ? 80 : 100; export const styles = StyleSheet.create({ body: { From 10865553a3ff2c39526d2ebb15cd40f95fcd18db Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Mon, 13 Oct 2025 17:30:07 -0700 Subject: [PATCH 4/4] refactor: remove unused session management and impression handling code from Embedded component --- example/src/components/Embedded/Embedded.tsx | 52 -------------------- 1 file changed, 52 deletions(-) diff --git a/example/src/components/Embedded/Embedded.tsx b/example/src/components/Embedded/Embedded.tsx index 1b0f8ce53..c603e210e 100644 --- a/example/src/components/Embedded/Embedded.tsx +++ b/example/src/components/Embedded/Embedded.tsx @@ -30,20 +30,6 @@ export const Embedded = () => { }); }, []); - const startEmbeddedSession = useCallback(() => { - console.log( - 'startEmbeddedSession --> check android/ios logs to check if it worked' - ); - Iterable.embeddedManager.startSession(); - }, []); - - const endEmbeddedSession = useCallback(() => { - console.log( - 'endEmbeddedSession --> check android/ios logs to check if it worked' - ); - Iterable.embeddedManager.endSession(); - }, []); - const getEmbeddedMessages = useCallback(() => { getPlacementIds() .then((ids: number[]) => Iterable.embeddedManager.getMessages(ids)) @@ -53,38 +39,6 @@ export const Embedded = () => { }); }, [getPlacementIds]); - // const startEmbeddedImpression = useCallback( - // (message: IterableEmbeddedMessage) => { - // console.log(`startEmbeddedImpression`, message); - // Iterable.embeddedManager.startImpression( - // message.metadata.messageId, - // // TODO: check if this should be changed to a number, as per the type - // Number(message.metadata.placementId) - // ); - // }, - // [] - // ); - - // const pauseEmbeddedImpression = useCallback( - // (message: IterableEmbeddedMessage) => { - // console.log(`pauseEmbeddedImpression:`, message); - // Iterable.embeddedManager.pauseImpression(message.metadata.messageId); - // }, - // [] - // ); - - // const handleClick = useCallback( - // ( - // message: IterableEmbeddedMessage, - // buttonId: string | null, - // action?: IterableAction | null - // ) => { - // console.log(`handleClick:`, message); - // Iterable.embeddedManager.handleClick(message, buttonId, action); - // }, - // [] - // ); - return ( EMBEDDED @@ -161,12 +115,6 @@ export const Embedded = () => { Get placement ids - - Start session - - - End session - Get messages