diff options
Diffstat (limited to 'LearnMapKit/App')
| -rw-r--r-- | LearnMapKit/App/RefactoredApp.swift | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/LearnMapKit/App/RefactoredApp.swift b/LearnMapKit/App/RefactoredApp.swift new file mode 100644 index 0000000..fbd7417 --- /dev/null +++ b/LearnMapKit/App/RefactoredApp.swift @@ -0,0 +1,277 @@ +// +// RefactoredApp.swift +// LearnMapKit +// +// Refactored app entry point with dependency injection +// + +import SwiftUI +import ArgumentParser + +@main +struct RefactoredLearnMapKitApp: App { + private let container = DependencyContainer() + + init() { + setupFromCommandLineArguments() + } + + var body: some Scene { + WindowGroup { + MainContentView() + .environmentObject(container.makeAircraftTrackingViewModel()) + .environmentObject(container.configurationManager) + } + } + + private func setupFromCommandLineArguments() { + guard let args = parseCommandLineArguments() else { return } + + Task { + // Configure based on command line arguments + if let hostname = args.hostname, let port = args.port { + await configureNetworkMode(hostname: hostname, port: port) + } else if let inputFile = args.inputfile { + await configureFileMode(filePath: inputFile) + } + // Otherwise use default configuration + } + } + + private func parseCommandLineArguments() -> CommandLineArgs? { + do { + return try CommandLineArgs.parse() + } catch { + // If parsing fails, continue with default configuration + print("Command line parsing failed: \(error)") + return nil + } + } + + private func configureNetworkMode(hostname: String, port: Int) async { + let networkConfig = NetworkDataSourceConfiguration(hostname: hostname, port: port) + let config = AppConfiguration( + dataSource: networkConfig, + displaySettings: DisplaySettings(), + mapSettings: MapSettings(), + networkSettings: NetworkSettings() + ) + await container.configurationManager.updateConfiguration(config) + } + + private func configureFileMode(filePath: String) async { + let fileConfig = FileDataSourceConfiguration(filePath: filePath) + let config = AppConfiguration( + dataSource: fileConfig, + displaySettings: DisplaySettings(), + mapSettings: MapSettings(), + networkSettings: NetworkSettings() + ) + await container.configurationManager.updateConfiguration(config) + } +} + +// MARK: - Main Content View +struct MainContentView: View { + @EnvironmentObject var viewModel: AircraftTrackingViewModel + @EnvironmentObject var configurationManager: ConfigurationManager + + var body: some View { + NavigationView { + ZStack { + AircraftMapView( + aircraft: viewModel.aircraft, + selectedAircraft: viewModel.selectedAircraft, + mapRegion: $viewModel.mapRegion, + onAircraftSelected: { aircraft in + viewModel.selectAircraft(aircraft) + } + ) + + VStack { + Spacer() + HStack { + ConnectionStatusBar( + state: viewModel.connectionState, + aircraftCount: viewModel.aircraftCount + ) + Spacer() + } + .padding() + } + + if viewModel.isLoading { + LoadingOverlay() + } + } + .navigationTitle("Aircraft Tracking") + .toolbar { + ToolbarItemGroup(placement: .primaryAction) { + ConnectionControlButton( + connectionState: viewModel.connectionState, + onStart: { + Task { await viewModel.startTracking() } + }, + onStop: { + Task { await viewModel.stopTracking() } + }, + onReconnect: { + Task { await viewModel.reconnect() } + } + ) + + Button("Settings") { + viewModel.showingSettings = true + } + } + } + .sheet(isPresented: $viewModel.showingSettings) { + SettingsView() + .environmentObject(viewModel) + } + .alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) { + Button("OK") { + viewModel.clearError() + } + } message: { + Text(viewModel.errorMessage ?? "") + } + } + .task { + // Auto-start tracking when view appears + await viewModel.startTracking() + } + } +} + +// MARK: - Supporting Views +struct ConnectionStatusBar: View { + let state: DataSourceState + let aircraftCount: Int + + var body: some View { + HStack(spacing: 12) { + StatusIndicator(state: state) + Text(statusText) + .font(.caption) + .foregroundColor(.secondary) + } + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 8)) + } + + private var statusText: String { + switch state { + case .connected: + return "Connected • \(aircraftCount) aircraft" + case .connecting: + return "Connecting..." + case .disconnected: + return "Disconnected" + case .error(let message): + return "Error: \(message)" + case .idle: + return "Ready" + case .configuring: + return "Configuring..." + } + } +} + +struct StatusIndicator: View { + let state: DataSourceState + + var body: some View { + Circle() + .fill(indicatorColor) + .frame(width: 8, height: 8) + } + + private var indicatorColor: Color { + switch state { + case .connected: + return .green + case .connecting, .configuring: + return .orange + case .disconnected, .idle: + return .gray + case .error: + return .red + } + } +} + +struct ConnectionControlButton: View { + let connectionState: DataSourceState + let onStart: () -> Void + let onStop: () -> Void + let onReconnect: () -> Void + + var body: some View { + Button(action: buttonAction) { + Image(systemName: buttonIconName) + } + } + + private var buttonIconName: String { + switch connectionState { + case .connected: + return "stop.circle" + case .connecting, .configuring: + return "stop.circle" + case .disconnected, .idle: + return "play.circle" + case .error: + return "arrow.clockwise.circle" + } + } + + private func buttonAction() { + switch connectionState { + case .connected, .connecting, .configuring: + onStop() + case .disconnected, .idle: + onStart() + case .error: + onReconnect() + } + } +} + +struct LoadingOverlay: View { + var body: some View { + ZStack { + Color.black.opacity(0.3) + .ignoresSafeArea() + + VStack(spacing: 16) { + ProgressView() + .scaleEffect(1.2) + Text("Connecting...") + .font(.caption) + .foregroundColor(.secondary) + } + .padding() + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) + } + } +} + +// MARK: - Command Line Arguments (Updated) +struct CommandLineArgs: ParsableCommand { + @Option(name: .shortAndLong, help: "Hostname for network mode") + var hostname: String? + + @Option(name: .shortAndLong, help: "Port for network mode") + var port: Int? + + @Option(name: .shortAndLong, help: "Input file path for file mode") + var inputfile: String? + + @Flag(name: .shortAndLong, help: "Enable debug mode") + var debug: Bool = false + + @Flag(name: .shortAndLong, help: "Show version information") + var version: Bool = false +}
\ No newline at end of file |
