mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-03 08:06:59 +00:00
129 lines
5.7 KiB
TypeScript
129 lines
5.7 KiB
TypeScript
import { NetworkError, RequestTimeoutError } from '../api/jmap-client';
|
|
import { useNetworkStore } from '../stores/network-store';
|
|
import {
|
|
NoLongerScheduledError, RecipientsRefusedError, ScheduleTooLateError, SubmissionQuotaError, formatRefusedRecipients,
|
|
type RefusedRecipient,
|
|
} from '../api/jmap-result';
|
|
import type { MessageParams } from '../i18n/format';
|
|
|
|
type Translate = (key: string, fallback?: string, params?: MessageParams) => string;
|
|
|
|
/**
|
|
* What to tell the user about a send (or reschedule) that failed (webmail
|
|
* send-failure-message). Most errors carry the server's own description;
|
|
* these are the ones it would misrepresent or leave unexplained:
|
|
* - a full submission quota on Stalwart: it asks the user to "delete some",
|
|
* but the app has already removed every record it may, so only an
|
|
* administrator can help. Another server's overQuota may stand for any
|
|
* sending limit, so its own description goes with a generic sentence;
|
|
* - a timeout, which is not a clean failure: the message may have gone out
|
|
* with only the answer lost, so point at Sent rather than invite a re-send
|
|
* (#702);
|
|
* - a hold beyond the server's limit;
|
|
* - "Send now" or a reschedule that found the message gone out, or no longer
|
|
* scheduled, while it made room for the replacement;
|
|
* - every recipient refused: the server took the submission, so nothing
|
|
* else would say that the message went nowhere. Its reply per address
|
|
* goes with it.
|
|
*/
|
|
export function sendFailureMessage(error: unknown, t: Translate): string {
|
|
if (error instanceof SubmissionQuotaError) {
|
|
return error.countsRecords
|
|
? t(
|
|
'email_composer.submission_quota',
|
|
'The server has too many sent-message records for this account. Bulwark removed the ones it could; if this keeps happening, ask your administrator to raise the email submission limit.',
|
|
)
|
|
: t(
|
|
'email_composer.submission_limit',
|
|
'The server refused to send the message because a limit was reached: {reason}',
|
|
{ reason: error.description || error.type },
|
|
);
|
|
}
|
|
if (error instanceof NoLongerScheduledError) {
|
|
return error.sent
|
|
? t('scheduled.already_sent', 'This message was already sent.')
|
|
: t('scheduled.no_longer_scheduled', 'This message is no longer scheduled.');
|
|
}
|
|
if (error instanceof RequestTimeoutError) {
|
|
return t(
|
|
'email_composer.send_timeout_body',
|
|
'The message may already have gone out. Check your Sent folder before sending it again.',
|
|
);
|
|
}
|
|
if (error instanceof ScheduleTooLateError) {
|
|
return t('email_composer.schedule_too_late_body', 'That is later than this server allows. Pick an earlier time.');
|
|
}
|
|
if (error instanceof RecipientsRefusedError) {
|
|
return t('email_composer.recipients_refused', 'Not sent: the server refused {recipients}.', {
|
|
recipients: formatRefusedRecipients(error.recipients),
|
|
});
|
|
}
|
|
return error instanceof Error && error.message
|
|
? error.message
|
|
: t('notifications.error_sending', 'Failed to send email');
|
|
}
|
|
|
|
/**
|
|
* sendFailureMessage for a message the user is writing (composer, quick
|
|
* reply): a send that never reached the server because the device is
|
|
* offline says so, and that the text is still there, instead of "Network
|
|
* request failed" (webmail email-composer.tsx send_offline). The server is
|
|
* unreachable when the transport failed (the client's NetworkError, or a
|
|
* bare fetch TypeError) or the device reports no connection.
|
|
*/
|
|
export function composerSendFailureMessage(error: unknown, t: Translate): string {
|
|
const offline = error instanceof NetworkError
|
|
|| error instanceof TypeError
|
|
|| !useNetworkStore.getState().online;
|
|
if (offline && !(error instanceof RequestTimeoutError)) {
|
|
return t(
|
|
'email_composer.send_offline',
|
|
'You seem to be offline. The message is still here, send it again once you are back online.',
|
|
);
|
|
}
|
|
return sendFailureMessage(error, t);
|
|
}
|
|
|
|
/**
|
|
* The title and message for a "Send now" or reschedule that failed: most
|
|
* failures get `failTitle` and sendFailureMessage (webmail
|
|
* rescheduleFailureToast). When the server refused recipients of the
|
|
* replacement that the original had been taken for, the original was kept
|
|
* for all of its recipients and still goes out at its old time,
|
|
* `scheduledAt` (formatted) when known. That leads, and nothing says "not
|
|
* sent": it would invite sending the message a second time.
|
|
*/
|
|
export function rescheduleFailureNotice(
|
|
error: unknown,
|
|
t: Translate,
|
|
failTitle: string,
|
|
scheduledAt?: string,
|
|
): { title: string; message: string } {
|
|
if (!(error instanceof RecipientsRefusedError)) return { title: failTitle, message: sendFailureMessage(error, t) };
|
|
return {
|
|
title: scheduledAt
|
|
? t('email_viewer.still_scheduled_at', 'The message is still scheduled for {date}.', { date: scheduledAt })
|
|
: t('email_viewer.still_scheduled', 'The message is still scheduled.'),
|
|
message: t('email_viewer.reschedule_recipients_refused', 'Nothing was changed: the server refused {recipients}.', {
|
|
recipients: formatRefusedRecipients(error.recipients),
|
|
}),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* The warning after a send that the server took for some recipients only:
|
|
* nothing else, not even a bounce, would tell the user who never gets it.
|
|
* The addresses and replies go in the message, which a toast shows on more
|
|
* lines than its title.
|
|
*/
|
|
export function partlyRefusedWarning(recipients: RefusedRecipient[], t: Translate): { title: string; message: string } {
|
|
return {
|
|
title: t('email_composer.recipients_partly_refused_title', 'Not sent to everyone'),
|
|
message: t(
|
|
'email_composer.recipients_partly_refused',
|
|
'The server refused {recipients}. The other recipients get the message.',
|
|
{ recipients: formatRefusedRecipients(recipients) },
|
|
),
|
|
};
|
|
}
|