Skip to content

Commit

Permalink
Merge branch 'main' into receipt_image_resize_fix_40788
Browse files Browse the repository at this point in the history
  • Loading branch information
samilabud committed May 17, 2024
2 parents e8d20ed + e8ae3c5 commit c19a2f3
Show file tree
Hide file tree
Showing 15 changed files with 113 additions and 85 deletions.
31 changes: 0 additions & 31 deletions docs/articles/expensify-classic/workspaces/reports/Currency.md

This file was deleted.

1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4767,6 +4767,7 @@ const CONST = {
DISTANCE: 'distance',
},

SEARCH_RESULTS_PAGE_SIZE: 50,
SEARCH_BOTTOM_TAB_URL: '/Search_Bottom_Tab',

SEARCH_DATA_TYPES: {
Expand Down
1 change: 1 addition & 0 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,7 @@ function MoneyRequestConfirmationList({
shouldPreventDefaultFocusOnSelectRow
footerContent={footerContent}
listFooterContent={listFooterContent}
containerStyle={[styles.flexBasisAuto]}
/>
);
}
Expand Down
28 changes: 24 additions & 4 deletions src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as SearchUtils from '@libs/SearchUtils';
import Navigation from '@navigation/Navigation';
import EmptySearchView from '@pages/Search/EmptySearchView';
import useCustomBackHandler from '@pages/Search/useCustomBackHandler';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
Expand All @@ -35,14 +36,15 @@ function Search({query, policyIDs}: SearchProps) {
return;
}

SearchActions.search(hash, query, policyIDs);
SearchActions.search(hash, query, 0, policyIDs);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hash, isOffline]);

const isLoading = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined;
const shouldShowEmptyState = !isLoading && isEmptyObject(searchResults?.data);
const isLoadingInitialItems = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined;
const isLoadingMoreItems = !isLoadingInitialItems && searchResults?.search?.isLoading;
const shouldShowEmptyState = !isLoadingInitialItems && isEmptyObject(searchResults?.data);

if (isLoading) {
if (isLoadingInitialItems) {
return <TableListItemSkeleton shouldAnimate />;
}

Expand All @@ -58,6 +60,14 @@ function Search({query, policyIDs}: SearchProps) {
Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(query, reportID));
};

const fetchMoreResults = () => {
if (!searchResults?.search?.hasMoreResults || isLoadingInitialItems || isLoadingMoreItems) {
return;
}
const currentOffset = searchResults?.search?.offset ?? 0;
SearchActions.search(hash, query, currentOffset + CONST.SEARCH_RESULTS_PAGE_SIZE);
};

const type = SearchUtils.getSearchType(searchResults?.search);

