mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 21:43:21 +00:00
user_settings: Add user setting to control the user list style.
Added a user_list_style personal user setting to the bottom of Settings > Display settings > Theme section which controls the look of the right sidebar user list. The radio button UI includes a preview of what the styles look like. The setting is intended to eventually have 3 possible values: COMPACT, WITH_STATUS and WITH_AVATAR; the final value is not yet implemented. Co-authored-by: Tim Abbott <tabbott@zulip.com>
This commit is contained in:
committed by
Tim Abbott
parent
5f626428de
commit
4dad9fa158
@@ -376,6 +376,7 @@ test("handlers", ({override, mock_template}) => {
|
|||||||
test("first/prev/next", ({override, mock_template}) => {
|
test("first/prev/next", ({override, mock_template}) => {
|
||||||
let rendered_alice;
|
let rendered_alice;
|
||||||
let rendered_fred;
|
let rendered_fred;
|
||||||
|
user_settings.user_list_style = 2;
|
||||||
|
|
||||||
mock_template("presence_row.hbs", false, (data) => {
|
mock_template("presence_row.hbs", false, (data) => {
|
||||||
switch (data.user_id) {
|
switch (data.user_id) {
|
||||||
@@ -392,6 +393,12 @@ test("first/prev/next", ({override, mock_template}) => {
|
|||||||
user_circle_status: "translated: Active",
|
user_circle_status: "translated: Active",
|
||||||
user_id: alice.user_id,
|
user_id: alice.user_id,
|
||||||
status_emoji_info: undefined,
|
status_emoji_info: undefined,
|
||||||
|
status_text: undefined,
|
||||||
|
user_list_style: {
|
||||||
|
COMPACT: false,
|
||||||
|
WITH_STATUS: true,
|
||||||
|
WITH_AVATAR: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case fred.user_id:
|
case fred.user_id:
|
||||||
@@ -407,6 +414,12 @@ test("first/prev/next", ({override, mock_template}) => {
|
|||||||
user_circle_status: "translated: Active",
|
user_circle_status: "translated: Active",
|
||||||
faded: false,
|
faded: false,
|
||||||
status_emoji_info: undefined,
|
status_emoji_info: undefined,
|
||||||
|
status_text: undefined,
|
||||||
|
user_list_style: {
|
||||||
|
COMPACT: false,
|
||||||
|
WITH_STATUS: true,
|
||||||
|
WITH_AVATAR: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
@@ -438,6 +451,7 @@ test("first/prev/next", ({override, mock_template}) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("insert_one_user_into_empty_list", ({override, mock_template}) => {
|
test("insert_one_user_into_empty_list", ({override, mock_template}) => {
|
||||||
|
user_settings.user_list_style = 2;
|
||||||
mock_template("presence_row.hbs", true, (data, html) => {
|
mock_template("presence_row.hbs", true, (data, html) => {
|
||||||
assert.deepEqual(data, {
|
assert.deepEqual(data, {
|
||||||
href: "#narrow/pm-with/1-alice",
|
href: "#narrow/pm-with/1-alice",
|
||||||
@@ -450,6 +464,12 @@ test("insert_one_user_into_empty_list", ({override, mock_template}) => {
|
|||||||
user_circle_status: "translated: Active",
|
user_circle_status: "translated: Active",
|
||||||
faded: true,
|
faded: true,
|
||||||
status_emoji_info: undefined,
|
status_emoji_info: undefined,
|
||||||
|
status_text: undefined,
|
||||||
|
user_list_style: {
|
||||||
|
COMPACT: false,
|
||||||
|
WITH_STATUS: true,
|
||||||
|
WITH_AVATAR: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
assert.ok(html.startsWith("<li data-user-id="));
|
assert.ok(html.startsWith("<li data-user-id="));
|
||||||
return html;
|
return html;
|
||||||
|
|||||||
@@ -509,6 +509,7 @@ test("get_items_for_users", () => {
|
|||||||
people.add_active_user(fred);
|
people.add_active_user(fred);
|
||||||
user_status.set_away(alice.user_id);
|
user_status.set_away(alice.user_id);
|
||||||
user_settings.emojiset = "google";
|
user_settings.emojiset = "google";
|
||||||
|
user_settings.user_list_style = 2;
|
||||||
const status_emoji_info = {
|
const status_emoji_info = {
|
||||||
emoji_alt_code: false,
|
emoji_alt_code: false,
|
||||||
emoji_name: "car",
|
emoji_name: "car",
|
||||||
@@ -521,6 +522,12 @@ test("get_items_for_users", () => {
|
|||||||
user_status.set_status_emoji({user_id, ...status_emoji_info});
|
user_status.set_status_emoji({user_id, ...status_emoji_info});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const user_list_style = {
|
||||||
|
COMPACT: false,
|
||||||
|
WITH_STATUS: true,
|
||||||
|
WITH_AVATAR: false,
|
||||||
|
};
|
||||||
|
|
||||||
assert.deepEqual(buddy_data.get_items_for_users(user_ids), [
|
assert.deepEqual(buddy_data.get_items_for_users(user_ids), [
|
||||||
{
|
{
|
||||||
faded: false,
|
faded: false,
|
||||||
@@ -530,9 +537,11 @@ test("get_items_for_users", () => {
|
|||||||
name: "Human Myself",
|
name: "Human Myself",
|
||||||
num_unread: 0,
|
num_unread: 0,
|
||||||
status_emoji_info,
|
status_emoji_info,
|
||||||
|
status_text: undefined,
|
||||||
user_circle_class: "user_circle_green",
|
user_circle_class: "user_circle_green",
|
||||||
user_circle_status: "translated: Active",
|
user_circle_status: "translated: Active",
|
||||||
user_id: 1001,
|
user_id: 1001,
|
||||||
|
user_list_style,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
faded: false,
|
faded: false,
|
||||||
@@ -542,9 +551,11 @@ test("get_items_for_users", () => {
|
|||||||
name: "Alice Smith",
|
name: "Alice Smith",
|
||||||
num_unread: 0,
|
num_unread: 0,
|
||||||
status_emoji_info,
|
status_emoji_info,
|
||||||
|
status_text: undefined,
|
||||||
user_circle_class: "user_circle_empty_line",
|
user_circle_class: "user_circle_empty_line",
|
||||||
user_circle_status: "translated: Unavailable",
|
user_circle_status: "translated: Unavailable",
|
||||||
user_id: 1002,
|
user_id: 1002,
|
||||||
|
user_list_style,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
faded: false,
|
faded: false,
|
||||||
@@ -554,9 +565,11 @@ test("get_items_for_users", () => {
|
|||||||
name: "Fred Flintstone",
|
name: "Fred Flintstone",
|
||||||
num_unread: 0,
|
num_unread: 0,
|
||||||
status_emoji_info,
|
status_emoji_info,
|
||||||
|
status_text: undefined,
|
||||||
user_circle_class: "user_circle_empty",
|
user_circle_class: "user_circle_empty",
|
||||||
user_circle_status: "translated: Offline",
|
user_circle_status: "translated: Offline",
|
||||||
user_id: 1003,
|
user_id: 1003,
|
||||||
|
user_list_style,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -885,6 +885,17 @@ run_test("user_settings", ({override}) => {
|
|||||||
assert_same(user_settings.demote_inactive_streams, 2);
|
assert_same(user_settings.demote_inactive_streams, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const stub = make_stub();
|
||||||
|
event = event_fixtures.user_settings__user_list_style;
|
||||||
|
override(settings_display, "report_user_list_style_change", stub.f);
|
||||||
|
user_settings.user_list_style = 1;
|
||||||
|
override(activity, "build_user_sidebar", stub.f);
|
||||||
|
dispatch(event);
|
||||||
|
assert.equal(stub.num_calls, 2);
|
||||||
|
assert_same(user_settings.user_list_style, 2);
|
||||||
|
}
|
||||||
|
|
||||||
event = event_fixtures.user_settings__enter_sends;
|
event = event_fixtures.user_settings__enter_sends;
|
||||||
user_settings.enter_sends = false;
|
user_settings.enter_sends = false;
|
||||||
dispatch(event);
|
dispatch(event);
|
||||||
|
|||||||
@@ -915,6 +915,13 @@ exports.fixtures = {
|
|||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
user_settings__user_list_style: {
|
||||||
|
type: "user_settings",
|
||||||
|
op: "update",
|
||||||
|
property: "user_list_style",
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
|
||||||
user_status__revoke_away: {
|
user_status__revoke_away: {
|
||||||
type: "user_status",
|
type: "user_status",
|
||||||
user_id: 63,
|
user_id: 63,
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ function get_realm_level_notification_settings(options) {
|
|||||||
export function build_page() {
|
export function build_page() {
|
||||||
const options = {
|
const options = {
|
||||||
custom_profile_field_types: page_params.custom_profile_field_types,
|
custom_profile_field_types: page_params.custom_profile_field_types,
|
||||||
|
full_name: page_params.full_name,
|
||||||
realm_name: page_params.realm_name,
|
realm_name: page_params.realm_name,
|
||||||
realm_org_type: page_params.realm_org_type,
|
realm_org_type: page_params.realm_org_type,
|
||||||
realm_available_video_chat_providers: page_params.realm_available_video_chat_providers,
|
realm_available_video_chat_providers: page_params.realm_available_video_chat_providers,
|
||||||
@@ -158,6 +159,7 @@ export function build_page() {
|
|||||||
settings_config.common_message_policy_values.by_admins_only.code,
|
settings_config.common_message_policy_values.by_admins_only.code,
|
||||||
...settings_org.get_organization_settings_options(),
|
...settings_org.get_organization_settings_options(),
|
||||||
demote_inactive_streams_values: settings_config.demote_inactive_streams_values,
|
demote_inactive_streams_values: settings_config.demote_inactive_streams_values,
|
||||||
|
user_list_style_values: settings_config.user_list_style_values,
|
||||||
color_scheme_values: settings_config.color_scheme_values,
|
color_scheme_values: settings_config.color_scheme_values,
|
||||||
default_view_values: settings_config.default_view_values,
|
default_view_values: settings_config.default_view_values,
|
||||||
settings_object: realm_user_settings_defaults,
|
settings_object: realm_user_settings_defaults,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import * as people from "./people";
|
|||||||
import * as presence from "./presence";
|
import * as presence from "./presence";
|
||||||
import * as timerender from "./timerender";
|
import * as timerender from "./timerender";
|
||||||
import * as unread from "./unread";
|
import * as unread from "./unread";
|
||||||
|
import {user_settings} from "./user_settings";
|
||||||
import * as user_status from "./user_status";
|
import * as user_status from "./user_status";
|
||||||
import * as util from "./util";
|
import * as util from "./util";
|
||||||
|
|
||||||
@@ -179,6 +180,13 @@ export function info_for(user_id) {
|
|||||||
|
|
||||||
const status_emoji_info = user_status.get_status_emoji(user_id);
|
const status_emoji_info = user_status.get_status_emoji(user_id);
|
||||||
const user_circle_status = status_description(user_id);
|
const user_circle_status = status_description(user_id);
|
||||||
|
const status_text = user_status.get_status_text(user_id);
|
||||||
|
const user_list_style_value = user_settings.user_list_style;
|
||||||
|
const user_list_style = {
|
||||||
|
COMPACT: user_list_style_value === 1,
|
||||||
|
WITH_STATUS: user_list_style_value === 2,
|
||||||
|
WITH_AVATAR: user_list_style_value === 3,
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
href: hash_util.pm_with_url(person.email),
|
href: hash_util.pm_with_url(person.email),
|
||||||
@@ -190,6 +198,8 @@ export function info_for(user_id) {
|
|||||||
num_unread: get_num_unread(user_id),
|
num_unread: get_num_unread(user_id),
|
||||||
user_circle_class,
|
user_circle_class,
|
||||||
user_circle_status,
|
user_circle_status,
|
||||||
|
status_text,
|
||||||
|
user_list_style,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export type RealmDefaultSettings = {
|
|||||||
starred_message_counts: boolean;
|
starred_message_counts: boolean;
|
||||||
translate_emoticons: boolean;
|
translate_emoticons: boolean;
|
||||||
twenty_four_hour_time: boolean;
|
twenty_four_hour_time: boolean;
|
||||||
|
user_list_style: boolean;
|
||||||
wildcard_mentions_notify: boolean;
|
wildcard_mentions_notify: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -621,6 +621,7 @@ export function dispatch_normal_event(event) {
|
|||||||
"twenty_four_hour_time",
|
"twenty_four_hour_time",
|
||||||
"translate_emoticons",
|
"translate_emoticons",
|
||||||
"display_emoji_reaction_users",
|
"display_emoji_reaction_users",
|
||||||
|
"user_list_style",
|
||||||
"starred_message_counts",
|
"starred_message_counts",
|
||||||
"send_stream_typing_notifications",
|
"send_stream_typing_notifications",
|
||||||
"send_private_typing_notifications",
|
"send_private_typing_notifications",
|
||||||
@@ -652,6 +653,12 @@ export function dispatch_normal_event(event) {
|
|||||||
stream_list.update_streams_sidebar();
|
stream_list.update_streams_sidebar();
|
||||||
stream_data.set_filter_out_inactives();
|
stream_data.set_filter_out_inactives();
|
||||||
}
|
}
|
||||||
|
if (event.property === "user_list_style") {
|
||||||
|
settings_display.report_user_list_style_change(
|
||||||
|
settings_display.user_settings_panel,
|
||||||
|
);
|
||||||
|
activity.build_user_sidebar();
|
||||||
|
}
|
||||||
if (event.property === "dense_mode") {
|
if (event.property === "dense_mode") {
|
||||||
$("body").toggleClass("less_dense_mode");
|
$("body").toggleClass("less_dense_mode");
|
||||||
$("body").toggleClass("more_dense_mode");
|
$("body").toggleClass("more_dense_mode");
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ export function build_page() {
|
|||||||
can_create_new_bots: settings_bots.can_create_new_bots(),
|
can_create_new_bots: settings_bots.can_create_new_bots(),
|
||||||
settings_label,
|
settings_label,
|
||||||
demote_inactive_streams_values: settings_config.demote_inactive_streams_values,
|
demote_inactive_streams_values: settings_config.demote_inactive_streams_values,
|
||||||
|
user_list_style_values: settings_config.user_list_style_values,
|
||||||
color_scheme_values: settings_config.color_scheme_values,
|
color_scheme_values: settings_config.color_scheme_values,
|
||||||
default_view_values: settings_config.default_view_values,
|
default_view_values: settings_config.default_view_values,
|
||||||
twenty_four_hour_time_values: settings_config.twenty_four_hour_time_values,
|
twenty_four_hour_time_values: settings_config.twenty_four_hour_time_values,
|
||||||
|
|||||||
@@ -39,6 +39,22 @@ export const demote_inactive_streams_values = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const user_list_style_values = {
|
||||||
|
compact: {
|
||||||
|
code: 1,
|
||||||
|
description: $t({defaultMessage: "Compact"}),
|
||||||
|
},
|
||||||
|
with_status: {
|
||||||
|
code: 2,
|
||||||
|
description: $t({defaultMessage: "Show status text"}),
|
||||||
|
},
|
||||||
|
// The `with_avatar` design in still in discussion.
|
||||||
|
// with_avatar: {
|
||||||
|
// code: 3,
|
||||||
|
// description: $t({defaultMessage: "Show status text and avatar"}),
|
||||||
|
// },
|
||||||
|
};
|
||||||
|
|
||||||
export const default_view_values = {
|
export const default_view_values = {
|
||||||
recent_topics: {
|
recent_topics: {
|
||||||
code: "recent_topics",
|
code: "recent_topics",
|
||||||
|
|||||||
@@ -166,6 +166,9 @@ export function set_up(settings_panel) {
|
|||||||
$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);
|
||||||
|
$container
|
||||||
|
.find(`.setting_user_list_style_choice[value=${settings_object.user_list_style}]`)
|
||||||
|
.prop("checked", true);
|
||||||
|
|
||||||
if (for_realm_settings) {
|
if (for_realm_settings) {
|
||||||
// For the realm-level defaults page, we use the common
|
// For the realm-level defaults page, we use the common
|
||||||
@@ -224,6 +227,29 @@ export function set_up(settings_panel) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$container.find(".setting_user_list_style_choice").on("click", function () {
|
||||||
|
const data = {user_list_style: $(this).val()};
|
||||||
|
const current_user_list_style = settings_object.user_list_style;
|
||||||
|
if (current_user_list_style === data.user_list_style) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const $spinner = $container.find(".theme-settings-status").expectOne();
|
||||||
|
loading.make_indicator($spinner, {text: settings_ui.strings.saving});
|
||||||
|
|
||||||
|
channel.patch({
|
||||||
|
url: "/json/settings",
|
||||||
|
data,
|
||||||
|
success() {},
|
||||||
|
error(xhr) {
|
||||||
|
ui_report.error(
|
||||||
|
settings_ui.strings.failure_html,
|
||||||
|
xhr,
|
||||||
|
$container.find(".theme-settings-status").expectOne(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function report_emojiset_change(settings_panel) {
|
export async function report_emojiset_change(settings_panel) {
|
||||||
@@ -247,6 +273,25 @@ export async function report_emojiset_change(settings_panel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function report_user_list_style_change(settings_panel) {
|
||||||
|
// TODO: Clean up how this works so we can use
|
||||||
|
// change_display_setting. The challenge is that we don't want to
|
||||||
|
// report success before the server_events request returns that
|
||||||
|
// causes the actual sprite sheet to change. The current
|
||||||
|
// implementation is wrong, though, in that it displays the UI
|
||||||
|
// update in all active browser windows.
|
||||||
|
const $spinner = $(settings_panel.container).find(".theme-settings-status");
|
||||||
|
if ($spinner.length) {
|
||||||
|
loading.destroy_indicator($spinner);
|
||||||
|
ui_report.success(
|
||||||
|
$t_html({defaultMessage: "User list style changed successfully!"}),
|
||||||
|
$spinner.expectOne(),
|
||||||
|
);
|
||||||
|
$spinner.expectOne();
|
||||||
|
settings_ui.display_checkmark($spinner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function update_page(property) {
|
export function update_page(property) {
|
||||||
if (!overlays.settings_open()) {
|
if (!overlays.settings_open()) {
|
||||||
return;
|
return;
|
||||||
@@ -262,8 +307,8 @@ export function update_page(property) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// settings_org.set_input_element_value doesn't support radio
|
// settings_org.set_input_element_value doesn't support radio
|
||||||
// button widgets like this one.
|
// button widgets like these.
|
||||||
if (property === "emojiset") {
|
if (property === "emojiset" || property === "user_list_style") {
|
||||||
$container.find(`input[value=${CSS.escape(value)}]`).prop("checked", true);
|
$container.find(`input[value=${CSS.escape(value)}]`).prop("checked", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,8 +244,14 @@ function get_subsection_property_elements(element) {
|
|||||||
// structure, it needs custom code.
|
// structure, it needs custom code.
|
||||||
const $color_scheme_elem = $subsection.find(".setting_color_scheme");
|
const $color_scheme_elem = $subsection.find(".setting_color_scheme");
|
||||||
const $emojiset_elem = $subsection.find("input[name='emojiset']:checked");
|
const $emojiset_elem = $subsection.find("input[name='emojiset']:checked");
|
||||||
|
const $user_list_style_elem = $subsection.find("input[name='user_list_style']:checked");
|
||||||
const $translate_emoticons_elem = $subsection.find(".translate_emoticons");
|
const $translate_emoticons_elem = $subsection.find(".translate_emoticons");
|
||||||
return [$color_scheme_elem, $emojiset_elem, $translate_emoticons_elem];
|
return [
|
||||||
|
$color_scheme_elem,
|
||||||
|
$emojiset_elem,
|
||||||
|
$user_list_style_elem,
|
||||||
|
$translate_emoticons_elem,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return Array.from($subsection.find(".prop-element"));
|
return Array.from($subsection.find(".prop-element"));
|
||||||
}
|
}
|
||||||
@@ -533,13 +539,21 @@ function discard_property_element_changes(elem, for_realm_default_settings) {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "emojiset":
|
case "emojiset":
|
||||||
// Because the emojiset widget has a unique radio button
|
// Because this widget has a radio button structure, it
|
||||||
// structure, it needs custom reset code.
|
// needs custom reset code.
|
||||||
$elem
|
$elem
|
||||||
.closest(".org-subsection-parent")
|
.closest(".org-subsection-parent")
|
||||||
.find(`.setting_emojiset_choice[value='${CSS.escape(property_value)}'`)
|
.find(`.setting_emojiset_choice[value='${CSS.escape(property_value)}'`)
|
||||||
.prop("checked", true);
|
.prop("checked", true);
|
||||||
break;
|
break;
|
||||||
|
case "user_list_style":
|
||||||
|
// Because this widget has a radio button structure, it
|
||||||
|
// needs custom reset code.
|
||||||
|
$elem
|
||||||
|
.closest(".org-subsection-parent")
|
||||||
|
.find(`.setting_user_list_style_choice[value='${CSS.escape(property_value)}'`)
|
||||||
|
.prop("checked", true);
|
||||||
|
break;
|
||||||
case "email_notifications_batching_period_seconds":
|
case "email_notifications_batching_period_seconds":
|
||||||
case "email_notification_batching_period_edit_minutes":
|
case "email_notification_batching_period_edit_minutes":
|
||||||
settings_notifications.set_notification_batching_ui(
|
settings_notifications.set_notification_batching_ui(
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export function update_page(property) {
|
|||||||
let value = realm_user_settings_defaults[property];
|
let value = realm_user_settings_defaults[property];
|
||||||
|
|
||||||
// settings_org.set_input_element_value doesn't support radio
|
// settings_org.set_input_element_value doesn't support radio
|
||||||
// button widgets like this one.
|
// button widgets like these.
|
||||||
if (property === "emojiset") {
|
if (property === "emojiset" || property === "user_list_style") {
|
||||||
$container.find(`input[value=${CSS.escape(value)}]`).prop("checked", true);
|
$container.find(`input[value=${CSS.escape(value)}]`).prop("checked", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export type UserSettings = (StreamNotificationSettings & PmNotificationSettings)
|
|||||||
pm_content_in_desktop_notifications: boolean;
|
pm_content_in_desktop_notifications: boolean;
|
||||||
presence_enabled: boolean;
|
presence_enabled: boolean;
|
||||||
realm_name_in_notifications: boolean;
|
realm_name_in_notifications: boolean;
|
||||||
|
user_list_style: number;
|
||||||
starred_message_counts: boolean;
|
starred_message_counts: boolean;
|
||||||
translate_emoticons: boolean;
|
translate_emoticons: boolean;
|
||||||
display_emoji_reaction_users: boolean;
|
display_emoji_reaction_users: boolean;
|
||||||
|
|||||||
@@ -130,6 +130,24 @@
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.user-name-and-status-emoji {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-text {
|
||||||
|
display: block;
|
||||||
|
width: 170px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
opacity: 0.75;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.status-text:not(:empty) {
|
||||||
|
margin-top: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
.unread_count {
|
.unread_count {
|
||||||
display: none;
|
display: none;
|
||||||
margin-top: 2.5px;
|
margin-top: 2.5px;
|
||||||
|
|||||||
@@ -918,15 +918,10 @@ input[type="checkbox"] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.emojiset_choices {
|
.emojiset_choices,
|
||||||
width: 250px;
|
.user_list_style_values {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
|
|
||||||
.emoji {
|
|
||||||
height: 22px;
|
|
||||||
width: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
label {
|
||||||
border-bottom: 1px solid hsla(0, 0%, 0%, 0.2);
|
border-bottom: 1px solid hsla(0, 0%, 0%, 0.2);
|
||||||
padding: 8px 0 10px;
|
padding: 8px 0 10px;
|
||||||
@@ -944,10 +939,56 @@ input[type="checkbox"] {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
float: right;
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emojiset_choices {
|
||||||
|
width: 250px;
|
||||||
|
|
||||||
|
.emoji {
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$right_sidebar_width: 170px;
|
||||||
|
$option_title_width: 180px;
|
||||||
|
|
||||||
|
.user_list_style_values {
|
||||||
|
max-width: calc($right_sidebar_width + $option_title_width);
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
background-color: inherit !important;
|
||||||
|
/* Match the 170px width of the right sidebar region for the name/status,
|
||||||
|
doing something reasonable if the window shrinks. */
|
||||||
|
width: calc(100% - $option_title_width);
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: visible;
|
||||||
|
position: relative;
|
||||||
|
height: 36px;
|
||||||
|
|
||||||
|
.user-name-and-status-text {
|
||||||
|
margin-top: -4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-text {
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 0.75;
|
||||||
|
font-size: 90%;
|
||||||
|
|
||||||
|
&:not(:empty) {
|
||||||
|
margin-top: -3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,18 @@
|
|||||||
href="{{href}}"
|
href="{{href}}"
|
||||||
data-user-id="{{user_id}}"
|
data-user-id="{{user_id}}"
|
||||||
data-name="{{name}}">
|
data-name="{{name}}">
|
||||||
<span class="user-name">{{name}}</span>
|
{{#if user_list_style.WITH_STATUS}}
|
||||||
{{> status_emoji status_emoji_info}}
|
<div>
|
||||||
|
<div class="user-name-and-status-emoji">
|
||||||
|
<span class="user-name">{{name}}</span>
|
||||||
|
{{> status_emoji status_emoji_info}}
|
||||||
|
</div>
|
||||||
|
<span class="status-text">{{status_text}}</span>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<span class="user-name">{{name}}</span>
|
||||||
|
{{> status_emoji status_emoji_info}}
|
||||||
|
{{/if}}
|
||||||
</a>
|
</a>
|
||||||
<span class="unread_count">{{#if num_unread}}{{num_unread}}{{/if}}</span>
|
<span class="unread_count">{{#if num_unread}}{{num_unread}}{{/if}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -77,6 +77,33 @@
|
|||||||
label=settings_label.display_emoji_reaction_users
|
label=settings_label.display_emoji_reaction_users
|
||||||
prefix=prefix}}
|
prefix=prefix}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<label class="title">{{t "User list style" }}</label>
|
||||||
|
<div class="user_list_style_values grey-box">
|
||||||
|
{{#each user_list_style_values}}
|
||||||
|
<label>
|
||||||
|
<input type="radio" class="setting_user_list_style_choice prop-element" name="user_list_style" value="{{this.code}}" data-setting-widget-type="radio-group"/>
|
||||||
|
<span>{{this.description}}</span>
|
||||||
|
<span class="right preview">
|
||||||
|
{{#if (eq this.code 1)}}
|
||||||
|
<span class="user-name">{{../full_name}}</span>
|
||||||
|
<div class="emoji status_emoji emoji-1f3e0"></div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq this.code 2)}}
|
||||||
|
<div class="user-name-and-status-text">
|
||||||
|
<div class="user-name-and-status-emoji">
|
||||||
|
<span class="user-name">{{../full_name}}</span>
|
||||||
|
{{> ../status_emoji emoji_code="1f3e0"}}
|
||||||
|
</div>
|
||||||
|
<span class="status-text">{{t "Working remotely" }}</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="advanced-settings {{#if for_realm_settings}}org-subsection-parent{{else}}subsection-parent{{/if}}">
|
<div class="advanced-settings {{#if for_realm_settings}}org-subsection-parent{{else}}subsection-parent{{/if}}">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
{{#*inline "z-link"}}<a href="/help/configure-default-new-user-settings" target="_blank" rel="noopener noreferrer">{{> @partial-block }}</a>{{/inline}}
|
{{#*inline "z-link"}}<a href="/help/configure-default-new-user-settings" target="_blank" rel="noopener noreferrer">{{> @partial-block }}</a>{{/inline}}
|
||||||
{{/tr}}
|
{{/tr}}
|
||||||
</div>
|
</div>
|
||||||
{{> display_settings prefix="realm_" for_realm_settings=true}}
|
{{> display_settings prefix="realm_" for_realm_settings=true full_name=full_name}}
|
||||||
|
|
||||||
{{> notification_settings prefix="realm_" for_realm_settings=true}}
|
{{> notification_settings prefix="realm_" for_realm_settings=true}}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ format used by the Zulip server that they are interacting with.
|
|||||||
|
|
||||||
## Changes in Zulip 6.0
|
## Changes in Zulip 6.0
|
||||||
|
|
||||||
|
**Feature level 141**
|
||||||
|
|
||||||
|
* [`POST /register`](/api/register-queue), [`PATCH
|
||||||
|
/settings`](/api/update-settings), [`PATCH
|
||||||
|
/realm/user_settings_defaults`](/api/update-realm-user-settings-defaults):
|
||||||
|
Added new `user_list_style` display setting, which controls the
|
||||||
|
layout of the right sidebar.
|
||||||
|
|
||||||
**Feature level 140**
|
**Feature level 140**
|
||||||
|
|
||||||
* [`POST /register`](/api/register-queue): Added string field `server_emoji_data_url`
|
* [`POST /register`](/api/register-queue): Added string field `server_emoji_data_url`
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3"
|
|||||||
# Changes should be accompanied by documentation explaining what the
|
# Changes should be accompanied by documentation explaining what the
|
||||||
# new level means in templates/zerver/api/changelog.md, as well as
|
# new level means in templates/zerver/api/changelog.md, as well as
|
||||||
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
|
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
|
||||||
API_FEATURE_LEVEL = 140
|
API_FEATURE_LEVEL = 141
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
23
zerver/migrations/0407_userprofile_user_list_style.py
Normal file
23
zerver/migrations/0407_userprofile_user_list_style.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.0.6 on 2022-08-14 18:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0406_alter_realm_message_content_edit_limit_seconds"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="realmuserdefault",
|
||||||
|
name="user_list_style",
|
||||||
|
field=models.PositiveSmallIntegerField(default=2),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="userprofile",
|
||||||
|
name="user_list_style",
|
||||||
|
field=models.PositiveSmallIntegerField(default=2),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1523,6 +1523,17 @@ class UserBaseSettings(models.Model):
|
|||||||
default=GOOGLE_EMOJISET, choices=EMOJISET_CHOICES, max_length=20
|
default=GOOGLE_EMOJISET, choices=EMOJISET_CHOICES, max_length=20
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# User list style
|
||||||
|
USER_LIST_STYLE_COMPACT = 1
|
||||||
|
USER_LIST_STYLE_WITH_STATUS = 2
|
||||||
|
USER_LIST_STYLE_WITH_AVATAR = 3
|
||||||
|
USER_LIST_STYLE_CHOICES = [
|
||||||
|
USER_LIST_STYLE_COMPACT,
|
||||||
|
USER_LIST_STYLE_WITH_STATUS,
|
||||||
|
USER_LIST_STYLE_WITH_AVATAR,
|
||||||
|
]
|
||||||
|
user_list_style: int = models.PositiveSmallIntegerField(default=USER_LIST_STYLE_WITH_STATUS)
|
||||||
|
|
||||||
### Notifications settings. ###
|
### Notifications settings. ###
|
||||||
|
|
||||||
email_notifications_batching_period_seconds: int = models.IntegerField(default=120)
|
email_notifications_batching_period_seconds: int = models.IntegerField(default=120)
|
||||||
@@ -1621,6 +1632,7 @@ class UserBaseSettings(models.Model):
|
|||||||
send_private_typing_notifications=bool,
|
send_private_typing_notifications=bool,
|
||||||
send_read_receipts=bool,
|
send_read_receipts=bool,
|
||||||
send_stream_typing_notifications=bool,
|
send_stream_typing_notifications=bool,
|
||||||
|
user_list_style=int,
|
||||||
)
|
)
|
||||||
|
|
||||||
modern_notification_settings: Dict[str, Any] = dict(
|
modern_notification_settings: Dict[str, Any] = dict(
|
||||||
|
|||||||
@@ -8516,6 +8516,23 @@ paths:
|
|||||||
- 2
|
- 2
|
||||||
- 3
|
- 3
|
||||||
example: 1
|
example: 1
|
||||||
|
- name: user_list_style
|
||||||
|
in: query
|
||||||
|
description: |
|
||||||
|
The style selected by the user for the right sidebar user list.
|
||||||
|
|
||||||
|
- 1 - Compact
|
||||||
|
- 2 - With status
|
||||||
|
- 3 - With avatar and status
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 6.0 (feature level 141).
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
enum:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
example: 1
|
||||||
- name: enable_stream_desktop_notifications
|
- name: enable_stream_desktop_notifications
|
||||||
in: query
|
in: query
|
||||||
description: |
|
description: |
|
||||||
@@ -10431,6 +10448,16 @@ paths:
|
|||||||
- 1 - Automatic
|
- 1 - Automatic
|
||||||
- 2 - Always
|
- 2 - Always
|
||||||
- 3 - Never
|
- 3 - Never
|
||||||
|
user_list_style:
|
||||||
|
type: integer
|
||||||
|
description: |
|
||||||
|
The style selected by the user for the right sidebar user list.
|
||||||
|
|
||||||
|
- 1 - Compact
|
||||||
|
- 2 - With status
|
||||||
|
- 3 - With avatar and status
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 6.0 (feature level 141).
|
||||||
timezone:
|
timezone:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
@@ -12344,6 +12371,16 @@ paths:
|
|||||||
- 1 - Automatic
|
- 1 - Automatic
|
||||||
- 2 - Always
|
- 2 - Always
|
||||||
- 3 - Never
|
- 3 - Never
|
||||||
|
user_list_style:
|
||||||
|
type: integer
|
||||||
|
description: |
|
||||||
|
The style selected by the user for the right sidebar user list.
|
||||||
|
|
||||||
|
- 1 - Compact
|
||||||
|
- 2 - With status
|
||||||
|
- 3 - With avatar and status
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 6.0 (feature level 141).
|
||||||
enable_stream_desktop_notifications:
|
enable_stream_desktop_notifications:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: |
|
description: |
|
||||||
@@ -13350,6 +13387,23 @@ paths:
|
|||||||
- 2
|
- 2
|
||||||
- 3
|
- 3
|
||||||
example: 1
|
example: 1
|
||||||
|
- name: user_list_style
|
||||||
|
in: query
|
||||||
|
description: |
|
||||||
|
The style selected by the user for the right sidebar user list.
|
||||||
|
|
||||||
|
- 1 - Compact
|
||||||
|
- 2 - With status
|
||||||
|
- 3 - With avatar and status
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 6.0 (feature level 141).
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
enum:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
example: 1
|
||||||
- name: timezone
|
- name: timezone
|
||||||
in: query
|
in: query
|
||||||
description: |
|
description: |
|
||||||
|
|||||||
@@ -2583,6 +2583,7 @@ class RealmPropertyActionTest(BaseAction):
|
|||||||
default_view=["recent_topics", "all_messages"],
|
default_view=["recent_topics", "all_messages"],
|
||||||
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,
|
||||||
|
user_list_style=UserProfile.USER_LIST_STYLE_CHOICES,
|
||||||
desktop_icon_count_display=[1, 2, 3],
|
desktop_icon_count_display=[1, 2, 3],
|
||||||
notification_sound=["zulip", "ding"],
|
notification_sound=["zulip", "ding"],
|
||||||
email_notifications_batching_period_seconds=[120, 300],
|
email_notifications_batching_period_seconds=[120, 300],
|
||||||
@@ -2656,6 +2657,7 @@ class UserDisplayActionTest(BaseAction):
|
|||||||
default_language=["es", "de", "en"],
|
default_language=["es", "de", "en"],
|
||||||
default_view=["all_messages", "recent_topics"],
|
default_view=["all_messages", "recent_topics"],
|
||||||
demote_inactive_streams=[2, 3, 1],
|
demote_inactive_streams=[2, 3, 1],
|
||||||
|
user_list_style=[1, 2, 3],
|
||||||
color_scheme=[2, 3, 1],
|
color_scheme=[2, 3, 1],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1202,6 +1202,7 @@ class RealmAPITest(ZulipTestCase):
|
|||||||
default_view=["recent_topics", "all_messages"],
|
default_view=["recent_topics", "all_messages"],
|
||||||
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,
|
||||||
|
user_list_style=UserProfile.USER_LIST_STYLE_CHOICES,
|
||||||
desktop_icon_count_display=[1, 2, 3],
|
desktop_icon_count_display=[1, 2, 3],
|
||||||
notification_sound=["zulip", "ding"],
|
notification_sound=["zulip", "ding"],
|
||||||
email_notifications_batching_period_seconds=[120, 300],
|
email_notifications_batching_period_seconds=[120, 300],
|
||||||
|
|||||||
@@ -357,6 +357,7 @@ class ChangeSettingsTest(ZulipTestCase):
|
|||||||
emojiset="google",
|
emojiset="google",
|
||||||
timezone="America/Denver",
|
timezone="America/Denver",
|
||||||
demote_inactive_streams=2,
|
demote_inactive_streams=2,
|
||||||
|
user_list_style=2,
|
||||||
color_scheme=2,
|
color_scheme=2,
|
||||||
email_notifications_batching_period_seconds=100,
|
email_notifications_batching_period_seconds=100,
|
||||||
notification_sound="ding",
|
notification_sound="ding",
|
||||||
@@ -369,7 +370,7 @@ class ChangeSettingsTest(ZulipTestCase):
|
|||||||
if test_value is None:
|
if test_value is None:
|
||||||
raise AssertionError(f"No test created for {setting_name}")
|
raise AssertionError(f"No test created for {setting_name}")
|
||||||
|
|
||||||
if setting_name not in ["demote_inactive_streams", "color_scheme"]:
|
if setting_name not in ["demote_inactive_streams", "user_list_style", "color_scheme"]:
|
||||||
data = {setting_name: test_value}
|
data = {setting_name: test_value}
|
||||||
else:
|
else:
|
||||||
data = {setting_name: orjson.dumps(test_value).decode()}
|
data = {setting_name: orjson.dumps(test_value).decode()}
|
||||||
@@ -395,6 +396,7 @@ class ChangeSettingsTest(ZulipTestCase):
|
|||||||
emojiset="apple",
|
emojiset="apple",
|
||||||
timezone="invalid_US/Mountain",
|
timezone="invalid_US/Mountain",
|
||||||
demote_inactive_streams=10,
|
demote_inactive_streams=10,
|
||||||
|
user_list_style=10,
|
||||||
color_scheme=10,
|
color_scheme=10,
|
||||||
notification_sound="invalid_sound",
|
notification_sound="invalid_sound",
|
||||||
desktop_icon_count_display=10,
|
desktop_icon_count_display=10,
|
||||||
|
|||||||
@@ -440,6 +440,9 @@ def update_realm_user_settings_defaults(
|
|||||||
json_validator=check_bool, default=None
|
json_validator=check_bool, default=None
|
||||||
),
|
),
|
||||||
send_read_receipts: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
send_read_receipts: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||||
|
user_list_style: Optional[int] = REQ(
|
||||||
|
json_validator=check_int_in(UserProfile.USER_LIST_STYLE_CHOICES), default=None
|
||||||
|
),
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
if notification_sound is not None or email_notifications_batching_period_seconds is not None:
|
if notification_sound is not None or email_notifications_batching_period_seconds is not None:
|
||||||
check_settings_values(notification_sound, email_notifications_batching_period_seconds)
|
check_settings_values(notification_sound, email_notifications_batching_period_seconds)
|
||||||
|
|||||||
@@ -217,6 +217,9 @@ def json_change_settings(
|
|||||||
),
|
),
|
||||||
send_stream_typing_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
send_stream_typing_notifications: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||||
send_read_receipts: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
send_read_receipts: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||||
|
user_list_style: Optional[int] = REQ(
|
||||||
|
json_validator=check_int_in(UserProfile.USER_LIST_STYLE_CHOICES), default=None
|
||||||
|
),
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
if (
|
if (
|
||||||
default_language is not None
|
default_language is not None
|
||||||
|
|||||||
Reference in New Issue
Block a user