Compare commits

...

12 Commits

Author SHA1 Message Date
Sahil Batra
2abbc058ab settings: Live update role text in "Profile" panel. 2025-10-20 16:40:01 -07:00
Sahil Batra
53a221dff9 user-profile: Redirect to profile panel when admin becomes non-admin.
This commit adds code to redirect to profile panel when an admins
changes their role to become a non-admin user from user profile
modal.
2025-10-20 16:40:01 -07:00
whilstsomebody
2378d7700b user_profile: Disable role dropdown for only owner.
This commit adds code to disable the role dropdown
when an owner is editing their own profile and they
are the only owner in the organization and a tooltip
is shown mentioning that.

We already keep the role dropdown disabled when an
admin user was managing an owner's profile.

Fixes #34830.
2025-10-20 16:40:01 -07:00
Sahil Batra
e10f1ef260 user-profile: Rename class for edit pencil buttons.
This commit renames classes used for edit pencil button in
the profile modal header so that class names are clear enough
to specify what the buttons do. This rename is needed because
admins can now see "Edit profile" tab, i.e. the "Manage user"
tab for other users, in their own profile modal.

Co-Authored-by: Aman Vishwakarma <vishwakarmarambhawan572@gmail.com>
2025-10-20 16:40:01 -07:00
whilstsomebody
f5af6f9d7f user_profile: Use "Edit profile" label for user's own profile.
This commit renames tab heading, tooltip and aria-label for the
pencil edit icon in the profile modal header from "Manage user"
to "Edit profile" for admin's own profile.

Fixes part of #34830.
2025-10-20 16:40:01 -07:00
Sahil Batra
c58ae7f4f0 user-profile: Update header edit icon for admin's own profile.
For admin users, clicking on pencil edit icon in the profile
modal header opens the "Manage user" tab.

For non-admins, "Profile" settings panel is opened as before.

Fixes part of #34830.

Co-Authored-by: Aman Vishwakarma <vishwakarmarambhawan572@gmail.com>
2025-10-20 16:40:01 -07:00
whilstsomebody
9813f11abf user_profile: Show manage profile tab for admin's own profile.
Previouly admins did not see "Manage user" tab in their own
profile modal. This commit updates it to show the "Manage user"
tab to admins in their own profile modal as well.

Fixes part of #34830.
2025-10-20 16:40:01 -07:00
whilstsomebody
30c6b35b95 custom_profile_fields: Refactor code for handling date type fields.
The currrent behavior is to auto-save field immediately on changing
the value only in "Profile" settings panel but not in "Manage user"
modal where we save the changes only after clicking "Save changes"
button.

To enable this behavior we checked if the date type field is being
changed for user's own account or for other user. But now we will
show "Manage user" modal in user's own profile as well so to enable
the above mentioned behavior we now use for_profile_settings_panel
boolean field.
2025-10-20 16:40:01 -07:00
Sahil Batra
feaaf9ff03 drodpown: Do not use fa-ban icon for disabled option. 2025-10-20 16:30:06 -07:00
Sahil Batra
60074db0ed dropdown-widget: Remove left padding for icons.
This commit removes left padding for the stream privacy
and disabled icon in dropdown widgets. There is already
a padding in grid container and we are fine with removing
the 2px padding to avoid maintaining more pixel values.
2025-10-20 16:30:06 -07:00
Sahil Batra
46f338f54f dropdown-widget: Fix alignment of disabled option.
The alignment of icon and text of the disabled option in
dropdown widget was somewhow broken due to an extra span
element present which broke the grid layout used for
rendering the icon and text properly.

This also makes the layout consistent with other options
shown with icon.

This was due to e643d7e6fd which resulted in space
between icon and text. We could have fix that by using
"~" character to remove whitespace in handlebar templates
but making the layout consistent felt a better choice.
2025-10-20 16:30:06 -07:00
Pratik Chanda
81ca9e1b36 message_list: Hide group DMs where everyone is muted.
This commit hides group DMs where all recipients are muted from
different views.

Fixes: zulip#34886.

