Skip to content

Commit

Permalink
🕒 - Show entire train route (#414)
Browse files Browse the repository at this point in the history
  • Loading branch information
planecore authored Mar 8, 2025
1 parent a2dc7e4 commit adc2312
Show file tree
Hide file tree
Showing 24 changed files with 227 additions and 92 deletions.
25 changes: 0 additions & 25 deletions app/components/calendar-icon/calendar-icon.tsx

This file was deleted.

1 change: 0 additions & 1 deletion app/components/calendar-icon/index.ts

This file was deleted.

1 change: 1 addition & 0 deletions app/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export * from "./date-picker-modal"
export * from "./list"
export * from "./sheets"
export * from "./chip"
export * from "./menu-icon"
1 change: 1 addition & 0 deletions app/components/menu-icon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./menu-icon"
22 changes: 22 additions & 0 deletions app/components/menu-icon/menu-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { TouchableOpacity, Image, type ImageStyle, type ViewStyle } from "react-native"

const ICON_STYLE: ImageStyle = {
width: 24,
height: 24,
resizeMode: "contain",
tintColor: "lightgrey",
opacity: 0.9,
}
const CONTAINER: ViewStyle = {
justifyContent: "center",
}

export const MenuIcon = function CalendarIcon(props: { style?: ViewStyle }) {
const { style } = props

return (
<TouchableOpacity style={[CONTAINER, style]}>
<Image source={require("../../../assets/ellipsis.png")} style={[ICON_STYLE]} />
</TouchableOpacity>
)
}
43 changes: 38 additions & 5 deletions app/components/route-details-header/route-details-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import { useNavigation } from "@react-navigation/native"
import { observer } from "mobx-react-lite"
import LinearGradient from "react-native-linear-gradient"
import { color, isDarkMode, spacing } from "../../theme"
import { Text, StarIcon } from "../"
import { Text, StarIcon, MenuIcon } from "../"
import HapticFeedback from "react-native-haptic-feedback"
import { stationsObject, stationLocale } from "../../data/stations"
import { isRTL, translate } from "../../i18n"
import { useStores } from "../../models"
import * as Burnt from "burnt"
import * as Calendar from "expo-calendar"
import { CalendarIcon } from "../calendar-icon/calendar-icon"
import type { RouteItem } from "../../services/api"
import type { BottomSheetMethods } from "@gorhom/bottom-sheet/lib/typescript/types"
import ContextMenu from "react-native-context-menu-view"

const AnimatedTouchable = RNAnimated.createAnimatedComponent(TouchableScale)
const arrowIcon = require("../../../assets/arrow-left.png")
Expand Down Expand Up @@ -96,10 +96,13 @@ export interface RouteDetailsHeaderProps {
style?: ViewStyle
eventConfig?: Calendar.Event
stationHoursSheetRef?: React.MutableRefObject<BottomSheetMethods>
showEntireRoute?: boolean
setShowEntireRoute?: React.Dispatch<React.SetStateAction<boolean>>
}

export const RouteDetailsHeader = observer(function RouteDetailsHeader(props: RouteDetailsHeaderProps) {
const { routeItem, originId, destinationId, screenName, style, stationHoursSheetRef } = props
const { routeItem, originId, destinationId, screenName, style, stationHoursSheetRef, showEntireRoute, setShowEntireRoute } =
props
const { favoriteRoutes, routePlan } = useStores()
const navigation = useNavigation()
const routeEditDisabled = screenName !== "routeList"
Expand Down Expand Up @@ -187,7 +190,27 @@ export const RouteDetailsHeader = observer(function RouteDetailsHeader(props: Ro

const renderHeaderRight = useCallback(() => {
if (screenName === "routeDetails") {
return <CalendarIcon onPress={addToCalendar} />
return (
<ContextMenu
dropdownMenuMode
actions={[
{ title: translate("routeDetails.addToCalendar"), systemIcon: "calendar" },
{
title: translate(showEntireRoute ? "routeDetails.hideAllStations" : "routeDetails.showAllStations"),
systemIcon: showEntireRoute ? "rectangle.compress.vertical" : "rectangle.expand.vertical",
},
]}
onPress={(event) => {
if (event.nativeEvent.index === 0) {
addToCalendar()
} else if (event.nativeEvent.index === 1) {
setShowEntireRoute((prev) => !prev)
}
}}
>
<MenuIcon />
</ContextMenu>
)
}

const handleFavoritePress = () => {
Expand Down Expand Up @@ -217,7 +240,17 @@ export const RouteDetailsHeader = observer(function RouteDetailsHeader(props: Ro
)}
</View>
)
}, [screenName, addToCalendar, isFavorite, routeId, favoriteRoutes, originId, destinationId])
}, [
screenName,
addToCalendar,
isFavorite,
routeId,
favoriteRoutes,
originId,
destinationId,
showEntireRoute,
setShowEntireRoute,
])

useLayoutEffect(() => {
if (screenName === "activeRide") return
Expand Down
2 changes: 2 additions & 0 deletions app/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"routeWarning": "تحذير الطريق الطويل",
"routeWarningText": "هناك طرق قطار أقصر حول وقت المغادرة.",
"addToCalendar": "إضافة إلى التقويم",
"showAllStations": "عرض جميع محطات المسار",
"hideAllStations": "إخفاء جميع محطات المسار",
"noCalendarAccessTitle": "تم تعطيل الوصول إلى التقويم",
"noCalendarAccessMessage": "للإضافة إلى تقويم المسار ، يرجى فتح إعدادات جهازك وتمكين الوصول إلى التقويم."
},
Expand Down
2 changes: 2 additions & 0 deletions app/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
"routeWarning": "Long Route Warning",
"routeWarningText": "There are shorter train routes around the departure time.",
"addToCalendar": "Add to Calendar",
"showAllStations": "Show all route stations",
"hideAllStations": "Hide all route stations",
"noCalendarAccessTitle": "Calendar access disabled",
"noCalendarAccessMessage": "To add the ride to your calendar, open your device settings and grant access to your calendar."
},
Expand Down
2 changes: 2 additions & 0 deletions app/i18n/he.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
"routeWarning": "רכבת מאספת",
"routeWarningText": "קיימים מסלולי נסיעה קצרים יותר סביב שעת היציאה הנבחרת",
"addToCalendar": "הוספה ליומן",
"showAllStations": "הצג את כל תחנות המסלול",
"hideAllStations": "הסתר את כל תחנות המסלול",
"noCalendarAccessTitle": "גישה ללוח שנה כבויה",
"noCalendarAccessMessage": "יש לאפשר גישה ללוח שנה בהגדרות המכשיר על מנת להוסיף נסיעה ליומן"
},
Expand Down
2 changes: 2 additions & 0 deletions app/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
"routeWarning": "Предупреждение о длинном маршруте",
"routeWarningText": "Во время отправления есть более короткие маршруты поездов.",
"addToCalendar": "Добавить в календарь",
"showAllStations": "Показать все станции маршрута",
"hideAllStations": "Скрыть все станции маршрута",
"noCalendarAccessTitle": "Доступ к календарю отключен",
"noCalendarAccessMessage": "Чтобы добавить маршрут в календарь, откройте настройки устройства и включите доступ к календарю."
},
Expand Down
2 changes: 0 additions & 2 deletions app/screens/route-details/components/route-station-circle.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { ViewStyle } from "react-native"
import Animated from "react-native-reanimated"
import { ROUTE_LINE_STATE_COLORS } from "./route-line"
import { useRideProgressAnimation } from "../../../hooks/use-ride-progress"
import { useAnimatedBackground, useAnimatedBorder } from "../../../hooks/animations/use-animated-color-props"
import { RouteElementStateType, useRouteColors } from "./use-route-colors"

Expand Down
33 changes: 24 additions & 9 deletions app/screens/route-details/components/route-stop-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React from "react"
import { View, ViewStyle, TextStyle } from "react-native"
import { Text } from "../../../components"
import { color, spacing } from "../../../theme"
import { ROUTE_LINE_STATE_COLORS, RouteLine } from "./route-line"
import { RouteLine } from "./route-line"
import { RouteStationCircle } from "./route-station-circle"
import { RouteLineStateType } from "../route-details-screen"
import { RouteElementStateType } from "./use-route-colors"

// #region styles
const ROUTE_STOP_WRAPPER: ViewStyle = {
Expand Down Expand Up @@ -32,6 +32,10 @@ const ROUTE_STOP_TIME_DELAYED: TextStyle = {
opacity: 0.5,
}

const OUTSIDE_JOURNEY_TEXT: TextStyle = {
color: color.dim,
}

// #endregion

type RouteStopCardProps = {
Expand All @@ -43,35 +47,46 @@ type RouteStopCardProps = {
delayedTime?: string

style?: ViewStyle
topLineState: RouteLineStateType
bottomLineState: RouteLineStateType
topLineState: RouteElementStateType
bottomLineState: RouteElementStateType
/**
* Indicates if this station is outside the user's selected journey
* (before origin or after destination)
*/
isOutsideUserJourney?: boolean
}

export const RouteStopCard = (props: RouteStopCardProps) => {
const { stationName, stopTime, delayedTime, topLineState, bottomLineState, style } = props
const { stationName, stopTime, delayedTime, topLineState, bottomLineState, style, isOutsideUserJourney } = props

return (
<View style={[ROUTE_STOP_WRAPPER, style]}>
<View style={ROUTE_STOP_DETAILS}>
<View style={{ flex: 0.265, alignItems: "flex-end" }}>
<Text style={[ROUTE_STOP_TIME, delayedTime && ROUTE_STOP_TIME_DELAYED]} maxFontSizeMultiplier={1.2}>
<Text
style={[ROUTE_STOP_TIME, delayedTime && ROUTE_STOP_TIME_DELAYED, isOutsideUserJourney && OUTSIDE_JOURNEY_TEXT]}
maxFontSizeMultiplier={1.2}
>
{stopTime}
</Text>
{delayedTime && (
<Text style={ROUTE_STOP_TIME} maxFontSizeMultiplier={1.2}>
<Text style={[ROUTE_STOP_TIME, isOutsideUserJourney && OUTSIDE_JOURNEY_TEXT]} maxFontSizeMultiplier={1.2}>
{delayedTime}
</Text>
)}
</View>

<View style={{ flex: 0.2, alignItems: "center" }}>
<RouteLine state={topLineState} />
<RouteStationCircle state={topLineState} />
<RouteStationCircle state={topLineState === "hidden" ? "idle" : topLineState} />
<RouteLine state={bottomLineState} />
</View>

<View style={{ flex: 0.55, right: 15 }}>
<Text style={{ fontWeight: "600", fontSize: 15, marginStart: spacing[3] }} maxFontSizeMultiplier={1.2}>
<Text
style={[{ fontWeight: "600", fontSize: 15, marginStart: spacing[3] }, isOutsideUserJourney && OUTSIDE_JOURNEY_TEXT]}
maxFontSizeMultiplier={1.2}
>
{stationName}
</Text>
</View>
Expand Down
4 changes: 3 additions & 1 deletion app/screens/route-details/components/use-route-colors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import { useColorScheme } from "react-native"
/**
* The station / line state
*/
export type RouteElementStateType = "idle" | "inProgress" | "passed"
export type RouteElementStateType = "idle" | "inProgress" | "passed" | "hidden"

export const ROUTE_LINE_STATE_COLORS = {
idle: { light: "#3C3C434A", dark: "#54545899" },
passed: { light: "#5CAB61", dark: "#475A42" },
inProgress: { light: "#FF9500FF", dark: "#E08A00" },
hidden: { light: "transparent", dark: "transparent" },
}

export const ROUTE_STOP_CIRCLE_COLORS = {
passed: { light: "#B0E1A5", dark: "#698661" },
inProgress: { light: "#F5AF00", dark: "#F5AF00" },
idle: { light: "#f2f2f7", dark: "#1c1c1e" },
hidden: { light: "transparent", dark: "transparent" },
}

/**
Expand Down
Loading

0 comments on commit adc2312

Please sign in to comment.