Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new marker Badge for Report unread messages #4603

Merged
merged 9 commits into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from 7 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
3 changes: 3 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ export default {
reportActionsView: {
beFirstPersonToComment: 'Be the first person to comment',
},
reportActionsViewMarkerBadge: {
newMsg: ({count}) => `${count} new messages`,
Beamanator marked this conversation as resolved.
Show resolved Hide resolved
},
reportTypingIndicator: {
isTyping: 'is typing...',
areTyping: 'are typing...',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ export default {
reportActionsView: {
beFirstPersonToComment: 'Sé el primero en comentar',
},
reportActionsViewMarkerBadge: {
newMsg: ({count}) => `${count} nuevos mensajes`,
},
reportTypingIndicator: {
isTyping: 'está escribiendo...',
areTyping: 'están escribiendo...',
Expand Down
5 changes: 4 additions & 1 deletion src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ class ReportScreen extends React.Component {
onNavigationMenuButtonClicked={() => Navigation.navigate(ROUTES.HOME)}
/>

<View nativeID={CONST.REPORT.DROP_NATIVE_ID} style={[styles.flex1, styles.justifyContentEnd]}>
<View
nativeID={CONST.REPORT.DROP_NATIVE_ID}
style={[styles.flex1, styles.justifyContentEnd, styles.overflowHidden]}
Beamanator marked this conversation as resolved.
Show resolved Hide resolved
>
<FullScreenLoadingIndicator visible={this.shouldShowLoader()} />
{!this.shouldShowLoader() && <ReportActionsView reportID={reportID} />}
{this.props.session.shouldShowComposeInput && (
Expand Down
125 changes: 125 additions & 0 deletions src/pages/home/report/MarkerBadge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React, {PureComponent} from 'react';
import {Animated, Text, View} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../../../styles/styles';
import Button from '../../../components/Button';
import Icon from '../../../components/Icon';
import {Close, DownArrow} from '../../../components/Icon/Expensicons';
import themeColors from '../../../styles/themes/default';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';

const MARKER_NOT_ACTIVE_TRANSLATE_Y = -30;
const MARKER_ACTIVE_TRANSLATE_Y = 10;
const propTypes = {
/** Count of messages to show in the badge */
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
count: PropTypes.number,

/** whether the marker is active */
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
active: PropTypes.bool,

/** Callback to be called when user click the badge */
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
onClose: PropTypes.func,

/** Callback to be called when user close the marker */
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
onClick: PropTypes.func,

...withLocalizePropTypes,
};
const defaultProps = {
count: 0,
active: false,
onClose: () => {},
onClick: () => {},
};
class Markerbadge extends PureComponent {
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
constructor(props) {
super(props);
this.translateY = new Animated.Value(MARKER_NOT_ACTIVE_TRANSLATE_Y);
this.show = this.show.bind(this);
this.hide = this.hide.bind(this);
}

componentDidUpdate() {
if (this.props.active && this.props.count > 0) {
this.show();
} else {
this.hide();
}
}

show() {
Animated.spring(this.translateY, {
toValue: MARKER_ACTIVE_TRANSLATE_Y,
duration: 80,
useNativeDriver: true,
}).start();
}

hide() {
Animated.spring(this.translateY, {
toValue: MARKER_NOT_ACTIVE_TRANSLATE_Y,
duration: 80,
useNativeDriver: true,
}).start();
}

render() {
return (
<View style={styles.reportMarkerBadgeWrapper}>
<Animated.View style={[
styles.reportMarkerBadge,
styles.reportMarkerBadgeTransformation(this.translateY),
]}
>
<View style={[
styles.flexRow,
styles.justifyContentBetween,
styles.alignItemsCenter,
]}
>
<Button
success
small
onPress={this.props.onClick}
ContentComponent={() => (
<View style={[styles.flexRow]}>
<Icon small src={DownArrow} fill={themeColors.textReversed} />
<Text
selectable={false}
style={[
styles.ml2,
styles.buttonSmallText,
styles.textWhite,
]}
>
{this.props.translate(
'reportActionsViewMarkerBadge.newMsg',
{count: this.props.count},
)}
</Text>
</View>
)}
shouldRemoveRightBorderRadius
/>
<Button
success
small
style={[styles.buttonDropdown]}
onPress={this.props.onClose}
shouldRemoveLeftBorderRadius
ContentComponent={() => (
<Icon small src={Close} fill={themeColors.textReversed} />
)}
/>
</View>
</Animated.View>
</View>
);
}
}

Markerbadge.propTypes = propTypes;
Markerbadge.defaultProps = defaultProps;
Markerbadge.displayName = 'Markerbadge';

export default withLocalize(Markerbadge);
56 changes: 56 additions & 0 deletions src/pages/home/report/ReportActionsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFo
import {contextMenuRef} from './ContextMenu/ReportActionContextMenu';
import PopoverReportActionContextMenu from './ContextMenu/PopoverReportActionContextMenu';
import variables from '../../../styles/variables';
import MarkerBadge from './MarkerBadge';

const propTypes = {
/** The ID of the report actions will be created for */
Expand Down Expand Up @@ -99,11 +100,16 @@ class ReportActionsView extends React.Component {

this.state = {
isLoadingMoreChats: false,
isMarkerActive: false,
};

this.currentScrollOffset = 0;
this.updateSortedReportActions(props.reportActions);
this.updateMostRecentIOUReportActionNumber(props.reportActions);
this.keyExtractor = this.keyExtractor.bind(this);
this.trackScroll = this.trackScroll.bind(this);
this.showMarker = this.showMarker.bind(this);
this.hideMarker = this.hideMarker.bind(this);
}

componentDidMount() {
Expand Down Expand Up @@ -152,6 +158,10 @@ class ReportActionsView extends React.Component {
return true;
}

if (nextState.isMarkerActive !== this.state.isMarkerActive) {
return true;
}

if (this.props.isSmallScreenWidth !== nextProps.isSmallScreenWidth) {
return true;
}
Expand Down Expand Up @@ -189,6 +199,9 @@ class ReportActionsView extends React.Component {
if (shouldRecordMaxAction) {
updateLastReadActionID(this.props.reportID);
}

// show new Marker badge when there is a new Message
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
this.showMarker();
}

// We want to mark the unread comments when user resize the screen to desktop
Expand Down Expand Up @@ -346,6 +359,42 @@ class ReportActionsView extends React.Component {
updateLastReadActionID(this.props.reportID);
}

/**
* Show the new Markerbadge when user is looking at the History of messages
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
*
* @memberof ReportActionsView
Beamanator marked this conversation as resolved.
Show resolved Hide resolved
*/
showMarker() {
if (this.currentScrollOffset < -200 && !this.state.isMarkerActive) {
this.setState({isMarkerActive: true});
}

if (this.currentScrollOffset > -200 && this.state.isMarkerActive) {
this.setState({isMarkerActive: false});
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
* Hide the new MarkerBadge
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
*
* @memberof ReportActionsView
*/
hideMarker() {
this.setState({isMarkerActive: false});
}

/**
* keeps track of the Scroll offset of the main messages list
*
* @param {*} {nativeEvent}
* @memberof ReportActionsView
*/
trackScroll({nativeEvent}) {
this.currentScrollOffset = -nativeEvent.contentOffset.y;
this.showMarker();
Beamanator marked this conversation as resolved.
Show resolved Hide resolved
}


parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
/**
* Runs when the FlatList finishes laying out
*/
Expand Down Expand Up @@ -427,6 +476,12 @@ class ReportActionsView extends React.Component {

return (
<>
<MarkerBadge
active={this.state.isMarkerActive}
count={this.props.report.unreadActionCount}
onClick={scrollToBottom}
onClose={this.hideMarker}
/>
<InvertedFlatList
ref={flatListRef}
data={this.sortedReportActions}
Expand All @@ -443,6 +498,7 @@ class ReportActionsView extends React.Component {
: null}
keyboardShouldPersistTaps="handled"
onLayout={this.recordTimeToMeasureItemLayout}
onScroll={this.trackScroll}
/>
<PopoverReportActionContextMenu ref={contextMenuRef} />
</>
Expand Down
25 changes: 24 additions & 1 deletion src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import textInputAlignSelf from './utilities/textInputAlignSelf';
import CONST from '../CONST';
import positioning from './utilities/positioning';
import codeStyles from './codeStyles';
import visibility from './utilities/visibility';

const expensiPicker = {
backgroundColor: 'transparent',
Expand Down Expand Up @@ -249,7 +250,8 @@ const styles = {
},

buttonDropdown: {
marginLeft: 1,
borderLeftWidth: 1,
borderColor: themeColors.textReversed,
Beamanator marked this conversation as resolved.
Show resolved Hide resolved
},

noRightBorderRadius: {
Expand Down Expand Up @@ -1984,6 +1986,27 @@ const styles = {
communicationsLinkHeight: {
height: 20,
},

reportMarkerBadgeWrapper: {
position: 'absolute',
left: '50%',
top: 0,
zIndex: 100,
backfaceVisibility: 'hidden',
parasharrajat marked this conversation as resolved.
Show resolved Hide resolved
...visibility('hidden'),
},

reportMarkerBadge: {
backfaceVisibility: 'visible',
left: '-50%',
...visibility('visible'),
},

reportMarkerBadgeTransformation: translateY => ({
transform: [
{translateY},
],
}),
};

const baseCodeTagStyles = {
Expand Down
1 change: 1 addition & 0 deletions src/styles/utilities/visibility/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default visibility => ({visibility});
1 change: 1 addition & 0 deletions src/styles/utilities/visibility/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => ({});