users: Add "(guest)" to names for guest users.

This commit adds code to add "(guest)" to user names of guest
users in the following places -
- right sidebar
- user pills, including the pills in search suggestion typehaead
- typeaheads for user
- sender names in message feed
- user profile popover and modals.
- user name in not subscribed warning banner.

Note that the indicator is shown only if enable_guest_user_indicator
setting is set to true.

As a result of this change, we now translate "deactivated" text
shown in user pills for deactivated users.

Fixes part of #26700.
This commit is contained in:
Sahil Batra 2023-09-13 23:00:52 +05:30 committed by Tim Abbott
parent c51c1d5135
commit 49a047c27f
29 changed files with 100 additions and 9 deletions

View File

@ -140,6 +140,10 @@ IGNORED_PHRASES = [
r"does not apply to moderators and administrators",
# Used in message-delete-time-limit setting label
r"does not apply to administrators",
# Used as indicator with names for guest users.
r"guest",
# Used in pills for deactivated users.
r"deactivated",
]
# Sort regexes in descending order of their lengths. As a result, the

View File

@ -152,6 +152,7 @@ export function info_for(user_id) {
user_circle_class,
status_text,
user_list_style,
should_add_guest_user_indicator: people.should_add_guest_user_indicator(user_id),
};
}

View File

@ -173,6 +173,7 @@ export function warn_if_mentioning_unsubscribed_user(mentioned, $textarea) {
can_subscribe_other_users,
name: mentioned.full_name,
classname: compose_banner.CLASSNAMES.recipient_not_subscribed,
should_add_guest_user_indicator: people.should_add_guest_user_indicator(user_id),
};
const new_row = render_not_subscribed_warning(context);

View File

