mirror of https://github.com/zulip/zulip.git
thumbnail: Advertize the thumbnail formats at client registration.
This commit is contained in:
parent
6c624805ce
commit
556b92810b
|
@ -20,6 +20,11 @@ format used by the Zulip server that they are interacting with.
|
|||
|
||||
## 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**
|
||||
|
||||
* [`POST /user_uploads`](/api/upload-file): `uri` was renamed
|
||||
|
|
|
@ -252,6 +252,7 @@ EXEMPT_FILES = make_set(
|
|||
"web/src/submessage.ts",
|
||||
"web/src/subscriber_api.ts",
|
||||
"web/src/theme.ts",
|
||||
"web/src/thumbnail.ts",
|
||||
"web/src/timerender.ts",
|
||||
"web/src/tippyjs.ts",
|
||||
"web/src/todo_widget.js",
|
||||
|
|
|
@ -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 = 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
|
||||
|
|
|
@ -179,6 +179,14 @@ const one_time_notice_schema = z.object({
|
|||
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'
|
||||
in future. Earlier, we had 'hotspot' and 'one time notice' as the two
|
||||
types. We can simply do:
|
||||
|
@ -374,6 +382,7 @@ const realm_schema = z.object({
|
|||
stream: 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_wait_period_milliseconds: z.number(),
|
||||
server_typing_stopped_wait_period_milliseconds: z.number(),
|
||||
|
|
|
@ -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)!;
|
||||
}
|
|
@ -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 sub_store from "./sub_store";
|
||||
import * as theme from "./theme";
|
||||
import * as thumbnail from "./thumbnail";
|
||||
import * as timerender from "./timerender";
|
||||
import * as tippyjs from "./tippyjs";
|
||||
import * as topic_list from "./topic_list";
|
||||
|
@ -426,6 +427,7 @@ export function initialize_everything(state_data) {
|
|||
if (page_params.is_spectator) {
|
||||
theme.initialize_theme_for_spectator();
|
||||
}
|
||||
thumbnail.initialize();
|
||||
widgets.initialize();
|
||||
tippyjs.initialize();
|
||||
compose_tooltips.initialize();
|
||||
|
|
|
@ -54,6 +54,7 @@ from zerver.lib.subscription_info import (
|
|||
gather_subscriptions_helper,
|
||||
get_web_public_subs,
|
||||
)
|
||||
from zerver.lib.thumbnail import THUMBNAIL_OUTPUT_FORMATS
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.lib.timezone import canonicalize_timezone
|
||||
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["server_inline_image_preview"] = settings.INLINE_IMAGE_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_name_changes_disabled"] = settings.NAME_CHANGES_DISABLED
|
||||
state["server_web_public_streams_enabled"] = settings.WEB_PUBLIC_STREAMS_ENABLED
|
||||
|
|
|
@ -16397,6 +16397,43 @@ paths:
|
|||
Clients containing administrative UI for changing
|
||||
`realm_inline_url_embed_preview` should consult this field before offering
|
||||
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:
|
||||
type: boolean
|
||||
description: |
|
||||
|
|
|
@ -219,6 +219,7 @@ class HomeTest(ZulipTestCase):
|
|||
"server_presence_offline_threshold_seconds",
|
||||
"server_presence_ping_interval_seconds",
|
||||
"server_supported_permission_settings",
|
||||
"server_thumbnail_formats",
|
||||
"server_timestamp",
|
||||
"server_typing_started_expiry_period_milliseconds",
|
||||
"server_typing_started_wait_period_milliseconds",
|
||||
|
|
Loading…
Reference in New Issue