diff --git a/src/browser/index.jsx b/src/browser/index.jsx index 60653f18..726690fb 100644 --- a/src/browser/index.jsx +++ b/src/browser/index.jsx @@ -25,7 +25,8 @@ var MainPage = React.createClass({ key: 0, unreadCounts: new Array(this.props.teams.length), mentionCounts: new Array(this.props.teams.length), - unreadAtActive: new Array(this.props.teams.length) + unreadAtActive: new Array(this.props.teams.length), + mentionAtActiveCounts: new Array(this.props.teams.length) }; }, componentDidMount: function() { @@ -49,22 +50,36 @@ 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 mentionAtActiveCounts = this.state.mentionAtActiveCounts; 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; + if (isMentioned) { + mentionAtActiveCounts[index]++; + } + } this.setState({ unreadCounts: unreadCounts, - mentionCounts: mentionCounts + mentionCounts: mentionCounts, + unreadAtActive: unreadAtActive, + mentionAtActiveCounts: mentionAtActiveCounts }); this.handleUnreadCountTotalChange(); }, - handleUnreadAtActiveChange: function(index, state) { + markReadAtActive: function(index) { var unreadAtActive = this.state.unreadAtActive; - unreadAtActive[index] = state; + var mentionAtActiveCounts = this.state.mentionAtActiveCounts; + unreadAtActive[index] = false; + mentionAtActiveCounts[index] = 0; this.setState({ - unreadAtActive: unreadAtActive + unreadAtActive: unreadAtActive, + mentionAtActiveCounts: mentionAtActiveCounts }); this.handleUnreadCountTotalChange(); }, @@ -81,19 +96,15 @@ var MainPage = React.createClass({ var allMentionCount = this.state.mentionCounts.reduce(function(prev, curr) { return prev + curr; }, 0); + this.state.mentionAtActiveCounts.forEach(function(count) { + allMentionCount += count; + }); this.props.onUnreadCountChange(allUnreadCount, allMentionCount); } }, - handleNotify: function(index) { - // Never turn on the unreadAtActive flag at current focused tab. - if (this.state.key === index && remote.getCurrentWindow().isFocused()) { - return; - } - this.handleUnreadAtActiveChange(index, true); - }, handleOnTeamFocused: function(index) { // Turn off the flag to indicate whether unread message of active channel contains at current tab. - this.handleUnreadAtActiveChange(index, false); + this.markReadAtActive(index); }, visibleStyle: function(visible) { @@ -114,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() { - thisObj.handleNotify(index); + 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 } @@ -149,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]; @@ -157,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.mentionAtActiveCounts[index] > 0) { + mentionCount += thisObj.props.mentionAtActiveCounts[index]; + } + + var badge; + if (mentionCount != 0) { badge = ( - { thisObj.props.mentionCounts[index] } + { mentionCount } ); } else if (unreadCount > 0) { badge = ( @@ -183,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() { - if (this.props.onNotify) { - this.props.onNotify(); + this.props.onUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned); } }, @@ -251,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(); @@ -259,9 +267,6 @@ var MattermostView = React.createClass({ case 'console': console.log(event.args[0]); break; - case 'onActiveChannelNotify': - thisObj.handleNotify(); - break; } }); diff --git a/src/browser/webview/mattermost.js b/src/browser/webview/mattermost.js index 9dcb088b..eecbe3ea 100644 --- a/src/browser/webview/mattermost.js +++ b/src/browser/webview/mattermost.js @@ -15,41 +15,73 @@ var unreadCountTimer = setInterval(function() { // unreadCount in sidebar // Note: the active channel doesn't have '.unread-title'. var unreadCount = document.getElementsByClassName('unread-title').length; - // mentionCount in sidebar - var elem = document.getElementsByClassName('badge') + // mentionCount in mobile navbar-toggle + var mentionBadge = document.getElementsByClassName('badge-notify'); var mentionCount = 0; - for (var i = 0; i < elem.length; i++) { - if (elem[i].offsetHeight != 0) { - mentionCount++; - } + if (mentionBadge.length > 0) { // older mattermost doesn't have badges. + mentionCount = Number(mentionBadge[0].innerHTML); } - // unreadCount for active channel - var newSeparators = document.getElementsByClassName('new-separator'); - var post; - for (var i = 0; i < newSeparators.length; i++) { - if (newSeparators[i].offsetParent !== null) { - post = newSeparators[i]; + var postAttrName = 'data-reactid'; + var lastPostElem = document.querySelector('div[' + postAttrName + '="' + this.lastCheckedPostId + '"]'); + var isUnread = false; + var isMentioned = false; + if (lastPostElem === null || !isElementVisible(lastPostElem)) { + // When load channel or change channel, this.lastCheckedPostId is invalid. + // So we get latest post and save lastCheckedPostId. + + // 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) { + this.lastCheckedPostId = post.getAttribute(postAttrName); + } + } } } - // mentionCount for active channel - if (post != null) { - while (post = post.nextSibling) { - var highlight = post.getElementsByClassName('mention-highlight'); - if (highlight.length != 0 && highlight[0].offsetHeight != null) { - mentionCount++; + else if (lastPostElem !== null) { + var newPostElem = lastPostElem; + while (newPostElem = newPostElem.nextSibling) { + this.lastCheckedPostId = 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) { - ipc.sendToHost('onUnreadCountChange', unreadCount, mentionCount); + 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() { @@ -67,12 +99,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'); @@ -84,14 +110,6 @@ function overrideNotificationWithBalloon() { 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); - if (activeChannel === title) { - ipc.sendToHost('onActiveChannelNotify'); - } }; Notification.requestPermission = function(callback) { callback('granted');