2023-04-14 21:35:56 +02:00
|
|
|
import $ from "jquery";
|
|
|
|
|
2023-05-16 12:16:24 +02:00
|
|
|
import render_compose_banner from "../templates/compose_banner/compose_banner.hbs";
|
2023-05-01 14:44:41 +02:00
|
|
|
import render_success_message_scheduled_banner from "../templates/compose_banner/success_message_scheduled_banner.hbs";
|
2023-05-10 14:10:47 +02:00
|
|
|
import render_send_later_modal_options from "../templates/send_later_modal_options.hbs";
|
2023-05-01 14:44:41 +02:00
|
|
|
|
2023-04-14 21:35:56 +02:00
|
|
|
import * as channel from "./channel";
|
|
|
|
import * as compose from "./compose";
|
|
|
|
import * as compose_actions from "./compose_actions";
|
2023-04-20 04:34:08 +02:00
|
|
|
import * as compose_banner from "./compose_banner";
|
2023-04-14 21:35:56 +02:00
|
|
|
import * as compose_ui from "./compose_ui";
|
2023-05-02 20:48:50 +02:00
|
|
|
import * as drafts from "./drafts";
|
2023-05-04 16:00:27 +02:00
|
|
|
import {$t} from "./i18n";
|
2023-04-14 21:35:56 +02:00
|
|
|
import * as narrow from "./narrow";
|
|
|
|
import * as people from "./people";
|
|
|
|
import * as popover_menus from "./popover_menus";
|
2023-05-01 14:29:57 +02:00
|
|
|
import * as stream_data from "./stream_data";
|
2023-05-05 17:12:30 +02:00
|
|
|
import * as timerender from "./timerender";
|
2023-04-14 21:35:56 +02:00
|
|
|
|
2023-05-08 17:55:34 +02:00
|
|
|
export const MINIMUM_SCHEDULED_MESSAGE_DELAY_SECONDS = 5 * 60;
|
2023-05-10 14:10:47 +02:00
|
|
|
export const SCHEDULING_MODAL_UPDATE_INTERVAL_IN_MILLISECONDS = 60 * 1000;
|
|
|
|
|
2023-04-14 21:35:56 +02:00
|
|
|
export let scheduled_messages_data = [];
|
|
|
|
|
2023-05-05 17:12:30 +02:00
|
|
|
function compute_send_times(now = new Date()) {
|
|
|
|
const send_times = {};
|
|
|
|
|
|
|
|
const today = new Date(now);
|
|
|
|
const tomorrow = new Date(new Date(now).setDate(now.getDate() + 1));
|
|
|
|
// Find the next Monday by subtracting the current day (0-6) from 8
|
|
|
|
const monday = new Date(new Date(now).setDate(now.getDate() + 8 - now.getDay()));
|
|
|
|
|
|
|
|
// Since setHours returns a timestamp, it's safe to mutate the
|
|
|
|
// original date objects here.
|
|
|
|
//
|
|
|
|
// today at 9am
|
|
|
|
send_times.today_nine_am = today.setHours(9, 0, 0, 0);
|
|
|
|
// today at 4pm
|
|
|
|
send_times.today_four_pm = today.setHours(16, 0, 0, 0);
|
|
|
|
// tomorrow at 9am
|
|
|
|
send_times.tomorrow_nine_am = tomorrow.setHours(9, 0, 0, 0);
|
|
|
|
// tomorrow at 4pm
|
|
|
|
send_times.tomorrow_four_pm = tomorrow.setHours(16, 0, 0, 0);
|
|
|
|
// next Monday at 9am
|
|
|
|
send_times.monday_nine_am = monday.setHours(9, 0, 0, 0);
|
|
|
|
return send_times;
|
|
|
|
}
|
2023-05-04 16:00:27 +02:00
|
|
|
|
2023-05-08 17:55:34 +02:00
|
|
|
export function is_send_later_timestamp_missing_or_expired(
|
|
|
|
timestamp_in_seconds,
|
|
|
|
current_time_in_seconds,
|
|
|
|
) {
|
|
|
|
if (!timestamp_in_seconds) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Determine if the selected timestamp is less than the minimum
|
|
|
|
// scheduled message delay
|
|
|
|
if (timestamp_in_seconds - current_time_in_seconds < MINIMUM_SCHEDULED_MESSAGE_DELAY_SECONDS) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-05-01 14:29:57 +02:00
|
|
|
function sort_scheduled_messages_data() {
|
|
|
|
scheduled_messages_data.sort(
|
|
|
|
(msg1, msg2) => msg1.scheduled_delivery_timestamp - msg2.scheduled_delivery_timestamp,
|
|
|
|
);
|
2023-04-14 21:35:56 +02:00
|
|
|
}
|
|
|
|
|
2023-05-10 09:00:14 +02:00
|
|
|
function hide_scheduled_message_success_compose_banner(scheduled_message_id) {
|
|
|
|
$(
|
|
|
|
`.message_scheduled_success_compose_banner[data-scheduled-message-id=${scheduled_message_id}]`,
|
|
|
|
).hide();
|
|
|
|
}
|
|
|
|
|
2023-05-01 14:29:57 +02:00
|
|
|
export function add_scheduled_messages(scheduled_messages) {
|
|
|
|
scheduled_messages_data.push(...scheduled_messages);
|
|
|
|
sort_scheduled_messages_data();
|
|
|
|
}
|
|
|
|
|
|
|
|
export function remove_scheduled_message(scheduled_message_id) {
|
|
|
|
const msg_index = scheduled_messages_data.findIndex(
|
|
|
|
(msg) => msg.scheduled_message_id === scheduled_message_id,
|
2023-04-14 21:35:56 +02:00
|
|
|
);
|
2023-05-01 14:29:57 +02:00
|
|
|
if (msg_index !== undefined) {
|
|
|
|
scheduled_messages_data.splice(msg_index, 1);
|
2023-05-10 09:00:14 +02:00
|
|
|
hide_scheduled_message_success_compose_banner(scheduled_message_id);
|
2023-05-01 14:29:57 +02:00
|
|
|
}
|
|
|
|
}
|
2023-04-14 21:35:56 +02:00
|
|
|
|
2023-05-01 14:29:57 +02:00
|
|
|
export function update_scheduled_message(scheduled_message) {
|
|
|
|
const msg_index = scheduled_messages_data.findIndex(
|
|
|
|
(msg) => msg.scheduled_message_id === scheduled_message.scheduled_message_id,
|
|
|
|
);
|
|
|
|
|
|
|
|
if (msg_index === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
2023-04-14 21:35:56 +02:00
|
|
|
|
2023-05-01 14:29:57 +02:00
|
|
|
scheduled_messages_data[msg_index] = scheduled_message;
|
|
|
|
sort_scheduled_messages_data();
|
|
|
|
}
|
|
|
|
|
2023-05-04 07:52:19 +02:00
|
|
|
function narrow_via_edit_scheduled_message(compose_args) {
|
|
|
|
if (compose_args.type === "stream") {
|
|
|
|
narrow.activate(
|
|
|
|
[
|
|
|
|
{operator: "stream", operand: compose_args.stream},
|
|
|
|
{operator: "topic", operand: compose_args.topic},
|
|
|
|
],
|
|
|
|
{trigger: "edit scheduled message"},
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
narrow.activate([{operator: "dm", operand: compose_args.private_message_recipient}], {
|
|
|
|
trigger: "edit scheduled message",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function open_scheduled_message_in_compose(scheduled_msg, should_narrow_to_recipient) {
|
2023-05-01 14:29:57 +02:00
|
|
|
let compose_args;
|
2023-04-14 21:35:56 +02:00
|
|
|
if (scheduled_msg.type === "stream") {
|
|
|
|
compose_args = {
|
|
|
|
type: "stream",
|
2023-05-01 14:29:57 +02:00
|
|
|
stream: stream_data.maybe_get_stream_name(scheduled_msg.to),
|
2023-04-14 21:35:56 +02:00
|
|
|
topic: scheduled_msg.topic,
|
|
|
|
content: scheduled_msg.content,
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
const recipient_emails = [];
|
|
|
|
if (scheduled_msg.to) {
|
|
|
|
for (const recipient_id of scheduled_msg.to) {
|
|
|
|
recipient_emails.push(people.get_by_user_id(recipient_id).email);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
compose_args = {
|
|
|
|
type: scheduled_msg.type,
|
|
|
|
private_message_recipient: recipient_emails.join(","),
|
|
|
|
content: scheduled_msg.content,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-05-04 07:52:19 +02:00
|
|
|
if (should_narrow_to_recipient) {
|
|
|
|
narrow_via_edit_scheduled_message(compose_args);
|
2023-04-14 21:35:56 +02:00
|
|
|
}
|
|
|
|
|
2023-04-22 09:32:26 +02:00
|
|
|
compose.clear_compose_box();
|
2023-05-01 14:29:57 +02:00
|
|
|
compose_banner.clear_message_sent_banners(false);
|
2023-04-14 21:35:56 +02:00
|
|
|
compose_actions.start(compose_args.type, compose_args);
|
|
|
|
compose_ui.autosize_textarea($("#compose-textarea"));
|
2023-05-01 19:32:55 +02:00
|
|
|
popover_menus.set_selected_schedule_timestamp(scheduled_msg.scheduled_delivery_timestamp);
|
2023-04-14 21:35:56 +02:00
|
|
|
}
|
|
|
|
|
2023-04-20 04:34:08 +02:00
|
|
|
export function send_request_to_schedule_message(scheduled_message_data, deliver_at) {
|
2023-05-02 11:05:37 +02:00
|
|
|
const $banner_container = $("#compose_banners");
|
2023-05-01 14:44:41 +02:00
|
|
|
const success = function (data) {
|
2023-05-02 20:48:50 +02:00
|
|
|
drafts.draft_model.deleteDraft($("#compose-textarea").data("draft-id"));
|
2023-04-20 04:34:08 +02:00
|
|
|
compose.clear_compose_box();
|
2023-05-01 14:44:41 +02:00
|
|
|
const new_row = render_success_message_scheduled_banner({
|
|
|
|
scheduled_message_id: data.scheduled_message_id,
|
|
|
|
deliver_at,
|
|
|
|
});
|
|
|
|
compose_banner.clear_message_sent_banners();
|
2023-05-02 11:05:37 +02:00
|
|
|
compose_banner.append_compose_banner_to_banner_list(new_row, $banner_container);
|
2023-04-20 04:34:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const error = function (xhr) {
|
|
|
|
const response = channel.xhr_error_message("Error sending message", xhr);
|
|
|
|
compose_ui.hide_compose_spinner();
|
|
|
|
compose_banner.show_error_message(
|
|
|
|
response,
|
|
|
|
compose_banner.CLASSNAMES.generic_compose_error,
|
2023-05-02 11:05:37 +02:00
|
|
|
$banner_container,
|
2023-04-20 04:34:08 +02:00
|
|
|
$("#compose-textarea"),
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
channel.post({
|
|
|
|
url: "/json/scheduled_messages",
|
|
|
|
data: scheduled_message_data,
|
|
|
|
success,
|
|
|
|
error,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-16 12:16:24 +02:00
|
|
|
function show_message_unscheduled_banner(scheduled_delivery_timestamp) {
|
|
|
|
const deliver_at = timerender.get_full_datetime(
|
|
|
|
new Date(scheduled_delivery_timestamp * 1000),
|
|
|
|
"time",
|
|
|
|
);
|
|
|
|
const unscheduled_banner = render_compose_banner({
|
|
|
|
banner_type: compose_banner.WARNING,
|
|
|
|
banner_text: $t(
|
|
|
|
{
|
|
|
|
defaultMessage: "This message is no longer scheduled for {deliver_at}.",
|
|
|
|
},
|
|
|
|
{deliver_at},
|
|
|
|
),
|
|
|
|
classname: compose_banner.CLASSNAMES.unscheduled_message,
|
|
|
|
});
|
|
|
|
compose_banner.append_compose_banner_to_banner_list(unscheduled_banner, $("#compose_banners"));
|
|
|
|
}
|
|
|
|
|
2023-05-04 07:52:19 +02:00
|
|
|
export function edit_scheduled_message(scheduled_message_id, should_narrow_to_recipient = true) {
|
2023-05-01 14:29:57 +02:00
|
|
|
const scheduled_msg = scheduled_messages_data.find(
|
|
|
|
(msg) => msg.scheduled_message_id === scheduled_message_id,
|
|
|
|
);
|
2023-05-16 12:16:24 +02:00
|
|
|
delete_scheduled_message(scheduled_message_id, () => {
|
|
|
|
open_scheduled_message_in_compose(scheduled_msg, should_narrow_to_recipient);
|
|
|
|
show_message_unscheduled_banner(scheduled_msg.scheduled_delivery_timestamp);
|
|
|
|
});
|
2023-05-01 14:29:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function delete_scheduled_message(scheduled_msg_id, success = () => {}) {
|
2023-04-14 21:35:56 +02:00
|
|
|
channel.del({
|
|
|
|
url: "/json/scheduled_messages/" + scheduled_msg_id,
|
2023-05-01 14:29:57 +02:00
|
|
|
success,
|
2023-04-14 21:35:56 +02:00
|
|
|
});
|
|
|
|
}
|
2023-05-01 14:29:57 +02:00
|
|
|
|
2023-05-02 21:08:15 +02:00
|
|
|
export function get_count() {
|
|
|
|
return scheduled_messages_data.length;
|
|
|
|
}
|
|
|
|
|
2023-05-04 16:00:27 +02:00
|
|
|
export function get_filtered_send_opts(date) {
|
2023-05-07 18:32:31 +02:00
|
|
|
const send_times = compute_send_times(date);
|
2023-05-05 17:12:30 +02:00
|
|
|
|
2023-05-04 16:00:27 +02:00
|
|
|
const day = date.getDay(); // Starts with 0 for Sunday.
|
2023-05-05 17:12:30 +02:00
|
|
|
|
|
|
|
const send_later_today = {
|
|
|
|
today_nine_am: {
|
|
|
|
text: $t(
|
|
|
|
{defaultMessage: "Today at {time}"},
|
|
|
|
{
|
|
|
|
time: timerender.get_localized_date_or_time_for_format(
|
|
|
|
send_times.today_nine_am,
|
|
|
|
"time",
|
|
|
|
),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
stamp: send_times.today_nine_am,
|
|
|
|
},
|
|
|
|
today_four_pm: {
|
|
|
|
text: $t(
|
|
|
|
{defaultMessage: "Today at {time}"},
|
|
|
|
{
|
|
|
|
time: timerender.get_localized_date_or_time_for_format(
|
|
|
|
send_times.today_four_pm,
|
|
|
|
"time",
|
|
|
|
),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
stamp: send_times.today_four_pm,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const send_later_tomorrow = {
|
|
|
|
tomorrow_nine_am: {
|
|
|
|
text: $t(
|
|
|
|
{defaultMessage: "Tomorrow at {time}"},
|
|
|
|
{
|
|
|
|
time: timerender.get_localized_date_or_time_for_format(
|
|
|
|
send_times.tomorrow_nine_am,
|
|
|
|
"time",
|
|
|
|
),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
stamp: send_times.tomorrow_nine_am,
|
|
|
|
},
|
|
|
|
tomorrow_four_pm: {
|
|
|
|
text: $t(
|
|
|
|
{defaultMessage: "Tomorrow at {time}"},
|
|
|
|
{
|
|
|
|
time: timerender.get_localized_date_or_time_for_format(
|
|
|
|
send_times.tomorrow_four_pm,
|
|
|
|
"time",
|
|
|
|
),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
stamp: send_times.tomorrow_four_pm,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const send_later_monday = {
|
|
|
|
monday_nine_am: {
|
|
|
|
text: $t(
|
|
|
|
{defaultMessage: "Monday at {time}"},
|
|
|
|
{
|
|
|
|
time: timerender.get_localized_date_or_time_for_format(
|
|
|
|
send_times.monday_nine_am,
|
|
|
|
"time",
|
|
|
|
),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
stamp: send_times.monday_nine_am,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const send_later_custom = {
|
|
|
|
text: $t({defaultMessage: "Custom"}),
|
|
|
|
};
|
|
|
|
|
2023-05-04 16:00:27 +02:00
|
|
|
let possible_send_later_today = {};
|
|
|
|
let possible_send_later_monday = {};
|
2023-05-08 16:17:58 +02:00
|
|
|
|
|
|
|
const minutes_into_day = date.getHours() * 60 + date.getMinutes();
|
|
|
|
// Show Today send options based on time of day
|
|
|
|
if (minutes_into_day < 9 * 60 - MINIMUM_SCHEDULED_MESSAGE_DELAY_SECONDS / 60) {
|
|
|
|
// Allow Today at 9:00am only up to minimum scheduled message delay
|
2023-05-04 16:00:27 +02:00
|
|
|
possible_send_later_today = send_later_today;
|
2023-05-08 23:39:47 +02:00
|
|
|
} else if (minutes_into_day < (12 + 4) * 60 - MINIMUM_SCHEDULED_MESSAGE_DELAY_SECONDS / 60) {
|
2023-05-08 16:17:58 +02:00
|
|
|
// Allow Today at 4:00pm only up to minimum scheduled message delay
|
2023-05-04 16:00:27 +02:00
|
|
|
possible_send_later_today.today_four_pm = send_later_today.today_four_pm;
|
|
|
|
} else {
|
|
|
|
possible_send_later_today = false;
|
|
|
|
}
|
|
|
|
// Show send_later_monday options only on Fridays and Saturdays.
|
|
|
|
if (day >= 5) {
|
|
|
|
possible_send_later_monday = send_later_monday;
|
|
|
|
} else {
|
|
|
|
possible_send_later_monday = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
possible_send_later_today,
|
|
|
|
send_later_tomorrow,
|
|
|
|
possible_send_later_monday,
|
|
|
|
send_later_custom,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-05-02 08:31:48 +02:00
|
|
|
export function initialize(scheduled_messages_params) {
|
|
|
|
scheduled_messages_data = scheduled_messages_params.scheduled_messages;
|
2023-05-01 14:44:41 +02:00
|
|
|
|
|
|
|
$("body").on("click", ".undo_scheduled_message", (e) => {
|
|
|
|
const scheduled_message_id = Number.parseInt(
|
|
|
|
$(e.target)
|
|
|
|
.parents(".message_scheduled_success_compose_banner")
|
|
|
|
.attr("data-scheduled-message-id"),
|
|
|
|
10,
|
|
|
|
);
|
2023-05-04 07:52:19 +02:00
|
|
|
const should_narrow_to_recipient = false;
|
|
|
|
edit_scheduled_message(scheduled_message_id, should_narrow_to_recipient);
|
2023-05-01 14:44:41 +02:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
2023-05-01 14:29:57 +02:00
|
|
|
}
|
2023-05-10 14:10:47 +02:00
|
|
|
|
|
|
|
// This function is exported for unit testing purposes.
|
|
|
|
export function should_update_send_later_options(date) {
|
|
|
|
const current_minute = date.getMinutes();
|
|
|
|
const current_hour = date.getHours();
|
|
|
|
|
|
|
|
if (current_hour === 0 && current_minute === 0) {
|
|
|
|
// We need to rerender the available options at midnight,
|
|
|
|
// since Monday could become in range.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rerender at MINIMUM_SCHEDULED_MESSAGE_DELAY_SECONDS before the
|
|
|
|
// hour, so we don't offer a 4:00PM send time at 3:59 PM.
|
|
|
|
return current_minute === 60 - MINIMUM_SCHEDULED_MESSAGE_DELAY_SECONDS / 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function update_send_later_options() {
|
|
|
|
const now = new Date();
|
|
|
|
if (should_update_send_later_options(now)) {
|
|
|
|
const filtered_send_opts = get_filtered_send_opts(now);
|
2023-05-15 14:44:03 +02:00
|
|
|
$("#send_later_options").replaceWith(render_send_later_modal_options(filtered_send_opts));
|
2023-05-10 14:10:47 +02:00
|
|
|
}
|
|
|
|
}
|