WIP (doesn't build yet) replacing draft with slate

This commit is contained in:
Matthew Hodgson 2018-04-23 01:13:18 +01:00
parent 44d92bb32e
commit 75a2be1a8d
4 changed files with 125 additions and 88 deletions

View file

@ -58,9 +58,6 @@
"classnames": "^2.1.2", "classnames": "^2.1.2",
"commonmark": "^0.28.1", "commonmark": "^0.28.1",
"counterpart": "^0.18.0", "counterpart": "^0.18.0",
"draft-js": "^0.11.0-alpha",
"draft-js-export-html": "^0.6.0",
"draft-js-export-markdown": "^0.3.0",
"emojione": "2.2.7", "emojione": "2.2.7",
"file-saver": "^1.3.3", "file-saver": "^1.3.3",
"filesize": "3.5.6", "filesize": "3.5.6",
@ -84,6 +81,10 @@
"react-beautiful-dnd": "^4.0.1", "react-beautiful-dnd": "^4.0.1",
"react-dom": "^15.6.0", "react-dom": "^15.6.0",
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef", "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"slate": "^0.33.4",
"slate-react": "^0.12.4",
"slate-html-serializer": "^0.6.1",
"slate-md-serializer": "^3.0.3",
"sanitize-html": "^1.14.1", "sanitize-html": "^1.14.1",
"text-encoding-utf-8": "^1.0.1", "text-encoding-utf-8": "^1.0.1",
"url": "^0.11.0", "url": "^0.11.0",

View file

@ -15,38 +15,55 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {ContentState, convertToRaw, convertFromRaw} from 'draft-js'; import { Value } from 'slate';
import Html from 'slate-html-serializer';
import Markdown as Md from 'slate-md-serializer';
import Plain from 'slate-plain-serializer';
import * as RichText from './RichText'; import * as RichText from './RichText';
import Markdown from './Markdown'; import Markdown from './Markdown';
import _clamp from 'lodash/clamp'; import _clamp from 'lodash/clamp';
type MessageFormat = 'html' | 'markdown'; type MessageFormat = 'rich' | 'markdown';
class HistoryItem { class HistoryItem {
// Keeping message for backwards-compatibility // Keeping message for backwards-compatibility
message: string; message: string;
rawContentState: RawDraftContentState; value: Value;
format: MessageFormat = 'html'; format: MessageFormat = 'rich';
constructor(contentState: ?ContentState, format: ?MessageFormat) { constructor(value: ?Value, format: ?MessageFormat) {
this.rawContentState = contentState ? convertToRaw(contentState) : null; this.rawContentState = contentState ? convertToRaw(contentState) : null;
this.format = format; this.format = format;
} }
toContentState(outputFormat: MessageFormat): ContentState { toValue(outputFormat: MessageFormat): Value {
const contentState = convertFromRaw(this.rawContentState);
if (outputFormat === 'markdown') { if (outputFormat === 'markdown') {
if (this.format === 'html') { if (this.format === 'rich') {
return ContentState.createFromText(RichText.stateToMarkdown(contentState)); // convert a rich formatted history entry to its MD equivalent
const markdown = new Markdown({});
return new Value({ data: markdown.serialize(value) });
// return ContentState.createFromText(RichText.stateToMarkdown(contentState));
} }
} else { else if (this.format === 'markdown') {
return value;
}
} else if (outputFormat === 'rich') {
if (this.format === 'markdown') { if (this.format === 'markdown') {
return RichText.htmlToContentState(new Markdown(contentState.getPlainText()).toHTML()); // convert MD formatted string to its rich equivalent.
const plain = new Plain({});
const md = new Md({});
return md.deserialize(plain.serialize(value));
// return RichText.htmlToContentState(new Markdown(contentState.getPlainText()).toHTML());
}
else if (this.format === 'rich') {
return value;
} }
} }
// history item has format === outputFormat log.error("unknown format -> outputFormat conversion");
return contentState; return value;
} }
} }
@ -69,16 +86,16 @@ export default class ComposerHistoryManager {
this.lastIndex = this.currentIndex; this.lastIndex = this.currentIndex;
} }
save(contentState: ContentState, format: MessageFormat) { save(value: Value, format: MessageFormat) {
const item = new HistoryItem(contentState, format); const item = new HistoryItem(value, format);
this.history.push(item); this.history.push(item);
this.currentIndex = this.lastIndex + 1; this.currentIndex = this.lastIndex + 1;
sessionStorage.setItem(`${this.prefix}[${this.lastIndex++}]`, JSON.stringify(item)); sessionStorage.setItem(`${this.prefix}[${this.lastIndex++}]`, JSON.stringify(item));
} }
getItem(offset: number, format: MessageFormat): ?ContentState { getItem(offset: number, format: MessageFormat): ?Value {
this.currentIndex = _clamp(this.currentIndex + offset, 0, this.lastIndex - 1); this.currentIndex = _clamp(this.currentIndex + offset, 0, this.lastIndex - 1);
const item = this.history[this.currentIndex]; const item = this.history[this.currentIndex];
return item ? item.toContentState(format) : null; return item ? item.toValue(format) : null;
} }
} }

