Skip to content

Commit

Permalink
Replace LinkInterceptor with LinkClickObserver
Browse files Browse the repository at this point in the history
Follow-up to #382

---

In the same style as [#382][], replace instances of
`LinkInterceptor` with `LinkClickObserver`, making the necessary
interface changes in classes that used to extend
`LinkInterceptorDelegate`.

Conditional logic that was once covered in the `LinkInterceptor` is
moved into the predicate methods of the delegates (for example, the
`FrameController.willFollowLinkToLocation` and
`FrameRedirector.willFollowLinkToLocation`).

[#382]: #382
  • Loading branch information
seanpdoyle committed Nov 9, 2021
1 parent dbd1e9b commit 4c25b6c
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 90 deletions.
21 changes: 11 additions & 10 deletions src/core/frames/frame_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { ViewDelegate } from "../view"
import { getAction, expandURL, urlsAreEqual, locationIsVisitable, Locatable } from "../url"
import { FormInterceptor, FormInterceptorDelegate } from "./form_interceptor"
import { FrameView } from "./frame_view"
import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor"
import { LinkClickObserver, LinkClickObserverDelegate } from "../../observers/link_click_observer"
import { FrameRenderer } from "./frame_renderer"
import { session } from "../index"

export class FrameController implements AppearanceObserverDelegate, FetchRequestDelegate, FormInterceptorDelegate, FormSubmissionDelegate, FrameElementDelegate, LinkInterceptorDelegate, ViewDelegate<Snapshot<FrameElement>> {
export class FrameController implements AppearanceObserverDelegate, FetchRequestDelegate, FormInterceptorDelegate, FormSubmissionDelegate, FrameElementDelegate, LinkClickObserverDelegate, ViewDelegate<Snapshot<FrameElement>> {
readonly element: FrameElement
readonly view: FrameView
readonly appearanceObserver: AppearanceObserver
readonly linkInterceptor: LinkInterceptor
readonly linkClickObserver: LinkClickObserver
readonly formInterceptor: FormInterceptor
currentURL?: string | null
formSubmission?: FormSubmission
Expand All @@ -31,7 +31,7 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest
this.element = element
this.view = new FrameView(this, this.element)
this.appearanceObserver = new AppearanceObserver(this, this.element)
this.linkInterceptor = new LinkInterceptor(this, this.element)
this.linkClickObserver = new LinkClickObserver(this, this.element)
this.formInterceptor = new FormInterceptor(this, this.element)
}

Expand All @@ -42,7 +42,7 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest
if (this.loadingStyle == FrameLoadingStyle.lazy) {
this.appearanceObserver.start()
}
this.linkInterceptor.start()
this.linkClickObserver.start()
this.formInterceptor.start()
this.sourceURLChanged()
}
Expand All @@ -52,7 +52,7 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest
if (this.connected) {
this.connected = false
this.appearanceObserver.stop()
this.linkInterceptor.stop()
this.linkClickObserver.stop()
this.formInterceptor.stop()
}
}
Expand Down Expand Up @@ -126,17 +126,18 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest

// Link interceptor delegate

shouldInterceptLinkClick(element: Element, url: string) {
willFollowLinkToLocation(element: Element, url: URL) {
if (element.hasAttribute("data-turbo-method")) {
return false
} else {
return this.shouldInterceptNavigation(element)
return element.closest("turbo-frame") == this.element &&
this.shouldInterceptNavigation(element)
}
}

linkClickIntercepted(element: Element, url: string) {
followedLinkToLocation(element: Element, url: URL) {
this.reloadable = true
this.navigateFrame(element, url)
this.navigateFrame(element, url.href)
}

// Form interceptor delegate
Expand Down
20 changes: 10 additions & 10 deletions src/core/frames/frame_redirector.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
import { FormInterceptor, FormInterceptorDelegate } from "./form_interceptor"
import { FrameElement } from "../../elements/frame_element"
import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor"
import { expandURL, getAction, locationIsVisitable } from "../url"
import { LinkClickObserver, LinkClickObserverDelegate } from "../../observers/link_click_observer"

export class FrameRedirector implements LinkInterceptorDelegate, FormInterceptorDelegate {
export class FrameRedirector implements LinkClickObserverDelegate, FormInterceptorDelegate {
readonly element: Element
readonly linkInterceptor: LinkInterceptor
readonly linkClickObserver: LinkClickObserver
readonly formInterceptor: FormInterceptor

constructor(element: Element) {
this.element = element
this.linkInterceptor = new LinkInterceptor(this, element)
this.linkClickObserver = new LinkClickObserver(this, element)
this.formInterceptor = new FormInterceptor(this, element)
}

start() {
this.linkInterceptor.start()
this.linkClickObserver.start()
this.formInterceptor.start()
}

stop() {
this.linkInterceptor.stop()
this.linkClickObserver.stop()
this.formInterceptor.stop()
}

shouldInterceptLinkClick(element: Element, url: string) {
return this.shouldRedirect(element)
willFollowLinkToLocation(element: Element, url: URL) {
return element.closest("turbo-frame") == null && this.shouldRedirect(element)
}

linkClickIntercepted(element: Element, url: string) {
followedLinkToLocation(element: Element, url: URL) {
const frame = this.findFrameElement(element)
if (frame) {
frame.setAttribute("reloadable", "")
frame.src = url
frame.src = url.href
}
}

Expand Down
60 changes: 0 additions & 60 deletions src/core/frames/link_interceptor.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/core/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class Session implements FormSubmitObserverDelegate, HistoryDelegate, Lin

readonly pageObserver = new PageObserver(this)
readonly cacheObserver = new CacheObserver()
readonly linkClickObserver = new LinkClickObserver(this)
readonly linkClickObserver = new LinkClickObserver(this, document.documentElement)
readonly formSubmitObserver = new FormSubmitObserver(this)
readonly scrollObserver = new ScrollObserver(this)
readonly streamObserver = new StreamObserver(this)
Expand All @@ -45,11 +45,11 @@ export class Session implements FormSubmitObserverDelegate, HistoryDelegate, Lin
if (!this.started) {
this.pageObserver.start()
this.cacheObserver.start()
this.frameRedirector.start()
this.linkClickObserver.start()
this.formSubmitObserver.start()
this.scrollObserver.start()
this.streamObserver.start()
this.frameRedirector.start()
this.history.start()
this.started = true
this.enabled = true
Expand All @@ -64,11 +64,11 @@ export class Session implements FormSubmitObserverDelegate, HistoryDelegate, Lin
if (this.started) {
this.pageObserver.stop()
this.cacheObserver.stop()
this.frameRedirector.stop()
this.linkClickObserver.stop()
this.formSubmitObserver.stop()
this.scrollObserver.stop()
this.streamObserver.stop()
this.frameRedirector.stop()
this.history.stop()
this.started = false
}
Expand Down
16 changes: 9 additions & 7 deletions src/observers/link_click_observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,35 @@ export interface LinkClickObserverDelegate {

export class LinkClickObserver {
readonly delegate: LinkClickObserverDelegate
readonly eventTarget: EventTarget
started = false

constructor(delegate: LinkClickObserverDelegate) {
constructor(delegate: LinkClickObserverDelegate, eventTarget: EventTarget) {
this.delegate = delegate
this.eventTarget = eventTarget
}

start() {
if (!this.started) {
addEventListener("click", this.clickCaptured, true)
this.eventTarget.addEventListener("click", this.clickCaptured, true)
this.started = true
}
}

stop() {
if (this.started) {
removeEventListener("click", this.clickCaptured, true)
this.eventTarget.removeEventListener("click", this.clickCaptured, true)
this.started = false
}
}

clickCaptured = () => {
removeEventListener("click", this.clickBubbled, false)
addEventListener("click", this.clickBubbled, false)
this.eventTarget.removeEventListener("click", this.clickBubbled, false)
this.eventTarget.addEventListener("click", this.clickBubbled, false)
}

clickBubbled = (event: MouseEvent) => {
if (this.clickEventIsSignificant(event)) {
clickBubbled = (event: Event) => {
if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
const target = (event.composedPath && event.composedPath()[0]) || event.target
const link = this.findLinkFromClickTarget(target)
if (link) {
Expand Down

0 comments on commit 4c25b6c

Please sign in to comment.