diff --git a/Gemfile.lock b/Gemfile.lock index 6815a87..0620bcc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,29 +1,32 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.6) + CFPropertyList (3.0.7) + base64 + nkf rexml - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) - artifactory (3.0.15) + artifactory (3.0.17) atomos (0.1.3) - aws-eventstream (1.2.0) - aws-partitions (1.843.0) - aws-sdk-core (3.185.1) - aws-eventstream (~> 1, >= 1.0.2) + aws-eventstream (1.3.0) + aws-partitions (1.937.0) + aws-sdk-core (3.196.1) + aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.5) + aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.72.0) - aws-sdk-core (~> 3, >= 3.184.0) + aws-sdk-kms (1.82.0) + aws-sdk-core (~> 3, >= 3.193.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.136.0) - aws-sdk-core (~> 3, >= 3.181.0) + aws-sdk-s3 (1.151.0) + aws-sdk-core (~> 3, >= 3.194.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.6) - aws-sigv4 (1.6.1) + aws-sigv4 (~> 1.8) + aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) + base64 (0.2.0) claide (1.1.0) colored (1.2) colored2 (3.1.2) @@ -32,11 +35,10 @@ GEM declarative (0.0.20) digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20240107) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.104.0) + excon (0.110.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -65,15 +67,15 @@ GEM faraday-retry (1.0.3) faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.7) - fastlane (2.216.0) + fastimage (2.3.1) + fastlane (2.220.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) babosa (>= 1.0.3, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) - colored + colored (~> 1.2) commander (~> 4.6) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) @@ -85,6 +87,7 @@ GEM gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) google-cloud-storage (~> 1.31) highline (~> 2.0) http-cookie (~> 1.0.5) @@ -93,10 +96,10 @@ GEM mini_magick (>= 4.9.4, < 5.0.0) multipart-post (>= 2.0.0, < 3.0.0) naturally (~> 2.2) - optparse (~> 0.1.1) + optparse (>= 0.1.1, < 1.0.0) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) + security (= 0.1.5) simctl (~> 1.6.3) terminal-notifier (>= 2.0.0, < 3.0.0) terminal-table (~> 3) @@ -105,11 +108,11 @@ GEM word_wrap (~> 1.0.0) xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.51.0) + google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.2) + google-apis-core (0.11.3) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -117,24 +120,23 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick google-apis-iamcredentials_v1 (0.17.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-playcustomapp_v1 (0.13.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.19.0) - google-apis-core (>= 0.9.0, < 2.a) - google-cloud-core (1.6.0) - google-cloud-env (~> 1.0) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.7.0) + google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.3.1) - google-cloud-storage (1.44.0) + google-cloud-errors (1.4.0) + google-cloud-storage (1.47.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.19.0) + google-apis-storage_v1 (~> 0.31.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) @@ -149,30 +151,33 @@ GEM domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) - json (2.6.3) - jwt (2.7.1) + json (2.7.2) + jwt (2.8.1) + base64 mini_magick (4.12.0) mini_mime (1.1.5) multi_json (1.15.0) - multipart-post (2.3.0) + multipart-post (2.4.1) nanaimo (0.3.0) naturally (2.2.1) - optparse (0.1.1) + nkf (0.2.0) + optparse (0.5.0) os (1.1.4) - plist (3.7.0) - public_suffix (5.0.3) - rake (13.1.0) + plist (3.7.1) + public_suffix (5.0.5) + rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.6) + rexml (3.2.8) + strscan (>= 3.0.9) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) - security (0.1.3) - signet (0.18.0) + security (0.1.5) + signet (0.19.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -180,25 +185,22 @@ GEM simctl (1.6.10) CFPropertyList naturally + strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) trailblazer-option (0.1.2) tty-cursor (0.7.1) - tty-screen (0.8.1) + tty-screen (0.8.2) tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) unicode-display_width (2.5.0) - webrick (1.8.1) word_wrap (1.0.0) xcode-install (2.8.1) claide (>= 0.9.1) fastlane (>= 2.1.0, < 3.0.0) - xcodeproj (1.23.0) + xcodeproj (1.24.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) diff --git a/LuasKit/AppMode.swift b/LuasKit/AppMode.swift new file mode 100644 index 0000000..a3602e1 --- /dev/null +++ b/LuasKit/AppMode.swift @@ -0,0 +1,34 @@ +// +// Created by Roland Gropmair on 27/05/2024. +// Copyright © 2024 mApps.ie. All rights reserved. +// + +import Foundation + +/// The way the user decided how current station should be determined +public enum AppMode: Equatable { + + /// need location + case closest + case closestOtherLine + + /// no location required, because user selected specific station (via various options) + case favourite(TrainStation) + case nearby(TrainStation) + case specific(TrainStation) + case recents(TrainStation) + + public var specificStation: TrainStation? { + switch self { + + case .closest, .closestOtherLine: + return nil + case .favourite(let station), .nearby(let station), .specific(let station), .recents(let station): + return station + } + } + + public var needsLocation: Bool { + self == .closest || self == .closestOtherLine + } +} diff --git a/LuasKit/AppModel+Codable.swift b/LuasKit/AppModel+Codable.swift index dc36459..61ca5b4 100644 --- a/LuasKit/AppModel+Codable.swift +++ b/LuasKit/AppModel+Codable.swift @@ -5,7 +5,7 @@ import Foundation -extension AppModel.AppMode: Codable { +extension AppMode: Codable { // https://stackoverflow.com/questions/69979095/codable-enum-with-arguments-and-fails-at-compile-time diff --git a/LuasKit/AppModel+CustomStringConvertible.swift b/LuasKit/AppModel+CustomStringConvertible.swift index 5abbd89..ff84bf8 100644 --- a/LuasKit/AppModel+CustomStringConvertible.swift +++ b/LuasKit/AppModel+CustomStringConvertible.swift @@ -5,7 +5,7 @@ import Foundation -extension AppModel.AppState: CustomStringConvertible { +extension AppState: CustomStringConvertible { public var description: String { switch self { @@ -30,7 +30,7 @@ extension AppModel.AppState: CustomStringConvertible { } } -extension AppModel.AppMode: CustomStringConvertible { +extension AppMode: CustomStringConvertible { public var description: String { switch self { diff --git a/LuasKit/AppModel.swift b/LuasKit/AppModel.swift index 34bd565..d351ced 100644 --- a/LuasKit/AppModel.swift +++ b/LuasKit/AppModel.swift @@ -10,56 +10,6 @@ import SwiftUI // @Observable does not work - circular reference? public class AppModel: ObservableObject { - /// state machine, drives UI - public enum AppState { - - case idle - - case gettingLocation - case locationAuthorizationUnknown - case errorGettingLocation(String) - - case errorGettingStation(String) - /// in case the user is too far away from Dublin area - - case loadingDueTimes(TrainStation, TrainsByDirection?) - case errorGettingDueTimes(TrainStation, String) - - case foundDueTimes(TrainsByDirection) - - public init(_ state: AppState) { - self = state - } - } - - /// how user decided how current station should be determined - public enum AppMode: Equatable { - - /// need location - case closest - case closestOtherLine - - /// no location required, because user selected specific station (via various options) - case favourite(TrainStation) - case nearby(TrainStation) - case specific(TrainStation) - case recents(TrainStation) - - public var specificStation: TrainStation? { - switch self { - - case .closest, .closestOtherLine: - return nil - case .favourite(let station), .nearby(let station), .specific(let station), .recents(let station): - return station - } - } - - public var needsLocation: Bool { - self == .closest || self == .closestOtherLine - } - } - @Published public var appState: AppState = .idle @Published public var appMode: AppMode = .closest { @@ -94,6 +44,11 @@ public class AppModel: ObservableObject { @Published public var locationDenied: Bool = false +#if DEBUG + // so we can simulate app state in a sequence + public let mockMode = false +#endif + public init() { if let storedAppModeData = UserDefaults.standard.object(forKey: "AppMode") as? Data, let storedAppMode = try? JSONDecoder().decode(AppMode.self, from: storedAppModeData) diff --git a/LuasKit/AppState.swift b/LuasKit/AppState.swift new file mode 100644 index 0000000..ee64413 --- /dev/null +++ b/LuasKit/AppState.swift @@ -0,0 +1,41 @@ +// +// Created by Roland Gropmair on 27/05/2024. +// Copyright © 2024 mApps.ie. All rights reserved. +// + +import Foundation + +/// App's state machine, drives UI +public enum AppState { + + case idle + + case gettingLocation + case locationAuthorizationUnknown + case errorGettingLocation(String) + + /// in case the user is too far away from Dublin area + case errorGettingStation(String) + + // cachedTrains is optional because when we load that station for the first time, we won't have any trains cached + case loadingDueTimes(TrainStation, cachedTrains: TrainsByDirection?) + + case errorGettingDueTimes(TrainStation, String) + + case foundDueTimes(TrainsByDirection) + + public init(_ state: AppState) { + self = state + } +} + +public extension AppState { + + var isLoading: Bool { + if case .loadingDueTimes = self { + return true + } else { + return false + } + } +} diff --git a/LuasKit/Info.plist b/LuasKit/Info.plist index c0701c6..b73dca0 100644 --- a/LuasKit/Info.plist +++ b/LuasKit/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - $(CURRENT_PROJECT_VERSION) + 2 diff --git a/LuasKit/LuasStrings.swift b/LuasKit/LuasStrings.swift index c3fa120..bfc0662 100644 --- a/LuasKit/LuasStrings.swift +++ b/LuasKit/LuasStrings.swift @@ -75,6 +75,8 @@ public struct LuasStrings { public static let noTrains = NSLocalizedString("No trains due", comment: "String shown when no trains in specified direction") + public static let trainsLoading = NSLocalizedString("Loading trains...", comment: "String shown when trains are loading for station") + public static let locationDeniedFooter = NSLocalizedString( "Unable to determine closest station, because location access not granted or disabled.\nYou can still select a station manually.", comment: "Label shown when location access was denied by user, to explain why nearby stations won't work") diff --git a/LuasWatch Watch App/Coordinator+Animiation.swift b/LuasWatch Watch App/Coordinator+Animiation.swift index 39779b9..425190e 100644 --- a/LuasWatch Watch App/Coordinator+Animiation.swift +++ b/LuasWatch Watch App/Coordinator+Animiation.swift @@ -8,13 +8,16 @@ import SwiftUI extension Coordinator { - func updateWithAnimation(to state: AppModel.AppState) { + func updateWithAnimation(to state: AppState) { withAnimation { - DispatchQueue.main.async { [weak self] in - self?.appModel.appState = state + if Thread.isMainThread { + appModel.appState = state + } else { + DispatchQueue.main.async { [weak self] in + self?.appModel.appState = state + } } } - } } diff --git a/LuasWatch Watch App/Coordinator+LocationDelegate.swift b/LuasWatch Watch App/Coordinator+LocationDelegate.swift index f9e2970..b1e884f 100644 --- a/LuasWatch Watch App/Coordinator+LocationDelegate.swift +++ b/LuasWatch Watch App/Coordinator+LocationDelegate.swift @@ -106,9 +106,9 @@ extension Coordinator: LocationDelegate { updateWithAnimation( to: .loadingDueTimes( closestStation, - cachedTrains.trains)) + cachedTrains: cachedTrains.trains)) } else { - updateWithAnimation(to: .loadingDueTimes(closestStation, nil)) + updateWithAnimation(to: .loadingDueTimes(closestStation, cachedTrains: nil)) } } else { myPrint("SidebarView is up -> ignore so we don't interfere UI") diff --git a/LuasWatch Watch App/Coordinator.swift b/LuasWatch Watch App/Coordinator.swift index c6b3ceb..51c3193 100644 --- a/LuasWatch Watch App/Coordinator.swift +++ b/LuasWatch Watch App/Coordinator.swift @@ -40,6 +40,23 @@ class Coordinator: NSObject { func start() { + #if DEBUG + if appModel.mockMode == true { + // force specific app flow for debugging and taking screenshots + + appModel.appState = .gettingLocation + + executeAfterDelay { [weak self] in + self?.appModel.appState = .errorGettingLocation("error getting location") + + self?.executeAfterDelay { + self?.appModel.appState = .locationAuthorizationUnknown + } + } + return + } + #endif + // ////////////////////////////////////////////// // step 1: if required, determine location location.delegate = self @@ -129,4 +146,12 @@ class Coordinator: NSObject { } } } + + #if DEBUG + private func executeAfterDelay(_ block: @escaping () -> Void) { + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + block() + } + } + #endif } diff --git a/LuasWatch Watch App/LuasWatchApp.swift b/LuasWatch Watch App/LuasWatchApp.swift index 7fe0626..983fa68 100644 --- a/LuasWatch Watch App/LuasWatchApp.swift +++ b/LuasWatch Watch App/LuasWatchApp.swift @@ -8,7 +8,7 @@ import SwiftData import SwiftUI @main -struct LuasWatch_Watch_AppApp: App { +struct LuasWatch_Watch_App: App { @Environment(\.scenePhase) var scenePhase @@ -62,6 +62,13 @@ struct LuasWatch_Watch_AppApp: App { case .active: myPrint("App became active -> fireAndScheduleTimer") + + #if DEBUG + if appModel.mockMode == true { + return + } + #endif + mainCoordinator.fireAndScheduleTimer() @unknown default: diff --git a/LuasWatch Watch App/Previews/PreviewsData.swift b/LuasWatch Watch App/Previews/PreviewsData.swift index b9775e8..7b55433 100644 --- a/LuasWatch Watch App/Previews/PreviewsData.swift +++ b/LuasWatch Watch App/Previews/PreviewsData.swift @@ -13,6 +13,10 @@ import SwiftUI latitude: CLLocationDegrees(53.3292817872831), longitude: CLLocationDegrees(-6.33382500275916)) + let locationMarlborough = CLLocation( + latitude: CLLocationDegrees(53.3492448734525), + longitude: CLLocationDegrees(-6.25773158174389)) + let userLocation = CLLocation( latitude: locationBluebell.coordinate.latitude + 0.00425, longitude: locationBluebell.coordinate.longitude + 0.005) @@ -95,14 +99,22 @@ import SwiftUI inbound: [trainGreen3], outbound: [trainGreen1, trainGreen2]) - private let stationOneWay = TrainStation( + let noTrainsGreen = TrainsByDirection( + trainStation: stationGreen, inbound: [], outbound: []) + + let stationOneWay = TrainStation( stationId: "stationId", - stationIdShort: "LUAS69", + stationIdShort: "LUAS62", shortCode: "MAR", route: .green, name: "Marlborough", - location: locationBluebell, + location: locationMarlborough, stationType: .oneway) + let trainsMarlborough = TrainsByDirection( + trainStation: stationOneWay, + inbound: [trainGreen3], + outbound: []) + let trainsOneWayStation = TrainsByDirection( trainStation: stationOneWay, inbound: [trainGreen2, trainGreen3], diff --git a/LuasWatch Watch App/Previews/PreviewsAllStates.swift b/LuasWatch Watch App/Previews/PreviewsErrorStates.swift similarity index 73% rename from LuasWatch Watch App/Previews/PreviewsAllStates.swift rename to LuasWatch Watch App/Previews/PreviewsErrorStates.swift index 3546c5d..3a7eaa0 100644 --- a/LuasWatch Watch App/Previews/PreviewsAllStates.swift +++ b/LuasWatch Watch App/Previews/PreviewsErrorStates.swift @@ -7,7 +7,7 @@ import LuasKit import SwiftUI #if DEBUG - private func luasView(state: AppModel.AppState) -> some View { + private func luasView(state: AppState) -> some View { let appModel = AppModel(state) appModel.appMode = .favourite(stationGreen) @@ -36,19 +36,7 @@ import SwiftUI luasView(state: .errorGettingStation("Some internal error getting station.")) } - #Preview("loading") { - luasView(state: .loadingDueTimes(stationGreen, nil)) - } - - #Preview("loading (cached)") { - luasView(state: .loadingDueTimes(stationGreen, trainsGreen)) - } - #Preview("errLoading") { luasView(state: .errorGettingDueTimes(stationGreen, "Error loading due times - could not access internet?")) } - - #Preview("OK") { - luasView(state: .foundDueTimes(trainsGreen)) - } #endif diff --git a/LuasWatch Watch App/Previews/PreviewsLoadingStates.swift b/LuasWatch Watch App/Previews/PreviewsLoadingStates.swift new file mode 100644 index 0000000..af082c1 --- /dev/null +++ b/LuasWatch Watch App/Previews/PreviewsLoadingStates.swift @@ -0,0 +1,38 @@ +// +// Created by Roland Gropmair on 14/05/2024. +// Copyright © 2024 mApps.ie. All rights reserved. +// + +import LuasKit +import SwiftUI + +#if DEBUG + private func luasView(state: AppState) -> some View { + let appModel = AppModel(state) + appModel.appMode = .favourite(stationGreen) + + return LuasMainScreen() + .environmentObject(appModel) + .modelContainer(Previews().container) + } + + #Preview("loading") { + luasView(state: .loadingDueTimes(stationGreen, cachedTrains: nil)) + } + + #Preview("loading (cached)") { + luasView(state: .loadingDueTimes(stationGreen, cachedTrains: trainsGreen)) + } + + #Preview("loading 1Way (cached)") { + luasView(state: .loadingDueTimes(stationOneWay, cachedTrains: trainsMarlborough)) + } + + #Preview("noTrains") { + luasView(state: .foundDueTimes(noTrainsGreen)) + } + + #Preview("OK") { + luasView(state: .foundDueTimes(trainsGreen)) + } +#endif diff --git a/LuasWatch Watch App/Views/DetailView/DoubleTimetableView.swift b/LuasWatch Watch App/Views/DetailView/DoubleTimetableView.swift index 6369dcc..0046d69 100644 --- a/LuasWatch Watch App/Views/DetailView/DoubleTimetableView.swift +++ b/LuasWatch Watch App/Views/DetailView/DoubleTimetableView.swift @@ -8,8 +8,12 @@ import SwiftUI struct DoubleTimetableView: View { + @EnvironmentObject private var appModel: AppModel + let trainsByDirection: TrainsByDirection - var isLoading: Bool +} + +extension DoubleTimetableView { var body: some View { @@ -25,7 +29,7 @@ struct DoubleTimetableView: View { due: $0.dueTimeDescription2) } if trainsByDirection.inboundHasOverflowSmall { - OverflowView() + OverflowDotsView() } Spacer() } @@ -43,12 +47,12 @@ struct DoubleTimetableView: View { due: $0.dueTimeDescription2) } if trainsByDirection.outboundHasOverflowSmall { - OverflowView() + OverflowDotsView() } Spacer() } } .timeTableStyle() - .opacity(isLoading ? 0.52 : 1.0) + .opacity(appModel.appState.isLoading ? 0.52 : 1.0) } } diff --git a/LuasWatch Watch App/Views/DetailView/LuasTextView.swift b/LuasWatch Watch App/Views/DetailView/LuasTextView.swift index 4940888..1f98fbc 100644 --- a/LuasWatch Watch App/Views/DetailView/LuasTextView.swift +++ b/LuasWatch Watch App/Views/DetailView/LuasTextView.swift @@ -9,9 +9,17 @@ struct LuasTextView: View { var text: String var body: some View { - Text(text) - .multilineTextAlignment(.center) - .frame(idealHeight: .greatestFiniteMagnitude) - .padding(.horizontal, 8) + HStack { + Spacer() + Text(text) + .font(.caption2) + .monospaced() + Spacer() + } + .foregroundColor(.yellow) + .timeTableStyle() + .toolbar { + ToolbarInactive() + } } } diff --git a/LuasWatch Watch App/Views/DetailView/OverflowView.swift b/LuasWatch Watch App/Views/DetailView/OverflowDotsView.swift similarity index 88% rename from LuasWatch Watch App/Views/DetailView/OverflowView.swift rename to LuasWatch Watch App/Views/DetailView/OverflowDotsView.swift index 607f1ee..5aa543c 100644 --- a/LuasWatch Watch App/Views/DetailView/OverflowView.swift +++ b/LuasWatch Watch App/Views/DetailView/OverflowDotsView.swift @@ -5,7 +5,7 @@ import SwiftUI -struct OverflowView: View { +struct OverflowDotsView: View { var body: some View { Text("...") .offset(CGSize(width: 0, height: -6)) diff --git a/LuasWatch Watch App/Views/DetailView/SimpleTimetableView.swift b/LuasWatch Watch App/Views/DetailView/SimpleTimetableView.swift index 90081c3..1403ece 100644 --- a/LuasWatch Watch App/Views/DetailView/SimpleTimetableView.swift +++ b/LuasWatch Watch App/Views/DetailView/SimpleTimetableView.swift @@ -8,6 +8,8 @@ import SwiftUI struct SimpleTimetableView: View { + @EnvironmentObject private var appModel: AppModel + let trainsByDirection: TrainsByDirection let direction: Direction } @@ -15,8 +17,10 @@ struct SimpleTimetableView: View { extension SimpleTimetableView { var body: some View { + var trains = [Train]() var hasOverflow = false + switch direction { /// noOverflowLarge cuts off after 6 - WIP: show overflow in subsequent tabView case .inbound: @@ -30,6 +34,9 @@ extension SimpleTimetableView { trains = trainsByDirection.inbound } + // we should always have train here, see where we're calling this view from + assert(trains.count > 0) + return VStack { ForEach(trains, id: \.id) { DueView( @@ -37,10 +44,11 @@ extension SimpleTimetableView { due: $0.dueTimeDescription2) } if hasOverflow { - OverflowView() + OverflowDotsView() } Spacer() } .timeTableStyle() + .opacity(appModel.appState.isLoading ? 0.52 : 1.0) } } diff --git a/LuasWatch Watch App/Views/DetailView/StationTimesView.swift b/LuasWatch Watch App/Views/DetailView/StationTimesView.swift index 2eb76a9..37e1472 100644 --- a/LuasWatch Watch App/Views/DetailView/StationTimesView.swift +++ b/LuasWatch Watch App/Views/DetailView/StationTimesView.swift @@ -13,8 +13,8 @@ struct StationTimesView: View { @State private var direction: Direction = .both - let trains: TrainsByDirection - var isLoading: Bool = false + let trainStation: TrainStation + let trains: TrainsByDirection? } extension StationTimesView { @@ -24,71 +24,85 @@ extension StationTimesView { NavigationStack { VStack { - Text(trains.trainStation.name) + Text(trainStation.name) .font(.title3) .padding(.bottom) Spacer() - if trains.trainStation.allowsSwitchingDirection { - - switch direction { - - case .inbound: - if trains.inbound.isEmpty { - NoTrainsView() - .timeTableStyle() - } else { - SimpleTimetableView( - trainsByDirection: trains, direction: .inbound) - } - - case .outbound: - if trains.outbound.isEmpty { - NoTrainsView() - .timeTableStyle() - } else { - SimpleTimetableView( - trainsByDirection: trains, direction: .outbound) - } - - case .both: - DoubleTimetableView( - trainsByDirection: trains, isLoading: isLoading) - } + if let trains { + + timetableView(for: trains) } else { - // we have a .terminal or .oneway station -> only show the inbound or outbound trains - if !trains.inbound.isEmpty { + // no cachedTrains: we're loading that station for the first time + TrainsViewLoading() + .timeTableStyle() + } + } - SimpleTimetableView( - trainsByDirection: trains, direction: .inbound) + .onAppear { + direction = modelContext.directionConsideringStationType(for: trainStation.shortCode) + } - } else if !trains.outbound.isEmpty { + .toolbar { + StationToolbar( + direction: $direction, + trainStation: trainStation) + } + } + } - SimpleTimetableView( - trainsByDirection: trains, direction: .outbound) + @ViewBuilder + fileprivate func timetableView(for trains: TrainsByDirection) -> some View { + + if trains.trainStation.allowsSwitchingDirection { + switch direction { + + case .inbound: + if trains.inbound.isEmpty { + NoTrainsView() + .timeTableStyle() } else { + SimpleTimetableView( + trainsByDirection: trains, direction: .inbound) + } + case .outbound: + if trains.outbound.isEmpty { NoTrainsView() .timeTableStyle() + } else { + SimpleTimetableView( + trainsByDirection: trains, direction: .outbound) } - } + case .both: + DoubleTimetableView( + trainsByDirection: trains) } - .onAppear { - direction = modelContext.directionConsideringStationType(for: trains.trainStation.shortCode) - } + } else { - .toolbar { - StationToolbar( - direction: $direction, - trains: trains, - isLoading: isLoading) + // we have a .terminal or .oneway station -> only show the inbound or outbound trains + if !trains.inbound.isEmpty { + + SimpleTimetableView( + trainsByDirection: trains, direction: .inbound) + + } else if !trains.outbound.isEmpty { + + SimpleTimetableView( + trainsByDirection: trains, direction: .outbound) + + } else { + + NoTrainsView() + .timeTableStyle() } + } } } diff --git a/LuasWatch Watch App/Views/DetailView/StationToolbar.swift b/LuasWatch Watch App/Views/DetailView/StationToolbar.swift index 30a8865..e17e29f 100644 --- a/LuasWatch Watch App/Views/DetailView/StationToolbar.swift +++ b/LuasWatch Watch App/Views/DetailView/StationToolbar.swift @@ -10,6 +10,8 @@ struct StationToolbar { @Environment(\.modelContext) private var modelContext + @EnvironmentObject private var appModel: AppModel + // have to let SwiftUI know that underlying context has changed - can we avoid the isFavourite state? @State private var isFavourite: Bool = false @@ -17,8 +19,7 @@ struct StationToolbar { @Binding var direction: Direction - let trains: TrainsByDirection - let isLoading: Bool + let trainStation: TrainStation } extension StationToolbar: ToolbarContent { @@ -42,13 +43,13 @@ extension StationToolbar: ToolbarContent { direction = direction.next() } - let shortCode = trains.trainStation.shortCode + let shortCode = trainStation.shortCode myPrint("\(#function) createOrUpdate \(shortCode) to \(direction)") modelContext.createOrUpdate(shortCode: shortCode, to: direction) } label: { - if trains.trainStation.allowsSwitchingDirection { + if trainStation.allowsSwitchingDirection { switch direction { case .inbound: @@ -65,24 +66,24 @@ extension StationToolbar: ToolbarContent { } } .onAppear { - isSwitchingDirectionEnabled = trains.trainStation.allowsSwitchingDirection + isSwitchingDirectionEnabled = trainStation.allowsSwitchingDirection } .disabled(!isSwitchingDirectionEnabled) - if isLoading { + if appModel.appState.isLoading { Text(LuasStrings.loadingDueTimes) .font(.subheadline) } /// Favourite Button { - modelContext.toggleFavouriteStation(shortCode: trains.trainStation.shortCode) + modelContext.toggleFavouriteStation(shortCode: trainStation.shortCode) isFavourite.toggle() } label: { isFavourite ? Image(systemName: "heart.fill") : Image(systemName: "heart") } .onAppear { - isFavourite = modelContext.doesFavouriteStationExist(shortCode: trains.trainStation.shortCode) + isFavourite = modelContext.doesFavouriteStationExist(shortCode: trainStation.shortCode) } } } diff --git a/LuasWatch Watch App/Views/DetailView/StationToolbarLoading.swift b/LuasWatch Watch App/Views/DetailView/StationToolbarLoading.swift new file mode 100644 index 0000000..639ec84 --- /dev/null +++ b/LuasWatch Watch App/Views/DetailView/StationToolbarLoading.swift @@ -0,0 +1,52 @@ +// +// Created by Roland Gropmair on 10/02/2024. +// Copyright © 2024 mApps.ie. All rights reserved. +// + +import LuasKit +import SwiftUI + +struct StationToolbarLoading { + + var isFavourite: Bool + var direction: Direction + var isSwitchingDirectionEnabled: Bool +} + +extension StationToolbarLoading: ToolbarContent { + + var body: some ToolbarContent { + + ToolbarItemGroup(placement: .bottomBar) { + + /// Change direction + Button { + // nop + } label: { + + if isSwitchingDirectionEnabled { + + switch direction { + case .inbound: + Image(systemName: "arrow.left") + case .outbound: + Image(systemName: "arrow.right") + case .both: + Image(systemName: "arrow.left.arrow.right") + } + + } else { + // if station is terminal or a one way stop: show hard coded arrow; we disable the button below + Image(systemName: "arrow.right") + } + } + .disabled(!isSwitchingDirectionEnabled) + + Text(LuasStrings.loadingDueTimes) + .font(.subheadline) + + /// Favourite + isFavourite ? Image(systemName: "heart.fill") : Image(systemName: "heart") + } + } +} diff --git a/LuasWatch Watch App/Views/DetailView/StationView.swift b/LuasWatch Watch App/Views/DetailView/StationView.swift index 9d29ea7..b4e243e 100644 --- a/LuasWatch Watch App/Views/DetailView/StationView.swift +++ b/LuasWatch Watch App/Views/DetailView/StationView.swift @@ -35,18 +35,14 @@ extension StationView: View { case .errorGettingStation(let errorMessage): LuasTextView(text: errorMessage) - case .loadingDueTimes(_, let trains): - if let trains { - StationTimesView(trains: trains, isLoading: true) - } else { - LuasTextView(text: appModel.appState.description) - } + case .loadingDueTimes(let trainStation, let cachedTrains): + StationTimesView(trainStation: trainStation, trains: cachedTrains) case .errorGettingDueTimes(_, let message): LuasTextView(text: message) case .foundDueTimes(let trains): - StationTimesView(trains: trains) + StationTimesView(trainStation: trains.trainStation, trains: trains) } } } diff --git a/LuasWatch Watch App/Views/DetailView/ToolbarInactive.swift b/LuasWatch Watch App/Views/DetailView/ToolbarInactive.swift new file mode 100644 index 0000000..74b191f --- /dev/null +++ b/LuasWatch Watch App/Views/DetailView/ToolbarInactive.swift @@ -0,0 +1,36 @@ +// +// Created by Roland Gropmair on 10/02/2024. +// Copyright © 2024 mApps.ie. All rights reserved. +// + +import LuasKit +import SwiftUI + +struct ToolbarInactive { + // no properties +} + +extension ToolbarInactive: ToolbarContent { + + var body: some ToolbarContent { + + ToolbarItemGroup(placement: .bottomBar) { + + /// Change direction + Button { + // nop + } label: { + Image(systemName: "arrow.left.arrow.right") + } + .disabled(true) + + /// Favourite + Button { + // nop + } label: { + Image(systemName: "heart") + } + .disabled(true) + } + } +} diff --git a/LuasWatch Watch App/Views/DetailView/TrainsViewLoading.swift b/LuasWatch Watch App/Views/DetailView/TrainsViewLoading.swift new file mode 100644 index 0000000..7153077 --- /dev/null +++ b/LuasWatch Watch App/Views/DetailView/TrainsViewLoading.swift @@ -0,0 +1,22 @@ +// +// Created by Roland Gropmair on 25/05/2024. +// Copyright © 2024 mApps.ie. All rights reserved. +// + +import LuasKit +import SwiftUI + +struct TrainsViewLoading: View { + + var body: some View { + VStack { + Spacer() + Text(LuasStrings.trainsLoading) + .font(.caption2) + .monospaced() + .foregroundColor(.yellow) + .frame(maxWidth: .infinity) + Spacer() + } + } +} diff --git a/LuasWatch Watch App/Views/Sidebar/FavouritesSidebarView.swift b/LuasWatch Watch App/Views/Sidebar/FavouritesSidebarView.swift index 522f626..ddecbe0 100644 --- a/LuasWatch Watch App/Views/Sidebar/FavouritesSidebarView.swift +++ b/LuasWatch Watch App/Views/Sidebar/FavouritesSidebarView.swift @@ -73,7 +73,7 @@ extension FavouritesSidebarView: View { #if DEBUG #Preview("Favourites") { - let appModel = AppModel(AppModel.AppState(.foundDueTimes(trainsOneWayStation))) + let appModel = AppModel(AppState(.foundDueTimes(trainsOneWayStation))) appModel.appMode = .favourite(stationGreen) return List { diff --git a/LuasWatch Watch App/Views/Sidebar/NearbyStationsView.swift b/LuasWatch Watch App/Views/Sidebar/NearbyStationsView.swift index bbf2f36..bfa30b4 100644 --- a/LuasWatch Watch App/Views/Sidebar/NearbyStationsView.swift +++ b/LuasWatch Watch App/Views/Sidebar/NearbyStationsView.swift @@ -50,7 +50,7 @@ extension NearbyStationsView: View { #if DEBUG #Preview("Nearby") { - let appModel = AppModel(AppModel.AppState(.foundDueTimes(trainsOneWayStation))) + let appModel = AppModel(AppState(.foundDueTimes(trainsOneWayStation))) appModel.appMode = .favourite(stationGreen) return List { diff --git a/LuasWatch Watch App/Views/Sidebar/SidebarView.swift b/LuasWatch Watch App/Views/Sidebar/SidebarView.swift index ddd0247..e2be70a 100644 --- a/LuasWatch Watch App/Views/Sidebar/SidebarView.swift +++ b/LuasWatch Watch App/Views/Sidebar/SidebarView.swift @@ -101,7 +101,7 @@ extension SidebarView: View { #Preview("Sidebar") { @State var selectedStation: TrainStation? - let appModel = AppModel(AppModel.AppState(.foundDueTimes(trainsOneWayStation))) + let appModel = AppModel(AppState(.foundDueTimes(trainsOneWayStation))) appModel.appMode = .favourite(stationGreen) return SidebarView(selectedStation: $selectedStation) @@ -112,7 +112,7 @@ extension SidebarView: View { #Preview("Sidebar (loc denied)") { @State var selectedStation: TrainStation? - let appModel = AppModel(AppModel.AppState(.foundDueTimes(trainsOneWayStation))) + let appModel = AppModel(AppState(.foundDueTimes(trainsOneWayStation))) appModel.appMode = .favourite(stationGreen) appModel.locationDenied = true diff --git a/LuasWatch Watch App/Views/Sidebar/StationsModal.swift b/LuasWatch Watch App/Views/Sidebar/StationsModal.swift index 894320c..1b5f272 100644 --- a/LuasWatch Watch App/Views/Sidebar/StationsModal.swift +++ b/LuasWatch Watch App/Views/Sidebar/StationsModal.swift @@ -47,7 +47,7 @@ extension StationsModal { #if DEBUG #Preview("Stations Modal (green)") { - let appModel = AppModel(AppModel.AppState(.foundDueTimes(trainsGreen))) + let appModel = AppModel(AppState(.foundDueTimes(trainsGreen))) appModel.appMode = .specific(stationGreen) // highlight in preview doesn't work?? does it used StoredAppMode? diff --git a/LuasWatch.xcodeproj/project.pbxproj b/LuasWatch.xcodeproj/project.pbxproj index b4e2cf0..118832f 100644 --- a/LuasWatch.xcodeproj/project.pbxproj +++ b/LuasWatch.xcodeproj/project.pbxproj @@ -88,11 +88,17 @@ AB925F492B6FCD0800F23497 /* StationDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB925F482B6FCD0800F23497 /* StationDirection.swift */; }; AB925F4B2B715FAD00F23497 /* AppModel+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB925F4A2B715FAD00F23497 /* AppModel+Codable.swift */; }; AB925F4D2B715FCD00F23497 /* AppModel+CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB925F4C2B715FCD00F23497 /* AppModel+CustomStringConvertible.swift */; }; + AB9D3F092BF409EA00584B69 /* PreviewsLoadingStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB9D3F082BF409EA00584B69 /* PreviewsLoadingStates.swift */; }; ABA68B242AF43977002A9F0D /* LuasMainScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA68B232AF43977002A9F0D /* LuasMainScreen.swift */; }; ABC207AB2B78E34A00C1F8F7 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC207AA2B78E34A00C1F8F7 /* Utils.swift */; }; ABC207AD2B8156BB00C1F8F7 /* CLAuthorizationStatus+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC207AC2B8156BB00C1F8F7 /* CLAuthorizationStatus+Extensions.swift */; }; ABC207AF2B81653200C1F8F7 /* Coordinator+Animiation.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC207AE2B81653200C1F8F7 /* Coordinator+Animiation.swift */; }; - ABCDB4C42B5DADE40068EFAA /* PreviewsAllStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABCDB4C32B5DADE40068EFAA /* PreviewsAllStates.swift */; }; + ABC24A7B2C0289D30004014B /* StationToolbarLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC24A7A2C0289D30004014B /* StationToolbarLoading.swift */; }; + ABC24A7D2C028E120004014B /* TrainsViewLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC24A7C2C028E120004014B /* TrainsViewLoading.swift */; }; + ABC24A7F2C0525F50004014B /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC24A7E2C0525F50004014B /* AppState.swift */; }; + ABC24A812C0526950004014B /* AppMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC24A802C0526950004014B /* AppMode.swift */; }; + ABC24A832C052DBB0004014B /* ToolbarInactive.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC24A822C052DBB0004014B /* ToolbarInactive.swift */; }; + ABCDB4C42B5DADE40068EFAA /* PreviewsErrorStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABCDB4C32B5DADE40068EFAA /* PreviewsErrorStates.swift */; }; ABD1CA4E2B781DDD0015DB81 /* DueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD1CA4D2B781DDD0015DB81 /* DueView.swift */; }; ABD1CA502B781F730015DB81 /* LuasTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD1CA4F2B781F730015DB81 /* LuasTextView.swift */; }; ABD1CA522B78211D0015DB81 /* StationTimesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD1CA512B78211D0015DB81 /* StationTimesView.swift */; }; @@ -104,7 +110,7 @@ ABE7F1A02B8A62FC00AE0644 /* NoTrainsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABE7F19F2B8A62FC00AE0644 /* NoTrainsView.swift */; }; ABE7F1A22B8A63EC00AE0644 /* View+LuasFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABE7F1A12B8A63EC00AE0644 /* View+LuasFormatting.swift */; }; ABF0388F2B9265FD000034D6 /* DoubleTimetableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF0388E2B9265FD000034D6 /* DoubleTimetableView.swift */; }; - ABF038912B926AB5000034D6 /* OverflowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF038902B926AB5000034D6 /* OverflowView.swift */; }; + ABF038912B926AB5000034D6 /* OverflowDotsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF038902B926AB5000034D6 /* OverflowDotsView.swift */; }; ABF038932B933639000034D6 /* StationsModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF038922B933639000034D6 /* StationsModal.swift */; }; /* End PBXBuildFile section */ @@ -304,12 +310,18 @@ AB925F482B6FCD0800F23497 /* StationDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StationDirection.swift; sourceTree = ""; }; AB925F4A2B715FAD00F23497 /* AppModel+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppModel+Codable.swift"; sourceTree = ""; }; AB925F4C2B715FCD00F23497 /* AppModel+CustomStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppModel+CustomStringConvertible.swift"; sourceTree = ""; }; + AB9D3F082BF409EA00584B69 /* PreviewsLoadingStates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewsLoadingStates.swift; sourceTree = ""; }; ABA68B232AF43977002A9F0D /* LuasMainScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LuasMainScreen.swift; sourceTree = ""; }; ABAFF5B424ED58A8004DB119 /* LuasPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = LuasPlayground.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; ABC207AA2B78E34A00C1F8F7 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; ABC207AC2B8156BB00C1F8F7 /* CLAuthorizationStatus+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLAuthorizationStatus+Extensions.swift"; sourceTree = ""; }; ABC207AE2B81653200C1F8F7 /* Coordinator+Animiation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Coordinator+Animiation.swift"; sourceTree = ""; }; - ABCDB4C32B5DADE40068EFAA /* PreviewsAllStates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewsAllStates.swift; sourceTree = ""; }; + ABC24A7A2C0289D30004014B /* StationToolbarLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StationToolbarLoading.swift; sourceTree = ""; }; + ABC24A7C2C028E120004014B /* TrainsViewLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrainsViewLoading.swift; sourceTree = ""; }; + ABC24A7E2C0525F50004014B /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; + ABC24A802C0526950004014B /* AppMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMode.swift; sourceTree = ""; }; + ABC24A822C052DBB0004014B /* ToolbarInactive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolbarInactive.swift; sourceTree = ""; }; + ABCDB4C32B5DADE40068EFAA /* PreviewsErrorStates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewsErrorStates.swift; sourceTree = ""; }; ABD1CA4D2B781DDD0015DB81 /* DueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DueView.swift; sourceTree = ""; }; ABD1CA4F2B781F730015DB81 /* LuasTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LuasTextView.swift; sourceTree = ""; }; ABD1CA512B78211D0015DB81 /* StationTimesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StationTimesView.swift; sourceTree = ""; }; @@ -321,7 +333,7 @@ ABE7F19F2B8A62FC00AE0644 /* NoTrainsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoTrainsView.swift; sourceTree = ""; }; ABE7F1A12B8A63EC00AE0644 /* View+LuasFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+LuasFormatting.swift"; sourceTree = ""; }; ABF0388E2B9265FD000034D6 /* DoubleTimetableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleTimetableView.swift; sourceTree = ""; }; - ABF038902B926AB5000034D6 /* OverflowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverflowView.swift; sourceTree = ""; }; + ABF038902B926AB5000034D6 /* OverflowDotsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverflowDotsView.swift; sourceTree = ""; }; ABF038922B933639000034D6 /* StationsModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StationsModal.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -571,6 +583,8 @@ AB3CC01A2AE4094F002AA60A /* LuasKit.h */, AB3CC0112AE4094F002AA60A /* Models */, AB3CC0072AE4094F002AA60A /* API */, + ABC24A7E2C0525F50004014B /* AppState.swift */, + ABC24A802C0526950004014B /* AppMode.swift */, AB8220782B49F79200022CA0 /* AppModel.swift */, AB925F4A2B715FAD00F23497 /* AppModel+Codable.swift */, AB925F4C2B715FCD00F23497 /* AppModel+CustomStringConvertible.swift */, @@ -624,7 +638,8 @@ AB695E5E2AE41CAC00B6DB99 /* Previews */ = { isa = PBXGroup; children = ( - ABCDB4C32B5DADE40068EFAA /* PreviewsAllStates.swift */, + ABCDB4C32B5DADE40068EFAA /* PreviewsErrorStates.swift */, + AB9D3F082BF409EA00584B69 /* PreviewsLoadingStates.swift */, ABD285802B5DB3F00000011C /* PreviewsStationView.swift */, ABE7F19B2B8A5BEA00AE0644 /* PreviewsStationView_EdgeCases.swift */, AB695E602AE41CAC00B6DB99 /* PreviewsData.swift */, @@ -678,10 +693,13 @@ ABD1CA512B78211D0015DB81 /* StationTimesView.swift */, ABD1CA532B7826610015DB81 /* SimpleTimetableView.swift */, ABF0388E2B9265FD000034D6 /* DoubleTimetableView.swift */, - ABF038902B926AB5000034D6 /* OverflowView.swift */, + ABF038902B926AB5000034D6 /* OverflowDotsView.swift */, ABD1CA552B7828870015DB81 /* StationToolbar.swift */, + ABC24A7A2C0289D30004014B /* StationToolbarLoading.swift */, + ABC24A822C052DBB0004014B /* ToolbarInactive.swift */, ABD1CA4D2B781DDD0015DB81 /* DueView.swift */, ABE7F19F2B8A62FC00AE0644 /* NoTrainsView.swift */, + ABC24A7C2C028E120004014B /* TrainsViewLoading.swift */, ABE7F1A12B8A63EC00AE0644 /* View+LuasFormatting.swift */, ); path = DetailView; @@ -1147,6 +1165,7 @@ files = ( AB3CC01E2AE4094F002AA60A /* Parser.swift in Sources */, AB3CC0272AE4094F002AA60A /* TrainStations.swift in Sources */, + ABC24A812C0526950004014B /* AppMode.swift in Sources */, AB3CC0252AE4094F002AA60A /* TrainStation.swift in Sources */, AB3CC0262AE4094F002AA60A /* Train.swift in Sources */, AB925F4D2B715FCD00F23497 /* AppModel+CustomStringConvertible.swift in Sources */, @@ -1156,6 +1175,7 @@ AB3CC02A2AE4094F002AA60A /* MyUserDefaults.swift in Sources */, AB3CC0242AE4094F002AA60A /* TrainsByDirection.swift in Sources */, AB3CC0292AE4094F002AA60A /* Direction.swift in Sources */, + ABC24A7F2C0525F50004014B /* AppState.swift in Sources */, AB8220792B49F79200022CA0 /* AppModel.swift in Sources */, AB3CC01B2AE4094F002AA60A /* APIParser.swift in Sources */, AB925F4B2B715FAD00F23497 /* AppModel+Codable.swift in Sources */, @@ -1172,16 +1192,19 @@ buildActionMask = 2147483647; files = ( AB38CED02B52F28C0074D901 /* CLLocation+Extensions.swift in Sources */, + ABC24A7D2C028E120004014B /* TrainsViewLoading.swift in Sources */, + ABC24A7B2C0289D30004014B /* StationToolbarLoading.swift in Sources */, AB695E7D2AE41CAC00B6DB99 /* Coordinator.swift in Sources */, ABD1CA562B7828870015DB81 /* StationToolbar.swift in Sources */, AB0BFCF12B42ECEF0060AAD5 /* FavouriteStation.swift in Sources */, - ABF038912B926AB5000034D6 /* OverflowView.swift in Sources */, + ABF038912B926AB5000034D6 /* OverflowDotsView.swift in Sources */, AB695E762AE41CAC00B6DB99 /* GrantLocationAuthView.swift in Sources */, ABD285812B5DB3F00000011C /* PreviewsStationView.swift in Sources */, AB695E332AE41C1D00B6DB99 /* LuasWatchApp.swift in Sources */, ABC207AF2B81653200C1F8F7 /* Coordinator+Animiation.swift in Sources */, ABE7F1A02B8A62FC00AE0644 /* NoTrainsView.swift in Sources */, AB0BFCED2B42E7780060AAD5 /* FavouritesSidebarView.swift in Sources */, + ABC24A832C052DBB0004014B /* ToolbarInactive.swift in Sources */, AB0A86CE2B461767002E7E31 /* Collection+Extensions.swift in Sources */, AB0BFCEB2B42E58A0060AAD5 /* SidebarView.swift in Sources */, AB38CECE2B52F16C0074D901 /* Coordinator+LocationDelegate.swift in Sources */, @@ -1191,7 +1214,7 @@ ABE7F19C2B8A5BEA00AE0644 /* PreviewsStationView_EdgeCases.swift in Sources */, AB0BFCE92B42E5430060AAD5 /* StationView.swift in Sources */, AB695E712AE41CAC00B6DB99 /* PreviewsData.swift in Sources */, - ABCDB4C42B5DADE40068EFAA /* PreviewsAllStates.swift in Sources */, + ABCDB4C42B5DADE40068EFAA /* PreviewsErrorStates.swift in Sources */, AB89FB8B2B49B7B50020A943 /* LineRow.swift in Sources */, AB0A86CC2B460C74002E7E31 /* AllStationsListView.swift in Sources */, ABE7F1A22B8A63EC00AE0644 /* View+LuasFormatting.swift in Sources */, @@ -1204,6 +1227,7 @@ AB0BFCEF2B42ECA80060AAD5 /* Previews.swift in Sources */, ABD1CA502B781F730015DB81 /* LuasTextView.swift in Sources */, AB0BFCF42B43129F0060AAD5 /* NearbyStationsView.swift in Sources */, + AB9D3F092BF409EA00584B69 /* PreviewsLoadingStates.swift in Sources */, ABD1CA522B78211D0015DB81 /* StationTimesView.swift in Sources */, AB925F492B6FCD0800F23497 /* StationDirection.swift in Sources */, AB89FB892B49B6BE0020A943 /* StationRow.swift in Sources */, @@ -1280,7 +1304,7 @@ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 2H43Y8J8SC; ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -1303,7 +1327,7 @@ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 2H43Y8J8SC; ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -1576,9 +1600,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 122; + CURRENT_PROJECT_VERSION = 123; DEBUG_INFORMATION_FORMAT = dwarf; - DYLIB_CURRENT_VERSION = 121; + DYLIB_CURRENT_VERSION = 2; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1597,7 +1621,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; - MARKETING_VERSION = 2.0; + MARKETING_VERSION = 2.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -1644,9 +1668,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 122; + CURRENT_PROJECT_VERSION = 123; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_CURRENT_VERSION = 121; + DYLIB_CURRENT_VERSION = 2; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1659,7 +1683,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; - MARKETING_VERSION = 2.0; + MARKETING_VERSION = 2.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 3be2c28..c648f3b 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -8,7 +8,7 @@ default_platform(:ios) platform :ios do before_all do |lane, options| - xcversion(version: "15.0") + xcversion(version: "15.4") end def build_app