Changes
3 changed files (+350/-17)
-
-
@@ -538,6 +538,159 @@ }} } }, "ServerAddressForm.Connect" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "Connect" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "接続する" } } } }, "ServerAddressForm.Description" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "By tapping Connect, Plac will open a HTTP connection to %@" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "%@ に対して接続を試みます。" } } } }, "ServerAddressForm.Host.Label" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "IPv4 address" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "IPアドレス (v4)" } } } }, "ServerAddressForm.InvalidHost.Close" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "Close" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "閉じる" } } } }, "ServerAddressForm.InvalidHost.Description" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "\"%@\" is not a valid IPv4 address." } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "“%@” は有効な IP アドレスではありません。" } } } }, "ServerAddressForm.InvalidHost.Title" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "Fill valid IPv4 address" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "有効な IP アドレスを入力してください。" } } } }, "ServerAddressForm.Port.Label" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "HTTP port" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "HTTP ポート" } } } }, "ServerAddressForm.SectionName" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "Roon server" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Roon サーバ" } } } }, "ServerAddressForm.Title" : { "extractionState" : "extracted_with_value", "localizations" : { "en" : { "stringUnit" : { "state" : "new", "value" : "Connect to server" } }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "サーバへ接続" } } } }, "ServerDiscovery.Empty.Description" : { "localizations" : { "en" : {
-
-
-
@@ -138,24 +138,40 @@ }) .focusedSceneValue(self.model) } else { ServerDiscoveryScreen() .onConnect { server in self.model.model = ConnectionDataModel( serverID: server.id, host: server.host, port: server.port, onConnect: { token in Self.saveToken(token) } ) #if os(macOS) ServerDiscoveryScreen() .onConnect { server in self.model.model = ConnectionDataModel( serverID: server.id, host: server.host, port: server.port, onConnect: { token in Self.saveToken(token) } ) Self.saveToken() Self.saveConnection( serverID: server.id, host: server.host, port: server.port ) } Self.saveToken() Self.saveConnection( serverID: server.id, host: server.host, port: server.port ) } #else ServerAddressForm() .onConnect { host, port in self.model.model = ConnectionDataModel( host: host, port: port, onConnect: { token in Self.saveToken(token) } ) Self.saveToken() Self.saveConnection(host: host, port: port) } #endif } } .commands {
-
-
-
@@ -0,0 +1,164 @@// 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 Network import SwiftUI struct ServerAddressForm: View { @State private var host: String = "" @State private var port: UInt16 = 9330 @State private var isInvalidHost: Bool = false fileprivate var onConnectAction: ((_ host: String, _ port: UInt16) -> Void)? = nil private var portFormatter: NumberFormatter { let formatter = NumberFormatter() formatter.numberStyle = .none formatter.allowsFloats = false formatter.maximum = NSNumber(value: UInt16.max) formatter.minimum = 1 return formatter } private var resolvedHost: String? { guard let addr = IPv4Address(host) else { return nil } return "\(addr)" } var body: some View { NavigationStack { Form { Section { TextField(text: $host) { Text( String( localized: "ServerAddressForm.Host.Label", defaultValue: "IPv4 address" ) ) } .autocorrectionDisabled() TextField(value: $port, formatter: portFormatter) { Text( String( localized: "ServerAddressForm.Port.Label", defaultValue: "HTTP port" ) ) } .autocorrectionDisabled() } header: { Text( String( localized: "ServerAddressForm.SectionName", defaultValue: "Roon server" ) ) } footer: { if let resolvedHost = resolvedHost { Text( String( localized: "ServerAddressForm.Description", defaultValue: "By tapping Connect, Plac will open a HTTP connection to \("\(resolvedHost):\(port)")" ) ) } else { Text( String( localized: "ServerAddressForm.InvalidHost.Description", defaultValue: "\"\(host)\" is not a valid IPv4 address." ) ) } } } .navigationTitle( String( localized: "ServerAddressForm.Title", defaultValue: "Connect to server" ) ) .toolbar { ToolbarItem(placement: .confirmationAction) { Button { guard let resolvedHost = resolvedHost else { isInvalidHost = true return } onConnectAction?(resolvedHost, port) } label: { Text( String( localized: "ServerAddressForm.Connect", defaultValue: "Connect" ) ) } .alert( String( localized: "ServerAddressForm.InvalidHost.Title", defaultValue: "Fill valid IPv4 address" ), isPresented: $isInvalidHost ) { Button { isInvalidHost = false } label: { Text( String( localized: "ServerAddressForm.InvalidHost.Close", defaultValue: "Close" ) ) } } message: { Text( String( localized: "ServerAddressForm.InvalidHost.Description", defaultValue: "\"\(host)\" is not a valid IPv4 address." ) ) } } } } } } extension ServerAddressForm { func onConnect(_ handler: @escaping (_ host: String, _ port: UInt16) -> Void) -> ServerAddressForm { var new = self new.onConnectAction = handler return new } } #Preview { ServerAddressForm() .onConnect { host, port in print(host) print(port) } }
-