feat(rooms): Disappearing Messages (MSC1763 m.room.retention)
B1 of the Matrix protocol-gaps roadmap, gate-green (688 tests): - StateEvent.RoomRetention + a shared utils/retention.ts (presets, isExpired, getRoomRetentionMs) with tests. - RoomRetention settings control (PL-gated preset buttons Off/1d/1w/1m) in Room Settings → General → Message Retention. - Timeline hides events past the room's max_lifetime (gated behind Show Hidden Events, like redactions) — messages visually disappear, losslessly. - Opt-in setting enforceRetentionLocally (default OFF) + a headless RetentionSweeper that permanently redacts the user's OWN expired messages (own-only, loaded-timeline scope, dedupe + retry). Nothing auto-deletes unless the user opts in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,8 @@ import { roomIdToActiveThreadIdAtomFamily } from '../../state/room/thread';
|
||||
import { ThreadSummary } from './thread/ThreadSummary';
|
||||
import { usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
||||
import { GetContentCallback, MessageEvent, StateEvent } from '../../../types/matrix/room';
|
||||
import { useStateEvent } from '../../hooks/useStateEvent';
|
||||
import { RetentionContent, isExpired } from '../../utils/retention';
|
||||
import { useKeyDown } from '../../hooks/useKeyDown';
|
||||
import { useDocumentFocusChange } from '../../hooks/useDocumentFocusChange';
|
||||
import { RenderMessageContent } from '../../components/RenderMessageContent';
|
||||
@@ -468,6 +470,11 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
const [encUrlPreview] = useSetting(settingsAtom, 'encUrlPreview');
|
||||
const showUrlPreview = room.hasEncryptionStateEvent() ? encUrlPreview : urlPreview;
|
||||
const [showHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');
|
||||
// MSC1763 retention: messages older than this window are hidden from the
|
||||
// timeline (unless "show hidden events" is on). Reactive so a policy change
|
||||
// re-renders. `undefined` = no policy.
|
||||
const retentionEvent = useStateEvent(room, StateEvent.RoomRetention);
|
||||
const retentionMs = retentionEvent?.getContent<RetentionContent>().max_lifetime;
|
||||
const [showDeveloperTools] = useSetting(settingsAtom, 'developerTools');
|
||||
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
||||
|
||||
@@ -2043,6 +2050,11 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
||||
if (eventSender && ignoredUsersSet.has(eventSender)) {
|
||||
return null;
|
||||
}
|
||||
// MSC1763: hide messages past the room's retention window (disappearing
|
||||
// messages). Power users can still inspect via "show hidden events".
|
||||
if (retentionMs && !showHiddenEvents && isExpired(mEvent.getTs(), retentionMs, Date.now())) {
|
||||
return null;
|
||||
}
|
||||
if (mEvent.isRedacted() && !showHiddenEvents) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
const t = mEvent.getType();
|
||||
|
||||
Reference in New Issue
Block a user