diff --git a/templates/zerver/app/index.html b/templates/zerver/app/index.html index 6a07838e29..353673403f 100644 --- a/templates/zerver/app/index.html +++ b/templates/zerver/app/index.html @@ -259,6 +259,8 @@
+
+
diff --git a/tools/test-js-with-node b/tools/test-js-with-node index da2c6136c9..15393b7b97 100755 --- a/tools/test-js-with-node +++ b/tools/test-js-with-node @@ -178,6 +178,7 @@ EXEMPT_FILES = make_set( "web/src/resize_handler.js", "web/src/rows.js", "web/src/scheduled_messages.js", + "web/src/scheduled_messages_feed_ui.js", "web/src/scheduled_messages_overlay_ui.js", "web/src/scheduled_messages_popover.js", "web/src/scheduled_messages_ui.js", diff --git a/web/src/narrow.js b/web/src/narrow.js index 581870b8c0..ef3515ae49 100644 --- a/web/src/narrow.js +++ b/web/src/narrow.js @@ -39,6 +39,7 @@ import * as pm_list from "./pm_list"; import * as recent_view_ui from "./recent_view_ui"; import * as recent_view_util from "./recent_view_util"; import * as resize from "./resize"; +import * as scheduled_messages_feed_ui from "./scheduled_messages_feed_ui"; import * as search from "./search"; import {web_mark_read_on_scroll_policy_values} from "./settings_config"; import * as spectators from "./spectators"; @@ -922,6 +923,7 @@ export function to_compose_target() { function handle_post_view_change(msg_list) { const filter = msg_list.data.filter; + scheduled_messages_feed_ui.update_schedule_message_indicator(); typing_events.render_notifications_for_narrow(); if (filter.contains_only_private_messages()) { diff --git a/web/src/scheduled_messages_feed_ui.js b/web/src/scheduled_messages_feed_ui.js new file mode 100644 index 0000000000..2581340f67 --- /dev/null +++ b/web/src/scheduled_messages_feed_ui.js @@ -0,0 +1,64 @@ +import $ from "jquery"; + +import render_scheduled_messages_indicator from "../templates/scheduled_messages_indicator.hbs"; + +import * as narrow_state from "./narrow_state"; +import * as scheduled_messages from "./scheduled_messages"; +import * as util from "./util"; + +function get_scheduled_messages_matching_narrow() { + const scheduled_messages_list = Object.values(scheduled_messages.scheduled_messages_data); + const filter = narrow_state.filter(); + const is_conversation_view = filter === undefined ? false : filter.is_conversation_view(); + const current_view_type = narrow_state.narrowed_to_pms() ? "private" : "stream"; + + if (!is_conversation_view) { + return false; + } + + const matching_scheduled_messages = scheduled_messages_list.filter((scheduled_message) => { + // One could imagine excluding scheduled messages that failed + // to send, but structurally, we want to raise awareness of + // them -- we expect users to cancel/clear/reschedule those if + // aware of them. + + if (current_view_type !== scheduled_message.type) { + return false; + } + + if (scheduled_message.type === "private") { + // Both of these will be the user IDs for all participants including the + // current user sorted in ascending order. + if (scheduled_message.to.toString() === narrow_state.pm_ids_string()) { + return true; + } + } else if (scheduled_message.type === "stream") { + const narrow_dict = { + stream_id: narrow_state.stream_sub().stream_id, + topic: narrow_state.topic(), + }; + const scheduled_message_dict = { + stream_id: scheduled_message.to, + topic: scheduled_message.topic, + }; + if (util.same_stream_and_topic(narrow_dict, scheduled_message_dict)) { + return true; + } + } + return false; + }); + return matching_scheduled_messages; +} + +export function update_schedule_message_indicator() { + $("#scheduled_message_indicator").empty(); + const matching_scheduled_messages = get_scheduled_messages_matching_narrow(); + const scheduled_message_count = matching_scheduled_messages.length; + if (scheduled_message_count > 0) { + $("#scheduled_message_indicator").html( + render_scheduled_messages_indicator({ + scheduled_message_count, + }), + ); + } +} diff --git a/web/src/server_events_dispatch.js b/web/src/server_events_dispatch.js index e5b0b7fdb6..4cd921a931 100644 --- a/web/src/server_events_dispatch.js +++ b/web/src/server_events_dispatch.js @@ -43,6 +43,7 @@ import * as realm_playground from "./realm_playground"; import {realm_user_settings_defaults} from "./realm_user_settings_defaults"; import * as reload from "./reload"; import * as scheduled_messages from "./scheduled_messages"; +import * as scheduled_messages_feed_ui from "./scheduled_messages_feed_ui"; import * as scheduled_messages_overlay_ui from "./scheduled_messages_overlay_ui"; import * as scheduled_messages_ui from "./scheduled_messages_ui"; import * as scroll_bar from "./scroll_bar"; @@ -493,12 +494,14 @@ export function dispatch_normal_event(event) { switch (event.op) { case "add": { scheduled_messages.add_scheduled_messages(event.scheduled_messages); + scheduled_messages_feed_ui.update_schedule_message_indicator(); scheduled_messages_overlay_ui.rerender(); left_sidebar_navigation_area.update_scheduled_messages_row(); break; } case "remove": { scheduled_messages.remove_scheduled_message(event.scheduled_message_id); + scheduled_messages_feed_ui.update_schedule_message_indicator(); scheduled_messages_ui.hide_scheduled_message_success_compose_banner( event.scheduled_message_id, ); diff --git a/web/styles/scheduled_messages.css b/web/styles/scheduled_messages.css index e2502e242b..6212c88702 100644 --- a/web/styles/scheduled_messages.css +++ b/web/styles/scheduled_messages.css @@ -18,3 +18,23 @@ } } } + +#scheduled_message_indicator { + display: block; + margin-left: 10px; + font-style: italic; + color: hsl(0deg 0% 53%); +} + +@media (width < $xl_min) { + #scheduled_message_indicator { + margin-right: 7px; + } +} + +@media (width < $md_min) { + #scheduled_message_indicator { + margin-right: 7px; + margin-left: 7px; + } +} diff --git a/web/templates/scheduled_messages_indicator.hbs b/web/templates/scheduled_messages_indicator.hbs new file mode 100644 index 0000000000..381982c1c1 --- /dev/null +++ b/web/templates/scheduled_messages_indicator.hbs @@ -0,0 +1,7 @@ + +
+ {{#tr}} + You have {scheduled_message_count, plural, =1 {1 scheduled message} other {# scheduled messages}} for this conversation. + {{#*inline "z-link"}}{{> @partial-block}}{{/inline}} + {{/tr}} +
diff --git a/web/tests/dispatch.test.js b/web/tests/dispatch.test.js index 8b1621d30e..b561987ed6 100644 --- a/web/tests/dispatch.test.js +++ b/web/tests/dispatch.test.js @@ -43,7 +43,9 @@ const realm_logo = mock_esm("../src/realm_logo"); const realm_playground = mock_esm("../src/realm_playground"); const reload = mock_esm("../src/reload"); const scheduled_messages = mock_esm("../src/scheduled_messages"); +const scheduled_messages_feed_ui = mock_esm("../src/scheduled_messages_feed_ui"); const scheduled_messages_overlay_ui = mock_esm("../src/scheduled_messages_overlay_ui"); +const scheduled_messages_ui = mock_esm("../src/scheduled_messages_ui"); const scroll_bar = mock_esm("../src/scroll_bar"); const settings_account = mock_esm("../src/settings_account"); const settings_bots = mock_esm("../src/settings_bots"); @@ -383,6 +385,9 @@ run_test("reaction", ({override}) => { run_test("scheduled_messages", ({override}) => { override(scheduled_messages_overlay_ui, "rerender", noop); override(scheduled_messages_overlay_ui, "remove_scheduled_message_id", noop); + override(scheduled_messages_feed_ui, "update_schedule_message_indicator", noop); + override(scheduled_messages_ui, "hide_scheduled_message_success_compose_banner", noop); + let event = event_fixtures.scheduled_messages__add; { const stub = make_stub();