Co-authored-by: Aman Agrawal <amanagr@zulip.com>
2025-10-20 15:42:15 -07:00
16 changed files with 136 additions and 75 deletions

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
d="m 2.3431459,2.3431458 a 7.9999996,7.9999996 0 0 0 0,11.3137082 7.9999996,7.9999996 0 0 0 11.3137081,0 7.9999996,7.9999996 0 0 0 0,-11.3137082 7.9999996,7.9999996 0 0 0 -11.3137081,0 z M 3.6287946,3.6287945 A 6.1818179,6.1818179 0 0 1 11.529507,2.9277144 L 2.9337411,11.52348 A 6.1818179,6.1818179 0 0 1 3.6287946,3.6287945 Z M 4.4765193,13.066259 13.066259,4.4765192 a 6.1818179,6.1818179 0 0 1 -0.695054,7.8946858 6.1818179,6.1818179 0 0 1 -7.8946857,0.695054 z"/>
</svg>

After

Width:  |  Height:  |  Size: 550 B

View File

@@ -209,7 +209,11 @@ export function format_date(date: Date | undefined, format: string): string {
return flatpickr.formatDate(date, format);
}
export function initialize_custom_date_type_fields(element_id: string, user_id: number): void {
export function initialize_custom_date_type_fields(
element_id: string,
user_id: number,
for_profile_settings_panel = false,
): void {
const $date_picker_elements = $(element_id).find(".custom_user_field .datepicker");
if ($date_picker_elements.length === 0) {
return;
@@ -224,7 +228,7 @@ export function initialize_custom_date_type_fields(element_id: string, user_id:
// our case it is a valid value when user does not want
// to set any value for the custom profile field.
if ($input_elem.parent().find(".date-field-alt-input").val() === "") {
if (user_id !== people.my_current_user_id()) {
if (!for_profile_settings_panel) {
// For "Manage user" modal, API request is made after
// clicking on "Save changes" button.
return;
@@ -246,7 +250,7 @@ export function initialize_custom_date_type_fields(element_id: string, user_id:
);
const original_value = people.get_custom_profile_data(user_id, field_id)?.value ?? "";
instance.setDate(original_value);
if (user_id !== people.my_current_user_id()) {
if (!for_profile_settings_panel) {
// Trigger "input" event so that save button state can
// be toggled in "Manage user" modal.
$input_elem
@@ -257,7 +261,7 @@ export function initialize_custom_date_type_fields(element_id: string, user_id:
return;
}
if (user_id !== people.my_current_user_id()) {
if (!for_profile_settings_panel) {
// For "Manage user" modal, API request is made after
// clicking on "Save changes" button.
return;

View File

@@ -1073,8 +1073,12 @@ export class Filter {
return this.has_operator("search");
}
is_non_group_direct_message(): boolean {
return this.has_operator("dm") && this.operands("dm")[0]!.split(",").length === 1;
is_search_for_specific_group_or_user(): boolean {
return (
this.has_operator("dm") ||
this.has_operator("dm-including") ||
this.has_operator("sender")
);
}
contains_no_partial_conversations(): boolean {

View File

@@ -237,8 +237,10 @@ export class MessageListData {
}
messages_filtered_for_user_mutes(messages: Message[]): Message[] {
if (this.filter.is_non_group_direct_message()) {
// We are in a 1:1 direct message narrow, so do not do any filtering.
// Don't exclude messages sent by muted users if we're
// searching for a specific group or user, since the user
// presumably wants to see those messages.
if (this.filter.is_search_for_specific_group_or_user()) {
return [...messages];
}
@@ -247,16 +249,9 @@ export class MessageListData {
return true;
}
const recipients = util.extract_pm_recipients(message.to_user_ids);
if (recipients.length > 1) {
// Direct message group message
return true;
}
const recipient_id = Number.parseInt(util.the(recipients), 10);
return (
!muted_users.is_user_muted(recipient_id) &&
!muted_users.is_user_muted(message.sender_id)
);
const recipient_ids = recipients.map((recipient) => Number.parseInt(recipient, 10));
const unmuted_recipient_ids = muted_users.filter_muted_user_ids(recipient_ids);
return unmuted_recipient_ids.length > 0;
});
}

View File

@@ -93,6 +93,15 @@ export function update_email_change_display(): void {
}
}
export function update_role_text(): void {
if ($("#user_role_details").length === 0) {
return;
}
const role_text = people.get_user_type(current_user.user_id)!;
$("#user_role_details .user-details-desc").text(role_text);
}
function display_avatar_upload_complete(): void {
$("#user-avatar-upload-widget .upload-spinner-background").css({visibility: "hidden"});
$("#user-avatar-upload-widget .image-upload-text").show();
@@ -236,6 +245,7 @@ export function add_custom_profile_fields_to_settings(): void {
custom_profile_fields_ui.initialize_custom_date_type_fields(
element_id,
people.my_current_user_id(),
true,
);
custom_profile_fields_ui.initialize_custom_pronouns_type_fields(element_id);
}

View File

@@ -115,6 +115,10 @@ export const update_person = function update(event: UserUpdate): void {
settings_users.update_user_data(event.user_id, event);
user_profile.update_profile_modal_ui(user, event);
if (people.is_my_user_id(event.user_id)) {
settings_account.update_role_text();
}
if (people.is_my_user_id(event.user_id) && current_user.is_owner !== user.is_owner) {
current_user.is_owner = user.is_owner;
settings_org.maybe_disable_widgets();

View File

@@ -638,11 +638,11 @@ export function show_user_profile(user: User, default_tab_key = "profile-tab"):
user_group_picker_pill.get_user_groups_allowed_to_add_members().length > 0 &&
people.is_person_active(user.user_id);
// We currently have the main UI for editing your own profile in
// settings, so can_manage_profile is artificially false for those.
// settings for non-admins, so can_manage_profile is artificially
// false for those.
const can_manage_profile =
(people.can_admin_user(user) || current_user.is_admin) &&
!user.is_system_bot &&
!people.is_my_user_id(user.user_id);
(current_user.is_admin || (user.is_bot && people.can_admin_user(user))) &&
!user.is_system_bot;
const args: Record<string, unknown> = {
can_manage_profile,
date_joined: timerender.get_localized_date_or_time_for_format(
@@ -750,7 +750,9 @@ export function show_user_profile(user: User, default_tab_key = "profile-tab"):
if (can_manage_profile) {
const manage_profile_label = user.is_bot
? $t({defaultMessage: "Manage bot"})
: $t({defaultMessage: "Manage user"});
: people.is_my_user_id(user.user_id)
? $t({defaultMessage: "Edit profile"})
: $t({defaultMessage: "Manage user"});
const manage_profile_tab = {
label: manage_profile_label,
key: "manage-profile-tab",
@@ -1110,6 +1112,35 @@ function toggle_submit_button($edit_form: JQuery): void {
$submit_button.prop("disabled", false);
}
export function disable_user_role_dropdown_if_needed(user: User): void {
if (user.is_owner && people.is_current_user_only_owner()) {
ui_util.disable_element_and_add_tooltip(
$("#user-role-select"),
"Because you are the only organization owner, you cannot change your role.",
);
return;
}
if (user.is_owner && !current_user.is_owner) {
$("#user-role-select").prop("disabled", true);
}
}
function maybe_redirect_to_profile_panel(user_id: number, role: number): void {
// Redirect to profile panel if an admin changes their own role so
// that they are no longer an admin.
if (current_user.user_id !== user_id) {
return;
}
if (
role !== settings_config.user_role_values.owner.code &&
role !== settings_config.user_role_values.admin.code
) {
browser_history.go_to_location("#settings/profile");
}
}
export function show_edit_user_info_modal(user_id: number, $container: JQuery): void {
const person = people.maybe_get_user_by_id(user_id);
const is_active = people.is_person_active(user_id);
@@ -1125,7 +1156,6 @@ export function show_edit_user_info_modal(user_id: number, $container: JQuery):
email: person.delivery_email,
full_name: person.full_name,
user_role_values: settings_config.user_role_values,
disable_role_dropdown: person.is_owner && !current_user.is_owner,
is_active,
hide_deactivate_button,
max_user_name_length: people.MAX_USER_NAME_LENGTH,
@@ -1141,6 +1171,7 @@ export function show_edit_user_info_modal(user_id: number, $container: JQuery):
)
.hide();
}
disable_user_role_dropdown_if_needed(person);
const custom_profile_field_form_selector = "#edit-user-form .custom-profile-field-form";
$(custom_profile_field_form_selector).empty();
@@ -1228,6 +1259,7 @@ export function show_edit_user_info_modal(user_id: number, $container: JQuery):
$("#edit-user-form-error").hide();
hide_button_spinner($submit_button);
original_values = get_current_values($("#edit-user-form"));
maybe_redirect_to_profile_panel(user_id, role);
toggle_submit_button($("#edit-user-form"));
ui_report.success(
$t_html({defaultMessage: "Saved"}),
@@ -1447,17 +1479,13 @@ export function initialize(): void {
e.preventDefault();
});
$("body").on(
"click",
"#user-profile-modal #name .user-profile-manage-others-edit-button",
(e) => {
show_manage_user_tab("manage-profile-tab");
e.stopPropagation();
e.preventDefault();
},
);
$("body").on("click", "#user-profile-modal #name .user-profile-update-user-tab-button", (e) => {
show_manage_user_tab("manage-profile-tab");
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", "#user-profile-modal #name .user-profile-manage-own-edit-button", () => {
$("body").on("click", "#user-profile-modal #name .user-profile-profile-settings-button", () => {
browser_history.go_to_location("#settings/profile");
hide_user_profile();
});

View File

@@ -1156,9 +1156,13 @@ div.overlay .flex.overlay-content > .overlay-container,
.setting-disabled-option {
color: hsl(38deg 46% 54%);
.setting-disabled-option-text,
.setting-disabled-option-icon {
/* Values set to match text alignment in stream dropdown. */
padding: 0 5px 0 1px;
color: hsl(38deg 46% 54%);
}
.setting-disabled-option-icon {
align-self: center;
}
}

View File

@@ -2241,7 +2241,8 @@ body:not(.spectator-view) {
vertically centered properly. */
line-height: 1.214;
.channel-privacy-type-icon {
.channel-privacy-type-icon,
.zulip-icon-deactivated-circle {
grid-area: privacy-icon;
line-height: 1.214;
font-size: 0.93em;
@@ -2250,6 +2251,7 @@ body:not(.spectator-view) {
regardless of the height. */
width: 0.93em;
padding-right: 5px;
padding-left: 0;
/* Override the [data-tippy-root] style in
`tooltips.css`.
Because we are manually holding icon

View File

@@ -24,22 +24,20 @@
</span>
</a>
{{else}}
<a class="dropdown-list-item-common-styles">
<a class="dropdown-list-item-common-styles {{#if is_setting_disabled}}setting-disabled-option{{/if}}">
{{#if stream}}
{{> inline_decorated_channel_name stream=stream show_colored_icon=true}}
{{else if is_direct_message}}
<i class="zulip-icon zulip-icon-users channel-privacy-type-icon"></i> <span class="decorated-dm-name">{{name}}</span>
{{else if is_setting_disabled}}
<span class="setting-disabled-option">
{{#if show_disabled_icon}}
<i class="setting-disabled-option-icon fa fa-ban" aria-hidden="true"></i>
{{/if}}
{{#if show_disabled_option_name}}
<span class="dropdown-list-text-neutral">{{name}}</span>
{{else}}
<span class="dropdown-list-text-neutral">{{t "Disable" }}</span>
{{/if}}
</span>
{{#if show_disabled_icon}}
<i class="setting-disabled-option-icon zulip-icon zulip-icon-deactivated-circle" aria-hidden="true"></i>
{{/if}}
{{#if show_disabled_option_name}}
<span class="setting-disabled-option-text dropdown-list-text-neutral">{{name}}</span>
{{else}}
<span class="setting-disabled-option-text dropdown-list-text-neutral">{{t "Disable" }}</span>
{{/if}}
{{else if (eq unique_id -2)}}
{{!-- This is the option for PresetUrlOption.MAPPING --}}
<i class="zulip-icon zulip-icon-equal channel-privacy-type-icon" aria-hidden="true"></i> <span class="dropdown-list-text-neutral">{{name}}</span>

View File

@@ -20,7 +20,7 @@
<label for="user-role-select" class="modal-field-label">{{t 'User role' }}
{{> ../help_link_widget link="/help/user-roles" }}
</label>
<select name="user-role-select" class="bootstrap-focus-style modal_select" id="user-role-select" data-setting-widget-type="number" {{#if disable_role_dropdown}}disabled{{/if}}>
<select name="user-role-select" class="bootstrap-focus-style modal_select" id="user-role-select" data-setting-widget-type="number">
{{> dropdown_options_widget option_values=user_role_values}}
</select>
</div>

View File

@@ -60,7 +60,7 @@
</div>
</div>
<div class="user-details">
<div class="input-group">
<div id="user_role_details" class="input-group">
<span class="user-details-title">{{t "Role" }}:</span>
<span class="user-details-desc">{{user_role_text}}</span>
</div>

View File

@@ -21,13 +21,16 @@
<span class="user-profile-header-actions">
{{> components/icon_button custom_classes="copy-link-to-user-profile tippy-zulip-delayed-tooltip" icon="link-alt" intent="neutral" data-tippy-content=(t "Copy link to profile") aria-label=(t "Copy link to profile") }}
{{#if is_me}}
{{> components/icon_button custom_classes="user-profile-manage-own-edit-button tippy-zulip-delayed-tooltip" icon="edit" intent="neutral" data-tippy-content=(t "Edit profile") aria-label=(t "Edit profile") }}
{{/if}}
{{#if can_manage_profile}}
{{#if is_bot}}
{{> components/icon_button custom_classes="user-profile-manage-others-edit-button tippy-zulip-delayed-tooltip" icon="edit" intent="neutral" data-tippy-content=(t "Manage bot") aria-label=(t "Manage bot") }}
{{#if can_manage_profile}}
{{> components/icon_button custom_classes="user-profile-update-user-tab-button tippy-zulip-delayed-tooltip" icon="edit" intent="neutral" data-tippy-content=(t "Edit profile") aria-label=(t "Edit profile") }}
{{else}}
{{> components/icon_button custom_classes="user-profile-manage-others-edit-button tippy-zulip-delayed-tooltip" icon="edit" intent="neutral" data-tippy-content=(t "Manage user") aria-label=(t 'Manage user') }}
{{> components/icon_button custom_classes="user-profile-profile-settings-button tippy-zulip-delayed-tooltip" icon="edit" intent="neutral" data-tippy-content=(t "Edit profile") aria-label=(t "Edit profile") }}
{{/if}}
{{else if can_manage_profile}}
{{#if is_bot}}
{{> components/icon_button custom_classes="user-profile-update-user-tab-button tippy-zulip-delayed-tooltip" icon="edit" intent="neutral" data-tippy-content=(t "Manage bot") aria-label=(t "Manage bot") }}
{{else}}
{{> components/icon_button custom_classes="user-profile-update-user-tab-button tippy-zulip-delayed-tooltip" icon="edit" intent="neutral" data-tippy-content=(t "Manage user") aria-label=(t 'Manage user') }}
{{/if}}
{{/if}}
</span>

View File

@@ -403,7 +403,7 @@ test("basics", () => {
terms = [{operator: "dm", operand: "joe@example.com"}];
filter = new Filter(terms);
assert.ok(filter.is_non_group_direct_message());
assert.ok(filter.is_search_for_specific_group_or_user());
assert.ok(filter.contains_only_private_messages());
assert.ok(filter.can_mark_messages_read());
assert.ok(filter.contains_no_partial_conversations());
@@ -421,7 +421,7 @@ test("basics", () => {
{operator: "near", operand: "17"},
];
filter = new Filter(terms);
assert.ok(filter.is_non_group_direct_message());
assert.ok(filter.is_search_for_specific_group_or_user());
assert.ok(filter.contains_only_private_messages());
assert.ok(!filter.can_mark_messages_read());
assert.ok(filter.contains_no_partial_conversations());
@@ -436,7 +436,7 @@ test("basics", () => {
terms = [{operator: "dm", operand: "joe@example.com,jack@example.com"}];
filter = new Filter(terms);
assert.ok(!filter.is_non_group_direct_message());
assert.ok(filter.is_search_for_specific_group_or_user());
assert.ok(filter.contains_only_private_messages());
assert.ok(filter.can_mark_messages_read());
assert.ok(filter.contains_no_partial_conversations());
@@ -474,7 +474,7 @@ test("basics", () => {
terms = [{operator: "dm-including", operand: "joe@example.com"}];
filter = new Filter(terms);
assert.ok(!filter.is_non_group_direct_message());
assert.ok(filter.is_search_for_specific_group_or_user());
assert.ok(filter.contains_only_private_messages());
assert.ok(!filter.has_operator("search"));
assert.ok(!filter.can_mark_messages_read());

View File

@@ -140,19 +140,21 @@ run_test("muting", () => {
// mentions override muting
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
// 10 = muted user, 9 = non-muted user, 11 = you
// 10,12 = muted users, 9 = non-muted user, 11 = you
// muted to group direct message
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
{id: 4, type: "private", to_user_ids: "9,10", sender_id: 10},
// non-muted to group direct message
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
{id: 5, type: "private", to_user_ids: "9,10", sender_id: 9},
// muted to 1:1 direct message
{id: 6, type: "private", to_user_ids: "11", sender_id: 10},
{id: 6, type: "private", to_user_ids: "10", sender_id: 10},
// non-muted to 1:1 direct message
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
{id: 7, type: "private", to_user_ids: "9", sender_id: 9},
// 1:1 direct message to muted
{id: 8, type: "private", to_user_ids: "10", sender_id: 11},
// 1:1 direct message to non-muted
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
// group direct message with everyone muted
{id: 10, type: "private", to_user_ids: "10,12", sender_id: 10},
];
user_topics.update_user_topics(
@@ -162,6 +164,7 @@ run_test("muting", () => {
user_topics.all_visibility_policies.MUTED,
);
muted_users.add_muted_user(10);
muted_users.add_muted_user(12);
// `messages_filtered_for_topic_mutes` should skip filtering
// messages if `excludes_muted_topics` is false.
@@ -180,12 +183,13 @@ run_test("muting", () => {
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true}, // mentions override muting
// `messages_filtered_for_topic_mutes` does not affect direct messages
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
{id: 6, type: "private", to_user_ids: "11", sender_id: 10},
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
{id: 4, type: "private", to_user_ids: "9,10", sender_id: 10},
{id: 5, type: "private", to_user_ids: "9,10", sender_id: 9},
{id: 6, type: "private", to_user_ids: "10", sender_id: 10},
{id: 7, type: "private", to_user_ids: "9", sender_id: 9},
{id: 8, type: "private", to_user_ids: "10", sender_id: 11},
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
{id: 10, type: "private", to_user_ids: "10,12", sender_id: 10},
]);
const res_user = mld.messages_filtered_for_user_mutes(msgs);
@@ -195,11 +199,11 @@ run_test("muting", () => {
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
// muted to group direct message
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
{id: 4, type: "private", to_user_ids: "9,10", sender_id: 10},
// non-muted to group direct message
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
{id: 5, type: "private", to_user_ids: "9,10", sender_id: 9},
// non-muted to 1:1 direct message
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
{id: 7, type: "private", to_user_ids: "9", sender_id: 9},
// 1:1 direct message to non-muted
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
]);
@@ -210,9 +214,9 @@ run_test("muting", () => {
assert.deepEqual(filtered_messages, [
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
{id: 4, type: "private", to_user_ids: "9,10", sender_id: 10},
{id: 5, type: "private", to_user_ids: "9,10", sender_id: 9},
{id: 7, type: "private", to_user_ids: "9", sender_id: 9},
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
]);

View File

@@ -14,6 +14,7 @@ const settings_account = mock_esm("../src/settings_account", {
update_email() {},
update_full_name() {},
update_account_settings_display() {},
update_role_text() {},
});
const settings_users = mock_esm("../src/settings_users", {
update_user_data() {},