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:
@@ -62,6 +62,37 @@ final class Bridge: NSObject, WKScriptMessageHandler {
|
||||
"keeping": Offline.shared.downloading.sorted()])
|
||||
}
|
||||
|
||||
/// A new server, checked, for the web view to load in place of this page (#445).
|
||||
var onServer: ((URL) -> Void)?
|
||||
|
||||
/// The app's own settings for the page's Settings to show (#445); `saved` says a change took.
|
||||
private func sendAppSettings(saved: Bool = false) {
|
||||
send(["t": "appSettings", "server": Server.url?.absoluteString ?? Server.suggested,
|
||||
"api": Server.apiURL?.absoluteString ?? Server.suggestedAPI,
|
||||
"tokenSet": Credentials.apiToken != nil, "saved": saved])
|
||||
}
|
||||
|
||||
/// The page's Settings, setting what an alert on shaking the phone used to (#445).
|
||||
private func setToken(_ m: [String: Any]) {
|
||||
guard let url = Server.normalise(m["api"] as? String ?? ""), url.scheme == "https" else {
|
||||
send(["t": "appSettingsError",
|
||||
"message": "The player address has to be https, such as \(Server.suggestedAPI)."])
|
||||
return
|
||||
}
|
||||
Server.apiURL = url
|
||||
if let token = m["token"] as? String, !token.isEmpty { Credentials.apiToken = token }
|
||||
sendAppSettings(saved: true)
|
||||
}
|
||||
|
||||
private func setServer(_ text: String) {
|
||||
guard let url = Server.normalise(text) else {
|
||||
send(["t": "appSettingsError", "message": "That is not an address the app can open. Plain http "
|
||||
+ "only works on your own network, such as 192.168.1.130:8099."])
|
||||
return
|
||||
}
|
||||
onServer?(url)
|
||||
}
|
||||
|
||||
/// What is loaded, for the page's player bar to take up (#442): the car's pick, or an episode
|
||||
/// playing when the page loads, as when the app was opened from CarPlay. Without it the sound
|
||||
/// went on with no bar on the phone to pause it.
|
||||
@@ -91,7 +122,8 @@ final class Bridge: NSObject, WKScriptMessageHandler {
|
||||
if let r = m["rate"] as? Double { playback.setRate(Float(r)) }
|
||||
if let v = m["volume"] as? Double { playback.setVolume(Float(v)) }
|
||||
// What this build can do, so a newer page offers nothing it would ask for in vain.
|
||||
send(["t": "host", "can": ["sleep", "route"]])
|
||||
send(["t": "host", "can": ["sleep", "route", "settings"]])
|
||||
sendAppSettings()
|
||||
// A page reloaded while the timer runs is told how long is left.
|
||||
if let at = playback.sleepAt { send(["t": "sleep", "left": max(0, at.timeIntervalSinceNow)]) }
|
||||
sendKept()
|
||||
@@ -123,6 +155,11 @@ final class Bridge: NSObject, WKScriptMessageHandler {
|
||||
case "route": showRoutes(m)
|
||||
case "haptic": haptic(m["kind"] as? String)
|
||||
case "unread": if let n = m["n"] as? Int { Badge.set(n) }
|
||||
case "setServer": setServer(m["url"] as? String ?? "")
|
||||
case "setToken": setToken(m)
|
||||
case "clearToken":
|
||||
Credentials.apiToken = nil
|
||||
sendAppSettings(saved: true)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user