Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.
13 changes: 12 additions & 1 deletion src/components/structures/MessagePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
Expand Down Expand Up @@ -471,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 = {};

Expand Down Expand Up @@ -559,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());
}
Expand All @@ -574,7 +586,6 @@ export default class MessagePanel extends React.Component {

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();
Expand Down
57 changes: 55 additions & 2 deletions src/components/views/rooms/EditMessageComposer.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {Action} from "../../../dispatcher/actions";
import CountlyAnalytics from "../../../CountlyAnalytics";
import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import SendHistoryManager from '../../../SendHistoryManager';
import Modal from '../../../Modal';

function _isReply(mxEvent) {
Expand Down Expand Up @@ -122,6 +123,7 @@ export default class EditMessageComposer extends React.Component {
saveDisabled: true,
};
this._createEditorModel();
window.addEventListener("beforeunload", this._saveStoredEditorState);
}

_setEditorRef = ref => {
Expand Down Expand Up @@ -175,11 +177,55 @@ 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});
dis.fire(Action.FocusComposer);
}

get _shouldSaveStoredEditorState() {
return localStorage.getItem(this._editorRoomKey) !== 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("Error parsing editing state: ", e);
}
}
}

_clearStoredEditorState() {
localStorage.removeItem(this._editorRoomKey);
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));
}

_isSlashCommand() {
const parts = this.model.parts;
const firstPart = parts[0];
Expand Down Expand Up @@ -266,6 +312,7 @@ 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"];

let shouldSend = true;

// If content is modified then send an updated event into the room
Expand Down Expand Up @@ -311,6 +358,7 @@ export default class EditMessageComposer extends React.Component {
if (shouldSend) {
this._cancelPreviousPendingEdit();
const prom = this.context.sendMessage(roomId, editContent);
this._clearStoredEditorState();
dis.dispatch({action: "message_sent"});
CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, true, false, editContent);
}
Expand Down Expand Up @@ -346,6 +394,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() {
Expand All @@ -358,10 +410,11 @@ 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);
//otherwise, either restore serialized parts from localStorage or parse the body of the event
parts = this._restoreStoredEditorState(partCreator) || parseEvent(editState.getEvent(), partCreator);
}
this.model = new EditorModel(parts, partCreator);
this._saveStoredEditorState();
}

_getInitialCaretPosition() {
Expand Down