From d0209aa8c362b45b501f2343525965b66ffb8c93 Mon Sep 17 00:00:00 2001 From: enahum Date: Mon, 20 Feb 2017 17:11:19 -0300 Subject: [PATCH] PLT-5488 Add an Unread indicator on the button to open the channel drawer (#277) * Unread indicator in burger button * Set badge to always be red --- app/components/badge.js | 39 +++++++++++------ app/scenes/channel/channel_drawer_button.js | 46 +++++++++++++++++++-- service/selectors/entities/channels.js | 23 +++++++++++ 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/app/components/badge.js b/app/components/badge.js index 49ca593e8..d00510f6d 100644 --- a/app/components/badge.js +++ b/app/components/badge.js @@ -1,8 +1,8 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import React from 'react'; -import {View, Text, StyleSheet} from 'react-native'; +import React, {PropTypes, PureComponent} from 'react'; +import {View, Text, TouchableWithoutFeedback, StyleSheet} from 'react-native'; const styles = StyleSheet.create({ container: { @@ -20,20 +20,22 @@ const styles = StyleSheet.create({ } }); -export default class Badge extends React.Component { +export default class Badge extends PureComponent { static defaultProps = { extraPaddingHorizontal: 10, minHeight: 0, - minWidth: 0 + minWidth: 0, + onPress: () => true }; static propTypes = { - count: React.PropTypes.number.isRequired, - extraPaddingHorizontal: React.PropTypes.number, + count: PropTypes.number.isRequired, + extraPaddingHorizontal: PropTypes.number, style: View.propTypes.style, countStyle: Text.propTypes.style, - minHeight: React.PropTypes.number, - minWidth: React.PropTypes.number + minHeight: PropTypes.number, + minWidth: PropTypes.number, + onPress: PropTypes.func }; constructor(props) { @@ -42,16 +44,25 @@ export default class Badge extends React.Component { } renderText = () => { + const {count} = this.props; + let text = count.toString(); + if (count < 0) { + text = '•'; + } return ( - {this.props.count} + {text} ); }; + badgeRef = (ref) => { + this.container = ref; + }; + onLayout = (e) => { let width; @@ -81,12 +92,14 @@ export default class Badge extends React.Component { render() { return ( { - this.container = component; - }} + ref={this.badgeRef} style={[styles.container, this.props.style]} > - {this.renderText()} + + + {this.renderText()} + + ); } diff --git a/app/scenes/channel/channel_drawer_button.js b/app/scenes/channel/channel_drawer_button.js index c09ce034a..83297d11f 100644 --- a/app/scenes/channel/channel_drawer_button.js +++ b/app/scenes/channel/channel_drawer_button.js @@ -8,10 +8,44 @@ import { View } from 'react-native'; import Icon from 'react-native-vector-icons/FontAwesome'; +import Badge from 'app/components/badge'; +import {getUnreads} from 'service/selectors/entities/channels'; import {getTheme} from 'service/selectors/entities/preferences'; function ChannelDrawerButton(props) { + let badge; + let badgeCount = props.mentionCount; + if (!badgeCount && props.messageCount) { + badgeCount = -1; + } + + if (badgeCount !== 0) { + const badgeStyle = { + position: 'absolute', + top: 5, + left: 5, + flexDirection: 'row', + backgroundColor: 'rgb(214, 73, 70)' + }; + + const mentionStyle = { + color: '#fff', + fontSize: 12 + }; + + badge = ( + props.emitter('open_channel_drawer')} + /> + ); + } + return ( + {badge} ); } ChannelDrawerButton.propTypes = { emitter: PropTypes.func.isRequired, - theme: PropTypes.object + theme: PropTypes.object, + messageCount: PropTypes.number, + mentionCount: PropTypes.number }; ChannelDrawerButton.defaultProps = { currentChannel: {}, - theme: {} + theme: {}, + messageCount: 0, + mentionCount: 0 }; function mapStateToProps(state) { return { - theme: getTheme(state) + theme: getTheme(state), + ...getUnreads(state) }; } diff --git a/service/selectors/entities/channels.js b/service/selectors/entities/channels.js index bc550f1c6..7d0c07d2d 100644 --- a/service/selectors/entities/channels.js +++ b/service/selectors/entities/channels.js @@ -101,3 +101,26 @@ export const getMoreChannels = createSelector( return getNotMemberChannels(Object.values(allChannels), myMembers); } ); + +export const getUnreads = createSelector( + getAllChannels, + getChannelMemberships, + (channels, myMembers) => { + let messageCount = 0; + let mentionCount = 0; + Object.keys(myMembers).forEach((id) => { + const m = myMembers[id]; + const channel = channels[id]; + if (channel.type === 'D') { + mentionCount += channel.total_msg_count - m.msg_count; + } else if (m.mention_count > 0) { + mentionCount += m.mention_count; + } + if (m.notify_props.mark_unread !== 'mention' && channel.total_msg_count - m.msg_count > 0) { + messageCount += 1; + } + }); + + return {messageCount, mentionCount}; + } +);