diff --git a/android/app/build.gradle b/android/app/build.gradle index 332a458a3..7b01f9b7f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -127,8 +127,8 @@ android { } prod { dimension "environment" - versionCode 70701000 - versionName "7.7.1" + versionCode 70702000 + versionName "7.7.2" } } }// Apply static values from `gradle.properties` to the `android.packagingOptions` diff --git a/android/wearable/build.gradle.kts b/android/wearable/build.gradle.kts index 104d5b2e3..56ab86491 100644 --- a/android/wearable/build.gradle.kts +++ b/android/wearable/build.gradle.kts @@ -39,8 +39,8 @@ android { } create("prod") { dimension = "environment" - versionCode = 70701001 - versionName = "7.7.1" + versionCode = 70702001 + versionName = "7.7.2" } } diff --git a/index.js b/index.js index b482847e8..527669bf3 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,9 @@ import * as TaskManager from 'expo-task-manager' +TaskManager.unregisterAllTasksAsync().catch(console.error) + +let lastTimestamp = 0 + if (!TaskManager.isTaskDefined(LOCATION_TASK_NAME)) { TaskManager.defineTask(LOCATION_TASK_NAME, ({ data, error }) => { if (error) { @@ -7,7 +11,11 @@ if (!TaskManager.isTaskDefined(LOCATION_TASK_NAME)) { return } - setLocation(data.locations[0]) + const latestTimestamp = data.locations[0]?.timestamp ?? 0 + if (lastTimestamp < latestTimestamp) { + setLocation(data.locations[0]) + lastTimestamp = latestTimestamp + } }) } @@ -18,8 +26,6 @@ import App from './src' import { LOCATION_TASK_NAME } from './src/constants' import { setLocation } from './src/hooks/useLocationStore' -TaskManager.unregisterAllTasksAsync().catch(console.error) - // registerRootComponent calls AppRegistry.registerComponent('main', () => App); // It also ensures that whether you load the app in the Expo client or in a native build, // the environment is set up appropriately diff --git a/ios/TrainLCD.xcodeproj/project.pbxproj b/ios/TrainLCD.xcodeproj/project.pbxproj index 7a9c93c63..71f90b7fd 100644 --- a/ios/TrainLCD.xcodeproj/project.pbxproj +++ b/ios/TrainLCD.xcodeproj/project.pbxproj @@ -1451,7 +1451,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 7.7.1; + MARKETING_VERSION = 7.7.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1489,7 +1489,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 7.7.1; + MARKETING_VERSION = 7.7.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -2131,7 +2131,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 7.7.1; + MARKETING_VERSION = 7.7.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -2175,7 +2175,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 7.7.1; + MARKETING_VERSION = 7.7.2; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = me.tinykitten.trainlcd.watchkitapp.watchkitextension; @@ -2213,7 +2213,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; IBSC_MODULE = WatchApp_Extension; INFOPLIST_FILE = WatchApp/Schemes/Prod/Info.plist; - MARKETING_VERSION = 7.7.1; + MARKETING_VERSION = 7.7.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -2253,7 +2253,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; IBSC_MODULE = WatchApp_Extension; INFOPLIST_FILE = WatchApp/Schemes/Prod/Info.plist; - MARKETING_VERSION = 7.7.1; + MARKETING_VERSION = 7.7.2; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = me.tinykitten.trainlcd.watchkitapp; diff --git a/src/components/LineBoardWest.tsx b/src/components/LineBoardWest.tsx index 1ffe1f55c..faee8764a 100644 --- a/src/components/LineBoardWest.tsx +++ b/src/components/LineBoardWest.tsx @@ -434,6 +434,8 @@ const StationNameCell: React.FC = ({ const LineBoardWest: React.FC = ({ stations, lineColors }: Props) => { const { selectedLine } = useRecoilValue(lineState) + const { arrived, approaching } = useRecoilValue(stationState) + const isPassing = useIsPassing() const currentLine = useCurrentLine() @@ -448,11 +450,11 @@ const LineBoardWest: React.FC = ({ stations, lineColors }: Props) => { key={s.groupId} station={s} stations={stations} - arrived={!isPassing} + arrived={!isPassing && !approaching && arrived} index={i} /> ), - [isPassing, stations] + [approaching, arrived, isPassing, stations] ) const emptyArray = useMemo( diff --git a/src/hooks/useSavedRoutes.ts b/src/hooks/useSavedRoutes.ts index a109daac9..526a0c9c3 100644 --- a/src/hooks/useSavedRoutes.ts +++ b/src/hooks/useSavedRoutes.ts @@ -1,5 +1,6 @@ import firestore from '@react-native-firebase/firestore' -import { useCallback, useEffect, useState } from 'react' +import useSWR from 'swr' +import useSWRMutation from 'swr/dist/mutation' import { GetStationByIdListRequest } from '../../gen/proto/stationapi_pb' import { grpcClient } from '../lib/grpc' import { SavedRoute } from '../models/SavedRoute' @@ -7,37 +8,48 @@ import useCachedInitAnonymousUser from './useCachedAnonymousUser' export const useSavedRoutes = () => { useCachedInitAnonymousUser() - const [routes, setRoutes] = useState([]) - const [loading, setLoading] = useState(false) - useEffect(() => { - const fetchRoutesAsync = async () => { - setLoading(true) - const routesSnapshot = await firestore() - .collection('uploadedCommunityRoutes') - .orderBy('createdAt', 'desc') - .get() - const routes = routesSnapshot.docs.map((doc) => ({ - id: doc.id, - ...doc.data(), + const { + data: routes, + isLoading: isRoutesLoading, + error: fetchRoutesError, + } = useSWR('/firestore/uploadedCommunityRoutes', async () => { + const routesSnapshot = await firestore() + .collection('uploadedCommunityRoutes') + .orderBy('createdAt', 'desc') + .get() + + return routesSnapshot.docs.map((doc) => ({ + id: doc.id, + ...doc.data(), + })) as SavedRoute[] + }) + + const { + isMutating: isStationsLoading, + error: fetchStationsError, + trigger: fetchStationsByRoute, + } = useSWRMutation( + '/app.trainlcd.grpc/GetStationByIdListRequest', + async (_, { arg: route }: { arg: SavedRoute }) => { + const req = new GetStationByIdListRequest() + req.ids = route.stations.map((sta) => sta.id) + const res = await grpcClient.getStationByIdList(req, {}) + const stations = res?.stations ?? [] + + return stations.map((sta) => ({ + ...sta, + stopCondition: route.stations.find((rs) => rs.id === sta.id) + ?.stopCondition, + trainType: route.trainType, })) - setRoutes(routes as SavedRoute[]) - setLoading(false) } - fetchRoutesAsync() - }, []) - - const fetchStationsByRoute = useCallback(async (route: SavedRoute) => { - const req = new GetStationByIdListRequest() - req.ids = route.stations.map((sta) => sta.id) - const res = await grpcClient.getStationByIdList(req, {}) - const stations = res?.stations ?? [] - return stations.map((sta, idx) => ({ - ...sta, - stopCondition: route.stations[idx].stopCondition, - trainType: route.trainType, - })) - }, []) + ) - return { routes, loading, fetchStationsByRoute } + return { + routes, + loading: isRoutesLoading || isStationsLoading, + error: fetchRoutesError || fetchStationsError, + fetchStationsByRoute, + } } diff --git a/src/hooks/useStartBackgroundLocationUpdates.ts b/src/hooks/useStartBackgroundLocationUpdates.ts index 18a83012c..6738f7364 100644 --- a/src/hooks/useStartBackgroundLocationUpdates.ts +++ b/src/hooks/useStartBackgroundLocationUpdates.ts @@ -10,15 +10,26 @@ export const useStartBackgroundLocationUpdates = () => { if (autoModeEnabled) { return } - Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, { - accuracy: Location.Accuracy.High, - distanceInterval: 100, - foregroundService: { - notificationTitle: translate('bgAlertTitle'), - notificationBody: translate('bgAlertContent'), - killServiceOnDestroy: true, - }, - }) + // eslint-disable-next-line @typescript-eslint/no-extra-semi + ;(async () => { + if ( + !(await Location.hasStartedLocationUpdatesAsync(LOCATION_TASK_NAME)) + ) { + Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, { + // NOTE: BestForNavigationにしたら暴走時のCPU使用率が50%ほど低下した + accuracy: Location.Accuracy.BestForNavigation, + // NOTE: マップマッチが勝手に行われると電車での経路と大きく異なることがあるはずなので + // OtherNavigationは必須 + activityType: Location.ActivityType.OtherNavigation, + distanceInterval: 100, + foregroundService: { + notificationTitle: translate('bgAlertTitle'), + notificationBody: translate('bgAlertContent'), + killServiceOnDestroy: true, + }, + }) + } + })() return () => { Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME).catch(console.debug)