Skip to content

Commit 5c58a85

Browse files
committed
feat: integrate postMessage demo with Expo 55
1 parent 7fbf575 commit 5c58a85

9 files changed

Lines changed: 206 additions & 7 deletions

File tree

apps/ExpoApp54/app/(tabs)/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function HomeScreen() {
2020
}
2121
>
2222
<ThemedView style={styles.titleContainer}>
23-
<ThemedText type="title">Welcome!</ThemedText>
23+
<ThemedText type="title">Welcome to Expo 54!</ThemedText>
2424
<HelloWave />
2525
</ThemedView>
2626
<ThemedView style={styles.stepContainer}>
File renamed without changes.

apps/ExpoApp/components/postMessage/MessageBubble.tsx renamed to apps/ExpoApp54/components/postMessage/MessageBubble.tsx

File renamed without changes.

apps/ExpoApp55/src/app/_layout.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
1+
import {
2+
DarkTheme,
3+
DefaultTheme,
4+
ThemeProvider,
5+
} from '@react-navigation/native';
26
import React from 'react';
37
import { useColorScheme } from 'react-native';
48

apps/ExpoApp55/src/app/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default function HomeScreen() {
3535
<ThemedView style={styles.heroSection}>
3636
<AnimatedIcon />
3737
<ThemedText type="title" style={styles.title}>
38-
Welcome to&nbsp;Expo
38+
Welcome to&nbsp;Expo&nbsp;55
3939
</ThemedText>
4040
</ThemedView>
4141

@@ -66,6 +66,7 @@ const styles = StyleSheet.create({
6666
flex: 1,
6767
justifyContent: 'center',
6868
flexDirection: 'row',
69+
paddingTop: Spacing.three,
6970
},
7071
safeArea: {
7172
flex: 1,
@@ -78,7 +79,6 @@ const styles = StyleSheet.create({
7879
heroSection: {
7980
alignItems: 'center',
8081
justifyContent: 'center',
81-
flex: 1,
8282
paddingHorizontal: Spacing.four,
8383
gap: Spacing.four,
8484
},
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { StyleSheet, FlatList, TouchableOpacity } from 'react-native';
2+
3+
import { useCallback, useEffect, useRef, useState } from 'react';
4+
import ReactNativeBrownfield from '@callstack/react-native-brownfield';
5+
import type { MessageEvent } from '@callstack/react-native-brownfield';
6+
7+
import { ThemedView } from '@/components/themed-view';
8+
import { ThemedText } from '@/components/themed-text';
9+
import type { Message } from '@/components/postMessage/Message';
10+
import { MessageBubble } from '@/components/postMessage/MessageBubble';
11+
12+
export default function HomeScreen() {
13+
const [messages, setMessages] = useState<Message[]>([]);
14+
const flatListRef = useRef<FlatList<Message>>(null);
15+
16+
const messageCounterRef = useRef(0);
17+
18+
useEffect(() => {
19+
const sub = ReactNativeBrownfield.onMessage((event: MessageEvent) => {
20+
const data = event.data as { text?: string };
21+
setMessages((prev) => [
22+
...prev,
23+
{
24+
id: String(++messageCounterRef.current),
25+
text: data?.text ?? JSON.stringify(event.data),
26+
from: 'native',
27+
timestamp: Date.now(),
28+
},
29+
]);
30+
});
31+
return () => sub.remove();
32+
}, []);
33+
34+
const sendMessage = useCallback(() => {
35+
const msg = {
36+
text: `Hello from Expo! (#${++messageCounterRef.current})`,
37+
timestamp: Date.now(),
38+
};
39+
ReactNativeBrownfield.postMessage(msg);
40+
setMessages((prev) => [
41+
...prev,
42+
{
43+
id: String(messageCounterRef.current),
44+
text: msg.text,
45+
from: 'rn',
46+
timestamp: msg.timestamp,
47+
},
48+
]);
49+
}, []);
50+
51+
return (
52+
<ThemedView style={styles.messageSection}>
53+
<TouchableOpacity
54+
style={styles.sendButton}
55+
onPress={sendMessage}
56+
activeOpacity={0.8}
57+
>
58+
<ThemedText style={styles.sendButtonText}>
59+
Send message to Native
60+
</ThemedText>
61+
</TouchableOpacity>
62+
63+
<FlatList
64+
data={messages}
65+
keyExtractor={(item) => `message-${item.id}`}
66+
renderItem={({ item }) => <MessageBubble item={item} />}
67+
style={styles.messageList}
68+
contentContainerStyle={styles.messageListContent}
69+
inverted={true} // ensure newest messages are at the top
70+
onContentSizeChange={() => {
71+
flatListRef.current?.scrollToEnd({ animated: true });
72+
}}
73+
ref={flatListRef}
74+
/>
75+
</ThemedView>
76+
);
77+
}
78+
79+
const styles = StyleSheet.create({
80+
messageSection: {
81+
flex: 1,
82+
width: '100%',
83+
padding: 12,
84+
paddingHorizontal: 20,
85+
},
86+
sendButton: {
87+
paddingVertical: 12,
88+
paddingHorizontal: 20,
89+
borderRadius: 10,
90+
alignItems: 'center',
91+
marginBottom: 10,
92+
backgroundColor: '#4F8EF7',
93+
},
94+
sendButtonText: {
95+
fontWeight: '700',
96+
fontSize: 15,
97+
color: '#fff',
98+
},
99+
messageList: {
100+
flex: 1,
101+
},
102+
messageListContent: {
103+
paddingBottom: 8,
104+
},
105+
});

apps/ExpoApp55/src/components/app-tabs.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,25 @@ export default function AppTabs() {
1212
<NativeTabs
1313
backgroundColor={colors.background}
1414
indicatorColor={colors.backgroundElement}
15-
labelStyle={{ selected: { color: colors.text } }}>
15+
labelStyle={{ selected: { color: colors.text } }}
16+
>
1617
<NativeTabs.Trigger name="index">
1718
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
18-
<NativeTabs.Trigger.Icon src={require('@/assets/images/tabIcons/home.png')} />
19+
<NativeTabs.Trigger.Icon
20+
src={require('@/assets/images/tabIcons/home.png')}
21+
/>
1922
</NativeTabs.Trigger>
2023

2124
<NativeTabs.Trigger name="explore">
2225
<NativeTabs.Trigger.Label>Explore</NativeTabs.Trigger.Label>
23-
<NativeTabs.Trigger.Icon src={require('@/assets/images/tabIcons/explore.png')} />
26+
<NativeTabs.Trigger.Icon
27+
src={require('@/assets/images/tabIcons/explore.png')}
28+
/>
29+
</NativeTabs.Trigger>
30+
31+
<NativeTabs.Trigger name="postMessage">
32+
<NativeTabs.Trigger.Label>postMessage API</NativeTabs.Trigger.Label>
33+
<NativeTabs.Trigger.Icon md="message" sf="message.fill" />
2434
</NativeTabs.Trigger>
2535
</NativeTabs>
2636
);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface Message {
2+
id: string;
3+
text: string;
4+
from: 'native' | 'rn';
5+
timestamp: number;
6+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Animated, StyleSheet } from 'react-native';
2+
import { Message } from './Message';
3+
import { useEffect, useRef } from 'react';
4+
import { ThemedText } from '@/components/themed-text';
5+
6+
export function MessageBubble({ item }: { item: Message }) {
7+
const opacity = useRef(new Animated.Value(0)).current;
8+
const translateY = useRef(new Animated.Value(20)).current;
9+
10+
useEffect(() => {
11+
Animated.parallel([
12+
Animated.timing(opacity, {
13+
toValue: 1,
14+
duration: 300,
15+
useNativeDriver: true,
16+
}),
17+
Animated.spring(translateY, {
18+
toValue: 0,
19+
tension: 80,
20+
friction: 10,
21+
useNativeDriver: true,
22+
}),
23+
]).start();
24+
}, [opacity, translateY]);
25+
26+
const isFromNative = item.from === 'native';
27+
28+
return (
29+
<Animated.View
30+
style={[
31+
styles.bubble,
32+
isFromNative ? styles.bubbleNative : styles.bubbleRN,
33+
{
34+
opacity,
35+
transform: [{ translateY }],
36+
borderColor: isFromNative ? '#4F8EF7' : '#DAA520',
37+
},
38+
]}
39+
>
40+
<ThemedText style={styles.bubbleLabel}>
41+
{isFromNative ? 'From Native' : 'From RN'}
42+
</ThemedText>
43+
<ThemedText style={styles.bubbleText}>{item.text}</ThemedText>
44+
</Animated.View>
45+
);
46+
}
47+
48+
const styles = StyleSheet.create({
49+
bubble: {
50+
padding: 10,
51+
borderRadius: 10,
52+
borderWidth: 1,
53+
marginBottom: 6,
54+
},
55+
bubbleNative: {
56+
alignSelf: 'flex-start',
57+
backgroundColor: 'rgba(79, 142, 247, 0.1)',
58+
maxWidth: '80%',
59+
},
60+
bubbleRN: {
61+
alignSelf: 'flex-end',
62+
backgroundColor: 'rgba(218, 165, 32, 0.1)',
63+
maxWidth: '80%',
64+
},
65+
bubbleLabel: {
66+
fontSize: 10,
67+
fontWeight: '600',
68+
marginBottom: 2,
69+
textTransform: 'uppercase',
70+
},
71+
bubbleText: {
72+
fontSize: 14,
73+
},
74+
});

0 commit comments

Comments
 (0)