feat(watch): add lap detection via accelerometer and workout sync to phone
This commit is contained in:
@@ -135,6 +135,51 @@ export async function getWorkoutLaps(workoutId: string) {
|
||||
return db.select().from(laps).where(eq(laps.workoutId, workoutId));
|
||||
}
|
||||
|
||||
export async function saveWatchCompletedWorkout(data: {
|
||||
workoutId: string;
|
||||
duration: number;
|
||||
totalDistance: number;
|
||||
calories: number;
|
||||
laps: Array<{
|
||||
setPosition: number;
|
||||
repNumber: number;
|
||||
distance: number;
|
||||
duration: number;
|
||||
heartRate: number;
|
||||
}>;
|
||||
}) {
|
||||
const avgHeartRate =
|
||||
data.laps.length > 0
|
||||
? Math.round(data.laps.reduce((sum, l) => sum + (l.heartRate || 0), 0) / data.laps.length)
|
||||
: 0;
|
||||
|
||||
await db
|
||||
.update(workouts)
|
||||
.set({
|
||||
finishedAt: new Date().toISOString(),
|
||||
totalDuration: data.duration,
|
||||
totalDistance: data.totalDistance || 0,
|
||||
calories: data.calories || 0,
|
||||
avgHeartRate,
|
||||
source: "watch",
|
||||
})
|
||||
.where(eq(workouts.id, data.workoutId));
|
||||
|
||||
for (const lap of data.laps) {
|
||||
await db.insert(laps).values({
|
||||
id: generateUUID(),
|
||||
workoutId: data.workoutId,
|
||||
setPosition: lap.setPosition,
|
||||
repNumber: lap.repNumber,
|
||||
distance: lap.distance,
|
||||
duration: lap.duration,
|
||||
heartRate: lap.heartRate || null,
|
||||
strokeCount: null,
|
||||
swolf: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Stats ───────────────────────────────────────────────────
|
||||
|
||||
export async function getWeeklyStats() {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import WatchConnectivity from "../../../modules/watch-connectivity";
|
||||
import { saveWatchCompletedWorkout } from "@db/operations";
|
||||
|
||||
export function useWatchWorkoutSync() {
|
||||
const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
pollingRef.current = setInterval(async () => {
|
||||
try {
|
||||
const events = (await WatchConnectivity.pollEvents()) as any[];
|
||||
for (const event of events) {
|
||||
if (event.type === "workoutCompleted") {
|
||||
console.log("[useWatchWorkoutSync] Received workoutCompleted:", event.workoutId);
|
||||
await saveWatchCompletedWorkout({
|
||||
workoutId: event.workoutId,
|
||||
duration: event.duration ?? 0,
|
||||
totalDistance: event.totalDistance ?? 0,
|
||||
calories: event.calories ?? 0,
|
||||
laps: event.laps ?? [],
|
||||
});
|
||||
console.log("[useWatchWorkoutSync] Saved completed workout to DB");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently ignore polling errors
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
return () => {
|
||||
if (pollingRef.current) {
|
||||
clearInterval(pollingRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
Reference in New Issue
Block a user