mirror of https://github.com/zulip/zulip.git
popovers: Enabled link result pastable both as a plain URL and HTML.
Fixes: #31813 addressed the feedback and added test for the module.
This commit is contained in:
parent
e0bd3713cc
commit
226493dc4d
|
@ -0,0 +1,42 @@
|
|||
import * as hash_util from "./hash_util";
|
||||
import * as stream_data from "./stream_data";
|
||||
|
||||
export function generate_formatted_link(link_text: string): string {
|
||||
const stream_topic = hash_util.decode_stream_topic_from_url(link_text);
|
||||
|
||||
if (!stream_topic) {
|
||||
return "Invalid stream";
|
||||
}
|
||||
|
||||
const stream = stream_data.get_sub_by_id(stream_topic.stream_id);
|
||||
|
||||
// had to replace <> characters from stream and topic name
|
||||
// with HTML entity because sites supporting html won't
|
||||
// render that name and treats that name as tag.
|
||||
|
||||
const topic_name = stream_topic.topic_name?.replace(/</g, "<")?.replace(/>/g, ">");
|
||||
const stream_name = stream?.name?.replace(/</g, "<")?.replace(/>/g, ">");
|
||||
|
||||
if (topic_name !== undefined) {
|
||||
return `<a href="${link_text}">#${stream_name}>${topic_name}</a>`;
|
||||
}
|
||||
|
||||
return `<a href="${link_text}">#${stream_name}</a>`;
|
||||
}
|
||||
|
||||
export function copy_to_clipboard(link_text: string, after_copy_cb: () => void): void {
|
||||
const formatted_url = generate_formatted_link(link_text);
|
||||
|
||||
if (formatted_url === "Invalid stream") {
|
||||
return;
|
||||
}
|
||||
|
||||
const clipboardItem = new ClipboardItem({
|
||||
"text/plain": new Blob([link_text], {
|
||||
type: "text/plain",
|
||||
}),
|
||||
"text/html": new Blob([formatted_url], {type: "text/html"}),
|
||||
});
|
||||
|
||||
void navigator.clipboard.write([clipboardItem]).then(after_copy_cb);
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import ClipboardJS from "clipboard";
|
||||
import $ from "jquery";
|
||||
import assert from "minimalistic-assert";
|
||||
|
||||
|
@ -8,6 +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 composebox_typeahead from "./composebox_typeahead.ts";
|
||||
import * as dialog_widget from "./dialog_widget.ts";
|
||||
import * as dropdown_widget from "./dropdown_widget.ts";
|
||||
|
@ -212,9 +212,11 @@ function build_stream_popover(opts) {
|
|||
$(e.currentTarget).hide();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
new ClipboardJS($popper.find(".copy_stream_link")[0]).on("success", () => {
|
||||
popover_menus.hide_current_popover_if_visible(instance);
|
||||
$popper.on("click", ".copy_stream_link", () => {
|
||||
clipboard_handler.copy_to_clipboard(
|
||||
$(".copy_stream_link").data("clipboard-text"),
|
||||
() => popover_menus.hide_current_popover_if_visible(instance),
|
||||
);
|
||||
});
|
||||
},
|
||||
onHidden() {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import ClipboardJS from "clipboard";
|
||||
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 confirm_dialog from "./confirm_dialog.ts";
|
||||
import {$t_html} from "./i18n.ts";
|
||||
import * as message_edit from "./message_edit.ts";
|
||||
|
@ -157,13 +157,12 @@ export function initialize() {
|
|||
stream_popover.build_move_topic_to_stream_popover(stream_id, topic_name, true);
|
||||
popover_menus.hide_current_popover_if_visible(instance);
|
||||
});
|
||||
|
||||
new ClipboardJS($popper.find(".sidebar-popover-copy-link-to-topic")[0]).on(
|
||||
"success",
|
||||
() => {
|
||||
popover_menus.hide_current_popover_if_visible(instance);
|
||||
},
|
||||
$popper.on("click", ".sidebar-popover-copy-link-to-topic", () => {
|
||||
clipboard_handler.copy_to_clipboard(
|
||||
$(".sidebar-popover-copy-link-to-topic").data("clipboard-text"),
|
||||
() => popover_menus.hide_current_popover_if_visible(instance),
|
||||
);
|
||||
});
|
||||
},
|
||||
onHidden(instance) {
|
||||
instance.destroy();
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
"use strict";
|
||||
|
||||
const assert = require("node:assert/strict");
|
||||
|
||||
const {zrequire} = require("./lib/namespace");
|
||||
const {make_stub} = require("./lib/stub");
|
||||
const {run_test} = require("./lib/test");
|
||||
|
||||
const clipboard_handler = zrequire("clipboard_handler");
|
||||
const stream_data = zrequire("stream_data");
|
||||
|
||||
const stream = {
|
||||
name: "Stream",
|
||||
description: "Color and Lights",
|
||||
stream_id: 1,
|
||||
subscribed: true,
|
||||
type: "stream",
|
||||
};
|
||||
|
||||
const markdown_stream = {
|
||||
name: "<Stream*$`&>",
|
||||
description: "Colors and lights",
|
||||
stream_id: 2,
|
||||
subscribe: true,
|
||||
type: "stream",
|
||||
};
|
||||
|
||||
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";
|
||||
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";
|
||||
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 markdown_stream_no_topic =
|
||||
"http://zulip.zulipdev.com/#narrow/channel/2-.3CStream*.24.60.26.3E";
|
||||
|
||||
run_test("generate_formatted_url", () => {
|
||||
assert.equal(
|
||||
clipboard_handler.generate_formatted_link(normal_stream_with_topic),
|
||||
`<a href="${normal_stream_with_topic}">#Stream>normal topic</a>`,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
clipboard_handler.generate_formatted_link(markdown_stream_with_normal_topic),
|
||||
`<a href="${markdown_stream_with_normal_topic}">#<Stream*$\`&>>normal topic</a>`,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
clipboard_handler.generate_formatted_link(normal_stream_with_markdown_topic),
|
||||
`<a href="${normal_stream_with_markdown_topic}">#Stream><$topic\`*&></a>`,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
clipboard_handler.generate_formatted_link(markdown_stream_with_markdown_topic),
|
||||
`<a href="${markdown_stream_with_markdown_topic}">#<Stream*$\`&>><$topic\`*&></a>`,
|
||||
);
|
||||
|
||||
assert.equal(clipboard_handler.generate_formatted_link(invalid_stream), "Invalid stream");
|
||||
|
||||
assert.equal(
|
||||
clipboard_handler.generate_formatted_link(normal_stream_no_topic),
|
||||
`<a href="${normal_stream_no_topic}">#Stream</a>`,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
clipboard_handler.generate_formatted_link(markdown_stream_no_topic),
|
||||
`<a href="${markdown_stream_no_topic}">#<Stream*$\`&></a>`,
|
||||
);
|
||||
});
|
||||
|
||||
global.ClipboardItem = class {
|
||||
constructor(items) {
|
||||
this.items = items;
|
||||
}
|
||||
};
|
||||
|
||||
global.navigator = {
|
||||
clipboard: {},
|
||||
};
|
||||
|
||||
run_test("copy_to_clipboard", async ({override}) => {
|
||||
const after_copy_cb = make_stub();
|
||||
|
||||
// replacing navigator.clipboard.write with custom function
|
||||
override(global.navigator.clipboard, "write", () => Promise.resolve());
|
||||
|
||||
// ensuring that function returns early when link in invalid
|
||||
await clipboard_handler.copy_to_clipboard(invalid_stream, after_copy_cb.f);
|
||||
assert.deepEqual(after_copy_cb.num_calls, 0);
|
||||
|
||||
// ensuring function is beign called and after_copy_cb is triggered when link is correct
|
||||
await clipboard_handler.copy_to_clipboard(normal_stream_with_topic, after_copy_cb.f);
|
||||
assert.deepEqual(global.navigator.clipboard.write(), Promise.resolve());
|
||||
assert.deepEqual(after_copy_cb.num_calls, 1);
|
||||
});
|
Loading…
Reference in New Issue