mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: show the scheduled time rounded to the minute and with its date
This commit is contained in:
@@ -16,6 +16,7 @@ import { useCopyLink } from "@/hooks/use-copy-link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Avatar } from "@/components/ui/avatar";
|
||||
import { formatFileSize, cn, MailboxNode, formatDate, formatDateTime, generateUUID } from "@/lib/utils";
|
||||
import { formatScheduledSendAt } from "@/lib/scheduled-send-label";
|
||||
import { buildMoveTargets, resolveMoveOwnerAccountId } from "@/lib/move-targets";
|
||||
import { emailDisplayDate } from "@/lib/email-date";
|
||||
import { TagBadge } from "./tag-badge";
|
||||
@@ -4722,7 +4723,7 @@ export function EmailViewer({
|
||||
<div className="flex items-center gap-2 text-primary">
|
||||
<CalendarClock className="w-4 h-4" />
|
||||
<span className="text-sm font-medium">
|
||||
{t('scheduled_banner', { date: email.scheduledSendAt ? formatDateTime(email.scheduledSendAt, timeFormat) : '' })}
|
||||
{t('scheduled_banner', { date: email.scheduledSendAt ? formatScheduledSendAt(email.scheduledSendAt, timeFormat) : '' })}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import React, { useCallback } from "react";
|
||||
import { cleanPreview, formatDate, formatDateTime } from "@/lib/utils";
|
||||
import { cleanPreview, formatDate } from "@/lib/utils";
|
||||
import { formatScheduledSendAt } from "@/lib/scheduled-send-label";
|
||||
import { Email, ThreadGroup } from "@/lib/jmap/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ListAttachmentChips, useListHasAttachment } from "./attachment-chips";
|
||||
@@ -324,7 +325,7 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
|
||||
const subjectSnippet = email.searchSnippet?.subject ?? null;
|
||||
const previewSnippet = showPreview ? (email.searchSnippet?.preview ?? null) : null;
|
||||
const scheduledSendLabel = email.isScheduled && email.scheduledSendAt
|
||||
? formatDateTime(email.scheduledSendAt, timeFormat)
|
||||
? formatScheduledSendAt(email.scheduledSendAt, timeFormat)
|
||||
: null;
|
||||
|
||||
const tagIds = sortTagIds(getEmailTagIds(email.keywords));
|
||||
@@ -825,7 +826,7 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
|
||||
const subjectSnippet = threadSnippet?.subject ?? null;
|
||||
const previewSnippet = showPreview ? (threadSnippet?.preview ?? null) : null;
|
||||
const scheduledSendLabel = latestEmail.isScheduled && latestEmail.scheduledSendAt
|
||||
? formatDateTime(latestEmail.scheduledSendAt, timeFormat)
|
||||
? formatScheduledSendAt(latestEmail.scheduledSendAt, timeFormat)
|
||||
: null;
|
||||
|
||||
const { selectedMailbox, mailboxes, selectedEmailKeys, toggleEmailSelection, selectRangeEmails, clearSelection, isUnifiedView, unifiedRole } = useEmailStore();
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { formatScheduledSendAt } from '../scheduled-send-label';
|
||||
|
||||
// Local times, so the expectations do not depend on the machine's time zone.
|
||||
const at = (y: number, mo: number, d: number, h: number, mi: number, s = 0) => new Date(y, mo - 1, d, h, mi, s);
|
||||
|
||||
describe('formatScheduledSendAt', () => {
|
||||
const today = at(2026, 10, 11, 18, 0);
|
||||
|
||||
it('shows 08:00 for a release one second before eight', () => {
|
||||
const label = formatScheduledSendAt(at(2026, 10, 11, 19, 59, 59).toISOString(), '24h', today);
|
||||
expect(label).toContain('20:00');
|
||||
expect(label).not.toContain('19:59');
|
||||
});
|
||||
|
||||
it('shows only the time for a release later today', () => {
|
||||
expect(formatScheduledSendAt(at(2026, 10, 11, 21, 30).toISOString(), '24h', today)).toBe('21:30');
|
||||
});
|
||||
|
||||
it('includes the date for a release on another day', () => {
|
||||
const label = formatScheduledSendAt(at(2026, 10, 12, 7, 59, 59).toISOString(), '24h', today);
|
||||
expect(label).toContain('08:00');
|
||||
expect(label).toMatch(/12/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { formatDateTime } from '@/lib/utils';
|
||||
|
||||
/**
|
||||
* The release time of a scheduled message, for lists and banners.
|
||||
*
|
||||
* The server can land a second before the minute the user picked (08:00
|
||||
* showed as 07:59), so the time is rounded to the nearest minute. A message
|
||||
* that is not due today also shows its date; a bare "07:59" for tomorrow
|
||||
* morning read like a time that had already passed.
|
||||
*/
|
||||
export function formatScheduledSendAt(
|
||||
sendAt: string,
|
||||
timeFormat: '12h' | '24h',
|
||||
now: Date = new Date(),
|
||||
): string {
|
||||
const ms = Date.parse(sendAt);
|
||||
if (!Number.isFinite(ms)) return sendAt;
|
||||
const rounded = new Date(Math.round(ms / 60_000) * 60_000);
|
||||
const sameDay = rounded.toDateString() === now.toDateString();
|
||||
return sameDay
|
||||
? formatDateTime(rounded, timeFormat)
|
||||
: formatDateTime(rounded, timeFormat, { weekday: 'short', month: 'short', day: 'numeric' });
|
||||
}
|
||||
Reference in New Issue
Block a user