forked from vergnet/application-amicale
simplify web section list
This commit is contained in:
parent
3cb6ddd7f9
commit
35a4b377f8
8 changed files with 60 additions and 100 deletions
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue