diff --git a/ts/components/MessagesTabIcon.tsx b/ts/components/MessagesTabIcon.tsx new file mode 100644 index 00000000000..71089fff0a8 --- /dev/null +++ b/ts/components/MessagesTabIcon.tsx @@ -0,0 +1,75 @@ +import { Badge, View } from "native-base"; +import React from "react"; +import { Platform, StyleSheet, Text } from "react-native"; +import { connect } from "react-redux"; +import { messagesUnreadSelector } from "../store/reducers/entities/messages"; +import { GlobalState } from "../store/reducers/types"; +import variables from "../theme/variables"; +import IconFont from "./ui/IconFont"; + +type OwnProps = { + color?: string; +}; +const MAX_BADGE_VALUE = 99; +const styles = StyleSheet.create({ + textBadgeStyle: { + fontSize: 10, + fontFamily: "Titillium Web", + fontWeight: "bold", + color: "white", + flex: 1, + position: "absolute", + height: 19, + width: 19, + textAlign: "center", + paddingRight: 3, + top: Platform.OS === "ios" ? 0 : undefined + }, + badgeStyle: { + backgroundColor: variables.brandPrimary, + borderColor: "white", + borderWidth: 2, + position: "absolute", + elevation: 0.1, + shadowColor: "white", + height: 19, + width: 19, + left: 12, + bottom: 10 + } +}); + +type Props = OwnProps & ReturnType; + +/** + * Message icon add badge. + */ +class MessagesTabIcon extends React.PureComponent { + public render() { + const { color, badgeValue } = this.props; + return ( + + + {badgeValue > 0 ? ( + + {badgeValue} + + ) : null} + + ); + } +} + +function mapStateToProps(state: GlobalState) { + const messagesUnread = messagesUnreadSelector(state); + return { + badgeValue: + messagesUnread < MAX_BADGE_VALUE ? messagesUnread : MAX_BADGE_VALUE + }; +} + +export default connect(mapStateToProps)(MessagesTabIcon); diff --git a/ts/components/messages/MessageListComponent.tsx b/ts/components/messages/MessageListComponent.tsx index 669a15d8d85..bf715f02a7d 100644 --- a/ts/components/messages/MessageListComponent.tsx +++ b/ts/components/messages/MessageListComponent.tsx @@ -8,7 +8,6 @@ import { StyleSheet, View } from "react-native"; - import { NavigationEvents } from "react-navigation"; import { MessageState } from "../../store/reducers/entities/messages/messagesById"; import { PaymentByRptIdState } from "../../store/reducers/entities/payments"; @@ -42,7 +41,6 @@ const keyExtractor = (_: MessageState) => _.meta.id; class MessageListComponent extends React.Component { private renderItem = (info: ListRenderItemInfo) => { const { meta } = info.item; - const service = this.props.servicesById[meta.sender_service_id]; return ( @@ -76,7 +74,6 @@ class MessageListComponent extends React.Component { paymentByRptId, ListEmptyComponent } = this.props; - const refreshControl = ( ); diff --git a/ts/navigation/MainNavigator.tsx b/ts/navigation/MainNavigator.tsx index 0446be90864..31cff42df88 100644 --- a/ts/navigation/MainNavigator.tsx +++ b/ts/navigation/MainNavigator.tsx @@ -14,7 +14,7 @@ import { NavigationState, StackActions } from "react-navigation"; - +import MessagesTabIcon from "../components/MessagesTabIcon"; import ProfileTabIcon from "../components/ProfileTabIcon"; import IconFont from "../components/ui/IconFont"; import I18n from "../i18n"; @@ -25,7 +25,6 @@ import PreferencesNavigator from "./PreferencesNavigator"; import ProfileNavigator from "./ProfileNavigator"; import ROUTES from "./routes"; import WalletNavigator from "./WalletNavigator"; - type Routes = keyof typeof ROUTES; type RouteLabelMap = { [key in Routes]?: string }; @@ -113,7 +112,6 @@ const getTabBarVisibility = ( if (NoTabBarRoutes.indexOf(routeName) !== -1) { return false; } - return true; }; @@ -166,7 +164,13 @@ const navigation = createBottomTabNavigator( tabBarIcon: (options: { tintColor: string | null; focused: boolean }) => { const { routeName } = nav.state; const iconName: string = getIcon(routeName); - + if (routeName === ROUTES.MESSAGES_NAVIGATOR) { + return ( + + ); + } if (iconName === ROUTE_ICON.PROFILE_NAVIGATOR) { return ( ); + } else { + return ( + + ); } - return ( - - ); }, tabBarOnPress: options => { if (options.navigation.state.index > 0) { diff --git a/ts/screens/messages/MessageDetailScreen.tsx b/ts/screens/messages/MessageDetailScreen.tsx index 322ff1342ec..8aa03ac7cde 100644 --- a/ts/screens/messages/MessageDetailScreen.tsx +++ b/ts/screens/messages/MessageDetailScreen.tsx @@ -5,7 +5,6 @@ import * as React from "react"; import { ActivityIndicator, Image, StyleSheet } from "react-native"; import { NavigationScreenProps } from "react-navigation"; import { connect } from "react-redux"; - import { CreatedMessageWithoutContent } from "../../../definitions/backend/CreatedMessageWithoutContent"; import { ServiceId } from "../../../definitions/backend/ServiceId"; import { ServicePublic } from "../../../definitions/backend/ServicePublic"; @@ -281,7 +280,6 @@ export class MessageDetailScreen extends React.PureComponent { const mapStateToProps = (state: GlobalState, ownProps: OwnProps) => { const messageId = ownProps.navigation.getParam("messageId"); - const maybeMessageState = fromNullable( messageStateByIdSelector(messageId)(state) ); diff --git a/ts/screens/messages/MessagesHomeScreen.tsx b/ts/screens/messages/MessagesHomeScreen.tsx index 1fc5095b26a..dd8e770c7c4 100644 --- a/ts/screens/messages/MessagesHomeScreen.tsx +++ b/ts/screens/messages/MessagesHomeScreen.tsx @@ -15,7 +15,6 @@ import * as React from "react"; import { StyleSheet } from "react-native"; import { NavigationScreenProps } from "react-navigation"; import { connect } from "react-redux"; - import MessagesArchive from "../../components/messages/MessagesArchive"; import MessagesDeadlines from "../../components/messages/MessagesDeadlines"; import MessagesInbox from "../../components/messages/MessagesInbox"; @@ -37,7 +36,6 @@ import customVariables from "../../theme/variables"; // Used to disable the Deadlines tab const DEADLINES_TAB_ENABLED = false; - type Props = NavigationScreenProps & ReturnType & ReturnType; @@ -310,7 +308,9 @@ const mapStateToProps = (state: GlobalState) => ({ }); const mapDispatchToProps = (dispatch: Dispatch) => ({ - refreshMessages: () => dispatch(loadMessages.request()), + refreshMessages: () => { + dispatch(loadMessages.request()); + }, navigateToMessageDetail: (messageId: string) => dispatch(navigateToMessageDetailScreenAction({ messageId })), updateMessagesArchivedState: ( diff --git a/ts/store/reducers/entities/messages/index.ts b/ts/store/reducers/entities/messages/index.ts index 70f063b7859..c985848d44d 100644 --- a/ts/store/reducers/entities/messages/index.ts +++ b/ts/store/reducers/entities/messages/index.ts @@ -12,6 +12,7 @@ import messagesAllIdsReducer, { messagesAllIdsSelector, MessagesAllIdsState } from "./messagesAllIds"; + import messagesByIdReducer, { messagesStateByIdSelector, MessageStateById @@ -34,7 +35,7 @@ const reducer = combineReducers({ // Selectors -/** +/**- * Returns array of messages IDs inversely lexically ordered. */ export const lexicallyOrderedMessagesIds = createSelector( @@ -59,4 +60,15 @@ export const lexicallyOrderedMessagesStateSelector = createSelector( ) ); +export const messagesUnreadSelector = createSelector( + lexicallyOrderedMessagesStateSelector, + potMessagesState => + pot.getOrElse( + pot.map(potMessagesState, _ => + _.filter(messageState => !messageState.isRead) + ), + [] + ).length +); + export default reducer;