diff --git a/src/webapp.js b/src/webapp.js index 73788629..4ebad239 100644 --- a/src/webapp.js +++ b/src/webapp.js @@ -427,7 +427,7 @@ function vcard(fn, imageDataUrl) { return card; } -// Deep-shallow compare two arrays. +// Deep-shallow compare two arrays: shallow compare each element. function arrayEqual(a, b) { // Compare lengths first. if (a.length != b.length) { @@ -446,134 +446,75 @@ function arrayEqual(a, b) { /* BEGIN Context Menu: popup/dropdown menu */ -// Function is used by context menu to set permissions. -function topicPermissionSetter(mode, params, errorHandler) { - var topic = Tinode.getTopic(params.topicName); - if (!topic) { - console.log("Topic not found", params.topicName); - return; - } - - var am, user; - if (params.user) { - user = topic.subscriber(params.user); - if (!user) { - console.log("Subscriber not found", params.topicName + "[" + params.user + "]"); - return; - } - am = user.acs.updateGiven(mode).getGiven(); - } else { - am = topic.getAccessMode().updateWant(mode).getWant(); - } - - var instance = this; - topic.setMeta({sub: {user: params.user, mode: am}}).catch(function(err) { - if (errorHandler) { - errorHandler(err.message, "err"); - } - }); -} - -function deleteMessages(all, hard, params, errorHandler) { - var topic = Tinode.getTopic(params.topicName); - if (!topic) { - console.log("Topic not found: ", params.topicName); - return; - } - // We don't know if the message is still pending (e.g. attachment is being uploaded), - // so try cancelling first. No harm if we can't cancel. - if (topic.cancelSend(params.seq)) { - return new Promise.resolve(); - } - // Can't cancel. Delete instead. - var promise = all ? - topic.delMessagesAll(hard) : - topic.delMessagesList([params.seq], hard); - promise.catch((err) => { - if (errorHandler) { - errorHandler(err.message, "err"); - } - }); -} - -// Context menu items. -var ContextMenuItems = { - "topic_info": {title: "Info", handler: null}, - - "messages_clear": {title: "Clear messages", handler: function(params, errorHandler) { - deleteMessages(true, false, params, errorHandler); - }}, - "messages_clear_hard": {title: "Clear for All", handler: function(params, errorHandler) { - deleteMessages(true, true, params, errorHandler); - }}, - "message_delete": {title: "Delete", handler: function(params, errorHandler) { - deleteMessages(false, false, params, errorHandler); - }}, - "message_delete_hard": {title: "Delete for All", handler: function(params, errorHandler) { - deleteMessages(false, true, params, errorHandler); - }}, - "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(params, errorHandler) { - var topic = Tinode.getTopic(params.topicName); - if (!topic) { - console.log("Topic not found: ", params.topicName); - return; - } - topic.delTopic().catch(function(err) { - if (errorHandler) { - errorHandler(err.message, "err"); - } - }); - }}, - - "permissions": {title: "Edit permissions", handler: null}, - "member_delete": {title: "Remove", handler: function(params, errorHandler) { - var topic = Tinode.getTopic(params.topicName); - if (!topic || !params.user) { - console.log("Topic or user not found: '" + params.topicName + "', '" + params.user + "'"); - return; - } - topic.delSubscription(params.user).catch(function(err) { - if (errorHandler) { - errorHandler(err.message, "err"); - } - }); - }}, - "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")}, -}; - class ContextMenu extends React.Component { constructor(props) { super(props); - this.toggle = this.toggle.bind(this); this.handlePageClick = this.handlePageClick.bind(this); this.handleEscapeKey = this.handleEscapeKey.bind(this); this.handleClick = this.handleClick.bind(this); + + // Preconfigured menu items. + this.MenuItems = { + "topic_info": {title: "Info", handler: null}, + + "messages_clear": {title: "Clear messages", handler: (params, errorHandler) => { + this.deleteMessages(true, false, params, errorHandler); + }}, + "messages_clear_hard": {title: "Clear for All", handler: (params, errorHandler) => { + this.deleteMessages(true, true, params, errorHandler); + }}, + "message_delete": {title: "Delete", handler: (params, errorHandler) => { + this.deleteMessages(false, false, params, errorHandler); + }}, + "message_delete_hard": {title: "Delete for All", handler: (params, errorHandler) => { + this.deleteMessages(false, true, params, errorHandler); + }}, + "topic_unmute": {title: "Unmute", handler: this.topicPermissionSetter.bind(this, "+P")}, + "topic_mute": {title: "Mute", handler: this.topicPermissionSetter.bind(this, "-P")}, + "topic_unblock": {title: "Unblock", handler: this.topicPermissionSetter.bind(this, "+J")}, + "topic_block": {title: "Block", handler: this.topicPermissionSetter.bind(this, "-J")}, + "topic_delete": {title: "Delete", handler: (params, errorHandler) => { + var topic = Tinode.getTopic(params.topicName); + if (!topic) { + console.log("Topic not found: ", params.topicName); + return; + } + topic.delTopic().catch(function(err) { + if (errorHandler) { + errorHandler(err.message, "err"); + } + }); + }}, + + "permissions": {title: "Edit permissions", handler: null}, + "member_delete": {title: "Remove", handler: (params, errorHandler) => { + var topic = Tinode.getTopic(params.topicName); + if (!topic || !params.user) { + console.log("Topic or user not found: '" + params.topicName + "', '" + params.user + "'"); + return; + } + topic.delSubscription(params.user).catch((err) => { + if (errorHandler) { + errorHandler(err.message, "err"); + } + }); + }}, + "member_mute": {title: "Mute", handler: this.topicPermissionSetter.bind(this, "-P")}, + "member_unmute": {title: "Unmute", handler: this.topicPermissionSetter.bind(this, "+P")}, + "member_block": {title: "Block", handler: this.topicPermissionSetter.bind(this, "-J")}, + "member_unblock": {title: "Unblock", handler: this.topicPermissionSetter.bind(this, "+J")}, + }; + } + + componentDidMount() { + document.addEventListener('mousedown', this.handlePageClick, false); + document.addEventListener('keyup', this.handleEscapeKey, false); } componentWillUnmount() { - this.toggle(false); - } - - UNSAFE_componentWillReceiveProps(nextProps) { - this.toggle(nextProps.visible); - } - - toggle(visible) { - if (visible) { - document.addEventListener('mousedown', this.handlePageClick, false); - document.addEventListener('keyup', this.handleEscapeKey, false); - } else { - document.removeEventListener('mousedown', this.handlePageClick, false); - document.removeEventListener('keyup', this.handleEscapeKey, false); - } + document.removeEventListener('mousedown', this.handlePageClick, false); + document.removeEventListener('keyup', this.handleEscapeKey, false); } handlePageClick(e) { @@ -599,17 +540,67 @@ class ContextMenu extends React.Component { item.handler(this.props.params, this.props.onError); } - render() { - if (!this.props.visible) { - return null; + // Menu Actions + + deleteMessages(all, hard, params, errorHandler) { + var topic = this.props.tinode.getTopic(params.topicName); + if (!topic) { + console.log("Topic not found: ", params.topicName); + return; + } + // We don't know if the message is still pending (e.g. attachment is being uploaded), + // so try cancelling first. No harm if we can't cancel. + if (topic.cancelSend(params.seq)) { + return new Promise.resolve(); + } + // Can't cancel. Delete instead. + var promise = all ? + topic.delMessagesAll(hard) : + topic.delMessagesList([params.seq], hard); + promise.catch((err) => { + if (errorHandler) { + errorHandler(err.message, "err"); + } + }); + } + + // Function is used by context menu to set permissions. + topicPermissionSetter(mode, params, errorHandler) { + var topic = this.props.tinode.getTopic(params.topicName); + if (!topic) { + console.log("Topic not found", params.topicName); + return; } + var am, user; + if (params.user) { + user = topic.subscriber(params.user); + if (!user) { + console.log("Subscriber not found", params.topicName + "[" + params.user + "]"); + return; + } + am = user.acs.updateGiven(mode).getGiven(); + } else { + am = topic.getAccessMode().updateWant(mode).getWant(); + } + + topic.setMeta({sub: {user: params.user, mode: am}}).catch((err) => { + if (errorHandler) { + errorHandler(err.message, "err"); + } + }); + } + + render() { let count = 0; let menu = []; this.props.items.map((item) => { + if (typeof item == 'string') { + item = this.MenuItems[item]; + } if (item && item.title) { menu.push( - item.title === "-" ? + item.title == "-" ?
: