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

Update "be the first person to comment" screen #4921

Merged
merged 38 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
19193c3
chat avatars for "be the first to comment" screen
MirFahad58 Aug 28, 2021
21fe129
custom avatar added for custom chat rooms using props as resuseable c…
MirFahad58 Aug 29, 2021
eb61ec0
fixed the lint
MirFahad58 Aug 29, 2021
fdfb878
custom text and other user name with pronouns added
MirFahad58 Aug 29, 2021
1a905ea
localisation added for chat begin screen
MirFahad58 Aug 29, 2021
e615ff3
focused on 1st step as user gets in
MirFahad58 Aug 30, 2021
854e82d
change place holder to say hello
MirFahad58 Aug 30, 2021
ba1bbf3
fixing show say hello first time Alone
MirFahad58 Aug 30, 2021
f9aa030
Merge branch 'main' into fahad-beTheFirstPersonToComment
MirFahad58 Aug 31, 2021
0ce0349
fixes for multi chat
MirFahad58 Aug 31, 2021
c3512c7
style fixes
MirFahad58 Sep 1, 2021
d72f9b7
updated the changes according to review
MirFahad58 Sep 12, 2021
92b0eba
Merge branch 'main' into fahad-beTheFirstPersonToComment
MirFahad58 Sep 12, 2021
d78baa5
number opacity fix
MirFahad58 Sep 20, 2021
29ab6ff
required changes updated
MirFahad58 Sep 26, 2021
024de31
removed the --fix
MirFahad58 Sep 26, 2021
a267581
changes for review.
MirFahad58 Sep 28, 2021
71cbcf0
auto open keyboard and other fixes
MirFahad58 Oct 3, 2021
97dcd15
lint fixed
MirFahad58 Oct 3, 2021
bd04568
Merge branch 'main' into fahad-beTheFirstPersonToComment
MirFahad58 Oct 5, 2021
57b3da4
canFocusInputOnScreenFocus revert
MirFahad58 Oct 5, 2021
4b57887
removed isFocussed and added in component
MirFahad58 Oct 5, 2021
698cfd1
update code styles changes
MirFahad58 Oct 10, 2021
9a58bf8
spelling issue resolved
MirFahad58 Oct 19, 2021
0a4e789
component spelling mistake
MirFahad58 Oct 19, 2021
0b423d9
added for beginningOfChatHistory in es.js and other changes
MirFahad58 Oct 19, 2021
be900c5
font weight bold in android.
MirFahad58 Oct 19, 2021
12f0f52
done with requested changes
MirFahad58 Oct 20, 2021
60e6bee
translation added
MirFahad58 Oct 20, 2021
0cf355c
updated and removed the icon as prop.
MirFahad58 Oct 25, 2021
55d4275
avatar updated
MirFahad58 Oct 26, 2021
08f9532
requested updates
MirFahad58 Nov 13, 2021
1cd7eb8
Merge branch 'main' into fahad-beTheFirstPersonToComment
MirFahad58 Nov 13, 2021
0b7a37a
updates
MirFahad58 Nov 13, 2021
acf319a
linted
MirFahad58 Nov 13, 2021
a3ea9f9
Merge branch 'main' into fahad-beTheFirstPersonToComment
MirFahad58 Nov 16, 2021
8fddba5
code linted
MirFahad58 Nov 17, 2021
8353261
bold text for #private room
MirFahad58 Nov 17, 2021
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
79 changes: 79 additions & 0 deletions src/components/ChatAvatars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, {memo} from 'react';
import PropTypes from 'prop-types';
import {Image, View} from 'react-native';
import styles from '../styles/styles';
import Text from './Text';
import ChatCustomAvatar from './ChatCustomAvatar';
import ActiveRoomAvatar from '../../assets/images/avatars/room.svg';

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


/** Whether this avatar is for an custom room */
isCustomChatRoom: PropTypes.bool,

/** Whether this avatar is for an custom room */
CustomChatRoomIcon: PropTypes.func,

};

