oficina / meet Public

Mac call: grid's last-row names clear the bar; a top-left self view clears the title

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 8b6b21d6ca808326c45439b93569982d0325749c parent bb7319e
3 files changed, +37 −7
  • native/apple/Sources/CallsInterop/CallSnapshots.swift +7 −0
  • native/apple/Sources/CallsKit/MacCallView.swift +3 −0
  • native/apple/Sources/CallsKit/MacFullWindowCall.swift +27 −7

CI passed — run details

CI log
=== vet ===
go vet ./...
=== fmt ===
=== test ===
go test ./...
ok  	amadan.net/oficina/calls/callscore	6.680s
?   	amadan.net/oficina/calls/cmd/calls	[no test files]
ok  	amadan.net/oficina/calls/internal/calls	1.451s
ok  	amadan.net/oficina/calls/internal/calls/apns	0.007s
ok  	amadan.net/oficina/calls/internal/calls/hub	0.303s
?   	amadan.net/oficina/calls/internal/calls/icons	[no test files]
ok  	amadan.net/oficina/calls/internal/calls/relay	0.003s
ok  	amadan.net/oficina/calls/internal/calls/seal	0.002s
ok  	amadan.net/oficina/calls/internal/callstest	8.515s
?   	amadan.net/oficina/calls/protocol	[no test files]
=== migration-check ===
go run amadan.net/rastrillo/rastrillo/cmd/rastrillo migration check
rastrillo migration check: models and migrations agree
=== race ===
CGO_ENABLED=1 go test -race ./internal/...
ok  	amadan.net/oficina/calls/internal/calls	8.816s
ok  	amadan.net/oficina/calls/internal/calls/apns	1.021s
ok  	amadan.net/oficina/calls/internal/calls/hub	3.264s
?   	amadan.net/oficina/calls/internal/calls/icons	[no test files]
ok  	amadan.net/oficina/calls/internal/calls/relay	1.018s
ok  	amadan.net/oficina/calls/internal/calls/seal	1.013s
ok  	amadan.net/oficina/calls/internal/callstest	24.660s
diff --git a/native/apple/Sources/CallsInterop/CallSnapshots.swift b/native/apple/Sources/CallsInterop/CallSnapshots.swift
index add0cb4..2d1336a 100644
--- a/native/apple/Sources/CallsInterop/CallSnapshots.swift
+++ b/native/apple/Sources/CallsInterop/CallSnapshots.swift
@@ -41,6 +41,13 @@ import SwiftUI
try await shot("notices-hidden", .notices, shown: false)
try await shot("classic-focus", .focus, style: .classic)
try await shot("classic-grid", .grid, grid: true, style: .classic)
+ // The self view in the corner the title pill shares: it sits below
+ // the pill while the controls show.
+ let corner = UserDefaults.standard.string(forKey: PiPCorner.key)
+ UserDefaults.standard.set(PiPCorner.topLeading.rawValue, forKey: PiPCorner.key)
+ try await shot("pip-top-leading-controls", .focus)
+ try await shot("pip-top-leading-hidden", .focus, shown: false)
+ UserDefaults.standard.set(corner, forKey: PiPCorner.key)
// The overlay over the worst pictures there are for it, in each of
// your own states: it should look the same in light and dark.
for backdrop in FixtureBackdrop.allCases {
diff --git a/native/apple/Sources/CallsKit/MacCallView.swift b/native/apple/Sources/CallsKit/MacCallView.swift
index c153497..2a1c893 100644
--- a/native/apple/Sources/CallsKit/MacCallView.swift
+++ b/native/apple/Sources/CallsKit/MacCallView.swift
@@ -18,6 +18,9 @@
@AppStorage(CallLayoutStyle.key) private var layoutChoice = CallLayoutStyle.defaultStyle.rawValue
@AppStorage(PiPCorner.key) var pipCorner = PiPCorner.bottomTrailing.rawValue
@State var pipDrag: CGSize = .zero
+ /// The title pill's height, so a self view in the top-left corner
+ /// sits below it rather than under it.
+ @State var titleHeight: CGFloat = 0
@State var menuTracking = false
@State var sheetShowing = false
@State var dismissedNotices: Set<String> = []
diff --git a/native/apple/Sources/CallsKit/MacFullWindowCall.swift b/native/apple/Sources/CallsKit/MacFullWindowCall.swift
index e6b3895..fdd5a54 100644
--- a/native/apple/Sources/CallsKit/MacFullWindowCall.swift
+++ b/native/apple/Sources/CallsKit/MacFullWindowCall.swift
@@ -181,30 +181,40 @@
let across =
fitted.width >= 200
? columns : max(1, Int((area.width + 4) / (tile.width + 4)))
+ let rows = (count + across - 1) / across
+ let gridHeight = CGFloat(rows) * tile.height + CGFloat(max(rows - 1, 0)) * 4
+ // The last row's names clear the bar while it shows: lifted by as
+ // much of the bar as reaches over them (the grid is centred).
+ let below = max(0, (size.height - 8 - gridHeight) / 2) + 4
+ let lastLift = max(0, lift - below)
+ let firstOfLastRow = (rows - 1) * across
ScrollView {
LazyVGrid(
columns: Array(repeating: GridItem(.fixed(tile.width), spacing: 4), count: across),
spacing: 4
) {
- ForEach(call.screens, id: \.self) { peer in
+ ForEach(Array(call.screens.enumerated()), id: \.element) { index, peer in
MacVideoTile(
track: call.remoteScreens[peer], label: screenCaption(call.name(of: peer)),
- screen: true, cornerRadius: 6, compact: true, backdrop: Self.tileBackdrop
+ screen: true, cornerRadius: 6, compact: true,
+ labelLift: index >= firstOfLastRow ? lastLift : 0, backdrop: Self.tileBackdrop
)
.frame(width: tile.width, height: tile.height)
}
MacVideoTile(
track: call.localTrack, label: "You", muted: call.muted, mirrored: true,
cameraOff: call.cameraOff, placeholder: call.selfPlaceholder, cornerRadius: 6,
- compact: true, backdrop: Self.tileBackdrop, audio: call.selfAudio
+ compact: true, labelLift: call.screens.count >= firstOfLastRow ? lastLift : 0,
+ backdrop: Self.tileBackdrop, audio: call.selfAudio
)
.frame(width: tile.width, height: tile.height)
- ForEach(peers) { peer in
+ ForEach(Array(peers.enumerated()), id: \.element.id) { index, peer in
MacVideoTile(
track: call.remoteTracks[peer.peer], label: peer.name,
muted: call.isMuted(peer.peer), cameraOff: call.isCameraOff(peer.peer),
- cornerRadius: 6, compact: true, backdrop: Self.tileBackdrop,
- audio: call.tileAudio(peer.peer)
+ cornerRadius: 6, compact: true,
+ labelLift: call.screens.count + 1 + index >= firstOfLastRow ? lastLift : 0,
+ backdrop: Self.tileBackdrop, audio: call.tileAudio(peer.peer)
)
.frame(width: tile.width, height: tile.height)
}
@@ -259,8 +269,14 @@
var trailing: CGFloat = 0
if showingChat { trailing += Self.panelWidth + 12 }
if stageShowsScreen, !peers.isEmpty { trailing += strip(in: size).width + 12 }
+ // The title pill sits beside the traffic lights while the controls
+ // show; a self view in that corner goes below it.
+ let corner = PiPCorner(rawValue: pipCorner) ?? .bottomTrailing
+ let top =
+ corner == .topLeading && chrome.visible
+ ? max(Self.titleBarHeight, 6 + titleHeight + 4) : Self.titleBarHeight
return EdgeInsets(
- top: Self.titleBarHeight, leading: 0,
+ top: top, leading: 0,
bottom: chrome.visible ? Self.barReserve - 12 : 0, trailing: trailing)
}
@@ -292,6 +308,10 @@
}
.padding(.horizontal, 12).padding(.vertical, 7)
.callOverlaySurface(RoundedRectangle(cornerRadius: 12, style: .continuous))
+ .background(GeometryReader { proxy in
+ Color.clear.onAppear { titleHeight = proxy.size.height }
+ .onChange(of: proxy.size.height) { _, height in titleHeight = height }
+ })
.accessibilityElement(children: .contain)
.accessibilityIdentifier("callTitle")
.frame(maxWidth: 420, alignment: .leading)