user_settings: Add option to configure channel click behavior.

This commit does not implement the setting's behavior, just the API
change and settings UI.
This commit is contained in:
Aman Agrawal 2024-07-03 10:43:47 +00:00 committed by Tim Abbott
parent 7e9a01ea7f
commit 9fc6b93347
20 changed files with 157 additions and 1 deletions

View File

@ -20,6 +20,14 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 9.0 ## Changes in Zulip 9.0
**Feature level 269**
* [`POST /register`](/api/register-queue), [`PATCH
/settings`](/api/update-settings), [`PATCH
/realm/user_settings_defaults`](/api/update-realm-user-settings-defaults):
Added new user setting `web_channel_default_view`, controlling the
behavior of clicking a channel link in the web/desktop apps.
**Feature level 268** **Feature level 268**
* [`PATCH /realm/user_settings_defaults`](/api/update-realm-user-settings-defaults), * [`PATCH /realm/user_settings_defaults`](/api/update-realm-user-settings-defaults),

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 = 268 API_FEATURE_LEVEL = 269
# 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

@ -188,6 +188,7 @@ export function build_page() {
demote_inactive_streams_values: settings_config.demote_inactive_streams_values, demote_inactive_streams_values: settings_config.demote_inactive_streams_values,
web_mark_read_on_scroll_policy_values: web_mark_read_on_scroll_policy_values:
settings_config.web_mark_read_on_scroll_policy_values, 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, user_list_style_values: settings_config.user_list_style_values,
web_stream_unreads_count_display_policy_values: web_stream_unreads_count_display_policy_values:
settings_config.web_stream_unreads_count_display_policy_values, settings_config.web_stream_unreads_count_display_policy_values,

View File

@ -52,6 +52,7 @@ export const realm_default_settings_schema = z.object({
translate_emoticons: z.boolean(), translate_emoticons: z.boolean(),
twenty_four_hour_time: z.boolean(), twenty_four_hour_time: z.boolean(),
user_list_style: z.number(), user_list_style: z.number(),
web_channel_default_view: z.number(),
web_escape_navigates_to_home_view: z.boolean(), web_escape_navigates_to_home_view: z.boolean(),
web_font_size_px: z.number(), web_font_size_px: z.number(),
web_home_view: z.string(), web_home_view: z.string(),

View File

@ -704,6 +704,7 @@ export function dispatch_normal_event(event) {
"demote_inactive_streams", "demote_inactive_streams",
"dense_mode", "dense_mode",
"web_mark_read_on_scroll_policy", "web_mark_read_on_scroll_policy",
"web_channel_default_view",
"emojiset", "emojiset",
"web_escape_navigates_to_home_view", "web_escape_navigates_to_home_view",
"fluid_layout_width", "fluid_layout_width",

View File

@ -103,6 +103,7 @@ export function build_page() {
demote_inactive_streams_values: settings_config.demote_inactive_streams_values, demote_inactive_streams_values: settings_config.demote_inactive_streams_values,
web_mark_read_on_scroll_policy_values: web_mark_read_on_scroll_policy_values:
settings_config.web_mark_read_on_scroll_policy_values, 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, user_list_style_values: settings_config.user_list_style_values,
web_stream_unreads_count_display_policy_values: web_stream_unreads_count_display_policy_values:
settings_config.web_stream_unreads_count_display_policy_values, settings_config.web_stream_unreads_count_display_policy_values,

View File

@ -57,6 +57,17 @@ export const web_mark_read_on_scroll_policy_values = {
}, },
}; };
export const web_channel_default_view_values = {
top_topic_in_channel: {
code: 1,
description: $t({defaultMessage: "Top topic in the channel"}),
},
channel_feed: {
code: 2,
description: $t({defaultMessage: "Channel feed"}),
},
};
export const user_list_style_values = { export const user_list_style_values = {
compact: { compact: {
code: 1, code: 1,

View File

@ -208,6 +208,9 @@ export function set_up(settings_panel: SettingsPanel): void {
$container $container
.find(".setting_web_mark_read_on_scroll_policy") .find(".setting_web_mark_read_on_scroll_policy")
.val(settings_object.web_mark_read_on_scroll_policy); .val(settings_object.web_mark_read_on_scroll_policy);
$container
.find(".setting_web_channel_default_view")
.val(settings_object.web_channel_default_view);
$container $container
.find(`.setting_emojiset_choice[value="${CSS.escape(settings_object.emojiset)}"]`) .find(`.setting_emojiset_choice[value="${CSS.escape(settings_object.emojiset)}"]`)
.prop("checked", true); .prop("checked", true);

View File

@ -71,6 +71,7 @@ export const user_settings_schema = stream_notification_settings_schema
translate_emoticons: z.boolean(), translate_emoticons: z.boolean(),
twenty_four_hour_time: z.boolean(), twenty_four_hour_time: z.boolean(),
user_list_style: z.number(), user_list_style: z.number(),
web_channel_default_view: z.number(),
web_escape_navigates_to_home_view: z.boolean(), web_escape_navigates_to_home_view: z.boolean(),
web_font_size_px: z.number(), web_font_size_px: z.number(),
web_home_view: z.enum(["inbox", "recent_topics", "all_messages"]), web_home_view: z.enum(["inbox", "recent_topics", "all_messages"]),

View File

@ -145,6 +145,13 @@
label=settings_label.web_navigate_to_sent_message label=settings_label.web_navigate_to_sent_message
prefix=prefix}} prefix=prefix}}
<div class="input-group">
<label for="web_channel_default_view" class="settings-field-label">{{t "Channel links in the left sidebar go to" }}</label>
<select name="web_channel_default_view" class="setting_web_channel_default_view prop-element settings_select bootstrap-focus-style" id="{{prefix}}web_channel_default_view" data-setting-widget-type="number">
{{> dropdown_options_widget option_values=web_channel_default_view_values}}
</select>
</div>
<div class="input-group"> <div class="input-group">
<label for="demote_inactive_streams" class="settings-field-label">{{t "Demote inactive channels" }} <label for="demote_inactive_streams" class="settings-field-label">{{t "Demote inactive channels" }}
{{> ../help_link_widget link="/help/manage-inactive-channels" }} {{> ../help_link_widget link="/help/manage-inactive-channels" }}

View File

@ -945,6 +945,11 @@ run_test("user_settings", ({override}) => {
dispatch(event); dispatch(event);
assert_same(user_settings.web_mark_read_on_scroll_policy, 1); assert_same(user_settings.web_mark_read_on_scroll_policy, 1);
event = event_fixtures.user_settings__web_channel_default_view;
user_settings.web_channel_default_view = 2;
dispatch(event);
assert_same(user_settings.web_channel_default_view, 1);
event = event_fixtures.user_settings__dense_mode; event = event_fixtures.user_settings__dense_mode;
user_settings.dense_mode = false; user_settings.dense_mode = false;
override(information_density, "set_base_typography_css_variables", noop); override(information_density, "set_base_typography_css_variables", noop);

View File

@ -1022,6 +1022,13 @@ exports.fixtures = {
value: 2, value: 2,
}, },
user_settings__web_channel_default_view: {
type: "user_settings",
op: "update",
property: "web_channel_default_view",
value: 1,
},
user_settings__web_escape_navigates_to_home_view: { user_settings__web_escape_navigates_to_home_view: {
type: "user_settings", type: "user_settings",
op: "update", op: "update",

View File

@ -0,0 +1,22 @@
# Generated by Django 5.0.6 on 2024-07-08 18:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("zerver", "0547_realmuserdefault_web_navigate_to_sent_message_and_more"),
]
operations = [
migrations.AddField(
model_name="realmuserdefault",
name="web_channel_default_view",
field=models.SmallIntegerField(db_default=1, default=1),
),
migrations.AddField(
model_name="userprofile",
name="web_channel_default_view",
field=models.SmallIntegerField(db_default=1, default=1),
),
]

View File

@ -116,6 +116,23 @@ class UserBaseSettings(models.Model):
web_mark_read_on_scroll_policy = models.SmallIntegerField(default=MARK_READ_ON_SCROLL_ALWAYS) web_mark_read_on_scroll_policy = models.SmallIntegerField(default=MARK_READ_ON_SCROLL_ALWAYS)
# UI setting controlling if clicking on a channel link should open
# the channel feed (interleaved view) or narrow to the first topic
# in the channel.
WEB_CHANNEL_DEFAULT_VIEW_FIRST_TOPIC = 1
WEB_CHANNEL_DEFAULT_VIEW_CHANNEL_FEED = 2
WEB_CHANNEL_DEFAULT_VIEW_CHOICES = [
WEB_CHANNEL_DEFAULT_VIEW_FIRST_TOPIC,
WEB_CHANNEL_DEFAULT_VIEW_CHANNEL_FEED,
]
web_channel_default_view = models.SmallIntegerField(
default=WEB_CHANNEL_DEFAULT_VIEW_FIRST_TOPIC,
db_default=WEB_CHANNEL_DEFAULT_VIEW_FIRST_TOPIC,
)
# Emoji sets # Emoji sets
GOOGLE_EMOJISET = "google" GOOGLE_EMOJISET = "google"
GOOGLE_BLOB_EMOJISET = "google-blob" GOOGLE_BLOB_EMOJISET = "google-blob"
@ -329,6 +346,7 @@ class UserBaseSettings(models.Model):
send_read_receipts=bool, send_read_receipts=bool,
send_stream_typing_notifications=bool, send_stream_typing_notifications=bool,
web_mark_read_on_scroll_policy=int, web_mark_read_on_scroll_policy=int,
web_channel_default_view=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_font_size_px=int,

View File

@ -11140,6 +11140,21 @@ paths:
- 2 - 2
- 3 - 3
example: 1 example: 1
web_channel_default_view:
description: |
Web/desktop app setting controlling the default navigation
behavior when clicking on a channel link.
- 1 - Top topic in the channel
- 2 - Channel feed
**Changes**: New in Zulip 9.0 (feature level 269). Previously, this
was not configurable, and every user had the "Channel feed" behavior.
type: integer
enum:
- 1
- 2
example: 1
web_font_size_px: web_font_size_px:
description: | description: |
User-configured primary `font-size` for the web application, in pixels. User-configured primary `font-size` for the web application, in pixels.
@ -11574,6 +11589,8 @@ 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_channel_default_view:
contentType: application/json
web_font_size_px: web_font_size_px:
contentType: application/json contentType: application/json
web_line_height_percent: web_line_height_percent:
@ -14093,6 +14110,17 @@ paths:
**Changes**: New in Zulip 7.0 (feature level 175). Previously, there was no **Changes**: New in Zulip 7.0 (feature level 175). Previously, there was no
way for the user to configure this behavior on the web, and the Zulip web and way for the user to configure this behavior on the web, and the Zulip web and
desktop apps behaved like the "Always" setting when marking messages as read. desktop apps behaved like the "Always" setting when marking messages as read.
web_channel_default_view:
type: integer
description: |
Web/desktop app setting controlling the default navigation
behavior when clicking on a channel link.
- 1 - Top topic in the channel
- 2 - Channel feed
**Changes**: New in Zulip 9.0 (feature level 269). Previously, this
was not configurable, and every user had the "Channel feed" behavior.
starred_message_counts: starred_message_counts:
type: boolean type: boolean
description: | description: |
@ -16489,6 +16517,17 @@ paths:
**Changes**: New in Zulip 7.0 (feature level 175). Previously, there was no **Changes**: New in Zulip 7.0 (feature level 175). Previously, there was no
way for the user to configure this behavior on the web, and the Zulip web and way for the user to configure this behavior on the web, and the Zulip web and
desktop apps behaved like the "Always" setting when marking messages as read. desktop apps behaved like the "Always" setting when marking messages as read.
web_channel_default_view:
type: integer
description: |
Web/desktop app setting controlling the default navigation
behavior when clicking on a channel link.
- 1 - Top topic in the channel
- 2 - Channel feed
**Changes**: New in Zulip 9.0 (feature level 269). Previously, this
was not configurable, and every user had the "Channel feed" behavior.
starred_message_counts: starred_message_counts:
type: boolean type: boolean
description: | description: |
@ -17599,6 +17638,21 @@ paths:
- 2 - 2
- 3 - 3
example: 1 example: 1
web_channel_default_view:
description: |
Web/desktop app setting controlling the default navigation
behavior when clicking on a channel link.
- 1 - Top topic in the channel
- 2 - Channel feed
**Changes**: New in Zulip 9.0 (feature level 269). Previously, this
was not configurable, and every user had the "Channel feed" behavior.
type: integer
enum:
- 1
- 2
example: 1
starred_message_counts: starred_message_counts:
description: | description: |
Whether clients should display the [number of starred Whether clients should display the [number of starred
@ -18183,6 +18237,8 @@ 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_channel_default_view:
contentType: application/json
starred_message_counts: starred_message_counts:
contentType: application/json contentType: application/json
receives_typing_notifications: receives_typing_notifications:

View File

@ -3750,6 +3750,7 @@ class RealmPropertyActionTest(BaseAction):
emojiset=[emojiset["key"] for emojiset in RealmUserDefault.emojiset_choices()], emojiset=[emojiset["key"] for emojiset in RealmUserDefault.emojiset_choices()],
demote_inactive_streams=UserProfile.DEMOTE_STREAMS_CHOICES, demote_inactive_streams=UserProfile.DEMOTE_STREAMS_CHOICES,
web_mark_read_on_scroll_policy=UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES, web_mark_read_on_scroll_policy=UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES,
web_channel_default_view=UserProfile.WEB_CHANNEL_DEFAULT_VIEW_CHOICES,
user_list_style=UserProfile.USER_LIST_STYLE_CHOICES, user_list_style=UserProfile.USER_LIST_STYLE_CHOICES,
web_stream_unreads_count_display_policy=UserProfile.WEB_STREAM_UNREADS_COUNT_DISPLAY_POLICY_CHOICES, web_stream_unreads_count_display_policy=UserProfile.WEB_STREAM_UNREADS_COUNT_DISPLAY_POLICY_CHOICES,
desktop_icon_count_display=UserProfile.DESKTOP_ICON_COUNT_DISPLAY_CHOICES, desktop_icon_count_display=UserProfile.DESKTOP_ICON_COUNT_DISPLAY_CHOICES,
@ -3878,6 +3879,7 @@ class UserDisplayActionTest(BaseAction):
web_home_view=["all_messages", "inbox", "recent_topics"], web_home_view=["all_messages", "inbox", "recent_topics"],
demote_inactive_streams=[2, 3, 1], demote_inactive_streams=[2, 3, 1],
web_mark_read_on_scroll_policy=[2, 3, 1], web_mark_read_on_scroll_policy=[2, 3, 1],
web_channel_default_view=[2, 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_font_size_px=[12, 16, 18],

View File

@ -1945,6 +1945,7 @@ class RealmAPITest(ZulipTestCase):
emojiset=[emojiset["key"] for emojiset in RealmUserDefault.emojiset_choices()], emojiset=[emojiset["key"] for emojiset in RealmUserDefault.emojiset_choices()],
demote_inactive_streams=UserProfile.DEMOTE_STREAMS_CHOICES, demote_inactive_streams=UserProfile.DEMOTE_STREAMS_CHOICES,
web_mark_read_on_scroll_policy=UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES, web_mark_read_on_scroll_policy=UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES,
web_channel_default_view=UserProfile.WEB_CHANNEL_DEFAULT_VIEW_CHOICES,
user_list_style=UserProfile.USER_LIST_STYLE_CHOICES, user_list_style=UserProfile.USER_LIST_STYLE_CHOICES,
web_stream_unreads_count_display_policy=UserProfile.WEB_STREAM_UNREADS_COUNT_DISPLAY_POLICY_CHOICES, web_stream_unreads_count_display_policy=UserProfile.WEB_STREAM_UNREADS_COUNT_DISPLAY_POLICY_CHOICES,
desktop_icon_count_display=UserProfile.DESKTOP_ICON_COUNT_DISPLAY_CHOICES, desktop_icon_count_display=UserProfile.DESKTOP_ICON_COUNT_DISPLAY_CHOICES,

View File

@ -350,6 +350,7 @@ class ChangeSettingsTest(ZulipTestCase):
timezone="America/Denver", timezone="America/Denver",
demote_inactive_streams=2, demote_inactive_streams=2,
web_mark_read_on_scroll_policy=2, web_mark_read_on_scroll_policy=2,
web_channel_default_view=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_font_size_px=14,
@ -375,6 +376,7 @@ class ChangeSettingsTest(ZulipTestCase):
"user_list_style", "user_list_style",
"color_scheme", "color_scheme",
"web_mark_read_on_scroll_policy", "web_mark_read_on_scroll_policy",
"web_channel_default_view",
"web_stream_unreads_count_display_policy", "web_stream_unreads_count_display_policy",
]: ]:
data = {setting_name: test_value} data = {setting_name: test_value}
@ -403,6 +405,7 @@ class ChangeSettingsTest(ZulipTestCase):
timezone="invalid_US/Mountain", timezone="invalid_US/Mountain",
demote_inactive_streams=10, demote_inactive_streams=10,
web_mark_read_on_scroll_policy=10, web_mark_read_on_scroll_policy=10,
web_channel_default_view=10,
user_list_style=10, user_list_style=10,
web_stream_unreads_count_display_policy=10, web_stream_unreads_count_display_policy=10,
color_scheme=10, color_scheme=10,

View File

@ -540,6 +540,10 @@ def update_realm_user_settings_defaults(
json_validator=check_int_in(UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES), json_validator=check_int_in(UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES),
default=None, default=None,
), ),
web_channel_default_view: Optional[int] = REQ(
json_validator=check_int_in(UserProfile.WEB_CHANNEL_DEFAULT_VIEW_CHOICES),
default=None,
),
starred_message_counts: Optional[bool] = REQ(json_validator=check_bool, default=None), starred_message_counts: Optional[bool] = REQ(json_validator=check_bool, default=None),
receives_typing_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), receives_typing_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None),
web_stream_unreads_count_display_policy: Optional[int] = REQ( web_stream_unreads_count_display_policy: Optional[int] = REQ(

View File

@ -207,6 +207,10 @@ def json_change_settings(
json_validator=check_int_in(UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES), json_validator=check_int_in(UserProfile.WEB_MARK_READ_ON_SCROLL_POLICY_CHOICES),
default=None, default=None,
), ),
web_channel_default_view: Optional[int] = REQ(
json_validator=check_int_in(UserProfile.WEB_CHANNEL_DEFAULT_VIEW_CHOICES),
default=None,
),
starred_message_counts: Optional[bool] = REQ(json_validator=check_bool, default=None), starred_message_counts: Optional[bool] = REQ(json_validator=check_bool, default=None),
receives_typing_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None), receives_typing_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None),
fluid_layout_width: Optional[bool] = REQ(json_validator=check_bool, default=None), fluid_layout_width: Optional[bool] = REQ(json_validator=check_bool, default=None),