Kegel pelvic floor trainer — Expo/RN MVP
Some checks failed
Build & Release APK / build (push) Failing after 15s

Guided session timer: progressive squeeze-holds (5s +1s/3 sessions, cap
30s), reverse-kegel push holds, quick flicks in/out. Local-only progress
in AsyncStorage; signed APK via Gitea Actions on the desktop runner.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 00:30:04 -07:00
commit 0dc1bd47a7
69 changed files with 9398 additions and 0 deletions

62
src/app/settings.tsx Normal file
View File

@@ -0,0 +1,62 @@
import { Alert, StyleSheet, Text, View } from "react-native";
import { Button, StatRow } from "../components/ui";
import { adjustHold, currentHold, resetProgress } from "../lib/store";
import { C } from "../lib/theme";
import { useStore } from "../lib/useStore";
import { MAX_HOLD, MIN_HOLD } from "../lib/workout";
export default function Settings() {
const { sessionsCompleted, holdAdjust } = useStore();
const hold = currentHold();
return (
<View style={s.screen}>
<Text style={s.section}>Difficulty</Text>
<View style={s.card}>
<StatRow label="Current hold" value={`${hold}s`} />
<StatRow label="Manual adjustment" value={`${holdAdjust >= 0 ? "+" : ""}${holdAdjust}s`} />
</View>
<View style={s.row}>
<View style={s.half}>
<Button title="Easier −1s" onPress={() => hold > MIN_HOLD && adjustHold(-1)} />
</View>
<View style={s.half}>
<Button title="Harder +1s" onPress={() => hold < MAX_HOLD && adjustHold(1)} />
</View>
</View>
<Text style={s.hint}>
Holds grow automatically by 1s every 3 completed sessions ({MIN_HOLD}–{MAX_HOLD}s). Use these if a session
feels too easy or too hard — the adjustment stacks with the automatic progression.
</Text>
<Text style={s.section}>Progress</Text>
<Button
title="Reset progress"
danger
onPress={() =>
Alert.alert("Reset progress?", `This clears ${sessionsCompleted} completed sessions and starts over at ${MIN_HOLD}s holds.`, [
{ text: "Cancel", style: "cancel" },
{ text: "Reset", style: "destructive", onPress: resetProgress },
])
}
/>
</View>
);
}
const s = StyleSheet.create({
screen: { flex: 1, backgroundColor: C.bg, padding: 20 },
section: { color: C.muted, fontSize: 13, textTransform: "uppercase", letterSpacing: 0.5, marginBottom: 8, marginTop: 12 },
card: {
backgroundColor: C.surface,
borderRadius: 12,
borderWidth: 1,
borderColor: C.border,
paddingHorizontal: 16,
paddingVertical: 4,
marginBottom: 12,
},
row: { flexDirection: "row", gap: 12 },
half: { flex: 1 },
hint: { color: C.muted, fontSize: 13, lineHeight: 19, marginBottom: 12 },
});