This repository has been archived by the owner on Sep 11, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 832
New composer: support forcing auto complete on name by hitting tab #3349
Merged
Merged
Changes from 12 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
713205e
close autocomplete when removing auto-completed part
bwindels 0f6465a
don't close autocomplete when hitting tab
bwindels f76a23d
return promise from updating autocomplete
bwindels 68c2bb7
introduce `transform` method so update can be called with a position
bwindels f02713d
force completion when hitting tab
bwindels f5bb872
some cleanup
bwindels e0ec827
extra docs
bwindels 8e66d38
don't crash on race with room members and initial composer render
bwindels d8bb9ec
bring insert method inline with transform callback, add docs
bwindels 994bcb5
dont expect rendered to be called from `range.replace()` anymore
bwindels c44fbb7
fix bug when replacing range starting at end of previous part
bwindels 85efb71
add visual bell when no replacements are available
bwindels 29f96e6
remove leftover code
bwindels eddaece
add visual bell color to theme + choose better value for dark mode
bwindels File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,15 @@ limitations under the License. | |
white-space: nowrap; | ||
} | ||
|
||
@keyframes visualbell { | ||
from { background-color: #faa; } | ||
to { background-color: $primary-bg-color; } | ||
} | ||
|
||
&.mx_BasicMessageComposer_input_error { | ||
animation: 0.2s visualbell; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also may want Nad to weigh in on the visual flash as it currently highlight the bit where the text box actually is which is normally not visible so looks a bit odd. Not sure what the best way would be to make it look better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
.mx_BasicMessageComposer_input { | ||
white-space: pre-wrap; | ||
word-wrap: break-word; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
import classNames from 'classnames'; | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import EditorModel from '../../../editor/model'; | ||
|
@@ -75,10 +77,10 @@ export default class BasicMessageEditor extends React.Component { | |
this._modifiedFlag = false; | ||
} | ||
|
||
_replaceEmoticon = (caret, inputType, diff) => { | ||
_replaceEmoticon = (caretPosition, inputType, diff) => { | ||
const {model} = this.props; | ||
const range = model.startRange(caret); | ||
// expand range max 8 characters backwards from caret, | ||
const range = model.startRange(caretPosition); | ||
// expand range max 8 characters backwards from caretPosition, | ||
// as a space to look for an emoticon | ||
let n = 8; | ||
range.expandBackwardsWhile((index, offset) => { | ||
|
@@ -91,6 +93,7 @@ export default class BasicMessageEditor extends React.Component { | |
const query = emoticonMatch[1].toLowerCase().replace("-", ""); | ||
const data = EMOJIBASE.find(e => e.emoticon ? e.emoticon.toLowerCase() === query : false); | ||
if (data) { | ||
const {partCreator} = model; | ||
const hasPrecedingSpace = emoticonMatch[0][0] === " "; | ||
// we need the range to only comprise of the emoticon | ||
// because we'll replace the whole range with an emoji, | ||
|
@@ -99,7 +102,7 @@ export default class BasicMessageEditor extends React.Component { | |
range.moveStart(emoticonMatch.index + (hasPrecedingSpace ? 1 : 0)); | ||
// this returns the amount of added/removed characters during the replace | ||
// so the caret position can be adjusted. | ||
return range.replace([this.props.model.partCreator.plain(data.unicode + " ")]); | ||
return range.replace([partCreator.plain(data.unicode + " ")]); | ||
} | ||
} | ||
} | ||
|
@@ -160,7 +163,7 @@ export default class BasicMessageEditor extends React.Component { | |
} | ||
|
||
_refreshLastCaretIfNeeded() { | ||
// TODO: needed when going up and down in editing messages ... not sure why yet | ||
// XXX: needed when going up and down in editing messages ... not sure why yet | ||
// because the editors should stop doing this when when blurred ... | ||
// maybe it's on focus and the _editorRef isn't available yet or something. | ||
if (!this._editorRef) { | ||
|
@@ -269,6 +272,9 @@ export default class BasicMessageEditor extends React.Component { | |
default: | ||
return; // don't preventDefault on anything else | ||
} | ||
} else if (event.key === "Tab") { | ||
this._tabCompleteName(); | ||
handled = true; | ||
} | ||
} | ||
if (handled) { | ||
|
@@ -277,6 +283,32 @@ export default class BasicMessageEditor extends React.Component { | |
} | ||
} | ||
|
||
async _tabCompleteName() { | ||
try { | ||
await new Promise(resolve => this.setState({showVisualBell: false}, resolve)); | ||
const {model} = this.props; | ||
const caret = this.getCaret(); | ||
const position = model.positionForOffset(caret.offset, caret.atNodeEnd); | ||
const range = model.startRange(position); | ||
range.expandBackwardsWhile((index, offset, part) => { | ||
return part.text[offset] !== " " && (part.type === "plain" || part.type === "pill-candidate"); | ||
}); | ||
const {partCreator} = model; | ||
// await for auto-complete to be open | ||
await model.transform(() => { | ||
const addedLen = range.replace([partCreator.pillCandidate(range.text)]); | ||
return model.positionForOffset(caret.offset + addedLen, true); | ||
}); | ||
await model.autoComplete.onTab(); | ||
if (!model.autoComplete.hasSelection()) { | ||
this.setState({showVisualBell: true}); | ||
model.autoComplete.close(); | ||
} | ||
} catch (err) { | ||
console.error(err); | ||
} | ||
} | ||
|
||
isModified() { | ||
return this._modifiedFlag; | ||
} | ||
|
@@ -304,7 +336,14 @@ export default class BasicMessageEditor extends React.Component { | |
// not really, but we could not serialize the parts, and just change the autoCompleter | ||
partCreator.setAutoCompleteCreator(autoCompleteCreator( | ||
() => this._autocompleteRef, | ||
query => this.setState({query}), | ||
query => { | ||
return new Promise(resolve => this.setState({query}, resolve)); | ||
// if setState | ||
// if (this.state.query === query) { | ||
// return Promise.resolve(); | ||
// } else { | ||
// } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unintentionally left in? |
||
}, | ||
)); | ||
this.historyManager = new HistoryManager(partCreator); | ||
// initial render of model | ||
|
@@ -345,7 +384,10 @@ export default class BasicMessageEditor extends React.Component { | |
/> | ||
</div>); | ||
} | ||
return (<div className="mx_BasicMessageComposer"> | ||
const classes = classNames("mx_BasicMessageComposer", { | ||
"mx_BasicMessageComposer_input_error": this.state.showVisualBell, | ||
}); | ||
return (<div className={classes}> | ||
{ autoComplete } | ||
<div | ||
className="mx_BasicMessageComposer_input" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
Copyright 2019 The Matrix.org Foundation C.I.C. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
export default class DocumentOffset { | ||
constructor(offset, atEnd) { | ||
this.offset = offset; | ||
this.atEnd = atEnd; | ||
} | ||
|
||
asPosition(model) { | ||
return model.positionForOffset(this.offset, this.atEnd); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be defined in the theme?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.