diff --git a/package-lock.json b/package-lock.json
index 551a997..16c2f69 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@bankify/redux-persist-realm": "^0.1.3",
"@likashefqet/react-native-image-zoom": "^1.3.0",
"@react-native-clipboard/clipboard": "^1.11.2",
+ "@react-native-community/hooks": "^3.0.0",
"@react-navigation/bottom-tabs": "^6.5.8",
"@react-navigation/native": "^6.1.7",
"@react-navigation/native-stack": "^6.9.13",
@@ -4112,6 +4113,15 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/@react-native-community/hooks": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@react-native-community/hooks/-/hooks-3.0.0.tgz",
+ "integrity": "sha512-g2OyxXHfwIytXUJitBR6Z/ISoOfp0WKx5FOv+NqJ/CrWjRDcTw6zXE5I1C9axfuh30kJqzWchVfCDrkzZYTxqg==",
+ "peerDependencies": {
+ "react": ">=17.0.2",
+ "react-native": ">=0.65"
+ }
+ },
"node_modules/@react-native/assets-registry": {
"version": "0.72.0",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.72.0.tgz",
@@ -18759,6 +18769,12 @@
"joi": "^17.2.1"
}
},
+ "@react-native-community/hooks": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@react-native-community/hooks/-/hooks-3.0.0.tgz",
+ "integrity": "sha512-g2OyxXHfwIytXUJitBR6Z/ISoOfp0WKx5FOv+NqJ/CrWjRDcTw6zXE5I1C9axfuh30kJqzWchVfCDrkzZYTxqg==",
+ "requires": {}
+ },
"@react-native/assets-registry": {
"version": "0.72.0",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.72.0.tgz",
diff --git a/package.json b/package.json
index a9f5bb6..a2aebef 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"@bankify/redux-persist-realm": "^0.1.3",
"@likashefqet/react-native-image-zoom": "^1.3.0",
"@react-native-clipboard/clipboard": "^1.11.2",
+ "@react-native-community/hooks": "^3.0.0",
"@react-navigation/bottom-tabs": "^6.5.8",
"@react-navigation/native": "^6.1.7",
"@react-navigation/native-stack": "^6.9.13",
diff --git a/src/app.tsx b/src/app.tsx
index 787e8c2..f44907a 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -14,7 +14,6 @@ import { store, persistor, validateSettings } from './state';
import { LoadingView } from './components';
import { Welcome } from './screens';
import styles from './styles';
-import { DimensionsProvider } from './contexts';
const App = () => {
const [showWelcome, setShowWelcome] = useState(false);
@@ -44,30 +43,28 @@ const App = () => {
return (
-
-
- }
- persistor={persistor}
- onBeforeLift={onBeforeLift}>
-
-
-
-
- {showWelcome ? (
- setShowWelcome(false)} />
- ) : (
-
- )}
-
-
-
-
-
-
+
+ }
+ persistor={persistor}
+ onBeforeLift={onBeforeLift}>
+
+
+
+
+ {showWelcome ? (
+ setShowWelcome(false)} />
+ ) : (
+
+ )}
+
+
+
+
+
);
};
diff --git a/src/components/floatingActionButton.tsx b/src/components/floatingActionButton.tsx
index d02439a..8f2deed 100644
--- a/src/components/floatingActionButton.tsx
+++ b/src/components/floatingActionButton.tsx
@@ -4,9 +4,9 @@ import { FAB } from 'react-native-paper';
import { ParamListBase, useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { pickSingle } from 'react-native-document-picker';
-import { ORIENTATION, useDimensions } from '../contexts';
import { ROUTE } from '../types';
import { allowedMimeTypes, noOp } from '../utilities';
+import { useDeviceOrientation } from '@react-native-community/hooks';
const floatingActionButtonStyles = StyleSheet.create({
fab: {
@@ -22,7 +22,7 @@ const floatingActionButtonStyles = StyleSheet.create({
const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
const { navigate } =
useNavigation>();
- const { orientation } = useDimensions();
+ const orientation = useDeviceOrientation();
const [state, setState] = useState(false);
const [keyboardOpen, setKeyboardOpen] = useState(false);
@@ -70,7 +70,7 @@ const FloatingActionButton = ({ visible = true }: { visible?: boolean }) => {
navigate(ROUTE.ADD_MEME, { file });
}}
style={
- orientation === ORIENTATION.PORTRAIT
+ orientation === 'portrait'
? floatingActionButtonStyles.fab
: floatingActionButtonStyles.fabLandscape
}
diff --git a/src/components/hideableHeader.tsx b/src/components/hideableHeader.tsx
index b464fd7..2bc0e1a 100644
--- a/src/components/hideableHeader.tsx
+++ b/src/components/hideableHeader.tsx
@@ -1,8 +1,8 @@
import React, { useEffect, useRef } from 'react';
import { Animated, StyleSheet } from 'react-native';
-import { ORIENTATION, useDimensions } from '../contexts';
-import styles from '../styles';
import { useTheme } from 'react-native-paper';
+import { useDeviceOrientation } from '@react-native-community/hooks';
+import styles from '../styles';
const hideableHeaderStyles = StyleSheet.create({
headerView: {
@@ -22,7 +22,7 @@ const HideableHeader = ({
children: React.ReactNode;
}) => {
const { colors } = useTheme();
- const { orientation } = useDimensions();
+ const orientation = useDeviceOrientation();
const headerAnim = useRef(new Animated.Value(visible ? 1 : 0)).current;
@@ -38,9 +38,7 @@ const HideableHeader = ({
{
return (
;
setMemeTags: (tags: Map) => void;
}) => {
- const { dimensions } = useDimensions();
+ const { width } = useSafeAreaFrame();
- const [imageWidth, setImageWidth] = useState();
- const [imageHeight, setImageHeight] = useState();
+ const { dimensions, loading, error } = useImageDimensions({ uri: memeUri });
- useEffect(() => {
- // eslint-disable-next-line unicorn/no-useless-undefined
- setImageWidth(undefined);
- // eslint-disable-next-line unicorn/no-useless-undefined
- setImageHeight(undefined);
-
- Image.getSize(memeUri, (width, height) => {
- const paddedWidth = dimensions.width * 0.92;
- const paddedHeight = Math.max(
- Math.min((paddedWidth / width) * height, 500),
- 100,
- );
-
- setImageWidth(paddedWidth);
- setImageHeight(paddedHeight);
- });
- }, [memeUri, dimensions.width]);
-
- if (!imageWidth || !imageHeight) return ;
+ if (loading || error || !dimensions) return ;
return (
<>
@@ -75,8 +57,14 @@ const MemeEditor = ({
source={{ uri: memeUri }}
style={[
{
- width: imageWidth,
- height: imageHeight,
+ width: width * 0.92,
+ height: Math.max(
+ Math.min(
+ ((width * 0.92) / dimensions.width) * dimensions.height,
+ 500,
+ ),
+ 100,
+ ),
},
memeEditorStyles.image,
]}
diff --git a/src/components/memes/memeTagSearchModal.tsx b/src/components/memes/memeTagSearchModal.tsx
index acf14cb..9a73b22 100644
--- a/src/components/memes/memeTagSearchModal.tsx
+++ b/src/components/memes/memeTagSearchModal.tsx
@@ -1,12 +1,13 @@
import React, { useEffect, useRef, useState } from 'react';
-import { TagChip } from '../tags';
-import { Tag } from '../../database';
import { useQuery, useRealm } from '@realm/react';
-import { TAG_SORT, tagSortQuery } from '../../types';
import { Chip, Modal, Portal, Searchbar, useTheme } from 'react-native-paper';
import { StyleSheet } from 'react-native';
-import styles from '../../styles';
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 styles from '../../styles';
import { validateTagName } from '../../utilities';
const memeTagSearchModalStyles = StyleSheet.create({
@@ -36,6 +37,7 @@ const MemeTagSearchModal = ({
memeTags: Map;
setMemeTags: (tags: Map) => void;
}) => {
+ const { width } = useSafeAreaFrame();
const { colors } = useTheme();
const realm = useRealm();
@@ -115,6 +117,10 @@ const MemeTagSearchModal = ({
extraData={memeTags}
horizontal
estimatedItemSize={120}
+ estimatedListSize={{
+ width: width - 10,
+ height: 34.5,
+ }}
showsHorizontalScrollIndicator={false}
keyboardShouldPersistTaps={'always'}
renderItem={({ item: tag }) => (
@@ -135,6 +141,7 @@ const MemeTagSearchModal = ({
Create Tag #{tagName.valid ? tagName.parsed : 'newTag'}
)}
+ fadingEdgeLength={50}
/>
diff --git a/src/components/memes/memeTagSelector.tsx b/src/components/memes/memeTagSelector.tsx
index 6bd04af..a4a00ac 100644
--- a/src/components/memes/memeTagSelector.tsx
+++ b/src/components/memes/memeTagSelector.tsx
@@ -1,11 +1,11 @@
import React, { ComponentProps, useState } from 'react';
import { 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 { useDimensions } from '../../contexts';
import { MemeTagSearchModal } from '.';
-import { FlashList } from '@shopify/flash-list';
const memeTagSelectorStyles = StyleSheet.create({
tagChip: {
@@ -21,7 +21,7 @@ const MemeTagSelector = ({
memeTags: Map;
setMemeTags: (tags: Map) => void;
} & ComponentProps) => {
- const { dimensions } = useDimensions();
+ const { width } = useSafeAreaFrame();
const [flashListMargin, setFlashListMargin] = useState(0);
const [tagSearchModalVisible, setTagSearchModalVisible] = useState(false);
@@ -37,7 +37,6 @@ const MemeTagSelector = ({
setTagSearchModalVisible(true)}
onLayout={event =>
setFlashListMargin(
- dimensions.width * 0.92 - event.nativeEvent.layout.width,
+ width * 0.92 - event.nativeEvent.layout.width,
)
}
style={{
@@ -64,6 +63,7 @@ const MemeTagSelector = ({
Add Tag
)}
+ fadingEdgeLength={50}
/>
{
- const { dimensions } = useDimensions();
- const { colors } = useTheme();
+ const { height, width } = useSafeAreaFrame();
- const [imageWidth, setImageWidth] = useState();
- const [imageHeight, setImageHeight] = useState();
+ const { dimensions, loading, error } = useImageDimensions({ uri: meme.uri });
- useEffect(() => {
- Image.getSize(meme.uri, (width, height) => {
- const ratio = width / height;
- const screenRatio = dimensions.width / dimensions.height - 160;
-
- if (ratio > screenRatio) {
- setImageWidth(dimensions.width);
- setImageHeight(dimensions.width / ratio);
- } else {
- setImageWidth(dimensions.height * ratio);
- setImageHeight(dimensions.height);
- }
- });
- }, [meme.uri, dimensions.width, dimensions.height]);
+ if (loading || error || !dimensions) return ;
return (
-
- {imageWidth && imageHeight ? (
-
- ) : (
-
- )}
+
+ width / (height - 128)
+ ? {
+ width,
+ height: width / (dimensions.width / dimensions.height),
+ }
+ : {
+ width: (height - 128) * (dimensions.width / dimensions.height),
+ height: height - 128,
+ }
+ }
+ minScale={0.5}
+ />
);
};
diff --git a/src/components/memes/memesList/memesGridItem.tsx b/src/components/memes/memesList/memesGridItem.tsx
index d211e13..d6acb22 100644
--- a/src/components/memes/memesList/memesGridItem.tsx
+++ b/src/components/memes/memesList/memesGridItem.tsx
@@ -1,8 +1,9 @@
-import React, { useState } from 'react';
-import { Image, TouchableHighlight, View } from 'react-native';
+import React from 'react';
+import { Image, TouchableHighlight } from 'react-native';
import { useSelector } from 'react-redux';
+import { useSafeAreaFrame } from 'react-native-safe-area-context';
+import { useImageDimensions } from '@react-native-community/hooks';
import { Meme } from '../../../database';
-import { useDimensions } from '../../../contexts';
import { RootState } from '../../../state';
const MemesGridItem = ({
@@ -14,33 +15,27 @@ const MemesGridItem = ({
index: number;
focusMeme: (index: number) => void;
}) => {
- const { dimensions } = useDimensions();
+ const { width } = useSafeAreaFrame();
const gridColumns = useSelector(
(state: RootState) => state.settings.gridColumns,
);
- const [imageWidth, setImageWidth] = useState();
- const [imageHeight, setImageHeight] = useState();
+ const { dimensions, loading, error } = useImageDimensions({ uri: meme.uri });
- Image.getSize(meme.uri, () => {
- const paddedWidth = (dimensions.width * 0.92 - 5) / gridColumns;
- setImageWidth(paddedWidth);
- setImageHeight(paddedWidth);
- });
+ if (loading || error || !dimensions) return <>>;
return (
- <>
- {imageWidth && imageHeight && (
-
- focusMeme(index)}>
-
-
-
- )}
- >
+ focusMeme(index)}>
+
+
);
};
diff --git a/src/components/memes/memesList/memesList.tsx b/src/components/memes/memesList/memesList.tsx
index 595ecb3..d1b6766 100644
--- a/src/components/memes/memesList/memesList.tsx
+++ b/src/components/memes/memesList/memesList.tsx
@@ -7,7 +7,7 @@ import {
} from 'react-native';
import { useSelector } from 'react-redux';
import { Divider, HelperText } from 'react-native-paper';
-import { useDimensions, ORIENTATION } from '../../../contexts';
+import { useSafeAreaFrame } from 'react-native-safe-area-context';
import { Meme } from '../../../database';
import { RootState } from '../../../state';
import { VIEW } from '../../../types';
@@ -17,35 +17,32 @@ import MemesMasonryItem from './memesMasonryItem';
import MemesGridItem from './memesGridItem';
import MemesListItem from './memesListItem';
-const memesMasonryListStyles = StyleSheet.create({
+const sharedMemesListStyles = StyleSheet.create({
flashList: {
paddingBottom: 100,
- // Needed to prevent fucky MasonryFlashList, see https://github.com/Shopify/flash-list/issues/876
- paddingHorizontal: 0.1,
},
helperText: {
marginVertical: 15,
},
});
+const memesMasonryListStyles = StyleSheet.create({
+ flashList: {
+ // Needed to prevent fucky MasonryFlashList, see https://github.com/Shopify/flash-list/issues/876
+ paddingHorizontal: 0.1,
+ },
+});
+
const memesGridListStyles = StyleSheet.create({
flashList: {
- paddingBottom: 100,
paddingHorizontal: 2.5,
},
- helperText: {
- marginVertical: 12.5,
- },
});
const memesListListStyles = StyleSheet.create({
flashList: {
- paddingBottom: 100,
paddingHorizontal: 5,
},
- helperText: {
- marginVertical: 15,
- },
});
const MemesList = ({
@@ -61,7 +58,7 @@ const MemesList = ({
handleScroll: (event: NativeSyntheticEvent) => void;
focusMeme: (index: number) => void;
}) => {
- const { dimensions, orientation } = useDimensions();
+ const { height, width } = useSafeAreaFrame();
const view = useSelector((state: RootState) => state.memes.view);
const masonryColumns = useSelector(
(state: RootState) => state.settings.masonryColumns,
@@ -70,10 +67,6 @@ const MemesList = ({
(state: RootState) => state.settings.gridColumns,
);
- const extraFlashListPadding =
- flashListPadding +
- dimensions.height * (orientation === ORIENTATION.PORTRAIT ? 0.02 : 0.04);
-
return (
<>
{view === VIEW.MASONRY && (
@@ -82,8 +75,8 @@ const MemesList = ({
data={memes}
estimatedItemSize={getFlashListItemHeight(masonryColumns)}
estimatedListSize={{
- height: dimensions.height,
- width: dimensions.width * 0.92,
+ height,
+ width: width * 0.92,
}}
numColumns={masonryColumns}
showsVerticalScrollIndicator={false}
@@ -91,17 +84,19 @@ const MemesList = ({
)}
contentContainerStyle={{
- paddingTop: extraFlashListPadding,
+ paddingTop: flashListPadding,
+ ...sharedMemesListStyles.flashList,
...memesMasonryListStyles.flashList,
}}
ListEmptyComponent={() => (
+ style={[sharedMemesListStyles.helperText, styles.centerText]}>
No memes found
)}
onScroll={handleScroll}
+ fadingEdgeLength={100}
/>
)}
{view === VIEW.GRID && (
@@ -110,8 +105,8 @@ const MemesList = ({
data={memes}
estimatedItemSize={getFlashListItemHeight(gridColumns)}
estimatedListSize={{
- height: dimensions.height,
- width: dimensions.width * 0.92,
+ height: height,
+ width: width * 0.92,
}}
numColumns={gridColumns}
showsVerticalScrollIndicator={false}
@@ -119,17 +114,19 @@ const MemesList = ({
)}
contentContainerStyle={{
- paddingTop: extraFlashListPadding + 2.5,
+ paddingTop: flashListPadding,
+ ...sharedMemesListStyles.flashList,
...memesGridListStyles.flashList,
}}
ListEmptyComponent={() => (
+ style={[sharedMemesListStyles.helperText, styles.centerText]}>
No memes found
)}
onScroll={handleScroll}
+ fadingEdgeLength={100}
/>
)}
{view === VIEW.LIST && (
@@ -138,8 +135,8 @@ const MemesList = ({
data={memes}
estimatedItemSize={50}
estimatedListSize={{
- height: dimensions.height,
- width: dimensions.width * 0.92,
+ height: height,
+ width: width * 0.92,
}}
showsVerticalScrollIndicator={false}
renderItem={({ item: meme, index }) => (
@@ -147,17 +144,19 @@ const MemesList = ({
)}
ItemSeparatorComponent={() => }
contentContainerStyle={{
- paddingTop: extraFlashListPadding,
+ paddingTop: flashListPadding,
+ ...sharedMemesListStyles.flashList,
...memesListListStyles.flashList,
}}
ListEmptyComponent={() => (
+ style={[sharedMemesListStyles.helperText, styles.centerText]}>
No memes found
)}
onScroll={handleScroll}
+ fadingEdgeLength={100}
/>
)}
>
diff --git a/src/components/memes/memesList/memesListItem.tsx b/src/components/memes/memesList/memesListItem.tsx
index 9f70d4c..afe38c6 100644
--- a/src/components/memes/memesList/memesListItem.tsx
+++ b/src/components/memes/memesList/memesListItem.tsx
@@ -1,15 +1,18 @@
-import React, { useState } from 'react';
+import React from 'react';
import { Image, StyleSheet, View } from 'react-native';
import { Text, TouchableRipple } from 'react-native-paper';
+import { useSafeAreaFrame } from 'react-native-safe-area-context';
+import { useImageDimensions } from '@react-native-community/hooks';
import { Meme } from '../../../database';
import styles from '../../../styles';
-import { useDimensions } from '../../../contexts';
const memesListItemStyles = StyleSheet.create({
view: {
paddingVertical: 10,
},
image: {
+ height: 75,
+ width: 75,
borderRadius: 5,
},
detailsView: {
@@ -30,71 +33,54 @@ const MemesListItem = ({
index: number;
focusMeme: (index: number) => void;
}) => {
- const { dimensions } = useDimensions();
+ const { width } = useSafeAreaFrame();
- const [imageWidth, setImageWidth] = useState();
- const [imageHeight, setImageHeight] = useState();
+ const { dimensions, loading, error } = useImageDimensions({ uri: meme.uri });
- Image.getSize(meme.uri, () => {
- const paddedWidth = 75;
- setImageWidth(paddedWidth);
- setImageHeight(paddedWidth);
- });
+ if (loading || error || !dimensions) return <>>;
return (
- <>
- {imageWidth && imageHeight && (
- focusMeme(index)}
- style={[memesListItemStyles.view, styles.flexRow]}>
- <>
-
- focusMeme(index)}
+ style={[memesListItemStyles.view, styles.flexRow]}>
+ <>
+
+
+
+ {meme.title}
+
+
+
+ {meme.dateModified.toLocaleDateString()} • {meme.size / 1000}
+ KB
+
+
+
+ {meme.tags.map(tag => (
+
-
-
-
- {meme.title}
+ numberOfLines={1}>
+ #{tag.name}
-
-
- {meme.dateModified.toLocaleDateString()} • {meme.size / 1000}
- KB
-
-
-
- {meme.tags.map(tag => (
-
- #{tag.name}
-
- ))}
-
-
- >
-
- )}
- >
+ ))}
+
+
+ >
+
);
};
diff --git a/src/components/memes/memesList/memesMasonryItem.tsx b/src/components/memes/memesList/memesMasonryItem.tsx
index 14657b0..739a49d 100644
--- a/src/components/memes/memesList/memesMasonryItem.tsx
+++ b/src/components/memes/memesList/memesMasonryItem.tsx
@@ -1,9 +1,10 @@
-import React, { useState } from 'react';
+import React from 'react';
import { Image, StyleSheet, TouchableHighlight } from 'react-native';
import { useSelector } from 'react-redux';
+import { useSafeAreaFrame } from 'react-native-safe-area-context';
import { Meme } from '../../../database';
-import { useDimensions } from '../../../contexts';
import { RootState } from '../../../state';
+import { useImageDimensions } from '@react-native-community/hooks';
const memeMasonryItemStyles = StyleSheet.create({
view: {
@@ -24,36 +25,31 @@ const MemesMasonryItem = ({
index: number;
focusMeme: (index: number) => void;
}) => {
- const { dimensions } = useDimensions();
+ const { width } = useSafeAreaFrame();
const masonryColumns = useSelector(
(state: RootState) => state.settings.masonryColumns,
);
- const [imageWidth, setImageWidth] = useState();
- const [imageHeight, setImageHeight] = useState();
+ const { dimensions, loading, error } = useImageDimensions({ uri: meme.uri });
- Image.getSize(meme.uri, (width, height) => {
- const paddedWidth = (dimensions.width * 0.92) / masonryColumns - 5;
- setImageWidth(paddedWidth);
- setImageHeight((paddedWidth / width) * height);
- });
+ if (loading || error || !dimensions) return <>>;
return (
- <>
- {imageWidth && imageHeight && (
- focusMeme(index)}
- style={memeMasonryItemStyles.view}>
-
-
- )}
- >
+ focusMeme(index)}
+ style={memeMasonryItemStyles.view}>
+
+
);
};
diff --git a/src/components/tags/tagChip.tsx b/src/components/tags/tagChip.tsx
index 2b4ebdb..7095ba9 100644
--- a/src/components/tags/tagChip.tsx
+++ b/src/components/tags/tagChip.tsx
@@ -14,11 +14,13 @@ const tagChipStyles = StyleSheet.create({
const TagChip = ({
tag,
active = true,
+ elevated = false,
onPress,
...props
}: {
tag: Tag;
active?: boolean;
+ elevated?: boolean;
onPress?: () => void;
} & Omit, 'children'>) => {
const theme = useTheme();
@@ -45,6 +47,7 @@ const TagChip = ({
);
}}
compact
+ elevated={elevated}
theme={chipTheme}
mode={active ? 'flat' : 'outlined'}
style={[tagChipStyles.chip, props.style]}
diff --git a/src/components/tags/tagPreview.tsx b/src/components/tags/tagPreview.tsx
index 9b2ecd4..c5cacc0 100644
--- a/src/components/tags/tagPreview.tsx
+++ b/src/components/tags/tagPreview.tsx
@@ -3,10 +3,12 @@ import { StyleSheet, View } from 'react-native';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import { Chip, useTheme } from 'react-native-paper';
import styles from '../../styles';
-import { useDimensions } from '../../contexts';
import { getContrastColor } from '../../utilities';
const tagPreviewStyles = StyleSheet.create({
+ view: {
+ margin: '10%',
+ },
chip: {
padding: 5,
},
@@ -17,7 +19,6 @@ const tagPreviewStyles = StyleSheet.create({
const TagPreview = ({ name, color }: { name: string; color: string }) => {
const theme = useTheme();
- const { responsive } = useDimensions();
const chipTheme = useMemo(() => {
return {
@@ -32,14 +33,7 @@ const TagPreview = ({ name, color }: { name: string; color: string }) => {
const contrastColor = getContrastColor(color);
return (
-
+
{
return ;
diff --git a/src/contexts/dimensions.tsx b/src/contexts/dimensions.tsx
deleted file mode 100644
index 7a29295..0000000
--- a/src/contexts/dimensions.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import React, {
- ReactNode,
- createContext,
- useContext,
- useEffect,
- useMemo,
- useState,
-} from 'react';
-import { Dimensions, ScaledSize } from 'react-native';
-
-const guidelineBaseWidth = 350;
-const guidelineBaseHeight = 680;
-
-enum ORIENTATION {
- PORTRAIT = 'portrait',
- LANDSCAPE = 'landscape',
-}
-
-interface ScaleFunctions {
- horizontalScale: (size: number) => number;
- verticalScale: (size: number) => number;
- moderateHorizontalScale: (size: number, factor?: number) => number;
- moderateVerticalScale: (size: number, factor?: number) => number;
-}
-
-interface DimensionsContext {
- orientation: ORIENTATION;
- dimensions: ScaledSize;
- responsive: ScaleFunctions;
-}
-
-const createScaleFunctions = (dimensionsIn: ScaledSize) => {
- const horizontalScale = (size: number) =>
- (dimensionsIn.width / guidelineBaseWidth) * size;
- const verticalScale = (size: number) =>
- (dimensionsIn.height / guidelineBaseHeight) * size;
- const moderateHorizontalScale = (size: number, factor = 0.5) =>
- size + (horizontalScale(size) - size) * factor;
- const moderateVerticalScale = (size: number, factor = 0.5) =>
- size + (verticalScale(size) - size) * factor;
-
- return {
- horizontalScale,
- verticalScale,
- moderateHorizontalScale,
- moderateVerticalScale,
- };
-};
-
-const DimensionsContext = createContext(
- undefined,
-);
-
-const DimensionsProvider = ({ children }: { children: ReactNode }) => {
- const [dimensions, setDimensions] = useState(Dimensions.get('window'));
-
- const orientation = useMemo(() => {
- return dimensions.width > dimensions.height
- ? ORIENTATION.LANDSCAPE
- : ORIENTATION.PORTRAIT;
- }, [dimensions]);
-
- const responsiveScale = createScaleFunctions(dimensions);
-
- useEffect(() => {
- const onChange = ({ window }: { window: ScaledSize }) => {
- setDimensions(window);
- };
-
- const subscription = Dimensions.addEventListener('change', onChange);
-
- return () => {
- subscription.remove();
- };
- }, []);
-
- return (
-
- {children}
-
- );
-};
-
-const useDimensions = (): DimensionsContext => {
- const context = useContext(DimensionsContext);
- if (!context) {
- throw new Error('useDimensions must be used within a DimensionsProvider');
- }
- return context;
-};
-
-export { ORIENTATION, DimensionsProvider, useDimensions };
diff --git a/src/contexts/index.ts b/src/contexts/index.ts
deleted file mode 100644
index cb4d26e..0000000
--- a/src/contexts/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { ORIENTATION, DimensionsProvider, useDimensions } from './dimensions';
diff --git a/src/screens/addMeme.tsx b/src/screens/addMeme.tsx
index be7c3b5..92d3abe 100644
--- a/src/screens/addMeme.tsx
+++ b/src/screens/addMeme.tsx
@@ -1,7 +1,6 @@
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, StyleSheet, View } from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { useRealm } from '@realm/react';
@@ -9,16 +8,17 @@ import { BSON } from 'realm';
import { AndroidScoped, FileSystem } from 'react-native-file-access';
import { useSelector } from 'react-redux';
import { extension } from 'react-native-mime-types';
+import { useDeviceOrientation } from '@react-native-community/hooks';
+import {
+ DocumentPickerResponse,
+ pickSingle,
+} from 'react-native-document-picker';
import styles from '../styles';
import { ROUTE, RootStackParamList } from '../types';
import { Meme, Tag } from '../database';
import { RootState } from '../state';
import { allowedMimeTypes, getMemeType, validateMemeTitle } from '../utilities';
import { MemeEditor } from '../components';
-import {
- DocumentPickerResponse,
- pickSingle,
-} from 'react-native-document-picker';
const addMemeStyles = StyleSheet.create({
saveAndAddButton: {
@@ -36,7 +36,7 @@ const AddMeme = ({
}: NativeStackScreenProps) => {
const { goBack } = useNavigation();
const { colors } = useTheme();
- const { orientation } = useDimensions();
+ const orientation = useDeviceOrientation();
const realm = useRealm();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const storageUri = useSelector(
@@ -104,7 +104,7 @@ const AddMeme = ({
{
const { goBack } = useNavigation();
const { colors } = useTheme();
- const { orientation } = useDimensions();
+ const orientation = useDeviceOrientation();
const realm = useRealm();
const [tagName, setTagName] = useState(validateTagName('newTag'));
@@ -57,7 +57,7 @@ const AddTag = () => {
) => {
const { goBack } = useNavigation();
const { colors } = useTheme();
- const { orientation } = useDimensions();
+ const orientation = useDeviceOrientation();
const realm = useRealm();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -80,7 +80,7 @@ const EditMeme = ({
) => {
const { goBack } = useNavigation();
const { colors } = useTheme();
- const { orientation } = useDimensions();
+ const orientation = useDeviceOrientation();
const realm = useRealm();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -52,7 +52,7 @@ const EditTag = ({
) => {
- const { orientation, dimensions } = useDimensions();
+ const { height, width } = useSafeAreaFrame();
const navigation = useNavigation>();
const realm = useRealm();
@@ -55,35 +65,30 @@ const MemeView = ({
return (
<>
-
+
navigation.goBack()} />
-
- {
- 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 }) => }
- />
-
-
+ {
+ const newIndex = Math.round(
+ event.nativeEvent.contentOffset.x /
+ event.nativeEvent.layoutMeasurement.width,
+ );
+ if (newIndex !== index) setIndex(newIndex);
+ }}
+ estimatedItemSize={width}
+ estimatedListSize={{ height, width }}
+ pagingEnabled
+ horizontal
+ showsHorizontalScrollIndicator={false}
+ renderItem={({ item: meme }) => }
+ />
+
favoriteMeme(realm, memes[index])}
diff --git a/src/screens/memes.tsx b/src/screens/memes.tsx
index 9312dd0..4ef3f15 100644
--- a/src/screens/memes.tsx
+++ b/src/screens/memes.tsx
@@ -4,6 +4,7 @@ import {
NativeScrollEvent,
NativeSyntheticEvent,
View,
+ useWindowDimensions,
} from 'react-native';
import { useQuery } from '@realm/react';
import { useTheme } from 'react-native-paper';
@@ -20,11 +21,14 @@ import { ROUTE, SORT_DIRECTION, memesSortQuery } from '../types';
import { RootState, setNavVisible } from '../state';
import { Meme } from '../database';
import { HideableHeader, MemesHeader, MemesList } from '../components';
+import { useDeviceOrientation } from '@react-native-community/hooks';
const Memes = () => {
const { colors } = useTheme();
const { navigate } =
useNavigation>();
+ const { height } = useWindowDimensions();
+ const orientation = useDeviceOrientation();
const sort = useSelector((state: RootState) => state.memes.sort);
const sortDirection = useSelector(
(state: RootState) => state.memes.sortDirection,
@@ -135,7 +139,10 @@ const Memes = () => {
search={search}
setSearch={setSearch}
onLayout={event => {
- setFlashListPadding(event.nativeEvent.layout.height);
+ setFlashListPadding(
+ event.nativeEvent.layout.height +
+ height * (orientation === 'portrait' ? 0.02 : 0.04),
+ );
}}
/>
diff --git a/src/screens/settings.tsx b/src/screens/settings.tsx
index c7e9fab..a030d94 100644
--- a/src/screens/settings.tsx
+++ b/src/screens/settings.tsx
@@ -13,6 +13,7 @@ import {
import { openDocumentTree } from 'react-native-scoped-storage';
import { useDispatch, useSelector } from 'react-redux';
import type {} from 'redux-thunk/extend-redux';
+import { useDeviceOrientation } from '@react-native-community/hooks';
import styles from '../styles';
import {
RootState,
@@ -21,7 +22,6 @@ import {
setNoMedia,
setStorageUri,
} from '../state';
-import { ORIENTATION, useDimensions } from '../contexts';
const settingsStyles = StyleSheet.create({
snackbar: {
@@ -34,7 +34,7 @@ const settingsStyles = StyleSheet.create({
const Settings = () => {
const { colors } = useTheme();
- const { orientation } = useDimensions();
+ const orientation = useDeviceOrientation();
const noMedia = useSelector((state: RootState) => state.settings.noMedia);
const masonryColumns = useSelector(
(state: RootState) => state.settings.masonryColumns,
@@ -60,85 +60,80 @@ const Settings = () => {
<>
-
-
- Views
-
- Masonry Columns
-
-
+ Views
+
+ Masonry Columns
+
+ {
+ void dispatch(
+ setMasonryColumns(Number.parseInt(value) as 1 | 2 | 3 | 4),
+ );
+ }}
+ buttons={[
+ { label: '1', value: '1' },
+ { label: '2', value: '2' },
+ { label: '3', value: '3' },
+ { label: '4', value: '4' },
+ ]}
+ style={settingsStyles.marginBottom}
+ />
+
+ Grid Columns
+
+ {
+ void dispatch(
+ setGridColumns(Number.parseInt(value) as 1 | 2 | 3 | 4),
+ );
+ }}
+ buttons={[
+ { label: '1', value: '1' },
+ { label: '2', value: '2' },
+ { label: '3', value: '3' },
+ { label: '4', value: '4' },
+ ]}
+ />
+
+
+ Media Storage
+
+
+ Hide media from gallery
+ {
- void dispatch(
- setMasonryColumns(Number.parseInt(value) as 1 | 2 | 3 | 4),
- );
+ void dispatch(setNoMedia(value));
}}
- buttons={[
- { label: '1', value: '1' },
- { label: '2', value: '2' },
- { label: '3', value: '3' },
- { label: '4', value: '4' },
- ]}
- style={settingsStyles.marginBottom}
/>
-
- Grid Columns
-
- {
- void dispatch(
- setGridColumns(Number.parseInt(value) as 1 | 2 | 3 | 4),
- );
- }}
- buttons={[
- { label: '1', value: '1' },
- { label: '2', value: '2' },
- { label: '3', value: '3' },
- { label: '4', value: '4' },
- ]}
- />
-
-
- Media Storage
-
-
- Hide media from gallery
- {
- void dispatch(setNoMedia(value));
- }}
- />
-
-
-
+
+
Database
);
diff --git a/src/screens/welcome.tsx b/src/screens/welcome.tsx
index 46e3b22..071b679 100644
--- a/src/screens/welcome.tsx
+++ b/src/screens/welcome.tsx
@@ -3,10 +3,10 @@ import { StyleSheet, View } from 'react-native';
import { Button, Text, useTheme } from 'react-native-paper';
import { useDispatch } from 'react-redux';
import { openDocumentTree } from 'react-native-scoped-storage';
+import { useDeviceOrientation } from '@react-native-community/hooks';
import styles from '../styles';
import { noOp } from '../utilities';
import { setStorageUri } from '../state';
-import { ORIENTATION, useDimensions } from '../contexts';
const welcomeStyles = StyleSheet.create({
text: {
@@ -19,7 +19,7 @@ const welcomeStyles = StyleSheet.create({
const Welcome = ({ onWelcomeComplete }: { onWelcomeComplete: () => void }) => {
const { colors } = useTheme();
- const { orientation } = useDimensions();
+ const orientation = useDeviceOrientation();
const dispatch = useDispatch();
const selectStorageLocation = async () => {
@@ -32,11 +32,9 @@ const Welcome = ({ onWelcomeComplete }: { onWelcomeComplete: () => void }) => {
return (