Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions app/src/app/(account)/account-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { useRouter } from "expo-router";
import { useFocusEffect } from "@react-navigation/native";

import { useAccountSettings } from "@hooks/use-account-settings";
import { useSession } from "@contexts/SessionContext";
import Header from "@components/ui/Header";
import OutlinedTextInput from "@components/ui/OutlinedTextInput";
import PrimaryButton from "@components/ui/PrimaryButton";

export default function AccountSettings() {
const router = useRouter();
const { user, username, setUsername, originalUsername, loading, handleUpdateProfile } =
useAccountSettings();
const { user, username, setUsername } = useSession();
const { originalUsername, loading, handleUpdateProfile } = useAccountSettings();

// navigation
function prevCallback() {
router.replace("/(tabs)/account");
}
Expand All @@ -25,10 +25,7 @@ export default function AccountSettings() {
return true;
};

const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction,
);
const backHandler = BackHandler.addEventListener("hardwareBackPress", backAction);

return () => backHandler.remove();
}, []),
Expand All @@ -47,7 +44,7 @@ export default function AccountSettings() {
<View className="flex-1">
<OutlinedTextInput
label="Username"
value={username}
value={username || ""}
onChangeText={setUsername}
testID="username"
/>
Expand Down
27 changes: 19 additions & 8 deletions app/src/app/(contribute)/toda-stops.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { router } from "expo-router";
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useCallback } from "react";
import { ActivityIndicator, Alert, SafeAreaView, View, Text, BackHandler } from "react-native";
import { useFocusEffect } from "@react-navigation/native";

Expand Down Expand Up @@ -32,6 +32,7 @@ export default function TodaStops() {

const [stops, setStops] = useState<StopData[]>([]);
const [loadingStops, setLoadingStops] = useState(false);
const [formSnapshot, setFormSnapshot] = useState("");

const loadStops = async () => {
setLoadingStops(true);
Expand All @@ -50,23 +51,29 @@ export default function TodaStops() {
loadStops();
}, []);

// navigation
// Navigation

const prevCallback = () => {
UnsavedChangesAlert(() => router.replace("/(tabs)/contribute"));
};
const hasChanges = !!formSnapshot;

const handleBackPress = useCallback(() => {
if (hasChanges) {
UnsavedChangesAlert(() => router.replace("/(tabs)/contribute"));
} else {
router.replace("/(tabs)/contribute");
}
}, [hasChanges]);

useFocusEffect(() => {
const backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
UnsavedChangesAlert(prevCallback);
handleBackPress();
return true;
});
return () => backHandler.remove();
});

return (
<SafeAreaView style={{ flex: 1 }}>
<Header title="Pin Toda Stops" prevCallback={prevCallback} />
<Header title="Pin Toda Stops" prevCallback={handleBackPress} />

<View>
<LocationSearchBar onSuggestionSelect={handleSuggestionSelect} onClear={handleClear} />
Expand Down Expand Up @@ -103,7 +110,11 @@ export default function TodaStops() {
<Text className="text-white">Loading stops...</Text>
</View>
)}
<TodaInformation coordinates={coordinates} onNewStopAdded={loadStops} />
<TodaInformation
coordinates={coordinates}
onNewStopAdded={loadStops}
onFormChange={(form) => setFormSnapshot(JSON.stringify(form))}
/>
</SafeAreaView>
);
}
9 changes: 6 additions & 3 deletions app/src/app/(search)/2-trip-suggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,18 @@ export default function SuggestedTrips() {
) : (
<ScrollView className="flex-1 p-4" showsVerticalScrollIndicator={false}>
{filteredTrips.map((trip) => (
<Pressable key={trip.id} onPress={() => handleSelectTrip(trip)}>
<TripPreview trip={trip} />
<Pressable
key={`${trip.id}-${filters.timeToLeave}`}
onPress={() => handleSelectTrip(trip)}
>
<TripPreview trip={trip} timeToLeave={filters.timeToLeave} />
</Pressable>
))}
</ScrollView>
)}

<View className="p-4">
<PrimaryButton label="Filter" onPress={handleOpenFilters} />
<PrimaryButton label="Filter & Sort" onPress={handleOpenFilters} />
</View>

<FilterSearch sheetRef={filterSheetRef} filters={filters} applyFilters={applyFilters} />
Expand Down
97 changes: 47 additions & 50 deletions app/src/app/(tabs)/account.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect } from "react";
import { Text, SafeAreaView, View, Alert } from "react-native";
import React from "react";
import { Text, SafeAreaView, View, Alert, ScrollView } from "react-native";

import { logoutUser } from "@services/account-service";
import { useSession } from "@contexts/SessionContext";
Expand All @@ -17,8 +17,8 @@ const tagIcon = require("@assets/option-tag.png");
const todaIcon = require("@assets/transpo-tricycle.png");

