Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 1938a76

Browse files
authored
Merge pull request #1444 from matrix-org/luke/refactor-editable-list-from-alias-settings
Factor out EditableItemList from AliasSettings
2 parents 011aadc + 28b8582 commit 1938a76

File tree

2 files changed

+190
-74
lines changed

2 files changed

+190
-74
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
Copyright 2017 New Vector Ltd.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import React from 'react';
18+
import PropTypes from 'prop-types';
19+
import sdk from '../../../index';
20+
import {_t} from '../../../languageHandler.js';
21+
22+
const EditableItem = React.createClass({
23+
displayName: 'EditableItem',
24+
25+
propTypes: {
26+
initialValue: PropTypes.string,
27+
index: PropTypes.number,
28+
placeholder: PropTypes.string,
29+
30+
onChange: PropTypes.func,
31+
onRemove: PropTypes.func,
32+
onAdd: PropTypes.func,
33+
34+
addOnChange: PropTypes.bool,
35+
},
36+
37+
onChange: function(value) {
38+
this.setState({ value });
39+
if (this.props.onChange) this.props.onChange(value, this.props.index);
40+
if (this.props.addOnChange && this.props.onAdd) this.props.onAdd(value);
41+
},
42+
43+
onRemove: function() {
44+
if (this.props.onRemove) this.props.onRemove(this.props.index);
45+
},
46+
47+
onAdd: function() {
48+
if (this.props.onAdd) this.props.onAdd(this.state.value);
49+
},
50+
51+
render: function() {
52+
const EditableText = sdk.getComponent('elements.EditableText');
53+
return <div className="mx_EditableItem">
54+
<EditableText
55+
className="mx_EditableItem_editable"
56+
placeholderClassName="mx_EditableItem_editablePlaceholder"
57+
placeholder={this.props.placeholder}
58+
blurToCancel={false}
59+
editable={true}
60+
initialValue={this.props.initialValue}
61+
onValueChanged={this.onChange} />
62+
{ this.props.onAdd ?
63+
<div className="mx_EditableItem_addButton">
64+
<img className="mx_filterFlipColor"
65+
src="img/plus.svg" width="14" height="14"
66+
alt={_t("Add")} onClick={this.onAdd} />
67+
</div>
68+
:
69+
<div className="mx_EditableItem_removeButton">
70+
<img className="mx_filterFlipColor"
71+
src="img/cancel-small.svg" width="14" height="14"
72+
alt={_t("Delete")} onClick={this.onRemove} />
73+
</div>
74+
}
75+
</div>;
76+
},
77+
});
78+
79+
module.exports = React.createClass({
80+
displayName: 'EditableItemList',
81+
82+
propTypes: {
83+
items: PropTypes.arrayOf(PropTypes.string).isRequired,
84+
onNewItemChanged: PropTypes.func,
85+
onItemAdded: PropTypes.func,
86+
onItemEdited: PropTypes.func,
87+
onItemRemoved: PropTypes. func,
88+
},
89+
90+
getDefaultProps: function() {
91+
return {
92+
onItemAdded: () => {},
93+
onItemEdited: () => {},
94+
onItemRemoved: () => {},
95+
onNewItemChanged: () => {},
96+
};
97+
},
98+
99+
onItemAdded: function(value) {
100+
this.props.onItemAdded(value);
101+
},
102+
103+
onItemEdited: function(value, index) {
104+
if (value.length === 0) {
105+
this.onItemRemoved(index);
106+
} else {
107+
this.onItemEdited(value, index);
108+
}
109+
},
110+
111+
onItemRemoved: function(index) {
112+
this.props.onItemRemoved(index);
113+
},
114+
115+
onNewItemChanged: function(value) {
116+
this.props.onNewItemChanged(value);
117+
},
118+
119+
render: function() {
120+
const editableItems = this.props.items.map((item, index) => {
121+
return <EditableItem
122+
key={index}
123+
index={index}
124+
initialValue={item}
125+
onChange={this.onItemEdited}
126+
onRemove={this.onItemRemoved}
127+
placeholder={this.props.placeholder}
128+
/>;
129+
});
130+
131+
const label = this.props.items.length > 0 ?
132+
this.props.itemsLabel : this.props.noItemsLabel;
133+
134+
return (<div className="mx_EditableItemList">
135+
<div className="mx_EditableItemList_label">
136+
{ label }
137+
</div>
138+
{ editableItems }
139+
<EditableItem
140+
key={-1}
141+
initialValue={this.props.newItem}
142+
onAdd={this.onItemAdded}
143+
onChange={this.onNewItemChanged}
144+
addOnChange={true}
145+
placeholder={this.props.placeholder}
146+
/>
147+
</div>);
148+
},
149+
});

