diff --git a/index.html b/index.html index 24ef097f..7356b685 100644 --- a/index.html +++ b/index.html @@ -127,10 +127,12 @@ return undefined; } - /* BEGIN Popup/dropdown menu */ + /* BEGIN Context Menu: popup/dropdown menu */ // Function is used by context menu to set permissions. function topicPermissionSetter(mode, topicName, uid) { + console.log("topicPermissionSetter called with arguments:"); + console.log(arguments); var topic = Tinode.getTopic(topicName); if (!topic) { console.log("Topic not found: " + topicName); @@ -167,10 +169,10 @@ }}, "message_delete": {title: "Delete", handler: null}, - "topic_unmute": {title: "Unmute", handler: topicPermissionSetter.bind("+P")}, - "topic_mute": {title: "Mute", handler: topicPermissionSetter.bind("-P")}, - "topic_unblock": {title: "Unblock", handler: topicPermissionSetter.bind("+J")}, - "topic_block": {title: "Block", handler: topicPermissionSetter.bind("-J")}, + "topic_unmute": {title: "Unmute", handler: topicPermissionSetter.bind(this, "+P")}, + "topic_mute": {title: "Mute", handler: topicPermissionSetter.bind(this, "-P")}, + "topic_unblock": {title: "Unblock", handler: topicPermissionSetter.bind(this, "+J")}, + "topic_block": {title: "Block", handler: topicPermissionSetter.bind(this, "-J")}, "topic_delete": {title: "Delete", handler: function(topicName) { var topic = Tinode.getTopic(topicName); if (!topic) { @@ -180,12 +182,19 @@ topic.delTopic(); }}, - "permissions": {title: "Permissions", handler: null}, - "member_delete": {title: "Delete", handler: null}, - "member_mute": {title: "Mute", handler: topicPermissionSetter.bind("-P")}, - "member_unmute": {title: "Unmute", handler: topicPermissionSetter.bind("+P")}, - "member_block": {title: "Block", handler: topicPermissionSetter.bind("-J")}, - "member_unblock": {title: "Unblock", handler: topicPermissionSetter.bind("+J")}, + "permissions": {title: "Edit permissions", handler: null}, + "member_delete": {title: "Remove", handler: function(topicName, user) { + var topic = Tinode.getTopic(topicName); + if (!topic || !user) { + console.log("Topic or user not found: '" + topicName + "', '" + user + "'"); + return; + } + topic.delSubscription(user); + }}, + "member_mute": {title: "Mute", handler: topicPermissionSetter.bind(this, "-P")}, + "member_unmute": {title: "Unmute", handler: topicPermissionSetter.bind(this, "+P")}, + "member_block": {title: "Block", handler: topicPermissionSetter.bind(this, "-J")}, + "member_unblock": {title: "Unblock", handler: topicPermissionSetter.bind(this, "+J")}, }; var ContextMenu = React.createClass({ @@ -223,7 +232,6 @@ this.props.hide(); var item = this.props.items[e.currentTarget.dataset.id]; item.handler(this.props.topic, this.props.user); - //this.props.onAction(this.props.topic, this.props.items[e.currentTarget.dataset.id]); }, render: function() { @@ -517,6 +525,10 @@ return (
+ {this.props.userTitle ? + : null} {this.props.compare ? @@ -1178,13 +1190,15 @@ handleClick: function(e) { e.preventDefault(); e.stopPropagation(); - this.props.onSelected(this.props.topic, this.props.index); + if (this.props.onSelected) { + this.props.onSelected(this.props.item, this.props.index); + } }, handleContextClick: function(e) { e.preventDefault(); e.stopPropagation(); - this.props.showContextMenu(this.props.topic, e.pageY, e.pageX); + this.props.showContextMenu(this.props.item, e.pageY, e.pageX); }, render: function() { @@ -1201,7 +1215,7 @@ onClick={this.handleClick}>
+ avatar={avatar} title={this.props.title} topic={this.props.item} /> {this.props.showOnline ? : (this.props.showCheckmark && this.props.selected ? @@ -1210,7 +1224,8 @@
{title} - {this.props.unread > 0 ? : null}
+ {this.props.showUnread && (this.props.unread > 0) ? + : null}
{this.props.comment ?
{this.props.comment}
: null} {this.props.showContextMenu ? @@ -1320,6 +1335,7 @@ emptyListMessage={You have no chats
¯\_(ツ)_/¯
} topicSelected={this.props.topicSelected} showOnline={true} + showUnread={true} onTopicSelected={this.props.onTopicSelected} showContextMenu={this.props.showContextMenu} /> ); @@ -1367,9 +1383,10 @@ showCheckmark={showCheckmark} selected={selected} showOnline={this.props.showOnline} + showUnread={this.props.showUnread} onSelected={this.props.onTopicSelected} showContextMenu={this.props.showContextMenu} - topic={key} + item={key} index={contactNodes.length} key={key} /> ); @@ -1392,7 +1409,7 @@ }); /* END ContactList */ - /* BEGIN GroupMembers: controlc for managing a list of group members */ + /* BEGIN GroupMembers: control for managing a list of group members */ var GroupManager = React.createClass({ getInitialState: function() { return { @@ -1530,6 +1547,8 @@ filter={this.state.contactFilter} filterFunc={this.doContactFiltering} emptyListMessage={this.state.noContactsMessage} + showOnline={false} + showUnread={false} onTopicSelected={this.handleContactSelected} />
@@ -1597,6 +1616,7 @@ emptyListMessage="Use search to find contacts" topicSelected={this.state.selectedContact} showOnline={false} + showUnread={false} showContextMenu={false} onTopicSelected={this.handleContactSelected} />
} @@ -1703,13 +1723,15 @@ getInitialState: function() { return { owner: false, + admin: false, + sharer: false, + muted: false, address: null, groupTopic: undefined, fullName: undefined, avatar: null, private: null, selectedContact: null, - muted: undefined, access: null, modeGiven: null, modeWant: null, @@ -1850,13 +1872,19 @@ case 'given': this.props.onChangePermissions(this.props.topic, perm, this.props.topic); break; + case 'user': + this.props.onChangePermissions(this.props.topic, perm, this.state.userPermissionsEdited); + break; } this.setState({showPermissionEditorFor: undefined}); }, - handleLaunchPermissionsEditor: function(which) { - var toEdit, toCompare, toSkip, titleEdit, titleCompare; + handleLaunchPermissionsEditor: function(which, uid) { + console.log("handleLaunchPermissionsEditor called with arguments"); + console.log(arguments); + + var toEdit, toCompare, toSkip, titleEdit, titleCompare, userTitle, userAvatar switch (which) { case 'mode': toEdit = this.state.access; @@ -1883,17 +1911,40 @@ toEdit = this.state.anon; toSkip = 'O'; break; + case 'user': { + var topic = Tinode.getTopic(this.props.topic); + if (!topic) { + return; + } + var user = topic.subscriber(uid); + if (!user || !user.acs) { + return; + } + toEdit = user.acs.getGiven(); + toCompare = user.acs.getWant(); + toSkip = this.state.owner ? '' : 'O'; + titleEdit = 'Granted'; + titleCompare = 'Requested'; + if (user.public) { + userTitle = user.public.fn; + userAvatar = user.public.photo; + } + break; + } default: console.log("Unknown permission editing mode '" + which + "'"); break; } this.setState({ showPermissionEditorFor: which, + userPermissionsEdited: uid, + userPermissionsTitle: userTitle, + userPermissionsAvatar: userAvatar, editedPermissions: toEdit, immutablePermissions: toCompare, editedPermissionsTitle: titleEdit, immutablePermissionsTitle: titleCompare, - editedPermissionsSkipped: toSkip + editedPermissionsSkipped: toSkip, }); }, @@ -1925,8 +1976,28 @@ }, handleContextMenu: function(item, x, y) { + console.log("handleContextMenu called with agruments"); console.log(arguments); - this.props.showContextMenu(item, x, y); + + var instance = this; + var topic = Tinode.getTopic(this.props.topic); + if (!topic) { + return; + } + var user = topic.subscriber(item); + if (!user || !user.acs) { + return; + } + + var menuItems = [ + {title: "Edit permissions", handler: function() { + instance.handleLaunchPermissionsEditor("user", item); + }}, + ContextMenuItems["member_delete"], + user.acs.isMuted() ? ContextMenuItems["member_unmute"] : ContextMenuItems["member_mute"], + user.acs.isJoiner() ? ContextMenuItems["member_block"] : ContextMenuItems["member_unblock"] + ]; + this.props.showContextMenu(this.props.topic, x, y, item, menuItems); }, render: function() { @@ -1953,6 +2024,8 @@ skip={this.state.editedPermissionsSkipped} modeTitle={this.state.editedPermissionsTitle} compareTitle={this.state.immutablePermissionsTitle} + userTitle={this.state.userPermissionsTitle} + userAvatar={this.state.userPermissionsAvatar} skip={this.state.editedPermissionsSkipped} onSubmit={this.handlePermissionsChanged} onCancel={this.handleHidePermissionsEditor} @@ -2065,9 +2138,10 @@ emptyListMessage="No members" topicSelected={this.state.selectedContact} showOnline={false} + showUnread={false} noScroll={true} onTopicSelected={this.handleMemberSelected} - showContextMenu={this.handleContextMenu} + showContextMenu={this.state.admin ? this.handleContextMenu : false} /> : @@ -2851,7 +2925,22 @@ handleDialogCancel: function() { this.setState({dialogSelected: null}); }, - handleShowContextMenu: function(topicName, y, x) { + + handleShowContextMenu: function(topicName, y, x, uid, menuItems) { + console.log("handleShowContextMenu called with arguments"); + console.log(arguments); + + this.setState({ + contextMenuVisible: true, + contextMenuClickAt: {x: x, y: y}, + contextMenuTopic: topicName, + contextMenuUser: uid, + contextMenuItems: menuItems || this.defaultTopicContextMenu(topicName), + contextMenuBounds: ReactDOM.findDOMNode(this).getBoundingClientRect() + }); + }, + + defaultTopicContextMenu: function(topicName) { var muted = false, blocked = false, subscribed = false; var topic = Tinode.getTopic(topicName); if (topic) { @@ -2862,74 +2951,26 @@ blocked = acs && !acs.isJoiner(); } } - this.setState({ - contextMenuVisible: true, - contextMenuClickAt: {x: x, y: y}, - contextMenuTopic: topicName, - contextMenuItems: [ - subscribed ? {title: "Info", handler: this.handleShowInfoView} : null, - subscribed ? ContextMenuItems["messages_clear"] : null, - subscribed ? (muted ? ContextMenuItems["topic_unmute"] : ContextMenuItems["topic_mute"]) : null, - subscribed ? (blocked ? ContextMenuItems["topic_unblock"] : ContextMenuItems["topic_block"]) : null, - ContextMenuItems["topic_delete"] - ], - contextMenuBounds: ReactDOM.findDOMNode(this).getBoundingClientRect() - }); + + return [ + subscribed ? {title: "Info", handler: this.handleShowInfoView} : null, + subscribed ? ContextMenuItems["messages_clear"] : null, + subscribed ? (muted ? ContextMenuItems["topic_unmute"] : ContextMenuItems["topic_mute"]) : null, + subscribed ? (blocked ? ContextMenuItems["topic_unblock"] : ContextMenuItems["topic_block"]) : null, + ContextMenuItems["topic_delete"] + ]; }, + handleHideContextMenu: function() { this.setState({ contextMenuVisible: false, contextMenuClickAt: null, - contextMenuTopic: '', + contextMenuTopic: null, + contextMenuUser: null, contextMenuBounds: null }); }, - // Menu click. - handleContextMenuAction: function(topicName, action) { - if (!topicName || !action) { - return; - } - var topic = Tinode.getTopic(topicName); - if (!topic) { - return; - } - - var metaParam = function(want) { - var am = topic.getAccessMode(); - am.updateWant(want); - topic.setMeta({sub: {mode: am.getWant()}}); - } - - switch (action) { - case "Info": - this.handleShowInfoView(); - break; - case "Clear messages": - topic.delMessagesAll(false).then(function(){ - console.log("refresh message list"); - }); - break; - case "Mute": - metaParam('-P'); - break; - case "Unmute": - metaParam('+P'); - break; - case "Block": - metaParam('-J'); - break; - case "Unblock": - metaParam('+J'); - break; - case "Delete": - topic.delTopic(); - break; - default: - console.log("Unknown menu action '" + action + "'"); - break; - } - }, handleShowInfoView: function() { this.setState({showInfoPanel: true, infoPanelShowMembers: false}); }, @@ -2972,6 +3013,7 @@ clickAt={this.state.contextMenuClickAt} visible={this.state.contextMenuVisible} topic={this.state.contextMenuTopic} + user={this.state.contextMenuUser} items={this.state.contextMenuItems} hide={this.handleHideContextMenu} onAction={this.handleContextMenuAction} />