feat(mobile): Expo → RN CLI 전환 + Android 빌드 성공

RN 0.85 + React 19 기반 apps/mobile-rn 프로젝트 생성.
6개 화면 이식 (Login, Dash, History, Record, Talk, Settings).
@react-navigation/native + bottom-tabs 네비게이션 구성.
Windows NDK CMake libc++_shared 링크 버그 워크아라운드 포함.
Galaxy Fold (SM_F956N) 무선 디버깅 APK 설치 확인.
This commit is contained in:
yunchan8804 2026-04-13 21:13:40 +09:00
parent 55eb62189a
commit bf36a9d97d
68 changed files with 13955 additions and 9375 deletions

View file

@ -0,0 +1,149 @@
// src/navigation/TabNavigator.tsx — Bottom tabs: DASH / HIST / REC / TALK / SET
import { View, StyleSheet, Pressable, Platform } from 'react-native'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { d3roNativePalette } from '@d3ro/ui-native'
import DashScreen from '../screens/DashScreen'
import HistoryScreen from '../screens/HistoryScreen'
import RecordScreen from '../screens/RecordScreen'
import TalkScreen from '../screens/TalkScreen'
import SettingsScreen from '../screens/SettingsScreen'
const MONO = Platform.OS === 'ios' ? 'Menlo' : 'monospace'
const Tab = createBottomTabNavigator()
function TabIcon({ type, color }: { type: string; color: string }): React.ReactElement {
if (type === 'dash') {
return (
<View style={[iconStyles.grid, { borderColor: color }]}>
{[0, 1, 2, 3].map((i) => (
<View key={i} style={[iconStyles.gridCell, { borderColor: color }]} />
))}
</View>
)
}
if (type === 'hist') {
return (
<View style={[iconStyles.circle, { borderColor: color }]}>
<View style={[iconStyles.hand, { backgroundColor: color }]} />
</View>
)
}
if (type === 'talk') {
return <View style={[iconStyles.chat, { borderColor: color }]} />
}
if (type === 'set') {
return <View style={[iconStyles.gear, { borderColor: color }]} />
}
return <View />
}
function RecordFAB(): React.ReactElement {
return (
<View style={fabStyles.fab}>
<View style={fabStyles.micIcon}>
<View style={fabStyles.micBody} />
<View style={fabStyles.micBase} />
</View>
</View>
)
}
export default function TabNavigator(): React.ReactElement {
return (
<Tab.Navigator
screenOptions={{
tabBarStyle: {
backgroundColor: 'rgba(25, 25, 27, 0.95)',
borderTopColor: d3roNativePalette.border.default,
borderTopWidth: 1,
height: 80,
paddingBottom: 20,
paddingTop: 8,
},
tabBarActiveTintColor: d3roNativePalette.accent.amber,
tabBarInactiveTintColor: d3roNativePalette.text.muted,
tabBarLabelStyle: {
fontFamily: MONO,
fontSize: 9,
fontWeight: '500',
letterSpacing: 0.5,
},
headerShown: false,
}}
>
<Tab.Screen
name="Dash"
component={DashScreen}
options={{
title: 'DASH',
tabBarIcon: ({ color }) => <TabIcon type="dash" color={color} />,
}}
/>
<Tab.Screen
name="History"
component={HistoryScreen}
options={{
title: 'HIST',
tabBarIcon: ({ color }) => <TabIcon type="hist" color={color} />,
}}
/>
<Tab.Screen
name="Record"
component={RecordScreen}
options={{
title: '',
tabBarIcon: () => <RecordFAB />,
tabBarLabel: () => null,
}}
/>
<Tab.Screen
name="Talk"
component={TalkScreen}
options={{
title: 'TALK',
tabBarIcon: ({ color }) => <TabIcon type="talk" color={color} />,
}}
/>
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={{
title: 'SET',
tabBarIcon: ({ color }) => <TabIcon type="set" color={color} />,
}}
/>
</Tab.Navigator>
)
}
const iconStyles = StyleSheet.create({
grid: { width: 22, height: 22, flexDirection: 'row', flexWrap: 'wrap', gap: 2 },
gridCell: { width: 9, height: 9, borderWidth: 1.5, borderRadius: 2 },
circle: { width: 22, height: 22, borderRadius: 11, borderWidth: 1.5, justifyContent: 'center', alignItems: 'center' },
hand: { width: 1.5, height: 7, position: 'absolute', top: 3 },
chat: { width: 22, height: 18, borderWidth: 1.5, borderRadius: 4 },
gear: { width: 22, height: 22, borderWidth: 1.5, borderRadius: 11 },
})
const fabStyles = StyleSheet.create({
fab: {
position: 'relative',
top: -20,
width: 56,
height: 56,
borderRadius: 28,
backgroundColor: d3roNativePalette.accent.amber,
justifyContent: 'center',
alignItems: 'center',
shadowColor: d3roNativePalette.accent.amber,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.4,
shadowRadius: 10,
elevation: 8,
borderWidth: 4,
borderColor: d3roNativePalette.bg.app,
},
micIcon: { alignItems: 'center' },
micBody: { width: 8, height: 14, borderRadius: 4, backgroundColor: d3roNativePalette.bg.app, marginBottom: 2 },
micBase: { width: 14, height: 2, borderRadius: 1, backgroundColor: d3roNativePalette.bg.app },
})