-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[HOLD] [WIP] Introduce "Mark as unread" functionality in Expensify.cash #1774
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
Changes from all commits
1cc91b4
5ef40a4
a02bcd2
1a5705b
74daa6d
dad6a80
36eae89
4076540
814f85e
a17f189
60aa19b
9506213
939c1ea
6cebb71
0c43eb9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,17 @@ | ||
| import React from 'react'; | ||
| import {Animated, View} from 'react-native'; | ||
| import PropTypes from 'prop-types'; | ||
| 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, | ||
| {opacity: 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; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -204,7 +204,7 @@ function setLocalLastRead(reportID, sequenceNumber) { | |
|
|
||
| // Update the report optimistically | ||
| Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { | ||
| unreadActionCount: 0, | ||
| unreadActionCount: Math.max(reportMaxSequenceNumbers[reportID] - sequenceNumber, 0), | ||
| lastVisitedTimestamp: Date.now(), | ||
| }); | ||
| } | ||
|
|
@@ -669,16 +669,18 @@ function addAction(reportID, text, file) { | |
| * | ||
| * @param {Number} reportID | ||
| * @param {Number} sequenceNumber | ||
| * @param {Boolean} ignoreOrder If set to true, we will not enforce the latest read action to be at the bottom of the | ||
| * chat. | ||
| */ | ||
| function updateLastReadActionID(reportID, sequenceNumber) { | ||
| function updateLastReadActionID(reportID, sequenceNumber, ignoreOrder = false) { | ||
| const currentMaxSequenceNumber = reportMaxSequenceNumbers[reportID]; | ||
| if (sequenceNumber < currentMaxSequenceNumber) { | ||
| if (!ignoreOrder && sequenceNumber < currentMaxSequenceNumber) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than add an
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? It only skips checking the order, though. It runs the rest of the function. Basically, it prevents the function from checking that the new "latest read" is at the end of the conversation. That check is relevant when new messages come in, but not when we are manually marking messages as unread.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm maybe we should remove that logic as well.. this logic is confusing and even with the added comment I'm having some trouble understanding in what other contexts this should be used.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I'm trying to think of a better suggestion for this...
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok so here is my confusion and would like to get @tgolen's thoughts to see if I'm missing something. I'm not sure I understand why we need to get the last action from the sorted actions here to mark everything as read... Here we are looking at the We are filtering out the "loading" actions, but that too could be done in the If that's true then we wouldn't need an function updateLastReadActionID(reportID, sequenceNumber) {
const currentMaxSequenceNumber = reportMaxSequenceNumbers[reportID];
const lastReadSequenceNumber = sequenceNumber
? sequenceNumber
: currentMaxSequenceNumber;
//... set the local last read
}Not sure if we need to do this change right now, but it seems more intuitive to "set to the most recent
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to dig into this a little so I'm going to pull the branch down and dive into it. I want to make sure I understand everything before offering up further comments on this thread. Thanks for being patient with me!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I love coming at it from this angle, and I agree it is much more intuitive.
I briefly looked at this, and I am pretty concerned that changing It makes a lot of sense to me to move this logic from the view into the action file: https://github.com/Expensify/Expensify.cash/blob/c5cb5076dc092ee5246e3410e4557fc2d55b4a88/src/pages/home/report/ReportActionsView.js#L266-L278
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm afraid I'm the one who is lost now.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the What I would like to see for a solution is to have and also: All the rest of the logic will then be encapsulated into the Does that help clarify what we are asking for? It doesn't spell out the changes needed in |
||
| return; | ||
| } | ||
|
|
||
| setLocalLastRead(reportID, sequenceNumber); | ||
|
|
||
| // Mark the report as not having any unread items | ||
| // Mark the unread items in the report | ||
| API.Report_UpdateLastRead({ | ||
| accountID: currentUserAccountID, | ||
| reportID, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,41 +7,6 @@ import { | |
| import getReportActionContextMenuStyles from '../../../styles/getReportActionContextMenuStyles'; | ||
| import ReportActionContextMenuItem from './ReportActionContextMenuItem'; | ||
|
|
||
| /** | ||
| * A list of all the context actions in this menu. | ||
| */ | ||
| const CONTEXT_ACTIONS = [ | ||
| // Copy to clipboard | ||
| { | ||
| text: 'Copy to Clipboard', | ||
| icon: Clipboard, | ||
| }, | ||
|
|
||
| // Copy chat link | ||
| { | ||
| text: 'Copy Link', | ||
| icon: LinkCopy, | ||
| }, | ||
|
|
||
| // Mark as Unread | ||
| { | ||
| text: 'Mark as Unread', | ||
| icon: Mail, | ||
| }, | ||
|
|
||
| // Edit Comment | ||
| { | ||
| text: 'Edit Comment', | ||
| icon: Pencil, | ||
| }, | ||
|
|
||
| // Delete Comment | ||
| { | ||
| text: 'Delete Comment', | ||
| icon: Trashcan, | ||
| }, | ||
| ]; | ||
|
|
||
| const propTypes = { | ||
| // The ID of the report this report action is attached to. | ||
| // eslint-disable-next-line react/no-unused-prop-types | ||
|
|
@@ -57,14 +22,57 @@ const propTypes = { | |
|
|
||
| // Controls the visibility of this component. | ||
| isVisible: PropTypes.bool, | ||
|
|
||
| // Function to trigger when we try to mark a message as unread | ||
| onMarkAsUnread: PropTypes.func, | ||
| }; | ||
|
|
||
| const defaultProps = { | ||
| isMini: false, | ||
| isVisible: false, | ||
| onMarkAsUnread: () => {}, | ||
| }; | ||
|
|
||
| const ReportActionContextMenu = (props) => { | ||
| /** | ||
| * A list of all the context actions in this menu. | ||
| */ | ||
| const CONTEXT_ACTIONS = [ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NAB, one small side effect of this change is that we are now recreating this object with each render. I don't think it will cause problems tho.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than recreating this object with every render, I think we should convert this to a class component, make
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or the |
||
| // Copy to clipboard | ||
| { | ||
| text: 'Copy to Clipboard', | ||
| icon: Clipboard, | ||
| onPress: () => {}, | ||
| }, | ||
|
|
||
| // Copy chat link | ||
| { | ||
| text: 'Copy Link', | ||
| icon: LinkCopy, | ||
| onPress: () => {}, | ||
| }, | ||
|
|
||
| // Mark as Unread | ||
| { | ||
| text: 'Mark as Unread', | ||
| icon: Mail, | ||
| onPress: props.onMarkAsUnread, | ||
| }, | ||
|
|
||
| // Edit Comment | ||
| { | ||
| text: 'Edit Comment', | ||
| icon: Pencil, | ||
| onPress: () => {}, | ||
| }, | ||
|
|
||
| // Delete Comment | ||
| { | ||
| text: 'Delete Comment', | ||
| icon: Trashcan, | ||
| onPress: () => {}, | ||
| }, | ||
| ]; | ||
| const wrapperStyle = getReportActionContextMenuStyles(props.isMini); | ||
| return props.isVisible && ( | ||
| <View style={wrapperStyle}> | ||
|
|
@@ -74,6 +82,7 @@ const ReportActionContextMenu = (props) => { | |
| text={contextAction.text} | ||
| isMini={props.isMini} | ||
| key={contextAction.text} | ||
| onPress={contextAction.onPress} | ||
| /> | ||
| ))} | ||
| </View> | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.