Fix various spacing issues

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2023-07-25 22:00:33 +03:00
parent e479e3c0ad
commit caa98736e9
28 changed files with 362 additions and 488 deletions

View File

@@ -1,11 +1,12 @@
import React, { useEffect, useState } from 'react';
import React from 'react';
import { HelperText, TextInput } from 'react-native-paper';
import { Image } from 'react-native';
import { useDimensions } from '../../contexts';
import { useSafeAreaFrame } from 'react-native-safe-area-context';
import LoadingView from '../loadingView';
import { MemeTagSelector } from '.';
import { Tag } from '../../database';
import { StringValidationResult, validateMemeTitle } from '../../utilities';
import { useImageDimensions } from '@react-native-community/hooks';
const memeEditorStyles = {
image: {
@@ -33,30 +34,11 @@ const MemeEditor = ({
memeTags: Map<string, Tag>;
setMemeTags: (tags: Map<string, Tag>) => void;
}) => {
const { dimensions } = useDimensions();
const { width } = useSafeAreaFrame();
const [imageWidth, setImageWidth] = useState<number>();
const [imageHeight, setImageHeight] = useState<number>();
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 <LoadingView />;
if (loading || error || !dimensions) return <LoadingView />;
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,
]}

View File

@@ -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<string, Tag>;
setMemeTags: (tags: Map<string, Tag>) => 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'}
</Chip>
)}
fadingEdgeLength={50}
/>
</Modal>
</Portal>

View File

@@ -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<string, Tag>;
setMemeTags: (tags: Map<string, Tag>) => void;
} & ComponentProps<typeof View>) => {
const { dimensions } = useDimensions();
const { width } = useSafeAreaFrame();
const [flashListMargin, setFlashListMargin] = useState(0);
const [tagSearchModalVisible, setTagSearchModalVisible] = useState(false);
@@ -37,7 +37,6 @@ const MemeTagSelector = ({
<View {...props}>
<FlashList
data={[...memeTags.values()]}
extraData={memeTags}
horizontal
estimatedItemSize={120}
showsHorizontalScrollIndicator={false}
@@ -55,7 +54,7 @@ const MemeTagSelector = ({
onPress={() => 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
</Chip>
)}
fadingEdgeLength={50}
/>
</View>
<MemeTagSearchModal

View File

@@ -1,51 +1,36 @@
import React, { useEffect, useState } from 'react';
import { ActivityIndicator, useTheme } from 'react-native-paper';
import React from 'react';
import { ImageZoom } from '@likashefqet/react-native-image-zoom';
import { Meme } from '../../database';
import { useDimensions } from '../../contexts';
import { Image, View } from 'react-native';
import { View } from 'react-native';
import styles from '../../styles';
import LoadingView from '../loadingView';
import { useSafeAreaFrame } from 'react-native-safe-area-context';
import { useImageDimensions } from '@react-native-community/hooks';
const MemeViewItem = ({ meme }: { meme: Meme }) => {
const { dimensions } = useDimensions();
const { colors } = useTheme();
const { height, width } = useSafeAreaFrame();
const [imageWidth, setImageWidth] = useState<number>();
const [imageHeight, setImageHeight] = useState<number>();
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 <LoadingView />;
return (
<View
style={[
{
width: dimensions.width,
height: dimensions.height - 160,
backgroundColor: colors.background,
},
styles.centered,
]}>
{imageWidth && imageHeight ? (
<ImageZoom
source={{ uri: meme.uri }}
style={{ width: imageWidth, height: imageHeight }}
/>
) : (
<ActivityIndicator size="large" color={colors.primary} />
)}
<View style={[{ width, height }, styles.center]}>
<ImageZoom
source={{ uri: meme.uri }}
style={
dimensions.aspectRatio > width / (height - 128)
? {
width,
height: width / (dimensions.width / dimensions.height),
}
: {
width: (height - 128) * (dimensions.width / dimensions.height),
height: height - 128,
}
}
minScale={0.5}
/>
</View>
);
};

View File

@@ -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<number>();
const [imageHeight, setImageHeight] = useState<number>();
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 && (
<View>
<TouchableHighlight onPress={() => focusMeme(index)}>
<Image
source={{ uri: meme.uri }}
style={[{ width: imageWidth, height: imageHeight }]}
/>
</TouchableHighlight>
</View>
)}
</>
<TouchableHighlight onPress={() => focusMeme(index)}>
<Image
source={{ uri: meme.uri }}
style={[
{
width: (width * 0.92 - 5) / gridColumns,
height: (width * 0.92 - 5) / gridColumns,
},
]}
/>
</TouchableHighlight>
);
};

View File

@@ -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<NativeScrollEvent>) => 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 = ({
<MemesMasonryItem meme={meme} index={index} focusMeme={focusMeme} />
)}
contentContainerStyle={{
paddingTop: extraFlashListPadding,
paddingTop: flashListPadding,
...sharedMemesListStyles.flashList,
...memesMasonryListStyles.flashList,
}}
ListEmptyComponent={() => (
<HelperText
type={'info'}
style={[memesMasonryListStyles.helperText, styles.centerText]}>
style={[sharedMemesListStyles.helperText, styles.centerText]}>
No memes found
</HelperText>
)}
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 = ({
<MemesGridItem meme={meme} index={index} focusMeme={focusMeme} />
)}
contentContainerStyle={{
paddingTop: extraFlashListPadding + 2.5,
paddingTop: flashListPadding,
...sharedMemesListStyles.flashList,
...memesGridListStyles.flashList,
}}
ListEmptyComponent={() => (
<HelperText
type={'info'}
style={[memesGridListStyles.helperText, styles.centerText]}>
style={[sharedMemesListStyles.helperText, styles.centerText]}>
No memes found
</HelperText>
)}
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={() => <Divider />}
contentContainerStyle={{
paddingTop: extraFlashListPadding,
paddingTop: flashListPadding,
...sharedMemesListStyles.flashList,
...memesListListStyles.flashList,
}}
ListEmptyComponent={() => (
<HelperText
type={'info'}
style={[memesListListStyles.helperText, styles.centerText]}>
style={[sharedMemesListStyles.helperText, styles.centerText]}>
No memes found
</HelperText>
)}
onScroll={handleScroll}
fadingEdgeLength={100}
/>
)}
</>

