From 7399d349dedc35c8c05b223a3c143a420bf2411f Mon Sep 17 00:00:00 2001 From: Pepijn Date: Thu, 8 Dec 2016 00:08:37 +0100 Subject: Created a new message indicator component to indicate new messages (#4299) * Created a new message indicator component to indicate new messages are present outside the user's view: * Created new component with styling * Theming and i18n support for new messages indicator * Count new unviewed messages and integrate with component * Coding style for new message indicator * Fixed bugs with new message indicator: * Fix display issues with new message indicator * Update text to deal with plurals in React * Fix coded style for ticket 'PLT-1917': add new message indicator. * Use only server generated timestamps for checking new messages at the bottom * Move transitionend to prop, fix style selectors --- .../post_view/components/new_message_indicator.jsx | 65 ++++++++++++++++++++++ .../components/post_view/components/post_list.jsx | 27 ++++++++- .../components/post_view/post_view_controller.jsx | 13 ++++- 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 webapp/components/post_view/components/new_message_indicator.jsx (limited to 'webapp/components') diff --git a/webapp/components/post_view/components/new_message_indicator.jsx b/webapp/components/post_view/components/new_message_indicator.jsx new file mode 100644 index 000000000..7407c1024 --- /dev/null +++ b/webapp/components/post_view/components/new_message_indicator.jsx @@ -0,0 +1,65 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +export default class NewMessageIndicator extends React.Component { + constructor(props) { + super(props); + this.state = { + visible: false, + rendered: false + }; + } + componentWillReceiveProps(nextProps) { + if (nextProps.newMessages > 0) { + this.setState({rendered: true}, () => { + this.setState({visible: true}); + }); + } else { + this.setState({visible: false}); + } + } + render() { + let className = 'nav-pills__unread-indicator-bottom'; + if (this.state.visible > 0) { + className += ' visible'; + } + if (!this.state.rendered) { + className += ' disabled'; + } + return ( +
+ + + + +
+ ); + } + + // Sync 'rendered' state with visibility param, only after transitions + // have ended + setRendered() { + this.setState({rendered: this.state.visible}); + } +} +NewMessageIndicator.defaultProps = { + newMessages: 0 +}; + +NewMessageIndicator.propTypes = { + onClick: React.PropTypes.func.isRequired, + newMessages: React.PropTypes.number +}; diff --git a/webapp/components/post_view/components/post_list.jsx b/webapp/components/post_view/components/post_list.jsx index a0cfb208e..157e0dde2 100644 --- a/webapp/components/post_view/components/post_list.jsx +++ b/webapp/components/post_view/components/post_list.jsx @@ -6,6 +6,7 @@ import $ from 'jquery'; import Post from './post.jsx'; import FloatingTimestamp from './floating_timestamp.jsx'; import ScrollToBottomArrows from './scroll_to_bottom_arrows.jsx'; +import NewMessageIndicator from './new_message_indicator.jsx'; import * as GlobalActions from 'actions/global_actions.jsx'; @@ -56,7 +57,8 @@ export default class PostList extends React.Component { this.state = { isScrolling: false, fullWidthIntro: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_FULL_SCREEN, - topPostId: null + topPostId: null, + unViewedCount: 0 }; if (props.channel) { @@ -74,6 +76,23 @@ export default class PostList extends React.Component { this.introText = createChannelIntroMessage(this.props.channel, this.state.fullWidthIntro); } } + + const posts = nextProps.postList.posts; + const order = nextProps.postList.order; + let unViewedCount = 0; + + // Only count if we're not at the bottom, not in highlight view, + // or anything else + if (nextProps.scrollType === Constants.ScrollTypes.FREE) { + for (let i = order.length - 1; i >= 0; i--) { + const post = posts[order[i]]; + if (post.create_at > nextProps.lastViewedBottom && + post.user_id !== nextProps.currentUser.id) { + unViewedCount++; + } + } + } + this.setState({unViewedCount}); } handleKeyDown(e) { @@ -553,6 +572,10 @@ export default class PostList extends React.Component { atBottom={this.wasAtBottom} onClick={this.scrollToBottomAnimated} /> +