diff --git a/src/components/UnreadActionIndicator.js b/src/components/UnreadActionIndicator.js index 0a3fa8d3d95d..d2ffcc232c2a 100644 --- a/src/components/UnreadActionIndicator.js +++ b/src/components/UnreadActionIndicator.js @@ -1,29 +1,17 @@ import React from 'react'; -import {Animated, View} from 'react-native'; -import PropTypes from 'prop-types'; -import styles, {getOpacityStyle} from '../styles/styles'; +import {View} from 'react-native'; +import styles from '../styles/styles'; import Text from './Text'; -const propTypes = { - // Animated opacity - // eslint-disable-next-line react/forbid-prop-types - animatedOpacity: PropTypes.object.isRequired, -}; - -const UnreadActionIndicator = props => ( - +const UnreadActionIndicator = () => ( + NEW - + ); -UnreadActionIndicator.propTypes = propTypes; UnreadActionIndicator.displayName = 'UnreadActionIndicator'; export default UnreadActionIndicator; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index c40efa8d516d..0a6863134d11 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -16,6 +16,7 @@ import PopoverWithMeasuredContent from '../../../components/PopoverWithMeasuredC import ReportActionItemSingle from './ReportActionItemSingle'; import ReportActionItemGrouped from './ReportActionItemGrouped'; import ReportActionContextMenu from './ReportActionContextMenu'; +import UnreadActionIndicator from '../../../components/UnreadActionIndicator'; const propTypes = { // The ID of the report this action is on. @@ -27,6 +28,9 @@ const propTypes = { // Should the comment have the appearance of being grouped with the previous comment? displayAsGroup: PropTypes.bool.isRequired, + // Should we display the new indicator on top of the comment? + displayNewIndicator: PropTypes.bool.isRequired, + /* --- Onyx Props --- */ // List of betas for the current user. betas: PropTypes.arrayOf(PropTypes.string), @@ -58,6 +62,7 @@ class ReportActionItem extends Component { shouldComponentUpdate(nextProps, nextState) { return this.state.isPopoverVisible !== nextState.isPopoverVisible || this.props.displayAsGroup !== nextProps.displayAsGroup + || (this.props.displayNewIndicator !== nextProps.displayNewIndicator) || !_.isEqual(this.props.action, nextProps.action); } @@ -109,6 +114,7 @@ class ReportActionItem extends Component { {hovered => ( + {this.props.displayNewIndicator && } {!this.props.displayAsGroup ? diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 708130ac689f..8daefa4808fe 100644 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -1,6 +1,5 @@ import React from 'react'; import { - Animated, View, Keyboard, AppState, @@ -11,7 +10,6 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; import Text from '../../../components/Text'; -import UnreadActionIndicator from '../../../components/UnreadActionIndicator'; import { fetchActions, updateLastReadActionID, @@ -69,16 +67,10 @@ class ReportActionsView extends React.Component { this.recordMaxAction = this.recordMaxAction.bind(this); this.onVisibilityChange = this.onVisibilityChange.bind(this); this.loadMoreChats = this.loadMoreChats.bind(this); + this.scheduledRecordMaxAction = this.scheduledRecordMaxAction.bind(this); this.sortedReportActions = []; - this.timers = []; - this.unreadIndicatorOpacity = new Animated.Value(1); - - // Helper variable that keeps track of the unread action count before it updates to zero - this.unreadActionCount = 0; - - // Helper variable that prevents the unread indicator to show up for new messages - // received while the report is still active - this.shouldShowUnreadActionIndicator = true; + this.unreadTimer = null; + this.newMessageMarkerPosition = -1; this.state = { isLoadingMoreChats: false, @@ -127,10 +119,9 @@ class ReportActionsView extends React.Component { this.scrollToListBottom(); } - // When the last action changes, wait three seconds, then record the max action - // This will make the unread indicator go away if you receive comments in the same chat you're looking at - if (Visibility.isVisible()) { - this.timers.push(setTimeout(this.recordMaxAction, 3000)); + // If we are adding a new action while the chat is open, record the max action immediately + if (!this.unreadTimer && Visibility.isVisible()) { + this.scheduledRecordMaxAction(); } } } @@ -142,7 +133,10 @@ class ReportActionsView extends React.Component { AppState.removeEventListener('change', this.onVisibilityChange); - _.each(this.timers, timer => clearTimeout(timer)); + if (this.unreadTimer) { + clearTimeout(this.unreadTimer); + this.unreadTimer = null; + } unsubscribeFromReportChannel(this.props.reportID); } @@ -151,33 +145,21 @@ class ReportActionsView extends React.Component { */ onVisibilityChange() { if (Visibility.isVisible()) { - this.timers.push(setTimeout(this.recordMaxAction, 3000)); + this.unreadTimer = setTimeout(this.scheduledRecordMaxAction, 3000); } } /** - * Checks if the unreadActionIndicator should be shown. - * If it does, starts a timeout for the fading out animation and creates - * a flag to not show it again if the report is still open + * Sets the max action after a set delay */ - setUpUnreadActionIndicator() { - if (!this.shouldShowUnreadActionIndicator) { - return; - } - - this.unreadActionCount = this.props.report.unreadActionCount; - - if (this.unreadActionCount > 0) { - this.unreadIndicatorOpacity = new Animated.Value(1); - this.timers.push(setTimeout(() => { - Animated.timing(this.unreadIndicatorOpacity, { - toValue: 0, - useNativeDriver: false, - }).start(); - }, 3000)); + scheduledRecordMaxAction() { + // Always cancel the existing timer + if (this.unreadTimer) { + clearTimeout(this.unreadTimer); + this.unreadTimer = null; } - this.shouldShowUnreadActionIndicator = false; + this.recordMaxAction(); } /** @@ -192,6 +174,12 @@ class ReportActionsView extends React.Component { // Fetch the new set of actions fetchActions(this.props.reportID); + this.newMessageMarkerPosition = -1; + + // Wait three seconds, then record the max action + if (Visibility.isVisible()) { + this.unreadTimer = setTimeout(this.scheduledRecordMaxAction, 3000); + } } /** @@ -335,15 +323,14 @@ class ReportActionsView extends React.Component { // are implemented on native and web/desktop which leads to // the unread indicator on native to render below the message instead of above it. - {this.unreadActionCount > 0 && index === this.unreadActionCount - 1 && ( - - )} 0 + && item.action.sequenceNumber === this.newMessageMarkerPosition} /> ); @@ -364,7 +351,12 @@ class ReportActionsView extends React.Component { ); } - this.setUpUnreadActionIndicator(); + if (this.newMessageMarkerPosition < 0) { + this.newMessageMarkerPosition = this.props.report.unreadActionCount === 0 + ? this.props.report.unreadActionCount + : _.size(this.props.reportActions) - this.props.report.unreadActionCount; + } + this.updateSortedReportActions(); return (