diff --git a/Andromida/App/Views/RootView.swift b/Andromida/App/Views/RootView.swift index 92def80..56aadb3 100644 --- a/Andromida/App/Views/RootView.swift +++ b/Andromida/App/Views/RootView.swift @@ -9,6 +9,7 @@ struct RootView: View { @State private var selectedTab: RootTab @State private var analyticsPrewarmTask: Task? @State private var isForegroundRefreshing = false + @State private var isResumingFromBackground = true private let foregroundRefreshMinimumSeconds: TimeInterval = 0.15 private let debugForegroundRefreshMinimumSeconds: TimeInterval = 0.8 private let debugForegroundRefreshKey = "debugForegroundRefreshNextForeground" @@ -38,6 +39,10 @@ struct RootView: View { self.settingsStore = settingsStore self.categoryStore = categoryStore self._selectedTab = State(initialValue: initialTab) + + // Update time-of-day immediately before any views render. + // This ensures correct rituals are shown when app resumes from background. + store.updateCurrentTimeOfDay() } var body: some View { @@ -46,6 +51,7 @@ struct RootView: View { Tab(String(localized: "Today"), systemImage: "sun.max.fill", value: RootTab.today) { NavigationStack { TodayView(store: store, categoryStore: categoryStore) + .id(store.currentTimeOfDay) } } @@ -83,10 +89,17 @@ struct RootView: View { } .transition(.opacity) } + + // Brief overlay when resuming to hide stale snapshot + if isResumingFromBackground { + AppSurface.primary + .ignoresSafeArea() + } } .tint(AppAccent.primary) .background(AppSurface.primary.ignoresSafeArea()) .animation(.easeInOut(duration: 0.12), value: isForegroundRefreshing) + .animation(.easeIn(duration: 0.05), value: isResumingFromBackground) .onChange(of: scenePhase) { _, newPhase in if newPhase == .active { store.reminderScheduler.clearBadge() @@ -95,6 +108,12 @@ struct RootView: View { // This ensures the correct rituals are shown without a visible transition. store.updateCurrentTimeOfDay() + // Hide resume overlay after a tiny delay to allow view to update + Task { @MainActor in + try? await Task.sleep(for: .milliseconds(50)) + isResumingFromBackground = false + } + let useDebugOverlay = UserDefaults.standard.bool(forKey: debugForegroundRefreshKey) if useDebugOverlay { UserDefaults.standard.set(false, forKey: debugForegroundRefreshKey) @@ -106,6 +125,9 @@ struct RootView: View { showOverlay: useDebugOverlay, minimumSeconds: useDebugOverlay ? debugForegroundRefreshMinimumSeconds : foregroundRefreshMinimumSeconds ) + } else if newPhase == .background { + // Prepare for next resume + isResumingFromBackground = true } } .onChange(of: selectedTab) { _, _ in