Skip to content

Commit 2e983a8

Browse files
committed
feat: implement IterableEmbeddedCard component
1 parent df4ae5e commit 2e983a8

2 files changed

Lines changed: 215 additions & 7 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { StyleSheet } from 'react-native';
2+
3+
export const IMAGE_HEIGHT = 230;
4+
export const PLACEHOLDER_IMAGE_HEIGHT = 56;
5+
export const PLACEHOLDER_IMAGE_WIDTH = 56;
6+
const SHADOW_COLOR_LIGHT = 'rgba(0, 0, 0, 0.06)';
7+
const SHADOW_COLOR_DARK = 'rgba(0, 0, 0, 0.08)';
8+
const IMAGE_BACKGROUND_COLOR = '#F5F4F4';
9+
10+
export const styles = StyleSheet.create({
11+
body: {
12+
alignSelf: 'stretch',
13+
fontSize: 14,
14+
fontWeight: '400',
15+
lineHeight: 20,
16+
},
17+
bodyContainer: {
18+
alignItems: 'flex-start',
19+
alignSelf: 'stretch',
20+
display: 'flex',
21+
flexDirection: 'column',
22+
gap: 24,
23+
paddingBottom: 16,
24+
paddingHorizontal: 16,
25+
paddingTop: 12,
26+
},
27+
button: {
28+
borderRadius: 32,
29+
gap: 8,
30+
},
31+
buttonContainer: {
32+
alignItems: 'flex-start',
33+
alignSelf: 'stretch',
34+
display: 'flex',
35+
flexDirection: 'row',
36+
gap: 12,
37+
width: '100%',
38+
},
39+
buttonText: {
40+
fontSize: 14,
41+
fontWeight: '700',
42+
lineHeight: 20,
43+
},
44+
container: {
45+
alignItems: 'center',
46+
borderStyle: 'solid',
47+
boxShadow:
48+
`0 1px 1px 0 ${SHADOW_COLOR_LIGHT}, 0 0 2px 0 ${SHADOW_COLOR_LIGHT}, 0 0 1px 0 ${SHADOW_COLOR_DARK}`,
49+
display: 'flex',
50+
elevation: 1,
51+
flexDirection: 'column',
52+
gap: 16,
53+
justifyContent: 'center',
54+
overflow: 'hidden',
55+
shadowColor: SHADOW_COLOR_LIGHT,
56+
shadowOffset: { width: 0, height: 1 },
57+
shadowOpacity: 0.9,
58+
shadowRadius: 2,
59+
width: '100%',
60+
},
61+
mediaContainer: {
62+
alignItems: 'flex-start',
63+
alignSelf: 'stretch',
64+
backgroundColor: IMAGE_BACKGROUND_COLOR,
65+
display: 'flex',
66+
flexDirection: 'row',
67+
height: IMAGE_HEIGHT,
68+
},
69+
mediaContainerNoImage: {
70+
alignItems: 'center',
71+
justifyContent: 'center',
72+
},
73+
mediaImage: {
74+
height: IMAGE_HEIGHT,
75+
paddingHorizontal: 0,
76+
paddingVertical: 0,
77+
width: '100%',
78+
},
79+
mediaImagePlaceholder: {
80+
height: PLACEHOLDER_IMAGE_HEIGHT,
81+
opacity: 0.25,
82+
width: PLACEHOLDER_IMAGE_WIDTH,
83+
},
84+
textContainer: {
85+
alignItems: 'flex-start',
86+
alignSelf: 'stretch',
87+
display: 'flex',
88+
flexDirection: 'column',
89+
gap: 8,
90+
},
91+
title: {
92+
fontSize: 18,
93+
fontWeight: '700',
94+
},
95+
});
Lines changed: 120 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,131 @@
1-
import { View, Text } from 'react-native';
1+
import {
2+
Image,
3+
PixelRatio,
4+
Pressable,
5+
Text,
6+
TouchableOpacity,
7+
View,
8+
type TextStyle,
9+
type ViewStyle,
10+
} from 'react-native';
11+
12+
import { IterableLogoGrey } from '../../../core/assets';
13+
import { IterableEmbeddedViewType } from '../../enums';
14+
import { useEmbeddedView } from '../../hooks/useEmbeddedView';
215
import type { IterableEmbeddedComponentProps } from '../../types/IterableEmbeddedComponentProps';
16+
import { IMAGE_HEIGHT, styles } from './IterableEmbeddedCard.styles';
17+
18+
/**
19+
* TODO: Add default action click handler. See IterableEmbeddedView for functionality.
20+
*/
321

