message_view: Live update on losing access to a stream.

This commit adds code to live update the message view when
user loses access to a stream and also remove the data of
messages from that stream.
This commit is contained in:
Sahil Batra
2025-04-25 12:17:09 +05:30
committed by Tim Abbott
parent 3383a69088
commit 52b20354e6
6 changed files with 114 additions and 33 deletions

View File

@@ -348,3 +348,9 @@ export function remove(message_ids: number[]): void {
stored_messages.delete(message_id);
}
}
export function get_message_ids_in_stream(stream_id: number): number[] {
return [...stored_messages.values()]
.filter((message) => message.type === "stream" && message.stream_id === stream_id)
.map((message) => message.id);
}

View File

@@ -1,5 +1,4 @@
import $ from "jquery";
import assert from "minimalistic-assert";
import * as activity_ui from "./activity_ui.ts";
import * as alert_words from "./alert_words.ts";
@@ -29,9 +28,9 @@ import * as message_edit from "./message_edit.ts";
import * as message_events from "./message_events.ts";
import * as message_lists from "./message_lists.ts";
import * as message_live_update from "./message_live_update.ts";
import * as message_store from "./message_store.ts";
import * as message_view from "./message_view.ts";
import * as muted_users_ui from "./muted_users_ui.ts";
import * as narrow_state from "./narrow_state.ts";
import * as narrow_title from "./narrow_title.ts";
import * as navbar_alerts from "./navbar_alerts.ts";
import * as onboarding_steps from "./onboarding_steps.ts";
@@ -647,7 +646,6 @@ export function dispatch_normal_event(event) {
case "delete":
for (const stream_id of event.stream_ids) {
const was_subscribed = sub_store.get(stream_id).subscribed;
const is_narrowed_to_stream = narrow_state.narrowed_to_stream_id(stream_id);
stream_data.delete_sub(stream_id);
stream_settings_ui.remove_stream(stream_id);
if (was_subscribed) {
@@ -670,10 +668,10 @@ export function dispatch_normal_event(event) {
"zulip_update_announcements_stream_id",
);
}
if (is_narrowed_to_stream) {
assert(message_lists.current !== undefined);
message_lists.current.update_trailing_bookend(true);
}
const message_ids = message_store.get_message_ids_in_stream(stream_id);
unread_ops.process_read_messages_event(message_ids);
message_events.remove_messages(message_ids);
stream_topic_history.remove_history_for_stream(stream_id);
}
stream_list.update_subscribe_to_more_streams_link();
break;

View File

@@ -398,3 +398,14 @@ export function add_request_pending_for(stream_id: number): void {
export function remove_request_pending_for(stream_id: number): void {
request_pending_stream_ids.delete(stream_id);
}
export function remove_history_for_stream(stream_id: number): void {
// Currently only used when user loses access to a stream.
if (stream_dict.has(stream_id)) {
stream_dict.delete(stream_id);
}
if (fetched_stream_ids.has(stream_id)) {
fetched_stream_ids.delete(stream_id);
}
}

View File

@@ -12,15 +12,14 @@ const event_fixtures = events.fixtures;
const test_user = events.test_user;
const compose_recipient = mock_esm("../src/compose_recipient");
const message_lists = mock_esm("../src/message_lists");
const narrow_state = mock_esm("../src/narrow_state");
const message_events = mock_esm("../src/message_events");
const overlays = mock_esm("../src/overlays");
const settings_org = mock_esm("../src/settings_org");
const settings_streams = mock_esm("../src/settings_streams");
const stream_events = mock_esm("../src/stream_events");
const stream_list = mock_esm("../src/stream_list");
const stream_settings_ui = mock_esm("../src/stream_settings_ui");
message_lists.current = {};
const unread_ops = mock_esm("../src/unread_ops");
const compose_state = zrequire("compose_state");
const peer_data = zrequire("peer_data");
@@ -216,13 +215,6 @@ test("stream delete (normal)", ({override}) => {
override(settings_streams, "update_default_streams_table", noop);
narrow_state.narrowed_to_stream_id = () => true;
let bookend_updates = 0;
override(message_lists.current, "update_trailing_bookend", () => {
bookend_updates += 1;
});
const removed_stream_ids = [];
override(stream_settings_ui, "remove_stream", (stream_id) => {
@@ -235,14 +227,12 @@ test("stream delete (normal)", ({override}) => {
});
override(stream_list, "update_subscribe_to_more_streams_link", noop);
override(unread_ops, "process_read_messages_event", noop);
override(message_events, "remove_messages", noop);
dispatch(event);
assert.deepEqual(removed_stream_ids, [event.stream_ids[0], event.stream_ids[1]]);
// We should possibly be able to make a single call to
// update_trailing_bookend, but we currently do it for each stream.
assert.equal(bookend_updates, 2);
assert.equal(removed_sidebar_rows, 1);
});
@@ -280,10 +270,12 @@ test("stream delete (special streams)", ({override}) => {
override(settings_org, "sync_realm_settings", noop);
override(settings_streams, "update_default_streams_table", noop);
override(message_lists.current, "update_trailing_bookend", noop);
override(stream_list, "remove_sidebar_row", noop);
override(stream_list, "update_subscribe_to_more_streams_link", noop);
override(unread_ops, "process_read_messages_event", noop);
override(message_events, "remove_messages", noop);
dispatch(event);
assert.deepEqual(removed_stream_ids, [event.stream_ids[0], event.stream_ids[1]]);
@@ -324,28 +316,20 @@ test("stream delete (stream is selected in compose)", ({override}) => {
override(settings_streams, "update_default_streams_table", noop);
narrow_state.narrowed_to_stream_id = () => true;
let bookend_updates = 0;
override(message_lists.current, "update_trailing_bookend", () => {
bookend_updates += 1;
});
let removed_sidebar_rows = 0;
override(stream_list, "remove_sidebar_row", () => {
removed_sidebar_rows += 1;
});
override(stream_list, "update_subscribe_to_more_streams_link", noop);
override(unread_ops, "process_read_messages_event", noop);
override(message_events, "remove_messages", noop);
dispatch(event);
assert.deepEqual(removed_stream_ids, [event.stream_ids[0], event.stream_ids[1]]);
assert.equal(compose_state.stream_name(), "");
// We should possibly be able to make a single call to
// update_trailing_bookend, but we currently do it for each stream.
assert.equal(bookend_updates, 2);
assert.equal(removed_sidebar_rows, 1);
});

View File

@@ -401,3 +401,52 @@ test("remove", () => {
assert.equal(message_store.get(message2.id).id, message2.id);
assert.equal(message_store.get(message3.id), undefined);
});
test("get_message_ids_in_stream", () => {
const message1 = {
type: "stream",
sender_full_name: alice.full_name,
sender_id: alice.user_id,
stream_id: devel.stream_id,
stream: devel.name,
display_recipient: devel.name,
topic: "test",
id: 100,
};
const message2 = {
sender_email: "me@example.com",
sender_id: me.user_id,
type: "private",
display_recipient: convert_recipients([me, bob, cindy]),
flags: ["has_alert_word"],
is_me_message: false,
id: 101,
};
const message3 = {
type: "stream",
sender_full_name: cindy.full_name,
sender_id: cindy.user_id,
stream_id: denmark.stream_id,
stream: denmark.name,
display_recipient: denmark.name,
topic: "test",
id: 102,
};
const message4 = {
type: "stream",
sender_full_name: me.full_name,
sender_id: me.user_id,
stream_id: devel.stream_id,
stream: devel.name,
display_recipient: devel.name,
topic: "test",
id: 103,
};
for (const message of [message1, message2, message3, message4]) {
message_helper.process_new_message(message);
}
assert.deepEqual(message_store.get_message_ids_in_stream(devel.stream_id), [100, 103]);
assert.deepEqual(message_store.get_message_ids_in_stream(denmark.stream_id), [102]);
});

View File

@@ -536,3 +536,36 @@ test("test_max_message_ids_in_channel_and_topics", () => {
"topic 1",
]);
});
test("remove_history_for_stream", () => {
const stream_id = 55;
stream_topic_history.add_message({
stream_id,
message_id: 101,
topic_name: "toPic1",
});
stream_topic_history.add_message({
stream_id: 56,
message_id: 102,
topic_name: "topic2",
});
assert.equal(stream_topic_history.stream_has_topics(stream_id), true);
assert.equal(stream_topic_history.stream_has_topics(56), true);
stream_topic_history.remove_history_for_stream(stream_id);
assert.equal(stream_topic_history.stream_has_topics(stream_id), false);
assert.equal(stream_topic_history.stream_has_topics(56), true);
stream_topic_history.add_history(stream_id, [
{name: "local", max_id: 501},
{name: "hist2", max_id: 31},
{name: "hist1", max_id: 30},
]);
assert.equal(stream_topic_history.has_history_for(stream_id), true);
assert.equal(stream_topic_history.stream_has_topics(stream_id), true);
stream_topic_history.remove_history_for_stream(stream_id);
assert.equal(stream_topic_history.stream_has_topics(stream_id), false);
assert.equal(stream_topic_history.has_history_for(stream_id), false);
});