feat: 初始提交(仅核心代码,已排除大文件)
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { orderBoxApi } from './api';
|
||||
import { theme } from './theme';
|
||||
import { OrderBoxState } from './types';
|
||||
import { CardsScreen, HistoryScreen } from './kits/motion_reminder';
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
||||
|
||||
type ScreenMode = 'hub' | 'motion_reminder';
|
||||
|
||||
const HubScreen = ({
|
||||
onEnterMotionReminder,
|
||||
loading,
|
||||
}: {
|
||||
onEnterMotionReminder: () => void;
|
||||
loading: boolean;
|
||||
}) => (
|
||||
<View style={styles.hubContainer}>
|
||||
<StatusBar barStyle="light-content" backgroundColor={theme.colors.background} />
|
||||
<View style={styles.hubHeader}>
|
||||
<Text style={styles.hubTitle}>框子</Text>
|
||||
<Text style={styles.hubSubtitle}>kit 聚合壳。当前阶段先落地第一个工具:运动提醒宝。</Text>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity style={styles.kitCard} disabled={loading} onPress={onEnterMotionReminder}>
|
||||
<View style={styles.kitCardIcon}>
|
||||
<Ionicons name="barbell-outline" size={28} color={theme.colors.primary} />
|
||||
</View>
|
||||
<View style={styles.kitCardBody}>
|
||||
<Text style={styles.kitCardTitle}>运动提醒宝</Text>
|
||||
<Text style={styles.kitCardText}>流程卡片、自动切步、语音播报、大字倒计时、下一个动作常驻显示。</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={24} color={theme.colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
||||
function MotionReminderKit({ onExit }: { onExit: () => void }) {
|
||||
const [serverState, setServerState] = useState<OrderBoxState | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const refreshData = useCallback(async () => {
|
||||
try {
|
||||
const nextState = await orderBoxApi.getState();
|
||||
setServerState(nextState);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '加载失败';
|
||||
Alert.alert('加载失败', message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void refreshData();
|
||||
}, [refreshData]);
|
||||
|
||||
return (
|
||||
<NavigationContainer independent>
|
||||
<Tab.Navigator
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: theme.colors.background },
|
||||
headerTintColor: theme.colors.text,
|
||||
tabBarStyle: { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border },
|
||||
tabBarActiveTintColor: theme.colors.primary,
|
||||
tabBarInactiveTintColor: theme.colors.textSecondary,
|
||||
}}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="Cards"
|
||||
children={() => <CardsScreen onExit={onExit} serverState={serverState} onRefresh={refreshData} />}
|
||||
options={{
|
||||
title: '运动提醒宝',
|
||||
tabBarLabel: '卡片',
|
||||
tabBarIcon: ({ color, size }) => <Ionicons name="albums-outline" size={size} color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="History"
|
||||
children={() => <HistoryScreen onRefresh={refreshData} />}
|
||||
options={{
|
||||
title: '执行历史',
|
||||
tabBarLabel: '历史',
|
||||
tabBarIcon: ({ color, size }) => <Ionicons name="time-outline" size={size} color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [screenMode, setScreenMode] = useState<ScreenMode>('hub');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const initialize = useCallback(async () => {
|
||||
try {
|
||||
await orderBoxApi.getMeta();
|
||||
const state = await orderBoxApi.getState();
|
||||
if (state.activeKit === 'motion_reminder') {
|
||||
setScreenMode('motion_reminder');
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '初始化失败';
|
||||
Alert.alert('初始化失败', message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void initialize();
|
||||
}, [initialize]);
|
||||
|
||||
const enterMotionReminder = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
await orderBoxApi.setActiveKit('motion_reminder');
|
||||
setScreenMode('motion_reminder');
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '进入失败';
|
||||
Alert.alert('进入失败', message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (screenMode === 'motion_reminder') {
|
||||
return <MotionReminderKit onExit={() => setScreenMode('hub')} />;
|
||||
}
|
||||
return <HubScreen loading={loading} onEnterMotionReminder={() => void enterMotionReminder()} />;
|
||||
}, [loading, screenMode]);
|
||||
|
||||
return <View style={styles.appContainer}>{content}</View>;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
appContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.background,
|
||||
},
|
||||
hubContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.background,
|
||||
padding: theme.spacing.lg,
|
||||
paddingTop: 72,
|
||||
},
|
||||
hubHeader: {
|
||||
marginBottom: theme.spacing.xl,
|
||||
},
|
||||
hubTitle: {
|
||||
...theme.typography.h1,
|
||||
color: theme.colors.text,
|
||||
marginBottom: theme.spacing.sm,
|
||||
},
|
||||
hubSubtitle: {
|
||||
...theme.typography.body,
|
||||
color: theme.colors.textSecondary,
|
||||
lineHeight: 24,
|
||||
},
|
||||
kitCard: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.colors.surface,
|
||||
borderRadius: theme.borderRadius.xl,
|
||||
padding: theme.spacing.lg,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.border,
|
||||
gap: theme.spacing.md,
|
||||
},
|
||||
kitCardIcon: {
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 28,
|
||||
backgroundColor: theme.colors.primarySoft,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
kitCardBody: {
|
||||
flex: 1,
|
||||
},
|
||||
kitCardTitle: {
|
||||
...theme.typography.h3,
|
||||
color: theme.colors.text,
|
||||
marginBottom: theme.spacing.xs,
|
||||
},
|
||||
kitCardText: {
|
||||
...theme.typography.bodySmall,
|
||||
color: theme.colors.textSecondary,
|
||||
lineHeight: 20,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user