diff --git a/components/Media.js b/components/Media.js index 16ef840..355929e 100644 --- a/components/Media.js +++ b/components/Media.js @@ -1,6 +1,6 @@ // Import necessary dependencies import React, { useState, useEffect } from 'react'; -import { View, TouchableHighlight, StyleSheet, FlatList, TouchableWithoutFeedback, Share } from 'react-native'; +import { View, TouchableHighlight, StyleSheet, FlatList, TouchableWithoutFeedback, TouchableOpacity, Share } from 'react-native'; import { Button, Text, ProgressBar } from 'react-native-paper'; import API from './../API.js'; import VideoPlayer from './VideoPlayer.js'; @@ -13,6 +13,7 @@ import { useNavigation } from '@react-navigation/native'; import { Image } from 'expo-image'; // Import Image from expo-image import * as FileSystem from 'expo-file-system'; import * as Sharing from 'expo-sharing'; +import i18n from "../i18nMessages.js"; // Extract Vimeo video ID from content string const videoIdF = (content) => { @@ -72,6 +73,7 @@ let Media = (props) => { // Extracting tags from content const imagesTag = imagesTagF(props.content, props.imageWidth || 1000, props.imageHeight || 1000); const imagesTagLimited = imagesTag.slice(0, 10); + const isImagesCapped = imagesTag.length > imagesTagLimited.length; const imageStyle = imagesTag.length === 1 ? styles.image : styles.multipleImage; const videosId = videoIdF(props.content); const hlsUrl = hlsIdF(props.content); @@ -229,19 +231,26 @@ let Media = (props) => { return ( {imagesTag.length > 2 ? ( - item[1]} - initialNumToRender={2} - style={{ - transform: [{ scale: 1.1 }], - paddingTop: 5, - paddingBottom: 10, - }} - showsHorizontalScrollIndicator={false} - /> + <> + item[1]} + initialNumToRender={2} + style={{ + transform: [{ scale: 1.1 }], + paddingTop: 5, + paddingBottom: 10, + }} + showsHorizontalScrollIndicator={false} + /> + {isImagesCapped ? ( + navigateToSlideshow(0)} style={styles.seeAllPhotosWrap}> + {i18n.t("message.clickToSeeAllPhotos")} + + ) : <>} + ) : ( {imagesTag.map((image, i) => ( @@ -294,5 +303,15 @@ const styles = StyleSheet.create({ iframe: { width: "100%", minHeight: 300, - } + }, + seeAllPhotosWrap: { + paddingTop: 2, + paddingBottom: 6, + paddingLeft: 6, + }, + seeAllPhotosText: { + color: "#5f6368", + textDecorationLine: "underline", + fontSize: 13, + }, }); diff --git a/components/Post.js b/components/Post.js index 9ede69f..9f005fb 100644 --- a/components/Post.js +++ b/components/Post.js @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; -import { Text, Pressable, FlatList, StyleSheet, View, Share, Alert, Linking } from 'react-native'; +import React, { useMemo, useRef, useState } from 'react'; +import { Text, Pressable, FlatList, StyleSheet, View, Share, Alert, Linking, Animated, PanResponder } from 'react-native'; import { Button, Card, Chip } from 'react-native-paper'; import API from './../API.js'; import UserName from './UserName.js'; @@ -21,8 +21,12 @@ let Post = (props) => { const viewer = gState.me; let [showCommentsB, changeshowCommentsB] = useState(props.showComments || false); let [post, changePost] = useState(props.post); + const [deleted, setDeleted] = useState(false); let [likes, changeLikes] = useState(Object.keys(post.reactions).length); let [bookmarked, changeBookmarked] = useState(post.bookmarks && post.bookmarks.includes(viewer._id)); + const isOwner = String(post.profileid || '') === String(viewer?._id || ''); + const swipeX = useRef(new Animated.Value(0)).current; + const SWIPE_WIDTH = 86; let toProfileText = post.toProfile && post.toProfile !== post.profileid ? : undefined; let cleanContent = post.content.replace(/@[A-z]+:.+\w/g, '').trim(); @@ -90,7 +94,51 @@ let Post = (props) => { url: url }); }; - return ( + const closeSwipe = () => { + Animated.spring(swipeX, { toValue: 0, useNativeDriver: true }).start(); + }; + const deletePost = async () => { + const result = await API.deletePost(post._id); + if (result?.status !== "ok") { + Alert.alert("Could not delete post", result?.status || "Please try again."); + return closeSwipe(); + } + setDeleted(true); + }; + const confirmDelete = () => { + Alert.alert( + "Delete post?", + "This action cannot be undone.", + [ + { text: "Cancel", style: "cancel", onPress: closeSwipe }, + { text: "Delete", style: "destructive", onPress: deletePost }, + ] + ); + }; + const panResponder = useMemo(() => PanResponder.create({ + onMoveShouldSetPanResponder: (_, gestureState) => + isOwner && + Math.abs(gestureState.dx) > 8 && + Math.abs(gestureState.dx) > Math.abs(gestureState.dy), + onPanResponderMove: (_, gestureState) => { + if (gestureState.dx > 0) { + swipeX.setValue(0); + return; + } + swipeX.setValue(Math.max(gestureState.dx, -SWIPE_WIDTH)); + }, + onPanResponderRelease: (_, gestureState) => { + const open = gestureState.dx < -SWIPE_WIDTH / 2; + Animated.spring(swipeX, { + toValue: open ? -SWIPE_WIDTH : 0, + useNativeDriver: true, + }).start(); + }, + onPanResponderTerminate: closeSwipe, + }), [isOwner, swipeX]); + if (deleted) return null; + + const postCard = ( { } ); + if (!isOwner) return postCard; + + return ( + + + + Delete + + + + {postCard} + + + ); } export default React.memo(Post); @@ -203,6 +265,30 @@ const styles = StyleSheet.create({ marginBottom: 2, padding: 0 }, + swipeWrap: { + position: "relative", + backgroundColor: "#edf2f7", + }, + deleteActionWrap: { + position: "absolute", + right: 0, + top: 0, + bottom: 2, + width: 86, + justifyContent: "center", + alignItems: "center", + backgroundColor: "#b3261e", + }, + deleteActionBtn: { + width: "100%", + height: "100%", + justifyContent: "center", + alignItems: "center", + }, + deleteActionText: { + color: "#fff", + fontWeight: "700", + }, comment: { margin: 8, marginTop: 0, diff --git a/i18nMessages.js b/i18nMessages.js index 1fc662a..a3f623e 100644 --- a/i18nMessages.js +++ b/i18nMessages.js @@ -78,6 +78,7 @@ const messages = { localMinistry: "Local Ministry", ocupation: "Ocupation", country: 'Country', + clickToSeeAllPhotos: "Click to see all photos", }, }, es: { @@ -151,6 +152,7 @@ const messages = { localMinistry: 'Ministerio local', ocupation: 'Ocupación', country: 'País', + clickToSeeAllPhotos: "Haz clic para ver todas las fotos", } }, fr: { @@ -224,6 +226,7 @@ const messages = { localMinistry: 'Ministère local', ocupation: 'Occupation', country: 'Pays', + clickToSeeAllPhotos: "Cliquez pour voir toutes les photos", } }, da: { @@ -297,6 +300,7 @@ const messages = { localMinistry: "Lokalt ministerium", ocupation: "Beskæftigelse", country: 'Land', + clickToSeeAllPhotos: "Klik for at se alle billeder", } } } @@ -315,4 +319,4 @@ if (messages[devideLocale]) { } moment.locale(i18n.locale); -export default i18n; \ No newline at end of file +export default i18n;