Skip to content
This repository was archived by the owner on Dec 7, 2024. It is now read-only.

Commit d7edb69

Browse files
committed
Allow room moderators to view redacted event content
Implements MSC2815 Signed-off-by: Tulir Asokan <[email protected]>
1 parent 579a166 commit d7edb69

File tree

4 files changed

+52
-1
lines changed

4 files changed

+52
-1
lines changed

res/css/views/rooms/_EventTile.scss

+9
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,15 @@ $left-gutter: 64px;
458458
opacity: 1;
459459
}
460460

461+
.mx_EventTile_e2eIcon_viewingRedacted {
462+
&::after {
463+
mask-image: url('$(res)/img/feather-customised/trash.custom.svg');
464+
background-color: $muted-fg-color;
465+
}
466+
467+
opacity: 1;
468+
}
469+
461470
/* Various markdown overrides */
462471

463472
.mx_EventTile_body {

src/components/views/context_menus/MessageContextMenu.tsx

+35-1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ interface IProps extends IPosition {
8383

8484
interface IState {
8585
canRedact: boolean;
86+
canViewRedacted: boolean;
8687
canPin: boolean;
8788
}
8889

@@ -92,6 +93,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
9293

9394
state = {
9495
canRedact: false,
96+
canViewRedacted: false,
9597
canPin: false,
9698
};
9799

@@ -118,11 +120,15 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
118120
&& this.props.mxEvent.getType() !== EventType.RoomServerAcl
119121
&& this.props.mxEvent.getType() !== EventType.RoomEncryption;
120122
let canPin = room.currentState.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli);
123+
// TODO check for server support first
124+
// TODO allow if isSynapseAdmin too
125+
const canViewRedacted = room.currentState.hasSufficientPowerLevelFor(
126+
'redact', room.currentState.getMember(cli.credentials.userId)?.powerLevel ?? 0);
121127

122128
// HACK: Intentionally say we can't pin if the user doesn't want to use the functionality
123129
if (!SettingsStore.getValue("feature_pinning")) canPin = false;
124130

125-
this.setState({ canRedact, canPin });
131+
this.setState({ canRedact, canViewRedacted, canPin });
126132
};
127133

128134
private isPinned(): boolean {
@@ -152,6 +158,22 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
152158
this.closeMenu();
153159
};
154160

161+
private onViewRedactedClick = (): void => {
162+
MatrixClientPeg.get().unstableFetchRedactedRoomEventContent(
163+
this.props.mxEvent.getRoomId(),
164+
this.props.mxEvent.getId(),
165+
).then(
166+
unredactedEvt => {
167+
console.log("Fetched redacted event content:", unredactedEvt);
168+
this.closeMenu();
169+
this.props.mxEvent.showRedactedContent(unredactedEvt);
170+
},
171+
err => {
172+
console.error("Failed to fetch redacted event content:", err);
173+
}
174+
);
175+
};
176+
155177
private onReportEventClick = (): void => {
156178
dis.dispatch<OpenReportEventDialogPayload>({
157179
action: Action.OpenReportEventDialog,
@@ -284,6 +306,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
284306
let endPollButton: JSX.Element;
285307
let resendReactionsButton: JSX.Element;
286308
let redactButton: JSX.Element;
309+
let viewRedactedButton: JSX.Element;
287310
let forwardButton: JSX.Element;
288311
let pinButton: JSX.Element;
289312
let unhidePreviewButton: JSX.Element;
@@ -306,6 +329,16 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
306329
}
307330
}
308331

332+
if (mxEvent.isRedacted() && this.state.canViewRedacted) {
333+
viewRedactedButton = (
334+
<IconizedContextMenuOption
335+
iconClassName=""
336+
label={_t('View content')}
337+
onClick={this.onViewRedactedClick}
338+
/>
339+
)
340+
}
341+
309342
if (isSent && this.state.canRedact) {
310343
redactButton = (
311344
<IconizedContextMenuOption
@@ -489,6 +522,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
489522
{ externalURLButton }
490523
{ unhidePreviewButton }
491524
{ viewSourceButton }
525+
{ viewRedactedButton }
492526
{ resendReactionsButton }
493527
{ collapseReplyChain }
494528
</IconizedContextMenuOptionList>

src/components/views/rooms/EventTile.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,11 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
852852
private renderE2EPadlock() {
853853
const ev = this.props.mxEvent;
854854

855+
if (ev.viewingRedactedContent) {
856+
// Not actually an e2ee thing, but we reuse the same slot in the UI.
857+
return <E2ePadlock title={_t("This is a deleted message")} icon={E2ePadlockIcon.Redacted} />
858+
}
859+
855860
// event could not be decrypted
856861
if (ev.getContent().msgtype === 'm.bad.encrypted') {
857862
return <E2ePadlockUndecryptable />;
@@ -1513,6 +1518,7 @@ function E2ePadlockUnauthenticated(props) {
15131518
enum E2ePadlockIcon {
15141519
Normal = "normal",
15151520
Warning = "warning",
1521+
Redacted = "viewingRedacted",
15161522
}
15171523

15181524
interface IE2ePadlockProps {

src/i18n/strings/en_EN.json

+2
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,7 @@
16461646
"Edit message": "Edit message",
16471647
"Mod": "Mod",
16481648
"From a thread": "From a thread",
1649+
"This is a deleted message": "This is a deleted message",
16491650
"This event could not be displayed": "This event could not be displayed",
16501651
"Your key share request has been sent - please check your other sessions for key share requests.": "Your key share request has been sent - please check your other sessions for key share requests.",
16511652
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.",
@@ -2867,6 +2868,7 @@
28672868
"Resume": "Resume",
28682869
"Hold": "Hold",
28692870
"Resend %(unsentCount)s reaction(s)": "Resend %(unsentCount)s reaction(s)",
2871+
"View content": "View content",
28702872
"Open in OpenStreetMap": "Open in OpenStreetMap",
28712873
"Forward": "Forward",
28722874
"View source": "View source",

0 commit comments

Comments
 (0)