Merge remote-tracking branch 'origin/develop' into dbkr/groups_better_groupview
This commit is contained in:
commit
696c72be2b
12 changed files with 95 additions and 123 deletions
|
@ -190,7 +190,7 @@ export default React.createClass({
|
|||
</div>;
|
||||
|
||||
let nameNode;
|
||||
if (summary.profile.name) {
|
||||
if (summary.profile && summary.profile.name) {
|
||||
nameNode = <div className="mx_RoomHeader_name">
|
||||
<span>{summary.profile.name}</span>
|
||||
<span className="mx_GroupView_header_groupid">
|
||||
|
@ -203,6 +203,8 @@ export default React.createClass({
|
|||
</div>;
|
||||
}
|
||||
|
||||
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
|
||||
|
||||
return (
|
||||
<div className="mx_GroupView">
|
||||
<div className="mx_RoomHeader">
|
||||
|
@ -210,7 +212,7 @@ export default React.createClass({
|
|||
<div className="mx_RoomHeader_avatar">
|
||||
<GroupAvatar
|
||||
groupId={this.props.groupId}
|
||||
groupAvatarUrl={summary.profile.avatar_url}
|
||||
groupAvatarUrl={groupAvatarUrl}
|
||||
width={48} height={48}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -61,9 +61,6 @@ export default withMatrixClient(React.createClass({
|
|||
this._fetch();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
},
|
||||
|
||||
_onCreateGroupClick: function() {
|
||||
const CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog");
|
||||
Modal.createDialog(CreateGroupDialog);
|
||||
|
@ -73,7 +70,7 @@ export default withMatrixClient(React.createClass({
|
|||
this.props.matrixClient.getJoinedGroups().done((result) => {
|
||||
this.setState({groups: result.groups, error: null});
|
||||
}, (err) => {
|
||||
this.setState({result: null, error: err});
|
||||
this.setState({groups: null, error: err});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -93,12 +90,12 @@ export default withMatrixClient(React.createClass({
|
|||
);
|
||||
});
|
||||
content = <div>
|
||||
<div>{_t('You are a member of these groups')}:</div>
|
||||
<div>{_t('You are a member of these groups:')}</div>
|
||||
{groupNodes}
|
||||
</div>;
|
||||
} else if (this.state.error) {
|
||||
content = <div className="mx_MyGroups_error">
|
||||
Error whilst fetching joined groups
|
||||
{_t('Error whilst fetching joined groups')}
|
||||
</div>;
|
||||
} else {
|
||||
content = <Loader />;
|
||||
|
|
|
@ -130,10 +130,10 @@ export default React.createClass({
|
|||
|
||||
render: function() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
|
||||
if (this.state.creating) {
|
||||
return <Loader />;
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
let createErrorNode;
|
||||
|
@ -154,29 +154,32 @@ export default React.createClass({
|
|||
>
|
||||
<form onSubmit={this._onFormSubmit}>
|
||||
<div className="mx_Dialog_content">
|
||||
<div className="mx_CreateGroupDialog_label">
|
||||
<label htmlFor="groupname">{_t('Group Name')}</label>
|
||||
<div className="mx_CreateGroupDialog_inputRow">
|
||||
<div className="mx_CreateGroupDialog_label">
|
||||
<label htmlFor="groupname">{_t('Group Name')}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="groupname" className="mx_CreateGroupDialog_input"
|
||||
autoFocus={true} size="64"
|
||||
placeholder={_t('Example')}
|
||||
onChange={this._onGroupNameChange}
|
||||
value={this.state.groupName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input id="groupname" className="mx_CreateGroupDialog_input"
|
||||
autoFocus={true} size="64"
|
||||
placeholder={_t('Example')}
|
||||
onChange={this._onGroupNameChange}
|
||||
value={this.state.groupName}
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<div className="mx_CreateGroupDialog_label">
|
||||
<label htmlFor="groupid">{_t('Group ID')}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="groupid" className="mx_CreateGroupDialog_input"
|
||||
size="64"
|
||||
placeholder={_t('+example:%(domain)s', {domain: MatrixClientPeg.get().getDomain()})}
|
||||
onChange={this._onGroupIdChange}
|
||||
onBlur={this._onGroupIdBlur}
|
||||
value={this.state.groupId}
|
||||
/>
|
||||
<div className="mx_CreateGroupDialog_inputRow">
|
||||
<div className="mx_CreateGroupDialog_label">
|
||||
<label htmlFor="groupid">{_t('Group ID')}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input id="groupid" className="mx_CreateGroupDialog_input"
|
||||
size="64"
|
||||
placeholder={_t('+example:%(domain)s', {domain: MatrixClientPeg.get().getDomain()})}
|
||||
onChange={this._onGroupIdChange}
|
||||
onBlur={this._onGroupIdBlur}
|
||||
value={this.state.groupId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="error">
|
||||
{this.state.groupIdError}
|
||||
|
|
|
@ -50,6 +50,7 @@ export default class Autocomplete extends React.Component {
|
|||
}
|
||||
|
||||
complete(query, selection) {
|
||||
this.queryRequested = query;
|
||||
if (this.debounceCompletionsRequest) {
|
||||
clearTimeout(this.debounceCompletionsRequest);
|
||||
}
|
||||
|
@ -74,16 +75,25 @@ export default class Autocomplete extends React.Component {
|
|||
|
||||
const deferred = Q.defer();
|
||||
this.debounceCompletionsRequest = setTimeout(() => {
|
||||
getCompletions(
|
||||
query, selection, this.state.forceComplete,
|
||||
).then((completions) => {
|
||||
this.processCompletions(completions);
|
||||
this.processQuery(query, selection).then(() => {
|
||||
deferred.resolve();
|
||||
});
|
||||
}, autocompleteDelay);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
processQuery(query, selection) {
|
||||
return getCompletions(
|
||||
query, selection, this.state.forceComplete,
|
||||
).then((completions) => {
|
||||
// Only ever process the completions for the most recent query being processed
|
||||
if (query !== this.queryRequested) {
|
||||
return;
|
||||
}
|
||||
this.processCompletions(completions);
|
||||
});
|
||||
}
|
||||
|
||||
processCompletions(completions) {
|
||||
const completionList = flatMap(completions, (provider) => provider.completions);
|
||||
|
||||
|
@ -105,14 +115,9 @@ export default class Autocomplete extends React.Component {
|
|||
}
|
||||
|
||||
let hide = this.state.hide;
|
||||
// These are lists of booleans that indicate whether whether the corresponding provider had a matching pattern
|
||||
const oldMatches = this.state.completions.map((completion) => !!completion.command.command),
|
||||
newMatches = completions.map((completion) => !!completion.command.command);
|
||||
|
||||
// So, essentially, we re-show autocomplete if any provider finds a new pattern or stops finding an old one
|
||||
if (!isEqual(oldMatches, newMatches)) {
|
||||
hide = false;
|
||||
}
|
||||
// If `completion.command.command` is truthy, then a provider has matched with the query
|
||||
const anyMatches = completions.some((completion) => !!completion.command.command);
|
||||
hide = !anyMatches;
|
||||
|
||||
this.setState({
|
||||
completions,
|
||||
|
|
|
@ -226,21 +226,6 @@ export default class MessageComposer extends React.Component {
|
|||
this.setState({inputState});
|
||||
}
|
||||
|
||||
onUpArrow() {
|
||||
return this.refs.autocomplete.onUpArrow();
|
||||
}
|
||||
|
||||
onDownArrow() {
|
||||
return this.refs.autocomplete.onDownArrow();
|
||||
}
|
||||
|
||||
_tryComplete(): boolean {
|
||||
if (this.refs.autocomplete) {
|
||||
return this.refs.autocomplete.onCompletionClicked();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_onAutocompleteConfirm(range, completion) {
|
||||
if (this.messageComposerInput) {
|
||||
this.messageComposerInput.setDisplayedCompletion(range, completion);
|
||||
|
@ -267,8 +252,7 @@ export default class MessageComposer extends React.Component {
|
|||
const uploadInputStyle = {display: 'none'};
|
||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput" +
|
||||
(UserSettingsStore.isFeatureEnabled('rich_text_editor') ? "" : "Old"));
|
||||
const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput");
|
||||
|
||||
const controls = [];
|
||||
|
||||
|
@ -351,8 +335,7 @@ export default class MessageComposer extends React.Component {
|
|||
title={_t("Show Text Formatting Toolbar")}
|
||||
src="img/button-text-formatting.svg"
|
||||
onClick={this.onToggleFormattingClicked}
|
||||
style={{visibility: this.state.showFormatting ||
|
||||
!UserSettingsStore.isFeatureEnabled('rich_text_editor') ? 'hidden' : 'visible'}}
|
||||
style={{visibility: this.state.showFormatting ? 'hidden' : 'visible'}}
|
||||
key="controls_formatting" />
|
||||
);
|
||||
|
||||
|
@ -367,10 +350,7 @@ export default class MessageComposer extends React.Component {
|
|||
room={this.props.room}
|
||||
placeholder={placeholderText}
|
||||
tryComplete={this._tryComplete}
|
||||
onUpArrow={this.onUpArrow}
|
||||
onDownArrow={this.onDownArrow}
|
||||
onFilesPasted={this.uploadFiles}
|
||||
tabComplete={this.props.tabComplete} // used for old messagecomposerinput/tabcomplete
|
||||
onContentChanged={this.onInputContentChanged}
|
||||
onInputStateChanged={this.onInputStateChanged} />,
|
||||
formattingButton,
|
||||
|
@ -389,18 +369,6 @@ export default class MessageComposer extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
let autoComplete;
|
||||
if (UserSettingsStore.isFeatureEnabled('rich_text_editor')) {
|
||||
autoComplete = <div className="mx_MessageComposer_autocomplete_wrapper">
|
||||
<Autocomplete
|
||||
ref="autocomplete"
|
||||
onConfirm={this._onAutocompleteConfirm}
|
||||
query={this.state.autocompleteQuery}
|
||||
selection={this.state.selection} />
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
||||
const {style, blockType} = this.state.inputState;
|
||||
const formatButtons = ["bold", "italic", "strike", "underline", "code", "quote", "bullet", "numbullet"].map(
|
||||
(name) => {
|
||||
|
@ -424,22 +392,20 @@ export default class MessageComposer extends React.Component {
|
|||
{controls}
|
||||
</div>
|
||||
</div>
|
||||
{UserSettingsStore.isFeatureEnabled('rich_text_editor') ?
|
||||
<div className="mx_MessageComposer_formatbar_wrapper">
|
||||
<div className="mx_MessageComposer_formatbar" style={this.state.showFormatting ? {} : {display: 'none'}}>
|
||||
{formatButtons}
|
||||
<div style={{flex: 1}}></div>
|
||||
<img title={ this.state.inputState.isRichtextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off") }
|
||||
onMouseDown={this.onToggleMarkdownClicked}
|
||||
className="mx_MessageComposer_formatbar_markdown mx_filterFlipColor"
|
||||
src={`img/button-md-${!this.state.inputState.isRichtextEnabled}.png`} />
|
||||
<img title={ _t("Hide Text Formatting Toolbar") }
|
||||
onClick={this.onToggleFormattingClicked}
|
||||
className="mx_MessageComposer_formatbar_cancel mx_filterFlipColor"
|
||||
src="img/icon-text-cancel.svg" />
|
||||
</div>
|
||||
</div>: null
|
||||
}
|
||||
<div className="mx_MessageComposer_formatbar_wrapper">
|
||||
<div className="mx_MessageComposer_formatbar" style={this.state.showFormatting ? {} : {display: 'none'}}>
|
||||
{formatButtons}
|
||||
<div style={{flex: 1}}></div>
|
||||
<img title={ this.state.inputState.isRichtextEnabled ? _t("Turn Markdown on") : _t("Turn Markdown off") }
|
||||
onMouseDown={this.onToggleMarkdownClicked}
|
||||
className="mx_MessageComposer_formatbar_markdown mx_filterFlipColor"
|
||||
src={`img/button-md-${!this.state.inputState.isRichtextEnabled}.png`} />
|
||||
<img title={ _t("Hide Text Formatting Toolbar") }
|
||||
onClick={this.onToggleFormattingClicked}
|
||||
className="mx_MessageComposer_formatbar_cancel mx_filterFlipColor"
|
||||
src="img/icon-text-cancel.svg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -514,6 +514,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
const currentBlockType = RichUtils.getCurrentBlockType(this.state.editorState);
|
||||
// If we're in any of these three types of blocks, shift enter should insert soft newlines
|
||||
// And just enter should end the block
|
||||
// XXX: Empirically enter does not end these blocks
|
||||
if(['blockquote', 'unordered-list-item', 'ordered-list-item'].includes(currentBlockType)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -629,8 +630,6 @@ export default class MessageComposerInput extends React.Component {
|
|||
editorState: this.createEditorState(),
|
||||
});
|
||||
|
||||
this.autocomplete.hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -909,14 +908,7 @@ MessageComposerInput.propTypes = {
|
|||
// called with current plaintext content (as a string) whenever it changes
|
||||
onContentChanged: React.PropTypes.func,
|
||||
|
||||
onUpArrow: React.PropTypes.func,
|
||||
|
||||
onDownArrow: React.PropTypes.func,
|
||||
|
||||
onFilesPasted: React.PropTypes.func,
|
||||
|
||||
// attempts to confirm currently selected completion, returns whether actually confirmed
|
||||
tryComplete: React.PropTypes.func,
|
||||
|
||||
onInputStateChanged: React.PropTypes.func,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue