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

Set up Avatars for Workspace Chats #7852

Merged
merged 91 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
08ffa3d
check if the given report is a Workspace Chat
marcochavezf Feb 21, 2022
86a1bbc
get the linked policy of the workspace chat
marcochavezf Feb 21, 2022
ef23069
return avatar of the workspace for non-admin users
marcochavezf Feb 21, 2022
a2dc66d
return participan and workspace avatar url for admins
marcochavezf Feb 21, 2022
57f4e6c
use report.reportName for workspace chats
marcochavezf Feb 21, 2022
be4c679
Merge branch 'main' into marco-avatarsWorkspaceChats
marcochavezf Feb 22, 2022
a61cafb
display workspace chat in search page
marcochavezf Feb 22, 2022
425e3b7
get reportName as option.text
marcochavezf Feb 22, 2022
d5a963b
expose option.isPolicyExpenseChat to use fullTitle
marcochavezf Feb 22, 2022
6dd30c9
dislay alternateText for non-admins for workspace chats
marcochavezf Feb 22, 2022
efe6be5
change name param name in getSearchText
marcochavezf Feb 22, 2022
0dab98c
show workspace chat in search page for admins
marcochavezf Feb 22, 2022
7195cd2
get the correct report icons when user is admin
marcochavezf Feb 23, 2022
3817498
create styles for SubscriptAvatar
marcochavezf Feb 23, 2022
39cc286
create SubscriptAvatar component
marcochavezf Feb 23, 2022
3763739
add showSubscript MultipleAvatars to render SubscriptAvatar
marcochavezf Feb 23, 2022
4ef49bc
add showSubscript to option
marcochavezf Feb 23, 2022
6543229
update subscript avatar styles
marcochavezf Feb 23, 2022
0690dee
show SubscriptAvatar in HeaderView
marcochavezf Feb 23, 2022
5cc113e
fix blank name in HeaderView for workspace chat
marcochavezf Feb 23, 2022
d83c3c2
save ReportUtils function results in variables
marcochavezf Feb 23, 2022
463e928
fix duplicate workspace chates for workspace members
marcochavezf Feb 23, 2022
5981680
fix a few jsdoc errors
marcochavezf Feb 23, 2022
0a0509a
fix array of strings jsdoc
marcochavezf Feb 23, 2022
533f851
fix duplicate admin rooms in Search Page
marcochavezf Feb 23, 2022
25fd44d
display policy name as alternateText for admins
marcochavezf Feb 23, 2022
3218eb3
fix displayed name in alternateText for workspace chats
marcochavezf Feb 24, 2022
6ad97fb
filter out workspace chat if user doesn't have permission
marcochavezf Feb 24, 2022
95aa9cc
add showSubscript to RoomHeaderAvatars
marcochavezf Feb 24, 2022
9fe06b7
show workspace icon for subscript avatar
marcochavezf Feb 24, 2022
f8f6975
add defaultSubscriptIcon to SubscriptAvatar
marcochavezf Feb 24, 2022
bedfc70
set Expensicons.Workspace in defaultSubscriptIcon
marcochavezf Feb 24, 2022
7652cf3
show SubscriptAvatar component in RoomHeaderAvatars
marcochavezf Feb 24, 2022
adc6ccc
create emptyAvatarLarge and add var avatarSizeLarge
marcochavezf Feb 24, 2022
1e54819
add size to SubscriptAvatar and use Avatar instead of Image
marcochavezf Feb 24, 2022
ec1dcec
add secondAvatarLarge and firstAvatar styles
marcochavezf Feb 25, 2022
b223cf2
apply different styles if size is large
marcochavezf Feb 25, 2022
4aff4d6
set large size in RoomHeaderAvatars
marcochavezf Feb 25, 2022
79a40ba
move chat room icons to Expensicons
marcochavezf Feb 25, 2022
2f73f0d
use Icon instead of RoomAvatar in Avatar
marcochavezf Feb 25, 2022
5b8b050
delete RoomAvatar (logic moved to Avatar)
marcochavezf Feb 25, 2022
704be62
display archived room in RoomHeaderAvatar
marcochavezf Feb 25, 2022
0e981d8
fix border for large avatar in RoomHeaderAvatar
marcochavezf Feb 25, 2022
00e207c
create LargeDualAvatars to remove complexity in SubscripAvatar
marcochavezf Feb 25, 2022
4290b91
remove LargeDualAvatars from RoomHeaderAvatars and call it directly
marcochavezf Feb 25, 2022
0a84fae
remove size from SubscriptAvatar
marcochavezf Feb 25, 2022
7818250
add new avatar sizes
marcochavezf Feb 25, 2022
9d39368
add size prop to Avatar to re-use it as Icon
marcochavezf Feb 25, 2022
752edb5
use size property to remove Icon from LargeDualAvatars
marcochavezf Feb 25, 2022
63d87a5
display workspace icon for empty workspace chats
marcochavezf Feb 25, 2022
6577c11
display workspace icon in OptionRow and RoomHeaderAvatar
marcochavezf Feb 25, 2022
b434b58
Merge branch 'main' into marco-avatarsWorkspaceChats
marcochavezf Feb 26, 2022
e2bdea4
add functions getAvatarSource
marcochavezf Feb 26, 2022
729301d
use OptionsListUtils.getAvatarSource
marcochavezf Feb 26, 2022
dd69004
remove logic to check avatar source from Avatar
marcochavezf Feb 26, 2022
00fb84e
get avatar sources (url or icon) for OptionRow
marcochavezf Feb 26, 2022
4775ec2
rename avatarImageURLs to avatarIcons and change props
marcochavezf Feb 26, 2022
ea668d8
remove unused isChatRoom, isArchivedRoom and isPolicyExpenseChat
marcochavezf Feb 26, 2022
a199aa6
change params to SubscriptAvatar
marcochavezf Feb 26, 2022
12ac881
use _.contains() instead of .includes()
marcochavezf Feb 26, 2022
d537a28
include participants in getParticipantEmailsFromReport
marcochavezf Feb 26, 2022
ca1b024
change showSubscript flag to shouldShowSubscript
marcochavezf Feb 26, 2022
d55c0f3
revert condition for reportMapForLogins (fixed with report.participan…
marcochavezf Feb 26, 2022
7aa05fa
give priority to isArchivedRoom in getAvatarSource
marcochavezf Feb 26, 2022
4687612
update comment about returning workspace avatar
marcochavezf Feb 26, 2022
8846c8d
use props.source directly
marcochavezf Feb 26, 2022
a7e2a00
move large dual avatars to RoomHeaderAvatars and remove LargeDualAvatars
marcochavezf Feb 26, 2022
40325c7
fix jsdoc for icons
marcochavezf Feb 26, 2022
9e69491
move avatar size logic to the new funciton getAvatarSize
marcochavezf Feb 28, 2022
d47652b
delete redundant comment
marcochavezf Feb 28, 2022
bbff6ef
move condition logic to one line
marcochavezf Feb 28, 2022
41b6c25
use {*} to follow jsdoc style guides
marcochavezf Feb 28, 2022
f11c423
merge logic of getAvatarSources
marcochavezf Feb 28, 2022
e827030
replace getAvatarSourceFromReport with getAvatarSources
marcochavezf Feb 28, 2022
b95477c
return Profile icon instead of personalDetail.avatar
marcochavezf Feb 28, 2022
5bcca5f
fix styles of large workspace room avatars
marcochavezf Feb 28, 2022
5ac4713
call SubscriptAvatar directly
marcochavezf Feb 28, 2022
2ac7de7
set avatarTooltips as optional prop
marcochavezf Mar 1, 2022
e8fa8f3
fix tooltips in SubscriptAvatar
marcochavezf Mar 1, 2022
41115dd
Update src/components/Avatar.js
marcochavezf Mar 1, 2022
34097c8
remove unnecessary View in SubscriptAvatar
marcochavezf Mar 1, 2022
dc34a8b
replace secondAvatarHovered with StyleUtils.getBackgroundAndBorderStyle
marcochavezf Mar 1, 2022
e3990d8
add missing displayName
marcochavezf Mar 1, 2022
5dc0073
get the first element of avatar sources in ReportDetailsPage
marcochavezf Mar 1, 2022
652a7d7
change SubscriptAvatar size in #focus mode
marcochavezf Mar 1, 2022
a884bc0
use Avatar size const instead in MultipleAvatars
marcochavezf Mar 1, 2022
b78492a
create getAvatarStyle function and add SMALL_SUBSCRIPT size
marcochavezf Mar 2, 2022
565cf0c
use StyleUtils.getAvatarStyle in Avatar
marcochavezf Mar 2, 2022
3f66b17
create OPTION_MODE and replace string literals
marcochavezf Mar 2, 2022
4b79410
add mode to SubscriptAvatar and secondAvatarSubscriptCompact style
marcochavezf Mar 2, 2022
45b90ae
set emptyAvatarSmall and update style for subscript avatar
marcochavezf Mar 2, 2022
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
14 changes: 14 additions & 0 deletions src/components/MultipleAvatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import styles from '../styles/styles';
import Avatar from './Avatar';
import Tooltip from './Tooltip';
import Text from './Text';
import SubscriptAvatar from './SubscriptAvatar';

const propTypes = {
/** Array of avatar URL */
Expand All @@ -25,6 +26,9 @@ const propTypes = {

/** Tooltip for the Avatar */
avatarTooltips: PropTypes.arrayOf(PropTypes.string),

/** Flag to show SubscriptAvatar component */
showSubscript: PropTypes.bool,
};

const defaultProps = {
Expand All @@ -34,6 +38,7 @@ const defaultProps = {
isChatRoom: false,
isArchivedRoom: false,
avatarTooltips: [],
showSubscript: false,
};

const MultipleAvatars = (props) => {
Expand Down Expand Up @@ -63,6 +68,15 @@ const MultipleAvatars = (props) => {
);
}

if (props.showSubscript) {
return (
<SubscriptAvatar
avatarImageURLs={props.avatarImageURLs}
avatarTooltips={props.avatarTooltips}
/>
);
}
marcochavezf marked this conversation as resolved.
Show resolved Hide resolved

return (
<View style={avatarContainerStyles}>
<View
Expand Down
40 changes: 40 additions & 0 deletions src/components/SubscriptAvatar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, {memo} from 'react';
import PropTypes from 'prop-types';
import {Image, View} from 'react-native';
import styles from '../styles/styles';
import Tooltip from './Tooltip';

const propTypes = {
/** Array of avatar URL */
avatarImageURLs: PropTypes.arrayOf(PropTypes.string).isRequired,

/** Tooltip for the Avatar */
avatarTooltips: PropTypes.arrayOf(PropTypes.string).isRequired,
};
marcochavezf marked this conversation as resolved.
Show resolved Hide resolved

const SubscriptAvatar = props => (
<View style={styles.emptyAvatar}>
<Tooltip text={props.avatarTooltips[0]} absolute>
<Image
source={{uri: props.avatarImageURLs[0]}}
style={styles.avatarNormal}
/>
</Tooltip>
<View
style={[
styles.secondAvatarSubscript,
styles.secondAvatarHovered,
]}
>
<Tooltip text={props.avatarTooltips[1]} absolute>
<Image
source={{uri: props.avatarImageURLs[1]}}
style={styles.singleSubscript}
/>
</Tooltip>
</View>
</View>
);

SubscriptAvatar.propTypes = propTypes;
export default memo(SubscriptAvatar);
47 changes: 34 additions & 13 deletions src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,13 @@ function getParticipantNames(personalDetailList) {
*
* @param {Object} report
* @param {Array} personalDetailList
* @param {Boolean} isChatRoom
* @param {Boolean} isChatRoomOrPolicyExpenseChat
* @return {String}
*/
function getSearchText(report, personalDetailList, isChatRoom) {
function getSearchText(report, personalDetailList, isChatRoomOrPolicyExpenseChat) {
const searchTerms = [];

if (!isChatRoom) {
if (!isChatRoomOrPolicyExpenseChat) {
_.each(personalDetailList, (personalDetail) => {
searchTerms.push(personalDetail.displayName);
searchTerms.push(personalDetail.login);
Expand All @@ -180,7 +180,7 @@ function getSearchText(report, personalDetailList, isChatRoom) {
searchTerms.push(...report.reportName);
searchTerms.push(..._.map(report.reportName.split(','), name => name.trim()));

if (isChatRoom) {
if (isChatRoomOrPolicyExpenseChat) {
const chatRoomSubtitle = ReportUtils.getChatRoomSubtitle(report, policies);
searchTerms.push(...chatRoomSubtitle);
searchTerms.push(..._.map(chatRoomSubtitle.split(','), name => name.trim()));
Expand Down Expand Up @@ -208,16 +208,18 @@ function hasReportDraftComment(report) {
* Creates a report list option
*
* @param {Array<Object>} personalDetailList
* @param {Object} [report]
* @param {Boolean} showChatPreviewLine
* @param {Boolean} forcePolicyNamePreview
* @param {Object} report
* @param {Object} options
* @param {Boolean} options.showChatPreviewLine
* @param {Boolean} options.forcePolicyNamePreview
* @returns {Object}
*/
function createOption(personalDetailList, report, {
showChatPreviewLine = false, forcePolicyNamePreview = false,
}) {
const isChatRoom = ReportUtils.isChatRoom(report);
const hasMultipleParticipants = personalDetailList.length > 1 || isChatRoom;
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report);
const hasMultipleParticipants = personalDetailList.length > 1 || isChatRoom || isPolicyExpenseChat;
const personalDetail = personalDetailList[0];
const hasDraftComment = hasReportDraftComment(report);
const hasOutstandingIOU = lodashGet(report, 'hasOutstandingIOU', false);
Expand All @@ -239,7 +241,7 @@ function createOption(personalDetailList, report, {
let text;
let alternateText;
let icons;
if (isChatRoom) {
if (isChatRoom || isPolicyExpenseChat) {
text = lodashGet(report, ['reportName'], '');
alternateText = (showChatPreviewLine && !forcePolicyNamePreview && lastMessageText)
? lastMessageText
Expand Down Expand Up @@ -274,13 +276,15 @@ function createOption(personalDetailList, report, {
isUnread: report ? report.unreadActionCount > 0 : null,
hasDraftComment,
keyForList: report ? String(report.reportID) : personalDetail.login,
searchText: getSearchText(report, personalDetailList, isChatRoom),
searchText: getSearchText(report, personalDetailList, isChatRoom || isPolicyExpenseChat),
isPinned: lodashGet(report, 'isPinned', false),
hasOutstandingIOU,
iouReportID: lodashGet(report, 'iouReportID'),
isIOUReportOwner: lodashGet(iouReport, 'ownerEmail', '') === currentUserLogin,
iouReportAmount: lodashGet(iouReport, 'total', 0),
isChatRoom,
isPolicyExpenseChat,
showSubscript: isPolicyExpenseChat && !report.isOwnPolicyExpenseChat,
marcochavezf marked this conversation as resolved.
Show resolved Hide resolved
isArchivedRoom: ReportUtils.isArchivedRoom(report),
};
}
Expand Down Expand Up @@ -396,7 +400,7 @@ function getOptions(reports, personalDetails, activeReportID, {
const logins = lodashGet(report, ['participants'], []);

// Report data can sometimes be incomplete. If we have no logins or reportID then we will skip this entry.
const shouldFilterNoParticipants = _.isEmpty(logins) && !ReportUtils.isChatRoom(report) && !ReportUtils.isDefaultRoom(report);
const shouldFilterNoParticipants = _.isEmpty(logins) && !ReportUtils.isChatRoom(report) && !ReportUtils.isDefaultRoom(report) && !ReportUtils.isPolicyExpenseChat(report);
if (!report || !report.reportID || shouldFilterNoParticipants) {
return;
}
Expand Down Expand Up @@ -433,9 +437,10 @@ function getOptions(reports, personalDetails, activeReportID, {
if (logins.length <= 1) {
reportMapForLogins[logins[0]] = report;
}
const isSearchingSomeonesPolicyExpenseChat = !report.isOwnPolicyExpenseChat && searchValue !== '';
allReportOptions.push(createOption(reportPersonalDetails, report, {
showChatPreviewLine,
forcePolicyNamePreview,
forcePolicyNamePreview: ReportUtils.isPolicyExpenseChat(report) ? isSearchingSomeonesPolicyExpenseChat : forcePolicyNamePreview,
}));
});

Expand Down Expand Up @@ -775,13 +780,29 @@ function getCurrencyListForSections(currencyOptions, searchValue) {
*
* @param {Object} report
* @param {Object} personalDetails
* @returns {String}
* @returns {String[]}
marcochavezf marked this conversation as resolved.
Show resolved Hide resolved
*/
function getReportIcons(report, personalDetails) {
// Default rooms have a specific avatar so we can return any non-empty array
if (ReportUtils.isChatRoom(report)) {
return [''];
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this wasn't added here, but anyone know what is up with the returning an array with an empty string? What is that supposed to mean and why should it be "non-empty"?

Copy link
Contributor

@TomatoToaster TomatoToaster Feb 25, 2022

Choose a reason for hiding this comment

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

I believe we return an empty string because we're using an Icon component instead of using a url for an Image when we're rendering the Avatars for rooms. This could be null, or empty or I guess we could pass the Icon component itself here, but the way we built it just has RoomAvatar.js just ignore whatevers stored here anyway. I think having this be empty string satisfies some propType checks.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok thanks for the explanation. Maybe we can return an empty array or allow the icons to be undefined and not have to say anything about how the report icons will be used or why the array must be non empty. But don't think we have to block this PR on it though - just something I was curious about.

Copy link
Contributor

Choose a reason for hiding this comment

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

@marcochavezf Did our changes to Avatar change this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah no, this getReportIcons is called before we save reports into Onyx and we can't set the corresponding Icon components here, as I suggested in this comment I think (in order to move/merge the logic of getAvatarSources to getReportIcons) we'll need to implement a different approach or maybe remove getReportIcons from PersonalDetails:

https://github.com/Expensify/App/blob/main/src/libs/actions/PersonalDetails.js#L192-L211

But I can address it in another ticket if it's ok because my gut tells me this can break something and we'll require more time to address it :D

}

if (ReportUtils.isPolicyExpenseChat(report)) {
const policyExpenseChatAvatarURL = lodashGet(policies, [
`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, 'avatarURL',
]);

// If the user is not an admin for this workspace chat, return avatar of the workspace
marcochavezf marked this conversation as resolved.
Show resolved Hide resolved
if (report.isOwnPolicyExpenseChat) {
return [policyExpenseChatAvatarURL];
}

// If the user is an admin, return avatar url of the other participant of the report
// (their workspace chat) and the avatar url of the workspace
return [lodashGet(personalDetails, [report.ownerEmail, 'avatarThumbnail']), policyExpenseChatAvatarURL];
}

const sortedParticipants = _.map(report.participants, dmParticipant => ({
firstName: lodashGet(personalDetails, [dmParticipant, 'firstName'], ''),
avatar: lodashGet(personalDetails, [dmParticipant, 'avatarThumbnail'], '')
Expand Down
4 changes: 2 additions & 2 deletions src/libs/actions/PersonalDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,12 @@ function getFromReportParticipants(reports) {
// skip over default rooms which aren't named by participants.
const reportsToUpdate = {};
_.each(reports, (report) => {
if (report.participants.length <= 0 && !ReportUtils.isChatRoom(report)) {
if (report.participants.length <= 0 && !ReportUtils.isChatRoom(report) && !ReportUtils.isPolicyExpenseChat(report)) {
return;
}

const avatars = OptionsListUtils.getReportIcons(report, details);
const reportName = ReportUtils.isChatRoom(report)
const reportName = (ReportUtils.isChatRoom(report) || ReportUtils.isPolicyExpenseChat(report))
? report.reportName
: _.chain(report.participants)
.filter(participant => participant !== currentUserEmail)
Expand Down
3 changes: 2 additions & 1 deletion src/pages/home/sidebar/OptionRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ const OptionRow = (props) => {
isChatRoom={props.option.isChatRoom}
isArchivedRoom={props.option.isArchivedRoom}
avatarTooltips={avatarTooltips}
showSubscript={props.option.showSubscript}
/>
)
}
Expand All @@ -188,7 +189,7 @@ const OptionRow = (props) => {
tooltipEnabled={props.showTitleTooltip}
numberOfLines={1}
textStyles={displayNameStyle}
shouldUseFullTitle={props.option.isChatRoom}
shouldUseFullTitle={props.option.isChatRoom || props.option.isPolicyExpenseChat}
/>
{props.option.alternateText ? (
<Text
Expand Down
16 changes: 16 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,13 @@ const styles = {
borderRadius: 24,
},

singleSubscript: {
height: 20,
width: 20,
backgroundColor: themeColors.icon,
borderRadius: 20,
},

singleAvatarSmall: {
height: 18,
width: 18,
Expand Down Expand Up @@ -1524,6 +1531,15 @@ const styles = {
borderColor: 'transparent',
},

secondAvatarSubscript: {
position: 'absolute',
right: -3,
bottom: -3,
borderWidth: 3,
borderRadius: 18,
borderColor: 'transparent',
},

secondAvatarInline: {
bottom: -3,
right: -25,
Expand Down