stream_settings: Add new "Stream details" section in General tab.

We want to make it easier to find stream details such as creator,
creation date and stream id. The commit replaces the "Email address"
section in General tab of stream overlay with a new section called
"Stream details", "Email address" is now a field in this section.

If the stream does not have a creator, we only show the stream creation
date in creation details.

Fixes: #25648.
This commit is contained in:
tnmkr 2024-03-23 12:36:21 +05:30 committed by Tim Abbott
parent 6c63043130
commit b524a2c1ea
12 changed files with 142 additions and 15 deletions

View File

@ -126,7 +126,7 @@ help center because they are primarily useful to API clients:
A user ID can be found by [viewing a user's profile][view-profile]
in the web or desktop apps. A stream ID can be found when [browsing
streams][browse-streams] in the web app via the URL.
streams][browse-streams] in the web or desktop apps.
The operands for these search options must be encoded either as an
integer ID or a JSON list of integer IDs. For example, to query

View File

@ -187,7 +187,7 @@ the integration will send direct messages to the bot's owner.
!!! tip ""
A stream ID can be found when [browsing streams][browse-streams]
in the web app via the URL.
in the web or desktop apps.
### topic

View File

@ -3,6 +3,7 @@ import * as color_data from "./color_data";
import {FoldDict} from "./fold_dict";
import {page_params} from "./page_params";
import * as peer_data from "./peer_data";
import type {User} from "./people";
import * as people from "./people";
import * as settings_config from "./settings_config";
import * as settings_data from "./settings_data";
@ -186,6 +187,14 @@ export function get_sub_by_id(stream_id: number): StreamSubscription | undefined
return stream_info.get(stream_id);
}
export function maybe_get_creator_details(creator_id: number | null): User | undefined {
if (creator_id === null) {
return undefined;
}
return people.get_user_by_id_assert_valid(creator_id);
}
export function get_stream_id(name: string): number | undefined {
// Note: Only use this function for situations where
// you are comfortable with a user dealing with an

View File

@ -1,15 +1,20 @@
import * as hash_util from "./hash_util";
import * as peer_data from "./peer_data";
import type {User} from "./people";
import * as settings_config from "./settings_config";
import {current_user} from "./state_data";
import * as stream_data from "./stream_data";
import type {StreamSpecificNotificationSettings, StreamSubscription} from "./sub_store";
import * as sub_store from "./sub_store";
import * as timerender from "./timerender";
import {user_settings} from "./user_settings";
import * as util from "./util";
export type SettingsSubscription = StreamSubscription & {
date_created_string: string;
is_realm_admin: boolean;
creator: User | undefined;
is_creator: boolean;
can_change_name_description: boolean;
should_display_subscription_button: boolean;
should_display_preview_button: boolean;
@ -26,6 +31,14 @@ export function get_sub_for_settings(sub: StreamSubscription): SettingsSubscript
return {
...sub,
// We get timestamp in seconds from the API but timerender needs milliseconds.
date_created_string: timerender.get_localized_date_or_time_for_format(
new Date(sub.date_created * 1000),
"dayofyear_year",
),
creator: stream_data.maybe_get_creator_details(sub.creator_id),
is_creator: sub.creator_id === current_user.user_id,
is_realm_admin: current_user.is_admin,
// Admin can change any stream's name & description either stream is public or
// private, subscribed or unsubscribed.

View File

@ -11,6 +11,7 @@ export const enum StreamPostPolicy {
// These types are taken from the `zerver/lib/types.py`.
export type Stream = {
creator_id: number | null;
date_created: number;
description: string;
first_message_id: number | null;

View File

@ -164,7 +164,8 @@
/* These pill are similar to .not-editable, but are meant to show
profile cards when clicked. */
.panel_user_list > .pill-container {
.panel_user_list > .pill-container,
.stream_creator_details > .display_only_pill {
background-color: hsl(0deg 0% 0% / 7%);
&:hover {
@ -191,6 +192,32 @@
}
}
.stream_creator_details > .display_only_pill.inline_with_text_pill {
padding: 0;
> .pill {
margin: 0;
align-items: baseline;
> .pill-image {
/* Add line-height equal to height to mimic baseline alignment. */
line-height: 20px;
align-self: center;
}
> .pill-label {
> .pill-value {
padding: 0 5px;
max-width: none;
}
> .my_user_status {
margin-right: 2px;
}
}
}
}
@keyframes shake {
10%,
90% {

View File

@ -950,7 +950,11 @@ h4.user_group_setting_subsection_title {
}
}
.stream-email-box {
.stream_details_box {
> .stream_details_subsection {
margin: 0 0 10px; /* mimic paragraph spacing */
}
.stream_email_address_error {
vertical-align: top;
margin-left: 15px;

View File

@ -75,22 +75,56 @@
can_remove_subscribers_setting_widget_name="can_remove_subscribers_group" }}
</div>
{{/with}}
<div class="stream-email-box" {{#unless can_access_stream_email}}style="display: none;"{{/unless}}>
<div class="stream-email-box-header">
<div class="stream_details_box">
<div class="stream_details_box_header">
<h3 class="stream_setting_subsection_title">
{{t "Email address" }}
{{> ../help_link_widget link="/help/message-a-stream-by-email" }}
{{t "Stream details" }}
</h3>
<div class="stream_email_address_error alert-notification"></div>
</div>
<p>
{{#with sub}}
<div class="stream_creator_details stream_details_subsection">
{{#if creator}}
{{#tr}}
Created by <z-user></z-user> on <z-stream-date-created></z-stream-date-created>.
{{#*inline "z-user"}}
{{> ../user_display_only_pill
is_inline=true
user_id=creator.user_id
img_src=creator.avatar_url
display_value=creator.full_name
is_current_user=is_creator }}
{{/inline}}
{{#*inline "z-stream-date-created"}}{{date_created_string}}{{/inline}}
{{/tr}}
{{else}}
{{#tr}}
Created on <z-stream-date-created></z-stream-date-created>.
{{#*inline "z-stream-date-created"}}{{date_created_string}}{{/inline}}
{{/tr}}
{{/if}}
</div>
<div class="stream_details_subsection">
{{t "Stream events"}}<br/>
<a href="{{preview_url}}/topic/stream.20events">{{t "View events" }}</a>
</div>
<div class="stream_details_subsection">
{{t "Stream ID"}}<br/>
{{stream_id}}
</div>
{{/with}}
{{#if can_access_stream_email}}
<div class="input-group stream-email-box">
<label for="copy_stream_email_button" class="title inline-block">Email address</label>
<p class="field-hint">
{{t "You can use email to send messages to Zulip streams."}}
{{> ../help_link_widget link="/help/message-a-stream-by-email" }}
</p>
<p>
<button class="button rounded copy_email_button" type="button" name="button">
<button class="button small rounded copy_email_button" id="copy_stream_email_button" type="button">
<span class="copy_button">{{t "Generate email address" }}</span>
</button>
</p>
</div>
{{/if}}
</div>
</div>

View File

@ -1,4 +1,4 @@
<span class="pill-container display_only_pill">
<span class="pill-container display_only_pill {{#if is_inline}}inline_with_text_pill{{/if}}">
<a data-user-id="{{user_id}}" class="view_user_profile pill" tabindex="0">
{{#if img_src}}
<img class="pill-image" src="{{img_src}}" />

View File

@ -46,6 +46,7 @@ exports.test_streams = {
invite_only: false,
stream_id: 101,
date_created: fake_now,
creator_id: null,
first_message_id: 1,
history_public_to_subscribers: false,
is_announcement_only: false,
@ -61,6 +62,7 @@ exports.test_streams = {
invite_only: true,
stream_id: 102,
date_created: fake_then,
creator_id: null,
first_message_id: 1,
history_public_to_subscribers: false,
is_web_public: false,

View File

@ -326,6 +326,8 @@ test("admin_options", () => {
is_muted: true,
invite_only: false,
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
stream_data.add_sub(sub);
return sub;
@ -379,6 +381,8 @@ test("stream_settings", () => {
subscribed: true,
invite_only: false,
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
const blue = {
@ -388,6 +392,8 @@ test("stream_settings", () => {
subscribed: false,
invite_only: false,
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
const amber = {
@ -400,6 +406,8 @@ test("stream_settings", () => {
stream_post_policy: settings_config.stream_post_policy_values.admins.code,
message_retention_days: 10,
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
stream_data.add_sub(cinnamon);
stream_data.add_sub(amber);
@ -810,6 +818,25 @@ test("create_sub", () => {
assert.equal(antarctica_sub.color, "#76ce90");
});
test("creator_id", () => {
people.add_active_user(test_user);
// When creator id is not a valid user id
assert.throws(() => stream_data.maybe_get_creator_details(-1), {
name: "Error",
message: "Unknown user_id in get_by_user_id: -1",
});
// When there is no creator
assert.equal(stream_data.maybe_get_creator_details(null), undefined);
const creator_details = people.get_by_user_id(test_user.user_id);
assert.deepStrictEqual(
stream_data.maybe_get_creator_details(test_user.user_id),
creator_details,
);
});
test("initialize", () => {
function get_params() {
const params = {};

View File

@ -50,6 +50,8 @@ run_test("redraw_left_panel", ({mock_template}) => {
stream_weekly_traffic: null,
color: "red",
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
const poland = {
elem: "poland",
@ -61,6 +63,8 @@ run_test("redraw_left_panel", ({mock_template}) => {
stream_weekly_traffic: 13,
color: "red",
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
const pomona = {
elem: "pomona",
@ -72,6 +76,8 @@ run_test("redraw_left_panel", ({mock_template}) => {
stream_weekly_traffic: 0,
color: "red",
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
const cpp = {
elem: "cpp",
@ -83,6 +89,8 @@ run_test("redraw_left_panel", ({mock_template}) => {
stream_weekly_traffic: 6,
color: "red",
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
const zzyzx = {
elem: "zzyzx",
@ -94,6 +102,8 @@ run_test("redraw_left_panel", ({mock_template}) => {
stream_weekly_traffic: 6,
color: "red",
can_remove_subscribers_group: admins_group.id,
date_created: 1691057093,
creator_id: null,
};
const sub_row_data = [denmark, poland, pomona, cpp, zzyzx];