src/components/views/room_settings/AliasSettings.js

Lines changed: 41 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -136,56 +136,56 @@ module.exports = React.createClass({
136136
return ObjectUtils.getKeyValueArrayDiffs(oldAliases, this.state.domainToAliases);
137137
},
138138

139-
onAliasAdded: function(alias) {
139+
onNewAliasChanged: function(value) {
140+
this.setState({newAlias: value});
141+
},
142+
143+
onLocalAliasAdded: function(alias) {
140144
if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases
141145

142-
if (this.isAliasValid(alias)) {
143-
// add this alias to the domain to aliases dict
144-
var domain = alias.replace(/^.*?:/, '');
145-
// XXX: do we need to deep copy aliases before editing it?
146-
this.state.domainToAliases[domain] = this.state.domainToAliases[domain] || [];
147-
this.state.domainToAliases[domain].push(alias);
146+
const localDomain = MatrixClientPeg.get().getDomain();
147+
if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
148+
this.state.domainToAliases[localDomain] = this.state.domainToAliases[localDomain] || [];
149+
this.state.domainToAliases[localDomain].push(alias);
150+
148151
this.setState({
149-
domainToAliases: this.state.domainToAliases
152+
domainToAliases: this.state.domainToAliases,
153+
// Reset the add field
154+
newAlias: "",
150155
});
151-
152-
// reset the add field
153-
this.refs.add_alias.setValue(''); // FIXME
154-
}
155-
else {
156-
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
156+
} else {
157+
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
157158
Modal.createTrackedDialog('Invalid alias format', '', ErrorDialog, {
158159
title: _t('Invalid alias format'),
159160
description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }),
160161
});
161162
}
162163
},
163164

