feat(mobile): replace starter template with dashboard-driven app flow
This commit is contained in:
121
MobileApp/app/(tabs)/activity.tsx
Normal file
121
MobileApp/app/(tabs)/activity.tsx
Normal 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,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user