message_reminder: Add UI to schedule message reminder.

This adds an option in message actions popover menu to schedule
a message reminder.
This commit is contained in:
Aman Agrawal
2025-06-19 17:52:13 +05:30
committed by Tim Abbott
parent 1384a8d19b
commit b5f7e24669
9 changed files with 134 additions and 9 deletions

View File

@@ -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",

View File

@@ -0,0 +1,33 @@
<svg
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<defs
id="defs5" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:#000000;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="m 12,4 c -4.9587181,0 -9,4.0412819 -9,9 0,4.958718 4.0412819,9 9,9 4.958718,0 9,-4.041282 9,-9 0,-4.9587181 -4.041282,-9 -9,-9 z m 0,2 c 3.877838,0 7,3.1221621 7,7 0,3.877838 -3.122162,7 -7,7 C 8.1221621,20 5,16.877838 5,13 5,9.1221621 8.1221621,6 12,6 Z"
id="circle1" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:#000000;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="m 12,8 a 1,1 0 0 0 -1,1 v 4 a 1.0001,1.0001 0 0 0 0.292969,0.707031 l 2,2 a 1,1 0 0 0 1.414062,0 1,1 0 0 0 0,-1.414062 L 13,12.585938 V 9 A 1,1 0 0 0 12,8 Z"
id="path1" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:#000000;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 5,2 A 1,1 0 0 0 4.2929687,2.2929688 l -3,2.9999999 a 1,1 0 0 0 0,1.4140625 1,1 0 0 0 1.4140626,0 L 5.7070312,3.7070313 a 1,1 0 0 0 0,-1.4140625 A 1,1 0 0 0 5,2 Z"
id="path2" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:#000000;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="m 18.292969,2.2929688 a 1,1 0 0 0 0,1.4140625 l 3,2.9999999 a 1,1 0 0 0 1.414062,0 1,1 0 0 0 0,-1.4140625 l -3,-2.9999999 a 1,1 0 0 0 -1.414062,0 z"
id="path3" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:#000000;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 5.6855469,17.980469 3.3046875,20.28125 a 1,1 0 0 0 -0.023437,1.414062 1,1 0 0 0 1.4140625,0.02344 l 2.3789062,-2.298828 a 1,1 0 0 0 0.025391,-1.414063 1,1 0 0 0 -1.4140625,-0.02539 z"
id="path4" />
<path
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:#000000;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="m 18.341797,17.958984 a 1,1 0 0 0 -1.414063,0.0078 1,1 0 0 0 0.0098,1.414062 l 2.359375,2.330078 a 1,1 0 0 0 1.414062,-0.0078 1,1 0 0 0 -0.0078,-1.414063 z"
id="path5" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);
},
});
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -59,7 +59,7 @@
</li>
{{/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)}}
<li role="separator" class="popover-menu-separator"></li>
{{/if}}
{{#if should_display_mark_as_unread}}
@@ -71,6 +71,14 @@
</a>
</li>
{{/if}}
{{#if should_display_remind_me_option}}
<li role="none" class="link-item popover-menu-list-item">
<a role="menuitem" data-message-id="{{message_id}}" class="message-reminder popover-menu-link" tabindex="0">
<i class="popover-menu-icon zulip-icon zulip-icon-alarm-clock" aria-hidden="true"></i>
<span class="popover-menu-label">{{t "Remind me about this" }}</span>
</a>
</li>
{{/if}}
{{#if should_display_hide_option}}
<li role="none" class="link-item popover-menu-list-item">
<a role="menuitem" data-message-id="{{message_id}}" class="rehide_muted_user_message popover-menu-link" tabindex="0">

View File

@@ -3,7 +3,11 @@
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="send_later_modal_label">
<header class="modal__header">
<h1 class="modal__title" id="send_later_modal_label">
{{t "Schedule message" }}
{{#if is_reminder}}
{{t "Schedule reminder" }}
{{else}}
{{t "Schedule message" }}
{{/if}}
</h1>
<button class="modal__close" aria-label="{{t 'Close modal' }}" data-micromodal-close></button>
</header>