From ca9259d5bda74fb09fd4b078c3952716c9c9af6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zo=C3=AB?= Date: Wed, 12 Aug 2026 13:52:23 +0200 Subject: [PATCH] feat(watch): add workout deletion sync and optimize delete operations --- modules/watch-connectivity/index.swift | 25 ++++ package.json | 121 +++++++++--------- src/db/operations.ts | 8 +- .../history/screens/HistoryScreen.tsx | 2 +- .../screens/WorkoutListScreen.tsx | 2 +- src/modules/watch-connectivity/index.ts | 1 + 6 files changed, 95 insertions(+), 64 deletions(-) diff --git a/modules/watch-connectivity/index.swift b/modules/watch-connectivity/index.swift index e7e391a..c7bd190 100644 --- a/modules/watch-connectivity/index.swift +++ b/modules/watch-connectivity/index.swift @@ -166,6 +166,31 @@ public class WatchConnectivityModule: Module { } } + AsyncFunction("sendWorkoutDeleted") { (id: String, promise: Promise) in + guard let session = self.session else { + promise.reject("NO_SESSION", "WCSession not available") + return + } + + func doSend() { + let message: [String: Any] = ["type": "workoutDeleted", "id": id] + + guard let cleaned = self.cleanForWCSession(message) as? [String: Any] else { + promise.resolve(["status": "sent"]) + return + } + + session.transferUserInfo(cleaned) + promise.resolve(["status": "sent"]) + } + + if self.isActivated { + doSend() + } else { + self.pendingPromises.append(doSend) + } + } + AsyncFunction("getApplicationContext") { () -> [String: Any]? in return self.session?.receivedApplicationContext as? [String: Any] } diff --git a/package.json b/package.json index 1247bd6..15776a8 100644 --- a/package.json +++ b/package.json @@ -1,62 +1,63 @@ { - "name": "swimbuddy", - "version": "1.0.0", - "main": "index.ts", - "dependencies": { - "@bacons/apple-targets": "^5.0.0", - "@expo/vector-icons": "^15.0.2", - "@react-navigation/bottom-tabs": "^7.18.14", - "@react-navigation/native": "^7.3.14", - "@react-navigation/native-stack": "^7.18.6", - "@sentry/react-native": "^8.22.0", - "@tailwindcss/postcss": "^4.3.3", - "drizzle-orm": "^0.45.2", - "expo": "~57.0.10", - "expo-application": "~57.0.2", - "expo-localization": "~57.0.1", - "expo-splash-screen": "~57.0.6", - "expo-sqlite": "~57.0.1", - "expo-status-bar": "~57.0.1", - "expo-system-ui": "~57.0.2", - "expo-updates": "^57.0.12", - "i18next": "^26.3.6", - "nativewind": "^5.0.0-preview.4", - "react": "19.2.3", - "react-i18next": "^17.0.11", - "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", - "react-native-screens": "~4.26.0" - }, - "devDependencies": { - "@types/react": "~19.2.2", - "babel-plugin-inline-import": "^3.0.0", - "drizzle-kit": "^0.31.10", - "tailwindcss": "^4", - "typescript": "~6.0.3" - }, - "scripts": { - "start": "expo start", - "android": "expo run:android", - "ios": "expo run:ios", - "web": "expo start --web", - "db:generate": "drizzle-kit generate", - "prebuild": "expo prebuild && npm run eas-build-post-install", - "prebuild:clean": "expo prebuild --clean && npm run eas-build-post-install", - "eas-build-post-install": "node scripts/add-sentry-watch.js", - "build:wearos:debug": "./scripts/build-wearos.sh debug", - "build:wearos:release": "./scripts/build-wearos.sh release", - "deploy:ios": "eas build --platform ios --profile ${1:-preview} && eas submit --platform ios --profile ${1:-preview}", - "deploy:android": "eas build --platform android --profile ${1:-preview} && eas submit --platform android --profile ${1:-preview}", - "deploy:wearos": "./scripts/build-wearos.sh release ${2:-}", - "update": "eas update --branch ${1:-preview}", - "update:production": "eas update --branch production" - }, - "private": true, - "overrides": { - "lightningcss": "1.30.1" - } + "name": "swimbuddy", + "version": "1.0.0", + "main": "index.ts", + "dependencies": { + "@bacons/apple-targets": "^5.0.0", + "@expo/vector-icons": "^15.0.2", + "@react-navigation/bottom-tabs": "^7.18.14", + "@react-navigation/native": "^7.3.14", + "@react-navigation/native-stack": "^7.18.6", + "@sentry/react-native": "^8.22.0", + "@tailwindcss/postcss": "^4.3.3", + "drizzle-orm": "^0.45.2", + "expo": "~57.0.10", + "expo-application": "~57.0.2", + "expo-localization": "~57.0.1", + "expo-splash-screen": "~57.0.6", + "expo-sqlite": "~57.0.1", + "expo-status-bar": "~57.0.1", + "expo-system-ui": "~57.0.2", + "expo-updates": "^57.0.12", + "i18next": "^26.3.6", + "nativewind": "^5.0.0-preview.4", + "react": "19.2.3", + "react-i18next": "^17.0.11", + "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", + "react-native-screens": "~4.26.0" + }, + "devDependencies": { + "@types/react": "~19.2.2", + "babel-plugin-inline-import": "^3.0.0", + "drizzle-kit": "^0.31.10", + "tailwindcss": "^4", + "typescript": "~6.0.3" + }, + "scripts": { + "start": "expo start", + "android": "expo run:android", + "ios": "expo run:ios", + "web": "expo start --web", + "clean": "rm -rf .expo ios android node_modules && npm i", + "db:generate": "drizzle-kit generate", + "prebuild": "expo prebuild && npm run eas-build-post-install", + "prebuild:clean": "expo prebuild --clean && npm run eas-build-post-install", + "eas-build-post-install": "node scripts/add-sentry-watch.js", + "build:wearos:debug": "./scripts/build-wearos.sh debug", + "build:wearos:release": "./scripts/build-wearos.sh release", + "deploy:ios": "eas build --platform ios --profile ${1:-preview} && eas submit --platform ios --profile ${1:-preview}", + "deploy:android": "eas build --platform android --profile ${1:-preview} && eas submit --platform android --profile ${1:-preview}", + "deploy:wearos": "./scripts/build-wearos.sh release ${2:-}", + "update": "eas update --branch ${1:-preview}", + "update:production": "eas update --branch production" + }, + "private": true, + "overrides": { + "lightningcss": "1.30.1" + } } diff --git a/src/db/operations.ts b/src/db/operations.ts index 5da1c29..a23675e 100644 --- a/src/db/operations.ts +++ b/src/db/operations.ts @@ -253,7 +253,9 @@ export async function deleteSession(sessionId: string) { export async function deleteSessionAndSync(sessionId: string) { await deleteSession(sessionId); - await WatchConnectivity.sendWorkoutDeleted(sessionId); + WatchConnectivity.sendWorkoutDeleted(sessionId).catch((err) => + console.warn("[ops] sync failed:", err) + ); } export async function deleteWorkout(id: string) { @@ -272,7 +274,9 @@ export async function deleteWorkout(id: string) { export async function deleteWorkoutAndSync(id: string) { await deleteWorkout(id); - await WatchConnectivity.sendWorkoutDeleted(id); + WatchConnectivity.sendWorkoutDeleted(id).catch((err) => + console.warn("[ops] sync failed:", err) + ); } // ─── Stats ─────────────────────────────────────────────────── diff --git a/src/features/history/screens/HistoryScreen.tsx b/src/features/history/screens/HistoryScreen.tsx index c4fd8ad..64d02bd 100644 --- a/src/features/history/screens/HistoryScreen.tsx +++ b/src/features/history/screens/HistoryScreen.tsx @@ -52,7 +52,7 @@ export function HistoryScreen() { style: "destructive", onPress: async () => { await deleteSessionAndSync(id); - await loadSessions(); + setSessions((prev) => prev.filter((s) => s.id !== id)); }, }, ]); diff --git a/src/features/workout-builder/screens/WorkoutListScreen.tsx b/src/features/workout-builder/screens/WorkoutListScreen.tsx index 9981ecf..1b6923b 100644 --- a/src/features/workout-builder/screens/WorkoutListScreen.tsx +++ b/src/features/workout-builder/screens/WorkoutListScreen.tsx @@ -64,7 +64,7 @@ export function WorkoutListScreen() { style: "destructive", onPress: async () => { await deleteWorkoutAndSync(id); - await loadWorkouts(); + setWorkouts((prev) => prev.filter((w) => w.id !== id)); }, }, ]); diff --git a/src/modules/watch-connectivity/index.ts b/src/modules/watch-connectivity/index.ts index 269cdf8..3eb6f3a 100644 --- a/src/modules/watch-connectivity/index.ts +++ b/src/modules/watch-connectivity/index.ts @@ -9,6 +9,7 @@ WatchConnectivity = requireOptionalNativeModule("WatchConnectivity") ?? { isReachable: async () => false, sendWorkoutPlan: async () => ({ error: "WatchConnectivity native module not available" }), sendWorkoutCompletion: async () => ({ error: "WatchConnectivity native module not available" }), + sendWorkoutDeleted: async () => ({ error: "WatchConnectivity native module not available" }), pollEvents: async () => [], getApplicationContext: async () => null, updateApplicationContext: async () => {},