iOS: the app's settings in Settings, not behind a shake; iPhone upright (#445)
The server address and the player token were set from alerts that opened on shaking the phone, which nobody finds. Settings now has a This device section in the app: the server, the player token and its address, and a Make button that creates an API token and hands it straight to the app's keychain. The app sends what it has at ready (appSettings) and takes setServer, setToken and clearToken; a new server rebuilds the web view as the alert did. The first-launch prompt and the Cannot reach alert stay native, since there is no page then. The iPhone is portrait only; the iPad keeps all four orientations, which Split View and Slide Over need (#434 checked the iPad's layout). Build 14. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -45,7 +45,9 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
|
||||
view.addSubview(webView)
|
||||
|
||||
cookies = CookieBridge(store: cfg.websiteDataStore.httpCookieStore)
|
||||
cfg.userContentController.add(Bridge(webView: webView, playback: playback), name: Bridge.name)
|
||||
let bridge = Bridge(webView: webView, playback: playback)
|
||||
bridge.onServer = { [weak self] url in self?.open(url) }
|
||||
cfg.userContentController.add(bridge, name: Bridge.name)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
@@ -57,16 +59,13 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
|
||||
|
||||
// MARK: - which server
|
||||
|
||||
/// Shake to change it. A setting touched about once, so it takes no room on the screen.
|
||||
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
|
||||
if motion == .motionShake { askForServer() }
|
||||
}
|
||||
|
||||
/// Asked on first launch, and when the server cannot be reached; otherwise changed in the
|
||||
/// page's Settings (#445), which replaced shaking the phone for it.
|
||||
private func askForServer(_ problem: String? = nil) {
|
||||
guard presentedViewController == nil else { return }
|
||||
let alert = UIAlertController(
|
||||
title: "Your iPX server",
|
||||
message: problem ?? "The address you open iPX at. Shake the phone to change it later.",
|
||||
message: problem ?? "The address you open iPX at. You can change it later in Settings.",
|
||||
preferredStyle: .alert)
|
||||
alert.addTextField { f in
|
||||
f.text = Server.url?.absoluteString ?? Server.suggested
|
||||
@@ -76,9 +75,6 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
|
||||
f.clearButtonMode = .whileEditing
|
||||
}
|
||||
if Server.url != nil { alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) }
|
||||
alert.addAction(UIAlertAction(title: "Player Token…", style: .default) { [weak self] _ in
|
||||
self?.askForCredentials()
|
||||
})
|
||||
alert.addAction(UIAlertAction(title: "Open", style: .default) { [weak self, weak alert] _ in
|
||||
guard let self else { return }
|
||||
let text = alert?.textFields?.first?.text ?? ""
|
||||
@@ -87,51 +83,16 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
|
||||
+ "on your own network, such as 192.168.1.130:8099.")
|
||||
return
|
||||
}
|
||||
Server.url = url
|
||||
self.playback.stop()
|
||||
self.makeWebView()
|
||||
self.webView.load(URLRequest(url: url))
|
||||
self.open(url)
|
||||
})
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
/// The token the player and CarPlay use in place of the page's cookie, and the address they
|
||||
/// send it to (#419). An empty token field keeps the one there is; Clear removes it.
|
||||
private func askForCredentials(_ problem: String? = nil) {
|
||||
guard presentedViewController == nil else { return }
|
||||
let alert = UIAlertController(
|
||||
title: "Player token",
|
||||
message: problem ?? "An ipx API token (Settings, API tokens) so playback and CarPlay keep "
|
||||
+ "working when the page's sign-in ends. "
|
||||
+ (Credentials.isSet ? "Set now; leave the token empty to keep it." : "Not set."),
|
||||
preferredStyle: .alert)
|
||||
alert.addTextField { f in
|
||||
f.placeholder = "ipx API token"
|
||||
f.isSecureTextEntry = true
|
||||
f.autocapitalizationType = .none
|
||||
f.autocorrectionType = .no
|
||||
}
|
||||
alert.addTextField { f in
|
||||
f.text = Server.apiURL?.absoluteString ?? Server.suggestedAPI
|
||||
f.keyboardType = .URL
|
||||
f.autocapitalizationType = .none
|
||||
f.autocorrectionType = .no
|
||||
}
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: "Clear", style: .destructive) { _ in
|
||||
Credentials.apiToken = nil
|
||||
})
|
||||
alert.addAction(UIAlertAction(title: "Save", style: .default) { [weak self, weak alert] _ in
|
||||
let fields = alert?.textFields ?? []
|
||||
guard fields.count == 2, let url = Server.normalise(fields[1].text ?? ""),
|
||||
url.scheme == "https" else {
|
||||
self?.askForCredentials("The player address has to be https, such as \(Server.suggestedAPI).")
|
||||
return
|
||||
}
|
||||
Server.apiURL = url
|
||||
if let token = fields[0].text, !token.isEmpty { Credentials.apiToken = token }
|
||||
})
|
||||
present(alert, animated: true)
|
||||
private func open(_ url: URL) {
|
||||
Server.url = url
|
||||
playback.stop()
|
||||
makeWebView()
|
||||
webView.load(URLRequest(url: url))
|
||||
}
|
||||
|
||||
// MARK: - the page
|
||||
|
||||
Reference in New Issue
Block a user