2016-02-02 12:46:14 +00:00
|
|
|
/*
|
|
|
|
Copyright 2016 OpenMarket Ltd
|
2018-09-20 00:07:01 +00:00
|
|
|
Copyright 2018 New Vector Ltd
|
2016-02-02 12:46:14 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2017-07-12 12:58:14 +00:00
|
|
|
import Promise from 'bluebird';
|
2017-10-11 16:56:17 +00:00
|
|
|
const React = require('react');
|
2017-12-26 01:03:18 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2017-10-11 16:56:17 +00:00
|
|
|
const ObjectUtils = require("../../../ObjectUtils");
|
|
|
|
const MatrixClientPeg = require('../../../MatrixClientPeg');
|
|
|
|
const sdk = require("../../../index");
|
2017-05-25 10:39:08 +00:00
|
|
|
import { _t } from '../../../languageHandler';
|
2019-01-26 03:53:38 +00:00
|
|
|
import Field from "../elements/Field";
|
2017-10-11 16:56:17 +00:00
|
|
|
const Modal = require("../../../Modal");
|
2016-02-02 12:46:14 +00:00
|
|
|
|
|
|
|
module.exports = React.createClass({
|
|
|
|
displayName: 'AliasSettings',
|
|
|
|
|
|
|
|
propTypes: {
|
2017-12-26 01:03:18 +00:00
|
|
|
roomId: PropTypes.string.isRequired,
|
|
|
|
canSetCanonicalAlias: PropTypes.bool.isRequired,
|
|
|
|
canSetAliases: PropTypes.bool.isRequired,
|
|
|
|
aliasEvents: PropTypes.array, // [MatrixEvent]
|
|
|
|
canonicalAliasEvent: PropTypes.object, // MatrixEvent
|
2016-02-02 12:46:14 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getDefaultProps: function() {
|
|
|
|
return {
|
|
|
|
canSetAliases: false,
|
|
|
|
canSetCanonicalAlias: false,
|
2017-10-11 16:56:17 +00:00
|
|
|
aliasEvents: [],
|
2016-02-02 12:46:14 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState: function() {
|
|
|
|
return this.recalculateState(this.props.aliasEvents, this.props.canonicalAliasEvent);
|
|
|
|
},
|
|
|
|
|
|
|
|
recalculateState: function(aliasEvents, canonicalAliasEvent) {
|
|
|
|
aliasEvents = aliasEvents || [];
|
|
|
|
|
2017-10-11 16:56:17 +00:00
|
|
|
const state = {
|
2016-02-02 12:46:14 +00:00
|
|
|
domainToAliases: {}, // { domain.com => [#alias1:domain.com, #alias2:domain.com] }
|
|
|
|
remoteDomains: [], // [ domain.com, foobar.com ]
|
2017-10-11 16:56:17 +00:00
|
|
|
canonicalAlias: null, // #canonical:domain.com
|
2016-02-02 12:46:14 +00:00
|
|
|
};
|
2017-10-11 16:56:17 +00:00
|
|
|
const localDomain = MatrixClientPeg.get().getDomain();
|
2016-02-02 12:46:14 +00:00
|
|
|
|
|
|
|
state.domainToAliases = this.aliasEventsToDictionary(aliasEvents);
|
|
|
|
|
2018-02-02 08:12:56 +00:00
|
|
|
state.remoteDomains = Object.keys(state.domainToAliases).filter((domain) => {
|
|
|
|
return domain !== localDomain && state.domainToAliases[domain].length > 0;
|
2016-02-02 12:46:14 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (canonicalAliasEvent) {
|
|
|
|
state.canonicalAlias = canonicalAliasEvent.getContent().alias;
|
|
|
|
}
|
|
|
|
|
|
|
|
return state;
|
|
|
|
},
|
|
|
|
|
|
|
|
saveSettings: function() {
|
2017-10-11 16:56:17 +00:00
|
|
|
let promises = [];
|
2016-02-02 12:46:14 +00:00
|
|
|
|
|
|
|
// save new aliases for m.room.aliases
|
2017-10-11 16:56:17 +00:00
|
|
|
const aliasOperations = this.getAliasOperations();
|
|
|
|
for (let i = 0; i < aliasOperations.length; i++) {
|
|
|
|
const alias_operation = aliasOperations[i];
|
2016-02-02 12:46:14 +00:00
|
|
|
console.log("alias %s %s", alias_operation.place, alias_operation.val);
|
|
|
|
switch (alias_operation.place) {
|
|
|
|
case 'add':
|
|
|
|
promises.push(
|
|
|
|
MatrixClientPeg.get().createAlias(
|
2017-10-11 16:56:17 +00:00
|
|
|
alias_operation.val, this.props.roomId,
|
|
|
|
),
|
2016-02-02 12:46:14 +00:00
|
|
|
);
|
|
|
|
break;
|
|
|
|
case 'del':
|
|
|
|
promises.push(
|
|
|
|
MatrixClientPeg.get().deleteAlias(
|
2017-10-11 16:56:17 +00:00
|
|
|
alias_operation.val,
|
|
|
|
),
|
2016-02-02 12:46:14 +00:00
|
|
|
);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.log("Unknown alias operation, ignoring: " + alias_operation.place);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-11 16:56:17 +00:00
|
|
|
let oldCanonicalAlias = null;
|
2016-09-16 17:01:14 +00:00
|
|
|
if (this.props.canonicalAliasEvent) {
|
|
|
|
oldCanonicalAlias = this.props.canonicalAliasEvent.getContent().alias;
|
|
|
|
}
|
2018-09-20 00:07:01 +00:00
|
|
|
|
2018-10-27 03:50:35 +00:00
|
|
|
const newCanonicalAlias = this.state.canonicalAlias;
|
2018-09-20 00:07:01 +00:00
|
|
|
|
|
|
|
if (this.props.canSetCanonicalAlias && oldCanonicalAlias !== newCanonicalAlias) {
|
2016-09-16 17:01:14 +00:00
|
|
|
console.log("AliasSettings: Updating canonical alias");
|
2017-07-12 13:04:20 +00:00
|
|
|
promises = [Promise.all(promises).then(
|
2016-09-16 17:01:14 +00:00
|
|
|
MatrixClientPeg.get().sendStateEvent(
|
|
|
|
this.props.roomId, "m.room.canonical_alias", {
|
2018-09-20 00:07:01 +00:00
|
|
|
alias: newCanonicalAlias,
|
2017-10-11 16:56:17 +00:00
|
|
|
}, "",
|
|
|
|
),
|
2017-01-20 14:22:27 +00:00
|
|
|
)];
|
2016-09-16 17:01:14 +00:00
|
|
|
}
|
|
|
|
|
2016-02-04 15:26:12 +00:00
|
|
|
return promises;
|
2016-02-02 12:46:14 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
aliasEventsToDictionary: function(aliasEvents) { // m.room.alias events
|
2017-10-11 16:56:17 +00:00
|
|
|
const dict = {};
|
2016-02-02 12:46:14 +00:00
|
|
|
aliasEvents.forEach((event) => {
|
|
|
|
dict[event.getStateKey()] = (
|
|
|
|
(event.getContent().aliases || []).slice() // shallow-copy
|
|
|
|
);
|
|
|
|
});
|
|
|
|
return dict;
|
|
|
|
},
|
|
|
|
|
|
|
|
isAliasValid: function(alias) {
|
2018-12-13 23:05:34 +00:00
|
|
|
// XXX: FIXME https://github.com/matrix-org/matrix-doc/issues/668
|
2016-02-02 12:46:14 +00:00
|
|
|
return (alias.match(/^#([^\/:,]+?):(.+)$/) && encodeURI(alias) === alias);
|
|
|
|
},
|
|
|
|
|
|
|
|
getAliasOperations: function() {
|
2017-10-11 16:56:17 +00:00
|
|
|
const oldAliases = this.aliasEventsToDictionary(this.props.aliasEvents);
|
2016-02-02 12:46:14 +00:00
|
|
|
return ObjectUtils.getKeyValueArrayDiffs(oldAliases, this.state.domainToAliases);
|
|
|
|
},
|
|
|
|
|
2017-10-04 09:00:01 +00:00
|
|
|
onNewAliasChanged: function(value) {
|
|
|
|
this.setState({newAlias: value});
|
|
|
|
},
|
|
|
|
|
|
|
|
onLocalAliasAdded: function(alias) {
|
2016-02-02 12:46:14 +00:00
|
|
|
if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases
|
|
|
|
|
2017-10-04 09:00:01 +00:00
|
|
|
const localDomain = MatrixClientPeg.get().getDomain();
|
2018-09-20 00:38:25 +00:00
|
|
|
if (!alias.includes(':')) alias += ':' + localDomain;
|
2017-10-04 09:00:01 +00:00
|
|
|
if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
|
|
|
|
this.state.domainToAliases[localDomain] = this.state.domainToAliases[localDomain] || [];
|
|
|
|
this.state.domainToAliases[localDomain].push(alias);
|
|
|
|
|
2017-01-20 14:22:27 +00:00
|
|
|
this.setState({
|
2017-10-04 09:00:01 +00:00
|
|
|
domainToAliases: this.state.domainToAliases,
|
|
|
|
// Reset the add field
|
|
|
|
newAlias: "",
|
2016-02-02 12:46:14 +00:00
|
|
|
});
|
2017-10-04 09:00:01 +00:00
|
|
|
} else {
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
2017-07-27 16:19:18 +00:00
|
|
|
Modal.createTrackedDialog('Invalid alias format', '', ErrorDialog, {
|
2017-05-23 14:16:31 +00:00
|
|
|
title: _t('Invalid alias format'),
|
2017-05-27 17:20:35 +00:00
|
|
|
description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }),
|
2016-02-02 12:46:14 +00:00
|
|
|
});
|
|
|
|
}
|
2018-09-20 00:07:01 +00:00
|
|
|
|
|
|
|
if (!this.props.canonicalAlias) {
|
|
|
|
this.setState({
|
2018-10-27 03:50:35 +00:00
|
|
|
canonicalAlias: alias,
|
2018-09-20 00:07:01 +00:00
|
|
|
});
|
|
|
|
}
|
2016-02-02 12:46:14 +00:00
|
|
|
},
|
|
|
|
|
2017-10-04 09:00:01 +00:00
|
|
|
onLocalAliasChanged: function(alias, index) {
|
2016-02-02 12:46:14 +00:00
|
|
|
if (alias === "") return; // hit the delete button to delete please
|
2017-10-04 09:00:01 +00:00
|
|
|
const localDomain = MatrixClientPeg.get().getDomain();
|
2018-09-20 00:38:25 +00:00
|
|
|
if (!alias.includes(':')) alias += ':' + localDomain;
|
2017-10-04 09:00:01 +00:00
|
|
|
if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
|
|
|
|
this.state.domainToAliases[localDomain][index] = alias;
|
|
|
|
} else {
|
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
2017-07-27 16:19:18 +00:00
|
|
|
Modal.createTrackedDialog('Invalid address format', '', ErrorDialog, {
|
2017-05-23 14:16:31 +00:00
|
|
|
title: _t('Invalid address format'),
|
2017-05-27 17:20:35 +00:00
|
|
|
description: _t('\'%(alias)s\' is not a valid format for an address', { alias: alias }),
|
2016-02-02 12:46:14 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-10-04 09:00:01 +00:00
|
|
|
onLocalAliasDeleted: function(index) {
|
|
|
|
const localDomain = MatrixClientPeg.get().getDomain();
|
2016-02-02 12:46:14 +00:00
|
|
|
// 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.
|
2018-09-20 00:07:01 +00:00
|
|
|
const alias = this.state.domainToAliases[localDomain].splice(index, 1);
|
2017-01-20 14:22:27 +00:00
|
|
|
this.setState({
|
2017-10-04 09:00:01 +00:00
|
|
|
domainToAliases: this.state.domainToAliases,
|
2016-02-02 12:46:14 +00:00
|
|
|
});
|
2018-09-20 00:07:01 +00:00
|
|
|
if (this.props.canonicalAlias === alias) {
|
|
|
|
this.setState({
|
|
|
|
canonicalAlias: null,
|
|
|
|
});
|
|
|
|
}
|
2016-02-02 12:46:14 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
onCanonicalAliasChange: function(event) {
|
|
|
|
this.setState({
|
2017-10-11 16:56:17 +00:00
|
|
|
canonicalAlias: event.target.value,
|
2016-02-02 12:46:14 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
2017-10-11 16:56:17 +00:00
|
|
|
const self = this;
|
|
|
|
const EditableText = sdk.getComponent("elements.EditableText");
|
|
|
|
const EditableItemList = sdk.getComponent("elements.EditableItemList");
|
|
|
|
const localDomain = MatrixClientPeg.get().getDomain();
|
2016-02-02 12:46:14 +00:00
|
|
|
|
2017-10-11 16:56:17 +00:00
|
|
|
let canonical_alias_section;
|
2016-02-02 12:46:14 +00:00
|
|
|
if (this.props.canSetCanonicalAlias) {
|
2018-09-20 00:23:29 +00:00
|
|
|
let found = false;
|
2018-10-19 20:18:05 +00:00
|
|
|
const canonicalValue = this.state.canonicalAlias || "";
|
2016-02-02 12:46:14 +00:00
|
|
|
canonical_alias_section = (
|
2019-01-26 03:53:38 +00:00
|
|
|
<Field onChange={this.onCanonicalAliasChange} value={canonicalValue}
|
|
|
|
element='select' id='canonicalAlias' label={_t('Main address')}>
|
2017-05-23 14:16:31 +00:00
|
|
|
<option value="" key="unset">{ _t('not specified') }</option>
|
2016-02-02 12:46:14 +00:00
|
|
|
{
|
2018-09-20 00:23:29 +00:00
|
|
|
Object.keys(self.state.domainToAliases).map((domain, i) => {
|
|
|
|
return self.state.domainToAliases[domain].map((alias, j) => {
|
|
|
|
if (alias === this.state.canonicalAlias) found = true;
|
2016-02-02 12:46:14 +00:00
|
|
|
return (
|
2017-10-11 16:56:17 +00:00
|
|
|
<option value={alias} key={i + "_" + j}>
|
2016-02-02 12:46:14 +00:00
|
|
|
{ alias }
|
|
|
|
</option>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
})
|
|
|
|
}
|
2018-09-20 00:23:29 +00:00
|
|
|
{
|
|
|
|
found || !this.stateCanonicalAlias ? '' :
|
|
|
|
<option value={ this.state.canonicalAlias } key='arbitrary'>
|
|
|
|
{ this.state.canonicalAlias }
|
|
|
|
</option>
|
|
|
|
}
|
2019-01-26 03:53:38 +00:00
|
|
|
</Field>
|
2016-02-02 12:46:14 +00:00
|
|
|
);
|
2017-10-11 16:56:17 +00:00
|
|
|
} else {
|
2016-02-02 12:46:14 +00:00
|
|
|
canonical_alias_section = (
|
2017-05-23 14:16:31 +00:00
|
|
|
<b>{ this.state.canonicalAlias || _t('not set') }</b>
|
2016-02-02 12:46:14 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-10-11 16:56:17 +00:00
|
|
|
let remote_aliases_section;
|
2016-02-02 12:46:14 +00:00
|
|
|
if (this.state.remoteDomains.length) {
|
|
|
|
remote_aliases_section = (
|
|
|
|
<div>
|
2019-02-04 20:25:26 +00:00
|
|
|
<div>
|
2017-10-11 16:56:17 +00:00
|
|
|
{ _t("Remote addresses for this room:") }
|
2016-02-02 12:46:14 +00:00
|
|
|
</div>
|
2019-02-08 16:11:30 +00:00
|
|
|
<ul>
|
2016-02-02 12:46:14 +00:00
|
|
|
{ this.state.remoteDomains.map((domain, i) => {
|
2019-02-08 16:11:30 +00:00
|
|
|
return this.state.domainToAliases[domain].map((alias, j) => {
|
|
|
|
return <li key={i + "_" + j}>{alias}</li>;
|
2016-02-02 12:46:14 +00:00
|
|
|
});
|
2017-10-11 16:56:17 +00:00
|
|
|
}) }
|
2019-02-08 16:11:30 +00:00
|
|
|
</ul>
|
2016-02-02 12:46:14 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2019-01-28 18:34:21 +00:00
|
|
|
<div className='mx_AliasSettings'>
|
2019-01-26 03:53:38 +00:00
|
|
|
{canonical_alias_section}
|
2017-10-04 09:00:01 +00:00
|
|
|
<EditableItemList
|
|
|
|
className={"mx_RoomSettings_localAliases"}
|
|
|
|
items={this.state.domainToAliases[localDomain] || []}
|
|
|
|
newItem={this.state.newAlias}
|
2017-10-04 09:26:43 +00:00
|
|
|
onNewItemChanged={this.onNewAliasChanged}
|
2017-10-24 15:19:09 +00:00
|
|
|
canEdit={this.props.canSetAliases}
|
2017-10-04 09:00:01 +00:00
|
|
|
onItemAdded={this.onLocalAliasAdded}
|
|
|
|
onItemEdited={this.onLocalAliasChanged}
|
|
|
|
onItemRemoved={this.onLocalAliasDeleted}
|
|
|
|
itemsLabel={_t('Local addresses for this room:')}
|
|
|
|
noItemsLabel={_t('This room has no local addresses')}
|
|
|
|
placeholder={_t(
|
|
|
|
'New address (e.g. #foo:%(localDomain)s)', {localDomain: localDomain},
|
|
|
|
)}
|
|
|
|
/>
|
2016-02-02 12:46:14 +00:00
|
|
|
|
|
|
|
{ remote_aliases_section }
|
|
|
|
|
|
|
|
</div>
|
|
|
|
);
|
2017-10-04 09:00:01 +00:00
|
|
|
},
|
2017-01-20 14:22:27 +00:00
|
|
|
});
|