Files
notesnook/apps/mobile/src/components/SimpleList/index.js

309 lines
8.3 KiB
JavaScript
Raw Normal View History

2020-12-20 23:01:35 +05:00
import React, {useEffect, useState} from 'react';
2020-11-04 15:06:06 +05:00
import {
2020-11-24 16:59:12 +05:00
ActivityIndicator,
2020-11-04 15:06:06 +05:00
RefreshControl,
useWindowDimensions,
2020-12-10 14:25:04 +05:00
View,
2020-11-04 15:06:06 +05:00
} from 'react-native';
2020-12-10 14:25:04 +05:00
import {TouchableWithoutFeedback} from 'react-native-gesture-handler';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {DataProvider, LayoutProvider, RecyclerListView} from 'recyclerlistview';
import {useTracked} from '../../provider';
import {DDS} from '../../services/DeviceDetection';
2020-12-20 23:01:35 +05:00
import {eSendEvent} from '../../services/EventManager';
import Sync from '../../services/Sync';
2020-12-10 14:25:04 +05:00
import {dHeight} from '../../utils';
import {COLORS_NOTE} from '../../utils/Colors';
2020-12-20 23:01:35 +05:00
import {eOpenJumpToDialog, eScrollEvent} from '../../utils/Events';
2020-12-10 14:25:04 +05:00
import {SIZE} from '../../utils/SizeUtils';
import {Button} from '../Button';
import {HeaderMenu} from '../Header/HeaderMenu';
2020-11-14 10:06:32 +05:00
import Seperator from '../Seperator';
import TagItem from '../TagItem';
2020-11-10 17:18:19 +05:00
import Heading from '../Typography/Heading';
import Paragraph from '../Typography/Paragraph';
2020-12-10 14:25:04 +05:00
import {ListHeaderComponent} from './ListHeaderComponent';
import {NotebookItemWrapper} from './NotebookItemWrapper';
import {NoteItemWrapper} from './NoteItemWrapper';
2020-09-18 20:47:52 +05:00
const header = {
2020-11-04 15:06:06 +05:00
type: 'MAIN_HEADER',
2020-09-18 20:47:52 +05:00
};
2020-05-10 22:19:23 +05:00
2020-03-09 20:06:55 +05:00
const SimpleList = ({
2020-11-04 15:06:06 +05:00
data,
type,
customRefresh,
customRefreshing,
refreshCallback,
sortMenuButton,
scrollRef,
2020-11-04 20:29:45 +05:00
jumpToDialog,
2020-11-10 17:18:19 +05:00
placeholderData,
2020-11-24 16:59:12 +05:00
loading,
2020-12-08 12:49:02 +05:00
headerProps = {
heading: 'Home',
},
2020-11-04 15:06:06 +05:00
}) => {
const [state, dispatch] = useTracked();
2020-12-08 12:49:02 +05:00
const {colors, searchResults, headerTextState} = state;
2020-11-04 15:06:06 +05:00
const [refreshing, setRefreshing] = useState(false);
2020-11-04 15:06:06 +05:00
const [dataProvider, setDataProvider] = useState(
new DataProvider((r1, r2) => {
return r1 !== r2;
2020-11-24 16:59:12 +05:00
}).cloneWithRows([header, {type: 'empty'}]),
2020-11-04 15:06:06 +05:00
);
2020-12-07 14:45:44 +05:00
const insets = useSafeAreaInsets();
const {width, fontScale, height} = useWindowDimensions();
2020-11-04 15:06:06 +05:00
const listData = data;
const dataType = type;
const _onScroll = (event) => {
if (!event) return;
let y = event.nativeEvent.contentOffset.y;
eSendEvent(eScrollEvent, y);
};
2020-09-18 20:47:52 +05:00
2020-11-04 15:06:06 +05:00
useEffect(() => {
loadData();
2021-01-05 10:29:00 +05:00
}, [data, searchResults.results, loading]);
2020-11-04 15:06:06 +05:00
const loadData = () => {
if (loading) return;
2020-12-08 12:09:34 +05:00
let mainData = [header, {type: 'empty'}];
mainData =
!listData || listData.length === 0 ? mainData : [header, ...listData];
setDataProvider(dataProvider.cloneWithRows(mainData));
2020-11-04 15:06:06 +05:00
};
2020-10-18 13:15:24 +05:00
2020-11-04 15:06:06 +05:00
const RenderSectionHeader = ({item, index}) => (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
width: '100%',
justifyContent: 'space-between',
paddingHorizontal: 12,
2021-01-05 10:29:00 +05:00
height: 30,
2020-12-16 20:49:39 +05:00
backgroundColor:
index === 1
? headerProps.color
? colors[headerProps.color]
: colors.shade
: colors.nav,
2020-12-16 20:02:24 +05:00
marginTop: index === 1 ? 0 : 5,
2020-11-04 15:06:06 +05:00
}}>
2020-12-06 15:14:38 +05:00
<TouchableWithoutFeedback
2020-11-04 15:06:06 +05:00
onPress={() => {
2020-11-04 20:29:45 +05:00
if (jumpToDialog) {
eSendEvent(eOpenJumpToDialog);
}
2020-11-04 15:06:06 +05:00
}}
2020-12-06 15:14:38 +05:00
hitSlop={{top: 10, left: 10, right: 30, bottom: 15}}
2020-11-20 01:23:05 +05:00
style={{
2020-12-06 15:14:38 +05:00
height: '100%',
justifyContent: 'center',
2020-11-20 01:23:05 +05:00
}}>
2020-12-16 20:02:24 +05:00
<Heading
2020-12-06 15:14:38 +05:00
color={colors.accent}
2020-12-16 20:02:24 +05:00
size={SIZE.sm}
2020-12-06 15:14:38 +05:00
style={{
minWidth: 60,
alignSelf: 'center',
textAlignVertical: 'center',
}}>
2020-12-20 23:01:35 +05:00
{!item.title || item.title === '' ? 'Pinned' : item.title}
2020-12-16 20:02:24 +05:00
</Heading>
2020-12-06 15:14:38 +05:00
</TouchableWithoutFeedback>
2020-11-04 15:06:06 +05:00
{index === 1 && sortMenuButton ? <HeaderMenu /> : null}
</View>
);
2020-10-26 11:14:02 +05:00
2020-12-20 23:01:35 +05:00
const _onRefresh = async () => {
await Sync.run();
if (refreshCallback) {
refreshCallback();
2020-11-04 15:06:06 +05:00
}
2020-12-20 23:01:35 +05:00
};
2020-11-04 15:06:06 +05:00
const _ListEmptyComponent = (
<View
style={[
{
backgroundColor: colors.bg,
2020-12-29 17:21:19 +05:00
height: height - 250 - insets.top,
2020-11-24 16:59:12 +05:00
width: '100%',
},
2020-11-04 15:06:06 +05:00
]}>
2020-11-10 17:18:19 +05:00
<View
style={{
flexGrow: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Heading>{placeholderData.heading}</Heading>
2020-12-20 23:01:35 +05:00
<Paragraph
style={{
textAlign: 'center',
width: '80%',
}}
color={colors.icon}>
2020-11-24 16:59:12 +05:00
{loading ? placeholderData.loading : placeholderData.paragraph}
</Paragraph>
2020-11-10 17:18:19 +05:00
<Seperator />
2020-11-24 16:59:12 +05:00
{placeholderData.button && !loading ? (
2020-11-14 10:06:32 +05:00
<Button
onPress={placeholderData.action}
title={placeholderData.button}
icon="plus"
2020-12-08 12:09:34 +05:00
type="accent"
2020-11-14 10:06:32 +05:00
fontSize={SIZE.md}
2020-12-08 12:09:34 +05:00
accentColor="bg"
2020-12-08 12:49:02 +05:00
accentText={
COLORS_NOTE[headerTextState.heading.toLowerCase()]
? headerTextState.heading.toLowerCase()
: 'accent'
}
2020-11-14 10:06:32 +05:00
/>
2020-11-26 16:22:32 +05:00
) : loading ? (
2020-11-24 16:59:12 +05:00
<ActivityIndicator color={colors.accent} />
2020-11-26 16:22:32 +05:00
) : null}
2020-11-10 17:18:19 +05:00
</View>
2020-11-04 15:06:06 +05:00
</View>
);
const _layoutProvider = new LayoutProvider(
(index) => {
return dataProvider.getDataForIndex(index).type;
},
(type, dim) => {
switch (type) {
case 'note':
dim.width = width;
dim.height = 100 * fontScale;
break;
case 'notebook':
dim.width = width;
dim.height = 110 * fontScale;
break;
case 'trash':
dim.width = width;
dim.height = 110 * fontScale;
break;
2020-11-24 16:59:12 +05:00
case 'empty':
dim.width = width;
2020-12-07 14:45:44 +05:00
dim.height = dHeight - 250 - insets.top;
2020-11-24 16:59:12 +05:00
break;
2020-11-04 15:06:06 +05:00
case 'topic':
dim.width = width;
dim.height = 80 * fontScale;
break;
case 'tag':
dim.width = width;
dim.height = 80 * fontScale;
break;
case 'header':
dim.width = width;
2020-12-16 20:02:24 +05:00
dim.height = 40 * fontScale;
2020-11-04 15:06:06 +05:00
break;
case 'MAIN_HEADER':
dim.width = width;
2020-11-20 01:23:05 +05:00
dim.height =
2020-12-16 20:02:24 +05:00
dataType === 'search' ? 0 : DDS.isLargeTablet() ? 50 : 195;
2020-11-04 15:06:06 +05:00
break;
default:
dim.width = width;
dim.height = 0;
}
},
);
2020-11-01 09:22:28 +05:00
2020-11-04 15:06:06 +05:00
const _renderRow = (type, data, index) => {
switch (type) {
case 'note':
2020-11-14 10:06:32 +05:00
return (
<NoteItemWrapper item={data} pinned={data.pinned} index={index} />
);
2020-11-04 15:06:06 +05:00
case 'notebook':
2020-11-14 10:06:32 +05:00
return (
<NotebookItemWrapper item={data} pinned={data.pinned} index={index} />
);
case 'tag':
return <TagItem item={data} index={index} />;
case 'topic':
return (
<NotebookItemWrapper
item={data}
isTopic={true}
pinned={data.pinned}
index={index}
/>
);
case 'trash':
return data.itemType === 'note' ? (
<NoteItemWrapper item={data} index={index} isTrash={true} />
) : (
<NotebookItemWrapper item={data} index={index} isTrash={true} />
);
2020-11-04 15:06:06 +05:00
case 'MAIN_HEADER':
return (
2020-12-08 12:49:02 +05:00
<ListHeaderComponent
title={headerProps.heading}
type={dataType}
index={index}
data={listData}
/>
2020-11-04 15:06:06 +05:00
);
case 'header':
return <RenderSectionHeader item={data} index={index} />;
2020-11-24 16:59:12 +05:00
case 'empty':
return _ListEmptyComponent;
2020-11-04 15:06:06 +05:00
}
};
2020-09-27 10:15:19 +05:00
2020-11-24 16:59:12 +05:00
return (
2020-11-04 15:06:06 +05:00
<RecyclerListView
ref={scrollRef}
layoutProvider={_layoutProvider}
dataProvider={dataProvider}
rowRenderer={_renderRow}
onScroll={_onScroll}
2020-12-07 14:45:44 +05:00
canChangeSize={true}
optimizeForInsertDeleteAnimations
2020-11-14 10:06:32 +05:00
forceNonDeterministicRendering
renderFooter={() => <View style={{height: 300}} />}
2020-11-04 15:06:06 +05:00
scrollViewProps={{
refreshControl: (
<RefreshControl
2020-11-09 19:20:18 +05:00
style={{
opacity: 0,
elevation: 0,
}}
2020-11-04 15:06:06 +05:00
tintColor={colors.accent}
colors={[colors.accent]}
progressViewOffset={150}
onRefresh={customRefresh ? customRefresh : _onRefresh}
refreshing={customRefresh ? customRefreshing : refreshing}
/>
),
2020-11-09 19:20:18 +05:00
overScrollMode: 'always',
2020-11-04 15:06:06 +05:00
contentContainerStyle: {
width: '100%',
alignSelf: 'center',
minHeight: '100%',
},
2020-12-01 22:52:01 +05:00
testID: 'list-' + type,
2020-11-04 15:06:06 +05:00
}}
2020-11-09 18:13:47 +05:00
style={{
height: '100%',
backgroundColor: colors.bg,
width: '100%',
}}
2020-11-04 15:06:06 +05:00
/>
);
2020-03-09 20:06:55 +05:00
};
export default SimpleList;