Skip to content

Commit

Permalink
Merge remote-tracking branch 'expensify/main' into bump-up-expo-SDK-t…
Browse files Browse the repository at this point in the history
…o-50.0.3

# Conflicts:
#	package-lock.json
  • Loading branch information
kowczarz committed Jan 30, 2024
2 parents 205e947 + 82ae62f commit f8e9744
Show file tree
Hide file tree
Showing 18 changed files with 115 additions and 83 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001043303
versionName "1.4.33-3"
versionCode 1001043304
versionName "1.4.33-4"
}

flavorDimensions "default"
Expand Down
2 changes: 1 addition & 1 deletion contributingGuides/FORMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function validate(values) {
errors = ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.hasInvalidCharacter');
}

if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_FIRST_NAMES)) {
if (ValidationUtils.doesContainReservedWord(values.firstName, CONST.DISPLAY_NAME.RESERVED_NAMES)) {
errors = ErrorUtils.addErrorMessage(errors, 'firstName', 'personalDetails.error.containsReservedWord');
}

Expand Down
4 changes: 0 additions & 4 deletions docs/_data/_routes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,3 @@ platforms:
icon: /assets/images/money-into-wallet.svg
description: Whether you submit an expense report or an invoice, find out here how to ensure a smooth and timely payback process every time.

- href: workspace-and-domain-settings
title: Workspace & Domain Settings
icon: /assets/images/shield.svg
description: Discover how to set up and manage your workspace, define user permissions, and implement domain-level rules.
2 changes: 1 addition & 1 deletion ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.4.33.3</string>
<string>1.4.33.4</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.4.33.3</string>
<string>1.4.33.4</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion ios/NotificationServiceExtension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<key>CFBundleShortVersionString</key>
<string>1.4.33</string>
<key>CFBundleVersion</key>
<string>1.4.33.3</string>
<string>1.4.33.4</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
Expand Down
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.4.33-3",
"version": "1.4.33-4",
"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
4 changes: 3 additions & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const CONST = {

DISPLAY_NAME: {
MAX_LENGTH: 50,
RESERVED_FIRST_NAMES: ['Expensify', 'Concierge'],
RESERVED_NAMES: ['Expensify', 'Concierge'],
},

LEGAL_NAME: {
Expand Down Expand Up @@ -566,6 +566,7 @@ const CONST = {
INDIVIDUAL_BUDGET_NOTIFICATION: 'POLICYCHANGELOG_INDIVIDUAL_BUDGET_NOTIFICATION',
INVITE_TO_ROOM: 'POLICYCHANGELOG_INVITETOROOM',
REMOVE_FROM_ROOM: 'POLICYCHANGELOG_REMOVEFROMROOM',
LEAVE_ROOM: 'POLICYCHANGELOG_LEAVEROOM',
REPLACE_CATEGORIES: 'POLICYCHANGELOG_REPLACE_CATEGORIES',
SET_AUTOREIMBURSEMENT: 'POLICYCHANGELOG_SET_AUTOREIMBURSEMENT',
SET_AUTO_JOIN: 'POLICYCHANGELOG_SET_AUTO_JOIN',
Expand Down Expand Up @@ -608,6 +609,7 @@ const CONST = {
ROOMCHANGELOG: {
INVITE_TO_ROOM: 'INVITETOROOM',
REMOVE_FROM_ROOM: 'REMOVEFROMROOM',
LEAVE_ROOM: 'LEAVEROOM',
},
},
THREAD_DISABLED: ['CREATED'],
Expand Down
22 changes: 21 additions & 1 deletion src/components/Hoverable/ActiveHoverable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function ActiveHoverable({onHoverIn, onHoverOut, shouldHandleScroll, children}:
const elementRef = useRef<HTMLElement | null>(null);
const isScrollingRef = useRef(false);
const isHoveredRef = useRef(false);
const isVisibiltyHidden = useRef(false);

const updateIsHovered = useCallback(
(hovered: boolean) => {
Expand Down Expand Up @@ -75,7 +76,14 @@ function ActiveHoverable({onHoverIn, onHoverOut, shouldHandleScroll, children}:
}, [isHovered, elementRef]);

useEffect(() => {
const unsetHoveredWhenDocumentIsHidden = () => document.visibilityState === 'hidden' && setIsHovered(false);
const unsetHoveredWhenDocumentIsHidden = () => {
if (document.visibilityState !== 'hidden') {
return;
}

isVisibiltyHidden.current = true;
setIsHovered(false);
};

document.addEventListener('visibilitychange', unsetHoveredWhenDocumentIsHidden);

Expand All @@ -86,9 +94,11 @@ function ActiveHoverable({onHoverIn, onHoverOut, shouldHandleScroll, children}:

const childOnMouseEnter = child.props.onMouseEnter;
const childOnMouseLeave = child.props.onMouseLeave;
const childOnMouseMove = child.props.onMouseMove;

const hoverAndForwardOnMouseEnter = useCallback(
(e: MouseEvent) => {
isVisibiltyHidden.current = false;
updateIsHovered(true);
childOnMouseEnter?.(e);
},
Expand Down Expand Up @@ -116,11 +126,21 @@ function ActiveHoverable({onHoverIn, onHoverOut, shouldHandleScroll, children}:
[child.props],
);

const handleAndForwardOnMouseMove = useCallback(
(e: MouseEvent) => {
isVisibiltyHidden.current = false;
updateIsHovered(true);
childOnMouseMove?.(e);
},
[updateIsHovered, childOnMouseMove],
);

return cloneElement(child, {
ref: mergeRefs(elementRef, outerRef, child.ref),
onMouseEnter: hoverAndForwardOnMouseEnter,
onMouseLeave: unhoverAndForwardOnMouseLeave,
onBlur: unhoverAndForwardOnBlur,
...(isVisibiltyHidden.current ? {onMouseMove: handleAndForwardOnMouseMove} : {}),
});
}

Expand Down
5 changes: 0 additions & 5 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,6 @@ function BaseSelectionList<TItem extends User | RadioItem>(
return;
}

// scroll is unnecessary if multiple options cannot be selected
if (!canSelectMultiple) {
return;
}

// set the focus on the first item when the sections list is changed
if (sections.length > 0) {
updateAndScrollToFocusedIndex(0);
Expand Down
2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ export default {
},
personalDetails: {
error: {
containsReservedWord: 'First name cannot contain the words Expensify or Concierge',
containsReservedWord: 'Name cannot contain the words Expensify or Concierge',
hasInvalidCharacter: 'Name cannot contain a comma or semicolon',
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,58 @@
import {useFocusEffect} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import PropTypes from 'prop-types';
import React, {useCallback, useRef, useState} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import type {AnimatedTextInputRef} from '@components/RNTextInput';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
import type {ReportWelcomeMessageNavigatorParamList} from '@navigation/types';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {Policy} from '@src/types/onyx';
import type {WithReportOrNotFoundProps} from './home/report/withReportOrNotFound';
import withReportOrNotFound from './home/report/withReportOrNotFound';
import reportPropTypes from './reportPropTypes';
import {policyDefaultProps, policyPropTypes} from './workspace/withPolicy';

const propTypes = {
...withLocalizePropTypes,
...policyPropTypes,

/** The report currently being looked at */
report: reportPropTypes.isRequired,

/** Route params */
route: PropTypes.shape({
params: PropTypes.shape({
/** Report ID passed via route r/:reportID/welcomeMessage */
reportID: PropTypes.string,
}),
}).isRequired,
type ReportWelcomeMessagePageOnyxProps = {
/** The policy object for the current route */
policy: OnyxEntry<Policy>;
};

const defaultProps = {
...policyDefaultProps,
};
type ReportWelcomeMessagePageProps = ReportWelcomeMessagePageOnyxProps &
WithReportOrNotFoundProps &
StackScreenProps<ReportWelcomeMessageNavigatorParamList, typeof SCREENS.REPORT_WELCOME_MESSAGE_ROOT>;

function ReportWelcomeMessagePage(props) {
function ReportWelcomeMessagePage({report, policy}: ReportWelcomeMessagePageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

const parser = new ExpensiMark();
const [welcomeMessage, setWelcomeMessage] = useState(() => parser.htmlToMarkdown(props.report.welcomeMessage));
const welcomeMessageInputRef = useRef(null);
const focusTimeoutRef = useRef(null);
const [welcomeMessage, setWelcomeMessage] = useState(() => parser.htmlToMarkdown(report?.welcomeMessage ?? ''));
const welcomeMessageInputRef = useRef<AnimatedTextInputRef | null>(null);
const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);

const handleWelcomeMessageChange = useCallback((value) => {
const handleWelcomeMessageChange = useCallback((value: string) => {
setWelcomeMessage(value);
}, []);

const submitForm = useCallback(() => {
Report.updateWelcomeMessage(props.report.reportID, props.report.welcomeMessage, welcomeMessage.trim());
}, [props.report.reportID, props.report.welcomeMessage, welcomeMessage]);
Report.updateWelcomeMessage(report?.reportID ?? '', report?.welcomeMessage ?? '', welcomeMessage.trim());
}, [report?.reportID, report?.welcomeMessage, welcomeMessage]);

useFocusEffect(
useCallback(() => {
Expand All @@ -82,33 +76,33 @@ function ReportWelcomeMessagePage(props) {
includeSafeAreaPaddingBottom={false}
testID={ReportWelcomeMessagePage.displayName}
>
<FullPageNotFoundView shouldShow={ReportUtils.shouldDisableWelcomeMessage(props.report, props.policy)}>
<FullPageNotFoundView shouldShow={ReportUtils.shouldDisableWelcomeMessage(report, policy)}>
<HeaderWithBackButton
title={props.translate('welcomeMessagePage.welcomeMessage')}
onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))}
title={translate('welcomeMessagePage.welcomeMessage')}
onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report?.reportID ?? ''))}
/>
<FormProvider
style={[styles.flexGrow1, styles.ph5]}
formID={ONYXKEYS.FORMS.WELCOME_MESSAGE_FORM}
onSubmit={submitForm}
submitButtonText={props.translate('common.save')}
submitButtonText={translate('common.save')}
enabledWhenOffline
>
<Text style={[styles.mb5]}>{props.translate('welcomeMessagePage.explainerText')}</Text>
<Text style={[styles.mb5]}>{translate('welcomeMessagePage.explainerText')}</Text>
<View style={[styles.mb6]}>
<InputWrapper
InputComponent={TextInput}
inputID="welcomeMessage"
label={props.translate('welcomeMessagePage.welcomeMessage')}
accessibilityLabel={props.translate('welcomeMessagePage.welcomeMessage')}
label={translate('welcomeMessagePage.welcomeMessage')}
accessibilityLabel={translate('welcomeMessagePage.welcomeMessage')}
role={CONST.ROLE.PRESENTATION}
autoGrowHeight
maxLength={CONST.MAX_COMMENT_LENGTH}
ref={(el) => {
if (!el) {
ref={(element: AnimatedTextInputRef) => {
if (!element) {
return;
}
welcomeMessageInputRef.current = el;
welcomeMessageInputRef.current = element;
updateMultilineInputRange(welcomeMessageInputRef.current);
}}
value={welcomeMessage}
Expand All @@ -124,15 +118,11 @@ function ReportWelcomeMessagePage(props) {
}

ReportWelcomeMessagePage.displayName = 'ReportWelcomeMessagePage';
ReportWelcomeMessagePage.propTypes = propTypes;
ReportWelcomeMessagePage.defaultProps = defaultProps;

export default compose(
withLocalize,
withReportOrNotFound(),
withOnyx({
export default withReportOrNotFound()(
withOnyx<ReportWelcomeMessagePageProps, ReportWelcomeMessagePageOnyxProps>({
policy: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`,
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`,
},
}),
)(ReportWelcomeMessagePage);
})(ReportWelcomeMessagePage),
);
23 changes: 19 additions & 4 deletions src/pages/home/report/withReportOrNotFound.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
/* eslint-disable rulesdir/no-negated-variables */
import type {RouteProp} from '@react-navigation/native';
import type {ComponentType, ForwardedRef, RefAttributes} from 'react';
import React from 'react';
import React, {useEffect} from 'react';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import getComponentDisplayName from '@libs/getComponentDisplayName';
import * as ReportUtils from '@libs/ReportUtils';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import * as Report from '@userActions/Report';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

type OnyxProps = {
/** The report currently being looked at */
Expand All @@ -22,21 +24,33 @@ type OnyxProps = {
isLoadingReportData: OnyxEntry<boolean>;
};

type ComponentProps = OnyxProps & {
type WithReportOrNotFoundProps = OnyxProps & {
route: RouteProp<{params: {reportID: string}}>;
};

export default function (
shouldRequireReportID = true,
): <TProps extends ComponentProps, TRef>(
): <TProps extends WithReportOrNotFoundProps, TRef>(
WrappedComponent: React.ComponentType<TProps & React.RefAttributes<TRef>>,
) => React.ComponentType<Omit<TProps & React.RefAttributes<TRef>, keyof OnyxProps>> {
return function <TProps extends ComponentProps, TRef>(WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>) {
return function <TProps extends WithReportOrNotFoundProps, TRef>(WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>) {
function WithReportOrNotFound(props: TProps, ref: ForwardedRef<TRef>) {
const contentShown = React.useRef(false);

const isReportIdInRoute = props.route.params.reportID?.length;

// When accessing certain report-dependant pages (e.g. Task Title) by deeplink, the OpenReport API is not called,
// So we need to call OpenReport API here to make sure the report data is loaded if it exists on the Server
useEffect(() => {
if (!isReportIdInRoute || !isEmptyObject(props.report)) {
// If the report is not required or is already loaded, we don't need to call the API
return;
}

Report.openReport(props.route.params.reportID);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isReportIdInRoute, props.route.params.reportID]);

if (shouldRequireReportID || isReportIdInRoute) {
const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.report ?? {}).length || !props.report?.reportID);

Expand Down Expand Up @@ -89,3 +103,4 @@ export default function (
})(React.forwardRef(WithReportOrNotFound));
};
}
export type {WithReportOrNotFoundProps};
Loading

0 comments on commit f8e9744

Please sign in to comment.