const defaultProps = {
avatarImageURLs: [],
isCustomChatRoom: false,
CustomChatRoomIcon: ActiveRoomAvatar,
};

const ChatAvatars = ({avatarImageURLs, isCustomChatRoom, CustomChatRoomIcon}) => {
if (!avatarImageURLs.length) {
return null;
}

if (avatarImageURLs.length === 1 || isCustomChatRoom) {
return (
<ChatCustomAvatar
source={avatarImageURLs[0]}
imageStyles={[styles.avatarLarge]}
isCustomChatRoom={isCustomChatRoom}
CustomChatRoomIcon={CustomChatRoomIcon}
/>
);
}

return (
<View pointerEvents="none">
<View style={[styles.flexRow, styles.wAuto, styles.ml3]}>
{avatarImageURLs.map((val, index) => {
if (index <= 3) {
return (
<View key={val} style={[styles.chatAvatarWraper, styles.justifyContentCenter, styles.alignItemsCenter]}>
<Image source={{uri: val}} style={[styles.chatAvatar]} />

{index === 3 && (
<View
style={[
styles.chatAvatar,
styles.justifyContentCenter,
styles.alignItemsCenter,
styles.chatOverLay,
]}
>
<Text style={styles.avatarInnerTextChat}>
{`+${avatarImageURLs.length - 4}`}
</Text>
</View>
)}
</View>
);
}
return null;
})}
</View>
</View>
);
};

ChatAvatars.defaultProps = defaultProps;
ChatAvatars.propTypes = propTypes;
export default memo(ChatAvatars);
61 changes: 61 additions & 0 deletions src/components/ChatBeginingText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, {memo} from 'react';
import PropTypes from 'prop-types';
import styles from '../styles/styles';
import Text from './Text';
import colors from '../styles/colors';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import compose from '../libs/compose';

const propTypes = {

isDefaultChatRoom: PropTypes.bool,
chatUsers: PropTypes.arrayOf(PropTypes.object),
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
...withLocalizePropTypes,

};

const defaultProps = {
chatUsers: [],
isDefaultChatRoom: false,
};

const ChatBeginingText = ({isDefaultChatRoom, chatUsers, translate}) => (
<Text style={[styles.mt3, styles.w70, styles.textAlignCenter]}>
<Text style={[{color: colors.dark}]}>
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
{isDefaultChatRoom ? `${translate('reportActionsView.beginingOfChatHistroyPrivate')} ` : `${translate('reportActionsView.beginingOfChatHistroy')} `}
</Text>
{isDefaultChatRoom
&& (
<Text style={[{color: colors.dark}]}>
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
{`${chatUsers?.[0]?.displayName} ${translate('reportActionsView.beginingOfChatHistroyPrivateSectionPart')}`}
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
</Text>
)}
{!isDefaultChatRoom
&& (
<Text>
{chatUsers.map(({displayName, pronouns}, index) => (
<Text key={displayName}>
Copy link
Member

Choose a reason for hiding this comment

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

Can be converted to Fragment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup done

<Text style={[{color: colors.dark, fontWeight: '700'}]}>
{displayName}
Copy link
Member

Choose a reason for hiding this comment

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

I don't see bold text in android?

Copy link
Member

Choose a reason for hiding this comment

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

@MirFahad58 Did you check this on Android?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, I will check it ASAP.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done it.

</Text>
{(pronouns !== undefined && pronouns !== '')
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
&& (
<Text>
{`(${pronouns})`}
</Text>
)}
{(chatUsers.length === 1 || chatUsers.length - 1 === index) && '.'}
{(chatUsers.length - 2 === index && chatUsers.length > 1) && ` ${translate('common.and')} `}
{(chatUsers.length - 2 !== index && chatUsers.length - 1 !== index) && chatUsers.length > 1 && ', '}

MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
</Text>
))}

MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
</Text>
)}
</Text>
);

