From 602f9fb505dd944f4d5768edc0e2a7c6caffdea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Eren?= Date: Wed, 24 Jul 2024 03:55:47 +0300 Subject: [PATCH] feat: post images --- JoyboyCommunity/src/modules/Post/index.tsx | 21 +++++- JoyboyCommunity/src/modules/Post/styles.ts | 4 +- .../src/screens/CreatePost/index.tsx | 75 +++++++++++++------ .../src/screens/CreatePost/styles.ts | 12 +++ JoyboyCommunity/src/utils/helpers.ts | 4 + 5 files changed, 90 insertions(+), 26 deletions(-) diff --git a/JoyboyCommunity/src/modules/Post/index.tsx b/JoyboyCommunity/src/modules/Post/index.tsx index 3304268d..bd663708 100644 --- a/JoyboyCommunity/src/modules/Post/index.tsx +++ b/JoyboyCommunity/src/modules/Post/index.tsx @@ -18,7 +18,7 @@ import {useProfile, useReact, useReactions, useReplyNotes, useStyles, useTheme} import {useTipModal} from '../../hooks/modals'; import {useAuth} from '../../store/auth'; import {MainStackNavigationProps} from '../../types'; -import {shortenPubkey} from '../../utils/helpers'; +import {getImageRatio, shortenPubkey} from '../../utils/helpers'; import {getElapsedTimeStringFull} from '../../utils/timestamp'; import stylesheet from './styles'; @@ -29,7 +29,6 @@ export type PostProps = { export const Post: React.FC = ({asComment, event}) => { const repostedEvent = undefined; - const postSource = undefined; const {theme} = useTheme(); const styles = useStyles(stylesheet); @@ -65,6 +64,16 @@ export const Post: React.FC = ({asComment, event}) => { return likesCount - dislikesCount; }, [reactions.data]); + const postSource = useMemo(() => { + if (!event?.tags) return; + + const imageTag = event.tags.find((tag) => tag[0] === 'image'); + if (!imageTag) return; + + const dimensions = imageTag[2].split('x').map(Number); + return {uri: imageTag[1], width: dimensions[0], height: dimensions[1]}; + }, [event?.tags]); + // Animated style for the icon const animatedIconStyle = useAnimatedStyle(() => ({ transform: [{scale: scale.value}], @@ -185,7 +194,13 @@ export const Post: React.FC = ({asComment, event}) => { {postSource && ( - + )} diff --git a/JoyboyCommunity/src/modules/Post/styles.ts b/JoyboyCommunity/src/modules/Post/styles.ts index fe504f14..d69981be 100644 --- a/JoyboyCommunity/src/modules/Post/styles.ts +++ b/JoyboyCommunity/src/modules/Post/styles.ts @@ -48,8 +48,10 @@ export default ThemedStyleSheet((theme) => ({ }, contentImage: { width: '100%', - height: 160, + height: 'auto', + resizeMode: 'cover', borderRadius: 8, + overflow: 'hidden', marginTop: Spacing.small, }, diff --git a/JoyboyCommunity/src/screens/CreatePost/index.tsx b/JoyboyCommunity/src/screens/CreatePost/index.tsx index 79dd824c..51de192f 100644 --- a/JoyboyCommunity/src/screens/CreatePost/index.tsx +++ b/JoyboyCommunity/src/screens/CreatePost/index.tsx @@ -1,13 +1,16 @@ import {useQueryClient} from '@tanstack/react-query'; +import * as ImagePicker from 'expo-image-picker'; import {useState} from 'react'; -import {KeyboardAvoidingView, Pressable, TextInput, View} from 'react-native'; +import {Image, KeyboardAvoidingView, Pressable, TextInput, View} from 'react-native'; import {SafeAreaView} from 'react-native-safe-area-context'; -import {CopyIcon, GalleryIcon, GifIcon, SendIconContained} from '../../assets/icons'; +import {GalleryIcon, SendIconContained} from '../../assets/icons'; import {TextButton} from '../../components'; import {useSendNote, useStyles, useTheme} from '../../hooks'; +import {useFileUpload} from '../../hooks/api'; import {useToast} from '../../hooks/modals'; import {CreatePostScreenProps} from '../../types'; +import {getImageRatio} from '../../utils/helpers'; import stylesheet from './styles'; export const CreatePost: React.FC = ({navigation}) => { @@ -15,19 +18,44 @@ export const CreatePost: React.FC = ({navigation}) => { const styles = useStyles(stylesheet); const [note, setNote] = useState(); + const [image, setImage] = useState(); + const fileUpload = useFileUpload(); const sendNote = useSendNote(); const queryClient = useQueryClient(); const {showToast} = useToast(); - const handleSendNote = () => { + const onGalleryPress = async () => { + const pickerResult = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: true, + allowsMultipleSelection: false, + selectionLimit: 1, + exif: false, + quality: 0.75, + }); + + if (pickerResult.canceled || !pickerResult.assets.length) return; + setImage(pickerResult.assets[0]); + }; + + const handleSendNote = async () => { if (!note || note?.length == 0) { showToast({type: 'error', title: 'Please write your note'}); return; } + let imageUrl: string | undefined; + if (image) { + const result = await fileUpload.mutateAsync(image); + if (result.data.url) imageUrl = result.data.url; + } + sendNote.mutate( - {content: note}, + { + content: note, + tags: image && imageUrl ? [['image', imageUrl, `${image.width}x${image.height}`]] : [], + }, { onSuccess() { showToast({type: 'success', title: 'Note sent successfully'}); @@ -54,29 +82,32 @@ export const CreatePost: React.FC = ({navigation}) => { - + + + + {image && ( + + + + )} + - + - - - - - - - - diff --git a/JoyboyCommunity/src/screens/CreatePost/styles.ts b/JoyboyCommunity/src/screens/CreatePost/styles.ts index d72fca84..5bb3943b 100644 --- a/JoyboyCommunity/src/screens/CreatePost/styles.ts +++ b/JoyboyCommunity/src/screens/CreatePost/styles.ts @@ -24,6 +24,9 @@ export default ThemedStyleSheet((theme) => ({ flex: 1, backgroundColor: theme.colors.background, }, + form: { + flex: 1, + }, input: { flex: 1, padding: Spacing.large, @@ -33,6 +36,15 @@ export default ThemedStyleSheet((theme) => ({ lineHeight: 24, ...Typography.medium, }, + imageContainer: { + padding: Spacing.pagePadding, + }, + image: { + width: '100%', + resizeMode: 'cover', + borderRadius: 8, + overflow: 'hidden', + }, buttons: { position: 'relative', diff --git a/JoyboyCommunity/src/utils/helpers.ts b/JoyboyCommunity/src/utils/helpers.ts index 837421ec..65f488d7 100644 --- a/JoyboyCommunity/src/utils/helpers.ts +++ b/JoyboyCommunity/src/utils/helpers.ts @@ -57,3 +57,7 @@ export const dataURLToBlob = (dataURL: string) => { return new Blob([ab], {type: mimeString}); }; + +export const getImageRatio = (width: number, height: number, minRatio = 0.75, maxRatio = 1.5) => { + return Math.max(minRatio, Math.min(maxRatio, width / height)); +};