mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
giphy: Migrate Giphy popover to Tippy.
This commit is contained in:
committed by
Tim Abbott
parent
4976655dda
commit
9c675ce62d
@@ -30,7 +30,12 @@ export function initialize() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
delegate("body", {
|
delegate("body", {
|
||||||
target: ".compose_control_button",
|
// Only display Tippy content on classes accompanied by a `data-` attribute.
|
||||||
|
target: `
|
||||||
|
.compose_control_button[data-tooltip-template-id],
|
||||||
|
.compose_control_button[data-tippy-content],
|
||||||
|
.compose_control_button_container
|
||||||
|
`,
|
||||||
// Add some additional delay when they open
|
// Add some additional delay when they open
|
||||||
// so that regular users don't have to see
|
// so that regular users don't have to see
|
||||||
// them unless they want to.
|
// them unless they want to.
|
||||||
|
154
web/src/giphy.js
154
web/src/giphy.js
@@ -8,6 +8,7 @@ import * as blueslip from "./blueslip";
|
|||||||
import * as compose_ui from "./compose_ui";
|
import * as compose_ui from "./compose_ui";
|
||||||
import {media_breakpoints_num} from "./css_variables";
|
import {media_breakpoints_num} from "./css_variables";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
|
import * as popover_menus from "./popover_menus";
|
||||||
import * as popovers from "./popovers";
|
import * as popovers from "./popovers";
|
||||||
import * as rows from "./rows";
|
import * as rows from "./rows";
|
||||||
import * as ui_util from "./ui_util";
|
import * as ui_util from "./ui_util";
|
||||||
@@ -15,13 +16,13 @@ import * as ui_util from "./ui_util";
|
|||||||
let giphy_fetch;
|
let giphy_fetch;
|
||||||
let search_term = "";
|
let search_term = "";
|
||||||
let gifs_grid;
|
let gifs_grid;
|
||||||
let $active_popover_element;
|
let giphy_popover_instance = null;
|
||||||
|
|
||||||
// Only used if popover called from edit message, otherwise it is `undefined`.
|
// Only used if popover called from edit message, otherwise it is `undefined`.
|
||||||
let edit_message_id;
|
let edit_message_id;
|
||||||
|
|
||||||
export function is_popped_from_edit_message() {
|
export function is_popped_from_edit_message() {
|
||||||
return $active_popover_element && edit_message_id !== undefined;
|
return giphy_popover_instance && edit_message_id !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function focus_current_edit_message() {
|
export function focus_current_edit_message() {
|
||||||
@@ -35,12 +36,6 @@ export function is_giphy_enabled() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Approximate width and height of
|
|
||||||
// giphy popover as computed by chrome
|
|
||||||
// + 25px;
|
|
||||||
const APPROX_HEIGHT = 350;
|
|
||||||
const APPROX_WIDTH = 300;
|
|
||||||
|
|
||||||
export function update_giphy_rating() {
|
export function update_giphy_rating() {
|
||||||
if (
|
if (
|
||||||
page_params.realm_giphy_rating === page_params.giphy_rating_options.disabled.id ||
|
page_params.realm_giphy_rating === page_params.giphy_rating_options.disabled.id ||
|
||||||
@@ -164,15 +159,15 @@ async function update_grid_with_search_term() {
|
|||||||
|
|
||||||
export function hide_giphy_popover() {
|
export function hide_giphy_popover() {
|
||||||
// Returns `true` if the popover was open.
|
// Returns `true` if the popover was open.
|
||||||
if ($active_popover_element) {
|
if (giphy_popover_instance) {
|
||||||
// We need to destroy the popover because when
|
// We need to destroy the popover because when
|
||||||
// we hide it, bootstrap popover
|
// we hide it, bootstrap popover
|
||||||
// library removes `giphy-content` element
|
// library removes `giphy-content` element
|
||||||
// as part of cleaning up everything inside
|
// as part of cleaning up everything inside
|
||||||
// `popover-content`, so we need to reinitialize
|
// `popover-content`, so we need to reinitialize
|
||||||
// the popover by destroying it.
|
// the popover by destroying it.
|
||||||
$active_popover_element.popover("destroy");
|
giphy_popover_instance.destroy();
|
||||||
$active_popover_element = undefined;
|
giphy_popover_instance = undefined;
|
||||||
edit_message_id = undefined;
|
edit_message_id = undefined;
|
||||||
gifs_grid = undefined;
|
gifs_grid = undefined;
|
||||||
return true;
|
return true;
|
||||||
@@ -188,101 +183,58 @@ function get_popover_content() {
|
|||||||
return render_giphy_picker();
|
return render_giphy_picker();
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_popover_placement() {
|
|
||||||
let placement = popovers.compute_placement(
|
|
||||||
$active_popover_element,
|
|
||||||
APPROX_HEIGHT,
|
|
||||||
APPROX_WIDTH,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (placement === "viewport_center") {
|
|
||||||
// For legacy reasons `compute_placement` actually can
|
|
||||||
// return `viewport_center` which used to place popover in
|
|
||||||
// the center of the screen, but bootstrap doesn't actually
|
|
||||||
// support that and we already handle it on small screen sizes
|
|
||||||
// by placing it in center using `popover-flex`.
|
|
||||||
placement = "left";
|
|
||||||
}
|
|
||||||
|
|
||||||
return placement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initialize() {
|
export function initialize() {
|
||||||
$("body").on("keydown", ".giphy-gif", ui_util.convert_enter_to_click);
|
popover_menus.register_popover_menu(".compose_control_button.compose_gif_icon", {
|
||||||
$("body").on("keydown", ".compose_gif_icon", ui_util.convert_enter_to_click);
|
placement: "top",
|
||||||
|
onCreate(instance) {
|
||||||
|
instance.setContent(ui_util.parse_html(get_popover_content()));
|
||||||
|
$(instance.popper).addClass("giphy-popover");
|
||||||
|
},
|
||||||
|
async onShow(instance) {
|
||||||
|
giphy_popover_instance = instance;
|
||||||
|
const $popper = $(giphy_popover_instance.popper).trigger("focus");
|
||||||
|
gifs_grid = await renderGIPHYGrid($popper.find(".giphy-content")[0]);
|
||||||
|
popovers.hide_all(true);
|
||||||
|
|
||||||
$("body").on("click", "#giphy_search_clear", async (e) => {
|
const $click_target = $(instance.reference);
|
||||||
e.stopPropagation();
|
if ($click_target.parents(".message_edit_form").length === 1) {
|
||||||
$("#giphy-search-query").val("").trigger("focus");
|
// Store message id in global variable edit_message_id so that
|
||||||
await update_grid_with_search_term();
|
// its value can be further used to correctly find the message textarea element.
|
||||||
});
|
edit_message_id = rows.id($click_target.parents(".message_row"));
|
||||||
|
} else {
|
||||||
$("body").on("click", ".compose_gif_icon", (e) => {
|
edit_message_id = undefined;
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const compose_click_target = compose_ui.get_compose_click_target(e);
|
|
||||||
if ($active_popover_element && $active_popover_element.get()[0] === compose_click_target) {
|
|
||||||
// Hide giphy popover if already active.
|
|
||||||
hide_giphy_popover();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
popovers.hide_all();
|
|
||||||
|
|
||||||
const $elt = $(compose_click_target);
|
|
||||||
if ($elt.parents(".message_edit_form").length === 1) {
|
|
||||||
// Store message id in global variable edit_message_id so that
|
|
||||||
// its value can be further used to correctly find the message textarea element.
|
|
||||||
edit_message_id = rows.id($elt.parents(".message_row"));
|
|
||||||
} else {
|
|
||||||
edit_message_id = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
$active_popover_element = $elt;
|
|
||||||
$active_popover_element.popover({
|
|
||||||
animation: true,
|
|
||||||
placement: get_popover_placement(),
|
|
||||||
fixed: true,
|
|
||||||
html: true,
|
|
||||||
trigger: "manual",
|
|
||||||
template: get_popover_content(),
|
|
||||||
/* Popovers without a content property are not displayed,
|
|
||||||
* so we need something here; but we haven't contacted the
|
|
||||||
* Giphy API yet to get the actual content to display. */
|
|
||||||
content: " ",
|
|
||||||
});
|
|
||||||
|
|
||||||
$active_popover_element.popover("show");
|
|
||||||
// It takes about 1s for the popover to show; So,
|
|
||||||
// we wait for popover to display before rendering GIFs
|
|
||||||
// in it, otherwise popover is rendered with empty content.
|
|
||||||
const popover_observer = new MutationObserver(async () => {
|
|
||||||
if ($("#giphy_grid_in_popover .giphy-content").is(":visible")) {
|
|
||||||
popover_observer.disconnect();
|
|
||||||
gifs_grid = await renderGIPHYGrid($("#giphy_grid_in_popover .giphy-content")[0]);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
const opts = {attributes: false, childList: true, characterData: false, subtree: true};
|
|
||||||
popover_observer.observe(document, opts);
|
|
||||||
|
|
||||||
$("body").on(
|
$(document).one("compose_canceled.zulip compose_finished.zulip", () => {
|
||||||
"keyup",
|
hide_giphy_popover();
|
||||||
"#giphy-search-query",
|
});
|
||||||
// Use debounce to create a 300ms interval between
|
|
||||||
// every search. This makes the UX of searching pleasant
|
|
||||||
// by allowing user to finish typing before search
|
|
||||||
// is executed.
|
|
||||||
_.debounce(update_grid_with_search_term, 300),
|
|
||||||
);
|
|
||||||
|
|
||||||
$(document).one("compose_canceled.zulip compose_finished.zulip", () => {
|
$popper.on(
|
||||||
|
"keyup",
|
||||||
|
"#giphy-search-query",
|
||||||
|
// Use debounce to create a 300ms interval between
|
||||||
|
// every search. This makes the UX of searching pleasant
|
||||||
|
// by allowing user to finish typing before search
|
||||||
|
// is executed.
|
||||||
|
_.debounce(update_grid_with_search_term, 300),
|
||||||
|
);
|
||||||
|
|
||||||
|
$popper.on("keydown", ".giphy-gif", ui_util.convert_enter_to_click);
|
||||||
|
$popper.on("keydown", ".compose_gif_icon", ui_util.convert_enter_to_click);
|
||||||
|
|
||||||
|
$popper.on("click", "#giphy_search_clear", async (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
$("#giphy-search-query").val("");
|
||||||
|
await update_grid_with_search_term();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Focus on search box by default.
|
||||||
|
// This is specially helpful for users
|
||||||
|
// navigating via keyboard.
|
||||||
|
$("#giphy-search-query").trigger("focus");
|
||||||
|
},
|
||||||
|
onHidden() {
|
||||||
hide_giphy_popover();
|
hide_giphy_popover();
|
||||||
});
|
},
|
||||||
|
|
||||||
// Focus on search box by default.
|
|
||||||
// This is specially helpful for users
|
|
||||||
// navigating via keyboard.
|
|
||||||
$("#giphy-search-query").trigger("focus");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@ import * as compose_state from "./compose_state";
|
|||||||
import * as compose_ui from "./compose_ui";
|
import * as compose_ui from "./compose_ui";
|
||||||
import * as dialog_widget from "./dialog_widget";
|
import * as dialog_widget from "./dialog_widget";
|
||||||
import * as emoji_picker from "./emoji_picker";
|
import * as emoji_picker from "./emoji_picker";
|
||||||
import * as giphy from "./giphy";
|
|
||||||
import * as hash_util from "./hash_util";
|
import * as hash_util from "./hash_util";
|
||||||
import {$t, $t_html} from "./i18n";
|
import {$t, $t_html} from "./i18n";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
@@ -1088,7 +1087,6 @@ export function hide_all_except_sidebars(opts) {
|
|||||||
hideAll();
|
hideAll();
|
||||||
}
|
}
|
||||||
emoji_picker.hide_emoji_popover();
|
emoji_picker.hide_emoji_popover();
|
||||||
giphy.hide_giphy_popover();
|
|
||||||
stream_popover.hide_stream_popover();
|
stream_popover.hide_stream_popover();
|
||||||
hide_all_user_info_popovers();
|
hide_all_user_info_popovers();
|
||||||
hide_playground_links_popover();
|
hide_playground_links_popover();
|
||||||
|
@@ -674,10 +674,26 @@ input.recipient_box {
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.compose_gif_icon {
|
/* We use the selector in this manner to maintain specificity. */
|
||||||
|
.compose_control_button_container .compose_gif_icon {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
height: 18px;
|
|
||||||
line-height: 18px;
|
/* Remove top and bottom padding. This is necessary
|
||||||
|
* because `compose_gif_icon` is no longer a flex item. */
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compose_control_button {
|
||||||
|
padding: 5px;
|
||||||
|
opacity: 0.7;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 17px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fa-eye {
|
.fa-eye {
|
||||||
@@ -751,19 +767,6 @@ input.recipient_box {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.compose_control_button {
|
|
||||||
padding: 5px;
|
|
||||||
opacity: 0.7;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 17px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.drag {
|
.drag {
|
||||||
display: none;
|
display: none;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
@@ -735,6 +735,14 @@ ul {
|
|||||||
left: -1px;
|
left: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.giphy-popover {
|
||||||
|
.tippy-content {
|
||||||
|
/* We remove the default padding from this container
|
||||||
|
as it is not necessary for the Giphy popover. */
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#giphy_grid_in_popover {
|
#giphy_grid_in_popover {
|
||||||
/* 300px of GIPHY grid + 5px is the extra gutter space
|
/* 300px of GIPHY grid + 5px is the extra gutter space
|
||||||
* between gif columns. */
|
* between gif columns. */
|
||||||
@@ -762,6 +770,10 @@ ul {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
|
/* By resetting to the default color from the `body`,
|
||||||
|
* we can ignore the colors applied from `tippy-box`. */
|
||||||
|
color: var(--color-text-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
.clear_search_button {
|
.clear_search_button {
|
||||||
@@ -787,7 +799,12 @@ ul {
|
|||||||
.popover-footer {
|
.popover-footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: hsl(0deg 0% 0%);
|
background-color: hsl(0deg 0% 0%);
|
||||||
border-radius: 0 0 6px 6px;
|
/* The border radius corresponds to the default radius value from `tippy-box`. */
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
|
||||||
|
/* This prevents the footer from experiencing height
|
||||||
|
fluctuations at the moment when the image is uploaded. */
|
||||||
|
min-height: 25px;
|
||||||
|
|
||||||
& img {
|
& img {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
|
@@ -8,7 +8,9 @@
|
|||||||
<a role="button" class="compose_control_button fa fa-video-camera video_link" aria-label="{{t 'Add video call' }}" tabindex=0 data-tippy-content="{{t 'Add video call' }}"></a>
|
<a role="button" class="compose_control_button fa fa-video-camera video_link" aria-label="{{t 'Add video call' }}" tabindex=0 data-tippy-content="{{t 'Add video call' }}"></a>
|
||||||
<a role="button" class="compose_control_button fa fa-smile-o emoji_map" aria-label="{{t 'Add emoji' }}" tabindex=0 data-tippy-content="{{t 'Add emoji' }}"></a>
|
<a role="button" class="compose_control_button fa fa-smile-o emoji_map" aria-label="{{t 'Add emoji' }}" tabindex=0 data-tippy-content="{{t 'Add emoji' }}"></a>
|
||||||
<a role="button" class="compose_control_button fa fa-clock-o time_pick" aria-label="{{t 'Add global time' }}" tabindex=0 data-tooltip-template-id="add-global-time-tooltip" data-tippy-maxWidth="none"></a>
|
<a role="button" class="compose_control_button fa fa-clock-o time_pick" aria-label="{{t 'Add global time' }}" tabindex=0 data-tooltip-template-id="add-global-time-tooltip" data-tippy-maxWidth="none"></a>
|
||||||
<a role="button" class="compose_control_button compose_gif_icon {{#unless giphy_enabled }} hide {{/unless}} zulip-icon zulip-icon-gif" aria-label="{{t 'Add GIF' }}" tabindex=0 data-tippy-content="{{t 'Add GIF' }}"></a>
|
<div class="compose_control_button_container {{#unless giphy_enabled }}hide{{/unless}}" data-tippy-content="{{t 'Add GIF' }}">
|
||||||
|
<a role="button" class="compose_control_button compose_gif_icon zulip-icon zulip-icon-gif" aria-label="{{t 'Add GIF' }}" tabindex=0></a>
|
||||||
|
</div>
|
||||||
<div class="divider hide-sm">|</div>
|
<div class="divider hide-sm">|</div>
|
||||||
<div class="{{#if message_id}}hide-lg{{else}}hide-sm{{/if}}">
|
<div class="{{#if message_id}}hide-lg{{else}}hide-sm{{/if}}">
|
||||||
{{> compose_control_buttons_in_popover}}
|
{{> compose_control_buttons_in_popover}}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="popover" id="giphy_grid_in_popover">
|
<div id="giphy_grid_in_popover">
|
||||||
<div class="arrow"></div>
|
<div class="arrow"></div>
|
||||||
<div class="popover-inner">
|
<div class="popover-inner">
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
|
Reference in New Issue
Block a user