settings: Add new web information density settings.

Note that these settings are not operative at present, and are only
visible in the settings UI in the development environment.
This commit is contained in:
Karl Stolley 2024-03-06 10:25:31 -06:00 committed by Tim Abbott
parent 8f0f41e236
commit 286d44bf33
21 changed files with 247 additions and 13 deletions

View File

@ -20,6 +20,16 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 9.0 ## Changes in Zulip 9.0
**Feature level 245**
* [`PATCH
/realm/user_settings_defaults`](/api/update-realm-user-settings-defaults)
[`POST /register`](/api/register-queue), [`GET
/events`](/api/get-events), [`PATCH
/settings`](/api/update-settings): Added new `web_font_size_px` and
`web_line_height_percent` settings to allow users to control the
styling of the web application.
**Feature level 244** **Feature level 244**
* [`POST /register`](/api/register-queue), [`GET /events`](/api/get-events), * [`POST /register`](/api/register-queue), [`GET /events`](/api/get-events),

View File

@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
# Changes should be accompanied by documentation explaining what the # Changes should be accompanied by documentation explaining what the
# 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 = 244 API_FEATURE_LEVEL = 245
# Bump the minor PROVISION_VERSION to indicate that folks should provision # Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump # only when going from an old version of the code to a newer version. Bump

View File

@ -190,6 +190,7 @@ export function build_page() {
web_home_view_values: settings_config.web_home_view_values, web_home_view_values: settings_config.web_home_view_values,
settings_object: realm_user_settings_defaults, settings_object: realm_user_settings_defaults,
display_settings: settings_config.get_all_preferences(), display_settings: settings_config.get_all_preferences(),
information_density_settings: settings_config.get_information_density_preferences(),
settings_label: settings_config.realm_user_settings_defaults_labels, settings_label: settings_config.realm_user_settings_defaults_labels,
desktop_icon_count_display_values: settings_config.desktop_icon_count_display_values, desktop_icon_count_display_values: settings_config.desktop_icon_count_display_values,
enable_sound_select: enable_sound_select:

View File

@ -1,5 +1,7 @@
export type RealmDefaultSettings = { export type RealmDefaultSettings = {
color_scheme: number; color_scheme: number;
web_font_size_px: number;
web_line_height_percent: number;
default_language: string; default_language: string;
web_home_view: string; web_home_view: string;
desktop_icon_count_display: number; desktop_icon_count_display: number;

View File

@ -693,6 +693,8 @@ export function dispatch_normal_event(event) {
const user_display_settings = [ const user_display_settings = [
"color_scheme", "color_scheme",
"web_font_size_px",
"web_line_height_percent",
"default_language", "default_language",
"web_home_view", "web_home_view",
"demote_inactive_streams", "demote_inactive_streams",

View File

@ -119,6 +119,7 @@ export function build_page() {
show_push_notifications_tooltip: show_push_notifications_tooltip:
settings_config.all_notifications(user_settings).show_push_notifications_tooltip, settings_config.all_notifications(user_settings).show_push_notifications_tooltip,
display_settings: settings_config.get_all_preferences(), display_settings: settings_config.get_all_preferences(),
information_density_settings: settings_config.get_information_density_preferences(),
user_can_change_name: settings_data.user_can_change_name(), user_can_change_name: settings_data.user_can_change_name(),
user_can_change_avatar: settings_data.user_can_change_avatar(), user_can_change_avatar: settings_data.user_can_change_avatar(),
user_can_change_email: settings_data.user_can_change_email(), user_can_change_email: settings_data.user_can_change_email(),

View File

@ -133,9 +133,12 @@ export type DisplaySettings = {
settings: { settings: {
user_display_settings: string[]; user_display_settings: string[];
}; };
render_group?: boolean;
render_only: { render_only: {
high_contrast_mode: boolean; high_contrast_mode?: boolean;
dense_mode: boolean; dense_mode?: boolean;
web_font_size_px?: boolean;
web_line_height_percent?: boolean;
}; };
}; };
@ -155,6 +158,18 @@ export const get_all_preferences = (): DisplaySettings => ({
}, },
}); });
/* istanbul ignore next */
export const get_information_density_preferences = (): DisplaySettings => ({
render_group: page_params.development_environment,
render_only: {
web_font_size_px: page_params.development_environment,
web_line_height_percent: page_params.development_environment,
},
settings: {
user_display_settings: ["web_font_size_px", "web_line_height_percent"],
},
});
export const email_address_visibility_values = { export const email_address_visibility_values = {
everyone: { everyone: {
code: 1, code: 1,
@ -555,6 +570,8 @@ export const preferences_settings_labels = {
}), }),
), ),
web_escape_navigates_to_home_view: $t({defaultMessage: "Escape key navigates to home view"}), web_escape_navigates_to_home_view: $t({defaultMessage: "Escape key navigates to home view"}),
web_font_size_px: $t({defaultMessage: "Message-area font size (px)"}),
web_line_height_percent: $t({defaultMessage: "Message-area line height (%)"}),
default_language_settings_label: $t({defaultMessage: "Language"}), default_language_settings_label: $t({defaultMessage: "Language"}),
}; };

