simplify web section list

This commit is contained in:
Arnaud Vergnet 2021-05-11 08:47:54 +02:00
parent 3cb6ddd7f9
commit 35a4b377f8
8 changed files with 60 additions and 100 deletions

View file

@ -26,8 +26,8 @@ import * as Animatable from 'react-native-animatable';
import { REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests'; import { REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
type Props = { type Props = {
status?: Exclude<REQUEST_STATUS, REQUEST_STATUS.SUCCESS>; status?: REQUEST_STATUS;
code?: Exclude<REQUEST_CODES, REQUEST_CODES.SUCCESS>; code?: REQUEST_CODES;
icon?: string; icon?: string;
message?: string; message?: string;
loading?: boolean; loading?: boolean;

View file

@ -4,7 +4,7 @@ import { useRequestLogic } from '../../utils/customHooks';
import { useFocusEffect } from '@react-navigation/native'; import { useFocusEffect } from '@react-navigation/native';
import BasicLoadingScreen from './BasicLoadingScreen'; import BasicLoadingScreen from './BasicLoadingScreen';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import { REQUEST_STATUS } from '../../utils/Requests'; import { REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
export type RequestScreenProps<T> = { export type RequestScreenProps<T> = {
request: () => Promise<T>; request: () => Promise<T>;
@ -13,7 +13,7 @@ export type RequestScreenProps<T> = {
loading: boolean, loading: boolean,
refreshData: (newRequest?: () => Promise<T>) => void, refreshData: (newRequest?: () => Promise<T>) => void,
status: REQUEST_STATUS, status: REQUEST_STATUS,
code: number | undefined code?: REQUEST_CODES
) => React.ReactElement; ) => React.ReactElement;
cache?: T; cache?: T;
onCacheUpdate?: (newCache: T) => void; onCacheUpdate?: (newCache: T) => void;

View file

@ -21,22 +21,19 @@ import React, { useState } from 'react';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import { Snackbar } from 'react-native-paper'; import { Snackbar } from 'react-native-paper';
import { import {
NativeScrollEvent,
NativeSyntheticEvent,
RefreshControl, RefreshControl,
SectionListData, SectionListData,
SectionListRenderItemInfo, SectionListProps,
StyleSheet, StyleSheet,
View, View,
} from 'react-native'; } from 'react-native';
import * as Animatable from 'react-native-animatable';
import ErrorView from './ErrorView'; import ErrorView from './ErrorView';
import BasicLoadingScreen from './BasicLoadingScreen';
import { TAB_BAR_HEIGHT } from '../Tabbar/CustomTabBar'; import { TAB_BAR_HEIGHT } from '../Tabbar/CustomTabBar';
import { ERROR_TYPE } from '../../utils/WebData';
import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList'; import CollapsibleSectionList from '../Collapsible/CollapsibleSectionList';
import GENERAL_STYLES from '../../constants/Styles'; import GENERAL_STYLES from '../../constants/Styles';
import RequestScreen from './RequestScreen'; import RequestScreen, { RequestScreenProps } from './RequestScreen';
import { CollapsibleComponentPropsType } from '../Collapsible/CollapsibleComponent';
import { REQUEST_CODES, REQUEST_STATUS } from '../../utils/Requests';
export type SectionListDataType<ItemT> = Array<{ export type SectionListDataType<ItemT> = Array<{
title: string; title: string;
@ -45,31 +42,36 @@ export type SectionListDataType<ItemT> = Array<{
keyExtractor?: (data: ItemT) => string; keyExtractor?: (data: ItemT) => string;
}>; }>;
type Props<ItemT, RawData> = { type Props<ItemT, RawData> = Omit<
request: () => Promise<RawData>; CollapsibleComponentPropsType,
refreshOnFocus: boolean; 'children' | 'paddedProps'
renderItem: (data: SectionListRenderItemInfo<ItemT>) => React.ReactNode; > &
createDataset: ( Omit<
data: RawData | undefined, RequestScreenProps<RawData>,
isLoading: boolean | 'render'
) => SectionListDataType<ItemT>; | 'showLoading'
| 'showError'
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void; | 'refresh'
showError?: boolean; | 'onFinish'
itemHeight?: number | null; | 'onMajorError'
autoRefreshTime?: number; > &
updateData?: number | string; Omit<
renderListHeaderComponent?: ( SectionListProps<ItemT>,
data?: RawData 'sections' | 'getItemLayout' | 'ListHeaderComponent' | 'ListEmptyComponent'
) => React.ComponentType<any> | React.ReactElement | null; > & {
renderSectionHeader?: ( createDataset: (
data: { section: SectionListData<ItemT> }, data: RawData | undefined,
isLoading: boolean isLoading: boolean
) => React.ReactElement | null; ) => SectionListDataType<ItemT>;
stickyHeader?: boolean; renderListHeaderComponent?: (
cache?: RawData; data?: RawData
onCacheUpdate?: (newCache: RawData) => void; ) => React.ComponentType<any> | React.ReactElement | null;
}; renderSectionHeader?: (
data: { section: SectionListData<ItemT> },
isLoading: boolean
) => React.ReactElement | null;
itemHeight?: number | null;
};
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -100,48 +102,12 @@ function WebSectionList<ItemT, RawData>(props: Props<ItemT, RawData>) {
}; };
}; };
const getRenderSectionHeader = (
data: { section: SectionListData<ItemT> },
loading: boolean
) => {
const { renderSectionHeader } = props;
if (renderSectionHeader) {
return (
<Animatable.View
animation={'fadeInUp'}
duration={500}
useNativeDriver={true}
>
{renderSectionHeader(data, loading)}
</Animatable.View>
);
}
return null;
};
const getRenderItem = (data: SectionListRenderItemInfo<ItemT>) => {
const { renderItem } = props;
return (
<Animatable.View
animation={'fadeInUp'}
duration={500}
useNativeDriver={true}
>
{renderItem(data)}
</Animatable.View>
);
};
const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
if (props.onScroll) {
props.onScroll(event);
}
};
const render = ( const render = (
data: RawData | undefined, data: RawData | undefined,
loading: boolean, loading: boolean,
refreshData: (newRequest?: () => Promise<RawData>) => void refreshData: (newRequest?: () => Promise<RawData>) => void,
status: REQUEST_STATUS,
code?: REQUEST_CODES
) => { ) => {
const { itemHeight } = props; const { itemHeight } = props;
const dataset = props.createDataset(data, loading); const dataset = props.createDataset(data, loading);
@ -150,8 +116,8 @@ function WebSectionList<ItemT, RawData>(props: Props<ItemT, RawData>) {
} }
return ( return (
<CollapsibleSectionList <CollapsibleSectionList
{...props}
sections={dataset} sections={dataset}
extraData={props.updateData}
paddedProps={(paddingTop) => ({ paddedProps={(paddingTop) => ({
refreshControl: ( refreshControl: (
<RefreshControl <RefreshControl
@ -161,9 +127,7 @@ function WebSectionList<ItemT, RawData>(props: Props<ItemT, RawData>) {
/> />
), ),
})} })}
renderSectionHeader={(info) => getRenderSectionHeader(info, loading)} renderItem={props.renderItem}
renderItem={getRenderItem}
stickySectionHeadersEnabled={props.stickyHeader}
style={styles.container} style={styles.container}
ListHeaderComponent={ ListHeaderComponent={
props.renderListHeaderComponent != null props.renderListHeaderComponent != null
@ -171,11 +135,10 @@ function WebSectionList<ItemT, RawData>(props: Props<ItemT, RawData>) {
: null : null
} }
ListEmptyComponent={ ListEmptyComponent={
loading ? ( loading ? undefined : (
<BasicLoadingScreen />
) : (
<ErrorView <ErrorView
status={ERROR_TYPE.CONNECTION_ERROR} status={status}
code={code}
button={{ button={{
icon: 'refresh', icon: 'refresh',
text: i18n.t('general.retry'), text: i18n.t('general.retry'),
@ -187,8 +150,6 @@ function WebSectionList<ItemT, RawData>(props: Props<ItemT, RawData>) {
getItemLayout={ getItemLayout={
itemHeight ? (d, i) => getItemLayout(itemHeight, d, i) : undefined itemHeight ? (d, i) => getItemLayout(itemHeight, d, i) : undefined
} }
onScroll={onScroll}
hasTab={true}
/> />
); );
}; };

View file

@ -26,7 +26,7 @@ import {
StyleSheet, StyleSheet,
} from 'react-native'; } from 'react-native';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import { ActivityIndicator, Headline, withTheme } from 'react-native-paper'; import { Headline, withTheme } from 'react-native-paper';
import { CommonActions } from '@react-navigation/native'; import { CommonActions } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
@ -315,11 +315,11 @@ class HomeScreen extends React.Component<PropsType, StateType> {
*/ */
getRenderItem = ({ item }: { item: FeedItemType }) => this.getFeedItem(item); getRenderItem = ({ item }: { item: FeedItemType }) => this.getFeedItem(item);
getRenderSectionHeader = ( getRenderSectionHeader = (data: {
data: { section: SectionListData<FeedItemType> }, section: SectionListData<FeedItemType>;
isLoading: boolean }) => {
) => {
const { props } = this; const { props } = this;
const icon = data.section.icon;
if (data.section.data.length > 0) { if (data.section.data.length > 0) {
return ( return (
<Headline style={styles.sectionHeader}>{data.section.title}</Headline> <Headline style={styles.sectionHeader}>{data.section.title}</Headline>
@ -335,16 +335,14 @@ class HomeScreen extends React.Component<PropsType, StateType> {
> >
{data.section.title} {data.section.title}
</Headline> </Headline>
{isLoading ? ( {icon ? (
<ActivityIndicator style={styles.activityIndicator} />
) : (
<MaterialCommunityIcons <MaterialCommunityIcons
name="access-point-network-off" name={icon}
size={100} size={100}
color={props.theme.colors.textDisabled} color={props.theme.colors.textDisabled}
style={GENERAL_STYLES.center} style={GENERAL_STYLES.center}
/> />
)} ) : null}
</View> </View>
); );
}; };
@ -406,6 +404,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
): Array<{ ): Array<{
title: string; title: string;
data: [] | Array<FeedItemType>; data: [] | Array<FeedItemType>;
icon?: string;
id: string; id: string;
}> => { }> => {
if (fetchedData) { if (fetchedData) {
@ -433,6 +432,7 @@ class HomeScreen extends React.Component<PropsType, StateType> {
? i18n.t('screens.home.feedLoading') ? i18n.t('screens.home.feedLoading')
: i18n.t('screens.home.feedError'), : i18n.t('screens.home.feedError'),
data: [], data: [],
icon: isLoading ? undefined : 'access-point-network-off',
id: SECTIONS_ID[1], id: SECTIONS_ID[1],
}, },
]; ];
@ -473,7 +473,6 @@ class HomeScreen extends React.Component<PropsType, StateType> {
renderItem={this.getRenderItem} renderItem={this.getRenderItem}
itemHeight={FEED_ITEM_HEIGHT} itemHeight={FEED_ITEM_HEIGHT}
onScroll={this.onScroll} onScroll={this.onScroll}
showError={false}
renderSectionHeader={this.getRenderSectionHeader} renderSectionHeader={this.getRenderSectionHeader}
renderListHeaderComponent={this.getListHeader} renderListHeaderComponent={this.getListHeader}
/> />

View file

@ -259,7 +259,7 @@ function GroupSelectionScreen() {
createDataset={createDataset} createDataset={createDataset}
refreshOnFocus={true} refreshOnFocus={true}
renderItem={getRenderItem} renderItem={getRenderItem}
updateData={currentSearchString + favoriteGroups.length} extraData={currentSearchString + favoriteGroups.length}
cache={groups} cache={groups}
onCacheUpdate={setGroups} onCacheUpdate={setGroups}
/> />

View file

@ -512,7 +512,7 @@ class ProxiwashScreen extends React.Component<PropsType, StateType> {
renderSectionHeader={this.getRenderSectionHeader} renderSectionHeader={this.getRenderSectionHeader}
autoRefreshTime={REFRESH_TIME} autoRefreshTime={REFRESH_TIME}
refreshOnFocus={true} refreshOnFocus={true}
updateData={state.machinesWatched.length} extraData={state.machinesWatched.length}
/> />
</View> </View>
<MascotPopup <MascotPopup

View file

@ -367,7 +367,7 @@ function ProximoListScreen(props: Props) {
createDataset={createDataset} createDataset={createDataset}
refreshOnFocus={true} refreshOnFocus={true}
renderItem={getRenderItem} renderItem={getRenderItem}
updateData={currentSearchString + currentSortMode} extraData={currentSearchString + currentSortMode}
itemHeight={LIST_ITEM_HEIGHT} itemHeight={LIST_ITEM_HEIGHT}
cache={articles} cache={articles}
onCacheUpdate={setArticles} onCacheUpdate={setArticles}

View file

@ -201,7 +201,7 @@ class SelfMenuScreen extends React.Component<PropsType> {
refreshOnFocus={true} refreshOnFocus={true}
renderItem={this.getRenderItem} renderItem={this.getRenderItem}
renderSectionHeader={this.getRenderSectionHeader} renderSectionHeader={this.getRenderSectionHeader}
stickyHeader={true} stickySectionHeadersEnabled={true}
/> />
); );
} }