Skip to content

Commit

Permalink
Merge branch 'main' into fix/20759-remove-new-notifiers-when-deleting…
Browse files Browse the repository at this point in the history
…-last-message
  • Loading branch information
DanutGavrus committed Jun 28, 2023
2 parents 2465c0f + 09a7432 commit cb5b492
Show file tree
Hide file tree
Showing 14 changed files with 316 additions and 192 deletions.
10 changes: 5 additions & 5 deletions docs/articles/other/Referral-Program.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
---
title: Expensify Referral Program
description: using joining links to track them, we'll pay referrers if their referral adopts Expensify.
description: Send your joining link, submit a receipt or invoice, and we'll pay you if your referral adopts Expensify.
---
<!-- The lines above are required by Jekyll to process the .md file -->

# About

Expensify has grown thanks to our users who love Expensify so much that they tell their friends, colleagues, managers, and fellow business founders to use it, too. This is how we’ve acquired more customers than all other expense management companies combined.
Expensify has grown thanks to our users who love Expensify so much that they tell their friends, colleagues, managers, and fellow business founders to use it, too.

As a thank you, every time you bring a new user into the platform who directly or indirectly leads to the adoption of a paid annual plan on Expensify, you will earn $250.

# How-to get paid for referring people to Expensify
# How to get paid for referring people to Expensify

1. Submit a report or invoice, or share your referral link with anyone you know who is spending too much time on expenses, or works at a company that could benefit from using Expensify.

2. You’ll get $250 for any referred business that commits to an annual subscription, has 2 or more active users, and makes two monthly payments.
2. You will get $250 for any referred business that commits to an annual subscription, has 2 or more active users, and makes two monthly payments.

That’s right! You can refer anyone working at any company you know.

Expand Down Expand Up @@ -50,4 +50,4 @@ Please send a message to [email protected] with the billing owner of the c

Expensify members who are opted-in for our newsletters will have received an email containing their unique referral link.

On the mobile app, go to Settings > Invite a Friend > Share Invite Link to retrieve your referral link.
On the mobile app, go to **Settings** > **Invite a Friend** > **Share Invite Link** to retrieve your referral link.
87 changes: 87 additions & 0 deletions patches/react-native+0.71.2-alpha.3.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
index 2f48f9e..6418c76 100644
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
@@ -65,6 +65,7 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
_subscriptions: Array<EventSubscription> = [];
viewRef: {current: React.ElementRef<typeof View> | null, ...};
_initialFrameHeight: number = 0;
+ _bottom: number = 0;

