Skip to content

Commit 23a47a1

Browse files
committed
merge: sync origin/main into local main
2 parents 1a7c855 + 8119cbc commit 23a47a1

17 files changed

Lines changed: 276 additions & 200 deletions

app.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"supportsTablet": true,
1616
"infoPlist": {
1717
"NSMicrophoneUsageDescription": "需要使用麦克风进行语音输入",
18-
"NSCameraUsageDescription": "需要使用相机进行扫码配置和实时视觉功能"
18+
"NSCameraUsageDescription": "需要使用相机进行扫码配置和实时视觉功能",
19+
"NSLocalNetworkUsageDescription": "需要访问本地网络连接到 N.E.K.O. 服务器"
1920
},
2021
"bundleIdentifier": "com.neko.app",
2122
"buildNumber": "1.1.0"

app/(tabs)/_layout.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Tabs } from 'expo-router';
22
import React from 'react';
3+
import { Ionicons } from '@expo/vector-icons';
34

45
import { HapticTab } from '@/components/haptic-tab';
5-
import { IconSymbol } from '@/components/ui/icon-symbol';
66
import { Colors } from '@/constants/theme';
77
import { useColorScheme } from '@/hooks/use-color-scheme';
88

@@ -15,20 +15,19 @@ export default function TabLayout() {
1515
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
1616
headerShown: false,
1717
tabBarButton: HapticTab,
18-
// tabBarStyle: { display: 'none', height: 0 },
1918
}}>
2019
<Tabs.Screen
2120
name="index"
2221
options={{
2322
title: 'Home',
24-
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
23+
tabBarIcon: ({ color, size }) => <Ionicons name="home-outline" size={size} color={color} />,
2524
}}
2625
/>
2726
<Tabs.Screen
2827
name="main"
2928
options={{
3029
title: 'Main UI',
31-
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
30+
tabBarIcon: ({ color, size }) => <Ionicons name="happy-outline" size={size} color={color} />,
3231
}}
3332
/>
3433
</Tabs>

