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