ChatBeginingText.defaultProps = defaultProps;
ChatBeginingText.propTypes = propTypes;
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
export default compose(memo, withLocalize)(ChatBeginingText);
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
62 changes: 62 additions & 0 deletions src/components/ChatCustomAvatar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, {PureComponent} from 'react';
import {Image, View, StyleSheet} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../styles/styles';
import ActiveRoomAvatar from '../../assets/images/avatars/room.svg';

const propTypes = {
/** Url source for the avatar */
source: PropTypes.string,

/** Extra styles to pass to Image */
imageStyles: PropTypes.arrayOf(PropTypes.object),

/** Extra styles to pass to View wrapper */
containerStyles: PropTypes.oneOfType([PropTypes.object, PropTypes.arrayOf(PropTypes.object)]),

/** Set the size of ChatCustomAvatar */
size: PropTypes.oneOf(['default', 'small']),

/** Whether this avatar is for an custom room */
isCustomChatRoom: PropTypes.bool,

/** Whether this avatar is for an custom room */
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
CustomChatRoomIcon: PropTypes.func,
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved

};

const defaultProps = {
source: '',
imageStyles: [],
containerStyles: [],
size: 'default',
isCustomChatRoom: false,
CustomChatRoomIcon: ActiveRoomAvatar,


MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
};

class ChatCustomAvatar extends PureComponent {
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
render() {
const {CustomChatRoomIcon} = this.props;
if (!this.props.source && !this.props.isCustomChatRoom) {
return null;
}

const imageStyle = [
this.props.size === 'small' ? styles.avatarSmall : styles.avatarNormal,
...this.props.imageStyles,
];
return (
<View pointerEvents="none" style={this.props.containerStyles}>
{this.props.isCustomChatRoom
? <CustomChatRoomIcon style={StyleSheet.flatten(imageStyle)} />
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
: <Image source={{uri: this.props.source}} style={imageStyle} />}
</View>
);
}
}

