From 5868915f2cb0a92108cfb743947afbffba08f6c6 Mon Sep 17 00:00:00 2001 From: or-else Date: Sat, 15 Jul 2017 12:35:53 +0300 Subject: [PATCH] permissions editing works now --- README.md | 4 +- css/base.css | 26 ++++++++ index.html | 184 +++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 198 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ef2df14e..f0f24b08 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ 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/) -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. -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 `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 `@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 `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 `@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) diff --git a/css/base.css b/css/base.css index a8229b6a..275f6f8a 100644 --- a/css/base.css +++ b/css/base.css @@ -393,6 +393,28 @@ ul.menu > li.separator:hover { } /* 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 */ /* Sidepanel styles */ @@ -417,6 +439,10 @@ ul.menu > li.separator:hover { justify-content: flex-end; } +.dialog-buttons button { + margin: 0 0.5rem; +} + .avatar-box { position: relative; flex-grow: 0; diff --git a/index.html b/index.html index a78beea0..e6e35c3b 100644 --- a/index.html +++ b/index.html @@ -342,6 +342,104 @@ } /* 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 ? + check_box : + check_box_outline_blank + ); + } + } + /* END CheckBox */ + + /* BEGIN PermissionsEditor: Component for editing permissions */ + // + 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= 0) { + continue; + } + items.push( +
  • -1)} onChange={this.handleChange}/> {names[c]}
  • + ); + } + + return ( +
    + +
      + {items} +
    +
    +
    + + +
    +
    + ); + } + }); + /* END PermissionsEditor */ + /* BEGIN ChipInput: group membership widget */ var ChipInput = React.createClass({ getInitialState: function() { @@ -514,6 +612,7 @@ this.setState({errorCleared: false}); this.props.onLogin(this.state.login.trim(), this.state.password.trim()); }, + render: function() { var submitClasses = "blue"; if (this.props.disabled) { @@ -1514,7 +1613,8 @@ auth: null, anon: null, contactList: [], - showMemberPanel: this.props.showMemberPanel + showMemberPanel: this.props.showMemberPanel, + showPermissionEditorFor: undefined }; }, @@ -1542,6 +1642,7 @@ }, resetSubs: function(topic) { + console.log("Reset subs"); var contacts = []; topic.subscribers(function(sub){ contacts.push(sub); @@ -1552,6 +1653,8 @@ }, resetDesc: function(topic) { + console.log("Reset desc"); + var defacs = topic.getDefaultAccess() || {}; var acs = topic.getAccessMode(); this.setState({ @@ -1589,7 +1692,7 @@ handleFullNameUpdate: function(fn) { if (this.state.fullName !== fn) { this.setState({fullName: fn}); - this.props.onSubmit(this.props.topic, + this.props.onTopicDescUpdate(this.props.topic, vcard(fn, this.state.avatar), null); } @@ -1598,7 +1701,7 @@ handlePrivateUpdate: function(priv) { if (this.state.priv !== priv) { this.setState({private: priv}); - this.props.onSubmit(this.props.topic, + this.props.onTopicDescUpdate(this.props.topic, null, priv); } @@ -1606,17 +1709,43 @@ handleImageChanged: function(img) { this.setState({avatar: img}); - this.props.onSubmit(this.props.topic, + this.props.onTopicDescUpdate(this.props.topic, vcard(this.state.fullName, img), null); }, - handleMuted: function(e) { - var checked = e.target.checked; + handleMuted: function(ignored, checked) { this.setState({muted: checked}); 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() { // this.props.onAddMember(this.props.topic); this.setState({showMemberPanel: true}); @@ -1661,6 +1790,14 @@ onCancel={this.handleHideAddMembers} onSubmit={this.handleMemberUpdateRequest} /> : + this.state.showPermissionEditorFor ? + + :
    @@ -1691,19 +1828,28 @@
    - {this.state.access} + + {this.state.access} +
    - - Muted: +
    {this.state.auth || this.state.anon ?
    -
    Auth: {this.state.auth}
    -
    Anon: {this.state.anon}
    +
    Auth: + {this.state.auth} +
    +
    Anon: + {this.state.anon} +
    : @@ -2441,10 +2587,20 @@ // FIXME(gene): launch group member manager //onAddMember}, - handleTopicUpdateRequest: function(topicName, pub, priv) { + handleTopicUpdateRequest: function(topicName, pub, priv, permissions) { var topic = Tinode.getTopic(topicName); 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} myUserId={this.state.myUserId} showMemberPanel={this.state.infoPanelShowMembers} - onSubmit={this.handleTopicUpdateRequest} + onTopicDescUpdate={this.handleTopicUpdateRequest} onCancel={this.handleHideInfoView} onChangePermissions={this.handleChangePermissions} onMemberUpdateRequest={this.handleMemberUpdateRequest}