apps: show delivery receipts — "✓ Seen by <device>"

Both apps now poll engine.receipts() alongside nearby() and render a "Seen by
<names>" bar above the composer when acks come in. Closes the loop the receipt
engine opened: you can see your clipboard was received, instead of asking.

Verified live on macOS: copying on the Mac with a peer in the room shows
"✓ Seen by Rust LAN Peer".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 02:59:35 -05:00
parent cb0df2af81
commit cd0ac012e0
4 changed files with 38 additions and 0 deletions

View File

@@ -14,12 +14,30 @@ struct ContentView: View {
}
feedArea
Divider()
if !store.receipts.isEmpty {
seenByBar
Divider()
}
composer
}
.frame(minWidth: 480, minHeight: 400)
.task { store.connectIfSaved() }
}
// MARK: - Delivery receipts
private var seenByBar: some View {
let names = Array(Set(store.receipts.map(\.name))).sorted()
return HStack(spacing: 6) {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green).font(.caption)
Text("Seen by \(names.joined(separator: ", "))")
.font(.caption).foregroundStyle(.secondary).lineLimit(1)
Spacer()
}
.padding(.horizontal, 14).padding(.vertical, 5)
}
// MARK: - Nearby devices (AirDrop-style picker)
private var nearbyStrip: some View {

View File

@@ -26,6 +26,7 @@ final class MacTetherStore {
}()
var feed: [FeedItem] = []
var nearby: [Peer] = [] // tether devices on the LAN, for the picker
var receipts: [Receipt] = [] // delivery acks "seen by <device>"
var connected = false
var lastError: String?
var hasAttemptedConnect = false
@@ -68,6 +69,7 @@ final class MacTetherStore {
try? await Task.sleep(for: .seconds(2))
guard let self, let engine = self.engine else { continue }
self.nearby = engine.nearby().sorted { $0.name < $1.name }
self.receipts = engine.receipts()
}
}

View File

@@ -15,6 +15,10 @@ struct RoomView: View {
}
feedList
Divider()
if !store.receipts.isEmpty {
seenByBar
Divider()
}
composer
}
.navigationTitle("tether")
@@ -86,6 +90,18 @@ struct RoomView: View {
}
}
private var seenByBar: some View {
let names = Array(Set(store.receipts.map(\.name))).sorted()
return HStack(spacing: 6) {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green).font(.caption)
Text("Seen by \(names.joined(separator: ", "))")
.font(.caption).foregroundStyle(.secondary).lineLimit(1)
Spacer()
}
.padding(.horizontal, 14).padding(.vertical, 5)
}
private func sourceIcon(_ source: String) -> String {
if source.contains("iphone") || source.contains("ios") { return "iphone" }
if source.contains("ipad") { return "ipad" }

View File

@@ -31,6 +31,7 @@ final class TetherStore {
}()
var feed: [FeedItem] = []
var nearby: [Peer] = [] // tether devices on the LAN (empty on iOS w/o multicast entitlement)
var receipts: [Receipt] = [] // delivery acks "seen by <device>"
var connected = false
var lastError: String?
@@ -67,6 +68,7 @@ final class TetherStore {
try? await Task.sleep(for: .seconds(2))
guard let self, let engine = self.engine else { continue }
self.nearby = engine.nearby().sorted { $0.name < $1.name }
self.receipts = engine.receipts()
}
}
}