app/(tabs)/index.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { StyleSheet, View, TouchableOpacity, Text } from 'react-native';
2+
import { Ionicons } from '@expo/vector-icons';
23
import { SafeAreaView } from 'react-native-safe-area-context';
34
import { useRouter } from 'expo-router';
45
import { useEffect, useState } from 'react';
@@ -60,6 +61,14 @@ export default function HomeScreen() {
6061
const isDark = colorScheme === 'dark';
6162
const theme = isDark ? DARK : LIGHT;
6263
const { t, i18n } = useTranslation();
64+
const [currentLang, setCurrentLang] = useState(i18n.resolvedLanguage ?? i18n.language);
65+
66+
// 监听语言变化,更新高亮状态
67+
useEffect(() => {
68+
const handler = (lang: string) => setCurrentLang(lang);
69+
i18n.on('languageChanged', handler);
70+
return () => i18n.off('languageChanged', handler);
71+
}, [i18n]);
6372

6473
const isFocused = useIsFocused();
6574
const [isConnected, setIsConnected] = useState(sessionStore.isConnected);
@@ -97,7 +106,7 @@ export default function HomeScreen() {
97106
onPress={() => router.push('/settings')}
98107
activeOpacity={0.8}
99108
>
100-
<Text style={styles.actionIcon}>🔑</Text>
109+
<Ionicons name="key-outline" size={28} color={theme.titleColor} />
101110
<Text style={[styles.actionText, { color: theme.textPrimary }]}>{t('home.apiSettings')}</Text>
102111
</TouchableOpacity>
103112

@@ -106,31 +115,31 @@ export default function HomeScreen() {
106115
onPress={() => router.push('/character-manager')}
107116
activeOpacity={0.8}
108117
>
109-
<Text style={styles.actionIcon}>🐱</Text>
118+
<Ionicons name="paw-outline" size={28} color={theme.titleColor} />
110119
<Text style={[styles.actionText, { color: theme.textPrimary }]}>{t('home.characterManager')}</Text>
111120
</TouchableOpacity>
112121
</View>
113122
</View>
114123

115124
{/* 语言选择 */}
116125
<View style={styles.section}>
117-
<Text style={[styles.sectionTitle, { color: theme.sectionTitle }]}>🌐 {t('settings.sections.language')}</Text>
126+
<Text style={[styles.sectionTitle, { color: theme.sectionTitle }]}>{t('settings.sections.language')}</Text>
118127
<View style={styles.langRow}>
119128
{SUPPORTED_LANGUAGES.map((lang) => (
120129
<TouchableOpacity
121130
key={lang.code}
122131
style={[
123132
styles.langButton,
124133
{ backgroundColor: theme.actionBtn, borderColor: theme.actionBorder },
125-
i18n.language === lang.code && { backgroundColor: theme.titleColor, borderColor: theme.titleColor },
134+
currentLang === lang.code && { backgroundColor: theme.titleColor, borderColor: theme.titleColor },
126135
]}
127136
onPress={() => changeLanguage(lang.code)}
128137
activeOpacity={0.8}
129138
>
130139
<Text style={[
131140
styles.langText,
132141
{ color: theme.textPrimary },
133-
i18n.language === lang.code && { color: '#fff', fontWeight: 'bold' },
142+
currentLang === lang.code && { color: '#fff', fontWeight: 'bold' },
134143
]}>
135144
{lang.name}
136145
</Text>
@@ -169,7 +178,7 @@ export default function HomeScreen() {
169178
onPress={() => router.push('/server-config')}
170179
activeOpacity={0.8}
171180
>
172-
<Text style={styles.configButtonIcon}>⚙️</Text>
181+
<Ionicons name="settings-outline" size={20} color={theme.configBtnText} />
173182
<Text style={[styles.configButtonText, { color: theme.configBtnText }]}>{t('home.actions.manualConfig')}</Text>
174183
</TouchableOpacity>
175184

@@ -178,7 +187,7 @@ export default function HomeScreen() {
178187
onPress={() => router.push({ pathname: '/qr-scanner', params: { returnTo: '/main' } })}
179188
activeOpacity={0.8}
180189
>
181-
<Text style={styles.configButtonIcon}>📷</Text>
190+
<Ionicons name="qr-code-outline" size={20} color={theme.configBtnText} />
182191
<Text style={[styles.configButtonText, { color: theme.configBtnText }]}>{t('home.actions.qrConfig')}</Text>
183192
</TouchableOpacity>
184193
</View>

app/(tabs)/main.tsx

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { mainManager } from '@/utils/MainManager';
1818
import { sessionStore } from '@/utils/sessionStore';
1919
import { VoicePrepareOverlay } from '@/components/VoicePrepareOverlay';
2020
import { useFocusEffect } from '@react-navigation/native';
21+
import { Ionicons } from '@expo/vector-icons';
2122
import { useLocalSearchParams } from 'expo-router';
2223
import { useTranslation } from 'react-i18next';
2324
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -741,6 +742,7 @@ const MainUIScreen: React.FC<MainUIScreenProps> = () => {
741742
return Gesture.Pan()
742743
.minPointers(2)
743744
.activateAfterLongPress(500)
745+
.enabled(isPageFocused)
744746
.runOnJS(true)
745747
.onStart(() => {
746748
const { width, height } = Dimensions.get('window');
@@ -776,12 +778,13 @@ const MainUIScreen: React.FC<MainUIScreenProps> = () => {
776778
setIsDraggingModel(false);
777779
});
778780
// eslint-disable-next-line react-hooks/exhaustive-deps
779-
}, []);
781+
}, [isPageFocused]);
780782

781783
// 双指缩放手势(捏合/张开)
782784
// 注意:Pinch 手势本身就需要双指,无需 minPointers
783785
const pinchGesture = useMemo(() => {
784786
return Gesture.Pinch()
787+
.enabled(isPageFocused)
785788
.runOnJS(true)
786789
.onStart(() => {
787790
// 记录开始时的缩放值
@@ -799,7 +802,7 @@ const MainUIScreen: React.FC<MainUIScreenProps> = () => {
799802
setIsScalingModel(false);
800803
});
801804
// eslint-disable-next-line react-hooks/exhaustive-deps
802-
}, []);
805+
}, [isPageFocused]);
803806

804807
// 组合手势:双指拖动 + 缩放同时识别
805808
const live2dGesture = useMemo(() => {
@@ -1158,7 +1161,7 @@ const MainUIScreen: React.FC<MainUIScreenProps> = () => {
11581161
clientMessageId,
11591162
});
11601163
}
1161-
chat.addMessage(`📸 [已发送${imagesToSend.length}张照片]`, 'user');
1164+
chat.addMessage(`[已发送${imagesToSend.length}张照片]`, 'user');
11621165
}
11631166

11641167
// 再发送文本
@@ -1227,41 +1230,10 @@ const MainUIScreen: React.FC<MainUIScreenProps> = () => {
12271230
<StatusToast ref={statusToastRef} />
12281231

12291232
{/* Live2D 舞台区域 */}
1230-
{/* Android 端:GestureDetector 包裹整个容器,同时支持单指注视(原生处理)和双指手势 */}
1231-
{/* iOS 端:直接渲染容器,暂不支持双指手势 */}
1232-
{Platform.OS === 'android' ? (
1233-
<GestureDetector gesture={live2dGesture}>
1234-
<View style={styles.live2dContainer}>
1235-
{/* 页面获得焦点时渲染 Live2D */}
1236-
{isPageFocused && (
1237-
<ReactNativeLive2dView
1238-
style={styles.live2dView}
1239-
{...live2d.live2dPropsForLipSync}
1240-
onTap={handleLive2DTap}
1241-
/>
1242-
)}
1243-
1244-
{/* 失去焦点时的显示 */}
1245-
{!isPageFocused && (
1246-
<View style={styles.pausedContainer}>
1247-
<Text style={styles.pausedText}>
1248-
{live2d.live2dProps.modelPath ? 'Live2D 已暂停' : '页面未激活'}
1249-
</Text>
1250-
</View>
1251-
)}
1252-
1253-
{(isDraggingModel || isScalingModel) && (
1254-
<View style={styles.dragIndicator} pointerEvents="none">
1255-
<Text style={styles.dragIndicatorText}>
1256-
{isDraggingModel && isScalingModel ? '拖动/缩放中' : isDraggingModel ? '拖动中' : '缩放中'}
1257-
</Text>
1258-
</View>
1259-
)}
1260-
</View>
1261-
</GestureDetector>
1262-
) : (
1233+
{/* GestureDetector 包裹整个容器,Android & iOS 共用双指拖动 + 捏合缩放 */}
1234+
<GestureDetector gesture={live2dGesture}>
12631235
<View style={styles.live2dContainer}>
1264-
{/* iOS 端暂不支持双指手势 */}
1236+
{/* 页面获得焦点时渲染 Live2D */}
12651237
{isPageFocused && (
12661238
<ReactNativeLive2dView
12671239
style={styles.live2dView}
@@ -1270,15 +1242,24 @@ const MainUIScreen: React.FC<MainUIScreenProps> = () => {
12701242
/>
12711243
)}
12721244

1245+
{/* 失去焦点时的显示 */}
12731246
{!isPageFocused && (
12741247
<View style={styles.pausedContainer}>
12751248
<Text style={styles.pausedText}>
12761249
{live2d.live2dProps.modelPath ? 'Live2D 已暂停' : '页面未激活'}
12771250
</Text>
12781251
</View>
12791252
)}
1253+
1254+
{(isDraggingModel || isScalingModel) && (
1255+
<View style={styles.dragIndicator} pointerEvents="none">
1256+
<Text style={styles.dragIndicatorText}>
1257+
{isDraggingModel && isScalingModel ? '拖动/缩放中' : isDraggingModel ? '拖动中' : '缩放中'}
1258+
</Text>
1259+
</View>
1260+
)}
12801261
</View>
1281-
)}
1262+
</GestureDetector>
12821263

12831264
{/*
12841265
【跨平台组件】Live2DRightToolbar 右侧工具栏
@@ -1529,7 +1510,7 @@ const MainUIScreen: React.FC<MainUIScreenProps> = () => {
15291510
}}
15301511
onPress={() => setDebugPanelVisible(true)}
15311512
>
1532-
<Text style={{ color: '#fff', fontSize: 20 }}>🔧</Text>
1513+
<Ionicons name="construct" size={20} color="#fff" />
15331514
</TouchableOpacity>
15341515

15351516
{/* 调试信息面板 */}
@@ -1559,7 +1540,7 @@ const MainUIScreen: React.FC<MainUIScreenProps> = () => {
15591540
onStartShouldSetResponder={() => true}
15601541
>
15611542
<Text style={{ color: '#40c5f1', fontSize: 18, fontWeight: 'bold', marginBottom: 15 }}>
1562-
🔧 {t('main.debug.title')}
1543+
{t('main.debug.title')}
15631544
</Text>
15641545
<ScrollView>
15651546
<Text style={{ color: '#fff', fontSize: 12, fontFamily: 'monospace', marginBottom: 10 }}>

0 commit comments

Comments
 (0)