Skip to content

Replace Report_AddComment with AddComment (Part 2) #9350

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

Merged
merged 21 commits into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from 9 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
6 changes: 6 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,12 @@ const CONST = {

// There's a limit of 60k characters in Auth - https://github.com/Expensify/Auth/blob/198d59547f71fdee8121325e8bc9241fc9c3236a/auth/lib/Request.h#L28
MAX_COMMENT_LENGTH: 60000,

ONYX: {
METHOD: {
MERGE: 'merge',
},
},
};

export default CONST;
7 changes: 6 additions & 1 deletion src/components/InlineSystemMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import Icon from './Icon';

const propTypes = {
/** Error to display */
message: PropTypes.string.isRequired,
message: PropTypes.string,
};

const defaultProps = {
message: '',
};

const InlineSystemMessage = (props) => {
Expand All @@ -25,5 +29,6 @@ const InlineSystemMessage = (props) => {
};

InlineSystemMessage.propTypes = propTypes;
InlineSystemMessage.defaultProps = defaultProps;
InlineSystemMessage.displayName = 'InlineSystemMessage';
export default InlineSystemMessage;
132 changes: 36 additions & 96 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import * as Pusher from '../Pusher/pusher';
import LocalNotification from '../Notification/LocalNotification';
import PushNotification from '../Notification/PushNotification';
import * as PersonalDetails from './PersonalDetails';
import * as User from './User';
import Navigation from '../Navigation/Navigation';
import * as ActiveClientManager from '../ActiveClientManager';
import Visibility from '../Visibility';
Expand All @@ -27,6 +26,7 @@ import * as ReportActions from './ReportActions';
import Growl from '../Growl';
import * as Localize from '../Localize';
import PusherUtils from '../PusherUtils';
import * as API from '../API';

let currentUserEmail;
let currentUserAccountID;
Expand Down Expand Up @@ -536,51 +536,6 @@ function updateReportActionMessage(reportID, sequenceNumber, message) {
});
}

/**
* Updates a report in the store with a new report action
*
* @param {Number} reportID
* @param {Object} reportAction
* @param {String} [notificationPreference] On what cadence the user would like to be notified
*/
function updateReportWithNewAction(
reportID,
reportAction,
notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
) {
const messageText = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED
? lodashGet(reportAction, 'originalMessage.html', '')
: lodashGet(reportAction, ['message', 0, 'text'], '');

const updatedReportObject = {
// Always merge the reportID into Onyx. If the report doesn't exist in Onyx yet, then all the rest of the data will be filled out by handleReportChanged
reportID,
maxSequenceNumber: reportAction.sequenceNumber,
notificationPreference,
lastMessageTimestamp: reportAction.timestamp,
lastMessageText: ReportUtils.formatReportLastMessageText(messageText),
lastActorEmail: reportAction.actorEmail,
};

Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, updatedReportObject);

const reportActionsToMerge = {};
if (reportAction.clientID) {
// Remove the optimistic action from the report since we are about to replace it
// with the real one (which has the true sequenceNumber)
reportActionsToMerge[reportAction.clientID] = null;
}

// Add the action into Onyx
reportActionsToMerge[reportAction.sequenceNumber] = {
...reportAction,
isAttachment: ReportUtils.isReportMessageAttachment(lodashGet(reportAction, ['message', 0], {})),
loading: false,
};

Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, reportActionsToMerge);
}

