Refactor tag screen to use FlashList
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -24,14 +24,17 @@ const RootScrollView = ({
|
||||
padded &&
|
||||
orientation == 'portrait' && [
|
||||
styles.paddingHorizontal,
|
||||
styles.paddingVertical,
|
||||
styles.paddingTop,
|
||||
],
|
||||
padded &&
|
||||
orientation == 'landscape' && [
|
||||
styles.paddingHorizontal,
|
||||
styles.smallPaddingVertical,
|
||||
styles.smallPaddingTop,
|
||||
],
|
||||
centered && [styles.centered, styles.flex],
|
||||
styles.fullSize,
|
||||
centered && [styles.centered, styles.flex],
|
||||
styles.fullSize,
|
||||
{ backgroundColor: colors.background },
|
||||
style,
|
||||
]}
|
||||
|
@@ -24,14 +24,15 @@ const RootView = ({
|
||||
padded &&
|
||||
orientation == 'portrait' && [
|
||||
styles.paddingHorizontal,
|
||||
styles.paddingVertical,
|
||||
styles.paddingTop,
|
||||
],
|
||||
padded &&
|
||||
orientation == 'landscape' && [
|
||||
styles.paddingHorizontal,
|
||||
styles.smallPaddingVertical,
|
||||
styles.smallPaddingTop,
|
||||
],
|
||||
centered && [styles.centered, styles.flex],
|
||||
styles.fullSize,
|
||||
{ backgroundColor: colors.background },
|
||||
style,
|
||||
]}>
|
||||
|
@@ -4,8 +4,8 @@ import { Chip } from 'react-native-paper';
|
||||
import { Tag } from '../database';
|
||||
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
|
||||
|
||||
const TagChip = (properties: { tag: Tag }) => {
|
||||
const contrastColor = getContrastColor(properties.tag.color);
|
||||
const TagChip = ({ tag }: { tag: Tag }) => {
|
||||
const contrastColor = getContrastColor(tag.color);
|
||||
|
||||
return (
|
||||
<Chip
|
||||
@@ -15,11 +15,11 @@ const TagChip = (properties: { tag: Tag }) => {
|
||||
compact
|
||||
style={[
|
||||
{
|
||||
backgroundColor: properties.tag.color,
|
||||
backgroundColor: tag.color,
|
||||
},
|
||||
]}
|
||||
textStyle={{ color: contrastColor }}>
|
||||
{'#' + properties.tag.name}
|
||||
{'#' + tag.name}
|
||||
</Chip>
|
||||
);
|
||||
};
|
||||
|
@@ -15,9 +15,9 @@ const tagPreviewStyles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
const TagPreview = (properties: { name: string; color: string }) => {
|
||||
const TagPreview = ({ name, color }: { name: string; color: string }) => {
|
||||
const { responsive } = useDimensions();
|
||||
const contrastColor = getContrastColor(properties.color);
|
||||
const contrastColor = getContrastColor(color);
|
||||
|
||||
return (
|
||||
<View
|
||||
@@ -36,11 +36,11 @@ const TagPreview = (properties: { name: string; color: string }) => {
|
||||
style={[
|
||||
tagPreviewStyles.chip,
|
||||
{
|
||||
backgroundColor: properties.color,
|
||||
backgroundColor: color,
|
||||
},
|
||||
]}
|
||||
textStyle={[tagPreviewStyles.text, { color: contrastColor }]}>
|
||||
{'#' + properties.name}
|
||||
{'#' + name}
|
||||
</Chip>
|
||||
</View>
|
||||
);
|
||||
|
@@ -45,7 +45,7 @@ class Meme extends Realm.Object<Meme> {
|
||||
title: 'string',
|
||||
description: 'string?',
|
||||
isFavorite: { type: 'bool', indexed: true, default: false },
|
||||
tags: 'Tag[]',
|
||||
tags: { type: 'list', objectType: 'Tag', default: [] },
|
||||
tagsLength: { type: 'int', default: 0 },
|
||||
dateCreated: { type: 'date', default: new Date() },
|
||||
dateModified: { type: 'date', default: new Date() },
|
||||
|
@@ -16,9 +16,9 @@ class Tag extends Realm.Object<Tag> {
|
||||
primaryKey: 'id',
|
||||
properties: {
|
||||
id: 'uuid',
|
||||
name: 'string',
|
||||
name: { type: 'string', indexed: true },
|
||||
color: 'string',
|
||||
memes: 'Meme[]',
|
||||
memes: { type: 'list', objectType: 'Meme', default: [] },
|
||||
memesLength: { type: 'int', default: 0 },
|
||||
dateCreated: { type: 'date', default: new Date() },
|
||||
dateModified: { type: 'date', default: new Date() },
|
||||
@@ -39,5 +39,4 @@ const deleteAllTags = (realm: Realm) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export { Tag, deleteTag, deleteAllTags };
|
||||
|
@@ -50,7 +50,6 @@ const AddTag = () => {
|
||||
id: new BSON.UUID(),
|
||||
name: tagName,
|
||||
color: tagColor,
|
||||
memes: [],
|
||||
});
|
||||
});
|
||||
navigation.goBack();
|
||||
|
@@ -1,16 +1,17 @@
|
||||
import React, { useState } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { StyleSheet, View, Text } from 'react-native';
|
||||
import {
|
||||
Button,
|
||||
DataTable,
|
||||
Divider,
|
||||
HelperText,
|
||||
Menu,
|
||||
Searchbar,
|
||||
TouchableRipple,
|
||||
} from 'react-native-paper';
|
||||
import { useQuery, useRealm } from '@realm/react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { RootScrollView, TagChip } from '../components';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
import { RootView, TagChip } from '../components';
|
||||
import { Tag, deleteTag } from '../database';
|
||||
import styles from '../styles';
|
||||
import {
|
||||
@@ -26,17 +27,48 @@ const tagStyles = StyleSheet.create({
|
||||
headerButtonView: {
|
||||
height: 50,
|
||||
},
|
||||
tagRow: {
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 10,
|
||||
},
|
||||
tagView: {
|
||||
flexShrink: 1,
|
||||
maxWidth: '80%',
|
||||
},
|
||||
helperText: {
|
||||
marginVertical: 10,
|
||||
},
|
||||
tagView: {
|
||||
justifyContent: 'center',
|
||||
maxWidth: '75%',
|
||||
},
|
||||
});
|
||||
|
||||
const Tags = () => {
|
||||
const TagRow = ({ tag }: { tag: Tag }) => {
|
||||
const realm = useRealm();
|
||||
|
||||
return (
|
||||
<TouchableRipple onPress={() => deleteTag(realm, tag)}>
|
||||
<View style={tagStyles.tagRow}>
|
||||
<View style={tagStyles.tagView}>
|
||||
<TagChip tag={tag} />
|
||||
</View>
|
||||
<Text>{tag.memesLength}</Text>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
);
|
||||
};
|
||||
|
||||
const ListEmpty = () => {
|
||||
return (
|
||||
<View style={styles.alignCenter}>
|
||||
<HelperText type={'info'} style={tagStyles.helperText}>
|
||||
No tags found
|
||||
</HelperText>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const Tags = () => {
|
||||
const sort = useSelector((state: RootState) => state.tags.sort);
|
||||
const sortDirection = useSelector(
|
||||
(state: RootState) => state.tags.sortDirection,
|
||||
@@ -60,16 +92,19 @@ const Tags = () => {
|
||||
};
|
||||
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
const tags = useQuery<Tag>(Tag.schema.name)
|
||||
.filtered(`name CONTAINS[c] "${search}"`)
|
||||
.sorted(tagSortQuery(sort), sortDirection === SORT_DIRECTION.ASCENDING);
|
||||
.sorted(tagSortQuery(sort), sortDirection === SORT_DIRECTION.DESCENDING);
|
||||
|
||||
return (
|
||||
<RootScrollView padded>
|
||||
<RootView padded>
|
||||
<Searchbar
|
||||
placeholder="Search Tags"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
onChangeText={(value: string) => {
|
||||
setSearch(value);
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
@@ -103,26 +138,14 @@ const Tags = () => {
|
||||
</Menu>
|
||||
</View>
|
||||
<Divider />
|
||||
<DataTable>
|
||||
{tags.map(tag => (
|
||||
<DataTable.Row
|
||||
key={tag.id.toHexString()}
|
||||
onPress={() => deleteTag(realm, tag)}>
|
||||
<View style={tagStyles.tagView}>
|
||||
<TagChip tag={tag} />
|
||||
</View>
|
||||
<DataTable.Cell numeric>{tag.memesLength}</DataTable.Cell>
|
||||
</DataTable.Row>
|
||||
))}
|
||||
</DataTable>
|
||||
{tags.length === 0 && (
|
||||
<View style={styles.alignCenter}>
|
||||
<HelperText type={'info'} style={tagStyles.helperText}>
|
||||
No tags found
|
||||
</HelperText>
|
||||
</View>
|
||||
)}
|
||||
</RootScrollView>
|
||||
<FlashList
|
||||
data={tags}
|
||||
estimatedItemSize={52}
|
||||
renderItem={({ item }) => <TagRow tag={item} />}
|
||||
ItemSeparatorComponent={() => <Divider />}
|
||||
ListEmptyComponent={() => <ListEmpty />}
|
||||
/>
|
||||
</RootView>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -10,6 +10,9 @@ const styles = StyleSheet.create({
|
||||
smallPaddingVertical: {
|
||||
paddingVertical: '2%',
|
||||
},
|
||||
smallPaddingTop: {
|
||||
paddingTop: '2%',
|
||||
},
|
||||
padding: {
|
||||
padding: '5%',
|
||||
},
|
||||
@@ -19,6 +22,9 @@ const styles = StyleSheet.create({
|
||||
paddingVertical: {
|
||||
paddingVertical: '5%',
|
||||
},
|
||||
paddingTop: {
|
||||
paddingTop: '5%',
|
||||
},
|
||||
centered: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
@@ -61,6 +67,16 @@ const styles = StyleSheet.create({
|
||||
justifyEnd: {
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
fullHeight: {
|
||||
height: '100%',
|
||||
},
|
||||
fullSize: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
export default styles;
|
||||
|
@@ -32,6 +32,7 @@ const homeSortQuery = (sort: HOME_SORT) => {
|
||||
|
||||
enum TAG_SORT {
|
||||
NAME = 'Name',
|
||||
COLOR = 'Color',
|
||||
MEMES_LENGTH = 'Items',
|
||||
DATE_CREATED = 'Date Created',
|
||||
DATE_MODIFIED = 'Date Modified',
|
||||
@@ -43,6 +44,9 @@ const tagSortQuery = (sort: TAG_SORT) => {
|
||||
case TAG_SORT.NAME: {
|
||||
return 'name';
|
||||
}
|
||||
case TAG_SORT.COLOR: {
|
||||
return 'color';
|
||||
}
|
||||
case TAG_SORT.MEMES_LENGTH: {
|
||||
return 'memesLength';
|
||||
}
|
||||
|
@@ -8,7 +8,8 @@ const getSortIcon = (
|
||||
|
||||
switch (sort) {
|
||||
case HOME_SORT.TITLE:
|
||||
case TAG_SORT.NAME: {
|
||||
case TAG_SORT.NAME:
|
||||
case TAG_SORT.COLOR: {
|
||||
sortIcon = 'sort-alphabetical';
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user