thumbnail: Advertize the thumbnail formats at client registration.

This commit is contained in:
Alex Vandiver 2024-07-13 03:16:41 +00:00 committed by Tim Abbott
parent 6c624805ce
commit 556b92810b
9 changed files with 103 additions and 1 deletions

View File

@ -20,6 +20,11 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 9.0 ## Changes in Zulip 9.0
**Feature level 273**
* [`POST /register`](/api/register-queue): Added `server_thumbnail_formats`
describing what formats the server will thumbnail images into.
**Feature level 272** **Feature level 272**
* [`POST /user_uploads`](/api/upload-file): `uri` was renamed * [`POST /user_uploads`](/api/upload-file): `uri` was renamed

View File

@ -252,6 +252,7 @@ EXEMPT_FILES = make_set(
"web/src/submessage.ts", "web/src/submessage.ts",
"web/src/subscriber_api.ts", "web/src/subscriber_api.ts",
"web/src/theme.ts", "web/src/theme.ts",
"web/src/thumbnail.ts",
"web/src/timerender.ts", "web/src/timerender.ts",
"web/src/tippyjs.ts", "web/src/tippyjs.ts",
"web/src/todo_widget.js", "web/src/todo_widget.js",

View File

@ -34,7 +34,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
# new level means in api_docs/changelog.md, as well as "**Changes**" # new level means in api_docs/changelog.md, as well as "**Changes**"
# entries in the endpoint's documentation in `zulip.yaml`. # entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 272 # Last bumped for "POST /user_uploads" API_FEATURE_LEVEL = 273 # Last bumped for server_thumbnail_formats
# Bump the minor PROVISION_VERSION to indicate that folks should provision # Bump the minor PROVISION_VERSION to indicate that folks should provision

View File

@ -179,6 +179,14 @@ const one_time_notice_schema = z.object({
type: z.literal("one_time_notice"), type: z.literal("one_time_notice"),
}); });
export const thumbnail_format_schema = z.object({
name: z.string(),
max_width: z.number(),
max_height: z.number(),
format: z.string(),
animated: z.boolean(),
});
/* We may introduce onboarding step of types other than 'one time notice' /* We may introduce onboarding step of types other than 'one time notice'
in future. Earlier, we had 'hotspot' and 'one time notice' as the two in future. Earlier, we had 'hotspot' and 'one time notice' as the two
types. We can simply do: types. We can simply do:
@ -374,6 +382,7 @@ const realm_schema = z.object({
stream: z.record(group_permission_setting_schema), stream: z.record(group_permission_setting_schema),
group: z.record(group_permission_setting_schema), group: z.record(group_permission_setting_schema),
}), }),
server_thumbnail_formats: z.array(thumbnail_format_schema),
server_typing_started_expiry_period_milliseconds: z.number(), server_typing_started_expiry_period_milliseconds: z.number(),
server_typing_started_wait_period_milliseconds: z.number(), server_typing_started_wait_period_milliseconds: z.number(),
server_typing_stopped_wait_period_milliseconds: z.number(), server_typing_stopped_wait_period_milliseconds: z.number(),

36
web/src/thumbnail.ts Normal file
View File

@ -0,0 +1,36 @@
import type {z} from "zod";
import {realm} from "./state_data";
import type {thumbnail_format_schema} from "./state_data";
type ThumbnailFormat = z.infer<typeof thumbnail_format_schema>;
export const thumbnail_formats: ThumbnailFormat[] = [];
export let preferred_format: ThumbnailFormat;
export let animated_format: ThumbnailFormat;
export function initialize(): void {
// Go looking for the size closest to 300x200, of the smallest format. We assume all browsers
// support webp.
const format_preferences = ["webp", "jpg", "gif"];
const sorted_formats = realm.server_thumbnail_formats.sort((a, b) => {
if (a.max_width !== b.max_width) {
return Math.abs(a.max_width - 300) < Math.abs(b.max_width - 300) ? -1 : 1;
} else if (a.format !== b.format) {
let a_index = format_preferences.indexOf(a.format);
if (a_index === -1) {
a_index = format_preferences.length;
}
let b_index = format_preferences.indexOf(b.format);
if (b_index === -1) {
b_index = format_preferences.length;
}
return a_index - b_index;
}
return 0;
});
preferred_format = sorted_formats.find((format) => !format.animated)!;
animated_format = sorted_formats.find((format) => format.animated)!;
}

View File

@ -126,6 +126,7 @@ import * as stream_topic_history from "./stream_topic_history";
import * as stream_topic_history_util from "./stream_topic_history_util"; import * as stream_topic_history_util from "./stream_topic_history_util";
import * as sub_store from "./sub_store"; import * as sub_store from "./sub_store";
import * as theme from "./theme"; import * as theme from "./theme";
import * as thumbnail from "./thumbnail";
import * as timerender from "./timerender"; import * as timerender from "./timerender";
import * as tippyjs from "./tippyjs"; import * as tippyjs from "./tippyjs";
import * as topic_list from "./topic_list"; import * as topic_list from "./topic_list";
@ -426,6 +427,7 @@ export function initialize_everything(state_data) {
if (page_params.is_spectator) { if (page_params.is_spectator) {
theme.initialize_theme_for_spectator(); theme.initialize_theme_for_spectator();
} }
thumbnail.initialize();
widgets.initialize(); widgets.initialize();
tippyjs.initialize(); tippyjs.initialize();
compose_tooltips.initialize(); compose_tooltips.initialize();

View File

@ -54,6 +54,7 @@ from zerver.lib.subscription_info import (
gather_subscriptions_helper, gather_subscriptions_helper,
get_web_public_subs, get_web_public_subs,
) )
from zerver.lib.thumbnail import THUMBNAIL_OUTPUT_FORMATS
from zerver.lib.timestamp import datetime_to_timestamp from zerver.lib.timestamp import datetime_to_timestamp
from zerver.lib.timezone import canonicalize_timezone from zerver.lib.timezone import canonicalize_timezone
from zerver.lib.topic import TOPIC_NAME from zerver.lib.topic import TOPIC_NAME
@ -381,6 +382,16 @@ def fetch_initial_state_data(
state["password_min_guesses"] = settings.PASSWORD_MIN_GUESSES state["password_min_guesses"] = settings.PASSWORD_MIN_GUESSES
state["server_inline_image_preview"] = settings.INLINE_IMAGE_PREVIEW state["server_inline_image_preview"] = settings.INLINE_IMAGE_PREVIEW
state["server_inline_url_embed_preview"] = settings.INLINE_URL_EMBED_PREVIEW state["server_inline_url_embed_preview"] = settings.INLINE_URL_EMBED_PREVIEW
state["server_thumbnail_formats"] = [
{
"name": str(thumbnail_format),
"max_width": thumbnail_format.max_width,
"max_height": thumbnail_format.max_height,
"format": thumbnail_format.extension,
"animated": thumbnail_format.animated,
}
for thumbnail_format in THUMBNAIL_OUTPUT_FORMATS
]
state["server_avatar_changes_disabled"] = settings.AVATAR_CHANGES_DISABLED state["server_avatar_changes_disabled"] = settings.AVATAR_CHANGES_DISABLED
state["server_name_changes_disabled"] = settings.NAME_CHANGES_DISABLED state["server_name_changes_disabled"] = settings.NAME_CHANGES_DISABLED
state["server_web_public_streams_enabled"] = settings.WEB_PUBLIC_STREAMS_ENABLED state["server_web_public_streams_enabled"] = settings.WEB_PUBLIC_STREAMS_ENABLED

View File

@ -16397,6 +16397,43 @@ paths:
Clients containing administrative UI for changing Clients containing administrative UI for changing
`realm_inline_url_embed_preview` should consult this field before offering `realm_inline_url_embed_preview` should consult this field before offering
that feature. that feature.
server_thumbnail_formats:
description: |
A list describing the image formats that uploaded
images will be thumbnailed into. Any image with a
source starting with `/user_uploads/thumbnail/` can
have its last path component replaced with any of the
names contained in this list, to obtain the desired
thumbnail size.
**Changes**: New in Zulip 9.0 (feature level 273).
type: array
items:
type: object
additionalProperties: false
properties:
name:
type: string
description: |
The file path component of the thumbnail format.
max_width:
type: integer
description: |
The maximum width of this format.
max_height:
type: integer
description: |
The maximum height of this format.
format:
type: string
description: |
The extension of this format.
animated:
type: boolean
description: |
If this file format is animated. These formats
are only generated for uploaded imates which
themselves are animated.
server_avatar_changes_disabled: server_avatar_changes_disabled:
type: boolean type: boolean
description: | description: |

View File

@ -219,6 +219,7 @@ class HomeTest(ZulipTestCase):
"server_presence_offline_threshold_seconds", "server_presence_offline_threshold_seconds",
"server_presence_ping_interval_seconds", "server_presence_ping_interval_seconds",
"server_supported_permission_settings", "server_supported_permission_settings",
"server_thumbnail_formats",
"server_timestamp", "server_timestamp",
"server_typing_started_expiry_period_milliseconds", "server_typing_started_expiry_period_milliseconds",
"server_typing_started_wait_period_milliseconds", "server_typing_started_wait_period_milliseconds",