mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-02 08:07:11 +00:00
fix: stop the date and time pickers offering times past the server's hold limit
This commit is contained in:
@@ -409,6 +409,17 @@ describe('JMAPClient', () => {
|
||||
expect(client.undoSendHold(30)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('gives the latest time a send can be held until, for the date pickers', async () => {
|
||||
global.fetch = mockFetch([{ status: 200, json: withSubmission(2_592_000, true) }]) as any;
|
||||
await client.connect('https://mail.example.com', 'user', 'pass');
|
||||
const now = Date.UTC(2026, 8, 24, 12, 0, 0);
|
||||
expect(client.latestHoldDate(undefined, now)?.toISOString()).toBe('2026-10-01T12:00:00.000Z');
|
||||
|
||||
global.fetch = mockFetch([{ status: 200, json: MOCK_SESSION }]) as any;
|
||||
await client.connect('https://mail.example.com', 'user', 'pass');
|
||||
expect(client.latestHoldDate(undefined, now)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('parses only hold-limit rejections', () => {
|
||||
expect(parseHoldLimit('Server rejected MAIL-FROM: 501 5.5.4 Requested hold time exceeds maximum of 604800 seconds.')).toBe(604_800);
|
||||
expect(parseHoldLimit('Server rejected MAIL-FROM: 550 nope')).toBeNull();
|
||||
|
||||
@@ -1087,6 +1087,15 @@ export class JMAPClient {
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* The latest time a send from `accountId` can be held until, or undefined
|
||||
* when it can't be held. Caps the "Pick date & time" pickers.
|
||||
*/
|
||||
latestHoldDate(accountId?: string, now = Date.now()): Date | undefined {
|
||||
if (!this.hasDelayedSend(accountId)) return undefined;
|
||||
return new Date(now + this.getMaxDelayedSend(accountId) * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember the hold limit a rejected submission named, so the pickers only
|
||||
* offer times the server accepts for the rest of the session.
|
||||
|
||||
@@ -1895,7 +1895,9 @@ export default function ComposeScreen({ route, navigation }: Props) {
|
||||
}, [t, scheduleSheetOpen]);
|
||||
|
||||
const startCustomPicker = () => {
|
||||
customDraftRef.current = new Date(Date.now() + 3600 * 1000);
|
||||
// An hour ahead, or the latest time the server can hold it if sooner.
|
||||
const latest = jmapClient.latestHoldDate()?.getTime() ?? Infinity;
|
||||
customDraftRef.current = new Date(Math.min(Date.now() + 3600 * 1000, latest));
|
||||
setScheduleSheetOpen(false);
|
||||
setCustomStage(Platform.OS === 'ios' ? 'datetime' : 'date');
|
||||
};
|
||||
@@ -2516,6 +2518,7 @@ export default function ComposeScreen({ route, navigation }: Props) {
|
||||
mode="datetime"
|
||||
display="spinner"
|
||||
minimumDate={new Date()}
|
||||
maximumDate={jmapClient.latestHoldDate()}
|
||||
onChange={onCustomPickerChange}
|
||||
/>
|
||||
<View style={styles.modalActions}>
|
||||
@@ -2542,6 +2545,7 @@ export default function ComposeScreen({ route, navigation }: Props) {
|
||||
mode={customStage === 'time' ? 'time' : 'date'}
|
||||
display="default"
|
||||
minimumDate={customStage === 'date' ? new Date() : undefined}
|
||||
maximumDate={customStage === 'date' ? jmapClient.latestHoldDate() : undefined}
|
||||
onChange={onCustomPickerChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -201,7 +201,9 @@ export default function ScheduledScreen({ navigation }: Props) {
|
||||
}, [t, rescheduleFor]);
|
||||
|
||||
const startCustomPicker = () => {
|
||||
customDraftRef.current = new Date(Date.now() + 3600 * 1000);
|
||||
// An hour ahead, or the latest time the server can hold it if sooner.
|
||||
const latest = jmapClient.latestHoldDate(rescheduleFor?.accountId)?.getTime() ?? Infinity;
|
||||
customDraftRef.current = new Date(Math.min(Date.now() + 3600 * 1000, latest));
|
||||
setCustomStage(Platform.OS === 'ios' ? 'datetime' : 'date');
|
||||
};
|
||||
|
||||
@@ -375,6 +377,7 @@ export default function ScheduledScreen({ navigation }: Props) {
|
||||
mode="datetime"
|
||||
display="spinner"
|
||||
minimumDate={new Date()}
|
||||
maximumDate={jmapClient.latestHoldDate(rescheduleFor?.accountId)}
|
||||
onChange={onCustomPickerChange}
|
||||
/>
|
||||
<View style={styles.cardActions}>
|
||||
@@ -396,6 +399,7 @@ export default function ScheduledScreen({ navigation }: Props) {
|
||||
mode={customStage === 'time' ? 'time' : 'date'}
|
||||
display="default"
|
||||
minimumDate={customStage === 'date' ? new Date() : undefined}
|
||||
maximumDate={customStage === 'date' ? jmapClient.latestHoldDate(rescheduleFor?.accountId) : undefined}
|
||||
onChange={onCustomPickerChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user