diff --git a/src/app/features/room/ScheduleMessageModal.tsx b/src/app/features/room/ScheduleMessageModal.tsx index 0bdb872b8..2ad8fa487 100644 --- a/src/app/features/room/ScheduleMessageModal.tsx +++ b/src/app/features/room/ScheduleMessageModal.tsx @@ -20,7 +20,12 @@ import { IContent } from 'matrix-js-sdk'; import { useTimestampFormatter } from '../../hooks/useTimestampFormatter'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { stopPropagation } from '../../utils/keyboard'; -import { scheduleMessage } from '../../utils/scheduledMessages'; +import { + describeScheduleError, + MAX_SCHEDULE_AHEAD_MS, + scheduleMessage, + SCHEDULE_TOO_FAR_TEXT, +} from '../../utils/scheduledMessages'; import { useModalStyle } from '../../hooks/useModalStyle'; import { toLocalDate, @@ -121,7 +126,13 @@ export function ScheduleMessageModal({ return; } const diffMs = sendAt.getTime() - Date.now(); - if (diffMs < 60_000) { + // [Gitea #275] Say so as soon as a too-late time is picked, not on submit. + const tooFar = diffMs > MAX_SCHEDULE_AHEAD_MS; + setError((e) => { + if (tooFar) return SCHEDULE_TOO_FAR_TEXT; + return e === SCHEDULE_TOO_FAR_TEXT ? null : e; + }); + if (diffMs < 60_000 || tooFar) { setPreview(null); return; } @@ -149,6 +160,10 @@ export function ScheduleMessageModal({ setError('Scheduled time must be at least 1 minute in the future.'); return; } + if (diffMs > MAX_SCHEDULE_AHEAD_MS) { + setError(SCHEDULE_TOO_FAR_TEXT); + return; + } if (!messageText.trim()) { setError('Please enter a message to schedule.'); return; @@ -162,7 +177,7 @@ export function ScheduleMessageModal({ onScheduled(delayId, sendAt.getTime(), content); onClose(); } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to schedule message.'); + setError(describeScheduleError(err)); setSubmitting(false); } }; @@ -247,6 +262,8 @@ export function ScheduleMessageModal({ setDateValue(e.target.value)} style={pickerInputStyle(color, config)} @@ -294,7 +311,8 @@ export function ScheduleMessageModal({ ) : ( - (dateValue || timeValue) && ( + (dateValue || timeValue) && + error !== SCHEDULE_TOO_FAR_TEXT && ( Must be at least 1 minute in the future diff --git a/src/app/utils/scheduledMessages.test.ts b/src/app/utils/scheduledMessages.test.ts index c3328fd5f..45c2e7fb3 100644 --- a/src/app/utils/scheduledMessages.test.ts +++ b/src/app/utils/scheduledMessages.test.ts @@ -179,3 +179,15 @@ test('groupScheduledByRoom: groups and sorts by sendAt', () => { ); assert.equal(g.get('!2')?.length, 1); }); + +test('[#275] the server limit maps to friendly text; other errors keep theirs', async () => { + const { describeScheduleError, SCHEDULE_TOO_FAR_TEXT, MAX_SCHEDULE_AHEAD_MS } = + await import('./scheduledMessages'); + assert.equal(MAX_SCHEDULE_AHEAD_MS, 86_400_000); + const tooLarge = Object.assign(new Error('The requested delay (172800000ms) exceeds…'), { + errcode: 'ORG.MATRIX.MSC4140_DELAY_TOO_LARGE', + }); + assert.equal(describeScheduleError(tooLarge), SCHEDULE_TOO_FAR_TEXT); + assert.equal(describeScheduleError(new Error('Boom')), 'Boom'); + assert.equal(describeScheduleError('nope'), 'Failed to schedule message.'); +}); diff --git a/src/app/utils/scheduledMessages.ts b/src/app/utils/scheduledMessages.ts index a8acde312..0b76e1ed9 100644 --- a/src/app/utils/scheduledMessages.ts +++ b/src/app/utils/scheduledMessages.ts @@ -1,6 +1,22 @@ import { IContent, MatrixClient, Method } from 'matrix-js-sdk'; import type { ScheduledMessage } from '../state/scheduledMessages'; +/** + * [Gitea #275] Production Synapse caps delayed events at + * `max_event_delay_duration: 24h`; a later time is rejected with + * ORG.MATRIX.MSC4140_DELAY_TOO_LARGE. The dialog enforces the same limit and + * the server's error maps to the same text, in case the limit changes. + */ +export const MAX_SCHEDULE_AHEAD_MS = 24 * 60 * 60 * 1000; +export const SCHEDULE_TOO_FAR_TEXT = 'You can schedule up to 24 hours ahead.'; + +/** A user-facing sentence for a failed schedule. */ +export const describeScheduleError = (err: unknown): string => { + const errcode = (err as { errcode?: unknown } | null)?.errcode; + if (errcode === 'ORG.MATRIX.MSC4140_DELAY_TOO_LARGE') return SCHEDULE_TOO_FAR_TEXT; + return err instanceof Error ? err.message : 'Failed to schedule message.'; +}; + /** * Schedule a message via MSC4140 (delayed messages). * Synapse implementation: pass org.matrix.msc4140.delay on the standard