Skip to content

Commit c6a9ab0

Browse files
authored
feat: bottom tabs and ability to play audio buffers (#100)
* feat: bottom tabs * fix * fix * fix * feat: add ability to play audiobuffers * feat: tweaks
1 parent 4790476 commit c6a9ab0

File tree

10 files changed

+504
-294
lines changed

10 files changed

+504
-294
lines changed

apps/example-apple/app.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,40 @@
3333
{
3434
"calendarPermission": "The app needs to access your calendar."
3535
}
36+
],
37+
"react-native-bottom-tabs",
38+
[
39+
"expo-build-properties",
40+
{
41+
"ios": {
42+
"extraPods": [
43+
{
44+
"name": "SDWebImage",
45+
"modular_headers": true
46+
},
47+
{
48+
"name": "SDWebImageSVGCoder",
49+
"modular_headers": true
50+
}
51+
]
52+
}
53+
}
54+
],
55+
[
56+
"react-native-audio-api",
57+
{
58+
"iosBackgroundMode": true,
59+
"iosMicrophonePermission": "This app requires access to the microphone to record audio.",
60+
"androidPermissions" : [
61+
"android.permission.MODIFY_AUDIO_SETTINGS",
62+
"android.permission.FOREGROUND_SERVICE",
63+
"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"
64+
],
65+
"androidForegroundService": true,
66+
"androidFSTypes": [
67+
"mediaPlayback"
68+
]
69+
}
3670
]
3771
]
3872
}

apps/example-apple/metro.config.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
// Learn more https://docs.expo.io/guides/customizing-metro
22
const { getDefaultConfig } = require('expo/metro-config')
33
const { withNativeWind } = require('nativewind/metro')
4+
const {
5+
wrapWithAudioAPIMetroConfig,
6+
} = require('react-native-audio-api/metro-config')
47

58
const config = getDefaultConfig(__dirname)
69

710
// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
811
config.resolver.disableHierarchicalLookup = true
912

10-
module.exports = withNativeWind(config, { input: './src/global.css' })
13+
module.exports = wrapWithAudioAPIMetroConfig(
14+
withNativeWind(config, { input: './src/global.css' })
15+
)

apps/example-apple/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,29 @@
1111
},
1212
"dependencies": {
1313
"@azure/core-asynciterator-polyfill": "^1.0.2",
14+
"@bottom-tabs/react-navigation": "^0.10.2",
1415
"@react-native-ai/apple": "workspace:*",
1516
"@react-native-picker/picker": "2.11.1",
1617
"@react-navigation/bottom-tabs": "^7.4.4",
1718
"@react-navigation/native": "^7.1.16",
19+
"@react-navigation/native-stack": "^7.3.25",
1820
"@ungap/structured-clone": "^1.3.0",
1921
"ai": "^5.0.0-beta.25",
2022
"expo": "^53.0.0",
2123
"expo-battery": "~9.1.4",
24+
"expo-build-properties": "~0.14.8",
2225
"expo-calendar": "~14.1.4",
2326
"expo-clipboard": "~7.1.5",
2427
"expo-document-picker": "~13.1.6",
2528
"expo-status-bar": "2.2.3",
2629
"nativewind": "^4.1.23",
2730
"react": "19.0.0",
2831
"react-native": "0.79.5",
32+
"react-native-audio-api": "^0.7.1",
33+
"react-native-bottom-tabs": "^0.11.0",
2934
"react-native-reanimated": "~3.17.4",
3035
"react-native-safe-area-context": "5.4.0",
31-
"react-native-screens": "~4.11.1",
36+
"react-native-screens": "4.15.4",
3237
"web-streams-polyfill": "^4.1.0",
3338
"zod": "^4.0.0"
3439
},

apps/example-apple/src/App.tsx

Lines changed: 110 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,126 @@
11
import './global.css'
22

3-
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
3+
import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation'
44
import { NavigationContainer } from '@react-navigation/native'
5+
import { createNativeStackNavigator } from '@react-navigation/native-stack'
56
import { StatusBar } from 'expo-status-bar'
67
import React from 'react'
8+
import { SafeAreaProvider } from 'react-native-safe-area-context'
79

