Make adding aliases direct manipulation

This commit is contained in:
Travis Ralston 2019-02-20 16:13:35 -07:00
parent 2903a0e712
commit 5d2f17c49a
3 changed files with 78 additions and 82 deletions

View file

@ -92,9 +92,11 @@ export default class EditableItemList extends React.Component{
itemsLabel: PropTypes.string, itemsLabel: PropTypes.string,
noItemsLabel: PropTypes.string, noItemsLabel: PropTypes.string,
placeholder: PropTypes.string, placeholder: PropTypes.string,
newItem: PropTypes.string,
onItemAdded: PropTypes.func, onItemAdded: PropTypes.func,
onItemRemoved: PropTypes.func, onItemRemoved: PropTypes.func,
onNewItemChanged: PropTypes.func,
canEdit: PropTypes.bool, canEdit: PropTypes.bool,
}; };
@ -103,22 +105,25 @@ export default class EditableItemList extends React.Component{
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (!this.refs.newItem) return; if (this.props.onItemAdded) this.props.onItemAdded(this.props.newItem);
const value = this.refs.newItem.value;
if (this.props.onItemAdded) this.props.onItemAdded(value);
}; };
_onItemRemoved = (index) => { _onItemRemoved = (index) => {
if (this.props.onItemRemoved) this.props.onItemRemoved(index); if (this.props.onItemRemoved) this.props.onItemRemoved(index);
}; };
_onNewItemChanged = (e) => {
if (this.props.onNewItemChanged) this.props.onNewItemChanged(e.target.value);
};
_renderNewItemField() { _renderNewItemField() {
return ( return (
<form onSubmit={this._onAddClick} autoComplete={false} <form onSubmit={this._onItemAdded} autoComplete={false}
noValidate={true} className="mx_EditableItemList_newItem"> noValidate={true} className="mx_EditableItemList_newItem">
<Field id="newEmailAddress" ref="newItem" label={this.props.placeholder} <Field id="newEmailAddress" label={this.props.placeholder}
type="text" autoComplete="off" /> type="text" autoComplete="off" value={this.props.newItem}
onChange={this._onNewItemChanged}
/>
<AccessibleButton onClick={this._onItemAdded} kind="primary"> <AccessibleButton onClick={this._onItemAdded} kind="primary">
{_t("Add")} {_t("Add")}
</AccessibleButton> </AccessibleButton>

View file

@ -1,6 +1,6 @@
/* /*
Copyright 2016 OpenMarket Ltd Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd Copyright 2018, 2019 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -23,32 +23,32 @@ const MatrixClientPeg = require('../../../MatrixClientPeg');
const sdk = require("../../../index"); const sdk = require("../../../index");
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import Field from "../elements/Field"; import Field from "../elements/Field";
import ErrorDialog from "../dialogs/ErrorDialog";
const Modal = require("../../../Modal"); const Modal = require("../../../Modal");
module.exports = React.createClass({ export default class AliasSettings extends React.Component {
displayName: 'AliasSettings', static propTypes = {
propTypes: {
roomId: PropTypes.string.isRequired, roomId: PropTypes.string.isRequired,
canSetCanonicalAlias: PropTypes.bool.isRequired, canSetCanonicalAlias: PropTypes.bool.isRequired,
canSetAliases: PropTypes.bool.isRequired, canSetAliases: PropTypes.bool.isRequired,
aliasEvents: PropTypes.array, // [MatrixEvent] aliasEvents: PropTypes.array, // [MatrixEvent]
canonicalAliasEvent: PropTypes.object, // MatrixEvent canonicalAliasEvent: PropTypes.object, // MatrixEvent
}, };
getDefaultProps: function() { static defaultProps = {
return { canSetAliases: false,
canSetAliases: false, canSetCanonicalAlias: false,
canSetCanonicalAlias: false, aliasEvents: [],
aliasEvents: [], };
};
},
getInitialState: function() { constructor(props) {
return this.recalculateState(this.props.aliasEvents, this.props.canonicalAliasEvent); super(props);
},
recalculateState: function(aliasEvents, canonicalAliasEvent) { const aliasState = this.recalculateState(props.aliasEvents, props.canonicalAliasEvent);
this.state = Object.assign({newItem: ""}, aliasState);
}
recalculateState(aliasEvents, canonicalAliasEvent) {
aliasEvents = aliasEvents || []; aliasEvents = aliasEvents || [];
const state = { const state = {
@ -69,9 +69,9 @@ module.exports = React.createClass({
} }
return state; return state;
}, }
saveSettings: function() { saveSettings() {
let promises = []; let promises = [];
// save new aliases for m.room.aliases // save new aliases for m.room.aliases
@ -118,9 +118,9 @@ module.exports = React.createClass({
} }
return promises; return promises;
}, }
aliasEventsToDictionary: function(aliasEvents) { // m.room.alias events aliasEventsToDictionary(aliasEvents) { // m.room.alias events
const dict = {}; const dict = {};
aliasEvents.forEach((event) => { aliasEvents.forEach((event) => {
dict[event.getStateKey()] = ( dict[event.getStateKey()] = (
@ -128,35 +128,53 @@ module.exports = React.createClass({
); );
}); });
return dict; return dict;
}, }
isAliasValid: function(alias) { isAliasValid(alias) {
// XXX: FIXME https://github.com/matrix-org/matrix-doc/issues/668 // XXX: FIXME https://github.com/matrix-org/matrix-doc/issues/668
return (alias.match(/^#([^\/:,]+?):(.+)$/) && encodeURI(alias) === alias); return (alias.match(/^#([^\/:,]+?):(.+)$/) && encodeURI(alias) === alias);
}, }
getAliasOperations: function() { getAliasOperations() {
const oldAliases = this.aliasEventsToDictionary(this.props.aliasEvents); const oldAliases = this.aliasEventsToDictionary(this.props.aliasEvents);
return ObjectUtils.getKeyValueArrayDiffs(oldAliases, this.state.domainToAliases); return ObjectUtils.getKeyValueArrayDiffs(oldAliases, this.state.domainToAliases);
}, }
onNewAliasChanged: function(value) { onNewAliasChanged = (value) => {
this.setState({newAlias: value}); this.setState({newAlias: value});
}, };
onLocalAliasAdded: function(alias) { onLocalAliasAdded = (alias) => {
if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases
const localDomain = MatrixClientPeg.get().getDomain(); const localDomain = MatrixClientPeg.get().getDomain();
if (!alias.includes(':')) alias += ':' + localDomain; if (!alias.includes(':')) alias += ':' + localDomain;
if (this.isAliasValid(alias) && alias.endsWith(localDomain)) { if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
this.state.domainToAliases[localDomain] = this.state.domainToAliases[localDomain] || []; MatrixClientPeg.get().createAlias(alias, this.props.roomId).then(() => {
this.state.domainToAliases[localDomain].push(alias); const localAliases = this.state.domainToAliases[localDomain] || [];
const domainAliases = Object.assign({}, this.state.domainToAliases);
domainAliases[localDomain] = [...localAliases, alias];
this.setState({ this.setState({
domainToAliases: this.state.domainToAliases, domainToAliases: domainAliases,
// Reset the add field // Reset the add field
newAlias: "", newAlias: "",
});
if (!this.props.canonicalAlias) {
this.setState({
canonicalAlias: alias,
});
}
}).catch((err) => {
console.error(err);
Modal.createTrackedDialog('Error creating alias', '', ErrorDialog, {
title: _t("Error creating alias"),
description: _t(
"There was an error creating that alias. It may not be allowed by the server " +
"or a temporary failure occurred."
),
});
}); });
} else { } else {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@ -165,30 +183,9 @@ module.exports = React.createClass({
description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }), description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }),
}); });
} }
};
if (!this.props.canonicalAlias) { onLocalAliasDeleted = (index) => {
this.setState({
canonicalAlias: alias,
});
}
},
onLocalAliasChanged: function(alias, index) {
if (alias === "") return; // hit the delete button to delete please
const localDomain = MatrixClientPeg.get().getDomain();
if (!alias.includes(':')) alias += ':' + localDomain;
if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
this.state.domainToAliases[localDomain][index] = alias;
} else {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Invalid address format', '', ErrorDialog, {
title: _t('Invalid address format'),
description: _t('\'%(alias)s\' is not a valid format for an address', { alias: alias }),
});
}
},
onLocalAliasDeleted: function(index) {
const localDomain = MatrixClientPeg.get().getDomain(); const localDomain = MatrixClientPeg.get().getDomain();
// It's a bit naughty to directly manipulate this.state, and React would // It's a bit naughty to directly manipulate this.state, and React would
// normally whine at you, but it can't see us doing the splice. Given we // normally whine at you, but it can't see us doing the splice. Given we
@ -204,17 +201,15 @@ module.exports = React.createClass({
canonicalAlias: null, canonicalAlias: null,
}); });
} }
}, };
onCanonicalAliasChange: function(event) { onCanonicalAliasChange = (event) => {
this.setState({ this.setState({
canonicalAlias: event.target.value, canonicalAlias: event.target.value,
}); });
}, };
render: function() { render() {
const self = this;
const EditableText = sdk.getComponent("elements.EditableText");
const EditableItemList = sdk.getComponent("elements.EditableItemList"); const EditableItemList = sdk.getComponent("elements.EditableItemList");
const localDomain = MatrixClientPeg.get().getDomain(); const localDomain = MatrixClientPeg.get().getDomain();
@ -227,8 +222,8 @@ module.exports = React.createClass({
element='select' id='canonicalAlias' label={_t('Main address')}> element='select' id='canonicalAlias' label={_t('Main address')}>
<option value="" key="unset">{ _t('not specified') }</option> <option value="" key="unset">{ _t('not specified') }</option>
{ {
Object.keys(self.state.domainToAliases).map((domain, i) => { Object.keys(this.state.domainToAliases).map((domain, i) => {
return self.state.domainToAliases[domain].map((alias, j) => { return this.state.domainToAliases[domain].map((alias, j) => {
if (alias === this.state.canonicalAlias) found = true; if (alias === this.state.canonicalAlias) found = true;
return ( return (
<option value={alias} key={i + "_" + j}> <option value={alias} key={i + "_" + j}>
@ -239,7 +234,7 @@ module.exports = React.createClass({
}) })
} }
{ {
found || !this.stateCanonicalAlias ? '' : found || !this.state.canonicalAlias ? '' :
<option value={ this.state.canonicalAlias } key='arbitrary'> <option value={ this.state.canonicalAlias } key='arbitrary'>
{ this.state.canonicalAlias } { this.state.canonicalAlias }
</option> </option>
@ -280,7 +275,6 @@ module.exports = React.createClass({
onNewItemChanged={this.onNewAliasChanged} onNewItemChanged={this.onNewAliasChanged}
canEdit={this.props.canSetAliases} canEdit={this.props.canSetAliases}
onItemAdded={this.onLocalAliasAdded} onItemAdded={this.onLocalAliasAdded}
onItemEdited={this.onLocalAliasChanged}
onItemRemoved={this.onLocalAliasDeleted} onItemRemoved={this.onLocalAliasDeleted}
itemsLabel={_t('Local addresses for this room:')} itemsLabel={_t('Local addresses for this room:')}
noItemsLabel={_t('This room has no local addresses')} noItemsLabel={_t('This room has no local addresses')}
@ -288,10 +282,8 @@ module.exports = React.createClass({
'New address (e.g. #foo:%(localDomain)s)', {localDomain: localDomain}, 'New address (e.g. #foo:%(localDomain)s)', {localDomain: localDomain},
)} )}
/> />
{ remote_aliases_section } { remote_aliases_section }
</div> </div>
); );
}, }
}); };

View file

@ -804,10 +804,10 @@
"Hide Stickers": "Hide Stickers", "Hide Stickers": "Hide Stickers",
"Show Stickers": "Show Stickers", "Show Stickers": "Show Stickers",
"Jump to first unread message.": "Jump to first unread message.", "Jump to first unread message.": "Jump to first unread message.",
"Error creating alias": "Error creating alias",
"There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.": "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.",
"Invalid alias format": "Invalid alias format", "Invalid alias format": "Invalid alias format",
"'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias", "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias",
"Invalid address format": "Invalid address format",
"'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address",
"Main address": "Main address", "Main address": "Main address",
"not specified": "not specified", "not specified": "not specified",
"not set": "not set", "not set": "not set",
@ -925,7 +925,6 @@
"Verify...": "Verify...", "Verify...": "Verify...",
"Join": "Join", "Join": "Join",
"No results": "No results", "No results": "No results",
"Delete": "Delete",
"Communities": "Communities", "Communities": "Communities",
"Home": "Home", "Home": "Home",
"You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)", "You cannot delete this image. (%(code)s)": "You cannot delete this image. (%(code)s)",