diff --git a/app/app.config.ts b/app/app.config.ts
index 0eb2ea6..21ff5fc 100644
--- a/app/app.config.ts
+++ b/app/app.config.ts
@@ -8,7 +8,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
slug: "Lakbayan",
name: "Lakbayan",
- version: "1.0.0",
+ version: "1.0.1",
orientation: "portrait",
scheme: "myapp",
userInterfaceStyle: "automatic",
diff --git a/app/src/app/(account)/bookmarked-trips.tsx b/app/src/app/(account)/bookmarked-trips.tsx
index 1d99b12..637801a 100644
--- a/app/src/app/(account)/bookmarked-trips.tsx
+++ b/app/src/app/(account)/bookmarked-trips.tsx
@@ -6,7 +6,7 @@ import {
ActivityIndicator,
Text,
TouchableOpacity,
- BackHandler
+ BackHandler,
} from "react-native";
import { useRouter } from "expo-router";
import { useFocusEffect } from "@react-navigation/native";
@@ -33,7 +33,7 @@ export default function BookmarkedTrips() {
postSegment: null,
};
- router.push({
+ router.replace({
pathname: "/(search)/3-trip-overview",
params: { tripData: JSON.stringify(tripSearch), from: "bookmarked-trips" },
});
@@ -51,10 +51,7 @@ export default function BookmarkedTrips() {
return true;
};
- const backHandler = BackHandler.addEventListener(
- "hardwareBackPress",
- backAction,
- );
+ const backHandler = BackHandler.addEventListener("hardwareBackPress", backAction);
return () => backHandler.remove();
}, []),
diff --git a/app/src/app/(account)/submitted-trips.tsx b/app/src/app/(account)/submitted-trips.tsx
index 22cb039..4b439df 100644
--- a/app/src/app/(account)/submitted-trips.tsx
+++ b/app/src/app/(account)/submitted-trips.tsx
@@ -25,7 +25,7 @@ export default function SubmittedTrips() {
const { submittedTrips, loading } = useSubmittedTrips(user?.id || null);
function handleTripPress(trip: TripSearch) {
- router.push({
+ router.replace({
pathname: "/(search)/3-trip-overview",
params: { tripData: JSON.stringify(trip), from: "submitted-trips" },
});
@@ -43,10 +43,7 @@ export default function SubmittedTrips() {
return true;
};
- const backHandler = BackHandler.addEventListener(
- "hardwareBackPress",
- backAction,
- );
+ const backHandler = BackHandler.addEventListener("hardwareBackPress", backAction);
return () => backHandler.remove();
}, []),
@@ -54,7 +51,7 @@ export default function SubmittedTrips() {
return (
-
+
{loading ? (
diff --git a/app/src/app/(contribute)/2-review-trip.tsx b/app/src/app/(contribute)/2-review-trip.tsx
index 79fb3e8..3236b82 100644
--- a/app/src/app/(contribute)/2-review-trip.tsx
+++ b/app/src/app/(contribute)/2-review-trip.tsx
@@ -1,7 +1,7 @@
-import React, { useEffect } from "react";
+import React, { useState } from "react";
import { router } from "expo-router";
import { useFocusEffect } from "@react-navigation/native";
-import { SafeAreaView, View, Alert, BackHandler } from "react-native";
+import { ActivityIndicator, SafeAreaView, View, Alert, Text, BackHandler } from "react-native";
import Header from "@components/ui/Header";
import LineSource from "@components/map/LineSource";
@@ -22,6 +22,7 @@ import {
fetchSubmitLogId,
deleteSubmitLog,
} from "@services/logs-service";
+import { set } from "lodash";
export default function TripReview() {
const { user } = useSession();
@@ -38,6 +39,9 @@ export default function TripReview() {
clearRouteData,
submitTrip,
} = useTripCreator();
+
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
if (!user) throw new Error("User must be logged in to create a trip!");
const handleCreateSegment = async () => {
@@ -59,18 +63,20 @@ export default function TripReview() {
};
const handleSubmitTrip = async () => {
+ setIsSubmitting(true);
try {
await submitTrip();
- Alert.alert("Trip Submitted", "Your trip has been submitted successfully!");
const id = await fetchSubmitLogId({ userId: user.id });
if (!id) {
console.error("No submit log ID found.");
return;
}
-
await updateSubmitLog({ id, status: "completed" });
+ Alert.alert("Trip Submitted", "Your trip has been submitted successfully!");
+ setIsSubmitting(false);
router.replace("/(tabs)");
} catch (error) {
+ setIsSubmitting(false);
Alert.alert("Error", "Failed to submit your trip. Please try again.");
}
};
@@ -113,7 +119,7 @@ export default function TripReview() {
});
return (
-
+
@@ -132,12 +138,20 @@ export default function TripReview() {
deleteSegment={handleDeleteSegment}
/>
-
+
+
+ {isSubmitting && (
+
+
+ Submitting trip, please wait...
+
+ )}
);
}
diff --git a/app/src/app/(contribute)/3-add-transfer.tsx b/app/src/app/(contribute)/3-add-transfer.tsx
index 0485aa2..3392e6f 100644
--- a/app/src/app/(contribute)/3-add-transfer.tsx
+++ b/app/src/app/(contribute)/3-add-transfer.tsx
@@ -1,3 +1,4 @@
+import { useMemo } from "react";
import { router } from "expo-router";
import { SafeAreaView, View, Alert, BackHandler } from "react-native";
import { useFocusEffect } from "@react-navigation/native";
@@ -54,13 +55,18 @@ export default function RouteSelectInfo() {
return () => backHandler.remove();
});
+ const memoizedEnd: [string | null, [number, number] | null] = useMemo(
+ () => [route.endLocation ?? null, route.endCoords ?? null],
+ [route.endLocation, route.endCoords],
+ );
+
return (
handleEndChange(c, l)}
/>
);
diff --git a/app/src/app/(contribute)/toda-stops.tsx b/app/src/app/(contribute)/toda-stops.tsx
index d69a99c..1556f24 100644
--- a/app/src/app/(contribute)/toda-stops.tsx
+++ b/app/src/app/(contribute)/toda-stops.tsx
@@ -33,6 +33,7 @@ export default function TodaStops() {
const [stops, setStops] = useState([]);
const [loadingStops, setLoadingStops] = useState(false);
const [formSnapshot, setFormSnapshot] = useState("");
+ const [isSubmitting, setIsSubmitting] = useState(false);
const loadStops = async () => {
setLoadingStops(true);
@@ -114,7 +115,15 @@ export default function TodaStops() {
coordinates={coordinates}
onNewStopAdded={loadStops}
onFormChange={(form) => setFormSnapshot(JSON.stringify(form))}
+ setIsSubmitting={setIsSubmitting}
+ isSubmitting={isSubmitting}
/>
+ {isSubmitting && (
+
+
+ Submitting TODA stop, please wait...
+
+ )}
);
}
diff --git a/app/src/app/(journal)/journal-review.tsx b/app/src/app/(journal)/journal-review.tsx
index 7085525..69429da 100644
--- a/app/src/app/(journal)/journal-review.tsx
+++ b/app/src/app/(journal)/journal-review.tsx
@@ -1,6 +1,6 @@
import { useRouter } from "expo-router";
import React, { useState, useCallback } from "react";
-import { SafeAreaView, Alert, BackHandler } from "react-native";
+import { SafeAreaView, Alert, BackHandler, View, ActivityIndicator, Text } from "react-native";
import { useFocusEffect } from "@react-navigation/native";
import Header from "@components/ui/Header";
@@ -26,7 +26,8 @@ export default function JournalReview() {
const router = useRouter();
const { user } = useSession();
const { cameraRef } = useMapView();
- const { trip, segments, transitJournal, rating, hasDeviated, setRating, setHasDeviated } = useTransitJournal();
+ const { trip, segments, transitJournal, rating, hasDeviated, setRating, setHasDeviated } =
+ useTransitJournal();
const [newComment, setNewComment] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -39,8 +40,14 @@ export default function JournalReview() {
const handleSubmit = async () => {
setIsSubmitting(true);
- if (!newComment.trim()) { setIsSubmitting(false); return; }
- if (!trip || !segments || !user) { setIsSubmitting(false); return; }
+ if (!newComment.trim()) {
+ setIsSubmitting(false);
+ return;
+ }
+ if (!trip || !segments || !user) {
+ setIsSubmitting(false);
+ return;
+ }
const journalPayload: Partial = {
id: transitJournal.id,
@@ -56,7 +63,10 @@ export default function JournalReview() {
// // if did not deviate, increment GPS count
if (!Boolean(hasDeviated)) {
- await incrementSegmentGPSCount(trip.segments.map(({ id }) => id), !Boolean(hasDeviated));
+ await incrementSegmentGPSCount(
+ trip.segments.map(({ id }) => id),
+ !Boolean(hasDeviated),
+ );
}
await updateTransitJournal(journalPayload);
await updateProfile({ id: user.id, transitJournalId: null });
@@ -65,6 +75,7 @@ export default function JournalReview() {
setIsSubmitting(false);
router.replace("/(tabs)");
} catch (error) {
+ setIsSubmitting(false);
Alert.alert("Error", "Failed to submit your transit journal. Please try again.");
}
};
@@ -88,10 +99,7 @@ export default function JournalReview() {
return true;
};
- const backHandler = BackHandler.addEventListener(
- "hardwareBackPress",
- backAction,
- );
+ const backHandler = BackHandler.addEventListener("hardwareBackPress", backAction);
return () => backHandler.remove();
}, []),
@@ -122,6 +130,13 @@ export default function JournalReview() {
setHasDeviated={setHasDeviated}
isSubmitting={isSubmitting}
/>
+
+ {isSubmitting && (
+
+
+ Submitting trip, please wait...
+
+ )}
);
}
diff --git a/app/src/app/(search)/1-search-trip.tsx b/app/src/app/(search)/1-search-trip.tsx
index d8c6139..7cc0c9e 100644
--- a/app/src/app/(search)/1-search-trip.tsx
+++ b/app/src/app/(search)/1-search-trip.tsx
@@ -59,7 +59,7 @@ export default function SearchTrip() {
}
try {
await fetchSuggestedTrips();
- router.push("/(search)/2-trip-suggestions");
+ router.replace("/(search)/2-trip-suggestions");
} catch (error) {
Alert.alert("Error fetching trips. Please try again.");
}
diff --git a/app/src/app/(search)/2-trip-suggestions.tsx b/app/src/app/(search)/2-trip-suggestions.tsx
index 49d6406..7e16619 100644
--- a/app/src/app/(search)/2-trip-suggestions.tsx
+++ b/app/src/app/(search)/2-trip-suggestions.tsx
@@ -2,7 +2,6 @@ import { useEffect, useRef } from "react";
import { useRouter } from "expo-router";
import BottomSheet from "@gorhom/bottom-sheet";
import { Text, SafeAreaView, View, Pressable } from "react-native";
-import { MaterialIcons } from "@expo/vector-icons";
import Header from "@components/ui/Header";
import TripPreview from "@components/ui/TripPreview";
@@ -47,9 +46,10 @@ export default function SuggestedTrips() {
}
}, []);
- const handleSelectTrip = (trip: TripSearch) => {
+ const handleSelectTrip = (trip: TripSearch, index: number) => {
setTrip(trip);
- router.push("/(search)/3-trip-overview");
+ console.log(`Selected trip: ${trip.id} with index ${index}`);
+ router.replace("/(search)/3-trip-overview");
};
const handleOpenFilters = () => filterSheetRef.current?.snapToIndex(1);
@@ -70,10 +70,11 @@ export default function SuggestedTrips() {
) : (
- {filteredTrips.map((trip) => (
+ {filteredTrips.map((trip, index) => (
handleSelectTrip(trip)}
+ onPress={() => handleSelectTrip(trip, index)}
+ android_ripple={{ color: "#ccc" }}
>
diff --git a/app/src/app/(social)/comments-list.tsx b/app/src/app/(social)/comments-list.tsx
index 72d884a..75b5429 100644
--- a/app/src/app/(social)/comments-list.tsx
+++ b/app/src/app/(social)/comments-list.tsx
@@ -8,6 +8,7 @@ import { getComments, addComment } from "@services/socials-service";
import Header from "@components/ui/Header";
import CommentItem from "@components/ui/CommentItem";
import PrimaryButton from "@components/ui/PrimaryButton";
+import { set } from "lodash";
export default function CommentsList() {
const { user } = useSession();
@@ -18,6 +19,7 @@ export default function CommentsList() {
const [comments, setComments] = useState([]);
const [content, setContent] = useState("");
const [loading, setLoading] = useState(false);
+ const [isSubmitting, setIsSubmitting] = useState(false);
useEffect(() => {
async function fetchComments() {
@@ -36,14 +38,19 @@ export default function CommentsList() {
}, [tripId]);
const handleCommentSubmit = async () => {
- if (!content.trim()) return;
-
+ setIsSubmitting(true);
+ if (!content.trim()) {
+ setIsSubmitting(false);
+ return;
+ }
try {
await addComment(tripId, user?.id || "", content, isGpsVerified);
const updatedComments = await getComments(tripId);
setComments(updatedComments || []);
setContent("");
+ setIsSubmitting(false);
} catch (error) {
+ setIsSubmitting(false);
console.error("Error adding comment:", error);
}
};
@@ -84,7 +91,11 @@ export default function CommentsList() {
className="flex-1 border border-gray-200 rounded-lg px-3"
testID="comment-input"
/>
-
+
diff --git a/app/src/app/(social)/contributor-account.tsx b/app/src/app/(social)/contributor-account.tsx
index 0e91e6a..d42473d 100644
--- a/app/src/app/(social)/contributor-account.tsx
+++ b/app/src/app/(social)/contributor-account.tsx
@@ -18,6 +18,7 @@ import {
import UserHeader from "@components/account/UserHeader";
import TripPreview from "@components/ui/TripPreview";
+import { SourceDestinationTitle } from "@components/ui/SourceDestinationTitle";
import { useUserTrips } from "@hooks/use-trip-data";
@@ -121,6 +122,10 @@ export default function ContributorAccount() {
keyExtractor={(trip) => trip.id}
renderItem={({ item }) => (
handleTripPress(item)}>
+
)}
diff --git a/app/src/app/(tabs)/account.tsx b/app/src/app/(tabs)/account.tsx
index 0fb7e0e..c89bee6 100644
--- a/app/src/app/(tabs)/account.tsx
+++ b/app/src/app/(tabs)/account.tsx
@@ -1,5 +1,6 @@
import React from "react";
import { Text, SafeAreaView, View, Alert, ScrollView } from "react-native";
+import Constants from "expo-constants";
import { logoutUser } from "@services/account-service";
import { useSession } from "@contexts/SessionContext";
@@ -20,6 +21,9 @@ export default function Account() {
const { user, username } = useSession();
const { userRole, points, joinedDate, loading } = useAccountDetails(user?.id);
+ const appVersion =
+ Constants.expoConfig?.version ?? (Constants?.manifest as any)?.version ?? "dev";
+
async function handleLogout() {
try {
await logoutUser();
@@ -96,6 +100,7 @@ export default function Account() {
)}
+ Version {appVersion}
diff --git a/app/src/app/(tabs)/index.tsx b/app/src/app/(tabs)/index.tsx
index c79bf89..863720d 100644
--- a/app/src/app/(tabs)/index.tsx
+++ b/app/src/app/(tabs)/index.tsx
@@ -18,7 +18,7 @@ export default function Index() {
const { symbolRef, updateLiveStatus } = useLiveUpdates("box", 10);
const handleTextInputFocus = () => {
- router.push("/(search)/1-search-trip");
+ router.replace("/(search)/1-search-trip");
};
const handleCameraChange = (state: MapBoxMapState) => {
diff --git a/app/src/components/ContributeOption.tsx b/app/src/components/ContributeOption.tsx
index 57034f1..9064f9a 100644
--- a/app/src/components/ContributeOption.tsx
+++ b/app/src/components/ContributeOption.tsx
@@ -14,7 +14,7 @@ interface OptionProps {
const Option = ({ title, description, link, icon }: OptionProps) => {
return (
router.push(link)}
+ onPress={() => router.replace(link)}
className="flex-row items-center justify-between mx-4 border-b border-gray-200 py-6"
>
diff --git a/app/src/components/TripListScreen.tsx b/app/src/components/TripListScreen.tsx
index dae0430..2cb8164 100644
--- a/app/src/components/TripListScreen.tsx
+++ b/app/src/components/TripListScreen.tsx
@@ -28,7 +28,7 @@ export default function TripListScreen({
const router = useRouter();
function handleTripPress(trip: TripSearch) {
- router.push({
+ router.replace({
pathname: "/(search)/3-trip-overview",
params: { tripData: JSON.stringify(trip) },
});
diff --git a/app/src/components/contribute/RouteInformation.tsx b/app/src/components/contribute/RouteInformation.tsx
index 48f0dcc..1946403 100644
--- a/app/src/components/contribute/RouteInformation.tsx
+++ b/app/src/components/contribute/RouteInformation.tsx
@@ -14,24 +14,25 @@ interface RouteInformationProps {
sheetRef: React.RefObject;
handleSubmit: () => void;
setIsEditingWaypoints: React.Dispatch>;
+ isEditing: boolean;
}
export default function RouteInformation({
sheetRef,
handleSubmit,
setIsEditingWaypoints,
+ isEditing = false,
}: RouteInformationProps) {
const { route, updateRoute } = useTripCreator();
React.useEffect(() => {
- if (route.segmentMode === "Walk" && !route.segmentName.toLowerCase().includes("walk")) {
- updateRoute({ segmentName: `Walk from ${route.startLocation} to ${route.endLocation}` });
- } else if (route.segmentName.toLowerCase().includes("walk")) {
- updateRoute({ segmentName: "" });
- } else {
- updateRoute({ segmentName: route.segmentName });
+ if (route.segmentMode === "Walk" && !isEditing) {
+ const defaultWalkName = `Walk from ${route.startLocation} to ${route.endLocation}`;
+ if (!route.segmentName || route.segmentName.startsWith("Walk from")) {
+ updateRoute({ segmentName: defaultWalkName });
+ }
}
- }, [route.segmentMode]);
+ }, [route.segmentMode, route.startLocation, route.endLocation]);
const handleEditRoute = () => {
setIsEditingWaypoints(true);
@@ -88,7 +89,7 @@ export default function RouteInformation({
handleEditRoute()}>Edit Route
- handleSubmit()} />
+ handleSubmit()} />
diff --git a/app/src/components/contribute/TodaInformation.tsx b/app/src/components/contribute/TodaInformation.tsx
index b541ffb..6ad4d73 100644
--- a/app/src/components/contribute/TodaInformation.tsx
+++ b/app/src/components/contribute/TodaInformation.tsx
@@ -41,9 +41,17 @@ interface TodaStopsProps {
coordinates: Coordinates | null;
onNewStopAdded: () => void;
onFormChange?: (form: { todaName: string; color: string; landmark: string }) => void;
+ setIsSubmitting?: React.Dispatch>;
+ isSubmitting?: boolean;
}
-export default function TodaStops({ coordinates, onNewStopAdded, onFormChange }: TodaStopsProps) {
+export default function TodaStops({
+ coordinates,
+ onNewStopAdded,
+ onFormChange,
+ setIsSubmitting,
+ isSubmitting,
+}: TodaStopsProps) {
const { user } = useSession();
const [form, setForm] = useState({ todaName: "", color: "", landmark: "" });
const [dialogVisible, setDialogVisible] = useState(false);
@@ -60,8 +68,10 @@ export default function TodaStops({ coordinates, onNewStopAdded, onFormChange }:
};
const handleSubmit = async () => {
+ setIsSubmitting?.(true);
if (!coordinates) {
Alert.alert("No pins set!", "Please select a location on the map.");
+ setIsSubmitting?.(false);
return;
}
@@ -97,8 +107,10 @@ export default function TodaStops({ coordinates, onNewStopAdded, onFormChange }:
Alert.alert("Success!", "TODA stop information submitted successfully!");
resetForm();
onNewStopAdded();
+ setIsSubmitting?.(false);
} catch (error: any) {
console.error("Error submitting TODA stop:", error);
+ setIsSubmitting?.(false);
Alert.alert("Submission failed", "Something went wrong. Please try again.");
}
};
@@ -132,7 +144,7 @@ export default function TodaStops({ coordinates, onNewStopAdded, onFormChange }:
/>
-
+