HomeScreen - complete refactor
This commit is contained in:
parent
25a11e9dde
commit
efe69b927f
|
@ -1,51 +1,19 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import { NetworkStatus, useQuery } from '@apollo/client';
|
||||
import { useVariables } from 'libs/native-base';
|
||||
import { Query, QueryProfessionsArgs } from 'libs/graphql';
|
||||
import { QUERY_PROFESSIONS } from './queries';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Container, Content, Spinner } from 'native-base';
|
||||
import List from './components/List/List';
|
||||
import { Container } from 'native-base';
|
||||
import Header from './components/Header/Header';
|
||||
import ModeSelector, { Mode } from './components/ModeSelector/ModeSelector';
|
||||
import NetworkConnectionAlert from './components/NetworkConnectionAlert/NetworkConnectionAlert';
|
||||
import Professions from './components/Professions/Professions';
|
||||
|
||||
const HomeScreen = () => {
|
||||
const [search, setSearch] = useState('');
|
||||
const [mode, setMode] = useState(Mode.All);
|
||||
const variables = useVariables();
|
||||
const { loading, data, refetch, networkStatus, error } = useQuery<
|
||||
Pick<Query, 'professions'>,
|
||||
QueryProfessionsArgs
|
||||
>(QUERY_PROFESSIONS, {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
variables: { sort: ['name ASC'] },
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
const professions = useMemo(() => {
|
||||
return (data?.professions.items ?? []).filter(
|
||||
profession => profession.qualifications.length > 0,
|
||||
);
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Header onSearch={setSearch} />
|
||||
<ModeSelector mode={mode} onChangeMode={setMode} />
|
||||
{loading && professions.length === 0 ? (
|
||||
<Content padder>
|
||||
<Spinner color={variables.brandPrimary} size="large" />
|
||||
</Content>
|
||||
) : (
|
||||
<List
|
||||
professions={professions}
|
||||
refreshing={networkStatus === NetworkStatus.refetch}
|
||||
onRefresh={refetch}
|
||||
mode={mode}
|
||||
search={search}
|
||||
/>
|
||||
)}
|
||||
<NetworkConnectionAlert error={error} />
|
||||
<Professions mode={mode} search={search} />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -43,6 +43,7 @@ const Header = ({ onSearch }: HeaderProps) => {
|
|||
value={search}
|
||||
ref={inputRef}
|
||||
allowFontScaling={false}
|
||||
clearButtonMode="always"
|
||||
/>
|
||||
</Item>
|
||||
<View>
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import React, { forwardRef, useCallback } from 'react';
|
||||
|
||||
import {
|
||||
FlatList,
|
||||
FlatListProps,
|
||||
RefreshControl,
|
||||
StyleSheet,
|
||||
} from 'react-native';
|
||||
import ListItem, { ListItemProps } from './ListItem';
|
||||
import ListEmpty from './ListEmpty';
|
||||
import ListLoading from './ListLoading';
|
||||
|
||||
export type Item = ListItemProps;
|
||||
export interface ListProps
|
||||
extends Pick<
|
||||
FlatListProps<Item>,
|
||||
'refreshing' | 'onRefresh' | 'contentContainerStyle'
|
||||
> {
|
||||
items: Item[];
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const MyList = forwardRef<FlatList<Item>, ListProps>(
|
||||
({ items, refreshing, onRefresh, loading, ...rest }: ListProps, ref) => {
|
||||
const renderItem = useCallback(({ item }: { item: Item }) => {
|
||||
return <ListItem {...item} />;
|
||||
}, []);
|
||||
const keyExtractor = useCallback(item => item.id, []);
|
||||
|
||||
console.log('render');
|
||||
return (
|
||||
<FlatList
|
||||
data={items}
|
||||
ref={ref}
|
||||
renderItem={renderItem}
|
||||
ListEmptyComponent={loading ? null : <ListEmpty />}
|
||||
ListFooterComponent={loading ? <ListLoading /> : null}
|
||||
ListFooterComponentStyle={styles.footerWrapper}
|
||||
keyExtractor={keyExtractor}
|
||||
maxToRenderPerBatch={5}
|
||||
onEndReachedThreshold={0.75}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing ?? false}
|
||||
onRefresh={onRefresh ?? noop}
|
||||
/>
|
||||
}
|
||||
initialNumToRender={10}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
footerWrapper: {
|
||||
flexGrow: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default MyList;
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { H1, Content } from 'native-base';
|
||||
import { H1, View } from 'native-base';
|
||||
|
||||
const ListEmpty = () => {
|
||||
return (
|
||||
<Content padder contentContainerStyle={styles.wrapper}>
|
||||
<View padder style={styles.wrapper}>
|
||||
<H1 style={styles.heading}>Nie znaleziono żadnej kwalifikacji</H1>
|
||||
</Content>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import { Spinner } from 'native-base';
|
||||
import { useVariables } from 'libs/native-base';
|
||||
|
||||
const ListLoading = () => {
|
||||
const variables = useVariables();
|
||||
return <Spinner size="large" color={variables.brandPrimary} />;
|
||||
};
|
||||
|
||||
export default ListLoading;
|
|
@ -0,0 +1,31 @@
|
|||
import { useMemo } from 'react';
|
||||
import { NetworkStatus, useQuery } from '@apollo/client';
|
||||
import { Query, QueryProfessionsArgs } from 'libs/graphql';
|
||||
import { QUERY_PROFESSIONS } from './queries';
|
||||
|
||||
const useProfessions = () => {
|
||||
const { loading, data, refetch, networkStatus, error } = useQuery<
|
||||
Pick<Query, 'professions'>,
|
||||
QueryProfessionsArgs
|
||||
>(QUERY_PROFESSIONS, {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
variables: { sort: ['name ASC'] },
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
const professions = useMemo(() => {
|
||||
return (data?.professions.items ?? []).filter(
|
||||
profession => profession.qualifications.length > 0,
|
||||
);
|
||||
}, [data]);
|
||||
|
||||
return {
|
||||
loading: professions.length === 0 && loading,
|
||||
refetch,
|
||||
professions,
|
||||
refetching: networkStatus === NetworkStatus.refetch,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export default useProfessions;
|
|
@ -0,0 +1,17 @@
|
|||
import { useRef } from 'react';
|
||||
import { FlatList } from 'react-native';
|
||||
import { Item } from './List/List';
|
||||
import { useUpdateEffect } from 'react-use';
|
||||
import { Mode } from '../ModeSelector/ModeSelector';
|
||||
|
||||
const useScrollTopOnSearchOrModeChange = (search: string, mode: Mode) => {
|
||||
const listRef = useRef<FlatList<Item>>(null);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
listRef.current?.scrollToOffset({ offset: 0, animated: false });
|
||||
}, [search, mode]);
|
||||
|
||||
return listRef;
|
||||
};
|
||||
|
||||
export default useScrollTopOnSearchOrModeChange;
|
|
@ -14,9 +14,7 @@ const Stack = createStackNavigator<AppStackParamList>();
|
|||
const AppStack = createStackNavigator<AppStackParamList>();
|
||||
|
||||
const AppScreens = () => (
|
||||
<AppStack.Navigator
|
||||
screenOptions={{ animationEnabled: false, headerShown: false }}
|
||||
>
|
||||
<AppStack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<Stack.Screen name={Screen.Home} component={HomeScreen} />
|
||||
<Stack.Screen name={Screen.Test} component={TestScreen} />
|
||||
</AppStack.Navigator>
|
||||
|
|
Reference in New Issue