mirror of
https://github.com/zulip/zulip.git
synced 2025-11-16 20:02:15 +00:00
reminder: Show scheduled reminder info on render message.
This commit is contained in:
@@ -22,6 +22,7 @@ import * as message_edit from "./message_edit.ts";
|
||||
import type {MessageList} from "./message_list.ts";
|
||||
import * as message_list_tooltips from "./message_list_tooltips.ts";
|
||||
import * as message_lists from "./message_lists.ts";
|
||||
import * as message_reminder from "./message_reminder.ts";
|
||||
import * as message_store from "./message_store.ts";
|
||||
import type {Message} from "./message_store.ts";
|
||||
import * as message_viewport from "./message_viewport.ts";
|
||||
@@ -777,6 +778,7 @@ export class MessageListView {
|
||||
for (const message of messages) {
|
||||
const message_reactions = reactions.get_message_reactions(message);
|
||||
message.message_reactions = message_reactions;
|
||||
message.reminders = message_reminder.get_reminders(message.id);
|
||||
|
||||
// These will be used to build the message container
|
||||
let include_recipient = false;
|
||||
@@ -1077,6 +1079,7 @@ export class MessageListView {
|
||||
_get_message_template(message_container: MessageContainer): string {
|
||||
const msg_reactions = reactions.get_message_reactions(message_container.msg);
|
||||
message_container.msg.message_reactions = msg_reactions;
|
||||
message_container.msg.reminders = message_reminder.get_reminders(message_container.msg.id);
|
||||
const msg_to_render = {
|
||||
...message_container,
|
||||
message_list_id: this.list.id,
|
||||
|
||||
@@ -1,17 +1,33 @@
|
||||
import $ from "jquery";
|
||||
import type * as z from "zod/mini";
|
||||
|
||||
import render_message_reminders from "../templates/message_reminders.hbs";
|
||||
|
||||
import * as channel from "./channel.ts";
|
||||
import * as feedback_widget from "./feedback_widget.ts";
|
||||
import {$t} from "./i18n.ts";
|
||||
import * as message_lists from "./message_lists.ts";
|
||||
import type {StateData, reminder_schema} from "./state_data.ts";
|
||||
import * as timerender from "./timerender.ts";
|
||||
import * as ui_report from "./ui_report.ts";
|
||||
|
||||
export type Reminder = z.infer<typeof reminder_schema>;
|
||||
|
||||
// Used to render reminders in message list.
|
||||
export type TimeFormattedReminder = {
|
||||
reminder_id: number;
|
||||
formatted_delivery_time: string;
|
||||
scheduled_delivery_timestamp: number;
|
||||
};
|
||||
|
||||
export const reminders_by_id = new Map<number, Reminder>();
|
||||
|
||||
export const reminders_by_message_id = new Map<number, TimeFormattedReminder[]>();
|
||||
|
||||
export function get_reminders(message_id: number): TimeFormattedReminder[] | undefined {
|
||||
return reminders_by_message_id.get(message_id);
|
||||
}
|
||||
|
||||
export function set_message_reminder(send_at_time: number, message_id: number, note: string): void {
|
||||
channel.post({
|
||||
url: "/json/reminders",
|
||||
@@ -48,8 +64,38 @@ export function set_message_reminder(send_at_time: number, message_id: number, n
|
||||
}
|
||||
|
||||
export function add_reminders(reminders: Reminder[]): void {
|
||||
const message_ids_to_rerender = new Set<number>();
|
||||
for (const reminder of reminders) {
|
||||
message_ids_to_rerender.add(reminder.reminder_target_message_id);
|
||||
reminders_by_id.set(reminder.reminder_id, reminder);
|
||||
|
||||
// Do all the formatting and sorting needed to display
|
||||
// reminders for a message to avoid doing at the time of render.
|
||||
const formatted_delivery_time = timerender.get_full_datetime(
|
||||
new Date(reminder.scheduled_delivery_timestamp * 1000),
|
||||
"time",
|
||||
);
|
||||
const time_formatted_reminder: TimeFormattedReminder = {
|
||||
reminder_id: reminder.reminder_id,
|
||||
formatted_delivery_time,
|
||||
scheduled_delivery_timestamp: reminder.scheduled_delivery_timestamp,
|
||||
};
|
||||
if (!reminders_by_message_id.has(reminder.reminder_target_message_id)) {
|
||||
reminders_by_message_id.set(reminder.reminder_target_message_id, [
|
||||
time_formatted_reminder,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
const message_reminders = get_reminders(reminder.reminder_target_message_id)!;
|
||||
message_reminders.push(time_formatted_reminder);
|
||||
// Sort reminders to show the earliest one first.
|
||||
message_reminders.sort(
|
||||
(a, b) => a.scheduled_delivery_timestamp - b.scheduled_delivery_timestamp,
|
||||
);
|
||||
}
|
||||
|
||||
for (const message_id of message_ids_to_rerender) {
|
||||
rerender_reminders_for_message(message_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +106,18 @@ export function initialize(reminders_params: StateData["reminders"]): void {
|
||||
export function remove_reminder(reminder_id: number): void {
|
||||
if (reminders_by_id.has(reminder_id)) {
|
||||
reminders_by_id.delete(reminder_id);
|
||||
|
||||
for (const [message_id, message_reminders] of reminders_by_message_id) {
|
||||
const index = message_reminders.findIndex((r) => r.reminder_id === reminder_id);
|
||||
if (index !== -1) {
|
||||
message_reminders.splice(index, 1);
|
||||
if (message_reminders.length === 0) {
|
||||
reminders_by_message_id.delete(message_id);
|
||||
}
|
||||
rerender_reminders_for_message(message_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,3 +131,34 @@ export function delete_reminder(reminder_id: number, success?: () => void): void
|
||||
export function get_count(): number {
|
||||
return reminders_by_id.size;
|
||||
}
|
||||
|
||||
export function rerender_reminders_for_message(message_id: number): void {
|
||||
const $rows = message_lists.all_rendered_row_for_message_id(message_id);
|
||||
if ($rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message_reminders = get_reminders(message_id) ?? [];
|
||||
if (message_reminders.length === 0) {
|
||||
$rows.find(".message-reminders").remove();
|
||||
return;
|
||||
}
|
||||
|
||||
const rendered_message_reminders_html = render_message_reminders({
|
||||
msg: {
|
||||
reminders: message_reminders,
|
||||
},
|
||||
});
|
||||
|
||||
$rows.each(function () {
|
||||
const $row = $(this);
|
||||
const $existing = $row.find(".message-reminders");
|
||||
if ($existing.length > 0) {
|
||||
$existing.replaceWith($(rendered_message_reminders_html));
|
||||
} else {
|
||||
// Insert after reactions if they exist, otherwise after "more" section.
|
||||
const $content = $row.find(".messagebox-content");
|
||||
$content.append($(rendered_message_reminders_html));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as z from "zod/mini";
|
||||
import * as blueslip from "./blueslip.ts";
|
||||
import type {RawLocalMessage} from "./echo.ts";
|
||||
import type {NewMessage, ProcessedMessage} from "./message_helper.ts";
|
||||
import type {TimeFormattedReminder} from "./message_reminder.ts";
|
||||
import * as people from "./people.ts";
|
||||
import {topic_link_schema} from "./types.ts";
|
||||
import type {UserStatusEmojiInfo} from "./user_status.ts";
|
||||
@@ -197,6 +198,10 @@ export type Message = (
|
||||
// Used in message_notifications to track if a notification has already
|
||||
// been sent for this message.
|
||||
notification_sent?: boolean;
|
||||
|
||||
// Added during message rendering in message_list_view.ts. Should
|
||||
// never be accessed outside rendering, as the value may be stale.
|
||||
reminders?: TimeFormattedReminder[] | undefined;
|
||||
} & (
|
||||
| {
|
||||
type: "private";
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
ensures the timestamp will always have enough space
|
||||
in the column. */
|
||||
grid-template:
|
||||
minmax(var(--message-box-sender-line-height), auto) repeat(3, auto) /
|
||||
minmax(var(--message-box-sender-line-height), auto) repeat(4, auto) /
|
||||
var(--message-box-avatar-column-width) minmax(0, 1fr) max-content
|
||||
8px minmax(var(--message-box-timestamp-column-width), max-content);
|
||||
/* Named grid areas provide flexibility for positioning grid items
|
||||
@@ -109,7 +109,16 @@
|
||||
"edited message controls . time"
|
||||
". message . . . "
|
||||
". more . . . "
|
||||
". reactions . . . ";
|
||||
". reactions . . . "
|
||||
". reminders . . . ";
|
||||
|
||||
&:has(.message-reminders) {
|
||||
.message_reactions {
|
||||
margin-bottom: calc(
|
||||
var(--message-box-markdown-aligned-vertical-space) / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&.content_edit_mode {
|
||||
cursor: default;
|
||||
@@ -203,6 +212,16 @@
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.message-reminders {
|
||||
grid-area: reminders;
|
||||
font-style: italic;
|
||||
color: hsl(0deg 0% 53%);
|
||||
|
||||
.message-reminder {
|
||||
margin-bottom: 0.1875em; /* 3px at 16px/1em */
|
||||
}
|
||||
}
|
||||
|
||||
.message_edit {
|
||||
grid-area: message;
|
||||
/* Align self to start, rather than baseline, so the baseline
|
||||
@@ -274,7 +293,8 @@
|
||||
"avatar sender controls . time"
|
||||
"avatar message . . . "
|
||||
". more . . . "
|
||||
". reactions . . . ";
|
||||
". reactions . . . "
|
||||
". reminders . . . ";
|
||||
|
||||
.message_edit {
|
||||
/* No top margin when there's a sender row */
|
||||
@@ -287,12 +307,13 @@
|
||||
"avatar sender controls . time"
|
||||
"avatar sender . . . "
|
||||
". more . . . "
|
||||
". reactions . . . ";
|
||||
". reactions . . . "
|
||||
". reminders . . . ";
|
||||
grid-template-rows:
|
||||
var(--message-box-vertical-margin) var(
|
||||
--message-box-avatar-height
|
||||
)
|
||||
minmax(0, auto) repeat(3, auto);
|
||||
minmax(0, auto) repeat(4, auto);
|
||||
/* We align items to the baseline on me messages,
|
||||
and unset the align-content property. */
|
||||
align-items: baseline;
|
||||
|
||||
@@ -74,3 +74,7 @@
|
||||
{{#if (and (not is_hidden) msg.message_reactions)}}
|
||||
{{> message_reactions . }}
|
||||
{{/if}}
|
||||
|
||||
{{#if (and (not is_hidden) msg.reminders)}}
|
||||
{{> message_reminders . }}
|
||||
{{/if}}
|
||||
|
||||
14
web/templates/message_reminders.hbs
Normal file
14
web/templates/message_reminders.hbs
Normal file
@@ -0,0 +1,14 @@
|
||||
<div class="message-reminders">
|
||||
{{#each this/msg/reminders}}
|
||||
<p class="message-reminder">
|
||||
{{#tr}}
|
||||
<z-link>Reminder</z-link> scheduled for {formatted_delivery_time}.
|
||||
{{#*inline "z-link"~}}
|
||||
<a href="#reminders" class="message-reminder-overlay-link" data-reminder-id="{{ this/reminder_id }}">
|
||||
{{> @partial-block}}
|
||||
</a>
|
||||
{{~/inline}}
|
||||
{{/tr}}
|
||||
</p>
|
||||
{{/each}}
|
||||
</div>
|
||||
Reference in New Issue
Block a user