MM-18335 Keep the icon badge in sync (#3246)
* MM-18335 Keep the icon badge in sync * feedback review * Update snapshot
This commit is contained in:
parent
0ef2156b3f
commit
6853707ef2
7 changed files with 308 additions and 167 deletions
|
|
@ -95,7 +95,8 @@ class PushNotification {
|
|||
}
|
||||
|
||||
setApplicationIconBadgeNumber(number) {
|
||||
NotificationsAndroid.setBadgesCount(number);
|
||||
const count = number < 0 ? 0 : number;
|
||||
NotificationsAndroid.setBadgesCount(count);
|
||||
}
|
||||
|
||||
getNotification() {
|
||||
|
|
@ -114,14 +115,9 @@ class PushNotification {
|
|||
}
|
||||
}
|
||||
|
||||
clearForegroundNotifications = () => {
|
||||
// TODO: Implement as part of https://mattermost.atlassian.net/browse/MM-17110
|
||||
};
|
||||
|
||||
clearNotifications = () => {
|
||||
this.setApplicationIconBadgeNumber(0);
|
||||
this.cancelAllLocalNotifications(); // TODO: Only cancel the local notifications that belong to this server
|
||||
this.clearForegroundNotifications(); // TODO: Only clear the foreground notifications that belong to this server
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {AppState} from 'react-native';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import NotificationsIOS, {NotificationAction, NotificationCategory} from 'react-native-notifications';
|
||||
|
||||
import ephemeralStore from 'app/store/ephemeral_store';
|
||||
|
||||
const CATEGORY = 'CAN_REPLY';
|
||||
const REPLY_ACTION = 'REPLY_ACTION';
|
||||
export const FOREGROUND_NOTIFICATIONS_KEY = '@FOREGROUND_NOTIFICATIONS';
|
||||
|
||||
let replyCategory;
|
||||
const replies = new Set();
|
||||
|
|
@ -53,10 +51,6 @@ class PushNotification {
|
|||
if (this.onNotification) {
|
||||
this.onNotification(this.deviceNotification);
|
||||
}
|
||||
|
||||
if (foreground) {
|
||||
this.trackForegroundNotification(data.channel_id);
|
||||
}
|
||||
};
|
||||
|
||||
handleReply = (action, completed) => {
|
||||
|
|
@ -139,10 +133,6 @@ class PushNotification {
|
|||
message: notification.getMessage(),
|
||||
};
|
||||
this.handleNotification(info, true, false);
|
||||
|
||||
NotificationsIOS.getBadgesCount((count) => {
|
||||
this.setApplicationIconBadgeNumber(count + 1);
|
||||
});
|
||||
};
|
||||
|
||||
onNotificationOpened = (notification) => {
|
||||
|
|
@ -173,21 +163,7 @@ class PushNotification {
|
|||
}
|
||||
|
||||
clearChannelNotifications(channelId) {
|
||||
NotificationsIOS.getDeliveredNotifications(async (notifications) => {
|
||||
let foregroundNotifications;
|
||||
try {
|
||||
const value = await AsyncStorage.getItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
foregroundNotifications = JSON.parse(value) || {};
|
||||
} catch (e) {
|
||||
foregroundNotifications = {};
|
||||
}
|
||||
|
||||
Reflect.deleteProperty(foregroundNotifications, channelId);
|
||||
AsyncStorage.setItem(FOREGROUND_NOTIFICATIONS_KEY, JSON.stringify(foregroundNotifications));
|
||||
|
||||
const foregroundCount = Object.values(foregroundNotifications).reduce((a, b) => a + b, 0);
|
||||
let badgeCount = notifications.length + foregroundCount;
|
||||
|
||||
NotificationsIOS.getDeliveredNotifications((notifications) => {
|
||||
const ids = [];
|
||||
for (let i = 0; i < notifications.length; i++) {
|
||||
const notification = notifications[i];
|
||||
|
|
@ -198,32 +174,14 @@ class PushNotification {
|
|||
}
|
||||
|
||||
if (ids.length) {
|
||||
badgeCount -= ids.length;
|
||||
NotificationsIOS.removeDeliveredNotifications(ids);
|
||||
}
|
||||
|
||||
this.setApplicationIconBadgeNumber(badgeCount);
|
||||
});
|
||||
}
|
||||
|
||||
trackForegroundNotification = async (channelId) => {
|
||||
const value = await AsyncStorage.getItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
const foregroundNotifications = value ? JSON.parse(value) : {};
|
||||
if (!foregroundNotifications.hasOwnProperty(channelId)) {
|
||||
foregroundNotifications[channelId] = 0;
|
||||
}
|
||||
foregroundNotifications[channelId] += 1;
|
||||
await AsyncStorage.setItem(FOREGROUND_NOTIFICATIONS_KEY, JSON.stringify(foregroundNotifications));
|
||||
}
|
||||
|
||||
clearForegroundNotifications = () => {
|
||||
AsyncStorage.removeItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
};
|
||||
|
||||
clearNotifications = () => {
|
||||
this.setApplicationIconBadgeNumber(0);
|
||||
this.cancelAllLocalNotifications(); // TODO: Only cancel the local notifications that belong to this server
|
||||
this.clearForegroundNotifications(); // TODO: Only clear the foreground notifications that belong to this server
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import NotificationsIOS from 'react-native-notifications';
|
||||
import PushNotification, {FOREGROUND_NOTIFICATIONS_KEY} from './push_notifications.ios';
|
||||
import PushNotification from './push_notifications.ios';
|
||||
|
||||
jest.mock('react-native-notifications', () => {
|
||||
let badgesCount = 0;
|
||||
|
|
@ -33,58 +32,8 @@ jest.mock('react-native-notifications', () => {
|
|||
describe('PushNotification', () => {
|
||||
const channel1ID = 'channel-1-id';
|
||||
const channel2ID = 'channel-2-id';
|
||||
const notification = {
|
||||
getData: jest.fn(),
|
||||
getMessage: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => AsyncStorage.clear());
|
||||
|
||||
it('should track foreground notifications for channel', async () => {
|
||||
let item = await AsyncStorage.getItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
expect(item).toBe(null);
|
||||
|
||||
await PushNotification.trackForegroundNotification(channel1ID);
|
||||
item = await AsyncStorage.getItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
expect(item).not.toBe(null);
|
||||
let foregroundNotifications = JSON.parse(item);
|
||||
expect(foregroundNotifications[channel1ID]).toBe(1);
|
||||
expect(foregroundNotifications[channel2ID]).toBe(undefined);
|
||||
|
||||
await PushNotification.trackForegroundNotification(channel1ID);
|
||||
item = await AsyncStorage.getItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
expect(item).not.toBe(null);
|
||||
foregroundNotifications = JSON.parse(item);
|
||||
expect(foregroundNotifications[channel1ID]).toBe(2);
|
||||
expect(foregroundNotifications[channel2ID]).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should NOT track foreground notifications for channel when opened', async () => {
|
||||
let item = await AsyncStorage.getItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
expect(item).toBe(null);
|
||||
|
||||
PushNotification.trackForegroundNotification = jest.fn();
|
||||
PushNotification.onNotificationOpened(notification);
|
||||
expect(PushNotification.trackForegroundNotification).not.toBeCalled();
|
||||
item = await AsyncStorage.getItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
expect(item).toBe(null);
|
||||
});
|
||||
|
||||
it('should increment badge number when foreground notification is received', () => {
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(0));
|
||||
|
||||
PushNotification.onNotificationReceivedForeground(notification);
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledWith(1);
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(1));
|
||||
|
||||
PushNotification.onNotificationReceivedForeground(notification);
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledWith(2);
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(2));
|
||||
});
|
||||
|
||||
it('should clear channel notifications and set correct badge number', async () => {
|
||||
it('should clear channel notifications', async () => {
|
||||
const deliveredNotifications = [
|
||||
|
||||
// Three channel1 delivered notifications
|
||||
|
|
@ -113,20 +62,13 @@ describe('PushNotification', () => {
|
|||
];
|
||||
NotificationsIOS.setDeliveredNotifications(deliveredNotifications);
|
||||
|
||||
const foregroundNotifications = {
|
||||
[channel1ID]: 1,
|
||||
[channel2ID]: 1,
|
||||
};
|
||||
await AsyncStorage.setItem(FOREGROUND_NOTIFICATIONS_KEY, JSON.stringify(foregroundNotifications));
|
||||
|
||||
const notificationCount = deliveredNotifications.length + Object.values(foregroundNotifications).reduce((a, b) => a + b);
|
||||
expect(notificationCount).toBe(7);
|
||||
const notificationCount = deliveredNotifications.length;
|
||||
expect(notificationCount).toBe(5);
|
||||
|
||||
NotificationsIOS.setBadgesCount(notificationCount);
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(notificationCount));
|
||||
|
||||
// Clear channel1 notifications
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
await PushNotification.clearChannelNotifications(channel1ID);
|
||||
|
||||
await NotificationsIOS.getDeliveredNotifications(async (deliveredNotifs) => {
|
||||
|
|
@ -135,30 +77,17 @@ describe('PushNotification', () => {
|
|||
const channel2DeliveredNotifications = deliveredNotifs.filter((n) => n.userInfo.channel_id === channel2ID);
|
||||
expect(channel1DeliveredNotifications.length).toBe(0);
|
||||
expect(channel2DeliveredNotifications.length).toBe(2);
|
||||
|
||||
const item = await AsyncStorage.getItem(FOREGROUND_NOTIFICATIONS_KEY);
|
||||
const foregroundNotifs = JSON.parse(item);
|
||||
|
||||
const channel1ForegroundNotifications = foregroundNotifs[channel1ID];
|
||||
const channel2ForegroundNotifications = foregroundNotifs[channel2ID];
|
||||
expect(channel1ForegroundNotifications).toBe(undefined);
|
||||
expect(channel2ForegroundNotifications).toBe(1);
|
||||
|
||||
const badgeNumber = channel2DeliveredNotifications.length + channel2ForegroundNotifications;
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledWith(badgeNumber);
|
||||
});
|
||||
});
|
||||
|
||||
it('should clear all notifications', () => {
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
const cancelAllLocalNotifications = jest.spyOn(PushNotification, 'cancelAllLocalNotifications');
|
||||
const clearForegroundNotifications = jest.spyOn(PushNotification, 'clearForegroundNotifications');
|
||||
|
||||
PushNotification.clearNotifications();
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledWith(0);
|
||||
expect(NotificationsIOS.setBadgesCount).toHaveBeenCalledWith(0);
|
||||
expect(cancelAllLocalNotifications).toHaveBeenCalled();
|
||||
expect(NotificationsIOS.cancelAllLocalNotifications).toHaveBeenCalled();
|
||||
expect(clearForegroundNotifications).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ChannelDrawerButton should match, full snapshot 1`] = `
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.2}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"width": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "column",
|
||||
"justifyContent": "center",
|
||||
"paddingHorizontal": 10,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"height": 25,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
`;
|
||||
|
||||
exports[`ChannelDrawerButton should match, full snapshot 2`] = `
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.2}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"width": 55,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "column",
|
||||
"justifyContent": "center",
|
||||
"paddingHorizontal": 10,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<Icon
|
||||
allowFontScaling={false}
|
||||
color="#ffffff"
|
||||
name="md-menu"
|
||||
size={25}
|
||||
/>
|
||||
<Badge
|
||||
containerStyle={
|
||||
Object {
|
||||
"borderColor": "#1153ab",
|
||||
"borderRadius": 14,
|
||||
"borderWidth": 2,
|
||||
"position": "absolute",
|
||||
"right": -14,
|
||||
"top": -7,
|
||||
}
|
||||
}
|
||||
count={1}
|
||||
countStyle={
|
||||
Object {
|
||||
"color": "#145dbf",
|
||||
"fontSize": 10,
|
||||
}
|
||||
}
|
||||
extraPaddingHorizontal={10}
|
||||
minHeight={20}
|
||||
minWidth={19}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": undefined,
|
||||
"height": 19,
|
||||
"padding": 3,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
`;
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import {
|
||||
TouchableOpacity,
|
||||
View,
|
||||
|
|
@ -12,34 +11,44 @@ import {
|
|||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
|
||||
import Badge from 'app/components/badge';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import {getUnreadsInCurrentTeam} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
class ChannelDrawerButton extends PureComponent {
|
||||
export default class ChannelDrawerButton extends PureComponent {
|
||||
static propTypes = {
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
openDrawer: PropTypes.func.isRequired,
|
||||
messageCount: PropTypes.number,
|
||||
mentionCount: PropTypes.number,
|
||||
myTeamMembers: PropTypes.object,
|
||||
badgeCount: PropTypes.number,
|
||||
theme: PropTypes.object,
|
||||
visible: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
badgeCount: 0,
|
||||
currentChannel: {},
|
||||
theme: {},
|
||||
messageCount: 0,
|
||||
mentionCount: 0,
|
||||
visible: true,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.badgeCount > 0) {
|
||||
// Only set the icon badge number if once the component mounts we have at least one mention
|
||||
// reason is to prevent the notification in the notification center to get cleared
|
||||
// while the app is retrieving unread mentions from the server
|
||||
PushNotifications.setApplicationIconBadgeNumber(this.props.badgeCount);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
// Once the component updates we know for sure if there are or not mentions when it mounted
|
||||
// a) the app had mentions
|
||||
if (prevProps.badgeCount !== this.props.badgeCount) {
|
||||
PushNotifications.setApplicationIconBadgeNumber(this.props.badgeCount);
|
||||
}
|
||||
}
|
||||
|
||||
handlePress = preventDoubleTap(() => {
|
||||
telemetry.start(['channel:open_drawer']);
|
||||
this.props.openDrawer();
|
||||
|
|
@ -47,32 +56,13 @@ class ChannelDrawerButton extends PureComponent {
|
|||
|
||||
render() {
|
||||
const {
|
||||
currentTeamId,
|
||||
mentionCount,
|
||||
messageCount,
|
||||
myTeamMembers,
|
||||
badgeCount,
|
||||
theme,
|
||||
visible,
|
||||
} = this.props;
|
||||
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
let mentions = mentionCount;
|
||||
let messages = messageCount;
|
||||
|
||||
const members = Object.values(myTeamMembers).filter((m) => m.team_id !== currentTeamId);
|
||||
members.forEach((m) => {
|
||||
mentions += (m.mention_count || 0);
|
||||
messages += (m.msg_count || 0);
|
||||
});
|
||||
|
||||
let badgeCount = 0;
|
||||
if (mentions) {
|
||||
badgeCount = mentions;
|
||||
} else if (messages) {
|
||||
badgeCount = -1;
|
||||
}
|
||||
|
||||
let badge;
|
||||
if (badgeCount && visible) {
|
||||
badge = (
|
||||
|
|
@ -156,14 +146,3 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
|||
},
|
||||
};
|
||||
});
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
myTeamMembers: getTeamMemberships(state),
|
||||
theme: getTheme(state),
|
||||
...getUnreadsInCurrentTeam(state),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(ChannelDrawerButton);
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
import NotificationsIOS from 'react-native-notifications';
|
||||
|
||||
import Preferences from 'mattermost-redux/constants/preferences';
|
||||
|
||||
import Badge from 'app/components/badge';
|
||||
import PushNotification from 'app/push_notifications/push_notifications.ios';
|
||||
|
||||
import ChannelDrawerButton from './channel_drawer_button';
|
||||
|
||||
jest.mock('react-native-notifications', () => {
|
||||
let badgesCount = 0;
|
||||
let deliveredNotifications = {};
|
||||
|
||||
return {
|
||||
getBadgesCount: jest.fn((callback) => callback(badgesCount)),
|
||||
setBadgesCount: jest.fn((count) => {
|
||||
badgesCount = count;
|
||||
}),
|
||||
addEventListener: jest.fn(),
|
||||
setDeliveredNotifications: jest.fn((notifications) => {
|
||||
deliveredNotifications = notifications;
|
||||
}),
|
||||
getDeliveredNotifications: jest.fn(async (callback) => {
|
||||
await callback(deliveredNotifications);
|
||||
}),
|
||||
removeDeliveredNotifications: jest.fn((ids) => {
|
||||
deliveredNotifications = deliveredNotifications.filter((n) => !ids.includes(n.identifier));
|
||||
}),
|
||||
cancelAllLocalNotifications: jest.fn(),
|
||||
NotificationAction: jest.fn(),
|
||||
NotificationCategory: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('ChannelDrawerButton', () => {
|
||||
const baseProps = {
|
||||
openDrawer: jest.fn(),
|
||||
badgeCount: 0,
|
||||
theme: Preferences.THEMES.default,
|
||||
visible: false,
|
||||
};
|
||||
|
||||
afterEach(() => NotificationsIOS.setBadgesCount(0));
|
||||
|
||||
test('should match, full snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<ChannelDrawerButton {...baseProps}/>
|
||||
);
|
||||
|
||||
// no badge to show
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
expect(wrapper.find(Badge).length).toEqual(0);
|
||||
|
||||
// badge should render
|
||||
wrapper.setProps({badgeCount: 1, visible: true});
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
expect(wrapper.find(Badge).length).toEqual(1);
|
||||
});
|
||||
|
||||
test('should not set app icon badge on mount', () => {
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
const props = {
|
||||
...baseProps,
|
||||
badgeCount: 0,
|
||||
};
|
||||
|
||||
shallow(
|
||||
<ChannelDrawerButton {...props}/>
|
||||
);
|
||||
expect(setApplicationIconBadgeNumber).not.toBeCalled();
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(0));
|
||||
});
|
||||
|
||||
test('should set app icon badge on mount', () => {
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
const props = {
|
||||
...baseProps,
|
||||
badgeCount: 1,
|
||||
};
|
||||
|
||||
shallow(
|
||||
<ChannelDrawerButton {...props}/>
|
||||
);
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledTimes(1);
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(1));
|
||||
});
|
||||
|
||||
test('should set app icon badge update', () => {
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
const props = {
|
||||
...baseProps,
|
||||
badgeCount: 0,
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
<ChannelDrawerButton {...props}/>
|
||||
);
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(0));
|
||||
|
||||
wrapper.setProps({badgeCount: 2});
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledTimes(1);
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledWith(2);
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(2));
|
||||
});
|
||||
|
||||
test('should set remove icon badge on update', () => {
|
||||
const setApplicationIconBadgeNumber = jest.spyOn(PushNotification, 'setApplicationIconBadgeNumber');
|
||||
const props = {
|
||||
...baseProps,
|
||||
badgeCount: 0,
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
<ChannelDrawerButton {...props}/>
|
||||
);
|
||||
wrapper.setProps({badgeCount: 2});
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledWith(2);
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(2));
|
||||
|
||||
wrapper.setProps({badgeCount: -1});
|
||||
expect(setApplicationIconBadgeNumber).toHaveBeenCalledWith(-1);
|
||||
NotificationsIOS.getBadgesCount((count) => expect(count).toBe(0));
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import {createSelector} from 'reselect';
|
||||
|
||||
import {getUnreadsInCurrentTeam} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import ChannelDrawerButton from './channel_drawer_button';
|
||||
|
||||
const getBadgeCount = createSelector(
|
||||
getCurrentTeamId,
|
||||
getTeamMemberships,
|
||||
(state, mentionCount) => mentionCount,
|
||||
(state, _, messageCount) => messageCount,
|
||||
(currentTeamId, myTeamMembers, mentionCount, messageCount) => {
|
||||
let mentions = mentionCount;
|
||||
let messages = messageCount;
|
||||
|
||||
const members = Object.values(myTeamMembers).filter((m) => m.team_id !== currentTeamId);
|
||||
members.forEach((m) => {
|
||||
mentions += (m.mention_count || 0);
|
||||
messages += (m.msg_count || 0);
|
||||
});
|
||||
|
||||
let badgeCount = 0;
|
||||
if (mentions) {
|
||||
badgeCount = mentions;
|
||||
} else if (messages) {
|
||||
badgeCount = -1;
|
||||
}
|
||||
|
||||
return badgeCount;
|
||||
}
|
||||
);
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const {mentionCount, messageCount} = getUnreadsInCurrentTeam(state);
|
||||
|
||||
return {
|
||||
badgeCount: getBadgeCount(state, mentionCount, messageCount),
|
||||
theme: getTheme(state),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(ChannelDrawerButton);
|
||||
Loading…
Reference in a new issue