iOS: presses are felt (#430)
The page asks the app for a haptic ({t:"haptic", kind}) and the app
plays the phone's own. A light tap for play, pause, flag and pull to
refresh; the system's success for marking read, one item or all; its
warning with an error toast. A browser has no host to ask, and an
older app ignores the message, so there is nothing to gate.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,3 +11,4 @@ The iOS app, `ios/`. Format: [Keep a Changelog](https://keepachangelog.com/en/1.
|
||||
- The icon shows All Subscriptions' unread count, as the page's badge does: set when the app comes forward, after an item is marked read, and by a refresh iOS runs now and then while the app is closed, so it can be hours behind (#421).
|
||||
- Episodes kept on the phone, with the page's Keep button, play with no connection: the app fetches the file in the background, even with the app closed, and plays it from the phone; where you got to and what you finished offline is sent once the connection is back. Signing out removes them (#425).
|
||||
- A sleep timer in the player, kept by the phone, so it pauses on time with the screen locked, fading out over the last ten seconds; and an AirPlay button beside it, for another speaker or a TV (#429).
|
||||
- Presses are felt: a light tap on play, pause, flag and pull to refresh, a success on marking read, a warning with an error (#430).
|
||||
|
||||
@@ -72,6 +72,7 @@ Page to app, on `webkit.messageHandlers.ipx`:
|
||||
| `{t:"keep", url}` `{t:"unkeep", url}` | keep a file on the phone, or let it go: the page's path for it, `/media/42` or `/media/42/deadair` (#425) |
|
||||
| `{t:"clearKept"}` | signing out: everything kept goes |
|
||||
| `{t:"sleep", secs}` | pause after `secs`, fading out over the last ten; 0 turns the timer off (#429) |
|
||||
| `{t:"haptic", kind}` | a press felt: `tap`, `done` (marked read) or `warn` (an error) (#430) |
|
||||
| `{t:"route", x, y, w, h}` | open the AirPlay picker, pointing at the page's button, in the web view's points (#429) |
|
||||
|
||||
App to page, as `window.ipxNative.on(...)`:
|
||||
|
||||
@@ -14,6 +14,8 @@ final class Bridge: NSObject, WKScriptMessageHandler {
|
||||
|
||||
private weak var webView: WKWebView?
|
||||
private let playback: Playback
|
||||
private let impact = UIImpactFeedbackGenerator(style: .light)
|
||||
private let notice = UINotificationFeedbackGenerator()
|
||||
/// The system's AirPlay picker, which has no API to open it but its own button: kept on the
|
||||
/// web view, invisible, and pressed when the page's button is.
|
||||
private lazy var routePicker: AVRoutePickerView = {
|
||||
@@ -91,6 +93,7 @@ final class Bridge: NSObject, WKScriptMessageHandler {
|
||||
case "clearKept": Offline.shared.removeAll()
|
||||
case "sleep": playback.sleep(after: m["secs"] as? Double ?? 0)
|
||||
case "route": showRoutes(m)
|
||||
case "haptic": haptic(m["kind"] as? String)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
@@ -110,6 +113,16 @@ final class Bridge: NSObject, WKScriptMessageHandler {
|
||||
button.sendActions(for: primary ? .primaryActionTriggered : .touchUpInside)
|
||||
}
|
||||
|
||||
/// The page's presses felt (#430): light for a tap, the system's success and warning for
|
||||
/// something done and something gone wrong, as Mail and Messages use them.
|
||||
private func haptic(_ kind: String?) {
|
||||
switch kind {
|
||||
case "done": notice.notificationOccurred(.success)
|
||||
case "warn": notice.notificationOccurred(.warning)
|
||||
default: impact.impactOccurred()
|
||||
}
|
||||
}
|
||||
|
||||
private static func controls(in v: UIView) -> [UIControl] {
|
||||
v.subviews.flatMap { ($0 as? UIControl).map { [$0] } ?? controls(in: $0) }
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ targets:
|
||||
properties:
|
||||
CFBundleDisplayName: iPX
|
||||
CFBundleShortVersionString: $(MARKETING_VERSION)
|
||||
CFBundleVersion: "7"
|
||||
CFBundleVersion: "8"
|
||||
# Playing on with the screen locked. A plist key, not a signed entitlement, so it works on
|
||||
# a free personal team; CarPlay's own dashboard app is an entitlement Apple has to grant.
|
||||
UIBackgroundModes: [audio, fetch]
|
||||
|
||||
Reference in New Issue
Block a user