From cd78742d1571bc2a926e5480ac4e47fddff62a30 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Sat, 5 Oct 2024 20:02:47 -0400 Subject: [PATCH] ChatGPT fixed my upload by adding batches and feedback --- Views/NewPost.js | 203 +++++++++++++++++++++++++++++++---------------- 1 file changed, 135 insertions(+), 68 deletions(-) diff --git a/Views/NewPost.js b/Views/NewPost.js index 9c1442e..6300327 100644 --- a/Views/NewPost.js +++ b/Views/NewPost.js @@ -7,125 +7,178 @@ import { useNavigation } from '@react-navigation/native'; import * as ImagePicker from 'expo-image-picker'; import i18n from "../i18nMessages"; import Media from '../components/Media'; +import { Platform } from 'react-native'; +const BATCH_SIZE = 1; // Constant for batch size let NewPostView = (props) => { let [postContent, setPostContent] = useState(''); let initialContent = props.route.params.intialContent; let [extraContent, setExtraContent] = useState([initialContent]); let [toProfile, setToProfile] = useState([]); - const [photo, setPhoto] = React.useState(null); + const [photo, setPhoto] = useState(null); + const [uploadProgress, setUploadProgress] = useState(0); + const [isUploading, setIsUploading] = useState(false); // Variable to prevent posting during upload + const [cancelUpload, setCancelUpload] = useState(false); // Variable to handle upload cancellation const navigation = useNavigation(); useEffect(() => { - //console.log(props) let subscribed = true; const getProfileData = async () => { - if (!props.route.params?.toProfile) return 0; - await API.getUserProfile(props.route.params.toProfile).then((profileObj) => { - if (!subscribed) return 0; - setToProfile(profileObj); - }); - if(props.route.params?.sendNow){ - console.log("sending from tab bar button") - await handleNewPostButton() + if (!props.route.params?.toProfile) return; + try { + // Fetch profile data and update state if component is still subscribed + const profileObj = await API.getUserProfile(props.route.params.toProfile); + if (subscribed) setToProfile(profileObj); + // If sendNow flag is present, trigger post creation + if (props.route.params?.sendNow) { + console.log("sending from tab bar button"); + await handleNewPostButton(); + } + } catch (error) { + console.error("Error fetching profile data", error); } }; getProfileData(); return () => { + // Cleanup subscription flag subscribed = false; } }, [props.route.params?.sendNow]); const pickImage = async () => { - // No permissions request is necessary for launching the image library - let result = await ImagePicker.launchImageLibraryAsync({ - mediaTypes: ImagePicker.MediaTypeOptions.Images, - //allowsEditing: true, - //aspect: [4, 3], - quality: 0.2, - allowsMultipleSelection: true, - }); - if (!result.canceled) { - setPhoto(result); - let newPhotoURLs = await handleUploadPhoto(result); - let newExtraContent = [extraContent] - newPhotoURLs.forEach((newPhotoURL) => { - newExtraContent = ["@image:" + newPhotoURL].concat(newExtraContent); + try { + // Launch image picker to select images from gallery + const result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + quality: 0.2, // Set image quality + allowsMultipleSelection: true, // Allow multiple images to be selected }); - setExtraContent(newExtraContent); - setPhoto(null); + if (!result.canceled) { + setIsUploading(true); // Set uploading state to true + setCancelUpload(false); // Reset cancel flag + await handleUploadPhoto(result); // Upload selected photos + setPhoto(null); // Clear selected photo after upload + setIsUploading(false); // Set uploading state to false + } + } catch (error) { + console.error("Error picking image", error); + setIsUploading(false); // Set uploading state to false in case of error } }; const handleUploadPhoto = async (results) => { if (!results) return; - let allUploads = []; - results.assets.forEach(photo => { - const uri = - Platform.OS === "android" - ? photo.uri - : photo.uri.replace("file://", ""); - const filename = photo.uri.split("/").pop(); - const match = /\.(\w+)$/.exec(filename); - const ext = match?.[1]; - const type = match ? `image/${match[1]}` : `image`; - const formData = new FormData(); - formData.append("banner", { - uri, - name: `image.${ext}`, - type, - }); - try { - allUploads.push(fetch("https://social.emmint.com/upload.php", { - method: "POST", - body: formData, - headers: { "Content-Type": "multipart/form-data" } - }) - .then((res) => res.json()) - .then((data) => { - return data.fileName; - }) - .catch((err) => console.error(err))); + try { + setIsUploading(true); // Set uploading state to true + let uploadedFiles = []; + let totalPhotos = results.assets.length; + let currentIndex = 0; - } catch (err) { - console.log(err); - alert("Something went wrong uploading the photo."); + // Upload photos in batches of BATCH_SIZE + while (currentIndex < totalPhotos) { + if (cancelUpload) { + setIsUploading(false); + setUploadProgress(0); + alert("Upload cancelled."); + return; + } + + const batch = results.assets.slice(currentIndex, currentIndex + BATCH_SIZE); + setPhoto(batch[0]); // Set the first photo in the batch to preview it + const batchUploads = batch.map((photo) => { + // Prepare photo for upload + const uri = + Platform.OS === "android" + ? photo.uri // Use photo URI directly on Android + : photo.uri.replace("file://", ""); // Remove file:// prefix on other platforms + const filename = photo.uri.split("/").pop(); // Extract filename from URI + const match = /\.(\w+)$/.exec(filename); // Extract file extension + const ext = match?.[1]; + const type = match ? `image/${match[1]}` : `image`; // Set MIME type based on file extension + const formData = new FormData(); + formData.append("banner", { + uri, + name: `image.${ext}`, // Set the file name for upload + type, + }); + // Upload photo to server using .then statements + return fetch("https://social.emmint.com/upload.php", { + method: "POST", + body: formData, + headers: { "Content-Type": "multipart/form-data" } + }) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to upload image'); + } + return response.json(); + }) + .then((data) => { + return data.fileName; // Return the filename from the server response + }); + }); + const batchResults = await Promise.all(batchUploads); // Wait for all uploads in the batch to complete + uploadedFiles = uploadedFiles.concat(batchResults); // Add uploaded files to the list + currentIndex += BATCH_SIZE; // Move to the next batch + setUploadProgress((currentIndex / totalPhotos) * 100); // Update upload progress + + // Update extra content after each batch completes + setExtraContent(prevExtraContent => { + return prevExtraContent.concat(batchResults.map(url => "@image:" + url)); + }); } - }); - uploadedFiles = Promise.all(allUploads); - return uploadedFiles; + setIsUploading(false); // Set uploading state to false after completion + } catch (error) { + console.error("Error uploading photo", error); + alert("Something went wrong uploading the photo."); + setIsUploading(false); // Set uploading state to false in case of error + } }; const handleNewPostButton = async () => { - //setPostContent(''); - API.newPost(postContent + " " + extraContent.join(" "), props.route.params?.toProfile).then((newPost) => { - setPostContent(''); - setExtraContent([]); - navigation.navigate('Feed', { reRender: Math.random() }); - //if (newPostCB) newPostCB(newPost); - }); + if (isUploading) { + alert("Please wait for the upload to finish before posting."); + return; + } + try { + // Create a new post with the combined content + await API.newPost(postContent + " " + extraContent.join(" "), props.route.params?.toProfile); + setPostContent(''); // Clear post content after submission + setExtraContent([]); // Clear extra content after submission + navigation.navigate('Feed', { reRender: Math.random() }); // Navigate back to the Feed and trigger re-render + } catch (error) { + console.error("Error creating new post", error); + } } + const handleCancelUpload = () => { + setCancelUpload(true); // Set cancel flag to true + }; + return ( + {/* Header section with status update text and post button */} {i18n.t("message.statusUpdate")}: + {/* Display the profile being posted on if available */} { toProfile._id ? - Posting on: {toProfile._id ? toProfile.profile.firstName + " " + toProfile.profile.lastName : ''} - : <> + Posting on: {toProfile.profile?.firstName} {toProfile.profile?.lastName} + : null } + {/* Text input for post content */} { autoFocus={true} /> + {/* Button to pick images from the gallery */} + {isUploading && ( + + )} + {/* Display uploading state and selected image preview */} {photo && ( Uploading... @@ -152,6 +212,13 @@ let NewPostView = (props) => { /> )} + {/* Display upload progress if in progress */} + { + uploadProgress > 0 && uploadProgress < 100 && ( + Upload Progress: {uploadProgress.toFixed(2)}% + ) + } + {/* Display media content if extra content is available */} { extraContent.length > 0 && (