Skip to content

Commit d5c8609

Browse files
authored
feat: postMessage API (#252)
* feat: proof of concept of postMessage API * feat: add postMessage demo to AppleApp * feat: iOS event emitter for postMessage * feat: dynamic ContentView entrypoint & greeting based on selected brownfield provider artifact * chore: revert accidental change * feat: rewrite the postMessage API using New Arch TurboModule APIs * chore: add changeset * chore: strip obsolete code * chore: stub old arch impl with postMessage placeholder * docs: updated docs * chore: update yarn.lock * fix(demo): fix TesterIntegrated * feat: add docstrings to postMessage methods * fix: deallocation of ReactNativeBrownfieldModule.mm static shared ref, missing import in Android OA * fix: brownfield scripts for AppleApp to use proper schemes
1 parent 71e6d3d commit d5c8609

File tree

46 files changed

+1643
-373
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1643
-373
lines changed

.changeset/fair-zebras-heal.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@callstack/react-native-brownfield': minor
3+
---
4+
5+
feat: added postMessage API
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.callstack.brownfield.android.example
22

33
object ReactNativeConstants {
4-
const val MAIN_MODULE_NAME = "RNApp"
4+
const val MAIN_MODULE_NAME = "main"
5+
const val APP_NAME = "Android (Expo)"
56
}

apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/MainActivity.kt

Lines changed: 10 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,22 @@ import androidx.appcompat.app.AppCompatActivity
99
import androidx.compose.foundation.background
1010
import androidx.compose.foundation.layout.Arrangement
1111
import androidx.compose.foundation.layout.Column
12+
import androidx.compose.foundation.layout.Spacer
1213
import androidx.compose.foundation.layout.fillMaxSize
13-
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.height
1415
import androidx.compose.foundation.layout.padding
1516
import androidx.compose.foundation.shape.RoundedCornerShape
16-
import androidx.compose.material3.Button
17-
import androidx.compose.material3.Card
18-
import androidx.compose.material3.CardDefaults
1917
import androidx.compose.material3.MaterialTheme
2018
import androidx.compose.material3.Scaffold
21-
import androidx.compose.material3.Text
2219
import androidx.compose.runtime.Composable
23-
import androidx.compose.runtime.getValue
24-
import androidx.compose.runtime.mutableStateOf
25-
import androidx.compose.runtime.saveable.rememberSaveable
26-
import androidx.compose.runtime.setValue
2720
import androidx.compose.ui.Alignment
2821
import androidx.compose.ui.Modifier
2922
import androidx.compose.ui.draw.clip
30-
import androidx.compose.ui.text.style.TextAlign
3123
import androidx.compose.ui.tooling.preview.Preview
3224
import androidx.compose.ui.unit.dp
3325
import androidx.fragment.compose.AndroidFragment
26+
import com.callstack.brownfield.android.example.components.GreetingCard
27+
import com.callstack.brownfield.android.example.components.PostMessageCard
3428
import com.callstack.brownfield.android.example.ui.theme.AndroidBrownfieldAppTheme
3529
import com.callstack.reactnativebrownfield.ReactNativeFragment
3630
import com.callstack.reactnativebrownfield.constants.ReactNativeFragmentArgNames
@@ -81,57 +75,22 @@ private fun MainScreen(modifier: Modifier = Modifier) {
8175
horizontalAlignment = Alignment.CenterHorizontally // center top bar content
8276
) {
8377
GreetingCard(
84-
name = "Android",
85-
modifier = Modifier.fillMaxWidth()
78+
name = ReactNativeConstants.APP_NAME,
8679
)
8780

81+
PostMessageCard()
82+
83+
Spacer(modifier = Modifier.height(1.dp))
84+
8885
ReactNativeView(
8986
modifier = Modifier
90-
.fillMaxWidth()
87+
.fillMaxSize()
9188
.clip(RoundedCornerShape(16.dp))
9289
.background(MaterialTheme.colorScheme.surface)
9390
)
9491
}
9592
}
9693

