Skip to content

Commit 7c65b04

Browse files
committed
Merge branch 'main' into revert-27414-@kosmydel/handle-invisible-characters
2 parents 2045847 + 9f9cb09 commit 7c65b04

File tree

11 files changed

+290
-197
lines changed

11 files changed

+290
-197
lines changed

.github/actions/javascript/authorChecklist/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ inputs:
55
description: Auth token for New Expensify Github
66
required: true
77
runs:
8-
using: 'node16'
8+
using: 'node20'
99
main: './index.js'

.github/actions/javascript/authorChecklist/authorChecklist.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import core from '@actions/core';
2-
import github from '@actions/github';
1+
import * as core from '@actions/core';
2+
import * as github from '@actions/github';
33
import escapeRegExp from 'lodash/escapeRegExp';
44
import CONST from '../../../libs/CONST';
55
import GithubUtils from '../../../libs/GithubUtils';

.github/actions/javascript/authorChecklist/index.js

Lines changed: 187 additions & 152 deletions
Large diffs are not rendered by default.

.github/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"extends": "../tsconfig.json",
33
"compilerOptions": {
4-
"incremental": false
4+
"incremental": false,
5+
"esModuleInterop": true
56
}
67
}

.github/workflows/README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
## Important tip for creating GitHub Workflows
44
All inputs and outputs to GitHub Actions and any data passed between jobs or workflows is JSON-encoded (AKA, strings). Keep this in mind whenever writing GitHub workflows – you may need to JSON-decode variables to access them accurately. Here's an example of a common way to misuse GitHub Actions data:
55

