Fixed edge case in scroll restoration

This commit is contained in:
squidfunk 2020-02-22 15:24:15 +01:00
parent c362179234
commit eb82c8d586
8 changed files with 85 additions and 62 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"assets/javascripts/bundle.js": "assets/javascripts/bundle.dcf1ce56.min.js",
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.dcf1ce56.min.js.map",
"assets/javascripts/bundle.js": "assets/javascripts/bundle.14f179c1.min.js",
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.14f179c1.min.js.map",
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.926ffd9e.min.js",
"assets/javascripts/worker/search.js.map": "assets/javascripts/worker/search.926ffd9e.min.js.map",
"assets/stylesheets/app-palette.scss": "assets/stylesheets/app-palette.3f90c815.min.css",

View File

@ -190,7 +190,7 @@
{% endblock %}
</div>
{% block scripts %}
<script src="{{ 'assets/javascripts/bundle.dcf1ce56.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.14f179c1.min.js' | url }}"></script>
{%- set translations = {} -%}
{%- for key in [
"clipboard.copy",

View File

@ -51,7 +51,8 @@ import {
pluck,
debounceTime,
distinctUntilKeyChanged,
distinctUntilChanged
distinctUntilChanged,
bufferCount
} from "rxjs/operators"
import {
@ -310,7 +311,7 @@ export function initialize(config: unknown) {
/**
* Location change
*/
interface LocationChange {
interface State {
url: URL // TODO: use URL!?
data?: ViewportOffset
}
@ -323,8 +324,8 @@ export function initialize(config: unknown) {
return el.hash.length > 0
}
function compareLocationChange(
{ url: a }: LocationChange, { url: b }: LocationChange
function compareState(
{ url: a }: State, { url: b }: State
) {
return a.href === b.href
}
@ -360,7 +361,7 @@ export function initialize(config: unknown) {
return NEVER
}),
distinctUntilChanged(),
map<string, LocationChange>(href => ({ url: new URL(href) })),
map<string, State>(href => ({ url: new URL(href) })),
share()
)
@ -374,8 +375,9 @@ export function initialize(config: unknown) {
/* Intercept popstate events (history back and forward) */
const popstate$ = fromEvent<PopStateEvent>(window, "popstate")
.pipe(
map<PopStateEvent, LocationChange>(ev => ({
url: new URL(getLocation()),
filter(ev => ev.state !== null),
map<PopStateEvent, State>(ev => ({
url: new URL(location.href),
data: ev.state
})),
share()
@ -391,7 +393,8 @@ export function initialize(config: unknown) {
/* Add dispatched link to history */
internal$
.pipe(
distinctUntilChanged(compareLocationChange),
// TODO: must start with the current location and ignore the first emission
distinctUntilChanged(compareState),
filter(({ url }) => !isAnchorLink(url))
)
.subscribe(({ url }) => {
@ -399,6 +402,41 @@ export function initialize(config: unknown) {
history.pushState({}, "", url.toString())
})
// special case
merge(internal$, popstate$)
.pipe(
bufferCount(2, 1),
// filter(([prev, next]) => {
// return prev.url.href.match(next.url.href) !== null
// && isAnchorLink(prev.url)
// })
)
.subscribe(([prev, next]) => {
console.log(`<- ${prev.url}`)
console.log(`-> ${next.url}`)
if (
prev.url.href.match(next.url.href) !== null &&
isAnchorLink(prev.url)
) {
dialog$.next(`Potential Candidate: ${JSON.stringify(next.data)}`, ) // awesome debugging.
setViewportOffset(next.data || { y: 0 })
}
// console.log("Potential Candidate")
})
// .subscribe((x) => console.log(x[0].url.toString(), x[1].url.toString()))
// filter(([prev, next]) => {
// return prev.url.href.match(next.url.href) !== null
// && isAnchorLink(prev.url)
// }),
// map(([, next]) => next)
// // distinctUntilChanged(compareLocationChange),
// // filter(({ url }) => !isAnchorLink(url))
// )
// .subscribe(({ url }) => {
// console.log(`Restore ${url}`)
// })
/* Persist viewport offset in history before hash change */
viewport$
.pipe(
@ -406,29 +444,18 @@ export function initialize(config: unknown) {
distinctUntilKeyChanged("offset"),
)
.subscribe(({ offset }) => {
console.log("Update", offset)
// console.log("Update", offset)
history.replaceState(offset, "")
})
// /* Edge case: go back from anchor to same
// // // TODO: better to just replace the state when this is encountered.
// // pop$
// // .pipe(
// // filter(({ href }) => !/#/.test(href)) // TODO: kind of sucks
// // )
// // .subscribe(({ data }) => {
// // // console.log("Detected", data) // detects too much...
// // setViewportOffset(data || { y: 0 }) // TOOD: must wait for document sample!
// // })
/* */
merge(dispatch$, popstate$)
.pipe(
sample(document$),
withLatestFrom(document$),
)
.subscribe(([{ url: href, data }, { title, head }]) => {
console.log("Done", href.href, data)
.subscribe(([{ url, data }, { title, head }]) => {
console.log("Done", url.href, data)
// setDocumentTitle
document.title = title
@ -471,11 +498,11 @@ export function initialize(config: unknown) {
// })
// dispatch$.subscribe(({ url }) => {
// console.log(`Push ${url}`)
// console.log(`Dispatch ${url}`)
// })
popstate$.subscribe(({ url }) => {
console.log(`Pop ${url}`)
console.log(`Popstate ${url.href}`, url)
})
}

View File

@ -24,9 +24,7 @@ import { NEVER, Observable } from "rxjs"
import { ajax } from "rxjs/ajax"
import {
catchError,
distinctUntilChanged,
filter,
map,
distinctUntilKeyChanged,
pluck,
share,
skip,
@ -54,7 +52,7 @@ interface WatchOptions {
/**
* Watch document switch
*
* This function returns an observables that fetches a document if the provided
* This function returns an observables that fetches a document if the provided // TODO: update docs
* location observable emits a new value (i.e. URL). If the emitted URL points
* to the same page, the request is effectively ignored (i.e. when only the
* fragment identifier changes).
@ -70,22 +68,20 @@ export function watchDocumentSwitch(
): Observable<Document> {
return location$
.pipe(
startWith(location), // TODO: getLocation should return URL or Location
filter(url => url.hash.length === 0), // use isAnchorLink
map(url => url.href),
distinctUntilChanged(),
startWith(location), // TODO: getLocation should return URL or Location
distinctUntilKeyChanged("pathname"),
skip(1),
/* Fetch document */
switchMap(url => ajax({
url,
url: url.href,
responseType: "document",
withCredentials: true
})
.pipe<Document, Document>(
pluck("response"),
catchError(() => {
setLocation(url)
setLocation(url.href) // TODO: setLocation should accept URL or location
return NEVER
})
)