97-
@Composable
98-
fun GreetingCard(
99-
name: String,
100-
modifier: Modifier = Modifier
101-
) {
102-
var counter by rememberSaveable { mutableStateOf(0) }
103-
104-
Card(
105-
modifier = modifier,
106-
shape = RoundedCornerShape(16.dp),
107-
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
108-
) {
109-
Column(
110-
modifier = Modifier
111-
.padding(16.dp)
112-
.fillMaxWidth(),
113-
horizontalAlignment = Alignment.CenterHorizontally,
114-
verticalArrangement = Arrangement.spacedBy(12.dp)
115-
) {
116-
Text(
117-
text = "Hello native $name 👋",
118-
style = MaterialTheme.typography.titleMedium,
119-
textAlign = TextAlign.Center
120-
)
121-
122-
Text(
123-
text = "You clicked the button $counter time${if (counter == 1) "" else "s"}",
124-
textAlign = TextAlign.Center,
125-
style = MaterialTheme.typography.bodyMedium
126-
)
127-
128-
Button(onClick = { counter++ }) {
129-
Text("Increment counter")
130-
}
131-
}
132-
}
133-
}
134-
13594
@Composable
13695
fun ReactNativeView(
13796
modifier: Modifier = Modifier
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.callstack.brownfield.android.example.components
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.material3.Button
8+
import androidx.compose.material3.MaterialTheme
9+
import androidx.compose.material3.Text
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.runtime.getValue
12+
import androidx.compose.runtime.mutableIntStateOf
13+
import androidx.compose.runtime.saveable.rememberSaveable
14+
import androidx.compose.runtime.setValue
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.text.style.TextAlign
18+
import androidx.compose.ui.unit.dp
19+
20+
@Composable
21+
fun GreetingCard(
22+
name: String,
23+
) {
24+
var counter by rememberSaveable { mutableIntStateOf(0) }
25+
26+
MaterialCard {
27+
Column(
28+
modifier = Modifier
29+
.padding(16.dp)
30+
.fillMaxWidth(),
31+
horizontalAlignment = Alignment.CenterHorizontally,
32+
verticalArrangement = Arrangement.spacedBy(12.dp)
33+
) {
34+
Text(
35+
text = "Hello native $name 👋",
36+
style = MaterialTheme.typography.titleMedium,
37+
textAlign = TextAlign.Center
38+
)
39+
40+
Text(
41+
text = "You clicked the button $counter time${if (counter == 1) "" else "s"}",
42+
textAlign = TextAlign.Center,
43+
style = MaterialTheme.typography.bodyMedium
44+
)
45+
46+
Button(onClick = { counter++ }) {
47+
Text("Increment counter")
48+
}
49+
}
50+
}
51+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.callstack.brownfield.android.example.components
2+
3+
import androidx.compose.foundation.shape.RoundedCornerShape
4+
import androidx.compose.material3.Card
5+
import androidx.compose.material3.CardDefaults
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.Modifier
8+
import androidx.compose.ui.unit.dp
9+
10+
@Composable
11+
fun MaterialCard(
12+
modifier: Modifier = Modifier,
13+
content: @Composable () -> Unit
14+
) {
15+
Card(
16+
modifier = modifier,
17+
shape = RoundedCornerShape(16.dp),
18+
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
19+
) {
20+
content()
21+
}
22+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.callstack.brownfield.android.example.components
2+
3+
import android.widget.Toast
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.material3.Button
10+
import androidx.compose.material3.MaterialTheme
11+
import androidx.compose.material3.OutlinedTextField
12+
import androidx.compose.material3.Text
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.DisposableEffect
15+
import androidx.compose.runtime.getValue
16+
import androidx.compose.runtime.mutableIntStateOf
17+
import androidx.compose.runtime.mutableStateOf
18+
import androidx.compose.runtime.remember
19+
import androidx.compose.runtime.setValue
20+
import androidx.compose.ui.Alignment
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.platform.LocalContext
23+
import androidx.compose.ui.unit.dp
24+
import com.callstack.reactnativebrownfield.OnMessageListener
25+
import com.callstack.reactnativebrownfield.ReactNativeBrownfield
26+
import org.json.JSONObject
27+
28+
@Composable
29+
fun PostMessageCard() {
30+
var nextId by remember { mutableIntStateOf(0) }
31+
var draft by remember { mutableStateOf("") }
32+
val lastToast = remember { mutableStateOf<Toast?>(null) }
33+
34+
val context = LocalContext.current
35+
36+
DisposableEffect(Unit) {
37+
val listener = OnMessageListener { raw ->
38+
val text = try {
39+
JSONObject(raw).optString("text", raw)
40+
} catch (_: Exception) {
41+
raw
42+
}
43+
val toast = Toast.makeText(
44+
context,
45+
"Received message from React Native: $text",
46+
Toast.LENGTH_LONG
47+
)
48+
lastToast.value?.cancel() // cancel previous toast if still visible
49+
toast.show()
50+
lastToast.value = toast
51+
}
52+
ReactNativeBrownfield.shared.addMessageListener(listener)
53+
onDispose { ReactNativeBrownfield.shared.removeMessageListener(listener) }
54+
}
55+
56+
MaterialCard {
57+
Column(
58+
modifier = Modifier
59+
.padding(16.dp)
60+
.fillMaxWidth(),
61+
horizontalAlignment = Alignment.CenterHorizontally,
62+
verticalArrangement = Arrangement.spacedBy(12.dp)
63+
) {
64+
Text(
65+
"postMessage",
66+
style = MaterialTheme.typography.titleMedium,
67+
modifier = Modifier
68+
.padding(bottom = 2.dp)
69+
.align(Alignment.CenterHorizontally)
70+
)
71+
72+
Row(
73+
modifier = Modifier.fillMaxWidth(),
74+
verticalAlignment = Alignment.CenterVertically,
75+
horizontalArrangement = Arrangement.spacedBy(8.dp)
76+
) {
77+
78+
OutlinedTextField(
79+
value = draft,
80+
onValueChange = { draft = it },
81+
modifier = Modifier.weight(1f),
82+
placeholder = { Text("Type a message...") },
83+
singleLine = true,
84+
)
85+
Button(onClick = {
86+
val text = draft.ifBlank { "Hello from Android! (#${nextId++})" }
87+
val json = JSONObject().put("text", text).toString()
88+
ReactNativeBrownfield.shared.postMessage(json)
89+
draft = ""
90+
}) {
91+
Text("Send")
92+
}
93+
}
94+
}
95+
}
96+
}

apps/AndroidApp/app/src/vanilla/java/com/callstack/brownfield/android/example/ReactNativeConstants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ package com.callstack.brownfield.android.example
22

33
object ReactNativeConstants {
44
const val MAIN_MODULE_NAME = "RNApp"
5+
const val APP_NAME = "Android"
56
}

0 commit comments

Comments
 (0)