Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
38e1cb7
Bump babel-plugin-react-compiler to experimental build with TemplateL…
roryabraham Apr 24, 2026
a1d77f0
Add package.json and babel config to react-compiler-compliance-check …
roryabraham Apr 24, 2026
5bb19ba
Merge remote-tracking branch 'origin/main' into rory-rc-tier0-fix
roryabraham Apr 24, 2026
5950a7e
drop useCallback in ReimbursementAccount NonUSD SignerInfo JobTitle
roryabraham Apr 24, 2026
edffc95
drop useCallback in ReimbursementAccount NonUSD SignerInfo Name
roryabraham Apr 24, 2026
c133775
drop useCallback/useMemo in ReimbursementAccount NonUSD SignerInfo Up…
roryabraham Apr 24, 2026
99d56c6
drop useMemo/useCallback wrappers in ReportAttachmentModalContent
roryabraham Apr 24, 2026
0ba8802
drop useMemo wrappers in WorkspaceEditCardLimitTypePage
roryabraham Apr 24, 2026
8a93d3b
drop useCallback/useMemo in usePlaybackContextVideoRefs
roryabraham Apr 24, 2026
e65b568
drop useMemo wrappers in CancelSubscriptionPage and inline content br…
roryabraham Apr 24, 2026
932f291
drop useCallback/useMemo wrappers in PaymentMethodList
roryabraham Apr 24, 2026
4c05872
drop useCallback wrappers in WorkspaceTravelInvoicingExportPage
roryabraham Apr 24, 2026
9771730
drop useCallback wrappers in ConfirmedRoute
roryabraham Apr 24, 2026
2408125
drop useCallback/useMemo wrappers in FeatureTrainingModal
roryabraham Apr 24, 2026
fbc0e09
drop useCallback wrappers in AvatarWithImagePicker
roryabraham Apr 24, 2026
52b20fc
drop useCallback/useMemo wrappers in SearchContext provider
roryabraham Apr 24, 2026
ba02d8e
fix missing dep in useFocusEffect in ReportScreen
roryabraham Apr 24, 2026
c0d79a9
Merge remote-tracking branch 'origin/main' into rory-rc-tier0-fix
roryabraham Apr 24, 2026
f8c273f
Drop manual memoization in DistanceRequestFooter
roryabraham Apr 24, 2026
c67b5f3
Refactor reaction list to action/state context
roryabraham Apr 28, 2026
4cddd2d
Merge remote-tracking branch 'origin/main' into rory-rc-tier0-fix
roryabraham Apr 28, 2026
c222a9c
Extract SettingsMenuItem to fix React Compiler refs-during-render
roryabraham Apr 28, 2026
ccb9162
Merge remote-tracking branch 'origin/main' into rory-rc-tier0-fix
roryabraham Apr 28, 2026
4ccac76
Fix typo in package-lock.json
roryabraham Apr 28, 2026
5992a40
Revert unhelpful changes to react-compiler-compliance.yml
roryabraham Apr 28, 2026
8df024e
Merge remote-tracking branch 'origin/main' into rory-rc-tier0-fix
roryabraham Apr 28, 2026
24973e5
Simplify waypointMarkers to a single for...of loop
roryabraham Apr 28, 2026
d30bcf8
Merge remote-tracking branch 'origin/main' into rory-rc-tier0-fix
roryabraham Apr 28, 2026
5670c75
Simplify getWaypointMarkers to an inline for...of loop
roryabraham Apr 29, 2026
b9ea884
Merge remote-tracking branch 'origin/main' into rory-rc-tier0-fix
roryabraham Apr 29, 2026
8fa551d
Keep parsed search queries referentially stable
roryabraham Apr 29, 2026
bfcc34b
Preserve search query mutation safety
roryabraham Apr 29, 2026
302d4b4
fix(search): freeze cached buildSearchQueryJSON; drop Stable()
roryabraham Apr 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
"babel-jest": "29.7.0",
"babel-loader": "^10.0.0",
"babel-plugin-module-resolver": "^5.0.0",
"babel-plugin-react-compiler": "1.0.0",
"babel-plugin-react-compiler": "0.0.0-experimental-a1856f3-20260422",
"babel-plugin-react-native-web": "^0.18.7",
"babel-plugin-transform-remove-console": "^6.9.4",
"clean-webpack-plugin": "^4.0.0",
Expand Down Expand Up @@ -360,6 +360,7 @@
"webpack-merge": "^5.8.0"
},
"overrides": {
"babel-plugin-react-compiler": "0.0.0-experimental-a1856f3-20260422",
"braces": "3.0.3",
"yargs": "17.7.2",
"yargs-parser": "21.1.1",
Expand Down
37 changes: 17 additions & 20 deletions src/components/AvatarWithImagePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {useIsFocused} from '@react-navigation/native';
import React, {useCallback, useEffect, useLayoutEffect, useRef, useState} from 'react';
import React, {useEffect, useLayoutEffect, useRef, useState} from 'react';
import {View} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
Expand Down Expand Up @@ -141,7 +141,7 @@ function AvatarWithImagePicker({
/**
* Validates an image and opens avatar crop modal if valid
*/
const showAvatarCropModal = useCallback((image: FileObject) => {
const showAvatarCropModal = (image: FileObject) => {
validateAvatarImage(image)
.then((validationResult) => {
if (!validationResult.isValid) {
Expand All @@ -161,7 +161,7 @@ function AvatarWithImagePicker({
.catch(() => {
setError('attachmentPicker.errorWhileSelectingCorruptedAttachment', {});
});
}, []);
};

const hideAvatarCropModal = () => {
setIsAvatarCropModalOpen(false);
Expand Down Expand Up @@ -201,23 +201,20 @@ function AvatarWithImagePicker({
return menuItems;
};

const onPressAvatar = useCallback(
(openPicker: OpenPicker) => {
anchorRef.current?.blur();
if (disabled && enablePreview && onViewPhotoPress) {
onViewPhotoPress();
return;
}
if (isUsingDefaultAvatar) {
openPicker({
onPicked: (data) => showAvatarCropModal(data.at(0) ?? {}),
});
return;
}
setIsMenuVisible((prev) => !prev);
},
[disabled, enablePreview, isUsingDefaultAvatar, onViewPhotoPress, showAvatarCropModal],
);
const onPressAvatar = (openPicker: OpenPicker) => {
anchorRef.current?.blur();
if (disabled && enablePreview && onViewPhotoPress) {
onViewPhotoPress();
return;
}
if (isUsingDefaultAvatar) {
openPicker({
onPicked: (data) => showAvatarCropModal(data.at(0) ?? {}),
});
return;
}
setIsMenuVisible((prev) => !prev);
};

useLayoutEffect(() => {
if (!anchorRef.current || !isMenuVisible) {
Expand Down
76 changes: 31 additions & 45 deletions src/components/ConfirmedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useCallback, useEffect} from 'react';
import React, {useEffect} from 'react';
import type {ReactNode} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
Expand All @@ -12,7 +12,6 @@ import {init as initMapboxToken, stop as stopMapboxToken} from '@userActions/Map
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Transaction} from '@src/types/onyx';
import type {WaypointCollection} from '@src/types/onyx/Transaction';
import type IconAsset from '@src/types/utils/IconAsset';
import DistanceMapView from './DistanceMapView';
import ImageSVG from './ImageSVG';
Expand Down Expand Up @@ -49,51 +48,38 @@ function ConfirmedRoute({transaction, isSmallerIcon, shouldHaveBorderRadius = tr

const [mapboxAccessToken] = useOnyx(ONYXKEYS.MAPBOX_ACCESS_TOKEN);

const getMarkerComponent = useCallback(
(icon: IconAsset): ReactNode => (
<ImageSVG
src={icon}
width={CONST.MAP_MARKER_SIZE}
height={CONST.MAP_MARKER_SIZE}
fill={theme.icon}
/>
),
[theme],
);

const getWaypointMarkers = useCallback(
(waypointsData: WaypointCollection): WayPoint[] => {
const numberOfWaypoints = Object.keys(waypointsData).length;
const lastWaypointIndex = numberOfWaypoints - 1;

return Object.entries(waypointsData)
.map(([key, waypoint]) => {
if (!waypoint?.lat || !waypoint?.lng) {
return;
}

const index = getWaypointIndex(key);
let MarkerComponent: IconAsset;
if (index === 0) {
MarkerComponent = expensifyIcons.DotIndicatorUnfilled;
} else if (index === lastWaypointIndex) {
MarkerComponent = expensifyIcons.Location;
} else {
MarkerComponent = expensifyIcons.DotIndicator;
}

return {
id: `${waypoint.lng},${waypoint.lat},${index}`,
coordinate: [waypoint.lng, waypoint.lat] as const,
markerComponent: (): ReactNode => getMarkerComponent(MarkerComponent),
};
})
.filter((waypoint): waypoint is WayPoint => !!waypoint);
},
[getMarkerComponent, expensifyIcons.DotIndicator, expensifyIcons.DotIndicatorUnfilled, expensifyIcons.Location],
const getMarkerComponent = (icon: IconAsset): ReactNode => (
<ImageSVG
src={icon}
width={CONST.MAP_MARKER_SIZE}
height={CONST.MAP_MARKER_SIZE}
fill={theme.icon}
/>
);

const waypointMarkers = getWaypointMarkers(waypoints);
const lastWaypointIndex = Object.keys(waypoints).length - 1;
const waypointMarkers: WayPoint[] = [];
for (const [key, waypoint] of Object.entries(waypoints)) {
if (!waypoint?.lat || !waypoint?.lng) {
continue;
}

const index = getWaypointIndex(key);
let MarkerComponent: IconAsset;
if (index === 0) {
MarkerComponent = expensifyIcons.DotIndicatorUnfilled;
} else if (index === lastWaypointIndex) {
MarkerComponent = expensifyIcons.Location;
} else {
MarkerComponent = expensifyIcons.DotIndicator;
}

waypointMarkers.push({
id: `${waypoint.lng},${waypoint.lat},${index}`,
coordinate: [waypoint.lng, waypoint.lat] as const,
markerComponent: (): ReactNode => getMarkerComponent(MarkerComponent),
});
}

useEffect(() => {
initMapboxToken();
Expand Down
64 changes: 28 additions & 36 deletions src/components/DistanceRequest/DistanceRequestFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useCallback, useMemo} from 'react';
import React from 'react';
import type {ReactNode} from 'react';
import {View} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
Expand Down Expand Up @@ -60,45 +60,37 @@ function DistanceRequestFooter({waypoints, transaction, navigateToWaypointEditPa
const mileageRate = isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policyCurrency, transaction) : defaultMileageRate;
const {unit} = mileageRate ?? {};

const getMarkerComponent = useCallback(
(icon: IconAsset): ReactNode => (
<ImageSVG
src={icon}
width={CONST.MAP_MARKER_SIZE}
height={CONST.MAP_MARKER_SIZE}
fill={theme.icon}
/>
),
[theme],
const getMarkerComponent = (icon: IconAsset): ReactNode => (
<ImageSVG
src={icon}
width={CONST.MAP_MARKER_SIZE}
height={CONST.MAP_MARKER_SIZE}
fill={theme.icon}
/>
);

const waypointMarkers = useMemo(
() =>
Object.entries(waypoints ?? {})
.map(([key, waypoint]) => {
if (!waypoint?.lat || !waypoint?.lng) {
return;
}
const waypointMarkers: WayPoint[] = [];
for (const [key, waypoint] of Object.entries(waypoints ?? {})) {
if (!waypoint?.lat || !waypoint?.lng) {
continue;
}

const index = getWaypointIndex(key);
let MarkerComponent: IconAsset;
if (index === 0) {
MarkerComponent = expensifyIcons.DotIndicatorUnfilled;
} else if (index === lastWaypointIndex) {
MarkerComponent = expensifyIcons.Location;
} else {
MarkerComponent = expensifyIcons.DotIndicator;
}
const index = getWaypointIndex(key);
let MarkerComponent: IconAsset;
if (index === 0) {
MarkerComponent = expensifyIcons.DotIndicatorUnfilled;
} else if (index === lastWaypointIndex) {
MarkerComponent = expensifyIcons.Location;
} else {
MarkerComponent = expensifyIcons.DotIndicator;
}

return {
id: `${waypoint.lng},${waypoint.lat},${index}`,
coordinate: [waypoint.lng, waypoint.lat] as const,
markerComponent: (): ReactNode => getMarkerComponent(MarkerComponent),
};
})
.filter((waypoint): waypoint is WayPoint => !!waypoint),
[waypoints, lastWaypointIndex, getMarkerComponent, expensifyIcons.DotIndicator, expensifyIcons.DotIndicatorUnfilled, expensifyIcons.Location],
);
waypointMarkers.push({
id: `${waypoint.lng},${waypoint.lat},${index}`,
coordinate: [waypoint.lng, waypoint.lat] as const,
markerComponent: (): ReactNode => getMarkerComponent(MarkerComponent),
});
}

return (
<>
Expand Down
48 changes: 11 additions & 37 deletions src/components/FeatureTrainingModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {ImageContentFit} from 'expo-image';
import type {SourceLoadEventPayload} from 'expo-video';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import React, {useEffect, useRef, useState} from 'react';
// eslint-disable-next-line no-restricted-imports
import {Image, InteractionManager, View} from 'react-native';
// eslint-disable-next-line no-restricted-imports
Expand Down Expand Up @@ -267,7 +267,7 @@ function FeatureTrainingModal({
setIllustrationAspectRatio(track.size.width / track.size.height);
};

const renderIllustration = useCallback(() => {
const renderIllustration = () => {
const aspectRatio = illustrationAspectRatio || VIDEO_ASPECT_RATIO;

return (
Expand Down Expand Up @@ -333,34 +333,11 @@ function FeatureTrainingModal({
)}
</View>
);
}, [
illustrationAspectRatio,
styles.w100,
styles.featureTrainingModalImage,
styles.onboardingVideoPlayer,
styles.flex1,
styles.alignItemsCenter,
styles.justifyContentCenter,
styles.h100,
illustrationInnerContainerStyle,
videoURL,
image,
shouldRenderSVG,
contentFitImage,
imageWidth,
imageHeight,
videoStatus,
animationStyle,
animation,
shouldUseNarrowLayout,
isInLandscapeMode,
isReduceMotionEnabled,
illustrations.Hands,
]);

const toggleWillShowAgain = useCallback(() => setWillShowAgain((prevWillShowAgain) => !prevWillShowAgain), []);

const closeModal = useCallback(() => {
};

const toggleWillShowAgain = () => setWillShowAgain((prevWillShowAgain) => !prevWillShowAgain);

const closeModal = () => {
Log.hmmm(`[FeatureTrainingModal] closeModal called - willShowAgain: ${willShowAgain}, shouldGoBack: ${shouldGoBack}, hasOnClose: ${!!onClose}`);

if (!willShowAgain) {
Expand All @@ -387,9 +364,9 @@ function FeatureTrainingModal({
Log.hmmm('[FeatureTrainingModal] No onClose callback provided');
}
});
}, [onClose, shouldGoBack, willShowAgain]);
};

const closeAndConfirmModal = useCallback(() => {
const closeAndConfirmModal = () => {
Log.hmmm(`[FeatureTrainingModal] Button pressed - shouldCloseOnConfirm: ${shouldCloseOnConfirm}, hasOnConfirm: ${!!onConfirm}, willShowAgain: ${willShowAgain}`);

if (shouldCloseOnConfirm) {
Expand All @@ -403,7 +380,7 @@ function FeatureTrainingModal({
} else {
Log.hmmm('[FeatureTrainingModal] No onConfirm callback provided');
}
}, [shouldCloseOnConfirm, onConfirm, closeModal, willShowAgain]);
};

// Scrolls modal to the bottom when keyboard appears so the action buttons are visible.
useEffect(() => {
Expand All @@ -415,10 +392,7 @@ function FeatureTrainingModal({

const Wrapper = shouldUseScrollView ? ScrollView : View;

const wrapperStyles = useMemo(
() => (shouldUseScrollView ? StyleUtils.getScrollableFeatureTrainingModalStyles(insets, isKeyboardActive) : {}),
[shouldUseScrollView, StyleUtils, insets, isKeyboardActive],
);
const wrapperStyles = shouldUseScrollView ? StyleUtils.getScrollableFeatureTrainingModalStyles(insets, isKeyboardActive) : {};

return (
<Modal
Expand Down
Loading
Loading