Split out alias settings into its own component: AliasSettings

This commit is contained in:
Kegan Dougal 2016-02-02 12:46:14 +00:00
parent 2c04b9cb5b
commit ce789ba962
5 changed files with 411 additions and 312 deletions

79
src/ObjectUtils.js Normal file
View file

@ -0,0 +1,79 @@
/*
Copyright 2016 OpenMarket 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.
*/
/**
* For two objects of the form { key: [val1, val2, val3] }, work out the added/removed
* values. Entirely new keys will result in the entire value array being added.
* @param {Object} before
* @param {Object} after
* @return {Object[]} An array of objects with the form:
* { key: $KEY, val: $VALUE, place: "add|del" }
*/
module.exports.getKeyValueArrayDiffs = function(before, after) {
var results = [];
var delta = {};
Object.keys(before).forEach(function(beforeKey) {
delta[beforeKey] = delta[beforeKey] || 0; // init to 0 initially
delta[beforeKey]--; // keys present in the past have -ve values
});
Object.keys(after).forEach(function(afterKey) {
delta[afterKey] = delta[afterKey] || 0; // init to 0 initially
delta[afterKey]++; // keys present in the future have +ve values
});
Object.keys(delta).forEach(function(muxedKey) {
switch (delta[muxedKey]) {
case 1: // A new key in after
after[muxedKey].forEach(function(afterVal) {
results.push({ place: "add", key: muxedKey, val: afterVal });
});
break;
case -1: // A before key was removed
before[muxedKey].forEach(function(beforeVal) {
results.push({ place: "del", key: muxedKey, val: beforeVal });
});
break;
case 0: // A mix of added/removed keys
// compare old & new vals
var itemDelta = {};
before[muxedKey].forEach(function(beforeVal) {
itemDelta[beforeVal] = itemDelta[beforeVal] || 0;
itemDelta[beforeVal]--;
});
after[muxedKey].forEach(function(afterVal) {
itemDelta[afterVal] = itemDelta[afterVal] || 0;
itemDelta[afterVal]++;
});
Object.keys(itemDelta).forEach(function(item) {
if (itemDelta[item] === 1) {
results.push({ place: "add", key: muxedKey, val: item });
} else if (itemDelta[item] === -1) {
results.push({ place: "del", key: muxedKey, val: item });
} else {
// itemDelta of 0 means it was unchanged between before/after
}
});
break;
default:
console.error("Calculated key delta of " + delta[muxedKey] +
" - this should never happen!");
break;
}
});
return results;
};

View file

