From 3b1877ea996960de44c2f316255e389c47a28866 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Kasaudhan Date: Wed, 9 Oct 2024 22:00:33 +0530 Subject: [PATCH] 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. --- web/src/invite.ts | 32 +++++++++++++++++++ web/src/peer_data.ts | 15 +++++++++ web/src/settings_data.ts | 7 ++++ web/templates/guest_visible_users_message.hbs | 6 ++++ web/templates/invite_user_modal.hbs | 2 ++ web/tests/peer_data.test.js | 24 ++++++++++++++ web/tests/settings_data.test.js | 28 ++++++++++++++++ 7 files changed, 114 insertions(+) create mode 100644 web/templates/guest_visible_users_message.hbs diff --git a/web/src/invite.ts b/web/src/invite.ts index e1aef02547..bbe180eee3 100644 --- a/web/src/invite.ts +++ b/web/src/invite.ts @@ -5,6 +5,7 @@ import assert from "minimalistic-assert"; import {z} from "zod"; 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_invite_user_modal from "../templates/invite_user_modal.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 invite_stream_picker_pill from "./invite_stream_picker_pill"; import {page_params} from "./page_params"; +import * as peer_data from "./peer_data"; import * as settings_config from "./settings_config"; import * as settings_data from "./settings_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( + $("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 { const {realm_description, realm_icon_source, custom_profile_fields} = realm; @@ -360,6 +387,11 @@ function open_invite_user_modal(e: JQuery.ClickEvent): void 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", () => { dialog_widget.close(); }); diff --git a/web/src/peer_data.ts b/web/src/peer_data.ts index ddcfb0de40..1801cc05b4 100644 --- a/web/src/peer_data.ts +++ b/web/src/peer_data.ts @@ -158,3 +158,18 @@ export function is_user_subscribed(stream_id: number, user_id: number): boolean const subscribers = get_user_set(stream_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; +} diff --git a/web/src/settings_data.ts b/web/src/settings_data.ts index f36f60bab4..258393ffec 100644 --- a/web/src/settings_data.ts +++ b/web/src/settings_data.ts @@ -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"; +} diff --git a/web/templates/guest_visible_users_message.hbs b/web/templates/guest_visible_users_message.hbs new file mode 100644 index 0000000000..0f030c88ca --- /dev/null +++ b/web/templates/guest_visible_users_message.hbs @@ -0,0 +1,6 @@ +

+ {{t 'Guests will be able to see {user_count} users in their channels when they join.'}} + + + +

diff --git a/web/templates/invite_user_modal.hbs b/web/templates/invite_user_modal.hbs index 3cf9da4502..c0c8806115 100644 --- a/web/templates/invite_user_modal.hbs +++ b/web/templates/invite_user_modal.hbs @@ -91,5 +91,7 @@ + {{/if}} diff --git a/web/tests/peer_data.test.js b/web/tests/peer_data.test.js index 02629b0de2..1b6b19e0bf 100644 --- a/web/tests/peer_data.test.js +++ b/web/tests/peer_data.test.js @@ -44,6 +44,13 @@ const george = { full_name: "George", 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) { 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); 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); +}); diff --git a/web/tests/settings_data.test.js b/web/tests/settings_data.test.js index 0e3c90a36a..91ef79a1d8 100644 --- a/web/tests/settings_data.test.js +++ b/web/tests/settings_data.test.js @@ -946,3 +946,31 @@ run_test("user_can_create_web_public_streams", ({override}) => { override(realm, "server_web_public_streams_enabled", 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()); +});