refactor headers into shared screen shell
This commit is contained in:
@@ -1,71 +1,45 @@
|
|||||||
import { View, Pressable, Text } from "react-native";
|
import type { ReactNode } from "react";
|
||||||
|
import { Pressable, Text } from "react-native";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
||||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
import { ArrowLeft } from "lucide-react-native";
|
import { ArrowLeft } from "lucide-react-native";
|
||||||
|
import { ScreenHeader } from "./screen-header";
|
||||||
|
|
||||||
interface BackHeaderProps {
|
interface BackHeaderProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
rightContent?: React.ReactNode;
|
rightContent?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BackHeader({ title, rightContent }: BackHeaderProps) {
|
export function BackHeader({ title, rightContent }: BackHeaderProps) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const insets = useSafeAreaInsets();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.header}>
|
<ScreenHeader
|
||||||
<View style={{ paddingTop: insets.top + 12 }}>
|
left={
|
||||||
<View style={styles.headerRow}>
|
<>
|
||||||
{/* Left side - Back button */}
|
<Pressable
|
||||||
<View style={styles.headerLeft}>
|
onPress={() => router.back()}
|
||||||
<Pressable
|
style={styles.backButton}
|
||||||
onPress={() => router.back()}
|
>
|
||||||
style={styles.backButton}
|
<ArrowLeft size={20} color={theme.colors.foreground} />
|
||||||
>
|
</Pressable>
|
||||||
<ArrowLeft size={20} color={theme.colors.foreground} />
|
{title && (
|
||||||
</Pressable>
|
<Text style={styles.title} numberOfLines={1}>
|
||||||
{title && (
|
{title}
|
||||||
<Text style={styles.title} numberOfLines={1}>
|
</Text>
|
||||||
{title}
|
)}
|
||||||
</Text>
|
</>
|
||||||
)}
|
}
|
||||||
</View>
|
right={rightContent}
|
||||||
|
leftStyle={styles.left}
|
||||||
{/* Right side */}
|
/>
|
||||||
<View style={styles.headerRight}>
|
|
||||||
{rightContent}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
const styles = StyleSheet.create((theme) => ({
|
||||||
header: {
|
left: {
|
||||||
backgroundColor: theme.colors.background,
|
|
||||||
},
|
|
||||||
headerRow: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
paddingHorizontal: theme.spacing[3],
|
|
||||||
paddingBottom: theme.spacing[2],
|
|
||||||
borderBottomWidth: theme.borderWidth[1],
|
|
||||||
borderBottomColor: theme.colors.border,
|
|
||||||
},
|
|
||||||
headerLeft: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: theme.spacing[3],
|
gap: theme.spacing[3],
|
||||||
},
|
},
|
||||||
headerRight: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: theme.spacing[2],
|
|
||||||
},
|
|
||||||
backButton: {
|
backButton: {
|
||||||
padding: theme.spacing[3],
|
padding: theme.spacing[3],
|
||||||
borderRadius: theme.borderRadius.lg,
|
borderRadius: theme.borderRadius.lg,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { View, Pressable } from "react-native";
|
import { Pressable } from "react-native";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
||||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
import { Settings, MessageSquare, Plus } from "lucide-react-native";
|
import { Settings, MessageSquare, Plus } from "lucide-react-native";
|
||||||
|
import { ScreenHeader } from "./screen-header";
|
||||||
|
|
||||||
interface HomeHeaderProps {
|
interface HomeHeaderProps {
|
||||||
onCreateAgent: () => void;
|
onCreateAgent: () => void;
|
||||||
@@ -10,64 +10,35 @@ interface HomeHeaderProps {
|
|||||||
|
|
||||||
export function HomeHeader({ onCreateAgent }: HomeHeaderProps) {
|
export function HomeHeader({ onCreateAgent }: HomeHeaderProps) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const insets = useSafeAreaInsets();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.header}>
|
<ScreenHeader
|
||||||
<View style={{ paddingTop: insets.top + 12 }}>
|
left={
|
||||||
<View style={styles.headerRow}>
|
<Pressable
|
||||||
{/* Left side - Settings */}
|
onPress={() => router.push("/settings")}
|
||||||
<View style={styles.headerLeft}>
|
style={styles.iconButton}
|
||||||
<Pressable
|
>
|
||||||
onPress={() => router.push("/settings")}
|
<Settings size={20} color={theme.colors.foreground} />
|
||||||
style={styles.iconButton}
|
</Pressable>
|
||||||
>
|
}
|
||||||
<Settings size={20} color={theme.colors.foreground} />
|
right={
|
||||||
</Pressable>
|
<>
|
||||||
</View>
|
<Pressable
|
||||||
|
onPress={() => router.push("/orchestrator")}
|
||||||
{/* Right side - Activity and New Agent */}
|
style={styles.iconButton}
|
||||||
<View style={styles.headerRight}>
|
>
|
||||||
<Pressable
|
<MessageSquare size={20} color={theme.colors.foreground} />
|
||||||
onPress={() => router.push("/orchestrator")}
|
</Pressable>
|
||||||
style={styles.iconButton}
|
<Pressable onPress={onCreateAgent} style={styles.iconButton}>
|
||||||
>
|
<Plus size={20} color={theme.colors.foreground} />
|
||||||
<MessageSquare size={20} color={theme.colors.foreground} />
|
</Pressable>
|
||||||
</Pressable>
|
</>
|
||||||
<Pressable onPress={onCreateAgent} style={styles.iconButton}>
|
}
|
||||||
<Plus size={20} color={theme.colors.foreground} />
|
/>
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
const styles = StyleSheet.create((theme) => ({
|
||||||
header: {
|
|
||||||
backgroundColor: theme.colors.background,
|
|
||||||
},
|
|
||||||
headerRow: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
paddingHorizontal: theme.spacing[3],
|
|
||||||
paddingBottom: theme.spacing[2],
|
|
||||||
borderBottomWidth: theme.borderWidth[1],
|
|
||||||
borderBottomColor: theme.colors.border,
|
|
||||||
},
|
|
||||||
headerLeft: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: theme.spacing[2],
|
|
||||||
},
|
|
||||||
headerRight: {
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: theme.spacing[2],
|
|
||||||
},
|
|
||||||
iconButton: {
|
iconButton: {
|
||||||
padding: theme.spacing[3],
|
padding: theme.spacing[3],
|
||||||
borderRadius: theme.borderRadius.lg,
|
borderRadius: theme.borderRadius.lg,
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { View, type StyleProp, type ViewStyle } from "react-native";
|
||||||
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
|
|
||||||
|
interface ScreenHeaderProps {
|
||||||
|
left?: ReactNode;
|
||||||
|
right?: ReactNode;
|
||||||
|
leftStyle?: StyleProp<ViewStyle>;
|
||||||
|
rightStyle?: StyleProp<ViewStyle>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared frame for the home/back headers so we only maintain padding, border,
|
||||||
|
* and safe-area logic in one place.
|
||||||
|
*/
|
||||||
|
export function ScreenHeader({ left, right, leftStyle, rightStyle }: ScreenHeaderProps) {
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
const { theme } = useUnistyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.header}>
|
||||||
|
<View style={[styles.inner, { paddingTop: insets.top + 12 }]}>
|
||||||
|
<View style={styles.row}>
|
||||||
|
<View style={[styles.left, leftStyle]}>{left}</View>
|
||||||
|
<View style={[styles.right, rightStyle]}>{right}</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create((theme) => ({
|
||||||
|
header: {
|
||||||
|
backgroundColor: theme.colors.background,
|
||||||
|
},
|
||||||
|
inner: {},
|
||||||
|
row: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingHorizontal: theme.spacing[3],
|
||||||
|
paddingBottom: theme.spacing[2],
|
||||||
|
borderBottomWidth: theme.borderWidth[1],
|
||||||
|
borderBottomColor: theme.colors.border,
|
||||||
|
},
|
||||||
|
left: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: theme.spacing[2],
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: theme.spacing[2],
|
||||||
|
},
|
||||||
|
}));
|
||||||
@@ -86,5 +86,6 @@
|
|||||||
- Split the server build/typecheck configs so the typechecker can include the app stream helpers without breaking builds, added path aliases for `@server/*`, and tightened the shared stream types (status unions + `isAgentToolCallItem`) so the server e2e suites can import them safely. Cleaned up every failing app/server test by narrowing tool-call payloads via the helper, updated the harness + Codex/Claude specs, and re-ran `npm run typecheck` (app + server) to green.
|
- Split the server build/typecheck configs so the typechecker can include the app stream helpers without breaking builds, added path aliases for `@server/*`, and tightened the shared stream types (status unions + `isAgentToolCallItem`) so the server e2e suites can import them safely. Cleaned up every failing app/server test by narrowing tool-call payloads via the helper, updated the harness + Codex/Claude specs, and re-ran `npm run typecheck` (app + server) to green.
|
||||||
- [x] audit codebase for unecessary untyped code, hacks, casts, and `any`, type things properly
|
- [x] audit codebase for unecessary untyped code, hacks, casts, and `any`, type things properly
|
||||||
- Tightened the Codex rollout parser typings so permission requests, rollout/event payloads, plan arguments, and JSON helpers all operate on well-defined TypeScript structures instead of `any`. Added explicit payload/entry guards, removed unsafe casts throughout `packages/server/src/server/agent/providers/codex-agent.ts`, and re-ran `npm run typecheck --workspace=@voice-dev/server` to validate the stricter typing.
|
- Tightened the Codex rollout parser typings so permission requests, rollout/event payloads, plan arguments, and JSON helpers all operate on well-defined TypeScript structures instead of `any`. Added explicit payload/entry guards, removed unsafe casts throughout `packages/server/src/server/agent/providers/codex-agent.ts`, and re-ran `npm run typecheck --workspace=@voice-dev/server` to validate the stricter typing.
|
||||||
- [ ] audit codebase for duplicated code, clean it up
|
- [x] audit codebase for duplicated code, clean it up
|
||||||
|
- Consolidated the duplicated home/back header layouts into a shared `ScreenHeader` component so safe-area padding, borders, and spacing live in one place; both headers now only define their unique button/title content. Ran `npm run typecheck --workspace=@voice-dev/app` to confirm the refactor stays type-safe.
|
||||||
- [ ] rename the project to Paseo, including the Expo app, package names etc.
|
- [ ] rename the project to Paseo, including the Expo app, package names etc.
|
||||||
|
|||||||
Reference in New Issue
Block a user