From f69f1f601602ac2ad702595c18ed9f17d8f6dd2c Mon Sep 17 00:00:00 2001 From: Tatsuya Niwa Date: Tue, 26 Jan 2016 00:35:59 +0900 Subject: [PATCH] * refactoring * bugfix: Post to active channel/DM with mention when window isn't focused, I expect count badge appear but unread badge appear. --- src/browser/index.jsx | 83 +++++++++----------- src/browser/webview/mattermost.js | 126 ++++++++++++++---------------- 2 files changed, 98 insertions(+), 111 deletions(-) diff --git a/src/browser/index.jsx b/src/browser/index.jsx index 481ccdd7..5528d797 100644 --- a/src/browser/index.jsx +++ b/src/browser/index.jsx @@ -50,22 +50,31 @@ var MainPage = React.createClass({ }); this.handleOnTeamFocused(key); }, - handleUnreadCountChange: function(index, unreadCount, mentionCount) { + handleUnreadCountChange: function(index, unreadCount, mentionCount, isUnread, isMentioned) { var unreadCounts = this.state.unreadCounts; var mentionCounts = this.state.mentionCounts; + var unreadAtActive = this.state.unreadAtActive; + var mentionAtActive = this.state.mentionAtActive; unreadCounts[index] = unreadCount; mentionCounts[index] = mentionCount; + // Never turn on the unreadAtActive flag at current focused tab. + if (this.state.key !== index || !remote.getCurrentWindow().isFocused()) { + unreadAtActive[index] = unreadAtActive[index] || isUnread; + mentionAtActive[index] = mentionAtActive[index] || isMentioned; + } this.setState({ unreadCounts: unreadCounts, - mentionCounts: mentionCounts + mentionCounts: mentionCounts, + unreadAtActive: unreadAtActive, + mentionAtActive: mentionAtActive }); this.handleUnreadCountTotalChange(); }, - handleUnreadAtActiveChange: function(index, isUnread, isMentioned) { + markReadAtActive: function(index) { var unreadAtActive = this.state.unreadAtActive; var mentionAtActive = this.state.mentionAtActive; - unreadAtActive[index] = isUnread; - mentionAtActive[index] = isMentioned; + unreadAtActive[index] = false; + mentionAtActive[index] = false; this.setState({ unreadAtActive: unreadAtActive, mentionAtActive: mentionAtActive @@ -93,16 +102,9 @@ var MainPage = React.createClass({ this.props.onUnreadCountChange(allUnreadCount, allMentionCount); } }, - handleNotify: function(index, isMentioned) { - // Never turn on the unreadAtActive flag at current focused tab. - if (this.state.key === index && remote.getCurrentWindow().isFocused()) { - return; - } - this.handleUnreadAtActiveChange(index, true, isMentioned); - }, handleOnTeamFocused: function(index) { // Turn off the flag to indicate whether unread message of active channel contains at current tab. - this.handleUnreadAtActiveChange(index, false, false); + this.markReadAtActive(index); }, visibleStyle: function(visible) { @@ -123,24 +125,21 @@ var MainPage = React.createClass({ if (this.props.teams.length > 1) { tabs_row = ( - + ); } var views = this.props.teams.map(function(team, index) { - var handleUnreadCountChange = function(unreadCount, mentionCount) { - thisObj.handleUnreadCountChange(index, unreadCount, mentionCount); - }; - var handleNotify = function(isMentioned) { - thisObj.handleNotify(index, isMentioned); + var handleUnreadCountChange = function(unreadCount, mentionCount, isUnread, isMentioned) { + thisObj.handleUnreadCountChange(index, unreadCount, mentionCount, isUnread, isMentioned); }; var handleNotificationClick = function() { thisObj.handleSelect(index); } - return () + return () }); var views_row = ( { views } @@ -158,7 +157,6 @@ var TabBar = React.createClass({ render: function() { var thisObj = this; var tabs = this.props.teams.map(function(team, index) { - var badge; var unreadCount = 0; if (thisObj.props.unreadCounts[index] > 0) { unreadCount = thisObj.props.unreadCounts[index]; @@ -166,9 +164,19 @@ var TabBar = React.createClass({ if (thisObj.props.unreadAtActive[index]) { unreadCount += 1; } - if (thisObj.props.mentionCounts[index] != 0) { + + var mentionCount = 0; + if (thisObj.props.mentionCounts[index] > 0) { + mentionCount = thisObj.props.mentionCounts[index]; + } + if (thisObj.props.mentionAtActive[index] === true) { + mentionCount += 1; + } + + var badge; + if (mentionCount != 0) { badge = ( - { thisObj.props.mentionCounts[index] } + { mentionCount } ); } else if (unreadCount > 0) { badge = ( @@ -192,23 +200,11 @@ var TabBar = React.createClass({ var MattermostView = React.createClass({ getInitialState: function() { return { - unreadCount: 0, - mentionCount: 0 }; }, - handleUnreadCountChange: function(unreadCount, mentionCount) { - this.setState({ - unreadCount: unreadCount, - mentionCount: mentionCount - }); + handleUnreadCountChange: function(unreadCount, mentionCount, isUnread, isMentioned) { if (this.props.onUnreadCountChange) { - this.props.onUnreadCountChange(unreadCount, mentionCount); - } - }, - - handleNotify: function(isMentioned) { - if (this.props.onNotify) { - this.props.onNotify(isMentioned); + this.props.onUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned); } }, @@ -260,7 +256,10 @@ var MattermostView = React.createClass({ case 'onUnreadCountChange': var unreadCount = event.args[0]; var mentionCount = event.args[1]; - thisObj.handleUnreadCountChange(unreadCount, mentionCount); + // isUnread and isMentioned is pulse flag. + var isUnread = event.args[2]; + var isMentioned = event.args[3]; + thisObj.handleUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned); break; case 'onNotificationClick': thisObj.props.onNotificationClick(); @@ -268,10 +267,6 @@ var MattermostView = React.createClass({ case 'console': console.log(event.args[0]); break; - case 'onActiveChannelNotify': - var isMentioned = event.args[0]; - thisObj.handleNotify(isMentioned); - break; } }); diff --git a/src/browser/webview/mattermost.js b/src/browser/webview/mattermost.js index ade81305..d5a5a7d6 100644 --- a/src/browser/webview/mattermost.js +++ b/src/browser/webview/mattermost.js @@ -19,18 +19,71 @@ var unreadCountTimer = setInterval(function() { var elem = document.getElementsByClassName('badge') var mentionCount = 0; for (var i = 0; i < elem.length; i++) { - if (elem[i].offsetHeight != 0) { + if (isElementVisible(elem[i])) { mentionCount++; } } - if (this.unreadCount != unreadCount || this.mentionCount != mentionCount) { - ipc.sendToHost('onUnreadCountChange', unreadCount, mentionCount); + var postAttrName = 'data-reactid'; + var lastPostElem = document.querySelector('div[' + postAttrName + '="' + lastCheckedPost.reactId + '"]'); + var isUnread = false; + var isMentioned = false; + if (lastPostElem === null || !isElementVisible(lastPostElem)) { + // When load channel or change channel, lastCheckedPost.reactId is invalid. + // So we get latest post and save lastCheckedPost. + + // find active post-list. + var postLists = document.querySelectorAll('div.post-list__content'); + var post; + for (var i = 0; i < postLists.length; i++) { + if (isElementVisible(postLists[i])) { + post = postLists[i].children[0]; + } + } + + // find latest post and save. + while (post = post.nextSibling) { + if (post.nextSibling === null) { + if (post.getAttribute(postAttrName) !== null) { + lastCheckedPost.reactId = post.getAttribute(postAttrName); + } + } + } + } + else if (lastPostElem !== null) { + var newPostElem = lastPostElem; + while (newPostElem = newPostElem.nextSibling) { + lastCheckedPost.reactId = newPostElem.getAttribute(postAttrName); + isUnread = true; + var activeChannel = document.querySelector('.active .sidebar-channel'); + var closeButton = activeChannel.getElementsByClassName('btn-close'); + if (closeButton.length === 1 && closeButton[0].getAttribute('aria-describedby') === 'remove-dm-tooltip') { + // If active channel is DM, all posts is treated as menion. + isMentioned = true; + break; + } + else { + // If active channel is public/private channel, only mentioned post is treated as mention. + var highlight = newPostElem.getElementsByClassName('mention-highlight'); + if (highlight.length != 0 && isElementVisible(highlight[0])) { + isMentioned = true; + break; + } + } + } + } + + if (this.unreadCount != unreadCount || this.mentionCount != mentionCount || isUnread || isMentioned) { + ipc.sendToHost('onUnreadCountChange', unreadCount, mentionCount, isUnread, isMentioned); } this.unreadCount = unreadCount; this.mentionCount = mentionCount; }, 1000); +function isElementVisible(elem) { + return elem.offsetHeight !== 0; +} + // On Windows 8.1 and Windows 8, a shortcut with a Application User Model ID must be installed to the Start screen. // In current version, use tray balloon for notification function isLowerThanOrEqualWindows8_1() { @@ -48,12 +101,6 @@ function overrideNotificationWithBalloon() { title: title, options: options }); - - // Send notification event at active channel. - var activeChannel = document.querySelector('.active .sidebar-channel').text; - if (activeChannel === title) { - ipc.sendToHost('onActiveChannelNotify'); - } }; Notification.requestPermission = function(callback) { callback('granted'); @@ -61,69 +108,14 @@ function overrideNotificationWithBalloon() { Notification.prototype.close = function() {}; }; -var lastUnread = {}; +var lastCheckedPost = { + reactId: null +}; // Show window even if it is hidden/minimized when notification is clicked. function overrideNotification() { Notification = function(title, options) { this.notification = new NativeNotification(title, options); - - // Send notification event at active channel. - var activeChannel = document.querySelector('.active .sidebar-channel').text; - console.log(activeChannel); - console.log(title); - - // mentionCount for active channel - var newSeparators = document.getElementsByClassName('new-separator'); - var post; - var isMentioned = false; - // Skip until real new-separator appear. - for (var i = 0; i < newSeparators.length; i++) { - if (newSeparators[i].offsetParent !== null) { - post = newSeparators[i]; - } - } - - // If active channel is DM, all posts is treated as menion. - if (activeChannel === title + "×") { - isMentioned = true; - } - else { - // If active channel is CHANNEL, only .mention-highlight post is treated as mention. - if (post != null) { - // Skip posts until last unread. - if (activeChannel === title && lastUnread.channel === title && lastUnread.post !== null) { - var firstPost = post; - while (post = post.nextSibling) { - if (lastUnread.post === post.getAttribute('data-reactid')) { - break; - } - } - // Because last unread post not found, set first post. - if (post === null) { - post = firstPost; - } - } - - while (post = post.nextSibling) { - var highlight = post.getElementsByClassName('mention-highlight'); - if (highlight.length != 0 && highlight[0].offsetHeight != null) { - isMentioned = true; - } - - // Remember last unread post. - if (post.nextSibling === null) { - lastUnread.post = post.getAttribute('data-reactid'); - lastUnread.channel = title; - } - } - } - } - - // Note: DM title is "{username}×". CHANNEL title is "{channel_title}". - if (activeChannel === title || activeChannel === title + "×") { - ipc.sendToHost('onActiveChannelNotify', isMentioned); - } }; Notification.requestPermission = function(callback) { callback('granted');