import React, { useCallback, useEffect, useRef, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { Box, Button, Dialog, Header, Icon, Icons, Overlay, OverlayBackdrop, OverlayCenter, Text, config, } from 'folds'; import { EventStatus, HttpApiEvent, HttpApiEventHandlerMap, MatrixEvent, Room, RoomEvent, RoomEventHandlerMap, } from 'matrix-js-sdk'; import { useMatrixClient } from '../hooks/useMatrixClient'; import { useModalStyle } from '../hooks/useModalStyle'; import { failedForConsent, safeConsentUri } from '../utils/consent'; // After "Later", don't re-open for every background retry; the next send the // user makes after this window explains again. const SNOOZE_MS = 10_000; /** * A homeserver with a consent requirement (e.g. Synapse's user_consent) blocks * sending until its terms are accepted, answering 403 M_CONSENT_NOT_GIVEN with a `consent_uri`. Without * this the message just shows "Failed to send". The SDK emits * HttpApiEvent.NoConsent for every such response; we explain why, link to the * acceptance page, and offer to resend what was blocked once accepted. */ export function ConsentRequiredPrompt() { const mx = useMatrixClient(); const [consentUri, setConsentUri] = useState(); const [open, setOpen] = useState(false); const [opened, setOpened] = useState(false); const [blockedCount, setBlockedCount] = useState(0); const snoozedUntil = useRef(0); const blocked = useRef(new Map()); const modalStyle = useModalStyle(440); const server = mx.getDomain() ?? 'Your homeserver'; useEffect(() => { const onNoConsent: HttpApiEventHandlerMap[HttpApiEvent.NoConsent] = (_message, uri) => { const safe = safeConsentUri(uri); if (safe) setConsentUri(safe); if (Date.now() >= snoozedUntil.current) setOpen(true); }; const onLocalEcho: RoomEventHandlerMap[RoomEvent.LocalEchoUpdated] = (event, room) => { if (failedForConsent(event)) blocked.current.set(event, room); else if (!blocked.current.delete(event)) return; setBlockedCount(blocked.current.size); }; mx.on(HttpApiEvent.NoConsent, onNoConsent); mx.on(RoomEvent.LocalEchoUpdated, onLocalEcho); return () => { mx.removeListener(HttpApiEvent.NoConsent, onNoConsent); mx.removeListener(RoomEvent.LocalEchoUpdated, onLocalEcho); }; }, [mx]); const review = useCallback(() => { if (!consentUri) return; window.open(consentUri, '_blank', 'noopener,noreferrer'); setOpened(true); }, [consentUri]); const retry = useCallback(() => { setOpen(false); setOpened(false); const pending = Array.from(blocked.current.entries()); blocked.current.clear(); setBlockedCount(0); pending.forEach(([event, room]) => { if (event.status === EventStatus.NOT_SENT) mx.resendEvent(event, room); }); }, [mx]); const later = useCallback(() => { snoozedUntil.current = Date.now() + SNOOZE_MS; setOpen(false); }, []); if (!open) return null; return ( }>
Accept the terms to keep chatting
Your message wasn't sent. {server} requires you to review and accept its terms of service before you can send messages. {consentUri ? 'Nothing you sent is lost: accept the terms, then come back here and retry.' : 'Your homeserver didn’t include a link to its terms. Contact its administrator, or accept them in another Matrix app, then retry.'} {consentUri && ( )}
); }