feat: Apple Watch app + Paywall + Privacy Policy + rebranding
## Major Features - Apple Watch companion app (6 phases complete) - WatchConnectivity iPhone ↔ Watch - HealthKit integration (HR, calories) - SwiftUI premium UI - 9 complication types - Always-On Display support - Paywall screen with RevenueCat integration - Privacy Policy screen - App rebranding: tabatago → TabataFit - Bundle ID: com.millianlmx.tabatafit ## Changes - New: ios/TabataFit Watch App/ (complete Watch app) - New: app/paywall.tsx (subscription UI) - New: app/privacy.tsx (privacy policy) - New: src/features/watch/ (Watch sync hooks) - New: admin-web/ (admin dashboard) - Updated: app.json, package.json (branding) - Updated: profile.tsx (paywall + privacy links) - Updated: i18n translations (EN/FR/DE/ES) - New: app icon 1024x1024 ## Watch App Files - TabataFitWatchApp.swift (entry point) - ContentView.swift (premium UI) - HealthKitManager.swift (HR + calories) - WatchSessionManager.swift (communication) - Complications/ (WidgetKit) - UserDefaults+Shared.swift (data sharing)
This commit is contained in:
@@ -29,9 +29,11 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useTimer } from '@/src/shared/hooks/useTimer'
|
||||
import { useHaptics } from '@/src/shared/hooks/useHaptics'
|
||||
import { useAudio } from '@/src/shared/hooks/useAudio'
|
||||
import { useMusicPlayer } from '@/src/shared/hooks/useMusicPlayer'
|
||||
import { useActivityStore } from '@/src/shared/stores'
|
||||
import { getWorkoutById } from '@/src/shared/data'
|
||||
import { useTranslatedWorkout } from '@/src/shared/data/useTranslatedData'
|
||||
import { useWatchSync } from '@/src/features/watch'
|
||||
|
||||
import { track } from '@/src/shared/services/analytics'
|
||||
import { BRAND, PHASE_COLORS, GRADIENTS, darkColors } from '@/src/shared/theme'
|
||||
@@ -280,8 +282,37 @@ export default function PlayerScreen() {
|
||||
const timer = useTimer(rawWorkout ?? null)
|
||||
const audio = useAudio()
|
||||
|
||||
// Music player - synced with workout timer
|
||||
useMusicPlayer({
|
||||
vibe: workout?.musicVibe ?? 'electronic',
|
||||
isPlaying: timer.isRunning && !timer.isPaused,
|
||||
})
|
||||
|
||||
const [showControls, setShowControls] = useState(true)
|
||||
|
||||
// Watch sync integration
|
||||
const { isAvailable: isWatchAvailable, sendWorkoutState } = useWatchSync({
|
||||
onPlay: () => {
|
||||
timer.resume()
|
||||
track('watch_control_play', { workout_id: workout?.id ?? id })
|
||||
},
|
||||
onPause: () => {
|
||||
timer.pause()
|
||||
track('watch_control_pause', { workout_id: workout?.id ?? id })
|
||||
},
|
||||
onSkip: () => {
|
||||
timer.skip()
|
||||
haptics.selection()
|
||||
track('watch_control_skip', { workout_id: workout?.id ?? id })
|
||||
},
|
||||
onStop: () => {
|
||||
haptics.phaseChange()
|
||||
timer.stop()
|
||||
router.back()
|
||||
track('watch_control_stop', { workout_id: workout?.id ?? id })
|
||||
},
|
||||
})
|
||||
|
||||
// Animation refs
|
||||
const timerScaleAnim = useRef(new Animated.Value(0.8)).current
|
||||
const phaseColor = PHASE_COLORS[timer.phase].fill
|
||||
@@ -398,6 +429,34 @@ export default function PlayerScreen() {
|
||||
}
|
||||
}, [timer.timeRemaining])
|
||||
|
||||
// Sync workout state with Apple Watch
|
||||
useEffect(() => {
|
||||
if (!isWatchAvailable || !timer.isRunning) return;
|
||||
|
||||
sendWorkoutState({
|
||||
phase: timer.phase,
|
||||
timeRemaining: timer.timeRemaining,
|
||||
currentRound: timer.currentRound,
|
||||
totalRounds: timer.totalRounds,
|
||||
currentExercise: timer.currentExercise,
|
||||
nextExercise: timer.nextExercise,
|
||||
calories: timer.calories,
|
||||
isPaused: timer.isPaused,
|
||||
isPlaying: timer.isRunning && !timer.isPaused,
|
||||
});
|
||||
}, [
|
||||
timer.phase,
|
||||
timer.timeRemaining,
|
||||
timer.currentRound,
|
||||
timer.totalRounds,
|
||||
timer.currentExercise,
|
||||
timer.nextExercise,
|
||||
timer.calories,
|
||||
timer.isPaused,
|
||||
timer.isRunning,
|
||||
isWatchAvailable,
|
||||
]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<StatusBar hidden />
|
||||
|
||||
Reference in New Issue
Block a user