Skip to content
Snippets Groups Projects
Unverified Commit a460b52e authored by Bruno Windels's avatar Bruno Windels Committed by GitHub
Browse files

Merge pull request #228 from matrix-org/danilafe/invalid-url

Display an error when a junk URL is entered, and provide suggestions.
parents 874635e9 38028295
No related branches found
No related tags found
No related merge requests found
/*
Copyright 2021 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 {TemplateView} from "./utils/TemplateView.js";
import {LinkKind, IdentifierKind} from "./Link.js";
export class InvalidUrlView extends TemplateView {
render(t, vm) {
return t.div({ className: "DisclaimerView card" }, [
t.h1("Invalid URL"),
t.p([
'The link you have entered is not valid. If you like, you can ',
t.a({ href: "#/" }, 'return to the home page.')
]),
vm.validFixes.length ? this._renderValidFixes(t, vm.validFixes) : [],
]);
}
_describeRoom(identifierKind) {
return identifierKind === IdentifierKind.RoomAlias ? "room alias" : "room";
}
_describeLinkKind(linkKind, identifierKind) {
switch (linkKind) {
case LinkKind.Room: return `The ${this._describeRoom(identifierKind)} `;
case LinkKind.User: return "The user ";
case LinkKind.Group: return "The group ";
case LinkKind.Event: return `An event in ${this._describeRoom(identifierKind)} `;
}
}
_renderValidFixes(t, validFixes) {
return t.p([
'Did you mean any of the following?',
t.ul(validFixes.map(fix =>
t.li([
this._describeLinkKind(fix.link.kind, fix.link.identifierKind),
t.a({ href: fix.url }, fix.link.identifier)
])
))
]);
}
}
/*
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 {ViewModel} from "./utils/ViewModel.js";
import {tryFixUrl} from "./Link.js";
export class InvalidUrlViewModel extends ViewModel {
constructor(options) {
super(options);
this.validFixes = tryFixUrl(options.fragment);
}
}
...@@ -71,6 +71,23 @@ export const LinkKind = createEnum( ...@@ -71,6 +71,23 @@ export const LinkKind = createEnum(
"Event" "Event"
) )
export function tryFixUrl(fragment) {
const attempts = [];
const afterHash = fragment.substring(fragment.startsWith("#/") ? 2 : 1);
attempts.push('#/@' + afterHash);
attempts.push('#/#' + afterHash);
attempts.push('#/!' + afterHash);
const validAttempts = [];
for (const attempt of [...new Set(attempts)]) {
const link = Link.parse(attempt);
if (link) {
validAttempts.push({ url: attempt, link });
}
}
return validAttempts;
}
export class Link { export class Link {
static validateIdentifier(identifier) { static validateIdentifier(identifier) {
return !!( return !!(
...@@ -105,9 +122,10 @@ export class Link { ...@@ -105,9 +122,10 @@ export class Link {
webInstances = getWebInstanceMap(queryParams); webInstances = getWebInstanceMap(queryParams);
} }
if (linkStr.startsWith("#/")) { if (!linkStr.startsWith("#/")) {
linkStr = linkStr.substr(2); return null;
} }
linkStr = linkStr.substr(2);
const [identifier, eventId] = linkStr.split("/"); const [identifier, eventId] = linkStr.split("/");
......
...@@ -19,10 +19,12 @@ import {OpenLinkView} from "./open/OpenLinkView.js"; ...@@ -19,10 +19,12 @@ import {OpenLinkView} from "./open/OpenLinkView.js";
import {CreateLinkView} from "./create/CreateLinkView.js"; import {CreateLinkView} from "./create/CreateLinkView.js";
import {LoadServerPolicyView} from "./policy/LoadServerPolicyView.js"; import {LoadServerPolicyView} from "./policy/LoadServerPolicyView.js";
import {DisclaimerView} from "./disclaimer/DisclaimerView.js"; import {DisclaimerView} from "./disclaimer/DisclaimerView.js";
import {InvalidUrlView} from "./InvalidUrlView.js";
export class RootView extends TemplateView { export class RootView extends TemplateView {
render(t, vm) { render(t, vm) {
return t.div({className: "RootView"}, [ return t.div({className: "RootView"}, [
t.mapView(vm => vm.invalidUrlViewModel, invalidVM => invalidVM ? new InvalidUrlView(invalidVM) : null),
t.mapView(vm => vm.showDisclaimer, disclaimer => disclaimer ? new DisclaimerView() : null), t.mapView(vm => vm.showDisclaimer, disclaimer => disclaimer ? new DisclaimerView() : null),
t.mapView(vm => vm.openLinkViewModel, vm => vm ? new OpenLinkView(vm) : null), t.mapView(vm => vm.openLinkViewModel, vm => vm ? new OpenLinkView(vm) : null),
t.mapView(vm => vm.createLinkViewModel, vm => vm ? new CreateLinkView(vm) : null), t.mapView(vm => vm.createLinkViewModel, vm => vm ? new CreateLinkView(vm) : null),
......
...@@ -20,6 +20,7 @@ import {OpenLinkViewModel} from "./open/OpenLinkViewModel.js"; ...@@ -20,6 +20,7 @@ import {OpenLinkViewModel} from "./open/OpenLinkViewModel.js";
import {createClients} from "./open/clients/index.js"; import {createClients} from "./open/clients/index.js";
import {CreateLinkViewModel} from "./create/CreateLinkViewModel.js"; import {CreateLinkViewModel} from "./create/CreateLinkViewModel.js";
import {LoadServerPolicyViewModel} from "./policy/LoadServerPolicyViewModel.js"; import {LoadServerPolicyViewModel} from "./policy/LoadServerPolicyViewModel.js";
import {InvalidUrlViewModel} from "./InvalidUrlViewModel.js";
import {Platform} from "./Platform.js"; import {Platform} from "./Platform.js";
export class RootViewModel extends ViewModel { export class RootViewModel extends ViewModel {
...@@ -29,26 +30,23 @@ export class RootViewModel extends ViewModel { ...@@ -29,26 +30,23 @@ export class RootViewModel extends ViewModel {
this.openLinkViewModel = null; this.openLinkViewModel = null;
this.createLinkViewModel = null; this.createLinkViewModel = null;
this.loadServerPolicyViewModel = null; this.loadServerPolicyViewModel = null;
this.invalidUrlViewModel = null;
this.showDisclaimer = false; this.showDisclaimer = false;
this.preferences.on("canClear", () => { this.preferences.on("canClear", () => {
this.emitChange(); this.emitChange();
}); });
} }
_updateChildVMs(oldLink) { _updateChildVMs(newLink, oldLink) {
if (this.link) { this.link = newLink;
this.createLinkViewModel = null; if (!newLink) {
if (!oldLink || !oldLink.equals(this.link)) {
this.openLinkViewModel = new OpenLinkViewModel(this.childOptions({
link: this.link,
clients: createClients(),
}));
}
} else {
this.openLinkViewModel = null; this.openLinkViewModel = null;
this.createLinkViewModel = new CreateLinkViewModel(this.childOptions()); } else if (!oldLink || !oldLink.equals(newLink)) {
this.openLinkViewModel = new OpenLinkViewModel(this.childOptions({
link: newLink,
clients: createClients(),
}));
} }
this.emitChange();
} }
_hideLinks() { _hideLinks() {
...@@ -58,24 +56,35 @@ export class RootViewModel extends ViewModel { ...@@ -58,24 +56,35 @@ export class RootViewModel extends ViewModel {
} }
updateHash(hash) { updateHash(hash) {
// All view models except openLink are re-created anyway. Might as well
// clear them to avoid having to manually reset (n-1)/n view models in every case.
// That just doesn't scale well when we add new views.
const oldLink = this.link;
this.invalidUrlViewModel = null;
this.showDisclaimer = false; this.showDisclaimer = false;
this.loadServerPolicyViewModel = null;
this.createLinkViewModel = null;
let newLink;
if (hash.startsWith("#/policy/")) { if (hash.startsWith("#/policy/")) {
const server = hash.substr(9); const server = hash.substr(9);
this._hideLinks(); this._updateChildVMs(null, oldLink);
this.loadServerPolicyViewModel = new LoadServerPolicyViewModel(this.childOptions({server})); this.loadServerPolicyViewModel = new LoadServerPolicyViewModel(this.childOptions({server}));
this.loadServerPolicyViewModel.load(); this.loadServerPolicyViewModel.load();
this.emitChange();
} else if (hash.startsWith("#/disclaimer/")) { } else if (hash.startsWith("#/disclaimer/")) {
this._hideLinks(); this._updateChildVMs(null, oldLink);
this.loadServerPolicyViewModel = null;
this.showDisclaimer = true; this.showDisclaimer = true;
this.emitChange(); } else if (hash === "" || hash === "#" || hash === "#/") {
this._updateChildVMs(null, oldLink);
this.createLinkViewModel = new CreateLinkViewModel(this.childOptions());
} else if (newLink = Link.parse(hash)) {
this._updateChildVMs(newLink, oldLink);
} else { } else {
const oldLink = this.link; this._updateChildVMs(null, oldLink);
this.loadServerPolicyViewModel = null; this.invalidUrlViewModel = new InvalidUrlViewModel(this.childOptions({
this.link = Link.parse(hash); fragment: hash
this._updateChildVMs(oldLink); }));
} }
this.emitChange();
} }
clearPreferences() { clearPreferences() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment