mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 03:25:12 +01:00
reduce navigation boilerplate
this makes the url router adjust the url when the navigation path is changed, instead of doing urlRouter.applyUrl() and urlRouter.history.pushUrl(). This history field and applyUrl method on URLRouter are now private, as the URLRouter should only be used to generate urls you want to put in an <a href="..."></a>, anything else should use navigator.push()
This commit is contained in:
parent
ddf7d01760
commit
788bce7904
@ -35,13 +35,13 @@ export class RootViewModel extends ViewModel {
|
||||
this._sessionViewModel = null;
|
||||
}
|
||||
|
||||
async load(lastUrlHash) {
|
||||
async load() {
|
||||
this.track(this.navigation.observe("login").subscribe(() => this._applyNavigation()));
|
||||
this.track(this.navigation.observe("session").subscribe(() => this._applyNavigation()));
|
||||
this._applyNavigation(lastUrlHash);
|
||||
this._applyNavigation(this.urlRouter.getLastUrl());
|
||||
}
|
||||
|
||||
async _applyNavigation(restoreHashIfAtDefault) {
|
||||
async _applyNavigation(restoreUrlIfAtDefault) {
|
||||
const isLogin = this.navigation.observe("login").get();
|
||||
const sessionId = this.navigation.observe("session").get();
|
||||
if (isLogin) {
|
||||
@ -58,30 +58,24 @@ export class RootViewModel extends ViewModel {
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
let url = restoreHashIfAtDefault;
|
||||
if (!url) {
|
||||
// redirect depending on what sessions are already present
|
||||
if (restoreUrlIfAtDefault) {
|
||||
this.urlRouter.pushUrl(restoreUrlIfAtDefault);
|
||||
} else {
|
||||
const sessionInfos = await this._sessionInfoStorage.getAll();
|
||||
url = this._urlForSessionInfos(sessionInfos);
|
||||
if (sessionInfos.length === 0) {
|
||||
this.navigation.push("login");
|
||||
} else if (sessionInfos.length === 1) {
|
||||
this.navigation.push("session", sessionInfos[0].id);
|
||||
} else {
|
||||
this.navigation.push("session");
|
||||
}
|
||||
}
|
||||
this.urlRouter.history.replaceUrl(url);
|
||||
this.urlRouter.applyUrl(url);
|
||||
} catch (err) {
|
||||
this._setSection(() => this._error = err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_urlForSessionInfos(sessionInfos) {
|
||||
if (sessionInfos.length === 0) {
|
||||
return this.urlRouter.urlForSegment("login");
|
||||
} else if (sessionInfos.length === 1) {
|
||||
return this.urlRouter.urlForSegment("session", sessionInfos[0].id);
|
||||
} else {
|
||||
return this.urlRouter.urlForSegment("session");
|
||||
}
|
||||
}
|
||||
|
||||
async _showPicker() {
|
||||
this._setSection(() => {
|
||||
this._sessionPickerViewModel = new SessionPickerViewModel(this.childOptions({
|
||||
@ -102,10 +96,8 @@ export class RootViewModel extends ViewModel {
|
||||
defaultHomeServer: "https://matrix.org",
|
||||
createSessionContainer: this._createSessionContainer,
|
||||
ready: sessionContainer => {
|
||||
const url = this.urlRouter.urlForSegment("session", sessionContainer.sessionId);
|
||||
this.urlRouter.applyUrl(url);
|
||||
this.urlRouter.history.replaceUrl(url);
|
||||
this._showSession(sessionContainer);
|
||||
this.navigation.push("session", sessionContainer.sessionId);
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
@ -14,19 +14,28 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseObservableValue} from "../../observable/ObservableValue.js";
|
||||
import {BaseObservableValue, ObservableValue} from "../../observable/ObservableValue.js";
|
||||
|
||||
export class Navigation {
|
||||
constructor(allowsChild) {
|
||||
this._allowsChild = allowsChild;
|
||||
this._path = new Path([], allowsChild);
|
||||
this._observables = new Map();
|
||||
this._pathObservable = new ObservableValue(this._path);
|
||||
}
|
||||
|
||||
get pathObservable() {
|
||||
return this._pathObservable;
|
||||
}
|
||||
|
||||
get path() {
|
||||
return this._path;
|
||||
}
|
||||
|
||||
push(type, value = undefined) {
|
||||
return this.applyPath(this.path.with(new Segment(type, value)));
|
||||
}
|
||||
|
||||
applyPath(path) {
|
||||
// Path is not exported, so you can only create a Path through Navigation,
|
||||
// so we assume it respects the allowsChild rules
|
||||
@ -45,6 +54,10 @@ export class Navigation {
|
||||
const observable = this._observables.get(segment.type);
|
||||
observable?.emitIfChanged();
|
||||
}
|
||||
// to observe the whole path having changed
|
||||
// Since paths are immutable,
|
||||
// we can just use set here which will compare the references
|
||||
this._pathObservable.set(this._path);
|
||||
}
|
||||
|
||||
observe(type) {
|
||||
|
@ -14,40 +14,50 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {Segment} from "./Navigation.js";
|
||||
|
||||
export class URLRouter {
|
||||
constructor({history, navigation, parseUrlPath, stringifyPath}) {
|
||||
this._subscription = null;
|
||||
this._history = history;
|
||||
this._navigation = navigation;
|
||||
this._parseUrlPath = parseUrlPath;
|
||||
this._stringifyPath = stringifyPath;
|
||||
this._subscription = null;
|
||||
this._pathSubscription = null;
|
||||
}
|
||||
|
||||
attach() {
|
||||
this._subscription = this._history.subscribe(url => {
|
||||
const redirectedUrl = this.applyUrl(url);
|
||||
const redirectedUrl = this._applyUrl(url);
|
||||
if (redirectedUrl !== url) {
|
||||
this._history.replaceUrl(redirectedUrl);
|
||||
this._history.replaceUrlSilently(redirectedUrl);
|
||||
}
|
||||
});
|
||||
this._applyUrl(this._history.get());
|
||||
this._pathSubscription = this._navigation.pathObservable.subscribe(path => {
|
||||
const url = this.urlForPath(path);
|
||||
if (url !== this._history.get()) {
|
||||
this._history.pushUrlSilently(url);
|
||||
}
|
||||
});
|
||||
this.applyUrl(this._history.get());
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._subscription = this._subscription();
|
||||
this._pathSubscription = this._pathSubscription();
|
||||
}
|
||||
|
||||
applyUrl(url) {
|
||||
_applyUrl(url) {
|
||||
const urlPath = this._history.urlAsPath(url)
|
||||
const navPath = this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path));
|
||||
this._navigation.applyPath(navPath);
|
||||
return this._history.pathAsUrl(this._stringifyPath(navPath));
|
||||
}
|
||||
|
||||
get history() {
|
||||
return this._history;
|
||||
pushUrl(url) {
|
||||
this._history.pushUrl(url);
|
||||
}
|
||||
|
||||
getLastUrl() {
|
||||
return this._history.getLastUrl();
|
||||
}
|
||||
|
||||
urlForSegments(segments) {
|
||||
@ -70,7 +80,7 @@ export class URLRouter {
|
||||
}
|
||||
|
||||
urlForPath(path) {
|
||||
return this.history.pathAsUrl(this._stringifyPath(path));
|
||||
return this._history.pathAsUrl(this._stringifyPath(path));
|
||||
}
|
||||
|
||||
openRoomActionUrl(roomId) {
|
||||
@ -78,26 +88,4 @@ export class URLRouter {
|
||||
const urlPath = `${this._stringifyPath(this._navigation.path.until("session"))}/open-room/${roomId}`;
|
||||
return this._history.pathAsUrl(urlPath);
|
||||
}
|
||||
|
||||
disableGridUrl() {
|
||||
let path = this._navigation.path.until("session");
|
||||
const room = this._navigation.path.get("room");
|
||||
if (room) {
|
||||
path = path.with(room);
|
||||
}
|
||||
return this.urlForPath(path);
|
||||
}
|
||||
|
||||
enableGridUrl() {
|
||||
let path = this._navigation.path.until("session");
|
||||
const room = this._navigation.path.get("room");
|
||||
if (room) {
|
||||
path = path.with(this._navigation.segment("rooms", [room.value]));
|
||||
path = path.with(room);
|
||||
} else {
|
||||
path = path.with(this._navigation.segment("rooms", []));
|
||||
path = path.with(this._navigation.segment("empty-grid-tile", 0));
|
||||
}
|
||||
return this.urlForPath(path);
|
||||
}
|
||||
}
|
||||
|
@ -83,16 +83,12 @@ export class RoomGridViewModel extends ViewModel {
|
||||
if (index === this._selectedIndex) {
|
||||
return;
|
||||
}
|
||||
let path = this.navigation.path;
|
||||
const vm = this._viewModels[index];
|
||||
if (vm) {
|
||||
path = path.with(this.navigation.segment("room", vm.id));
|
||||
this.navigation.push("room", vm.id);
|
||||
} else {
|
||||
path = path.with(this.navigation.segment("empty-grid-tile", index));
|
||||
this.navigation.push("empty-grid-tile", index);
|
||||
}
|
||||
let url = this.urlRouter.urlForPath(path);
|
||||
url = this.urlRouter.applyUrl(url);
|
||||
this.urlRouter.history.pushUrl(url);
|
||||
}
|
||||
|
||||
/** called from SessionViewModel */
|
||||
|
@ -76,14 +76,25 @@ export class LeftPanelViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
toggleGrid() {
|
||||
let url;
|
||||
if (this.gridEnabled) {
|
||||
url = this.urlRouter.disableGridUrl();
|
||||
} else {
|
||||
url = this.urlRouter.enableGridUrl();
|
||||
let path = this.navigation.path.until("session");
|
||||
const room = this.navigation.path.get("room");
|
||||
if (room) {
|
||||
path = path.with(room);
|
||||
}
|
||||
this.navigation.applyPath(path);
|
||||
} else {
|
||||
let path = this.navigation.path.until("session");
|
||||
const room = this.navigation.path.get("room");
|
||||
if (room) {
|
||||
path = path.with(this.navigation.segment("rooms", [room.value]));
|
||||
path = path.with(room);
|
||||
} else {
|
||||
path = path.with(this.navigation.segment("rooms", []));
|
||||
path = path.with(this.navigation.segment("empty-grid-tile", 0));
|
||||
}
|
||||
this.navigation.applyPath(path);
|
||||
}
|
||||
url = this.urlRouter.applyUrl(url);
|
||||
this.urlRouter.history.pushUrl(url);
|
||||
}
|
||||
|
||||
get roomList() {
|
||||
|
@ -118,8 +118,7 @@ export async function main(container, paths, legacyExtras) {
|
||||
}
|
||||
|
||||
const navigation = createNavigation();
|
||||
const history = new History();
|
||||
const urlRouter = createRouter({navigation, history});
|
||||
const urlRouter = createRouter({navigation, history: new History()});
|
||||
urlRouter.attach();
|
||||
|
||||
const vm = new RootViewModel({
|
||||
@ -143,7 +142,7 @@ export async function main(container, paths, legacyExtras) {
|
||||
navigation
|
||||
});
|
||||
window.__brawlViewModel = vm;
|
||||
await vm.load(history.getLastUrl());
|
||||
await vm.load();
|
||||
// TODO: replace with platform.createAndMountRootView(vm, container);
|
||||
const view = new RootView(vm);
|
||||
container.appendChild(view.mount());
|
||||
|
@ -20,14 +20,9 @@ export class History extends BaseObservableValue {
|
||||
constructor() {
|
||||
super();
|
||||
this._boundOnHashChange = null;
|
||||
this._expectSetEcho = false;
|
||||
}
|
||||
|
||||
_onHashChange() {
|
||||
if (this._expectSetEcho) {
|
||||
this._expectSetEcho = false;
|
||||
return;
|
||||
}
|
||||
this.emit(this.get());
|
||||
this._storeHash(this.get());
|
||||
}
|
||||
@ -37,28 +32,19 @@ export class History extends BaseObservableValue {
|
||||
}
|
||||
|
||||
/** does not emit */
|
||||
replaceUrl(url) {
|
||||
replaceUrlSilently(url) {
|
||||
window.history.replaceState(null, null, url);
|
||||
this._storeHash(url);
|
||||
}
|
||||
|
||||
/** does not emit */
|
||||
pushUrl(url) {
|
||||
pushUrlSilently(url) {
|
||||
window.history.pushState(null, null, url);
|
||||
this._storeHash(url);
|
||||
// const hash = this.urlAsPath(url);
|
||||
// // important to check before we expect an echo
|
||||
// // as setting the hash to it's current value doesn't
|
||||
// // trigger onhashchange
|
||||
// if (hash === document.location.hash) {
|
||||
// return;
|
||||
// }
|
||||
// // this operation is silent,
|
||||
// // so avoid emitting on echo hashchange event
|
||||
// if (this._boundOnHashChange) {
|
||||
// this._expectSetEcho = true;
|
||||
// }
|
||||
// document.location.hash = hash;
|
||||
}
|
||||
|
||||
pushUrl(url) {
|
||||
document.location.hash = url;
|
||||
}
|
||||
|
||||
urlAsPath(url) {
|
||||
|
Loading…
Reference in New Issue
Block a user