constructor(props: Props) {
super(props);
@@ -107,18 +108,20 @@ class KeyboardAvoidingView extends React.Component<Props, State> {

_onKeyboardChange = (event: ?KeyboardEvent) => {
this._keyboardEvent = event;
+ // $FlowFixMe[unused-promise]
this._updateBottomIfNecessary();
};

_onLayout = async (event: ViewLayoutEvent) => {
- const wasFrameNull = this._frame == null;
+ const oldFrame = this._frame;
this._frame = event.nativeEvent.layout;
if (!this._initialFrameHeight) {
// save the initial frame height, before the keyboard is visible
this._initialFrameHeight = this._frame.height;
}

- if (wasFrameNull) {
+ // update bottom height for the first time or when the height is changed
+ if (!oldFrame || oldFrame.height !== this._frame.height) {
await this._updateBottomIfNecessary();
}

@@ -127,20 +130,31 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
}
};

+ // Avoid unnecessary renders if the KeyboardAvoidingView is disabled.
+ _setBottom = (value: number) => {
+ const {enabled = true} = this.props;
+ this._bottom = value;
+ if (enabled) {
+ this.setState({bottom: value});
+ }
+ };
+
_updateBottomIfNecessary = async () => {
if (this._keyboardEvent == null) {
- this.setState({bottom: 0});
+ this._setBottom(0);
return;
}

const {duration, easing, endCoordinates} = this._keyboardEvent;
const height = await this._relativeKeyboardHeight(endCoordinates);

- if (this.state.bottom === height) {
+ if (this._bottom === height) {
return;
}

- if (duration && easing) {
+ this._setBottom(height);
+
+ if (enabled && duration && easing) {
LayoutAnimation.configureNext({
// We have to pass the duration equal to minimal accepted duration defined here: RCTLayoutAnimation.m
duration: duration > 10 ? duration : 10,
@@ -150,9 +164,15 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
},
});
}
- this.setState({bottom: height});
};

+ componentDidUpdate(_: Props, prevState: State): void {
+ const {enabled = true} = this.props;
+ if (enabled && this._bottom !== prevState.bottom) {
+ this.setState({bottom: this._bottom});
+ }
+ }
+
componentDidMount(): void {
if (Platform.OS === 'ios') {
this._subscriptions = [
30 changes: 28 additions & 2 deletions src/components/LHNOptionsList/OptionRowLHN.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import _ from 'underscore';
import React from 'react';
import React, {useEffect} from 'react';
import PropTypes from 'prop-types';
import {View, StyleSheet} from 'react-native';
import lodashGet from 'lodash/get';
import * as optionRowStyles from '../../styles/optionRowStyles';
import styles from '../../styles/styles';
import * as StyleUtils from '../../styles/StyleUtils';
Expand All @@ -12,6 +13,7 @@ import Hoverable from '../Hoverable';
import DisplayNames from '../DisplayNames';
import colors from '../../styles/colors';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
import {withReportCommentDrafts} from '../OnyxProvider';
import Text from '../Text';
import SubscriptAvatar from '../SubscriptAvatar';
import CONST from '../../CONST';
Expand All @@ -23,12 +25,18 @@ import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteract
import * as ReportActionContextMenu from '../../pages/home/report/ContextMenu/ReportActionContextMenu';
import * as ContextMenuActions from '../../pages/home/report/ContextMenu/ContextMenuActions';
import * as OptionsListUtils from '../../libs/OptionsListUtils';
import compose from '../../libs/compose';
import ONYXKEYS from '../../ONYXKEYS';
import * as Report from '../../libs/actions/Report';

const propTypes = {
/** Style for hovered state */
// eslint-disable-next-line react/forbid-prop-types
hoverStyle: PropTypes.object,

/** The comment left by the user */
comment: PropTypes.string,

/** The ID of the report that the option is for */
reportID: PropTypes.string.isRequired,

Expand All @@ -52,11 +60,20 @@ const defaultProps = {
onSelectRow: () => {},
isFocused: false,
style: null,
comment: '',
};

function OptionRowLHN(props) {
const optionItem = SidebarUtils.getOptionData(props.reportID);

useEffect(() => {
if (!optionItem || optionItem.hasDraftComment || !props.comment || props.comment.length <= 0 || props.isFocused) {
return;
}
Report.setReportWithDraft(props.reportID, true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if (!optionItem) {
return null;
}
Expand Down Expand Up @@ -254,4 +271,13 @@ OptionRowLHN.propTypes = propTypes;
OptionRowLHN.defaultProps = defaultProps;
OptionRowLHN.displayName = 'OptionRowLHN';

export default withLocalize(OptionRowLHN);
export default compose(
withLocalize,
withReportCommentDrafts({
propName: 'comment',
transformValue: (drafts, props) => {
const draftKey = `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${props.reportID}`;
return lodashGet(drafts, draftKey, '');
},
}),
)(OptionRowLHN);
15 changes: 13 additions & 2 deletions src/components/OnyxProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const [withCurrentDate, CurrentDateProvider] = createOnyxContext(ONYXKEYS.CURREN
const [withReportActionsDrafts, ReportActionsDraftsProvider] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS);
const [withBlockedFromConcierge, BlockedFromConciergeProvider] = createOnyxContext(ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE);
const [withBetas, BetasProvider] = createOnyxContext(ONYXKEYS.BETAS);
const [withReportCommentDrafts, ReportCommentDraftsProvider] = createOnyxContext(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT);

const propTypes = {
/** Rendered child component */
Expand All @@ -20,7 +21,17 @@ const propTypes = {

function OnyxProvider(props) {
return (
<ComposeProviders components={[NetworkProvider, PersonalDetailsProvider, ReportActionsDraftsProvider, CurrentDateProvider, BlockedFromConciergeProvider, BetasProvider]}>
<ComposeProviders
components={[
NetworkProvider,
PersonalDetailsProvider,
ReportActionsDraftsProvider,
CurrentDateProvider,
BlockedFromConciergeProvider,
BetasProvider,
ReportCommentDraftsProvider,
]}
>
{props.children}
</ComposeProviders>
);
Expand All @@ -31,4 +42,4 @@ OnyxProvider.propTypes = propTypes;

export default OnyxProvider;

export {withNetwork, withPersonalDetails, withReportActionsDrafts, withCurrentDate, withBlockedFromConcierge, withBetas, NetworkContext};
export {withNetwork, withPersonalDetails, withReportActionsDrafts, withCurrentDate, withBlockedFromConcierge, withBetas, NetworkContext, withReportCommentDrafts};
1 change: 1 addition & 0 deletions src/components/ScreenWrapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class ScreenWrapper extends React.Component {
<KeyboardAvoidingView
style={[styles.w100, styles.h100, {maxHeight}]}
behavior={this.props.keyboardAvoidingViewBehavior}
enabled={this.props.shouldEnableKeyboardAvoidingView}
>
<PickerAvoidingView
style={styles.flex1}
Expand Down
4 changes: 4 additions & 0 deletions src/components/ScreenWrapper/propTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const propTypes = {
* Search 'switch(behavior)' in ./node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js for more context */
keyboardAvoidingViewBehavior: PropTypes.oneOf(['padding', 'height', 'position']),

/** Whether KeyboardAvoidingView should be enabled. Use false for screens where this functionality is not necessary */
shouldEnableKeyboardAvoidingView: PropTypes.bool,

/** Whether picker modal avoiding should be enabled. Should be enabled when there's a picker at the bottom of a
* scrollable form, gives a subtly better UX if disabled on non-scrollable screens with a submit button */
shouldEnablePickerAvoiding: PropTypes.bool,
Expand All @@ -47,6 +50,7 @@ const defaultProps = {
includePaddingTop: true,
onEntryTransitionEnd: () => {},
keyboardAvoidingViewBehavior: 'padding',
shouldEnableKeyboardAvoidingView: true,
shouldEnableMaxHeight: false,
shouldEnablePickerAvoiding: true,
shouldShowOfflineIndicator: true,
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ function updateWelcomeMessage(reportID, previousValue, newValue) {
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {welcomeMessage: newValue},
value: {welcomeMessage: parsedWelcomeMessage},
},
];
const failureData = [
Expand Down
24 changes: 24 additions & 0 deletions src/libs/focusAndUpdateMultilineInputRange.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Focus a multiline text input and place the cursor at the end of the value (if there is a value in the input).
*
* When a multiline input contains a text value that goes beyond the scroll height, the cursor will be placed
* at the end of the text value, and automatically scroll the input field to this position after the field gains
* focus. This provides a better user experience in cases where the text in the field has to be edited. The auto-
* scroll behaviour works on all platforms except iOS native.
* See https://github.com/Expensify/App/issues/20836 for more details.
*
* @param {Object} input the input element
*/
export default function focusAndUpdateMultilineInputRange(input) {
if (!input) {
return;
}

input.focus();
if (input.value && input.setSelectionRange) {
const length = input.value.length;
input.setSelectionRange(length, length);
// eslint-disable-next-line no-param-reassign
input.scrollTop = input.scrollHeight;
}
}
5 changes: 4 additions & 1 deletion src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,10 @@ class ReportScreen extends React.Component {
reactionListRef: this.reactionListRef,
}}
>
<ScreenWrapper style={screenWrapperStyle}>
<ScreenWrapper
style={screenWrapperStyle}
shouldEnableKeyboardAvoidingView={this.props.isFocused}
>
<FullPageNotFoundView
shouldShow={(!this.props.report.reportID && !this.props.report.isLoadingReportActions && !isLoading) || shouldHideReport}
subtitleKey="notFound.noAccess"
Expand Down
4 changes: 4 additions & 0 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ class ReportActionCompose extends React.Component {
showPopoverMenu: this.showPopoverMenu,
});
}

if (this.props.comment.length !== 0) {
Report.setReportWithDraft(this.props.reportID, true);
}
}

componentDidUpdate(prevProps) {
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class BaseSidebarScreen extends Component {
return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnableKeyboardAvoidingView={false}
style={[styles.sidebar, Browser.isMobile() ? styles.userSelectNone : {}]}
>
{({insets}) => (
Expand Down
Loading

0 comments on commit cb5b492

Please sign in to comment.