From 4398f9970f01dab624a1b9b0257cc642bc3ce451 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Thu, 26 Sep 2024 02:51:00 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=E4=B8=80=E6=97=A6=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=96=E3=82=A2=E3=82=AF=E3=83=86=E3=82=A3=E3=83=93=E3=83=86?= =?UTF-8?q?=E3=82=A3=E3=81=8B=E3=82=89=E9=80=9A=E9=81=8E=E3=82=B3=E3=83=BC?= =?UTF-8?q?=E3=83=89=E3=82=92=E6=B6=88=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WidgetConfigurationExtension.swift | 2 +- .../LiveActivity/LiveActivityModule.swift | 4 +- .../RideSessionActivity.swift | 257 +++++++++--------- .../RideSessionAttributes.swift | 10 +- src/hooks/useStationNumberIndexFunc.ts | 2 +- src/hooks/useUpdateLiveActivities.ts | 159 +++++------ src/utils/isPass.ts | 12 +- src/utils/native/ios/liveActivityModule.ts | 6 +- 8 files changed, 217 insertions(+), 235 deletions(-) diff --git a/ios/Extensions/WidgetConfigurationExtension.swift b/ios/Extensions/WidgetConfigurationExtension.swift index ef94e79e2..b805b7d95 100644 --- a/ios/Extensions/WidgetConfigurationExtension.swift +++ b/ios/Extensions/WidgetConfigurationExtension.swift @@ -14,7 +14,7 @@ extension WidgetConfiguration func supplementalActivityFamiliesIfAvailable() -> some WidgetConfiguration { if #available(iOSApplicationExtension 18.0, *) { - return self.supplementalActivityFamilies([ActivityFamily.small, ActivityFamily.medium]) + return self.supplementalActivityFamilies([ActivityFamily.small]) } else { return self } diff --git a/ios/Modules/LiveActivity/LiveActivityModule.swift b/ios/Modules/LiveActivity/LiveActivityModule.swift index ff3afddc2..6e9b58082 100644 --- a/ios/Modules/LiveActivity/LiveActivityModule.swift +++ b/ios/Modules/LiveActivity/LiveActivityModule.swift @@ -16,12 +16,10 @@ class LiveActivityModule: NSObject { stationNumber: state["stationNumber"] as? String ?? "", nextStationNumber: state["nextStationNumber"] as? String ?? "", approaching: state["approaching"] as? Bool ?? false, - stopping: state["stopping"] as? Bool ?? true, + stopped: state["stopped"] as? Bool ?? false, boundStationName: state["boundStationName"] as? String ?? "", boundStationNumber: state["boundStationNumber"] as? String ?? "", trainTypeName: state["trainTypeName"] as? String ?? "", - passingStationName: state["passingStationName"] as? String ?? "", - passingStationNumber: state["passingStationNumber"] as? String ?? "", isLoopLine: state["isLoopLine"] as? Bool ?? false, isNextLastStop: state["isNextLastStop"] as? Bool ?? false, lineColor: state["lineColor"] as? String ?? "#000000", diff --git a/ios/RideSessionActivity/RideSessionActivity.swift b/ios/RideSessionActivity/RideSessionActivity.swift index ad28ac7e5..d04e1c8bf 100644 --- a/ios/RideSessionActivity/RideSessionActivity.swift +++ b/ios/RideSessionActivity/RideSessionActivity.swift @@ -6,30 +6,29 @@ // Copyright © 2022 Facebook. All rights reserved. // -import WidgetKit import SwiftUI +import WidgetKit func getStationNumberText(_ stationNumber: String) -> String { - if (stationNumber.isEmpty) { + if stationNumber.isEmpty { return "" } return "(\(stationNumber))" } -func getRunningStateText(approaching: Bool, stopping: Bool, isNextLastStop: Bool, isPassing: Bool = false) -> String { - if (isPassing){ - return NSLocalizedString("pass", comment: "") - } - if (approaching) { - if (isNextLastStop) { +func getRunningStateText( + approaching: Bool, stopped: Bool, isNextLastStop: Bool +) -> String { + if approaching { + if isNextLastStop { return NSLocalizedString("soonLast", comment: "") } return NSLocalizedString("soon", comment: "") } - if (stopping) { + if stopped { return NSLocalizedString("stop", comment: "") } - if (isNextLastStop) { + if isNextLastStop { return NSLocalizedString("nextLast", comment: "") } return NSLocalizedString("next", comment: "") @@ -40,10 +39,10 @@ struct RideSessionWidget: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: RideSessionAttributes.self) { context in LockScreenLiveActivityView(context: context) - } dynamicIsland: {context in + } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { - if (context.state.stopping) { + if context.state.stopped { EmptyView() } else { VStack(alignment: .center) { @@ -51,7 +50,7 @@ struct RideSessionWidget: Widget { .font(.callout) .opacity(0.5) .multilineTextAlignment(.center) - if (!context.state.stationNumber.isEmpty) { + if !context.state.stationNumber.isEmpty { Text(getStationNumberText(context.state.stationNumber)) .font(.caption) .opacity(0.5) @@ -60,9 +59,9 @@ struct RideSessionWidget: Widget { } } } - + DynamicIslandExpandedRegion(.trailing) { - if (context.state.stopping) { + if context.state.stopped { EmptyView() } else { VStack(alignment: .center) { @@ -70,7 +69,7 @@ struct RideSessionWidget: Widget { .font(.callout) .bold() .multilineTextAlignment(.center) - if (!context.state.nextStationNumber.isEmpty) { + if !context.state.nextStationNumber.isEmpty { Text(getStationNumberText(context.state.nextStationNumber)) .font(.caption) .bold() @@ -79,22 +78,24 @@ struct RideSessionWidget: Widget { } } } - + DynamicIslandExpandedRegion(.center) { - if (context.state.stopping) { - VStack(alignment: .center ) { - Text(getRunningStateText( - approaching: context.state.approaching, - stopping: context.state.stopping, - isNextLastStop: context.state.isNextLastStop - )) + if context.state.stopped { + VStack(alignment: .center) { + Text( + getRunningStateText( + approaching: context.state.approaching, + stopped: context.state.stopped, + isNextLastStop: context.state.isNextLastStop + ) + ) .bold() .font(.caption) .multilineTextAlignment(.center) Text(context.state.stationName) .bold() .multilineTextAlignment(.center) - if (!context.state.stationNumber.isEmpty) { + if !context.state.stationNumber.isEmpty { Text(getStationNumberText(context.state.stationNumber)) .font(.caption) .bold() @@ -103,34 +104,22 @@ struct RideSessionWidget: Widget { } } else { VStack(alignment: .center) { - Text(getRunningStateText( - approaching: context.state.approaching, - stopping: context.state.stopping, - isNextLastStop: context.state.isNextLastStop - )) + Text( + getRunningStateText( + approaching: context.state.approaching, + stopped: context.state.stopped, + isNextLastStop: context.state.isNextLastStop + ) + ) .bold() .font(.caption) .multilineTextAlignment(.center) Image(systemName: "arrow.right") .foregroundColor(.white) - if (!context.state.passingStationName.isEmpty) { - HStack { - Text( - String( - format: NSLocalizedString("passingStation", comment: ""), - "\(context.state.passingStationName)\(getStationNumberText(context.state.passingStationNumber))" - ) - ) - .font(.caption) - .bold() - .multilineTextAlignment(.center) - } - .padding(.top, 4) - } } } } - + DynamicIslandExpandedRegion(.bottom) { EmptyView() } @@ -138,22 +127,22 @@ struct RideSessionWidget: Widget { Text( getRunningStateText( approaching: context.state.approaching, - stopping: context.state.stopping, - isNextLastStop: context.state.isNextLastStop, - isPassing: !context.state.passingStationName.isEmpty - )) + stopped: context.state.stopped, + isNextLastStop: context.state.isNextLastStop + ) + ) .multilineTextAlignment(.trailing) .font(.caption) .bold() .padding(.leading, 8) } compactTrailing: { - if (context.state.stopping) { + if context.state.stopped { VStack { Text(context.state.stationName) .font(.caption2) .bold() .multilineTextAlignment(.center) - if (!context.state.stationNumber.isEmpty) { + if !context.state.stationNumber.isEmpty { Text(getStationNumberText(context.state.stationNumber)) .font(.caption2) .bold() @@ -170,15 +159,20 @@ struct RideSessionWidget: Widget { .padding(.trailing, 8) } else { VStack { - Text(context.state.passingStationName.isEmpty ? context.state.nextStationName : context.state.passingStationName) + Text( + context.state.nextStationName + ) + .font(.caption2) + .bold() + .multilineTextAlignment(.center) + if !context.state.nextStationNumber.isEmpty { + Text( + getStationNumberText( + context.state.nextStationNumber) + ) .font(.caption2) .bold() .multilineTextAlignment(.center) - if (!context.state.nextStationNumber.isEmpty || !context.state.passingStationNumber.isEmpty) { - Text(getStationNumberText(context.state.passingStationName.isEmpty ? context.state.nextStationNumber : context.state.passingStationNumber)) - .font(.caption2) - .bold() - .multilineTextAlignment(.center) } } .frame( @@ -206,13 +200,15 @@ struct LockScreenLiveActivityContentView: View { var body: some View { VStack { Group { - if (context.state.stopping) { + if context.state.stopped { VStack { - Text(getRunningStateText( - approaching: context.state.approaching, - stopping: context.state.stopping, - isNextLastStop: context.state.isNextLastStop - ) ) + Text( + getRunningStateText( + approaching: context.state.approaching, + stopped: context.state.stopped, + isNextLastStop: context.state.isNextLastStop + ) + ) .bold() .font(.caption) .multilineTextAlignment(.center) @@ -222,7 +218,7 @@ struct LockScreenLiveActivityContentView: View { .bold() .multilineTextAlignment(.center) .foregroundColor(.accentColor) - if (!context.state.stationNumber.isEmpty) { + if !context.state.stationNumber.isEmpty { Text(getStationNumberText(context.state.stationNumber)) .font(.caption) .bold() @@ -235,11 +231,13 @@ struct LockScreenLiveActivityContentView: View { .padding(8) } else { VStack { - Text(getRunningStateText( - approaching: context.state.approaching, - stopping: context.state.stopping, - isNextLastStop: context.state.isNextLastStop - )) + Text( + getRunningStateText( + approaching: context.state.approaching, + stopped: context.state.stopped, + isNextLastStop: context.state.isNextLastStop + ) + ) .font(.caption) .bold() .multilineTextAlignment(.center) @@ -250,7 +248,7 @@ struct LockScreenLiveActivityContentView: View { .opacity(0.75) .multilineTextAlignment(.center) .foregroundColor(.accentColor) - if (!context.state.nextStationNumber.isEmpty) { + if !context.state.stationNumber.isEmpty { Text(getStationNumberText(context.state.stationNumber)) .font(.caption) .opacity(0.75) @@ -261,12 +259,12 @@ struct LockScreenLiveActivityContentView: View { .frame(minWidth: 0, maxWidth: .infinity) Image(systemName: "arrow.right") .foregroundColor(.accentColor) - VStack{ + VStack { Text(context.state.nextStationName) .bold() .multilineTextAlignment(.center) .foregroundColor(.accentColor) - if (!context.state.nextStationNumber.isEmpty) { + if !context.state.nextStationNumber.isEmpty { Text(getStationNumberText(context.state.nextStationNumber)) .font(.caption) .bold() @@ -280,14 +278,25 @@ struct LockScreenLiveActivityContentView: View { .padding(8) } } - .background(Rectangle().fill(colorScheme == .dark ? .black.opacity(0.75) : .white.opacity(0.75))) - - if (!context.state.passingStationName.isEmpty) { - HStack { + .background( + Rectangle().fill( + colorScheme == .dark ? .black.opacity(0.75) : .white.opacity(0.75))) + + HStack { + if !context.state.trainTypeName.isEmpty { + Text(context.state.trainTypeName) + .multilineTextAlignment(.center) + .foregroundColor(.accentColor) + .bold() + .font(.caption) + } + if !context.state.boundStationName.isEmpty { Text( String( - format: NSLocalizedString("passingStation", comment: ""), - "\(context.state.passingStationName)\(getStationNumberText(context.state.passingStationNumber))" + format: NSLocalizedString( + context.state.isLoopLine + ? "boundStationLoopline" : "boundStation", comment: ""), + "\(context.state.boundStationName)\(getStationNumberText(context.state.boundStationNumber))" ) ) .multilineTextAlignment(.center) @@ -295,32 +304,8 @@ struct LockScreenLiveActivityContentView: View { .bold() .font(.caption) } - .padding(.bottom, 8) - } else { - HStack { - if (!context.state.trainTypeName.isEmpty) { - Text(context.state.trainTypeName) - .multilineTextAlignment(.center) - .foregroundColor(.accentColor) - .bold() - .font(.caption) - } - if (!context.state.boundStationName.isEmpty) { - Text( - String( - format: NSLocalizedString(context.state.isLoopLine ? "boundStationLoopline": "boundStation", comment: ""), - "\(context.state.boundStationName)\(getStationNumberText(context.state.boundStationNumber))" - ) - ) - .multilineTextAlignment(.center) - .foregroundColor(.accentColor) - .bold() - .font(.caption) - } - } - .padding(.bottom, 8) - } + .padding(.bottom, 8) } .frame( minWidth: 0, @@ -329,14 +314,19 @@ struct LockScreenLiveActivityContentView: View { maxHeight: .infinity, alignment: .center ) - .activityBackgroundTint(colorScheme == .dark ? .black.opacity(0.5) : .white.opacity(0.5)) + .activityBackgroundTint( + colorScheme == .dark ? .black.opacity(0.5) : .white.opacity(0.5) + ) .accentColor(colorScheme == .dark ? .white : .black) - .widgetURL(URL(string: schemeName == "CanaryTrainLCD" ? "trainlcd-canary://" : "trainlcd://")) + .widgetURL( + URL( + string: schemeName == "CanaryTrainLCD" + ? "trainlcd-canary://" : "trainlcd://")) } } struct EarlierLockScreenLiveActivityContentView: View { - let context: ActivityViewContext + let context: ActivityViewContext var body: some View { LockScreenLiveActivityContentView(context: context) @@ -345,9 +335,8 @@ struct EarlierLockScreenLiveActivityContentView: View { @available(iOS 18.0, *) struct SmartStackLiveActivityContentView: View { - @Environment(\.colorScheme) var colorScheme let context: ActivityViewContext - + var body: some View { ZStack { VStack(alignment: .leading) { @@ -362,27 +351,34 @@ struct SmartStackLiveActivityContentView: View { .bold() .multilineTextAlignment(.leading) .opacity(0.75) - } - Text(getRunningStateText( - approaching: context.state.approaching, - stopping: context.state.stopping, - isNextLastStop: context.state.isNextLastStop - )) + + Text( + getRunningStateText( + approaching: context.state.approaching, + stopped: context.state.stopped, + isNextLastStop: context.state.isNextLastStop + ) + ) .font(.callout) .bold() .multilineTextAlignment(.leading) - Text(context.state.stopping ? context.state.stationName : context.state.nextStationName) - .font(.headline) - .bold() - .multilineTextAlignment(.leading) - if (!context.state.stationNumber.isEmpty) { - Text(context.state.stopping ? context.state.stationNumber : context.state.nextStationNumber) - .font(.caption) - .bold() - .opacity(0.75) - .multilineTextAlignment(.leading) - } + + Text( + context.state.nextStationName.isEmpty + ? context.state.stationName : context.state.nextStationName + ) + .font(.headline) + .bold() + .multilineTextAlignment(.leading) + Text( + context.state.nextStationNumber.isEmpty + ? context.state.stationNumber : context.state.nextStationNumber + ) + .font(.caption) + .bold() + .opacity(0.75) + .multilineTextAlignment(.leading) } .frame( minWidth: 0, @@ -398,8 +394,10 @@ struct SmartStackLiveActivityContentView: View { Rectangle().fill(Color(hex: context.state.lineColor)) Rectangle() .fill( - LinearGradient(colors: [.gray, .clear], startPoint: .top, endPoint: .bottom) - .opacity(0.5) + LinearGradient( + colors: [.gray, .clear], startPoint: .top, endPoint: .bottom + ) + .opacity(0.5) ) .blendMode(.multiply) } @@ -407,12 +405,11 @@ struct SmartStackLiveActivityContentView: View { } } - @available(iOS 18.0, *) struct NewerLockScreenLiveActivityContentView: View { @Environment(\.activityFamily) var activityFamily let context: ActivityViewContext - + var body: some View { switch activityFamily { case .small: @@ -420,7 +417,7 @@ struct NewerLockScreenLiveActivityContentView: View { case .medium: LockScreenLiveActivityContentView(context: context) @unknown default: - LockScreenLiveActivityContentView(context: context) + EmptyView() } } } diff --git a/ios/RideSessionActivity/RideSessionAttributes.swift b/ios/RideSessionActivity/RideSessionAttributes.swift index e6c777b24..865216767 100644 --- a/ios/RideSessionActivity/RideSessionAttributes.swift +++ b/ios/RideSessionActivity/RideSessionAttributes.swift @@ -6,27 +6,25 @@ // Copyright © 2022 Facebook. All rights reserved. // -import Foundation import ActivityKit +import Foundation struct RideSessionAttributes: ActivityAttributes { public typealias RideSessionStatus = ContentState - + public struct ContentState: Codable, Hashable { var stationName: String var nextStationName: String var stationNumber: String var nextStationNumber: String var approaching: Bool - var stopping: Bool + var stopped: Bool var boundStationName: String var boundStationNumber: String var trainTypeName: String - var passingStationName: String - var passingStationNumber: String var isLoopLine: Bool var isNextLastStop: Bool var lineColor: String var lineName: String } -} +} diff --git a/src/hooks/useStationNumberIndexFunc.ts b/src/hooks/useStationNumberIndexFunc.ts index 87fcdf712..e53bbf905 100644 --- a/src/hooks/useStationNumberIndexFunc.ts +++ b/src/hooks/useStationNumberIndexFunc.ts @@ -2,7 +2,7 @@ import { useCallback } from 'react' import { Line, Station } from '../../gen/proto/stationapi_pb' const useStationNumberIndexFunc = () => { - const func = useCallback((station: Station | undefined, line?: Line) => { + const func = useCallback((station: Station | null, line?: Line) => { return ( line?.lineSymbols?.findIndex(({ symbol }) => station?.stationNumbers?.some(({ lineSymbol }) => symbol === lineSymbol) diff --git a/src/hooks/useUpdateLiveActivities.ts b/src/hooks/useUpdateLiveActivities.ts index 9d19ced20..7283f1d0d 100644 --- a/src/hooks/useUpdateLiveActivities.ts +++ b/src/hooks/useUpdateLiveActivities.ts @@ -7,7 +7,7 @@ import { import { directionToDirectionName } from '../models/Bound' import stationState from '../store/atoms/station' import { isJapanese } from '../translation' -import getIsPass from '../utils/isPass' +import { getIsPassFromStopCondition } from '../utils/isPass' import { startLiveActivity, stopLiveActivity, @@ -18,10 +18,8 @@ import { useCurrentLine } from './useCurrentLine' import { useCurrentStation } from './useCurrentStation' import useCurrentTrainType from './useCurrentTrainType' import useIsNextLastStop from './useIsNextLastStop' -import useIsPassing from './useIsPassing' import { useLoopLine } from './useLoopLine' import { useNextStation } from './useNextStation' -import usePreviousStation from './usePreviousStation' import useStationNumberIndexFunc from './useStationNumberIndexFunc' export const useUpdateLiveActivities = (): void => { @@ -29,10 +27,9 @@ export const useUpdateLiveActivities = (): void => { const { arrived, selectedBound, selectedDirection, approaching } = useRecoilValue(stationState) - const previousStation = usePreviousStation() const currentLine = useCurrentLine() + const previousStation = useCurrentStation(true) const currentStation = useCurrentStation() - const stoppedCurrentStation = useCurrentStation(true) const nextStation = useNextStation() const { directionalStops } = useBounds() const isNextLastStop = useIsNextLastStop() @@ -40,7 +37,15 @@ export const useUpdateLiveActivities = (): void => { const trainType = useCurrentTrainType() const { isLoopLine, isPartiallyLoopLine, isYamanoteLine, isOsakaLoopLine } = useLoopLine() - const isPassing = useIsPassing() + + const currentStationStopCond = useMemo( + () => currentStation?.stopCondition, + [currentStation?.stopCondition] + ) + const nextStationStopCond = useMemo( + () => nextStation?.stopCondition, + [nextStation?.stopCondition] + ) const trainTypeName = useMemo(() => { // 山手線か大阪環状線の直通がない種別が選択されていて、日本語環境でもない場合 @@ -88,64 +93,56 @@ export const useUpdateLiveActivities = (): void => { }, [directionalStops, getStationNumberIndex]) const stoppedStation = useMemo( - () => stoppedCurrentStation ?? previousStation, - [previousStation, stoppedCurrentStation] - ) - const stoppedStationName = useMemo( - () => stoppedStation?.name, - [stoppedStation?.name] + () => previousStation ?? currentStation, + [currentStation, previousStation] ) - const stoppedStationNameRoman = useMemo( - () => stoppedStation?.nameRoman, - [stoppedStation?.nameRoman] + const stationName = useMemo( + () => (isJapanese ? stoppedStation?.name : stoppedStation?.nameRoman) ?? '', + [stoppedStation?.name, stoppedStation?.nameRoman] ) - const nextStationName = useMemo(() => nextStation?.name, [nextStation?.name]) - const nextStationNameRoman = useMemo( - () => nextStation?.nameRoman, - [nextStation?.nameRoman] - ) - - const passingStationName = useMemo( - () => (isJapanese ? currentStation?.name : currentStation?.nameRoman) ?? '', - [currentStation?.name, currentStation?.nameRoman] + const nextStationName = useMemo( + () => + arrived && !getIsPassFromStopCondition(currentStationStopCond) + ? '' + : (isJapanese ? nextStation?.name : nextStation?.nameRoman) ?? '', + [arrived, currentStationStopCond, nextStation?.name, nextStation?.nameRoman] ) - const stoppedStationNumberingIndex = getStationNumberIndex(stoppedStation) - const stoppedStationNumber = useMemo( - () => + const stoppedStationNumber = useMemo(() => { + const stoppedStationNumberingIndex = getStationNumberIndex( + stoppedStation ?? null + ) + return ( stoppedStation?.stationNumbers?.[stoppedStationNumberingIndex] - ?.stationNumber ?? '', - [stoppedStation?.stationNumbers, stoppedStationNumberingIndex] - ) + ?.stationNumber ?? '' + ) + }, [getStationNumberIndex, stoppedStation]) - const currentStationNumberingIndex = getStationNumberIndex( - currentStation ?? undefined - ) - const nextStationNumberingIndex = getStationNumberIndex(nextStation) - const nextStationNumber = useMemo( - () => + const nextStationNumber = useMemo(() => { + if (arrived && !getIsPassFromStopCondition(currentStationStopCond)) { + return '' + } + + const nextStationNumberingIndex = getStationNumberIndex(nextStation ?? null) + return ( nextStation?.stationNumbers?.[nextStationNumberingIndex]?.stationNumber ?? - '', - [nextStation?.stationNumbers, nextStationNumberingIndex] - ) + '' + ) + }, [arrived, currentStationStopCond, getStationNumberIndex, nextStation]) const isApproachingForLA = useMemo( - () => !!(approaching && !arrived && !getIsPass(nextStation ?? null)), - [approaching, arrived, nextStation] - ) - const isStoppingForLA = useMemo( - () => !!(arrived && currentStation && !getIsPass(currentStation)), - [arrived, currentStation] - ) - - const passingStationNumber = useMemo( () => - isPassing - ? currentStation?.stationNumbers[currentStationNumberingIndex] - ?.stationNumber ?? '' - : '', - [currentStation?.stationNumbers, currentStationNumberingIndex, isPassing] + !!( + approaching && + !arrived && + !getIsPassFromStopCondition(nextStationStopCond) + ), + [approaching, arrived, nextStationStopCond] + ) + const isStoppedForLA = useMemo( + () => !!(arrived && !getIsPassFromStopCondition(currentStationStopCond)), + [arrived, currentStationStopCond] ) const lineColor = useMemo( @@ -157,49 +154,39 @@ export const useUpdateLiveActivities = (): void => { [currentLine?.nameRoman, currentLine?.nameShort] ) - const activityState = useMemo(() => { - return { - stationName: isJapanese - ? stoppedStationName ?? '' - : stoppedStationNameRoman ?? '', - nextStationName: isJapanese - ? nextStationName ?? '' - : nextStationNameRoman ?? '', + const activityState = useMemo( + () => ({ + stationName, + nextStationName, stationNumber: stoppedStationNumber, nextStationNumber: nextStationNumber, approaching: isApproachingForLA, - stopping: isStoppingForLA, + stopped: isStoppedForLA, boundStationName, boundStationNumber, trainTypeName, - passingStationName: isPassing ? passingStationName : '', - passingStationNumber, isLoopLine: isLoopLine || isPartiallyLoopLine, isNextLastStop, lineColor, lineName, - } - }, [ - boundStationName, - boundStationNumber, - isApproachingForLA, - isLoopLine, - isNextLastStop, - isPartiallyLoopLine, - isPassing, - isStoppingForLA, - lineColor, - lineName, - nextStationName, - nextStationNameRoman, - nextStationNumber, - passingStationName, - passingStationNumber, - stoppedStationName, - stoppedStationNameRoman, - stoppedStationNumber, - trainTypeName, - ]) + }), + [ + boundStationName, + boundStationNumber, + isApproachingForLA, + isLoopLine, + isNextLastStop, + isPartiallyLoopLine, + isStoppedForLA, + lineColor, + lineName, + nextStationName, + nextStationNumber, + stationName, + stoppedStationNumber, + trainTypeName, + ] + ) useEffect(() => { if (!IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { diff --git a/src/utils/isPass.ts b/src/utils/isPass.ts index ce349a103..515284c10 100644 --- a/src/utils/isPass.ts +++ b/src/utils/isPass.ts @@ -4,12 +4,14 @@ import { getIsHoliday } from './isHoliday' const getIsPass = ( station: Station | null, ignoreDayCondition?: boolean -): boolean => { - if (!station) { - return false - } +): boolean => + getIsPassFromStopCondition(station?.stopCondition, ignoreDayCondition) - switch (station.stopCondition) { +export const getIsPassFromStopCondition = ( + stopCondition: StopCondition | undefined, + ignoreDayCondition?: boolean +) => { + switch (stopCondition) { case StopCondition.All: case StopCondition.PartialStop: // 一部停車は一旦停車扱い case StopCondition.Partial: // 一部通過は停車扱い diff --git a/src/utils/native/ios/liveActivityModule.ts b/src/utils/native/ios/liveActivityModule.ts index da3335ba7..68bd68ccc 100644 --- a/src/utils/native/ios/liveActivityModule.ts +++ b/src/utils/native/ios/liveActivityModule.ts @@ -12,9 +12,9 @@ type LiveActivityWidgetState = { stationNumber: string; nextStationNumber: string; approaching: boolean; - stopping: boolean; - passingStationName: string; - passingStationNumber: string; + stopped: boolean; + lineName:string + lineColor:string }; export const startLiveActivity = ( From a69751c8b0668ae439e99e990c52250d630ff8e1 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Thu, 26 Sep 2024 02:58:00 +0900 Subject: [PATCH 2/7] =?UTF-8?q?ios=E3=83=8D=E3=82=A4=E3=83=86=E3=82=A3?= =?UTF-8?q?=E3=83=96=E7=94=A8TS=E3=82=B3=E3=83=BC=E3=83=89=E3=81=AEprettie?= =?UTF-8?q?r=E6=9C=89=E5=8A=B9=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .prettierignore | 4 +- src/utils/native/ios/liveActivityModule.ts | 45 +++++++++---------- .../native/ios/sensitiveNotificationMoudle.ts | 20 ++++----- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/.prettierignore b/.prettierignore index 12be8254d..b82de8414 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,2 @@ -ios -android \ No newline at end of file +/ios +/android \ No newline at end of file diff --git a/src/utils/native/ios/liveActivityModule.ts b/src/utils/native/ios/liveActivityModule.ts index 68bd68ccc..8c8c15d2e 100644 --- a/src/utils/native/ios/liveActivityModule.ts +++ b/src/utils/native/ios/liveActivityModule.ts @@ -1,45 +1,42 @@ -import { NativeModules } from 'react-native'; -import { IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM } from '../../../constants'; - - -const { LiveActivityModule } = NativeModules; - +import { NativeModules } from 'react-native' +import { IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM } from '../../../constants' +const { LiveActivityModule } = NativeModules type LiveActivityWidgetState = { - stationName: string; - nextStationName: string; - stationNumber: string; - nextStationNumber: string; - approaching: boolean; - stopped: boolean; - lineName:string - lineColor:string -}; + stationName: string + nextStationName: string + stationNumber: string + nextStationNumber: string + approaching: boolean + stopped: boolean + lineName: string + lineColor: string +} export const startLiveActivity = ( state?: LiveActivityWidgetState ): (() => void) | null => { if (IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return LiveActivityModule?.startLiveActivity?.(state); + return LiveActivityModule?.startLiveActivity?.(state) } - return null; -}; + return null +} export const updateLiveActivity = ( state: LiveActivityWidgetState ): (() => void) | null => { if (IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return LiveActivityModule?.updateLiveActivity?.(state); + return LiveActivityModule?.updateLiveActivity?.(state) } - return null; -}; + return null +} export const stopLiveActivity = ( state?: LiveActivityWidgetState ): (() => void) | null => { if (IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return LiveActivityModule?.stopLiveActivity?.(state); + return LiveActivityModule?.stopLiveActivity?.(state) } - return null; -}; + return null +} diff --git a/src/utils/native/ios/sensitiveNotificationMoudle.ts b/src/utils/native/ios/sensitiveNotificationMoudle.ts index b538ca215..e4259699a 100644 --- a/src/utils/native/ios/sensitiveNotificationMoudle.ts +++ b/src/utils/native/ios/sensitiveNotificationMoudle.ts @@ -1,20 +1,20 @@ -import * as Notifications from 'expo-notifications'; -import { NativeModules, Platform } from 'react-native'; +import * as Notifications from 'expo-notifications' +import { NativeModules, Platform } from 'react-native' -const { SensitiveNotificationModule } = NativeModules; +const { SensitiveNotificationModule } = NativeModules const ELIGIBLE_PLATFORM = - Platform.OS === 'ios' && parseFloat(Platform.Version) >= 15.0; + Platform.OS === 'ios' && parseFloat(Platform.Version) >= 15.0 const sendNotificationAsync = async ({ title, body, }: { - title: string; - body: string; + title: string + body: string }): Promise => { if (ELIGIBLE_PLATFORM) { - return SensitiveNotificationModule.sendNotification(title, body); + return SensitiveNotificationModule.sendNotification(title, body) } return Notifications.scheduleNotificationAsync({ content: { @@ -23,7 +23,7 @@ const sendNotificationAsync = async ({ sound: true, }, trigger: null, - }); -}; + }) +} -export default sendNotificationAsync; +export default sendNotificationAsync From 52a4cea5be40bfe758deb97d2dc74be98b52b896 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Thu, 26 Sep 2024 03:13:47 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=E9=80=9A=E9=81=8E=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=AE=E5=BE=A9=E6=B4=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LiveActivity/LiveActivityModule.swift | 2 + .../RideSessionActivity.swift | 50 ++++++++++++------- .../RideSessionAttributes.swift | 2 + src/hooks/useUpdateLiveActivities.ts | 22 ++++++++ src/utils/native/ios/liveActivityModule.ts | 2 + 5 files changed, 61 insertions(+), 17 deletions(-) diff --git a/ios/Modules/LiveActivity/LiveActivityModule.swift b/ios/Modules/LiveActivity/LiveActivityModule.swift index 6e9b58082..b50604447 100644 --- a/ios/Modules/LiveActivity/LiveActivityModule.swift +++ b/ios/Modules/LiveActivity/LiveActivityModule.swift @@ -19,6 +19,8 @@ class LiveActivityModule: NSObject { stopped: state["stopped"] as? Bool ?? false, boundStationName: state["boundStationName"] as? String ?? "", boundStationNumber: state["boundStationNumber"] as? String ?? "", + passingStationName: state["passingStationName"] as? String ?? "", + passingStationNumber: state["passingStationNumber"] as? String ?? "", trainTypeName: state["trainTypeName"] as? String ?? "", isLoopLine: state["isLoopLine"] as? Bool ?? false, isNextLastStop: state["isNextLastStop"] as? Bool ?? false, diff --git a/ios/RideSessionActivity/RideSessionActivity.swift b/ios/RideSessionActivity/RideSessionActivity.swift index d04e1c8bf..b55507bca 100644 --- a/ios/RideSessionActivity/RideSessionActivity.swift +++ b/ios/RideSessionActivity/RideSessionActivity.swift @@ -282,30 +282,46 @@ struct LockScreenLiveActivityContentView: View { Rectangle().fill( colorScheme == .dark ? .black.opacity(0.75) : .white.opacity(0.75))) - HStack { - if !context.state.trainTypeName.isEmpty { - Text(context.state.trainTypeName) + if context.state.passingStationName.isEmpty { + HStack { + if !context.state.trainTypeName.isEmpty { + Text(context.state.trainTypeName) + .multilineTextAlignment(.center) + .foregroundColor(.accentColor) + .bold() + .font(.caption) + } + if !context.state.boundStationName.isEmpty { + Text( + String( + format: NSLocalizedString( + context.state.isLoopLine + ? "boundStationLoopline" : "boundStation", comment: ""), + "\(context.state.boundStationName)\(getStationNumberText(context.state.boundStationNumber))" + ) + ) .multilineTextAlignment(.center) .foregroundColor(.accentColor) .bold() .font(.caption) + } } - if !context.state.boundStationName.isEmpty { - Text( - String( - format: NSLocalizedString( - context.state.isLoopLine - ? "boundStationLoopline" : "boundStation", comment: ""), - "\(context.state.boundStationName)\(getStationNumberText(context.state.boundStationNumber))" + .padding(.bottom, 8) + } else { + HStack { + Text( + String( + format: NSLocalizedString("passingStation", comment: ""), + "\(context.state.passingStationName)\(getStationNumberText(context.state.passingStationNumber))" + ) ) - ) - .multilineTextAlignment(.center) - .foregroundColor(.accentColor) - .bold() - .font(.caption) - } + .multilineTextAlignment(.center) + .foregroundColor(.accentColor) + .bold() + .font(.caption) + } + .padding(.bottom, 8) } - .padding(.bottom, 8) } .frame( minWidth: 0, diff --git a/ios/RideSessionActivity/RideSessionAttributes.swift b/ios/RideSessionActivity/RideSessionAttributes.swift index 865216767..c6b89b6e8 100644 --- a/ios/RideSessionActivity/RideSessionAttributes.swift +++ b/ios/RideSessionActivity/RideSessionAttributes.swift @@ -21,6 +21,8 @@ struct RideSessionAttributes: ActivityAttributes { var stopped: Bool var boundStationName: String var boundStationNumber: String + var passingStationName: String + var passingStationNumber: String var trainTypeName: String var isLoopLine: Bool var isNextLastStop: Bool diff --git a/src/hooks/useUpdateLiveActivities.ts b/src/hooks/useUpdateLiveActivities.ts index 7283f1d0d..29d766564 100644 --- a/src/hooks/useUpdateLiveActivities.ts +++ b/src/hooks/useUpdateLiveActivities.ts @@ -154,6 +154,24 @@ export const useUpdateLiveActivities = (): void => { [currentLine?.nameRoman, currentLine?.nameShort] ) + const passingStationName = useMemo( + () => + getIsPassFromStopCondition(currentStationStopCond) + ? (isJapanese ? currentStation?.name : currentStation?.nameRoman) ?? '' + : '', + [currentStation?.name, currentStation?.nameRoman, currentStationStopCond] + ) + + const passingStationNumber = useMemo(() => { + const currentStationNumberingIndex = getStationNumberIndex( + currentStation ?? null + ) + return getIsPassFromStopCondition(currentStationStopCond) + ? currentStation?.stationNumbers?.[currentStationNumberingIndex] + ?.stationNumber ?? '' + : '' + }, [currentStation, currentStationStopCond, getStationNumberIndex]) + const activityState = useMemo( () => ({ stationName, @@ -169,6 +187,8 @@ export const useUpdateLiveActivities = (): void => { isNextLastStop, lineColor, lineName, + passingStationName, + passingStationNumber, }), [ boundStationName, @@ -182,6 +202,8 @@ export const useUpdateLiveActivities = (): void => { lineName, nextStationName, nextStationNumber, + passingStationName, + passingStationNumber, stationName, stoppedStationNumber, trainTypeName, diff --git a/src/utils/native/ios/liveActivityModule.ts b/src/utils/native/ios/liveActivityModule.ts index 8c8c15d2e..a287d4e30 100644 --- a/src/utils/native/ios/liveActivityModule.ts +++ b/src/utils/native/ios/liveActivityModule.ts @@ -12,6 +12,8 @@ type LiveActivityWidgetState = { stopped: boolean lineName: string lineColor: string + passingStationName: string + passingStationNumber: string } export const startLiveActivity = ( From bb25fadd27d5b5e81b8ac8e65fc3fef917355e39 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Sat, 28 Sep 2024 02:45:00 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=96=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=83=86=E3=82=A3=E3=83=93=E3=83=86=E3=82=A3=E3=83=90?= =?UTF-8?q?=E3=82=B0=E4=BF=AE=E6=AD=A3=E3=83=BB=E6=94=B9=E8=89=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Localizable/en.lproj/Localizable.strings | 1 + ios/Localizable/ja.lproj/Localizable.strings | 1 + .../RideSessionActivity.swift | 209 ++++++++++-------- ios/TrainLCD/Schemes/Dev/Info.plist | 2 + ios/TrainLCD/Schemes/Prod/Info.plist | 2 + src/hooks/useIsPassing.ts | 14 +- src/hooks/useUpdateLiveActivities.ts | 165 +++++++------- src/utils/native/ios/liveActivityModule.ts | 21 +- 8 files changed, 213 insertions(+), 202 deletions(-) diff --git a/ios/Localizable/en.lproj/Localizable.strings b/ios/Localizable/en.lproj/Localizable.strings index b38e31f8e..3fa38fcd9 100644 --- a/ios/Localizable/en.lproj/Localizable.strings +++ b/ios/Localizable/en.lproj/Localizable.strings @@ -11,6 +11,7 @@ "soon" = "Soon"; "soonLast" = "Soon last stop"; "next" = "Next"; +"nextLast" = "Next last stop"; "nowStoppingAt" = "Now stopping at"; "stop" = "Stopping at"; "nextLast" = "Next last stop"; diff --git a/ios/Localizable/ja.lproj/Localizable.strings b/ios/Localizable/ja.lproj/Localizable.strings index 6e8604a60..f1df9e124 100644 --- a/ios/Localizable/ja.lproj/Localizable.strings +++ b/ios/Localizable/ja.lproj/Localizable.strings @@ -11,6 +11,7 @@ "soon" = "まもなく"; "soonLast" = "まもなく終点"; "next" = "次は"; +"nextLast" = "次は終点"; "nowStoppingAt" = "ただいま"; "stop" = "ただいま"; "nextLast" = "次は終点"; diff --git a/ios/RideSessionActivity/RideSessionActivity.swift b/ios/RideSessionActivity/RideSessionActivity.swift index b55507bca..e6e1ff999 100644 --- a/ios/RideSessionActivity/RideSessionActivity.swift +++ b/ios/RideSessionActivity/RideSessionActivity.swift @@ -9,29 +9,30 @@ import SwiftUI import WidgetKit -func getStationNumberText(_ stationNumber: String) -> String { - if stationNumber.isEmpty { - return "" - } - return "(\(stationNumber))" -} - +// NOTE: 通過中の値を追加するとなぜかライブアクティビティが死ぬので含めていない +// ちなみにライブアクティビティにスピナーが表示され固まる func getRunningStateText( approaching: Bool, stopped: Bool, isNextLastStop: Bool ) -> String { + if !stopped && !approaching { + if isNextLastStop { + return String(localized: "nextLast") + } + return String(localized: "next") + } if approaching { if isNextLastStop { - return NSLocalizedString("soonLast", comment: "") + return String(localized: "soonLast") } - return NSLocalizedString("soon", comment: "") + return String(localized: "soon") } if stopped { - return NSLocalizedString("stop", comment: "") + return String(localized: "stop") } if isNextLastStop { - return NSLocalizedString("nextLast", comment: "") + return String(localized: "nextLast") } - return NSLocalizedString("next", comment: "") + return "" } @main @@ -51,7 +52,7 @@ struct RideSessionWidget: Widget { .opacity(0.5) .multilineTextAlignment(.center) if !context.state.stationNumber.isEmpty { - Text(getStationNumberText(context.state.stationNumber)) + Text(context.state.stationNumber) .font(.caption) .opacity(0.5) .multilineTextAlignment(.center) @@ -70,7 +71,7 @@ struct RideSessionWidget: Widget { .bold() .multilineTextAlignment(.center) if !context.state.nextStationNumber.isEmpty { - Text(getStationNumberText(context.state.nextStationNumber)) + Text(context.state.nextStationNumber) .font(.caption) .bold() .multilineTextAlignment(.center) @@ -96,7 +97,7 @@ struct RideSessionWidget: Widget { .bold() .multilineTextAlignment(.center) if !context.state.stationNumber.isEmpty { - Text(getStationNumberText(context.state.stationNumber)) + Text(context.state.stationNumber) .font(.caption) .bold() .multilineTextAlignment(.center) @@ -124,66 +125,98 @@ struct RideSessionWidget: Widget { EmptyView() } } compactLeading: { - Text( - getRunningStateText( - approaching: context.state.approaching, - stopped: context.state.stopped, - isNextLastStop: context.state.isNextLastStop - ) - ) - .multilineTextAlignment(.trailing) - .font(.caption) - .bold() - .padding(.leading, 8) - } compactTrailing: { - if context.state.stopped { - VStack { - Text(context.state.stationName) - .font(.caption2) + Group { + if !context.state.passingStationName.isEmpty { + HStack { + Image(systemName: "chevron.left.chevron.left.dotted") + + VStack(spacing: 0) { + Text( + context.state.passingStationName + ) + .font(.caption) + .bold() + .multilineTextAlignment(.center) + .opacity(0.75) + + if !context.state.passingStationNumber.isEmpty { + Text(context.state.passingStationNumber) + .font(.footnote) + .bold() + .multilineTextAlignment(.center) + .opacity(0.75) + } + } + } + } else if context.state.stopped { + VStack(spacing: 0) { + Text( + context.state.stationName + ) + .font(.caption) .bold() .multilineTextAlignment(.center) - if !context.state.stationNumber.isEmpty { - Text(getStationNumberText(context.state.stationNumber)) - .font(.caption2) + + if !context.state.stationNumber.isEmpty { + Text( + context.state.stationNumber + ) + .font(.footnote) .bold() .multilineTextAlignment(.center) + } } - } - .frame( - minWidth: 0, - maxWidth: .infinity, - minHeight: 0, - maxHeight: .infinity, - alignment: .center - ) - .padding(.trailing, 8) - } else { - VStack { - Text( - context.state.nextStationName - ) - .font(.caption2) - .bold() - .multilineTextAlignment(.center) - if !context.state.nextStationNumber.isEmpty { + } else { + VStack(spacing: 0) { Text( - getStationNumberText( - context.state.nextStationNumber) + context.state.nextStationName ) - .font(.caption2) + .font(.caption) .bold() .multilineTextAlignment(.center) + + if !context.state.nextStationNumber.isEmpty { + Text( + context.state.nextStationNumber + ) + .font(.footnote) + .bold() + .multilineTextAlignment(.center) + } } } - .frame( - minWidth: 0, - maxWidth: .infinity, - minHeight: 0, - maxHeight: .infinity, - alignment: .center - ) - .padding(.trailing, 8) } + .frame(maxWidth: .infinity) + .padding(.leading, 4) + } compactTrailing: { + HStack { + if !context.state.passingStationName.isEmpty { + EmptyView() + } else if context.state.stopped { + Image(systemName: "stop.fill") + } else if context.state.isNextLastStop { + Image(systemName: "chevron.backward.to.line") + } else { + Image(systemName: "chevron.backward") + } + + if context.state.passingStationName.isEmpty { + Text( + getRunningStateText( + approaching: context.state.approaching, + stopped: context.state.stopped, + isNextLastStop: context.state.isNextLastStop + ) + ) + .font(.caption) + .bold() + .multilineTextAlignment(.center) + } else { + Text("pass") + } + } + .frame(maxWidth: .infinity) + .padding(.trailing, 4) } minimal: { Image(systemName: "tram") } @@ -219,7 +252,7 @@ struct LockScreenLiveActivityContentView: View { .multilineTextAlignment(.center) .foregroundColor(.accentColor) if !context.state.stationNumber.isEmpty { - Text(getStationNumberText(context.state.stationNumber)) + Text(context.state.stationNumber) .font(.caption) .bold() .multilineTextAlignment(.center) @@ -249,7 +282,7 @@ struct LockScreenLiveActivityContentView: View { .multilineTextAlignment(.center) .foregroundColor(.accentColor) if !context.state.stationNumber.isEmpty { - Text(getStationNumberText(context.state.stationNumber)) + Text(context.state.stationNumber) .font(.caption) .opacity(0.75) .multilineTextAlignment(.center) @@ -265,7 +298,7 @@ struct LockScreenLiveActivityContentView: View { .multilineTextAlignment(.center) .foregroundColor(.accentColor) if !context.state.nextStationNumber.isEmpty { - Text(getStationNumberText(context.state.nextStationNumber)) + Text(context.state.nextStationNumber) .font(.caption) .bold() .multilineTextAlignment(.center) @@ -275,6 +308,7 @@ struct LockScreenLiveActivityContentView: View { .frame(minWidth: 0, maxWidth: .infinity) } } + .padding(8) } } @@ -294,10 +328,11 @@ struct LockScreenLiveActivityContentView: View { if !context.state.boundStationName.isEmpty { Text( String( - format: NSLocalizedString( - context.state.isLoopLine - ? "boundStationLoopline" : "boundStation", comment: ""), - "\(context.state.boundStationName)\(getStationNumberText(context.state.boundStationNumber))" + format: String( + localized: + context.state.isLoopLine + ? "boundStationLoopline" : "boundStation"), + "\(context.state.boundStationName)(\(context.state.boundStationNumber))" ) ) .multilineTextAlignment(.center) @@ -309,27 +344,20 @@ struct LockScreenLiveActivityContentView: View { .padding(.bottom, 8) } else { HStack { - Text( - String( - format: NSLocalizedString("passingStation", comment: ""), - "\(context.state.passingStationName)\(getStationNumberText(context.state.passingStationNumber))" - ) + Text( + String( + format: String(localized: "passingStation"), + "\(context.state.passingStationName)(\(context.state.passingStationNumber))" ) - .multilineTextAlignment(.center) - .foregroundColor(.accentColor) - .bold() - .font(.caption) - } + ) + .multilineTextAlignment(.center) + .foregroundColor(.accentColor) + .bold() + .font(.caption) + } .padding(.bottom, 8) } } - .frame( - minWidth: 0, - maxWidth: .infinity, - minHeight: 0, - maxHeight: .infinity, - alignment: .center - ) .activityBackgroundTint( colorScheme == .dark ? .black.opacity(0.5) : .white.opacity(0.5) ) @@ -349,14 +377,13 @@ struct EarlierLockScreenLiveActivityContentView: View { } } -@available(iOS 18.0, *) struct SmartStackLiveActivityContentView: View { let context: ActivityViewContext var body: some View { ZStack { VStack(alignment: .leading) { - HStack(spacing: 2) { + HStack { Text(context.state.lineName) .font(.caption) .bold() @@ -381,14 +408,14 @@ struct SmartStackLiveActivityContentView: View { .multilineTextAlignment(.leading) Text( - context.state.nextStationName.isEmpty + context.state.stopped ? context.state.stationName : context.state.nextStationName ) .font(.headline) .bold() .multilineTextAlignment(.leading) Text( - context.state.nextStationNumber.isEmpty + context.state.stopped ? context.state.stationNumber : context.state.nextStationNumber ) .font(.caption) diff --git a/ios/TrainLCD/Schemes/Dev/Info.plist b/ios/TrainLCD/Schemes/Dev/Info.plist index d338a6277..db774bea3 100644 --- a/ios/TrainLCD/Schemes/Dev/Info.plist +++ b/ios/TrainLCD/Schemes/Dev/Info.plist @@ -68,6 +68,8 @@ NSSupportsLiveActivities + NSSupportsLiveActivitiesFrequentUpdates + UIAppFonts Fonts/FrutigerNeueLTPro-Bold.ttf diff --git a/ios/TrainLCD/Schemes/Prod/Info.plist b/ios/TrainLCD/Schemes/Prod/Info.plist index fd8ee0ace..19ed312a1 100644 --- a/ios/TrainLCD/Schemes/Prod/Info.plist +++ b/ios/TrainLCD/Schemes/Prod/Info.plist @@ -64,6 +64,8 @@ NSSupportsLiveActivities + NSSupportsLiveActivitiesFrequentUpdates + UIAppFonts Fonts/FrutigerNeueLTPro-Bold.ttf diff --git a/src/hooks/useIsPassing.ts b/src/hooks/useIsPassing.ts index 76ad1e9c1..30f73c345 100644 --- a/src/hooks/useIsPassing.ts +++ b/src/hooks/useIsPassing.ts @@ -1,21 +1,17 @@ import { useMemo } from 'react' import { useRecoilValue } from 'recoil' import stationState from '../store/atoms/station' -import getIsPass from '../utils/isPass' +import { getIsPassFromStopCondition } from '../utils/isPass' import { useCurrentStation } from './useCurrentStation' -import { useNextStation } from './useNextStation' const useIsPassing = (): boolean => { const { arrived } = useRecoilValue(stationState) const currentStation = useCurrentStation() - const nextStation = useNextStation() - const passing = useMemo(() => { - if (!nextStation) { - return false - } - return !!(currentStation && getIsPass(currentStation) && arrived) - }, [arrived, currentStation, nextStation]) + const passing = useMemo( + () => getIsPassFromStopCondition(currentStation?.stopCondition) && arrived, + [arrived, currentStation?.stopCondition] + ) return passing } diff --git a/src/hooks/useUpdateLiveActivities.ts b/src/hooks/useUpdateLiveActivities.ts index 29d766564..108b711ec 100644 --- a/src/hooks/useUpdateLiveActivities.ts +++ b/src/hooks/useUpdateLiveActivities.ts @@ -1,13 +1,9 @@ import { useEffect, useMemo, useState } from 'react' import { useRecoilValue } from 'recoil' -import { - IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM, - parenthesisRegexp, -} from '../constants' +import { parenthesisRegexp } from '../constants' import { directionToDirectionName } from '../models/Bound' import stationState from '../store/atoms/station' import { isJapanese } from '../translation' -import { getIsPassFromStopCondition } from '../utils/isPass' import { startLiveActivity, stopLiveActivity, @@ -18,34 +14,35 @@ import { useCurrentLine } from './useCurrentLine' import { useCurrentStation } from './useCurrentStation' import useCurrentTrainType from './useCurrentTrainType' import useIsNextLastStop from './useIsNextLastStop' +import useIsPassing from './useIsPassing' import { useLoopLine } from './useLoopLine' import { useNextStation } from './useNextStation' import useStationNumberIndexFunc from './useStationNumberIndexFunc' export const useUpdateLiveActivities = (): void => { const [started, setStarted] = useState(false) - const { arrived, selectedBound, selectedDirection, approaching } = - useRecoilValue(stationState) + const { + arrived: arrivedFromState, + approaching: approachingFromState, + selectedBound, + selectedDirection, + } = useRecoilValue(stationState) const currentLine = useCurrentLine() const previousStation = useCurrentStation(true) - const currentStation = useCurrentStation() + const currentStation = useCurrentStation(false, true) const nextStation = useNextStation() const { directionalStops } = useBounds() const isNextLastStop = useIsNextLastStop() const getStationNumberIndex = useStationNumberIndexFunc() const trainType = useCurrentTrainType() - const { isLoopLine, isPartiallyLoopLine, isYamanoteLine, isOsakaLoopLine } = - useLoopLine() - - const currentStationStopCond = useMemo( - () => currentStation?.stopCondition, - [currentStation?.stopCondition] - ) - const nextStationStopCond = useMemo( - () => nextStation?.stopCondition, - [nextStation?.stopCondition] - ) + const { + isLoopLine: isFullLoopLine, + isPartiallyLoopLine, + isYamanoteLine, + isOsakaLoopLine, + } = useLoopLine() + const isPassing = useIsPassing() const trainTypeName = useMemo(() => { // 山手線か大阪環状線の直通がない種別が選択されていて、日本語環境でもない場合 @@ -54,7 +51,7 @@ export const useUpdateLiveActivities = (): void => { if ((isYamanoteLine || isOsakaLoopLine) && !isJapanese) { return '' } - if (selectedDirection && isLoopLine) { + if (selectedDirection && isFullLoopLine) { return directionToDirectionName(currentStation?.line, selectedDirection) } if (isJapanese) { @@ -67,7 +64,7 @@ export const useUpdateLiveActivities = (): void => { .replace(/\n/, '') }, [ currentStation?.line, - isLoopLine, + isFullLoopLine, isOsakaLoopLine, isYamanoteLine, selectedDirection, @@ -76,12 +73,12 @@ export const useUpdateLiveActivities = (): void => { ]) const boundStationName = useMemo(() => { - const jaSuffix = isLoopLine || isPartiallyLoopLine ? '方面' : '' + const jaSuffix = isFullLoopLine || isPartiallyLoopLine ? '方面' : '' return `${directionalStops .map((s) => (isJapanese ? s.name : s.nameRoman)) .join(isJapanese ? '・' : '/')}${isJapanese ? jaSuffix : ''}` - }, [directionalStops, isLoopLine, isPartiallyLoopLine]) + }, [directionalStops, isFullLoopLine, isPartiallyLoopLine]) const boundStationNumber = useMemo(() => { return directionalStops @@ -102,47 +99,35 @@ export const useUpdateLiveActivities = (): void => { ) const nextStationName = useMemo( - () => - arrived && !getIsPassFromStopCondition(currentStationStopCond) - ? '' - : (isJapanese ? nextStation?.name : nextStation?.nameRoman) ?? '', - [arrived, currentStationStopCond, nextStation?.name, nextStation?.nameRoman] + () => (isJapanese ? nextStation?.name : nextStation?.nameRoman) ?? '', + [nextStation?.name, nextStation?.nameRoman] ) - const stoppedStationNumber = useMemo(() => { - const stoppedStationNumberingIndex = getStationNumberIndex( - stoppedStation ?? null - ) - return ( + const stoppedStationNumberingIndex = useMemo( + () => getStationNumberIndex(stoppedStation ?? null), + [getStationNumberIndex, stoppedStation] + ) + const stationNumber = useMemo( + () => stoppedStation?.stationNumbers?.[stoppedStationNumberingIndex] - ?.stationNumber ?? '' - ) - }, [getStationNumberIndex, stoppedStation]) + ?.stationNumber ?? '', + [stoppedStation?.stationNumbers, stoppedStationNumberingIndex] + ) + const nextStationNumberingIndex = useMemo( + () => getStationNumberIndex(nextStation ?? null), + [getStationNumberIndex, nextStation] + ) const nextStationNumber = useMemo(() => { - if (arrived && !getIsPassFromStopCondition(currentStationStopCond)) { - return '' - } - - const nextStationNumberingIndex = getStationNumberIndex(nextStation ?? null) return ( nextStation?.stationNumbers?.[nextStationNumberingIndex]?.stationNumber ?? '' ) - }, [arrived, currentStationStopCond, getStationNumberIndex, nextStation]) + }, [nextStation?.stationNumbers, nextStationNumberingIndex]) - const isApproachingForLA = useMemo( - () => - !!( - approaching && - !arrived && - !getIsPassFromStopCondition(nextStationStopCond) - ), - [approaching, arrived, nextStationStopCond] - ) - const isStoppedForLA = useMemo( - () => !!(arrived && !getIsPassFromStopCondition(currentStationStopCond)), - [arrived, currentStationStopCond] + const stopped = useMemo( + () => arrivedFromState && !isPassing, + [arrivedFromState, isPassing] ) const lineColor = useMemo( @@ -156,34 +141,48 @@ export const useUpdateLiveActivities = (): void => { const passingStationName = useMemo( () => - getIsPassFromStopCondition(currentStationStopCond) - ? (isJapanese ? currentStation?.name : currentStation?.nameRoman) ?? '' - : '', - [currentStation?.name, currentStation?.nameRoman, currentStationStopCond] + !isPassing + ? '' + : (isJapanese ? currentStation?.name : currentStation?.nameRoman) ?? '', + [currentStation?.name, currentStation?.nameRoman, isPassing] ) - const passingStationNumber = useMemo(() => { - const currentStationNumberingIndex = getStationNumberIndex( - currentStation ?? null - ) - return getIsPassFromStopCondition(currentStationStopCond) - ? currentStation?.stationNumbers?.[currentStationNumberingIndex] - ?.stationNumber ?? '' - : '' - }, [currentStation, currentStationStopCond, getStationNumberIndex]) + const currentStationNumberingIndex = useMemo( + () => getStationNumberIndex(currentStation ?? null), + [currentStation, getStationNumberIndex] + ) + + const passingStationNumber = useMemo( + () => + !isPassing + ? '' + : currentStation?.stationNumbers?.[currentStationNumberingIndex] + ?.stationNumber ?? '', + [currentStation?.stationNumbers, currentStationNumberingIndex, isPassing] + ) + + const isLoopLine = useMemo( + () => isFullLoopLine || isPartiallyLoopLine, + [isFullLoopLine, isPartiallyLoopLine] + ) + + const approaching = useMemo( + () => approachingFromState, + [approachingFromState] + ) const activityState = useMemo( () => ({ stationName, nextStationName, - stationNumber: stoppedStationNumber, - nextStationNumber: nextStationNumber, - approaching: isApproachingForLA, - stopped: isStoppedForLA, + stationNumber, + nextStationNumber, + approaching, + stopped, boundStationName, boundStationNumber, trainTypeName, - isLoopLine: isLoopLine || isPartiallyLoopLine, + isLoopLine, isNextLastStop, lineColor, lineName, @@ -191,13 +190,11 @@ export const useUpdateLiveActivities = (): void => { passingStationNumber, }), [ + approaching, boundStationName, boundStationNumber, - isApproachingForLA, isLoopLine, isNextLastStop, - isPartiallyLoopLine, - isStoppedForLA, lineColor, lineName, nextStationName, @@ -205,25 +202,20 @@ export const useUpdateLiveActivities = (): void => { passingStationName, passingStationNumber, stationName, - stoppedStationNumber, + stationNumber, + stopped, trainTypeName, ] ) useEffect(() => { - if (!IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return - } - if (selectedBound && !started && activityState) { + if (selectedBound && !started) { startLiveActivity(activityState) setStarted(true) } }, [activityState, selectedBound, started]) useEffect(() => { - if (!IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return - } if (!selectedBound) { stopLiveActivity() setStarted(false) @@ -231,9 +223,8 @@ export const useUpdateLiveActivities = (): void => { }, [selectedBound]) useEffect(() => { - if (!IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return + if (started) { + updateLiveActivity(activityState) } - updateLiveActivity(activityState) - }, [activityState]) + }, [activityState, started]) } diff --git a/src/utils/native/ios/liveActivityModule.ts b/src/utils/native/ios/liveActivityModule.ts index a287d4e30..67946a8e1 100644 --- a/src/utils/native/ios/liveActivityModule.ts +++ b/src/utils/native/ios/liveActivityModule.ts @@ -16,29 +16,20 @@ type LiveActivityWidgetState = { passingStationNumber: string } -export const startLiveActivity = ( - state?: LiveActivityWidgetState -): (() => void) | null => { +export const startLiveActivity = (state?: LiveActivityWidgetState) => { if (IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return LiveActivityModule?.startLiveActivity?.(state) + LiveActivityModule?.startLiveActivity?.(state) } - return null } -export const updateLiveActivity = ( - state: LiveActivityWidgetState -): (() => void) | null => { +export const updateLiveActivity = (state: LiveActivityWidgetState) => { if (IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return LiveActivityModule?.updateLiveActivity?.(state) + LiveActivityModule?.updateLiveActivity?.(state) } - return null } -export const stopLiveActivity = ( - state?: LiveActivityWidgetState -): (() => void) | null => { +export const stopLiveActivity = (state?: LiveActivityWidgetState) => { if (IS_LIVE_ACTIVITIES_ELIGIBLE_PLATFORM) { - return LiveActivityModule?.stopLiveActivity?.(state) + LiveActivityModule?.stopLiveActivity?.(state) } - return null } From 9536c7d36670ae5446f2ec6ab1683986edb8abb0 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Sat, 28 Sep 2024 02:48:59 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=E3=83=8A=E3=83=B3=E3=83=90=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=81=8C=E3=81=AA=E3=81=84=E6=99=82=E3=81=AE?= =?UTF-8?q?=E3=83=90=E3=82=B0=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/RideSessionActivity/RideSessionActivity.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ios/RideSessionActivity/RideSessionActivity.swift b/ios/RideSessionActivity/RideSessionActivity.swift index e6e1ff999..42e542f6d 100644 --- a/ios/RideSessionActivity/RideSessionActivity.swift +++ b/ios/RideSessionActivity/RideSessionActivity.swift @@ -138,7 +138,7 @@ struct RideSessionWidget: Widget { .bold() .multilineTextAlignment(.center) .opacity(0.75) - + if !context.state.passingStationNumber.isEmpty { Text(context.state.passingStationNumber) .font(.footnote) @@ -308,7 +308,7 @@ struct LockScreenLiveActivityContentView: View { .frame(minWidth: 0, maxWidth: .infinity) } } - + .padding(8) } } @@ -347,7 +347,9 @@ struct LockScreenLiveActivityContentView: View { Text( String( format: String(localized: "passingStation"), - "\(context.state.passingStationName)(\(context.state.passingStationNumber))" + context.state.passingStationNumber.isEmpty + ? "\(context.state.passingStationName)" + : "\(context.state.passingStationName)(\(context.state.passingStationNumber))" ) ) .multilineTextAlignment(.center) From 86dbe91b9d06080cd63620971fac58ff080e4a23 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Sat, 28 Sep 2024 02:51:49 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=E3=83=8A=E3=83=B3=E3=83=90=E3=83=AA?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=81=8C=E3=81=AA=E3=81=84=E6=99=82=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/RideSessionActivity/RideSessionActivity.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/RideSessionActivity/RideSessionActivity.swift b/ios/RideSessionActivity/RideSessionActivity.swift index 42e542f6d..8abeaa022 100644 --- a/ios/RideSessionActivity/RideSessionActivity.swift +++ b/ios/RideSessionActivity/RideSessionActivity.swift @@ -332,7 +332,9 @@ struct LockScreenLiveActivityContentView: View { localized: context.state.isLoopLine ? "boundStationLoopline" : "boundStation"), - "\(context.state.boundStationName)(\(context.state.boundStationNumber))" + context.state.boundStationNumber.isEmpty + ? "\(context.state.boundStationName)" + : "\(context.state.boundStationName)(\(context.state.boundStationNumber))" ) ) .multilineTextAlignment(.center) From 75586e1bf8a35598f2c1c0ea92923efe6bfac7e9 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Sat, 28 Sep 2024 03:18:40 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=E3=83=87=E3=82=B6=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RideSessionActivity.swift | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/ios/RideSessionActivity/RideSessionActivity.swift b/ios/RideSessionActivity/RideSessionActivity.swift index 8abeaa022..8ab7cf677 100644 --- a/ios/RideSessionActivity/RideSessionActivity.swift +++ b/ios/RideSessionActivity/RideSessionActivity.swift @@ -125,11 +125,40 @@ struct RideSessionWidget: Widget { EmptyView() } } compactLeading: { + HStack { + if context.state.stopped { + Image(systemName: "stop.fill") + } + + if context.state.passingStationName.isEmpty { + Text( + getRunningStateText( + approaching: context.state.approaching, + stopped: context.state.stopped, + isNextLastStop: context.state.isNextLastStop + ) + ) + .font(.caption) + .bold() + .multilineTextAlignment(.center) + } else { + Text("pass") + } + + if !context.state.passingStationName.isEmpty || context.state.stopped { + EmptyView() + } else if context.state.isNextLastStop { + Image(systemName: "chevron.forward.to.line") + } else { + Image(systemName: "chevron.forward") + } + } + .frame(maxWidth: .infinity) + .padding(.leading, 8) + } compactTrailing: { Group { if !context.state.passingStationName.isEmpty { HStack { - Image(systemName: "chevron.left.chevron.left.dotted") - VStack(spacing: 0) { Text( context.state.passingStationName @@ -147,6 +176,8 @@ struct RideSessionWidget: Widget { .opacity(0.75) } } + + Image(systemName: "chevron.forward.dotted.chevron.forward") } } else if context.state.stopped { VStack(spacing: 0) { @@ -187,36 +218,7 @@ struct RideSessionWidget: Widget { } } .frame(maxWidth: .infinity) - .padding(.leading, 4) - } compactTrailing: { - HStack { - if !context.state.passingStationName.isEmpty { - EmptyView() - } else if context.state.stopped { - Image(systemName: "stop.fill") - } else if context.state.isNextLastStop { - Image(systemName: "chevron.backward.to.line") - } else { - Image(systemName: "chevron.backward") - } - - if context.state.passingStationName.isEmpty { - Text( - getRunningStateText( - approaching: context.state.approaching, - stopped: context.state.stopped, - isNextLastStop: context.state.isNextLastStop - ) - ) - .font(.caption) - .bold() - .multilineTextAlignment(.center) - } else { - Text("pass") - } - } - .frame(maxWidth: .infinity) - .padding(.trailing, 4) + .padding(.trailing, 8) } minimal: { Image(systemName: "tram") }