diff --git a/src/components/VolunteerOpportunity.js b/src/components/VolunteerOpportunity.js index 8162180..ccd0930 100644 --- a/src/components/VolunteerOpportunity.js +++ b/src/components/VolunteerOpportunity.js @@ -21,6 +21,7 @@ import SimpleLineIcons from "@expo/vector-icons/SimpleLineIcons"; import { Image } from "expo-image"; import { Platform, StyleSheet, Text, View } from "react-native"; import { TouchableOpacity } from "react-native-gesture-handler"; +import Markdown from "react-native-markdown-display"; export default function VolunteerOpportunity({ navigation, @@ -80,16 +81,23 @@ export default function VolunteerOpportunity({ {date} {/* Location info with icon */} - + - {" "} - {location} - + + {location} + + {/* Right-side indicator chevron */} diff --git a/src/screens/VolunteerFormScreen.js b/src/screens/VolunteerFormScreen.js index 39423c6..7ac0bdc 100644 --- a/src/screens/VolunteerFormScreen.js +++ b/src/screens/VolunteerFormScreen.js @@ -17,6 +17,7 @@ import { Text, View, } from "react-native"; +import Markdown from "react-native-markdown-display"; import WebView from "react-native-webview"; import Fuse from "fuse.js"; import ErrorBoundary from "react-native-error-boundary"; @@ -24,12 +25,11 @@ import ErrorBoundary from "react-native-error-boundary"; import NextButton from "../components/NextButton"; import PersistScrollView from "../components/PersistScrollView"; -import { alertError, sendErrorEmail, openInMaps } from "../utils"; +import { openURL, sendErrorEmail } from "../utils"; import DanceClub from "../utils/forms/DanceClub"; import LibraryMusicHour from "../utils/forms/LibraryMusicHour"; import MusicByTheTracks from "../utils/forms/MusicByTheTracks"; import RequestConcert from "../utils/forms/RequestConcert"; -import colors from "../constants/colors"; import formIDs from "../constants/formIDs"; // Factory: choose form class by event title using fuzzy matching @@ -160,14 +160,21 @@ export default function VolunteerFormScreen({ navigation, route }) { )} {location == null ? null : ( - openInMaps(location)}> - + { + openURL(url); + return false; + }} + style={{ + body: { + fontSize: 18, + }, + }} > {location} - - + + )} {/* Render each question component from form */} @@ -262,8 +269,4 @@ const styles = StyleSheet.create({ justifyContent: "flex-end", marginBottom: 50, }, - locationText: { - textDecorationLine: "underline", - color: colors.primary, - }, }); diff --git a/src/screens/VolunteerOpportunityScreen.js b/src/screens/VolunteerOpportunityScreen.js index a43ca75..912ffc2 100644 --- a/src/screens/VolunteerOpportunityScreen.js +++ b/src/screens/VolunteerOpportunityScreen.js @@ -14,14 +14,7 @@ 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"; @@ -29,7 +22,7 @@ import PersistScrollView from "../components/PersistScrollView"; import PostersButton from "../components/PostersButton"; import Tag from "../components/Tag"; import colors from "../constants/colors"; -import { openInMaps } from "../utils"; +import { openURL } from "../utils"; export default function VolunteerOpportunityScreen({ route, navigation }) { // Destructure parameters passed via navigation @@ -95,14 +88,19 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { size={20} color={colors.black} /> - openInMaps(location)}> - - {location} - - + { + openURL(url); + return false; + }} + style={{ + body: { + fontSize: 18, + }, + }} + > + {location} + {/* Optional description section */} @@ -111,7 +109,7 @@ export default function VolunteerOpportunityScreen({ route, navigation }) { About { - Linking.openURL(url); + openURL(url); return false; }} style={{ @@ -269,8 +267,4 @@ const styles = StyleSheet.create({ detailsText: { fontSize: 18, }, - locationText: { - textDecorationLine: "underline", - color: colors.primary, - }, }); diff --git a/src/utils/index.js b/src/utils/index.js index 7e84f61..0ba7008 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -2,14 +2,13 @@ * index.js * Shared utility functions: * - alertError: standardized error alert + EmailJS error report - * - openURL / maybeOpenURL: external link handling with app store fallback + * - openURL: external link handling with app store fallback * - request: retry wrapper with exponential backoff for network calls * - strToDate / formatDate: Google Sheets date parsing and formatting * - Question: form question helper class * - emptyQuestionState: hook for question state * - isAtLeast, isNotEmpty, isExactly: basic validation predicates * - isValidEmail, isValidPhoneNumber: validator.js-backed field validators - * - openInMaps: launch maps app for a location */ import Constants from "expo-constants"; @@ -79,30 +78,6 @@ export function openURL(url) { }); } -/** - * Try to open URL, fallback to app store if scheme fails. - * @param {string} url - * @param {string} appName - * @param {string} appStoreID - * @param {string} playStoreID - */ -export function maybeOpenURL(url, appName, appStoreID, playStoreID) { - Linking.openURL(url).catch((error) => { - if (error.code == "EUNSPECIFIED") { - if (Platform.OS == "ios") { - openURL(`https://apps.apple.com/us/app/${appName}/id${appStoreID}`); - } else { - openURL(`https://play.google.com/store/apps/details?id=${playStoreID}`); - } - } else { - Alert.alert( - `Unable to open URL`, - `Your device does not support opening ${url} from this app. Please copy and paste the URL into your browser.`, - ); - } - }); -} - /** * Utility delay function for retry backoff. */ @@ -215,25 +190,3 @@ export const isAtLeast = (value, len) => export const isNotEmpty = (value) => isAtLeast(value, 1); export const isExactly = (value, len) => !isAtLeast(value, len + 1) && isAtLeast(value, len); - -/** - * Launch maps application or fallback to web URL for a location. - * @param {string} location - */ -export function openInMaps(location) { - const encodedLocation = encodeURIComponent(location); - const url = Platform.select({ - ios: `maps://maps.apple.com/?q=${encodedLocation}`, - android: `https://www.google.com/maps/search/?api=1&query=${encodedLocation}`, - }); - Linking.canOpenURL(url).then((supported) => { - if (supported) { - Linking.openURL(url); - } else { - // Fallback to Google Maps web URL - Linking.openURL( - `https://www.google.com/maps/search/?api=1&query=${encodedLocation}`, - ); - } - }); -}