164-
onAliasChanged: function(domain, index, alias) {
165+
onLocalAliasChanged: function(alias, index) {
165166
if (alias === "") return; // hit the delete button to delete please
166-
var oldAlias;
167-
if (this.isAliasValid(alias)) {
168-
oldAlias = this.state.domainToAliases[domain][index];
169-
this.state.domainToAliases[domain][index] = alias;
170-
}
171-
else {
172-
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
167+
const localDomain = MatrixClientPeg.get().getDomain();
168+
if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
169+
this.state.domainToAliases[localDomain][index] = alias;
170+
} else {
171+
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
173172
Modal.createTrackedDialog('Invalid address format', '', ErrorDialog, {
174173
title: _t('Invalid address format'),
175174
description: _t('\'%(alias)s\' is not a valid format for an address', { alias: alias }),
176175
});
177176
}
178177
},
179178

180-
onAliasDeleted: function(domain, index) {
179+
onLocalAliasDeleted: function(index) {
180+
const localDomain = MatrixClientPeg.get().getDomain();
181181
// It's a bit naughty to directly manipulate this.state, and React would
182182
// normally whine at you, but it can't see us doing the splice. Given we
183183
// promptly setState anyway, it's just about acceptable. The alternative
184184
// would be to arbitrarily deepcopy to a temp variable and then setState
185185
// that, but why bother when we can cut this corner.
186-
var alias = this.state.domainToAliases[domain].splice(index, 1);
186+
this.state.domainToAliases[localDomain].splice(index, 1);
187187
this.setState({
188-
domainToAliases: this.state.domainToAliases
188+
domainToAliases: this.state.domainToAliases,
189189
});
190190
},
191191

@@ -198,6 +198,7 @@ module.exports = React.createClass({
198198
render: function() {
199199
var self = this;
200200
var EditableText = sdk.getComponent("elements.EditableText");
201+
var EditableItemList = sdk.getComponent("elements.EditableItemList");
201202
var localDomain = MatrixClientPeg.get().getDomain();
202203

203204
var canonical_alias_section;
@@ -257,58 +258,24 @@ module.exports = React.createClass({
257258
<div className="mx_RoomSettings_aliasLabel">
258259
{ _t('The main address for this room is') }: { canonical_alias_section }
259260
</div>
260-
<div className="mx_RoomSettings_aliasLabel">
261-
{ (this.state.domainToAliases[localDomain] &&
262-
this.state.domainToAliases[localDomain].length > 0)
263-
? _t('Local addresses for this room:')
264-
: _t('This room has no local addresses') }
265-
</div>
266-
<div className="mx_RoomSettings_aliasesTable">
267-
{ (this.state.domainToAliases[localDomain] || []).map((alias, i) => {
268-
var deleteButton;
269-
if (this.props.canSetAliases) {
270-
deleteButton = (
271-
<img src="img/cancel-small.svg" width="14" height="14"
272-
alt={ _t('Delete') } onClick={ self.onAliasDeleted.bind(self, localDomain, i) } />
273-
);
274-
}
275-
return (
276-
<div className="mx_RoomSettings_aliasesTableRow" key={ i }>
277-
<EditableText
278-
className="mx_RoomSettings_alias mx_RoomSettings_editable"
279-
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
280-
placeholder={ _t('New address (e.g. #foo:%(localDomain)s)', { localDomain: localDomain}) }
281-
blurToCancel={ false }
282-
onValueChanged={ self.onAliasChanged.bind(self, localDomain, i) }
283-
editable={ self.props.canSetAliases }
284-
initialValue={ alias } />
285-
<div className="mx_RoomSettings_deleteAlias mx_filterFlipColor">
286-
{ deleteButton }
287-
</div>
288-
</div>
289-
);
290-
})}
291-
292-
{ this.props.canSetAliases ?
293-
<div className="mx_RoomSettings_aliasesTableRow" key="new">
294-
<EditableText
295-
ref="add_alias"
296-
className="mx_RoomSettings_alias mx_RoomSettings_editable"
297-
placeholderClassName="mx_RoomSettings_aliasPlaceholder"
298-
placeholder={ _t('New address (e.g. #foo:%(localDomain)s)', { localDomain: localDomain}) }
299-
blurToCancel={ false }
300-
onValueChanged={ self.onAliasAdded } />
301-
<div className="mx_RoomSettings_addAlias mx_filterFlipColor">
302-
<img src="img/plus.svg" width="14" height="14" alt={ _t("Add") }
303-
onClick={ self.onAliasAdded.bind(self, undefined) }/>
304-
</div>
305-
</div> : ""
306-
}
307-
</div>
261+
<EditableItemList
262+
className={"mx_RoomSettings_localAliases"}
263+
items={this.state.domainToAliases[localDomain] || []}
264+
newItem={this.state.newAlias}
265+
onNewItemChanged={this.onNewAliasChanged}
266+
onItemAdded={this.onLocalAliasAdded}
267+
onItemEdited={this.onLocalAliasChanged}
268+
onItemRemoved={this.onLocalAliasDeleted}
269+
itemsLabel={_t('Local addresses for this room:')}
270+
noItemsLabel={_t('This room has no local addresses')}
271+
placeholder={_t(
272+
'New address (e.g. #foo:%(localDomain)s)', {localDomain: localDomain},
273+
)}
274+
/>
308275

309276
{ remote_aliases_section }
310277

311278
</div>
312279
);
313-
}
280+
},
314281
});

0 commit comments

Comments
 (0)