- Add HealthKit State of Mind sync for mood entries - Add Live Activity with streak display and rating time window - Add App Shortcuts/Siri integration for voice mood logging - Add TipKit hints for feature discovery - Add centralized MoodLogger for consistent side effects - Add reminder time setting in Settings with time picker - Fix duplicate notifications when changing reminder time - Fix Live Activity streak showing 0 when not yet rated today - Fix slow tap response in entry detail mood selection - Update widget timeline to refresh at rating time - Sync widgets when reminder time changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
94 lines
3.5 KiB
Swift
94 lines
3.5 KiB
Swift
//
|
|
// FeelsApp.swift
|
|
// Shared
|
|
//
|
|
// Created by Trey Tartt on 1/5/22.
|
|
//
|
|
|
|
import SwiftUI
|
|
import SwiftData
|
|
import BackgroundTasks
|
|
import WidgetKit
|
|
import TipKit
|
|
|
|
@main
|
|
struct FeelsApp: App {
|
|
@Environment(\.scenePhase) private var scenePhase
|
|
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
|
|
|
let dataController = DataController.shared
|
|
@StateObject var iapManager = IAPManager()
|
|
@StateObject var authManager = BiometricAuthManager()
|
|
@StateObject var healthKitManager = HealthKitManager.shared
|
|
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
|
|
@State private var showSubscriptionFromWidget = false
|
|
|
|
init() {
|
|
BGTaskScheduler.shared.cancelAllTaskRequests()
|
|
BGTaskScheduler.shared.register(forTaskWithIdentifier: BGTask.updateDBMissingID, using: nil) { (task) in
|
|
BGTask.runFillInMissingDatesTask(task: task as! BGProcessingTask)
|
|
}
|
|
UNUserNotificationCenter.current().setBadgeCount(0)
|
|
|
|
// Configure TipKit
|
|
TipsManager.shared.configure()
|
|
|
|
// Initialize Live Activity scheduler
|
|
LiveActivityScheduler.shared.scheduleBasedOnCurrentTime()
|
|
}
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
ZStack {
|
|
MainTabView(dayView: DayView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: false)),
|
|
monthView: MonthView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: true)),
|
|
yearView: YearView(viewModel: YearViewModel()),
|
|
insightsView: InsightsView())
|
|
.modelContainer(dataController.container)
|
|
.environmentObject(iapManager)
|
|
.environmentObject(authManager)
|
|
.environmentObject(healthKitManager)
|
|
.sheet(isPresented: $showSubscriptionFromWidget) {
|
|
FeelsSubscriptionStoreView()
|
|
.environmentObject(iapManager)
|
|
}
|
|
.onOpenURL { url in
|
|
if url.scheme == "feels" && url.host == "subscribe" {
|
|
showSubscriptionFromWidget = true
|
|
}
|
|
}
|
|
|
|
// Lock screen overlay
|
|
if authManager.isLockEnabled && !authManager.isUnlocked {
|
|
LockScreenView(authManager: authManager)
|
|
.transition(.opacity)
|
|
}
|
|
}
|
|
}.onChange(of: scenePhase) { _, newPhase in
|
|
if newPhase == .background {
|
|
//BGTask.scheduleBackgroundProcessing()
|
|
WidgetCenter.shared.reloadAllTimelines()
|
|
// Lock the app when going to background
|
|
authManager.lock()
|
|
}
|
|
|
|
if newPhase == .active {
|
|
UNUserNotificationCenter.current().setBadgeCount(0)
|
|
// Check subscription status on each app launch
|
|
Task {
|
|
await iapManager.checkSubscriptionStatus()
|
|
}
|
|
// Authenticate if locked
|
|
if authManager.isLockEnabled && !authManager.isUnlocked {
|
|
Task {
|
|
await authManager.authenticate()
|
|
}
|
|
}
|
|
// Reschedule Live Activity when app becomes active
|
|
LiveActivityScheduler.shared.scheduleBasedOnCurrentTime()
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|