11import type { KeyboardEventHandler , MouseEvent , RefObject } from 'react' ;
22import { forwardRef , useCallback , useEffect , useRef , useState , useMemo } from 'react' ;
3- import { useAtom , useAtomValue } from 'jotai' ;
3+ import { useAtom , useAtomValue , useSetAtom } from 'jotai' ;
4+
45import { isKeyHotkey } from 'is-hotkey' ;
56import type {
67 IContent ,
@@ -10,6 +11,7 @@ import type {
1011 RoomMessageEventContent ,
1112 StickerEventContent ,
1213} from '$types/matrix-sdk' ;
14+ import { MatrixError } from '$types/matrix-sdk' ;
1315import { EventType , MsgType , RelationType } from '$types/matrix-sdk' ;
1416import { ReactEditor } from 'slate-react' ;
1517import { Editor , Point , Range , Transforms } from 'slate' ;
@@ -108,14 +110,15 @@ import {
108110 delayedEventsSupportedAtom ,
109111 roomIdToScheduledTimeAtomFamily ,
110112 roomIdToEditingScheduledDelayIdAtomFamily ,
113+ serverMaxDelayMsAtom ,
111114} from '$state/scheduledMessages' ;
112115import {
113116 sendDelayedMessage ,
114117 sendDelayedMessageE2EE ,
115118 computeDelayMs ,
116119 cancelDelayedEvent ,
117120} from '$utils/delayedEvents' ;
118- import { timeHourMinute , timeDayMonthYear } from '$utils/time' ;
121+ import { timeHourMinute , timeDayMonthYear , daysToMs } from '$utils/time' ;
119122import { stopPropagation } from '$utils/keyboard' ;
120123
121124import { usePowerLevelsContext } from '$hooks/usePowerLevels' ;
@@ -128,6 +131,7 @@ import {
128131} from '$hooks/usePerMessageProfile' ;
129132import { Microphone , Stop } from '@phosphor-icons/react' ;
130133import { getSupportedAudioExtension } from '$plugins/voice-recorder-kit/supportedCodec' ;
134+ import { ErrorCode } from '../../cs-errorcode' ;
131135import { sanitizeText } from '$utils/sanitize' ;
132136import { PKitCommandMessageHandler } from '$plugins/pluralkit-handler/PKitCommandMessageHandler' ;
133137import { PKitProxyMessageHandler } from '$plugins/pluralkit-handler/PKitProxyMessageHandler' ;
@@ -383,6 +387,8 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
383387 const [ pollCreatorOpen , setPollCreatorOpen ] = useState ( false ) ;
384388 const [ silentReply , setSilentReply ] = useState ( ! mentionInReplies ) ;
385389 const [ hour24Clock ] = useSetting ( settingsAtom , 'hour24Clock' ) ;
390+ const setServerMaxDelayMs = useSetAtom ( serverMaxDelayMsAtom ) ;
391+ const [ sendError , setSendError ] = useState < string | undefined > ( ) ;
386392 const isEncrypted = room . hasEncryptionStateEvent ( ) ;
387393
388394 useElementSizeObserver (
@@ -961,12 +967,28 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
961967 } else {
962968 await sendDelayedMessage ( mx , roomId , content as RoomMessageEventContent , delayMs ) ;
963969 }
970+ setSendError ( undefined ) ;
964971 invalidate ( ) ;
965972 setEditingScheduledDelayId ( null ) ;
966973 setScheduledTime ( null ) ;
967974 resetInput ( ) ;
968- } catch {
969- // Network/server error — leave editor and scheduled state intact for retry
975+ } catch ( e : unknown ) {
976+ if (
977+ e instanceof MatrixError &&
978+ ( e . errcode === ErrorCode . M_MAX_DELAY_EXCEEDED ||
979+ e . data ?. [ 'org.matrix.msc4140.errcode' ] === 'M_MAX_DELAY_EXCEEDED' )
980+ ) {
981+ const maxDelay =
982+ ( e . data as { max_delay ?: number } ) ?. max_delay ??
983+ e . data ?. [ 'org.matrix.msc4140.max_delay' ] ;
984+ if ( typeof maxDelay === 'number' ) setServerMaxDelayMs ( maxDelay ) ;
985+ const maxDelayDays = maxDelay / daysToMs ( 1 ) ;
986+ setSendError (
987+ `Scheduled time exceeds the maximum delay allowed by this server. Please choose an earlier time. The Maximum Delay is of ${ maxDelayDays } day${ maxDelayDays > 1 ? 's' : '' } .`
988+ ) ;
989+ } else {
990+ setSendError ( 'Failed to schedule message. Please try again.' ) ;
991+ }
970992 }
971993 } else if ( editingScheduledDelayId ) {
972994 try {
@@ -1057,6 +1079,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
10571079 isEncrypted ,
10581080 setEditingScheduledDelayId ,
10591081 setScheduledTime ,
1082+ setServerMaxDelayMs ,
10601083 ] ) ;
10611084
10621085 const handleKeyDown : KeyboardEventHandler = useCallback (
@@ -1387,6 +1410,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
13871410 onClick = { ( ) => {
13881411 setScheduledTime ( null ) ;
13891412 setEditingScheduledDelayId ( null ) ;
1413+ setSendError ( undefined ) ;
13901414 } }
13911415 variant = "SurfaceVariant"
13921416 size = "300"
@@ -1405,6 +1429,19 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
14051429 </ Box >
14061430 </ div >
14071431 ) }
1432+ { sendError && (
1433+ < div >
1434+ < Box
1435+ alignItems = "Center"
1436+ gap = "300"
1437+ style = { { padding : `${ config . space . S200 } ${ config . space . S300 } 0` } }
1438+ >
1439+ < Text style = { { color : color . Critical . Main } } size = "T300" >
1440+ { sendError }
1441+ </ Text >
1442+ </ Box >
1443+ </ div >
1444+ ) }
14081445 { replyDraft && ( ! threadRootId || replyDraft . body ) && (
14091446 < div >
14101447 < Box
@@ -1738,6 +1775,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
17381775 onSubmit = { ( date ) => {
17391776 setScheduledTime ( date ) ;
17401777 setShowSchedulePicker ( false ) ;
1778+ setSendError ( undefined ) ;
17411779 } }
17421780 />
17431781 ) }
0 commit comments