Skip to content

Commit

Permalink
Boscojwho/hoist navigation dismiss (#611)
Browse files Browse the repository at this point in the history
  • Loading branch information
boscojwho authored Sep 15, 2023
1 parent 847451d commit 981fccb
Show file tree
Hide file tree
Showing 37 changed files with 609 additions and 115 deletions.
13 changes: 13 additions & 0 deletions Mlem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,9 @@
E40E018C2AABF85500410B2C /* NavigationRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018B2AABF85500410B2C /* NavigationRoutes.swift */; };
E40E018E2AABFBDE00410B2C /* NavigationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018D2AABFBDE00410B2C /* NavigationRouter.swift */; };
E40E01902AABFC9300410B2C /* AnyNavigationPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018F2AABFC9300410B2C /* AnyNavigationPath.swift */; };
E41FAD792AB12C2500557719 /* Environment+ScrollViewReaderProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41FAD782AB12C2500557719 /* Environment+ScrollViewReaderProxy.swift */; };
E41FAD7B2AB12D5900557719 /* ScrollToView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41FAD7A2AB12D5900557719 /* ScrollToView.swift */; };
E4516E472AAC4B3500F496BE /* DismissAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4516E462AAC4B3500F496BE /* DismissAction.swift */; };
E453477E2A9DE37300D1B46F /* Array+SafeIndexing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E453477D2A9DE37300D1B46F /* Array+SafeIndexing.swift */; };
E453A1D02A81C2140004BB8A /* QuickLookPreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E453A1CF2A81C2140004BB8A /* QuickLookPreviewController.swift */; };
E47478132AAC350E001CB1AC /* NavigationLink+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47478122AAC350E001CB1AC /* NavigationLink+Helpers.swift */; };
Expand Down Expand Up @@ -858,6 +861,9 @@
E40E018B2AABF85500410B2C /* NavigationRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRoutes.swift; sourceTree = "<group>"; };
E40E018D2AABFBDE00410B2C /* NavigationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = "<group>"; };
E40E018F2AABFC9300410B2C /* AnyNavigationPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyNavigationPath.swift; sourceTree = "<group>"; };
E41FAD782AB12C2500557719 /* Environment+ScrollViewReaderProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Environment+ScrollViewReaderProxy.swift"; sourceTree = "<group>"; };
E41FAD7A2AB12D5900557719 /* ScrollToView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollToView.swift; sourceTree = "<group>"; };
E4516E462AAC4B3500F496BE /* DismissAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissAction.swift; sourceTree = "<group>"; };
E453477D2A9DE37300D1B46F /* Array+SafeIndexing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+SafeIndexing.swift"; sourceTree = "<group>"; };
E453A1CF2A81C2140004BB8A /* QuickLookPreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickLookPreviewController.swift; sourceTree = "<group>"; };
E47478122AAC350E001CB1AC /* NavigationLink+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NavigationLink+Helpers.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1316,6 +1322,8 @@
63A200522A2DDD38005CDDE3 /* Dictionary - Append.swift */,
E4DDB4312A81819300B3A7E0 /* Double.swift */,
B1955A202A6145C00056CF99 /* Environment - EasterFlagSetter.swift */,
E41FAD782AB12C2500557719 /* Environment+ScrollViewReaderProxy.swift */,
CDE8F2382A68DA7D00E0AE68 /* Environment - Force Onboard.swift */,
507573902A5AD53C00AA7ABD /* Error+Equatable.swift */,
6D8601ED2A43C0B1002A56FC /* Image.swift */,
6DA61F842A568F99001EA633 /* Int.swift */,
Expand Down Expand Up @@ -1746,6 +1754,7 @@
B11D72822A49FAA7009DC22F /* Cached Image.swift */,
50F2851B2A5C5C1500CF8865 /* TokenRefreshView.swift */,
CD863FBB2A6B026400A31ED9 /* DocumentView.swift */,
E41FAD7A2AB12D5900557719 /* ScrollToView.swift */,
);
path = Shared;
sourceTree = "<group>";
Expand Down Expand Up @@ -2237,6 +2246,7 @@
E49F0E752A90395400BC4EE3 /* NavigationPath+Helpers.swift */,
E47B2B772A902E3C00629AF7 /* Router */,
E47B2B742A902DB400629AF7 /* Route */,
E4516E462AAC4B3500F496BE /* DismissAction.swift */,
);
path = Navigation;
sourceTree = "<group>";
Expand Down Expand Up @@ -2509,6 +2519,7 @@
CDEBC3282A9A57F200518D9D /* Content Model Identifier.swift in Sources */,
B1955A1D2A606B950056CF99 /* Easter Rewards.swift in Sources */,
CDB0117F2A6F70A000D043EB /* Editor Tracker.swift in Sources */,
E4516E472AAC4B3500F496BE /* DismissAction.swift in Sources */,
6354F30A2A2E20040074C08D /* Alert - Multiple Alerts.swift in Sources */,
6318EDC727EE4E1500BFCAE8 /* Post.swift in Sources */,
6372186C2A3A2AAD008C4816 /* SaveComment.swift in Sources */,
Expand Down Expand Up @@ -2598,6 +2609,7 @@
637218502A3A2AAD008C4816 /* APIPersonAggregates.swift in Sources */,
6D693A422A5114DF009E2D76 /* APIPostReport.swift in Sources */,
5064D03F2A6DE0DB00B22EE3 /* Notifier+Dependency.swift in Sources */,
E41FAD792AB12C2500557719 /* Environment+ScrollViewReaderProxy.swift in Sources */,
6D8003792A45FD1300363206 /* Bundle.swift in Sources */,
63344C712A098060001BC616 /* Sidebar View.swift in Sources */,
6DE118392A4A20D600810C7E /* Lazy Load Post Link.swift in Sources */,
Expand Down Expand Up @@ -2777,6 +2789,7 @@
6318DE5427FB958800CC2AD6 /* Stickied Tag.swift in Sources */,
CD7B53B72A5F258B00006E81 /* APIPrivateMessageReportView.swift in Sources */,
030AC04F2A6464DA00037155 /* CommunitySettingsView.swift in Sources */,
E41FAD7B2AB12D5900557719 /* ScrollToView.swift in Sources */,
6386E03A2A0455BC006B3C1D /* String - Contains Elements From Array.swift in Sources */,
63F0C7A62A05225100A18C5D /* Saved Account.swift in Sources */,
637218462A3A2AAD008C4816 /* APIComment.swift in Sources */,
Expand Down
3 changes: 1 addition & 2 deletions Mlem/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ struct ContentView: View {

// tabs
@State private var tabSelection: TabSelection = .feeds
@State private var tabNavigation: any FancyTabBarSelection = TabSelection._tabBarNavigation
@State private var showLoading: Bool = false
@GestureState private var isDetectingLongPress = false

Expand All @@ -36,7 +35,7 @@ struct ContentView: View {
var accessibilityFont: Bool { UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory }

var body: some View {
FancyTabBar(selection: $tabSelection, navigationSelection: $tabNavigation, dragUpGestureCallback: showAccountSwitcherDragCallback) {
FancyTabBar(selection: $tabSelection, dragUpGestureCallback: showAccountSwitcherDragCallback) {
Group {
FeedRoot(showLoading: showLoading)
.fancyTabItem(tag: TabSelection.feeds) {
Expand Down
5 changes: 2 additions & 3 deletions Mlem/Custom Tab Bar/FancyTabBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct FancyTabBar<Selection: FancyTabBarSelection, Content: View>: View {

@Binding private var selection: Selection
/// Keeps track of tab "re-selected" state.
@Binding private var navigationSelection: NavigationSelection
@State private var navigationSelection: NavigationSelection
@State private var __tempNavigationSelection: Int = -1
/// We only toggle this to trigger an `onChange` event.
@State private var __navigationSelectionSignal: Bool = false
Expand All @@ -30,12 +30,11 @@ struct FancyTabBar<Selection: FancyTabBarSelection, Content: View>: View {

init(
selection: Binding<Selection>,
navigationSelection: Binding<NavigationSelection>,
dragUpGestureCallback: (() -> Void)? = nil,
@ViewBuilder content: @escaping () -> Content
) {
self._selection = selection
self._navigationSelection = navigationSelection
self._navigationSelection = .init(wrappedValue: selection.wrappedValue)
self.content = content
self.dragUpGestureCallback = dragUpGestureCallback
}
Expand Down
19 changes: 19 additions & 0 deletions Mlem/Extensions/Environment+ScrollViewReaderProxy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Environment+ScrollViewReaderProxy.swift
// Mlem
//
// Created by Bosco Ho on 2023-09-12.
//

import SwiftUI

private struct ScrollViewReaderProxy: EnvironmentKey {
static let defaultValue: ScrollViewProxy? = nil
}

extension EnvironmentValues {
var scrollViewProxy: ScrollViewProxy? {
get { self[ScrollViewReaderProxy.self] }
set { self[ScrollViewReaderProxy.self] = newValue }
}
}
3 changes: 3 additions & 0 deletions Mlem/Extensions/IconSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ let iconFinder = Regex {
// struct AlternativeIcons: View {
struct IconSettingsView: View {
@State var currentIcon: String? = UIApplication.shared.alternateIconName

@Environment(\.dismiss) private var dismiss
@EnvironmentObject var easterTracker: EasterFlagsTracker

var body: some View {
Expand All @@ -33,6 +35,7 @@ struct IconSettingsView: View {
}
}
.fancyTabScrollCompatible()
.hoistNavigation(dismiss: dismiss)
}

func getAllIcons() -> [AlternativeIcon] {
Expand Down
136 changes: 136 additions & 0 deletions Mlem/Navigation/DismissAction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//
// DismissAction.swift
// Mlem
//
// Created by Bosco Ho on 2023-09-08.
//

import Dependencies
import Foundation
import SwiftUI

// MARK: - Navigation
final class Navigation: ObservableObject {

/// Return `true` to indicate that an auxiliary action was performed.
typealias AuxiliaryAction = () -> Bool

var pathActions: [Int: (dismiss: DismissAction?, auxiliaryAction: AuxiliaryAction?)] = [:]

/// Navigation always performs dismiss action (if available), but may choose to perform an auxiliary action first.
///
/// This action includes support for popping back to sidebar view in a `NavigationSplitView`.
var dismiss: DismissAction?
/// An auxiliary action may consist of multiple sub-actions: To do so, simply configure this action to return false once all sub-actions have been (or can no longer be) performed.
///
/// - Warning: Navigation may skip this action, depending on user preference or other factors. Do not perform critical logic in this action.
var auxiliaryAction: AuxiliaryAction?
}

// MARK: - Hoist dismiss action
extension View {

func hoistNavigation(
dismiss: DismissAction,
auxiliaryAction: Navigation.AuxiliaryAction? = nil
) -> some View {
modifier(
NavigationDismissHoisting(
dismiss: dismiss,
auxiliaryAction: auxiliaryAction
)
)
}
}

struct NavigationDismissHoisting: ViewModifier {

@EnvironmentObject private var navigation: Navigation
@Environment(\.navigationPathWithRoutes) private var navigationPath

/// - Note: Unfortunately, we can't access the dismiss action via View.environment...doing so causes SwiftUI to enter into infinite loop. [2023.09]
let dismiss: DismissAction
let auxiliaryAction: Navigation.AuxiliaryAction?

@State private var didAppear = false

func body(content: Content) -> some View {
content
.onAppear {
defer { didAppear = true }

/// This must only be called once:
/// For example, user may wish to drag to peek at the previous view, but then cancel that drag action. During this, the previous view's .onAppear will get called. If we run this logic for that view again, the actual top view's dismiss action will get lost. [2023.09]
if didAppear == false {
print("hoist navigation dismiss action")
navigation.dismiss = dismiss
navigation.auxiliaryAction = auxiliaryAction
let pathIndex = max(0, navigationPath.count)
print("adding path action at index -> \(pathIndex)")
navigation.pathActions[pathIndex] = (dismiss, auxiliaryAction)
}
}
.onDisappear {
print("onDisappear: path count -> \(navigationPath.count), action count -> \(navigation.pathActions.count)")
print("removing path action at index -> \(navigationPath.count + 1)")
navigation.pathActions.removeValue(forKey: navigationPath.count + 1)
}
}
}

// MARK: - Enable tab bar navigation
extension View {

/// Unconditionally enable tab bar navigation.
func tabBarNavigationEnabled(_ tab: TabSelection, _ navigator: Navigation) -> some View {
modifier(PerformTabBarNavigation(tab: tab, navigator: navigator))
}
}

struct PerformTabBarNavigation: ViewModifier {

@Dependency(\.hapticManager) private var hapticManager
@Environment(\.navigationPathWithRoutes) private var navigationPath
@Environment(\.tabNavigationSelectionHashValue) private var selectedNavigationTabHashValue

let tab: TabSelection
let navigator: Navigation

func body(content: Content) -> some View {
content.onChange(of: selectedNavigationTabHashValue) { newValue in
if newValue == tab.hashValue {
hapticManager.play(haptic: .gentleInfo, priority: .high)
// Customization based on user preference should occur here, for example:
// performSystemPopToRootBehaviour()
// noOp()
// performDimsissOnly()
performDismissAfterAuxiliary()
}
}
}

/// Runs all auxiliary actions before calling system dismiss action.
private func performDismissAfterAuxiliary() {
print("perform action on path index -> \(navigationPath.count)")
guard let pathAction = navigator.pathActions[navigationPath.count] else {
print("path action not found at index -> \(navigationPath.count)")
return
}

if let auxiliaryAction = pathAction.auxiliaryAction {
let performed = auxiliaryAction()
if !performed, let dismiss = pathAction.dismiss {
print("found auxiliary action, but that logic has been exhausted...perform standard dismiss action")
print("perform tab navigation on \(tab) tab")
dismiss()
} else {
print("performed auxiliary action")
}
} else if let dismiss = pathAction.dismiss {
print("perform dismiss action via tab navigation on \(tab) tab")
dismiss()
} else {
print("attempted tab navigation -> action(s) not found")
}
}
}
8 changes: 8 additions & 0 deletions Mlem/Views/Shared/Accounts/Accounts Page.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ struct AccountsPage: View {
}
}
}
.hoistNavigation(dismiss: dismiss)
.onAppear {
selectedAccount = appState.currentActiveAccount
}
.onChange(of: selectedAccount) { account in
guard let account else { return }
appState.setActiveAccount(account)
}
.sheet(isPresented: $isShowingInstanceAdditionSheet) {
AddSavedInstanceView(onboarding: false)
}
Expand Down
3 changes: 3 additions & 0 deletions Mlem/Views/Shared/DocumentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import SwiftUI

/// Displays a document
struct DocumentView: View {

@Environment(\.dismiss) private var dismiss
let text: String

var body: some View {
Expand All @@ -18,5 +20,6 @@ struct DocumentView: View {
.padding()
}
.fancyTabScrollCompatible()
.hoistNavigation(dismiss: dismiss)
}
}
9 changes: 9 additions & 0 deletions Mlem/Views/Shared/Posts/Expanded Post.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ struct ExpandedPost: View {
@AppStorage("showCommentJumpButton") var showCommentJumpButton: Bool = true
@AppStorage("commentJumpButtonSide") var commentJumpButtonSide: JumpButtonLocation = .bottomTrailing

@Environment(\.dismiss) private var dismiss

@EnvironmentObject var appState: AppState
@EnvironmentObject var editorTracker: EditorTracker
@EnvironmentObject var layoutWidgetTracker: LayoutWidgetTracker
Expand All @@ -74,6 +76,7 @@ struct ExpandedPost: View {
contentView
.environmentObject(commentTracker)
.navigationBarTitle(post.community.name, displayMode: .inline)
.hoistNavigation(dismiss: dismiss)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) { toolbarMenu }
}
Expand All @@ -85,6 +88,12 @@ struct ExpandedPost: View {
commentTracker.comments = sortComments(commentTracker.comments, by: newSortingType)
}
}
.onAppear {
print("ExpandedPost appeared")
}
.onDisappear {
print("ExpandedPost disappeared")
}
}

private var contentView: some View {
Expand Down
3 changes: 3 additions & 0 deletions Mlem/Views/Shared/Posts/Lazy Load Expanded Post.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import SwiftUI
struct LazyLoadExpandedPost: View {
@Dependency(\.errorHandler) var errorHandler

@Environment(\.dismiss) private var dismiss

let post: APIPost
let scrollTarget: Int?

Expand All @@ -38,6 +40,7 @@ struct LazyLoadExpandedPost: View {
progressView
}
}
.hoistNavigation(dismiss: dismiss)
}

private var progressView: some View {
Expand Down
Loading

0 comments on commit 981fccb

Please sign in to comment.