Move spec permalinks into their own class

This allows for Riot permalinks to be introduced without if-else ladders everywhere.
This commit is contained in:
Travis Ralston 2019-09-30 12:51:17 -06:00
parent f9d5e89017
commit 926e1146f9
4 changed files with 93 additions and 31 deletions

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {baseUrl} from "./utils/permalinks/SpecPermalinks"; import {baseUrl} from "./utils/permalinks/SpecPermalinkConstructor";
function matrixLinkify(linkify) { function matrixLinkify(linkify) {
// Text tokens // Text tokens

View file

@ -14,15 +14,24 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export const host = "matrix.to";
export const baseUrl = `https://${host}`;
/** /**
* Generates matrix.to permalinks * Interface for classes that actually produce permalinks (strings).
* TODO: Convert this to a real TypeScript interface
*/ */
export default class SpecPermalinks { export default class PermalinkConstructor {
constructor() { forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
throw new Error("Not implemented");
} }
// TODO: The class forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
throw new Error("Not implemented");
}
forGroup(groupId: string): string {
throw new Error("Not implemented");
}
forUser(userId: string): string {
throw new Error("Not implemented");
}
} }

View file

@ -17,7 +17,13 @@ limitations under the License.
import MatrixClientPeg from "../../MatrixClientPeg"; import MatrixClientPeg from "../../MatrixClientPeg";
import isIp from "is-ip"; import isIp from "is-ip";
import utils from 'matrix-js-sdk/lib/utils'; import utils from 'matrix-js-sdk/lib/utils';
import {host as matrixtoHost, baseUrl as matrixtoBaseUrl} from "SpecPermalinks"; import SpecPermalinkConstructor, {
baseUrl as matrixtoBaseUrl,
host as matrixtoHost
} from "./SpecPermalinkConstructor";
import PermalinkConstructor from "./PermalinkConstructor";
const SdkConfig = require("../../SdkConfig");
// The maximum number of servers to pick when working out which servers // The maximum number of servers to pick when working out which servers
// to add to permalinks. The servers are appended as ?via=example.org // to add to permalinks. The servers are appended as ?via=example.org
@ -71,7 +77,7 @@ export class RoomPermalinkCreator {
// We support being given a roomId as a fallback in the event the `room` object // We support being given a roomId as a fallback in the event the `room` object
// doesn't exist or is not healthy for us to rely on. For example, loading a // doesn't exist or is not healthy for us to rely on. For example, loading a
// permalink to a room which the MatrixClient doesn't know about. // permalink to a room which the MatrixClient doesn't know about.
constructor(room, roomId=null) { constructor(room, roomId = null) {
this._room = room; this._room = room;
this._roomId = room ? room.roomId : roomId; this._roomId = room ? room.roomId : roomId;
this._highestPlUserId = null; this._highestPlUserId = null;
@ -126,15 +132,11 @@ export class RoomPermalinkCreator {
} }
forEvent(eventId) { forEvent(eventId) {
const roomId = this._roomId; return getPermalinkConstructor().forEvent(this._roomId, eventId, this._serverCandidates);
const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}/${eventId}`;
return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
} }
forRoom() { forRoom() {
const roomId = this._roomId; return getPermalinkConstructor().forRoom(this._roomId, this._serverCandidates);
const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}`;
return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
} }
onRoomState(event) { onRoomState(event) {
@ -184,8 +186,8 @@ export class RoomPermalinkCreator {
} }
const serverName = getServerName(userId); const serverName = getServerName(userId);
return !isHostnameIpAddress(serverName) && return !isHostnameIpAddress(serverName) &&
!isHostInRegex(serverName, this._bannedHostsRegexps) && !isHostInRegex(serverName, this._bannedHostsRegexps) &&
isHostInRegex(serverName, this._allowedHostsRegexps); isHostInRegex(serverName, this._allowedHostsRegexps);
}); });
const maxEntry = allowedEntries.reduce((max, entry) => { const maxEntry = allowedEntries.reduce((max, entry) => {
return (entry[1] > max[1]) ? entry : max; return (entry[1] > max[1]) ? entry : max;
@ -223,7 +225,7 @@ export class RoomPermalinkCreator {
} }
_updatePopulationMap() { _updatePopulationMap() {
const populationMap: {[server:string]:number} = {}; const populationMap: { [server: string]: number } = {};
for (const member of this._room.getJoinedMembers()) { for (const member of this._room.getJoinedMembers()) {
const serverName = getServerName(member.userId); const serverName = getServerName(member.userId);
if (!populationMap[serverName]) { if (!populationMap[serverName]) {
@ -244,9 +246,9 @@ export class RoomPermalinkCreator {
.sort((a, b) => this._populationMap[b] - this._populationMap[a]) .sort((a, b) => this._populationMap[b] - this._populationMap[a])
.filter(a => { .filter(a => {
return !candidates.includes(a) && return !candidates.includes(a) &&
!isHostnameIpAddress(a) && !isHostnameIpAddress(a) &&
!isHostInRegex(a, this._bannedHostsRegexps) && !isHostInRegex(a, this._bannedHostsRegexps) &&
isHostInRegex(a, this._allowedHostsRegexps); isHostInRegex(a, this._allowedHostsRegexps);
}); });
const remainingServers = serversByPopulation.slice(0, MAX_SERVER_CANDIDATES - candidates.length); const remainingServers = serversByPopulation.slice(0, MAX_SERVER_CANDIDATES - candidates.length);
@ -257,24 +259,22 @@ export class RoomPermalinkCreator {
} }
export function makeUserPermalink(userId) { export function makeUserPermalink(userId) {
return `${matrixtoBaseUrl}/#/${userId}`; return getPermalinkConstructor().forUser(userId);
} }
export function makeRoomPermalink(roomId) { export function makeRoomPermalink(roomId) {
const permalinkBase = `${matrixtoBaseUrl}/#/${roomId}`;
if (!roomId) { if (!roomId) {
throw new Error("can't permalink a falsey roomId"); throw new Error("can't permalink a falsey roomId");
} }
// If the roomId isn't actually a room ID, don't try to list the servers. // If the roomId isn't actually a room ID, don't try to list the servers.
// Aliases are already routable, and don't need extra information. // Aliases are already routable, and don't need extra information.
if (roomId[0] !== '!') return permalinkBase; if (roomId[0] !== '!') return getPermalinkConstructor().forRoom(roomId, []);
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const room = client.getRoom(roomId); const room = client.getRoom(roomId);
if (!room) { if (!room) {
return permalinkBase; return getPermalinkConstructor().forRoom(roomId, []);
} }
const permalinkCreator = new RoomPermalinkCreator(room); const permalinkCreator = new RoomPermalinkCreator(room);
permalinkCreator.load(); permalinkCreator.load();
@ -282,12 +282,15 @@ export function makeRoomPermalink(roomId) {
} }
export function makeGroupPermalink(groupId) { export function makeGroupPermalink(groupId) {
return `${matrixtoBaseUrl}/#/${groupId}`; return getPermalinkConstructor().forGroup(groupId);
} }
export function encodeServerCandidates(candidates) { function getPermalinkConstructor(): PermalinkConstructor {
if (!candidates || candidates.length === 0) return ''; if (SdkConfig.get()['useRiotToCreatePermalinks']) {
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; // TODO: Return a RiotPermalinkConstructor
}
return new SpecPermalinkConstructor();
} }
function getServerName(userId) { function getServerName(userId) {

View file

@ -0,0 +1,50 @@
/*
Copyright 2019 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 PermalinkConstructor from "./PermalinkConstructor";
export const host = "matrix.to";
export const baseUrl = `https://${host}`;
/**
* Generates matrix.to permalinks
*/
export default class SpecPermalinkConstructor extends PermalinkConstructor {
constructor() {
super();
}
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
return `${baseUrl}/#/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`;
}
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
return `${baseUrl}/#/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`;
}
forUser(userId: string): string {
return `${baseUrl}/#/${userId}`;
}
forGroup(groupId: string): string {
return `${baseUrl}/#/${groupId}`;
}
encodeServerCandidates(candidates: string[]) {
if (!candidates || candidates.length === 0) return '';
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
}
}