Implemented full page diffs

This commit is contained in:
Eduard Urbach 2017-07-19 05:23:06 +02:00
parent 647aed0e76
commit 197ef0197a
3 changed files with 55 additions and 11 deletions

View File

@ -108,9 +108,6 @@ export class AnimeNotifier {
// Let"s start // Let"s start
this.app.run() this.app.run()
// Service worker
this.registerServiceWorker()
// Push manager // Push manager
this.pushManager = new PushManager() this.pushManager = new PushManager()
} }
@ -158,8 +155,13 @@ export class AnimeNotifier {
} }
onIdle() { onIdle() {
// Service worker
this.registerServiceWorker()
// Analytics
this.pushAnalytics() this.pushAnalytics()
// Offline message
if(navigator.onLine === false) { if(navigator.onLine === false) {
this.statusMessage.showError("You are viewing an offline version of the site now.") this.statusMessage.showError("You are viewing an offline version of the site now.")
} }
@ -170,6 +172,8 @@ export class AnimeNotifier {
return return
} }
console.log("register service worker")
navigator.serviceWorker.register("/service-worker").then(registration => { navigator.serviceWorker.register("/service-worker").then(registration => {
registration.update() registration.update()
}) })
@ -178,7 +182,7 @@ export class AnimeNotifier {
this.onServiceWorkerMessage(evt) this.onServiceWorkerMessage(evt)
}) })
document.addEventListener("DOMContentLoaded", () => { let sendContentLoadedEvent = () => {
if(!navigator.serviceWorker.controller) { if(!navigator.serviceWorker.controller) {
return return
} }
@ -194,8 +198,18 @@ export class AnimeNotifier {
message.url = window.location.href message.url = window.location.href
} }
console.log("send loaded event to service worker")
navigator.serviceWorker.controller.postMessage(JSON.stringify(message)) navigator.serviceWorker.controller.postMessage(JSON.stringify(message))
}) }
// For future loaded events
document.addEventListener("DOMContentLoaded", sendContentLoadedEvent)
// If the page is loaded already, send the loaded event right now.
if(document.readyState !== "loading") {
sendContentLoadedEvent()
}
} }
onServiceWorkerMessage(evt: ServiceWorkerMessageEvent) { onServiceWorkerMessage(evt: ServiceWorkerMessageEvent) {
@ -307,7 +321,6 @@ export class AnimeNotifier {
return Promise.reject("old request") return Promise.reject("old request")
} }
this.app.eTag = response.headers.get("ETag")
return Promise.resolve(response) return Promise.resolve(response)
}) })
.then(response => response.text()) .then(response => response.text())
@ -316,7 +329,29 @@ export class AnimeNotifier {
} }
reloadPage() { reloadPage() {
location.reload() console.log("reload page")
let headers = new Headers()
headers.append("X-Reload", "true")
let path = this.app.currentPath
return fetch(path, {
credentials: "same-origin",
headers
})
.then(response => {
if(this.app.currentPath !== path) {
return Promise.reject("old request")
}
return Promise.resolve(response)
})
.then(response => response.text())
.then(html => {
Diff.root(document.documentElement, html)
})
.then(() => this.app.emit("DOMContentLoaded"))
} }
loading(isLoading: boolean) { loading(isLoading: boolean) {
@ -477,7 +512,6 @@ export class AnimeNotifier {
credentials: "same-origin" credentials: "same-origin"
}) })
.then(response => { .then(response => {
this.app.eTag = response.headers.get("ETag")
return response.text() return response.text()
}) })

View File

@ -13,7 +13,6 @@ export class Application {
loading: HTMLElement loading: HTMLElement
currentPath: string currentPath: string
originalPath: string originalPath: string
eTag: string
lastRequest: XMLHttpRequest lastRequest: XMLHttpRequest
constructor() { constructor() {
@ -46,8 +45,6 @@ export class Application {
request.onerror = () => reject(new Error("You are either offline or the requested page doesn't exist.")) request.onerror = () => reject(new Error("You are either offline or the requested page doesn't exist."))
request.ontimeout = () => reject(new Error("The page took too much time to respond.")) request.ontimeout = () => reject(new Error("The page took too much time to respond."))
request.onload = () => { request.onload = () => {
this.eTag = request.getResponseHeader("ETag")
if(request.status < 200 || request.status >= 400) if(request.status < 200 || request.status >= 400)
reject(request.responseText) reject(request.responseText)
else else

View File

@ -3,6 +3,7 @@ export class Diff {
// Reuse container for diffs to avoid memory allocation // Reuse container for diffs to avoid memory allocation
static container: HTMLElement static container: HTMLElement
static rootContainer: HTMLElement
// innerHTML will diff the element with the given HTML string and apply DOM mutations. // innerHTML will diff the element with the given HTML string and apply DOM mutations.
static innerHTML(aRoot: HTMLElement, html: string) { static innerHTML(aRoot: HTMLElement, html: string) {
@ -14,6 +15,18 @@ export class Diff {
Diff.childNodes(aRoot, Diff.container) Diff.childNodes(aRoot, Diff.container)
} }
// root will diff the document root element with the given HTML string and apply DOM mutations.
static root(aRoot: HTMLElement, html: string) {
if(!Diff.rootContainer) {
Diff.rootContainer = document.createElement("html")
}
Diff.rootContainer.innerHTML = html.replace("<!DOCTYPE html>", "")
console.log(Diff.rootContainer)
Diff.childNodes(aRoot, Diff.rootContainer)
}
// childNodes diffs the child nodes of 2 given elements and applies DOM mutations. // childNodes diffs the child nodes of 2 given elements and applies DOM mutations.
static childNodes(aRoot: Node, bRoot: Node) { static childNodes(aRoot: Node, bRoot: Node) {
let aChild = [...aRoot.childNodes] let aChild = [...aRoot.childNodes]