mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	message_feed: Notify users when skipping unsubscribed channels.
Zulip has an invariant that all unread messages must be in channels the user is subscribed to. Therefore, in this commit, we add a feedback widget to notify users when a channel to which they are not subscribed is skipped while marking messages as unread in an interleaved view. Fixes #23470. Co-authored-by: Hemant Umre <hemantumre12@gmail.com>.
This commit is contained in:
		@@ -4,14 +4,17 @@ import assert from "minimalistic-assert";
 | 
			
		||||
import {z} from "zod";
 | 
			
		||||
 | 
			
		||||
import render_confirm_mark_all_as_read from "../templates/confirm_dialog/confirm_mark_all_as_read.hbs";
 | 
			
		||||
import render_inline_decorated_stream_name from "../templates/inline_decorated_stream_name.hbs";
 | 
			
		||||
import render_skipped_marking_unread from "../templates/skipped_marking_unread.hbs";
 | 
			
		||||
 | 
			
		||||
import * as blueslip from "./blueslip.ts";
 | 
			
		||||
import * as channel from "./channel.ts";
 | 
			
		||||
import * as confirm_dialog from "./confirm_dialog.ts";
 | 
			
		||||
import * as desktop_notifications from "./desktop_notifications.ts";
 | 
			
		||||
import * as dialog_widget from "./dialog_widget.ts";
 | 
			
		||||
import * as feedback_widget from "./feedback_widget.ts";
 | 
			
		||||
import {Filter} from "./filter.ts";
 | 
			
		||||
import {$t_html} from "./i18n.ts";
 | 
			
		||||
import {$t, $t_html} from "./i18n.ts";
 | 
			
		||||
import * as loading from "./loading.ts";
 | 
			
		||||
import * as message_flags from "./message_flags.ts";
 | 
			
		||||
import * as message_lists from "./message_lists.ts";
 | 
			
		||||
@@ -24,11 +27,14 @@ import * as people from "./people.ts";
 | 
			
		||||
import * as recent_view_ui from "./recent_view_ui.ts";
 | 
			
		||||
import type {MessageDetails} from "./server_event_types.ts";
 | 
			
		||||
import type {NarrowTerm} from "./state_data.ts";
 | 
			
		||||
import * as sub_store from "./sub_store.ts";
 | 
			
		||||
import * as ui_report from "./ui_report.ts";
 | 
			
		||||
import * as unread from "./unread.ts";
 | 
			
		||||
import * as unread_ui from "./unread_ui.ts";
 | 
			
		||||
import * as util from "./util.ts";
 | 
			
		||||
 | 
			
		||||
let loading_indicator_displayed = false;
 | 
			
		||||
let unsubscribed_ignored_channels: number[] = [];
 | 
			
		||||
 | 
			
		||||
// We might want to use a slightly smaller batch for the first
 | 
			
		||||
// request, because empirically, the first request can be
 | 
			
		||||
