invite: Add guest visibility note with live-updating user count.

Previously, when the guest role was selected in the Invite modal and
guests were restricted from viewing all other users, there was no
indication of how many users the invited guests would be able to see
once they joined.

This commit resolves that issue by adding a note in the Invite modal
that dynamically informs users of the number of visible users.

Fixes #31159.
This commit is contained in:
Aditya Kumar Kasaudhan 2024-10-09 22:00:33 +05:30 committed by Tim Abbott
parent 574932d4e3
commit 3b1877ea99
7 changed files with 114 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import assert from "minimalistic-assert";
import {z} from "zod"; import {z} from "zod";
import copy_invite_link from "../templates/copy_invite_link.hbs"; import copy_invite_link from "../templates/copy_invite_link.hbs";
import render_guest_visible_users_message from "../templates/guest_visible_users_message.hbs";
import render_invitation_failed_error from "../templates/invitation_failed_error.hbs"; import render_invitation_failed_error from "../templates/invitation_failed_error.hbs";
import render_invite_user_modal from "../templates/invite_user_modal.hbs"; import render_invite_user_modal from "../templates/invite_user_modal.hbs";
import render_invite_tips_banner from "../templates/modal_banner/invite_tips_banner.hbs"; import render_invite_tips_banner from "../templates/modal_banner/invite_tips_banner.hbs";
@ -22,6 +23,7 @@ import {$t, $t_html} from "./i18n";
import * as input_pill from "./input_pill"; import * as input_pill from "./input_pill";
import * as invite_stream_picker_pill from "./invite_stream_picker_pill"; import * as invite_stream_picker_pill from "./invite_stream_picker_pill";
import {page_params} from "./page_params"; import {page_params} from "./page_params";
import * as peer_data from "./peer_data";
import * as settings_config from "./settings_config"; import * as settings_config from "./settings_config";
import * as settings_data from "./settings_data"; import * as settings_data from "./settings_data";
import {current_user, realm} from "./state_data"; import {current_user, realm} from "./state_data";
@ -308,6 +310,31 @@ function set_streams_to_join_list_visibility(): void {
} }
} }
function update_guest_visible_users_count(): void {
const invite_as = Number.parseInt(
$<HTMLSelectOneElement>("select:not([multiple])#invite_as").val()!,
10,
);
assert(!Number.isNaN(invite_as));
const guest_role_selected = invite_as === settings_config.user_role_values.guest.code;
if (!guest_role_selected || settings_data.guests_can_access_all_other_users()) {
$("#guest_visible_users_container").hide();
return;
}
const stream_ids = $("#invite_select_default_streams").is(":checked")
? stream_data.get_default_stream_ids()
: stream_pill.get_stream_ids(stream_pill_widget);
const visible_users_count = peer_data.get_unique_subscriber_count_for_streams(stream_ids);
const message_html = render_guest_visible_users_message({
user_count: visible_users_count,
});
$("#guest_visible_users_container").html(message_html).show();
}
function generate_invite_tips_data(): Record<string, boolean> { function generate_invite_tips_data(): Record<string, boolean> {
const {realm_description, realm_icon_source, custom_profile_fields} = realm; const {realm_description, realm_icon_source, custom_profile_fields} = realm;
@ -360,6 +387,11 @@ function open_invite_user_modal(e: JQuery.ClickEvent<Document, undefined>): void
stream_pill_widget = invite_stream_picker_pill.create($stream_pill_container); stream_pill_widget = invite_stream_picker_pill.create($stream_pill_container);
} }
$("#invite_as, #invite_streams_container .input, #invite_select_default_streams").on(
"change",
update_guest_visible_users_count,
);
$("#invite-user-modal").on("click", ".setup-tips-container .banner_content a", () => { $("#invite-user-modal").on("click", ".setup-tips-container .banner_content a", () => {
dialog_widget.close(); dialog_widget.close();
}); });

View File

