Commit 743d11c1 authored by enahum's avatar enahum Committed by Joram Wilander

[ICU-654] Fix unreads not clearing (#689)

* [ICU-654] Fix unreads not clearing

* Feedback review
parent a8fc9ca9
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {createDirectChannel, getChannelAndMyMember, getChannelStats, getMyChannelMember, joinChannel, viewChannel} from 'mattermost-redux/actions/channels';
import {createDirectChannel, getChannelAndMyMember, getChannelStats, getMyChannelMember, joinChannel, markChannelAsRead, viewChannel} from 'mattermost-redux/actions/channels';
import {getPostThread} from 'mattermost-redux/actions/posts';
import {removeUserFromTeam} from 'mattermost-redux/actions/teams';
import {Client4} from 'mattermost-redux/client';
......@@ -53,14 +53,10 @@ export function emitChannelClickEvent(channel) {
viewChannel(chan.id, oldChannelId)(dispatch, getState);
// Mark previous and next channel as read
ChannelStore.resetCounts([chan.id, oldChannelId]);
dispatch(markChannelAsRead(chan.id, oldChannelId));
reloadIfServerVersionChanged();
});
// Subtract mentions for the team
const {msgs, mentions} = ChannelStore.getUnreadCounts()[chan.id] || {msgs: 0, mentions: 0};
TeamStore.subtractUnread(chan.team_id, msgs, mentions);
BrowserStore.setGlobalItem(chan.team_id, chan.id);
loadProfilesForSidebar();
......
......@@ -3,15 +3,15 @@
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getMyChannelMembers, viewChannel} from 'mattermost-redux/actions/channels';
import {fetchMyChannelsAndMembers, getMyChannelMembers, markChannelAsRead, viewChannel} from 'mattermost-redux/actions/channels';
import {getMyTeamUnreads} from 'mattermost-redux/actions/teams';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {withRouter} from 'react-router-dom';
import NeedsTeam from './needs_team.jsx';
function mapStateToProps(state, ownProps) {
function mapStateToProps(state) {
return {
...ownProps,
theme: getTheme(state)
};
}
......@@ -19,7 +19,10 @@ function mapStateToProps(state, ownProps) {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
fetchMyChannelsAndMembers,
getMyTeamUnreads,
viewChannel,
markChannelAsRead,
getMyChannelMembers
}, dispatch)
};
......
......@@ -7,9 +7,6 @@ import React from 'react';
import {Route, Switch, Redirect} from 'react-router-dom';
import iNoBounce from 'inobounce';
import {getMyTeamUnreads} from 'mattermost-redux/actions/teams';
import {fetchMyChannelsAndMembers} from 'mattermost-redux/actions/channels';
import {loadStatusesForChannelAndSidebar, startPeriodicStatusUpdates, stopPeriodicStatusUpdates} from 'actions/status_actions.jsx';
import {startPeriodicSync, stopPeriodicSync, reconnect} from 'actions/websocket_actions.jsx';
import ChannelStore from 'stores/channel_store.jsx';
......@@ -54,27 +51,24 @@ import loadBackstageController from 'bundle-loader?lazy!components/backstage/bac
const BackstageController = makeAsyncComponent(loadBackstageController);
import store from 'stores/redux_store.jsx';
const dispatch = store.dispatch;
const getState = store.getState;
const UNREAD_CHECK_TIME_MILLISECONDS = 10000;
let wakeUpInterval;
let lastTime = (new Date()).getTime();
const WAKEUP_CHECK_INTERVAL = 30000; // 30 seconds
const WAKEUP_THRESHOLD = 60000; // 60 seconds
const UNREAD_CHECK_TIME_MILLISECONDS = 10000;
export default class NeedsTeam extends React.Component {
static propTypes = {
params: PropTypes.object,
actions: PropTypes.shape({
fetchMyChannelsAndMembers: PropTypes.func.isRequired,
getMyTeamUnreads: PropTypes.func.isRequired,
viewChannel: PropTypes.func.isRequired,
markChannelAsRead: PropTypes.func.isRequired,
getMyChannelMembers: PropTypes.func.isRequired
}).isRequired,
theme: PropTypes.object.isRequired
}
};
constructor(params) {
super(params);
......@@ -128,7 +122,7 @@ export default class NeedsTeam extends React.Component {
// If current team is set, then this is not first load
// The first load action pulls team unreads
if (TeamStore.getCurrentId()) {
getMyTeamUnreads()(dispatch, getState);
this.props.actions.getMyTeamUnreads();
}
TeamStore.saveMyTeam(team);
......@@ -136,7 +130,7 @@ export default class NeedsTeam extends React.Component {
TeamStore.emitChange();
GlobalActions.emitCloseRightHandSide();
fetchMyChannelsAndMembers(team.id)(dispatch, getState).then(
this.props.actions.fetchMyChannelsAndMembers(team.id).then(
() => {
this.setState({
finishedFetchingChannels: true
......@@ -167,7 +161,7 @@ export default class NeedsTeam extends React.Component {
// Set up tracking for whether the window is active
window.isActive = true;
$(window).on('focus', async () => {
ChannelStore.resetCounts([ChannelStore.getCurrentId()]);
this.props.actions.markChannelAsRead(ChannelStore.getCurrentId());
ChannelStore.emitChange();
window.isActive = true;
......
......@@ -3,7 +3,8 @@
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getChannel, getChannelsNameMapInCurrentTeam} from 'mattermost-redux/selectors/entities/channels';
import {getChannelsNameMapInCurrentTeam, makeGetChannel} from 'mattermost-redux/selectors/entities/channels';
import {getUserIdsInChannels, getUser} from 'mattermost-redux/selectors/entities/users';
import {get as getPreference} from 'mattermost-redux/selectors/entities/preferences';
import {savePreferences} from 'mattermost-redux/actions/preferences';
......@@ -11,49 +12,58 @@ import {leaveChannel} from 'mattermost-redux/actions/channels';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {Constants} from 'utils/constants.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import SidebarChannel from './sidebar_channel.jsx';
function mapStateToProps(state, ownProps) {
const config = getConfig(state);
const channel = getChannel(state, ownProps.channelId) || {};
const tutorialStep = getPreference(state, Constants.Preferences.TUTORIAL_STEP, ownProps.currentUserId, 999);
const channelsByName = getChannelsNameMapInCurrentTeam(state);
const memberIds = getUserIdsInChannels(state);
function makeMapStateToProps() {
const getChannel = makeGetChannel();
return (state, ownProps) => {
const config = getConfig(state);
const channel = getChannel(state, {id: ownProps.channelId}) || {};
const tutorialStep = getPreference(state, Constants.Preferences.TUTORIAL_STEP, ownProps.currentUserId, 999);
const channelsByName = getChannelsNameMapInCurrentTeam(state);
const memberIds = getUserIdsInChannels(state);
let membersCount = 0;
if (memberIds && memberIds[channel.id]) {
membersCount = memberIds[channel.id].size;
if (memberIds[channel.id].has(ownProps.currentUserId)) {
membersCount--;
let membersCount = 0;
if (memberIds && memberIds[channel.id]) {
membersCount = memberIds[channel.id].size;
if (memberIds[channel.id].has(ownProps.currentUserId)) {
membersCount--;
}
}
}
const unreads = ChannelStore.getUnreadCount(channel.id);
const member = ownProps.membership;
const unreadMentions = member ? member.mention_count : 0;
let unreadMsgs = channel && member ? channel.total_msg_count - member.msg_count : 0;
if (unreadMsgs < 0) {
unreadMsgs = 0;
}
let teammate = null;
if (channel.teammate_id) {
teammate = getUser(state, channel.teammate_id);
}
let teammate = null;
if (channel.teammate_id) {
teammate = getUser(state, channel.teammate_id);
}
return {
config,
channelId: channel.id,
channelName: channel.name,
channelDisplayName: channel.display_name,
channelType: channel.type,
channelStatus: channel.status,
channelFake: channel.fake,
channelStringified: channel.fake && JSON.stringify(channel),
channelTeammateId: teammate && teammate.id,
channelTeammateDeletedAt: teammate && teammate.delete_at,
showTutorialTip: tutorialStep === Constants.TutorialSteps.CHANNEL_POPOVER && config.EnableTutorial === 'true',
townSquareDisplayName: channelsByName[Constants.DEFAULT_CHANNEL] && channelsByName[Constants.DEFAULT_CHANNEL].display_name,
offTopicDisplayName: channelsByName[Constants.OFFTOPIC_CHANNEL] && channelsByName[Constants.OFFTOPIC_CHANNEL].display_name,
unreadMsgs: unreads.msgs,
unreadMentions: unreads.mentions,
membersCount
return {
config,
channelId: channel.id,
channelName: channel.name,
channelDisplayName: channel.display_name,
channelType: channel.type,
channelStatus: channel.status,
channelFake: channel.fake,
channelStringified: channel.fake && JSON.stringify(channel),
channelTeammateId: teammate && teammate.id,
channelTeammateDeletedAt: teammate && teammate.delete_at,
showTutorialTip: tutorialStep === Constants.TutorialSteps.CHANNEL_POPOVER && config.EnableTutorial === 'true',
townSquareDisplayName: channelsByName[Constants.DEFAULT_CHANNEL] && channelsByName[Constants.DEFAULT_CHANNEL].display_name,
offTopicDisplayName: channelsByName[Constants.OFFTOPIC_CHANNEL] && channelsByName[Constants.OFFTOPIC_CHANNEL].display_name,
unreadMsgs,
unreadMentions,
membersCount
};
};
}
......@@ -66,4 +76,4 @@ function mapDispatchToProps(dispatch) {
};
}
export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(SidebarChannel);
export default connect(makeMapStateToProps, mapDispatchToProps, null, {withRef: true})(SidebarChannel);
......@@ -5,14 +5,15 @@ import EventEmitter from 'events';
import {batchActions} from 'redux-batched-actions';
import {ChannelTypes} from 'mattermost-redux/action_types';
import {markChannelAsRead, markChannelAsUnread, markChannelAsViewed} from 'mattermost-redux/actions/channels';
import * as Selectors from 'mattermost-redux/selectors/entities/channels';
import {isFromWebhook, isSystemMessage, shouldIgnorePost} from 'mattermost-redux/utils/post_utils';
import UserStore from 'stores/user_store.jsx';
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import store from 'stores/redux_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import {ActionTypes, Constants} from 'utils/constants.jsx';
import {isFromWebhook, isSystemMessage} from 'utils/post_utils.jsx';
var ChannelUtils;
var Utils;
......@@ -183,7 +184,9 @@ class ChannelStoreClass extends EventEmitter {
}
});
this.storeMyChannelMembersList(membersToStore);
if (membersToStore.length) {
this.storeMyChannelMembersList(membersToStore);
}
}
getCurrentId() {
......@@ -546,27 +549,28 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
});
break;
case ActionTypes.RECEIVED_POST:
if (Constants.IGNORE_POST_TYPES.indexOf(action.post.type) !== -1) {
case ActionTypes.RECEIVED_POST: {
const {post, websocketMessageProps: data} = action;
const {dispatch} = store;
if (shouldIgnorePost(post)) {
return;
}
if (action.post.user_id === UserStore.getCurrentId() && !isSystemMessage(action.post) && !isFromWebhook(action.post)) {
return;
let markAsRead = false;
if (post.user_id === UserStore.getCurrentId() && !isSystemMessage(post) && !isFromWebhook(post)) {
markAsRead = true;
} else if (action.post.channel_id === ChannelStore.getCurrentId() && window.isActive) {
markAsRead = true;
}
var id = action.post.channel_id;
var teamId = action.websocketMessageProps ? action.websocketMessageProps.team_id : null;
var markRead = id === ChannelStore.getCurrentId() && window.isActive;
if (TeamStore.getCurrentId() === teamId || teamId === '') {
if (!markRead) {
ChannelStore.incrementMentionsIfNeeded(id, action.websocketMessageProps);
}
ChannelStore.incrementMessages(id, markRead);
if (markAsRead) {
dispatch(markChannelAsRead(post.channel_id, null, false));
dispatch(markChannelAsViewed(post.channel_id));
} else {
dispatch(markChannelAsUnread(data.team_id, post.channel_id, data.mentions));
}
break;
}
case ActionTypes.CREATE_POST:
ChannelStore.incrementMessages(action.post.channel_id, true);
break;
......
......@@ -10,7 +10,6 @@ import ChannelStore from 'stores/channel_store.jsx';
import store from 'stores/redux_store.jsx';
import UserStore from 'stores/user_store.jsx';
import Constants from 'utils/constants.jsx';
import {isFromWebhook, isSystemMessage} from 'utils/post_utils.jsx';
import {getSiteURL} from 'utils/url.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
......@@ -371,23 +370,6 @@ class TeamStoreClass extends EventEmitter {
}
}
subtractUnread(teamId, msgs, mentions) {
let member = this.getMyTeamMembers().filter((m) => m.team_id === teamId)[0];
if (member) {
const msgCount = member.msg_count - msgs;
const mentionCount = member.mention_count - mentions;
member = Object.assign({}, member);
member.msg_count = (msgCount > 0) ? msgCount : 0;
member.mention_count = (mentionCount > 0) ? mentionCount : 0;
store.dispatch({
type: TeamTypes.RECEIVED_MY_TEAM_MEMBER,
data: member
});
}
}
incrementMessages(id, channelId) {
const channelMember = ChannelStore.getMyMember(channelId);
if (channelMember && channelMember.notify_props && channelMember.notify_props.mark_unread === NotificationPrefs.MENTION) {
......@@ -458,21 +440,6 @@ TeamStore.dispatchToken = AppDispatcher.register((payload) => {
case ActionTypes.RECEIVED_TEAM_STATS:
TeamStore.saveStats(action.team_id, action.stats);
break;
case ActionTypes.RECEIVED_POST:
if (Constants.IGNORE_POST_TYPES.indexOf(action.post.type) !== -1) {
return;
}
if (action.post.user_id === UserStore.getCurrentId() && !isSystemMessage(action.post) && !isFromWebhook(action.post)) {
return;
}
var id = action.websocketMessageProps ? action.websocketMessageProps.team_id : null;
if (id && TeamStore.getCurrentId() !== id) {
TeamStore.incrementMessages(id, action.post.channel_id);
TeamStore.incrementMentionsIfNeeded(id, action.websocketMessageProps);
}
break;
default:
}
});
......
......@@ -5568,7 +5568,7 @@ math-expression-evaluator@^1.2.14:
mattermost-redux@mattermost/mattermost-redux:
version "1.1.0"
resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/02d1ef0a7b460eb8935ac2b8fdf3744b1b7514dc"
resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/ed1d042770aa5fe805949da62a508f607452a746"
dependencies:
deep-equal "1.0.1"
form-data "2.3.1"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment