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.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');
|
||||
|
|
|
@ -1072,14 +1072,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) {
|
||||
|
@ -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) {
|
||||
// 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];
|
||||
|
@ -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) {
|
||||
deferreds.push(
|
||||
MatrixClientPeg.get().setRoomAccountData(
|
||||
|
@ -1243,6 +1191,8 @@ module.exports = React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
deferreds.push(this.refs.room_settings.saveAliases());
|
||||
|
||||
if (deferreds.length) {
|
||||
var self = this;
|
||||
q.allSettled(deferreds).then(
|
||||
|
@ -1386,9 +1336,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(),
|
||||
|
|
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.
|
||||
*/
|
||||
|
||||
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">
|
||||
|
|
Loading…
Reference in a new issue