From 6f98aa06c4d3247f4561224f39bea210327f50a1 Mon Sep 17 00:00:00 2001 From: Jaiwanth Date: Sun, 9 May 2021 13:18:01 +0530 Subject: [PATCH 1/4] Save edited state when switching rooms Signed-off-by: Jaiwanth --- src/components/structures/MessagePanel.js | 11 ++++- .../views/rooms/EditMessageComposer.js | 45 +++++++++++++++++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index c93f07fa0f..555ee38d17 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -34,6 +34,7 @@ import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResiz import DMRoomMap from "../../utils/DMRoomMap"; import NewRoomIntro from "../views/rooms/NewRoomIntro"; import {replaceableComponent} from "../../utils/replaceableComponent"; +import defaultDispatcher from '../../dispatcher/dispatcher'; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = ['m.sticker', 'm.room.message']; @@ -564,15 +565,23 @@ export default class MessagePanel extends React.Component { return ret; } + _wasEventBeingEdited = (mxEv) => { + return localStorage.getItem(`mx_edit_state_${mxEv.getRoomId()} + _${mxEv.getId()}`) !== null; + } + _getTilesForEvent(prevEvent, mxEv, last, nextEvent, nextEventWithTile) { const TileErrorBoundary = sdk.getComponent('messages.TileErrorBoundary'); const EventTile = sdk.getComponent('rooms.EventTile'); const DateSeparator = sdk.getComponent('messages.DateSeparator'); const ret = []; + if (!this.props.editState && this._wasEventBeingEdited(mxEv) ) { + defaultDispatcher.dispatch({action: "edit_event", event: mxEv}); + } + const isEditing = this.props.editState && this.props.editState.getEvent().getId() === mxEv.getId(); - // local echoes have a fake date, which could even be yesterday. Treat them // as 'today' for the date separators. let ts1 = mxEv.getTs(); diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index b006fe8c8d..dd87654438 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -34,6 +34,7 @@ import {Action} from "../../../dispatcher/actions"; import CountlyAnalytics from "../../../CountlyAnalytics"; import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager'; import {replaceableComponent} from "../../../utils/replaceableComponent"; +import SendHistoryManager from '../../../SendHistoryManager'; function _isReply(mxEvent) { const relatesTo = mxEvent.getContent()["m.relates_to"]; @@ -120,6 +121,7 @@ export default class EditMessageComposer extends React.Component { saveDisabled: true, }; this._createEditorModel(); + window.addEventListener("beforeunload", this._saveStoredEditorState); } _setEditorRef = ref => { @@ -174,10 +176,43 @@ export default class EditMessageComposer extends React.Component { } _cancelEdit = () => { + this._clearStoredEditorState(); dis.dispatch({action: "edit_event", event: null}); dis.fire(Action.FocusComposer); } + get _shouldSaveStoredEditorState() { + return localStorage.getItem(`mx_edit_state_${this.props.editState.getEvent().getRoomId()} + _${this.props.editState.getEvent().event.event_id}`) !== null; + } + + _restoreStoredEditorState(partCreator) { + const json = localStorage.getItem(this._editorStateKey); + if (json) { + try { + const {parts: serializedParts} = JSON.parse(json); + const parts = serializedParts.map(p => partCreator.deserializePart(p)); + return parts; + } catch (e) { + console.error(e); + } + } + } + + get _editorStateKey() { + return `mx_edit_state_${this.props.editState.getEvent().getRoomId()} + _${this.props.editState.getEvent().event.event_id}`; + } + + _clearStoredEditorState() { + localStorage.removeItem(this._editorStateKey); + } + + _saveStoredEditorState() { + const item = SendHistoryManager.createItem(this.model); + localStorage.setItem(this._editorStateKey, JSON.stringify(item)); + } + _isContentModified(newContent) { // if nothing has changed then bail const oldContent = this.props.editState.getEvent().getContent(); @@ -195,13 +230,13 @@ export default class EditMessageComposer extends React.Component { const editedEvent = this.props.editState.getEvent(); const editContent = createEditContent(this.model, editedEvent); const newContent = editContent["m.new_content"]; - // If content is modified then send an updated event into the room if (this._isContentModified(newContent)) { const roomId = editedEvent.getRoomId(); this._cancelPreviousPendingEdit(); const prom = this.context.sendMessage(roomId, editContent); dis.dispatch({action: "message_sent"}); + this._clearStoredEditorState(); CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, true, false, editContent); } @@ -235,6 +270,10 @@ export default class EditMessageComposer extends React.Component { // then when mounting the editor again with the same editor state, // it will set the cursor at the end. this.props.editState.setEditorState(caret, parts); + window.removeEventListener("beforeunload", this._saveStoredEditorState); + if (this._shouldSaveStoredEditorState) { + this._saveStoredEditorState(); + } } _createEditorModel() { @@ -247,10 +286,10 @@ export default class EditMessageComposer extends React.Component { // restore serialized parts from the state parts = editState.getSerializedParts().map(p => partCreator.deserializePart(p)); } else { - // otherwise, parse the body of the event - parts = parseEvent(editState.getEvent(), partCreator); + parts = this._restoreStoredEditorState(partCreator) || parseEvent(editState.getEvent(), partCreator); } this.model = new EditorModel(parts, partCreator); + this._saveStoredEditorState(); } _getInitialCaretPosition() { From 1f7704875013b70717fa0866a3db028319b2c972 Mon Sep 17 00:00:00 2001 From: Jaiwanth Date: Sun, 9 May 2021 16:12:04 +0530 Subject: [PATCH 2/4] Minor refactor --- src/components/views/rooms/EditMessageComposer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index dd87654438..46999bb793 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -182,8 +182,7 @@ export default class EditMessageComposer extends React.Component { } get _shouldSaveStoredEditorState() { - return localStorage.getItem(`mx_edit_state_${this.props.editState.getEvent().getRoomId()} - _${this.props.editState.getEvent().event.event_id}`) !== null; + return localStorage.getItem(this._editorStateKey) !== null; } _restoreStoredEditorState(partCreator) { From 376befd38eb87dc9afd5eaed64b79a5fcdc5eba1 Mon Sep 17 00:00:00 2001 From: Jaiwanth Date: Tue, 11 May 2021 10:37:53 +0530 Subject: [PATCH 3/4] Update src/components/views/rooms/EditMessageComposer.js Co-authored-by: Travis Ralston --- src/components/views/rooms/EditMessageComposer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index 46999bb793..81a26df56c 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -193,7 +193,7 @@ export default class EditMessageComposer extends React.Component { const parts = serializedParts.map(p => partCreator.deserializePart(p)); return parts; } catch (e) { - console.error(e); + console.error("Error parsing editing state: ", e); } } } From 240753a84f0fdb6eb8db842fec4a578494132aac Mon Sep 17 00:00:00 2001 From: Jaiwanth Date: Tue, 11 May 2021 18:37:41 +0530 Subject: [PATCH 4/4] Check for a pending edit only once per render and clear any pending events while switching between edits --- src/components/structures/MessagePanel.js | 20 +++++++++------- .../views/rooms/EditMessageComposer.js | 24 ++++++++++++++----- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 00f2c4c826..73a2a3c4b6 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -472,6 +472,10 @@ export default class MessagePanel extends React.Component { return {nextEvent, nextTile}; } + get _roomHasPendingEdit() { + return localStorage.getItem(`mx_edit_room_${this.props.room.roomId}`); + } + _getEventTiles() { this.eventNodes = {}; @@ -560,6 +564,13 @@ export default class MessagePanel extends React.Component { } } + if (!this.props.editState && this._roomHasPendingEdit) { + defaultDispatcher.dispatch({ + action: "edit_event", + event: this.props.room.findEventById(this._roomHasPendingEdit), + }); + } + if (grouper) { ret.push(...grouper.getTiles()); } @@ -567,21 +578,12 @@ export default class MessagePanel extends React.Component { return ret; } - _wasEventBeingEdited = (mxEv) => { - return localStorage.getItem(`mx_edit_state_${mxEv.getRoomId()} - _${mxEv.getId()}`) !== null; - } - _getTilesForEvent(prevEvent, mxEv, last, isGrouped=false, nextEvent, nextEventWithTile) { const TileErrorBoundary = sdk.getComponent('messages.TileErrorBoundary'); const EventTile = sdk.getComponent('rooms.EventTile'); const DateSeparator = sdk.getComponent('messages.DateSeparator'); const ret = []; - if (!this.props.editState && this._wasEventBeingEdited(mxEv) ) { - defaultDispatcher.dispatch({action: "edit_event", event: mxEv}); - } - const isEditing = this.props.editState && this.props.editState.getEvent().getId() === mxEv.getId(); // local echoes have a fake date, which could even be yesterday. Treat them diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index ceb49b32c6..897388d5d2 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -177,6 +177,14 @@ export default class EditMessageComposer extends React.Component { } } + get _editorRoomKey() { + return `mx_edit_room_${this._getRoom().roomId}`; + } + + get _editorStateKey() { + return `mx_edit_state_${this.props.editState.getEvent().getId()}`; + } + _cancelEdit = () => { this._clearStoredEditorState(); dis.dispatch({action: "edit_event", event: null}); @@ -184,7 +192,7 @@ export default class EditMessageComposer extends React.Component { } get _shouldSaveStoredEditorState() { - return localStorage.getItem(this._editorStateKey) !== null; + return localStorage.getItem(this._editorRoomKey) !== null; } _restoreStoredEditorState(partCreator) { @@ -200,17 +208,21 @@ export default class EditMessageComposer extends React.Component { } } - get _editorStateKey() { - return `mx_edit_state_${this.props.editState.getEvent().getRoomId()} - _${this.props.editState.getEvent().event.event_id}`; + _clearStoredEditorState() { + localStorage.removeItem(this._editorRoomKey); + localStorage.removeItem(this._editorStateKey); } - _clearStoredEditorState() { - localStorage.removeItem(this._editorStateKey); + _clearPreviousEdit() { + if (localStorage.getItem(this._editorRoomKey)) { + localStorage.removeItem(`mx_edit_state_${localStorage.getItem(this._editorRoomKey)}`); + } } _saveStoredEditorState() { const item = SendHistoryManager.createItem(this.model); + this._clearPreviousEdit(); + localStorage.setItem(this._editorRoomKey, this.props.editState.getEvent().getId()); localStorage.setItem(this._editorStateKey, JSON.stringify(item)); }