ChatCustomAvatar.defaultProps = defaultProps;
ChatCustomAvatar.propTypes = propTypes;
export default ChatCustomAvatar;
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 5 additions & 1 deletion src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export default {
sendAttachment: 'Send attachment',
addAttachment: 'Add attachment',
writeSomething: 'Write something...',
sayHello: 'Say hello!',
blockedFromConcierge: 'Communication is barred',
youAppearToBeOffline: 'You appear to be offline.',
fileUploadFailed: 'Upload failed. File is not supported.',
Expand All @@ -143,7 +144,10 @@ export default {
deleteConfirmation: 'Are you sure you want to delete this comment?',
},
reportActionsView: {
beFirstPersonToComment: 'Be the first person to comment',
beginingOfChatHistroy: 'This is the beginning of your chat history with',
beginingOfChatHistroyPrivate: 'This is the beginning of the private',
beginingOfChatHistroyPrivateSectionPart: 'room, invite others by @mentioning them.',

MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
},
reportActionsViewMarkerBadge: {
newMsg: ({count}) => `${count} new message${count > 1 ? 's' : ''}`,
Expand Down
5 changes: 4 additions & 1 deletion src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export default {
sendAttachment: 'Enviar adjunto',
addAttachment: 'Agregar archivo adjunto',
writeSomething: 'Escribe algo...',
sayHello: 'Di hola!',
blockedFromConcierge: 'Comunicación no permitida',
youAppearToBeOffline: 'Parece que estás desconectado.',
fileUploadFailed: 'Subida fallida. El archivo no es compatible.',
Expand All @@ -143,7 +144,9 @@ export default {
deleteConfirmation: '¿Estás seguro de que quieres eliminar este comentario?',
},
reportActionsView: {
beFirstPersonToComment: 'Sé el primero en comentar',
beginingOfChatHistroy: 'This is the beginning of your chat history with',
beginingOfChatHistroyPrivate: 'This is the beginning of the private',
beginingOfChatHistroyPrivateSectionPart: 'room, invite others by @mentioning them.',
Copy link
Member

Choose a reason for hiding this comment

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

Please use proper translations and you can ask them in the Slack channel or on the issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@parasharrajat proper translation on es.js side?
or in English side

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok asked on issue

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as i get answer i will update

},
reportActionsViewMarkerBadge: {
newMsg: ({count}) => `${count} mensaje${count > 1 ? 's' : ''} nuevo${count > 1 ? 's' : ''}`,
Expand Down
8 changes: 8 additions & 0 deletions src/libs/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ function startCurrentDateUpdater() {
});
}

/*
* get the time using user's timezone for chat start screen.
*/
export const currentTimeStampFromProvidedZone = (userTimeZone, format) => {
Copy link
Member

Choose a reason for hiding this comment

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

Don't use export and const for functions. See the Style guide or follow older code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we didn't even use it so I removed that.

const currentTime = moment().tz(userTimeZone);
return moment(currentTime).format(format);
};

/*
* Updates user's timezone, if their timezone is set to automatic and
* is different from current timezone
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class ReportScreen extends React.Component {
reportID={reportID}
reportActions={this.props.reportActions}
report={this.props.report}
isFocused
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need 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.

same reason "need to autoFocus on devices that are supported as discussed in the issue."

Copy link
Member

Choose a reason for hiding this comment

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

If you go through the history of this Component, you will find that we deliberately block focus on mobile devices. If you could please share a link to the discussion, I can reflect that into the comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

@parasharrajat parasharrajat Sep 28, 2021

Choose a reason for hiding this comment

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

If you go through the history of this Component, you will find that we deliberately block focus on mobile devices.

if you want to know more about why we blocked the opened keyboard.

/>
</SwipeableView>
)}
Expand Down
7 changes: 5 additions & 2 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,14 @@ class ReportActionCompose extends React.Component {
this.addEmojiToTextBox = this.addEmojiToTextBox.bind(this);
this.focus = this.focus.bind(this);
this.comment = props.comment;
this.shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus();
this.shouldFocusInputOnScreenFocus = !canFocusInputOnScreenFocus();
Copy link
Member

Choose a reason for hiding this comment

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

Why this change is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

need to autoFocus on devices that are supported as discussed in the issue.

Copy link
Member

Choose a reason for hiding this comment

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

discussion link, please ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

by default keyboard should be open (auto-focused) because the user is here to start the chat.

And I think the same. The keyboard should open when it's a new chat and you see this avatar preview and in all other cases, the keyboard should not open. Thus, this change is going to create a couple of regressions.

Instead, only focus when the chat is new.

MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
this.focusEmojiSearchInput = this.focusEmojiSearchInput.bind(this);
this.measureEmojiPopoverAnchorPosition = this.measureEmojiPopoverAnchorPosition.bind(this);
this.onSelectionChange = this.onSelectionChange.bind(this);
this.emojiPopoverAnchor = null;
this.emojiSearchInput = null;
this.setTextInputRef = this.setTextInputRef.bind(this);
this.getInputPlaceholder = this.getInputPlaceholder.bind(this);

this.state = {
isFocused: this.shouldFocusInputOnScreenFocus,
textInputShouldClear: false,
Expand Down Expand Up @@ -265,6 +264,10 @@ class ReportActionCompose extends React.Component {
return this.props.translate('reportActionCompose.blockedFromConcierge');
}

if (_.size(this.props.reportActions) === 1) {
return this.props.translate('reportActionCompose.sayHello');
}

return this.props.translate('reportActionCompose.writeSomething');
}

Expand Down
65 changes: 62 additions & 3 deletions src/pages/home/report/ReportActionsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import PropTypes from 'prop-types';
import _ from 'underscore';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
import Str from 'expensify-common/lib/str';
import Text from '../../../components/Text';
import {
fetchActions,
Expand All @@ -17,6 +19,7 @@ import {
subscribeToReportTypingEvents,
unsubscribeFromReportChannel,
} from '../../../libs/actions/Report';
import {currentTimeStampFromProvidedZone} from '../../../libs/DateUtils';
import ReportActionItem from './ReportActionItem';
import styles from '../../../styles/styles';
import ReportActionPropTypes from './ReportActionPropTypes';
Expand All @@ -37,6 +40,12 @@ import PopoverReportActionContextMenu from './ContextMenu/PopoverReportActionCon
import variables from '../../../styles/variables';
import MarkerBadge from './MarkerBadge';
import Performance from '../../../libs/Performance';
import ChatAvatars from '../../../components/ChatAvatars';
import {isDefaultRoom} from '../../../libs/reportUtils';
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
import ONYXKEYS from '../../../ONYXKEYS';
import {getPersonalDetailsForLogins} from '../../../libs/OptionsListUtils';
import ChatBeginingText from '../../../components/ChatBeginingText';
import colors from '../../../styles/colors';

const propTypes = {
/** The ID of the report actions will be created for */
Expand Down Expand Up @@ -497,6 +506,36 @@ class ReportActionsView extends React.Component {
}

render() {
const isDefaultChatRoom = isDefaultRoom(this.props.report);
const participants = lodashGet(this.props.report, 'participants', []);
const isMultipleParticipant = participants.length > 1;
const displayNamesWithTooltips = _.map(
getPersonalDetailsForLogins(participants, this.props.personalDetails),
({
displayName, firstName, login, pronouns,
}) => {
const displayNameTrimmed = Str.isSMSLogin(login) ? this.props.toLocalPhone(displayName) : displayName;

return {
displayName: (isMultipleParticipant ? firstName : displayNameTrimmed) || Str.removeSMSDomain(login),
tooltip: Str.removeSMSDomain(login),
pronouns,
};
},
);

const displayTimeWithTimeZone = _.map(
getPersonalDetailsForLogins(participants, this.props.personalDetails),
({timezone}) => ({
timezone: timezone?.selected ?? null,
}),
);
const chatUserTimeZone = isMultipleParticipant ? false : displayTimeWithTimeZone.map(({timezone}) => timezone).join('');

const chatUsers = isDefaultChatRoom ? [{displayName: this.props.report.reportName}] : displayNamesWithTooltips;

const userTime = currentTimeStampFromProvidedZone(chatUserTimeZone, 'hh:mm a');

// Comments have not loaded at all yet do nothing
if (!_.size(this.props.reportActions)) {
return null;
Expand All @@ -506,9 +545,21 @@ class ReportActionsView extends React.Component {
if (_.size(this.props.reportActions) === 1) {
return (
<View style={[styles.chatContent, styles.chatContentEmpty]}>
<Text>
{this.props.translate('reportActionsView.beFirstPersonToComment')}
</Text>
<View style={[styles.justifyContentCenter, styles.alignItemsCenter, {flex: 0.95}]}>
<ChatAvatars
avatarImageURLs={this.props.report.icons}
secondAvatarStyle={[styles.secondAvatarHovered]}
isCustomChatRoom={isDefaultChatRoom}
/>
<ChatBeginingText chatUsers={chatUsers} isDefaultChatRoom={isDefaultChatRoom} />
</View>
<View style={[styles.justifyContentEnd, {flex: 0.05}]}>
{chatUserTimeZone !== false && (
<Text style={{fontSize: variables.fontSizeSmall, color: colors.gray4}}>
{`It's ${userTime} for ${chatUsers?.[0]?.displayName}.`}
</Text>
)}
</View>
</View>
);
}
Expand Down Expand Up @@ -553,4 +604,12 @@ export default compose(
withWindowDimensions,
withDrawerState,
withLocalize,
withOnyx({
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS,
},
policies: {
MirFahad58 marked this conversation as resolved.
Show resolved Hide resolved
key: ONYXKEYS.COLLECTION.POLICY,
},
}),
)(ReportActionsView);
Loading