Skip to content

Commit

Permalink
✨ - Israel Railways Announcements & Urgent Updates (#289)
Browse files Browse the repository at this point in the history
Co-authored-by: Guy Tepper <[email protected]>
  • Loading branch information
planecore and guytepper authored Sep 26, 2023
1 parent 0f0d0b1 commit 10d05f1
Show file tree
Hide file tree
Showing 27 changed files with 6,166 additions and 7,523 deletions.
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ android {
applicationId "com.betterrail"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 70
versionCode 71
versionName "2.2.0"
missingDimensionStrategy "store", "play"
}
Expand Down
4 changes: 2 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ buildscript {
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '21')
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '33')
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '33')
if (findProperty('android.kotlinVersion')) {
kotlinVersion = findProperty('android.kotlinVersion')
if (findProperty('android.kotlinVersion')) {
kotlinVersion = findProperty('android.kotlinVersion')
}
frescoVersion = findProperty('expo.frescoVersion') ?: '2.5.0'

Expand Down
4 changes: 3 additions & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
newArchEnabled=false

android.kotlinVersion=1.7.0
47 changes: 47 additions & 0 deletions app/components/announcements/announcement-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ViewStyle, Platform, View, TextStyle } from "react-native"
import TouchableScale from "react-native-touchable-scale"
import { fontScale, spacing, color } from "../../theme"
import { Text } from "../text/text"
import { openLink } from "../../utils/helpers/open-link"

const ANNOUNCEMENT_CARD: ViewStyle = {
minHeight: 80 * fontScale,

marginBottom: spacing[4],
paddingVertical: spacing[3],
paddingHorizontal: spacing[3],

borderRadius: Platform.select({ ios: 12, android: 8 }),
backgroundColor: Platform.select({ ios: color.tertiaryBackground, android: color.inputBackground }),
shadowColor: color.palette.black,
shadowOffset: { height: 0, width: 0 },
shadowOpacity: 0.05,
elevation: 1,
}

const TITLE_STYLE: TextStyle = {
fontFamily: "Heebo",
color: color.primary,
fontSize: 16,
marginBottom: spacing[0],
}

const BODY_STYLE: TextStyle = {
fontFamily: "Heebo",
fontSize: 14,
}

type AnnouncementCardProps = {
title?: string
body: string
link?: string
}

export const AnnouncementCard = ({ title, body, link }: AnnouncementCardProps) => (
<TouchableScale activeScale={0.98} friction={10} disabled={!link} onPress={() => openLink(link)}>
<View style={ANNOUNCEMENT_CARD}>
{title && <Text style={TITLE_STYLE}>{title}</Text>}
<Text style={BODY_STYLE}>{body}</Text>
</View>
</TouchableScale>
)
30 changes: 30 additions & 0 deletions app/components/announcements/announcements-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { View, Image, ViewStyle } from "react-native"
import { spacing, color } from "../../theme"
import { Text } from "../text/text"

const SEPARATOR_STYLE: ViewStyle = {
backgroundColor: color.separator,
height: 1,
width: "100%",
marginVertical: spacing[4],
}

type AnnouncementsHeaderProps = {
separator?: "top" | "bottom"
}

export const AnnouncementsHeader: React.FC<AnnouncementsHeaderProps> = ({ separator }) => {
return (
<>
{separator === "top" && <View style={SEPARATOR_STYLE} />}
<View style={{ width: "100%", marginBottom: spacing[4], flexDirection: "row", alignItems: "center" }}>
<Image
style={{ width: 20, height: 20, marginEnd: spacing[2], tintColor: color.text }}
source={require("../../../assets/info.png")}
/>
<Text tx="routes.updates" style={{ fontWeight: "500" }} />
</View>
{separator === "bottom" && <View style={SEPARATOR_STYLE} />}
</>
)
}
78 changes: 78 additions & 0 deletions app/components/announcements/announcements-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from "react"
import { ActivityIndicator, Button, Image, TextStyle, View, ViewStyle } from "react-native"
import { useQuery } from "react-query"
import { translate, userLocale } from "../../i18n"
import { Announcement, PopUpMessage, railApi } from "../../services/api"
import { color, spacing } from "../../theme"
import { AnnouncementCard } from "./announcement-card"
import { Text } from "../text/text"

