mirror of
https://github.com/0xWheatyz/handler.git
synced 2026-08-30 06:06:24 +00:00
c0342a9796
Wire the mobile app to the real Handler API instead of the transcribed prototype data: - src/api/client.ts: typed client ported from frontend/lib/api.ts (bearer auth, AuthError/ApiError, base URL param, allow401 for admin-only resume) - src/api/format.ts: relative-time + status label/tone/color helpers - src/state/ServerConfig.tsx: endpoint+token persisted to AsyncStorage - src/screens/ConnectScreen.tsx: first-open config, validated via /health then /projects - src/state/AppState.tsx: data-driven store polling projects -> agents -> checkmarks -> logs every 10s with per-item failure isolation; derives fleet counts, waiting list, recent checkmarks, merged log; answer+resume, spawn, kill mutations; 401 routes back to ConnectScreen - screens render live data; detail meta is Started/Status/Tests/Build; Pause removed (no endpoint); Kill confirms; log filters are per-project - delete src/data/mock.ts tsc clean; Hermes bundle builds (200).
103 lines
3.2 KiB
TypeScript
103 lines
3.2 KiB
TypeScript
import React from "react";
|
|
import { ScrollView, StyleSheet, Text, View } from "react-native";
|
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
import { text } from "../theme/tokens";
|
|
import { useTheme } from "../theme/useTheme";
|
|
import { Chip } from "../components/Chip";
|
|
import { Mono, SectionLabel } from "../components/primitives";
|
|
import { TabBar } from "../components/TabBar";
|
|
import { useAppState } from "../state/AppState";
|
|
import { clockTime, statusColor } from "../api/format";
|
|
|
|
export function LogScreen() {
|
|
const { colors } = useTheme();
|
|
const insets = useSafeAreaInsets();
|
|
const { logFilter, setLogFilter, globalLog, projects } = useAppState();
|
|
|
|
const filters: { key: string; label: string }[] = [
|
|
{ key: "all", label: "All" },
|
|
...projects.map((p) => ({ key: p.id, label: p.id })),
|
|
{ key: "errors", label: "Errors" },
|
|
];
|
|
|
|
const entries = globalLog.filter((e) =>
|
|
logFilter === "all" ? true : logFilter === "errors" ? e.err : e.project === logFilter,
|
|
);
|
|
|
|
return (
|
|
<View style={[styles.page, { backgroundColor: colors.surfacePage }]}>
|
|
<View style={{ height: insets.top }} />
|
|
|
|
<View style={styles.header}>
|
|
<Text style={[text.h3, { color: colors.textHeading }]}>Log</Text>
|
|
<ScrollView
|
|
horizontal
|
|
showsHorizontalScrollIndicator={false}
|
|
contentContainerStyle={styles.filters}
|
|
>
|
|
{filters.map((f) => (
|
|
<Chip
|
|
key={f.key}
|
|
label={f.label}
|
|
selected={logFilter === f.key}
|
|
onPress={() => setLogFilter(f.key)}
|
|
/>
|
|
))}
|
|
</ScrollView>
|
|
</View>
|
|
|
|
<View style={styles.todayLabel}>
|
|
<SectionLabel>Activity</SectionLabel>
|
|
</View>
|
|
|
|
<ScrollView
|
|
style={{ flex: 1 }}
|
|
contentContainerStyle={[
|
|
styles.feed,
|
|
{ backgroundColor: colors.surfaceSunken, borderTopColor: colors.borderSubtle },
|
|
]}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
{entries.length === 0 ? (
|
|
<Text style={[text.bodySm, { color: colors.textMuted }]}>No activity yet.</Text>
|
|
) : (
|
|
entries.map((e) => (
|
|
<View key={e.key} style={styles.logRow}>
|
|
<Mono style={[styles.mono, { color: colors.ink4 }]}>
|
|
{clockTime(e.createdAt)}
|
|
</Mono>
|
|
<Mono
|
|
numberOfLines={1}
|
|
style={[styles.mono, styles.idCol, { color: colors.ink6 }]}
|
|
>
|
|
{e.name}
|
|
</Mono>
|
|
<Mono style={[styles.mono, { color: colors[statusColor(e.status)], flex: 1 }]}>
|
|
{e.msg}
|
|
</Mono>
|
|
</View>
|
|
))
|
|
)}
|
|
</ScrollView>
|
|
|
|
<TabBar active="log" />
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
page: { flex: 1 },
|
|
header: { paddingHorizontal: 20, paddingTop: 20, paddingBottom: 16 },
|
|
filters: { flexDirection: "row", gap: 8, marginTop: 14, paddingRight: 20 },
|
|
todayLabel: { paddingHorizontal: 20, paddingBottom: 8 },
|
|
feed: {
|
|
borderTopWidth: 1,
|
|
paddingHorizontal: 20,
|
|
paddingVertical: 16,
|
|
flexGrow: 1,
|
|
},
|
|
logRow: { flexDirection: "row", gap: 10 },
|
|
mono: { fontSize: 12.5, lineHeight: 26 },
|
|
idCol: { width: 86 },
|
|
});
|