Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perform same-page visit proposals without recreating the current destination #292

Merged
merged 9 commits into from
Feb 23, 2024
18 changes: 11 additions & 7 deletions turbo/src/main/assets/js/turbo_bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,18 @@
// Adapter interface

visitProposedToLocation(location, options) {
if (window.Turbo && typeof Turbo.navigator.locationWithActionIsSamePage === "function") {
if (Turbo.navigator.locationWithActionIsSamePage(location, options.action)) {
if (window.Turbo && Turbo.navigator.locationWithActionIsSamePage(location, options.action)) {
// Scroll to the anchor on the page
TurboSession.visitProposalScrollingToAnchor(location.toString(), JSON.stringify(options))
Turbo.navigator.view.scrollToAnchorFromLocation(location)
return
} else if (window.Turbo && Turbo.navigator.location?.href === location.href) {
// Refresh the page without native proposal
TurboSession.visitProposalRefreshingPage(location.toString(), JSON.stringify(options))
this.visitLocationWithOptionsAndRestorationIdentifier(location, JSON.stringify(options), Turbo.navigator.restorationIdentifier)
} else {
// Propose the visit
TurboSession.visitProposedToLocation(location.toString(), JSON.stringify(options))
}
}

TurboSession.visitProposedToLocation(location.toString(), JSON.stringify(options))
}

// Turbolinks 5
Expand All @@ -114,7 +118,7 @@
}

visitStarted(visit) {
TurboSession.visitStarted(visit.identifier, visit.hasCachedSnapshot(), visit.location.toString())
TurboSession.visitStarted(visit.identifier, visit.hasCachedSnapshot(), visit.isPageRefresh || false, visit.location.toString())
this.currentVisit = visit
this.issueRequestForVisitWithIdentifier(visit.identifier)
this.changeHistoryForVisitWithIdentifier(visit.identifier)
Expand Down
41 changes: 38 additions & 3 deletions turbo/src/main/kotlin/dev/hotwire/turbo/session/TurboSession.kt
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class TurboSession internal constructor(
// Callbacks from Turbo JS Core

/**
* Called by Turbo bridge when a new visit is initiated.
* Called by Turbo bridge when a new visit is proposed.
*
* Warning: This method is public so it can be used as a Javascript Interface.
* You should never call this directly as it could lead to unintended behavior.
Expand All @@ -195,6 +195,38 @@ class TurboSession internal constructor(
callback { it.visitProposedToLocation(location, options) }
}

/**
* Called by Turbo bridge when a new visit proposal will refresh the
* current page.
*
* Warning: This method is public so it can be used as a Javascript Interface.
* You should never call this directly as it could lead to unintended behavior.
*
* @param location The location to visit.
* @param optionsJson A JSON block to be serialized into [TurboVisitOptions].
*/
@JavascriptInterface
fun visitProposalRefreshingPage(location: String, optionsJson: String) {
val options = TurboVisitOptions.fromJSON(optionsJson) ?: return
logEvent("visitProposalRefreshingPage", "location" to location, "options" to options)
}

/**
* Called by Turbo bridge when a new visit proposal will scroll to an anchor
* on the same page.
*
* Warning: This method is public so it can be used as a Javascript Interface.
* You should never call this directly as it could lead to unintended behavior.
*
* @param location The location to visit.
* @param optionsJson A JSON block to be serialized into [TurboVisitOptions].
*/
@JavascriptInterface
fun visitProposalScrollingToAnchor(location: String, optionsJson: String) {
val options = TurboVisitOptions.fromJSON(optionsJson) ?: return
logEvent("visitProposalScrollingToAnchor", "location" to location, "options" to options)
}

/**
* Called by Turbo bridge when a new visit has just started.
*
Expand All @@ -206,11 +238,14 @@ class TurboSession internal constructor(
* @param location The location being visited.
*/
@JavascriptInterface
fun visitStarted(visitIdentifier: String, visitHasCachedSnapshot: Boolean, location: String) {
fun visitStarted(visitIdentifier: String, visitHasCachedSnapshot: Boolean,
visitIsPageRefresh: Boolean, location: String
) {
logEvent(
"visitStarted", "location" to location,
"visitIdentifier" to visitIdentifier,
"visitHasCachedSnapshot" to visitHasCachedSnapshot
"visitHasCachedSnapshot" to visitHasCachedSnapshot,
"visitIsPageRefresh" to visitIsPageRefresh
)

currentVisit?.identifier = visitIdentifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,25 @@ class TurboSessionTest {
@Test
fun visitProposedToLocationFiresCallback() {
val options = TurboVisitOptions()
val newLocation = "${visit.location}/page"

session.currentVisit = visit
session.visitProposedToLocation(visit.location, options.toJson())
session.visitProposedToLocation(newLocation, options.toJson())

verify(callback).visitProposedToLocation(visit.location, options)
verify(callback).visitProposedToLocation(newLocation, options)
}

@Test
fun visitStartedSavesCurrentVisitIdentifier() {
val visitIdentifier = "12345"

session.currentVisit = visit.copy(identifier = visitIdentifier)
session.visitStarted(visitIdentifier, true, "https://turbo.hotwired.dev")
session.visitStarted(
visitIdentifier = visitIdentifier,
visitHasCachedSnapshot = true,
visitIsPageRefresh = false,
location = "https://turbo.hotwired.dev"
)

assertThat(session.currentVisit?.identifier).isEqualTo(visitIdentifier)
}
Expand Down
Loading