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

LHN: Hide the pencil icon when message is sent or draft is deleted and show the icon when user started to type #6050 #6084

Merged
merged 9 commits into from
Nov 3, 2021
1 change: 1 addition & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export default {
REPORT_USER_IS_TYPING: 'reportUserIsTyping_',
REPORT_IOUS: 'reportIOUs_',
POLICY: 'policy_',
REPORTS_WITH_DRAFT: 'reportWithDraft_',
},

// Indicates which locale should be used
Expand Down
28 changes: 19 additions & 9 deletions src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ Onyx.connect({
callback: val => preferredLocale = val || CONST.DEFAULT_LOCALE,
});

const reportsWithDraft = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORTS_WITH_DRAFT,
callback: (hasDraft, key) => {
if (key) {
reportsWithDraft[key] = hasDraft
}
},
});

const policies = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY,
Expand Down Expand Up @@ -188,16 +198,16 @@ function getSearchText(report, personalDetailList, isDefaultChatRoom) {
* @param {Boolean} forcePolicyNamePreview
* @returns {Object}
*/
function createOption(personalDetailList, report, draftComments, {
function createOption(personalDetailList, report, {
showChatPreviewLine = false, forcePolicyNamePreview = false,
}) {
const isDefaultChatRoom = isDefaultRoom(report);
const hasMultipleParticipants = personalDetailList.length > 1 || isDefaultChatRoom;
const personalDetail = personalDetailList[0];
const reportDraftComment = report
&& draftComments
&& lodashGet(draftComments, `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`, '');

const hasDraft = report
&& reportsWithDraft
&& lodashGet(reportsWithDraft, `${ONYXKEYS.COLLECTION.REPORTS_WITH_DRAFT}${report.reportID}`, false);
const hasOutstandingIOU = lodashGet(report, 'hasOutstandingIOU', false);
const iouReport = hasOutstandingIOU
? lodashGet(iouReports, `${ONYXKEYS.COLLECTION.REPORT_IOUS}${report.iouReportID}`, {})
Expand Down Expand Up @@ -243,7 +253,7 @@ function createOption(personalDetailList, report, draftComments, {
phoneNumber: !hasMultipleParticipants ? personalDetail.phoneNumber : null,
payPalMeAddress: !hasMultipleParticipants ? personalDetail.payPalMeAddress : null,
isUnread: report ? report.unreadActionCount > 0 : null,
hasDraftComment: _.size(reportDraftComment) > 0,
hasDraftComment: hasDraft,
keyForList: report ? String(report.reportID) : personalDetail.login,
searchText: getSearchText(report, personalDetailList, isDefaultChatRoom),
isPinned: lodashGet(report, 'isPinned', false),
Expand Down Expand Up @@ -398,14 +408,14 @@ function getOptions(reports, personalDetails, draftComments, activeReportID, {
if (logins.length <= 1) {
reportMapForLogins[logins[0]] = report;
}
allReportOptions.push(createOption(reportPersonalDetails, report, draftComments, {
allReportOptions.push(createOption(reportPersonalDetails, report, {
showChatPreviewLine,
forcePolicyNamePreview,
}));
});

const allPersonalDetailsOptions = _.map(personalDetails, personalDetail => (
createOption([personalDetail], reportMapForLogins[personalDetail.login], draftComments, {
createOption([personalDetail], reportMapForLogins[personalDetail.login], {
showChatPreviewLine,
forcePolicyNamePreview,
})
Expand Down Expand Up @@ -522,7 +532,7 @@ function getOptions(reports, personalDetails, draftComments, activeReportID, {
? `+${countryCodeByIP}${searchValue}`
: searchValue;
const userInvitePersonalDetails = getPersonalDetailsForLogins([login], personalDetails);
userToInvite = createOption(userInvitePersonalDetails, null, draftComments, {
userToInvite = createOption(userInvitePersonalDetails, null, {
showChatPreviewLine,
});
userToInvite.icons = [defaultAvatarForUserToInvite];
Expand Down
11 changes: 11 additions & 0 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,16 @@ function saveReportComment(reportID, comment) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, comment);
}

/**
* Immediate indication whether the report has a draft comment.
*
* @param {String} reportID
* @param {Boolean} hasDraft
*/
function setReportWithDraft(reportID, hasDraft) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORTS_WITH_DRAFT}${reportID}`, hasDraft);
}

/**
* Broadcasts whether or not a user is typing on a report over the report's private pusher channel.
*
Expand Down Expand Up @@ -1416,4 +1426,5 @@ export {
syncChatAndIOUReports,
navigateToConciergeChat,
handleInaccessibleReport,
setReportWithDraft,
};
2 changes: 2 additions & 0 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
saveReportComment,
saveReportActionDraft,
broadcastUserIsTyping,
setReportWithDraft,
} from '../../../libs/actions/Report';
import ReportTypingIndicator from './ReportTypingIndicator';
import AttachmentModal from '../../../components/AttachmentModal';
Expand Down Expand Up @@ -332,6 +333,7 @@ class ReportActionCompose extends React.Component {
isCommentEmpty: newComment.length === 0,
});
this.comment = newComment;
setReportWithDraft(this.props.reportID.toString(), newComment.length !== 0);
mountiny marked this conversation as resolved.
Show resolved Hide resolved
this.debouncedSaveReportComment(newComment);
if (newComment) {
this.debouncedBroadcastUserIsTyping();
Expand Down
112 changes: 57 additions & 55 deletions src/pages/home/sidebar/SidebarLinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {participantPropTypes} from './optionPropTypes';
import themeColors from '../../../styles/themes/default';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import * as App from '../../../libs/actions/App';
import lodashGet from 'lodash/get';

const propTypes = {
/** Toggles the navigation menu open and closed */
Expand All @@ -44,6 +45,9 @@ const propTypes = {
/** List of draft comments. We don't know the shape, since the keys include the report numbers */
draftComments: PropTypes.objectOf(PropTypes.string),

/** List of reports that have a draft comment. */
reportsWithDraft: PropTypes.objectOf(PropTypes.bool),

/** List of users' personal details */
personalDetails: PropTypes.objectOf(participantPropTypes),

Expand Down Expand Up @@ -83,6 +87,7 @@ const propTypes = {
const defaultProps = {
reports: {},
draftComments: {},
reportsWithDraft: {},
personalDetails: {},
myPersonalDetails: {
avatar: getDefaultAvatar(),
Expand All @@ -95,7 +100,46 @@ const defaultProps = {
};

class SidebarLinks extends React.Component {
shouldComponentUpdate(nextProps) {
constructor(props) {
super(props);
this.state = {
orderedReports: []
};
}

getRecentReports(props) {
const activeReportID = parseInt(props.currentlyViewedReportID, 10);
const {recentReports} = getSidebarOptions(
props.reports,
props.personalDetails,
props.draftComments,
activeReportID,
props.priorityMode,
props.betas,
);
return recentReports
}

componentDidMount() {
this.setState({
orderedReports: this.getRecentReports(this.props)
});
}

componentWillReceiveProps(nextProps) {
mountiny marked this conversation as resolved.
Show resolved Hide resolved
const shouldReorder = this.shouldReorder(nextProps);
const recentReports = this.getRecentReports(nextProps);
const orderedReports = shouldReorder
? recentReports
: this.state.orderedReports.map(ordRep =>
recentReports.filter(recRep => ordRep.reportID == recRep.reportID)[0]);

this.setState({
orderedReports: orderedReports
});
}

shouldReorder(nextProps) {
// We do not want to re-order reports in the LHN if the only change is the draft comment in the
// current report.

Expand All @@ -111,48 +155,12 @@ class SidebarLinks extends React.Component {
return true;
}

const previousDraftComments = this.props.draftComments;
const nextDraftComments = nextProps.draftComments;

const previousDraftReports = Object.keys(previousDraftComments);
const nextDraftReports = Object.keys(nextDraftComments);

const reportsWithNewDraftComments = nextDraftReports.filter((report) => {
const isNewDraftComment = !previousDraftReports.includes(report);
const wasNonEmptyDraftComment = previousDraftComments[report] === '';
const hasDraftCommentChanged = previousDraftComments[report] !== nextDraftComments[report];

return isNewDraftComment || (hasDraftCommentChanged && wasNonEmptyDraftComment);
});
const reportsWithRemovedDraftComments = previousDraftReports.filter((report) => {
const isRemovedDraftComment = !nextDraftReports.includes(report);
const isEmptyDraftComment = nextDraftComments[report] === '';
const hasDraftCommentChanged = previousDraftComments[report] !== nextDraftComments[report];

return isRemovedDraftComment || (hasDraftCommentChanged && isEmptyDraftComment);
});
const reportsWithEditedDraftComments = nextDraftReports.filter((report) => {
const didDraftCommentExistPreviously = previousDraftReports.includes(report);
const hasDraftCommentChanged = previousDraftComments[report] !== nextDraftComments[report];
const isNotANewDraftComment = !reportsWithNewDraftComments.includes(report);
const isNotARemovedDraftComment = !reportsWithRemovedDraftComments.includes(report);

return didDraftCommentExistPreviously && hasDraftCommentChanged && isNotANewDraftComment && isNotARemovedDraftComment;
});

const allReportsWithDraftCommentChanges = [
...reportsWithNewDraftComments,
...reportsWithRemovedDraftComments,
...reportsWithEditedDraftComments,
];

const activeReportID = this.props.currentlyViewedReportID;
const reportKey = `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${activeReportID}`;

// Do not re-order reports if draft comment changes are only in the current report.
if (allReportsWithDraftCommentChanges.length === 1 && allReportsWithDraftCommentChanges.includes(reportKey)) {
return false;
// Never re-order if the active report has a draft and vice versa
mountiny marked this conversation as resolved.
Show resolved Hide resolved
if (nextProps.currentlyViewedReportID) {
const hasActiveReportDraft = lodashGet(nextProps.reportsWithDraft, `${ONYXKEYS.COLLECTION.REPORTS_WITH_DRAFT}${nextProps.currentlyViewedReportID}`, false)
return !hasActiveReportDraft;
mountiny marked this conversation as resolved.
Show resolved Hide resolved
}

return true;
}

Expand All @@ -161,26 +169,17 @@ class SidebarLinks extends React.Component {
}

render() {
const activeReportID = parseInt(this.props.currentlyViewedReportID, 10);

mountiny marked this conversation as resolved.
Show resolved Hide resolved
// Wait until the reports and personalDetails are actually loaded before displaying the LHN
if (!this.props.initialReportDataLoaded || _.isEmpty(this.props.personalDetails)) {
return null;
}

const activeReportID = parseInt(this.props.currentlyViewedReportID, 10);

const {recentReports} = getSidebarOptions(
this.props.reports,
this.props.personalDetails,
this.props.draftComments,
activeReportID,
this.props.priorityMode,
this.props.betas,
);

const sections = [{
title: '',
indexOffset: 0,
data: recentReports,
data: this.state.orderedReports ?? [],
mountiny marked this conversation as resolved.
Show resolved Hide resolved
shouldShow: true,
}];

Expand Down Expand Up @@ -233,7 +232,7 @@ class SidebarLinks extends React.Component {
{paddingBottom: getSafeAreaMargins(this.props.insets).marginBottom},
]}
sections={sections}
focusedIndex={_.findIndex(recentReports, (
focusedIndex={_.findIndex(this.state.orderedReports, (
option => option.reportID === activeReportID
))}
onSelectRow={(option) => {
Expand Down Expand Up @@ -289,5 +288,8 @@ export default compose(
betas: {
key: ONYXKEYS.BETAS,
},
reportsWithDraft: {
key: ONYXKEYS.COLLECTION.REPORTS_WITH_DRAFT,
}
}),
)(SidebarLinks);