Changes
1 changed files (+132/-77)
-
-
@@ -110,6 +110,11 @@ }} } enum ConnectionState<TZone: Zone & Identifiable> { case authorizing case authorized(zone_id: String?, zones: [String: TZone]) } struct ConnectedView: View { private var conn: Connection
-
@@ -119,113 +124,163 @@private let queue = DispatchQueue(label: "plac.event-loop") var body: some View { PureConnectedView(zone_id: $zone_id, zones: zones) let state: Binding<ConnectionState<CoreZone>> = .init( get: { if let zone_id = zone_id { .authorized(zone_id: zone_id, zones: zones) } else { .authorizing } }, set: { newValue in switch newValue { case .authorizing: zone_id = nil case .authorized(let newZoneId, _): zone_id = newZoneId } } ) PureConnectedView(state: state) .onAppear { queue.async { let logger = Logger() startLoop() } } logger.debug("Starting getEvent loop...") init(server: CoreServer) { conn = Connection(server: server) } while true { let event = conn.getEvent() func startLoop() { queue.async { let logger = Logger() guard let event = event else { logger.debug("Got null event, quitting") return } logger.debug("Starting getEvent loop...") while true { let event = conn.getEvent() switch event { case .connected(_): conn.subscribeZoneChanges() break case .connectionError(let ev): logger.error("Failed to connect: \(ev.code.rawValue)") break case .zoneList(let ev): DispatchQueue.main.async { for zone in ev.addedZones { zones[zone.id] = zone } guard let event = event else { logger.debug("Got null event, quitting") return } for zone in ev.changedZones { zones[zone.id] = zone } switch event { case .connected(_): conn.subscribeZoneChanges() break case .connectionError(let ev): logger.error("Failed to connect: \(ev.code.rawValue)") break case .zoneList(let ev): DispatchQueue.main.async { for zone in ev.addedZones { zones[zone.id] = zone } for id in ev.removedZoneIds { zones.removeValue(forKey: id) if zone_id == id { zone_id = nil } } for zone in ev.changedZones { zones[zone.id] = zone } if zone_id == nil { zone_id = zones.first?.value.id } for id in ev.removedZoneIds { zones.removeValue(forKey: id) if zone_id == id { zone_id = nil } break } if zone_id == nil { zone_id = zones.first?.value.id } } break } } } } init(server: CoreServer) { conn = Connection(server: server) } } struct PureConnectedView<TZone: Zone & Identifiable>: View { @Binding var zone_id: String? var zones: [String: TZone] = [:] @Binding var state: ConnectionState<TZone> var body: some View { VStack(spacing: 0) { NavigationSplitView { List { NavigationLink { Text("LIBRARY") } label: { Text("Library") } switch state { case .authorized(let zone_id, let zones): let zoneIdProxy: Binding<String?> = .init( get: { zone_id }, set: { newId in state = .authorized(zone_id: newId, zones: zones) } ) NavigationLink { Text("DISCOVER") } label: { Text("Discover") VStack(spacing: 0) { NavigationSplitView { List { NavigationLink { Text("LIBRARY") } label: { Text("Library") } NavigationLink { Text("DISCOVER") } label: { Text("Discover") } } } detail: { } } detail: { } Divider() Divider() PlaybackBar<TZone>(zone_id: $zone_id, zones: zones) .frame(maxWidth: .infinity) PlaybackBar<TZone>(zone_id: zoneIdProxy, zones: zones) .frame(maxWidth: .infinity) } case .authorizing: ContentUnavailableView { Label( "Requesting Access", systemImage: "lock.circle.dotted" ) .symbolEffect(.pulse) } description: { Text( "Connecting to your Roon Server. Enable access at \"Settings > Extensions\" in official Roon Client application." ) } } } } #Preview("Layout") { @Previewable @State var zone_id: String? = "foo" @Previewable @State var state: ConnectionState<MockZone> = .authorized( zone_id: "foo", zones: [ "foo": MockZone( id: "foo", name: "Foo", playback: PlacKit.PLAC_TRANSPORT_PLAYBACK_PLAYING, nowPlaying: MockNowPlaying( line1: "My great song", line2: "Some untrusted artist", seek: (5, 3 * 60 + 30) ) ), "bar": MockZone( id: "bar", name: "Bar", playback: PlacKit.PLAC_TRANSPORT_PLAYBACK_STOPPED ), ] ) var zones: [String: MockZone] = [ "foo": MockZone( id: "foo", name: "Foo", playback: PlacKit.PLAC_TRANSPORT_PLAYBACK_PLAYING, nowPlaying: MockNowPlaying( line1: "My great song", line2: "Some untrusted artist", seek: (5, 3 * 60 + 30) ) ), "bar": MockZone( id: "bar", name: "Bar", playback: PlacKit.PLAC_TRANSPORT_PLAYBACK_STOPPED ), ] PureConnectedView<MockZone>(state: $state) } #Preview("Authorizing") { @Previewable @State var state: ConnectionState<MockZone> = .authorizing PureConnectedView<MockZone>(zone_id: $zone_id, zones: zones) PureConnectedView<MockZone>(state: $state) }
-