Skip to content

Commit

Permalink
Merge pull request #9720 from Expensify/OSBotify-cherry-pick-staging-…
Browse files Browse the repository at this point in the history
…9350

🍒 Cherry pick PR #9350 to staging 🍒
  • Loading branch information
OSBotify authored Jul 5, 2022
2 parents 6a178bb + ea795fa commit c272b4c
Show file tree
Hide file tree
Showing 20 changed files with 167 additions and 141 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001017915
versionName "1.1.79-15"
versionCode 1001017916
versionName "1.1.79-16"
}
splits {
abi {
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.1.79.15</string>
<string>1.1.79.16</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.1.79.15</string>
<string>1.1.79.16</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.1.79-15",
"version": "1.1.79-16",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down
6 changes: 6 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,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;
35 changes: 27 additions & 8 deletions src/libs/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,21 +115,38 @@ function startCurrentDateUpdater() {
});
}

/*
* Updates user's timezone, if their timezone is set to automatic and
* is different from current timezone
/**
* @returns {Object}
*/
function updateTimezone() {
function getCurrentTimezone() {
const currentTimezone = moment.tz.guess(true);
if (timezone.automatic && timezone.selected !== currentTimezone) {
PersonalDetails.setPersonalDetails({timezone: {...timezone, selected: currentTimezone}});
return {...timezone, selected: currentTimezone};
}
return timezone;
}

/*
* Returns a version of updateTimezone function throttled by 5 minutes
* Updates user's timezone, if their timezone is set to automatic and
* is different from current timezone
*/
function updateTimezone() {
PersonalDetails.setPersonalDetails({timezone: getCurrentTimezone()});
}

// Used to throttle updates to the timezone when necessary
let lastUpdatedTimezoneTime = moment();

/**
* @returns {Boolean}
*/
const throttledUpdateTimezone = _.throttle(() => updateTimezone(), 1000 * 60 * 5);
function canUpdateTimezone() {
return lastUpdatedTimezoneTime.isBefore(moment().subtract(5, 'minutes'));
}

function setTimezoneUpdated() {
lastUpdatedTimezoneTime = moment();
}

/**
* @namespace DateUtils
Expand All @@ -139,8 +156,10 @@ const DateUtils = {
timestampToDateTime,
startCurrentDateUpdater,
updateTimezone,
throttledUpdateTimezone,
getLocalMomentFromTimestamp,
getCurrentTimezone,
canUpdateTimezone,
setTimezoneUpdated,
};

export default DateUtils;
155 changes: 64 additions & 91 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,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 DateUtils from '../DateUtils';

let currentUserEmail;
let currentUserAccountID;
Expand Down Expand Up @@ -532,54 +532,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 messageHtml = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED
? lodashGet(reportAction, 'originalMessage.html', '')
: lodashGet(reportAction, ['message', 0, 'html'], '');

const parser = new ExpensiMark();
const messageText = parser.htmlToText(messageHtml);

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);
}

/**
* Get the private pusher channel name for a Report.
*
Expand All @@ -604,13 +556,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 an 'edit comment' event is received.
PusherUtils.subscribeToPrivateUserChannelEvent(Pusher.TYPE.REPORT_COMMENT_EDIT,
currentUserAccountID,
Expand All @@ -621,9 +566,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 @@ -901,7 +846,7 @@ function fetchAllReports(
* @param {String} text
* @param {File} [file]
*/
function addAction(reportID, text, file) {
function addComment(reportID, text, file) {
// For comments shorter than 10k chars, convert the comment from MD into HTML because that's how it is stored in the database
// For longer comments, skip parsing and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!!
const parser = new ExpensiMark();
Expand All @@ -919,12 +864,12 @@ function addAction(reportID, text, file) {
: parser.htmlToText(htmlForNewComment);

// 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 @@ -940,12 +885,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 @@ -974,41 +917,70 @@ function addAction(reportID, text, file) {
isFirstItem: false,
isAttachment,
attachmentInfo,
loading: true,
isLoading: 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;
}
const 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,
},
];

updateReportWithNewAction(reportID, response.reportAction);
// Update the timezone if it's been 5 minutes from the last time the user added a comment
if (DateUtils.canUpdateTimezone()) {
const timezone = DateUtils.getCurrentTimezone();
parameters.timezone = JSON.stringify(timezone);
optimisticData.push({
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: ONYXKEYS.MY_PERSONAL_DETAILS,
value: {timezone},
});
optimisticData.push({
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: ONYXKEYS.PERSONAL_DETAILS,
value: {[currentUserEmail]: timezone},
});
DateUtils.setTimezoneUpdated();
}

API.write(isAttachment ? 'AddAttachment' : 'AddComment', parameters, {
optimisticData,
failureData: [
{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[optimisticReportActionID]: {
isLoading: false,
},
},
},
],
});
}

/**
* @param {Number} reportID
* @param {Object} file
*/
function addAttachment(reportID, file) {
addComment(reportID, '', file);
}

/**
Expand Down Expand Up @@ -1522,7 +1494,8 @@ export {
fetchChatReportsByIDs,
fetchIOUReportByID,
fetchIOUReportByIDAndUpdateChatReport,
addAction,
addComment,
addAttachment,
updateLastReadActionID,
updateNotificationPreference,
setNewMarkerPosition,
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 @@ -34,7 +34,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.isLoading);
const mostRecentAction = actionsArray[mostRecentNonLoadingActionIndex];
if (!mostRecentAction || _.isUndefined(mostRecentAction.sequenceNumber)) {
return;
Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class ReportScreen extends React.Component {
* @param {String} text
*/
onSubmitComment(text) {
Report.addAction(getReportID(this.props.route), text);
Report.addComment(getReportID(this.props.route), text);
}

/**
Expand Down
Loading

0 comments on commit c272b4c

Please sign in to comment.