Skip to content

Commit

Permalink
Merge pull request #220 from hotwired/fix-refresh
Browse files Browse the repository at this point in the history
Fix refresh behavior
  • Loading branch information
svara authored Jul 12, 2024
2 parents 97d0219 + 84f0eca commit 3ce4f97
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 12 deletions.
15 changes: 11 additions & 4 deletions Source/Turbo Navigator/TurboNavigationHierarchyController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,21 +168,28 @@ class TurboNavigationHierarchyController {
if navigationController.presentedViewController != nil {
if modalNavigationController.viewControllers.count == 1 {
navigationController.dismiss(animated: proposal.animated)
delegate.refresh(navigationStack: .main)
refreshIfTopViewControllerIsVisitable(from: .main)
} else {
modalNavigationController.popViewController(animated: proposal.animated)
delegate.refresh(navigationStack: .modal)
refreshIfTopViewControllerIsVisitable(from: .modal)
}
} else {
navigationController.popViewController(animated: proposal.animated)
delegate.refresh(navigationStack: .main)
refreshIfTopViewControllerIsVisitable(from: .main)
}
}

private func refreshIfTopViewControllerIsVisitable(from stack: NavigationStackType) {
if let navControllerTopmostVisitable = navController(for: stack).topViewController as? Visitable {
delegate.refreshVisitable(navigationStack: stack,
newTopmostVisitable: navControllerTopmostVisitable)
}
}

private func clearAll(via proposal: VisitProposal) {
navigationController.dismiss(animated: proposal.animated)
navigationController.popToRootViewController(animated: proposal.animated)
delegate.refresh(navigationStack: .main)
refreshIfTopViewControllerIsVisitable(from: .main)
}

private func replaceRoot(with controller: UIViewController, via proposal: VisitProposal) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
import SafariServices
import WebKit

/// Implement to be notified when certain navigations are performed
/// or to render a native controller instead of a Turbo web visit.
protocol TurboNavigationHierarchyControllerDelegate: AnyObject {
func visit(_ : Visitable, on: TurboNavigationHierarchyController.NavigationStackType, with: VisitOptions)
func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType)

/// Once the navigation hierarchy is modified, begin a visit on a navigation controller.
///
/// - Parameters:
/// - _: the Visitable destination
/// - on: the navigation controller that was modified
/// - with: the visit options
func visit(_ : Visitable,
on: TurboNavigationHierarchyController.NavigationStackType,
with: VisitOptions)

/// A refresh will pop (or dismiss) then ask the session to refresh the previous (or underlying) Visitable.
///
/// - Parameters:
/// - navigationStack: the stack where the refresh is happening
/// - newTopmostVisitable: the visitable to be refreshed
func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType,
newTopmostVisitable: Visitable)
}
9 changes: 6 additions & 3 deletions Source/Turbo Navigator/TurboNavigator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,13 @@ extension TurboNavigator: TurboNavigationHierarchyControllerDelegate {
}
}

func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType) {
func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType,
newTopmostVisitable: any Visitable) {
switch navigationStack {
case .main: session.reload()
case .modal: modalSession.reload()
case .main:
session.visit(newTopmostVisitable, action: .restore)
case .modal:
modalSession.visit(newTopmostVisitable, action: .restore)
}
}
}
Expand Down
57 changes: 56 additions & 1 deletion Tests/Turbo Navigator/TurboNavigatorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,61 @@ final class TurboNavigationHierarchyControllerTests: XCTestCase {
XCTAssert(navigationController.viewControllers.last is VisitableViewController)
assertVisited(url: proposal.url, on: .main)
}

func test_default_default_refresh_refreshesPreviousController() {
navigator.route(oneURL)
XCTAssertEqual(navigationController.viewControllers.count, 1)

navigator.route(twoURL)
XCTAssertEqual(navigator.rootViewController.viewControllers.count, 2)

/// Refreshing should pop the view controller and refresh the underlying controller.
let proposal = VisitProposal(presentation: .refresh)
navigator.route(proposal)

let visitable = navigator.session.activeVisitable as! VisitableViewController
XCTAssertEqual(visitable.visitableURL, oneURL)
XCTAssertEqual(navigator.rootViewController.viewControllers.count, 1)
}

func test_default_modal_refresh_refreshesPreviousController() {
navigationController.pushViewController(UIViewController(), animated: false)
XCTAssertEqual(navigationController.viewControllers.count, 1)

let oneURLProposal = VisitProposal(path: "/one", context: .modal)
navigator.route(oneURLProposal)

let twoURLProposal = VisitProposal(path: "/two", context: .modal)
navigator.route(twoURLProposal)
XCTAssertEqual(modalNavigationController.viewControllers.count, 2)

/// Refreshing should pop the view controller and refresh the underlying controller.
let proposal = VisitProposal(presentation: .refresh)
navigator.route(proposal)

let visitable = navigator.modalSession.activeVisitable as! VisitableViewController
XCTAssertEqual(visitable.visitableURL, oneURL)
XCTAssertEqual(modalNavigationController.viewControllers.count, 1)
}

func test_default_modal_refresh_dismissesAndRefreshesMainStackTopViewController() {
navigator.route(oneURL)
XCTAssertEqual(navigationController.viewControllers.count, 1)

let twoURLProposal = VisitProposal(path: "/two", context: .modal)
navigator.route(twoURLProposal)
XCTAssertEqual(modalNavigationController.viewControllers.count, 1)

/// Refreshing should dismiss the view controller and refresh the underlying controller.
let proposal = VisitProposal(context: .modal, presentation: .refresh)
navigator.route(proposal)

let visitable = navigator.session.activeVisitable as! VisitableViewController
XCTAssertEqual(visitable.visitableURL, oneURL)

XCTAssertNil(navigationController.presentedViewController)
XCTAssertEqual(navigator.rootViewController.viewControllers.count, 1)
}

func test_default_modal_default_presentsModal() {
navigationController.pushViewController(UIViewController(), animated: false)
Expand Down Expand Up @@ -309,7 +364,7 @@ final class TurboNavigationHierarchyControllerTests: XCTestCase {

private class EmptyNavigationDelegate: TurboNavigationHierarchyControllerDelegate {
func visit(_: Visitable, on: TurboNavigationHierarchyController.NavigationStackType, with: VisitOptions) {}
func refresh(navigationStack: TurboNavigationHierarchyController.NavigationStackType) {}
func refreshVisitable(navigationStack: TurboNavigationHierarchyController.NavigationStackType, newTopmostVisitable: any Visitable) { }
}

// MARK: - VisitProposal extension
Expand Down

0 comments on commit 3ce4f97

Please sign in to comment.