mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
overlays: Add overlay to display scheduled reminders.
This commit is contained in:
@@ -139,6 +139,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1943053 -->
|
|||||||
<div id="groups_overlay_container"></div>
|
<div id="groups_overlay_container"></div>
|
||||||
<div id="drafts_table"></div>
|
<div id="drafts_table"></div>
|
||||||
<div id="scheduled_messages_overlay_container"></div>
|
<div id="scheduled_messages_overlay_container"></div>
|
||||||
|
<div id="reminders-overlay-container"></div>
|
||||||
<div id="settings_overlay_container" class="overlay" data-overlay="settings" aria-hidden="true">
|
<div id="settings_overlay_container" class="overlay" data-overlay="settings" aria-hidden="true">
|
||||||
</div>
|
</div>
|
||||||
<div id="message-edit-history-overlay-container"></div>
|
<div id="message-edit-history-overlay-container"></div>
|
||||||
|
@@ -193,6 +193,7 @@ EXEMPT_FILES = make_set(
|
|||||||
"web/src/recent_view_ui.ts",
|
"web/src/recent_view_ui.ts",
|
||||||
"web/src/reload.ts",
|
"web/src/reload.ts",
|
||||||
"web/src/reload_setup.js",
|
"web/src/reload_setup.js",
|
||||||
|
"web/src/reminders_overlay_ui.ts",
|
||||||
"web/src/resize.ts",
|
"web/src/resize.ts",
|
||||||
"web/src/resize_handler.ts",
|
"web/src/resize_handler.ts",
|
||||||
"web/src/rows.ts",
|
"web/src/rows.ts",
|
||||||
|
@@ -66,6 +66,7 @@ export function is_overlay_hash(hash: string | undefined): boolean {
|
|||||||
"search-operators",
|
"search-operators",
|
||||||
"about-zulip",
|
"about-zulip",
|
||||||
"scheduled",
|
"scheduled",
|
||||||
|
"reminders",
|
||||||
"user",
|
"user",
|
||||||
];
|
];
|
||||||
const main_hash = get_hash_category(hash);
|
const main_hash = get_hash_category(hash);
|
||||||
|
@@ -20,6 +20,7 @@ import {page_params} from "./page_params.ts";
|
|||||||
import * as people from "./people.ts";
|
import * as people from "./people.ts";
|
||||||
import * as popovers from "./popovers.ts";
|
import * as popovers from "./popovers.ts";
|
||||||
import * as recent_view_ui from "./recent_view_ui.ts";
|
import * as recent_view_ui from "./recent_view_ui.ts";
|
||||||
|
import * as reminders_overlay_ui from "./reminders_overlay_ui.ts";
|
||||||
import * as scheduled_messages_overlay_ui from "./scheduled_messages_overlay_ui.ts";
|
import * as scheduled_messages_overlay_ui from "./scheduled_messages_overlay_ui.ts";
|
||||||
import * as settings from "./settings.ts";
|
import * as settings from "./settings.ts";
|
||||||
import * as settings_panel_menu from "./settings_panel_menu.ts";
|
import * as settings_panel_menu from "./settings_panel_menu.ts";
|
||||||
@@ -260,6 +261,7 @@ function do_hashchange_normal(from_reload: boolean, restore_selected_id: boolean
|
|||||||
case "#settings":
|
case "#settings":
|
||||||
case "#about-zulip":
|
case "#about-zulip":
|
||||||
case "#scheduled":
|
case "#scheduled":
|
||||||
|
case "#reminders":
|
||||||
blueslip.error("overlay logic skipped for: " + hash[0]);
|
blueslip.error("overlay logic skipped for: " + hash[0]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -504,6 +506,11 @@ function do_hashchange_overlay(old_hash: string | undefined): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (base === "reminders") {
|
||||||
|
reminders_overlay_ui.launch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (base === "user") {
|
if (base === "user") {
|
||||||
const user_id = Number.parseInt(hash_parser.get_current_hash_section(), 10);
|
const user_id = Number.parseInt(hash_parser.get_current_hash_section(), 10);
|
||||||
if (!people.is_known_user_id(user_id)) {
|
if (!people.is_known_user_id(user_id)) {
|
||||||
|
@@ -47,6 +47,7 @@ import * as reactions from "./reactions.ts";
|
|||||||
import * as read_receipts from "./read_receipts.ts";
|
import * as read_receipts from "./read_receipts.ts";
|
||||||
import * as recent_view_ui from "./recent_view_ui.ts";
|
import * as recent_view_ui from "./recent_view_ui.ts";
|
||||||
import * as recent_view_util from "./recent_view_util.ts";
|
import * as recent_view_util from "./recent_view_util.ts";
|
||||||
|
import * as reminders_overlay_ui from "./reminders_overlay_ui.ts";
|
||||||
import * as scheduled_messages_overlay_ui from "./scheduled_messages_overlay_ui.ts";
|
import * as scheduled_messages_overlay_ui from "./scheduled_messages_overlay_ui.ts";
|
||||||
import * as search from "./search.ts";
|
import * as search from "./search.ts";
|
||||||
import {message_edit_history_visibility_policy_values} from "./settings_config.ts";
|
import {message_edit_history_visibility_policy_values} from "./settings_config.ts";
|
||||||
@@ -616,6 +617,11 @@ export function process_enter_key(e) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (overlays.reminders_open()) {
|
||||||
|
reminders_overlay_ui.handle_keyboard_events("enter");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Transfer the enter keypress from button to the `<i>` tag inside
|
// Transfer the enter keypress from button to the `<i>` tag inside
|
||||||
// it since it is the trigger for the popover. <button> is already used
|
// it since it is the trigger for the popover. <button> is already used
|
||||||
// to trigger the tooltip so it cannot be used to trigger the popover.
|
// to trigger the tooltip so it cannot be used to trigger the popover.
|
||||||
@@ -872,6 +878,10 @@ export function process_hotkey(e, hotkey) {
|
|||||||
scheduled_messages_overlay_ui.handle_keyboard_events(event_name);
|
scheduled_messages_overlay_ui.handle_keyboard_events(event_name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (overlays.reminders_open()) {
|
||||||
|
reminders_overlay_ui.handle_keyboard_events(event_name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (overlays.message_edit_history_open()) {
|
if (overlays.message_edit_history_open()) {
|
||||||
message_edit_history.handle_keyboard_events(event_name);
|
message_edit_history.handle_keyboard_events(event_name);
|
||||||
return true;
|
return true;
|
||||||
|
@@ -3,6 +3,7 @@ import _ from "lodash";
|
|||||||
|
|
||||||
import type {Filter} from "./filter.ts";
|
import type {Filter} from "./filter.ts";
|
||||||
import {localstorage} from "./localstorage.ts";
|
import {localstorage} from "./localstorage.ts";
|
||||||
|
import * as message_reminder from "./message_reminder.ts";
|
||||||
import {page_params} from "./page_params.ts";
|
import {page_params} from "./page_params.ts";
|
||||||
import * as people from "./people.ts";
|
import * as people from "./people.ts";
|
||||||
import * as resize from "./resize.ts";
|
import * as resize from "./resize.ts";
|
||||||
@@ -59,6 +60,17 @@ export function update_scheduled_messages_row(): void {
|
|||||||
ui_util.update_unread_count_in_dom($scheduled_li, count);
|
ui_util.update_unread_count_in_dom($scheduled_li, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function update_reminders_row(): void {
|
||||||
|
const $reminders_li = $(".top_left_reminders");
|
||||||
|
const count = message_reminder.get_count();
|
||||||
|
if (count > 0) {
|
||||||
|
$reminders_li.addClass("show-with-reminders");
|
||||||
|
} else {
|
||||||
|
$reminders_li.removeClass("show-with-reminders");
|
||||||
|
}
|
||||||
|
ui_util.update_unread_count_in_dom($reminders_li, count);
|
||||||
|
}
|
||||||
|
|
||||||
export function update_dom_with_unread_counts(
|
export function update_dom_with_unread_counts(
|
||||||
counts: unread.FullUnreadCountsData,
|
counts: unread.FullUnreadCountsData,
|
||||||
skip_animations: boolean,
|
skip_animations: boolean,
|
||||||
@@ -251,6 +263,7 @@ export function handle_home_view_changed(new_home_view: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function initialize(): void {
|
export function initialize(): void {
|
||||||
|
update_reminders_row();
|
||||||
update_scheduled_messages_row();
|
update_scheduled_messages_row();
|
||||||
restore_views_state();
|
restore_views_state();
|
||||||
|
|
||||||
|
@@ -1,11 +1,17 @@
|
|||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
|
import type {z} from "zod";
|
||||||
|
|
||||||
import * as channel from "./channel.ts";
|
import * as channel from "./channel.ts";
|
||||||
import * as feedback_widget from "./feedback_widget.ts";
|
import * as feedback_widget from "./feedback_widget.ts";
|
||||||
import {$t} from "./i18n.ts";
|
import {$t} from "./i18n.ts";
|
||||||
|
import type {StateData, reminder_schema} from "./state_data.ts";
|
||||||
import * as timerender from "./timerender.ts";
|
import * as timerender from "./timerender.ts";
|
||||||
import * as ui_report from "./ui_report.ts";
|
import * as ui_report from "./ui_report.ts";
|
||||||
|
|
||||||
|
export type Reminder = z.infer<typeof reminder_schema>;
|
||||||
|
|
||||||
|
export const reminders_by_id = new Map<number, Reminder>();
|
||||||
|
|
||||||
export function set_message_reminder(send_at_time: number, message_id: number): void {
|
export function set_message_reminder(send_at_time: number, message_id: number): void {
|
||||||
channel.post({
|
channel.post({
|
||||||
url: "/json/reminders",
|
url: "/json/reminders",
|
||||||
@@ -39,3 +45,30 @@ export function set_message_reminder(send_at_time: number, message_id: number):
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function add_reminders(reminders: Reminder[]): void {
|
||||||
|
for (const reminder of reminders) {
|
||||||
|
reminders_by_id.set(reminder.reminder_id, reminder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initialize(reminders_params: StateData["reminders"]): void {
|
||||||
|
add_reminders(reminders_params.reminders);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function remove_reminder(reminder_id: number): void {
|
||||||
|
if (reminders_by_id.has(reminder_id)) {
|
||||||
|
reminders_by_id.delete(reminder_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function delete_reminder(reminder_id: number, success?: () => void): void {
|
||||||
|
void channel.del({
|
||||||
|
url: "/json/reminders/" + reminder_id,
|
||||||
|
success,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_count(): number {
|
||||||
|
return reminders_by_id.size;
|
||||||
|
}
|
||||||
|
@@ -73,6 +73,10 @@ export function scheduled_messages_open(): boolean {
|
|||||||
return open_overlay_name === "scheduled";
|
return open_overlay_name === "scheduled";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function reminders_open(): boolean {
|
||||||
|
return open_overlay_name === "reminders";
|
||||||
|
}
|
||||||
|
|
||||||
export function message_edit_history_open(): boolean {
|
export function message_edit_history_open(): boolean {
|
||||||
return open_overlay_name === "message_edit_history";
|
return open_overlay_name === "message_edit_history";
|
||||||
}
|
}
|
||||||
|
131
web/src/reminders_overlay_ui.ts
Normal file
131
web/src/reminders_overlay_ui.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import $ from "jquery";
|
||||||
|
import assert from "minimalistic-assert";
|
||||||
|
|
||||||
|
import render_reminder_list from "../templates/reminder_list.hbs";
|
||||||
|
import render_reminders_overlay from "../templates/reminders_overlay.hbs";
|
||||||
|
|
||||||
|
import * as browser_history from "./browser_history.ts";
|
||||||
|
import * as message_reminder from "./message_reminder.ts";
|
||||||
|
import type {Reminder} from "./message_reminder.ts";
|
||||||
|
import * as messages_overlay_ui from "./messages_overlay_ui.ts";
|
||||||
|
import * as overlays from "./overlays.ts";
|
||||||
|
import * as timerender from "./timerender.ts";
|
||||||
|
|
||||||
|
type ReminderRenderContext = Reminder & {
|
||||||
|
formatted_send_at_time: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const keyboard_handling_context = {
|
||||||
|
get_items_ids() {
|
||||||
|
const reminders_ids = [];
|
||||||
|
const sorted_reminders = sort_reminders(message_reminder.reminders_by_id);
|
||||||
|
for (const reminder of sorted_reminders) {
|
||||||
|
reminders_ids.push(reminder.reminder_id.toString());
|
||||||
|
}
|
||||||
|
return reminders_ids;
|
||||||
|
},
|
||||||
|
on_enter() {
|
||||||
|
// TODO: Allow editing reminder.
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
on_delete() {
|
||||||
|
const focused_element_id = messages_overlay_ui.get_focused_element_id(this);
|
||||||
|
if (focused_element_id === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const $focused_row = messages_overlay_ui.row_with_focus(this);
|
||||||
|
messages_overlay_ui.focus_on_sibling_element(this);
|
||||||
|
// We need to have a super responsive UI feedback here, so we remove the row from the DOM manually
|
||||||
|
$focused_row.remove();
|
||||||
|
message_reminder.delete_reminder(Number.parseInt(focused_element_id, 10));
|
||||||
|
},
|
||||||
|
items_container_selector: "reminders-container",
|
||||||
|
items_list_selector: "reminders-list",
|
||||||
|
row_item_selector: "reminder-row",
|
||||||
|
box_item_selector: "reminder-info-box",
|
||||||
|
id_attribute_name: "data-reminder-id",
|
||||||
|
};
|
||||||
|
|
||||||
|
function sort_reminders(reminders: Map<number, Reminder>): Reminder[] {
|
||||||
|
const sorted_reminders = [...reminders.values()].sort(
|
||||||
|
(reminder1, reminder2) =>
|
||||||
|
reminder1.scheduled_delivery_timestamp - reminder2.scheduled_delivery_timestamp,
|
||||||
|
);
|
||||||
|
return sorted_reminders;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handle_keyboard_events(event_key: string): void {
|
||||||
|
messages_overlay_ui.modals_handle_events(event_key, keyboard_handling_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
function format(reminders: Map<number, Reminder>): ReminderRenderContext[] {
|
||||||
|
const formatted_reminders = [];
|
||||||
|
const sorted_reminders = sort_reminders(reminders);
|
||||||
|
|
||||||
|
for (const reminder of sorted_reminders) {
|
||||||
|
const time = new Date(reminder.scheduled_delivery_timestamp * 1000);
|
||||||
|
const formatted_send_at_time = timerender.get_full_datetime(time, "time");
|
||||||
|
const reminder_render_context = {
|
||||||
|
...reminder,
|
||||||
|
formatted_send_at_time,
|
||||||
|
};
|
||||||
|
formatted_reminders.push(reminder_render_context);
|
||||||
|
}
|
||||||
|
return formatted_reminders;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function launch(): void {
|
||||||
|
$("#reminders-overlay-container").html(render_reminders_overlay());
|
||||||
|
overlays.open_overlay({
|
||||||
|
name: "reminders",
|
||||||
|
$overlay: $("#reminders-overlay"),
|
||||||
|
on_close() {
|
||||||
|
browser_history.exit_overlay();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const rendered_list = render_reminder_list({
|
||||||
|
reminders_data: format(message_reminder.reminders_by_id),
|
||||||
|
});
|
||||||
|
const $messages_list = $("#reminders-overlay .overlay-messages-list");
|
||||||
|
$messages_list.append($(rendered_list));
|
||||||
|
|
||||||
|
const first_element_id = keyboard_handling_context.get_items_ids()[0];
|
||||||
|
messages_overlay_ui.set_initial_element(first_element_id, keyboard_handling_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rerender(): void {
|
||||||
|
if (!overlays.reminders_open()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rendered_list = render_reminder_list({
|
||||||
|
reminders_data: format(message_reminder.reminders_by_id),
|
||||||
|
});
|
||||||
|
const $messages_list = $("#reminders-overlay .overlay-messages-list");
|
||||||
|
$messages_list.find(".reminder-row").remove();
|
||||||
|
$messages_list.append($(rendered_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function remove_reminder_id(reminder_id: number): void {
|
||||||
|
if (overlays.reminders_open()) {
|
||||||
|
$(`#reminders-overlay .reminder-row[data-reminder-id=${reminder_id}]`).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initialize(): void {
|
||||||
|
$("body").on("click", ".reminder-row .delete-overlay-message", (e) => {
|
||||||
|
const scheduled_msg_id = $(e.currentTarget)
|
||||||
|
.closest(".reminder-row")
|
||||||
|
.attr("data-reminder-id");
|
||||||
|
assert(scheduled_msg_id !== undefined);
|
||||||
|
|
||||||
|
message_reminder.delete_reminder(Number.parseInt(scheduled_msg_id, 10));
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on("focus", ".reminder-info-box", function (this: HTMLElement) {
|
||||||
|
messages_overlay_ui.activate_element(this, keyboard_handling_context);
|
||||||
|
});
|
||||||
|
}
|
@@ -30,6 +30,7 @@ import * as message_edit from "./message_edit.ts";
|
|||||||
import * as message_events from "./message_events.ts";
|
import * as message_events from "./message_events.ts";
|
||||||
import * as message_lists from "./message_lists.ts";
|
import * as message_lists from "./message_lists.ts";
|
||||||
import * as message_live_update from "./message_live_update.ts";
|
import * as message_live_update from "./message_live_update.ts";
|
||||||
|
import * as message_reminder from "./message_reminder.ts";
|
||||||
import * as message_store from "./message_store.ts";
|
import * as message_store from "./message_store.ts";
|
||||||
import * as message_view from "./message_view.ts";
|
import * as message_view from "./message_view.ts";
|
||||||
import * as muted_users_ui from "./muted_users_ui.ts";
|
import * as muted_users_ui from "./muted_users_ui.ts";
|
||||||
@@ -47,6 +48,7 @@ import * as realm_playground from "./realm_playground.ts";
|
|||||||
import {realm_user_settings_defaults} from "./realm_user_settings_defaults.ts";
|
import {realm_user_settings_defaults} from "./realm_user_settings_defaults.ts";
|
||||||
import * as recent_view_ui from "./recent_view_ui.ts";
|
import * as recent_view_ui from "./recent_view_ui.ts";
|
||||||
import * as reload from "./reload.ts";
|
import * as reload from "./reload.ts";
|
||||||
|
import * as reminders_overlay_ui from "./reminders_overlay_ui.ts";
|
||||||
import * as saved_snippets from "./saved_snippets.ts";
|
import * as saved_snippets from "./saved_snippets.ts";
|
||||||
import * as saved_snippets_ui from "./saved_snippets_ui.ts";
|
import * as saved_snippets_ui from "./saved_snippets_ui.ts";
|
||||||
import * as scheduled_messages from "./scheduled_messages.ts";
|
import * as scheduled_messages from "./scheduled_messages.ts";
|
||||||
@@ -642,6 +644,24 @@ export function dispatch_normal_event(event) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "reminders":
|
||||||
|
switch (event.op) {
|
||||||
|
case "add": {
|
||||||
|
message_reminder.add_reminders(event.reminders);
|
||||||
|
reminders_overlay_ui.rerender();
|
||||||
|
left_sidebar_navigation_area.update_reminders_row();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "remove": {
|
||||||
|
message_reminder.remove_reminder(event.reminder_id);
|
||||||
|
reminders_overlay_ui.remove_reminder_id(event.reminder_id);
|
||||||
|
left_sidebar_navigation_area.update_reminders_row();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// No default
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case "stream":
|
case "stream":
|
||||||
switch (event.op) {
|
switch (event.op) {
|
||||||
case "update":
|
case "update":
|
||||||
|
@@ -63,6 +63,17 @@ export const scheduled_message_schema = z
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const reminder_schema = z.object({
|
||||||
|
reminder_id: z.number(),
|
||||||
|
type: z.literal("private"),
|
||||||
|
to: z.array(z.number()),
|
||||||
|
content: z.string(),
|
||||||
|
rendered_content: z.string(),
|
||||||
|
scheduled_delivery_timestamp: z.number(),
|
||||||
|
failed: z.boolean(),
|
||||||
|
reminder_target_message_id: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
export const profile_datum_schema = z.object({
|
export const profile_datum_schema = z.object({
|
||||||
value: z.string(),
|
value: z.string(),
|
||||||
rendered_value: z.string().nullish(),
|
rendered_value: z.string().nullish(),
|
||||||
@@ -559,6 +570,7 @@ export const state_data_schema = z
|
|||||||
.object({scheduled_messages: z.array(scheduled_message_schema)})
|
.object({scheduled_messages: z.array(scheduled_message_schema)})
|
||||||
.transform((scheduled_messages) => ({scheduled_messages})),
|
.transform((scheduled_messages) => ({scheduled_messages})),
|
||||||
)
|
)
|
||||||
|
.and(z.object({reminders: z.array(reminder_schema)}).transform((reminders) => ({reminders})))
|
||||||
.and(
|
.and(
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
|
@@ -73,6 +73,7 @@ import * as message_fetch from "./message_fetch.ts";
|
|||||||
import * as message_list_hover from "./message_list_hover.ts";
|
import * as message_list_hover from "./message_list_hover.ts";
|
||||||
import * as message_list_tooltips from "./message_list_tooltips.ts";
|
import * as message_list_tooltips from "./message_list_tooltips.ts";
|
||||||
import * as message_lists from "./message_lists.ts";
|
import * as message_lists from "./message_lists.ts";
|
||||||
|
import * as message_reminder from "./message_reminder.ts";
|
||||||
import * as message_scroll from "./message_scroll.ts";
|
import * as message_scroll from "./message_scroll.ts";
|
||||||
import * as message_view from "./message_view.ts";
|
import * as message_view from "./message_view.ts";
|
||||||
import * as message_view_header from "./message_view_header.ts";
|
import * as message_view_header from "./message_view_header.ts";
|
||||||
@@ -103,6 +104,7 @@ import * as realm_playground from "./realm_playground.ts";
|
|||||||
import * as realm_user_settings_defaults from "./realm_user_settings_defaults.ts";
|
import * as realm_user_settings_defaults from "./realm_user_settings_defaults.ts";
|
||||||
import * as recent_view_ui from "./recent_view_ui.ts";
|
import * as recent_view_ui from "./recent_view_ui.ts";
|
||||||
import * as reload_setup from "./reload_setup.js";
|
import * as reload_setup from "./reload_setup.js";
|
||||||
|
import * as reminders_overlay_ui from "./reminders_overlay_ui.ts";
|
||||||
import * as resize_handler from "./resize_handler.ts";
|
import * as resize_handler from "./resize_handler.ts";
|
||||||
import * as saved_snippets from "./saved_snippets.ts";
|
import * as saved_snippets from "./saved_snippets.ts";
|
||||||
import * as scheduled_messages from "./scheduled_messages.ts";
|
import * as scheduled_messages from "./scheduled_messages.ts";
|
||||||
@@ -452,7 +454,9 @@ export async function initialize_everything(state_data) {
|
|||||||
left_sidebar_tooltips.initialize();
|
left_sidebar_tooltips.initialize();
|
||||||
// This populates data for scheduled messages.
|
// This populates data for scheduled messages.
|
||||||
scheduled_messages.initialize(state_data.scheduled_messages);
|
scheduled_messages.initialize(state_data.scheduled_messages);
|
||||||
|
message_reminder.initialize(state_data.reminders);
|
||||||
scheduled_messages_ui.initialize();
|
scheduled_messages_ui.initialize();
|
||||||
|
reminders_overlay_ui.initialize();
|
||||||
popover_menus.initialize();
|
popover_menus.initialize();
|
||||||
left_sidebar_navigation_area_popovers.initialize();
|
left_sidebar_navigation_area_popovers.initialize();
|
||||||
user_topic_popover.initialize();
|
user_topic_popover.initialize();
|
||||||
|
@@ -822,9 +822,11 @@ li.active-sub-filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Don't show the scheduled messages item... */
|
/* Don't show the scheduled messages item... */
|
||||||
|
li.top_left_reminders,
|
||||||
li.top_left_scheduled_messages {
|
li.top_left_scheduled_messages {
|
||||||
display: none;
|
display: none;
|
||||||
/* ...unless there are scheduled messages to show. */
|
/* ...unless there are scheduled messages to show. */
|
||||||
|
&.show-with-reminders,
|
||||||
&.show-with-scheduled-messages {
|
&.show-with-scheduled-messages {
|
||||||
/* Use display: grid to preserve the grid
|
/* Use display: grid to preserve the grid
|
||||||
layout when visible. */
|
layout when visible. */
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
#reminders-overlay-container,
|
||||||
#scheduled_messages_overlay_container {
|
#scheduled_messages_overlay_container {
|
||||||
.no-overlay-messages {
|
.no-overlay-messages {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -14,3 +15,7 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: hsl(0deg 0% 53%);
|
color: hsl(0deg 0% 53%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#reminders-overlay-container .message_content {
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
@@ -152,6 +152,16 @@
|
|||||||
<span class="unread_count quiet-count"></span>
|
<span class="unread_count quiet-count"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="top_left_reminders top_left_row hidden-for-spectators">
|
||||||
|
<a class="left-sidebar-navigation-label-container tippy-left-sidebar-tooltip" href="#reminders" data-tooltip-template-id="reminders-tooltip-template">
|
||||||
|
<span class="filter-icon">
|
||||||
|
<i class="zulip-icon zulip-icon-alarm-clock" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
{{~!-- squash whitespace --~}}
|
||||||
|
<span class="left-sidebar-navigation-label">{{t 'Reminders' }}</span>
|
||||||
|
<span class="unread_count quiet-count"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
27
web/templates/reminder_list.hbs
Normal file
27
web/templates/reminder_list.hbs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{{#each reminders_data}}
|
||||||
|
<div class="reminder-row overlay-message-row" data-reminder-id="{{reminder_id}}">
|
||||||
|
<div class="reminder-info-box overlay-message-info-box" tabindex="0">
|
||||||
|
<div class="message_header message_header_private_message">
|
||||||
|
<div class="message-header-contents">
|
||||||
|
<div class="message_label_clickable stream_label">
|
||||||
|
<span class="private_message_header_icon"><i class="zulip-icon zulip-icon-user"></i></span>
|
||||||
|
<span class="private_message_header_name">{{t "Notification Bot to you" }}</span>
|
||||||
|
</div>
|
||||||
|
{{> scheduled_message_stream_pm_common .}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="message_row{{#unless is_stream}} private-message{{/unless}}" role="listitem">
|
||||||
|
<div class="messagebox">
|
||||||
|
<div class="messagebox-content">
|
||||||
|
<div class="message_top_line">
|
||||||
|
<div class="overlay_message_controls">
|
||||||
|
{{> ./components/icon_button intent="danger" custom_classes="delete-overlay-message tippy-zulip-delayed-tooltip" icon="trash" data-tooltip-template-id="delete-reminder-tooltip-template" aria-label=(t "Delete") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="message_content rendered_markdown restore-overlay-message">{{rendered_markdown rendered_content}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
17
web/templates/reminders_overlay.hbs
Normal file
17
web/templates/reminders_overlay.hbs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<div id="reminders-overlay" class="overlay" data-overlay="reminders">
|
||||||
|
<div class="flex overlay-content">
|
||||||
|
<div class="overlay-messages-container overlay-container reminders-container">
|
||||||
|
<div class="overlay-messages-header">
|
||||||
|
<h1>{{t 'Scheduled reminders' }}</h1>
|
||||||
|
<div class="exit">
|
||||||
|
<span class="exit-sign">×</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="reminders-list overlay-messages-list">
|
||||||
|
<div class="no-overlay-messages">
|
||||||
|
{{t 'No reminders scheduled.'}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -161,6 +161,12 @@
|
|||||||
<div class="tootlip-inner-content views-message-count italic"></div>
|
<div class="tootlip-inner-content views-message-count italic"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template id="reminders-tooltip-template">
|
||||||
|
<div class="views-tooltip-container" data-view-code="reminders">
|
||||||
|
<div>{{t 'Reminders'}}</div>
|
||||||
|
<div class="tootlip-inner-content views-message-count italic"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template id="show-all-direct-messages-template">
|
<template id="show-all-direct-messages-template">
|
||||||
{{t 'Direct message feed' }}
|
{{t 'Direct message feed' }}
|
||||||
{{tooltip_hotkey_hints "Shift" "P"}}
|
{{tooltip_hotkey_hints "Shift" "P"}}
|
||||||
@@ -253,6 +259,10 @@
|
|||||||
{{t 'Delete scheduled message' }}
|
{{t 'Delete scheduled message' }}
|
||||||
{{tooltip_hotkey_hints "Backspace"}}
|
{{tooltip_hotkey_hints "Backspace"}}
|
||||||
</template>
|
</template>
|
||||||
|
<template id="delete-reminder-tooltip-template">
|
||||||
|
{{t 'Delete reminder' }}
|
||||||
|
{{tooltip_hotkey_hints "Backspace"}}
|
||||||
|
</template>
|
||||||
<template id="create-new-stream-tooltip-template">
|
<template id="create-new-stream-tooltip-template">
|
||||||
{{t 'Create new channel' }}
|
{{t 'Create new channel' }}
|
||||||
{{tooltip_hotkey_hints "N"}}
|
{{tooltip_hotkey_hints "N"}}
|
||||||
|
@@ -49,6 +49,8 @@ const realm_icon = mock_esm("../src/realm_icon");
|
|||||||
const realm_logo = mock_esm("../src/realm_logo");
|
const realm_logo = mock_esm("../src/realm_logo");
|
||||||
const realm_playground = mock_esm("../src/realm_playground");
|
const realm_playground = mock_esm("../src/realm_playground");
|
||||||
const reload = mock_esm("../src/reload");
|
const reload = mock_esm("../src/reload");
|
||||||
|
const message_reminder = mock_esm("../src/message_reminder");
|
||||||
|
const reminders_overlay_ui = mock_esm("../src/reminders_overlay_ui");
|
||||||
const saved_snippets = mock_esm("../src/saved_snippets");
|
const saved_snippets = mock_esm("../src/saved_snippets");
|
||||||
const saved_snippets_ui = mock_esm("../src/saved_snippets_ui");
|
const saved_snippets_ui = mock_esm("../src/saved_snippets_ui");
|
||||||
const scheduled_messages = mock_esm("../src/scheduled_messages");
|
const scheduled_messages = mock_esm("../src/scheduled_messages");
|
||||||
@@ -89,6 +91,7 @@ const submessage = mock_esm("../src/submessage");
|
|||||||
mock_esm("../src/left_sidebar_navigation_area", {
|
mock_esm("../src/left_sidebar_navigation_area", {
|
||||||
update_starred_count() {},
|
update_starred_count() {},
|
||||||
update_scheduled_messages_row() {},
|
update_scheduled_messages_row() {},
|
||||||
|
update_reminders_row() {},
|
||||||
handle_home_view_changed() {},
|
handle_home_view_changed() {},
|
||||||
});
|
});
|
||||||
const typing_events = mock_esm("../src/typing_events");
|
const typing_events = mock_esm("../src/typing_events");
|
||||||
@@ -453,6 +456,30 @@ run_test("reaction", ({override}) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
run_test("reminders", ({override}) => {
|
||||||
|
override(reminders_overlay_ui, "rerender", noop);
|
||||||
|
override(reminders_overlay_ui, "remove_reminder_id", noop);
|
||||||
|
|
||||||
|
let event = event_fixtures.reminders__add;
|
||||||
|
{
|
||||||
|
const stub = make_stub();
|
||||||
|
override(message_reminder, "add_reminders", stub.f);
|
||||||
|
dispatch(event);
|
||||||
|
assert.equal(stub.num_calls, 1);
|
||||||
|
const args = stub.get_args("reminders");
|
||||||
|
assert_same(args.reminders, event.reminders);
|
||||||
|
}
|
||||||
|
event = event_fixtures.reminders__remove;
|
||||||
|
{
|
||||||
|
const stub = make_stub();
|
||||||
|
override(message_reminder, "remove_reminder", stub.f);
|
||||||
|
dispatch(event);
|
||||||
|
assert.equal(stub.num_calls, 1);
|
||||||
|
const args = stub.get_args("reminder_id");
|
||||||
|
assert_same(args.reminder_id, event.reminder_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
run_test("scheduled_messages", ({override}) => {
|
run_test("scheduled_messages", ({override}) => {
|
||||||
override(scheduled_messages_overlay_ui, "rerender", noop);
|
override(scheduled_messages_overlay_ui, "rerender", noop);
|
||||||
override(scheduled_messages_overlay_ui, "remove_scheduled_message_id", noop);
|
override(scheduled_messages_overlay_ui, "remove_scheduled_message_id", noop);
|
||||||
|
@@ -67,6 +67,7 @@ const overlays = mock_esm("../src/overlays", {
|
|||||||
lightbox_open: () => false,
|
lightbox_open: () => false,
|
||||||
drafts_open: () => false,
|
drafts_open: () => false,
|
||||||
scheduled_messages_open: () => false,
|
scheduled_messages_open: () => false,
|
||||||
|
reminders_open: () => false,
|
||||||
info_overlay_open: () => false,
|
info_overlay_open: () => false,
|
||||||
message_edit_history_open: () => false,
|
message_edit_history_open: () => false,
|
||||||
});
|
});
|
||||||
|
@@ -14,6 +14,9 @@ const scheduled_messages = mock_esm("../src/scheduled_messages");
|
|||||||
|
|
||||||
scheduled_messages.get_count = () => 555;
|
scheduled_messages.get_count = () => 555;
|
||||||
|
|
||||||
|
const message_reminder = mock_esm("../src/message_reminder");
|
||||||
|
message_reminder.get_count = () => 888;
|
||||||
|
|
||||||
const {Filter} = zrequire("../src/filter");
|
const {Filter} = zrequire("../src/filter");
|
||||||
const left_sidebar_navigation_area = zrequire("left_sidebar_navigation_area");
|
const left_sidebar_navigation_area = zrequire("left_sidebar_navigation_area");
|
||||||
|
|
||||||
@@ -116,6 +119,8 @@ run_test("update_count_in_dom", () => {
|
|||||||
|
|
||||||
make_elem($(".top_left_scheduled_messages"), "<scheduled-count>");
|
make_elem($(".top_left_scheduled_messages"), "<scheduled-count>");
|
||||||
|
|
||||||
|
make_elem($(".top_left_reminders"), "<reminders-count>");
|
||||||
|
|
||||||
make_elem($("#streams_header"), "<stream-count>");
|
make_elem($("#streams_header"), "<stream-count>");
|
||||||
|
|
||||||
make_elem($("#topics_header"), "<topics-count>");
|
make_elem($("#topics_header"), "<topics-count>");
|
||||||
@@ -129,18 +134,22 @@ run_test("update_count_in_dom", () => {
|
|||||||
assert.equal($("<home-count>").text(), "333");
|
assert.equal($("<home-count>").text(), "333");
|
||||||
assert.equal($("<starred-count>").text(), "444");
|
assert.equal($("<starred-count>").text(), "444");
|
||||||
assert.equal($("<scheduled-count>").text(), "555");
|
assert.equal($("<scheduled-count>").text(), "555");
|
||||||
|
assert.equal($("<reminders-count>").text(), "888");
|
||||||
assert.equal($("<stream-count>").text(), "666");
|
assert.equal($("<stream-count>").text(), "666");
|
||||||
assert.equal($("<topics-count>").text(), "666");
|
assert.equal($("<topics-count>").text(), "666");
|
||||||
|
|
||||||
counts.mentioned_message_count = 0;
|
counts.mentioned_message_count = 0;
|
||||||
scheduled_messages.get_count = () => 0;
|
scheduled_messages.get_count = () => 0;
|
||||||
|
message_reminder.get_count = () => 0;
|
||||||
|
|
||||||
left_sidebar_navigation_area.update_dom_with_unread_counts(counts, false);
|
left_sidebar_navigation_area.update_dom_with_unread_counts(counts, false);
|
||||||
left_sidebar_navigation_area.update_starred_count(444, true);
|
left_sidebar_navigation_area.update_starred_count(444, true);
|
||||||
left_sidebar_navigation_area.update_scheduled_messages_row();
|
left_sidebar_navigation_area.update_scheduled_messages_row();
|
||||||
|
left_sidebar_navigation_area.update_reminders_row();
|
||||||
|
|
||||||
assert.ok(!$("<mentioned-count>").visible());
|
assert.ok(!$("<mentioned-count>").visible());
|
||||||
assert.equal($("<mentioned-count>").text(), "");
|
assert.equal($("<mentioned-count>").text(), "");
|
||||||
assert.equal($("<starred-count>").text(), "444");
|
assert.equal($("<starred-count>").text(), "444");
|
||||||
assert.ok(!$(".top_left_scheduled_messages").visible());
|
assert.ok(!$(".top_left_scheduled_messages").visible());
|
||||||
|
assert.ok(!$(".top_left_reminders").visible());
|
||||||
});
|
});
|
||||||
|
@@ -655,6 +655,29 @@ exports.fixtures = {
|
|||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
reminders__add: {
|
||||||
|
type: "reminders",
|
||||||
|
op: "add",
|
||||||
|
reminders: [
|
||||||
|
{
|
||||||
|
reminder_id: 17,
|
||||||
|
type: "private",
|
||||||
|
to: [6],
|
||||||
|
content: "Hello there!",
|
||||||
|
rendered_content: "<p>Hello there!</p>",
|
||||||
|
scheduled_delivery_timestamp: 1681662420,
|
||||||
|
failed: false,
|
||||||
|
reminder_target_message_id: 213,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
reminders__remove: {
|
||||||
|
type: "reminders",
|
||||||
|
op: "remove",
|
||||||
|
reminder_id: 17,
|
||||||
|
},
|
||||||
|
|
||||||
restart: {
|
restart: {
|
||||||
type: "restart",
|
type: "restart",
|
||||||
zulip_version: "9.0-dev-753-gced3e85da9",
|
zulip_version: "9.0-dev-753-gced3e85da9",
|
||||||
|
Reference in New Issue
Block a user