feat(mobile): replace starter template with dashboard-driven app flow

This commit is contained in:
2026-03-07 10:20:00 +00:00
parent d03b22a99f
commit 64684eaae6
34 changed files with 4645 additions and 895 deletions

View File

@@ -0,0 +1,121 @@
import { useFocusEffect } from '@react-navigation/native';
import React from 'react';
import { Pressable, SafeAreaView, ScrollView, StyleSheet, Text, View } from 'react-native';
import { useApp } from '@/src/app-context';
export default function ActivityScreen() {
const { state, actions } = useApp();
useFocusEffect(
React.useCallback(() => {
actions.setPage('activity');
return undefined;
}, [actions]),
);
return (
<SafeAreaView style={styles.safe}>
<View style={styles.header}>
<Text style={styles.title}>Activity History</Text>
<Pressable style={styles.clearButton} onPress={actions.clearNotifications}>
<Text style={styles.clearButtonText}>Clear Read</Text>
</Pressable>
</View>
<ScrollView contentContainerStyle={styles.content}>
{state.motionNotifications.length === 0 ? (
<View style={styles.emptyState}>
<Text style={styles.emptyText}>All quiet. No notifications yet.</Text>
</View>
) : (
state.motionNotifications.map((notification) => (
<Pressable
key={notification.id}
style={[styles.item, notification.isRead ? styles.readItem : styles.unreadItem]}
onPress={() =>
void actions.openMotionNotificationTarget(notification.id, notification.cameraDeviceId)
}>
<Text style={styles.itemMessage}>{notification.message}</Text>
<Text style={styles.itemDate}>{new Date(notification.createdAt).toLocaleString()}</Text>
</Pressable>
))
)}
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: {
flex: 1,
backgroundColor: '#0a0a0c',
},
header: {
paddingHorizontal: 14,
paddingTop: 8,
paddingBottom: 10,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
title: {
color: '#f9fafb',
fontSize: 22,
fontWeight: '700',
},
clearButton: {
borderRadius: 10,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.16)',
paddingHorizontal: 10,
height: 34,
alignItems: 'center',
justifyContent: 'center',
},
clearButtonText: {
color: '#d1d5db',
fontSize: 12,
fontWeight: '600',
},
content: {
padding: 14,
gap: 10,
},
emptyState: {
borderRadius: 16,
borderWidth: 1,
borderStyle: 'dashed',
borderColor: 'rgba(255,255,255,0.2)',
backgroundColor: '#111218',
padding: 26,
alignItems: 'center',
},
emptyText: {
color: '#6b7280',
fontSize: 13,
},
item: {
borderRadius: 12,
borderWidth: 1,
padding: 12,
gap: 6,
},
unreadItem: {
backgroundColor: '#14213d',
borderColor: '#1d4ed8',
},
readItem: {
backgroundColor: '#111218',
borderColor: 'rgba(255,255,255,0.08)',
},
itemMessage: {
color: '#e5e7eb',
fontSize: 12,
fontWeight: '500',
},
itemDate: {
color: '#6b7280',
fontSize: 11,
},
});