Add broken URI handling
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { HelperText, TextInput } from 'react-native-paper';
|
||||
import { Image } from 'react-native';
|
||||
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { useImageDimensions } from '@react-native-community/hooks/lib/useImageDimensions';
|
||||
import LoadingView from '../loadingView';
|
||||
import { MemeTagSelector } from '.';
|
||||
import { MemeFail, MemeTagSelector } from '.';
|
||||
import { Tag } from '../../database';
|
||||
import { StringValidationResult, validateMemeTitle } from '../../utilities';
|
||||
import { useImageDimensions } from '@react-native-community/hooks';
|
||||
|
||||
const memeEditorStyles = {
|
||||
image: {
|
||||
@@ -23,12 +23,16 @@ const memeEditorStyles = {
|
||||
|
||||
const MemeEditor = ({
|
||||
memeUri,
|
||||
memeUriError,
|
||||
setMemeUriError,
|
||||
memeTitle,
|
||||
setMemeTitle,
|
||||
memeTags,
|
||||
setMemeTags,
|
||||
}: {
|
||||
memeUri: string;
|
||||
memeUriError: Error | undefined;
|
||||
setMemeUriError: (error: Error | undefined) => void;
|
||||
memeTitle: StringValidationResult;
|
||||
setMemeTitle: (name: StringValidationResult) => void;
|
||||
memeTags: Map<string, Tag>;
|
||||
@@ -37,8 +41,9 @@ const MemeEditor = ({
|
||||
const { width } = useSafeAreaFrame();
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri: memeUri });
|
||||
setMemeUriError(error);
|
||||
|
||||
if (loading || error || !dimensions) return <LoadingView />;
|
||||
if (!memeUriError && (loading || !dimensions)) return <LoadingView />;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -53,23 +58,36 @@ const MemeEditor = ({
|
||||
<HelperText type="error" visible={!memeTitle.valid}>
|
||||
{memeTitle.error}
|
||||
</HelperText>
|
||||
<Image
|
||||
source={{ uri: memeUri }}
|
||||
style={[
|
||||
{
|
||||
width: width * 0.92,
|
||||
height: Math.max(
|
||||
Math.min(
|
||||
((width * 0.92) / dimensions.width) * dimensions.height,
|
||||
500,
|
||||
{memeUriError || !dimensions ? (
|
||||
<MemeFail
|
||||
style={[
|
||||
{
|
||||
width: width * 0.92,
|
||||
height: width * 0.92,
|
||||
},
|
||||
memeEditorStyles.image,
|
||||
]}
|
||||
iconSize={50}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
source={{ uri: memeUri }}
|
||||
style={[
|
||||
{
|
||||
width: width * 0.92,
|
||||
height: Math.max(
|
||||
Math.min(
|
||||
((width * 0.92) / dimensions.width) * dimensions.height,
|
||||
500,
|
||||
),
|
||||
100,
|
||||
),
|
||||
100,
|
||||
),
|
||||
},
|
||||
memeEditorStyles.image,
|
||||
]}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
},
|
||||
memeEditorStyles.image,
|
||||
]}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
)}
|
||||
<MemeTagSelector
|
||||
memeTags={memeTags}
|
||||
setMemeTags={setMemeTags}
|
||||
|
41
src/components/memes/memeFail.tsx
Normal file
41
src/components/memes/memeFail.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React, { ComponentProps } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { useTheme } from 'react-native-paper';
|
||||
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
|
||||
import { rgbToRgba } from '../../utilities';
|
||||
|
||||
const memeFailStyles = StyleSheet.create({
|
||||
view: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
const MemeFail = ({
|
||||
iconSize,
|
||||
...props
|
||||
}: {
|
||||
iconSize?: number;
|
||||
} & ComponentProps<typeof View>) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View
|
||||
{...props}
|
||||
style={[
|
||||
props.style,
|
||||
memeFailStyles.view,
|
||||
{
|
||||
backgroundColor: rgbToRgba(colors.error, 0.2),
|
||||
},
|
||||
]}>
|
||||
<FontAwesome5
|
||||
name="exclamation-triangle"
|
||||
size={iconSize}
|
||||
color={colors.error}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemeFail;
|
@@ -5,6 +5,7 @@ import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { useImageDimensions } from '@react-native-community/hooks';
|
||||
import LoadingView from '../loadingView';
|
||||
import { Meme } from '../../database';
|
||||
import MemeFail from './memeFail';
|
||||
|
||||
const memeViewItemStyles = StyleSheet.create({
|
||||
view: {
|
||||
@@ -18,25 +19,42 @@ const MemeViewItem = ({ meme }: { meme: Meme }) => {
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri: meme.uri });
|
||||
|
||||
if (loading || error || !dimensions) return <LoadingView />;
|
||||
if (!error && (loading || !dimensions)) {
|
||||
return (
|
||||
<View style={{ width, height }}>
|
||||
<LoadingView />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[{ width, height }, memeViewItemStyles.view]}>
|
||||
<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}
|
||||
/>
|
||||
{error || !dimensions ? (
|
||||
<MemeFail
|
||||
style={{
|
||||
width: Math.min(width, height - 128),
|
||||
height: Math.min(width, height - 128),
|
||||
}}
|
||||
iconSize={50}
|
||||
/>
|
||||
) : (
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
@@ -5,6 +5,8 @@ import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { useImageDimensions } from '@react-native-community/hooks';
|
||||
import { Meme } from '../../../database';
|
||||
import { RootState } from '../../../state';
|
||||
import { MemeFail } from '..';
|
||||
import { getFontAwesome5IconSize } from '../../../utilities';
|
||||
|
||||
const MemesGridItem = ({
|
||||
meme,
|
||||
@@ -22,19 +24,29 @@ const MemesGridItem = ({
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri: meme.uri });
|
||||
|
||||
if (loading || error || !dimensions) return <></>;
|
||||
if (!error && (loading || !dimensions)) return <></>;
|
||||
|
||||
return (
|
||||
<TouchableHighlight onPress={() => focusMeme(index)}>
|
||||
<Image
|
||||
source={{ uri: meme.uri }}
|
||||
style={[
|
||||
{
|
||||
{error ? (
|
||||
<MemeFail
|
||||
style={{
|
||||
width: (width * 0.92 - 5) / gridColumns,
|
||||
height: (width * 0.92 - 5) / gridColumns,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}}
|
||||
iconSize={getFontAwesome5IconSize(gridColumns)}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
source={{ uri: meme.uri }}
|
||||
style={[
|
||||
{
|
||||
width: (width * 0.92 - 5) / gridColumns,
|
||||
height: (width * 0.92 - 5) / gridColumns,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
};
|
||||
|
@@ -4,6 +4,7 @@ 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 { MemeFail } from '..';
|
||||
|
||||
const memesListItemStyles = StyleSheet.create({
|
||||
view: {
|
||||
@@ -42,14 +43,18 @@ const MemesListItem = ({
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri: meme.uri });
|
||||
|
||||
if (loading || error || !dimensions) return <></>;
|
||||
if (!error && (loading || !dimensions)) return <></>;
|
||||
|
||||
return (
|
||||
<TouchableRipple
|
||||
onPress={() => focusMeme(index)}
|
||||
style={memesListItemStyles.view}>
|
||||
<>
|
||||
<Image source={{ uri: meme.uri }} style={memesListItemStyles.image} />
|
||||
{error ? (
|
||||
<MemeFail style={memesListItemStyles.image} />
|
||||
) : (
|
||||
<Image source={{ uri: meme.uri }} style={memesListItemStyles.image} />
|
||||
)}
|
||||
<View
|
||||
style={[
|
||||
memesListItemStyles.detailsView,
|
||||
|
@@ -5,6 +5,8 @@ import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { Meme } from '../../../database';
|
||||
import { RootState } from '../../../state';
|
||||
import { useImageDimensions } from '@react-native-community/hooks';
|
||||
import { MemeFail } from '..';
|
||||
import { getFontAwesome5IconSize } from '../../../utilities';
|
||||
|
||||
const memeMasonryItemStyles = StyleSheet.create({
|
||||
view: {
|
||||
@@ -32,23 +34,36 @@ const MemesMasonryItem = ({
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri: meme.uri });
|
||||
|
||||
if (loading || error || !dimensions) return <></>;
|
||||
if (!error && (loading || !dimensions)) return <></>;
|
||||
|
||||
return (
|
||||
<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,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{error || !dimensions ? (
|
||||
<MemeFail
|
||||
style={[
|
||||
memeMasonryItemStyles.image,
|
||||
{
|
||||
width: (width * 0.92) / masonryColumns - 5,
|
||||
height: (width * 0.92) / masonryColumns - 5,
|
||||
},
|
||||
]}
|
||||
iconSize={getFontAwesome5IconSize(masonryColumns)}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
source={{ uri: meme.uri }}
|
||||
style={[
|
||||
memeMasonryItemStyles.image,
|
||||
{
|
||||
width: (width * 0.92) / masonryColumns - 5,
|
||||
height:
|
||||
((width * 0.92) / masonryColumns - 5) / dimensions.aspectRatio,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user