diff --git a/Mail/Utils/NavigationStore.swift b/Mail/Utils/NavigationStore.swift index 9c34f259e..8c2339ce9 100644 --- a/Mail/Utils/NavigationStore.swift +++ b/Mail/Utils/NavigationStore.swift @@ -17,10 +17,15 @@ */ import Foundation -import SwiftUI import MailCore +import SwiftUI -class NavigationStore: ObservableObject { +/// Something that represents the state of navigation +final class NavigationStore: ObservableObject { @Published var messageReply: MessageReply? + + /// Represents the state of navigation + /// + /// The selected thread is the last in collection, by convention. @Published var threadPath = [Thread]() } diff --git a/Mail/Views/Thread List/ThreadListCell.swift b/Mail/Views/Thread List/ThreadListCell.swift index 46d35de56..41351c0d4 100644 --- a/Mail/Views/Thread List/ThreadListCell.swift +++ b/Mail/Views/Thread List/ThreadListCell.swift @@ -25,6 +25,7 @@ import SwiftUI struct ThreadListCell: View { @EnvironmentObject var splitViewManager: SplitViewManager + @EnvironmentObject var navigationStore: NavigationStore let viewModel: ThreadListViewModel @ObservedObject var multipleSelectionViewModel: ThreadListMultipleSelectionViewModel @@ -77,7 +78,10 @@ struct ThreadListCell: View { if splitViewManager.splitViewController?.splitBehavior == .overlay { splitViewManager.splitViewController?.hide(.supplementary) } + + // Update both viewModel and navigationStore on the truth. viewModel.selectedThread = thread + navigationStore.threadPath = [thread] } } } diff --git a/Mail/Views/Thread List/ThreadListView.swift b/Mail/Views/Thread List/ThreadListView.swift index 04a6775f1..6b482d460 100644 --- a/Mail/Views/Thread List/ThreadListView.swift +++ b/Mail/Views/Thread List/ThreadListView.swift @@ -24,7 +24,7 @@ import MailResources import RealmSwift import SwiftUI -class FlushAlertState: Identifiable { +final class FlushAlertState: Identifiable { let id = UUID() let deletedMessages: Int? let completion: () async -> Void diff --git a/Mail/Views/Thread List/ThreadListViewModel.swift b/Mail/Views/Thread List/ThreadListViewModel.swift index 419e126b7..0cafd4344 100644 --- a/Mail/Views/Thread List/ThreadListViewModel.swift +++ b/Mail/Views/Thread List/ThreadListViewModel.swift @@ -101,7 +101,7 @@ final class DateSection: Identifiable, Equatable { } } -@MainActor class ThreadListViewModel: ObservableObject { +@MainActor final class ThreadListViewModel: ObservableObject { let mailboxManager: MailboxManager @Published var folder: Folder @@ -224,10 +224,23 @@ final class DateSection: Identifiable, Equatable { } func nextThreadIfNeeded(from threads: [Thread]) { - guard !isCompact, - !threads.isEmpty, - !threads.contains(where: { $0.uid == selectedThread?.uid }), - let lastIndex = selectedThreadIndex else { return } + // iPad only + guard !isCompact else { + return + } + + // No more threads ? + guard !threads.isEmpty else { + selectedThread = nil + return + } + + // Matching thread to selection state + guard !threads.contains(where: { $0.uid == selectedThread?.uid }), + let lastIndex = selectedThreadIndex else { + return + } + let validIndex = min(lastIndex, threads.count - 1) selectedThread = threads[validIndex] } diff --git a/Mail/Views/Thread/ThreadView.swift b/Mail/Views/Thread/ThreadView.swift index b4a490046..806230e07 100644 --- a/Mail/Views/Thread/ThreadView.swift +++ b/Mail/Views/Thread/ThreadView.swift @@ -129,13 +129,12 @@ struct ThreadView: View { .frame(maxWidth: .infinity) } .onChange(of: thread.messages) { newMessagesList in - if newMessagesList.isEmpty || thread.messageInFolderCount == 0 { - if isCompactWindow { - dismiss() // For iPhone - } else { - navigationStore.threadPath = [] // For iPad - } + guard isCompactWindow, newMessagesList.isEmpty || thread.messageInFolderCount == 0 else { + return } + + // Dismiss on iPhone only + dismiss() } .matomoView(view: [MatomoUtils.View.threadView.displayName, "Main"]) }