feat(ios): iPhone channel drill-down, fix chat compose box, unify chat+activity

- Channels tab is now a drill-down on iPhone: ChannelBrowserView lists top-level
  channels; ChannelDetailView shows the people in a channel, its sub-channels, and
  an explicit Join button (with password prompt). iPad split view unchanged.
- Extract self-contained UserRow (context menu + sheets) from UserListView so admin
  actions are reused in the drill-down.
- Fix off-screen chat compose box: pin VoiceControlsView via per-tab
  .safeAreaInset(edge: .bottom) instead of a floating overlay, so it reserves layout
  space above the tab bar (keeping the compose box visible, cooperating with keyboard
  avoidance) without covering the tab bar buttons.
- Collapse Activity into Chat like macOS/Windows: ChatView renders a merged,
  time-sorted timeline of messages + activity (activity rows in gray); remove the
  Activity tab and ActivityLogView.
- Label the RPSystemBroadcastPickerView inner UIButton for VoiceOver
  ("Share/Stop sharing screen audio") instead of relying on an outer SwiftUI label.
This commit is contained in:
2026-06-21 03:02:58 +02:00
parent c5ad080692
commit b07362e525
10 changed files with 419 additions and 187 deletions

View File

@@ -64,8 +64,6 @@ struct VoiceControlsView: View {
// SCREEN_AUDIO stream. Tinted while sharing.
BroadcastPickerButton(isSharing: session.voiceState.screenSharing)
.frame(width: 32, height: 32)
.accessibilityLabel(session.voiceState.screenSharing
? "Stop sharing screen audio" : "Share screen audio")
// Disconnect
Button(role: .destructive) {
@@ -134,6 +132,13 @@ private struct BroadcastPickerButton: UIViewRepresentable {
func updateUIView(_ uiView: RPSystemBroadcastPickerView, context: Context) {
uiView.tintColor = isSharing ? .systemGreen : .label
// RPSystemBroadcastPickerView owns an inner UIButton that VoiceOver focuses; its default
// label is the picker's image name ("module icon"). Label the inner button directly
// a SwiftUI .accessibilityLabel on the representable doesn't reach it.
let label = isSharing ? "Stop sharing screen audio" : "Share screen audio"
for case let button as UIButton in uiView.subviews {
button.accessibilityLabel = label
}
}
}