From 1b09b058e45b597779c2fa5a5dce0f96a64037b2 Mon Sep 17 00:00:00 2001 From: Nikolaos Karaolidis Date: Fri, 4 Aug 2023 13:39:55 +0300 Subject: [PATCH] Improve performance Signed-off-by: Nikolaos Karaolidis --- package-lock.json | 16 +++++++ package.json | 1 + src/screens/editors/meme/memeEditor.tsx | 11 +++-- src/screens/memes/memesList/memesGridItem.tsx | 46 +++++++------------ src/screens/memes/memesList/memesList.tsx | 30 ++++++++++-- src/screens/memes/memesList/memesListItem.tsx | 35 +++++--------- .../memes/memesList/memesMasonryItem.tsx | 41 ++++++++--------- 7 files changed, 95 insertions(+), 85 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9dd6a6..e8db158 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "react": "18.2.0", "react-native": "0.72.2", "react-native-document-picker": "^9.0.1", + "react-native-fast-image": "^8.6.3", "react-native-file-access": "^3.0.4", "react-native-gesture-handler": "^2.12.0", "react-native-get-random-values": "^1.9.0", @@ -13434,6 +13435,15 @@ } } }, + "node_modules/react-native-fast-image": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz", + "integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==", + "peerDependencies": { + "react": "^17 || ^18", + "react-native": ">=0.60.0" + } + }, "node_modules/react-native-file-access": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-native-file-access/-/react-native-file-access-3.0.4.tgz", @@ -25861,6 +25871,12 @@ "invariant": "^2.2.4" } }, + "react-native-fast-image": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz", + "integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==", + "requires": {} + }, "react-native-file-access": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-native-file-access/-/react-native-file-access-3.0.4.tgz", diff --git a/package.json b/package.json index d12a95f..2510a67 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "react": "18.2.0", "react-native": "0.72.2", "react-native-document-picker": "^9.0.1", + "react-native-fast-image": "^8.6.3", "react-native-file-access": "^3.0.4", "react-native-gesture-handler": "^2.12.0", "react-native-get-random-values": "^1.9.0", diff --git a/src/screens/editors/meme/memeEditor.tsx b/src/screens/editors/meme/memeEditor.tsx index 919a63a..efed875 100644 --- a/src/screens/editors/meme/memeEditor.tsx +++ b/src/screens/editors/meme/memeEditor.tsx @@ -1,8 +1,12 @@ import React, { useEffect, useMemo, useState } from 'react'; import { HelperText, Text, TextInput, useTheme } from 'react-native-paper'; -import { Image, LayoutAnimation, View } from 'react-native'; +import { LayoutAnimation, View } from 'react-native'; import { useSafeAreaFrame } from 'react-native-safe-area-context'; import Video from 'react-native-video'; +import TextRecognition, { + TextRecognitionResult, +} from '@react-native-ml-kit/text-recognition'; +import FastImage from 'react-native-fast-image'; import { LoadingView, MemeFail, TextOverlay } from '../../../components'; import { getFilenameFromUri, @@ -13,9 +17,6 @@ import { StagingMeme } from '../../../types'; import { useMemeDimensions } from '../../../hooks'; import { MEME_TYPE } from '../../../database'; import MemeTagSelector from './memeTagSelector/memeTagSelector'; -import TextRecognition, { - TextRecognitionResult, -} from '@react-native-ml-kit/text-recognition'; const memeEditorStyles = { media: { @@ -95,7 +96,7 @@ const MemeEditor = ({ case MEME_TYPE.GIF: { return ( - void; + focusMeme: () => void; + uri: string; + columns: number; }) => { const { width } = useSafeAreaFrame(); - const gridColumns = useSelector( - (state: RootState) => state.settings.gridColumns, - ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const storageUri = useSelector( - (state: RootState) => state.settings.storageUri, - )!; - const uri = AndroidScoped.appendPath(storageUri, meme.filename); + const itemWidth = useMemo( + () => (width * 0.92 - 5) / columns, + [columns, width], + ); const { dimensions, loading, error } = useMemeDimensions(uri, meme.mimeType); - const itemWidth = (width * 0.92 - 5) / gridColumns; - const mediaComponent = useMemo(() => { switch (meme.memeType) { case MEME_TYPE.IMAGE: case MEME_TYPE.GIF: case MEME_TYPE.VIDEO: { return ( - ); } @@ -59,14 +48,11 @@ const MemesGridItem = ({ if (!error && (loading || !dimensions)) return <>; return ( - focusMeme(index)}> + {error ? ( ) : ( mediaComponent diff --git a/src/screens/memes/memesList/memesList.tsx b/src/screens/memes/memesList/memesList.tsx index 3a7fa8d..28012d2 100644 --- a/src/screens/memes/memesList/memesList.tsx +++ b/src/screens/memes/memesList/memesList.tsx @@ -15,6 +15,7 @@ import { getFlashListItemHeight } from '../../../utilities'; import MemesMasonryItem from './memesMasonryItem'; import MemesGridItem from './memesGridItem'; import MemesListItem from './memesListItem'; +import { AndroidScoped } from 'react-native-file-access'; const sharedMemesListStyles = StyleSheet.create({ flashList: { @@ -66,6 +67,10 @@ const MemesList = ({ const gridColumns = useSelector( (state: RootState) => state.settings.gridColumns, ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const storageUri = useSelector( + (state: RootState) => state.settings.storageUri, + )!; return ( <> @@ -82,7 +87,12 @@ const MemesList = ({ numColumns={masonryColumns} showsVerticalScrollIndicator={false} renderItem={({ item: meme, index }) => ( - + focusMeme(index)} + uri={AndroidScoped.appendPath(storageUri, meme.filename)} + columns={masonryColumns} + /> )} contentContainerStyle={{ paddingTop: flashListPadding, @@ -96,6 +106,7 @@ const MemesList = ({ )} onScroll={handleScroll} fadingEdgeLength={100} + overScrollMode="never" /> )} {view === VIEW.GRID && ( @@ -111,7 +122,12 @@ const MemesList = ({ numColumns={gridColumns} showsVerticalScrollIndicator={false} renderItem={({ item: meme, index }) => ( - + focusMeme(index)} + uri={AndroidScoped.appendPath(storageUri, meme.filename)} + columns={gridColumns} + /> )} contentContainerStyle={{ paddingTop: flashListPadding, @@ -125,20 +141,25 @@ const MemesList = ({ )} onScroll={handleScroll} fadingEdgeLength={100} + overScrollMode="never" /> )} {view === VIEW.LIST && ( ( - + focusMeme(index)} + uri={AndroidScoped.appendPath(storageUri, meme.filename)} + /> )} ItemSeparatorComponent={() => } contentContainerStyle={{ @@ -153,6 +174,7 @@ const MemesList = ({ )} onScroll={handleScroll} fadingEdgeLength={100} + overScrollMode="never" /> )} diff --git a/src/screens/memes/memesList/memesListItem.tsx b/src/screens/memes/memesList/memesListItem.tsx index da81188..17347ea 100644 --- a/src/screens/memes/memesList/memesListItem.tsx +++ b/src/screens/memes/memesList/memesListItem.tsx @@ -1,12 +1,10 @@ import React, { useMemo } from 'react'; -import { Image, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { Text, TouchableRipple } from 'react-native-paper'; import { useSafeAreaFrame } from 'react-native-safe-area-context'; -import { AndroidScoped } from 'react-native-file-access'; -import { useSelector } from 'react-redux'; +import FastImage from 'react-native-fast-image'; import { MEME_TYPE, Meme } from '../../../database'; import { MemeFail } from '../../../components'; -import { RootState } from '../../../state'; import { useMemeDimensions } from '../../../hooks'; const memesListItemStyles = StyleSheet.create({ @@ -35,20 +33,19 @@ const memesListItemStyles = StyleSheet.create({ const MemesListItem = ({ meme, - index, focusMeme, + uri, }: { meme: Meme; - index: number; - focusMeme: (index: number) => void; + focusMeme: () => void; + uri: string; }) => { const { width } = useSafeAreaFrame(); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const storageUri = useSelector( - (state: RootState) => state.settings.storageUri, - )!; - const uri = AndroidScoped.appendPath(storageUri, meme.filename); + const listItemWidth = useMemo( + () => ({ width: width * 0.92 - 75 - 10 }), + [width], + ); const { dimensions, loading, error } = useMemeDimensions(uri, meme.mimeType); @@ -57,7 +54,7 @@ const MemesListItem = ({ case MEME_TYPE.IMAGE: case MEME_TYPE.GIF: case MEME_TYPE.VIDEO: { - return ; + return ; } default: { return <>; @@ -68,22 +65,14 @@ const MemesListItem = ({ if (!error && (loading || !dimensions)) return <>; return ( - focusMeme(index)} - style={memesListItemStyles.view}> + <> {error ? ( ) : ( mediaComponent )} - + {meme.title} diff --git a/src/screens/memes/memesList/memesMasonryItem.tsx b/src/screens/memes/memesList/memesMasonryItem.tsx index 5407fde..69f5328 100644 --- a/src/screens/memes/memesList/memesMasonryItem.tsx +++ b/src/screens/memes/memesList/memesMasonryItem.tsx @@ -1,10 +1,8 @@ import React, { useMemo } from 'react'; -import { Image, StyleSheet, TouchableHighlight } from 'react-native'; -import { useSelector } from 'react-redux'; +import { StyleSheet, TouchableHighlight } from 'react-native'; import { useSafeAreaFrame } from 'react-native-safe-area-context'; -import { AndroidScoped } from 'react-native-file-access'; +import FastImage from 'react-native-fast-image'; import { MEME_TYPE, Meme } from '../../../database'; -import { RootState } from '../../../state'; import { MemeFail } from '../../../components'; import { getFontAwesome5IconSize } from '../../../utilities'; import { useMemeDimensions } from '../../../hooks'; @@ -21,29 +19,28 @@ const memeMasonryItemStyles = StyleSheet.create({ const MemesMasonryItem = ({ meme, - index, focusMeme, + uri, + columns, }: { meme: Meme; - index: number; - focusMeme: (index: number) => void; + focusMeme: () => void; + uri: string; + columns: number; }) => { const { width } = useSafeAreaFrame(); - const masonryColumns = useSelector( - (state: RootState) => state.settings.masonryColumns, - ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const storageUri = useSelector( - (state: RootState) => state.settings.storageUri, - )!; - const uri = AndroidScoped.appendPath(storageUri, meme.filename); + const itemWidth = useMemo( + () => (width * 0.92 - 5) / columns - 5, + [columns, width], + ); const { dimensions, loading, error } = useMemeDimensions(uri, meme.mimeType); - const itemWidth = (width * 0.92 - 5) / masonryColumns - 5; - const itemHeight = - ((width * 0.92) / masonryColumns - 5) / (dimensions?.aspectRatio ?? 1); + const itemHeight = useMemo( + () => ((width * 0.92) / columns - 5) / (dimensions?.aspectRatio ?? 1), + [columns, dimensions?.aspectRatio, width], + ); const mediaComponent = useMemo(() => { switch (meme.memeType) { @@ -51,7 +48,7 @@ const MemesMasonryItem = ({ case MEME_TYPE.GIF: case MEME_TYPE.VIDEO: { return ( - ; return ( - focusMeme(index)} - style={memeMasonryItemStyles.view}> + {error || !dimensions ? ( ) : ( mediaComponent