diff --git a/src/embedded/components/IterableEmbeddedView.tsx b/src/embedded/components/IterableEmbeddedView.tsx index 7234e3904..365d6029d 100644 --- a/src/embedded/components/IterableEmbeddedView.tsx +++ b/src/embedded/components/IterableEmbeddedView.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { View, Text } from 'react-native'; +import { View, Text, Image } from 'react-native'; import { IterableEmbeddedViewType } from '../enums/IterableEmbeddedViewType'; @@ -45,40 +45,14 @@ export const IterableEmbeddedView = ({ } }, [viewType]); - const { parsedStyles } = useEmbeddedView(viewType, props); + const { media } = useEmbeddedView(viewType, props); return Cmp ? ( - - parsedStyles.backgroundColor: {String(parsedStyles.backgroundColor)} - - parsedStyles.borderColor: {String(parsedStyles.borderColor)} - parsedStyles.borderWidth: {parsedStyles.borderWidth} - - parsedStyles.borderCornerRadius: {parsedStyles.borderCornerRadius} - - - parsedStyles.primaryBtnBackgroundColor:{' '} - {String(parsedStyles.primaryBtnBackgroundColor)} - - - parsedStyles.primaryBtnTextColor:{' '} - {String(parsedStyles.primaryBtnTextColor)} - - - parsedStyles.secondaryBtnBackgroundColor:{' '} - {String(parsedStyles.secondaryBtnBackgroundColor)} - - - parsedStyles.secondaryBtnTextColor:{' '} - {String(parsedStyles.secondaryBtnTextColor)} - - - parsedStyles.titleTextColor: {String(parsedStyles.titleTextColor)} - - - parsedStyles.bodyTextColor: {String(parsedStyles.bodyTextColor)} - + media.url: {media.url} + media.caption: {media.caption} + media.shouldShow: {media.shouldShow ? 'true' : 'false'} + {media.url ? : null} ) : null; diff --git a/src/embedded/hooks/useEmbeddedView/getMedia.test.ts b/src/embedded/hooks/useEmbeddedView/getMedia.test.ts new file mode 100644 index 000000000..b4e793ffd --- /dev/null +++ b/src/embedded/hooks/useEmbeddedView/getMedia.test.ts @@ -0,0 +1,136 @@ +import { getMedia } from './getMedia'; +import { IterableEmbeddedViewType } from '../../enums'; +import type { IterableEmbeddedMessage } from '../../types/IterableEmbeddedMessage'; + +const minimalMessage: IterableEmbeddedMessage = { + metadata: { messageId: 'msg-1', placementId: 1 }, +}; + +describe('getMedia', () => { + describe('viewType Notification', () => { + it('returns no media regardless of message content', () => { + const result = getMedia(IterableEmbeddedViewType.Notification, minimalMessage); + + expect(result).toEqual({ url: null, caption: null, shouldShow: false }); + }); + + it('returns no media even when message has mediaUrl and caption', () => { + const message: IterableEmbeddedMessage = { + ...minimalMessage, + elements: { + mediaUrl: 'https://example.com/image.png', + mediaUrlCaption: 'Example caption', + }, + }; + + const result = getMedia(IterableEmbeddedViewType.Notification, message); + + expect(result).toEqual({ url: null, caption: null, shouldShow: false }); + }); + }); + + describe('viewType Card', () => { + it('returns url and caption from message.elements, shouldShow true when url is non-empty', () => { + const message: IterableEmbeddedMessage = { + ...minimalMessage, + elements: { + mediaUrl: 'https://example.com/photo.jpg', + mediaUrlCaption: 'A nice photo', + }, + }; + + const result = getMedia(IterableEmbeddedViewType.Card, message); + + expect(result).toEqual({ + url: 'https://example.com/photo.jpg', + caption: 'A nice photo', + shouldShow: true, + }); + }); + + it('returns url only (caption null) when message has no mediaUrlCaption', () => { + const message: IterableEmbeddedMessage = { + ...minimalMessage, + elements: { mediaUrl: 'https://example.com/img.png' }, + }; + + const result = getMedia(IterableEmbeddedViewType.Card, message); + + expect(result).toEqual({ + url: 'https://example.com/img.png', + caption: null, + shouldShow: true, + }); + }); + + it('returns shouldShow false when mediaUrl is empty string', () => { + const message: IterableEmbeddedMessage = { + ...minimalMessage, + elements: { mediaUrl: '', mediaUrlCaption: 'Caption' }, + }; + + const result = getMedia(IterableEmbeddedViewType.Card, message); + + expect(result.url).toBe(''); + expect(result.caption).toBe('Caption'); + expect(result.shouldShow).toBe(false); + }); + + it('returns null url/caption and shouldShow false when message has no elements', () => { + const result = getMedia(IterableEmbeddedViewType.Card, minimalMessage); + + expect(result).toEqual({ url: null, caption: null, shouldShow: false }); + }); + + it('returns null url/caption when elements exist but mediaUrl is undefined', () => { + const message: IterableEmbeddedMessage = { + ...minimalMessage, + elements: { title: 'Title', body: 'Body' }, + }; + + const result = getMedia(IterableEmbeddedViewType.Card, message); + + expect(result).toEqual({ url: null, caption: null, shouldShow: false }); + }); + }); + + describe('viewType Banner', () => { + it('returns url and caption from message.elements, shouldShow true when url is non-empty', () => { + const message: IterableEmbeddedMessage = { + ...minimalMessage, + elements: { + mediaUrl: 'https://example.com/banner.png', + mediaUrlCaption: 'Banner caption', + }, + }; + + const result = getMedia(IterableEmbeddedViewType.Banner, message); + + expect(result).toEqual({ + url: 'https://example.com/banner.png', + caption: 'Banner caption', + shouldShow: true, + }); + }); + + it('returns null url/caption and shouldShow false when message has no elements', () => { + const result = getMedia(IterableEmbeddedViewType.Banner, minimalMessage); + + expect(result).toEqual({ url: null, caption: null, shouldShow: false }); + }); + }); + + describe('return shape', () => { + it('returns an object with url, caption, and shouldShow', () => { + const result = getMedia(IterableEmbeddedViewType.Card, minimalMessage); + + expect(Object.keys(result)).toHaveLength(3); + expect(result).toHaveProperty('url'); + expect(result).toHaveProperty('caption'); + expect(result).toHaveProperty('shouldShow'); + expect(typeof result.shouldShow).toBe('boolean'); + expect(result.url === null || typeof result.url === 'string').toBe(true); + expect(result.caption === null || typeof result.caption === 'string').toBe(true); + }); + }); +}); diff --git a/src/embedded/hooks/useEmbeddedView/getMedia.ts b/src/embedded/hooks/useEmbeddedView/getMedia.ts new file mode 100644 index 000000000..7260ca407 --- /dev/null +++ b/src/embedded/hooks/useEmbeddedView/getMedia.ts @@ -0,0 +1,31 @@ +import type { IterableEmbeddedMessage } from '../../types/IterableEmbeddedMessage'; +import { IterableEmbeddedViewType } from '../../enums'; + +/** + * This function is used to get the media to render for a given embedded view + * type and message. + * + * @param viewType - The type of view to render. + * @param message - The message to render. + * @returns The media to render. + * + * @example + * const media = getMedia(IterableEmbeddedViewType.Notification, message); + * console.log(media.url); + * console.log(media.caption); + * console.log(media.shouldShow ? 'true' : 'false'); + */ +export const getMedia = ( + /** The type of view to render. */ + viewType: IterableEmbeddedViewType, + /** The message to render. */ + message: IterableEmbeddedMessage +) => { + if (viewType === IterableEmbeddedViewType.Notification) { + return { url: null, caption: null, shouldShow: false }; + } + const url = message.elements?.mediaUrl ?? null; + const caption = message.elements?.mediaUrlCaption ?? null; + const shouldShow = !!url && url.length > 0; + return { url, caption, shouldShow }; +}; diff --git a/src/embedded/hooks/useEmbeddedView/useEmbeddedView.ts b/src/embedded/hooks/useEmbeddedView/useEmbeddedView.ts index eb4997782..1ed04d3ee 100644 --- a/src/embedded/hooks/useEmbeddedView/useEmbeddedView.ts +++ b/src/embedded/hooks/useEmbeddedView/useEmbeddedView.ts @@ -1,6 +1,8 @@ import { useMemo } from 'react'; + import { IterableEmbeddedViewType } from '../../enums'; import type { IterableEmbeddedComponentProps } from '../../types/IterableEmbeddedComponentProps'; +import { getMedia } from './getMedia'; import { getStyles } from './getStyles'; /** @@ -11,7 +13,7 @@ import { getStyles } from './getStyles'; * @returns The embedded view. * * @example - * const \{ parsedStyles \} = useEmbeddedView(IterableEmbeddedViewType.Notification, \{ + * const \{ media, parsedStyles \} = useEmbeddedView(IterableEmbeddedViewType.Notification, \{ * message, * config, * onButtonClick, @@ -20,6 +22,8 @@ import { getStyles } from './getStyles'; * * return ( * + * \{media.url\} + * \{media.caption\} * \{parsedStyles.backgroundColor\} * * ); @@ -30,13 +34,18 @@ export const useEmbeddedView = ( /** The props for the embedded view. */ { config, + message, }: IterableEmbeddedComponentProps ) => { const parsedStyles = useMemo(() => { return getStyles(viewType, config); }, [viewType, config]); + const media = useMemo(() => { + return getMedia(viewType, message); + }, [viewType, message]); return { parsedStyles, + media, }; };