mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 21:24:59 +08:00
unify setState() and onChange()
also make emoji autocomplete work again also remove the onInputContentChanged prop also slateify the onInputStateChanged prop
This commit is contained in:
parent
a247ea2f77
commit
7405b49b44
@ -35,7 +35,6 @@ export default class MessageComposer extends React.Component {
|
|||||||
this.onUploadFileSelected = this.onUploadFileSelected.bind(this);
|
this.onUploadFileSelected = this.onUploadFileSelected.bind(this);
|
||||||
this.uploadFiles = this.uploadFiles.bind(this);
|
this.uploadFiles = this.uploadFiles.bind(this);
|
||||||
this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
|
this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
|
||||||
this.onInputContentChanged = this.onInputContentChanged.bind(this);
|
|
||||||
this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this);
|
this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this);
|
||||||
this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this);
|
this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this);
|
||||||
this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
|
this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
|
||||||
@ -44,13 +43,10 @@ export default class MessageComposer extends React.Component {
|
|||||||
this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);
|
this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
autocompleteQuery: '',
|
|
||||||
selection: null,
|
|
||||||
inputState: {
|
inputState: {
|
||||||
style: [],
|
marks: [],
|
||||||
blockType: null,
|
blockType: null,
|
||||||
isRichtextEnabled: SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'),
|
isRichtextEnabled: SettingsStore.getValue('MessageComposerInput.isRichTextEnabled'),
|
||||||
wordCount: 0,
|
|
||||||
},
|
},
|
||||||
showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'),
|
showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'),
|
||||||
isQuoting: Boolean(RoomViewStore.getQuotingEvent()),
|
isQuoting: Boolean(RoomViewStore.getQuotingEvent()),
|
||||||
@ -209,13 +205,6 @@ export default class MessageComposer extends React.Component {
|
|||||||
// this._startCallApp(true);
|
// this._startCallApp(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
onInputContentChanged(content: string, selection: {start: number, end: number}) {
|
|
||||||
this.setState({
|
|
||||||
autocompleteQuery: content,
|
|
||||||
selection,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onInputStateChanged(inputState) {
|
onInputStateChanged(inputState) {
|
||||||
this.setState({inputState});
|
this.setState({inputState});
|
||||||
}
|
}
|
||||||
@ -348,7 +337,6 @@ export default class MessageComposer extends React.Component {
|
|||||||
room={this.props.room}
|
room={this.props.room}
|
||||||
placeholder={placeholderText}
|
placeholder={placeholderText}
|
||||||
onFilesPasted={this.uploadFiles}
|
onFilesPasted={this.uploadFiles}
|
||||||
onContentChanged={this.onInputContentChanged}
|
|
||||||
onInputStateChanged={this.onInputStateChanged} />,
|
onInputStateChanged={this.onInputStateChanged} />,
|
||||||
formattingButton,
|
formattingButton,
|
||||||
stickerpickerButton,
|
stickerpickerButton,
|
||||||
@ -365,10 +353,10 @@ export default class MessageComposer extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {style, blockType} = this.state.inputState;
|
const {marks, blockType} = this.state.inputState;
|
||||||
const formatButtons = ["bold", "italic", "strike", "underline", "code", "quote", "bullet", "numbullet"].map(
|
const formatButtons = ["bold", "italic", "strike", "underline", "code", "quote", "bullet", "numbullet"].map(
|
||||||
(name) => {
|
(name) => {
|
||||||
const active = style.includes(name) || blockType === name;
|
const active = marks.includes(name) || blockType === name;
|
||||||
const suffix = active ? '-o-n' : '';
|
const suffix = active ? '-o-n' : '';
|
||||||
const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
|
const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
|
||||||
const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
|
const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
|
||||||
|
@ -184,23 +184,6 @@ export default class MessageComposerInput extends React.Component {
|
|||||||
this.direction = '';
|
this.direction = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
findPillEntities(contentState: ContentState, contentBlock: ContentBlock, callback) {
|
|
||||||
contentBlock.findEntityRanges(
|
|
||||||
(character) => {
|
|
||||||
const entityKey = character.getEntity();
|
|
||||||
return (
|
|
||||||
entityKey !== null &&
|
|
||||||
(
|
|
||||||
contentState.getEntity(entityKey).getType() === 'LINK' ||
|
|
||||||
contentState.getEntity(entityKey).getType() === ENTITY_TYPES.AT_ROOM_PILL
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}, callback,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Does the right thing" to create an Editor value, 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
|
||||||
@ -226,14 +209,12 @@ 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) => {
|
||||||
@ -375,6 +356,19 @@ export default class MessageComposerInput extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (editorState.document.getFirstText().text !== '') {
|
||||||
|
this.onTypingActivity();
|
||||||
|
} else {
|
||||||
|
this.onFinishedTyping();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// XXX: what was this ever doing?
|
||||||
|
if (!state.hasOwnProperty('originalEditorState')) {
|
||||||
|
state.originalEditorState = null;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
editorState = RichText.attachImmutableEntitiesToEmoji(editorState);
|
editorState = RichText.attachImmutableEntitiesToEmoji(editorState);
|
||||||
|
|
||||||
@ -405,6 +399,10 @@ export default class MessageComposerInput extends React.Component {
|
|||||||
// Reset selection
|
// Reset selection
|
||||||
editorState = EditorState.forceSelection(editorState, currentSelection);
|
editorState = EditorState.forceSelection(editorState, currentSelection);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const text = editorState.startText.text;
|
||||||
|
const currentStartOffset = editorState.startOffset;
|
||||||
|
|
||||||
// Automatic replacement of plaintext emoji to Unicode emoji
|
// Automatic replacement of plaintext emoji to Unicode emoji
|
||||||
if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
|
if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
|
||||||
@ -415,23 +413,26 @@ export default class MessageComposerInput extends React.Component {
|
|||||||
const emojiUc = asciiList[emojiMatch[1]];
|
const emojiUc = asciiList[emojiMatch[1]];
|
||||||
// hex unicode -> shortname -> actual unicode
|
// hex unicode -> shortname -> actual unicode
|
||||||
const unicodeEmoji = shortnameToUnicode(EMOJI_UNICODE_TO_SHORTNAME[emojiUc]);
|
const unicodeEmoji = shortnameToUnicode(EMOJI_UNICODE_TO_SHORTNAME[emojiUc]);
|
||||||
const newContentState = Modifier.replaceText(
|
|
||||||
editorState.getCurrentContent(),
|
const range = Range.create({
|
||||||
currentSelection.merge({
|
anchorKey: editorState.selection.startKey,
|
||||||
anchorOffset: currentStartOffset - emojiMatch[1].length - 1,
|
anchorOffset: currentStartOffset - emojiMatch[1].length - 1,
|
||||||
focusOffset: currentStartOffset,
|
focusKey: editorState.selection.startKey,
|
||||||
}),
|
focusOffset: currentStartOffset,
|
||||||
unicodeEmoji,
|
});
|
||||||
);
|
change = change.insertTextAtRange(range, unicodeEmoji);
|
||||||
editorState = EditorState.push(
|
editorState = change.value;
|
||||||
editorState,
|
|
||||||
newContentState,
|
|
||||||
'insert-characters',
|
|
||||||
);
|
|
||||||
editorState = EditorState.forceSelection(editorState, newContentState.getSelectionAfter());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
// Record the editor state for this room so that it can be retrieved after
|
||||||
|
// switching to another room and back
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'editor_state',
|
||||||
|
room_id: this.props.room.roomId,
|
||||||
|
editor_state: editorState,
|
||||||
|
});
|
||||||
|
|
||||||
/* Since a modification was made, set originalEditorState to null, since newState is now our original */
|
/* Since a modification was made, set originalEditorState to null, since newState is now our original */
|
||||||
this.setState({
|
this.setState({
|
||||||
editorState,
|
editorState,
|
||||||
@ -439,68 +440,6 @@ export default class MessageComposerInput extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* We're overriding setState here because it's the most convenient way to monitor changes to the editorState.
|
|
||||||
* Doing it using a separate function that calls setState is a possibility (and was the old approach), but that
|
|
||||||
* approach requires a callback and an extra setState whenever trying to set multiple state properties.
|
|
||||||
*
|
|
||||||
* @param state
|
|
||||||
* @param callback
|
|
||||||
*/
|
|
||||||
setState(state, callback) {
|
|
||||||
/*
|
|
||||||
if (state.editorState != null) {
|
|
||||||
state.editorState = RichText.attachImmutableEntitiesToEmoji(
|
|
||||||
state.editorState);
|
|
||||||
|
|
||||||
// Hide the autocomplete if the cursor location changes but the plaintext
|
|
||||||
// content stays the same. We don't hide if the pt has changed because the
|
|
||||||
// autocomplete will probably have different completions to show.
|
|
||||||
if (
|
|
||||||
!state.editorState.getSelection().equals(
|
|
||||||
this.state.editorState.getSelection(),
|
|
||||||
)
|
|
||||||
&& state.editorState.getCurrentContent().getPlainText() ===
|
|
||||||
this.state.editorState.getCurrentContent().getPlainText()
|
|
||||||
) {
|
|
||||||
this.autocomplete.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.editorState.getCurrentContent().hasText()) {
|
|
||||||
this.onTypingActivity();
|
|
||||||
} else {
|
|
||||||
this.onFinishedTyping();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record the editor state for this room so that it can be retrieved after
|
|
||||||
// switching to another room and back
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'editor_state',
|
|
||||||
room_id: this.props.room.roomId,
|
|
||||||
editor_state: state.editorState.getCurrentContent(),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!state.hasOwnProperty('originalEditorState')) {
|
|
||||||
state.originalEditorState = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
super.setState(state, () => {
|
|
||||||
if (callback != null) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
const textContent = this.state.editorState.getCurrentContent().getPlainText();
|
|
||||||
const selection = RichText.selectionStateToTextOffsets(
|
|
||||||
this.state.editorState.getSelection(),
|
|
||||||
this.state.editorState.getCurrentContent().getBlocksAsArray());
|
|
||||||
if (this.props.onContentChanged) {
|
|
||||||
this.props.onContentChanged(textContent, selection);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
enableRichtext(enabled: boolean) {
|
enableRichtext(enabled: boolean) {
|
||||||
if (enabled === this.state.isRichtextEnabled) return;
|
if (enabled === this.state.isRichtextEnabled) return;
|
||||||
|
|
||||||
@ -1133,36 +1072,12 @@ export default class MessageComposerInput extends React.Component {
|
|||||||
/* 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. */
|
||||||
getSelectionInfo(editorState: Value) {
|
getSelectionInfo(editorState: Value) {
|
||||||
return {};
|
|
||||||
/*
|
|
||||||
const styleName = {
|
|
||||||
BOLD: _td('bold'),
|
|
||||||
ITALIC: _td('italic'),
|
|
||||||
STRIKETHROUGH: _td('strike'),
|
|
||||||
UNDERLINE: _td('underline'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const originalStyle = editorState.getCurrentInlineStyle().toArray();
|
|
||||||
const style = originalStyle
|
|
||||||
.map((style) => styleName[style] || null)
|
|
||||||
.filter((styleName) => !!styleName);
|
|
||||||
|
|
||||||
const blockName = {
|
|
||||||
'code-block': _td('code'),
|
|
||||||
'blockquote': _td('quote'),
|
|
||||||
'unordered-list-item': _td('bullet'),
|
|
||||||
'ordered-list-item': _td('numbullet'),
|
|
||||||
};
|
|
||||||
const originalBlockType = editorState.getCurrentContent()
|
|
||||||
.getBlockForKey(editorState.getSelection().getStartKey())
|
|
||||||
.getType();
|
|
||||||
const blockType = blockName[originalBlockType] || null;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
style,
|
marks: editorState.activeMarks,
|
||||||
blockType,
|
// XXX: shouldn't we return all the types of blocks in the current selection,
|
||||||
|
// not just the anchor?
|
||||||
|
blockType: editorState.anchorBlock.type,
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAutocompleteQuery(editorState: Value) {
|
getAutocompleteQuery(editorState: Value) {
|
||||||
|
Loading…
Reference in New Issue
Block a user