From 5a24dad5f857f84fad63c175505aa216f15b9ec3 Mon Sep 17 00:00:00 2001 From: Kislay Udbhav Verma Date: Sun, 27 Oct 2024 19:13:14 +0530 Subject: [PATCH] copy_and_paste: Transform message links to pretty syntax. Similar to #29302, we transform zulip message links to a pretty syntax and in case there are some problematic characters (#30071), we generate a fallback markdown link. Fixes #31920 --- web/src/copy_and_paste.ts | 17 +++++++++++++++-- web/src/hash_util.ts | 13 +++++++++---- web/src/topic_link_util.ts | 16 ++++++++++++++-- web/tests/copy_and_paste.test.js | 11 +++++++++-- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/web/src/copy_and_paste.ts b/web/src/copy_and_paste.ts index 892444a053..14825f8fd2 100644 --- a/web/src/copy_and_paste.ts +++ b/web/src/copy_and_paste.ts @@ -673,20 +673,33 @@ export function try_stream_topic_syntax_text(text: string): string | null { assert(stream !== undefined); const stream_name = stream.name; if (topic_link_util.will_produce_broken_stream_topic_link(stream_name)) { - return topic_link_util.get_fallback_markdown_link(stream_name, stream_topic.topic_name); + return topic_link_util.get_fallback_markdown_link( + stream_name, + stream_topic.topic_name, + stream_topic.message_id, + ); } if ( stream_topic.topic_name !== undefined && topic_link_util.will_produce_broken_stream_topic_link(stream_topic.topic_name) ) { - return topic_link_util.get_fallback_markdown_link(stream_name, stream_topic.topic_name); + return topic_link_util.get_fallback_markdown_link( + stream_name, + stream_topic.topic_name, + stream_topic.message_id, + ); } let syntax_text = "#**" + stream_name; if (stream_topic.topic_name) { syntax_text += ">" + stream_topic.topic_name; } + + if (stream_topic.message_id !== undefined) { + syntax_text += "@" + stream_topic.message_id; + } + syntax_text += "**"; return syntax_text; } diff --git a/web/src/hash_util.ts b/web/src/hash_util.ts index ccf554f4e1..e4e1b3558d 100644 --- a/web/src/hash_util.ts +++ b/web/src/hash_util.ts @@ -293,7 +293,7 @@ export function validate_group_settings_hash(hash: string): string { export function decode_stream_topic_from_url( url_str: string, -): {stream_id: number; topic_name?: string} | null { +): {stream_id: number; topic_name?: string; message_id?: string} | null { try { const url = new URL(url_str); if (url.origin !== window.location.origin || !url.hash.startsWith("#narrow")) { @@ -303,9 +303,8 @@ export function decode_stream_topic_from_url( if (terms === undefined) { return null; } - if (terms.length > 2) { + if (terms.length > 3) { // The link should only contain stream and topic, - // near/ links are not transformed. return null; } // This check is important as a malformed url @@ -324,7 +323,13 @@ export function decode_stream_topic_from_url( if (terms[1]?.operator !== "topic") { return null; } - return {stream_id, topic_name: terms[1].operand}; + if (terms.length === 2) { + return {stream_id, topic_name: terms[1].operand}; + } + if (terms[2]?.operator !== "near") { + return null; + } + return {stream_id, topic_name: terms[1].operand, message_id: terms[2].operand}; } catch { return null; } diff --git a/web/src/topic_link_util.ts b/web/src/topic_link_util.ts index 5a83414bbe..80960ced2c 100644 --- a/web/src/topic_link_util.ts +++ b/web/src/topic_link_util.ts @@ -37,13 +37,25 @@ export function html_escape_markdown_syntax_characters(text: string): string { return text.replaceAll(invalid_stream_topic_regex, escape_invalid_stream_topic_characters); } -export function get_fallback_markdown_link(stream_name: string, topic_name?: string): string { +export function get_fallback_markdown_link( + stream_name: string, + topic_name?: string, + message_id?: string, +): string { const stream = stream_data.get_sub(stream_name); const stream_id = stream?.stream_id; assert(stream_id !== undefined); const escape = html_escape_markdown_syntax_characters; if (topic_name !== undefined) { - return `[#${escape(stream_name)} > ${escape(topic_name)}](${internal_url.by_stream_topic_url(stream_id, topic_name, () => stream_name)})`; + const stream_topic_url = internal_url.by_stream_topic_url( + stream_id, + topic_name, + () => stream_name, + ); + if (message_id !== undefined) { + return `[#${escape(stream_name)} > ${escape(topic_name)} @ 💬](${stream_topic_url}/near/${message_id})`; + } + return `[#${escape(stream_name)} > ${escape(topic_name)}](${stream_topic_url})`; } return `[#${escape(stream_name)}](${internal_url.by_stream_url(stream_id, () => stream_name)})`; } diff --git a/web/tests/copy_and_paste.test.js b/web/tests/copy_and_paste.test.js index 7252bf735e..2b883c8725 100644 --- a/web/tests/copy_and_paste.test.js +++ b/web/tests/copy_and_paste.test.js @@ -33,17 +33,20 @@ run_test("try_stream_topic_syntax_text", () => { ], ["http://different.origin.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT"], - + [ + "http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT/near/100", + "#**Rome>old FAILED EXPORT@100**", + ], // malformed urls ["http://zulip.zulipdev.com/narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT"], ["http://zulip.zulipdev.com/#not_narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT"], ["http://zulip.zulipdev.com/#narrow/not_stream/4-Rome/topic/old.20FAILED.20EXPORT"], ["http://zulip.zulipdev.com/#narrow/channel/4-Rome/not_topic/old.20FAILED.20EXPORT"], - ["http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT/near/100"], ["http://zulip.zulipdev.com/#narrow/channel/4-Rome/", "#**Rome**"], ["http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic"], ["http://zulip.zulipdev.com/#narrow/topic/cheese"], ["http://zulip.zulipdev.com/#narrow/topic/pizza/stream/Rome"], + ["http://zulip.zulipdev.com/#narrow/channel/4-Rome/topic/old.20FAILED.20EXPORT/near/"], // When a url containing characters which are known to produce broken // #**stream>topic** urls is pasted, a normal markdown link syntax is produced. @@ -67,6 +70,10 @@ run_test("try_stream_topic_syntax_text", () => { "http://zulip.zulipdev.com/#narrow/stream/5-Romeo.60s-lair/topic/normal", "[#Romeo`s lair > normal](#narrow/channel/5-Romeo.60s-lair/topic/normal)", ], + [ + "http://zulip.zulipdev.com/#narrow/stream/4-Rome/topic/100.25.20profits.60/near/20", + "[#Rome > 100% profits` @ 💬](#narrow/channel/4-Rome/topic/100.25.20profits.60/near/20)", + ], ]; for (const test_case of test_cases) {