From b5f7e2466978b0a6e04cd42444efbeaff71f6f22 Mon Sep 17 00:00:00 2001 From: Aman Agrawal Date: Thu, 19 Jun 2025 17:52:13 +0530 Subject: [PATCH] message_reminder: Add UI to schedule message reminder. This adds an option in message actions popover menu to schedule a message reminder. --- tools/test-js-with-node | 1 + web/shared/icons/alarm-clock.svg | 33 +++++++++++++++ web/src/compose_send_menu_popover.js | 31 +++++++++++--- web/src/message_actions_popover.ts | 14 ++++++- web/src/message_reminder.ts | 40 +++++++++++++++++++ web/src/popover_menus_data.ts | 3 ++ web/src/ui_init.js | 5 ++- .../popovers/message_actions_popover.hbs | 10 ++++- web/templates/send_later_modal.hbs | 6 ++- 9 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 web/shared/icons/alarm-clock.svg create mode 100644 web/src/message_reminder.ts diff --git a/tools/test-js-with-node b/tools/test-js-with-node index 34df0c0e37..805b3a6c99 100755 --- a/tools/test-js-with-node +++ b/tools/test-js-with-node @@ -155,6 +155,7 @@ EXEMPT_FILES = make_set( "web/src/message_lists.ts", "web/src/message_live_update.ts", "web/src/message_notifications.ts", + "web/src/message_reminder.ts", "web/src/message_scroll.ts", "web/src/message_scroll_state.ts", "web/src/message_summary.ts", diff --git a/web/shared/icons/alarm-clock.svg b/web/shared/icons/alarm-clock.svg new file mode 100644 index 0000000000..f1bd6d69b1 --- /dev/null +++ b/web/shared/icons/alarm-clock.svg @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/web/src/compose_send_menu_popover.js b/web/src/compose_send_menu_popover.js index 4c4978cde3..fdacce896c 100644 --- a/web/src/compose_send_menu_popover.js +++ b/web/src/compose_send_menu_popover.js @@ -12,6 +12,7 @@ import * as compose_state from "./compose_state.ts"; import * as compose_validate from "./compose_validate.ts"; import * as drafts from "./drafts.ts"; import * as flatpickr from "./flatpickr.ts"; +import * as message_reminder from "./message_reminder.ts"; import * as modals from "./modals.ts"; import * as popover_menus from "./popover_menus.ts"; import * as scheduled_messages from "./scheduled_messages.ts"; @@ -28,16 +29,30 @@ function set_compose_box_schedule(element) { return selected_send_at_time; } -export function open_send_later_menu() { - if (!compose_validate.validate(true)) { +export function open_send_later_menu(remind_message_id = undefined) { + if (remind_message_id === undefined && !compose_validate.validate(true)) { return; } // Only show send later options that are possible today. const date = new Date(); const filtered_send_opts = scheduled_messages.get_filtered_send_opts(date); - $("body").append($(render_send_later_modal(filtered_send_opts))); + $("body").append( + $( + render_send_later_modal({ + ...filtered_send_opts, + is_reminder: remind_message_id !== undefined, + }), + ), + ); let interval; + const message_schedule_callback = (time) => { + if (remind_message_id !== undefined) { + do_schedule_reminder(time, remind_message_id); + } else { + do_schedule_message(time); + } + }; modals.open("send_later_modal", { autoremove: true, @@ -67,7 +82,7 @@ export function open_send_later_menu() { const current_time = new Date(); flatpickr.show_flatpickr( $(".send_later_custom")[0], - do_schedule_message, + message_schedule_callback, new Date(current_time.getTime() + 60 * 60 * 1000), { minDate: new Date( @@ -101,7 +116,7 @@ export function open_send_later_menu() { ".send_later_today, .send_later_tomorrow, .send_later_monday", (e) => { const send_at_time = set_compose_box_schedule(e.currentTarget); - do_schedule_message(send_at_time); + message_schedule_callback(send_at_time); e.preventDefault(); e.stopPropagation(); }, @@ -133,6 +148,12 @@ export function do_schedule_message(send_at_time) { compose.finish(true); } +export function do_schedule_reminder(send_at_time, remind_message_id) { + modals.close_if_open("send_later_modal"); + send_at_time = parse_sent_at_time(send_at_time); + message_reminder.set_message_reminder(send_at_time, remind_message_id); +} + function get_send_later_menu_items() { const $send_later_options = $("#send_later_options"); if ($send_later_options.length === 0) { diff --git a/web/src/message_actions_popover.ts b/web/src/message_actions_popover.ts index fe5fd159c6..3acc566433 100644 --- a/web/src/message_actions_popover.ts +++ b/web/src/message_actions_popover.ts @@ -64,7 +64,11 @@ export function toggle_message_actions_menu(message: Message): boolean { return true; } -export function initialize(): void { +export function initialize({ + message_reminder_click_handler, +}: { + message_reminder_click_handler: (remind_message_id: number) => void; +}): void { popover_menus.register_popover_menu(".actions_hover .message-actions-menu-button", { theme: "popover-menu", placement: "bottom", @@ -141,6 +145,14 @@ export function initialize(): void { popover_menus.hide_current_popover_if_visible(instance); }); + $popper.one("click", ".message-reminder", (e) => { + const remind_message_id = Number($(e.currentTarget).attr("data-message-id")); + message_reminder_click_handler(remind_message_id); + e.preventDefault(); + e.stopPropagation(); + popover_menus.hide_current_popover_if_visible(instance); + }); + $popper.one("click", ".popover_move_message", (e) => { const message_id = Number($(e.currentTarget).attr("data-message-id")); assert(message_lists.current !== undefined); diff --git a/web/src/message_reminder.ts b/web/src/message_reminder.ts new file mode 100644 index 0000000000..1e74f2643f --- /dev/null +++ b/web/src/message_reminder.ts @@ -0,0 +1,40 @@ +import $ from "jquery"; + +import * as channel from "./channel.ts"; +import * as feedback_widget from "./feedback_widget.ts"; +import {$t} from "./i18n.ts"; +import * as timerender from "./timerender.ts"; +import * as ui_report from "./ui_report.ts"; + +export function set_message_reminder(send_at_time: number, message_id: number): void { + channel.post({ + url: "/json/reminders", + data: { + message_id, + scheduled_delivery_timestamp: send_at_time, + }, + success(): void { + const populate: (element: JQuery) => void = ($container) => { + $container.html( + $t( + {defaultMessage: "Your reminder has been scheduled for {translated_time}."}, + { + translated_time: timerender.get_full_datetime( + new Date(send_at_time * 1000), + "time", + ), + }, + ), + ); + }; + const title_text = $t({defaultMessage: "Reminder scheduled"}); + feedback_widget.show({ + populate, + title_text, + }); + }, + error(xhr: JQuery.jqXHR): void { + ui_report.error($t({defaultMessage: "Failed"}), xhr, $("#home-error"), 2000); + }, + }); +} diff --git a/web/src/popover_menus_data.ts b/web/src/popover_menus_data.ts index 981a3c5937..31639b85a8 100644 --- a/web/src/popover_menus_data.ts +++ b/web/src/popover_menus_data.ts @@ -38,6 +38,7 @@ type ActionPopoverContext = { view_source_menu_item: string | undefined; should_display_hide_option: boolean; should_display_mark_as_unread: boolean; + should_display_remind_me_option: boolean; should_display_collapse: boolean; should_display_uncollapse: boolean; should_display_quote_message: boolean; @@ -214,6 +215,7 @@ export function get_actions_popover_content_context(message_id: number): ActionP const should_display_delete_option = message_edit.get_deletability(message) && not_spectator; const should_display_read_receipts_option = realm.realm_enable_read_receipts && not_spectator; + const should_display_remind_me_option = not_spectator; function is_add_reaction_icon_visible(): boolean { assert(message_lists.current !== undefined); @@ -237,6 +239,7 @@ export function get_actions_popover_content_context(message_id: number): ActionP editability_menu_item, move_message_menu_item, should_display_mark_as_unread, + should_display_remind_me_option, view_source_menu_item, should_display_collapse, should_display_uncollapse, diff --git a/web/src/ui_init.js b/web/src/ui_init.js index bd94f5783f..1f49b135b0 100644 --- a/web/src/ui_init.js +++ b/web/src/ui_init.js @@ -457,7 +457,10 @@ export async function initialize_everything(state_data) { left_sidebar_navigation_area_popovers.initialize(); user_topic_popover.initialize(); topic_popover.initialize(); - message_actions_popover.initialize(); + const message_reminder_click_handler = (remind_message_id) => { + compose_send_menu_popover.open_send_later_menu(remind_message_id); + }; + message_actions_popover.initialize({message_reminder_click_handler}); compose_send_menu_popover.initialize(); realm_user_settings_defaults.initialize(state_data.realm_settings_defaults); diff --git a/web/templates/popovers/message_actions_popover.hbs b/web/templates/popovers/message_actions_popover.hbs index 7616553a3a..d58c800680 100644 --- a/web/templates/popovers/message_actions_popover.hbs +++ b/web/templates/popovers/message_actions_popover.hbs @@ -59,7 +59,7 @@ {{/if}} {{!-- Group 4 --}} - {{#if (or should_display_mark_as_unread should_display_hide_option should_display_collapse should_display_uncollapse)}} + {{#if (or should_display_mark_as_unread should_display_remind_me_option should_display_hide_option should_display_collapse should_display_uncollapse)}} {{/if}} {{#if should_display_mark_as_unread}} @@ -71,6 +71,14 @@ {{/if}} + {{#if should_display_remind_me_option}} + + {{/if}} {{#if should_display_hide_option}}