channel_settings: Improve clarity of subscription result message.

The result message shown when adding members to a stream
previously listed the name of every member being added,
which could be noisy for large groups.

This commit updates stream_subscription_request_result.hbs to
conditionally display clearer, more concise messages based
on the number of subscribed and already subscribed users.

This improves readability and user experience when handling
large subscription actions.

Fixes part of #34347

Co-authored-by: Rajeev Tapadia <108951750+rajeevtapadia@users.noreply.github.com>
Signed-off-by: apoorvapendse <apoorvavpendse@gmail.com>
This commit is contained in:
Apoorva Pendse
2025-04-15 14:32:16 +05:30
committed by Tim Abbott
parent 5288631514
commit f75fbdda8b
4 changed files with 134 additions and 73 deletions

View File

@@ -1,3 +1,4 @@
import Handlebars from "handlebars/runtime.js";
import $ from "jquery"; import $ from "jquery";
import assert from "minimalistic-assert"; import assert from "minimalistic-assert";
import {z} from "zod"; import {z} from "zod";
@@ -31,6 +32,7 @@ import * as subscriber_api from "./subscriber_api.ts";
import type {CombinedPillContainer} from "./typeahead_helper.ts"; import type {CombinedPillContainer} from "./typeahead_helper.ts";
import * as user_groups from "./user_groups.ts"; import * as user_groups from "./user_groups.ts";
import * as user_sort from "./user_sort.ts"; import * as user_sort from "./user_sort.ts";
import * as util from "./util.ts";
const remove_user_id_api_response_schema = z.object({ const remove_user_id_api_response_schema = z.object({
removed: z.array(z.string()), removed: z.array(z.string()),
@@ -68,37 +70,81 @@ function get_sub(stream_id: number): StreamSubscription | undefined {
return sub; return sub;
} }
function show_stream_subscription_request_result({ function generate_subscribe_success_messages(
message, subscribed_users: User[],
add_class, already_subscribed_users: User[],
remove_class, ): {
subscribed_users, subscribed_users_message: string;
already_subscribed_users, already_subscribed_users_message: string;
ignored_deactivated_users, } {
}: { const subscribed_user_links = subscribed_users.map(
message?: string; (user) =>
add_class: string; `<a data-user-id="${user.user_id}" class="view_user_profile">${Handlebars.Utils.escapeExpression(user.full_name)}</a>`,
remove_class: string; );
subscribed_users?: User[]; const already_subscribed_user_links = already_subscribed_users.map(
already_subscribed_users?: User[]; (user) =>
ignored_deactivated_users?: User[]; `<a data-user-id="${user.user_id}" class="view_user_profile">${Handlebars.Utils.escapeExpression(user.full_name)}</a>`,
}): void { );
const subscribed_users_message = util.format_array_as_list_with_conjunction(
subscribed_user_links,
"long",
);
const already_subscribed_users_message = util.format_array_as_list_with_conjunction(
already_subscribed_user_links,
"long",
);
return {
subscribed_users_message,
already_subscribed_users_message,
};
}
function show_stream_subscription_request_error_result(error_message: string): void {
const $stream_subscription_req_result_elem = $( const $stream_subscription_req_result_elem = $(
".stream_subscription_request_result", ".stream_subscription_request_result",
).expectOne(); ).expectOne();
const html = render_stream_subscription_request_result({ const html = render_stream_subscription_request_result({
message, error_message,
});
scroll_util.get_content_element($stream_subscription_req_result_elem).html(html);
}
function show_stream_subscription_request_success_result({
subscribed_users, subscribed_users,
already_subscribed_users, already_subscribed_users,
ignored_deactivated_users, ignored_deactivated_users,
}: {
subscribed_users: User[];
already_subscribed_users: User[];
ignored_deactivated_users: User[];
}): void {
const subscribed_users_count = subscribed_users.length;
const already_subscribed_users_count = already_subscribed_users.length;
const is_total_subscriber_more_than_five =
subscribed_users_count + already_subscribed_users_count > 5;
const $stream_subscription_req_result_elem = $(
".stream_subscription_request_result",
).expectOne();
let subscribe_success_messages;
if (!is_total_subscriber_more_than_five) {
subscribe_success_messages = generate_subscribe_success_messages(
subscribed_users,
already_subscribed_users,
);
}
const html = render_stream_subscription_request_result({
subscribe_success_messages,
subscribed_users,
already_subscribed_users,
subscribed_users_count,
already_subscribed_users_count,
is_total_subscriber_more_than_five,
ignored_deactivated_users,
}); });
scroll_util.get_content_element($stream_subscription_req_result_elem).html(html); scroll_util.get_content_element($stream_subscription_req_result_elem).html(html);
if (add_class) {
$stream_subscription_req_result_elem.addClass(add_class);
}
if (remove_class) {
$stream_subscription_req_result_elem.removeClass(remove_class);
}
} }
function update_notification_choice_checkbox(added_user_count: number): void { function update_notification_choice_checkbox(added_user_count: number): void {
@@ -265,18 +311,20 @@ function subscribe_new_users({pill_user_ids}: {pill_user_ids: number[]}): void {
people.get_by_user_id(user_id), people.get_by_user_id(user_id),
); );
} }
if (user_id_set.size === 0) {
show_stream_subscription_request_result({ const user_ids = [...user_id_set];
message: $t({defaultMessage: "No user to subscribe."}), if (user_ids.length === 0) {
add_class: "text-error", // No need to make a network call in this case.
remove_class: "text-success", // This will show "All users were already subscribed."
pill_widget.clear();
show_stream_subscription_request_success_result({
subscribed_users: [],
already_subscribed_users: [],
ignored_deactivated_users, ignored_deactivated_users,
}); });
return; return;
} }
const user_ids = [...user_id_set];
function invite_success(raw_data: unknown): void { function invite_success(raw_data: unknown): void {
const data = add_user_ids_api_response_schema.parse(raw_data); const data = add_user_ids_api_response_schema.parse(raw_data);
pill_widget.clear(); pill_widget.clear();
@@ -287,9 +335,7 @@ function subscribe_new_users({pill_user_ids}: {pill_user_ids: number[]}): void {
people.get_by_user_id(Number(user_id)), people.get_by_user_id(Number(user_id)),
); );
show_stream_subscription_request_result({ show_stream_subscription_request_success_result({
add_class: "text-success",
remove_class: "text-error",
subscribed_users, subscribed_users,
already_subscribed_users, already_subscribed_users,
ignored_deactivated_users, ignored_deactivated_users,
@@ -297,7 +343,7 @@ function subscribe_new_users({pill_user_ids}: {pill_user_ids: number[]}): void {
} }
function invite_failure(xhr: JQuery.jqXHR): void { function invite_failure(xhr: JQuery.jqXHR): void {
let message = "Failed to subscribe user!"; let error_message = "Failed to subscribe user!";
const parsed = z const parsed = z
.object({ .object({
@@ -308,13 +354,9 @@ function subscribe_new_users({pill_user_ids}: {pill_user_ids: number[]}): void {
.safeParse(xhr.responseJSON); .safeParse(xhr.responseJSON);
if (parsed.success) { if (parsed.success) {
message = parsed.data.msg; error_message = parsed.data.msg;
} }
show_stream_subscription_request_result({ show_stream_subscription_request_error_result(error_message);
message,
add_class: "text-error",
remove_class: "text-success",
});
} }
subscriber_api.add_user_ids_to_stream( subscriber_api.add_user_ids_to_stream(
@@ -359,11 +401,8 @@ function remove_subscriber({
function removal_failure(): void { function removal_failure(): void {
buttons.hide_button_loading_indicator($remove_button); buttons.hide_button_loading_indicator($remove_button);
show_stream_subscription_request_result({ const error_message = $t({defaultMessage: "Error removing user from this channel."});
message: $t({defaultMessage: "Error removing user from this channel."}), show_stream_subscription_request_error_result(error_message);
add_class: "text-error",
remove_class: "text-success",
});
} }
function remove_user_from_private_stream(): void { function remove_user_from_private_stream(): void {

View File

@@ -155,6 +155,10 @@ h4.user_group_setting_subsection_title {
.subscriber_list_settings_container { .subscriber_list_settings_container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.input-group {
margin-bottom: 10px;
}
} }
.add-group-member-loading-spinner, .add-group-member-loading-spinner,
@@ -242,13 +246,14 @@ h4.user_group_setting_subsection_title {
.member_list_add { .member_list_add {
width: 100%; width: 100%;
margin: 0 0 10px; margin: 0 0 10px;
}
.user_group_subscription_request_result, .user_group_subscription_request_result,
.stream_subscription_request_result { .stream_subscription_request_result {
& a { & a {
color: inherit; color: inherit;
} }
} margin-bottom: 10px;
} }
.member-search, .member-search,

View File

@@ -5,8 +5,8 @@
</h4> </h4>
<div class="subscriber_list_settings"> <div class="subscriber_list_settings">
<div class="subscriber_list_add float-left"> <div class="subscriber_list_add float-left">
{{> add_subscribers_form .}}
<div class="stream_subscription_request_result"></div> <div class="stream_subscription_request_result"></div>
{{> add_subscribers_form .}}
</div> </div>
<div class="clear-float"></div> <div class="clear-float"></div>
</div> </div>

View File

@@ -1,26 +1,43 @@
{{#if message}} {{#if error_message}}
{{message}} <div class="banner-wrapper">
<br /> <div class="banner banner-danger">
{{/if}} <span class="banner-label">
{{#if subscribed_users}} {{error_message}}
{{#if subscribed_users.[1]}} </span>
{{t "Successfully subscribed users:" }} </div>
</div>
{{else}}
<div class="banner-wrapper">
<div class="banner banner-success">
<span class="banner-label">
{{#if (eq subscribed_users_count 0)}}
{{t "All users were already subscribed."}}
{{else}} {{else}}
{{t "Successfully subscribed user:" }} {{#if (not is_total_subscriber_more_than_five) }}
{{#if subscribed_users}}
{{t "Subscribed:" }} {{{subscribe_success_messages.subscribed_users_message}}}.
{{/if}} {{/if}}
{{#each subscribed_users}} {{#if already_subscribed_users}}
<a data-user-id="{{user_id}}" class="view_user_profile">{{full_name}}</a>{{#unless @last}},{{else}}.{{/unless}} {{t "Already a subscriber:" }} {{{subscribe_success_messages.already_subscribed_users_message}}}.
{{/each}}
<br />
{{/if}}
{{#if already_subscribed_users}}
{{#each already_subscribed_users}}
{{#if @first}}
{{t "Already subscribed users:" }}
{{/if}} {{/if}}
<a data-user-id="{{user_id}}" class="view_user_profile">{{full_name}}</a>{{#unless @last}},{{else}}.{{/unless}} {{else}}
{{/each}} {{#if subscribed_users}}
<br /> {{t "{subscribed_users_count, plural,
one {Subscribed: {subscribed_users_count} user.}
other {Subscribed: {subscribed_users_count} users.}
}"}}
{{/if}}
{{#if already_subscribed_users}}
{{t "{already_subscribed_users_count, plural,
one {Already subscribed: {already_subscribed_users_count} user.}
other {Already subscribed: {already_subscribed_users_count} users.}
}"}}
{{/if}}
{{/if}}
{{/if}}
</span>
</div>
</div>
{{/if}} {{/if}}
{{#if ignored_deactivated_users}} {{#if ignored_deactivated_users}}
{{#each ignored_deactivated_users}} {{#each ignored_deactivated_users}}