stream_pill: Add channel privacy icons in input_pill.

Modify stream pills to add channel privacy decorations inside
input pills for areas eg: the UI for adding members to a channel.

Partly fixes: #25257.
This commit is contained in:
adnan-td 2024-07-04 00:43:10 +05:30 committed by Tim Abbott
parent 9e096382cd
commit 6f4d14ddde
6 changed files with 46 additions and 23 deletions

View File

@ -10,6 +10,7 @@ import * as blueslip from "./blueslip";
import type {EmojiRenderingDetails} from "./emoji";
import * as keydown_util from "./keydown_util";
import type {SearchUserPill} from "./search_pill";
import type {StreamSubscription} from "./sub_store";
import * as ui_util from "./ui_util";
// See https://zulip.readthedocs.io/en/latest/subsystems/input-pills.html
@ -25,6 +26,7 @@ export type InputPillItem<T> = {
group_id?: number;
// Used for search pills
operator?: string;
stream?: StreamSubscription;
} & T;
export type InputPillConfig = {
@ -75,6 +77,8 @@ type InputPillRenderingDetails = {
should_add_guest_user_indicator: boolean | undefined;
user_id?: number | undefined;
group_id?: number | undefined;
has_stream?: boolean;
stream?: StreamSubscription;
};
// These are the functions that are exposed to other modules.
@ -203,6 +207,11 @@ export function create<T>(opts: InputPillCreateOptions<T>): InputPillContainer<T
opts.img_src = item.img_src;
}
if (item.type === "stream" && item.stream) {
opts.has_stream = true;
opts.stream = item.stream;
}
if (store.pill_config?.show_user_status_emoji === true) {
const has_status = item.status_emoji_info !== undefined;
if (has_status) {

View File

@ -7,8 +7,7 @@ import type {CombinedPillContainer, CombinedPillItem} from "./typeahead_helper";
export type StreamPill = {
type: "stream";
stream_id: number;
stream_name: string;
stream: StreamSubscription;
};
export type StreamPillWidget = InputPillContainer<StreamPill>;
@ -18,7 +17,7 @@ export type StreamPillData = StreamSubscription & {type: "stream"};
function format_stream_name_and_subscriber_count(sub: StreamSubscription): string {
const sub_count = peer_data.get_subscriber_count(sub.stream_id);
return $t(
{defaultMessage: "#{stream_name}: {sub_count} users"},
{defaultMessage: "{stream_name}: {sub_count} users"},
{stream_name: sub.name, sub_count},
);
}
@ -48,11 +47,15 @@ export function create_item_from_stream_name(
return undefined;
}
if (current_items.some((item) => item.type === "stream" && item.stream_id === sub.stream_id)) {
if (
current_items.some(
(item) => item.type === "stream" && item.stream.stream_id === sub.stream_id,
)
) {
return undefined;
}
let display_value = "#" + sub.name;
let display_value = sub.name;
if (show_subscriber_count) {
display_value = format_stream_name_and_subscriber_count(sub);
}
@ -60,20 +63,19 @@ export function create_item_from_stream_name(
return {
type: "stream",
display_value,
stream_id: sub.stream_id,
stream_name: sub.name,
stream: sub,
};
}
export function get_stream_name_from_item(item: InputPillItem<StreamPill>): string {
return item.stream_name;
return item.stream.name;
}
export function get_user_ids(pill_widget: StreamPillWidget | CombinedPillContainer): number[] {
let user_ids = pill_widget
.items()
.flatMap((item) =>
item.type === "stream" ? peer_data.get_subscribers(item.stream_id) : [],
item.type === "stream" ? peer_data.get_subscribers(item.stream.stream_id) : [],
);
user_ids = [...new Set(user_ids)];
user_ids.sort((a, b) => a - b);
@ -85,22 +87,21 @@ export function append_stream(
pill_widget: StreamPillWidget | CombinedPillContainer,
show_subscriber_count = true,
): void {
let display_value = "#" + stream.name;
let display_value = stream.name;
if (show_subscriber_count) {
display_value = format_stream_name_and_subscriber_count(stream);
}
pill_widget.appendValidatedData({
type: "stream",
display_value,
stream_id: stream.stream_id,
stream_name: stream.name,
stream,
});
pill_widget.clear_text();
}
export function get_stream_ids(pill_widget: StreamPillWidget | CombinedPillContainer): number[] {
const items = pill_widget.items();
return items.flatMap((item) => (item.type === "stream" ? item.stream_id : []));
return items.flatMap((item) => (item.type === "stream" ? item.stream.stream_id : []));
}
export function filter_taken_streams(

View File

@ -39,6 +39,10 @@
border-radius: 4px 0 0 4px;
}
.zulip-icon {
padding-right: 2px;
}
.pill-label {
/* Treat as flex container to better position status
emoji and control ellipsis on the pill value. */

View File

@ -3,7 +3,18 @@
<img class="pill-image" src="{{img_src}}" />
{{/if}}
<span class="pill-label">
<span class="pill-value">{{ display_value }}</span>
<span class="pill-value">
{{#if has_stream}}
{{~#if stream.invite_only ~}}
<i class="zulip-icon zulip-icon-lock stream-privacy-type-icon" aria-hidden="true"></i>
{{~ else if stream.is_web_public ~}}
<i class="zulip-icon zulip-icon-globe stream-privacy-type-icon" aria-hidden="true"></i>
{{~ else ~}}
<i class="zulip-icon zulip-icon-hashtag stream-privacy-type-icon" aria-hidden="true"></i>
{{~/if~}}
{{/if}}
{{ display_value }}
</span>
{{~#if should_add_guest_user_indicator}}&nbsp;<i>({{t 'guest'}})</i>{{~/if~}}
{{~#if deactivated}}&nbsp;({{t 'deactivated'}}){{~/if~}}
{{~#if has_status~}}

View File

@ -200,13 +200,13 @@ run_test("initialize", ({override, override_rewire, mock_template}) => {
let expected_value = `<div class="search_list_item">\n <span>Search for zo</span>\n</div>\n`;
assert.equal(opts.highlighter_html(source[0]), expected_value);
expected_value = `<div class="search_list_item">\n <span>sent by</span>\n <span class="pill-container">\n <div class='pill ' tabindex=0>\n <img class="pill-image" src="https://secure.gravatar.com/avatar/0f030c97ab51312c7bbffd3966198ced?d&#x3D;identicon&amp;version&#x3D;1&amp;s&#x3D;50" />\n <span class="pill-label">\n <span class="pill-value">&lt;strong&gt;Zo&lt;/strong&gt;e</span></span>\n <div class="exit">\n <a role="button" class="zulip-icon zulip-icon-close pill-close-button"></a>\n </div>\n</div>\n </span>\n</div>\n`;
expected_value = `<div class="search_list_item">\n <span>sent by</span>\n <span class="pill-container">\n <div class='pill ' tabindex=0>\n <img class="pill-image" src="https://secure.gravatar.com/avatar/0f030c97ab51312c7bbffd3966198ced?d&#x3D;identicon&amp;version&#x3D;1&amp;s&#x3D;50" />\n <span class="pill-label">\n <span class="pill-value">\n &lt;strong&gt;Zo&lt;/strong&gt;e\n </span></span>\n <div class="exit">\n <a role="button" class="zulip-icon zulip-icon-close pill-close-button"></a>\n </div>\n</div>\n </span>\n</div>\n`;
assert.equal(opts.highlighter_html(source[1]), expected_value);
expected_value = `<div class="search_list_item">\n <span>direct messages with</span>\n <span class="pill-container">\n <div class='pill ' tabindex=0>\n <img class="pill-image" src="https://secure.gravatar.com/avatar/0f030c97ab51312c7bbffd3966198ced?d&#x3D;identicon&amp;version&#x3D;1&amp;s&#x3D;50" />\n <span class="pill-label">\n <span class="pill-value">&lt;strong&gt;Zo&lt;/strong&gt;e</span></span>\n <div class="exit">\n <a role="button" class="zulip-icon zulip-icon-close pill-close-button"></a>\n </div>\n</div>\n </span>\n</div>\n`;
expected_value = `<div class="search_list_item">\n <span>direct messages with</span>\n <span class="pill-container">\n <div class='pill ' tabindex=0>\n <img class="pill-image" src="https://secure.gravatar.com/avatar/0f030c97ab51312c7bbffd3966198ced?d&#x3D;identicon&amp;version&#x3D;1&amp;s&#x3D;50" />\n <span class="pill-label">\n <span class="pill-value">\n &lt;strong&gt;Zo&lt;/strong&gt;e\n </span></span>\n <div class="exit">\n <a role="button" class="zulip-icon zulip-icon-close pill-close-button"></a>\n </div>\n</div>\n </span>\n</div>\n`;
assert.equal(opts.highlighter_html(source[2]), expected_value);
expected_value = `<div class="search_list_item">\n <span>group direct messages including</span>\n <span class="pill-container">\n <div class='pill ' tabindex=0>\n <img class="pill-image" src="https://secure.gravatar.com/avatar/0f030c97ab51312c7bbffd3966198ced?d&#x3D;identicon&amp;version&#x3D;1&amp;s&#x3D;50" />\n <span class="pill-label">\n <span class="pill-value">&lt;strong&gt;Zo&lt;/strong&gt;e</span></span>\n <div class="exit">\n <a role="button" class="zulip-icon zulip-icon-close pill-close-button"></a>\n </div>\n</div>\n </span>\n</div>\n`;
expected_value = `<div class="search_list_item">\n <span>group direct messages including</span>\n <span class="pill-container">\n <div class='pill ' tabindex=0>\n <img class="pill-image" src="https://secure.gravatar.com/avatar/0f030c97ab51312c7bbffd3966198ced?d&#x3D;identicon&amp;version&#x3D;1&amp;s&#x3D;50" />\n <span class="pill-label">\n <span class="pill-value">\n &lt;strong&gt;Zo&lt;/strong&gt;e\n </span></span>\n <div class="exit">\n <a role="button" class="zulip-icon zulip-icon-close pill-close-button"></a>\n </div>\n</div>\n </span>\n</div>\n`;
assert.equal(opts.highlighter_html(source[3]), expected_value);
/* Test sorter */

View File

@ -32,16 +32,14 @@ peer_data.set_subscribers(denmark.stream_id, [1, 2, 3, 77]);
peer_data.set_subscribers(sweden.stream_id, [1, 2, 3, 4, 5]);
const denmark_pill = {
stream_name: denmark.name,
stream_id: denmark.stream_id,
type: "stream",
display_value: "#Denmark: 3 users",
display_value: "Denmark: 3 users",
stream: denmark,
};
const sweden_pill = {
stream_name: sweden.name,
stream_id: sweden.stream_id,
type: "stream",
display_value: "translated: #Sweden: 5 users",
display_value: "translated: Sweden: 5 users",
stream: sweden,
};
const subs = [denmark, sweden, germany];