diff --git a/package-lock.json b/package-lock.json
index 16c2f69..e997a8d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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",
diff --git a/package.json b/package.json
index a2aebef..2f86d06 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@karaolidis/terminally-online",
- "version": "0.0.1",
+ "version": "0.0.2",
"private": true,
"scripts": {
"postinstall": "patch-package",
diff --git a/src/app.tsx b/src/app.tsx
index 783ee99..8a38997 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -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) {
diff --git a/src/components/index.ts b/src/components/index.ts
index ad5eaa2..c6aa651 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -2,7 +2,6 @@ export {
MemesList,
MemeEditor,
MemesHeader,
- MemeTagSearchModal,
MemeTagSelector,
MemeViewItem,
} from './memes';
diff --git a/src/components/memes/index.ts b/src/components/memes/index.ts
index 97bc873..dfd802c 100644
--- a/src/components/memes/index.ts
+++ b/src/components/memes/index.ts
@@ -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';
diff --git a/src/components/memes/memeEditor.tsx b/src/components/memes/memeEditor.tsx
index d811de1..ca3b439 100644
--- a/src/components/memes/memeEditor.tsx
+++ b/src/components/memes/memeEditor.tsx
@@ -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 ;
diff --git a/src/components/memes/memeTagSearchModal.tsx b/src/components/memes/memeTagSelector/memeTagSearchModal.tsx
similarity index 77%
rename from src/components/memes/memeTagSearchModal.tsx
rename to src/components/memes/memeTagSelector/memeTagSearchModal.tsx
index de60300..cf6f408 100644
--- a/src/components/memes/memeTagSearchModal.tsx
+++ b/src/components/memes/memeTagSelector/memeTagSearchModal.tsx
@@ -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(
@@ -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={{
diff --git a/src/components/memes/memeTagSelector.tsx b/src/components/memes/memeTagSelector/memeTagSelector.tsx
similarity index 67%
rename from src/components/memes/memeTagSelector.tsx
rename to src/components/memes/memeTagSelector/memeTagSelector.tsx
index a4a00ac..064e2b7 100644
--- a/src/components/memes/memeTagSelector.tsx
+++ b/src/components/memes/memeTagSelector/memeTagSelector.tsx
@@ -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) => {
const { width } = useSafeAreaFrame();
+ const flashListRef = useRef>(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 (
<>
tag.id.toHexString()}
horizontal
estimatedItemSize={120}
showsHorizontalScrollIndicator={false}