diff --git a/Rosetta.xcodeproj/project.pbxproj b/Rosetta.xcodeproj/project.pbxproj index db40619..906df3f 100644 --- a/Rosetta.xcodeproj/project.pbxproj +++ b/Rosetta.xcodeproj/project.pbxproj @@ -288,7 +288,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.10; + MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.rosetta.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -327,7 +327,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.10; + MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.rosetta.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Rosetta/Features/Auth/AuthCoordinator.swift b/Rosetta/Features/Auth/AuthCoordinator.swift index b8477d2..5b275b3 100644 --- a/Rosetta/Features/Auth/AuthCoordinator.swift +++ b/Rosetta/Features/Auth/AuthCoordinator.swift @@ -68,7 +68,7 @@ struct AuthCoordinator: View { .ignoresSafeArea() .opacity(fadeOverlay ? 1 : 0) .allowsHitTesting(fadeOverlay) - .animation(.easeInOut(duration: 0.08), value: fadeOverlay) + .animation(.easeInOut(duration: 0.035), value: fadeOverlay) } .overlay(alignment: .leading) { if canSwipeBack { @@ -174,9 +174,9 @@ private extension AuthCoordinator { navigationDirection = .forward fadeOverlay = true Task { @MainActor in - try? await Task.sleep(nanoseconds: 80_000_000) + try? await Task.sleep(nanoseconds: 35_000_000) currentScreen = screen - try? await Task.sleep(nanoseconds: 20_000_000) + try? await Task.sleep(nanoseconds: 10_000_000) fadeOverlay = false } } diff --git a/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift b/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift index 9eda262..e1c52a4 100644 --- a/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift +++ b/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift @@ -119,19 +119,22 @@ struct ChatDetailView: View { .toolbar(.hidden, for: .tabBar) .task { isViewActive = true + // Suppress notifications & clear badge immediately (no 600ms delay). + // setDialogActive only touches MessageRepository.activeDialogs (Set), + // does NOT mutate DialogRepository, so ForEach won't rebuild. + MessageRepository.shared.setDialogActive(route.publicKey, isActive: true) + clearDeliveredNotifications(for: route.publicKey) // Reset idle timer — user is actively viewing a chat. SessionManager.shared.recordUserInteraction() // Request user info (non-mutating, won't trigger list rebuild) requestUserInfoIfNeeded() - // Delay ALL dialog mutations to let navigation transition complete. + // Delay DialogRepository mutations to let navigation transition complete. // Without this, DialogRepository update rebuilds ChatListView's ForEach // mid-navigation, recreating the NavigationLink and canceling the push. try? await Task.sleep(for: .milliseconds(600)) guard isViewActive else { return } activateDialog() markDialogAsRead() - // Clear delivered notifications from this sender - clearDeliveredNotifications(for: route.publicKey) // Subscribe to opponent's online status (Android parity) — only after settled SessionManager.shared.subscribeToOnlineStatus(publicKey: route.publicKey) // Desktop parity: force-refresh user info (incl. online status) on chat open. diff --git a/Rosetta/RosettaApp.swift b/Rosetta/RosettaApp.swift index fcb7a31..b85e223 100644 --- a/Rosetta/RosettaApp.swift +++ b/Rosetta/RosettaApp.swift @@ -164,7 +164,7 @@ struct RosettaApp: App { .ignoresSafeArea() .opacity(transitionOverlay ? 1 : 0) .allowsHitTesting(transitionOverlay) - .animation(.easeInOut(duration: 0.08), value: transitionOverlay) + .animation(.easeInOut(duration: 0.035), value: transitionOverlay) } .preferredColorScheme(.dark) .onAppear { @@ -228,9 +228,9 @@ struct RosettaApp: App { guard !transitionOverlay else { return } transitionOverlay = true Task { @MainActor in - try? await Task.sleep(nanoseconds: 80_000_000) // wait for overlay fade-in + try? await Task.sleep(nanoseconds: 35_000_000) // wait for overlay fade-in appState = newState - try? await Task.sleep(nanoseconds: 20_000_000) // brief settle + try? await Task.sleep(nanoseconds: 10_000_000) // brief settle transitionOverlay = false } }