Add video support
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -1,13 +1,20 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { HelperText, Text, TextInput, useTheme } from 'react-native-paper';
|
||||
import { Image, LayoutAnimation } from 'react-native';
|
||||
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { LoadingView, MemeFail, MemeTagSelector } from '..';
|
||||
import { getFilenameFromUri, validateMemeTitle } from '../../utilities';
|
||||
import { Dimensions, StagingMeme } from '../../types';
|
||||
import {
|
||||
getFilenameFromUri,
|
||||
getMemeTypeFromMimeType,
|
||||
validateMemeTitle,
|
||||
} from '../../utilities';
|
||||
import { StagingMeme } from '../../types';
|
||||
import { useMemeDimensions } from '../../hooks';
|
||||
import { MEME_TYPE } from '../../database';
|
||||
import Video from 'react-native-video';
|
||||
|
||||
const memeEditorStyles = {
|
||||
image: {
|
||||
media: {
|
||||
marginBottom: 15,
|
||||
borderRadius: 5,
|
||||
},
|
||||
@@ -26,6 +33,7 @@ const memeEditorStyles = {
|
||||
const MemeEditor = ({
|
||||
uri,
|
||||
mimeType,
|
||||
loading,
|
||||
setLoading,
|
||||
error,
|
||||
setError,
|
||||
@@ -44,7 +52,59 @@ const MemeEditor = ({
|
||||
const { width } = useSafeAreaFrame();
|
||||
const { colors } = useTheme();
|
||||
|
||||
const [dimensions, setDimensions] = useState<Dimensions>();
|
||||
const { dimensions } = useMemeDimensions(
|
||||
uri,
|
||||
mimeType,
|
||||
useMemo(
|
||||
() => () => {
|
||||
setLoading(false);
|
||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
},
|
||||
[setLoading],
|
||||
),
|
||||
useMemo(() => (errorIn: Error) => setError(errorIn), [setError]),
|
||||
);
|
||||
|
||||
const mediaComponent = useMemo(() => {
|
||||
if (!mimeType || !dimensions) return <></>;
|
||||
|
||||
const dimensionStyles = {
|
||||
width: width * 0.92,
|
||||
height: Math.max(
|
||||
Math.min((width * 0.92) / dimensions.aspectRatio, 500),
|
||||
100,
|
||||
),
|
||||
};
|
||||
|
||||
const memeType = getMemeTypeFromMimeType(mimeType);
|
||||
if (!memeType) return <></>;
|
||||
|
||||
switch (memeType) {
|
||||
case MEME_TYPE.IMAGE:
|
||||
case MEME_TYPE.GIF: {
|
||||
return (
|
||||
<Image
|
||||
source={{ uri }}
|
||||
style={[memeEditorStyles.media, dimensionStyles]}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
);
|
||||
}
|
||||
case MEME_TYPE.VIDEO: {
|
||||
return (
|
||||
<Video
|
||||
source={{ uri }}
|
||||
style={[memeEditorStyles.media, dimensionStyles]}
|
||||
resizeMode="contain"
|
||||
controls
|
||||
/>
|
||||
);
|
||||
}
|
||||
default: {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}, [dimensions, mimeType, uri, width]);
|
||||
|
||||
if (!uri || !mimeType || !staging) return <LoadingView />;
|
||||
|
||||
@@ -70,51 +130,15 @@ const MemeEditor = ({
|
||||
width: width * 0.92,
|
||||
height: width * 0.92,
|
||||
},
|
||||
memeEditorStyles.image,
|
||||
memeEditorStyles.media,
|
||||
]}
|
||||
iconSize={50}
|
||||
/>
|
||||
) : // eslint-disable-next-line unicorn/no-nested-ternary
|
||||
loading || !dimensions ? (
|
||||
<></>
|
||||
) : (
|
||||
<Image
|
||||
source={{ uri }}
|
||||
style={[
|
||||
dimensions
|
||||
? {
|
||||
width: width * 0.92,
|
||||
height: Math.max(
|
||||
Math.min(
|
||||
((width * 0.92) / dimensions.width) * dimensions.height,
|
||||
500,
|
||||
),
|
||||
100,
|
||||
),
|
||||
}
|
||||
: // eslint-disable-next-line react-native/no-inline-styles
|
||||
{
|
||||
width: width * 0.92,
|
||||
height: 1,
|
||||
},
|
||||
memeEditorStyles.image,
|
||||
]}
|
||||
resizeMode="contain"
|
||||
onLoad={event => {
|
||||
setDimensions({
|
||||
width: event.nativeEvent.source.width,
|
||||
height: event.nativeEvent.source.height,
|
||||
});
|
||||
setLoading(false);
|
||||
LayoutAnimation.configureNext(
|
||||
LayoutAnimation.Presets.easeInEaseOut,
|
||||
);
|
||||
}}
|
||||
onError={() =>
|
||||
setError(
|
||||
new Error(
|
||||
'The URI for this meme appears to be broken. This may have been caused by the file being moved or deleted.',
|
||||
),
|
||||
)
|
||||
}
|
||||
/>
|
||||
mediaComponent
|
||||
)}
|
||||
<Text
|
||||
variant="bodySmall"
|
||||
|
@@ -1,12 +1,13 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { useImageDimensions } from '@react-native-community/hooks';
|
||||
import { AndroidScoped } from 'react-native-file-access';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Meme } from '../../database';
|
||||
import { MEME_TYPE, Meme } from '../../database';
|
||||
import { RootState } from '../../state';
|
||||
import { AnimatedImage, LoadingView, MemeFail } from '..';
|
||||
import { useMemeDimensions } from '../../hooks';
|
||||
import Video from 'react-native-video';
|
||||
|
||||
const memeViewItemStyles = StyleSheet.create({
|
||||
view: {
|
||||
@@ -24,7 +25,34 @@ const MemeViewItem = ({ meme }: { meme: Meme }) => {
|
||||
|
||||
const uri = AndroidScoped.appendPath(storageUri, meme.filename);
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri });
|
||||
const { dimensions, loading, error } = useMemeDimensions(uri, meme.mimeType);
|
||||
|
||||
const mediaComponent = useMemo(() => {
|
||||
if (!dimensions) return <></>;
|
||||
|
||||
const dimensionStyles =
|
||||
dimensions.aspectRatio > width / (height - 128)
|
||||
? {
|
||||
width,
|
||||
height: width / (dimensions.width / dimensions.height),
|
||||
}
|
||||
: {
|
||||
width: (height - 128) * (dimensions.width / dimensions.height),
|
||||
height: height - 128,
|
||||
};
|
||||
|
||||
switch (meme.memeType) {
|
||||
case MEME_TYPE.IMAGE:
|
||||
case MEME_TYPE.GIF: {
|
||||
return <AnimatedImage source={{ uri }} style={dimensionStyles} />;
|
||||
}
|
||||
default: {
|
||||
return (
|
||||
<Video source={{ uri }} style={dimensionStyles} paused controls />
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [dimensions, height, meme.memeType, uri, width]);
|
||||
|
||||
if (!error && (loading || !dimensions)) {
|
||||
return <LoadingView style={{ width, height }} />;
|
||||
@@ -41,21 +69,7 @@ const MemeViewItem = ({ meme }: { meme: Meme }) => {
|
||||
iconSize={50}
|
||||
/>
|
||||
) : (
|
||||
<AnimatedImage
|
||||
source={{ uri }}
|
||||
style={
|
||||
dimensions.aspectRatio > width / (height - 128)
|
||||
? {
|
||||
width,
|
||||
height: width / (dimensions.width / dimensions.height),
|
||||
}
|
||||
: {
|
||||
width:
|
||||
(height - 128) * (dimensions.width / dimensions.height),
|
||||
height: height - 128,
|
||||
}
|
||||
}
|
||||
/>
|
||||
mediaComponent
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } 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 { AndroidScoped } from 'react-native-file-access';
|
||||
import { Meme } from '../../../database';
|
||||
import { MEME_TYPE, Meme } from '../../../database';
|
||||
import { RootState } from '../../../state';
|
||||
import { MemeFail } from '..';
|
||||
import { getFontAwesome5IconSize } from '../../../utilities';
|
||||
import { useMemeDimensions } from '../../../hooks';
|
||||
|
||||
const MemesGridItem = ({
|
||||
meme,
|
||||
@@ -29,7 +29,32 @@ const MemesGridItem = ({
|
||||
|
||||
const uri = AndroidScoped.appendPath(storageUri, meme.filename);
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri });
|
||||
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 (
|
||||
<Image
|
||||
source={{ uri }}
|
||||
style={[
|
||||
{
|
||||
width: itemWidth,
|
||||
height: itemWidth,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default: {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}, [itemWidth, meme.memeType, uri]);
|
||||
|
||||
if (!error && (loading || !dimensions)) return <></>;
|
||||
|
||||
@@ -44,15 +69,7 @@ const MemesGridItem = ({
|
||||
iconSize={getFontAwesome5IconSize(gridColumns)}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
source={{ uri }}
|
||||
style={[
|
||||
{
|
||||
width: (width * 0.92 - 5) / gridColumns,
|
||||
height: (width * 0.92 - 5) / gridColumns,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
mediaComponent
|
||||
)}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } 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 { AndroidScoped } from 'react-native-file-access';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Meme } from '../../../database';
|
||||
import { MEME_TYPE, Meme } from '../../../database';
|
||||
import { MemeFail } from '..';
|
||||
import { RootState } from '../../../state';
|
||||
import { useMemeDimensions } from '../../../hooks';
|
||||
|
||||
const memesListItemStyles = StyleSheet.create({
|
||||
view: {
|
||||
@@ -50,7 +50,20 @@ const MemesListItem = ({
|
||||
|
||||
const uri = AndroidScoped.appendPath(storageUri, meme.filename);
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri });
|
||||
const { dimensions, loading, error } = useMemeDimensions(uri, meme.mimeType);
|
||||
|
||||
const mediaComponent = useMemo(() => {
|
||||
switch (meme.memeType) {
|
||||
case MEME_TYPE.IMAGE:
|
||||
case MEME_TYPE.GIF:
|
||||
case MEME_TYPE.VIDEO: {
|
||||
return <Image source={{ uri }} style={[memesListItemStyles.image]} />;
|
||||
}
|
||||
default: {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}, [meme.memeType, uri]);
|
||||
|
||||
if (!error && (loading || !dimensions)) return <></>;
|
||||
|
||||
@@ -62,7 +75,7 @@ const MemesListItem = ({
|
||||
{error ? (
|
||||
<MemeFail style={memesListItemStyles.image} />
|
||||
) : (
|
||||
<Image source={{ uri }} style={memesListItemStyles.image} />
|
||||
mediaComponent
|
||||
)}
|
||||
<View
|
||||
style={[
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Image, StyleSheet, TouchableHighlight } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useSafeAreaFrame } from 'react-native-safe-area-context';
|
||||
import { AndroidScoped } from 'react-native-file-access';
|
||||
import { useImageDimensions } from '@react-native-community/hooks';
|
||||
import { Meme } from '../../../database';
|
||||
import { MEME_TYPE, Meme } from '../../../database';
|
||||
import { RootState } from '../../../state';
|
||||
import { MemeFail } from '..';
|
||||
import { getFontAwesome5IconSize } from '../../../utilities';
|
||||
import { useMemeDimensions } from '../../../hooks';
|
||||
|
||||
const memeMasonryItemStyles = StyleSheet.create({
|
||||
view: {
|
||||
@@ -39,7 +39,35 @@ const MemesMasonryItem = ({
|
||||
|
||||
const uri = AndroidScoped.appendPath(storageUri, meme.filename);
|
||||
|
||||
const { dimensions, loading, error } = useImageDimensions({ uri });
|
||||
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 mediaComponent = useMemo(() => {
|
||||
switch (meme.memeType) {
|
||||
case MEME_TYPE.IMAGE:
|
||||
case MEME_TYPE.GIF:
|
||||
case MEME_TYPE.VIDEO: {
|
||||
return (
|
||||
<Image
|
||||
source={{ uri }}
|
||||
style={[
|
||||
memeMasonryItemStyles.image,
|
||||
{
|
||||
width: itemWidth,
|
||||
height: itemHeight,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default: {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}, [itemHeight, itemWidth, meme.memeType, uri]);
|
||||
|
||||
if (!error && (loading || !dimensions)) return <></>;
|
||||
|
||||
@@ -51,25 +79,12 @@ const MemesMasonryItem = ({
|
||||
<MemeFail
|
||||
style={[
|
||||
memeMasonryItemStyles.image,
|
||||
{
|
||||
width: (width * 0.92) / masonryColumns - 5,
|
||||
height: (width * 0.92) / masonryColumns - 5,
|
||||
},
|
||||
{ width: itemWidth, height: itemHeight },
|
||||
]}
|
||||
iconSize={getFontAwesome5IconSize(masonryColumns)}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
source={{ uri }}
|
||||
style={[
|
||||
memeMasonryItemStyles.image,
|
||||
{
|
||||
width: (width * 0.92) / masonryColumns - 5,
|
||||
height:
|
||||
((width * 0.92) / masonryColumns - 5) / dimensions.aspectRatio,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
mediaComponent
|
||||
)}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
|
Reference in New Issue
Block a user