diff --git a/web/components/chat/ChatTextField/ChatTextField.tsx b/web/components/chat/ChatTextField/ChatTextField.tsx
index aa443cfd9..5a2499421 100644
--- a/web/components/chat/ChatTextField/ChatTextField.tsx
+++ b/web/components/chat/ChatTextField/ChatTextField.tsx
@@ -1,27 +1,101 @@
import { SmileOutlined } from '@ant-design/icons';
import { Button, Input } from 'antd';
-import React, { useRef, useState } from 'react';
+import React, { useState, useMemo } from 'react';
import { useRecoilValue } from 'recoil';
-import ContentEditable from 'react-contenteditable';
+import { Transforms, createEditor, Node, BaseEditor, Text } from 'slate';
+import { Slate, Editable, withReact, ReactEditor } from 'slate-react';
import WebsocketService from '../../../services/websocket-service';
import { websocketServiceAtom } from '../../stores/ClientConfigStore';
import { getCaretPosition, convertToText, convertOnPaste } from '../chat';
import { MessageType } from '../../../interfaces/socket-events';
import s from './ChatTextField.module.scss';
+type CustomElement = { type: 'paragraph'; children: CustomText[] };
+type CustomText = { text: string };
+
+declare module 'slate' {
+ interface CustomTypes {
+ Editor: BaseEditor & ReactEditor;
+ Element: CustomElement;
+ Text: CustomText;
+ }
+}
+
interface Props {
value?: string;
}
+const Image = ({ element }) => (
+
+);
+
+const insertImage = (editor, url) => {
+ const text = { text: '' };
+ const image: ImageElement = { type: 'image', url, children: [text] };
+ Transforms.insertNodes(editor, image);
+};
+
+const withImages = editor => {
+ const { isVoid } = editor;
+
+ editor.isVoid = element => (element.type === 'image' ? true : isVoid(element));
+ editor.isInline = element => element.type === 'image';
+
+ return editor;
+};
+
+export type EmptyText = {
+ text: string;
+};
+
+type ImageElement = {
+ type: 'image';
+ url: string;
+ children: EmptyText[];
+};
+
+const Element = props => {
+ const { attributes, children, element } = props;
+
+ switch (element.type) {
+ case 'image':
+ return ;
+ default:
+ return
{children}
;
+ }
+};
+
+const serialize = node => {
+ if (Text.isText(node)) {
+ let string = node.text; // escapeHtml(node.text);
+ if (node.bold) {
+ string = `${string}`;
+ }
+ return string;
+ }
+
+ const children = node.children.map(n => serialize(n)).join('');
+
+ switch (node.type) {
+ case 'paragraph':
+ return `${children}
`;
+ case 'image':
+ return ``;
+ default:
+ return children;
+ }
+};
+
export default function ChatTextField(props: Props) {
const { value: originalValue } = props;
- const [value, setValue] = useState(originalValue);
const [showEmojis, setShowEmojis] = useState(false);
const websocketService = useRecoilValue(websocketServiceAtom);
+ const [editor] = useState(() => withImages(withReact(createEditor())));
- const text = useRef(value);
-
- // large is 40px
const size = 'small';
const sendMessage = () => {
@@ -30,41 +104,46 @@ export default function ChatTextField(props: Props) {
return;
}
- const message = convertToText(value);
+ const message = serialize(editor);
+
websocketService.send({ type: MessageType.CHAT, body: message });
- setValue('');
+
+ // Clear the editor.
+ Transforms.select(editor, [0, editor.children.length - 1]);
+ Transforms.delete(editor);
};
- const handleChange = evt => {
- text.current = evt.target.value;
- setValue(evt.target.value);
- };
+ const handleChange = e => {};
- const handleKeyDown = event => {
- const key = event && event.key;
-
- if (key === 'Enter') {
+ const onKeyDown = e => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ sendMessage();
}
};
+ const initialValue = [
+ {
+ type: 'paragraph',
+ children: [{ text: originalValue }],
+ },
+ ];
+
return (
-
- {
- handleKeyDown(e);
- }}
+
+ }
+ placeholder="Chat message goes here..."
/>
-
-
-
+
+
+
);
}
diff --git a/web/package-lock.json b/web/package-lock.json
index e8e065293..769ee8586 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -33,6 +33,8 @@
"react-markdown-editor-lite": "1.3.2",
"react-virtuoso": "^2.10.2",
"recoil": "^0.7.2",
+ "slate": "^0.78.0",
+ "slate-react": "^0.79.0",
"ua-parser-js": "1.0.2",
"video.js": "^7.18.1"
},
@@ -10813,6 +10815,11 @@
"resolved": "https://registry.npmjs.org/@types/is-function/-/is-function-1.0.1.tgz",
"integrity": "sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q=="
},
+ "node_modules/@types/is-hotkey": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.7.tgz",
+ "integrity": "sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ=="
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@@ -10859,6 +10866,11 @@
"integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==",
"dev": true
},
+ "node_modules/@types/lodash": {
+ "version": "4.14.182",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+ "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q=="
+ },
"node_modules/@types/markdown-it": {
"version": "12.2.3",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
@@ -15461,6 +15473,18 @@
"node": ">=8"
}
},
+ "node_modules/direction": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz",
+ "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==",
+ "bin": {
+ "direction": "cli.js"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -19065,6 +19089,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/immer": {
+ "version": "9.0.12",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz",
+ "integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/immutable": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
@@ -19597,6 +19630,11 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/is-hotkey": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz",
+ "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ=="
+ },
"node_modules/is-installed-globally": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
@@ -28434,6 +28472,52 @@
"node": ">=8"
}
},
+ "node_modules/slate": {
+ "version": "0.78.0",
+ "resolved": "https://registry.npmjs.org/slate/-/slate-0.78.0.tgz",
+ "integrity": "sha512-VwQ0RafT3JPf9SFrXI02Dh3S4Iz9en7d1nn50C/LJjjqjfgv+a2ORbgWMdYjhycPYldaxJwcI3OpP9D1g4SXEg==",
+ "dependencies": {
+ "immer": "^9.0.6",
+ "is-plain-object": "^5.0.0",
+ "tiny-warning": "^1.0.3"
+ }
+ },
+ "node_modules/slate-react": {
+ "version": "0.79.0",
+ "resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.79.0.tgz",
+ "integrity": "sha512-uPzYArGwHKq4QvpzRvP/DVvsDgB2Zw6x9Som/hJWaydu18r0Vp2vmgHL0Dbc4EU6pUNV6o83XbBJLLEwISzBHQ==",
+ "dependencies": {
+ "@types/is-hotkey": "^0.1.1",
+ "@types/lodash": "^4.14.149",
+ "direction": "^1.0.3",
+ "is-hotkey": "^0.1.6",
+ "is-plain-object": "^5.0.0",
+ "lodash": "^4.17.4",
+ "scroll-into-view-if-needed": "^2.2.20",
+ "tiny-invariant": "1.0.6"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0",
+ "slate": ">=0.65.3"
+ }
+ },
+ "node_modules/slate-react/node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/slate/node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -29808,6 +29892,16 @@
"node": ">=0.6.0"
}
},
+ "node_modules/tiny-invariant": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
+ "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
+ },
+ "node_modules/tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
"node_modules/tlds": {
"version": "1.231.0",
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.231.0.tgz",
@@ -39698,6 +39792,11 @@
"resolved": "https://registry.npmjs.org/@types/is-function/-/is-function-1.0.1.tgz",
"integrity": "sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q=="
},
+ "@types/is-hotkey": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/@types/is-hotkey/-/is-hotkey-0.1.7.tgz",
+ "integrity": "sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ=="
+ },
"@types/istanbul-lib-coverage": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@@ -39744,6 +39843,11 @@
"integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==",
"dev": true
},
+ "@types/lodash": {
+ "version": "4.14.182",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+ "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q=="
+ },
"@types/markdown-it": {
"version": "12.2.3",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
@@ -43358,6 +43462,11 @@
"path-type": "^4.0.0"
}
},
+ "direction": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz",
+ "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ=="
+ },
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -46160,6 +46269,11 @@
"integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
"optional": true
},
+ "immer": {
+ "version": "9.0.12",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz",
+ "integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA=="
+ },
"immutable": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
@@ -46529,6 +46643,11 @@
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="
},
+ "is-hotkey": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.8.tgz",
+ "integrity": "sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ=="
+ },
"is-installed-globally": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
@@ -53282,6 +53401,45 @@
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
},
+ "slate": {
+ "version": "0.78.0",
+ "resolved": "https://registry.npmjs.org/slate/-/slate-0.78.0.tgz",
+ "integrity": "sha512-VwQ0RafT3JPf9SFrXI02Dh3S4Iz9en7d1nn50C/LJjjqjfgv+a2ORbgWMdYjhycPYldaxJwcI3OpP9D1g4SXEg==",
+ "requires": {
+ "immer": "^9.0.6",
+ "is-plain-object": "^5.0.0",
+ "tiny-warning": "^1.0.3"
+ },
+ "dependencies": {
+ "is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
+ }
+ }
+ },
+ "slate-react": {
+ "version": "0.79.0",
+ "resolved": "https://registry.npmjs.org/slate-react/-/slate-react-0.79.0.tgz",
+ "integrity": "sha512-uPzYArGwHKq4QvpzRvP/DVvsDgB2Zw6x9Som/hJWaydu18r0Vp2vmgHL0Dbc4EU6pUNV6o83XbBJLLEwISzBHQ==",
+ "requires": {
+ "@types/is-hotkey": "^0.1.1",
+ "@types/lodash": "^4.14.149",
+ "direction": "^1.0.3",
+ "is-hotkey": "^0.1.6",
+ "is-plain-object": "^5.0.0",
+ "lodash": "^4.17.4",
+ "scroll-into-view-if-needed": "^2.2.20",
+ "tiny-invariant": "1.0.6"
+ },
+ "dependencies": {
+ "is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
+ }
+ }
+ },
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -54368,6 +54526,16 @@
"setimmediate": "^1.0.4"
}
},
+ "tiny-invariant": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
+ "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
+ },
+ "tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
"tlds": {
"version": "1.231.0",
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.231.0.tgz",
diff --git a/web/package.json b/web/package.json
index 758f914d5..94b782a17 100644
--- a/web/package.json
+++ b/web/package.json
@@ -36,6 +36,8 @@
"react-markdown-editor-lite": "1.3.2",
"react-virtuoso": "^2.10.2",
"recoil": "^0.7.2",
+ "slate": "^0.78.0",
+ "slate-react": "^0.79.0",
"ua-parser-js": "1.0.2",
"video.js": "^7.18.1"
},