Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 48 additions & 10 deletions apps/TesterIntegrated/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { useEffect } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { useEffect } from 'react';
import { StyleSheet, Text, View, Button, TextInput } from 'react-native';
import { useBrownieStore } from '@callstack/brownie';
import {
createNativeStackNavigator,
type NativeStackScreenProps,
} from '@react-navigation/native-stack';
import ReactNativeBrownfield from '@callstack/react-native-brownfield';
import { NavigationContainer } from '@react-navigation/native';
import './BrownfieldStore.brownie';

const getRandomValue = () => Math.round(Math.random() * 255);
const getRandomTheme = () => {
Expand All @@ -22,10 +24,17 @@ const getRandomTheme = () => {
};
};

type Props = NativeStackScreenProps<RootStackParamList, 'Home'>;
type RootStackParamList = {
Home: { theme: { primary: string; secondary: string } };
};

function HomeScreen({ navigation, route }: Props) {
const colors = route.params?.theme || getRandomTheme();
type HomeScreenProps = NativeStackScreenProps<RootStackParamList, 'Home'>;

const theme = getRandomTheme();

function HomeScreen({ navigation, route }: HomeScreenProps) {
const colors = route.params?.theme || theme;
const [state, setState] = useBrownieStore('BrownfieldStore');

useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
Expand All @@ -37,10 +46,29 @@ function HomeScreen({ navigation, route }: Props) {

return (
<View style={[styles.container, { backgroundColor: colors.primary }]}>
<Text style={[styles.text, { color: colors.secondary }]}>
<Text style={[styles.title, { color: colors.secondary }]}>
React Native Screen
</Text>

<Text style={[styles.text, { color: colors.secondary }]}>
Count: {state.counter}
</Text>

<TextInput
style={styles.input}
value={state.user.name}
onChangeText={(text) =>
setState((prev) => ({ user: { ...prev.user, name: text } }))
}
placeholder="User name"
/>

<Button
onPress={() => setState((prev) => ({ counter: prev.counter + 1 }))}
color={colors.secondary}
title="Increment"
/>

<Button
onPress={() => {
navigation.push('Home', {
Expand All @@ -65,9 +93,6 @@ function HomeScreen({ navigation, route }: Props) {
</View>
);
}
type RootStackParamList = {
Home: { theme: { primary: string; secondary: string } };
};

const Stack = createNativeStackNavigator<RootStackParamList>();

Expand All @@ -87,9 +112,22 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
},
text: {
title: {
fontSize: 30,
fontWeight: 'bold',
margin: 10,
},
text: {
fontSize: 16,
margin: 5,
},
input: {
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
padding: 10,
width: 200,
marginVertical: 10,
backgroundColor: '#fff',
},
});
21 changes: 21 additions & 0 deletions apps/TesterIntegrated/BrownfieldStore.brownie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { BrownieStore } from '@callstack/brownie';

interface BrownfieldStore extends BrownieStore {
counter: number;
user: {
name: string;
};
}

interface SettingsStore extends BrownieStore {
theme: 'light' | 'dark';
notificationsEnabled: boolean;
privacyMode: boolean;
}

declare module '@callstack/brownie' {
interface BrownieStores {
BrownfieldStore: BrownfieldStore;
SettingsStore: SettingsStore;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.callstack.kotlinexample

data class BrownfieldStore (
val counter: Double,
val user: User
) {
companion object {
const val STORE_NAME = "BrownfieldStore"
}
}

data class User (
val name: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.callstack.kotlinexample

data class SettingsStore (
val notificationsEnabled: Boolean,
val privacyMode: Boolean,
val theme: Theme
) {
companion object {
const val STORE_NAME = "SettingsStore"
}
}

enum class Theme {
Dark,
Light
}
23 changes: 9 additions & 14 deletions apps/TesterIntegrated/metro.config.cjs
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const path = require('path');
const { getDefaultConfig } = require('@react-native/metro-config'); // Import from `@expo/metro-config` if using Expo CLI
const { withMetroConfig } = require('react-native-monorepo-config');

const root = path.resolve(__dirname, '../..');

/**
* Metro configuration
* https://reactnative.dev/docs/metro
*
* @type {import('@react-native/metro-config').MetroConfig}
*/
const config = {
watchFolders: [root],
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);
module.exports = withMetroConfig(
getDefaultConfig(__dirname), // Metro config to extend
{
root: path.resolve(__dirname, '../..'), // Path to the monorepo root
dirname: __dirname, // Path to the current directory
}
);
21 changes: 15 additions & 6 deletions apps/TesterIntegrated/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
"scripts": {
"start": "react-native start",
"build:tester-integrated:android": "cd kotlin && ./gradlew assembleDebug",
"build:tester-integrated:ios": "cd swift && xcodebuild -workspace SwiftExample.xcworkspace -scheme SwiftExample -configuration Release -sdk iphonesimulator build CODE_SIGNING_ALLOWED=NO -derivedDataPath ./build"
"build:tester-integrated:ios": "cd swift && xcodebuild -workspace SwiftExample.xcworkspace -scheme SwiftExample -configuration Release -sdk iphonesimulator build CODE_SIGNING_ALLOWED=NO -derivedDataPath ./build",
"codegen": "brownie codegen"
},
"dependencies": {
"@callstack/brownie": "*",
"@callstack/react-native-brownfield": "*",
"@react-navigation/native": "^7.0.15",
"@react-navigation/native-stack": "^7.2.1",
"react": "19.1.1",
"react-native": "0.82.1"
"react-native": "0.82.1",
"react-native-safe-area-context": "^5.3.0",
"react-native-screens": "^4.15.2"
},
"devDependencies": {
"@babel/core": "^7.25.2",
Expand All @@ -22,12 +28,15 @@
"@react-native/babel-preset": "0.82.1",
"@react-native/metro-config": "0.82.1",
"@react-native/typescript-config": "0.82.1",
"@react-navigation/native": "^7.0.15",
"@react-navigation/native-stack": "^7.2.1",
"react-native-safe-area-context": "^5.3.0",
"react-native-screens": "^4.15.2"
"@types/react": "^19.1.1",
"react-native-monorepo-config": "^0.3.1"
},
"engines": {
"node": ">=20"
},
"brownie": {
"swift": "./swift/Generated",
"kotlin": "./kotlin/app/src/main/java/com/callstack/kotlinexample/Generated",
"kotlinPackageName": "com.callstack.kotlinexample"
}
}
12 changes: 10 additions & 2 deletions apps/TesterIntegrated/react-native.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const path = require('path');
const pkg = require('../../packages/react-native-brownfield/package.json');
const brownfieldPkg = require('../../packages/react-native-brownfield/package.json');
const browniePkg = require('../../packages/brownie/package.json');

module.exports = {
project: {
Expand All @@ -11,12 +12,19 @@ module.exports = {
},
},
dependencies: {
[pkg.name]: {
[brownfieldPkg.name]: {
root: path.join(__dirname, '../../packages/react-native-brownfield'),
platforms: {
ios: {},
android: {},
},
},
[browniePkg.name]: {
root: path.join(__dirname, '../../packages/brownie'),
platforms: {
ios: {},
android: null,
},
},
},
};
129 changes: 107 additions & 22 deletions apps/TesterIntegrated/swift/App.swift
Original file line number Diff line number Diff line change
@@ -1,35 +1,120 @@
import Brownie
import ReactBrownfield
import SwiftUI

let initialState = BrownfieldStore(
counter: 0,
user: User(name: "Username")
)

/*
Toggles testing playground for side by side brownie mode.
Default: false
*/
let isSideBySideMode = false

@main
struct MyApp: App {
init() {
ReactNativeBrownfield.shared.startReactNative {
print("loaded")
}
init() {
ReactNativeBrownfield.shared.startReactNative {
print("loaded")
}

_ = Store(initialState, key: BrownfieldStore.storeName)
}

var body: some Scene {
WindowGroup {
ContentView()
}
}

struct ContentView: View {
var body: some View {
if isSideBySideMode {
SideBySideView()
} else {
FullScreenView()
}
}
}

struct SideBySideView: View {
var body: some View {
VStack(spacing: 0) {
NativeView()
.frame(maxHeight: .infinity)

Divider()

ReactNativeView(moduleName: "ReactNative")
.frame(maxHeight: .infinity)
}
}
}

var body: some Scene {
WindowGroup {
ContentView()
struct FullScreenView: View {
@UseStore<BrownfieldStore> var store

var body: some View {
NavigationView {
VStack {
Text("React Native Brownfield App")
.font(.title)
.bold()
.padding()
.multilineTextAlignment(.center)

Text("Count: \(Int(store.state.counter))")

TextField("Name", text: Binding(get: { store.state.user.name }, set: { data in
store.set { $0.user.name = data }
}))
.textFieldStyle(.roundedBorder)
.padding(.horizontal)

Button("Increment") {
store.set { $0.counter += 1 }
}
.buttonStyle(.borderedProminent)
.padding(.bottom)

NavigationLink("Push React Native Screen") {
ReactNativeView(moduleName: "ReactNative")
.navigationBarHidden(true)
}
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
}

struct NativeView: View {
@UseStore<BrownfieldStore> var store

struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("React Native Brownfield App")
.font(.title)
.bold()
.padding()

NavigationLink("Push React Native Screen") {
ReactNativeView(moduleName: "ReactNative")
.navigationBarHidden(true)
}
}
}.navigationViewStyle(StackNavigationViewStyle())
VStack {
Text("Native Side")
.font(.headline)
.padding(.top)

Text("User: \(store.state.user.name)")
Text("Count: \(Int(store.state.counter))")

TextField("Name", text: Binding(get: { store.state.user.name }, set: { data in
store.set { $0.user.name = data }
}))
.textFieldStyle(.roundedBorder)
.padding(.horizontal)

Button("Increment") {
store.set { $0.counter += 1 }
}
.buttonStyle(.borderedProminent)

Spacer()
}
.frame(maxWidth: .infinity)
.background(Color(.systemBackground))
}
}
}
Loading