type AnnouncementsListProps = {
updatesType: "regular" | "urgent"
relevantStationIds?: string[]
hideLoader?: boolean
}

export const AnnouncementsList: React.FC<AnnouncementsListProps> = ({ updatesType, relevantStationIds, hideLoader }) => {
const {
isLoading,
data: announcements,
isError,
refetch,
} = useQuery<Announcement[] | PopUpMessage[]>(["announcements", updatesType, relevantStationIds], () => {
return updatesType === "regular"
? railApi.getAnnouncements(userLocale, relevantStationIds)
: railApi.getPopupMessages(userLocale)
})

if (isLoading) {
if (hideLoader) {
return <></>
}

return <ActivityIndicator style={{ marginTop: spacing[5] }} size="large" />
}

if (isError) {
return <ErrorMessage refetch={refetch} />
}

return (
<>
{announcements.map((announcement, index) => {
if (updatesType === "regular") {
const a: Announcement = announcement
return <AnnouncementCard key={index} title={a.updateHeader} body={a.updateContent} link={a.updateLink} />
} else {
const a: PopUpMessage = announcement
return <AnnouncementCard key={index} title={a.title} body={a.messageBody} />
}
})}
</>
)
}

const ERORR_MESSAGE_WRAPPER: ViewStyle = {
paddingHorizontal: spacing[3],
alignItems: "center",
}

const ERORR_MESSAGE_TEXT: TextStyle = {
fontSize: 20,
fontWeight: "bold",
marginBottom: spacing[1],
}

function ErrorMessage({ refetch }) {
return (
<View style={ERORR_MESSAGE_WRAPPER}>
<Image
source={require("../../../assets/info.png")}
style={{ width: 80, height: 80, marginBottom: spacing[0], tintColor: color.error }}
/>
<Text tx="common.error" style={ERORR_MESSAGE_TEXT} />
<Text tx="routes.updatesError" style={{ textAlign: "center", marginBottom: spacing[2] }} />
<Button onPress={() => refetch()} title={translate("common.tryAgain")} />
</View>
)
}
26 changes: 26 additions & 0 deletions app/components/announcements/urgent-announcements.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react"
import { Platform, View } from "react-native"
import { useQuery } from "react-query"
import { userLocale } from "../../i18n"
import { PopUpMessage, railApi } from "../../services/api"
import { spacing } from "../../theme"
import { Text } from "../text/text"
import { Screen } from "../screen/screen"
import { useIsDarkMode } from "../../hooks"
import { AnnouncementCard } from "./announcement-card"

export const UrgentAnnouncements = () => {
const isDarkMode = useIsDarkMode()
const { data: messages } = useQuery<PopUpMessage[]>(["announcements", "urgent"], () => railApi.getPopupMessages(userLocale))

return (
<Screen unsafe statusBar={Platform.select({ ios: "light-content" })} statusBarBackgroundColor={isDarkMode ? "#000" : "#fff"}>
<Text style={{ fontSize: 48, textAlign: "center", marginVertical: spacing[4] }}>📣</Text>
<View style={{ paddingHorizontal: spacing[4] }}>
{messages?.map((m) => (
<AnnouncementCard body={m.messageBody} />
))}
</View>
</Screen>
)
}
5 changes: 3 additions & 2 deletions app/components/chip/chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ interface ChipProps {
color: keyof typeof COLORS
onPress: () => void
icon?: React.ReactNode
style?: ViewStyle
}

