by codex: feat. General Improvements
CodexBar Mobile Build / build-android (push) Successful in 21m9s
CodexBar Mobile Build / release (push) Successful in 11s

This commit is contained in:
2026-06-25 14:27:58 +02:00
parent 9dd17cf565
commit 2c31be11c4
33 changed files with 1217 additions and 296 deletions
+64 -46
View File
@@ -8,7 +8,6 @@ import {
TextInput,
Switch,
KeyboardAvoidingView,
Platform,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { router, useFocusEffect } from "expo-router";
@@ -26,22 +25,16 @@ import {
import { pickAndReadCodexAuth } from "@/lib/fileReader";
import { resetOnboarding } from "@/lib/setupState";
import { useNotificationSettings } from "@/hooks/useNotificationSettings";
import {
isClaudeSessionKeyValid,
validateClaudeSessionKey,
} from "@/lib/claudeCredentials";
import { COLORS } from "@/lib/constants";
import { formatTime, parseTimeInput } from "@/lib/timeUtils";
import { ScreenErrorBoundary } from "@/components/ScreenErrorBoundary";
import type { CodexAuth } from "@/types/codex";
import Constants from "expo-constants";
function parseTimeInput(s: string): { hour: number; minute: number } | null {
const m = s.trim().match(/^(\d{1,2}):(\d{2})$/);
if (!m) return null;
const h = parseInt(m[1], 10);
const min = parseInt(m[2], 10);
if (h < 0 || h > 23 || min < 0 || min > 59) return null;
return { hour: h, minute: min };
}
function formatTime(hour: number, minute: number): string {
return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
}
function SettingRow({
icon,
label,
@@ -120,7 +113,7 @@ function Divider() {
return <View className="ml-16 mr-0 h-px bg-neutral-100 dark:bg-neutral-800" />;
}
export default function SettingsTab() {
function SettingsTabContent() {
const [codexAuth, setCodexAuth] = useState<CodexAuth | null>(null);
const [claudeKey, setClaudeKey] = useState<string | null>(null);
const [pendingKey, setPendingKey] = useState("");
@@ -129,6 +122,7 @@ export default function SettingsTab() {
const notif = useNotificationSettings();
const [timeInput, setTimeInput] = useState("");
const [timeError, setTimeError] = useState<string | null>(null);
const [thresholdInput, setThresholdInput] = useState("");
// Sync local text inputs when settings load
@@ -173,8 +167,9 @@ export default function SettingsTab() {
const handleSaveClaude = async () => {
const trimmed = pendingKey.trim();
if (!trimmed.startsWith("sk-ant-")) {
setClaudeError("Key must start with sk-ant-");
const validationError = validateClaudeSessionKey(trimmed);
if (validationError) {
setClaudeError(validationError);
return;
}
const orgTrimmed = pendingOrg.trim() || undefined;
@@ -249,9 +244,10 @@ export default function SettingsTab() {
const parsed = parseTimeInput(timeInput);
if (parsed) {
void notif.update({ dailyHour: parsed.hour, dailyMinute: parsed.minute });
setTimeInput(formatTime(parsed.hour, parsed.minute));
setTimeError(null);
} else {
// Reset to last valid value
setTimeInput(formatTime(notif.settings.dailyHour, notif.settings.dailyMinute));
setTimeError("Use HH:MM format, from 00:00 to 23:59.");
}
}, [timeInput, notif]);
@@ -264,7 +260,9 @@ export default function SettingsTab() {
}
}, [thresholdInput, notif]);
const canSaveClaude = pendingKey.trim().startsWith("sk-ant-");
const canSaveClaude = isClaudeSessionKeyValid(pendingKey);
const inlineClaudeError =
claudeError ?? (pendingKey ? validateClaudeSessionKey(pendingKey) : null);
const appVersion = Constants.expoConfig?.version ?? "1.0.0";
return (
@@ -274,7 +272,7 @@ export default function SettingsTab() {
>
<KeyboardAvoidingView
className="flex-1"
behavior={Platform.OS === "ios" ? "padding" : "height"}
behavior={process.env.EXPO_OS === "ios" ? "padding" : "height"}
>
<ScrollView
className="flex-1"
@@ -341,9 +339,9 @@ export default function SettingsTab() {
</>
) : (
<View className="p-4">
{claudeError && (
<Text className="text-xs text-red-500 mb-2">
{claudeError}
{inlineClaudeError && (
<Text selectable className="text-xs text-red-500 mb-2">
{inlineClaudeError}
</Text>
)}
<TextInput
@@ -373,12 +371,14 @@ export default function SettingsTab() {
disabled={!canSaveClaude}
className="py-3 px-4 rounded-xl items-center"
style={{
backgroundColor: canSaveClaude ? "#d97706" : "#e5e5e5",
backgroundColor: canSaveClaude
? COLORS.claude
: COLORS.disabled,
}}
>
<Text
className="font-semibold text-sm"
style={{ color: canSaveClaude ? "white" : "#a3a3a3" }}
style={{ color: canSaveClaude ? "white" : COLORS.muted }}
>
Save Session Key
</Text>
@@ -446,7 +446,7 @@ export default function SettingsTab() {
<Switch
value={notif.settings.dailyEnabled}
onValueChange={(v) => void notif.update({ dailyEnabled: v })}
trackColor={{ false: "#e5e5e5", true: "#10a37f" }}
trackColor={{ false: COLORS.disabled, true: COLORS.codex }}
thumbColor="white"
/>
</View>
@@ -454,25 +454,35 @@ export default function SettingsTab() {
{notif.settings.dailyEnabled && (
<>
<Divider />
<View className="flex-row items-center gap-x-3 py-3 px-4">
<View className="w-8 items-center">
<MaterialIcons name="schedule" size={20} color="#737373" />
<View className="px-4 py-3">
<View className="flex-row items-center gap-x-3">
<View className="w-8 items-center">
<MaterialIcons name="schedule" size={20} color="#737373" />
</View>
<Text className="flex-1 text-sm font-medium text-neutral-800 dark:text-neutral-100">
Time
</Text>
<TextInput
value={timeInput}
onChangeText={(value) => {
setTimeInput(value);
setTimeError(null);
}}
onBlur={handleTimeBlur}
placeholder="09:00"
placeholderTextColor={COLORS.muted}
keyboardType="numbers-and-punctuation"
returnKeyType="done"
maxLength={5}
className="text-sm font-mono text-neutral-600 dark:text-neutral-300 text-right"
style={{ minWidth: 52 }}
/>
</View>
<Text className="flex-1 text-sm font-medium text-neutral-800 dark:text-neutral-100">
Time
</Text>
<TextInput
value={timeInput}
onChangeText={setTimeInput}
onBlur={handleTimeBlur}
placeholder="09:00"
placeholderTextColor="#a3a3a3"
keyboardType="numbers-and-punctuation"
returnKeyType="done"
maxLength={5}
className="text-sm font-mono text-neutral-600 dark:text-neutral-300 text-right"
style={{ minWidth: 52 }}
/>
{timeError && (
<Text selectable className="ml-11 mt-1.5 text-xs text-red-500">
{timeError}
</Text>
)}
</View>
</>
)}
@@ -495,7 +505,7 @@ export default function SettingsTab() {
<Switch
value={notif.settings.thresholdEnabled}
onValueChange={(v) => void notif.update({ thresholdEnabled: v })}
trackColor={{ false: "#e5e5e5", true: "#d97706" }}
trackColor={{ false: COLORS.disabled, true: COLORS.claude }}
thumbColor="white"
/>
</View>
@@ -554,3 +564,11 @@ export default function SettingsTab() {
</SafeAreaView>
);
}
export default function SettingsTab() {
return (
<ScreenErrorBoundary screenName="Settings">
<SettingsTabContent />
</ScreenErrorBoundary>
);
}