Add tag selector layout animations
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@karaolidis/terminally-online",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@karaolidis/terminally-online",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@bankify/redux-persist-realm": "^0.1.3",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@karaolidis/terminally-online",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"postinstall": "patch-package",
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { AppState, StatusBar, useColorScheme, StyleSheet } from 'react-native';
|
||||
import {
|
||||
AppState,
|
||||
StatusBar,
|
||||
useColorScheme,
|
||||
StyleSheet,
|
||||
UIManager,
|
||||
} from 'react-native';
|
||||
import { PaperProvider } from 'react-native-paper';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import { RealmProvider } from '@realm/react';
|
||||
@@ -27,6 +33,7 @@ const App = () => {
|
||||
const theme = isDarkMode ? darkTheme : lightTheme;
|
||||
|
||||
const onBeforeLift = async () => {
|
||||
UIManager.setLayoutAnimationEnabledExperimental(true);
|
||||
await store.dispatch(validateSettings());
|
||||
const { settings } = store.getState();
|
||||
if (!settings.storageUri) {
|
||||
|
@@ -2,7 +2,6 @@ export {
|
||||
MemesList,
|
||||
MemeEditor,
|
||||
MemesHeader,
|
||||
MemeTagSearchModal,
|
||||
MemeTagSelector,
|
||||
MemeViewItem,
|
||||
} from './memes';
|
||||
|
@@ -2,6 +2,5 @@ export { default as MemesList } from './memesList/memesList';
|
||||
export { default as MemeEditor } from './memeEditor';
|
||||
export { default as MemeFail } from './memeFail';
|
||||
export { default as MemesHeader } from './memesHeader';
|
||||
export { default as MemeTagSearchModal } from './memeTagSearchModal';
|
||||
export { default as MemeTagSelector } from './memeTagSelector';
|
||||
export { default as MemeTagSelector } from './memeTagSelector/memeTagSelector';
|
||||
export { default as MemeViewItem } from './memeViewItem';
|
||||
|
@@ -41,7 +41,7 @@ const MemeEditor = ({
|
||||
const { width } = useSafeAreaFrame();
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri: memeUri });
|
||||
setMemeUriError(error);
|
||||
useEffect(() => setMemeUriError(error), [error, setMemeUriError]);
|
||||
|
||||
if (!memeUriError && (loading || !dimensions)) return <LoadingView />;
|
||||
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { useQuery, useRealm } from '@realm/react';
|
||||
import { Chip, Modal, Portal, Searchbar, useTheme } from 'react-native-paper';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { LayoutAnimation, StyleSheet } from 'react-native';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { TAG_SORT, tagSortQuery } from '../../types';
|
||||
import { TagChip } from '../tags';
|
||||
import { Tag } from '../../database';
|
||||
import { validateTagName } from '../../utilities';
|
||||
import { TAG_SORT, tagSortQuery } from '../../../types';
|
||||
import { TagChip } from '../../tags';
|
||||
import { Tag } from '../../../database';
|
||||
import { validateTagName } from '../../../utilities';
|
||||
|
||||
const memeTagSearchModalStyles = StyleSheet.create({
|
||||
modal: {
|
||||
@@ -26,6 +26,22 @@ const memeTagSearchModalStyles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
const tagLayoutAnimation = {
|
||||
duration: 150,
|
||||
create: {
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.opacity,
|
||||
},
|
||||
update: {
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.opacity,
|
||||
},
|
||||
delete: {
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.opacity,
|
||||
},
|
||||
};
|
||||
|
||||
const MemeTagSearchModal = ({
|
||||
visible,
|
||||
setVisible,
|
||||
@@ -46,13 +62,10 @@ const MemeTagSearchModal = ({
|
||||
const [search, setSearch] = useState('');
|
||||
const [tagName, setTagName] = useState(validateTagName(search));
|
||||
|
||||
useEffect(() => {
|
||||
setTagName(validateTagName(search));
|
||||
}, [search]);
|
||||
|
||||
const handleSearch = (newSearch: string) => {
|
||||
flashListRef.current?.scrollToOffset({ offset: 0 });
|
||||
setSearch(newSearch);
|
||||
setTagName(validateTagName(newSearch));
|
||||
};
|
||||
|
||||
const tags = useQuery<Tag>(
|
||||
@@ -78,6 +91,8 @@ const MemeTagSearchModal = ({
|
||||
const id = tag.id.toHexString();
|
||||
memeTags.delete(id) || memeTags.set(id, tag);
|
||||
setMemeTags(new Map(memeTags));
|
||||
flashListRef.current?.prepareForLayoutAnimationRender();
|
||||
LayoutAnimation.configureNext(tagLayoutAnimation);
|
||||
};
|
||||
|
||||
const handleCreateTag = (name: string) => {
|
||||
@@ -90,6 +105,8 @@ const MemeTagSearchModal = ({
|
||||
if (!tag) return;
|
||||
memeTags.set(tag.id.toHexString(), tag);
|
||||
setMemeTags(new Map(memeTags));
|
||||
flashListRef.current?.prepareForLayoutAnimationRender();
|
||||
LayoutAnimation.configureNext(tagLayoutAnimation);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -114,6 +131,7 @@ const MemeTagSearchModal = ({
|
||||
ref={flashListRef}
|
||||
data={tags}
|
||||
extraData={memeTags}
|
||||
keyExtractor={tag => tag.id.toHexString()}
|
||||
horizontal
|
||||
estimatedItemSize={120}
|
||||
estimatedListSize={{
|
@@ -1,11 +1,11 @@
|
||||
import React, { ComponentProps, useState } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import React, { ComponentProps, useRef, useState } from 'react';
|
||||
import { LayoutAnimation, StyleSheet, View } from 'react-native';
|
||||
import { Chip } from 'react-native-paper';
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { TagChip } from '../tags';
|
||||
import { Tag } from '../../database';
|
||||
import { MemeTagSearchModal } from '.';
|
||||
import { TagChip } from '../../tags';
|
||||
import { Tag } from '../../../database';
|
||||
import MemeTagSearchModal from './memeTagSearchModal';
|
||||
|
||||
const memeTagSelectorStyles = StyleSheet.create({
|
||||
tagChip: {
|
||||
@@ -13,6 +13,22 @@ const memeTagSelectorStyles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
const tagLayoutAnimation = {
|
||||
duration: 150,
|
||||
create: {
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.opacity,
|
||||
},
|
||||
update: {
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.opacity,
|
||||
},
|
||||
delete: {
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.opacity,
|
||||
},
|
||||
};
|
||||
|
||||
const MemeTagSelector = ({
|
||||
memeTags,
|
||||
setMemeTags,
|
||||
@@ -23,20 +39,26 @@ const MemeTagSelector = ({
|
||||
} & ComponentProps<typeof View>) => {
|
||||
const { width } = useSafeAreaFrame();
|
||||
|
||||
const flashListRef = useRef<FlashList<Tag>>(null);
|
||||
|
||||
const [flashListMargin, setFlashListMargin] = useState(0);
|
||||
const [tagSearchModalVisible, setTagSearchModalVisible] = useState(false);
|
||||
|
||||
const handleTagPress = (tag: Tag) => {
|
||||
const id = tag.id.toHexString();
|
||||
memeTags.delete(id) || memeTags.set(id, tag);
|
||||
memeTags.delete(id);
|
||||
setMemeTags(new Map(memeTags));
|
||||
flashListRef.current?.prepareForLayoutAnimationRender();
|
||||
LayoutAnimation.configureNext(tagLayoutAnimation);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<View {...props}>
|
||||
<FlashList
|
||||
ref={flashListRef}
|
||||
data={[...memeTags.values()]}
|
||||
keyExtractor={tag => tag.id.toHexString()}
|
||||
horizontal
|
||||
estimatedItemSize={120}
|
||||
showsHorizontalScrollIndicator={false}
|
Reference in New Issue
Block a user