Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Search v1] Add empty search List skeleton #40682

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
110 changes: 42 additions & 68 deletions src/components/OptionsListSkeletonView.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,54 @@
import React, {useMemo, useState} from 'react';
import {View} from 'react-native';
import React from 'react';
import {Circle, Rect} from 'react-native-svg';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import SkeletonViewContentLoader from './SkeletonViewContentLoader';
import ItemListSkeletonView from './Skeletons/ItemListSkeletonView';

function getLinedWidth(index: number): string {
const step = index % 3;
if (step === 0) {
return '100%';
}

if (step === 1) {
return '50%';
}

return '25%';
}

type OptionsListSkeletonViewProps = {
shouldAnimate?: boolean;
};

function OptionsListSkeletonView({shouldAnimate = true}: OptionsListSkeletonViewProps) {
const theme = useTheme();
const themeStyles = useThemeStyles();

const [numItems, setNumItems] = useState(0);
const skeletonViewItems = useMemo(() => {
const items = [];
for (let i = 0; i < numItems; i++) {
const step = i % 3;
let lineWidth;
switch (step) {
case 0:
lineWidth = '100%';
break;
case 1:
lineWidth = '50%';
break;
default:
lineWidth = '25%';
}
items.push(
<SkeletonViewContentLoader
key={`skeletonViewItems${i}`}
animate={shouldAnimate}
height={CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT}
backgroundColor={theme.skeletonLHNIn}
foregroundColor={theme.skeletonLHNOut}
style={themeStyles.mr5}
>
<Circle
cx="40"
cy="32"
r="20"
/>
<Rect
x="72"
y="18"
width="20%"
height="8"
/>
<Rect
x="72"
y="38"
width={lineWidth}
height="8"
/>
</SkeletonViewContentLoader>,
);
}
return items;
}, [numItems, shouldAnimate, theme, themeStyles]);

return (
<View
style={[themeStyles.flex1, themeStyles.overflowHidden]}
onLayout={(event) => {
const newNumItems = Math.ceil(event.nativeEvent.layout.height / CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT);
if (newNumItems === numItems) {
return;
}
setNumItems(newNumItems);
<ItemListSkeletonView
shouldAnimate={shouldAnimate}
renderSkeletonItem={({itemIndex}) => {
const lineWidth = getLinedWidth(itemIndex);

return (
<>
<Circle
cx="40"
cy="32"
r="20"
/>
<Rect
x="72"
y="18"
width="20%"
height="8"
/>
<Rect
x="72"
y="38"
width={lineWidth}
height="8"
/>
</>
);
}}
>
<View>{skeletonViewItems}</View>
</View>
/>
);
}

Expand Down
55 changes: 55 additions & 0 deletions src/components/Skeletons/ItemListSkeletonView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, {useMemo, useState} from 'react';
import {View} from 'react-native';
import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';

type ListItemSkeletonProps = {
shouldAnimate?: boolean;
renderSkeletonItem: (args: {itemIndex: number}) => React.ReactNode;
};

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

const [numItems, setNumItems] = useState(0);
const skeletonViewItems = useMemo(() => {
const items = [];
for (let i = 0; i < numItems; i++) {
items.push(
<SkeletonViewContentLoader
key={`skeletonViewItems${i}`}
animate={shouldAnimate}
height={CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT}
backgroundColor={theme.skeletonLHNIn}
foregroundColor={theme.skeletonLHNOut}
style={themeStyles.mr5}
>
{renderSkeletonItem({itemIndex: i})}
</SkeletonViewContentLoader>,
);
}
return items;
}, [numItems, shouldAnimate, theme, themeStyles, renderSkeletonItem]);

return (
<View
style={[themeStyles.flex1, themeStyles.overflowHidden]}
onLayout={(event) => {
const newNumItems = Math.ceil(event.nativeEvent.layout.height / CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT);
if (newNumItems === numItems) {
return;
}
setNumItems(newNumItems);
}}
>
<View>{skeletonViewItems}</View>
</View>
);
}

ItemListSkeletonView.displayName = 'ListItemSkeleton';

export default ItemListSkeletonView;
65 changes: 65 additions & 0 deletions src/components/Skeletons/TableListItemSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';
import {Rect} from 'react-native-svg';
import ItemListSkeletonView from './ItemListSkeletonView';

type TableListItemSkeletonProps = {
shouldAnimate?: boolean;
};

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

function TableListItemSkeleton({shouldAnimate = true}: TableListItemSkeletonProps) {
return (
<ItemListSkeletonView
shouldAnimate={shouldAnimate}
renderSkeletonItem={() => (
<>
<Rect
x="20"
y="10"
rx="5"
ry="5"
width="40"
height="40"
/>
<Rect
x="80"
y="25"
width={shortBarWidth}
height={barHeight}
/>
<Rect
x="150"
y="25"
width={longBarWidth}
height={barHeight}
/>
<Rect
x="320"
y="25"
width={longBarWidth}
height={barHeight}
/>
<Rect
x="480"
y="25"
width={longBarWidth}
height={barHeight}
/>
<Rect
x="660"
y="25"
width="100%"
height={barHeight}
/>
</>
)}
/>
);
}

TableListItemSkeleton.displayName = 'TableListItemSkeleton';

export default TableListItemSkeleton;
10 changes: 10 additions & 0 deletions src/pages/Search/EmptySearchView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import TableListItemSkeleton from '@components/Skeletons/TableListItemSkeleton';

function EmptySearchView() {
return <TableListItemSkeleton shouldAnimate />;
}

EmptySearchView.displayName = 'EmptySearchView';

export default EmptySearchView;
2 changes: 2 additions & 0 deletions src/pages/Search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import ScreenWrapper from '@components/ScreenWrapper';
import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types';
import type SCREENS from '@src/SCREENS';
// import EmptySearchView from './EmptySearchView';
import SearchResults from './SearchResults';
import useCustomBackHandler from './useCustomBackHandler';

Expand All @@ -14,6 +15,7 @@ function SearchPage({route}: SearchPageProps) {
return (
<ScreenWrapper testID={SearchPage.displayName}>
<SearchResults query={route.params.query} />
{/* <EmptySearchView /> */}
</ScreenWrapper>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/pages/Search/SearchPageBottomTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type IconAsset from '@src/types/utils/IconAsset';

// import EmptySearchView from './EmptySearchView';

type SearchMenuItem = {
title: string;
icon: IconAsset;
Expand Down Expand Up @@ -59,6 +61,7 @@ function SearchPageBottomTab() {
/>
))}
</View>
{/* <EmptySearchView /> */}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this here for ease of reviewing, I can clean this up before final merge.

</ScreenWrapper>
);
}
Expand Down
Loading