if (type === undefined) {
Expand All @@ -80,6 +90,16 @@ function Search({query, policyIDs}: SearchProps) {
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
containerStyle={[styles.pv0]}
showScrollIndicator={false}
onEndReachedThreshold={0.75}
onEndReached={fetchMoreResults}
listFooterContent={
isLoadingMoreItems ? (
<TableListItemSkeleton
shouldAnimate
fixedNumItems={5}
/>
) : undefined
}
/>
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ function BaseSelectionList<TItem extends ListItem>(
sectionTitleStyles,
textInputAutoFocus = true,
shouldTextInputInterceptSwipe = false,
onEndReached = () => {},
onEndReachedThreshold,
}: BaseSelectionListProps<TItem>,
ref: ForwardedRef<SelectionListHandle>,
) {
Expand Down Expand Up @@ -618,6 +620,8 @@ function BaseSelectionList<TItem extends ListItem>(
onLayout={onSectionListLayout}
style={(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0}
ListFooterComponent={listFooterContent ?? ShowMoreButtonInstance}
onEndReached={onEndReached}
onEndReachedThreshold={onEndReachedThreshold}
/>
{children}
</>
Expand Down
11 changes: 11 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,17 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
* When false, the list will render immediately and scroll to the bottom which works great for small lists.
*/
shouldHideListOnInitialRender?: boolean;

/** Called once when the scroll position gets within onEndReachedThreshold of the rendered content. */
onEndReached?: () => void;

/**
* How far from the end (in units of visible length of the list) the bottom edge of the
* list must be from the end of the content to trigger the `onEndReached` callback.
* Thus a value of 0.5 will trigger `onEndReached` when the end of the content is
* within half the visible length of the list.
*/
onEndReachedThreshold?: number;
} & TRightHandSideComponent<TItem>;

type SelectionListHandle = {
Expand Down
9 changes: 7 additions & 2 deletions src/components/Skeletons/ItemListSkeletonView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import CONST from '@src/CONST';
type ListItemSkeletonProps = {
shouldAnimate?: boolean;
renderSkeletonItem: (args: {itemIndex: number}) => React.ReactNode;
fixedNumItems?: number;
};

function ItemListSkeletonView({shouldAnimate = true, renderSkeletonItem}: ListItemSkeletonProps) {
function ItemListSkeletonView({shouldAnimate = true, renderSkeletonItem, fixedNumItems}: ListItemSkeletonProps) {
const theme = useTheme();
const themeStyles = useThemeStyles();

const [numItems, setNumItems] = useState(0);
const [numItems, setNumItems] = useState(fixedNumItems ?? 0);
const skeletonViewItems = useMemo(() => {
const items = [];
for (let i = 0; i < numItems; i++) {
Expand All @@ -38,6 +39,10 @@ function ItemListSkeletonView({shouldAnimate = true, renderSkeletonItem}: ListIt
<View
style={[themeStyles.flex1, themeStyles.overflowHidden]}
onLayout={(event) => {
if (fixedNumItems) {
return;
}

const newNumItems = Math.ceil(event.nativeEvent.layout.height / CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT);
if (newNumItems === numItems) {
return;
Expand Down
4 changes: 3 additions & 1 deletion src/components/Skeletons/TableListItemSkeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ import ItemListSkeletonView from './ItemListSkeletonView';

type TableListItemSkeletonProps = {
shouldAnimate?: boolean;
fixedNumItems?: number;
};

const barHeight = '10';
const shortBarWidth = '40';
const longBarWidth = '120';

function TableListItemSkeleton({shouldAnimate = true}: TableListItemSkeletonProps) {
function TableListItemSkeleton({shouldAnimate = true, fixedNumItems}: TableListItemSkeletonProps) {
return (
<ItemListSkeletonView
shouldAnimate={shouldAnimate}
fixedNumItems={fixedNumItems}
renderSkeletonItem={() => (
<>
<Rect
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ type SearchParams = {
query: string;
policyIDs?: string;
hash: number;
offset: number;
};

export default SearchParams;
31 changes: 29 additions & 2 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
import Onyx from 'react-native-onyx';
import type {OnyxUpdate} from 'react-native-onyx';
import * as API from '@libs/API';
import {READ_COMMANDS} from '@libs/API/types';
import ONYXKEYS from '@src/ONYXKEYS';

function search(hash: number, query: string, policyIDs?: string) {
API.read(READ_COMMANDS.SEARCH, {hash, query, policyIDs});
function search(hash: number, query: string, offset = 0, policyIDs?: string) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
value: {
search: {
isLoading: true,
},
},
},
];

const finallyData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`,
value: {
search: {
isLoading: false,
},
},
},
];

API.read(READ_COMMANDS.SEARCH, {hash, query, offset, policyIDs}, {optimisticData, finallyData});
}

export {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/iou/request/step/IOURequestStepMerchant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ function IOURequestStepMerchant({
navigateBack();
return;
}
IOU.setMoneyRequestMerchant(transactionID, newMerchant ?? '', !isEditing);
// When creating/editing an expense, newMerchant can be blank so we fall back on PARTIAL_TRANSACTION_MERCHANT
IOU.setMoneyRequestMerchant(transactionID, newMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, !isEditing);
if (isEditing) {
// When creating a new expense, newMerchant can be blank so we fall back on PARTIAL_TRANSACTION_MERCHANT
IOU.updateMoneyRequestMerchant(transactionID, reportID, newMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, policy, policyTags, policyCategories);
}
navigateBack();
Expand Down
1 change: 1 addition & 0 deletions src/types/onyx/SearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type SearchResultsInfo = {
offset: number;
type: string;
hasMoreResults: boolean;
isLoading: boolean;
};

type SearchPersonalDetails = {
Expand Down
34 changes: 13 additions & 21 deletions tests/actions/PolicyCategoryTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import createRandomPolicy from '../utils/collections/policies';
import createRandomPolicyCategories from '../utils/collections/policyCategory';
import * as TestHelper from '../utils/TestHelper';
import type {MockFetch} from '../utils/TestHelper';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';

OnyxUpdateManager();
Expand All @@ -16,9 +17,10 @@ describe('actions/PolicyCategory', () => {
});
});

let mockFetch: MockFetch;
beforeEach(() => {
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
global.fetch = TestHelper.getGlobalFetchMock();
mockFetch = fetch as MockFetch;
return Onyx.clear().then(waitForBatchedUpdates);
});

Expand All @@ -27,8 +29,7 @@ describe('actions/PolicyCategory', () => {
const fakePolicy = createRandomPolicy(0);
fakePolicy.requiresCategory = false;

// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
fetch.pause();
mockFetch?.pause?.();
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy);
Policy.setWorkspaceRequiresCategory(fakePolicy.id, true);
await waitForBatchedUpdates();
Expand All @@ -46,8 +47,7 @@ describe('actions/PolicyCategory', () => {
},
});
});
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
await fetch.resume();
await mockFetch?.resume?.();
await waitForBatchedUpdates();
await new Promise<void>((resolve) => {
const connectionID = Onyx.connect({
Expand All @@ -68,8 +68,7 @@ describe('actions/PolicyCategory', () => {
const fakePolicy = createRandomPolicy(0);
const fakeCategories = createRandomPolicyCategories(3);
const newCategoryName = 'New category';
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
fetch.pause();
mockFetch?.pause?.();
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy);
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories);
Policy.createPolicyCategory(fakePolicy.id, newCategoryName);
Expand All @@ -89,8 +88,7 @@ describe('actions/PolicyCategory', () => {
},
});
});
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
await fetch.resume();
await mockFetch?.resume?.();
await waitForBatchedUpdates();
await new Promise<void>((resolve) => {
const connectionID = Onyx.connect({
Expand All @@ -115,8 +113,7 @@ describe('actions/PolicyCategory', () => {
const fakeCategories = createRandomPolicyCategories(3);
const oldCategoryName = Object.keys(fakeCategories)[0];
const newCategoryName = 'Updated category';
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
fetch.pause();
mockFetch?.pause?.();
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy);
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories);
Policy.renamePolicyCategory(fakePolicy.id, {
Expand All @@ -140,8 +137,7 @@ describe('actions/PolicyCategory', () => {
},
});
});
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
await fetch.resume();
await mockFetch?.resume?.();
await waitForBatchedUpdates();
await new Promise<void>((resolve) => {
const connectionID = Onyx.connect({
Expand Down Expand Up @@ -170,8 +166,7 @@ describe('actions/PolicyCategory', () => {
enabled: true,
},
};
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
fetch.pause();
mockFetch?.pause?.();
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy);
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories);
Policy.setWorkspaceCategoryEnabled(fakePolicy.id, categoriesToUpdate);
Expand All @@ -191,8 +186,7 @@ describe('actions/PolicyCategory', () => {
},
});
});
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
await fetch.resume();
await mockFetch?.resume?.();
await waitForBatchedUpdates();
await new Promise<void>((resolve) => {
const connectionID = Onyx.connect({
Expand All @@ -217,8 +211,7 @@ describe('actions/PolicyCategory', () => {
const fakeCategories = createRandomPolicyCategories(3);
const categoryNameToDelete = Object.keys(fakeCategories)[0];
const categoriesToDelete = [categoryNameToDelete];
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
fetch.pause();
mockFetch?.pause?.();
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy);
Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories);
Policy.deleteWorkspaceCategories(fakePolicy.id, categoriesToDelete);
Expand All @@ -235,8 +228,7 @@ describe('actions/PolicyCategory', () => {
},
});
});
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
await fetch.resume();
await mockFetch?.resume?.();
await waitForBatchedUpdates();
await new Promise<void>((resolve) => {
const connectionID = Onyx.connect({
Expand Down
Loading

0 comments on commit c19a2f3

Please sign in to comment.