View file

@ -18,9 +18,16 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import type SyntheticKeyboardEvent from 'react/lib/SyntheticKeyboardEvent'; import type SyntheticKeyboardEvent from 'react/lib/SyntheticKeyboardEvent';
import {Editor, EditorState, RichUtils, CompositeDecorator, Modifier, import { Editor } from 'slate-react';
getDefaultKeyBinding, KeyBindingUtil, ContentState, ContentBlock, SelectionState, import { Value } from 'slate';
Entity} from 'draft-js';
import Html from 'slate-html-serializer';
import Markdown as Md from 'slate-md-serializer';
import Plain from 'slate-plain-serializer';
// import {Editor, EditorState, RichUtils, CompositeDecorator, Modifier,
// getDefaultKeyBinding, KeyBindingUtil, ContentState, ContentBlock, SelectionState,
// Entity} from 'draft-js';
import classNames from 'classnames'; import classNames from 'classnames';
import escape from 'lodash/escape'; import escape from 'lodash/escape';
@ -61,20 +68,10 @@ const REGEX_EMOJI_WHITESPACE = new RegExp('(?:^|\\s)(' + asciiRegexp + ')\\s$');
const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000; const TYPING_USER_TIMEOUT = 10000, TYPING_SERVER_TIMEOUT = 30000;
const ZWS_CODE = 8203;
const ZWS = String.fromCharCode(ZWS_CODE); // zero width space
const ENTITY_TYPES = { const ENTITY_TYPES = {
AT_ROOM_PILL: 'ATROOMPILL', AT_ROOM_PILL: 'ATROOMPILL',
}; };
function stateToMarkdown(state) {
return __stateToMarkdown(state)
.replace(
ZWS, // draft-js-export-markdown adds these
''); // this is *not* a zero width space, trust me :)
}
function onSendMessageFailed(err, room) { function onSendMessageFailed(err, room) {
// XXX: temporary logging to try to diagnose // XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148 // https://github.com/vector-im/riot-web/issues/3148
@ -103,8 +100,6 @@ export default class MessageComposerInput extends React.Component {
}; };
static getKeyBinding(ev: SyntheticKeyboardEvent): string { static getKeyBinding(ev: SyntheticKeyboardEvent): string {
const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev);
// Restrict a subset of key bindings to ONLY having ctrl/meta* pressed and // Restrict a subset of key bindings to ONLY having ctrl/meta* pressed and
// importantly NOT having alt, shift, meta/ctrl* pressed. draft-js does not // importantly NOT having alt, shift, meta/ctrl* pressed. draft-js does not
// handle this in `getDefaultKeyBinding` so we do it ourselves here. // handle this in `getDefaultKeyBinding` so we do it ourselves here.
@ -121,7 +116,7 @@ export default class MessageComposerInput extends React.Component {
}[ev.keyCode]; }[ev.keyCode];
if (ctrlCmdCommand) { if (ctrlCmdCommand) {
if (!ctrlCmdOnly) { if (!isOnlyCtrlOrCmdKeyEvent(ev)) {
return null; return null;
} }
return ctrlCmdCommand; return ctrlCmdCommand;
@ -145,17 +140,6 @@ export default class MessageComposerInput extends React.Component {
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.onAction = this.onAction.bind(this);
this.handleReturn = this.handleReturn.bind(this);
this.handleKeyCommand = this.handleKeyCommand.bind(this);
this.onEditorContentChanged = this.onEditorContentChanged.bind(this);
this.onUpArrow = this.onUpArrow.bind(this);
this.onDownArrow = this.onDownArrow.bind(this);
this.onTab = this.onTab.bind(this);
this.onEscape = this.onEscape.bind(this);
this.setDisplayedCompletion = this.setDisplayedCompletion.bind(this);
this.onMarkdownToggleClicked = this.onMarkdownToggleClicked.bind(this);
this.onTextPasted = this.onTextPasted.bind(this);
const isRichtextEnabled = SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'); const isRichtextEnabled = SettingsStore.getValue('MessageComposerInput.isRichTextEnabled');
@ -185,6 +169,7 @@ export default class MessageComposerInput extends React.Component {
this.client = MatrixClientPeg.get(); this.client = MatrixClientPeg.get();
} }
/*
findPillEntities(contentState: ContentState, contentBlock: ContentBlock, callback) { findPillEntities(contentState: ContentState, contentBlock: ContentBlock, callback) {
contentBlock.findEntityRanges( contentBlock.findEntityRanges(
(character) => { (character) => {
@ -199,13 +184,15 @@ export default class MessageComposerInput extends React.Component {
}, callback, }, callback,
); );
} }
*/
/* /*
* "Does the right thing" to create an EditorState, based on: * "Does the right thing" to create an Editor value, based on:
* - whether we've got rich text mode enabled * - whether we've got rich text mode enabled
* - contentState was passed in * - contentState was passed in
*/ */
createEditorState(richText: boolean, contentState: ?ContentState): EditorState { createEditorState(richText: boolean, value: ?Value): Value {
/*
const decorators = richText ? RichText.getScopedRTDecorators(this.props) : const decorators = richText ? RichText.getScopedRTDecorators(this.props) :
RichText.getScopedMDDecorators(this.props); RichText.getScopedMDDecorators(this.props);
const shouldShowPillAvatar = !SettingsStore.getValue("Pill.shouldHidePillAvatar"); const shouldShowPillAvatar = !SettingsStore.getValue("Pill.shouldHidePillAvatar");
@ -239,7 +226,6 @@ export default class MessageComposerInput extends React.Component {
}, },
}); });
const compositeDecorator = new CompositeDecorator(decorators); const compositeDecorator = new CompositeDecorator(decorators);
let editorState = null; let editorState = null;
if (contentState) { if (contentState) {
editorState = EditorState.createWithContent(contentState, compositeDecorator); editorState = EditorState.createWithContent(contentState, compositeDecorator);
@ -248,6 +234,8 @@ export default class MessageComposerInput extends React.Component {
} }
return EditorState.moveFocusToEnd(editorState); return EditorState.moveFocusToEnd(editorState);
*/
return value;
} }
componentDidMount() { componentDidMount() {
@ -260,12 +248,14 @@ export default class MessageComposerInput extends React.Component {
} }
componentWillUpdate(nextProps, nextState) { componentWillUpdate(nextProps, nextState) {
/*
// this is dirty, but moving all this state to MessageComposer is dirtier // this is dirty, but moving all this state to MessageComposer is dirtier
if (this.props.onInputStateChanged && nextState !== this.state) { if (this.props.onInputStateChanged && nextState !== this.state) {
const state = this.getSelectionInfo(nextState.editorState); const state = this.getSelectionInfo(nextState.editorState);
state.isRichtextEnabled = nextState.isRichtextEnabled; state.isRichtextEnabled = nextState.isRichtextEnabled;
this.props.onInputStateChanged(state); this.props.onInputStateChanged(state);
} }
*/
} }
onAction = (payload) => { onAction = (payload) => {
@ -277,6 +267,7 @@ export default class MessageComposerInput extends React.Component {
case 'focus_composer': case 'focus_composer':
editor.focus(); editor.focus();
break; break;
/*
case 'insert_mention': { case 'insert_mention': {
// Pretend that we've autocompleted this user because keeping two code // Pretend that we've autocompleted this user because keeping two code
// paths for inserting a user pill is not fun // paths for inserting a user pill is not fun
@ -322,6 +313,7 @@ export default class MessageComposerInput extends React.Component {
} }
} }
break; break;
*/
} }
}; };
@ -372,7 +364,7 @@ export default class MessageComposerInput extends React.Component {
stopServerTypingTimer() { stopServerTypingTimer() {
if (this.serverTypingTimer) { if (this.serverTypingTimer) {
clearTimeout(this.servrTypingTimer); clearTimeout(this.serverTypingTimer);
this.serverTypingTimer = null; this.serverTypingTimer = null;
} }
} }
@ -492,9 +484,9 @@ export default class MessageComposerInput extends React.Component {
// Record the editor state for this room so that it can be retrieved after // Record the editor state for this room so that it can be retrieved after
// switching to another room and back // switching to another room and back
dis.dispatch({ dis.dispatch({
action: 'content_state', action: 'editor_state',
room_id: this.props.room.roomId, room_id: this.props.room.roomId,
content_state: state.editorState.getCurrentContent(), editor_state: state.editorState.getCurrentContent(),
}); });
if (!state.hasOwnProperty('originalEditorState')) { if (!state.hasOwnProperty('originalEditorState')) {
@ -528,28 +520,36 @@ export default class MessageComposerInput extends React.Component {
enableRichtext(enabled: boolean) { enableRichtext(enabled: boolean) {
if (enabled === this.state.isRichtextEnabled) return; if (enabled === this.state.isRichtextEnabled) return;
let contentState = null; // FIXME: this conversion should be handled in the store, surely
// i.e. "convert my current composer value into Rich or MD, as ComposerHistoryManager already does"
let value = null;
if (enabled) { if (enabled) {
const md = new Markdown(this.state.editorState.getCurrentContent().getPlainText()); // const md = new Markdown(this.state.editorState.getCurrentContent().getPlainText());
contentState = RichText.htmlToContentState(md.toHTML()); // contentState = RichText.htmlToContentState(md.toHTML());
const plain = new Plain({});
const md = new Md({});
value = md.deserialize(plain.serialize(this.state.editorState));
} else { } else {
let markdown = RichText.stateToMarkdown(this.state.editorState.getCurrentContent()); // let markdown = RichText.stateToMarkdown(this.state.editorState.getCurrentContent());
if (markdown[markdown.length - 1] === '\n') { // value = ContentState.createFromText(markdown);
markdown = markdown.substring(0, markdown.length - 1); // stateToMarkdown tacks on an extra newline (?!?)
} const markdown = new Markdown({});
contentState = ContentState.createFromText(markdown); value = Value({ data: markdown.serialize(value) });
} }
Analytics.setRichtextMode(enabled); Analytics.setRichtextMode(enabled);
this.setState({ this.setState({
editorState: this.createEditorState(enabled, contentState), editorState: this.createEditorState(enabled, value),
isRichtextEnabled: enabled, isRichtextEnabled: enabled,
}); });
SettingsStore.setValue("MessageComposerInput.isRichTextEnabled", null, SettingLevel.ACCOUNT, enabled); SettingsStore.setValue("MessageComposerInput.isRichTextEnabled", null, SettingLevel.ACCOUNT, enabled);
} }
handleKeyCommand = (command: string): boolean => { handleKeyCommand = (command: string): boolean => {
/*
if (command === 'toggle-mode') { if (command === 'toggle-mode') {
this.enableRichtext(!this.state.isRichtextEnabled); this.enableRichtext(!this.state.isRichtextEnabled);
return true; return true;
@ -658,11 +658,11 @@ export default class MessageComposerInput extends React.Component {
this.setState({editorState: newState}); this.setState({editorState: newState});
return true; return true;
} }
*/
return false; return false;
}; };
/*
onTextPasted(text: string, html?: string) { onTextPasted = (text: string, html?: string) => {
const currentSelection = this.state.editorState.getSelection(); const currentSelection = this.state.editorState.getSelection();
const currentContent = this.state.editorState.getCurrentContent(); const currentContent = this.state.editorState.getCurrentContent();
@ -682,9 +682,10 @@ export default class MessageComposerInput extends React.Component {
newEditorState = EditorState.forceSelection(newEditorState, contentState.getSelectionAfter()); newEditorState = EditorState.forceSelection(newEditorState, contentState.getSelectionAfter());
this.onEditorContentChanged(newEditorState); this.onEditorContentChanged(newEditorState);
return true; return true;
} };
*/
handleReturn(ev) { handleReturn = (ev) => {
/*
if (ev.shiftKey) { if (ev.shiftKey) {
this.onEditorContentChanged(RichUtils.insertSoftNewline(this.state.editorState)); this.onEditorContentChanged(RichUtils.insertSoftNewline(this.state.editorState));
return true; return true;
@ -701,15 +702,21 @@ export default class MessageComposerInput extends React.Component {
// See handleKeyCommand (when command === 'backspace') // See handleKeyCommand (when command === 'backspace')
return false; return false;
} }
*/
const contentState = this.state.editorState.getCurrentContent(); const contentState = this.state.editorState;
/*
if (!contentState.hasText()) { if (!contentState.hasText()) {
return true; return true;
} }
*/
const plain = new Plain({});
value = md.deserialize();
let contentText = contentState.getPlainText(), contentHTML; let contentText = plain.serialize(contentState);
let contentHTML;
/*
// Strip MD user (tab-completed) mentions to preserve plaintext mention behaviour. // Strip MD user (tab-completed) mentions to preserve plaintext mention behaviour.
// We have to do this now as opposed to after calculating the contentText for MD // We have to do this now as opposed to after calculating the contentText for MD
// mode because entity positions may not be maintained when using // mode because entity positions may not be maintained when using
@ -720,10 +727,12 @@ export default class MessageComposerInput extends React.Component {
// Some commands (/join) require pills to be replaced with their text content // Some commands (/join) require pills to be replaced with their text content
const commandText = this.removeMDLinks(contentState, ['#']); const commandText = this.removeMDLinks(contentState, ['#']);
*/
const commandText = contentText;
const cmd = SlashCommands.processInput(this.props.room.roomId, commandText); const cmd = SlashCommands.processInput(this.props.room.roomId, commandText);
if (cmd) { if (cmd) {
if (!cmd.error) { if (!cmd.error) {
this.historyManager.save(contentState, this.state.isRichtextEnabled ? 'html' : 'markdown'); this.historyManager.save(contentState, this.state.isRichtextEnabled ? 'rich' : 'markdown');
this.setState({ this.setState({
editorState: this.createEditorState(), editorState: this.createEditorState(),
}); });
@ -754,6 +763,7 @@ export default class MessageComposerInput extends React.Component {
const quotingEv = RoomViewStore.getQuotingEvent(); const quotingEv = RoomViewStore.getQuotingEvent();
if (this.state.isRichtextEnabled) { if (this.state.isRichtextEnabled) {
/*
// We should only send HTML if any block is styled or contains inline style // We should only send HTML if any block is styled or contains inline style
let shouldSendHTML = false; let shouldSendHTML = false;
@ -788,6 +798,8 @@ export default class MessageComposerInput extends React.Component {
}); });
shouldSendHTML = hasLink; shouldSendHTML = hasLink;
} }
*/
let shouldSendHTML = true;
if (shouldSendHTML) { if (shouldSendHTML) {
contentHTML = HtmlUtils.processHtmlForSending( contentHTML = HtmlUtils.processHtmlForSending(
RichText.contentStateToHTML(contentState), RichText.contentStateToHTML(contentState),
@ -797,6 +809,7 @@ export default class MessageComposerInput extends React.Component {
// Use the original contentState because `contentText` has had mentions // Use the original contentState because `contentText` has had mentions
// stripped and these need to end up in contentHTML. // stripped and these need to end up in contentHTML.
/*
// Replace all Entities of type `LINK` with markdown link equivalents. // Replace all Entities of type `LINK` with markdown link equivalents.
// TODO: move this into `Markdown` and do the same conversion in the other // TODO: move this into `Markdown` and do the same conversion in the other
// two places (toggling from MD->RT mode and loading MD history into RT mode) // two places (toggling from MD->RT mode and loading MD history into RT mode)
@ -817,7 +830,7 @@ export default class MessageComposerInput extends React.Component {
}); });
return blockText; return blockText;
}).join('\n'); }).join('\n');
*/
const md = new Markdown(pt); const md = new Markdown(pt);
// if contains no HTML and we're not quoting (needing HTML) // if contains no HTML and we're not quoting (needing HTML)
if (md.isPlainText() && !quotingEv) { if (md.isPlainText() && !quotingEv) {
@ -832,7 +845,7 @@ export default class MessageComposerInput extends React.Component {
this.historyManager.save( this.historyManager.save(
contentState, contentState,
this.state.isRichtextEnabled ? 'html' : 'markdown', this.state.isRichtextEnabled ? 'rich' : 'markdown',
); );
if (contentText.startsWith('/me')) { if (contentText.startsWith('/me')) {
@ -881,7 +894,7 @@ export default class MessageComposerInput extends React.Component {
}); });
return true; return true;
} };
onUpArrow = (e) => { onUpArrow = (e) => {
this.onVerticalArrow(e, true); this.onVerticalArrow(e, true);
@ -896,6 +909,7 @@ export default class MessageComposerInput extends React.Component {
return; return;
} }
/*
// Select history only if we are not currently auto-completing // Select history only if we are not currently auto-completing
if (this.autocomplete.state.completionList.length === 0) { if (this.autocomplete.state.completionList.length === 0) {
// Don't go back in history if we're in the middle of a multi-line message // Don't go back in history if we're in the middle of a multi-line message
@ -927,8 +941,10 @@ export default class MessageComposerInput extends React.Component {
this.moveAutocompleteSelection(up); this.moveAutocompleteSelection(up);
e.preventDefault(); e.preventDefault();
} }
*/
}; };
/*
selectHistory = async (up) => { selectHistory = async (up) => {
const delta = up ? -1 : 1; const delta = up ? -1 : 1;
@ -950,7 +966,7 @@ export default class MessageComposerInput extends React.Component {
return; return;
} }
const newContent = this.historyManager.getItem(delta, this.state.isRichtextEnabled ? 'html' : 'markdown'); const newContent = this.historyManager.getItem(delta, this.state.isRichtextEnabled ? 'rich' : 'markdown');
if (!newContent) return false; if (!newContent) return false;
let editorState = EditorState.push( let editorState = EditorState.push(
this.state.editorState, this.state.editorState,
@ -969,6 +985,7 @@ export default class MessageComposerInput extends React.Component {
this.setState({editorState}); this.setState({editorState});
return true; return true;
}; };
*/
onTab = async (e) => { onTab = async (e) => {
this.setState({ this.setState({
@ -1061,8 +1078,9 @@ export default class MessageComposerInput extends React.Component {
return true; return true;
}; };
onFormatButtonClicked(name: "bold" | "italic" | "strike" | "code" | "underline" | "quote" | "bullet" | "numbullet", e) { onFormatButtonClicked = (name: "bold" | "italic" | "strike" | "code" | "underline" | "quote" | "bullet" | "numbullet", e) => {
e.preventDefault(); // don't steal focus from the editor! e.preventDefault(); // don't steal focus from the editor!
/*
const command = { const command = {
code: 'code-block', code: 'code-block',
quote: 'blockquote', quote: 'blockquote',
@ -1070,7 +1088,8 @@ export default class MessageComposerInput extends React.Component {
numbullet: 'ordered-list-item', numbullet: 'ordered-list-item',
}[name] || name; }[name] || name;
this.handleKeyCommand(command); this.handleKeyCommand(command);
} */
};
/* returns inline style and block type of current SelectionState so MessageComposer can render formatting /* returns inline style and block type of current SelectionState so MessageComposer can render formatting
buttons. */ buttons. */

View file

@ -14,16 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import dis from '../dispatcher'; import dis from '../dispatcher';
import {Store} from 'flux/utils'; import { Store } from 'flux/utils';
import {convertToRaw, convertFromRaw} from 'draft-js';
const INITIAL_STATE = { const INITIAL_STATE = {
editorStateMap: localStorage.getItem('content_state') ? // a map of room_id to rich text editor composer state
JSON.parse(localStorage.getItem('content_state')) : {}, editorStateMap: localStorage.getItem('editor_state') ?
JSON.parse(localStorage.getItem('editor_state')) : {},
}; };
/** /**
* A class for storing application state to do with the message composer. This is a simple * A class for storing application state to do with the message composer (specifically
* in-progress message drafts). This is a simple
* flux store that listens for actions and updates its state accordingly, informing any * flux store that listens for actions and updates its state accordingly, informing any
* listeners (views) of state changes. * listeners (views) of state changes.
*/ */
@ -42,7 +43,7 @@ class MessageComposerStore extends Store {
__onDispatch(payload) { __onDispatch(payload) {
switch (payload.action) { switch (payload.action) {
case 'content_state': case 'editor_state':
this._contentState(payload); this._contentState(payload);
break; break;
case 'on_logged_out': case 'on_logged_out':
@ -53,16 +54,15 @@ class MessageComposerStore extends Store {
_contentState(payload) { _contentState(payload) {
const editorStateMap = this._state.editorStateMap; const editorStateMap = this._state.editorStateMap;
editorStateMap[payload.room_id] = convertToRaw(payload.content_state); editorStateMap[payload.room_id] = payload.editor_state;
localStorage.setItem('content_state', JSON.stringify(editorStateMap)); localStorage.setItem('editor_state', JSON.stringify(editorStateMap));
this._setState({ this._setState({
editorStateMap: editorStateMap, editorStateMap: editorStateMap,
}); });
} }
getContentState(roomId) { getContentState(roomId) {
return this._state.editorStateMap[roomId] ? return this._state.editorStateMap[roomId];
convertFromRaw(this._state.editorStateMap[roomId]) : null;
} }
reset() { reset() {