@@ -70,8 +76,49 @@ const update_flags_for_narrow_response_schema = z.object({
 | 
			
		||||
    last_processed_id: z.number().nullable(),
 | 
			
		||||
    found_oldest: z.boolean(),
 | 
			
		||||
    found_newest: z.boolean(),
 | 
			
		||||
    ignored_because_not_subscribed_channels: z.array(z.number()),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const update_flags_for_response_schema = z.object({
 | 
			
		||||
    ignored_because_not_subscribed_channels: z.array(z.number()),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function handle_skipped_unsubscribed_streams(
 | 
			
		||||
    ignored_because_not_subscribed_channels: number[],
 | 
			
		||||
): void {
 | 
			
		||||
    if (ignored_because_not_subscribed_channels.length > 0) {
 | 
			
		||||
        // Zulip has an invariant that all unread messages must be in streams
 | 
			
		||||
        // the user is subscribed to. Notify the user if messages from
 | 
			
		||||
        // unsubscribed streams are ignored by the server.
 | 
			
		||||
        const stream_names_with_privacy_symbol_html = ignored_because_not_subscribed_channels.map(
 | 
			
		||||
            (stream_id) => {
 | 
			
		||||
                const stream = sub_store.get(stream_id);
 | 
			
		||||
                const decorated_stream_name = render_inline_decorated_stream_name({stream});
 | 
			
		||||
                return `<span class="white-space-nowrap">${decorated_stream_name}</span>`;
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const populate: (element: JQuery) => void = ($container) => {
 | 
			
		||||
            const formatted_stream_list_text = util.format_array_as_list(
 | 
			
		||||
                stream_names_with_privacy_symbol_html,
 | 
			
		||||
                "long",
 | 
			
		||||
                "conjunction",
 | 
			
		||||
            );
 | 
			
		||||
            const rendered_html = render_skipped_marking_unread({
 | 
			
		||||
                streams: formatted_stream_list_text,
 | 
			
		||||
            });
 | 
			
		||||
            $container.html(rendered_html);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const title_text = $t({defaultMessage: "Skipped unsubscribed channels"});
 | 
			
		||||
 | 
			
		||||
        feedback_widget.show({
 | 
			
		||||
            populate,
 | 
			
		||||
            title_text,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function bulk_update_read_flags_for_narrow(
 | 
			
		||||
    narrow: NarrowTerm[],
 | 
			
		||||
    op: "add" | "remove",
 | 
			
		||||
@@ -330,6 +377,12 @@ function do_mark_unread_by_narrow(
 | 
			
		||||
        success(raw_data) {
 | 
			
		||||
            const data = update_flags_for_narrow_response_schema.parse(raw_data);
 | 
			
		||||
            messages_marked_unread_till_now += data.updated_count;
 | 
			
		||||
            unsubscribed_ignored_channels = [
 | 
			
		||||
                ...new Set([
 | 
			
		||||
                    ...unsubscribed_ignored_channels,
 | 
			
		||||
                    ...data.ignored_because_not_subscribed_channels,
 | 
			
		||||
                ]),
 | 
			
		||||
            ];
 | 
			
		||||
            if (!data.found_newest) {
 | 
			
		||||
                assert(data.last_processed_id !== null);
 | 
			
		||||
                // If we weren't able to complete the request fully in
 | 
			
		||||
@@ -358,9 +411,15 @@ function do_mark_unread_by_narrow(
 | 
			
		||||
                    FOLLOWUP_BATCH_SIZE,
 | 
			
		||||
                    narrow,
 | 
			
		||||
                );
 | 
			
		||||
            } else if (loading_indicator_displayed) {
 | 
			
		||||
            } else {
 | 
			
		||||
                if (loading_indicator_displayed) {
 | 
			
		||||
                    finish_loading(messages_marked_unread_till_now);
 | 
			
		||||
                }
 | 
			
		||||
                if (unsubscribed_ignored_channels.length > 0) {
 | 
			
		||||
                    handle_skipped_unsubscribed_streams(unsubscribed_ignored_channels);
 | 
			
		||||
                    unsubscribed_ignored_channels = [];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        error(xhr) {
 | 
			
		||||
            handle_mark_unread_from_here_error(xhr, {
 | 
			
		||||
@@ -382,10 +441,16 @@ function do_mark_unread_by_ids(message_ids_to_update: number[]): void {
 | 
			
		||||
    void channel.post({
 | 
			
		||||
        url: "/json/messages/flags",
 | 
			
		||||
        data: {messages: JSON.stringify(message_ids_to_update), op: "remove", flag: "read"},
 | 
			
		||||
        success() {
 | 
			
		||||
        success(raw_data) {
 | 
			
		||||
            if (loading_indicator_displayed) {
 | 
			
		||||
                finish_loading(message_ids_to_update.length);
 | 
			
		||||
            }
 | 
			
		||||
            const data = update_flags_for_response_schema.parse(raw_data);
 | 
			
		||||
            const ignored_because_not_subscribed_channels =
 | 
			
		||||
                data.ignored_because_not_subscribed_channels;
 | 
			
		||||
            if (ignored_because_not_subscribed_channels.length > 0) {
 | 
			
		||||
                handle_skipped_unsubscribed_streams(ignored_because_not_subscribed_channels);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        error(xhr) {
 | 
			
		||||
            handle_mark_unread_from_here_error(xhr, {
 | 
			
		||||
 
 | 
			
		||||
@@ -389,6 +389,10 @@ body.has-overlay-scrollbar {
 | 
			
		||||
    white-space: pre;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.white-space-nowrap {
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Set flex display on login buttons only in the
 | 
			
		||||
   spectator view. We want them to display none
 | 
			
		||||
   otherwise. */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								web/templates/skipped_marking_unread.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								web/templates/skipped_marking_unread.hbs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
{{#tr}}
 | 
			
		||||
    Because you are not subscribed to <z-streams></z-streams>, messages in this channel were not marked as unread.
 | 
			
		||||
    {{#*inline "z-streams"}}<strong>{{{streams}}}</strong>{{/inline}}
 | 
			
		||||
{{/tr}}
 | 
			
		||||
		Reference in New Issue
	
	Block a user