MM-37660 Fix keep mention for channel A when opening a push notification on channel B (#5597)

* Fix keep mention for channel A when opening a push notification on channel B

* properly fix race condition

* Load channels and channel members when opening the app from a push notification
This commit is contained in:
Elias Nahum
2021-08-05 22:56:36 -04:00
committed by GitHub
parent 3d5f8fbf9b
commit a7b604e8c9
4 changed files with 51 additions and 40 deletions

View File

@@ -6,10 +6,10 @@ import {batchActions} from 'redux-batched-actions';
import {Client4} from '@client/rest';
import {NavigationTypes, ViewTypes} from '@constants';
import {ChannelTypes, GeneralTypes, TeamTypes} from '@mm-redux/action_types';
import {fetchMyChannelsAndMembers, getChannelAndMyMember} from '@mm-redux/actions/channels';
import {getChannelAndMyMember} from '@mm-redux/actions/channels';
import {getDataRetentionPolicy} from '@mm-redux/actions/general';
import {receivedNewPost} from '@mm-redux/actions/posts';
import {getMyTeams, getMyTeamMembers} from '@mm-redux/actions/teams';
import {getMyTeams, getMyTeamMembers, getMyTeamUnreads} from '@mm-redux/actions/teams';
import {General} from '@mm-redux/constants';
import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
import EventEmitter from '@mm-redux/utils/event_emitter';
@@ -17,7 +17,7 @@ import {getViewingGlobalThreads} from '@selectors/threads';
import initialState from '@store/initial_state';
import {getStateForReset} from '@store/utils';
import {markAsViewedAndReadBatch} from './channel';
import {loadChannelsForTeam, markAsViewedAndReadBatch} from './channel';
import {handleNotViewingGlobalThreadsScreen} from './threads';
export function startDataCleanup() {
@@ -64,12 +64,11 @@ export function loadConfigAndLicense() {
};
}
export function loadFromPushNotification(notification) {
export function loadFromPushNotification(notification, isInitialNotification) {
return async (dispatch, getState) => {
const state = getState();
const {payload} = notification;
const {currentTeamId, teams, myMembers: myTeamMembers} = state.entities.teams;
const {channels} = state.entities.channels;
let channelId = '';
let teamId = currentTeamId;
@@ -88,8 +87,9 @@ export function loadFromPushNotification(notification) {
loading.push(dispatch(getMyTeamMembers()));
}
if (channelId && !channels[channelId]) {
loading.push(dispatch(fetchMyChannelsAndMembers(teamId)));
if (isInitialNotification) {
loading.push(dispatch(getMyTeamUnreads()));
loading.push(dispatch(loadChannelsForTeam(teamId)));
}
if (loading.length > 0) {
@@ -105,10 +105,18 @@ export function loadFromPushNotification(notification) {
export function handleSelectTeamAndChannel(teamId, channelId) {
return async (dispatch, getState) => {
const dt = Date.now();
await dispatch(getChannelAndMyMember(channelId));
let state = getState();
let {channels, myMembers} = state.entities.channels;
const state = getState();
const {channels, currentChannelId, myMembers} = state.entities.channels;
if (channelId && (!channels[channelId] || !myMembers[channelId])) {
await dispatch(getChannelAndMyMember(channelId));
state = getState();
}
channels = state.entities.channels.channels;
myMembers = state.entities.channels.myMembers;
const {currentChannelId} = state.entities.channels;
const {currentTeamId} = state.entities.teams;
const channel = channels[channelId];
const member = myMembers[channelId];

View File

@@ -174,10 +174,10 @@ const NetworkIndicator = ({
handleWebSocket(active);
if (active) {
// Clear the notifications for the current channel after one second
// Clear the notifications for the current channel after two seconds
// this is done so we can cancel it in case the app is brought to the
// foreground by tapping a notification from another channel
clearNotificationTimeout.current = setTimeout(clearNotifications, 1000);
clearNotificationTimeout.current = setTimeout(clearNotifications, 2000);
}
});
@@ -185,14 +185,22 @@ const NetworkIndicator = ({
return () => {
AppState.removeEventListener('change', handleAppStateChange);
if (clearNotificationTimeout.current && AppState.currentState !== 'active') {
clearTimeout(clearNotificationTimeout.current);
}
};
}, [netinfo.isInternetReachable]);
}, [netinfo.isInternetReachable, channelId]);
useEffect(() => {
if (clearNotificationTimeout.current) {
clearTimeout(clearNotificationTimeout.current);
clearNotificationTimeout.current = undefined;
if (channelId) {
clearNotificationTimeout.current = setTimeout(clearNotifications, 1500);
}
return () => {
if (clearNotificationTimeout.current && channelId) {
clearTimeout(clearNotificationTimeout.current);
}
};
}, [channelId]);
useEffect(() => {

View File

@@ -100,19 +100,24 @@ class PushNotifications {
Notifications.ios.removeDeliveredNotifications(ids);
}
if (Store.redux) {
const totalMentions = getBadgeCount(Store.redux.getState());
if (totalMentions > -1) {
// replaces the badge count based on the redux store.
badgeCount = totalMentions;
}
}
this.setBadgeCountByMentions(badgeCount);
}
}
if (Platform.OS === 'ios') {
badgeCount = badgeCount <= 0 ? 0 : badgeCount;
Notifications.ios.setBadgeCount(badgeCount);
setBadgeCountByMentions = (initialBadge = 0) => {
let badgeCount = initialBadge;
if (Store.redux) {
const totalMentions = getBadgeCount(Store.redux.getState());
if (totalMentions > -1) {
// replaces the badge count based on the redux store.
badgeCount = totalMentions;
}
}
if (Platform.OS === 'ios') {
badgeCount = badgeCount <= 0 ? 0 : badgeCount;
Notifications.ios.setBadgeCount(badgeCount);
}
}
createReplyCategory = () => {
@@ -144,13 +149,13 @@ class PushNotifications {
const interval = setInterval(() => {
if (Store.redux) {
clearInterval(interval);
this.handleNotification(notification);
this.handleNotification(notification, true);
}
}, 500);
}
}
handleNotification = (notification: NotificationWithData) => {
handleNotification = (notification: NotificationWithData, isInitialNotification = false) => {
const {payload, foreground, userInteraction} = notification;
if (Store.redux && payload) {
@@ -167,8 +172,9 @@ class PushNotifications {
if (foreground) {
EventEmitter.emit(ViewTypes.NOTIFICATION_IN_APP, notification);
this.setBadgeCountByMentions();
} else if (userInteraction && !payload.userInfo?.local) {
dispatch(loadFromPushNotification(notification));
dispatch(loadFromPushNotification(notification, isInitialNotification));
const componentId = EphemeralStore.getNavigationTopComponentId();
if (componentId) {
EventEmitter.emit(NavigationTypes.CLOSE_MAIN_SIDEBAR);

View File

@@ -9,7 +9,6 @@ import {Alert, Animated, Keyboard, StyleSheet} from 'react-native';
import {showModal, showModalOverCurrentContext} from '@actions/navigation';
import CompassIcon from '@components/compass_icon';
import {TYPING_VISIBLE} from '@constants/post_draft';
import PushNotifications from '@init/push_notifications';
import {General} from '@mm-redux/constants';
import EventEmitter from '@mm-redux/utils/event_emitter';
import EphemeralStore from '@store/ephemeral_store';
@@ -87,7 +86,6 @@ export default class ChannelBase extends PureComponent {
}
if (currentChannelId) {
this.clearChannelNotifications();
requestAnimationFrame(() => {
actions.getChannelStats(currentChannelId);
});
@@ -125,8 +123,6 @@ export default class ChannelBase extends PureComponent {
}
if (this.props.currentChannelId && this.props.currentChannelId !== prevProps.currentChannelId) {
this.clearChannelNotifications();
requestAnimationFrame(() => {
this.props.actions.getChannelStats(this.props.currentChannelId);
});
@@ -139,13 +135,6 @@ export default class ChannelBase extends PureComponent {
EventEmitter.off(General.REMOVED_FROM_CHANNEL, this.handleRemovedFromChannel);
}
clearChannelNotifications = () => {
const clearNotificationsTimeout = setTimeout(() => {
clearTimeout(clearNotificationsTimeout);
PushNotifications.clearChannelNotifications(this.props.currentChannelId);
}, 1000);
}
registerTypingAnimation = (animation) => {
const length = this.typingAnimations.push(animation);
const removeAnimation = () => {