diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss
index 57ae79da8c..eefb45afe5 100644
--- a/res/css/views/elements/_MessageEditor.scss
+++ b/res/css/views/elements/_MessageEditor.scss
@@ -17,12 +17,28 @@ limitations under the License.
.mx_MessageEditor {
border-radius: 4px;
background-color: #f3f8fd;
- padding: 10px;
+ padding: 11px 13px 7px 56px;
.editor {
border-radius: 4px;
border: solid 1px #e9edf1;
background-color: #ffffff;
+ padding: 10px;
+
+ span {
+ display: inline-block;
+ padding: 0 5px;
+ border-radius: 4px;
+ color: white;
+ }
+
+ span.user-pill {
+ background: red;
+ }
+
+ span.room-pill {
+ background: green;
+ }
}
.buttons {
@@ -39,4 +55,12 @@ limitations under the License.
font-weight: 600;
}
}
+
+ .model {
+ background: lightgrey;
+ padding: 5px;
+ display: block;
+ white-space: pre;
+ font-size: 12px;
+ }
}
diff --git a/src/components/views/elements/MessageEditor.js b/src/components/views/elements/MessageEditor.js
index f57521dbe9..026f92238b 100644
--- a/src/components/views/elements/MessageEditor.js
+++ b/src/components/views/elements/MessageEditor.js
@@ -18,6 +18,9 @@ import sdk from '../../../index';
import {_t} from '../../../languageHandler';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher';
+import EditorModel from '../../../editor/model';
+import {PlainPart} from '../../../editor/parts';
+import {getCaretOffset, setCaretPosition} from '../../../editor/caret';
import {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
export default class MessageEditor extends React.Component {
@@ -34,8 +37,24 @@ export default class MessageEditor extends React.Component {
constructor(props, context) {
super(props, context);
- this.state = {};
+ const body = this.props.event.getContent().body;
+ this.model = new EditorModel();
+ this.model.update(body, undefined, {offset: body.length});
+ this.state = {
+ parts: this.model.serializeParts(),
+ };
this._onCancelClicked = this._onCancelClicked.bind(this);
+ this._onInput = this._onInput.bind(this);
+ }
+
+ _onInput(event) {
+ const editor = event.target;
+ const caretOffset = getCaretOffset(editor);
+ const caret = this.model.update(editor.textContent, event.inputType, caretOffset);
+ const parts = this.model.serializeParts();
+ this.setState({parts}, () => {
+ setCaretPosition(editor, caret);
+ });
}
_onCancelClicked() {
@@ -43,14 +62,24 @@ export default class MessageEditor extends React.Component {
}
render() {
+ const parts = this.state.parts.map((p, i) => {
+ const key = `${i}-${p.type}`;
+ switch (p.type) {
+ case "plain": return p.text;
+ case "room-pill": return ({p.text});
+ case "user-pill": return ({p.text});
+ }
+ });
+ const modelOutput = JSON.stringify(this.state.parts, undefined, 2);
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return
-
- {this.props.event.getContent().body}
+
+ {parts}
+
{modelOutput}
;
}
}
diff --git a/src/editor/caret.js b/src/editor/caret.js
index 3b803f35c3..a252ebddc6 100644
--- a/src/editor/caret.js
+++ b/src/editor/caret.js
@@ -14,21 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-export function getCaretPosition(editor) {
+export function getCaretOffset(editor) {
const sel = document.getSelection();
const atNodeEnd = sel.focusOffset === sel.focusNode.textContent.length;
- let position = sel.focusOffset;
+ let offset = sel.focusOffset;
let node = sel.focusNode;
// when deleting the last character of a node,
// the caret gets reported as being after the focusOffset-th node,
// with the focusNode being the editor
if (node === editor) {
- let position = 0;
+ let offset = 0;
for (let i = 0; i < sel.focusOffset; ++i) {
- position += editor.childNodes[i].textContent.length;
+ offset += editor.childNodes[i].textContent.length;
}
- return {position, atNodeEnd: false};
+ return {offset, atNodeEnd: false};
}
// first make sure we're at the level of a direct child of editor
@@ -36,7 +36,7 @@ export function getCaretPosition(editor) {
// include all preceding siblings of the non-direct editor children
while (node.previousSibling) {
node = node.previousSibling;
- position += node.textContent.length;
+ offset += node.textContent.length;
}
// then move up
// I guess technically there could be preceding text nodes in the parents here as well,
@@ -48,13 +48,13 @@ export function getCaretPosition(editor) {
// now include the text length of all preceding direct editor children
while (node.previousSibling) {
node = node.previousSibling;
- position += node.textContent.length;
+ offset += node.textContent.length;
}
- {
- const {focusOffset, focusNode} = sel;
- console.log("selection", {focusOffset, focusNode, position, atNodeEnd});
- }
- return {position, atNodeEnd};
+ // {
+ // const {focusOffset, focusNode} = sel;
+ // console.log("selection", {focusOffset, focusNode, position, atNodeEnd});
+ // }
+ return {offset, atNodeEnd};
}
export function setCaretPosition(editor, caretPosition) {
diff --git a/src/editor/model.js b/src/editor/model.js
index ffd2e17c01..b3d6682f79 100644
--- a/src/editor/model.js
+++ b/src/editor/model.js
@@ -40,18 +40,22 @@ export default class EditorModel {
return this._parts;
}
+ serializeParts() {
+ return this._parts.map(({type, text}) => {return {type, text};});
+ }
+
_diff(newValue, inputType, caret) {
if (inputType === "deleteByDrag") {
return diffDeletion(this._previousValue, newValue);
} else {
- return diffAtCaret(this._previousValue, newValue, caret.position);
+ return diffAtCaret(this._previousValue, newValue, caret.offset);
}
}
update(newValue, inputType, caret) {
const diff = this._diff(newValue, inputType, caret);
const position = this._positionForOffset(diff.at, caret.atNodeEnd);
- console.log("update at", {position, diff});
+ console.log("update at", {position, diff, newValue, prevValue: this._previousValue});
if (diff.removed) {
this._removeText(position, diff.removed.length);
}