diff --git a/src/components/views/elements/EditableItemList.js b/src/components/views/elements/EditableItemList.js new file mode 100644 index 0000000000..267244759a --- /dev/null +++ b/src/components/views/elements/EditableItemList.js @@ -0,0 +1,149 @@ +/* +Copyright 2017 New Vector Ltd. + +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. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import sdk from '../../../index'; +import {_t} from '../../../languageHandler.js'; + +const EditableItem = React.createClass({ + displayName: 'EditableItem', + + propTypes: { + initialValue: PropTypes.string, + index: PropTypes.number, + placeholder: PropTypes.string, + + onChange: PropTypes.func, + onRemove: PropTypes.func, + onAdd: PropTypes.func, + + addOnChange: PropTypes.bool, + }, + + onChange: function(value) { + this.setState({ value }); + if (this.props.onChange) this.props.onChange(value, this.props.index); + if (this.props.addOnChange && this.props.onAdd) this.props.onAdd(value); + }, + + onRemove: function() { + if (this.props.onRemove) this.props.onRemove(this.props.index); + }, + + onAdd: function() { + if (this.props.onAdd) this.props.onAdd(this.state.value); + }, + + render: function() { + const EditableText = sdk.getComponent('elements.EditableText'); + return
+ + { this.props.onAdd ? +
+ {_t("Add")} +
+ : +
+ {_t("Delete")} +
+ } +
; + }, +}); + +module.exports = React.createClass({ + displayName: 'EditableItemList', + + propTypes: { + items: PropTypes.arrayOf(PropTypes.string).isRequired, + onNewItemChanged: PropTypes.func, + onItemAdded: PropTypes.func, + onItemEdited: PropTypes.func, + onItemRemoved: PropTypes. func, + }, + + getDefaultProps: function() { + return { + onItemAdded: () => {}, + onItemEdited: () => {}, + onItemRemoved: () => {}, + onNewItemChanged: () => {}, + }; + }, + + onItemAdded: function(value) { + this.props.onItemAdded(value); + }, + + onItemEdited: function(value, index) { + if (value.length === 0) { + this.onItemRemoved(index); + } else { + this.onItemEdited(value, index); + } + }, + + onItemRemoved: function(index) { + this.props.onItemRemoved(index); + }, + + onNewItemChanged: function(value) { + this.props.onNewItemChanged(value); + }, + + render: function() { + const editableItems = this.props.items.map((item, index) => { + return ; + }); + + const label = this.props.items.length > 0 ? + this.props.itemsLabel : this.props.noItemsLabel; + + return (
+
+ { label } +
+ { editableItems } + +
); + }, +}); diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js index ea3bad390f..94301c432d 100644 --- a/src/components/views/room_settings/AliasSettings.js +++ b/src/components/views/room_settings/AliasSettings.js @@ -136,24 +136,25 @@ module.exports = React.createClass({ return ObjectUtils.getKeyValueArrayDiffs(oldAliases, this.state.domainToAliases); }, - onAliasAdded: function(alias) { + onNewAliasChanged: function(value) { + this.setState({newAlias: value}); + }, + + onLocalAliasAdded: function(alias) { if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases - if (this.isAliasValid(alias)) { - // add this alias to the domain to aliases dict - var domain = alias.replace(/^.*?:/, ''); - // XXX: do we need to deep copy aliases before editing it? - this.state.domainToAliases[domain] = this.state.domainToAliases[domain] || []; - this.state.domainToAliases[domain].push(alias); - this.setState({ - domainToAliases: this.state.domainToAliases - }); + const localDomain = MatrixClientPeg.get().getDomain(); + if (this.isAliasValid(alias) && alias.endsWith(localDomain)) { + this.state.domainToAliases[localDomain] = this.state.domainToAliases[localDomain] || []; + this.state.domainToAliases[localDomain].push(alias); - // reset the add field - this.refs.add_alias.setValue(''); // FIXME - } - else { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + this.setState({ + domainToAliases: this.state.domainToAliases, + // Reset the add field + newAlias: "", + }); + } else { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Invalid alias format', '', ErrorDialog, { title: _t('Invalid alias format'), description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }), @@ -161,15 +162,13 @@ module.exports = React.createClass({ } }, - onAliasChanged: function(domain, index, alias) { + onLocalAliasChanged: function(alias, index) { if (alias === "") return; // hit the delete button to delete please - var oldAlias; - if (this.isAliasValid(alias)) { - oldAlias = this.state.domainToAliases[domain][index]; - this.state.domainToAliases[domain][index] = alias; - } - else { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + const localDomain = MatrixClientPeg.get().getDomain(); + 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 }), @@ -177,15 +176,16 @@ module.exports = React.createClass({ } }, - onAliasDeleted: function(domain, index) { + onLocalAliasDeleted: function(index) { + const localDomain = MatrixClientPeg.get().getDomain(); // 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 // promptly setState anyway, it's just about acceptable. The alternative // would be to arbitrarily deepcopy to a temp variable and then setState // that, but why bother when we can cut this corner. - var alias = this.state.domainToAliases[domain].splice(index, 1); + this.state.domainToAliases[localDomain].splice(index, 1); this.setState({ - domainToAliases: this.state.domainToAliases + domainToAliases: this.state.domainToAliases, }); }, @@ -198,6 +198,7 @@ module.exports = React.createClass({ render: function() { var self = this; var EditableText = sdk.getComponent("elements.EditableText"); + var EditableItemList = sdk.getComponent("elements.EditableItemList"); var localDomain = MatrixClientPeg.get().getDomain(); var canonical_alias_section; @@ -257,58 +258,24 @@ module.exports = React.createClass({
{ _t('The main address for this room is') }: { canonical_alias_section }
-
- { (this.state.domainToAliases[localDomain] && - this.state.domainToAliases[localDomain].length > 0) - ? _t('Local addresses for this room:') - : _t('This room has no local addresses') } -
-
- { (this.state.domainToAliases[localDomain] || []).map((alias, i) => { - var deleteButton; - if (this.props.canSetAliases) { - deleteButton = ( - { - ); - } - return ( -
- -
- { deleteButton } -
-
- ); - })} - - { this.props.canSetAliases ? -
- -
- { -
-
: "" - } -
+ { remote_aliases_section } ); - } + }, });