View File

@@ -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<number>();
const [imageHeight, setImageHeight] = useState<number>();
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 && (
<TouchableRipple
onPress={() => focusMeme(index)}
style={[memesListItemStyles.view, styles.flexRow]}>
<>
<View style={{ width: imageWidth, height: imageHeight }}>
<Image
source={{ uri: meme.uri }}
<TouchableRipple
onPress={() => focusMeme(index)}
style={[memesListItemStyles.view, styles.flexRow]}>
<>
<Image source={{ uri: meme.uri }} style={memesListItemStyles.image} />
<View
style={[
memesListItemStyles.detailsView,
styles.flexColumn,
{
width: width * 0.92 - 75 - 10,
},
]}>
<Text variant="titleMedium" style={memesListItemStyles.text}>
{meme.title}
</Text>
<View style={styles.flexRow}>
<Text variant="labelSmall" style={memesListItemStyles.text}>
{meme.dateModified.toLocaleDateString()} {meme.size / 1000}
KB
</Text>
</View>
<View style={[styles.flexRow, styles.flexWrap]}>
{meme.tags.map(tag => (
<Text
variant="labelMedium"
key={tag.id.toHexString()}
style={[
{ width: imageWidth, height: imageHeight },
memesListItemStyles.image,
{
color: tag.color,
},
memesListItemStyles.text,
]}
/>
</View>
<View
style={[
memesListItemStyles.detailsView,
styles.flexColumn,
{
width: dimensions.width * 0.92 - imageWidth - 10,
},
]}>
<Text variant="titleMedium" style={memesListItemStyles.text}>
{meme.title}
numberOfLines={1}>
#{tag.name}
</Text>
<View style={styles.flexRow}>
<Text variant="labelSmall" style={memesListItemStyles.text}>
{meme.dateModified.toLocaleDateString()} {meme.size / 1000}
KB
</Text>
</View>
<View style={[styles.flexRow, styles.flexWrap]}>
{meme.tags.map(tag => (
<Text
variant="labelMedium"
key={tag.id.toHexString()}
style={[
{
color: tag.color,
},
memesListItemStyles.text,
]}
numberOfLines={1}>
#{tag.name}
</Text>
))}
</View>
</View>
</>
</TouchableRipple>
)}
</>
))}
</View>
</View>
</>
</TouchableRipple>
);
};

View File

@@ -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<number>();
const [imageHeight, setImageHeight] = useState<number>();
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 && (
<TouchableHighlight
onPress={() => focusMeme(index)}
style={memeMasonryItemStyles.view}>
<Image
source={{ uri: meme.uri }}
style={[
memeMasonryItemStyles.image,
{ width: imageWidth, height: imageHeight },
]}
/>
</TouchableHighlight>
)}
</>
<TouchableHighlight
onPress={() => focusMeme(index)}
style={memeMasonryItemStyles.view}>
<Image
source={{ uri: meme.uri }}
style={[
memeMasonryItemStyles.image,
{
width: (width * 0.92) / masonryColumns - 5,
height:
((width * 0.92) / masonryColumns - 5) / dimensions.aspectRatio,
},
]}
/>
</TouchableHighlight>
);
};