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

New Thread Report Empty State, Header, LHN, OpenReport, Buttons for DEV #18522

Merged
merged 71 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
7265063
Add new param for parentReportID
stitesExpensify May 3, 2023
c33c504
Add new params for threads, and build different optimisticData
stitesExpensify May 3, 2023
8f641d4
Add new method for threads and revert old method
stitesExpensify May 3, 2023
129cb0e
Update openReport to support threads
stitesExpensify May 4, 2023
10a1729
Don't find threads when finding reports by participants
stitesExpensify May 4, 2023
1333574
Add the 'reply in thread' button
stitesExpensify May 4, 2023
22f561d
Fix logic and correctly pass reportActionID
stitesExpensify May 4, 2023
0aacb49
Fix reportActions type
stitesExpensify May 4, 2023
0c9ed14
Remove test vars
stitesExpensify May 4, 2023
7e347d3
Merge branch 'main' into stites-updateOpenReportForThreads
grgia May 5, 2023
7436a46
Merge branch 'main' into stites-updateOpenReportForThreads
grgia May 6, 2023
4266c41
Hardcoded New Thread Report
grgia May 6, 2023
1410cff
Add empty state for threads
grgia May 6, 2023
1146e62
Fix reportActions type, dev comments
grgia May 6, 2023
021ee2e
Fix LHN name (will work when reportName data is the parent message)
grgia May 6, 2023
fffd148
This is where we will set the LHN header to the parent message
grgia May 6, 2023
af69c87
Show header and subtitle for threads (hard coded)
grgia May 7, 2023
d997131
icons
grgia May 7, 2023
4918794
icons cont
grgia May 8, 2023
1863c6e
icons cont
grgia May 8, 2023
18087c7
Update Empty State
grgia May 8, 2023
5f35afb
Clean up icons
grgia May 8, 2023
a580293
Merge branch 'main' into georgia-ThreadReportUI
grgia May 8, 2023
ec85280
Remove comments
grgia May 8, 2023
b938973
Clean up
grgia May 9, 2023
6f9e323
Clean up
grgia May 9, 2023
48f1445
Merge branch 'main' into georgia-ThreadReportUI
grgia May 9, 2023
9b1884a
Only show button in DEV environment
grgia May 9, 2023
91eb65f
Clean up comments, update helper function deescriptions
grgia May 10, 2023
2092286
clean up ReportActionItemParentAction props
grgia May 10, 2023
23d0703
Remove console statement
grgia May 10, 2023
6bbca5f
supply parentReportID to ReportActionItemParentAction
grgia May 10, 2023
cd3b78f
Fix opening child report for already created thread
grgia May 10, 2023
b4f5c03
Merge branch 'main' into georgia-ThreadReportUI
grgia May 10, 2023
567dac6
default childReportID to string
grgia May 10, 2023
7199670
Prevent deleting thread parents for now.
grgia May 10, 2023
214bc12
spacing
grgia May 10, 2023
48e02ed
Merge branch 'main' into georgia-ThreadReportUI
grgia May 10, 2023
7a48a56
prettier
grgia May 10, 2023
b5013f7
Remove threads from search for users
grgia May 10, 2023
cd90b12
Fix threads UI in search bar
grgia May 10, 2023
2ef1e03
Refactor getParentReportAction, update language key
grgia May 11, 2023
cc91bda
Remove unnecessary lodashGet, clean code
grgia May 11, 2023
d05b4a5
Use getReportName for title
grgia May 11, 2023
896c317
Refactor to fix import/no-cycle
grgia May 11, 2023
8957ff2
Merge branch 'main' into georgia-ThreadReportUI
grgia May 11, 2023
9f44675
Show deleted parentActions
grgia May 11, 2023
f6055f9
Merge branch 'main' into georgia-ThreadReportUI
grgia May 11, 2023
0752bd4
Remove childReportID
grgia May 11, 2023
5227c01
Merge branch 'main' into georgia-ThreadReportUI
grgia May 11, 2023
bbd9ac4
Merge branch 'main' of github.com:Expensify/App into georgia-ThreadRe…
stitesExpensify May 11, 2023
88e0ca5
fix workspace icon bug
grgia May 11, 2023
d89ac1c
Missed one line to fix icon
grgia May 11, 2023
4210c7c
Merge branch 'georgia-ThreadReportUI' of github.com:Expensify/App int…
stitesExpensify May 11, 2023
8d2ec3f
Merge branch 'main' of github.com:Expensify/App into georgia-ThreadRe…
stitesExpensify May 11, 2023
9a815ca
Add beta check to threads
stitesExpensify May 11, 2023
6effecb
Style
stitesExpensify May 11, 2023
3a659c1
Don't show thread button for the parent comment
stitesExpensify May 12, 2023
81a3d59
If the report is a task, pass the parent
stitesExpensify May 12, 2023
a3f17f2
Check for undefined because not all actions have a childReportID
stitesExpensify May 12, 2023
074e5b2
Style
stitesExpensify May 12, 2023
e59c020
Remove newlines from headerText
grgia May 12, 2023
ec3fe30
fix lint errors
grgia May 12, 2023
0e5e7e2
Use sentence casing (slack convo)
grgia May 12, 2023
ddb9185
Show threads in lhn regardles of number of participants
stitesExpensify May 12, 2023
bd091a9
Merge branch 'main' of github.com:Expensify/App into georgia-ThreadRe…
stitesExpensify May 12, 2023
3396611
Merge branch 'georgia-ThreadReportUI' of github.com:Expensify/App int…
stitesExpensify May 12, 2023
47a8a40
Style updates and simplifications
stitesExpensify May 12, 2023
a473d85
Don't show threads in search for now
grgia May 12, 2023
702e6a6
Fix logic to always return bool
stitesExpensify May 12, 2023
e3b809e
Merge branch 'georgia-ThreadReportUI' of github.com:Expensify/App int…
stitesExpensify May 12, 2023
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
13 changes: 9 additions & 4 deletions src/components/LHNOptionsList/OptionRowLHN.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ const OptionRowLHN = (props) => {
const hasBrickError = optionItem.brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
const shouldShowGreenDotIndicator = !hasBrickError && (optionItem.isUnreadWithMention || (optionItem.hasOutstandingIOU && !optionItem.isIOUReportOwner));

// If the item is a thread within a workspace, we will show the subtitle as the second line instead of in a pill
const alternativeText = optionItem.isThread && optionItem.subtitle ? optionItem.subtitle : optionItem.alternateText;
Comment on lines +84 to +85
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it intentional to show workspace name and room name in thread?
As there's no way to see last thread message in LHN, the issue was reported here - #19447.
This only works in DM chat as subtitle not exist.

So I'd like to confirm if it's fine to use optionItem.alternateText here no matter thread or not.

Copy link
Contributor

Choose a reason for hiding this comment

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

@grgia bump ^

Here's design doc:

LHN:

2023-06-02_11-08-58

Header:

2023-06-02_11-09-07


return (
<OfflineWithFeedback
pendingAction={optionItem.pendingAction}
Expand Down Expand Up @@ -147,23 +150,25 @@ const OptionRowLHN = (props) => {
tooltipEnabled
numberOfLines={1}
textStyles={displayNameStyle}
shouldUseFullTitle={optionItem.isChatRoom || optionItem.isPolicyExpenseChat || optionItem.isTaskReport || optionItem.isMoneyRequestReport}
shouldUseFullTitle={
optionItem.isChatRoom || optionItem.isPolicyExpenseChat || optionItem.isTaskReport || optionItem.isThread || optionItem.isMoneyRequestReport
}
/>
{optionItem.isChatRoom && (
{optionItem.isChatRoom && !optionItem.isThread && (
<TextPill
style={textPillStyle}
accessibilityLabel={props.translate('accessibilityHints.workspaceName')}
text={optionItem.subtitle}
/>
)}
</View>
{optionItem.alternateText ? (
{alternativeText ? (
<Text
style={alternateTextStyle}
numberOfLines={1}
accessibilityLabel={props.translate('accessibilityHints.lastChatMessagePreview')}
>
{optionItem.alternateText}
{alternativeText}
</Text>
) : null}
</View>
Expand Down
4 changes: 4 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ export default {
deleteComment: 'Delete comment',
deleteConfirmation: 'Are you sure you want to delete this comment?',
onlyVisible: 'Only visible to',
replyInThread: 'Reply in thread',
},
emojiReactions: {
addReactionTooltip: 'Add reaction',
Expand Down Expand Up @@ -1306,4 +1307,7 @@ export default {
chatUserDisplayNames: 'Chat user display names',
scrollToNewestMessages: 'Scroll to newest messages',
},
parentReportAction: {
deletedMessage: '[Deleted message]',
},
};
4 changes: 4 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export default {
deleteComment: 'Eliminar comentario',
deleteConfirmation: '¿Estás seguro de que quieres eliminar este comentario?',
onlyVisible: 'Visible sólo para',
replyInThread: 'Responder en el hilo',
},
emojiReactions: {
addReactionTooltip: 'Añadir una reacción',
Expand Down Expand Up @@ -1771,4 +1772,7 @@ export default {
chatUserDisplayNames: 'Nombres de los usuarios del chat',
scrollToNewestMessages: 'Desplázate a los mensajes más recientes',
},
parentReportAction: {
deletedMessage: '[Mensaje eliminado]',
},
};
9 changes: 9 additions & 0 deletions src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ Onyx.connect({
});

const lastReportActions = {};
const allReportActions = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
callback: (actions, key) => {
if (!key || !actions) {
return;
}
allReportActions[key] = actions;
const reportID = CollectionUtils.extractCollectionItemID(key);
lastReportActions[reportID] = _.last(_.toArray(actions));
},
Expand Down Expand Up @@ -406,6 +408,7 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show
result.isDefaultRoom = ReportUtils.isDefaultRoom(report);
result.isArchivedRoom = ReportUtils.isArchivedRoom(report);
result.isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report);
result.isThread = ReportUtils.isThread(report);
result.shouldShowSubscript = result.isPolicyExpenseChat && !report.isOwnPolicyExpenseChat && !result.isArchivedRoom;
result.allReportErrors = getAllReportErrors(report, reportActions);
result.brickRoadIndicator = !_.isEmpty(result.allReportErrors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '';
Expand Down Expand Up @@ -542,6 +545,7 @@ function getOptions(
sortPersonalDetailsByAlphaAsc = true,
forcePolicyNamePreview = false,
includeOwnedWorkspaceChats = false,
includeThreads = false,
},
) {
if (!isPersonalDetailsReady(personalDetails)) {
Expand Down Expand Up @@ -581,6 +585,7 @@ function getOptions(
return;
}

const isThread = ReportUtils.isThread(report);
const isChatRoom = ReportUtils.isChatRoom(report);
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report);
const logins = report.participants || [];
Expand All @@ -589,6 +594,10 @@ function getOptions(
return;
}

if (isThread && !includeThreads) {
return;
}

// Save the report in the map if this is a single participant so we can associate the reportID with the
// personal detail option later. Individuals should not be associated with single participant
// policyExpenseChats or chatRooms since those are not people.
Expand Down
19 changes: 17 additions & 2 deletions src/libs/ReportActionsUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ function isDeletedAction(reportAction) {
return message.length === 0 || lodashGet(message, [0, 'html']) === '';
}

/**
* Returns the parentReportAction if the given report is a thread.
*
* @param {Object} report
* @returns {Object}
*/
function getParentReportAction(report) {
grgia marked this conversation as resolved.
Show resolved Hide resolved
if (!report || !report.parentReportID || !report.parentReportActionID) {
return {};
}
grgia marked this conversation as resolved.
Show resolved Hide resolved
return lodashGet(allReportActions, [report.parentReportID, report.parentReportActionID], {});
}

/**
* Sort an array of reportActions by their created timestamp first, and reportActionID second
* This gives us a stable order even in the case of multiple reportActions created on the same millisecond
Expand Down Expand Up @@ -206,10 +219,11 @@ function shouldReportActionBeVisible(reportAction, key) {
return false;
}

// All other actions are displayed except deleted, non-pending actions
// All other actions are displayed except thread parents, deleted, or non-pending actions
const isDeleted = isDeletedAction(reportAction);
const isPending = !_.isEmpty(reportAction.pendingAction);
return !isDeleted || isPending;
const isDeletedParentAction = lodashGet(reportAction, ['message', 0, 'isDeletedParentAction'], false);
return !isDeleted || isPending || isDeletedParentAction;
}

/**
Expand Down Expand Up @@ -303,4 +317,5 @@ export {
getLatestReportActionFromOnyxData,
getLinkedTransactionID,
isCreatedTaskReportAction,
getParentReportAction,
};
70 changes: 67 additions & 3 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,12 +427,42 @@ function isPolicyExpenseChatAdmin(report, policies) {
return policyRole === CONST.POLICY.ROLE.ADMIN;
}

/**
* Returns true if report has a parent and is therefore a Thread.
*
* @param {Object} report
* @returns {Boolean}
*/
function isThread(report) {
return Boolean(report && report.parentReportID && report.parentReportActionID);
}

/**
* Returns true if reportAction has a child.
*
* @param {Object} reportAction
* @returns {Boolean}
*/
function isThreadParent(reportAction) {
return reportAction && reportAction.childReportID && reportAction.childReportID !== 0;
}

/**
* Get either the policyName or domainName the chat is tied to
* @param {Object} report
* @returns {String}
*/
function getChatRoomSubtitle(report) {
if (isThread(report)) {
if (!getChatType(report)) {
return '';
}

// If thread is not from a DM or group chat, the subtitle will follow the pattern 'Workspace Name • #roomName'
const workspaceName = getPolicyName(report);
const roomName = isChatRoom(report) ? lodashGet(report, 'displayName') : '';
return [workspaceName, roomName].join(' • ');
}
if (!isDefaultRoom(report) && !isUserCreatedPolicyRoom(report) && !isPolicyExpenseChat(report)) {
return '';
}
Expand Down Expand Up @@ -719,6 +749,27 @@ function getIcons(report, personalDetails, defaultIcon = null) {
result.source = Expensicons.DeletedRoomAvatar;
return [result];
}
if (isThread(report)) {
const parentReport = lodashGet(allReports, [`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]);
const parentReportAction = ReportActionsUtils.getParentReportAction(report);

grgia marked this conversation as resolved.
Show resolved Hide resolved
if (!parentReport) {
stitesExpensify marked this conversation as resolved.
Show resolved Hide resolved
result.source = Expensicons.ActiveRoomAvatar;
return [result];
}

if (getChatType(parentReport)) {
result.source = getWorkspaceAvatar(parentReport);
result.type = CONST.ICON_TYPE_WORKSPACE;
result.name = getPolicyName(parentReport);
return [result];
}

const actorEmail = lodashGet(parentReportAction, 'actorEmail', '');
result.source = getAvatar(lodashGet(personalDetails, [actorEmail, 'avatar']), actorEmail);
result.name = actorEmail;
return [result];
}
if (isDomainRoom(report)) {
result.source = Expensicons.DomainRoomAvatar;
return [result];
Expand Down Expand Up @@ -935,6 +986,11 @@ function getMoneyRequestReportName(report) {
*/
function getReportName(report) {
let formattedName;
if (isThread(report)) {
const parentReportAction = ReportActionsUtils.getParentReportAction(report);
const parentReportActionMessage = lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' ');
return parentReportActionMessage || Localize.translateLocal('parentReportAction.deletedMessage');
Copy link
Member

Choose a reason for hiding this comment

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

When the action is an Attachment, the title of the report will be Attachment. But this will be translated so we need to handle that ourselves which is being done in #19517.

}
if (isChatRoom(report) || isTaskReport(report)) {
formattedName = report.reportName;
}
Expand Down Expand Up @@ -1295,6 +1351,8 @@ function buildOptimisticIOUReportAction(type, amount, currency, comment, partici
* @param {String} oldPolicyName
* @param {String} visibility
* @param {String} notificationPreference
* @param {String} parentReportActionID
* @param {String} parentReportID
* @returns {Object}
*/
function buildOptimisticChatReport(
Expand All @@ -1307,6 +1365,8 @@ function buildOptimisticChatReport(
oldPolicyName = '',
visibility = undefined,
notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
parentReportActionID,
parentReportID,
) {
const currentTime = DateUtils.getDBTime();
return {
Expand All @@ -1323,6 +1383,8 @@ function buildOptimisticChatReport(
notificationPreference,
oldPolicyName,
ownerEmail: ownerEmail || CONST.REPORT.OWNER_EMAIL_FAKE,
parentReportActionID,
parentReportID,
participants: participantList,
policyID,
reportID: generateReportID(),
Expand Down Expand Up @@ -1628,7 +1690,7 @@ function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, curr
// Exclude reports that have no data because there wouldn't be anything to show in the option item.
// This can happen if data is currently loading from the server or a report is in various stages of being created.
// This can also happen for anyone accessing a public room or archived room for which they don't have access to the underlying policy.
if (!report || !report.reportID || (_.isEmpty(report.participants) && !isPublicRoom(report) && !isArchivedRoom(report) && !isMoneyRequestReport(report))) {
if (!report || !report.reportID || (_.isEmpty(report.participants) && !isThread(report) && !isPublicRoom(report) && !isArchivedRoom(report) && !isMoneyRequestReport(report))) {
return false;
}

Expand Down Expand Up @@ -1678,15 +1740,15 @@ function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, curr
}

/**
* Attempts to find a report in onyx with the provided list of participants
* Attempts to find a report in onyx with the provided list of participants. Does not include threads
* @param {Array} newParticipantList
* @returns {Array|undefined}
*/
function getChatByParticipants(newParticipantList) {
newParticipantList.sort();
return _.find(allReports, (report) => {
// If the report has been deleted, or there are no participants (like an empty #admins room) then skip it
if (!report || !report.participants) {
if (!report || !report.participants || isThread(report)) {
return false;
}

Expand Down Expand Up @@ -2005,6 +2067,8 @@ export {
canRequestMoney,
getWhisperDisplayNames,
getWorkspaceAvatar,
isThread,
isThreadParent,
shouldReportShowSubscript,
isSettled,
};
4 changes: 3 additions & 1 deletion src/libs/SidebarUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ function getOptionData(reportID) {
const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForLogins(report.participants, personalDetails));
const personalDetail = participantPersonalDetailList[0] || {};

result.isThread = ReportUtils.isThread(report);
result.isChatRoom = ReportUtils.isChatRoom(report);
result.isTaskReport = ReportUtils.isTaskReport(report);
result.isArchivedRoom = ReportUtils.isArchivedRoom(report);
Expand Down Expand Up @@ -302,7 +303,7 @@ function getOptionData(reportID) {
});
}

if ((result.isChatRoom || result.isPolicyExpenseChat) && !result.isArchivedRoom) {
if ((result.isChatRoom || result.isPolicyExpenseChat || result.isThread) && !result.isArchivedRoom) {
result.alternateText = lastMessageTextFromReport.length > 0 ? lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet');
} else if (result.isTaskReport) {
result.alternateText = Localize.translate(preferredLocale, 'newTaskPage.task');
Expand Down Expand Up @@ -340,6 +341,7 @@ function getOptionData(reportID) {
}

const reportName = ReportUtils.getReportName(report);

grgia marked this conversation as resolved.
Show resolved Hide resolved
result.text = reportName;
result.subtitle = subtitle;
result.participantsList = participantPersonalDetailList;
Expand Down
Loading
Loading