BigBrainParking/app/src/screens/SessionsScreen.tsx
Hank 1dede50995 Initial commit: parksmarter-client + BigBrainParking app
Reverse-engineered ParkSmarter API client (TypeScript, live-verified) plus a
de-Googled Expo/React Native app for GrapheneOS: MapLibre meter map with GPS,
VisionCamera QR kiosk scanning with save/share, local session-expiry reminders,
UnifiedPush wiring, and Gitea CI to publish signed APKs for Obtainium.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:33:47 -07:00

75 lines
2.5 KiB
TypeScript

import React, { useCallback, useState } from 'react';
import { RefreshControl, ScrollView, StyleSheet, Text, View } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
import { ps } from '@/api/client';
import type { ActiveSession, PastSession } from 'parksmarter-client';
export function SessionsScreen() {
const [active, setActive] = useState<ActiveSession[]>([]);
const [past, setPast] = useState<PastSession[]>([]);
const [refreshing, setRefreshing] = useState(false);
const load = useCallback(async () => {
setRefreshing(true);
try {
const [a, p] = await Promise.all([
ps.getActiveParkingSessions(),
ps.getPastParkingSessions({ currentPage: 1, pageSize: 20 }),
]);
setActive(a.ParkingSession ?? []);
setPast(p.Session ?? []);
} finally {
setRefreshing(false);
}
}, []);
useFocusEffect(
useCallback(() => {
void load();
}, [load]),
);
return (
<ScrollView
contentContainerStyle={{ padding: 16 }}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={load} />}
>
<Text style={styles.header}>Active</Text>
{active.length === 0 ? (
<Text style={styles.empty}>No active sessions.</Text>
) : (
active.map((s, i) => (
<View key={i} style={[styles.card, styles.activeCard]}>
<Text style={styles.zone}>{s.ZoneName ?? 'Session'}</Text>
<Text style={styles.meta}>
{s.SpaceName ?? s.Space ?? ''} · ends {s.EndTimeDisplay ?? s.EndTime ?? ''}
</Text>
</View>
))
)}
<Text style={[styles.header, { marginTop: 20 }]}>History</Text>
{past.length === 0 ? (
<Text style={styles.empty}>No past sessions.</Text>
) : (
past.map((s, i) => (
<View key={i} style={styles.card}>
<Text style={styles.zone}>{s.ZoneName ?? 'Session'}</Text>
<Text style={styles.meta}>
{s.StartTime ?? ''} · {s.Amount != null ? `$${s.Amount}` : ''}
</Text>
</View>
))
)}
</ScrollView>
);
}
const styles = StyleSheet.create({
header: { fontSize: 18, fontWeight: '700', marginBottom: 8 },
empty: { color: '#888', marginBottom: 8 },
card: { backgroundColor: '#f4f4f4', borderRadius: 10, padding: 14, marginBottom: 10 },
activeCard: { backgroundColor: '#e8f5e9' },
zone: { fontSize: 16, fontWeight: '600' },
meta: { color: '#777', fontSize: 13, marginTop: 2 },
});