From b8dcb249ffce44784051450923a5b9a2e9173a76 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 6 Oct 2020 18:03:12 +0200 Subject: [PATCH] implement url router --- src/domain/navigation/URLRouter.js | 74 ++++++++++++++++++++++++++++++ src/ui/web/dom/HashObservable.js | 59 ++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 src/domain/navigation/URLRouter.js create mode 100644 src/ui/web/dom/HashObservable.js diff --git a/src/domain/navigation/URLRouter.js b/src/domain/navigation/URLRouter.js new file mode 100644 index 00000000..5dcd9944 --- /dev/null +++ b/src/domain/navigation/URLRouter.js @@ -0,0 +1,74 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import {Segment} from "./Navigation.js"; + +export class URLRouter { + constructor(pathObservable, navigation) { + this._subscription = null; + this._pathObservable = pathObservable; + this._navigation = navigation; + } + + start() { + this._subscription = this._pathObservable.subscribe(url => { + const segments = this._segmentsFromUrl(url); + const path = this._navigation.pathFrom(segments); + this._navigation.applyPath(path); + }); + } + + stop() { + this._subscription = this._subscription(); + } + + _segmentsFromUrl(path) { + const parts = path.split("/"); + let index = 0; + const segments = []; + while (index < parts.length) { + const type = parts[index]; + if ((index + 1) < parts.length) { + index += 1; + const value = parts[index]; + segments.push(new Segment(type, value)); + } else { + segments.push(new Segment(type)); + } + index += 1; + } + return segments; + } + + urlForSegment(type, value) { + const path = this._navigation.path.with(new Segment(type, value)); + if (path) { + return this.urlForPath(path); + } + } + + urlForPath(path) { + let url = "#"; + for (const {type, value} of path.segments) { + if (typeof value === "boolean") { + url += `/${type}`; + } else { + url += `/${type}/${value}`; + } + } + return url; + } +} diff --git a/src/ui/web/dom/HashObservable.js b/src/ui/web/dom/HashObservable.js new file mode 100644 index 00000000..4239f6ac --- /dev/null +++ b/src/ui/web/dom/HashObservable.js @@ -0,0 +1,59 @@ +/* +Copyright 2020 Bruno Windels + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import {BaseObservableValue} from "../../../observable/ObservableValue.js"; + +export class HashObservable extends BaseObservableValue { + constructor() { + super(); + this._boundOnHashChange = null; + this._expectSetEcho = false; + } + + _onHashChange() { + if (this._expectSetEcho) { + this._expectSetEcho = false; + return; + } + this.emit(this.get()); + } + + get() { + const hash = document.location.hash; + if (hash.length) { + // trim '#' + return hash.substr(1); + } + return hash; + } + + set(hash) { + if (this._boundOnHashChange) { + this._expectSetEcho = true; + } + document.location.hash = hash; + } + + onSubscribeFirst() { + this._boundOnHashChange = this._onHashChange.bind(this); + window.addEventListener('hashchange', this._boundOnHashChange); + } + + onUnsubscribeLast() { + window.removeEventListener('hashchange', this._boundOnHashChange); + this._boundOnHashChange = null; + } +}