more refactoring of topic and account editing

This commit is contained in:
or-else
2017-06-29 14:02:16 +03:00
parent 933ede1220
commit 45eb91765b
2 changed files with 219 additions and 98 deletions
+28 -9
View File
@@ -40,14 +40,15 @@ a:focus {
form { form {
font-size: 1.1rem; font-size: 1.1rem;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
line-height: 1.75; line-height: 1.5;
} }
form label { label {
font-size: 1.1rem; font-size: 1.1rem;
margin-right: auto;
} }
form label.small { label.small {
color: #1e88e5; color: #1e88e5;
font-size: 0.95rem; font-size: 0.95rem;
} }
@@ -251,7 +252,7 @@ ul.menu > li.separator:hover {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 0.3; flex-grow: 0;
flex-shrink: 0.3; flex-shrink: 0.3;
width: 24rem; width: 24rem;
min-width: 18rem; min-width: 18rem;
@@ -268,7 +269,7 @@ ul.menu > li.separator:hover {
#info-view { #info-view {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 0.3; flex-grow: 0;
flex-shrink: 0.3; flex-shrink: 0.3;
width: 24rem; width: 24rem;
min-width: 18rem; min-width: 18rem;
@@ -461,6 +462,8 @@ ul.menu > li.separator:hover {
display: flex; display: flex;
padding: 0.35rem 0.25rem 0.5rem 0.35rem; padding: 0.35rem 0.25rem 0.5rem 0.35rem;
min-height: 3.5rem; min-height: 3.5rem;
flex-grow: 0;
flex-shrink: 0;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
} }
.contact-box > li:hover { .contact-box > li:hover {
@@ -544,20 +547,35 @@ ul.menu > li.separator:hover {
flex-direction: column; flex-direction: column;
} }
.panel-form-one { .panel-form-row {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
padding: 0.5rem 0.75rem;
line-height: 1.5;
flex-grow: 0;
flex-shrink: 0;
} }
.panel-form-two { .panel-form-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 0;
flex-shrink: 0;
padding: 0.5rem 0.75rem;
line-height: 1.5;
}
/* Make sure padding is only applied at
the top level panel */
.panel-form-row > .panel-form-column {
padding: 0!important;
flex-grow: 1; flex-grow: 1;
} }
.panel-form-column > .panel-form-row {
padding: 0!important;
}
/* Styling of avatar upload control */
/* Styling of avatar upload area in the New Account panel */
.avatar-upload { .avatar-upload {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -565,6 +583,7 @@ ul.menu > li.separator:hover {
width: 7rem; width: 7rem;
height: 9rem; height: 9rem;
margin-left: 0.5rem; margin-left: 0.5rem;
font-size: 4.5rem;
padding: 0; padding: 0;
} }
/* Clearfix */ /* Clearfix */
+161 -59
View File
@@ -244,9 +244,13 @@
this.setState({text: e.target.value}); this.setState({text: e.target.value});
}, },
handleKeyPress: function(e) { handleKeyDown: function(e) {
if (e.keyCode === 27) { if (e.keyCode === 27) {
// Escape pressed
this.setState({text: this.props.text, active: false}); this.setState({text: this.props.text, active: false});
} else if (e.keyCode === 13) {
// Enter pressed
this.handleEditingFinished();
} }
}, },
@@ -257,8 +261,9 @@
handleEditingFinished: function() { handleEditingFinished: function() {
this.setState({active: false}); this.setState({active: false});
if (this.state.text !== this.props.text) { var text = this.state.text.trim();
this.props.onFinished(this.state.text); if (text !== this.props.text) {
this.props.onFinished(text);
} }
}, },
@@ -271,8 +276,8 @@
spanText = this.props.placeholder; spanText = this.props.placeholder;
spanClass += " placeholder"; spanClass += " placeholder";
} }
if (spanText.length > 16) { if (spanText.length > 20) {
spanText = spanText.substring(0, 16) + "..."; spanText = spanText.substring(0, 19) + "...";
} }
return ( return (
this.state.active ? this.state.active ?
@@ -280,7 +285,7 @@
value={this.state.text} value={this.state.text}
placeholder={this.props.placeholder} placeholder={this.props.placeholder}
onChange={this.handeTextChange} onChange={this.handeTextChange}
onKeyPress={this.handleKeyPress} onKeyDown={this.handleKeyDown}
onBlur={this.handleEditingFinished} onBlur={this.handleEditingFinished}
autoFocus /> autoFocus />
: :
@@ -448,8 +453,8 @@
} }
return ( return (
<form className="panel-form" onSubmit={this.handleSubmit}> <form className="panel-form" onSubmit={this.handleSubmit}>
<div className="panel-form-one"> <div className="panel-form-row">
<div className="panel-form-two"> <div className="panel-form-column">
<input type="text" placeholder="Login" <input type="text" placeholder="Login"
value={this.state.login} onChange={this.handleLoginChange} required autoFocus /> value={this.state.login} onChange={this.handleLoginChange} required autoFocus />
<input type="password" placeholder="Password" <input type="password" placeholder="Password"
@@ -506,6 +511,8 @@
<div className="avatar-upload"> <div className="avatar-upload">
{this.state.dataUrl ? {this.state.dataUrl ?
<img src={this.state.dataUrl} className="preview" /> : <img src={this.state.dataUrl} className="preview" /> :
this.props.readOnly && this.props.uid ?
<LetterTile avatar={true} topic={this.props.uid} title={this.props.title} /> :
<div className="blank">128&times;128</div>} <div className="blank">128&times;128</div>}
{this.props.readOnly ? null : {this.props.readOnly ? null :
<input type="file" id="file-input-avatar" className="inputfile hidden" <input type="file" id="file-input-avatar" className="inputfile hidden"
@@ -726,8 +733,6 @@
var EditAccountView = React.createClass({ var EditAccountView = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
updatedDesc: false,
updatedPasswd: false,
address: null, address: null,
fullName: undefined, fullName: undefined,
avatar: null, avatar: null,
@@ -743,7 +748,6 @@
fullName: me.public ? me.public.fn : undefined, fullName: me.public ? me.public.fn : undefined,
avatar: makeImageUrl(me.public ? me.public.photo : null), avatar: makeImageUrl(me.public ? me.public.photo : null),
address: Tinode.getCurrentUserID(), address: Tinode.getCurrentUserID(),
login: this.props.login,
auth: defacs.auth, auth: defacs.auth,
anon: defacs.anon anon: defacs.anon
}); });
@@ -758,29 +762,23 @@
}, },
handleFullNameUpdate: function(fn) { handleFullNameUpdate: function(fn) {
this.setState({updatedDesc: true, fullName: fn}); this.setState({fullName: fn});
this.props.onSubmit(null, vcard(fn, this.state.avatar));
}, },
handlePasswordUpdate: function(pwd) { handlePasswordUpdate: function(pwd) {
this.setState({updatedPasswd: true, password: pwd}); this.setState({password: pwd});
this.props.onSubmit(pwd, null);
}, },
handleImageChanged: function(img) { handleImageChanged: function(img) {
this.setState({updatedDesc: true, avatar: img}); this.setState({avatar: img});
}, this.props.onSubmit(null, vcard(this.state.fullName, img));
handleSubmit: function(e) {
e.preventDefault();
if (this.state.updatedDesc || this.state.updatedPasswd) {
this.props.onSubmit(
this.state.updatedPasswd ? this.state.password : null,
this.state.updatedDesc ? vcard(this.state.fullName, this.state.avatar) : null);
}
}, },
render: function() { render: function() {
return ( return (
<form id="edit-account" className="panel-form" onSubmit={this.handleSubmit}> <div id="edit-account" className="panel-form">
<div className="panel-form-one"> <div className="panel-form-row">
<div className="panel-form-two"> <div className="panel-form-column">
<div><label className="small">Your name</label></div> <div><label className="small">Your name</label></div>
<div><InPlaceEdit <div><InPlaceEdit
placeholder="Full name, e.g. John Doe" placeholder="Full name, e.g. John Doe"
@@ -794,22 +792,28 @@
</div> </div>
<AvatarUpload <AvatarUpload
avatar={this.state.avatar} avatar={this.state.avatar}
uid={this.state.address}
title={this.state.fullName}
onImageChanged={this.handleImageChanged} /> onImageChanged={this.handleImageChanged} />
</div> </div>
<div className="panel-form-column">
<div className="panel-form-row">
<label>Login:</label>
<tt>{this.props.login}</tt>
</div>
<div className="panel-form-row">
<label>Address:</label>
<tt>{this.state.address}</tt>
</div>
<div> <div>
<div>Login: <tt>{this.state.login}</tt></div> <label className="small">Default access mode:</label>
<div>Address: <tt>{this.state.address}</tt></div> </div>
<div><label className="small">Default access mode:</label></div>
<div className="quoted"> <div className="quoted">
<div>Auth: <tt>{this.state.auth}</tt></div> <div>Auth: <tt>{this.state.auth}</tt></div>
<div>Anon: <tt>{this.state.anon}</tt></div> <div>Anon: <tt>{this.state.anon}</tt></div>
</div> </div>
</div> </div>
<div className="dialog-buttons">
<button className="blue" type="submit">Update</button>
</div> </div>
</form>
); );
} }
}); });
@@ -845,7 +849,7 @@
<div className="avatar-box"> <div className="avatar-box">
<LetterTile <LetterTile
avatar={avatar} title={this.props.title} topic={this.props.topic} /> avatar={avatar} title={this.props.title} topic={this.props.topic} />
<span className={online} /> {this.props.showOnline ? <span className={online} /> : null}
</div> </div>
<div className="text-box"> <div className="text-box">
<div><span className="contact-title">{title}</span> <div><span className="contact-title">{title}</span>
@@ -966,6 +970,7 @@
contacts={this.state.contactList} contacts={this.state.contactList}
emptyListMessage={<span>You have no chats<br />¯\_(ツ)_/¯</span>} emptyListMessage={<span>You have no chats<br />¯\_(ツ)_/¯</span>}
topicSelected={this.props.topicSelected} topicSelected={this.props.topicSelected}
showOnline={true}
onTopicSelected={this.props.onTopicSelected} onTopicSelected={this.props.onTopicSelected}
showContextMenu={this.props.showContextMenu} /> showContextMenu={this.props.showContextMenu} />
); );
@@ -980,6 +985,10 @@
var contactNodes = []; var contactNodes = [];
if (this.props.contacts && this.props.contacts.length > 0) { if (this.props.contacts && this.props.contacts.length > 0) {
this.props.contacts.map(function(c) { this.props.contacts.map(function(c) {
var key = c.topic ? c.topic : c.user;
if (!key) {
console.log("empty key!");
}
contactNodes.push( contactNodes.push(
<Contact <Contact
title={c.public ? c.public.fn : null} title={c.public ? c.public.fn : null}
@@ -987,10 +996,11 @@
comment={c.private} comment={c.private}
unread={c.unread} now={c.online} unread={c.unread} now={c.online}
selected={this.props.topicSelected} selected={this.props.topicSelected}
showOnline={this.props.showOnline}
onSelected={this.props.onTopicSelected} onSelected={this.props.onTopicSelected}
showContextMenu={this.props.showContextMenu} showContextMenu={this.props.showContextMenu}
topic={c.topic} topic={key}
key={c.topic} /> key={key} />
); );
}, this); }, this);
} }
@@ -1109,8 +1119,8 @@
} }
return ( return (
<form className="panel-form" onSubmit={this.handleSubmit}> <form className="panel-form" onSubmit={this.handleSubmit}>
<div className="panel-form-one"> <div className="panel-form-row">
<div className="panel-form-two"> <div className="panel-form-column">
<input type="text" placeholder="Name of the topic to create" <input type="text" placeholder="Name of the topic to create"
value={this.state.fn} onChange={this.handleFnChange} autoFocus required /> value={this.state.fn} onChange={this.handleFnChange} autoFocus required />
<div className="dialog-buttons"> <div className="dialog-buttons">
@@ -1152,7 +1162,7 @@
render: function() { render: function() {
return ( return (
<form className="panel-form" onSubmit={this.handleFindContacts}> <form className="panel-form" onSubmit={this.handleFindContacts}>
<div className="panel-form-one"> <div className="panel-form-row">
<input type="text" placeholder="Comma separated list: email:alice@example.com, tel:17025550003..." <input type="text" placeholder="Comma separated list: email:alice@example.com, tel:17025550003..."
value={this.state.search} onChange={this.handleSearchChange} onKeyPress={this.handleKeyPress} required /> value={this.state.search} onChange={this.handleSearchChange} onKeyPress={this.handleKeyPress} required />
<a href="#" onClick={this.handleSearch}> <a href="#" onClick={this.handleSearch}>
@@ -1211,11 +1221,14 @@
getInitialState: function() { getInitialState: function() {
return { return {
owner: false, owner: false,
updated: false,
address: null, address: null,
groupTopic: undefined,
fullName: undefined, fullName: undefined,
avatar: null, avatar: null,
private: null private: null,
selectedContact: null,
muted: undefined,
contactList: []
}; };
}, },
componentWillMount: function() { componentWillMount: function() {
@@ -1225,6 +1238,31 @@
} }
topic.onMetaDesc = this.onMetaDesc; topic.onMetaDesc = this.onMetaDesc;
topic.onSubsUpdated = this.onSubsUpdated;
this.resetDesc(topic);
this.resetSubs(topic);
},
componentWillUnmount: function() {
var topic = Tinode.getTopic(this.props.topic);
if (!topic) {
return;
}
topic.onMetaDesc = undefined;
topic.onSubsUpdated = undefined;
},
resetSubs: function(topic) {
var contacts = [];
topic.subscribers(function(sub){
contacts.push(sub);
}, this);
this.setState({
contactList: contacts
});
},
resetDesc: function(topic) {
var defacs = topic.getDefaultAccess() || {}; var defacs = topic.getDefaultAccess() || {};
var acs = topic.getAccessMode(); var acs = topic.getAccessMode();
this.setState({ this.setState({
@@ -1233,30 +1271,62 @@
avatar: makeImageUrl(topic.public ? topic.public.photo : null), avatar: makeImageUrl(topic.public ? topic.public.photo : null),
private: topic.private, private: topic.private,
address: topic.name, address: topic.name,
groupTopic: (topic.getType() === "grp"),
auth: defacs.auth, auth: defacs.auth,
anon: defacs.anon anon: defacs.anon,
muted: acs && acs.isMuted()
}); });
}, },
componentWillUnmount: function() {
onMetaDesc: function(desc) {
var topic = Tinode.getTopic(this.props.topic); var topic = Tinode.getTopic(this.props.topic);
if (!topic) { if (!topic) {
return; return;
} }
topic.onMetaDesc = undefined; this.resetDesc(topic);
}, },
onMetaDesc: function(desc) { onSubsUpdated: function() {
// Update form with changed values var topic = Tinode.getTopic(this.props.topic);
if (!topic) {
return;
}
this.resetSubs(topic);
}, },
handleFullNameUpdate: function(fn) { handleFullNameUpdate: function(fn) {
this.setState({updated: true, fullName: fn}); this.setState({fullName: fn});
this.props.onSubmit(this.props.topic,
vcard(fn, this.state.avatar),
null);
}, },
handlePrivateUpdate: function(priv) { handlePrivateUpdate: function(priv) {
this.setState({updated: true, private: priv}); this.setState({private: priv});
this.props.onSubmit(this.props.topic,
null,
priv);
}, },
handleImageChanged: function(img) { handleImageChanged: function(img) {
this.setState({updated: true, avatar: img}); this.setState({avatar: img});
this.props.onSubmit(this.props.topic,
vcard(this.state.fullName, img),
priv);
},
handleMuted: function(e) {
var checked = e.target.checked;
this.setState({muted: checked});
this.props.onChangePermissions(this.props.topic, checked ? "-P" : "+P");
},
handleMemberSelected: function(uid) {
this.setState({selectedContact: uid});
},
handleContextMenu: function(item) {
console.log("context menu: " + item);
}, },
handleSubmit: function(e) { handleSubmit: function(e) {
@@ -1277,9 +1347,8 @@
<MenuCancel onCancel={this.props.onCancel} /> <MenuCancel onCancel={this.props.onCancel} />
</div> </div>
</div> </div>
<form id="edit-account" className="panel-form" onSubmit={this.handleSubmit}> <div className="panel-form-row">
<div className="panel-form-one"> <div className="panel-form-column">
<div className="panel-form-two">
<div><label className="small">Name</label></div> <div><label className="small">Name</label></div>
<div><InPlaceEdit <div><InPlaceEdit
placeholder="Group name" placeholder="Group name"
@@ -1294,10 +1363,21 @@
</div> </div>
<AvatarUpload <AvatarUpload
avatar={this.state.avatar} avatar={this.state.avatar}
readOnly={!this.state.owner}
uid={this.props.topic}
title={this.state.fullName}
onImageChanged={this.handleImageChanged} /> onImageChanged={this.handleImageChanged} />
</div> </div>
<div> <div className="panel-form-column">
<div>Address: <tt>{this.state.address}</tt></div> <div className="panel-form-row">
<label>Address:</label>
<tt>{this.state.address}</tt>
</div>
<div className="panel-form-row">
<label htmlFor="info-muted">Muted:</label>
<input id="info-muted" type="checkbox" checked={this.state.muted}
onChange={this.handleMuted} />
</div>
{this.state.auth || this.state.anon ? {this.state.auth || this.state.anon ?
<div> <div>
<div><label className="small">Default access mode:</label></div> <div><label className="small">Default access mode:</label></div>
@@ -1310,10 +1390,20 @@
null null
} }
</div> </div>
<div className="dialog-buttons"> {this.state.groupTopic ?
<button className="blue" type="submit">Update</button> <div className="panel-form-row">
</div> <label className="small">Group members:</label>
</form> </div> : null}
{this.state.groupTopic ?
<ContactList
contacts={this.state.contactList}
emptyListMessage="No members"
topicSelected={this.state.selectedContact}
showOnline={false}
onTopicSelected={this.handleMemberSelected}
showContextMenu={this.handleContextMenu}
/>
: null}
</div> </div>
); );
} }
@@ -1897,7 +1987,6 @@
}, },
handleUpdateAccountRequest: function(password, pub, priv) { handleUpdateAccountRequest: function(password, pub, priv) {
this.handleSidepanelCancel();
if (pub || priv) { if (pub || priv) {
Tinode.getMeTopic().setMeta({desc: {public: pub, private: priv}}); Tinode.getMeTopic().setMeta({desc: {public: pub, private: priv}});
} }
@@ -1964,6 +2053,18 @@
var topic = Tinode.getTopic(topicName); var topic = Tinode.getTopic(topicName);
topic.setMeta({desc: {public: pub, private: priv}}); topic.setMeta({desc: {public: pub, private: priv}});
}, },
handleChangePermissions: function(topicName, want) {
var topic = Tinode.getTopic(topicName);
if (!topic) {
return;
}
var am = topic.getAccessMode();
am.updateWant(want);
topic.setMeta({sub: {mode: am.getWant()}});
},
/** Called when the user enters a contact into the contact search field in the NewAccount panel /** Called when the user enters a contact into the contact search field in the NewAccount panel
@param query {Array} is an array of contacts to search for @param query {Array} is an array of contacts to search for
*/ */
@@ -2122,6 +2223,7 @@
topic={this.state.topicSelected} topic={this.state.topicSelected}
onSubmit={this.handleTopicUpdateRequest} onSubmit={this.handleTopicUpdateRequest}
onCancel={this.handleHideInfoView} onCancel={this.handleHideInfoView}
onChangePermissions={this.handleChangePermissions}
/> />
: :
null null