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

Clean up Thread UI #18876

Merged
merged 14 commits into from
May 16, 2023
2 changes: 2 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,8 @@ const CONST = {
// Furthermore, applying markup is very resource-consuming, so let's set a slightly lower limit for that
MAX_MARKUP_LENGTH: 10000,

MAX_THREAD_REPLIES_PREVIEW: 99,

FORM_CHARACTER_LIMIT: 50,
LEGAL_NAMES_CHARACTER_LIMIT: 150,
WORKSPACE_NAME_CHARACTER_LIMIT: 80,
Expand Down
8 changes: 6 additions & 2 deletions src/components/MultipleAvatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ const propTypes = {

/** Whether #focus mode is on */
isFocusMode: PropTypes.bool,

/** Whether avatars are displayed within a reportAction */
isInReportAction: PropTypes.bool,
};

const defaultProps = {
Expand All @@ -52,6 +55,7 @@ const defaultProps = {
isHovered: false,
isPressed: false,
isFocusMode: false,
isInReportAction: false,
};

const MultipleAvatars = (props) => {
Expand Down Expand Up @@ -108,7 +112,7 @@ const MultipleAvatars = (props) => {
style={[
styles.justifyContentCenter,
styles.alignItemsCenter,
StyleUtils.getHorizontalStackedAvatarBorderStyle(props.isHovered, props.isPressed),
StyleUtils.getHorizontalStackedAvatarBorderStyle(props.isHovered, props.isPressed, props.isInReportAction),
StyleUtils.getHorizontalStackedAvatarStyle(index, overlapSize, oneAvatarBorderWidth, oneAvatarSize.width),
icon.type === CONST.ICON_TYPE_WORKSPACE ? StyleUtils.getAvatarBorderRadius(props.size, icon.type) : {},
]}
Expand All @@ -127,7 +131,7 @@ const MultipleAvatars = (props) => {
style={[
styles.alignItemsCenter,
styles.justifyContentCenter,
StyleUtils.getHorizontalStackedAvatarBorderStyle(props.isHovered, props.isPressed),
StyleUtils.getHorizontalStackedAvatarBorderStyle(props.isHovered, props.isPressed, props.isInReportAction),

// Set overlay background color with RGBA value so that the text will not inherit opacity
StyleUtils.getBackgroundColorWithOpacityStyle(themeColors.overlay, variables.overlayOpacity),
Expand Down
5 changes: 3 additions & 2 deletions src/components/withLocalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,11 @@ class LocaleContextProvider extends React.Component {
/**
* @param {String} datetime - ISO-formatted datetime string
* @param {Boolean} [includeTimezone]
* @param {Boolean} isLowercase
* @returns {String}
*/
datetimeToCalendarTime(datetime, includeTimezone) {
return DateUtils.datetimeToCalendarTime(this.props.preferredLocale, datetime, includeTimezone, lodashGet(this.props, 'currentUserPersonalDetails.timezone.selected'));
datetimeToCalendarTime(datetime, includeTimezone, isLowercase = false) {
return DateUtils.datetimeToCalendarTime(this.props.preferredLocale, datetime, includeTimezone, lodashGet(this.props, 'currentUserPersonalDetails.timezone.selected'), isLowercase);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,7 @@ export default {
deletedMessage: '[Deleted message]',
},
threads: {
lastReply: 'Last Reply',
lastReply: 'Last reply',
replies: 'Replies',
reply: 'Reply',
},
Expand Down
15 changes: 11 additions & 4 deletions src/libs/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,25 @@ function getLocalMomentFromDatetime(locale, datetime, currentSelectedTimezone =
* @param {String} datetime
* @param {Boolean} includeTimeZone
* @param {String} [currentSelectedTimezone]
* @param {Boolean} isLowercase
*
* @returns {String}
*/
function datetimeToCalendarTime(locale, datetime, includeTimeZone = false, currentSelectedTimezone) {
function datetimeToCalendarTime(locale, datetime, includeTimeZone = false, currentSelectedTimezone, isLowercase = false) {
const date = getLocalMomentFromDatetime(locale, datetime, currentSelectedTimezone);
const tz = includeTimeZone ? ' [UTC]Z' : '';

const todayAt = Localize.translate(locale, 'common.todayAt');
const tomorrowAt = Localize.translate(locale, 'common.tomorrowAt');
const yesterdayAt = Localize.translate(locale, 'common.yesterdayAt');
let todayAt = Localize.translate(locale, 'common.todayAt');
let tomorrowAt = Localize.translate(locale, 'common.tomorrowAt');
let yesterdayAt = Localize.translate(locale, 'common.yesterdayAt');
const at = Localize.translate(locale, 'common.conjunctionAt');

if (isLowercase) {
todayAt = todayAt.toLowerCase();
tomorrowAt = tomorrowAt.toLowerCase();
yesterdayAt = yesterdayAt.toLowerCase();
}

return moment(date).calendar({
sameDay: `[${todayAt}] LT${tz}`,
nextDay: `[${tomorrowAt}] LT${tz}`,
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ class ReportActionItem extends Component {
childReportID={`${this.props.action.childReportID}`}
numberOfReplies={this.props.action.childVisibleActionCount || 0}
mostRecentReply={`${this.props.action.childLastVisibleActionCreated}`}
isHovered={hovered}
icons={ReportUtils.getIconsForParticipants(oldestFourEmails, this.props.personalDetails)}
/>
)}
Expand Down
79 changes: 47 additions & 32 deletions src/pages/home/report/ReportActionItemThread.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import _ from 'underscore';
import styles from '../../../styles/styles';
import * as Report from '../../../libs/actions/Report';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import CONST from '../../../CONST';
import avatarPropTypes from '../../../components/avatarPropTypes';
import MultipleAvatars from '../../../components/MultipleAvatars';
import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
import compose from '../../../libs/compose';

const propTypes = {
/** List of participant icons for the thread */
Expand All @@ -24,43 +26,56 @@ const propTypes = {
/** ID of child thread report */
childReportID: PropTypes.string.isRequired,

/** localization props */
/** Whether the thread item / message is being hovered */
isHovered: PropTypes.bool.isRequired,

...withLocalizePropTypes,
...windowDimensionsPropTypes,
};

const ReportActionItemThread = (props) => (
<View style={[styles.chatItemMessage]}>
<Pressable
onPress={() => {
Report.openReport(props.childReportID);
Navigation.navigate(ROUTES.getReportRoute(props.childReportID));
}}
>
<View style={[styles.flexRow, styles.alignItemsCenter, styles.mt2]}>
<MultipleAvatars
size={CONST.AVATAR_SIZE.SMALLER}
icons={props.icons}
shouldStackHorizontally
avatarTooltips={_.map(props.icons, (icon) => icon.name)}
/>
<View style={[styles.flexRow, styles.lh140Percent, styles.alignItemsEnd]}>
<Text
selectable={false}
style={[styles.link, styles.ml2, styles.h4]}
>
{`${props.numberOfReplies} ${props.numberOfReplies === 1 ? props.translate('threads.reply') : props.translate('threads.replies')}`}
</Text>
<Text
selectable={false}
style={[styles.ml2, styles.textMicroSupporting]}
>{`${props.translate('threads.lastReply')} ${props.datetimeToCalendarTime(props.mostRecentReply)}`}</Text>
const ReportActionItemThread = (props) => {
const numberOfRepliesText = props.numberOfReplies > CONST.MAX_THREAD_REPLIES_PREVIEW ? `${CONST.MAX_THREAD_REPLIES_PREVIEW}+` : `${props.numberOfReplies}`;
const replyText = props.numberOfReplies === 1 ? props.translate('threads.reply') : props.translate('threads.replies');

const timeStamp = props.datetimeToCalendarTime(props.mostRecentReply, false, true);

return (
<View style={[styles.chatItemMessage]}>
<Pressable
onPress={() => {
Report.openReport(props.childReportID);
Navigation.navigate(ROUTES.getReportRoute(props.childReportID));
}}
>
<View style={[styles.flexRow, styles.alignItemsCenter, styles.mt2]}>
<MultipleAvatars
size={CONST.AVATAR_SIZE.SMALL}
icons={props.icons}
shouldStackHorizontally
avatarTooltips={_.map(props.icons, (icon) => icon.name)}
isHovered={props.isHovered}
isInReportAction
/>
<View style={[styles.flex1, styles.flexRow, styles.lh140Percent, styles.alignItemsEnd]}>
<Text
selectable={false}
style={[styles.link, styles.ml2, styles.h4, styles.noWrap]}
>
{`${numberOfRepliesText} ${replyText}`}
</Text>
<Text
selectable={false}
numberOfLines={1}
style={[styles.ml2, styles.textMicroSupporting]}
>{`${props.translate('threads.lastReply')} ${timeStamp}`}</Text>
</View>
</View>
</View>
</Pressable>
</View>
);
</Pressable>
</View>
);
};

ReportActionItemThread.propTypes = propTypes;
ReportActionItemThread.displayName = 'ReportActionItemThread';

export default withLocalize(ReportActionItemThread);
export default compose(withLocalize, withWindowDimensions)(ReportActionItemThread);
9 changes: 5 additions & 4 deletions src/styles/StyleUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ function getAvatarBorderWidth(size) {
[CONST.AVATAR_SIZE.SMALL_SUBSCRIPT]: 2,
[CONST.AVATAR_SIZE.MID_SUBSCRIPT]: 2,
[CONST.AVATAR_SIZE.SUBSCRIPT]: 2,
[CONST.AVATAR_SIZE.SMALL]: 3,
[CONST.AVATAR_SIZE.SMALL]: 2,
grgia marked this conversation as resolved.
Show resolved Hide resolved
[CONST.AVATAR_SIZE.SMALLER]: 2,
[CONST.AVATAR_SIZE.LARGE]: 4,
[CONST.AVATAR_SIZE.MEDIUM]: 3,
Expand Down Expand Up @@ -807,17 +807,18 @@ function getKeyboardShortcutsModalWidth(isSmallScreenWidth) {
/**
* @param {Boolean} isHovered
* @param {Boolean} isPressed
* @param {Boolean} isInReportAction
* @returns {Object}
*/
function getHorizontalStackedAvatarBorderStyle(isHovered, isPressed) {
function getHorizontalStackedAvatarBorderStyle(isHovered, isPressed, isInReportAction = false) {
let backgroundColor = themeColors.appBG;

if (isHovered) {
backgroundColor = themeColors.border;
backgroundColor = isInReportAction ? themeColors.highlightBG : themeColors.border;
}

if (isPressed) {
backgroundColor = themeColors.buttonPressedBG;
backgroundColor = isInReportAction ? themeColors.highlightBG : themeColors.buttonPressedBG;
}

return {
Expand Down