View File

@ -193,16 +193,20 @@ export function set_up(settings_panel) {
// Common handler for sending requests to the server when an input // Common handler for sending requests to the server when an input
// element is changed. // element is changed.
$container.on("change", "input[type=checkbox], select", function (e) { $container.on(
const $input_elem = $(e.currentTarget); "change",
const setting = $input_elem.attr("name"); "input[type=checkbox], .information-density-settings input[type=text], select",
const data = {}; function (e) {
data[setting] = settings_components.get_input_element_value(this); const $input_elem = $(e.currentTarget);
const $status_element = $input_elem const setting = $input_elem.attr("name");
.closest(".subsection-parent") const data = {};
.find(".alert-notification"); data[setting] = settings_components.get_input_element_value(this);
change_display_setting(data, $status_element); const $status_element = $input_elem
}); .closest(".subsection-parent")
.find(".alert-notification");
change_display_setting(data, $status_element);
},
);
$container.find(".setting_emojiset_choice").on("click", function () { $container.find(".setting_emojiset_choice").on("click", function () {
const data = {emojiset: $(this).val()}; const data = {emojiset: $(this).val()};

View File

@ -60,6 +60,8 @@ export type UserSettings = (StreamNotificationSettings &
automatically_unmute_topics_in_muted_streams_policy: number; automatically_unmute_topics_in_muted_streams_policy: number;
automatically_follow_topics_where_mentioned: boolean; automatically_follow_topics_where_mentioned: boolean;
timezone: string; timezone: string;
web_font_size_px: number;
web_line_height_percent: number;
}; };
export let user_settings: UserSettings; export let user_settings: UserSettings;

View File

@ -493,6 +493,27 @@ input[type="checkbox"] {
} }
} }
.information-density-settings {
max-width: 325px;
.input-group {
display: flex;
gap: 5px;
align-items: baseline;
label {
flex: 1 0 max-content;
}
.settings_text_input {
width: 0;
flex: 0 0 8ch;
margin-left: auto;
text-align: right;
}
}
}
#stream-specific-notify-table .unmute_stream { #stream-specific-notify-table .unmute_stream {
position: relative; position: relative;
left: 3px; left: 3px;

View File

@ -155,6 +155,20 @@
</select> </select>
</div> </div>
{{#if information_density_settings.render_group}}
<div class="information-density-settings">
<div class="title">{{t "Information density settings"}}</div>
{{#each information_density_settings.settings.user_display_settings}}
{{> settings_numeric_input
setting_name=this
setting_value=(lookup ../settings_object this)
label=(lookup ../settings_label this)
render_only=(lookup ../information_density_settings.render_only this)
prefix=../prefix}}
{{/each}}
</div>
{{/if}}
{{#each display_settings.settings.user_display_settings}} {{#each display_settings.settings.user_display_settings}}
{{> settings_checkbox {{> settings_checkbox
setting_name=this setting_name=this

View File

@ -0,0 +1,18 @@
{{#unless (eq render_only false)}}
<div class="input-group setting-next-is-related {{#if is_disabled}}control-label-disabled{{/if}}">
<label for="{{#if prefix}}{{prefix}}{{/if}}{{setting_name}}" class="{{setting_name}}_label" id="{{#if prefix}}{{prefix}}{{/if}}{{setting_name}}_label">
{{label}}
{{#if label_parens_text}}
(<i>{{label_parens_text}}</i>)
{{/if}}
{{#if help_link}}
{{> ../help_link_widget link=help_link }}
{{/if}}
{{#if tooltip_text}}
<i class="tippy-zulip-tooltip fa fa-info-circle settings-info-icon" {{#if hide_tooltip}}style="display: none;"{{/if}} data-tippy-content="{{tooltip_text}}"></i>
{{/if}}
</label>
<input type="text" inputmode="numeric" pattern="\d*" value="{{setting_value}}" class="{{setting_name}} settings_text_input setting-widget prop-element" name="{{setting_name}}" data-setting-widget-type="number"
id="{{#if prefix}}{{prefix}}{{/if}}{{setting_name}}" {{#if is_disabled}}disabled{{/if}} />
</div>
{{/unless}}

View File

@ -89,6 +89,9 @@ run_test("tr_tag", ({mock_template}) => {
const args = { const args = {
botserverrc: "botserverrc", botserverrc: "botserverrc",
date_joined_text: "Mar 21, 2022", date_joined_text: "Mar 21, 2022",
information_density_settings: {
settings: {},
},
display_settings: { display_settings: {
settings: {}, settings: {},
}, },

View File

@ -0,0 +1,32 @@
# Generated by Django 4.2.11 on 2024-03-26 20:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("zerver", "0504_customprofilefield_required"),
]
operations = [
migrations.AddField(
model_name="realmuserdefault",
name="web_font_size_px",
field=models.PositiveSmallIntegerField(default=14),
),
migrations.AddField(
model_name="realmuserdefault",
name="web_line_height_percent",
field=models.PositiveSmallIntegerField(default=122),
),
migrations.AddField(
model_name="userprofile",
name="web_font_size_px",
field=models.PositiveSmallIntegerField(default=14),
),
migrations.AddField(
model_name="userprofile",
name="web_line_height_percent",
field=models.PositiveSmallIntegerField(default=122),
),
]

View File

@ -78,6 +78,15 @@ class UserBaseSettings(models.Model):
COLOR_SCHEME_CHOICES = [COLOR_SCHEME_AUTOMATIC, COLOR_SCHEME_NIGHT, COLOR_SCHEME_LIGHT] COLOR_SCHEME_CHOICES = [COLOR_SCHEME_AUTOMATIC, COLOR_SCHEME_NIGHT, COLOR_SCHEME_LIGHT]
color_scheme = models.PositiveSmallIntegerField(default=COLOR_SCHEME_AUTOMATIC) color_scheme = models.PositiveSmallIntegerField(default=COLOR_SCHEME_AUTOMATIC)
# Information density is established through
# adjustments to the font size and line height.
WEB_FONT_SIZE_PX_LEGACY = 14
WEB_LINE_HEIGHT_PERCENT_LEGACY = 122
web_font_size_px = models.PositiveSmallIntegerField(default=WEB_FONT_SIZE_PX_LEGACY)
web_line_height_percent = models.PositiveSmallIntegerField(
default=WEB_LINE_HEIGHT_PERCENT_LEGACY
)
# UI setting controlling Zulip's behavior of demoting in the sort # UI setting controlling Zulip's behavior of demoting in the sort
# order and graying out streams with no recent traffic. The # order and graying out streams with no recent traffic. The
# default behavior, automatic, enables this behavior once a user # default behavior, automatic, enables this behavior once a user
@ -314,6 +323,8 @@ class UserBaseSettings(models.Model):
web_mark_read_on_scroll_policy=int, web_mark_read_on_scroll_policy=int,
user_list_style=int, user_list_style=int,
web_stream_unreads_count_display_policy=int, web_stream_unreads_count_display_policy=int,
web_font_size_px=int,
web_line_height_percent=int,
) )
modern_notification_settings: Dict[str, Any] = dict( modern_notification_settings: Dict[str, Any] = dict(

View File

@ -10549,6 +10549,25 @@ paths:
- 2 - 2
- 3 - 3
example: 1 example: 1
web_font_size_px:
description: |
User-configured primary `font-size` for the web application, in pixels.
**Changes**: New in Zulip 9.0 (feature level 245). Previously, font size was
only adjustable via browser zoom. Note that this setting was not fully
implemented at this feature level.
type: integer
example: 14
web_line_height_percent:
description: |
User-configured primary `line-height` for the web application, in percent, so a
value of 120 represents a `line-height` of 1.2.
**Changes**: New in Zulip 9.0 (feature level 245). Previously, line height was
not user-configurable. Note that this setting was not fully implemented at this
feature level.
type: integer
example: 122
color_scheme: color_scheme:
description: | description: |
Controls which [color theme](/help/dark-theme) to use. Controls which [color theme](/help/dark-theme) to use.
@ -10953,6 +10972,10 @@ paths:
contentType: application/json contentType: application/json
web_mark_read_on_scroll_policy: web_mark_read_on_scroll_policy:
contentType: application/json contentType: application/json
web_font_size_px:
contentType: application/json
web_line_height_percent:
contentType: application/json
color_scheme: color_scheme:
contentType: application/json contentType: application/json
enable_drafts_synchronization: enable_drafts_synchronization:
@ -12991,6 +13014,25 @@ paths:
description: | description: |
This setting is reserved for use to control variations in Zulip's design This setting is reserved for use to control variations in Zulip's design
to help visually impaired users. to help visually impaired users.
web_font_size_px:
description: |
User-configured primary `font-size` for the web application, in pixels.
**Changes**: New in Zulip 9.0 (feature level 245). Previously, font size was
only adjustable via browser zoom. Note that this setting was not fully
implemented at this feature level.
type: integer
example: 14
web_line_height_percent:
description: |
User-configured primary `line-height` for the web application, in percent, so a
value of 120 represents a `line-height` of 1.2.
**Changes**: New in Zulip 9.0 (feature level 245). Previously, line height was
not user-configurable. Note that this setting was not fully implemented at this
feature level.
type: integer
example: 122
color_scheme: color_scheme:
type: integer type: integer
description: | description: |
@ -15263,6 +15305,25 @@ paths:
description: | description: |
This setting is reserved for use to control variations in Zulip's design This setting is reserved for use to control variations in Zulip's design
to help visually impaired users. to help visually impaired users.
web_font_size_px:
description: |
User-configured primary `font-size` for the web application, in pixels.
**Changes**: New in Zulip 9.0 (feature level 245). Previously, font size was
only adjustable via browser zoom. Note that this setting was not fully
implemented at this feature level.
type: integer
example: 14
web_line_height_percent:
description: |
User-configured primary `line-height` for the web application, in percent, so a
value of 120 represents a `line-height` of 1.2.
**Changes**: New in Zulip 9.0 (feature level 245). Previously, line height was
not user-configurable. Note that this setting was not fully implemented at this
feature level.
type: integer
example: 122
color_scheme: color_scheme:
type: integer type: integer
description: | description: |
@ -16337,6 +16398,25 @@ paths:
the `PATCH /settings/display` endpoint. the `PATCH /settings/display` endpoint.
type: boolean type: boolean
example: true example: true
web_font_size_px:
description: |
User-configured primary `font-size` for the web application, in pixels.
**Changes**: New in Zulip 9.0 (feature level 245). Previously, font size was
only adjustable via browser zoom. Note that this setting was not fully
implemented at this feature level.
type: integer
example: 14
web_line_height_percent:
description: |
User-configured primary `line-height` for the web application, in percent, so a
value of 120 represents a `line-height` of 1.2.
**Changes**: New in Zulip 9.0 (feature level 245). Previously, line height was
not user-configurable. Note that this setting was not fully implemented at this
feature level.
type: integer
example: 122
color_scheme: color_scheme:
description: | description: |
Controls which [color theme](/help/dark-theme) to use. Controls which [color theme](/help/dark-theme) to use.
@ -16859,6 +16939,10 @@ paths:
contentType: application/json contentType: application/json
high_contrast_mode: high_contrast_mode:
contentType: application/json contentType: application/json
web_font_size_px:
contentType: application/json
web_line_height_percent:
contentType: application/json
color_scheme: color_scheme:
contentType: application/json contentType: application/json
enable_drafts_synchronization: enable_drafts_synchronization:

View File

@ -3713,6 +3713,8 @@ class RealmPropertyActionTest(BaseAction):
def do_set_realm_user_default_setting_test(self, name: str) -> None: def do_set_realm_user_default_setting_test(self, name: str) -> None:
bool_tests: List[bool] = [True, False, True] bool_tests: List[bool] = [True, False, True]
test_values: Dict[str, Any] = dict( test_values: Dict[str, Any] = dict(
web_font_size_px=[UserProfile.WEB_FONT_SIZE_PX_LEGACY],
web_line_height_percent=[UserProfile.WEB_LINE_HEIGHT_PERCENT_LEGACY],
color_scheme=UserProfile.COLOR_SCHEME_CHOICES, color_scheme=UserProfile.COLOR_SCHEME_CHOICES,
web_home_view=["recent_topics", "inbox", "all_messages"], web_home_view=["recent_topics", "inbox", "all_messages"],
emojiset=[emojiset["key"] for emojiset in RealmUserDefault.emojiset_choices()], emojiset=[emojiset["key"] for emojiset in RealmUserDefault.emojiset_choices()],
@ -3854,6 +3856,8 @@ class UserDisplayActionTest(BaseAction):
web_mark_read_on_scroll_policy=[2, 3, 1], web_mark_read_on_scroll_policy=[2, 3, 1],
user_list_style=[1, 2, 3], user_list_style=[1, 2, 3],
web_stream_unreads_count_display_policy=[1, 2, 3], web_stream_unreads_count_display_policy=[1, 2, 3],
web_font_size_px=[12, 16, 18],
web_line_height_percent=[105, 120, 160],
color_scheme=[2, 3, 1], color_scheme=[2, 3, 1],
email_address_visibility=[5, 4, 1, 2, 3], email_address_visibility=[5, 4, 1, 2, 3],
) )

View File

@ -1579,6 +1579,8 @@ class RealmAPITest(ZulipTestCase):
def do_test_realm_default_setting_update_api(self, name: str) -> None: def do_test_realm_default_setting_update_api(self, name: str) -> None:
bool_tests: List[bool] = [False, True] bool_tests: List[bool] = [False, True]
test_values: Dict[str, Any] = dict( test_values: Dict[str, Any] = dict(
web_font_size_px=[UserProfile.WEB_FONT_SIZE_PX_LEGACY],
web_line_height_percent=[UserProfile.WEB_LINE_HEIGHT_PERCENT_LEGACY],
color_scheme=UserProfile.COLOR_SCHEME_CHOICES, color_scheme=UserProfile.COLOR_SCHEME_CHOICES,
web_home_view=["recent_topics", "inbox", "all_messages"], web_home_view=["recent_topics", "inbox", "all_messages"],
emojiset=[emojiset["key"] for emojiset in RealmUserDefault.emojiset_choices()], emojiset=[emojiset["key"] for emojiset in RealmUserDefault.emojiset_choices()],

View File

@ -352,6 +352,8 @@ class ChangeSettingsTest(ZulipTestCase):
web_mark_read_on_scroll_policy=2, web_mark_read_on_scroll_policy=2,
user_list_style=2, user_list_style=2,
web_stream_unreads_count_display_policy=2, web_stream_unreads_count_display_policy=2,
web_font_size_px=14,
web_line_height_percent=122,
color_scheme=2, color_scheme=2,
email_notifications_batching_period_seconds=100, email_notifications_batching_period_seconds=100,
notification_sound="ding", notification_sound="ding",

View File

@ -529,6 +529,8 @@ def update_realm_user_settings_defaults(
color_scheme: Optional[int] = REQ( color_scheme: Optional[int] = REQ(
json_validator=check_int_in(UserProfile.COLOR_SCHEME_CHOICES), default=None json_validator=check_int_in(UserProfile.COLOR_SCHEME_CHOICES), default=None
), ),
web_font_size_px: Optional[int] = REQ(json_validator=check_int, default=None),
web_line_height_percent: Optional[int] = REQ(json_validator=check_int, default=None),
translate_emoticons: Optional[bool] = REQ(json_validator=check_bool, default=None), translate_emoticons: Optional[bool] = REQ(json_validator=check_bool, default=None),
display_emoji_reaction_users: Optional[bool] = REQ(json_validator=check_bool, default=None), display_emoji_reaction_users: Optional[bool] = REQ(json_validator=check_bool, default=None),
web_home_view: Optional[str] = REQ( web_home_view: Optional[str] = REQ(

View File

@ -213,6 +213,8 @@ def json_change_settings(
color_scheme: Optional[int] = REQ( color_scheme: Optional[int] = REQ(
json_validator=check_int_in(UserProfile.COLOR_SCHEME_CHOICES), default=None json_validator=check_int_in(UserProfile.COLOR_SCHEME_CHOICES), default=None
), ),
web_font_size_px: Optional[int] = REQ(json_validator=check_int, default=None),
web_line_height_percent: Optional[int] = REQ(json_validator=check_int, default=None),
translate_emoticons: Optional[bool] = REQ(json_validator=check_bool, default=None), translate_emoticons: Optional[bool] = REQ(json_validator=check_bool, default=None),
display_emoji_reaction_users: Optional[bool] = REQ(json_validator=check_bool, default=None), display_emoji_reaction_users: Optional[bool] = REQ(json_validator=check_bool, default=None),
default_language: Optional[str] = REQ(default=None), default_language: Optional[str] = REQ(default=None),