Some checks failed
Upstream Sync / Sync latest commits from upstream repo (push) Has been cancelled
178 lines
5.3 KiB
TypeScript
178 lines
5.3 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react'
|
||
import {
|
||
View,
|
||
Text,
|
||
ScrollView,
|
||
StyleSheet,
|
||
TextInput,
|
||
Pressable,
|
||
Switch,
|
||
RefreshControl,
|
||
} from 'react-native'
|
||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||
|
||
import { useBind } from '@/context/BindContext'
|
||
import { apiFetch } from '@/lib/api'
|
||
import { theme } from '@/constants/theme'
|
||
|
||
type ProxyState = { enabled: boolean; http: string; socks: string }
|
||
|
||
export default function MoreScreen() {
|
||
const { bind } = useBind()
|
||
const [proxy, setProxy] = useState<ProxyState>({ enabled: false, http: '', socks: '' })
|
||
const [metrics, setMetrics] = useState<string>('')
|
||
const [msg, setMsg] = useState('')
|
||
const [refreshing, setRefreshing] = useState(false)
|
||
|
||
const load = useCallback(async () => {
|
||
if (!bind) return
|
||
setMsg('')
|
||
try {
|
||
const pr = await apiFetch(bind, '/proxy_config')
|
||
if (pr.ok) {
|
||
const p = (await pr.json()) as ProxyState
|
||
setProxy({
|
||
enabled: !!p.enabled,
|
||
http: String(p.http || ''),
|
||
socks: String(p.socks || ''),
|
||
})
|
||
}
|
||
const mr = await apiFetch(bind, '/system_metrics')
|
||
if (mr.ok) {
|
||
const m = await mr.json()
|
||
setMetrics(JSON.stringify(m, null, 2))
|
||
}
|
||
} catch (e) {
|
||
setMsg(e instanceof Error ? e.message : String(e))
|
||
}
|
||
}, [bind])
|
||
|
||
useEffect(() => {
|
||
void load()
|
||
}, [load])
|
||
|
||
const onRefresh = useCallback(async () => {
|
||
setRefreshing(true)
|
||
await load()
|
||
setRefreshing(false)
|
||
}, [load])
|
||
|
||
const saveProxy = useCallback(async () => {
|
||
if (!bind) return
|
||
try {
|
||
const res = await apiFetch(bind, '/proxy_config', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(proxy),
|
||
})
|
||
if (!res.ok) throw new Error(String(res.status))
|
||
setMsg('代理已保存')
|
||
} catch (e) {
|
||
setMsg(e instanceof Error ? e.message : String(e))
|
||
}
|
||
}, [bind, proxy])
|
||
|
||
if (!bind) {
|
||
return (
|
||
<SafeAreaView style={styles.safe}>
|
||
<Text style={styles.muted}>请先完成配对</Text>
|
||
</SafeAreaView>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<SafeAreaView style={styles.safe} edges={['bottom']}>
|
||
<ScrollView
|
||
contentContainerStyle={styles.scroll}
|
||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => void onRefresh()} />}
|
||
>
|
||
<Text style={styles.h1}>更多</Text>
|
||
|
||
<View style={styles.card}>
|
||
<Text style={styles.cardTitle}>录制代理(仅进程)</Text>
|
||
<View style={styles.row}>
|
||
<Text style={styles.label}>启用</Text>
|
||
<Switch value={proxy.enabled} onValueChange={(v) => setProxy((p) => ({ ...p, enabled: v }))} />
|
||
</View>
|
||
<Text style={styles.label}>HTTP</Text>
|
||
<TextInput
|
||
style={styles.input}
|
||
value={proxy.http}
|
||
onChangeText={(t) => setProxy((p) => ({ ...p, http: t }))}
|
||
placeholder="http://..."
|
||
placeholderTextColor={theme.muted}
|
||
autoCapitalize="none"
|
||
/>
|
||
<Text style={styles.label}>SOCKS</Text>
|
||
<TextInput
|
||
style={styles.input}
|
||
value={proxy.socks}
|
||
onChangeText={(t) => setProxy((p) => ({ ...p, socks: t }))}
|
||
placeholder="socks5://..."
|
||
placeholderTextColor={theme.muted}
|
||
autoCapitalize="none"
|
||
/>
|
||
<Pressable style={styles.btn} onPress={() => void saveProxy()}>
|
||
<Text style={styles.btnText}>保存代理</Text>
|
||
</Pressable>
|
||
</View>
|
||
|
||
<View style={styles.card}>
|
||
<Text style={styles.cardTitle}>系统指标</Text>
|
||
{metrics ? <Text style={styles.pre}>{metrics}</Text> : <Text style={styles.muted}>加载中…</Text>}
|
||
</View>
|
||
|
||
<View style={styles.card}>
|
||
<Text style={styles.cardTitle}>微信 Chatbot</Text>
|
||
<Text style={styles.muted}>
|
||
扫码登录与 Chatbot 配置依赖本机环境与桌面端逻辑,请在 Windows 控制台「微信」页完成。
|
||
</Text>
|
||
</View>
|
||
|
||
{!!msg && <Text style={styles.ok}>{msg}</Text>}
|
||
</ScrollView>
|
||
</SafeAreaView>
|
||
)
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
safe: { flex: 1, backgroundColor: theme.bg },
|
||
scroll: { padding: 20, paddingBottom: 40 },
|
||
h1: { fontSize: 22, fontWeight: '700', color: theme.text, marginBottom: 16 },
|
||
card: {
|
||
padding: 14,
|
||
borderRadius: 12,
|
||
backgroundColor: theme.bgCard,
|
||
borderWidth: 1,
|
||
borderColor: theme.border,
|
||
marginBottom: 14,
|
||
},
|
||
cardTitle: { fontSize: 15, fontWeight: '600', color: theme.text, marginBottom: 12 },
|
||
row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 },
|
||
label: { fontSize: 12, color: theme.muted, marginBottom: 6 },
|
||
input: {
|
||
borderWidth: 1,
|
||
borderColor: theme.border,
|
||
borderRadius: 10,
|
||
padding: 10,
|
||
color: theme.text,
|
||
marginBottom: 12,
|
||
backgroundColor: theme.bg,
|
||
},
|
||
btn: {
|
||
backgroundColor: '#3d7eff',
|
||
paddingVertical: 12,
|
||
borderRadius: 10,
|
||
alignItems: 'center',
|
||
},
|
||
btnText: { color: '#fff', fontWeight: '700' },
|
||
pre: {
|
||
fontFamily: 'monospace',
|
||
fontSize: 10,
|
||
color: '#c8cdd5',
|
||
lineHeight: 16,
|
||
},
|
||
muted: { color: theme.muted, fontSize: 13, lineHeight: 20 },
|
||
ok: { color: theme.success, marginTop: 8 },
|
||
})
|