422
export const IterableEmbeddedCard = ({
523
config,
624
message,
725
onButtonClick = () => {},
26+
onMessageClick = () => {},
827
}: IterableEmbeddedComponentProps) => {
9-
console.log(`🚀 > IterableEmbeddedCard > config:`, config);
10-
console.log(`🚀 > IterableEmbeddedCard > message:`, message);
11-
console.log(`🚀 > IterableEmbeddedCard > onButtonClick:`, onButtonClick);
28+
const {
29+
handleButtonClick,
30+
handleMessageClick,
31+
media,
32+
parsedStyles,
33+
} = useEmbeddedView(IterableEmbeddedViewType.Card, {
34+
message,
35+
config,
36+
onButtonClick,
37+
onMessageClick,
38+
});
39+
const buttons = message?.elements?.buttons ?? [];
1240

1341
return (
14-
<View>
15-
<Text>IterableEmbeddedCard</Text>
16-
</View>
42+
<Pressable onPress={() => handleMessageClick()}>
43+
<View
44+
style={[
45+
styles.container,
46+
{
47+
backgroundColor: parsedStyles.backgroundColor,
48+
borderColor: parsedStyles.borderColor,
49+
borderRadius: parsedStyles.borderCornerRadius,
50+
borderWidth: parsedStyles.borderWidth,
51+
} as ViewStyle,
52+
]}
53+
>
54+
<View
55+
style={[
56+
styles.mediaContainer,
57+
media.shouldShow ? {} : styles.mediaContainerNoImage,
58+
]}
59+
>
60+
<Image
61+
source={
62+
media.shouldShow
63+
? {
64+
uri: media.url as string,
65+
height: PixelRatio.getPixelSizeForLayoutSize(IMAGE_HEIGHT),
66+
}
67+
:
68+
IterableLogoGrey
69+
}
70+
style={
71+
media.shouldShow
72+
? styles.mediaImage
73+
: styles.mediaImagePlaceholder
74+
}
75+
alt={media.caption as string}
76+
/>
77+
</View>
78+
<View style={styles.bodyContainer}>
79+
<View style={styles.textContainer}>
80+
<Text
81+
style={[
82+
styles.title,
83+
{ color: parsedStyles.titleTextColor } as TextStyle,
84+
]}
85+
>
86+
{message.elements?.title}
87+
</Text>
88+
<Text
89+
style={[
90+
styles.body,
91+
{ color: parsedStyles.bodyTextColor } as TextStyle,
92+
]}
93+
>
94+
{message.elements?.body}
95+
</Text>
96+
</View>
97+
{buttons.length > 0 && (
98+
<View style={styles.buttonContainer}>
99+
{buttons.map((button, index) => {
100+
const backgroundColor =
101+
index === 0
102+
? parsedStyles.primaryBtnBackgroundColor
103+
: parsedStyles.secondaryBtnBackgroundColor;
104+
const textColor =
105+
index === 0
106+
? parsedStyles.primaryBtnTextColor
107+
: parsedStyles.secondaryBtnTextColor;
108+
return (
109+
<TouchableOpacity
110+
style={[styles.button, { backgroundColor } as ViewStyle]}
111+
onPress={() => handleButtonClick(button)}
112+
key={button.id}
113+
>
114+
<Text
115+
style={[
116+
styles.buttonText,
117+
{ color: textColor } as TextStyle,
118+
]}
119+
>
120+
{button.title}
121+
</Text>
122+
</TouchableOpacity>
123+
);
124+
})}
125+
</View>
126+
)}
127+
</View>
128+
</View>
129+
</Pressable>
17130
);
18131
};

0 commit comments

Comments
 (0)