6-
7-
86
```yaml
97
name: CI
108
on: pull_request

.github/workflows/authorChecklist.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ jobs:
1111
runs-on: ubuntu-latest
1212
if: github.actor != 'OSBotify' && github.actor != 'imgbot[bot]'
1313
steps:
14+
- uses: actions/checkout@v3
15+
1416
- name: authorChecklist.js
15-
uses: Expensify/App/.github/actions/javascript/authorChecklist@main
17+
uses: ./.github/actions/javascript/authorChecklist
1618
with:
1719
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

src/components/EmojiPicker/EmojiPickerButton.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import * as Expensicons from '@components/Icon/Expensicons';
55
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
66
import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
77
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
8+
import withNavigationFocus from '@components/withNavigationFocus';
9+
import compose from '@libs/compose';
810
import getButtonState from '@libs/getButtonState';
911
import styles from '@styles/styles';
1012
import * as StyleUtils from '@styles/StyleUtils';
@@ -41,6 +43,9 @@ function EmojiPickerButton(props) {
4143
style={({hovered, pressed}) => [styles.chatItemEmojiButton, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed))]}
4244
disabled={props.isDisabled}
4345
onPress={() => {
46+
if (!props.isFocused) {
47+
return;
48+
}
4449
if (!EmojiPickerAction.emojiPickerRef.current.isEmojiPickerVisible) {
4550
EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor.current, undefined, () => {}, props.emojiPickerID);
4651
} else {
@@ -64,4 +69,4 @@ function EmojiPickerButton(props) {
6469
EmojiPickerButton.propTypes = propTypes;
6570
EmojiPickerButton.defaultProps = defaultProps;
6671
EmojiPickerButton.displayName = 'EmojiPickerButton';
67-
export default withLocalize(EmojiPickerButton);
72+
export default compose(withLocalize, withNavigationFocus)(EmojiPickerButton);
Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,59 @@
1-
import lodashGet from 'lodash/get';
2-
import PropTypes from 'prop-types';
31
import React, {memo} from 'react';
42
import {View} from 'react-native';
5-
import _ from 'underscore';
3+
import {ValueOf} from 'type-fest';
4+
import type {AvatarSource} from '@libs/UserUtils';
65
import styles from '@styles/styles';
76
import * as StyleUtils from '@styles/StyleUtils';
87
import themeColors from '@styles/themes/default';
98
import CONST from '@src/CONST';
109
import Avatar from './Avatar';
11-
import avatarPropTypes from './avatarPropTypes';
1210
import UserDetailsTooltip from './UserDetailsTooltip';
1311

14-
const propTypes = {
12+
type SubAvatar = {
13+
/** Avatar source to display */
14+
source?: AvatarSource;
15+
16+
/** Denotes whether it is an avatar or a workspace avatar */
17+
type?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE;
18+
19+
/** Owner of the avatar. If user, displayName. If workspace, policy name */
20+
name?: string;
21+
22+
/** Avatar id */
23+
id?: number | string;
24+
25+
/** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */
26+
fallbackIcon?: AvatarSource;
27+
};
28+
29+
type SubscriptAvatarProps = {
1530
/** Avatar URL or icon */
16-
mainAvatar: avatarPropTypes,
31+
mainAvatar?: SubAvatar;
1732

1833
/** Subscript avatar URL or icon */
19-
secondaryAvatar: avatarPropTypes,
34+
secondaryAvatar?: SubAvatar;
2035

2136
/** Set the size of avatars */
22-
size: PropTypes.oneOf(_.values(CONST.AVATAR_SIZE)),
37+
size?: ValueOf<typeof CONST.AVATAR_SIZE>;
2338

2439
/** Background color used for subscript avatar border */
25-
backgroundColor: PropTypes.string,
40+
backgroundColor?: string;
2641

2742
/** Removes margin from around the avatar, used for the chat view */
28-
noMargin: PropTypes.bool,
43+
noMargin?: boolean;
2944

3045
/** Whether to show the tooltip */
31-
showTooltip: PropTypes.bool,
32-
};
33-
34-
const defaultProps = {
35-
size: CONST.AVATAR_SIZE.DEFAULT,
36-
backgroundColor: themeColors.componentBG,
37-
mainAvatar: {},
38-
secondaryAvatar: {},
39-
noMargin: false,
40-
showTooltip: true,
46+
showTooltip?: boolean;
4147
};
4248

43-
function SubscriptAvatar({size, backgroundColor, mainAvatar, secondaryAvatar, noMargin, showTooltip}) {
49+
function SubscriptAvatar({
50+
mainAvatar = {},
51+
secondaryAvatar = {},
52+
size = CONST.AVATAR_SIZE.DEFAULT,
53+
backgroundColor = themeColors.componentBG,
54+
noMargin = false,
55+
showTooltip = true,
56+
}: SubscriptAvatarProps) {
4457
const isSmall = size === CONST.AVATAR_SIZE.SMALL;
4558
const subscriptStyle = size === CONST.AVATAR_SIZE.SMALL_NORMAL ? styles.secondAvatarSubscriptSmallNormal : styles.secondAvatarSubscript;
4659
const containerStyle = StyleUtils.getContainerStyles(size);
@@ -49,14 +62,14 @@ function SubscriptAvatar({size, backgroundColor, mainAvatar, secondaryAvatar, no
4962
<View style={[containerStyle, noMargin ? styles.mr0 : {}]}>
5063
<UserDetailsTooltip
5164
shouldRender={showTooltip}
52-
accountID={lodashGet(mainAvatar, 'id', -1)}
65+
accountID={mainAvatar.id ?? -1}
5366
icon={mainAvatar}
5467
>
5568
<View>
5669
<Avatar
5770
containerStyles={StyleUtils.getWidthAndHeightStyle(StyleUtils.getAvatarSize(size || CONST.AVATAR_SIZE.DEFAULT))}
5871
source={mainAvatar.source}
59-
size={size || CONST.AVATAR_SIZE.DEFAULT}
72+
size={size}
6073
name={mainAvatar.name}
6174
type={mainAvatar.type}
6275
fallbackIcon={mainAvatar.fallbackIcon}
@@ -65,7 +78,7 @@ function SubscriptAvatar({size, backgroundColor, mainAvatar, secondaryAvatar, no
6578
</UserDetailsTooltip>
6679
<UserDetailsTooltip
6780
shouldRender={showTooltip}
68-
accountID={lodashGet(secondaryAvatar, 'id', -1)}
81+
accountID={secondaryAvatar.id ?? -1}
6982
icon={secondaryAvatar}
7083
>
7184
<View
@@ -93,6 +106,5 @@ function SubscriptAvatar({size, backgroundColor, mainAvatar, secondaryAvatar, no
93106
}
94107

95108
SubscriptAvatar.displayName = 'SubscriptAvatar';
96-
SubscriptAvatar.propTypes = propTypes;
97-
SubscriptAvatar.defaultProps = defaultProps;
109+
98110
export default memo(SubscriptAvatar);

src/libs/UserUtils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import hashCode from './hashCode';
88

99
type AvatarRange = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24;
1010

11+
type AvatarSource = React.FC<SvgProps> | string;
12+
1113
type LoginListIndicator = ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | '';
1214

1315
/**
@@ -202,3 +204,4 @@ export {
202204
getFullSizeAvatar,
203205
generateAccountID,
204206
};
207+
export type {AvatarSource};

src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.js

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import PropTypes from 'prop-types';
2-
import React, {useMemo} from 'react';
2+
import React, {useCallback, useEffect, useMemo} from 'react';
33
import {View} from 'react-native';
44
import {withOnyx} from 'react-native-onyx';
55
import _ from 'underscore';
@@ -9,9 +9,12 @@ import * as Expensicons from '@components/Icon/Expensicons';
99
import PopoverMenu from '@components/PopoverMenu';
1010
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
1111
import Tooltip from '@components/Tooltip/PopoverAnchorTooltip';
12+
import withNavigationFocus from '@components/withNavigationFocus';
1213
import useLocalize from '@hooks/useLocalize';
14+
import usePrevious from '@hooks/usePrevious';
1315
import useWindowDimensions from '@hooks/useWindowDimensions';
1416
import * as Browser from '@libs/Browser';
17+
import compose from '@libs/compose';
1518
import Permissions from '@libs/Permissions';
1619
import * as ReportUtils from '@libs/ReportUtils';
1720
import styles from '@styles/styles';
@@ -84,6 +87,9 @@ const propTypes = {
8487
// eslint-disable-next-line react/forbid-prop-types
8588
current: PropTypes.object,
8689
}).isRequired,
90+
91+
/** Whether navigation is focused */
92+
isFocused: PropTypes.bool.isRequired,
8793
};
8894

8995
const defaultProps = {
@@ -116,6 +122,7 @@ function AttachmentPickerWithMenuItems({
116122
onAddActionPressed,
117123
onItemSelected,
118124
actionButtonRef,
125+
isFocused,
119126
}) {
120127
const {translate} = useLocalize();
121128
const {windowHeight} = useWindowDimensions();
@@ -164,10 +171,33 @@ function AttachmentPickerWithMenuItems({
164171
];
165172
}, [betas, report, reportID, translate]);
166173

167-
const onPopoverMenuClose = () => {
174+
const onPopoverMenuClose = useCallback(() => {
168175
setMenuVisibility(false);
169176
onMenuClosed();
170-
};
177+
}, [onMenuClosed, setMenuVisibility]);
178+
179+
const prevIsFocused = usePrevious(isFocused);
180+
181+
/**
182+
* Check if current screen is inactive and previous screen is active.
183+
* Used to close already opened popover menu when any other page is opened over current page.
184+
*
185+
* @return {Boolean}
186+
*/
187+
const didScreenBecomeInactive = useCallback(
188+
() =>
189+
// When any other page is opened over LHN
190+
!isFocused && prevIsFocused,
191+
[isFocused, prevIsFocused],
192+
);
193+
194+
// When the navigation is focused, we want to close the popover menu.
195+
useEffect(() => {
196+
if (!didScreenBecomeInactive()) {
197+
return;
198+
}
199+
onPopoverMenuClose();
200+
}, [didScreenBecomeInactive, onPopoverMenuClose]);
171201

172202
return (
173203
<AttachmentPicker>
@@ -239,6 +269,10 @@ function AttachmentPickerWithMenuItems({
239269
ref={actionButtonRef}
240270
onPress={(e) => {
241271
e.preventDefault();
272+
if (!isFocused) {
273+
return;
274+
}
275+
242276
onAddActionPressed();
243277

244278
// Drop focus to avoid blue focus ring.
@@ -256,7 +290,7 @@ function AttachmentPickerWithMenuItems({
256290
</View>
257291
<PopoverMenu
258292
animationInTiming={CONST.ANIMATION_IN_TIMING}
259-
isVisible={isMenuVisible}
293+
isVisible={isMenuVisible && isFocused}
260294
onClose={onPopoverMenuClose}
261295
onItemSelected={(item, index) => {
262296
setMenuVisibility(false);
@@ -286,8 +320,11 @@ AttachmentPickerWithMenuItems.propTypes = propTypes;
286320
AttachmentPickerWithMenuItems.defaultProps = defaultProps;
287321
AttachmentPickerWithMenuItems.displayName = 'AttachmentPickerWithMenuItems';
288322

289-
export default withOnyx({
290-
betas: {
291-
key: ONYXKEYS.BETAS,
292-
},
293-
})(AttachmentPickerWithMenuItems);
323+
export default compose(
324+
withNavigationFocus,
325+
withOnyx({
326+
betas: {
327+
key: ONYXKEYS.BETAS,
328+
},
329+
}),
330+
)(AttachmentPickerWithMenuItems);

src/styles/StyleUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,8 +1292,8 @@ function getAmountFontSizeAndLineHeight(baseFontSize: number, baseLineHeight: nu
12921292
/**
12931293
* Returns container styles for showing the icons in MultipleAvatars/SubscriptAvatar
12941294
*/
1295-
function getContainerStyles(size: string, isInReportAction = false): Array<ViewStyle | CSSProperties> {
1296-
let containerStyles: Array<ViewStyle | CSSProperties>;
1295+
function getContainerStyles(size: string, isInReportAction = false): ViewStyle[] {
1296+
let containerStyles: ViewStyle[];
12971297

12981298
switch (size) {
12991299
case CONST.AVATAR_SIZE.SMALL:

0 commit comments

Comments
 (0)