@ -63,6 +63,7 @@ module.exports.components['views.messages.MVideoBody'] = require('./components/v
module.exports.components['views.messages.TextualBody'] = require('./components/views/messages/TextualBody');
module.exports.components['views.messages.TextualEvent'] = require('./components/views/messages/TextualEvent');
module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody');
module.exports.components['views.room_settings.AliasSettings'] = require('./components/views/room_settings/AliasSettings');
module.exports.components['views.rooms.EntityTile'] = require('./components/views/rooms/EntityTile');
module.exports.components['views.rooms.EventTile'] = require('./components/views/rooms/EventTile');
module.exports.components['views.rooms.MemberInfo'] = require('./components/views/rooms/MemberInfo');

View file

@ -960,14 +960,6 @@ module.exports = React.createClass({
old_guest_join = false;
}
var old_canonical_alias = this.state.room.currentState.getStateEvents('m.room.canonical_alias', '');
if (old_canonical_alias) {
old_canonical_alias = old_canonical_alias.getContent().alias;
}
else {
old_canonical_alias = "";
}
var deferreds = [];
if (old_name != newVals.name && newVals.name != undefined) {
@ -1046,41 +1038,7 @@ module.exports = React.createClass({
);
}
if (newVals.alias_operations) {
var oplist = [];
for (var i = 0; i < newVals.alias_operations.length; i++) {
var alias_operation = newVals.alias_operations[i];
switch (alias_operation.type) {
case 'put':
oplist.push(
MatrixClientPeg.get().createAlias(
alias_operation.alias, this.state.room.roomId
)
);
break;
case 'delete':
oplist.push(
MatrixClientPeg.get().deleteAlias(
alias_operation.alias
)
);
break;
default:
console.log("Unknown alias operation, ignoring: " + alias_operation.type);
}
}
if (oplist.length) {
var deferred = oplist[0];
oplist.splice(1).forEach(function (f) {
deferred = deferred.then(f);
});
deferreds.push(deferred);
}
}
if (newVals.tag_operations) {
// FIXME: should probably be factored out with alias_operations above
var oplist = [];
for (var i = 0; i < newVals.tag_operations.length; i++) {
var tag_operation = newVals.tag_operations[i];
@ -1113,16 +1071,6 @@ module.exports = React.createClass({
}
}
if (old_canonical_alias !== newVals.canonical_alias) {
deferreds.push(
MatrixClientPeg.get().sendStateEvent(
this.state.room.roomId, "m.room.canonical_alias", {
alias: newVals.canonical_alias
}, ""
)
);
}
if (newVals.color_scheme) {
deferreds.push(
MatrixClientPeg.get().setRoomAccountData(
@ -1131,6 +1079,8 @@ module.exports = React.createClass({
);
}
deferreds.push(this.refs.room_settings.saveAliases());
if (deferreds.length) {
var self = this;
q.allSettled(deferreds).then(
@ -1241,9 +1191,7 @@ module.exports = React.createClass({
history_visibility: this.refs.room_settings.getHistoryVisibility(),
are_notifications_muted: this.refs.room_settings.areNotificationsMuted(),
power_levels: this.refs.room_settings.getPowerLevels(),
alias_operations: this.refs.room_settings.getAliasOperations(),
tag_operations: this.refs.room_settings.getTagOperations(),
canonical_alias: this.refs.room_settings.getCanonicalAlias(),
guest_join: this.refs.room_settings.canGuestsJoin(),
guest_read: this.refs.room_settings.canGuestsRead(),
color_scheme: this.refs.room_settings.getColorScheme(),

View file

@ -0,0 +1,313 @@
/*
Copyright 2016 OpenMarket 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.
*/
var q = require("q");
var React = require('react');
var ObjectUtils = require("../../../ObjectUtils");
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require("../../../index");
var Modal = require("../../../Modal");
module.exports = React.createClass({
displayName: 'AliasSettings',
propTypes: {
roomId: React.PropTypes.string.isRequired,
canSetCanonicalAlias: React.PropTypes.bool.isRequired,
canSetAliases: React.PropTypes.bool.isRequired,
aliasEvents: React.PropTypes.array, // [MatrixEvent]
canonicalAliasEvent: React.PropTypes.object // MatrixEvent
},
getDefaultProps: function() {
return {
canSetAliases: false,
canSetCanonicalAlias: false,
aliasEvents: []
};
},
getInitialState: function() {
return this.recalculateState(this.props.aliasEvents, this.props.canonicalAliasEvent);
},
recalculateState: function(aliasEvents, canonicalAliasEvent) {
aliasEvents = aliasEvents || [];
var state = {
domainToAliases: {}, // { domain.com => [#alias1:domain.com, #alias2:domain.com] }
remoteDomains: [], // [ domain.com, foobar.com ]
canonicalAlias: null // #canonical:domain.com
};
var localDomain = MatrixClientPeg.get().getDomain();
state.domainToAliases = this.aliasEventsToDictionary(aliasEvents);
state.remoteDomains = Object.keys(state.domainToAliases).filter((alias) => {
return alias !== localDomain;
});
if (canonicalAliasEvent) {
state.canonicalAlias = canonicalAliasEvent.getContent().alias;
}
return state;
},
saveSettings: function() {
console.log("AliasSettings.saveSettings room=%s", this.props.roomId);
var promises = [];
// save new canonical alias
var oldCanonicalAlias = null;
if (this.props.canonicalAliasEvent) {
oldCanonicalAlias = this.props.canonicalAliasEvent.getContent().alias;
}
console.log("canon old=%s new=%s", oldCanonicalAlias, this.state.canonicalAlias);
if (oldCanonicalAlias !== this.state.canonicalAlias) {
promises.push(
MatrixClientPeg.get().sendStateEvent(
this.props.roomId, "m.room.canonical_alias", {
alias: this.state.canonicalAlias
}, ""
)
);
}
// save new aliases for m.room.aliases
var aliasOperations = this.getAliasOperations();
var promises = [];
for (var i = 0; i < aliasOperations.length; i++) {
var alias_operation = aliasOperations[i];
console.log("alias %s %s", alias_operation.place, alias_operation.val);
switch (alias_operation.place) {
case 'add':
promises.push(
MatrixClientPeg.get().createAlias(
alias_operation.val, this.props.roomId
)
);
break;
case 'del':
promises.push(
MatrixClientPeg.get().deleteAlias(
alias_operation.val
)
);
break;
default:
console.log("Unknown alias operation, ignoring: " + alias_operation.place);
}
}
return q.allSettled(promises);
},
aliasEventsToDictionary: function(aliasEvents) { // m.room.alias events
var dict = {};
aliasEvents.forEach((event) => {
dict[event.getStateKey()] = (
(event.getContent().aliases || []).slice() // shallow-copy
);
});
return dict;
},
isAliasValid: function(alias) {
// XXX: FIXME SPEC-1
return (alias.match(/^#([^\/:,]+?):(.+)$/) && encodeURI(alias) === alias);
},
getAliasOperations: function() {
var oldAliases = this.aliasEventsToDictionary(this.props.aliasEvents);
return ObjectUtils.getKeyValueArrayDiffs(oldAliases, this.state.domainToAliases);
},
onAliasAdded: 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
});
// reset the add field
this.refs.add_alias.setValue(''); // FIXME
}
else {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Invalid alias format",
description: "'" + alias + "' is not a valid format for an alias",
});
}
},
onAliasChanged: function(domain, index, alias) {
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");
Modal.createDialog(ErrorDialog, {
title: "Invalid address format",
description: "'" + alias + "' is not a valid format for an address",
});
}
},
onAliasDeleted: function(domain, index) {
// 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.setState({
domainToAliases: this.state.domainToAliases
});
},
onCanonicalAliasChange: function(event) {
this.setState({
canonicalAlias: event.target.value
});
},
render: function() {
var self = this;
var EditableText = sdk.getComponent("elements.EditableText");
var localDomain = MatrixClientPeg.get().getDomain();
var canonical_alias_section;
if (this.props.canSetCanonicalAlias) {
canonical_alias_section = (
<select onChange={this.onCanonicalAliasChange} defaultValue={ this.state.canonicalAlias }>
{
Object.keys(self.state.domainToAliases).map(function(domain, i) {
return self.state.domainToAliases[domain].map(function(alias, j) {
return (
<option value={ alias } key={ i + "_" + j }>
{ alias }
</option>
);
});
})
}
<option value="" key="unset">not specified</option>
</select>
);
}
else {
canonical_alias_section = (
<b>{ this.state.canonicalAlias || "not set" }</b>
);
}
var remote_aliases_section;
if (this.state.remoteDomains.length) {
remote_aliases_section = (
<div>
<div className="mx_RoomSettings_aliasLabel">
Remote addresses for this room:
</div>
<div className="mx_RoomSettings_aliasesTable">
{ this.state.remoteDomains.map((domain, i) => {
return this.state.domainToAliases[domain].map(function(alias, j) {
return (
<div className="mx_RoomSettings_aliasesTableRow" key={ i + "_" + j }>
<EditableText
className="mx_RoomSettings_alias mx_RoomSettings_editable"
blurToCancel={ false }
editable={ false }
initialValue={ alias } />
</div>
);
});
})}
</div>
</div>
);
}
return (
<div>
<h3>Addresses</h3>
<div className="mx_RoomSettings_aliasLabel">
The main address for this room is: { canonical_alias_section }
</div>
<div className="mx_RoomSettings_aliasLabel">
{ (this.state.domainToAliases[localDomain] &&
this.state.domainToAliases[localDomain].length > 0)
? "Local addresses for this room:"
: "This room has no local addresses" }
</div>
<div className="mx_RoomSettings_aliasesTable">
{ (this.state.domainToAliases[localDomain] || []).map((alias, i) => {
var deleteButton;
if (this.props.canSetAliases) {
deleteButton = (
<img src="img/cancel-small.svg" width="14" height="14"
alt="Delete" onClick={ self.onAliasDeleted.bind(self, localDomain, i) } />
);
}
return (
<div className="mx_RoomSettings_aliasesTableRow" key={ i }>
<EditableText
className="mx_RoomSettings_alias mx_RoomSettings_editable"
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
placeholder={ "New address (e.g. #foo:" + localDomain + ")" }
blurToCancel={ false }
onValueChanged={ self.onAliasChanged.bind(self, localDomain, i) }
editable={ self.props.canSetAliases }
initialValue={ alias } />
<div className="mx_RoomSettings_deleteAlias">
{ deleteButton }
</div>
</div>
);
})}
<div className="mx_RoomSettings_aliasesTableRow" key="new">
<EditableText
ref="add_alias"
className="mx_RoomSettings_alias mx_RoomSettings_editable"
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
placeholder={ "New address (e.g. #foo:" + localDomain + ")" }
blurToCancel={ false }
onValueChanged={ self.onAliasAdded } />
<div className="mx_RoomSettings_addAlias">
<img src="img/plus.svg" width="14" height="14" alt="Add"
onClick={ self.onAliasAdded.bind(self, undefined) }/>
</div>
</div>
</div>
{ remote_aliases_section }
</div>
);
}
});

View file

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var q = require("q");
var React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Tinter = require('../../../Tinter');
@ -71,15 +72,6 @@ module.exports = React.createClass({
room_color_index = 0;
}
// get the aliases
var aliases = {};
var domain = MatrixClientPeg.get().getDomain();
var alias_events = this.props.room.currentState.getStateEvents('m.room.aliases');
for (var i = 0; i < alias_events.length; i++) {
aliases[alias_events[i].getStateKey()] = alias_events[i].getContent().aliases.slice(); // shallow copy
}
aliases[domain] = aliases[domain] || [];
var tags = {};
Object.keys(this.props.room.tags).forEach(function(tagName) {
tags[tagName] = {};
@ -89,15 +81,18 @@ module.exports = React.createClass({
power_levels_changed: false,
color_scheme_changed: false,
color_scheme_index: room_color_index,
aliases_changed: false,
aliases: aliases,
tags_changed: false,
tags: tags,
};
},
saveAliases: function() {
if (!this.refs.alias_settings) { return q(); }
return this.refs.alias_settings.saveSettings();
},
resetState: function() {
this.set.state(this.getInitialState());
this.setState(this.getInitialState());
},
canGuestsJoin: function() {
@ -145,84 +140,6 @@ module.exports = React.createClass({
return new_power_levels;
},
getCanonicalAlias: function() {
return this.refs.canonical_alias ? this.refs.canonical_alias.value : "";
},
getAliasOperations: function() {
if (!this.state.aliases_changed) return undefined;
// work out the delta from room state to UI state
var ops = [];
// calculate original ("old") aliases
var oldAliases = {};
var aliases = this.state.aliases;
var alias_events = this.props.room.currentState.getStateEvents('m.room.aliases');
for (var i = 0; i < alias_events.length; i++) {
var domain = alias_events[i].getStateKey();
oldAliases[domain] = alias_events[i].getContent().aliases.slice(); // shallow copy
}
// FIXME: this whole delta-based set comparison function used for domains, aliases & tags
// should be factored out asap rather than duplicated like this.
// work out whether any domains have entirely disappeared or appeared
var domainDelta = {}
Object.keys(oldAliases).forEach(function(domain) {
domainDelta[domain] = domainDelta[domain] || 0;
domainDelta[domain]--;
});
Object.keys(aliases).forEach(function(domain) {
domainDelta[domain] = domainDelta[domain] || 0;
domainDelta[domain]++;
});
Object.keys(domainDelta).forEach(function(domain) {
switch (domainDelta[domain]) {
case 1: // entirely new domain
aliases[domain].forEach(function(alias) {
ops.push({ type: "put", alias : alias });
});
break;
case -1: // entirely removed domain
oldAliases[domain].forEach(function(alias) {
ops.push({ type: "delete", alias : alias });
});
break;
case 0: // mix of aliases in this domain.
// compare old & new aliases for this domain
var delta = {};
oldAliases[domain].forEach(function(item) {
delta[item] = delta[item] || 0;
delta[item]--;
});
aliases[domain].forEach(function(item) {
delta[item] = delta[item] || 0;
delta[item]++;
});
Object.keys(delta).forEach(function(alias) {
if (delta[alias] == 1) {
ops.push({ type: "put", alias: alias });
} else if (delta[alias] == -1) {
ops.push({ type: "delete", alias: alias });
} else {
console.error("Calculated alias delta of " + delta[alias] +
" - this should never happen!");
}
});
break;
default:
console.error("Calculated domain delta of " + domainDelta[domain] +
" - this should never happen!");
break;
}
});
return ops;
},
getTagOperations: function() {
if (!this.state.tags_changed) return undefined;
@ -276,72 +193,6 @@ module.exports = React.createClass({
});
},
onAliasChanged: function(domain, index, alias) {
if (alias === "") return; // hit the delete button to delete please
var oldAlias;
if (this.isAliasValid(alias)) {
oldAlias = this.state.aliases[domain][index];
this.state.aliases[domain][index] = alias;
this.setState({ aliases_changed : true });
}
else {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Invalid address format",
description: "'" + alias + "' is not a valid format for an address",
});
}
},
onAliasDeleted: function(domain, index) {
// 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.aliases[domain].splice(index, 1);
this.setState({
aliases: this.state.aliases
});
this.setState({ aliases_changed : true });
},
onAliasAdded: function(alias) {
if (alias === "") return; // ignore attempts to create blank aliases
if (alias === undefined) {
alias = this.refs.add_alias ? this.refs.add_alias.getValue() : undefined;
if (alias === undefined || alias === "") return;
}
if (this.isAliasValid(alias)) {
var domain = alias.replace(/^.*?:/, '');
// XXX: do we need to deep copy aliases before editing it?
this.state.aliases[domain] = this.state.aliases[domain] || [];
this.state.aliases[domain].push(alias);
this.setState({
aliases: this.state.aliases
});
// reset the add field
this.refs.add_alias.setValue('');
this.setState({ aliases_changed : true });
}
else {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Invalid alias format",
description: "'" + alias + "' is not a valid format for an alias",
});
}
},
isAliasValid: function(alias) {
// XXX: FIXME SPEC-1
return (alias.match(/^#([^\/:,]+?):(.+)$/) && encodeURI(alias) === alias);
},
onTagChange: function(tagName, event) {
if (event.target.checked) {
if (tagName === 'm.favourite') {
@ -368,6 +219,7 @@ module.exports = React.createClass({
// TODO: go through greying out things you don't have permission to change
// (or turning them into informative stuff)
var AliasSettings = sdk.getComponent("room_settings.AliasSettings");
var EditableText = sdk.getComponent('elements.EditableText');
var PowerSelector = sdk.getComponent('elements.PowerSelector');
@ -445,13 +297,13 @@ module.exports = React.createClass({
var room_aliases_level = state_default;
if (events_levels['m.room.aliases'] !== undefined) {
room_avatar_level = events_levels['m.room.aliases'];
room_aliases_level = events_levels['m.room.aliases'];
}
var can_set_room_aliases = current_user_level >= room_aliases_level;
var canonical_alias_level = state_default;
if (events_levels['m.room.canonical_alias'] !== undefined) {
room_avatar_level = events_levels['m.room.canonical_alias'];
canonical_alias_level = events_levels['m.room.canonical_alias'];
}
var can_set_canonical_alias = current_user_level >= canonical_alias_level;
@ -459,105 +311,6 @@ module.exports = React.createClass({
var self = this;
var canonical_alias_event = this.props.room.currentState.getStateEvents('m.room.canonical_alias', '');
var canonical_alias = canonical_alias_event ? canonical_alias_event.getContent().alias : "";
var domain = MatrixClientPeg.get().getDomain();
var remote_domains = Object.keys(this.state.aliases).filter(function(alias) { return alias !== domain });
var remote_aliases_section;
if (remote_domains.length) {
remote_aliases_section =
<div>
<div className="mx_RoomSettings_aliasLabel">
Remote addresses for this room:
</div>
<div className="mx_RoomSettings_aliasesTable">
{ remote_domains.map(function(state_key, i) {
return self.state.aliases[state_key].map(function(alias, j) {
return (
<div className="mx_RoomSettings_aliasesTableRow" key={ i + "_" + j }>
<EditableText
className="mx_RoomSettings_alias mx_RoomSettings_editable"
blurToCancel={ false }
editable={ false }
initialValue={ alias } />
</div>
);
});
})}
</div>
</div>
}
var canonical_alias_section;
if (can_set_canonical_alias) {
canonical_alias_section =
<select ref="canonical_alias" defaultValue={ canonical_alias }>
{ Object.keys(self.state.aliases).map(function(stateKey, i) {
return self.state.aliases[stateKey].map(function(alias, j) {
return <option value={ alias } key={ i + "_" + j }>{ alias }</option>
});
})}
<option value="" key="unset">not specified</option>
</select>
}
else {
canonical_alias_section = <b>{ canonical_alias || "not set" }</b>;
}
var aliases_section =
<div>
<h3>Addresses</h3>
<div className="mx_RoomSettings_aliasLabel">The main address for this room is: { canonical_alias_section }</div>
<div className="mx_RoomSettings_aliasLabel">
{ this.state.aliases[domain].length
? "Local addresses for this room:"
: "This room has no local addresses" }
</div>
<div className="mx_RoomSettings_aliasesTable">
{ this.state.aliases[domain].map(function(alias, i) {
var deleteButton;
if (can_set_room_aliases) {
deleteButton = <img src="img/cancel-small.svg" width="14" height="14" alt="Delete"
onClick={ self.onAliasDeleted.bind(self, domain, i) }/>;
}
return (
<div className="mx_RoomSettings_aliasesTableRow" key={ i }>
<EditableText
className="mx_RoomSettings_alias mx_RoomSettings_editable"
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
placeholder={ "New address (e.g. #foo:" + domain + ")" }
blurToCancel={ false }
onValueChanged={ self.onAliasChanged.bind(self, domain, i) }
editable={ can_set_room_aliases }
initialValue={ alias } />
<div className="mx_RoomSettings_deleteAlias">
{ deleteButton }
</div>
</div>
);
})}
<div className="mx_RoomSettings_aliasesTableRow" key="new">
<EditableText
ref="add_alias"
className="mx_RoomSettings_alias mx_RoomSettings_editable"
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
placeholder={ "New address (e.g. #foo:" + domain + ")" }
blurToCancel={ false }
onValueChanged={ self.onAliasAdded } />
<div className="mx_RoomSettings_addAlias">
<img src="img/plus.svg" width="14" height="14" alt="Add"
onClick={ self.onAliasAdded.bind(self, undefined) }/>
</div>
</div>
</div>
{ remote_aliases_section }
</div>;
var room_colors_section =
<div>
<h3>Room Colour</h3>
@ -676,7 +429,12 @@ module.exports = React.createClass({
{ room_colors_section }
{ aliases_section }
<AliasSettings ref="alias_settings"
roomId={this.props.room.roomId}
canSetCanonicalAlias={can_set_canonical_alias}
canSetAliases={can_set_room_aliases}
canonicalAliasEvent={this.props.room.currentState.getStateEvents('m.room.canonical_alias', '')}
aliasEvents={this.props.room.currentState.getStateEvents('m.room.aliases')} />
<h3>Permissions</h3>
<div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings">