permissions editing works now

This commit is contained in:
or-else
2017-07-15 12:35:53 +03:00
parent 1054c81de3
commit 5868915f2c
3 changed files with 198 additions and 16 deletions
+2 -2
View File
@@ -2,11 +2,11 @@
Example using [Tinode](https://github.com/tinode/chat/) to build a single-page chat application. The app uses Example using [Tinode](https://github.com/tinode/chat/) to build a single-page chat application. The app uses
[React](https://facebook.github.io/react/), Google's material design [fonts](https://www.google.com/fonts/) [React](https://facebook.github.io/react/), Google's material design [fonts](https://www.google.com/fonts/)
and [icons](https://google.github.io/material-design-icons/#icon-font-for-the-web). The [Tinode javascript library](https://github.com/tinode/tinode-js/) has no external dependencies. and [icons](https://google.github.io/material-design-icons/#icon-font-for-the-web). The [Tinode javascript library](https://github.com/tinode/tinode-js/) has no external dependencies. Overall it's a lot like open source WhatsApp or Telegram web apps.
This is work in progress. Bugs are expected. The app was tested in the latest Chrome & Firefox only. This is work in progress. Bugs are expected. The app was tested in the latest Chrome & Firefox only.
Try it (possibly older version) live at http://api.tinode.co/x/example-react-js/. Login as one of `alice`, `bob`, `carol`, `dave`, `frank`. Password is `<login>123`, e.g. password for `alice` is `alice123`. You can discover other users by email or phone by prefixing them with `email:` or `tel:` respectively. Emails are `<login>@example.com`, e.g. `alice@example.com`, phones are `17025550001` through `17025550009`. The demo is reset every night at 3:15 am Pacific time. Try it (possibly older version) live at http://api.tinode.co/x/example-react-js/. Login as one of `alice`, `bob`, `carol`, `dave`, `frank`. Password is `<login>123`, e.g. password for `alice` is `alice123`. You can discover other users by email or phone by prefixing them with `email:` or `tel:` respectively. Emails are `<login>@example.com`, e.g. `alice@example.com`, phones are `17025550001` through `17025550009`. The demo is reset every night at 2:15 am Pacific time.
License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0)
+26
View File
@@ -393,6 +393,28 @@ ul.menu > li.separator:hover {
} }
/* END of InPlaceEdit styles */ /* END of InPlaceEdit styles */
ul.permission-editor {
list-style-type: none;
}
ul.permission-editor > li {
margin: 0.35rem 0;
font-size: 1.1rem;
vertical-align: middle;
}
.permission-editor .material-icons {
font-size: 1.1rem;
line-height: 1.1rem;
vertical-align: middle;
cursor: pointer;
}
.clickable {
border-bottom: 1px dashed #ddd;
cursor: pointer;
}
/* END of top-level layout */ /* END of top-level layout */
/* Sidepanel styles */ /* Sidepanel styles */
@@ -417,6 +439,10 @@ ul.menu > li.separator:hover {
justify-content: flex-end; justify-content: flex-end;
} }
.dialog-buttons button {
margin: 0 0.5rem;
}
.avatar-box { .avatar-box {
position: relative; position: relative;
flex-grow: 0; flex-grow: 0;
+170 -14
View File
@@ -342,6 +342,104 @@
} }
/* END Combobox for selecting host name */ /* END Combobox for selecting host name */
/* BEGIN CheckBox: styled checkbox */
class CheckBox extends React.PureComponent {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.props.onChange(this.props.name, !this.props.checked);
}
render() {
return (
this.props.checked ?
<i className="material-icons blue" onClick={this.handleChange}>check_box</i> :
<i className="material-icons blue" onClick={this.handleChange}>check_box_outline_blank</i>
);
}
}
/* END CheckBox */
/* BEGIN PermissionsEditor: Component for editing permissions */
// <PermissionsEditor mode="JWROD" skip="O" onChange={this.handleCheckboxTest} />
var PermissionsEditor = React.createClass({
getInitialState: function() {
return {
mode: (this.props.mode || "").replace("N", "")
};
},
handleChange: function(val) {
var mode = this.state.mode;
var idx = mode.indexOf(val);
if (idx == -1) {
mode += val;
} else {
mode = mode.replace(val, "");
}
this.setState({mode: mode});
},
handleSubmit: function() {
var mode = this.state.mode || "N";
if (mode !== this.props.mode) {
this.props.onSubmit(mode);
} else {
this.props.onCancel();
}
},
handleCancel: function() {
this.props.onCancel();
},
render: function() {
var all = 'JRWPASDO';
var names = {
'J': 'Join (J)',
'R': 'Read (R)',
'W': 'Write (W)',
'P': 'Get notified (P)',
'A': 'Approve (A)',
'S': 'Share (S)',
'D': 'Delete (D)',
'O': 'Owner (O)'
};
var skip = this.props.skip || "";
var mode = this.state.mode || "";
var items = [];
for (var i=0; i<all.length; i++) {
var c = all.charAt(i);
if (skip.indexOf(c) >= 0) {
continue;
}
items.push(
<li key={c}><CheckBox name={c}
checked={(mode.indexOf(c) > -1)} onChange={this.handleChange}/> {names[c]}</li>
);
}
return (
<div className="panel-form-column">
<label className="small">Permissions</label>
<ul className="permission-editor">
{items}
</ul>
<br />
<div className="dialog-buttons">
<button className="blue" onClick={this.handleSubmit}>Ok</button>
<button className="white" onClick={this.handleCancel}>Cancel</button>
</div>
</div>
);
}
});
/* END PermissionsEditor */
/* BEGIN ChipInput: group membership widget */ /* BEGIN ChipInput: group membership widget */
var ChipInput = React.createClass({ var ChipInput = React.createClass({
getInitialState: function() { getInitialState: function() {
@@ -514,6 +612,7 @@
this.setState({errorCleared: false}); this.setState({errorCleared: false});
this.props.onLogin(this.state.login.trim(), this.state.password.trim()); this.props.onLogin(this.state.login.trim(), this.state.password.trim());
}, },
render: function() { render: function() {
var submitClasses = "blue"; var submitClasses = "blue";
if (this.props.disabled) { if (this.props.disabled) {
@@ -1514,7 +1613,8 @@
auth: null, auth: null,
anon: null, anon: null,
contactList: [], contactList: [],
showMemberPanel: this.props.showMemberPanel showMemberPanel: this.props.showMemberPanel,
showPermissionEditorFor: undefined
}; };
}, },
@@ -1542,6 +1642,7 @@
}, },
resetSubs: function(topic) { resetSubs: function(topic) {
console.log("Reset subs");
var contacts = []; var contacts = [];
topic.subscribers(function(sub){ topic.subscribers(function(sub){
contacts.push(sub); contacts.push(sub);
@@ -1552,6 +1653,8 @@
}, },
resetDesc: function(topic) { resetDesc: function(topic) {
console.log("Reset desc");
var defacs = topic.getDefaultAccess() || {}; var defacs = topic.getDefaultAccess() || {};
var acs = topic.getAccessMode(); var acs = topic.getAccessMode();
this.setState({ this.setState({
@@ -1589,7 +1692,7 @@
handleFullNameUpdate: function(fn) { handleFullNameUpdate: function(fn) {
if (this.state.fullName !== fn) { if (this.state.fullName !== fn) {
this.setState({fullName: fn}); this.setState({fullName: fn});
this.props.onSubmit(this.props.topic, this.props.onTopicDescUpdate(this.props.topic,
vcard(fn, this.state.avatar), vcard(fn, this.state.avatar),
null); null);
} }
@@ -1598,7 +1701,7 @@
handlePrivateUpdate: function(priv) { handlePrivateUpdate: function(priv) {
if (this.state.priv !== priv) { if (this.state.priv !== priv) {
this.setState({private: priv}); this.setState({private: priv});
this.props.onSubmit(this.props.topic, this.props.onTopicDescUpdate(this.props.topic,
null, null,
priv); priv);
} }
@@ -1606,17 +1709,43 @@
handleImageChanged: function(img) { handleImageChanged: function(img) {
this.setState({avatar: img}); this.setState({avatar: img});
this.props.onSubmit(this.props.topic, this.props.onTopicDescUpdate(this.props.topic,
vcard(this.state.fullName, img), vcard(this.state.fullName, img),
null); null);
}, },
handleMuted: function(e) { handleMuted: function(ignored, checked) {
var checked = e.target.checked;
this.setState({muted: checked}); this.setState({muted: checked});
this.props.onChangePermissions(this.props.topic, checked ? "-P" : "+P"); this.props.onChangePermissions(this.props.topic, checked ? "-P" : "+P");
}, },
handlePermissionsChanged: function(perm) {
if (this.state.showPermissionEditorFor === 'auth') {
this.props.onTopicDescUpdate(this.props.topic, null, null, {auth: perm});
} else if (this.state.showPermissionEditorFor === 'anon') {
this.props.onTopicDescUpdate(this.props.topic, null, null, {anon: perm});
} else if (this.state.showPermissionEditorFor === 'mode') {
console.log("Change permissions to " + perm);
this.props.onChangePermissions(this.props.topic, perm);
}
this.setState({showPermissionEditorFor: undefined});
},
handleLaunchPermissionsEditor: function(which) {
this.setState({
showPermissionEditorFor: which,
editedPermissions: which === 'mode' ?
this.state.access :
which === 'auth' ? this.state.auth : this.state.anon,
editedPermissionsSkipped: "O"
});
},
handleHidePermissionsEditor: function() {
this.setState({showPermissionEditorFor: undefined});
},
handleShowAddMembers: function() { handleShowAddMembers: function() {
// this.props.onAddMember(this.props.topic); // this.props.onAddMember(this.props.topic);
this.setState({showMemberPanel: true}); this.setState({showMemberPanel: true});
@@ -1661,6 +1790,14 @@
onCancel={this.handleHideAddMembers} onCancel={this.handleHideAddMembers}
onSubmit={this.handleMemberUpdateRequest} /> onSubmit={this.handleMemberUpdateRequest} />
: :
this.state.showPermissionEditorFor ?
<PermissionsEditor
mode={this.state.editedPermissions}
skip={this.state.editedPermissionsSkipped}
onSubmit={this.handlePermissionsChanged}
onCancel={this.handleHidePermissionsEditor}
/>
:
<div id="info-view-content" className="scrollable-panel"> <div id="info-view-content" className="scrollable-panel">
<div className="panel-form-row"> <div className="panel-form-row">
<div className="panel-form-column"> <div className="panel-form-column">
@@ -1691,19 +1828,28 @@
</div> </div>
<div className="panel-form-row"> <div className="panel-form-row">
<label>Permissions:</label> <label>Permissions:</label>
<tt>{this.state.access}</tt> <tt className="clickable"
onClick={this.handleLaunchPermissionsEditor.bind(this, 'mode')}>
{this.state.access}
</tt>
</div> </div>
<div className="panel-form-row"> <div className="panel-form-row">
<label htmlFor="info-muted">Muted:</label> <label>Muted:</label>
<input id="info-muted" type="checkbox" checked={this.state.muted} <CheckBox name="P" checked={this.state.muted}
onChange={this.handleMuted} /> onChange={this.handleMuted} />
</div> </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>
<div className="quoted"> <div className="quoted">
<div>Auth: <tt>{this.state.auth}</tt></div> <div>Auth: <tt className="clickable"
<div>Anon: <tt>{this.state.anon}</tt></div> onClick={this.handleLaunchPermissionsEditor.bind(this, 'auth')}>
{this.state.auth}
</tt></div>
<div>Anon: <tt className="clickable"
onClick={this.handleLaunchPermissionsEditor.bind(this, 'anon')}>
{this.state.anon}
</tt></div>
</div> </div>
</div> </div>
: :
@@ -2441,10 +2587,20 @@
// FIXME(gene): launch group member manager // FIXME(gene): launch group member manager
//onAddMember}, //onAddMember},
handleTopicUpdateRequest: function(topicName, pub, priv) { handleTopicUpdateRequest: function(topicName, pub, priv, permissions) {
var topic = Tinode.getTopic(topicName); var topic = Tinode.getTopic(topicName);
if (topic) { if (topic) {
topic.setMeta({desc: {public: pub, private: priv}}); var params = {};
if (pub) {
params.public = pub;
}
if (priv) {
params.private = priv;
}
if (permissions) {
params.defacs = permissions;
}
topic.setMeta({desc: params});
} }
}, },
@@ -2650,7 +2806,7 @@
foundContacts={this.state.foundContacts} foundContacts={this.state.foundContacts}
myUserId={this.state.myUserId} myUserId={this.state.myUserId}
showMemberPanel={this.state.infoPanelShowMembers} showMemberPanel={this.state.infoPanelShowMembers}
onSubmit={this.handleTopicUpdateRequest} onTopicDescUpdate={this.handleTopicUpdateRequest}
onCancel={this.handleHideInfoView} onCancel={this.handleHideInfoView}
onChangePermissions={this.handleChangePermissions} onChangePermissions={this.handleChangePermissions}
onMemberUpdateRequest={this.handleMemberUpdateRequest} onMemberUpdateRequest={this.handleMemberUpdateRequest}