export default function Account() {
const { user } = useSession();
const { username, userRole, points, joinedDate, loading } = useAccountDetails(user?.id);
const { user, username } = useSession();
const { userRole, points, joinedDate, loading } = useAccountDetails(user?.id);

async function handleLogout() {
try {
Expand All @@ -33,14 +33,10 @@ export default function Account() {
}

function handleLogoutPress() {
Alert.alert(
"Confirm Logout",
"Do you really want to log out?",
[
{ text: "Cancel", style: "cancel" },
{ text: "Log Out", style: "destructive", onPress: () => handleLogout() },
]
);
Alert.alert("Confirm Logout", "Do you really want to log out?", [
{ text: "Cancel", style: "cancel" },
{ text: "Log Out", style: "destructive", onPress: () => handleLogout() },
]);
}

return (
Expand All @@ -57,50 +53,51 @@ export default function Account() {
joinedDate={joinedDate}
/>
)}

<View className="flex-1 p-4 justify-between">
<View>
<View className="pb-10 border-b-1">
<Text className="text-black text-2xl font-bold mx-4 mt-4">My trips</Text>
<Option
title="Bookmarked Trips"
description="Take your favorite trips or the trips you have saved for later!"
link="/(account)/bookmarked-trips"
icon={bookmarkIcon}
/>
<Option
title="Submitted Trips"
description="View the trips you have shared with other users so far!"
link="/(account)/submitted-trips"
icon={submissionIcon}
/>
<Option
title="Account Settings"
description="Change your login credentials"
link="/(account)/account-settings"
icon={accountIcon}
/>
</View>
{userRole === "moderator" && (
<View>
<Text className="text-black text-2xl font-bold mx-4 mt-2">Moderation</Text>
<ScrollView>
<View className="flex-1 p-4 justify-between">
<View className="mb-10">
<View className="pb-10 border-b-1">
<Text className="text-black text-2xl font-bold mx-4 mt-4">My trips</Text>
<Option
title="Tag routes as verified"
description="Tag user-submitted routes as verified!"
link="/(moderation)/moderate-trips-list"
icon={tagIcon}
title="Bookmarked Trips"
description="Take your favorite trips or the trips you have saved for later!"
link="/(account)/bookmarked-trips"
icon={bookmarkIcon}
/>
<Option
title="Tag TODAs as verified"
description="Tag toda stops as verified!"
link="/(moderation)/moderate-todas-list-review"
icon={todaIcon}
title="Submitted Trips"
description="View the trips you have shared with other users so far!"
link="/(account)/submitted-trips"
icon={submissionIcon}
/>
<Option
title="Account Settings"
description="Change your login credentials"
link="/(account)/account-settings"
icon={accountIcon}
/>
</View>
)}
{userRole === "moderator" && (
<View>
<Text className="text-black text-2xl font-bold mx-4 mt-2">Moderation</Text>
<Option
title="Tag routes as verified"
description="Tag user-submitted routes as verified!"
link="/(moderation)/moderate-trips-list"
icon={tagIcon}
/>
<Option
title="Tag TODAs as verified"
description="Tag toda stops as verified!"
link="/(moderation)/moderate-todas-list-review"
icon={todaIcon}
/>
</View>
)}
</View>
<SecondaryButton label="Log out" onPress={handleLogoutPress} />
</View>
<SecondaryButton label="Log out" onPress={handleLogoutPress} />
</View>
</ScrollView>
</SafeAreaView>
);
}
4 changes: 1 addition & 3 deletions app/src/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@ import SymbolSource from "@components/map/SymbolSource";
import RecentTrips from "@components/search/RecentTrips";

import { useSession } from "@contexts/SessionContext";
import { useAccountDetails } from "@hooks/use-account-details";
import { useMapView } from "@hooks/use-map-view";
import { useLiveUpdates } from "@hooks/use-live-updates";

export default function Index() {
const { user } = useSession();
const { user, username } = useSession();
if (!user) throw new Error("User must be logged in to view this page.");
const { username } = useAccountDetails(user.id);
const router = useRouter();
const { userLocation } = useMapView();
const { symbolRef, updateLiveStatus } = useLiveUpdates("box", 10);
Expand Down
20 changes: 11 additions & 9 deletions app/src/components/contribute/RouteInformation.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { Text, View } from "react-native";
import { ScrollView } from "react-native-gesture-handler";

import OutlinedTextInput from "@components/ui/OutlinedTextInput";
import PrimaryButton from "@components/ui/PrimaryButton";
Expand All @@ -21,12 +22,11 @@ export default function RouteInformation({
setIsEditingWaypoints,
}: RouteInformationProps) {
const { route, updateRoute } = useTripCreator();
const snapPoints = ["48%"];

React.useEffect(() => {
if (route.segmentMode === "Walk" && route.segmentName !== "Walk") {
updateRoute({ segmentName: "Walk" });
} else if (route.segmentName === "Walk") {
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 });
Expand All @@ -43,15 +43,17 @@ export default function RouteInformation({
<BottomSheetScrollView className="flex flex-col mx-4 pb-20">
<Text className="text-2xl font-bold mb-4">Route Information</Text>
<View className="flex flex-col gap-2">
<TransportModeInput
value={route.segmentMode}
onChange={(segmentMode) => updateRoute({ segmentMode })}
/>
<ScrollView horizontal scrollEnabled showsHorizontalScrollIndicator={true}>
<TransportModeInput
value={route.segmentMode}
onChange={(segmentMode) => updateRoute({ segmentMode })}
/>
</ScrollView>
<View className="flex-row gap-3">
<View className="flex-1">
<OutlinedTextInput
label="Route Name"
value={route.segmentName}
value={route.segmentName.startsWith("Walk") ? "Walk" : route.segmentName}
editable={route.segmentMode !== "Walk"}
disabled={route.segmentMode === "Walk"}
onChangeText={(segmentName) => updateRoute({ segmentName })}
Expand Down
8 changes: 6 additions & 2 deletions app/src/components/contribute/TodaInformation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,25 @@ const TODA_COLORS = [
"Violet",
"Black",
"White",
"Pink",
"None",
];

interface TodaStopsProps {
coordinates: Coordinates | null;
onNewStopAdded: () => void;
onFormChange?: (form: { todaName: string; color: string; landmark: string }) => void;
}

export default function TodaStops({ coordinates, onNewStopAdded }: TodaStopsProps) {
export default function TodaStops({ coordinates, onNewStopAdded, onFormChange }: TodaStopsProps) {
const { user } = useSession();
const [form, setForm] = useState({ todaName: "", color: "", landmark: "" });
const [dialogVisible, setDialogVisible] = useState(false);

const updateForm = (key: keyof typeof form, value: string) => {
setForm((prev) => ({ ...prev, [key]: value }));
const newForm = { ...form, [key]: value };
setForm(newForm);
onFormChange?.(newForm);
};

const resetForm = () => {
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/contribute/TransportModeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function TransportModeInput({ value, onChange }: Props) {
};

return (
<View className="flex flex-col px-5 gap-3">
<View className="flex flex-col gap-3">
<View className="flex flex-row gap-2 justify-center">
{TRANSPORTATION_MODES.map((mode) => (
<TouchableOpacity
Expand Down
10 changes: 6 additions & 4 deletions app/src/components/search/FilterSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ interface Props {
export default function FilterSearch({ sheetRef, filters, applyFilters }: Props) {
const snapPoints = ["60%"];

const [timeToLeave, setTimeToLeave] = useState(new Date());
const [showTimePicker, setShowTimePicker] = useState(false);

const [timeToLeave, setTimeToLeave] = useState(filters.timeToLeave);
const [sortBy, setSortBy] = useState(filters.sortBy);
const [selectedModes, setSelectedModes] = useState(filters.transportModes);

Expand All @@ -34,9 +34,11 @@ export default function FilterSearch({ sheetRef, filters, applyFilters }: Props)
selectedModes.length === filters.transportModes.length &&
selectedModes.every((m) => filters.transportModes.includes(m));

const isUnchanged = isSortSame && areModesSame;
const isTimeSame = timeToLeave.getTime() === filters.timeToLeave.getTime();

const isUnchanged = isSortSame && areModesSame && isTimeSame;
if (!isUnchanged) {
applyFilters({ sortBy, transportModes: selectedModes });
applyFilters({ timeToLeave, sortBy, transportModes: selectedModes });
}
sheetRef.current?.close();
};
Expand All @@ -51,7 +53,7 @@ export default function FilterSearch({ sheetRef, filters, applyFilters }: Props)
>
<BottomSheetView className="flex flex-col px-5 pb-8 gap-3">
<View className="flex-row items-center gap-2">
<Text className="text-xl font-bold">Filter trips</Text>
<Text className="text-xl font-bold">Filter & sort trips</Text>
</View>

<View>
Expand Down
5 changes: 4 additions & 1 deletion app/src/components/search/TripSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ export default function TripSummary({
<View className="flex flex-row gap-5 items-center">
{currentUserId && <VotingBar trip={trip} userId={currentUserId} />}
<TouchableOpacity onPress={() => handleCommentPress(trip.id)} hitSlop={10}>
<Image source={comment} style={{ width: 12, height: 12 }} resizeMode="contain" />
<View className="flex flex-row gap-1 items-center justify-center">
<Image source={comment} style={{ width: 11, height: 11 }} resizeMode="contain" />
<Text className="text-sm">{trip.comments}</Text>
</View>
</TouchableOpacity>
</View>
</View>
Expand Down
Loading
Loading