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()); +});