Changes
2 changed files (+61/-41)
-
-
@@ -0,0 +1,28 @@// Copyright 2025 Shota FUJI // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 import SwiftUI extension EnvironmentValues { /// Connection to Roon server, if established. @Entry var placConnection: Connection? = nil } extension View { func placConnection(_ connection: Connection) -> some View { environment(\.placConnection, connection) } }
-
-
-
@@ -44,10 +44,11 @@ switch state {case .loading: ProgressView() case .found(let conn): ConnectedView(conn: conn) WithServer() .onAuthorize { token in savedToken = token } .placConnection(conn) case .findError(_): ContentUnavailableView { Label(
-
@@ -139,14 +140,14 @@ case authorizingcase authorized(zone_id: String?, zones: [String: TZone]) } struct ConnectedView: View { let conn: Connection struct WithServer: View { @Environment(\.placConnection) var conn: Connection? @State private var zone_id: String? = nil @State private var zones: [String: CoreZone] = [:] private let queue = DispatchQueue(label: "plac.event-loop") private let actionQueue = DispatchQueue(label: "plac.ConnectedView.actions") private let actionQueue = DispatchQueue(label: "plac.WithServer.actionQueue") var onAuthorizeAction: ((_ token: String) -> Void)?
-
@@ -171,23 +172,7 @@ }} ) PureConnectedView(state: state) .onPlaybackAction { action in guard let zone_id = zone_id else { return } guard let zone = zones[zone_id] else { return } await withUnsafeContinuation { cont in actionQueue.async { conn.control(zone, action) cont.resume() } } } ConnectedView(state: state) .task { do { try await startLoop() } catch { Logger().info("Cancelled message loop")
-
@@ -212,7 +197,7 @@ cont.resume(throwing: CancellationError())return } let event = conn.getEvent() let event = conn?.getEvent() if Task.isCancelled { cont.resume(throwing: CancellationError())
-
@@ -228,7 +213,7 @@switch event { case .connected(let ev): onAuthorizeAction?(ev.token) conn.subscribeZoneChanges() conn?.subscribeZoneChanges() break case .connectionError(let ev): logger.error("Failed to connect: \(ev.code.rawValue)")
-
@@ -267,25 +252,29 @@ }// FIXME: The below "close()" crashes. Once fixed, remove the above "terminate()" call. // actionQueue.async { // conn.close() // conn?.close() // } } ) } } extension ConnectedView { func onAuthorize(_ handler: @escaping (String) -> Void) -> ConnectedView { extension WithServer { func onAuthorize(_ handler: @escaping (String) -> Void) -> WithServer { var new = self new.onAuthorizeAction = handler return new } } struct PureConnectedView<TZone: Zone & Identifiable>: View { struct ConnectedView<TZone: Zone & Identifiable>: View { @Environment(\.placConnection) var conn: Connection? @Binding var state: ConnectionState<TZone> var playbackActionHandler: ((PlaybackAction) async -> Void)? private let actionQueue = DispatchQueue( label: "plac.ConnectedView.actionQueue" ) var body: some View { switch state {
-
@@ -317,7 +306,20 @@ Divider()PlaybackBar<TZone>(zone_id: zoneIdProxy, zones: zones) .onPlaybackAction { action in await playbackActionHandler?(action) guard let zone_id = zone_id, let conn = conn else { return } guard let zone = zones[zone_id] as? CoreZone else { return } await withUnsafeContinuation { cont in actionQueue.async { conn.control(zone, action) cont.resume() } } } .frame(maxWidth: .infinity) }
-
@@ -337,16 +339,6 @@ }} } extension PureConnectedView { func onPlaybackAction(_ handler: @escaping (PlaybackAction) async -> Void) -> PureConnectedView { var new = self new.playbackActionHandler = handler return new } } #Preview("Layout") { @Previewable @State var state: ConnectionState<MockZone> = .authorized( zone_id: "foo",
-
@@ -369,11 +361,11 @@ ),] ) PureConnectedView<MockZone>(state: $state) ConnectedView<MockZone>(state: $state) } #Preview("Authorizing") { @Previewable @State var state: ConnectionState<MockZone> = .authorizing PureConnectedView<MockZone>(state: $state) ConnectedView<MockZone>(state: $state) }
-