mirror of
https://github.com/0xWheatyz/handler.git
synced 2026-08-30 17:56:25 +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).
98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
import React, { useState } from "react";
|
|
import {
|
|
StyleSheet,
|
|
TextInput,
|
|
View,
|
|
type KeyboardTypeOptions,
|
|
type StyleProp,
|
|
type TextInputProps,
|
|
type ViewStyle,
|
|
} from "react-native";
|
|
import { fonts, radius } from "../theme/tokens";
|
|
import { useTheme } from "../theme/useTheme";
|
|
|
|
/**
|
|
* Text field covering both the Leeworks single-line Input (answer reply) and
|
|
* the multiline task textarea on the spawn screen. Focus swaps the border to
|
|
* `--border-strong` per the Input component.
|
|
*/
|
|
export function TextField({
|
|
value,
|
|
onChangeText,
|
|
placeholder,
|
|
multiline = false,
|
|
height = 48,
|
|
style,
|
|
secureTextEntry = false,
|
|
autoCapitalize,
|
|
autoCorrect,
|
|
keyboardType,
|
|
}: {
|
|
value: string;
|
|
onChangeText: (t: string) => void;
|
|
placeholder?: string;
|
|
multiline?: boolean;
|
|
/** For single-line this is the control height; for multiline, the box height. */
|
|
height?: number;
|
|
style?: StyleProp<ViewStyle>;
|
|
secureTextEntry?: boolean;
|
|
autoCapitalize?: TextInputProps["autoCapitalize"];
|
|
autoCorrect?: boolean;
|
|
keyboardType?: KeyboardTypeOptions;
|
|
}) {
|
|
const { colors } = useTheme();
|
|
const [focus, setFocus] = useState(false);
|
|
return (
|
|
<View
|
|
style={[
|
|
styles.wrap,
|
|
{
|
|
height,
|
|
backgroundColor: colors.surfaceCard,
|
|
borderColor: focus ? colors.borderStrong : colors.borderDefault,
|
|
alignItems: multiline ? "stretch" : "center",
|
|
paddingVertical: multiline ? 12 : 0,
|
|
},
|
|
style,
|
|
]}
|
|
>
|
|
<TextInput
|
|
value={value}
|
|
onChangeText={onChangeText}
|
|
placeholder={placeholder}
|
|
placeholderTextColor={colors.textMuted}
|
|
multiline={multiline}
|
|
secureTextEntry={secureTextEntry}
|
|
autoCapitalize={autoCapitalize}
|
|
autoCorrect={autoCorrect}
|
|
keyboardType={keyboardType}
|
|
onFocus={() => setFocus(true)}
|
|
onBlur={() => setFocus(false)}
|
|
style={[
|
|
styles.input,
|
|
{
|
|
color: colors.textHeading,
|
|
textAlignVertical: multiline ? "top" : "center",
|
|
},
|
|
]}
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
wrap: {
|
|
flexDirection: "row",
|
|
borderWidth: 1,
|
|
borderRadius: radius.md,
|
|
paddingHorizontal: 14,
|
|
},
|
|
input: {
|
|
flex: 1,
|
|
alignSelf: "stretch",
|
|
fontFamily: fonts.bodyRegular,
|
|
fontSize: 15,
|
|
padding: 0,
|
|
},
|
|
});
|