mirror of
https://github.com/element-hq/element-web
synced 2024-11-27 11:47:23 +03:00
bring back composer send history and arrow up to edit previous message
This commit is contained in:
parent
ca3539d53e
commit
cc82353d8f
3 changed files with 84 additions and 46 deletions
|
@ -15,38 +15,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {Value} from 'slate';
|
||||
|
||||
import _clamp from 'lodash/clamp';
|
||||
|
||||
type MessageFormat = 'rich' | 'markdown';
|
||||
|
||||
class HistoryItem {
|
||||
// We store history items in their native format to ensure history is accurate
|
||||
// and then convert them if our RTE has subsequently changed format.
|
||||
value: Value;
|
||||
format: MessageFormat = 'rich';
|
||||
|
||||
constructor(value: ?Value, format: ?MessageFormat) {
|
||||
this.value = value;
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
static fromJSON(obj: Object): HistoryItem {
|
||||
return new HistoryItem(
|
||||
Value.fromJSON(obj.value),
|
||||
obj.format,
|
||||
);
|
||||
}
|
||||
|
||||
toJSON(): Object {
|
||||
return {
|
||||
value: this.value.toJSON(),
|
||||
format: this.format,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default class ComposerHistoryManager {
|
||||
history: Array<HistoryItem> = [];
|
||||
prefix: string;
|
||||
|
@ -57,26 +27,30 @@ export default class ComposerHistoryManager {
|
|||
this.prefix = prefix + roomId;
|
||||
|
||||
// TODO: Performance issues?
|
||||
let item;
|
||||
for (; item = sessionStorage.getItem(`${this.prefix}[${this.currentIndex}]`); this.currentIndex++) {
|
||||
let index = 0;
|
||||
let itemJSON;
|
||||
|
||||
while (itemJSON = sessionStorage.getItem(`${this.prefix}[${index}]`)) {
|
||||
try {
|
||||
this.history.push(
|
||||
HistoryItem.fromJSON(JSON.parse(item)),
|
||||
);
|
||||
const serializedParts = JSON.parse(itemJSON);
|
||||
this.history.push(serializedParts);
|
||||
} catch (e) {
|
||||
console.warn("Throwing away unserialisable history", e);
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
this.lastIndex = this.currentIndex;
|
||||
this.lastIndex = this.history.length - 1;
|
||||
// reset currentIndex to account for any unserialisable history
|
||||
this.currentIndex = this.history.length;
|
||||
this.currentIndex = this.lastIndex + 1;
|
||||
}
|
||||
|
||||
save(value: Value, format: MessageFormat) {
|
||||
const item = new HistoryItem(value, format);
|
||||
this.history.push(item);
|
||||
save(editorModel: Object) {
|
||||
const serializedParts = editorModel.serializeParts();
|
||||
this.history.push(serializedParts);
|
||||
this.currentIndex = this.history.length;
|
||||
sessionStorage.setItem(`${this.prefix}[${this.lastIndex++}]`, JSON.stringify(item.toJSON()));
|
||||
this.lastIndex += 1;
|
||||
sessionStorage.setItem(`${this.prefix}[${this.lastIndex}]`, JSON.stringify(serializedParts));
|
||||
}
|
||||
|
||||
getItem(offset: number): ?HistoryItem {
|
||||
|
|
|
@ -144,6 +144,10 @@ export default class BasicMessageEditor extends React.Component {
|
|||
return this._lastCaret;
|
||||
}
|
||||
|
||||
isSelectionCollapsed() {
|
||||
return !this._lastSelection || this._lastSelection.isCollapsed;
|
||||
}
|
||||
|
||||
isCaretAtStart() {
|
||||
return this.getCaret().offset === 0;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ import ReplyPreview from "./ReplyPreview";
|
|||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
import ReplyThread from "../elements/ReplyThread";
|
||||
import {parseEvent} from '../../../editor/deserialize';
|
||||
import {findEditableEvent} from '../../../utils/EventUtils';
|
||||
import ComposerHistoryManager from "../../../ComposerHistoryManager";
|
||||
|
||||
function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) {
|
||||
const replyContent = ReplyThread.makeReplyMixIn(repliedToEvent);
|
||||
|
@ -79,6 +81,7 @@ export default class SendMessageComposer extends React.Component {
|
|||
super(props, context);
|
||||
this.model = null;
|
||||
this._editorRef = null;
|
||||
this.currentlyComposedEditorState = null;
|
||||
}
|
||||
|
||||
_setEditorRef = ref => {
|
||||
|
@ -86,19 +89,75 @@ export default class SendMessageComposer extends React.Component {
|
|||
};
|
||||
|
||||
_onKeyDown = (event) => {
|
||||
if (event.metaKey || event.altKey || event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
const hasModifier = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
||||
if (event.key === "Enter" && !hasModifier) {
|
||||
this._sendMessage();
|
||||
event.preventDefault();
|
||||
} else if (event.key === "ArrowUp") {
|
||||
this.onVerticalArrow(event, true);
|
||||
} else if (event.key === "ArrowDown") {
|
||||
this.onVerticalArrow(event, false);
|
||||
}
|
||||
}
|
||||
|
||||
onVerticalArrow(e, up) {
|
||||
if (e.ctrlKey || e.shiftKey || e.metaKey) return;
|
||||
|
||||
const shouldSelectHistory = e.altKey;
|
||||
const shouldEditLastMessage = !e.altKey && up && !RoomViewStore.getQuotingEvent();
|
||||
|
||||
if (shouldSelectHistory) {
|
||||
// Try select composer history
|
||||
const selected = this.selectSendHistory(up);
|
||||
if (selected) {
|
||||
// We're selecting history, so prevent the key event from doing anything else
|
||||
e.preventDefault();
|
||||
}
|
||||
} else if (shouldEditLastMessage) {
|
||||
// selection must be collapsed and caret at start
|
||||
if (this._editorRef.isSelectionCollapsed() && this._editorRef.isCaretAtStart()) {
|
||||
const editEvent = findEditableEvent(this.props.room, false);
|
||||
if (editEvent) {
|
||||
// We're selecting history, so prevent the key event from doing anything else
|
||||
e.preventDefault();
|
||||
dis.dispatch({
|
||||
action: 'edit_event',
|
||||
event: editEvent,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectSendHistory(up) {
|
||||
const delta = up ? -1 : 1;
|
||||
|
||||
// True if we are not currently selecting history, but composing a message
|
||||
if (this.sendHistoryManager.currentIndex === this.sendHistoryManager.history.length) {
|
||||
// We can't go any further - there isn't any more history, so nop.
|
||||
if (!up) {
|
||||
return;
|
||||
}
|
||||
this.currentlyComposedEditorState = this.model.serializeParts();
|
||||
} else if (this.sendHistoryManager.currentIndex + delta === this.sendHistoryManager.history.length) {
|
||||
// True when we return to the message being composed currently
|
||||
this.model.reset(this.currentlyComposedEditorState);
|
||||
this.sendHistoryManager.currentIndex = this.sendHistoryManager.history.length;
|
||||
return;
|
||||
}
|
||||
const serializedParts = this.sendHistoryManager.getItem(delta);
|
||||
if (serializedParts) {
|
||||
this.model.reset(serializedParts);
|
||||
this._editorRef.focus();
|
||||
}
|
||||
}
|
||||
|
||||
_sendMessage() {
|
||||
const isReply = !!RoomViewStore.getQuotingEvent();
|
||||
const {roomId} = this.props.room;
|
||||
this.context.matrixClient.sendMessage(roomId, createMessageContent(this.model, this.props.permalinkCreator));
|
||||
const content = createMessageContent(this.model, this.props.permalinkCreator);
|
||||
this.context.matrixClient.sendMessage(roomId, content);
|
||||
this.sendHistoryManager.save(this.model);
|
||||
this.model.reset([]);
|
||||
this._editorRef.clearUndoHistory();
|
||||
|
||||
|
@ -125,6 +184,7 @@ export default class SendMessageComposer extends React.Component {
|
|||
const partCreator = new PartCreator(this.props.room, this.context.matrixClient);
|
||||
this.model = new EditorModel([], partCreator);
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
this.sendHistoryManager = new ComposerHistoryManager(this.props.room.roomId, 'mx_slate_composer_history_');
|
||||
}
|
||||
|
||||
onAction = (payload) => {
|
||||
|
|
Loading…
Reference in a new issue