89 lines
3.0 KiB
Swift
89 lines
3.0 KiB
Swift
import SwiftUI
|
|
|
|
// MARK: - Minimized Call Bar (Telegram-style)
|
|
|
|
/// Telegram-style call status bar that sits at the very top of the screen,
|
|
/// extending into the safe area (status bar region). Content is centered
|
|
/// in the bottom 24pt of the bar. Tapping ANYWHERE on the gradient
|
|
/// (including the status bar area) expands back to full-screen.
|
|
struct MinimizedCallBar: View {
|
|
@ObservedObject var callManager: CallManager
|
|
|
|
private var state: CallUiState {
|
|
callManager.uiState
|
|
}
|
|
|
|
private var durationText: String {
|
|
let duration = max(state.durationSec, 0)
|
|
let minutes = duration / 60
|
|
let seconds = duration % 60
|
|
return String(format: "%d:%02d", minutes, seconds)
|
|
}
|
|
|
|
// Telegram colors: green=speaking, blue=muted/active, gray=connecting
|
|
private var gradientColors: [Color] {
|
|
switch state.phase {
|
|
case .active:
|
|
if state.isMuted {
|
|
// Blue gradient (muted)
|
|
return [Color(hex: 0x007FFF), Color(hex: 0x00AFFE)]
|
|
} else {
|
|
// Green gradient (speaking)
|
|
return [Color(hex: 0x33C659), Color(hex: 0x00A0B9)]
|
|
}
|
|
case .incoming:
|
|
return [Color(hex: 0x33C659), Color(hex: 0x00A0B9)]
|
|
case .outgoing, .keyExchange, .webRtcExchange:
|
|
// Gray (connecting)
|
|
return [Color(hex: 0xB6B6BB), Color(hex: 0xB6B6BB)]
|
|
case .ended:
|
|
return [Color(hex: 0xEF436C), Color(hex: 0xC0508D)]
|
|
case .idle:
|
|
return [Color(hex: 0x007FFF), Color(hex: 0x00AFFE)]
|
|
}
|
|
}
|
|
|
|
private var statusText: String {
|
|
switch state.phase {
|
|
case .active: return durationText
|
|
case .incoming: return "Tap to Answer"
|
|
case .outgoing: return "Ringing..."
|
|
case .keyExchange, .webRtcExchange: return "Connecting..."
|
|
case .ended: return "Ended"
|
|
case .idle: return ""
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
// Content stays BELOW Dynamic Island. Only gradient extends behind it.
|
|
HStack(spacing: 5) {
|
|
Image(systemName: "phone.fill")
|
|
.font(.system(size: 12, weight: .bold))
|
|
|
|
Text(state.displayName)
|
|
.font(.system(size: 13, weight: .semibold))
|
|
.lineLimit(1)
|
|
|
|
Text(statusText)
|
|
.font(.system(size: 13, weight: .regular).monospacedDigit())
|
|
}
|
|
.foregroundStyle(.white)
|
|
.frame(height: 28)
|
|
.frame(maxWidth: .infinity)
|
|
.contentShape(Rectangle())
|
|
.onTapGesture {
|
|
callManager.expandCall()
|
|
}
|
|
// Block swipe gestures — bar responds to taps only.
|
|
.highPriorityGesture(DragGesture())
|
|
.background(
|
|
LinearGradient(
|
|
colors: gradientColors,
|
|
startPoint: .leading,
|
|
endPoint: .trailing
|
|
)
|
|
.ignoresSafeArea(edges: .top) // Only gradient extends behind Dynamic Island
|
|
)
|
|
}
|
|
}
|