diff --git a/src/domain/navigation/URLRouter.ts b/src/domain/navigation/URLRouter.ts index bf1c218d..23503530 100644 --- a/src/domain/navigation/URLRouter.ts +++ b/src/domain/navigation/URLRouter.ts @@ -144,7 +144,7 @@ export class URLRouter implements IURLRou openRoomActionUrl(roomId: string): string { // not a segment to navigation knowns about, so append it manually - const urlPath = `${this._stringifyPath(this._navigation.path.until("session"))}/open-room/${roomId}`; + const urlPath = `${this._stringifyPath(this._navigation.path.until("session"))}/open-room/${encodeURIComponent(roomId)}`; return this._history.pathAsUrl(urlPath); } diff --git a/src/domain/navigation/index.ts b/src/domain/navigation/index.ts index af3c35bd..f580206e 100644 --- a/src/domain/navigation/index.ts +++ b/src/domain/navigation/index.ts @@ -147,7 +147,7 @@ export function parseUrlPath(urlPath: string, currentNavPath: Path, segments.push(new Segment("empty-grid-tile", selectedIndex)); } } else if (type === "open-room") { - const roomId = iterator.next().value; + const roomId = decodeURIComponent(iterator.next().value); if (!roomId) { break; } const rooms = currentNavPath.get("rooms"); if (rooms) { @@ -176,7 +176,7 @@ export function parseUrlPath(urlPath: string, currentNavPath: Path, } else if (type === "details" || type === "members") { pushRightPanelSegment(segments, type); } else if (type === "member") { - const userId = iterator.next().value; + const userId = decodeURIComponent(iterator.next().value); if (!userId) { break; } pushRightPanelSegment(segments, type, userId); } else if (type.includes("loginToken")) { @@ -185,7 +185,7 @@ export function parseUrlPath(urlPath: string, currentNavPath: Path, segments.push(new Segment("sso", loginToken)); } else { // might be undefined, which will be turned into true by Segment - const value = iterator.next().value; + const value = decodeURIComponent(iterator.next().value); segments.push(new Segment(type, value)); } } @@ -196,19 +196,20 @@ export function stringifyPath(path: Path): string { let urlPath = ""; let prevSegment: Segment | undefined; for (const segment of path.segments) { + const encodedSegmentValue = encodeSegmentValue(segment.value); switch (segment.type) { case "rooms": - urlPath += `/rooms/${segment.value.join(",")}`; + urlPath += `/rooms/${(encodedSegmentValue as string[]).join(",")}`; break; case "empty-grid-tile": - urlPath += `/${segment.value}`; + urlPath += `/${encodedSegmentValue}`; break; case "room": if (prevSegment?.type === "rooms") { const index = prevSegment.value.indexOf(segment.value); urlPath += `/${index}`; } else { - urlPath += `/${segment.type}/${segment.value}`; + urlPath += `/${segment.type}/${encodedSegmentValue}`; } break; case "right-panel": @@ -217,8 +218,8 @@ export function stringifyPath(path: Path): string { continue; default: urlPath += `/${segment.type}`; - if (segment.value && segment.value !== true) { - urlPath += `/${segment.value}`; + if (encodedSegmentValue && encodedSegmentValue !== true) { + urlPath += `/${encodedSegmentValue}`; } } prevSegment = segment; @@ -226,6 +227,19 @@ export function stringifyPath(path: Path): string { return urlPath; } +function encodeSegmentValue(value: SegmentType[keyof SegmentType]) { + if (typeof value === "boolean") { + // Nothing to encode for boolean + return value; + } + else if (Array.isArray(value)) { + return value.map(v => encodeURIComponent(v)); + } + else { + return encodeURIComponent(value); + } +} + export function tests() { function createEmptyPath() { const nav: Navigation = new Navigation(allowsChild); diff --git a/src/domain/session/rightpanel/MemberTileViewModel.js b/src/domain/session/rightpanel/MemberTileViewModel.js index c8dcf63a..9f9a5483 100644 --- a/src/domain/session/rightpanel/MemberTileViewModel.js +++ b/src/domain/session/rightpanel/MemberTileViewModel.js @@ -48,7 +48,7 @@ export class MemberTileViewModel extends ViewModel { get detailsUrl() { const roomId = this.navigation.path.get("room").value; - return `${this.urlRouter.openRoomActionUrl(roomId)}/member/${this._member.userId}`; + return `${this.urlRouter.openRoomActionUrl(roomId)}/member/${encodeURIComponent(this._member.userId)}`; } _updatePreviousName(newName) {