Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 5 additions & 18 deletions src/components/UnreadActionIndicator.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,16 @@
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 => (
<Animated.View style={[
styles.unreadIndicatorContainer,
getOpacityStyle(props.animatedOpacity),
]}
>
const UnreadActionIndicator = () => (
<View style={styles.unreadIndicatorContainer}>
<View style={styles.unreadIndicatorLine} />
<Text style={styles.unreadIndicatorText}>
NEW
</Text>
</Animated.View>
</View>
);

UnreadActionIndicator.propTypes = propTypes;
UnreadActionIndicator.displayName = 'UnreadActionIndicator';

export default UnreadActionIndicator;
8 changes: 8 additions & 0 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,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.
Expand All @@ -23,6 +24,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?
shouldDisplayNewIndicator: PropTypes.bool.isRequired,
};

class ReportActionItem extends Component {
Expand All @@ -46,6 +50,7 @@ class ReportActionItem extends Component {
shouldComponentUpdate(nextProps, nextState) {
return this.state.isPopoverVisible !== nextState.isPopoverVisible
|| this.props.displayAsGroup !== nextProps.displayAsGroup
|| (this.props.shouldDisplayNewIndicator !== nextProps.shouldDisplayNewIndicator)
|| !_.isEqual(this.props.action, nextProps.action);
}

Expand Down Expand Up @@ -85,6 +90,9 @@ class ReportActionItem extends Component {
<Hoverable>
{hovered => (
<View>
{!hovered && this.props.shouldDisplayNewIndicator && (
<UnreadActionIndicator />
)}
<View style={getReportActionItemStyle(hovered)}>
{!this.props.displayAsGroup
? <ReportActionItemSingle action={this.props.action} />
Expand Down
64 changes: 17 additions & 47 deletions src/pages/home/report/ReportActionsView.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import {
Animated,
View,
Keyboard,
AppState,
Expand All @@ -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,
Expand Down Expand Up @@ -39,6 +37,9 @@ const propTypes = {
report: PropTypes.shape({
// Number of actions unread
unreadActionCount: PropTypes.number,

// The largest sequenceNumber on this report
maxSequenceNumber: PropTypes.number,
}),

// Array of report actions for this report
Expand All @@ -54,6 +55,7 @@ const propTypes = {
const defaultProps = {
report: {
unreadActionCount: 0,
maxSequenceNumber: 0,
},
reportActions: {},
session: {},
Expand All @@ -71,10 +73,10 @@ class ReportActionsView extends React.Component {
this.loadMoreChats = this.loadMoreChats.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;
this.initialNewMarkerPosition = props.report.unreadActionCount === 0
? 0
: (props.report.maxSequenceNumber + 1) - props.report.unreadActionCount;

this.state = {
isLoadingMoreChats: false,
Expand All @@ -89,7 +91,6 @@ class ReportActionsView extends React.Component {
this.keyboardEvent = Keyboard.addListener('keyboardDidShow', this.scrollToListBottom);
this.recordMaxAction();
fetchActions(this.props.reportID);
this.setUpUnreadActionIndicator();
Timing.end(CONST.TIMING.SWITCH_REPORT, CONST.TIMING.COLD);
}

Expand Down Expand Up @@ -146,25 +147,6 @@ class ReportActionsView extends React.Component {
}
}

/**
* 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
*/
setUpUnreadActionIndicator() {
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));
}
}

/**
* Retrieves the next set of report actions for the chat once we are nearing the end of what we are currently
* displaying.
Expand Down Expand Up @@ -232,8 +214,7 @@ class ReportActionsView extends React.Component {
}

/**
* When the bottom of the list is reached, this is triggered, so it's a little different than recording the max
* action when scrolled
* Recorded when the report first opens and when the list is scrolled to the bottom
*/
recordMaxAction() {
const reportActions = lodashGet(this.props, 'reportActions', {});
Expand Down Expand Up @@ -265,8 +246,8 @@ class ReportActionsView extends React.Component {

/**
* This function overrides the CellRendererComponent (defaults to a plain View), giving each ReportActionItem a
* higher z-index than the one below it. This prevents issues where the ReportActionContextMenu overlapping between
* rows is hidden beneath other rows.
* higher z-index than the one below it. This prevents issues where the ReportActionContextMenu overlapping between
* rows is hidden beneath other rows.
*
* @param {Object} index - The ReportAction item in the FlatList.
* @param {Object|Array} style – The default styles of the CellRendererComponent provided by the CellRenderer.
Expand All @@ -291,31 +272,21 @@ class ReportActionsView extends React.Component {
* @param {Object} args
* @param {Object} args.item
* @param {Number} args.index
* @param {Function} args.onLayout
*
* @returns {React.Component}
*/
renderItem({
item,
index,
onLayout,
}) {
return (

// Using <View /> instead of a Fragment because there is a difference between how
// <InvertedFlatList /> are implemented on native and web/desktop which leads to
// the unread indicator on native to render below the message instead of above it.
<View>
{this.unreadActionCount > 0 && index === this.unreadActionCount - 1 && (
<UnreadActionIndicator animatedOpacity={this.unreadIndicatorOpacity} />
)}
<ReportActionItem
reportID={this.props.reportID}
action={item.action}
displayAsGroup={this.isConsecutiveActionMadeByPreviousActor(index)}
onLayout={onLayout}
/>
</View>
<ReportActionItem
reportID={this.props.reportID}
action={item.action}
displayAsGroup={this.isConsecutiveActionMadeByPreviousActor(index)}
shouldDisplayNewIndicator={this.initialNewMarkerPosition > 0
&& item.action.sequenceNumber === this.initialNewMarkerPosition}
/>
);
}

Expand All @@ -334,7 +305,6 @@ class ReportActionsView extends React.Component {
);
}

this.setUpUnreadActionIndicator();
return (
<InvertedFlatList
ref={el => this.actionListElement = el}
Expand Down
7 changes: 6 additions & 1 deletion src/styles/getReportActionItemStyles.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import colors from './colors';
import themeColors from './themes/default';
import positioning from './utilities/positioning';

Expand All @@ -11,7 +12,11 @@ export function getReportActionItemStyle(isHovered = false) {
return {
display: 'flex',
justifyContent: 'space-between',
backgroundColor: isHovered ? themeColors.hoverComponentBG : themeColors.componentBG,
backgroundColor: isHovered
? themeColors.hoverComponentBG

// Warning: Setting this to a non-transparent color will cause unread indicator to break on Android
: colors.transparent,
cursor: 'default',
};
}
Expand Down
18 changes: 1 addition & 17 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -828,14 +828,6 @@ const styles = {
marginRight: 4,
},

navigationMenuOpenAbsolute: {
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
zIndex: 2,
},

navigationModalOverlay: {
position: 'absolute',
width: '100%',
Expand Down Expand Up @@ -1151,6 +1143,7 @@ const styles = {
paddingHorizontal: 20,
flexDirection: 'row',
alignItems: 'center',
zIndex: 1,
},

unreadIndicatorLine: {
Expand Down Expand Up @@ -1518,14 +1511,6 @@ function getWidthAndHeightStyle(width, height) {
};
}

/**
* @param {Number} opacity
* @returns {Object}
*/
function getOpacityStyle(opacity) {
return {opacity};
}

/**
* @param {Object} params
* @returns {Object}
Expand Down Expand Up @@ -1564,6 +1549,5 @@ export {
getIconFillColor,
getAnimatedFABStyle,
getWidthAndHeightStyle,
getOpacityStyle,
getModalPaddingStyles,
};