From 51ac5421c9848be775c977009454e7f55c79d155 Mon Sep 17 00:00:00 2001
From: Panagiotis <27917356+panoschal@users.noreply.github.com>
Date: Sat, 6 Mar 2021 11:30:31 +0200
Subject: [PATCH 3/7] chore: refactor code
pass only the mxEvent object to ViewSource
derive the necessary values inside the component
---
src/components/structures/ViewSource.js | 161 +++++++++---------
.../views/context_menus/MessageContextMenu.js | 9 +-
.../views/messages/EditHistoryMessage.js | 6 +-
3 files changed, 86 insertions(+), 90 deletions(-)
diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js
index a31876ea76..369a0a1ddd 100644
--- a/src/components/structures/ViewSource.js
+++ b/src/components/structures/ViewSource.js
@@ -26,127 +26,134 @@ import { SendCustomEvent } from "../views/dialogs/DevtoolsDialog";
export default class ViewSource extends React.Component {
static propTypes = {
- content: PropTypes.object.isRequired,
onFinished: PropTypes.func.isRequired,
- roomId: PropTypes.string.isRequired,
- eventId: PropTypes.string.isRequired,
- isEncrypted: PropTypes.bool.isRequired,
- decryptedContent: PropTypes.object,
- event: PropTypes.object.isRequired, // the MatrixEvent associated with the context menu
+ mxEvent: PropTypes.object.isRequired, // the MatrixEvent associated with the context menu
};
constructor(props) {
super(props);
this.state = {
- editComponent: null,
+ isEditing: false,
};
}
onBack() {
- this.setState({ editComponent: null });
+ this.setState({ isEditing: false });
}
- editEvent() {
- const isStateEvent = this.props.event.isState();
- console.log("isStateEvent", isStateEvent);
- if (isStateEvent) {
- this.setState({
- editComponent: (
-
- {(cli) => (
- this.onBack()}
- inputs={{
- eventType: this.props.event.getType(),
- evContent: JSON.stringify(this.props.event.getContent(), null, "\t"),
- stateKey: this.props.event.getStateKey(),
- }}
- />
- )}
-
- ),
- });
- } else {
- // send an edit-message event
- // prefill the "m.new_content" field
- const originalContent = this.props.event.getContent();
- const originalEventId = this.props.eventId;
- const content = {
- ...originalContent,
- "m.new_content": originalContent,
- "m.relates_to": {
- rel_type: "m.replace",
- event_id: originalEventId,
- },
- };
- this.setState({
- editComponent: (
-
- {(cli) => (
- this.onBack()}
- inputs={{
- eventType: this.props.event.getType(),
- evContent: JSON.stringify(content, null, "\t"),
- }}
- />
- )}
-
- ),
- });
- }
+ onEdit() {
+ this.setState({ isEditing: true });
}
- render() {
- const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog");
+ // returns the dialog body for viewing the event source
+ viewSourceContent() {
+ const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
+ const isEncrypted = this.props.mxEvent.getType() !== this.props.mxEvent.getWireType();
+ const decryptedEventSource = mxEvent._clearEvent; // FIXME: _clearEvent is private
+ const originalEventSource = mxEvent.event;
- let content;
- if (this.props.isEncrypted) {
- content = (
+ if (isEncrypted) {
+ return (
<>
{_t("Decrypted event source")}
- {JSON.stringify(this.props.decryptedContent, null, 2)}
+ {JSON.stringify(decryptedEventSource, null, 2)}
{_t("Original event source")}
- {JSON.stringify(this.props.content, null, 2)}
+ {JSON.stringify(originalEventSource, null, 2)}
>
);
} else {
- content = (
+ return (
<>
{_t("Original event source")}
-
{JSON.stringify(this.props.content, null, 2)}
+
{JSON.stringify(originalEventSource, null, 2)}
>
);
}
+ }
- const isEditing = this.state.editComponent !== null;
- console.log(isEditing);
+ // returns the SendCustomEvent component prefilled with the correct details
+ editSourceContent() {
+ const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
+ const isStateEvent = mxEvent.isState();
+ console.log("isStateEvent", isStateEvent);
+ const roomId = mxEvent.getRoomId();
+ const eventId = mxEvent.getId();
+ const originalContent = mxEvent.getContent();
+ if (isStateEvent) {
+ return (
+
+ {(cli) => (
+ this.onBack()}
+ inputs={{
+ eventType: mxEvent.getType(),
+ evContent: JSON.stringify(originalContent, null, "\t"),
+ stateKey: mxEvent.getStateKey(),
+ }}
+ />
+ )}
+
+ );
+ } else {
+ // send an edit-message event
+ // prefill the "m.new_content" field
+ const newContent = {
+ ...originalContent,
+ "m.new_content": originalContent,
+ "m.relates_to": {
+ rel_type: "m.replace",
+ event_id: eventId,
+ },
+ };
+ return (
+
+ {(cli) => (
+ this.onBack()}
+ inputs={{
+ eventType: mxEvent.getType(),
+ evContent: JSON.stringify(newContent, null, "\t"),
+ }}
+ />
+ )}
+
+ );
+ }
+ }
+
+ render() {
+ const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog");
+ const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
+
+ const isEditing = this.state.isEditing;
+ const roomId = mxEvent.getRoomId();
+ const eventId = mxEvent.getId();
return (
-
Room ID: {this.props.roomId}
-
Event ID: {this.props.eventId}
+
Room ID: {roomId}
+
Event ID: {eventId}
- {isEditing ? this.state.editComponent : content}
+ {isEditing ? this.editSourceContent() : this.viewSourceContent()}
{!isEditing && (
-
+
)}
diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js
index a1c111b19c..6809d28e36 100644
--- a/src/components/views/context_menus/MessageContextMenu.js
+++ b/src/components/views/context_menus/MessageContextMenu.js
@@ -124,16 +124,9 @@ export default class MessageContextMenu extends React.Component {
};
onViewSourceClick = () => {
- const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent;
const ViewSource = sdk.getComponent('structures.ViewSource');
Modal.createTrackedDialog('View Event Source', '', ViewSource, {
- roomId: ev.getRoomId(),
- eventId: ev.getId(),
- content: ev.event,
- event: ev,
- isEncrypted: this.props.mxEvent.getType() !== this.props.mxEvent.getWireType(),
- // FIXME: _clearEvent is private
- decryptedContent: ev._clearEvent,
+ mxEvent: this.props.mxEvent,
}, 'mx_Dialog_viewsource');
this.closeMenu();
};
diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js
index 68a3c95745..3bd9dfbd21 100644
--- a/src/components/views/messages/EditHistoryMessage.js
+++ b/src/components/views/messages/EditHistoryMessage.js
@@ -74,11 +74,7 @@ export default class EditHistoryMessage extends React.PureComponent {
_onViewSourceClick = () => {
const ViewSource = sdk.getComponent('structures.ViewSource');
Modal.createTrackedDialog('View Event Source', 'Edit history', ViewSource, {
- roomId: this.props.mxEvent.getRoomId(),
- eventId: this.props.mxEvent.getId(),
- content: this.props.mxEvent.event,
- isEncrypted: this.props.mxEvent.getType() !== this.props.mxEvent.getWireType(),
- decryptedContent: this.props.mxEvent._clearEvent,
+ mxEvent: this.props.mxEvent,
}, 'mx_Dialog_viewsource');
};
From 29b95e60833fef1bed598897dad2deff4e875ff4 Mon Sep 17 00:00:00 2001
From: Panagiotis <27917356+panoschal@users.noreply.github.com>
Date: Sat, 6 Mar 2021 16:47:29 +0200
Subject: [PATCH 4/7] fix: make edit prefill work correctly from EditHistory
handle encrypted and unencrypted events
get the correct event_id (the base message) when called from EditHistoryMessage
keep only the `body` and `msgtype` fields when prefilling
---
src/components/structures/ViewSource.js | 33 +++++++++++++++++++------
1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js
index 369a0a1ddd..4ee70ee2a7 100644
--- a/src/components/structures/ViewSource.js
+++ b/src/components/structures/ViewSource.js
@@ -39,6 +39,7 @@ export default class ViewSource extends React.Component {
}
onBack() {
+ // TODO: refresh the "Event ID:" modal header
this.setState({ isEditing: false });
}
@@ -80,15 +81,28 @@ export default class ViewSource extends React.Component {
}
}
+ // returns the id of the initial message, not the id of the previous edit
+ getBaseEventId() {
+ const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
+ const isEncrypted = this.props.mxEvent.getType() !== this.props.mxEvent.getWireType();
+ const baseMxEvent = this.props.mxEvent;
+
+ if (isEncrypted) {
+ // `relates_to` field is inside the encrypted event
+ return mxEvent.event.content["m.relates_to"]?.event_id ?? baseMxEvent.getId();
+ } else {
+ return mxEvent.getContent()["m.relates_to"]?.event_id ?? baseMxEvent.getId();
+ }
+ }
+
// returns the SendCustomEvent component prefilled with the correct details
editSourceContent() {
const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
const isStateEvent = mxEvent.isState();
- console.log("isStateEvent", isStateEvent);
const roomId = mxEvent.getRoomId();
- const eventId = mxEvent.getId();
const originalContent = mxEvent.getContent();
+
if (isStateEvent) {
return (
@@ -107,14 +121,19 @@ export default class ViewSource extends React.Component {
);
} else {
- // send an edit-message event
- // prefill the "m.new_content" field
+ // prefill an edit-message event
+ // keep only the `body` and `msgtype` fields of originalContent
+ const bodyToStartFrom = originalContent["m.new_content"]?.body ?? originalContent.body; // prefill the last edit body, to start editing from there
const newContent = {
- ...originalContent,
- "m.new_content": originalContent,
+ "body": ` * ${bodyToStartFrom}`,
+ "msgtype": originalContent.msgtype,
+ "m.new_content": {
+ body: bodyToStartFrom,
+ msgtype: originalContent.msgtype,
+ },
"m.relates_to": {
rel_type: "m.replace",
- event_id: eventId,
+ event_id: this.getBaseEventId(),
},
};
return (
From df52ec28d60ecdf9719f3b5339a805f6a643f753 Mon Sep 17 00:00:00 2001
From: Panagiotis <27917356+panoschal@users.noreply.github.com>
Date: Sat, 6 Mar 2021 17:09:46 +0200
Subject: [PATCH 5/7] fix: show edit button only if you have permission
---
src/components/structures/ViewSource.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js
index 4ee70ee2a7..ddcffe4f7f 100644
--- a/src/components/structures/ViewSource.js
+++ b/src/components/structures/ViewSource.js
@@ -23,6 +23,7 @@ import { _t } from "../../languageHandler";
import * as sdk from "../../index";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { SendCustomEvent } from "../views/dialogs/DevtoolsDialog";
+import { canEditContent } from "../../utils/EventUtils";
export default class ViewSource extends React.Component {
static propTypes = {
@@ -162,6 +163,7 @@ export default class ViewSource extends React.Component {
const isEditing = this.state.isEditing;
const roomId = mxEvent.getRoomId();
const eventId = mxEvent.getId();
+ const canEdit = canEditContent(this.props.mxEvent);
return (
@@ -170,7 +172,7 @@ export default class ViewSource extends React.Component {
{isEditing ? this.editSourceContent() : this.viewSourceContent()}
- {!isEditing && (
+ {!isEditing && canEdit && (
From 9287e8dfa4f55b368a3c108d3c15442cdfdc4c1c Mon Sep 17 00:00:00 2001
From: Panagiotis <27917356+panoschal@users.noreply.github.com>
Date: Mon, 8 Mar 2021 22:15:34 +0200
Subject: [PATCH 6/7] use isEncrypted, edit state events
---
src/components/structures/ViewSource.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js
index ddcffe4f7f..cfe28e9f73 100644
--- a/src/components/structures/ViewSource.js
+++ b/src/components/structures/ViewSource.js
@@ -51,7 +51,7 @@ export default class ViewSource extends React.Component {
// returns the dialog body for viewing the event source
viewSourceContent() {
const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
- const isEncrypted = this.props.mxEvent.getType() !== this.props.mxEvent.getWireType();
+ const isEncrypted = mxEvent.isEncrypted();
const decryptedEventSource = mxEvent._clearEvent; // FIXME: _clearEvent is private
const originalEventSource = mxEvent.event;
@@ -85,7 +85,7 @@ export default class ViewSource extends React.Component {
// returns the id of the initial message, not the id of the previous edit
getBaseEventId() {
const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
- const isEncrypted = this.props.mxEvent.getType() !== this.props.mxEvent.getWireType();
+ const isEncrypted = mxEvent.isEncrypted();
const baseMxEvent = this.props.mxEvent;
if (isEncrypted) {
@@ -163,7 +163,7 @@ export default class ViewSource extends React.Component {
const isEditing = this.state.isEditing;
const roomId = mxEvent.getRoomId();
const eventId = mxEvent.getId();
- const canEdit = canEditContent(this.props.mxEvent);
+ const canEdit = canEditContent(this.props.mxEvent) || mxEvent.isState();
return (
From 0936ea7e640ac10449150eaed1615bc99c52e70c Mon Sep 17 00:00:00 2001
From: Panagiotis <27917356+panoschal@users.noreply.github.com>
Date: Tue, 9 Mar 2021 14:46:37 +0200
Subject: [PATCH 7/7] feat: show edit button only when user has permissions
call appropriate functions for state events and edit message events
---
src/components/structures/ViewSource.js | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/ViewSource.js b/src/components/structures/ViewSource.js
index cfe28e9f73..39666edd65 100644
--- a/src/components/structures/ViewSource.js
+++ b/src/components/structures/ViewSource.js
@@ -24,6 +24,7 @@ import * as sdk from "../../index";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { SendCustomEvent } from "../views/dialogs/DevtoolsDialog";
import { canEditContent } from "../../utils/EventUtils";
+import { MatrixClientPeg } from '../../MatrixClientPeg';
export default class ViewSource extends React.Component {
static propTypes = {
@@ -156,6 +157,12 @@ export default class ViewSource extends React.Component {
}
}
+ canSendStateEvent(mxEvent) {
+ const cli = MatrixClientPeg.get();
+ const room = cli.getRoom(mxEvent.getRoomId());
+ return room.currentState.mayClientSendStateEvent(mxEvent.getType(), cli);
+ }
+
render() {
const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog");
const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
@@ -163,7 +170,7 @@ export default class ViewSource extends React.Component {
const isEditing = this.state.isEditing;
const roomId = mxEvent.getRoomId();
const eventId = mxEvent.getId();
- const canEdit = canEditContent(this.props.mxEvent) || mxEvent.isState();
+ const canEdit = mxEvent.isState() ? this.canSendStateEvent(mxEvent) : canEditContent(this.props.mxEvent);
return (