From 66a96bee719b74258fc926b2123109c42c06f493 Mon Sep 17 00:00:00 2001 From: roanster007 Date: Sun, 21 Jul 2024 18:53:56 +0530 Subject: [PATCH] settings: Add setting to control how animated images are played. Previously animated images were automatically played in the message feed of the web app. Now that we have still thumbnails available for them, we can add a new personal setting, "web_animate_image_previews", which controls how the animated images would be played in the web app message feed -- always played, on hover, or only in the image viewer. Fixes #31016. --- api_docs/changelog.md | 8 +++ version.py | 2 +- web/src/admin.js | 1 + web/src/click_handlers.js | 1 + web/src/lightbox.ts | 2 +- web/src/message_list_hover.js | 36 +++++++++++++ web/src/realm_user_settings_defaults.ts | 1 + web/src/rendered_markdown.ts | 16 +++++- web/src/server_events_dispatch.js | 7 +++ web/src/settings.js | 1 + web/src/settings_config.ts | 15 ++++++ web/src/settings_preferences.ts | 3 ++ web/src/user_settings.ts | 1 + web/styles/rendered_markdown.css | 3 +- .../settings/display_settings_information.hbs | 7 +++ web/tests/dispatch.test.js | 20 +++++++ web/tests/lib/events.js | 21 ++++++++ web/tests/rendered_markdown.test.js | 22 +++++++- ...ult_web_animate_image_previews_and_more.py | 22 ++++++++ zerver/models/users.py | 4 ++ zerver/openapi/zulip.yaml | 52 +++++++++++++++++++ zerver/tests/test_audit_log.py | 1 + zerver/tests/test_events.py | 2 + zerver/tests/test_realm.py | 1 + zerver/tests/test_settings.py | 2 + zerver/views/realm.py | 1 + zerver/views/user_settings.py | 4 ++ 27 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 zerver/migrations/0558_realmuserdefault_web_animate_image_previews_and_more.py diff --git a/api_docs/changelog.md b/api_docs/changelog.md index 6eb250d328..dba6d76e08 100644 --- a/api_docs/changelog.md +++ b/api_docs/changelog.md @@ -20,6 +20,14 @@ format used by the Zulip server that they are interacting with. ## Changes in Zulip 9.0 +**Feature level 275** + +* [`POST /register`](/api/register-queue), [`PATCH + /settings`](/api/update-settings), [`PATCH + /realm/user_settings_defaults`](/api/update-realm-user-settings-defaults): + Added new `web_animate_image_previews` setting, which controls how + animated images should be played in the web/desktop app message feed. + **Feature level 274** * [`GET /events`](/api/get-events): `delete_message` events are now diff --git a/version.py b/version.py index de9464f90e..cdc2fce7a0 100644 --- a/version.py +++ b/version.py @@ -34,7 +34,7 @@ DESKTOP_WARNING_VERSION = "5.9.3" # new level means in api_docs/changelog.md, as well as "**Changes**" # entries in the endpoint's documentation in `zulip.yaml`. -API_FEATURE_LEVEL = 274 # Last bumped for `delete_message` event. +API_FEATURE_LEVEL = 275 # Last bumped for `web_animate_image_previews` setting. # Bump the minor PROVISION_VERSION to indicate that folks should provision diff --git a/web/src/admin.js b/web/src/admin.js index dc875f2ec1..2bbee868bb 100644 --- a/web/src/admin.js +++ b/web/src/admin.js @@ -194,6 +194,7 @@ export function build_page() { user_list_style_values: settings_config.user_list_style_values, web_stream_unreads_count_display_policy_values: settings_config.web_stream_unreads_count_display_policy_values, + web_animate_image_previews_values: settings_config.web_animate_image_previews_values, color_scheme_values: settings_config.color_scheme_values, web_home_view_values: settings_config.web_home_view_values, settings_object: realm_user_settings_defaults, diff --git a/web/src/click_handlers.js b/web/src/click_handlers.js index 61ce74c12f..2a1fa151e9 100644 --- a/web/src/click_handlers.js +++ b/web/src/click_handlers.js @@ -119,6 +119,7 @@ export function initialize() { // Inline image, video and twitter previews. if ( $target.is("img.message_inline_image") || + $target.is(".message_inline_animated_image_still") || $target.is("video") || $target.is(".message_inline_video") || $target.is("img.twitter-avatar") diff --git a/web/src/lightbox.ts b/web/src/lightbox.ts index 35eb9699ca..b6ff0e3b45 100644 --- a/web/src/lightbox.ts +++ b/web/src/lightbox.ts @@ -580,7 +580,7 @@ export function initialize(): void { $("#main_div, #compose .preview_content").on( "click", - ".message_inline_image:not(.message_inline_video) a", + ".message_inline_image:not(.message_inline_video) a, .message_inline_animated_image_still", function (e) { // prevent the link from opening in a new page. e.preventDefault(); diff --git a/web/src/message_list_hover.js b/web/src/message_list_hover.js index 9400a7ea3a..a59926f764 100644 --- a/web/src/message_list_hover.js +++ b/web/src/message_list_hover.js @@ -6,6 +6,8 @@ import render_edit_content_button from "../templates/edit_content_button.hbs"; import * as message_edit from "./message_edit"; import * as message_lists from "./message_lists"; import * as rows from "./rows"; +import * as thumbnail from "./thumbnail"; +import {user_settings} from "./user_settings"; let $current_message_hover; export function message_unhover() { @@ -73,6 +75,40 @@ export function initialize() { $row.removeClass("sender_info_hovered"); }); + $("#main_div").on( + "mouseover", + '.message-list div.message_inline_image img[data-animated="true"]', + function () { + if (user_settings.web_animate_image_previews !== "on_hover") { + return; + } + const $img = $(this); + $img.closest(".message_inline_image").removeClass( + "message_inline_animated_image_still", + ); + $img.attr( + "src", + $img.attr("src").replace(/\/[^/]+$/, "/" + thumbnail.animated_format.name), + ); + }, + ); + + $("#main_div").on( + "mouseout", + '.message-list div.message_inline_image img[data-animated="true"]', + function () { + if (user_settings.web_animate_image_previews !== "on_hover") { + return; + } + const $img = $(this); + $img.closest(".message_inline_image").addClass("message_inline_animated_image_still"); + $img.attr( + "src", + $img.attr("src").replace(/\/[^/]+$/, "/" + thumbnail.preferred_format.name), + ); + }, + ); + function handle_video_preview_mouseenter($elem) { // Set image height and css vars for play button position, if not done already const setPosition = !$elem.data("entered-before"); diff --git a/web/src/realm_user_settings_defaults.ts b/web/src/realm_user_settings_defaults.ts index e67562501b..ffbd4eb313 100644 --- a/web/src/realm_user_settings_defaults.ts +++ b/web/src/realm_user_settings_defaults.ts @@ -52,6 +52,7 @@ export const realm_default_settings_schema = z.object({ translate_emoticons: z.boolean(), twenty_four_hour_time: z.boolean(), user_list_style: z.number(), + web_animate_image_previews: z.string(), web_channel_default_view: z.number(), web_escape_navigates_to_home_view: z.boolean(), web_font_size_px: z.number(), diff --git a/web/src/rendered_markdown.ts b/web/src/rendered_markdown.ts index a96e0c1e8a..f9fc2cd75d 100644 --- a/web/src/rendered_markdown.ts +++ b/web/src/rendered_markdown.ts @@ -345,7 +345,21 @@ export const update_elements = ($content: JQuery): void => { const $inline_img_thumbnail = $(this); let thumbnail_name = thumbnail.preferred_format.name; if ($inline_img_thumbnail.attr("data-animated") === "true") { - thumbnail_name = thumbnail.animated_format.name; + if ( + user_settings.web_animate_image_previews === "always" || + // Treat on_hover as "always" on mobile web, where + // hovering is impossible and there's much less on + // the screen. + (user_settings.web_animate_image_previews === "on_hover" && util.is_mobile()) + ) { + thumbnail_name = thumbnail.animated_format.name; + } else { + // If we're showing a still thumbnail, show a play + // button so that users that it can be played. + $inline_img_thumbnail + .closest(".message_inline_image") + .addClass("message_inline_animated_image_still"); + } } $inline_img_thumbnail.attr( "src", diff --git a/web/src/server_events_dispatch.js b/web/src/server_events_dispatch.js index baccea2ac8..3df1434fe3 100644 --- a/web/src/server_events_dispatch.js +++ b/web/src/server_events_dispatch.js @@ -728,6 +728,7 @@ export function dispatch_normal_event(event) { "translate_emoticons", "display_emoji_reaction_users", "user_list_style", + "web_animate_image_previews", "web_stream_unreads_count_display_policy", "starred_message_counts", "send_stream_typing_notifications", @@ -779,6 +780,12 @@ export function dispatch_normal_event(event) { stream_list.update_streams_sidebar(); stream_list_sort.set_filter_out_inactives(); } + if (event.property === "web_animate_image_previews") { + // Rerender the whole message list UI + for (const msg_list of message_lists.all_rendered_message_lists()) { + msg_list.rerender(); + } + } if (event.property === "web_stream_unreads_count_display_policy") { stream_list.update_dom_unread_counts_visibility(); } diff --git a/web/src/settings.js b/web/src/settings.js index 187d3093db..0c7d6b4fb9 100644 --- a/web/src/settings.js +++ b/web/src/settings.js @@ -105,6 +105,7 @@ export function build_page() { settings_config.web_mark_read_on_scroll_policy_values, web_channel_default_view_values: settings_config.web_channel_default_view_values, user_list_style_values: settings_config.user_list_style_values, + web_animate_image_previews_values: settings_config.web_animate_image_previews_values, web_stream_unreads_count_display_policy_values: settings_config.web_stream_unreads_count_display_policy_values, color_scheme_values: settings_config.color_scheme_values, diff --git a/web/src/settings_config.ts b/web/src/settings_config.ts index 22ed27b57f..dfa589618d 100644 --- a/web/src/settings_config.ts +++ b/web/src/settings_config.ts @@ -84,6 +84,21 @@ export const user_list_style_values = { // }, }; +export const web_animate_image_previews_values = { + always: { + code: "always", + description: $t({defaultMessage: "Always"}), + }, + on_hover: { + code: "on_hover", + description: $t({defaultMessage: "On hover"}), + }, + never: { + code: "never", + description: $t({defaultMessage: "Only in image viewer"}), + }, +}; + export const web_stream_unreads_count_display_policy_values = { all_streams: { code: 1, diff --git a/web/src/settings_preferences.ts b/web/src/settings_preferences.ts index 38d0954b92..9f0d90de9b 100644 --- a/web/src/settings_preferences.ts +++ b/web/src/settings_preferences.ts @@ -230,6 +230,9 @@ export function set_up(settings_panel: SettingsPanel): void { .find(`.setting_user_list_style_choice[value=${settings_object.user_list_style}]`) .prop("checked", true); + $container + .find(".setting_web_animate_image_previews") + .val(settings_object.web_animate_image_previews); $container .find(".setting_web_stream_unreads_count_display_policy") .val(settings_object.web_stream_unreads_count_display_policy); diff --git a/web/src/user_settings.ts b/web/src/user_settings.ts index 4e5a8b755e..446856afc1 100644 --- a/web/src/user_settings.ts +++ b/web/src/user_settings.ts @@ -71,6 +71,7 @@ export const user_settings_schema = stream_notification_settings_schema translate_emoticons: z.boolean(), twenty_four_hour_time: z.boolean(), user_list_style: z.number(), + web_animate_image_previews: z.enum(["always", "on_hover", "never"]), web_channel_default_view: z.number(), web_escape_navigates_to_home_view: z.boolean(), web_font_size_px: z.number(), diff --git a/web/styles/rendered_markdown.css b/web/styles/rendered_markdown.css index 086e3fc37f..26966fbaa0 100644 --- a/web/styles/rendered_markdown.css +++ b/web/styles/rendered_markdown.css @@ -564,7 +564,8 @@ float: none; } - .message_inline_video { + .message_inline_video, + .message_inline_animated_image_still { &:hover { &::after { transform: scale(1); diff --git a/web/templates/settings/display_settings_information.hbs b/web/templates/settings/display_settings_information.hbs index f68d91f973..96bbbb1885 100644 --- a/web/templates/settings/display_settings_information.hbs +++ b/web/templates/settings/display_settings_information.hbs @@ -35,6 +35,13 @@ + +