810
import LLMScreen from './screens/LLMScreen'
911
import PlaygroundScreen from './screens/PlaygroundScreen'
1012
import SpeechScreen from './screens/SpeechScreen'
1113
import TranscribeScreen from './screens/TranscribeScreen'
1214

13-
const Tab = createBottomTabNavigator()
15+
const Tab = createNativeBottomTabNavigator()
16+
17+
const RootStack = createNativeStackNavigator()
18+
const LLMStack = createNativeStackNavigator()
19+
const PlaygroundStack = createNativeStackNavigator()
20+
const TranscribeStack = createNativeStackNavigator()
21+
const SpeechStack = createNativeStackNavigator()
22+
23+
const headerOptions = {
24+
headerShown: true,
25+
headerLargeTitle: true,
26+
}
27+
28+
function LLMStackScreen() {
29+
return (
30+
<LLMStack.Navigator>
31+
<LLMStack.Screen
32+
name="LLMScreen"
33+
component={LLMScreen}
34+
options={headerOptions}
35+
/>
36+
</LLMStack.Navigator>
37+
)
38+
}
39+
40+
function PlaygroundStackScreen() {
41+
return (
42+
<PlaygroundStack.Navigator>
43+
<PlaygroundStack.Screen
44+
name="PlaygroundScreen"
45+
component={PlaygroundScreen}
46+
options={headerOptions}
47+
/>
48+
</PlaygroundStack.Navigator>
49+
)
50+
}
51+
52+
function TranscribeStackScreen() {
53+
return (
54+
<TranscribeStack.Navigator>
55+
<TranscribeStack.Screen
56+
name="TranscribeScreen"
57+
component={TranscribeScreen}
58+
options={headerOptions}
59+
/>
60+
</TranscribeStack.Navigator>
61+
)
62+
}
63+
64+
function SpeechStackScreen() {
65+
return (
66+
<SpeechStack.Navigator>
67+
<SpeechStack.Screen
68+
name="SpeechScreen"
69+
component={SpeechScreen}
70+
options={headerOptions}
71+
/>
72+
</SpeechStack.Navigator>
73+
)
74+
}
75+
76+
function Tabs() {
77+
return (
78+
<Tab.Navigator sidebarAdaptable>
79+
<Tab.Screen
80+
name="LLM"
81+
component={LLMStackScreen}
82+
options={{
83+
tabBarIcon: () => ({ sfSymbol: 'brain.head.profile' }),
84+
}}
85+
/>
86+
<Tab.Screen
87+
name="Playground"
88+
component={PlaygroundStackScreen}
89+
options={{
90+
tabBarIcon: () => ({ sfSymbol: 'play.circle' }),
91+
}}
92+
/>
93+
<Tab.Screen
94+
name="Transcribe"
95+
component={TranscribeStackScreen}
96+
options={{
97+
tabBarIcon: () => ({ sfSymbol: 'text.quote' }),
98+
}}
99+
/>
100+
<Tab.Screen
101+
name="Speech"
102+
component={SpeechStackScreen}
103+
options={{
104+
tabBarIcon: () => ({ sfSymbol: 'speaker.wave.3' }),
105+
}}
106+
/>
107+
</Tab.Navigator>
108+
)
109+
}
14110

15111
export default function App() {
16112
return (
17-
<NavigationContainer>
18-
<Tab.Navigator>
19-
<Tab.Screen name="LLM" component={LLMScreen} />
20-
<Tab.Screen name="Playground" component={PlaygroundScreen} />
21-
<Tab.Screen name="Transcribe" component={TranscribeScreen} />
22-
<Tab.Screen name="Speech" component={SpeechScreen} />
23-
</Tab.Navigator>
24-
<StatusBar style="auto" />
25-
</NavigationContainer>
113+
<SafeAreaProvider>
114+
<NavigationContainer>
115+
<RootStack.Navigator>
116+
<RootStack.Screen
117+
name="Home"
118+
component={Tabs}
119+
options={{ headerShown: false }}
120+
/>
121+
</RootStack.Navigator>
122+
<StatusBar style="auto" />
123+
</NavigationContainer>
124+
</SafeAreaProvider>
26125
)
27126
}

0 commit comments

Comments
 (0)