Merge pull request #128 from matrix-org/kegan/alias-settings
Split out alias settings into its own component: AliasSettings
This commit is contained in:
commit
6c9d48bd3a
5 changed files with 411 additions and 312 deletions
79
src/ObjectUtils.js
Normal file
79
src/ObjectUtils.js
Normal 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;
|
||||||
|
};
|
|
@ -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.TextualBody'] = require('./components/views/messages/TextualBody');
|
||||||
module.exports.components['views.messages.TextualEvent'] = require('./components/views/messages/TextualEvent');
|
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.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.EntityTile'] = require('./components/views/rooms/EntityTile');
|
||||||
module.exports.components['views.rooms.EventTile'] = require('./components/views/rooms/EventTile');
|
module.exports.components['views.rooms.EventTile'] = require('./components/views/rooms/EventTile');
|
||||||
module.exports.components['views.rooms.MemberInfo'] = require('./components/views/rooms/MemberInfo');
|
module.exports.components['views.rooms.MemberInfo'] = require('./components/views/rooms/MemberInfo');
|
||||||
|
|
|
@ -1072,14 +1072,6 @@ module.exports = React.createClass({
|
||||||
old_guest_join = false;
|
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 = [];
|
var deferreds = [];
|
||||||
|
|
||||||
if (old_name != newVals.name && newVals.name != undefined) {
|
if (old_name != newVals.name && newVals.name != undefined) {
|
||||||
|
@ -1158,41 +1150,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) {
|
if (newVals.tag_operations) {
|
||||||
// FIXME: should probably be factored out with alias_operations above
|
|
||||||
var oplist = [];
|
var oplist = [];
|
||||||
for (var i = 0; i < newVals.tag_operations.length; i++) {
|
for (var i = 0; i < newVals.tag_operations.length; i++) {
|
||||||
var tag_operation = newVals.tag_operations[i];
|
var tag_operation = newVals.tag_operations[i];
|
||||||
|
@ -1225,16 +1183,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) {
|
if (newVals.color_scheme) {
|
||||||
deferreds.push(
|
deferreds.push(
|
||||||
MatrixClientPeg.get().setRoomAccountData(
|
MatrixClientPeg.get().setRoomAccountData(
|
||||||
|
@ -1243,6 +1191,8 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deferreds.push(this.refs.room_settings.saveAliases());
|
||||||
|
|
||||||
if (deferreds.length) {
|
if (deferreds.length) {
|
||||||
var self = this;
|
var self = this;
|
||||||
q.allSettled(deferreds).then(
|
q.allSettled(deferreds).then(
|
||||||
|
@ -1386,9 +1336,7 @@ module.exports = React.createClass({
|
||||||
history_visibility: this.refs.room_settings.getHistoryVisibility(),
|
history_visibility: this.refs.room_settings.getHistoryVisibility(),
|
||||||
are_notifications_muted: this.refs.room_settings.areNotificationsMuted(),
|
are_notifications_muted: this.refs.room_settings.areNotificationsMuted(),
|
||||||
power_levels: this.refs.room_settings.getPowerLevels(),
|
power_levels: this.refs.room_settings.getPowerLevels(),
|
||||||
alias_operations: this.refs.room_settings.getAliasOperations(),
|
|
||||||
tag_operations: this.refs.room_settings.getTagOperations(),
|
tag_operations: this.refs.room_settings.getTagOperations(),
|
||||||
canonical_alias: this.refs.room_settings.getCanonicalAlias(),
|
|
||||||
guest_join: this.refs.room_settings.canGuestsJoin(),
|
guest_join: this.refs.room_settings.canGuestsJoin(),
|
||||||
guest_read: this.refs.room_settings.canGuestsRead(),
|
guest_read: this.refs.room_settings.canGuestsRead(),
|
||||||
color_scheme: this.refs.room_settings.getColorScheme(),
|
color_scheme: this.refs.room_settings.getColorScheme(),
|
||||||
|
|
313
src/components/views/room_settings/AliasSettings.js
Normal file
313
src/components/views/room_settings/AliasSettings.js
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var q = require("q");
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||||
var Tinter = require('../../../Tinter');
|
var Tinter = require('../../../Tinter');
|
||||||
|
@ -71,15 +72,6 @@ module.exports = React.createClass({
|
||||||
room_color_index = 0;
|
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 = {};
|
var tags = {};
|
||||||
Object.keys(this.props.room.tags).forEach(function(tagName) {
|
Object.keys(this.props.room.tags).forEach(function(tagName) {
|
||||||
tags[tagName] = {};
|
tags[tagName] = {};
|
||||||
|
@ -89,15 +81,18 @@ module.exports = React.createClass({
|
||||||
power_levels_changed: false,
|
power_levels_changed: false,
|
||||||
color_scheme_changed: false,
|
color_scheme_changed: false,
|
||||||
color_scheme_index: room_color_index,
|
color_scheme_index: room_color_index,
|
||||||
aliases_changed: false,
|
|
||||||
aliases: aliases,
|
|
||||||
tags_changed: false,
|
tags_changed: false,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
saveAliases: function() {
|
||||||
|
if (!this.refs.alias_settings) { return q(); }
|
||||||
|
return this.refs.alias_settings.saveSettings();
|
||||||
|
},
|
||||||
|
|
||||||
resetState: function() {
|
resetState: function() {
|
||||||
this.set.state(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
},
|
},
|
||||||
|
|
||||||
canGuestsJoin: function() {
|
canGuestsJoin: function() {
|
||||||
|
@ -145,84 +140,6 @@ module.exports = React.createClass({
|
||||||
return new_power_levels;
|
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() {
|
getTagOperations: function() {
|
||||||
if (!this.state.tags_changed) return undefined;
|
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) {
|
onTagChange: function(tagName, event) {
|
||||||
if (event.target.checked) {
|
if (event.target.checked) {
|
||||||
if (tagName === 'm.favourite') {
|
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
|
// TODO: go through greying out things you don't have permission to change
|
||||||
// (or turning them into informative stuff)
|
// (or turning them into informative stuff)
|
||||||
|
|
||||||
|
var AliasSettings = sdk.getComponent("room_settings.AliasSettings");
|
||||||
var EditableText = sdk.getComponent('elements.EditableText');
|
var EditableText = sdk.getComponent('elements.EditableText');
|
||||||
var PowerSelector = sdk.getComponent('elements.PowerSelector');
|
var PowerSelector = sdk.getComponent('elements.PowerSelector');
|
||||||
|
|
||||||
|
@ -445,13 +297,13 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
var room_aliases_level = state_default;
|
var room_aliases_level = state_default;
|
||||||
if (events_levels['m.room.aliases'] !== undefined) {
|
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 can_set_room_aliases = current_user_level >= room_aliases_level;
|
||||||
|
|
||||||
var canonical_alias_level = state_default;
|
var canonical_alias_level = state_default;
|
||||||
if (events_levels['m.room.canonical_alias'] !== undefined) {
|
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;
|
var can_set_canonical_alias = current_user_level >= canonical_alias_level;
|
||||||
|
|
||||||
|
@ -459,105 +311,6 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
var self = this;
|
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 =
|
var room_colors_section =
|
||||||
<div>
|
<div>
|
||||||
<h3>Room Colour</h3>
|
<h3>Room Colour</h3>
|
||||||
|
@ -676,7 +429,12 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
{ room_colors_section }
|
{ 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>
|
<h3>Permissions</h3>
|
||||||
<div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings">
|
<div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings">
|
||||||
|
|
Loading…
Reference in a new issue