From 2e525db79a9dc8923a1b525b7e4737563dc59330 Mon Sep 17 00:00:00 2001 From: Philippe Weidmann Date: Wed, 19 Apr 2023 14:57:49 +0200 Subject: [PATCH 1/6] feat: iOS 16 navigation backport --- .package.resolved | 13 +++++++++-- Mail/Views/SplitView.swift | 21 +++++++++++++++-- Mail/Views/Thread List/ThreadListCell.swift | 25 +++++++-------------- Project.swift | 2 ++ 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/.package.resolved b/.package.resolved index 8f2d1d3c8..3a0482a0e 100644 --- a/.package.resolved +++ b/.package.resolved @@ -134,6 +134,15 @@ "version" : "7.5.2" } }, + { + "identity" : "navigationbackport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/johnpatrickmorgan/NavigationBackport", + "state" : { + "revision" : "5096dda355148dd40162810e7f56292ce0a2b09b", + "version" : "0.7.5" + } + }, { "identity" : "nuke", "kind" : "remoteSourceControl", @@ -175,8 +184,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa", "state" : { - "revision" : "ac224c437a3070ffe34460137ac8761eddaf2852", - "version" : "8.3.3" + "revision" : "92a6472efc750a4e18bdee21c204942ab0bc4dcd", + "version" : "8.4.0" } }, { diff --git a/Mail/Views/SplitView.swift b/Mail/Views/SplitView.swift index 70fa00756..055cf351b 100644 --- a/Mail/Views/SplitView.swift +++ b/Mail/Views/SplitView.swift @@ -21,9 +21,21 @@ import InfomaniakCore import Introspect import MailCore import MailResources +import NavigationBackport import RealmSwift import SwiftUI +struct MailNavigationPathKey: EnvironmentKey { + static var defaultValue: Binding? +} + +extension EnvironmentValues { + var mailNavigationPath: Binding? { + get { self[MailNavigationPathKey.self] } + set { self[MailNavigationPathKey.self] = newValue } + } +} + class GlobalBottomSheet: DisplayedFloatingPanelState { enum State { case getMoreStorage @@ -62,6 +74,7 @@ struct SplitView: View { @StateObject private var alert = GlobalAlert() @StateObject private var splitViewManager: SplitViewManager + @State var path = NBNavigationPath() var isCompact: Bool { sizeClass == .compact || verticalSizeClass == .compact @@ -77,9 +90,13 @@ struct SplitView: View { Group { if isCompact { ZStack { - NavigationView { + NBNavigationStack(path: $path) { ThreadListManagerView(isCompact: isCompact) - .accessibilityHidden(navigationDrawerController.isOpen) + .accessibilityHidden(navigationDrawerController.isOpen) + .nbNavigationDestination(for: Thread.self) { thread in + ThreadView(thread: thread) + } + .environment(\.mailNavigationPath, $path) } .navigationViewStyle(.stack) diff --git a/Mail/Views/Thread List/ThreadListCell.swift b/Mail/Views/Thread List/ThreadListCell.swift index a95996504..59e2165f8 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 + @Environment(\.mailNavigationPath) var path let thread: Thread @@ -53,22 +54,12 @@ struct ThreadListCell: View { } var body: some View { - ZStack { - if !thread.shouldPresentAsDraft { - NavigationLink(destination: ThreadView(thread: thread, - onDismiss: { viewModel.selectedThread = nil }), - isActive: $shouldNavigateToThreadList) { EmptyView() } - .opacity(0) - .disabled(multipleSelectionViewModel.isEnabled) - } - - ThreadCell( - thread: thread, - density: threadDensity, - isMultipleSelectionEnabled: multipleSelectionViewModel.isEnabled, - isSelected: isSelected - ) - } + ThreadCell( + thread: thread, + density: threadDensity, + isMultipleSelectionEnabled: multipleSelectionViewModel.isEnabled, + isSelected: isSelected + ) .background(SelectionBackground(selectionType: selectionType, paddingLeading: 4)) .onTapGesture { didTapCell() } .onLongPressGesture(minimumDuration: 0.3) { didLongPressCell() } @@ -101,7 +92,7 @@ struct ThreadListCell: View { splitViewManager.splitViewController?.hide(.supplementary) } viewModel.selectedThread = thread - shouldNavigateToThreadList = true + path?.wrappedValue.append(thread) } } } diff --git a/Project.swift b/Project.swift index ddb50d720..43ec46ebf 100644 --- a/Project.swift +++ b/Project.swift @@ -48,6 +48,7 @@ let project = Project(name: "Mail", .package(url: "https://github.com/kean/Nuke", .upToNextMajor(from: "12.0.0")), .package(url: "https://github.com/airbnb/lottie-ios", .exact("3.5.0")), .package(url: "https://github.com/scinfu/SwiftSoup", .upToNextMajor(from: "2.5.3")), + .package(url: "https://github.com/johnpatrickmorgan/NavigationBackport", .upToNextMajor(from: "0.7.2")), .package(url: "https://github.com/aheze/Popovers", .upToNextMajor(from: "1.3.2")) ], targets: [ @@ -79,6 +80,7 @@ let project = Project(name: "Mail", .package(product: "WrappingHStack"), .package(product: "FloatingPanel"), .package(product: "Lottie"), + .package(product: "NavigationBackport"), .package(product: "Popovers") ], settings: .settings(base: baseSettings), From 576dbc3e6a692671687230ef1eaf56958f4a79f5 Mon Sep 17 00:00:00 2001 From: Ambroise Decouttere Date: Tue, 25 Apr 2023 10:12:31 +0200 Subject: [PATCH 2/6] fix(Navigation): iPad navigation done --- Mail/Views/SplitView.swift | 17 ++++++++---- Mail/Views/Thread List/ThreadListCell.swift | 22 +++++----------- Mail/Views/Thread List/ThreadListView.swift | 11 +++++++- .../Thread List/ThreadListViewModel.swift | 17 +++++++----- Mail/Views/Thread/ThreadView.swift | 26 +++++++++---------- 5 files changed, 51 insertions(+), 42 deletions(-) diff --git a/Mail/Views/SplitView.swift b/Mail/Views/SplitView.swift index 055cf351b..0b8335594 100644 --- a/Mail/Views/SplitView.swift +++ b/Mail/Views/SplitView.swift @@ -26,11 +26,11 @@ import RealmSwift import SwiftUI struct MailNavigationPathKey: EnvironmentKey { - static var defaultValue: Binding? + static var defaultValue: Binding<[Thread]>? } extension EnvironmentValues { - var mailNavigationPath: Binding? { + var mailNavigationPath: Binding<[Thread]>? { get { self[MailNavigationPathKey.self] } set { self[MailNavigationPathKey.self] = newValue } } @@ -74,7 +74,7 @@ struct SplitView: View { @StateObject private var alert = GlobalAlert() @StateObject private var splitViewManager: SplitViewManager - @State var path = NBNavigationPath() + @State var path = [Thread]() var isCompact: Bool { sizeClass == .compact || verticalSizeClass == .compact @@ -96,7 +96,6 @@ struct SplitView: View { .nbNavigationDestination(for: Thread.self) { thread in ThreadView(thread: thread) } - .environment(\.mailNavigationPath, $path) } .navigationViewStyle(.stack) @@ -112,7 +111,11 @@ struct SplitView: View { ThreadListManagerView(isCompact: isCompact) - EmptyStateView.emptyThread(from: splitViewManager.selectedFolder) + if let thread = path.last { + ThreadView(mailboxManager: mailboxManager, thread: thread) + } else { + EmptyStateView.emptyThread(from: splitViewManager.selectedFolder) + } } } } @@ -121,6 +124,10 @@ struct SplitView: View { try await mailboxManager.folders() } } + .environment(\.mailNavigationPath, $path) + .environmentObject(splitViewManager) + .environmentObject(navigationDrawerController) + .defaultAppStorage(.shared) .onAppear { AppDelegate.orientationLock = .all } diff --git a/Mail/Views/Thread List/ThreadListCell.swift b/Mail/Views/Thread List/ThreadListCell.swift index 59e2165f8..bed120f4d 100644 --- a/Mail/Views/Thread List/ThreadListCell.swift +++ b/Mail/Views/Thread List/ThreadListCell.swift @@ -35,22 +35,17 @@ struct ThreadListCell: View { let threadDensity: ThreadDensity let isSelected: Bool + let isMultiSelected: Bool @Binding var editedMessageDraft: Draft? @State private var shouldNavigateToThreadList = false private var selectionType: SelectionBackgroundKind { - if isSelected { - return .multiple - } else if !multipleSelectionViewModel.isEnabled && viewModel.selectedThread?.uid == thread.uid { - return .single + if multipleSelectionViewModel.isEnabled { + return isMultiSelected ? .multiple : .none } - return .none - } - - private var selectedThreadBackground: Bool { - return !multipleSelectionViewModel.isEnabled && (viewModel.selectedThread?.uid == thread.uid) + return isSelected ? .single : .none } var body: some View { @@ -58,7 +53,7 @@ struct ThreadListCell: View { thread: thread, density: threadDensity, isMultipleSelectionEnabled: multipleSelectionViewModel.isEnabled, - isSelected: isSelected + isSelected: isMultiSelected ) .background(SelectionBackground(selectionType: selectionType, paddingLeading: 4)) .onTapGesture { didTapCell() } @@ -67,11 +62,6 @@ struct ThreadListCell: View { .listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0)) .listRowSeparator(.hidden) .listRowBackground(MailResourcesAsset.backgroundColor.swiftUIColor) - .onChange(of: viewModel.selectedThread) { newThread in - if newThread?.uid == thread.uid { - shouldNavigateToThreadList = true - } - } } private func didTapCell() { @@ -92,7 +82,6 @@ struct ThreadListCell: View { splitViewManager.splitViewController?.hide(.supplementary) } viewModel.selectedThread = thread - path?.wrappedValue.append(thread) } } } @@ -123,6 +112,7 @@ struct ThreadListCell_Previews: PreviewProvider { multipleSelectionViewModel: ThreadListMultipleSelectionViewModel(mailboxManager: PreviewHelper.sampleMailboxManager), threadDensity: .large, isSelected: false, + isMultiSelected: false, editedMessageDraft: .constant(nil) ) } diff --git a/Mail/Views/Thread List/ThreadListView.swift b/Mail/Views/Thread List/ThreadListView.swift index aa6ba203d..de57d2edb 100644 --- a/Mail/Views/Thread List/ThreadListView.swift +++ b/Mail/Views/Thread List/ThreadListView.swift @@ -53,6 +53,7 @@ struct ThreadListView: View { @EnvironmentObject var splitViewManager: SplitViewManager @EnvironmentObject var globalBottomSheet: GlobalBottomSheet + @Environment(\.mailNavigationPath) var path @AppStorage(UserDefaults.shared.key(.threadDensity)) private var threadDensity = DefaultPreferences.threadDensity @AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor @@ -142,7 +143,8 @@ struct ThreadListView: View { viewModel: viewModel, multipleSelectionViewModel: multipleSelectionViewModel, threadDensity: threadDensity, - isSelected: multipleSelectionViewModel.selectedItems + isSelected: viewModel.selectedThread?.uid == thread.uid, + isMultiSelected: multipleSelectionViewModel.selectedItems .contains { $0.id == thread.id }, editedMessageDraft: $editedMessageDraft) .id(thread.id) @@ -232,6 +234,13 @@ struct ThreadListView: View { .onChange(of: splitViewManager.selectedFolder) { newFolder in changeFolder(newFolder: newFolder) } + .onChange(of: viewModel.selectedThread) { newThread in + if let newThread = newThread { + path?.wrappedValue = [newThread] + } else { + path?.wrappedValue = [] + } + } .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in updateFetchingTask() } diff --git a/Mail/Views/Thread List/ThreadListViewModel.swift b/Mail/Views/Thread List/ThreadListViewModel.swift index 54961c349..2911b3d3c 100644 --- a/Mail/Views/Thread List/ThreadListViewModel.swift +++ b/Mail/Views/Thread List/ThreadListViewModel.swift @@ -93,13 +93,7 @@ class DateSection: Identifiable { @Published var sections = [DateSection]() @Published var selectedThread: Thread? { didSet { - guard !isCompact, !filteredThreads.isEmpty else { return } - if selectedThread == nil, let index = selectedThreadIndex { - let realIndex = min(index, filteredThreads.count - 1) - selectedThread = filteredThreads[realIndex] - } else if let thread = selectedThread, let index = filteredThreads.firstIndex(where: { $0.uid == thread.uid }) { - selectedThreadIndex = index - } + selectedThreadIndex = filteredThreads.firstIndex { $0.uid == selectedThread?.uid } } } @@ -235,6 +229,7 @@ class DateSection: Identifiable { } case .update(let results, _, _, _): let filteredThreads = Array(results.freezeIfNeeded()) + self?.nextThreadIfNeeded(from: filteredThreads) guard let newSections = self?.sortThreadsIntoSections(threads: filteredThreads) else { return } DispatchQueue.main.sync { @@ -263,6 +258,14 @@ class DateSection: Identifiable { } } + func nextThreadIfNeeded(from threads: [Thread]) { + guard !isCompact, + !threads.contains(where: { $0.uid == selectedThread?.uid }), + let lastIndex = selectedThreadIndex else { return } + let validIndex = min(lastIndex, threads.count - 1) + selectedThread = threads[validIndex] + } + private func sortThreadsIntoSections(threads: [Thread]) -> [DateSection]? { var newSections = [DateSection]() diff --git a/Mail/Views/Thread/ThreadView.swift b/Mail/Views/Thread/ThreadView.swift index d8427c718..13caa523a 100644 --- a/Mail/Views/Thread/ThreadView.swift +++ b/Mail/Views/Thread/ThreadView.swift @@ -41,10 +41,10 @@ class MessageBottomSheet: DisplayedFloatingPanelState struct ThreadView: View { @EnvironmentObject private var splitViewManager: SplitViewManager + @Environment(\.mailNavigationPath) var path @EnvironmentObject private var mailboxManager: MailboxManager @ObservedRealmObject var thread: Thread - var onDismiss: (() -> Void)? @State private var headerHeight: CGFloat = 0 @State private var displayNavigationTitle = false @@ -58,9 +58,14 @@ struct ThreadView: View { @EnvironmentObject var globalBottomSheet: GlobalBottomSheet @EnvironmentObject var globalAlert: GlobalAlert - @Environment(\.verticalSizeClass) var sizeClass + @Environment(\.horizontalSizeClass) var sizeClass + @Environment(\.verticalSizeClass) var verticalSizeClass @Environment(\.dismiss) var dismiss + var isCompact: Bool { + sizeClass == .compact || verticalSizeClass == .compact + } + @LazyInjectService private var matomo: MatomoUtils private let toolbarActions: [Action] = [.reply, .forward, .archive, .delete] @@ -176,19 +181,14 @@ struct ThreadView: View { } } .onChange(of: thread.messages) { newMessagesList in - if newMessagesList.isEmpty { - showEmptyView = true - onDismiss?() - dismiss() - } - if thread.messageInFolderCount == 0 { - onDismiss?() - dismiss() + if newMessagesList.isEmpty || thread.messageInFolderCount == 0 { + if isCompact { + dismiss() // For iPhone + } else { + path?.wrappedValue = [] // For iPad + } } } - .emptyState(isEmpty: showEmptyView) { - EmptyStateView.emptyThread(from: splitViewManager.selectedFolder) - } .matomoView(view: [MatomoUtils.View.threadView.displayName, "Main"]) } From f67b81bdc790cf862cdbba9411045f81b43b2cb4 Mon Sep 17 00:00:00 2001 From: Ambroise Decouttere Date: Tue, 25 Apr 2023 12:08:54 +0200 Subject: [PATCH 3/6] fix: CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3372a42f..e77d09edf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: jobs: build: name: Build and Test project - runs-on: self-hosted + runs-on: [ self-hosted, iOS ] steps: - name: Cancel Previous Runs From 4d7b432f3b430f74d6c183e2471d798224016a1e Mon Sep 17 00:00:00 2001 From: Ambroise Decouttere Date: Wed, 26 Apr 2023 10:26:07 +0200 Subject: [PATCH 4/6] fix: Rebase master --- Mail/Views/SplitView.swift | 15 ++++++--------- Mail/Views/Thread List/ThreadListViewModel.swift | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Mail/Views/SplitView.swift b/Mail/Views/SplitView.swift index 0b8335594..32000da53 100644 --- a/Mail/Views/SplitView.swift +++ b/Mail/Views/SplitView.swift @@ -92,10 +92,10 @@ struct SplitView: View { ZStack { NBNavigationStack(path: $path) { ThreadListManagerView(isCompact: isCompact) - .accessibilityHidden(navigationDrawerController.isOpen) - .nbNavigationDestination(for: Thread.self) { thread in - ThreadView(thread: thread) - } + .accessibilityHidden(navigationDrawerController.isOpen) + .nbNavigationDestination(for: Thread.self) { thread in + ThreadView(thread: thread) + } } .navigationViewStyle(.stack) @@ -112,7 +112,7 @@ struct SplitView: View { ThreadListManagerView(isCompact: isCompact) if let thread = path.last { - ThreadView(mailboxManager: mailboxManager, thread: thread) + ThreadView(thread: thread) } else { EmptyStateView.emptyThread(from: splitViewManager.selectedFolder) } @@ -124,10 +124,6 @@ struct SplitView: View { try await mailboxManager.folders() } } - .environment(\.mailNavigationPath, $path) - .environmentObject(splitViewManager) - .environmentObject(navigationDrawerController) - .defaultAppStorage(.shared) .onAppear { AppDelegate.orientationLock = .all } @@ -181,6 +177,7 @@ struct SplitView: View { EmptyView() } } + .environment(\.mailNavigationPath, $path) .environment(\.realmConfiguration, mailboxManager.realmConfiguration) .environmentObject(mailboxManager) .environmentObject(splitViewManager) diff --git a/Mail/Views/Thread List/ThreadListViewModel.swift b/Mail/Views/Thread List/ThreadListViewModel.swift index 2911b3d3c..5f3fe1bf7 100644 --- a/Mail/Views/Thread List/ThreadListViewModel.swift +++ b/Mail/Views/Thread List/ThreadListViewModel.swift @@ -229,10 +229,10 @@ class DateSection: Identifiable { } case .update(let results, _, _, _): let filteredThreads = Array(results.freezeIfNeeded()) - self?.nextThreadIfNeeded(from: filteredThreads) guard let newSections = self?.sortThreadsIntoSections(threads: filteredThreads) else { return } DispatchQueue.main.sync { + self?.nextThreadIfNeeded(from: filteredThreads) self?.filteredThreads = filteredThreads if self?.filter != .all && filteredThreads.count == 1 && self?.filter.accepts(thread: filteredThreads[0]) != true { From 7e72aba15fc479f3109bef458a474df02de8bc89 Mon Sep 17 00:00:00 2001 From: Ambroise Decouttere Date: Wed, 26 Apr 2023 13:17:09 +0200 Subject: [PATCH 5/6] fix: PR feedback --- Mail/Views/SplitView.swift | 2 +- Mail/Views/Thread List/ThreadListCell.swift | 2 +- Mail/Views/Thread List/ThreadListView.swift | 2 +- Mail/Views/Thread/ThreadView.swift | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Mail/Views/SplitView.swift b/Mail/Views/SplitView.swift index 32000da53..5a1b92fce 100644 --- a/Mail/Views/SplitView.swift +++ b/Mail/Views/SplitView.swift @@ -74,7 +74,7 @@ struct SplitView: View { @StateObject private var alert = GlobalAlert() @StateObject private var splitViewManager: SplitViewManager - @State var path = [Thread]() + @State private var path = [Thread]() var isCompact: Bool { sizeClass == .compact || verticalSizeClass == .compact diff --git a/Mail/Views/Thread List/ThreadListCell.swift b/Mail/Views/Thread List/ThreadListCell.swift index bed120f4d..e791ba606 100644 --- a/Mail/Views/Thread List/ThreadListCell.swift +++ b/Mail/Views/Thread List/ThreadListCell.swift @@ -25,7 +25,7 @@ import SwiftUI struct ThreadListCell: View { @EnvironmentObject var splitViewManager: SplitViewManager - @Environment(\.mailNavigationPath) var path + @Environment(\.mailNavigationPath) private var path let thread: Thread diff --git a/Mail/Views/Thread List/ThreadListView.swift b/Mail/Views/Thread List/ThreadListView.swift index de57d2edb..bb4519af6 100644 --- a/Mail/Views/Thread List/ThreadListView.swift +++ b/Mail/Views/Thread List/ThreadListView.swift @@ -235,7 +235,7 @@ struct ThreadListView: View { changeFolder(newFolder: newFolder) } .onChange(of: viewModel.selectedThread) { newThread in - if let newThread = newThread { + if let newThread { path?.wrappedValue = [newThread] } else { path?.wrappedValue = [] diff --git a/Mail/Views/Thread/ThreadView.swift b/Mail/Views/Thread/ThreadView.swift index 13caa523a..79368d474 100644 --- a/Mail/Views/Thread/ThreadView.swift +++ b/Mail/Views/Thread/ThreadView.swift @@ -58,8 +58,8 @@ struct ThreadView: View { @EnvironmentObject var globalBottomSheet: GlobalBottomSheet @EnvironmentObject var globalAlert: GlobalAlert - @Environment(\.horizontalSizeClass) var sizeClass - @Environment(\.verticalSizeClass) var verticalSizeClass + @Environment(\.horizontalSizeClass) private var sizeClass + @Environment(\.verticalSizeClass) private var verticalSizeClass @Environment(\.dismiss) var dismiss var isCompact: Bool { From 7a78ec55f424a948135f420135158db51f60d3ff Mon Sep 17 00:00:00 2001 From: Ambroise Decouttere Date: Wed, 26 Apr 2023 16:48:01 +0200 Subject: [PATCH 6/6] fix: PR feedback --- Mail/Views/Thread List/ThreadListView.swift | 2 +- Mail/Views/Thread/ThreadView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Mail/Views/Thread List/ThreadListView.swift b/Mail/Views/Thread List/ThreadListView.swift index bb4519af6..f91aaf5a8 100644 --- a/Mail/Views/Thread List/ThreadListView.swift +++ b/Mail/Views/Thread List/ThreadListView.swift @@ -53,7 +53,7 @@ struct ThreadListView: View { @EnvironmentObject var splitViewManager: SplitViewManager @EnvironmentObject var globalBottomSheet: GlobalBottomSheet - @Environment(\.mailNavigationPath) var path + @Environment(\.mailNavigationPath) private var path @AppStorage(UserDefaults.shared.key(.threadDensity)) private var threadDensity = DefaultPreferences.threadDensity @AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor diff --git a/Mail/Views/Thread/ThreadView.swift b/Mail/Views/Thread/ThreadView.swift index 79368d474..479462b98 100644 --- a/Mail/Views/Thread/ThreadView.swift +++ b/Mail/Views/Thread/ThreadView.swift @@ -41,7 +41,7 @@ class MessageBottomSheet: DisplayedFloatingPanelState struct ThreadView: View { @EnvironmentObject private var splitViewManager: SplitViewManager - @Environment(\.mailNavigationPath) var path + @Environment(\.mailNavigationPath) private var path @EnvironmentObject private var mailboxManager: MailboxManager @ObservedRealmObject var thread: Thread