zulip/web/shared/src/internal_url.ts
roanster007 41f30e1052 topics: Change topic links of left sidebar to use new permalinks.
This commit updates the topic links obtained from clicking
the topics in the left sidebar, recent view and inbox, and
those obtained from "Copy link to topic" to use the new
topic permalinks.

Fixes part of #21505.
2025-02-12 16:21:03 -08:00

79 lines
2.4 KiB
TypeScript

type MaybeGetStreamName = (id: number) => string | undefined;
const hashReplacements = new Map([
["%", "."],
["(", ".28"],
[")", ".29"],
[".", ".2E"],
]);
// Some browsers zealously URI-decode the contents of
// window.location.hash. So we hide our URI-encoding
// by replacing % with . (like MediaWiki).
export function encodeHashComponent(str: string): string {
return encodeURIComponent(str).replace(/[%().]/g, (matched) => hashReplacements.get(matched)!);
}
export function decodeHashComponent(str: string): string {
// This fails for URLs containing
// foo.foo or foo%foo due to our fault in special handling
// of such characters when encoding. This can also,
// fail independent of our fault.
// Here we let the calling code handle the exception.
return decodeURIComponent(str.replace(/\./g, "%"));
}
export function stream_id_to_slug(
stream_id: number,
maybe_get_stream_name: MaybeGetStreamName,
): string {
let name = maybe_get_stream_name(stream_id) ?? "unknown";
// The name part of the URL doesn't really matter, so we try to
// make it pretty.
// TODO: Convert this to replaceAll once mobile no longer supports
// browsers that don't have it.
name = name.replace(/ /g, "-");
return `${stream_id}-${name}`;
}
export function encode_stream_id(
stream_id: number,
maybe_get_stream_name: MaybeGetStreamName,
): string {
// stream_id_to_slug appends the stream name, but it does not do the
// URI encoding piece.
const slug = stream_id_to_slug(stream_id, maybe_get_stream_name);
return encodeHashComponent(slug);
}
export function by_stream_url(
stream_id: number,
maybe_get_stream_name: MaybeGetStreamName,
): string {
return `#narrow/channel/${encode_stream_id(stream_id, maybe_get_stream_name)}`;
}
// The message_id parameter is used to obtain topic permalinks,
// by using it in a `with` operator.
export function by_stream_topic_url(
stream_id: number,
topic: string,
maybe_get_stream_name: MaybeGetStreamName,
message_id?: number,
): string {
if (message_id === undefined) {
return `#narrow/channel/${encode_stream_id(
stream_id,
maybe_get_stream_name,
)}/topic/${encodeHashComponent(topic)}`;
}
return `#narrow/channel/${encode_stream_id(
stream_id,
maybe_get_stream_name,
)}/topic/${encodeHashComponent(topic)}/with/${message_id}`;
}