/**
* Updates a report in Onyx with a new pinned state.
*
Expand Down Expand Up @@ -615,21 +570,6 @@ function subscribeToUserEvents() {
return;
}

// Live-update a report's actions when a 'report comment' event is received.
PusherUtils.subscribeToPrivateUserChannelEvent(
Pusher.TYPE.REPORT_COMMENT,
currentUserAccountID,
pushJSON => updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction, pushJSON.notificationPreference),
);

// Live-update a report's actions when a 'chunked report comment' event is received.
PusherUtils.subscribeToPrivateUserChannelEvent(
Pusher.TYPE.REPORT_COMMENT_CHUNK,
currentUserAccountID,
pushJSON => updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction, pushJSON.notificationPreference),
true,
);

// Live-update a report's actions when an 'edit comment' event is received.
PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT_EDIT,
currentUserAccountID,
Expand All @@ -653,9 +593,9 @@ function subscribeToUserEvents() {
* Setup reportComment push notification callbacks.
*/
function subscribeToReportCommentPushNotifications() {
PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportAction}) => {
PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, onyxData}) => {
Log.info('[Report] Handled event sent by Airship', false, {reportID});
updateReportWithNewAction(reportID, reportAction);
Onyx.update(onyxData);
});

// Open correct report when push notification is clicked
Expand Down Expand Up @@ -951,12 +891,12 @@ function addAction(reportID, text, file) {
: htmlForNewComment.replace(/((<br[^>]*>)+)/gi, ' ').replace(/<[^>]*>?/gm, '');

// Update the report in Onyx to have the new sequence number
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {
const optimisticReport = {
maxSequenceNumber: newSequenceNumber,
lastMessageTimestamp: moment().unix(),
lastMessageText: ReportUtils.formatReportLastMessageText(textForNewComment),
lastActorEmail: currentUserEmail,
});
};

// Generate a clientID so we can save the optimistic action to storage with the clientID as key. Later, we will
// remove the optimistic action when we add the real action created in the server. We do this because it's not
Expand All @@ -972,12 +912,10 @@ function addAction(reportID, text, file) {
// Store the optimistic action ID on the report the comment was added to. It will be removed later when refetching
// report actions in order to clear out any stuck actions (i.e. actions where the client never received a Pusher
// event, for whatever reason, from the server with the new action data
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {
optimisticReportActionIDs: [...(optimisticReportActionIDs[reportID] || []), optimisticReportActionID],
});
optimisticReport.optimisticReportActionIDs = [...(optimisticReportActionIDs[reportID] || []), optimisticReportActionID];

// Optimistically add the new comment to the store before waiting to save it to the server
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {
const optimisticReportActions = {
[optimisticReportActionID]: {
actionName: CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT,
actorEmail: currentUserEmail,
Expand Down Expand Up @@ -1006,41 +944,43 @@ function addAction(reportID, text, file) {
isFirstItem: false,
isAttachment,
attachmentInfo,
loading: true,
isPending: true,
shouldShow: true,
},
});
};

DeprecatedAPI.Report_AddComment({
const parameters = {
reportID,
file,
reportComment: commentText,
clientID: optimisticReportActionID,
persist: true,
})
.then((response) => {
if (response.jsonCode === 408) {
Growl.error(Localize.translateLocal('reportActionCompose.fileUploadFailed'));
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {
[optimisticReportActionID]: null,
});
console.error(response.message);
return;
}

if (response.jsonCode === 666 && reportID === conciergeChatReportID) {
Growl.error(Localize.translateLocal('reportActionCompose.blockedFromConcierge'));
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {
[optimisticReportActionID]: null,
});

// The fact that the API is returning this error means the BLOCKED_FROM_CONCIERGE nvp in the user details has changed since the last time we checked, so let's update
User.getUserDetails();
return;
}
};

