diff --git a/AGENTS.md b/AGENTS.md
index 171015e..f94f916 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -25,7 +25,7 @@ src/
navigation/ # React Navigation (RootNav, TabNav, feature stacks)
db/ # Drizzle schema, client, relations, operations
providers/ # DatabaseProvider, SettingsProvider (MMKV)
- utils/ # formatTime, calculateSwolf, etc.
+ utils/ # formatTime, calculateSwolf, uuid, etc.
constants/ # strokes, poolLengths, colors
types/ # workout.ts, common.ts
modules/watch-connectivity/# Expo native module bridging WCSession
@@ -45,7 +45,7 @@ targets/watch/ # Apple Watch app (Swift/SwiftUI) — survives preb
- Lives in `targets/watch/`, NOT in `ios/` — survives `expo prebuild --clean`
- Config: `targets/watch/expo-target.config.js`
- Build: open `ios/SwimBuddy.xcworkspace` in Xcode, select Watch scheme
-- Uses App Groups (`group.nl.guido-it.swimbuddy`) for data sharing via UserDefaults
+- Uses App Groups (`group.nl.guido-it.swimbuddy`) for data sharing
- WCSession for real-time messaging, `transferUserInfo` for reliable offline sync
- HealthKit for swim workout tracking (indoor swimming)
- Swift types `StoredWorkout`, `StoredSet`, `CompletedLap` in `WorkoutManager.swift`
@@ -53,12 +53,40 @@ targets/watch/ # Apple Watch app (Swift/SwiftUI) — survives preb
## Database
- **expo-sqlite** + **Drizzle ORM** (SQLite dialect, expo driver)
-- Schema: `src/db/schema.ts` — 4 tables: templates, workouts, sets, laps
+- Schema: `src/db/schema.ts` — tables: templates, workouts, sets, laps
- Relations: `src/db/relations.ts`
- CRUD: `src/db/operations.ts`
- Client: `src/db/client.ts` — exports `db` and `DATABASE_NAME`
- Migrations in `drizzle/` — generated by `npx drizzle-kit generate`
+## WCSession Gotchas (Critical)
+
+These are hard-won bugs that will waste hours if rediscovered:
+
+- **`NSNull` crashes `sendMessage`**: JS `null` becomes `NSNull` in ObjC, causing `WCErrorCodePayloadUnsupportedTypes` (Code 7010). Always strip `null`/`undefined` values before sending via `cleanForWCSession()`.
+- **Two delegate methods conflict**: Having both `session(_:didReceiveMessage:)` AND `session(_:didReceiveMessage:replyHandler:)` on the same delegate causes iOS to deliver neither. Watch uses `didReceiveMessage` (no replyHandler); phone uses `didReceiveMessage:replyHandler:` for bidirectional sync.
+- **`isReachable` is unreliable**: Only `true` when both apps are foreground. Check `session.activationState == .activated` instead for connection readiness.
+- **Phone `sendMessage` must use `replyHandler: nil`**: Otherwise JS thread blocks waiting for a reply that watch never sends. Use fire-and-forget for workout plans; use `transferUserInfo` for data sync.
+- **Watch `suiteName` vs `UserDefaults.standard`**: Watch only needs local storage, so use `UserDefaults.standard` — the App Groups suite name is only needed for phone↔watch shared data.
+- **Phone `pendingEvents` polling**: Phone polls `pollEvents()` every 2s because Expo Modules' `sendEvent` API doesn't work on `AppContext`. Watch data arrives via `didReceiveUserInfo` delegate → stored in `pendingEvents` array.
+
+## Watch ↔ Phone Data Flow
+
+- **Phone → Watch**: `sendMessage` with workout plan (fire-and-forget, promise resolves immediately with `["status":"sent"]`)
+- **Watch → Phone**: `transferUserInfo` with completed workout data (reliable, works in background)
+- **Phone receives**: `pollEvents()` called from JS every 2s, `saveWatchCompletedWorkout()` writes to SQLite
+
+## UI Conventions
+
+- Swipe-to-delete on lists uses `SwipeableRow` component (`react-native-gesture-handler`)
+- BuilderScreen pool length is a 25m/50m toggle (not read-only from settings)
+- Templates and Stats features removed from navigation (DB schema preserved but UI deleted)
+- Ionicons used for bottom tab icons (`@expo/vector-icons`)
+
+## NativeWind v5 TextInput Crash
+
+`react-native-css` has a bug where the `textAlign` mapping on `TextInput` causes a crash (`path.split` on boolean `true`). The fix is in `src/utils/` — don't remove the workaround without testing TextInput rendering.
+
## Gotchas
- `metro.config.js` has `.sql` in `sourceExts` — required for Drizzle migration imports
diff --git a/package-lock.json b/package-lock.json
index 47cf60e..705e570 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
"react": "19.2.3",
"react-native": "0.86.2",
"react-native-css": "^3.0.7",
+ "react-native-gesture-handler": "~2.32.0",
"react-native-mmkv": "^4.3.2",
"react-native-reanimated": "4.5.1",
"react-native-safe-area-context": "~5.7.0",
@@ -1610,6 +1611,18 @@
"dev": true,
"license": "Apache-2.0"
},
+ "node_modules/@egjs/hammerjs": {
+ "version": "2.0.17",
+ "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
+ "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hammerjs": "^2.0.36"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/@esbuild-kit/core-utils": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz",
@@ -3774,6 +3787,12 @@
"@types/ms": "*"
}
},
+ "node_modules/@types/hammerjs": {
+ "version": "2.0.46",
+ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz",
+ "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==",
+ "license": "MIT"
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -3817,12 +3836,20 @@
"version": "19.2.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
- "devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
}
},
+ "node_modules/@types/react-test-renderer": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz",
+ "integrity": "sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/yargs": {
"version": "17.0.35",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz",
@@ -4639,7 +4666,6 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "devOptional": true,
"license": "MIT"
},
"node_modules/debug": {
@@ -5992,6 +6018,21 @@
"hermes-estree": "0.35.0"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
"node_modules/hosted-git-info": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
@@ -7938,6 +7979,22 @@
"@babel/types": "^7.26.0"
}
},
+ "node_modules/react-native-gesture-handler": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.32.0.tgz",
+ "integrity": "sha512-uYIMOKlKENORq2SABE+jIjbPU+h5I/sQKcq2v16zRq848nwEp1fWRVwML4QWqijc8UcXJC25o54S8GQd4Mf2OA==",
+ "license": "MIT",
+ "dependencies": {
+ "@egjs/hammerjs": "^2.0.17",
+ "@types/react-test-renderer": "^19.1.0",
+ "hoist-non-react-statics": "^3.3.0",
+ "invariant": "^2.2.4"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/react-native-is-edge-to-edge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz",
diff --git a/package.json b/package.json
index 5e4ae2f..6fd979c 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
"react": "19.2.3",
"react-native": "0.86.2",
"react-native-css": "^3.0.7",
+ "react-native-gesture-handler": "~2.32.0",
"react-native-mmkv": "^4.3.2",
"react-native-reanimated": "4.5.1",
"react-native-safe-area-context": "~5.7.0",
diff --git a/src/components/SwipeableRow.tsx b/src/components/SwipeableRow.tsx
new file mode 100644
index 0000000..051207c
--- /dev/null
+++ b/src/components/SwipeableRow.tsx
@@ -0,0 +1,100 @@
+import { useRef } from "react";
+import { View, Text, Pressable, Animated, StyleSheet } from "react-native";
+import { PanGestureHandler, State } from "react-native-gesture-handler";
+
+type SwipeableRowProps = {
+ children: React.ReactNode;
+ onDelete: () => void;
+};
+
+export function SwipeableRow({ children, onDelete }: SwipeableRowProps) {
+ const translateX = useRef(new Animated.Value(0)).current;
+ const lastOffset = useRef(0);
+
+ const onGestureEvent = Animated.event([{ nativeEvent: { translationX: translateX } }], {
+ useNativeDriver: true,
+ });
+
+ const onHandlerStateChange = (event: any) => {
+ if (event.oldState === State.ACTIVE) {
+ const { translationX } = event.nativeEvent;
+ const newOffset = lastOffset.current + translationX;
+
+ if (newOffset < -80) {
+ // Swiped left enough — show delete
+ Animated.spring(translateX, {
+ toValue: -80,
+ useNativeDriver: true,
+ }).start();
+ lastOffset.current = -80;
+ } else {
+ // Snap back
+ Animated.spring(translateX, {
+ toValue: 0,
+ useNativeDriver: true,
+ }).start();
+ lastOffset.current = 0;
+ }
+ }
+ };
+
+ const handleDelete = () => {
+ Animated.timing(translateX, {
+ toValue: -400,
+ duration: 200,
+ useNativeDriver: true,
+ }).start(() => {
+ onDelete();
+ });
+ };
+
+ return (
+
+
+
+ Delete
+
+
+
+
+ {children}
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ marginBottom: 12,
+ },
+ deleteBackground: {
+ position: "absolute",
+ right: 0,
+ top: 0,
+ bottom: 0,
+ width: 80,
+ borderRadius: 16,
+ backgroundColor: "#ef4444",
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ deleteButton: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ width: "100%",
+ },
+ deleteText: {
+ color: "white",
+ fontWeight: "600",
+ fontSize: 13,
+ },
+ foreground: {
+ backgroundColor: "white",
+ borderRadius: 16,
+ },
+});
diff --git a/src/features/history/screens/HistoryScreen.tsx b/src/features/history/screens/HistoryScreen.tsx
index b681dce..3ee029c 100644
--- a/src/features/history/screens/HistoryScreen.tsx
+++ b/src/features/history/screens/HistoryScreen.tsx
@@ -1,11 +1,13 @@
-import { useEffect, useState } from "react";
-import { View, Text, Pressable, FlatList } from "react-native";
+import { useCallback, useState } from "react";
+import { View, Text, Pressable, FlatList, Alert } from "react-native";
import { useNavigation } from "@react-navigation/native";
+import { useFocusEffect } from "@react-navigation/native";
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import type { HistoryStackParamList } from "@navigation/types";
-import { getAllWorkouts } from "@db/operations";
+import { getAllWorkouts, deleteWorkout } from "@db/operations";
import { formatTime } from "@utils/formatTime";
import { formatDistance } from "@utils/formatDistance";
+import { SwipeableRow } from "@components/SwipeableRow";
type WorkoutRow = {
id: string;
@@ -20,16 +22,39 @@ type WorkoutRow = {
export function HistoryScreen() {
const navigation = useNavigation>();
const [workouts, setWorkouts] = useState([]);
-
- useEffect(() => {
- loadWorkouts();
- }, []);
+ const [refreshing, setRefreshing] = useState(false);
const loadWorkouts = async () => {
const data = await getAllWorkouts();
setWorkouts((data as WorkoutRow[]).filter((w) => w.finishedAt));
};
+ useFocusEffect(
+ useCallback(() => {
+ loadWorkouts();
+ }, [])
+ );
+
+ const onRefresh = async () => {
+ setRefreshing(true);
+ await loadWorkouts();
+ setRefreshing(false);
+ };
+
+ const onDelete = (id: string) => {
+ Alert.alert("Delete Workout", "Are you sure?", [
+ { text: "Cancel", style: "cancel" },
+ {
+ text: "Delete",
+ style: "destructive",
+ onPress: async () => {
+ await deleteWorkout(id);
+ await loadWorkouts();
+ },
+ },
+ ]);
+ };
+
const grouped = workouts.reduce>((acc, w) => {
const date = new Date(w.startedAt).toLocaleDateString();
if (!acc[date]) acc[date] = [];
@@ -41,19 +66,12 @@ export function HistoryScreen() {
return (
-
- navigation.navigate("Stats")}
- className="bg-slate-100 rounded-2xl p-3 items-center mb-4"
- >
- View Statistics
-
-
-
date}
contentContainerClassName="px-4 pb-8"
+ refreshing={refreshing}
+ onRefresh={onRefresh}
ListEmptyComponent={
No completed workouts yet
@@ -65,30 +83,31 @@ export function HistoryScreen() {
{date}
{rows.map((w) => (
- navigation.navigate("SessionDetail", { sessionId: w.id })}
- className="bg-slate-50 rounded-2xl p-4 mb-2 border border-slate-200"
- >
-
-
-
- {w.name || "Swim Session"}
-
-
- {formatDistance(w.totalDistance ?? 0)}
- {w.totalDuration ? ` · ${formatTime(w.totalDuration)}` : ""}
- {w.calories ? ` · ${Math.round(w.calories)} kcal` : ""}
+ onDelete(w.id)}>
+ navigation.navigate("SessionDetail", { sessionId: w.id })}
+ className="p-4"
+ >
+
+
+
+ {w.name || "Swim Session"}
+
+
+ {formatDistance(w.totalDistance ?? 0)}
+ {w.totalDuration ? ` · ${formatTime(w.totalDuration)}` : ""}
+ {w.calories ? ` · ${Math.round(w.calories)} kcal` : ""}
+
+
+
+ {new Date(w.startedAt).toLocaleTimeString([], {
+ hour: "2-digit",
+ minute: "2-digit",
+ })}
-
- {new Date(w.startedAt).toLocaleTimeString([], {
- hour: "2-digit",
- minute: "2-digit",
- })}
-
-
-
+
+
))}
)}
diff --git a/src/features/history/screens/SessionDetailScreen.tsx b/src/features/history/screens/SessionDetailScreen.tsx
index e798ac1..a66e44b 100644
--- a/src/features/history/screens/SessionDetailScreen.tsx
+++ b/src/features/history/screens/SessionDetailScreen.tsx
@@ -1,7 +1,8 @@
import { useEffect, useState } from "react";
-import { View, Text, ScrollView } from "react-native";
+import { View, Text, ScrollView, Pressable, Alert } from "react-native";
+import { useNavigation } from "@react-navigation/native";
import type { HistoryStackProps } from "@navigation/types";
-import { getWorkoutWithSets } from "@db/operations";
+import { getWorkoutWithSets, deleteWorkout } from "@db/operations";
import { formatTime } from "@utils/formatTime";
import { formatDistance } from "@utils/formatDistance";
import { STROKE_COLORS } from "@constants/strokes";
@@ -34,6 +35,7 @@ type WorkoutDetail = {
};
export function SessionDetailScreen({ route }: HistoryStackProps<"SessionDetail">) {
+ const navigation = useNavigation();
const [workout, setWorkout] = useState(null);
useEffect(() => {
@@ -45,6 +47,20 @@ export function SessionDetailScreen({ route }: HistoryStackProps<"SessionDetail"
setWorkout(data as WorkoutDetail | null);
};
+ const handleDelete = () => {
+ Alert.alert("Delete Workout", "Are you sure?", [
+ { text: "Cancel", style: "cancel" },
+ {
+ text: "Delete",
+ style: "destructive",
+ onPress: async () => {
+ await deleteWorkout(route.params.sessionId);
+ navigation.goBack();
+ },
+ },
+ ]);
+ };
+
if (!workout) {
return (
@@ -62,13 +78,21 @@ export function SessionDetailScreen({ route }: HistoryStackProps<"SessionDetail"
return (
-
-
- {workout.name || "Swim Session"}
-
-
- {new Date(workout.startedAt).toLocaleDateString()}
-
+
+
+
+ {workout.name || "Swim Session"}
+
+
+ {new Date(workout.startedAt).toLocaleDateString()}
+
+
+
+ 🗑
+
diff --git a/src/features/workout-builder/screens/BuilderScreen.tsx b/src/features/workout-builder/screens/BuilderScreen.tsx
index e052322..d07b7e4 100644
--- a/src/features/workout-builder/screens/BuilderScreen.tsx
+++ b/src/features/workout-builder/screens/BuilderScreen.tsx
@@ -11,7 +11,8 @@ const STROKES: StrokeType[] = ["Freestyle", "Backstroke", "Breaststroke", "Butte
export function BuilderScreen() {
const navigation = useNavigation>();
- const { poolLength } = useSettings();
+ const { poolLength: defaultPoolLength } = useSettings();
+ const [poolLength, setPoolLength] = useState(defaultPoolLength);
const {
sets,
workoutName,
@@ -53,6 +54,29 @@ export function BuilderScreen() {
/>
+
+ Pool Length
+
+ {[25, 50].map((length) => (
+ setPoolLength(length)}
+ className={`flex-1 py-2.5 rounded-xl items-center ${
+ poolLength === length ? "bg-sky-500" : "bg-slate-100"
+ }`}
+ >
+
+ {length}m
+
+
+ ))}
+
+
+
{sets.map((set, index) => (
diff --git a/src/features/workout-builder/screens/WorkoutListScreen.tsx b/src/features/workout-builder/screens/WorkoutListScreen.tsx
index c27d872..b786874 100644
--- a/src/features/workout-builder/screens/WorkoutListScreen.tsx
+++ b/src/features/workout-builder/screens/WorkoutListScreen.tsx
@@ -1,11 +1,13 @@
-import { useEffect, useState } from "react";
-import { View, Text, Pressable, FlatList } from "react-native";
+import { useCallback, useState } from "react";
+import { View, Text, Pressable, FlatList, Alert } from "react-native";
import { useNavigation } from "@react-navigation/native";
+import { useFocusEffect } from "@react-navigation/native";
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import type { WorkoutStackParamList } from "@navigation/types";
import { getAllWorkouts, deleteWorkout } from "@db/operations";
import { formatTime } from "@utils/formatTime";
import { formatDistance } from "@utils/formatDistance";
+import { SwipeableRow } from "@components/SwipeableRow";
type WorkoutRow = {
id: string;
@@ -19,37 +21,56 @@ type WorkoutRow = {
export function WorkoutListScreen() {
const navigation = useNavigation>();
const [workouts, setWorkouts] = useState([]);
-
- useEffect(() => {
- loadWorkouts();
- }, []);
+ const [refreshing, setRefreshing] = useState(false);
const loadWorkouts = async () => {
const data = await getAllWorkouts();
setWorkouts(data as WorkoutRow[]);
};
+ useFocusEffect(
+ useCallback(() => {
+ loadWorkouts();
+ }, [])
+ );
+
+ const onRefresh = async () => {
+ setRefreshing(true);
+ await loadWorkouts();
+ setRefreshing(false);
+ };
+
+ const onDelete = (id: string) => {
+ Alert.alert("Delete Workout", "Are you sure?", [
+ { text: "Cancel", style: "cancel" },
+ {
+ text: "Delete",
+ style: "destructive",
+ onPress: async () => {
+ await deleteWorkout(id);
+ await loadWorkouts();
+ },
+ },
+ ]);
+ };
+
return (
navigation.navigate("Builder")}
- className="bg-sky-500 rounded-2xl p-4 items-center mb-3"
+ className="bg-sky-500 rounded-2xl p-4 items-center mb-4"
>
+ New Workout
- navigation.navigate("Templates")}
- className="bg-slate-100 rounded-2xl p-3 items-center mb-4"
- >
- Browse Templates
-
item.id}
contentContainerClassName="px-4 pb-8"
+ refreshing={refreshing}
+ onRefresh={onRefresh}
ListEmptyComponent={
No workouts yet
@@ -57,30 +78,32 @@ export function WorkoutListScreen() {
}
renderItem={({ item }) => (
- navigation.navigate("PreSwim", { workoutId: item.id })}
- className="bg-slate-50 rounded-2xl p-4 mb-3 border border-slate-200"
- >
-
-
-
- {item.name || "Untitled Workout"}
-
-
- {formatDistance(item.totalDistance ?? 0)}
- {item.totalDuration ? ` · ${formatTime(item.totalDuration)}` : ""}
-
-
- {new Date(item.startedAt).toLocaleDateString()}
-
-
- {item.finishedAt && (
-
- Done
+ onDelete(item.id)}>
+ navigation.navigate("PreSwim", { workoutId: item.id })}
+ className="p-4"
+ >
+
+
+
+ {item.name || "Untitled Workout"}
+
+
+ {formatDistance(item.totalDistance ?? 0)}
+ {item.totalDuration ? ` · ${formatTime(item.totalDuration)}` : ""}
+
+
+ {new Date(item.startedAt).toLocaleDateString()}
+
- )}
-
-
+ {item.finishedAt && (
+
+ Done
+
+ )}
+
+
+
)}
/>
diff --git a/src/navigation/HistoryNavigator.tsx b/src/navigation/HistoryNavigator.tsx
index 57d2ab0..ead6ed5 100644
--- a/src/navigation/HistoryNavigator.tsx
+++ b/src/navigation/HistoryNavigator.tsx
@@ -2,7 +2,6 @@ import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { HistoryStackParamList } from "./types";
import { HistoryScreen } from "../features/history/screens/HistoryScreen";
import { SessionDetailScreen } from "../features/history/screens/SessionDetailScreen";
-import { StatsScreen } from "../features/history/screens/StatsScreen";
const Stack = createNativeStackNavigator();
@@ -25,11 +24,6 @@ export function HistoryNavigator() {
component={SessionDetailScreen}
options={{ title: "Session Detail" }}
/>
-
);
}
diff --git a/src/navigation/RootNavigator.tsx b/src/navigation/RootNavigator.tsx
index c647364..9cf8890 100644
--- a/src/navigation/RootNavigator.tsx
+++ b/src/navigation/RootNavigator.tsx
@@ -5,7 +5,6 @@ import { BuilderScreen } from "../features/workout-builder/screens/BuilderScreen
import { PreSwimScreen } from "../features/active-workout/screens/PreSwimScreen";
import { PostSwimScreen } from "../features/active-workout/screens/PostSwimScreen";
import { SessionDetailScreen } from "../features/history/screens/SessionDetailScreen";
-import { StatsScreen } from "../features/history/screens/StatsScreen";
const Stack = createNativeStackNavigator();
@@ -43,11 +42,6 @@ export function RootNavigator() {
component={SessionDetailScreen}
options={{ title: "Session" }}
/>
-
);
}
diff --git a/src/navigation/WorkoutNavigator.tsx b/src/navigation/WorkoutNavigator.tsx
index fef766d..56aa1e2 100644
--- a/src/navigation/WorkoutNavigator.tsx
+++ b/src/navigation/WorkoutNavigator.tsx
@@ -2,7 +2,6 @@ import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { WorkoutStackParamList } from "./types";
import { WorkoutListScreen } from "../features/workout-builder/screens/WorkoutListScreen";
import { BuilderScreen } from "../features/workout-builder/screens/BuilderScreen";
-import { TemplatesScreen } from "../features/templates/screens/TemplatesScreen";
import { PreSwimScreen } from "../features/active-workout/screens/PreSwimScreen";
const Stack = createNativeStackNavigator();
@@ -26,11 +25,6 @@ export function WorkoutNavigator() {
component={BuilderScreen}
options={{ title: "Build Workout" }}
/>
-
=