@ -17,6 +17,7 @@ export type InputPillItem<T> = {
img_src?: string;
deactivated?: boolean;
status_emoji_info?: EmojiRenderingDetails & {emoji_alt_code: boolean}; // TODO: Move this in user_status.js
should_add_guest_user_indicator?: boolean;
} & T;
type InputPillCreateOptions<T> = {
@ -55,6 +56,7 @@ type InputPillRenderingDetails = {
deactivated?: boolean;
has_status?: boolean;
status_emoji_info?: EmojiRenderingDetails & {emoji_alt_code: boolean};
should_add_guest_user_indicator?: boolean;
};
// These are the functions that are exposed to other modules.
@ -156,6 +158,7 @@ export function create<T>(opts: InputPillCreateOptions<T>): InputPillContainer<T
display_value: item.display_value,
has_image,
deactivated: item.deactivated,
should_add_guest_user_indicator: item.should_add_guest_user_indicator,
};
if (has_image) {

View File

@ -418,6 +418,8 @@ export class MessageListView {
message_container.sender_is_bot = people.sender_is_bot(message_container.msg);
message_container.sender_is_guest = people.sender_is_guest(message_container.msg);
message_container.should_add_guest_indicator_for_sender =
people.should_add_guest_user_indicator(message_container.msg.sender_id);
message_container.small_avatar_url = people.small_avatar_url(message_container.msg);
if (message_container.msg.stream_id) {

View File

@ -68,6 +68,7 @@ export const page_params: {
realm_private_message_policy: number;
realm_push_notifications_enabled: boolean;
realm_sentry_key: string | undefined;
realm_enable_guest_user_indicator: boolean;
realm_upload_quota_mib: number | null;
realm_uri: string;
realm_user_group_edit_policy: number;

View File

@ -770,6 +770,15 @@ export function user_is_bot(user_id: number): boolean {
return user.is_bot;
}
export function should_add_guest_user_indicator(user_id: number): boolean {
if (!page_params.realm_enable_guest_user_indicator) {
return false;
}
const user = get_by_user_id(user_id);
return user.is_guest;
}
export function user_can_direct_message(recipient_ids_string: string): boolean {
// Common function for checking if a user can send a direct
// message to the target user (or group of users) represented by a

View File

@ -34,6 +34,7 @@ function highlight_person(person, highlighter) {
display_value: new Handlebars.SafeString(highlighted_name),
has_image: true,
img_src: avatar_url,
should_add_guest_user_indicator: people.should_add_guest_user_indicator(person.user_id),
};
}

View File

@ -92,6 +92,7 @@ export function render_person(person) {
user_circle_class,
is_person: true,
status_emoji_info,
should_add_guest_user_indicator: people.should_add_guest_user_indicator(person.user_id),
};
typeahead_arguments.secondary = person.delivery_email;

View File

@ -289,6 +289,7 @@ function get_user_card_popover_data(
user_mention_syntax: people.get_mention_syntax(user.full_name, user.user_id),
date_joined,
spectator_view,
should_add_guest_user_indicator: people.should_add_guest_user_indicator(user.user_id),
};
if (user.is_bot) {

View File

@ -53,12 +53,12 @@ export function create_item_from_email(email, current_items) {
img_src: avatar_url,
deactivated: false,
status_emoji_info,
should_add_guest_user_indicator: people.should_add_guest_user_indicator(user.user_id),
};
// We pass deactivated true for a deactivated user
if (!people.is_person_active(user.user_id)) {
item.deactivated = true;
item.display_value = user.full_name + " (deactivated)";
}
return item;
@ -81,6 +81,7 @@ export function append_person(opts) {
email: person.email,
img_src: avatar_url,
status_emoji_info,
should_add_guest_user_indicator: people.should_add_guest_user_indicator(person.user_id),
};
pill_widget.appendValidatedData(pill_data);

View File

@ -366,6 +366,7 @@ export function show_user_profile(user, default_tab_key = "profile-tab") {
user_is_guest: user.is_guest,
show_user_subscribe_widget,
can_manage_profile,
should_add_guest_user_indicator: people.should_add_guest_user_indicator(user.user_id),
};
if (user.is_bot) {

View File

@ -223,7 +223,7 @@ ul {
display: inline-block;
text-align: center;
&:not(.popover_action_icon) {
&:not(.popover_action_icon, .guest-indicator) {
width: 14px;
}

View File

@ -1,9 +1,17 @@
{{#> compose_banner }}
<p class="banner_message">
{{#if can_subscribe_other_users}}
{{#if should_add_guest_user_indicator}}
{{#tr}}<strong>{name}</strong> <i>(guest)</i> is not subscribed to this stream. They will not be notified unless you subscribe them.{{/tr}}
{{else}}
{{#tr}}<strong>{name}</strong> is not subscribed to this stream. They will not be notified unless you subscribe them.{{/tr}}
{{/if}}
{{else}}
{{#if should_add_guest_user_indicator}}
{{#tr}}<strong>{name}</strong> <i>(guest)</i> is not subscribed to this stream. They will not be notified if you mention them.{{/tr}}
{{else}}
{{#tr}}<strong>{name}</strong> is not subscribed to this stream. They will not be notified if you mention them.{{/tr}}
{{/if}}
{{/if}}
</p>
{{/compose_banner}}

View File

@ -4,6 +4,8 @@
{{/if}}
<span class="pill-label">
<span class="pill-value">{{ display_value }}</span>
{{~#if should_add_guest_user_indicator}}&nbsp;<i>({{t 'guest'}})</i>{{~/if~}}
{{~#if deactivated}}&nbsp;({{t 'deactivated'}}){{~/if~}}
{{~#if has_status~}}
{{~> status_emoji status_emoji_info~}}
{{~/if~}}

View File

@ -5,7 +5,7 @@
{{#if include_sender}}
<span class="sender_info_hover sender_name" role="button" tabindex="0">
<span class="view_user_card_tooltip sender_name_text" data-tooltip-template-id="view-user-card-tooltip-template">
{{msg/sender_full_name}}
{{> user_full_name name=msg/sender_full_name should_add_guest_user_indicator=should_add_guest_indicator_for_sender}}
</span>
{{#unless status_message}}
{{> status_emoji msg/status_emoji_info}}

View File

@ -3,7 +3,7 @@
<div class="user-card-popover-content no-auto-hide-right-sidebar-overlay">
<ul class="nav nav-list user-card-popover-actions" id="user_card_popover" data-user-id="{{user_id}}">
<li class="popover_user_name_row">
<b class="user_full_name" data-tippy-content="{{user_full_name}}">{{user_full_name}}</b>
<b class="user_full_name" data-tippy-content="{{user_full_name}}">{{> ../../user_full_name name=user_full_name}}</b>
{{#if is_active }}
{{#if is_bot}}
<i class="zulip-icon zulip-icon-bot" aria-hidden="true"></i>

View File

@ -8,13 +8,13 @@
{{#if user_list_style.WITH_STATUS}}
<div class="user-name-and-status-wrapper">
<div class="user-name-and-status-emoji">
<span class="user-name">{{name}}</span>
<span class="user-name">{{> user_full_name}}</span>
{{> status_emoji status_emoji_info}}
</div>
<span class="status-text">{{status_text}}</span>
</div>
{{else}}
<span class="user-name">{{name}}</span>
{{> user_full_name}}
{{> status_emoji status_emoji_info}}
{{/if}}
</a>

View File

@ -24,6 +24,9 @@
{{~ primary ~}}
{{/if}}
</strong>
{{~#if should_add_guest_user_indicator}}
<i>({{t 'guest'}})</i>
{{~/if}}
{{~#if has_status}}
{{> status_emoji status_emoji_info}}
{{~/if}}

View File

@ -0,0 +1,5 @@
{{#if should_add_guest_user_indicator}}
{{#tr}}{name} <i class="guest-indicator">(guest)</i>{{/tr}}
{{else}}
<span class="user-name">{{name}}</span>
{{/if}}

View File

@ -11,7 +11,7 @@
{{#if is_bot}}
<i class="zulip-icon zulip-icon-bot" aria-hidden="true"></i>
{{/if}}
<span class="user_profile_name">{{full_name}}</span>
<span class="user_profile_name">{{> user_full_name name=full_name}}</span>
{{#if is_me}}
<a href="/#settings/profile">
<i class="fa fa-edit tippy-zulip-tooltip user_profile_manage_own_edit_button" data-tippy-content="{{t 'Edit profile' }}" aria-hidden="true"></i>

View File

@ -400,6 +400,7 @@ test("first/prev/next", ({override, mock_template}) => {
WITH_STATUS: true,
WITH_AVATAR: false,
},
should_add_guest_user_indicator: false,
});
break;
case fred.user_id:
@ -419,6 +420,7 @@ test("first/prev/next", ({override, mock_template}) => {
WITH_STATUS: true,
WITH_AVATAR: false,
},
should_add_guest_user_indicator: false,
});
break;
/* istanbul ignore next */
@ -489,6 +491,7 @@ test("insert_one_user_into_empty_list", ({override, mock_template}) => {
WITH_STATUS: true,
WITH_AVATAR: false,
},
should_add_guest_user_indicator: false,
});
assert.ok(html.startsWith("<li data-user-id="));
return html;

View File

@ -519,6 +519,7 @@ test("get_items_for_users", () => {
user_circle_class: "user_circle_green",
user_id: 1001,
user_list_style,
should_add_guest_user_indicator: false,
},
{
faded: false,
@ -531,6 +532,7 @@ test("get_items_for_users", () => {
user_circle_class: "user_circle_empty",
user_id: 1002,
user_list_style,
should_add_guest_user_indicator: false,
},
{
faded: false,
@ -543,6 +545,7 @@ test("get_items_for_users", () => {
user_circle_class: "user_circle_empty",
user_id: 1003,
user_list_style,
should_add_guest_user_indicator: false,
},
]);
});

View File

@ -124,7 +124,7 @@ run_test("pills", ({override, override_rewire}) => {
assert.ok(get_by_email_called);
assert.equal(typeof res, "object");
assert.equal(res.user_id, iago.user_id);
assert.equal(res.display_value, iago.full_name + " (deactivated)");
assert.equal(res.display_value, iago.full_name);
assert.ok(res.deactivated);
people.add_active_user(iago);
})();

View File

@ -37,6 +37,7 @@ mock_esm("../src/rows", {
mock_esm("../src/people", {
sender_is_bot: () => false,
sender_is_guest: () => false,
should_add_guest_user_indicator: () => false,
small_avatar_url: () => "fake/small/avatar/url",
});

View File

@ -1276,6 +1276,19 @@ test_people("get_realm_active_human_users", () => {
assert.deepEqual(humans, [me]);
});
test_people("should_show_guest_user_indicator", () => {
people.add_active_user(charles);
people.add_active_user(guest);
page_params.realm_enable_guest_user_indicator = false;
assert.equal(people.should_add_guest_user_indicator(charles.user_id), false);
assert.equal(people.should_add_guest_user_indicator(guest.user_id), false);
page_params.realm_enable_guest_user_indicator = true;
assert.equal(people.should_add_guest_user_indicator(charles.user_id), false);
assert.equal(people.should_add_guest_user_indicator(guest.user_id), true);
});
// reset to native Date()
run_test("reset MockDate", () => {
MockDate.reset();

View File

@ -853,6 +853,7 @@ test("people_suggestions", ({override, mock_template}) => {
user_id: 202,
full_name: "Bob Térry",
avatar_url: example_avatar_url,
is_guest: false,
};
const alice = {
@ -917,6 +918,31 @@ test("people_suggestions", ({override, mock_template}) => {
assert.equal(get_avatar_url("sender:bob@zulip.com"), expectedString);
assert.equal(get_avatar_url("dm-including:bob@zulip.com"), expectedString);
function get_should_add_guest_user_indicator(q) {
return suggestions.lookup_table.get(q).user_pill_context.should_add_guest_user_indicator;
}
page_params.realm_enable_guest_user_indicator = true;
suggestions = get_suggestions(query);
assert.equal(get_should_add_guest_user_indicator("dm:bob@zulip.com"), false);
assert.equal(get_should_add_guest_user_indicator("sender:bob@zulip.com"), false);
assert.equal(get_should_add_guest_user_indicator("dm-including:bob@zulip.com"), false);
bob.is_guest = true;
suggestions = get_suggestions(query);
assert.equal(get_should_add_guest_user_indicator("dm:bob@zulip.com"), true);
assert.equal(get_should_add_guest_user_indicator("sender:bob@zulip.com"), true);
assert.equal(get_should_add_guest_user_indicator("dm-including:bob@zulip.com"), true);
page_params.realm_enable_guest_user_indicator = false;
suggestions = get_suggestions(query);
assert.equal(get_should_add_guest_user_indicator("dm:bob@zulip.com"), false);
assert.equal(get_should_add_guest_user_indicator("sender:bob@zulip.com"), false);
assert.equal(get_should_add_guest_user_indicator("dm-including:bob@zulip.com"), false);
suggestions = get_suggestions("Ted "); // note space
expected = ["Ted", "sender:ted@zulip.com", "dm:ted@zulip.com", "dm-including:ted@zulip.com"];

View File

@ -306,7 +306,7 @@ test_ui("populate_user_groups", ({mock_template, override, override_rewire}) =>
assert.ok(get_by_email_called);
assert.equal(typeof res, "object");
assert.equal(res.user_id, bob.user_id);
assert.equal(res.display_value, bob.full_name + " (deactivated)");
assert.equal(res.display_value, bob.full_name);
assert.ok(res.deactivated);
people.add_active_user(bob);
})();

View File

@ -36,6 +36,7 @@ const isaac_item = {
deactivated: false,
img_src: `http://zulip.zulipdev.com/avatar/${isaac.user_id}?s=50`,
status_emoji_info: undefined,
should_add_guest_user_indicator: false,
};
let pill_widget = {};