export function Chip({ children, color, onPress }: ChipProps) {
export function Chip({ children, color, onPress, style }: ChipProps) {
return (
<TouchableOpacity style={[CHIP_WRAPPER, { backgroundColor: COLORS[color] }]} onPress={onPress}>
<TouchableOpacity style={[CHIP_WRAPPER, { backgroundColor: COLORS[color] }, style]} onPress={onPress}>
{children}
</TouchableOpacity>
)
Expand Down
5 changes: 4 additions & 1 deletion app/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"relax": "الرجاء, لا تنسى ان تتنفس",
"save": "حفظ",
"dismiss": "إلغاء",
"areYouSure": "هل أنت متأكد؟"
"areYouSure": "هل أنت متأكد؟",
"tryAgain": "حاول ثانية",
"error": "خطأ"
},
"plan": {
"title": "تخطيط المسار",
Expand Down Expand Up @@ -94,6 +96,7 @@
"renamePromptPlaceholder": "أدخل اسمًا للطريق ...",
"delete": "إزالة من المفضلة",
"updates": "تحديثات من قطار اسرائيل",
"updatesError": "حدث خطأ أثناء تحميل التحديثات من قطارات إسرائيل",
"shortRoute": "طريق قصير",
"fromStation": "من %{stationName}",
"toStation": "إلى %{stationName}"
Expand Down
5 changes: 4 additions & 1 deletion app/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"save": "Save",
"next": "Next",
"dismiss": "Dismiss",
"areYouSure": "Are you sure?"
"areYouSure": "Are you sure?",
"tryAgain": "Try Again",
"error": "Error"
},
"plan": {
"title": "Trip Plan",
Expand Down Expand Up @@ -46,6 +48,7 @@
"sameStationsMessage": "Choose different origin and destination stations",
"noInternetConnection": "It looks like your device is not currently connected to the internet.\nPlease check your network connection and try again.",
"updates": "Updates from Israel Railways",
"updatesError": "An error occurred while attempting to load updates from Israel Railways",
"shortRoute": "Short Route"
},
"routeDetails": {
Expand Down
5 changes: 4 additions & 1 deletion app/i18n/he.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"save": "שמירה",
"next": "המשך",
"dismiss": "ביטול",
"areYouSure": "האם את/ה בטוח/ה?"
"areYouSure": "האם את/ה בטוח/ה?",
"tryAgain": "נסו שנית",
"error": "שגיאה"
},
"plan": {
"title": "תכנון מסלול",
Expand Down Expand Up @@ -46,6 +48,7 @@
"sameStationsMessage": "יש לבחור תחנות מוצא ויעד שונות",
"noInternetConnection": "נראה שהמכשיר אינו מחובר לאינטרנט.\n אנא בדקו את חיבור הרשת ונסו שוב.",
"updates": "עדכונים מרכבת ישראל",
"updatesError": "אירעה שגיאה בעת טעינת עדכונים מרכבת ישראל",
"shortRoute": "מסלול קצר"
},
"routeDetails": {
Expand Down
7 changes: 7 additions & 0 deletions app/i18n/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ i18n.translations = { he, en, ar, ru }

export type LanguageCode = "he" | "ar" | "en" | "ru"

export const railApiLocales = {
en: "English",
he: "Hebrew",
ru: "Russian",
ar: "Arabic",
}

export const isRTL = I18nManager.isRTL
export let userLocale: LanguageCode = "en"
export let dateFnsLocalization = enUS
Expand Down
5 changes: 4 additions & 1 deletion app/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"save": "Сохранить",
"next": "Продолжить",
"dismiss": "Отклонить",
"areYouSure": "Вы уверены?"
"areYouSure": "Вы уверены?",
"tryAgain": "Попробуйте еще раз",
"error": "ошибка"
},
"plan": {
"title": "Поездка",
Expand Down Expand Up @@ -44,6 +46,7 @@
"delayTime": "минут отставание",
"noInternetConnection": "Похоже, что ваше устройство в настоящее время не подключено к Интернету\nПожалуйста, проверьте соединение с сетью и попробуйте еще раз.",
"updates": "Обновления из поезда",
"updatesError": "Произошла ошибка при загрузке обновлений от Железных дорог Израиля",
"shortRoute": "короткий маршрут"
},
"routeDetails": {
Expand Down
51 changes: 51 additions & 0 deletions app/navigators/announcements/announcements-navigator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react"
import { createStackNavigator, StackScreenProps } from "@react-navigation/stack"
import { CloseButton } from "../../components"
import { AnnouncementsScreen } from "../../screens/announcements/announcements-screen"
import { color, spacing, typography } from "../../theme"
import { Platform, TextStyle } from "react-native"
import { translate } from "../../i18n"
import { UrgentAnnouncements } from "../../components/announcements/urgent-announcements"

const AnnouncementsStack = createStackNavigator()

export type AnnouncementsScreenProps = StackScreenProps<{}>

export const AnnouncementsNavigator = () => (
<AnnouncementsStack.Navigator
screenOptions={{
headerTintColor: color.primary,
headerBackTitleVisible: false,
headerStatusBarHeight: Platform.select({ ios: 10, android: 5 }),
}}
>
<AnnouncementsStack.Screen
name="announcement"
component={AnnouncementsScreen}
options={({ navigation }) => ({
title: translate("routes.updates"),
headerLeft: () => <CloseButton onPress={() => navigation.goBack()} style={{ marginRight: spacing[2] }} />,
headerTitleStyle: Platform.select({ ios: iOSTitleStyle, android: { ...androidTitleStyle, marginBottom: 10 } }),
})}
/>
<AnnouncementsStack.Screen
name="urgent"
component={UrgentAnnouncements}
options={({ navigation }) => ({
title: translate("routes.updates"),
headerLeft: () => <CloseButton onPress={() => navigation.goBack()} style={{ marginRight: spacing[2] }} />,
headerTitleStyle: Platform.select({ ios: iOSTitleStyle, android: { ...androidTitleStyle, marginBottom: 10 } }),
})}
/>
</AnnouncementsStack.Navigator>
)

const iOSTitleStyle: TextStyle = {
fontSize: 19,
fontFamily: typography.primary,
fontWeight: "400",
marginRight: 10,
marginBottom: 8,
}

const androidTitleStyle: TextStyle = { marginLeft: -18.5, marginBottom: 7.5 }
4 changes: 3 additions & 1 deletion app/navigators/root-navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { SettingsNavigator } from "./settings/settings-navigator"
import { ActiveRideNavigator } from "./active-ride/active-ride-navigator"
import { WidgetOnboardingNavigator } from "./widget-onboarding/widget-onboarding-navigator"
import { PaywallNavigator } from "./paywall/paywall-navigator"
import { LiveAnnouncement } from "../screens/live-announcement"
import { LiveAnnouncementNavigator } from "./live-activity-announcement/live-activity-announcement-stack"
import { AnnouncementsNavigator } from "./announcements/announcements-navigator"

export type RootParamList = {
mainStack: undefined
secondaryStack: undefined
settingsStack: undefined
activeRideStack: undefined
announcementsStack: undefined
paywallStack: undefined
widgetOnboardingStack: undefined
liveAnnouncementStack: undefined
Expand All @@ -33,6 +34,7 @@ const RootStack = () => {
<Stack.Group screenOptions={{ presentation: "modal" }}>
<Stack.Screen name="settingsStack" component={SettingsNavigator} />
<Stack.Screen name="activeRideStack" component={ActiveRideNavigator} />
<Stack.Screen name="announcementsStack" component={AnnouncementsNavigator} />
</Stack.Group>
<Stack.Group screenOptions={{ presentation: "fullScreenModal" }}>
<Stack.Screen name="liveAnnouncementStack" component={LiveAnnouncementNavigator} />
Expand Down
Loading

0 comments on commit 10d05f1

Please sign in to comment.