feat: WatermelonDB migration, minimize animation & performance fixes#4
Closed
Yannam-Builds wants to merge 51 commits into
Closed
feat: WatermelonDB migration, minimize animation & performance fixes#4Yannam-Builds wants to merge 51 commits into
Yannam-Builds wants to merge 51 commits into
Conversation
…s/Pre-Built Programs.jpg
Full rewrite of the data layer from AsyncStorage + AppContext to WatermelonDB (offline-first SQLite ORM). Includes all Claude session changes that Codex missed during its port. Core WatermelonDB migration: - New db/ layer: schema (v2), models, repositories for all data types - New hooks: useWatermelonAppData, useWatermelonHistory, useWatermelonPlans, useWatermelonStats, useWatermelonSettings, useWatermelonGymProfiles, useWatermelonBodyMeasurements, useWatermelonManualRecovery, useWatermelonHome - WatermelonDB-backed workout repository, import/export repository, settings repository with typed key-value store - Native Android WorkManager backup scheduler (IronlogBackupSchedulerModule, IronlogBackupWorker, IronlogBackupTaskService) - Headless JS backup task registered in index.js Feature improvements (Claude sessions, missed by Codex): - Celebration animation: removed burst/fountain variants, kept only fireworks (85 particles) and wave (55 particles) to fix device lag - Google Drive OAuth removed; replaced with native share sheet via react-native-share (no credentials needed) - Daily scheduled backup via WorkManager PeriodicWorkRequest (24h repeat) with selectable hour:minute time picker in BackupCenterScreen - Battery optimization exemption prompt when scheduling auto-backups - Rolling local snapshot retention control (2-20 files, default 4) - DataPortabilityScreen: simple plain-JSON export/import (no encryption), accessible from Settings > Backup & Export - BackupCenterScreen renamed to "Advanced Backup Centre" in Settings - Legacy backup format support removed; only ironlog_watermelon_export v1 - Active session prefix mismatch fixed (store uses full key as-is) - Scheduled backup config keys: scheduledBackupEnabled/Hour/Minute - AndroidManifest: added FOREGROUND_SERVICE and REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permissions; removed OAuth deep-link - CLAUDE.md added for AI-assisted development New screens: DataPortabilityScreen, SplashScreen, AIPlanScreen, TrainingIntelligenceScreen, OnboardingScreen New navigation: PagerView-based tab system replacing bottom tab navigator New components: FluidTabBar, AppScreenHeader, GoldConfettiBurst, PRHud, GlassModeContext, ui/Skeleton, ui/InlineToast Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
app.json and eas.json are Expo-managed files no longer used after the migration to bare React Native + Gradle. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Performance: - StatsScreen: wrap streak/totalSets/avgDur, chartData/chartMax/pointSpacing, pbEntries, and STATS in useMemo — were recomputed on every render - HomeScreen: wrap weeklyGoalDays, goalStreak, weekSessions/hitDays/avgDur, and activePlanDays in useMemo — were recomputed on every render - GoldConfettiBurst: remove SharedValue from useEffect dep array — it's a stable Reanimated ref and caused spurious effect reruns - HistoryScreen: use stable exercise keys (name+index) in renderHistoryCard Bug fixes: - AppNavigator: add AppErrorBoundary class component so crashes show a recoverable error screen instead of a blank white screen - database.js: register SmokeTest model (table was in schema but unregistered) - IronlogBackupSchedulerPackage: suppress @deprecated override warning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- favoriteExercisesService: replaced AsyncStorage with WatermelonDB settingsRepository - ExerciseLibraryService: removed all AsyncStorage calls; index now sourced from WatermelonDB wmGetExercisesSnapshot with in-memory _bundledIndexCache fallback; retryLibraryFetch invalidates cache instead of clearing AS keys - backupService: replaced readJsonStorage/writeJsonStorage with settingsRepository; migrated getOrCreateDeviceId and clearQueuedBackup to settingsRepository; getManagedStorageMap now builds from exportDatabase() (WM tables) instead of AsyncStorage.getAllKeys/multiGet; removed legacy AsyncStorage.multiRemove/multiSet from restore path (WM importDatabase sync block already handles restore) Dead code files (storage.js, nativeMigrationBridge.js, trainingRepository.js, migrations.js) retain AsyncStorage for their one-time upgrade migration logic but are no longer imported by any active component. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deleted five files with no active importers: - src/storage/storage.js (original AsyncStorage layer, superseded by WM) - src/services/migrations.js (legacy AS schema migration helpers) - src/services/nativeMigrationBridge.js (one-time AS→SQLite bridge, never imported) - src/domain/storage/trainingRepository.js (AS training repo, superseded by WM) - src/domain/storage/trainingDatabase.js (companion to trainingRepository) All five were unreferenced. Removing them before Play Store release eliminates dead AsyncStorage dependencies and shrinks the bundle. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents all bugs Codex introduced/missed, every file changed, and the key invariants that must be preserved going forward. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
replaceTrainingSnapshotCompat was calling importDatabase() which only accepts the native WM export format. importAnyPayload() handles legacy format detection too, so restoring older encrypted backups works correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Race condition: `initialized` (WM collection hooks) was becoming true before the separate useEffect that reads `onboarding_complete` from settingsRepository completed. The navigator mounted with `onboardingComplete = false` and `initialRouteName='Onboarding'` was already locked in. Added `settingsLoaded` flag — set to true only after the settings useEffect resolves — and gated it into the `initialized` check so the navigator never renders until both WM data AND the onboarding flag are ready. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the slider only animated via withSpring after onPageSelected fired (page fully settled), so it never moved during a swipe — then jumped to catch up. Felt completely disconnected from the gesture. Fix: - AppNavigator creates a Reanimated scrollX shared value - PagerView.onPageScroll writes position+offset to scrollX on every frame of the swipe (0.0 → 1.0 → 2.0 etc.) - scrollX is threaded through FluidTabBar → PremiumFrostedTabBar - tabWidthSV shared value keeps tabWidth reactive inside worklets - useAnimatedStyle reads scrollX.value * tabWidthSV.value directly, so the slider tracks the finger 1:1 during drag - withSpring fallback kept for initial render / when scrollX absent - Tap navigation still works: setPage() triggers onPageScroll which updates scrollX naturally Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ActiveWorkoutScreen crash: - Outer ActiveWorkoutScreen referenced `s.container` (makeStyles result) in the !day guard, but `s` is only defined inside the inner WorkoutContent component. In Hermes release builds this becomes ReferenceError 's' doesn't exist. Fixed using plain inline styles for the guard fallback view. Onboarding popup every launch: - onboarding_complete was previously stored in AsyncStorage; after the WM migration the key was never written to WM, so every launch read null -> false -> popup triggered. - HomeScreen now only shows the popup for truly new users (no plans AND no history). Existing users with plans are never shown the popup again. - useWatermelonAppData adds a one-time migration effect: when settingsLoaded becomes true and the user has plans but the flag is unset, it writes onboarding_complete=true to WM so all future cold starts resolve correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Full WatermelonDB migration: all screens use repository layer, no direct DB access from screens/hooks - Active workout minimize animation: slide-down/slide-up via Reanimated withSpring (replaced shrink-to-pill approach) - Fix black flash on maximize: return null (transparent) instead of colored View when WatermelonDB not yet initialized - Fix home screen flash: useDeferredScreenReady stayReady option prevents intelligence card re-triggering on focus changes - Notification bridge: workoutNotificationBridge.js for workout reminder scheduling - Navigation ref: navigationRef.js for imperative navigation outside component tree - Misc: Button component, CustomAlert, PremiumFrostedTabBar, colorUtils updates Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
databaseimports in UI codewithSpringActiveWorkoutScreenreturned a full-screen coloredViewwhile WatermelonDB was hydrating; changed toreturn nullso the underlying home screen shows through insteaduseDeferredScreenReadygained astayReadyoption (backed by aneverReadyref) so the Training Intelligence card never resets once it has loaded, even when the active workout screen overlays and then releases focusKey files changed
src/screens/ActiveWorkoutScreen.jsreturn nullon init, root export fixsrc/hooks/useDeferredScreenReady.jsstayReady+everReadyrefsrc/screens/HomeScreen.jsstayReady: truefor intelligence cardsrc/context/ActiveWorkoutBannerContext.jssrc/components/FloatingWorkoutWidget.jssrc/navigation/AppNavigator.jstransparentModal+animation: none+contentStyle: transparentsrc/navigation/navigationRef.jssrc/services/workoutNotificationBridge.jsKnown remaining issue
A brief black flash still appears before the slide-up animation when tapping the pill to re-open the workout. The JS-layer fix (
return null) is in place but the flash appears to originate at the native layer — Android creates the Fragment/surface before JS renders. Further investigation needed (may require keeping the screen mounted and usingpointerEventstoggling instead of navigate away/back).Test plan