Add meme view & sharing
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { Appbar, Button, useTheme } from 'react-native-paper';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { ORIENTATION, useDimensions } from '../contexts';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import { useRealm } from '@realm/react';
|
||||
import { BSON } from 'realm';
|
||||
@@ -13,8 +13,23 @@ import styles from '../styles';
|
||||
import { ROUTE, RootStackParamList } from '../types';
|
||||
import { Meme, Tag } from '../database';
|
||||
import { RootState } from '../state';
|
||||
import { getMemeType, validateMemeTitle } from '../utilities';
|
||||
import { allowedMimeTypes, getMemeType, validateMemeTitle } from '../utilities';
|
||||
import { MemeEditor } from '../components';
|
||||
import {
|
||||
DocumentPickerResponse,
|
||||
pickSingle,
|
||||
} from 'react-native-document-picker';
|
||||
|
||||
const addMemeStyles = StyleSheet.create({
|
||||
saveAndAddButton: {
|
||||
flex: 1,
|
||||
marginRight: 5,
|
||||
},
|
||||
saveButton: {
|
||||
flex: 1,
|
||||
marginLeft: 5,
|
||||
},
|
||||
});
|
||||
|
||||
const AddMeme = ({
|
||||
route,
|
||||
@@ -28,31 +43,32 @@ const AddMeme = ({
|
||||
(state: RootState) => state.settings.storageUri,
|
||||
)!;
|
||||
|
||||
const { file } = route.params;
|
||||
const file = useRef(route.params.file);
|
||||
|
||||
const [memeUri, setMemeUri] = useState(file.current.uri);
|
||||
const [memeTitle, setMemeTitle] = useState(validateMemeTitle('New Meme'));
|
||||
const [memeIsFavorite, setMemeIsFavorite] = useState(false);
|
||||
const [memeTags, setMemeTags] = useState(new Map<string, Tag>());
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [isSavingAndAddingAnother, setIsSavingAndAddingAnother] =
|
||||
useState(false);
|
||||
|
||||
const handleSave = async () => {
|
||||
setIsSaving(true);
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
const uuid = new BSON.UUID();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const mimeType = file.type!;
|
||||
const mimeType = file.current.type!;
|
||||
const memeType = getMemeType(mimeType);
|
||||
|
||||
const fileExtension = extension(mimeType);
|
||||
const fileExtension = extension(mimeType) as string;
|
||||
if (!fileExtension) goBack();
|
||||
|
||||
const uri = AndroidScoped.appendPath(
|
||||
storageUri,
|
||||
`${uuid.toHexString()}.${fileExtension as string}`,
|
||||
`${uuid.toHexString()}.${fileExtension}`,
|
||||
);
|
||||
|
||||
await FileSystem.cp(file.uri, uri);
|
||||
await FileSystem.cp(file.current.uri, uri);
|
||||
const { size } = await FileSystem.stat(uri);
|
||||
|
||||
realm.write(() => {
|
||||
@@ -60,6 +76,7 @@ const AddMeme = ({
|
||||
id: uuid,
|
||||
type: memeType,
|
||||
uri,
|
||||
mimeType,
|
||||
size,
|
||||
title: memeTitle.parsed,
|
||||
isFavorite: memeIsFavorite,
|
||||
@@ -73,9 +90,7 @@ const AddMeme = ({
|
||||
tag.memesLength = tag.memes.length;
|
||||
});
|
||||
});
|
||||
|
||||
goBack();
|
||||
};
|
||||
}, [goBack, memeIsFavorite, memeTags, memeTitle.parsed, realm, storageUri]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -99,20 +114,46 @@ const AddMeme = ({
|
||||
]}>
|
||||
<View style={[styles.flex, styles.justifyStart]}>
|
||||
<MemeEditor
|
||||
imageUri={file.uri}
|
||||
memeUri={memeUri}
|
||||
memeTitle={memeTitle}
|
||||
setMemeTitle={setMemeTitle}
|
||||
memeTags={memeTags}
|
||||
setMemeTags={setMemeTags}
|
||||
/>
|
||||
</View>
|
||||
<View style={[styles.flex, styles.justifyEnd]}>
|
||||
<View style={[styles.flexRow, styles.fullWidth]}>
|
||||
<Button
|
||||
mode="contained-tonal"
|
||||
icon="plus"
|
||||
onPress={async () => {
|
||||
setIsSavingAndAddingAnother(true);
|
||||
await handleSave();
|
||||
setIsSavingAndAddingAnother(false);
|
||||
file.current = (await pickSingle({
|
||||
type: allowedMimeTypes,
|
||||
}).catch(goBack)) as DocumentPickerResponse;
|
||||
setMemeUri(file.current.uri);
|
||||
setMemeTitle(validateMemeTitle('New Meme'));
|
||||
setMemeIsFavorite(false);
|
||||
setMemeTags(new Map<string, Tag>());
|
||||
}}
|
||||
disabled={!memeTitle.valid || isSaving || isSavingAndAddingAnother}
|
||||
loading={isSavingAndAddingAnother}
|
||||
style={addMemeStyles.saveAndAddButton}>
|
||||
Save & Add
|
||||
</Button>
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="floppy"
|
||||
onPress={handleSave}
|
||||
disabled={!memeTitle.valid || isSaving}
|
||||
loading={isSaving}>
|
||||
onPress={async () => {
|
||||
setIsSaving(true);
|
||||
await handleSave();
|
||||
setIsSaving(false);
|
||||
goBack();
|
||||
}}
|
||||
disabled={!memeTitle.valid || isSaving || isSavingAndAddingAnother}
|
||||
loading={isSaving}
|
||||
style={addMemeStyles.saveButton}>
|
||||
Save
|
||||
</Button>
|
||||
</View>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||
import { Appbar, Button, useTheme } from 'react-native-paper';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { useRealm } from '@realm/react';
|
||||
@@ -13,6 +13,17 @@ import { ORIENTATION, useDimensions } from '../contexts';
|
||||
import { Tag } from '../database';
|
||||
import { TagEditor } from '../components';
|
||||
|
||||
const addTagStyles = StyleSheet.create({
|
||||
saveAndAddButton: {
|
||||
flex: 1,
|
||||
marginRight: 5,
|
||||
},
|
||||
saveButton: {
|
||||
flex: 1,
|
||||
marginLeft: 5,
|
||||
},
|
||||
});
|
||||
|
||||
const AddTag = () => {
|
||||
const { goBack } = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
@@ -24,16 +35,19 @@ const AddTag = () => {
|
||||
validateColor(generateRandomColor()),
|
||||
);
|
||||
|
||||
const handleSave = () => {
|
||||
// Although saving tags is instantaneous, we still want to show a loading
|
||||
// indicator to prevent the user from spamming the save button.
|
||||
const [isSavingAndAddingAnother, setIsSavingAndAddingAnother] =
|
||||
useState(false);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
realm.write(() => {
|
||||
realm.create(Tag.schema.name, {
|
||||
name: tagName.parsed,
|
||||
color: tagColor.parsed,
|
||||
});
|
||||
});
|
||||
|
||||
goBack();
|
||||
};
|
||||
}, [realm, tagColor.parsed, tagName.parsed]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -59,12 +73,31 @@ const AddTag = () => {
|
||||
setTagColor={setTagColor}
|
||||
/>
|
||||
</View>
|
||||
<View style={[styles.flex, styles.justifyEnd]}>
|
||||
<View style={[styles.flexRow, styles.fullWidth]}>
|
||||
<Button
|
||||
mode="contained-tonal"
|
||||
icon="plus"
|
||||
onPress={() => {
|
||||
setIsSavingAndAddingAnother(true);
|
||||
handleSave();
|
||||
setTimeout(() => setIsSavingAndAddingAnother(false), 250);
|
||||
setTagName(validateTagName('newTag'));
|
||||
setTagColor(validateColor(generateRandomColor()));
|
||||
}}
|
||||
disabled={!tagName.valid || isSavingAndAddingAnother}
|
||||
loading={isSavingAndAddingAnother}
|
||||
style={addTagStyles.saveAndAddButton}>
|
||||
Save & Add
|
||||
</Button>
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="floppy"
|
||||
onPress={handleSave}
|
||||
disabled={!tagName.valid || !tagColor.valid}>
|
||||
onPress={() => {
|
||||
handleSave();
|
||||
goBack();
|
||||
}}
|
||||
disabled={!tagName.valid || isSavingAndAddingAnother}
|
||||
style={addTagStyles.saveButton}>
|
||||
Save
|
||||
</Button>
|
||||
</View>
|
||||
|
@@ -1,16 +1,15 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import { Appbar, Button, useTheme } from 'react-native-paper';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import { useObject, useRealm } from '@realm/react';
|
||||
import { FileSystem } from 'react-native-file-access';
|
||||
import { BSON } from 'realm';
|
||||
import { ORIENTATION, useDimensions } from '../contexts';
|
||||
import styles from '../styles';
|
||||
import { RootStackParamList, ROUTE } from '../types';
|
||||
import { Tag, Meme } from '../database';
|
||||
import { validateMemeTitle } from '../utilities';
|
||||
import { deleteMeme, favoriteMeme, validateMemeTitle } from '../utilities';
|
||||
import { MemeEditor } from '../components';
|
||||
|
||||
const EditMeme = ({
|
||||
@@ -34,9 +33,7 @@ const EditMeme = ({
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
const handleSave = () => {
|
||||
setIsSaving(true);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
realm.write(() => {
|
||||
meme.tags.forEach(tag => {
|
||||
if (!memeTags.has(tag.id.toHexString())) {
|
||||
@@ -60,32 +57,7 @@ const EditMeme = ({
|
||||
meme.tagsLength = memeTags.size;
|
||||
meme.dateModified = new Date();
|
||||
});
|
||||
|
||||
goBack();
|
||||
};
|
||||
|
||||
const handleFavorite = () => {
|
||||
realm.write(() => {
|
||||
meme.isFavorite = !meme.isFavorite;
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
setIsSaving(true);
|
||||
await FileSystem.unlink(meme.uri);
|
||||
|
||||
realm.write(() => {
|
||||
for (const tag of meme.tags) {
|
||||
tag.dateModified = new Date();
|
||||
tag.memes.slice(tag.memes.indexOf(meme), 1);
|
||||
tag.memesLength -= 1;
|
||||
}
|
||||
|
||||
realm.delete(meme);
|
||||
});
|
||||
|
||||
goBack();
|
||||
};
|
||||
}, [meme, memeTags, memeTitle.parsed, realm]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -94,9 +66,17 @@ const EditMeme = ({
|
||||
<Appbar.Content title={'Edit Meme'} />
|
||||
<Appbar.Action
|
||||
icon={meme.isFavorite ? 'heart' : 'heart-outline'}
|
||||
onPress={handleFavorite}
|
||||
onPress={() => favoriteMeme(realm, meme)}
|
||||
/>
|
||||
<Appbar.Action
|
||||
icon="delete"
|
||||
onPress={async () => {
|
||||
setIsSaving(true);
|
||||
await deleteMeme(realm, meme);
|
||||
setIsSaving(false);
|
||||
goBack();
|
||||
}}
|
||||
/>
|
||||
<Appbar.Action icon="delete" onPress={handleDelete} />
|
||||
</Appbar.Header>
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
@@ -110,7 +90,7 @@ const EditMeme = ({
|
||||
]}>
|
||||
<View style={[styles.flex, styles.justifyStart]}>
|
||||
<MemeEditor
|
||||
imageUri={meme.uri}
|
||||
memeUri={meme.uri}
|
||||
memeTitle={memeTitle}
|
||||
setMemeTitle={setMemeTitle}
|
||||
memeTags={memeTags}
|
||||
@@ -121,7 +101,12 @@ const EditMeme = ({
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="floppy"
|
||||
onPress={handleSave}
|
||||
onPress={() => {
|
||||
setIsSaving(true);
|
||||
handleSave();
|
||||
setIsSaving(false);
|
||||
goBack();
|
||||
}}
|
||||
disabled={!memeTitle.valid || isSaving}
|
||||
loading={isSaving}>
|
||||
Save
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import { Appbar, Button, useTheme } from 'react-native-paper';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
@@ -10,7 +10,7 @@ import styles from '../styles';
|
||||
import { ORIENTATION, useDimensions } from '../contexts';
|
||||
import { ROUTE, RootStackParamList } from '../types';
|
||||
import { Tag } from '../database';
|
||||
import { validateColor, validateTagName } from '../utilities';
|
||||
import { deleteTag, validateColor, validateTagName } from '../utilities';
|
||||
|
||||
const EditTag = ({
|
||||
route,
|
||||
@@ -29,36 +29,26 @@ const EditTag = ({
|
||||
const [tagName, setTagName] = useState(validateTagName(tag.name));
|
||||
const [tagColor, setTagColor] = useState(validateColor(tag.color));
|
||||
|
||||
const handleSave = () => {
|
||||
const handleSave = useCallback(() => {
|
||||
realm.write(() => {
|
||||
tag.name = tagName.parsed;
|
||||
tag.color = tagColor.parsed;
|
||||
tag.dateModified = new Date();
|
||||
});
|
||||
|
||||
goBack();
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
realm.write(() => {
|
||||
for (const meme of tag.memes) {
|
||||
meme.dateModified = new Date();
|
||||
meme.tags.slice(meme.tags.indexOf(tag), 1);
|
||||
meme.tagsLength -= 1;
|
||||
}
|
||||
|
||||
realm.delete(tag);
|
||||
});
|
||||
|
||||
goBack();
|
||||
};
|
||||
}, [realm, tag, tagColor.parsed, tagName.parsed]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Appbar.Header>
|
||||
<Appbar.BackAction onPress={() => goBack()} />
|
||||
<Appbar.Content title={'Edit Tag'} />
|
||||
<Appbar.Action icon="delete" onPress={handleDelete} />
|
||||
<Appbar.Action
|
||||
icon="delete"
|
||||
onPress={() => {
|
||||
deleteTag(realm, tag);
|
||||
goBack();
|
||||
}}
|
||||
/>
|
||||
</Appbar.Header>
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
@@ -83,7 +73,10 @@ const EditTag = ({
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="floppy"
|
||||
onPress={handleSave}
|
||||
onPress={() => {
|
||||
handleSave();
|
||||
goBack();
|
||||
}}
|
||||
disabled={!tagName.valid || !tagColor.valid}>
|
||||
Save
|
||||
</Button>
|
||||
|
@@ -3,6 +3,7 @@ export { default as AddTag } from './addTag';
|
||||
export { default as EditMeme } from './editMeme';
|
||||
export { default as EditTag } from './editTag';
|
||||
export { default as Memes } from './memes';
|
||||
export { default as MemeView } from './memeView';
|
||||
export { default as Settings } from './settings';
|
||||
export { default as Tags } from './tags';
|
||||
export { default as Welcome } from './welcome';
|
||||
|
136
src/screens/memeView.tsx
Normal file
136
src/screens/memeView.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { NativeStackScreenProps } from '@react-navigation/native-stack';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { useQuery, useRealm } from '@realm/react';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
import { Appbar, Portal, Snackbar } from 'react-native-paper';
|
||||
import { RootStackParamList, ROUTE } from '../types';
|
||||
import { Meme } from '../database';
|
||||
import { useDimensions } from '../contexts';
|
||||
import { MemeViewItem } from '../components';
|
||||
import {
|
||||
copyMeme,
|
||||
deleteMeme,
|
||||
editMeme,
|
||||
favoriteMeme,
|
||||
multipleIdQuery,
|
||||
shareMeme,
|
||||
} from '../utilities';
|
||||
import { NavigationProp, useNavigation } from '@react-navigation/native';
|
||||
import styles from '../styles';
|
||||
|
||||
const memeViewStyles = StyleSheet.create({
|
||||
footer: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 80,
|
||||
},
|
||||
snackbar: {
|
||||
marginBottom: 90,
|
||||
},
|
||||
});
|
||||
|
||||
const MemeView = ({
|
||||
route,
|
||||
}: NativeStackScreenProps<RootStackParamList, ROUTE.MEME_VIEW>) => {
|
||||
const { orientation, dimensions } = useDimensions();
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const realm = useRealm();
|
||||
|
||||
const { ids } = route.params;
|
||||
const [index, setIndex] = useState(route.params.index);
|
||||
|
||||
const [snackbarVisible, setSnackbarVisible] = useState(false);
|
||||
const [snackbarMessage, setSnackbarMessage] = useState('');
|
||||
|
||||
const flashListRef = useRef<FlashList<Meme>>(null);
|
||||
|
||||
const memes = useQuery<Meme>(Meme.schema.name, collectionIn => {
|
||||
return collectionIn.filtered(multipleIdQuery(ids));
|
||||
});
|
||||
|
||||
if (memes.length === 0) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Appbar.Header>
|
||||
<Appbar.BackAction onPress={() => navigation.goBack()} />
|
||||
<Appbar.Content title={memes[index].title} />
|
||||
</Appbar.Header>
|
||||
<View>
|
||||
<FlashList
|
||||
ref={flashListRef}
|
||||
key={orientation}
|
||||
data={memes}
|
||||
initialScrollIndex={index}
|
||||
onScroll={event => {
|
||||
const newIndex = Math.round(
|
||||
event.nativeEvent.contentOffset.x /
|
||||
event.nativeEvent.layoutMeasurement.width,
|
||||
);
|
||||
if (newIndex !== index) setIndex(newIndex);
|
||||
}}
|
||||
estimatedItemSize={dimensions.width}
|
||||
pagingEnabled
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
estimatedListSize={{
|
||||
height: dimensions.height - 160,
|
||||
width: dimensions.width,
|
||||
}}
|
||||
renderItem={({ item: meme }) => <MemeViewItem meme={meme} />}
|
||||
/>
|
||||
</View>
|
||||
<Appbar style={[memeViewStyles.footer, styles.flexRowSpaceEvenly]}>
|
||||
<Appbar.Action
|
||||
icon={memes[index].isFavorite ? 'heart' : 'heart-outline'}
|
||||
onPress={() => favoriteMeme(realm, memes[index])}
|
||||
/>
|
||||
<Appbar.Action icon="share" onPress={() => shareMeme(memes[index])} />
|
||||
<Appbar.Action
|
||||
icon="content-copy"
|
||||
onPress={() => {
|
||||
copyMeme(memes[index]);
|
||||
setSnackbarMessage('Meme copied!');
|
||||
setSnackbarVisible(true);
|
||||
}}
|
||||
/>
|
||||
<Appbar.Action
|
||||
icon="pencil"
|
||||
onPress={() => {
|
||||
editMeme(navigation, memes[index]);
|
||||
}}
|
||||
/>
|
||||
<Appbar.Action
|
||||
icon="delete"
|
||||
onPress={() => {
|
||||
if (index === memes.length - 1) {
|
||||
setIndex(index - 1);
|
||||
flashListRef.current?.scrollToIndex({
|
||||
index: index - 1,
|
||||
});
|
||||
}
|
||||
void deleteMeme(realm, memes[index]);
|
||||
if (memes.length === 1) navigation.goBack();
|
||||
}}
|
||||
/>
|
||||
</Appbar>
|
||||
<Portal>
|
||||
<Snackbar
|
||||
visible={snackbarVisible}
|
||||
onDismiss={() => setSnackbarVisible(false)}
|
||||
style={memeViewStyles.snackbar}
|
||||
action={{
|
||||
label: 'Dismiss',
|
||||
onPress: () => setSnackbarVisible(false),
|
||||
}}>
|
||||
{snackbarMessage}
|
||||
</Snackbar>
|
||||
</Portal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemeView;
|
@@ -1,4 +1,4 @@
|
||||
import React, { RefObject, useCallback, useRef, useState } from 'react';
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import {
|
||||
BackHandler,
|
||||
NativeScrollEvent,
|
||||
@@ -9,71 +9,22 @@ import { useQuery } from '@realm/react';
|
||||
import { useTheme } from 'react-native-paper';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import {
|
||||
ParamListBase,
|
||||
useFocusEffect,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import styles from '../styles';
|
||||
import { SORT_DIRECTION, VIEW, memesSortQuery } from '../types';
|
||||
import { ROUTE, SORT_DIRECTION, memesSortQuery } from '../types';
|
||||
import { RootState, setNavVisible } from '../state';
|
||||
import { Meme } from '../database';
|
||||
import {
|
||||
HideableHeader,
|
||||
MemesHeader,
|
||||
MemesMasonryView,
|
||||
MemesGridView,
|
||||
MemesListView,
|
||||
} from '../components';
|
||||
|
||||
const MemesView = ({
|
||||
memes,
|
||||
flashListRef,
|
||||
flashListPadding,
|
||||
handleScroll,
|
||||
}: {
|
||||
memes: Realm.Results<Meme & Realm.Object<Meme>>;
|
||||
flashListRef: RefObject<FlashList<Meme>>;
|
||||
flashListPadding: number;
|
||||
handleScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
||||
}) => {
|
||||
const view = useSelector((state: RootState) => state.memes.view);
|
||||
|
||||
switch (view) {
|
||||
case VIEW.MASONRY: {
|
||||
return (
|
||||
<MemesMasonryView
|
||||
memes={memes}
|
||||
flashListRef={flashListRef}
|
||||
flashListPadding={flashListPadding}
|
||||
handleScroll={handleScroll}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case VIEW.GRID: {
|
||||
return (
|
||||
<MemesGridView
|
||||
memes={memes}
|
||||
flashListRef={flashListRef}
|
||||
flashListPadding={flashListPadding}
|
||||
handleScroll={handleScroll}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case VIEW.LIST: {
|
||||
return (
|
||||
<MemesListView
|
||||
memes={memes}
|
||||
flashListRef={flashListRef}
|
||||
flashListPadding={flashListPadding}
|
||||
handleScroll={handleScroll}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default: {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
};
|
||||
import { HideableHeader, MemesHeader, MemesList } from '../components';
|
||||
|
||||
const Memes = () => {
|
||||
const { colors } = useTheme();
|
||||
const { navigate } =
|
||||
useNavigation<NativeStackNavigationProp<ParamListBase>>();
|
||||
const sort = useSelector((state: RootState) => state.memes.sort);
|
||||
const sortDirection = useSelector(
|
||||
(state: RootState) => state.memes.sortDirection,
|
||||
@@ -154,11 +105,9 @@ const Memes = () => {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
const handleBackPress = () => {
|
||||
if (scrollOffset > 0) {
|
||||
flashListRef.current?.scrollToOffset({ offset: 0, animated: true });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (scrollOffset <= 0) return false;
|
||||
flashListRef.current?.scrollToOffset({ offset: 0, animated: true });
|
||||
return true;
|
||||
};
|
||||
|
||||
BackHandler.addEventListener('hardwareBackPress', handleBackPress);
|
||||
@@ -190,11 +139,17 @@ const Memes = () => {
|
||||
}}
|
||||
/>
|
||||
</HideableHeader>
|
||||
<MemesView
|
||||
<MemesList
|
||||
memes={memes}
|
||||
flashListRef={flashListRef}
|
||||
flashListPadding={flashListPadding}
|
||||
handleScroll={handleScroll}
|
||||
focusMeme={(index: number) => {
|
||||
navigate(ROUTE.MEME_VIEW, {
|
||||
ids: memes.map(meme => meme.id.toHexString()),
|
||||
index,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
@@ -19,12 +19,12 @@ import { SORT_DIRECTION, tagSortQuery } from '../types';
|
||||
import { ORIENTATION, useDimensions } from '../contexts';
|
||||
|
||||
const tagsStyles = StyleSheet.create({
|
||||
helperText: {
|
||||
marginVertical: 10,
|
||||
},
|
||||
flashList: {
|
||||
paddingBottom: 100,
|
||||
},
|
||||
helperText: {
|
||||
marginVertical: 15,
|
||||
},
|
||||
});
|
||||
|
||||
const Tags = () => {
|
||||
|
Reference in New Issue
Block a user