mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 00:14:18 +00:00
fix: count a pop-out's undo send from the delay the user picked
This commit is contained in:
@@ -7,6 +7,7 @@ import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useEmailStore } from '@/stores/email-store';
|
||||
import { useIdentityStore } from '@/stores/identity-store';
|
||||
import { usePluginStore } from '@/stores/plugin-store';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
import { emailHooks } from '@/lib/plugin-hooks';
|
||||
|
||||
// The composer's window: when it tells the opening window to let go, and what
|
||||
@@ -614,6 +615,15 @@ describe('a send held for undo', () => {
|
||||
expect(sent?.notice.releasesAt).toBe(sendAt);
|
||||
});
|
||||
|
||||
// The server's time runs past the delay: "Sending in 6 seconds" for 5 s.
|
||||
it('never counts from more than the delay the user picked', async () => {
|
||||
const before = useSettingsStore.getState().sendDelaySeconds;
|
||||
useSettingsStore.setState({ sendDelaySeconds: 5 } as never);
|
||||
await sendFromWindow(5.8);
|
||||
expect(screen.getByTestId('popout-sending')).toHaveAttribute('data-seconds', '5');
|
||||
useSettingsStore.setState({ sendDelaySeconds: before } as never);
|
||||
});
|
||||
|
||||
// compose-24: the window sat with a greyed Send until the server answered.
|
||||
it('says "Sending…" as soon as Send goes through, before the server answers', async () => {
|
||||
let answer!: () => void;
|
||||
|
||||
@@ -10,6 +10,8 @@ import { useAuthStore } from "@/stores/auth-store";
|
||||
import { useEmailStore } from "@/stores/email-store";
|
||||
import { usePluginStore } from "@/stores/plugin-store";
|
||||
import { toast } from "@/stores/toast-store";
|
||||
import { useSettingsStore } from "@/stores/settings-store";
|
||||
import { secondsUntilSend } from "@/lib/outgoing/send-progress-toast";
|
||||
import { formatRejectedRecipients } from "@/lib/jmap/client";
|
||||
import { debug } from "@/lib/debug";
|
||||
import { composerSendOptions } from "@/lib/outgoing/composer-send-options";
|
||||
@@ -68,9 +70,9 @@ interface ReopenTarget {
|
||||
}
|
||||
|
||||
/** "Sending…" or "Sent", where the composer was, with the send icon. */
|
||||
function SendStatus({ text, children }: { text: string; children?: React.ReactNode }) {
|
||||
function SendStatus({ text, seconds, children }: { text: string; seconds?: number; children?: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-4 p-6 text-center" data-testid="popout-sending">
|
||||
<div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-4 p-6 text-center" data-testid="popout-sending" data-seconds={seconds}>
|
||||
<Send className="h-6 w-6 text-muted-foreground" aria-hidden="true" />
|
||||
<p role="status" className="text-sm text-foreground">{text}</p>
|
||||
{children}
|
||||
@@ -134,11 +136,12 @@ function SendingWithUndo({ held, onUndone, onGone }: {
|
||||
onUndone(held.isSmime ? null : restored);
|
||||
};
|
||||
|
||||
const seconds = Math.max(1, Math.ceil((sendAt - now) / 1000));
|
||||
// As the main window's toast counts: never past the delay the user picked.
|
||||
const seconds = secondsUntilSend(sendAt, now, useSettingsStore.getState().sendDelaySeconds);
|
||||
// Once Undo is over, "Sent" for the moment before the window goes, as the
|
||||
// main window's toast says it.
|
||||
return (
|
||||
<SendStatus text={over && !busy ? tSend("sent") : tViewer("sending_in", { seconds })}>
|
||||
<SendStatus text={over && !busy ? tSend("sent") : tViewer("sending_in", { seconds })} seconds={over ? undefined : seconds}>
|
||||
{!over && (
|
||||
<Button onClick={() => void undo()} disabled={busy} data-testid="popout-undo-send">
|
||||
{tViewer("undo_send")}
|
||||
|
||||
@@ -71,13 +71,20 @@ export interface UndoCountdownOptions {
|
||||
* word that the message went. Stops by itself once the toast is gone (Undo,
|
||||
* Send now, closed). Returns a stop for the caller.
|
||||
*/
|
||||
/**
|
||||
* Whole seconds until a held send goes, at least 1 and never more than the
|
||||
* delay the user picked: the server's time runs a moment past it, which
|
||||
* showed "Sending in 6 seconds" for a 5 s delay.
|
||||
*/
|
||||
export function secondsUntilSend(sendAt: number, now: number, maxSeconds?: number): number {
|
||||
const left = Math.max(1, Math.ceil((sendAt - now) / 1000));
|
||||
return maxSeconds && maxSeconds > 0 ? Math.min(left, maxSeconds) : left;
|
||||
}
|
||||
|
||||
export function runUndoSendCountdown(id: string, options: UndoCountdownOptions): () => void {
|
||||
const now = options.now ?? Date.now;
|
||||
const present = () => useToastStore.getState().toasts.some((t) => t.id === id);
|
||||
const secondsLeft = () => {
|
||||
const left = Math.max(1, Math.ceil((options.sendAt - now()) / 1000));
|
||||
return options.maxSeconds && options.maxSeconds > 0 ? Math.min(left, options.maxSeconds) : left;
|
||||
};
|
||||
const secondsLeft = () => secondsUntilSend(options.sendAt, now(), options.maxSeconds);
|
||||
let shown = -1;
|
||||
const tick = () => {
|
||||
if (!present()) return stop();
|
||||
|
||||
Reference in New Issue
Block a user