Skip to content

Commit

Permalink
Merge pull request #15982 from Expensify/update-staging-from-main
Browse files Browse the repository at this point in the history
Update version to 1.2.85-0 on staging
  • Loading branch information
OSBotify authored Mar 15, 2023
2 parents 2c42e72 + 5743a10 commit b54c2ef
Show file tree
Hide file tree
Showing 19 changed files with 284 additions and 46 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001028401
versionName "1.2.84-1"
versionCode 1001028500
versionName "1.2.85-0"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()

if (isNewArchitectureEnabled()) {
Expand Down
2 changes: 1 addition & 1 deletion contributingGuides/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ A job could be fixing a bug or working on a new feature. There are two ways you
This is the most common scenario for contributors. The Expensify team posts new jobs to the Upwork job list [here](https://www.upwork.com/ab/jobs/search/?q=Expensify%20React%20Native&sort=recency&user_location_match=2) (you must be signed in to Upwork to view jobs). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. You can also view all open jobs in the Expensify/App GH repository by searching for GH issues with the [`Help Wanted` label](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22). Lastly, you can follow the [@ExpensifyOSS](https://twitter.com/ExpensifyOSS) Twitter account to see a live feed of jobs that are posted.

#### Proposing a job that Expensify hasn't posted
It’s possible that you found a new bug that we haven’t posted as a job to the [GitHub repository](https://github.com/Expensify/App/issues?q=is%3Aissue). This is an opportunity to propose a job. If it's a valid job proposal that we choose to implement by deploying it to production — either internally or via an external contributor — then we will compensate you $250 for identifying and proposing the bug (we do not compensate for reporting new feature requests). If the bug is fixed by a PR that is not associated with your bug report, then you will not be eligible for the corresponding compensation unless you can find the PR that fixed it and prove your proposal came first.
It’s possible that you found a new bug that we haven’t posted as a job to the [GitHub repository](https://github.com/Expensify/App/issues?q=is%3Aissue). This is an opportunity to propose a job. If it's a valid job proposal that we choose to implement by deploying it to production — either internally or via an external contributor — then we will compensate you $250 for identifying and proposing the bug (we do not compensate for reporting new feature requests). If the bug is fixed by a PR that is not associated with your bug report, then you will not be eligible for the corresponding compensation unless you can find the PR that fixed it and prove your bug report came first.
- Note: If you get assigned the job you proposed **and** you complete the job, this $250 for identifying the improvement is *in addition to* the reward you will be paid for completing the job.
- Note about proposed bugs: Expensify has the right not to pay the $250 reward if the suggested bug has already been reported. Following, if more than one contributor proposes the same bug, the contributor who posted it first in the [#expensify-bugs](https://expensify.slack.com/archives/C049HHMV9SM) Slack channel is the one who is eligible for the bonus.
- Note: whilst you may optionally propose a solution for that job on Slack, solutions are ultimately reviewed in GitHub. The onus is on you to propose the solution on GitHub, and/or ensure the issue creator will include a link to your proposal.
Expand Down
4 changes: 2 additions & 2 deletions ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.84</string>
<string>1.2.85</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
Expand All @@ -30,7 +30,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.2.84.1</string>
<string>1.2.85.0</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
4 changes: 2 additions & 2 deletions ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.84</string>
<string>1.2.85</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.2.84.1</string>
<string>1.2.85.0</string>
</dict>
</plist>
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.2.84-1",
"version": "1.2.85-0",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down
46 changes: 45 additions & 1 deletion src/components/AvatarCropModal/AvatarCropModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ const AvatarCropModal = (props) => {
const translateSlider = useSharedValue(0);
const isPressableEnabled = useSharedValue(true);

// The previous offset values are maintained to recalculate the offset value in proportion
// to the container size, especially when the window size is first decreased and then increased
const prevMaxOffsetX = useSharedValue(0);
const prevMaxOffsetY = useSharedValue(0);

const [imageContainerSize, setImageContainerSize] = useState(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE);
const [sliderContainerSize, setSliderContainerSize] = useState(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE);
const [isImageContainerInitialized, setIsImageContainerInitialized] = useState(false);
Expand All @@ -82,7 +87,10 @@ const AvatarCropModal = (props) => {
const initializeImageContainer = useCallback((event) => {
setIsImageContainerInitialized(true);
const {height, width} = event.nativeEvent.layout;
setImageContainerSize(Math.floor(Math.min(height - styles.imageCropRotateButton.height, width)));

// Even if the browser height is reduced too much, the relative height should not be negative
const relativeHeight = Math.max(height - styles.imageCropRotateButton.height, CONST.AVATAR_CROP_MODAL.INITIAL_SIZE);
setImageContainerSize(Math.floor(Math.min(relativeHeight, width)));
}, [props.isSmallScreenWidth]);

// An onLayout callback, that initializes the slider container size, for proper render of a slider
Expand All @@ -99,6 +107,8 @@ const AvatarCropModal = (props) => {
scale.value = CONST.AVATAR_CROP_MODAL.MIN_SCALE;
rotation.value = 0;
translateSlider.value = 0;
prevMaxOffsetX.value = 0;
prevMaxOffsetY.value = 0;
setImageContainerSize(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE);
setSliderContainerSize(CONST.AVATAR_CROP_MODAL.INITIAL_SIZE);
setIsImageContainerInitialized(false);
Expand Down Expand Up @@ -166,6 +176,8 @@ const AvatarCropModal = (props) => {
const maxOffsetY = (height - imageContainerSize) / 2;
translateX.value = clamp(offsetX, [maxOffsetX * -1, maxOffsetX]);
translateY.value = clamp(offsetY, [maxOffsetY * -1, maxOffsetY]);
prevMaxOffsetX.value = maxOffsetX;
prevMaxOffsetY.value = maxOffsetY;
}, [imageContainerSize, scale, clamp]);

/**
Expand Down Expand Up @@ -199,6 +211,38 @@ const AvatarCropModal = (props) => {
},
}, [imageContainerSize, updateImageOffset, translateX, translateY]);

// This effect is needed to recalculate the maximum offset values
// when the browser window is resized.
useEffect(() => {
// If no panning has happened and the value is 0, do an early return.
if (!prevMaxOffsetX.value && !prevMaxOffsetY.value) {
return;
}
const {height, width} = getDisplayedImageSize();
const maxOffsetX = (width - imageContainerSize) / 2;
const maxOffsetY = (height - imageContainerSize) / 2;

// Since interpolation is expensive, we only want to do it if
// image has been panned across X or Y axis by the user.
if (prevMaxOffsetX) {
translateX.value = interpolate(
translateX.value,
[prevMaxOffsetX.value * -1, prevMaxOffsetX.value],
[maxOffsetX * -1, maxOffsetX],
);
}

if (prevMaxOffsetY) {
translateY.value = interpolate(
translateY.value,
[prevMaxOffsetY.value * -1, prevMaxOffsetY.value],
[maxOffsetY * -1, maxOffsetY],
);
}
prevMaxOffsetX.value = maxOffsetX;
prevMaxOffsetY.value = maxOffsetY;
}, [imageContainerSize]);

/**
* Calculates new scale value and updates images offset to ensure
* that image stays in the center of the container after changing scale.
Expand Down
51 changes: 38 additions & 13 deletions src/components/Picker.js → src/components/Picker/Picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import React, {PureComponent} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import RNPickerSelect from 'react-native-picker-select';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
import FormHelpMessage from './FormHelpMessage';
import Text from './Text';
import styles from '../styles/styles';
import themeColors from '../styles/themes/default';
import {ScrollContext} from './ScrollViewWithContext';
import Icon from '../Icon';
import * as Expensicons from '../Icon/Expensicons';
import FormHelpMessage from '../FormHelpMessage';
import Text from '../Text';
import styles from '../../styles/styles';
import themeColors from '../../styles/themes/default';
import {ScrollContext} from '../ScrollViewWithContext';

const propTypes = {
/** Picker label */
Expand Down Expand Up @@ -75,6 +75,9 @@ const propTypes = {
current: PropTypes.element,
}),
]),

/** Additional events passed to the core Picker for specific platforms such as web */
additionalPickerEvents: PropTypes.func,
};

const defaultProps = {
Expand All @@ -97,16 +100,19 @@ const defaultProps = {
),
onBlur: () => {},
innerRef: () => {},
additionalPickerEvents: () => {},
};

class Picker extends PureComponent {
constructor(props) {
super(props);
this.state = {
isOpen: false,
isHighlighted: false,
};

this.onInputChange = this.onInputChange.bind(this);
this.enableHighlight = this.enableHighlight.bind(this);
this.disableHighlight = this.disableHighlight.bind(this);

// Windows will reuse the text color of the select for each one of the options
// so we might need to color accordingly so it doesn't blend with the background.
Expand Down Expand Up @@ -151,6 +157,18 @@ class Picker extends PureComponent {
this.props.onInputChange(this.props.items[0].value, 0);
}

enableHighlight() {
this.setState({
isHighlighted: true,
});
}

disableHighlight() {
this.setState({
isHighlighted: false,
});
}

render() {
const hasError = !_.isEmpty(this.props.errorText);

Expand All @@ -161,7 +179,7 @@ class Picker extends PureComponent {
styles.pickerContainer,
this.props.isDisabled && styles.inputDisabled,
...this.props.containerStyles,
this.state.isOpen && styles.borderColorFocus,
this.state.isHighlighted && styles.borderColorFocus,
hasError && styles.borderColorDanger,
]}
>
Expand All @@ -184,15 +202,22 @@ class Picker extends PureComponent {
Icon={() => this.props.icon(this.props.size)}
disabled={this.props.isDisabled}
fixAndroidTouchableBug
onOpen={() => this.setState({isOpen: true})}
onClose={() => this.setState({isOpen: false})}
onOpen={this.enableHighlight}
onClose={this.disableHighlight}
textInputProps={{allowFontScaling: false}}
pickerProps={{
onFocus: () => this.setState({isOpen: true}),
onFocus: this.enableHighlight,
onBlur: () => {
this.setState({isOpen: false});
this.disableHighlight();
this.props.onBlur();
},
...this.props.additionalPickerEvents(
this.enableHighlight,
(value, index) => {
this.onInputChange(value, index);
this.disableHighlight();
},
),
}}
ref={(el) => {
if (!_.isFunction(this.props.innerRef)) {
Expand Down
19 changes: 19 additions & 0 deletions src/components/Picker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, {forwardRef} from 'react';
import BasePicker from './Picker';

const additionalPickerEvents = (onMouseDown, onChange) => ({
onMouseDown,
onChange: (e) => {
if (e.target.selectedIndex === undefined) {
return;
}
const index = e.target.selectedIndex;
const value = e.target.options[index].value;
onChange(value, index);
},
});

export default forwardRef((props, ref) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<BasePicker {...props} additionalPickerEvents={additionalPickerEvents} ref={ref} />
));
7 changes: 7 additions & 0 deletions src/components/Picker/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React, {forwardRef} from 'react';
import BasePicker from './Picker';

export default forwardRef((props, ref) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<BasePicker {...props} ref={ref} />
));
48 changes: 31 additions & 17 deletions src/components/Reactions/EmojiReactionBubble.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import withCurrentUserPersonalDetails, {
withCurrentUserPersonalDetailsPropTypes,
} from '../withCurrentUserPersonalDetails';
import * as Report from '../../libs/actions/Report';
import Tooltip from '../Tooltip';
import ReactionTooltipContent from './ReactionTooltipContent';

const propTypes = {
emojiName: PropTypes.string.isRequired,

/**
* The emoji codes to display in the bubble.
*/
Expand All @@ -35,7 +39,7 @@ const propTypes = {
/**
* The account ids of the users who reacted.
*/
reactionUsers: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
reactionUsers: PropTypes.arrayOf(PropTypes.string),

/**
* The default size of the reaction bubble is defined
Expand All @@ -59,31 +63,41 @@ const defaultProps = {
const EmojiReactionBubble = (props) => {
const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, props.reactionUsers);
return (
<Pressable
style={({hovered}) => [
styles.emojiReactionBubble,
StyleUtils.getEmojiReactionBubbleStyle(hovered, hasUserReacted, props.sizeScale),
]}
onPress={props.onPress}
onLongPress={props.onReactionListOpen}
<Tooltip
renderTooltipContent={() => (
<ReactionTooltipContent
emojiName={props.emojiName}
emojiCodes={props.emojiCodes}
accountIDs={props.reactionUsers}
/>
)}
>
<Text style={[
styles.emojiReactionText,
StyleUtils.getEmojiReactionTextStyle(props.sizeScale),
]}
<Pressable
style={({hovered, pressed}) => [
styles.emojiReactionBubble,
StyleUtils.getEmojiReactionBubbleStyle(hovered || pressed, hasUserReacted, props.sizeScale),
]}
onPress={props.onPress}
onLongPress={props.onReactionListOpen}
>
{props.emojiCodes.join('')}
</Text>
{props.count > 0 && (
<Text style={[
styles.emojiReactionText,
StyleUtils.getEmojiReactionTextStyle(props.sizeScale),
]}
>
{props.emojiCodes.join('')}
</Text>
{props.count > 0 && (
<Text style={[
styles.reactionCounterText,
StyleUtils.getEmojiReactionCounterTextStyle(hasUserReacted, props.sizeScale),
]}
>
{props.count}
</Text>
)}
</Pressable>
)}
</Pressable>
</Tooltip>
);
};

Expand Down
Loading

0 comments on commit b54c2ef

Please sign in to comment.