@ -158,3 +158,18 @@ export function is_user_subscribed(stream_id: number, user_id: number): boolean
const subscribers = get_user_set(stream_id); const subscribers = get_user_set(stream_id);
return subscribers.has(user_id); return subscribers.has(user_id);
} }
export function get_unique_subscriber_count_for_streams(stream_ids: number[]): number {
const valid_subscribers = new LazySet([]);
for (const stream_id of stream_ids) {
const subscribers = get_user_set(stream_id);
for (const user_id of subscribers.keys()) {
if (!people.is_valid_bot_user(user_id)) {
valid_subscribers.add(user_id);
}
}
}
return valid_subscribers.size;
}

View File

@ -381,3 +381,10 @@ export function get_request_data_for_stream_privacy(selected_val: string): {
} }
} }
} }
export function guests_can_access_all_other_users(): boolean {
const everyone_group = user_groups.get_user_group_from_id(
realm.realm_can_access_all_users_group,
);
return everyone_group.name === "role:everyone";
}

View File

@ -0,0 +1,6 @@
<p id="guest_visible_users_message">
{{t 'Guests will be able to see {user_count} users in their channels when they join.'}}
<a id="guest_help_link" class="help_link_widget" href="/help/guest-users#configure-whether-guests-can-see-all-other-users" target="_blank" rel="noopener noreferrer">
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
</a>
</p>

View File

@ -91,5 +91,7 @@
</div> </div>
</div> </div>
</div> </div>
<div id="guest_visible_users_container" class="input-group" style="display: none;">
</div>
{{/if}} {{/if}}
</form> </form>

View File

@ -44,6 +44,13 @@ const george = {
full_name: "George", full_name: "George",
user_id: 103, user_id: 103,
}; };
const bot_botson = {
email: "botson-bot@example.com",
user_id: 35,
full_name: "Bot Botson",
is_bot: true,
role: 300,
};
function contains_sub(subs, sub) { function contains_sub(subs, sub) {
return subs.some((s) => s.name === sub.name); return subs.some((s) => s.name === sub.name);
@ -292,3 +299,20 @@ test("is_subscriber_subset", () => {
peer_data.is_subscriber_subset(undefined, sub_a.stream_id); peer_data.is_subscriber_subset(undefined, sub_a.stream_id);
blueslip.reset(); blueslip.reset();
}); });
test("get_unique_subscriber_count_for_streams", () => {
const sub = {name: "Rome", subscribed: true, stream_id: 1001};
stream_data.add_sub(sub);
people.add_active_user(fred);
people.add_active_user(gail);
people.add_active_user(george);
people.add_active_user(bot_botson);
const stream_id = sub.stream_id;
peer_data.set_subscribers(stream_id, [me.user_id, fred.user_id, bot_botson.user_id]);
const count = peer_data.get_unique_subscriber_count_for_streams([stream_id]);
assert.equal(count, 2);
});

View File

@ -946,3 +946,31 @@ run_test("user_can_create_web_public_streams", ({override}) => {
override(realm, "server_web_public_streams_enabled", false); override(realm, "server_web_public_streams_enabled", false);
assert.equal(settings_data.user_can_create_web_public_streams(), false); assert.equal(settings_data.user_can_create_web_public_streams(), false);
}); });
run_test("guests_can_access_all_other_users", () => {
const guest_user_id = 1;
const member_user_id = 2;
const members = {
name: "role:members",
id: 1,
members: new Set([member_user_id]),
is_system_group: true,
direct_subgroup_ids: new Set([]),
};
const everyone = {
name: "role:everyone",
id: 2,
members: new Set([guest_user_id]),
is_system_group: true,
direct_subgroup_ids: new Set([1]),
};
user_groups.initialize({realm_user_groups: [members]});
realm.realm_can_access_all_users_group = members.id;
assert.ok(!settings_data.guests_can_access_all_other_users());
user_groups.initialize({realm_user_groups: [members, everyone]});
realm.realm_can_access_all_users_group = everyone.id;
assert.ok(settings_data.guests_can_access_all_other_users());
});