diff --git a/templates/zerver/integrations/include/generate-integration-url.md b/templates/zerver/integrations/include/generate-integration-url.md new file mode 100644 index 0000000000..c6a0605c6d --- /dev/null +++ b/templates/zerver/integrations/include/generate-integration-url.md @@ -0,0 +1,9 @@ +Generate the URL for your integration by clicking the **link** () icon on the profile card for the bot you +created. + +{% if all_event_types is defined %} + +{!event-filtering-instruction.md!} + +{% endif %} diff --git a/web/src/settings_bots.js b/web/src/settings_bots.js index 7cdf1df657..0856c06877 100644 --- a/web/src/settings_bots.js +++ b/web/src/settings_bots.js @@ -8,6 +8,7 @@ import render_bot_settings_tip from "../templates/settings/bot_settings_tip.hbs" import render_edit_bot_form from "../templates/settings/edit_bot_form.hbs"; import render_settings_edit_embedded_bot_service from "../templates/settings/edit_embedded_bot_service.hbs"; import render_settings_edit_outgoing_webhook_service from "../templates/settings/edit_outgoing_webhook_service.hbs"; +import render_generate_integration_url_modal from "../templates/settings/generate_integration_url_modal.hbs"; import * as avatar from "./avatar"; import * as bot_data from "./bot_data"; @@ -20,10 +21,12 @@ import {page_params} from "./page_params"; import * as people from "./people"; import * as settings_config from "./settings_config"; import * as settings_users from "./settings_users"; +import * as stream_data from "./stream_data"; import {show_copied_confirmation} from "./tippyjs"; import * as ui_report from "./ui_report"; import * as user_profile from "./user_profile"; +const INCOMING_WEBHOOK_BOT_TYPE = 2; const OUTGOING_WEBHOOK_BOT_TYPE = "3"; const EMBEDDED_BOT_TYPE = "4"; export let bot_owner_dropdown_widget; @@ -77,6 +80,7 @@ export function render_bots() { avatar_url: elem.avatar_url, api_key: elem.api_key, is_active: elem.is_active, + is_incoming_webhook_bot: elem.bot_type === INCOMING_WEBHOOK_BOT_TYPE, zuliprc: "zuliprc", // Most browsers do not allow filename starting with `.` }); user_owns_an_active_bot = user_owns_an_active_bot || elem.is_active; @@ -356,6 +360,7 @@ export function show_edit_bot_info_modal(user_id, $container) { bot_avatar_url: bot.avatar_url, owner_full_name, current_bot_owner: bot.bot_owner_id, + is_incoming_webhook_bot: bot.bot_type === INCOMING_WEBHOOK_BOT_TYPE, }); $container.append(html_body); let avatar_widget; @@ -504,9 +509,158 @@ export function show_edit_bot_info_modal(user_id, $container) { } confirm_bot_deactivation(bot_id, handle_confirm, true); }); + + $("#bot-edit-form").on("click", ".generate_url_for_integration", (e) => { + e.preventDefault(); + e.stopPropagation(); + const current_bot_data = bot_data.get(bot.user_id); + show_generate_integration_url_modal(current_bot_data.api_key); + }); } } +export function show_generate_integration_url_modal(api_key) { + const default_url_message = $t_html({defaultMessage: "Integration URL will appear here."}); + const streams = stream_data.subscribed_subs(); + const direct_messages_option = { + name: $t_html({defaultMessage: "Direct message to me"}), + unique_id: "", + is_direct_message: true, + }; + const html_body = render_generate_integration_url_modal({ + default_url_message, + max_topic_length: page_params.max_topic_length, + }); + + function generate_integration_url_post_render() { + let selected_integration = ""; + let stream_input_dropdown_widget; + + const $integration_input = $("#integration-input"); + const $override_topic = $("#integration-url-override-topic"); + const $topic_input = $("#integration-url-topic-input"); + const $integration_url = $("#generate-integration-url-modal .integration-url"); + const $dialog_submit_button = $("#generate-integration-url-modal .dialog_submit_button"); + + $dialog_submit_button.prop("disabled", true); + + new ClipboardJS("#generate-integration-url-modal .dialog_submit_button", { + text() { + return $integration_url.text(); + }, + }).on("success", (e) => { + show_copied_confirmation(e.trigger); + }); + + $integration_input + .typeahead({ + items: 5, + fixed: true, + source: () => + page_params.realm_incoming_webhook_bots.map((bot) => bot.display_name), + updater(item) { + selected_integration = page_params.realm_incoming_webhook_bots.find( + (bot) => bot.display_name === item, + ).name; + return item; + }, + }) + .on("input", function () { + const current_value = $(this).val(); + if (current_value === "") { + selected_integration = ""; + } + }); + + $override_topic.on("change", function () { + const checked = $(this).prop("checked"); + $topic_input.parent().toggleClass("hide", !checked); + }); + + $("#generate-integration-url-modal .integration-url-parameter").on("change input", () => { + update_url(); + }); + + function update_url() { + if (selected_integration === "") { + $integration_url.text(default_url_message); + $dialog_submit_button.prop("disabled", true); + return; + } + + const stream_name = stream_input_dropdown_widget.value(); + const topic_name = $topic_input.val(); + + const params = new URLSearchParams({api_key}); + if (stream_name !== "") { + params.set("stream", stream_name); + if (topic_name !== "") { + params.set("topic", topic_name); + } + } + + const realm_url = page_params.realm_uri; + const base_url = `${realm_url}/api/v1/external/`; + $integration_url.text(`${base_url}${selected_integration}?${params}`); + $dialog_submit_button.prop("disabled", false); + } + + stream_input_dropdown_widget = new dropdown_widget.DropdownWidget({ + widget_name: "integration-url-stream", + get_options: get_options_for_stream_dropdown_widget, + item_click_callback, + $events_container: $("#generate-integration-url-modal"), + tippy_props: { + placement: "bottom-start", + }, + default_id: direct_messages_option.unique_id, + unique_id_type: dropdown_widget.DATA_TYPES.STRING, + }); + stream_input_dropdown_widget.setup(); + + function get_options_for_stream_dropdown_widget() { + const options = [ + direct_messages_option, + ...streams.map((stream) => ({ + name: stream.name, + unique_id: stream.name, + stream, + })), + ]; + return options; + } + + function item_click_callback(event, dropdown) { + stream_input_dropdown_widget.render(); + $(".integration-url-stream-wrapper").trigger("input"); + const user_selected_option = stream_input_dropdown_widget.value(); + if (user_selected_option === direct_messages_option.unique_id) { + $override_topic.prop("checked", false).prop("disabled", true); + $override_topic.closest(".input-group").addClass("control-label-disabled"); + $topic_input.val(""); + } else { + $override_topic.prop("disabled", false); + $override_topic.closest(".input-group").removeClass("control-label-disabled"); + } + $override_topic.trigger("change"); + + dropdown.hide(); + event.preventDefault(); + event.stopPropagation(); + } + } + + dialog_widget.launch({ + html_heading: $t_html({defaultMessage: "Generate URL for an integration"}), + html_body, + id: "generate-integration-url-modal", + html_submit_button: $t_html({defaultMessage: "Copy URL"}), + html_exit_button: $t_html({defaultMessage: "Close"}), + on_click() {}, + post_render: generate_integration_url_post_render, + }); +} + export function set_up() { $("#download_botserverrc").on("click", function () { const OUTGOING_WEBHOOK_BOT_TYPE_INT = 3; @@ -610,6 +764,13 @@ export function set_up() { user_profile.show_user_profile(bot, "user-profile-streams-tab"); }); + $("#active_bots_list").on("click", "button.open-generate-integration-url-modal", (e) => { + e.preventDefault(); + e.stopPropagation(); + const api_key = $(e.currentTarget).attr("data-api-key"); + show_generate_integration_url_modal(api_key); + }); + const clipboard = new ClipboardJS("#copy_zuliprc", { text(trigger) { const $bot_info = $(trigger).closest(".bot-information-box").find(".bot_info"); diff --git a/web/styles/dark_theme.css b/web/styles/dark_theme.css index d63b4da388..07ebaff10c 100644 --- a/web/styles/dark_theme.css +++ b/web/styles/dark_theme.css @@ -874,6 +874,7 @@ .grey-box, .white-box, .stream-email, + #generate-integration-url-modal .integration-url, #settings_page .settings-header, #settings_page .sidebar li.active, #settings_page .sidebar-wrapper .tab-container, diff --git a/web/styles/settings.css b/web/styles/settings.css index 1b09194858..7ce3c14f55 100644 --- a/web/styles/settings.css +++ b/web/styles/settings.css @@ -869,6 +869,10 @@ input[type="checkbox"] { .purple { color: hsl(278deg 62% 68%); } + + .steel-blue { + color: hsl(207deg 44% 49%); + } } .bot-information-box { @@ -2017,3 +2021,33 @@ $option_title_width: 180px; 0 0 8px hsl(206deg 80% 62% / 60%); } } + +#generate-integration-url-modal { + .inline { + display: inline; + } + + .integration-url { + font-family: "Source Code Pro", monospace; + padding: 10px; + font-size: 0.85rem; + border: 1px solid hsl(0deg 0% 87%); + border-radius: 4px; + overflow-wrap: break-word; + word-break: break-all; + } + + .integration-url-stream-wrapper .dropdown_widget_value { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .integration-url-stream-wrapper .dropdown-widget-button { + width: 75%; + } + + #stream-not-specified-notice { + margin-top: -10px; + } +} diff --git a/web/templates/settings/bot_avatar_row.hbs b/web/templates/settings/bot_avatar_row.hbs index 3eb7489f8d..fb3ebaa00e 100644 --- a/web/templates/settings/bot_avatar_row.hbs +++ b/web/templates/settings/bot_avatar_row.hbs @@ -20,6 +20,11 @@ + {{#if is_incoming_webhook_bot}} + + {{/if}} {{/if}} diff --git a/web/templates/settings/edit_bot_form.hbs b/web/templates/settings/edit_bot_form.hbs index 56a93dc69f..1e5fd0041c 100644 --- a/web/templates/settings/edit_bot_form.hbs +++ b/web/templates/settings/edit_bot_form.hbs @@ -42,6 +42,13 @@
+ {{#if is_incoming_webhook_bot}} +
+ +
+ {{/if}}