import { useEffect, useRef, useState } from "react"; import { useT } from "../../i18n"; import type { MessageKey } from "../../i18n/en"; import { formatClock, getDateLocale } from "../../lib/timeFormat"; const MORNING = 8; // the hour "tomorrow", "this weekend" and "next week" start at function at(day: Date, hour: number): Date { const d = new Date(day); d.setHours(hour, 0, 0, 0); return d; } function addDays(day: Date, n: number): Date { const d = new Date(day); d.setDate(d.getDate() + n); return d; } /** The quick choices, in the order shown. Ones already past are left out. */ export function snoozePresets(now: Date): { label: MessageKey; when: Date }[] { const out: { label: MessageKey; when: Date }[] = []; // Three hours on, rounded up to the hour, while that is still this evening. const later = new Date(now); later.setHours(later.getHours() + 3, 0, 0, 0); if (later.getDate() === now.getDate() && later.getHours() <= 21) { out.push({ label: "snooze.laterToday", when: later }); } out.push({ label: "snooze.tomorrow", when: at(addDays(now, 1), MORNING) }); const day = now.getDay(); // 0 = Sunday if (day >= 1 && day <= 4) { out.push({ label: "snooze.weekend", when: at(addDays(now, 6 - day), MORNING) }); } // The coming Monday — or, on a Sunday, the one after (tomorrow is offered already). const toMonday = ((8 - day) % 7) || 7; out.push({ label: "snooze.nextWeek", when: at(addDays(now, toMonday === 1 ? 8 : toMonday), MORNING) }); return out; } function whenLabel(d: Date, now: Date): string { const sameDay = d.toDateString() === now.toDateString(); const day = sameDay ? "" : d.toLocaleDateString(getDateLocale(), { weekday: "short", day: "numeric", month: "short" }) + ", "; return day + formatClock(d); } // "YYYY-MM-DDTHH:MM" in local time, for . function localInput(d: Date): string { const pad = (n: number) => String(n).padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; } /** Popover: when should this conversation come back? */ export function SnoozeMenu({ onPick, onClose, align = "left", }: { onPick: (when: Date) => void; onClose: () => void; align?: "left" | "right"; }) { const t = useT(); const ref = useRef(null); const now = new Date(); const [custom, setCustom] = useState(false); const [value, setValue] = useState(() => localInput(at(addDays(now, 1), MORNING))); const picked = new Date(value); const valid = !Number.isNaN(picked.getTime()) && picked.getTime() > Date.now(); useEffect(() => { const onDown = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) onClose(); }; const onKey = (e: KeyboardEvent) => e.key === "Escape" && onClose(); document.addEventListener("mousedown", onDown); document.addEventListener("keydown", onKey); return () => { document.removeEventListener("mousedown", onDown); document.removeEventListener("keydown", onKey); }; }, [onClose]); return (
{t("snooze.until")}
{snoozePresets(now).map((p) => ( ))}
{custom ? (
setValue(e.target.value)} autoFocus />
) : ( )}
); }