updateReportWithNewAction(reportID, response.reportAction);
});
API.write('AddComment', parameters, {
optimisticData: [
{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: optimisticReport,
},
{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: optimisticReportActions,
},
],
failureData: [
{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActionID]: {
isPending: false,
},
},
},
],
});
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/ReportActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Onyx.connect({
const reportID = CollectionUtils.extractCollectionItemID(key);
const actionsArray = _.toArray(actions);
reportActions[reportID] = actionsArray;
const mostRecentNonLoadingActionIndex = _.findLastIndex(actionsArray, action => !action.loading);
const mostRecentNonLoadingActionIndex = _.findLastIndex(actionsArray, action => !action.isPending);
const mostRecentAction = actionsArray[mostRecentNonLoadingActionIndex];
if (!mostRecentAction || _.isUndefined(mostRecentAction.sequenceNumber)) {
return;
Expand Down
10 changes: 8 additions & 2 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMenu';
import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu';
import * as ContextMenuActions from './ContextMenu/ContextMenuActions';
import {withReportActionsDrafts} from '../../../components/OnyxProvider';
import {withNetwork, withReportActionsDrafts} from '../../../components/OnyxProvider';
import RenameAction from '../../../components/ReportActionItem/RenameAction';
import InlineSystemMessage from '../../../components/InlineSystemMessage';
import styles from '../../../styles/styles';

const propTypes = {
/** The ID of the report this action is on. */
Expand Down Expand Up @@ -173,7 +175,7 @@ class ReportActionItem extends Component {
hovered
|| this.state.isContextMenuActive
|| this.props.draftMessage,
this.props.action.isPending || this.props.action.error,
(this.props.network.isOffline && this.props.action.isPending) || this.props.action.error,
)}
>
{!this.props.displayAsGroup
Expand Down Expand Up @@ -202,6 +204,9 @@ class ReportActionItem extends Component {
</View>
)}
</Hoverable>
<View style={styles.reportActionSystemMessageContainer}>
<InlineSystemMessage message={this.props.action.error} />
</View>
</PressableWithSecondaryInteraction>
);
}
Expand All @@ -211,6 +216,7 @@ ReportActionItem.defaultProps = defaultProps;

export default compose(
withWindowDimensions,
withNetwork(),
withReportActionsDrafts({
propName: 'draftMessage',
transformValue: (drafts, props) => {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/home/report/ReportActionItemMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const propTypes = {
};

const ReportActionItemMessage = (props) => {
const isUnsent = props.network.isOffline && props.action.loading;
const isUnsent = props.network.isOffline && props.action.isPending;

return (
<View style={[styles.chatItemMessage, isUnsent && styles.chatItemUnsentMessage]}>
Expand All @@ -32,7 +32,7 @@ const ReportActionItemMessage = (props) => {
fragment={fragment}
isAttachment={props.action.isAttachment}
attachmentInfo={props.action.attachmentInfo}
loading={props.action.loading}
loading={props.action.isPending}
/>
))}
</View>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/report/ReportActionItemSingle.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const ReportActionItemSingle = (props) => {
fragment={fragment}
tooltipText={props.action.actorEmail}
isAttachment={props.action.isAttachment}
isLoading={props.action.loading}
isLoading={props.action.isPending}
isSingleLine
/>
))}
Expand Down
3 changes: 1 addition & 2 deletions src/pages/home/report/reportActionPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ export default {
IOUTransactionID: PropTypes.string,
}),

/** If the reportAction is pending, that means we have not yet sent the Report_AddComment command to the server.
This should most often occur when the client is offline. */
/** Whether we have received a response back from the server */
isPending: PropTypes.bool,

/** Error message that's come back from the server. */
Expand Down
6 changes: 5 additions & 1 deletion src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,10 @@ const styles = {
...{borderRadius: variables.componentBorderRadiusSmall},
},

reportActionSystemMessageContainer: {
marginLeft: 42,
},

reportDetailsTitleContainer: {
...flex.dFlex,
...flex.flexColumn,
Expand Down Expand Up @@ -2530,7 +2534,7 @@ const styles = {
color: themeColors.textSupporting,
fontSize: variables.fontSizeLabel,
fontFamily: fontFamily.GTA,
marginLeft: 4,
marginLeft: 6,
},
};

Expand Down
2 changes: 1 addition & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ describe('actions/Report', () => {
// the comment we left and it will be in a loading state because
// it's an "optimistic comment"
expect(action.message[0].text).toBe('Hello!');
expect(action.loading).toBe(true);
expect(action.isPending).toBe(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be isLoading?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes good catch.

});
});
});
Expand Down
Loading