diff --git a/web/src/clipboard_handler.ts b/web/src/clipboard_handler.ts
index ce947719b2..bd155de12f 100644
--- a/web/src/clipboard_handler.ts
+++ b/web/src/clipboard_handler.ts
@@ -1,8 +1,8 @@
-import * as hash_util from "./hash_util";
-import * as stream_data from "./stream_data";
+import * as hash_util from "./hash_util.ts";
+import * as stream_data from "./stream_data.ts";
-export function generate_formatted_link(link_text: string): string {
- const stream_topic = hash_util.decode_stream_topic_from_url(link_text);
+export function generate_formatted_link(link_data: string): string {
+ const stream_topic = hash_util.decode_stream_topic_from_url(link_data);
if (!stream_topic) {
return "Invalid stream";
@@ -17,22 +17,30 @@ export function generate_formatted_link(link_text: string): string {
const topic_name = stream_topic.topic_name?.replace(//g, ">");
const stream_name = stream?.name?.replace(//g, ">");
- if (topic_name !== undefined) {
- return `#${stream_name}>${topic_name}`;
- }
+ const message_id = stream_topic?.message_id;
+ let formatted_url;
- return `#${stream_name}`;
+ if (message_id === undefined) {
+ if (topic_name !== undefined) {
+ formatted_url = `#${stream_name}>${topic_name}`;
+ } else {
+ formatted_url = `#${stream_name}`;
+ }
+ } else {
+ formatted_url = `#${stream_name}>${topic_name}@${message_id}`;
+ }
+ return formatted_url;
}
-export function copy_to_clipboard(link_text: string, after_copy_cb: () => void): void {
- const formatted_url = generate_formatted_link(link_text);
+export function copy_to_clipboard(link_data: string, after_copy_cb: () => void): void {
+ const formatted_url = generate_formatted_link(link_data);
if (formatted_url === "Invalid stream") {
return;
}
const clipboardItem = new ClipboardItem({
- "text/plain": new Blob([link_text], {
+ "text/plain": new Blob([link_data], {
type: "text/plain",
}),
"text/html": new Blob([formatted_url], {type: "text/html"}),
diff --git a/web/src/message_actions_popover.js b/web/src/message_actions_popover.js
index 957da457b4..2c17c94880 100644
--- a/web/src/message_actions_popover.js
+++ b/web/src/message_actions_popover.js
@@ -1,10 +1,10 @@
-import ClipboardJS from "clipboard";
import $ from "jquery";
import assert from "minimalistic-assert";
import render_message_actions_popover from "../templates/popovers/message_actions_popover.hbs";
import * as blueslip from "./blueslip.ts";
+import * as clipboard_handler from "./clipboard_handler.ts";
import * as compose_reply from "./compose_reply.ts";
import * as condense from "./condense.ts";
import {show_copied_confirmation} from "./copied_tooltip.ts";
@@ -217,14 +217,11 @@ export function initialize() {
popover_menus.hide_current_popover_if_visible(instance);
});
- new ClipboardJS($popper.find(".copy_link")[0]).on("success", () => {
+ $popper.on("click", ".copy_link", () => {
+ clipboard_handler.copy_to_clipboard($(".copy_link").data("clipboard-text"), () =>
+ popover_menus.hide_current_popover_if_visible(instance),
+ );
show_copied_confirmation($(instance.reference).closest(".message_controls")[0]);
- setTimeout(() => {
- // The Clipboard library works by focusing to a hidden textarea.
- // We unfocus this so keyboard shortcuts, etc., will work again.
- $(":focus").trigger("blur");
- }, 0);
- popover_menus.hide_current_popover_if_visible(instance);
});
},
onHidden(instance) {
diff --git a/web/src/stream_popover.js b/web/src/stream_popover.js
index 102e8004f1..9dcc0de7a1 100644
--- a/web/src/stream_popover.js
+++ b/web/src/stream_popover.js
@@ -7,7 +7,7 @@ import render_left_sidebar_stream_actions_popover from "../templates/popovers/le
import * as blueslip from "./blueslip.ts";
import * as browser_history from "./browser_history.ts";
-import * as clipboard_handler from "./clipboard_handler";
+import * as clipboard_handler from "./clipboard_handler.ts";
import * as composebox_typeahead from "./composebox_typeahead.ts";
import * as dialog_widget from "./dialog_widget.ts";
import * as dropdown_widget from "./dropdown_widget.ts";
diff --git a/web/src/topic_popover.js b/web/src/topic_popover.js
index d4bd9f7d82..f4c4eb7ab8 100644
--- a/web/src/topic_popover.js
+++ b/web/src/topic_popover.js
@@ -3,7 +3,7 @@ import $ from "jquery";
import render_delete_topic_modal from "../templates/confirm_dialog/confirm_delete_topic.hbs";
import render_left_sidebar_topic_actions_popover from "../templates/popovers/left_sidebar/left_sidebar_topic_actions_popover.hbs";
-import * as clipboard_handler from "./clipboard_handler";
+import * as clipboard_handler from "./clipboard_handler.ts";
import * as confirm_dialog from "./confirm_dialog.ts";
import {$t_html} from "./i18n.ts";
import * as message_edit from "./message_edit.ts";
diff --git a/web/tests/clipboard_handler.test.js b/web/tests/clipboard_handler.test.cjs
similarity index 84%
rename from web/tests/clipboard_handler.test.js
rename to web/tests/clipboard_handler.test.cjs
index 0b8779c8e6..b1249be1a0 100644
--- a/web/tests/clipboard_handler.test.js
+++ b/web/tests/clipboard_handler.test.cjs
@@ -2,9 +2,9 @@
const assert = require("node:assert/strict");
-const {zrequire} = require("./lib/namespace");
-const {make_stub} = require("./lib/stub");
-const {run_test} = require("./lib/test");
+const {zrequire} = require("./lib/namespace.cjs");
+const {make_stub} = require("./lib/stub.cjs");
+const {run_test} = require("./lib/test.cjs");
const clipboard_handler = zrequire("clipboard_handler");
const stream_data = zrequire("stream_data");
@@ -29,18 +29,19 @@ stream_data.add_sub(stream);
stream_data.add_sub(markdown_stream);
const normal_stream_with_topic =
- "http://zulip.zulipdev.com/#narrow/stream/1-Stream/topic/normal.20topic";
+ "http://zulip.zulipdev.com/#narrow/channel/1-Stream/topic/normal.20topic";
const markdown_stream_with_normal_topic =
"http://zulip.zulipdev.com/#narrow/channel/2-.3CStream*.24.60.26.3E/topic/normal.20topic";
const normal_stream_with_markdown_topic =
- "http://zulip.zulipdev.com/#narrow/stream/1-Stream/topic/.3C.24topic.60*.26.3E";
+ "http://zulip.zulipdev.com/#narrow/channel/1-Stream/topic/.3C.24topic.60*.26.3E";
const markdown_stream_with_markdown_topic =
"http://zulip.zulipdev.com/#narrow/channel/2-.3CStream*.24.60.26.3E/topic/.3C.24topic.60*.26.3E";
const invalid_stream = "http://zulip.zulipdev.com/#narrow/stream/99-Stream";
-const normal_stream_no_topic = "http://zulip.zulipdev.com/#narrow/stream/1-Stream";
+const normal_stream_no_topic = "http://zulip.zulipdev.com/#narrow/channel/1-Stream";
const markdown_stream_no_topic =
"http://zulip.zulipdev.com/#narrow/channel/2-.3CStream*.24.60.26.3E";
-
+const link_to_message =
+ "http://zulip.zulipdev.com/#narrow/channel/1-Stream/topic/normal.20topic/near/86";
run_test("generate_formatted_url", () => {
assert.equal(
clipboard_handler.generate_formatted_link(normal_stream_with_topic),
@@ -73,6 +74,10 @@ run_test("generate_formatted_url", () => {
clipboard_handler.generate_formatted_link(markdown_stream_no_topic),
`#<Stream*$\`&>`,
);
+ assert.equal(
+ clipboard_handler.generate_formatted_link(link_to_message),
+ `#Stream>normal topic@86`,
+ );
});
global.ClipboardItem = class {