From 8e6d9bc2fc8cc5308dd73e630c41ff32fe8cdbfa Mon Sep 17 00:00:00 2001 From: Raphael Muthu Date: Sat, 28 Mar 2026 08:15:49 -0400 Subject: [PATCH 01/11] Added Modal to make images pop-up when clicked Now, when then show posters and programs are displayed, the user can click on them to make the image pop up and enlarge. --- src/screens/VolunteerOpportunityScreen.js | 261 +++++++++++----------- 1 file changed, 135 insertions(+), 126 deletions(-) diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index 53171f2..1e25dd1 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -1,59 +1,97 @@ -/** +/* VolunteerOpportunityScreen.js * VolunteerOpportunityScreen.js * Detailed view of a volunteer event. * - Displays banner image with gradient overlay and title * - Shows event date and location (tap to open in maps) * - Optionally displays description and tags * - Provides Sign Up button (navigates to form or Google Forms URL) - */ + * - Provides Show Posters & Programs button to display all show programs and posters associated with the event + * - Show Posters & Programs are clickable and pop-up when clicked +*/ -import Markdown from "react-native-markdown-display"; + +import { useState, useEffect } from "react"; import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; import SimpleLineIcons from "@expo/vector-icons/SimpleLineIcons"; -import { ImageBackground } from "expo-image"; +import { Image, ImageBackground } from "expo-image"; import { LinearGradient } from "expo-linear-gradient"; -import { - Linking, - Pressable, - SafeAreaView, - StyleSheet, - Text, - View, -} from "react-native"; +import { Modal, Pressable, SafeAreaView, StyleSheet, Text, View } from "react-native"; import Heading from "../components/Heading"; import NextButton from "../components/NextButton"; import PersistScrollView from "../components/PersistScrollView"; import Tag from "../components/Tag"; import colors from "../constants/colors"; -import { openInMaps } from "../utils"; + +import PublicGoogleSheetsParser from "../utils/PublicGoogleSheetsParser"; + +const sheetId = process.env.EXPO_PUBLIC_SHEET_ID; +const sheetName = process.env.EXPO_PUBLIC_SHEET_NAME; export default function VolunteerOpportunityScreen({ route, navigation }) { - // Destructure parameters passed via navigation const { title, location, date, image, description, - tags, + tags = [], formURL, isSubmitted, - max, - signedUp, } = route.params; - const remainingSpots = parseInt(max) - parseInt(signedUp); - - // Map tags to Tag components const tagsIcons = tags.map((text) => ); + const [showImages, setShowImages] = useState(false); + const [imageGallery, setImageGallery] = useState([]); + const [selectedImage, setSelectedImage] = useState(null); + + + useEffect(() => { + const fetchImages = async () => { + try { + console.log("sheetId:", sheetId); + console.log("sheetName:", sheetName); + + if (!sheetId || !sheetName) { + throw new Error( + "Missing sheetId or sheetName. Ensure EXPO_PUBLIC_SHEET_ID and EXPO_PUBLIC_SHEET_NAME are set.", + ); + } + + + const parser = new PublicGoogleSheetsParser(sheetId, { + sheetName, + }); + + const rows = await parser.parse(); + + console.log("rows from parser:", rows?.length ?? 0, rows?.slice?.(0, 3)); + + + const imageObjects = (rows || []) + .map((row) => { + + const url = row && row.ProgramList ? String(row.ProgramList).trim() : ""; + return url; + }) + .filter((url) => url && url.length > 0) + .map((url) => ({ uri: url })); + + setImageGallery(imageObjects); + } catch (error) { + console.error("Error loading images from Google Sheet:", error); + } + }; + + fetchImages(); + }, []); + return ( - {/* Banner with background image and gradient overlay */} - {/* Overlay title text at bottom-left of banner */} - - {title} - + {title} - {/* Scrollable content area */} - {/* Event details: date and location */} - - {date} - + {date} - openInMaps(location)}> - - {location} - - + {location} - {/* Optional description section */} - {typeof description === "string" && description.trim() !== "" && ( + {description !== "" ? ( About - - {description} - + {description} - )} + ) : null} - {/* Optional tags section */} - {tags.length > 0 && ( + {tags.length > 0 ? ( Tags {tagsIcons} - )} - {/* Sign Up button with warning if already submitted */} + ) : null} + + setShowImages(!showImages)}> + + {showImages ? "Hide Posters & Programs" : "Show Posters & Programs"} + + + + {showImages && imageGallery.length > 0 && ( + + Gallery + + {imageGallery.map((img, index) => ( + setSelectedImage(img)}> + + + ))} + + +)} + + setSelectedImage(null)} +> + setSelectedImage(null)} + > + {selectedImage && ( + + )} + + + - {isSubmitted && ( - + {isSubmitted ? ( + Warning: You have already submitted this form. - )} + ) : null} - remainingSpots <= 0 || isSubmitted - ? null - : navigation.navigate( - formURL == null ? "Sign Up Form" : "Google Forms", - formURL == null - ? { - title, - location, - date, - } - : { formURL }, - ) + formURL == null + ? navigation.navigate("Sign Up Form", { + title, + location, + date, + }) + : navigation.navigate("Google Forms", { formURL }) } > Sign Up - {/* Show remaining spots if applicable */} - {!isNaN(remainingSpots) && - (remainingSpots <= 0 ? ( - - Registration is full. - - ) : ( - - {remainingSpots} spot{remainingSpots !== 1 ? "s" : ""}{" "} - remaining - - ))} @@ -259,7 +272,7 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: "flex-end", alignItems: "flex-end", - marginTop: 50, + marginTop: 20, }, headerText: { position: "absolute", @@ -271,8 +284,4 @@ const styles = StyleSheet.create({ detailsText: { fontSize: 18, }, - locationText: { - textDecorationLine: "underline", - color: colors.primary, - }, }); From e6db1653b7b75deb2c4f636d5585def6dd1fce17 Mon Sep 17 00:00:00 2001 From: Raphael Muthu Date: Sat, 28 Mar 2026 08:28:05 -0400 Subject: [PATCH 02/11] Refactor import statements and clean up code Fixed PR failure --- src/screens/VolunteerOpportunityScreen.js | 119 ++++++++++++---------- 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index 1e25dd1..75f7029 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -7,15 +7,21 @@ * - Provides Sign Up button (navigates to form or Google Forms URL) * - Provides Show Posters & Programs button to display all show programs and posters associated with the event * - Show Posters & Programs are clickable and pop-up when clicked -*/ - + */ import { useState, useEffect } from "react"; import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; import SimpleLineIcons from "@expo/vector-icons/SimpleLineIcons"; import { Image, ImageBackground } from "expo-image"; import { LinearGradient } from "expo-linear-gradient"; -import { Modal, Pressable, SafeAreaView, StyleSheet, Text, View } from "react-native"; +import { + Modal, + Pressable, + SafeAreaView, + StyleSheet, + Text, + View, +} from "react-native"; import Heading from "../components/Heading"; import NextButton from "../components/NextButton"; @@ -46,7 +52,6 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { const [imageGallery, setImageGallery] = useState([]); const [selectedImage, setSelectedImage] = useState(null); - useEffect(() => { const fetchImages = async () => { try { @@ -59,20 +64,22 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { ); } - const parser = new PublicGoogleSheetsParser(sheetId, { sheetName, }); const rows = await parser.parse(); - console.log("rows from parser:", rows?.length ?? 0, rows?.slice?.(0, 3)); - + console.log( + "rows from parser:", + rows?.length ?? 0, + rows?.slice?.(0, 3), + ); const imageObjects = (rows || []) .map((row) => { - - const url = row && row.ProgramList ? String(row.ProgramList).trim() : ""; + const url = + row && row.ProgramList ? String(row.ProgramList).trim() : ""; return url; }) .filter((url) => url && url.length > 0) @@ -144,57 +151,59 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { setShowImages(!showImages)}> - {showImages ? "Hide Posters & Programs" : "Show Posters & Programs"} + {showImages + ? "Hide Posters & Programs" + : "Show Posters & Programs"} - {showImages && imageGallery.length > 0 && ( - - Gallery - - {imageGallery.map((img, index) => ( - setSelectedImage(img)}> - 0 && ( + + Gallery + + {imageGallery.map((img, index) => ( + setSelectedImage(img)}> + + + ))} + + + )} + + setSelectedImage(null)} + > + - - ))} - - -)} - - setSelectedImage(null)} -> - setSelectedImage(null)} - > - {selectedImage && ( - - )} - - + onPress={() => setSelectedImage(null)} + > + {selectedImage && ( + + )} + + {isSubmitted ? ( From c5f34d7743a641c04e7e3e86126dc2a01a95e397 Mon Sep 17 00:00:00 2001 From: Raphael Muthu Date: Sat, 28 Mar 2026 12:02:47 -0400 Subject: [PATCH 03/11] Refactor image fetching logic in VolunteerOpportunityScreen Re-adjusted sheets so only posters associated with the event are displayed --- src/screens/VolunteerOpportunityScreen.js | 38 +++++++++-------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index 75f7029..7028ae4 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -55,45 +55,37 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { useEffect(() => { const fetchImages = async () => { try { - console.log("sheetId:", sheetId); - console.log("sheetName:", sheetName); - if (!sheetId || !sheetName) { throw new Error( "Missing sheetId or sheetName. Ensure EXPO_PUBLIC_SHEET_ID and EXPO_PUBLIC_SHEET_NAME are set.", ); } - + const parser = new PublicGoogleSheetsParser(sheetId, { sheetName, }); - + const rows = await parser.parse(); - - console.log( - "rows from parser:", - rows?.length ?? 0, - rows?.slice?.(0, 3), - ); - - const imageObjects = (rows || []) - .map((row) => { - const url = - row && row.ProgramList ? String(row.ProgramList).trim() : ""; - return url; - }) - .filter((url) => url && url.length > 0) - .map((url) => ({ uri: url })); - + + // Find the row that matches the current event by title + const eventRow = rows.find((row) => row.Title === title); + + // Collect all ProgramList columns from that row + const imageObjects = []; + let i = 1; + while (eventRow && eventRow[`ProgramList${i}`]) { + imageObjects.push({ uri: String(eventRow[`ProgramList${i}`]).trim() }); + i++; + } + setImageGallery(imageObjects); } catch (error) { console.error("Error loading images from Google Sheet:", error); } }; - + fetchImages(); }, []); - return ( From 1ce5816e127db1910742180a9ab6e99ef93b50a7 Mon Sep 17 00:00:00 2001 From: Raphael Muthu Date: Sat, 28 Mar 2026 12:12:31 -0400 Subject: [PATCH 04/11] Refactor imageObjects push for clarity Added prettier style --- src/screens/VolunteerOpportunityScreen.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index 7028ae4..fad4846 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -60,30 +60,32 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { "Missing sheetId or sheetName. Ensure EXPO_PUBLIC_SHEET_ID and EXPO_PUBLIC_SHEET_NAME are set.", ); } - + const parser = new PublicGoogleSheetsParser(sheetId, { sheetName, }); - + const rows = await parser.parse(); - + // Find the row that matches the current event by title const eventRow = rows.find((row) => row.Title === title); - + // Collect all ProgramList columns from that row const imageObjects = []; let i = 1; while (eventRow && eventRow[`ProgramList${i}`]) { - imageObjects.push({ uri: String(eventRow[`ProgramList${i}`]).trim() }); + imageObjects.push({ + uri: String(eventRow[`ProgramList${i}`]).trim(), + }); i++; } - + setImageGallery(imageObjects); } catch (error) { console.error("Error loading images from Google Sheet:", error); } }; - + fetchImages(); }, []); return ( From 73552be71ad2327996adb324ffb0dfc5cb74bde8 Mon Sep 17 00:00:00 2001 From: Pranav Maddineedi Date: Sat, 28 Mar 2026 22:28:51 -0700 Subject: [PATCH 05/11] add back features + formatting --- src/screens/VolunteerOpportunityScreen.js | 131 ++++++++++++++++++---- 1 file changed, 111 insertions(+), 20 deletions(-) diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index fad4846..9f22e99 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -9,12 +9,14 @@ * - Show Posters & Programs are clickable and pop-up when clicked */ +import Markdown from "react-native-markdown-display"; import { useState, useEffect } from "react"; import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; import SimpleLineIcons from "@expo/vector-icons/SimpleLineIcons"; import { Image, ImageBackground } from "expo-image"; import { LinearGradient } from "expo-linear-gradient"; import { + Linking, Modal, Pressable, SafeAreaView, @@ -28,6 +30,7 @@ import NextButton from "../components/NextButton"; import PersistScrollView from "../components/PersistScrollView"; import Tag from "../components/Tag"; import colors from "../constants/colors"; +import { openInMaps } from "../utils"; import PublicGoogleSheetsParser from "../utils/PublicGoogleSheetsParser"; @@ -35,6 +38,7 @@ const sheetId = process.env.EXPO_PUBLIC_SHEET_ID; const sheetName = process.env.EXPO_PUBLIC_SHEET_NAME; export default function VolunteerOpportunityScreen({ route, navigation }) { + // Destructure parameters passed via navigation const { title, location, @@ -44,8 +48,13 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { tags = [], formURL, isSubmitted, + max, + signedUp, } = route.params; + const remainingSpots = parseInt(max) - parseInt(signedUp); + + // Map tags to Tag components const tagsIcons = tags.map((text) => ); const [showImages, setShowImages] = useState(false); @@ -90,6 +99,7 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { }, []); return ( + {/* Banner with background image and gradient overlay */} + {/* Overlay title text at bottom-left of banner */} - {title} + + {title} + - + {/* Scrollable content area */} + {/* Event details: date and location */} - {date} + + {date} + - {location} + openInMaps(location)}> + + {location} + + - - {description !== "" ? ( + {/* Optional description section */} + {typeof description === "string" && description.trim() !== "" && ( About - {description} + + {description} + - ) : null} + )} - {tags.length > 0 ? ( + {/* Optional tags section */} + {tags.length > 0 && ( Tags {tagsIcons} - ) : null} + )} setShowImages(!showImages)}> @@ -173,7 +229,7 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { )} - + {/* Sign Up button with warning if already submitted */} - {isSubmitted ? ( + {isSubmitted && ( Warning: You have already submitted this form. - ) : null} + )} - formURL == null - ? navigation.navigate("Sign Up Form", { - title, - location, - date, - }) - : navigation.navigate("Google Forms", { formURL }) + remainingSpots <= 0 || isSubmitted + ? null + : navigation.navigate( + formURL == null ? "Sign Up Form" : "Google Forms", + formURL == null + ? { + title, + location, + date, + } + : { formURL }, + ) } > Sign Up + {/* Show remaining spots if applicable */} + {!isNaN(remainingSpots) && + (remainingSpots <= 0 ? ( + + Registration is full. + + ) : ( + + {remainingSpots} spot{remainingSpots !== 1 ? "s" : ""}{" "} + remaining + + ))} @@ -287,4 +374,8 @@ const styles = StyleSheet.create({ detailsText: { fontSize: 18, }, + locationText: { + textDecorationLine: "underline", + color: colors.primary, + }, }); From 3ab34df6e23cebf2103ef546c2b86f866b513309 Mon Sep 17 00:00:00 2001 From: Pranav Maddineedi Date: Sat, 28 Mar 2026 22:31:03 -0700 Subject: [PATCH 06/11] undelete deleted lines --- src/screens/VolunteerOpportunityScreen.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index 9f22e99..c8ec693 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -257,7 +257,7 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { {isSubmitted && ( - + Warning: You have already submitted this form. )} @@ -362,7 +362,7 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: "flex-end", alignItems: "flex-end", - marginTop: 20, + marginTop: 50, }, headerText: { position: "absolute", From 842a2450e0eab283218a73cee89b64c52dea8b3b Mon Sep 17 00:00:00 2001 From: Pranav Maddineedi Date: Mon, 30 Mar 2026 21:45:35 -0700 Subject: [PATCH 07/11] done! edited icon, refractored button, using carousel (manually vibecoded) --- src/components/CarouselSection.js | 1 + src/components/PostersButton.js | 166 ++++++++++++++++++++++ src/components/VolunteerOpportunity.js | 2 + src/screens/HomeScreen.js | 6 + src/screens/VolunteerOpportunityScreen.js | 120 +++------------- src/utils/index.js | 2 - 6 files changed, 197 insertions(+), 100 deletions(-) create mode 100644 src/components/PostersButton.js diff --git a/src/components/CarouselSection.js b/src/components/CarouselSection.js index d1d7345..6cc1116 100644 --- a/src/components/CarouselSection.js +++ b/src/components/CarouselSection.js @@ -37,6 +37,7 @@ function CarouselSection({ navigation, data, onRefresh }) { location={event.Location} date={formatDate(event.Date)} image={event.Image} + posters={event.Posters} description={event.Description ?? ""} tags={ event.Tags == null diff --git a/src/components/PostersButton.js b/src/components/PostersButton.js new file mode 100644 index 0000000..9fc7cde --- /dev/null +++ b/src/components/PostersButton.js @@ -0,0 +1,166 @@ +import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; +import { Image } from "expo-image"; +import { useRef, useState } from "react"; +import { + Dimensions, + FlatList, + Modal, + Pressable, + StyleSheet, + Text, + View, +} from "react-native"; + +import colors from "../constants/colors"; + +const { width, height } = Dimensions.get("window"); + +export default function PostersButton({ posters }) { + const [showGallery, setShowGallery] = useState(false); + const [currentIndex, setCurrentIndex] = useState(0); + const flatListRef = useRef(null); + + const images = posters.filter((uri, i) => posters.indexOf(uri) === i); + const count = images.length; + const looped = count > 0 ? [...images, ...images, ...images] : []; + + function openGallery() { + setCurrentIndex(count); + setShowGallery(true); + setTimeout(() => { + flatListRef.current?.scrollToIndex({ index: count, animated: false }); + }, 0); + } + + function onMomentumScrollEnd(e) { + const idx = Math.round(e.nativeEvent.contentOffset.x / width); + let normalized = idx; + + if (idx < count) { + normalized = idx + count; + flatListRef.current?.scrollToIndex({ + index: normalized, + animated: false, + }); + } else if (idx >= count * 2) { + normalized = idx - count; + flatListRef.current?.scrollToIndex({ + index: normalized, + animated: false, + }); + } + + setCurrentIndex(normalized); + } + + return ( + + + + Show Posters & Programs + + + setShowGallery(false)} + > + + setShowGallery(false)} + > + + + + String(i)} + getItemLayout={(_, i) => ({ + length: width, + offset: width * i, + index: i, + })} + initialScrollIndex={count} + onMomentumScrollEnd={onMomentumScrollEnd} + renderItem={({ item }) => ( + + + + )} + /> + + + {(currentIndex % count) + 1} / {count} + + + + + ); +} + +const styles = StyleSheet.create({ + button: { + flexDirection: "row", + alignItems: "center", + gap: 15, + paddingVertical: 10, + paddingHorizontal: 15, + borderRadius: 15, + borderWidth: 1, + borderColor: colors.primary, + alignSelf: "center", + marginTop: 20, + }, + buttonText: { + fontSize: 22, + color: colors.primary, + fontWeight: "500", + }, + modalContainer: { + flex: 1, + backgroundColor: "black", + justifyContent: "center", + }, + closeButton: { + position: "absolute", + top: 50, + right: 20, + zIndex: 10, + }, + imagePage: { + width, + height, + justifyContent: "center", + alignItems: "center", + paddingTop: 50, + paddingBottom: 50, + paddingLeft: 50, + paddingRight: 50, + }, + image: { + width: "100%", + height: "100%", + }, + footer: { + color: "white", + fontSize: 16, + textAlign: "center", + position: "absolute", + bottom: 30, + width: "100%", + }, +}); diff --git a/src/components/VolunteerOpportunity.js b/src/components/VolunteerOpportunity.js index cb76e82..2f500b5 100644 --- a/src/components/VolunteerOpportunity.js +++ b/src/components/VolunteerOpportunity.js @@ -29,6 +29,7 @@ export default function VolunteerOpportunity({ location, date, image, + posters, description, tags, formURL, @@ -46,6 +47,7 @@ export default function VolunteerOpportunity({ location, date, image, + posters, description, tags, formURL, diff --git a/src/screens/HomeScreen.js b/src/screens/HomeScreen.js index f7e7520..2547caa 100644 --- a/src/screens/HomeScreen.js +++ b/src/screens/HomeScreen.js @@ -133,6 +133,12 @@ export default function HomeScreen({ navigation, route }) { opportunity.Location ??= "Unknown Location"; opportunity.Date = strToDate(opportunity.Date) ?? new Date(0); + opportunity.Posters = opportunity.Posters + ? opportunity.Posters.split(",") + .map((s) => s.trim()) + .filter(Boolean) + : []; + const event_midnight = new Date(opportunity.Date); event_midnight.setHours(23, 59, 59, 999); diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index c8ec693..f5ce0f7 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -13,11 +13,10 @@ import Markdown from "react-native-markdown-display"; import { useState, useEffect } from "react"; import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; import SimpleLineIcons from "@expo/vector-icons/SimpleLineIcons"; -import { Image, ImageBackground } from "expo-image"; +import { ImageBackground } from "expo-image"; import { LinearGradient } from "expo-linear-gradient"; import { Linking, - Modal, Pressable, SafeAreaView, StyleSheet, @@ -28,6 +27,7 @@ import { import Heading from "../components/Heading"; import NextButton from "../components/NextButton"; import PersistScrollView from "../components/PersistScrollView"; +import PostersButton from "../components/PostersButton"; import Tag from "../components/Tag"; import colors from "../constants/colors"; import { openInMaps } from "../utils"; @@ -44,8 +44,9 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { location, date, image, + posters, description, - tags = [], + tags, formURL, isSubmitted, max, @@ -57,46 +58,6 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { // Map tags to Tag components const tagsIcons = tags.map((text) => ); - const [showImages, setShowImages] = useState(false); - const [imageGallery, setImageGallery] = useState([]); - const [selectedImage, setSelectedImage] = useState(null); - - useEffect(() => { - const fetchImages = async () => { - try { - if (!sheetId || !sheetName) { - throw new Error( - "Missing sheetId or sheetName. Ensure EXPO_PUBLIC_SHEET_ID and EXPO_PUBLIC_SHEET_NAME are set.", - ); - } - - const parser = new PublicGoogleSheetsParser(sheetId, { - sheetName, - }); - - const rows = await parser.parse(); - - // Find the row that matches the current event by title - const eventRow = rows.find((row) => row.Title === title); - - // Collect all ProgramList columns from that row - const imageObjects = []; - let i = 1; - while (eventRow && eventRow[`ProgramList${i}`]) { - imageObjects.push({ - uri: String(eventRow[`ProgramList${i}`]).trim(), - }); - i++; - } - - setImageGallery(imageObjects); - } catch (error) { - console.error("Error loading images from Google Sheet:", error); - } - }; - - fetchImages(); - }, []); return ( {/* Banner with background image and gradient overlay */} @@ -199,61 +160,7 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { )} - setShowImages(!showImages)}> - - {showImages - ? "Hide Posters & Programs" - : "Show Posters & Programs"} - - - - {showImages && imageGallery.length > 0 && ( - - Gallery - - {imageGallery.map((img, index) => ( - setSelectedImage(img)}> - - - ))} - - - )} - {/* Sign Up button with warning if already submitted */} - setSelectedImage(null)} - > - setSelectedImage(null)} - > - {selectedImage && ( - - )} - - + {isSubmitted && ( @@ -378,4 +285,21 @@ const styles = StyleSheet.create({ textDecorationLine: "underline", color: colors.primary, }, + postersButton: { + flexDirection: "row", + alignItems: "center", + gap: 6, + paddingVertical: 8, + paddingHorizontal: 12, + borderRadius: 8, + borderWidth: 1, + borderColor: colors.primary, + alignSelf: "flex-start", + marginVertical: 10, + }, + postersButtonText: { + fontSize: 14, + color: colors.primary, + fontWeight: "500", + }, }); diff --git a/src/utils/index.js b/src/utils/index.js index 1ea6b84..328810c 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -49,8 +49,6 @@ export async function sendErrorEmail(error) { publicKey: process.env.EXPO_PUBLIC_EMAIL_PUBLIC_KEY, }, ); - - console.log("SUCCESS!"); } catch (err) { if (err instanceof EmailJSResponseStatus) { console.log("EmailJS Request Failed...", err); From 1a4d6fd8cb3f989e8088badcccc65e624be42cd4 Mon Sep 17 00:00:00 2001 From: Pranav Maddineedi Date: Mon, 30 Mar 2026 22:11:03 -0700 Subject: [PATCH 08/11] account for the no-poster case --- src/components/PostersButton.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/PostersButton.js b/src/components/PostersButton.js index 9fc7cde..6ca6a0d 100644 --- a/src/components/PostersButton.js +++ b/src/components/PostersButton.js @@ -53,6 +53,8 @@ export default function PostersButton({ posters }) { setCurrentIndex(normalized); } + if (count === 0) return null; + return ( From 6360ce9eff57fc0e274900911241581a609a045e Mon Sep 17 00:00:00 2001 From: Pranav Maddineedi Date: Mon, 30 Mar 2026 22:19:25 -0700 Subject: [PATCH 09/11] removing unused vars & styles & imports --- src/screens/VolunteerOpportunityScreen.js | 32 +---------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index f5ce0f7..d7ad16a 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -10,19 +10,11 @@ */ import Markdown from "react-native-markdown-display"; -import { useState, useEffect } from "react"; import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; import SimpleLineIcons from "@expo/vector-icons/SimpleLineIcons"; import { ImageBackground } from "expo-image"; import { LinearGradient } from "expo-linear-gradient"; -import { - Linking, - Pressable, - SafeAreaView, - StyleSheet, - Text, - View, -} from "react-native"; +import { Pressable, SafeAreaView, StyleSheet, Text, View } from "react-native"; import Heading from "../components/Heading"; import NextButton from "../components/NextButton"; @@ -32,11 +24,6 @@ import Tag from "../components/Tag"; import colors from "../constants/colors"; import { openInMaps } from "../utils"; -import PublicGoogleSheetsParser from "../utils/PublicGoogleSheetsParser"; - -const sheetId = process.env.EXPO_PUBLIC_SHEET_ID; -const sheetName = process.env.EXPO_PUBLIC_SHEET_NAME; - export default function VolunteerOpportunityScreen({ route, navigation }) { // Destructure parameters passed via navigation const { @@ -285,21 +272,4 @@ const styles = StyleSheet.create({ textDecorationLine: "underline", color: colors.primary, }, - postersButton: { - flexDirection: "row", - alignItems: "center", - gap: 6, - paddingVertical: 8, - paddingHorizontal: 12, - borderRadius: 8, - borderWidth: 1, - borderColor: colors.primary, - alignSelf: "flex-start", - marginVertical: 10, - }, - postersButtonText: { - fontSize: 14, - color: colors.primary, - fontWeight: "500", - }, }); From a89ac8ff65bffaf498a755b123c755d1b2bdd348 Mon Sep 17 00:00:00 2001 From: Pranav Maddineedi <77818951+Pramad712@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:25:03 -0700 Subject: [PATCH 10/11] Update src/components/PostersButton.js efficiency for links Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/components/PostersButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PostersButton.js b/src/components/PostersButton.js index 6ca6a0d..7165e1b 100644 --- a/src/components/PostersButton.js +++ b/src/components/PostersButton.js @@ -20,7 +20,7 @@ export default function PostersButton({ posters }) { const [currentIndex, setCurrentIndex] = useState(0); const flatListRef = useRef(null); - const images = posters.filter((uri, i) => posters.indexOf(uri) === i); + const images = Array.from(new Set(posters)); const count = images.length; const looped = count > 0 ? [...images, ...images, ...images] : []; From ce38e9cf7217a708cdbf336e715ea08438d6671a Mon Sep 17 00:00:00 2001 From: Pranav Maddineedi Date: Tue, 31 Mar 2026 17:02:56 -0700 Subject: [PATCH 11/11] remove test error code --- src/screens/VolunteerFormScreen.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/screens/VolunteerFormScreen.js b/src/screens/VolunteerFormScreen.js index b1ea13b..f5d6f4a 100644 --- a/src/screens/VolunteerFormScreen.js +++ b/src/screens/VolunteerFormScreen.js @@ -181,12 +181,6 @@ export default function VolunteerFormScreen({ navigation, route }) { {/* Render each question component from form */} - {(() => { - const Bad = () => { - throw new Error("Test error"); - }; - return ; - })()} {form .questions() .filter((question) => question?.isVisible())