examples/simple-chatbot: move clients to client directory
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import SwiftUI
|
||||
|
||||
struct MicrophoneView: View {
|
||||
var audioLevel: Float // Current audio level
|
||||
var isMuted: Bool // Muted state
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
let width = geometry.size.width
|
||||
let circleSize = width * 0.9
|
||||
let innerCircleSize = width * 0.82
|
||||
let audioCircleSize = CGFloat(audioLevel) * (width * 0.95)
|
||||
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(Color.gray, lineWidth: 1)
|
||||
.frame(width: circleSize)
|
||||
|
||||
Circle()
|
||||
.fill(isMuted ? Color.disabledMic : Color.backgroundCircle)
|
||||
.frame(width: innerCircleSize)
|
||||
|
||||
if !isMuted {
|
||||
Circle()
|
||||
.fill(Color.micVolume)
|
||||
.opacity(0.5)
|
||||
.frame(width: audioCircleSize)
|
||||
.animation(.easeInOut(duration: 0.2), value: audioLevel)
|
||||
}
|
||||
|
||||
Image(systemName: isMuted ? "mic.slash.fill" : "mic.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: width * 0.2)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity) // Ensures the ZStack is centered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MicrophoneView(audioLevel: 1, isMuted: false)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ToastModifier: ViewModifier {
|
||||
var message: String?
|
||||
var isShowing: Bool
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
ZStack {
|
||||
content
|
||||
if isShowing, let message = message {
|
||||
VStack {
|
||||
Text(message)
|
||||
.padding()
|
||||
.background(Color.black.opacity(0.7))
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(8)
|
||||
.transition(.slide)
|
||||
.padding(.top, 50)
|
||||
Spacer()
|
||||
}
|
||||
.animation(.easeInOut(duration: 0.5), value: isShowing)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func toast(message: String?, isShowing: Bool) -> some View {
|
||||
self.modifier(ToastModifier(message: message, isShowing: isShowing))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import SwiftUI
|
||||
|
||||
struct WaveformView: View {
|
||||
|
||||
var audioLevel: Float
|
||||
var isBotReady: Bool
|
||||
var voiceClientStatus: String
|
||||
|
||||
@State
|
||||
private var audioLevels: [Float] = Array(repeating: 0, count: 5)
|
||||
private let dotCount = 5
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
VStack {
|
||||
Spacer()
|
||||
HStack {
|
||||
Spacer()
|
||||
ZStack {
|
||||
// Outer gray border
|
||||
Circle()
|
||||
.stroke(Color.gray, lineWidth: 1)
|
||||
.frame(width: geometry.size.width * 0.9, height: geometry.size.width * 0.9)
|
||||
|
||||
// Gray middle
|
||||
Circle()
|
||||
.fill(isBotReady ? Color.backgroundCircle : Color.backgroundCircleNotConnected)
|
||||
.frame(width: geometry.size.width * 0.82, height: geometry.size.width * 0.82)
|
||||
|
||||
if isBotReady {
|
||||
if audioLevel > 0 {
|
||||
// Waveform bars inside the circle
|
||||
HStack(spacing: 10) {
|
||||
ForEach(0..<dotCount, id: \.self) { index in
|
||||
Rectangle()
|
||||
.fill(Color.white)
|
||||
.frame(height: CGFloat(audioLevels[index]) * (geometry.size.height))
|
||||
.cornerRadius(12)
|
||||
.animation(.easeInOut(duration: 0.2), value: audioLevels[index])
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.horizontal, 5)
|
||||
}
|
||||
.frame(width: geometry.size.width * 0.5, height: geometry.size.width * 0.5)
|
||||
.mask(Circle().frame(width: geometry.size.width * 0.82, height: geometry.size.width * 0.82))
|
||||
} else {
|
||||
// Dots inside the circle
|
||||
HStack(spacing: 10) {
|
||||
ForEach(0..<dotCount, id: \.self) { _ in
|
||||
Circle()
|
||||
.fill(Color.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.frame(width: geometry.size.width * 0.5, height: geometry.size.height * 0.5)
|
||||
}
|
||||
} else {
|
||||
// Gray circle with loading icon when not connected
|
||||
VStack {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||||
.scaleEffect(2) // Adjust size of the loading spinner
|
||||
.padding()
|
||||
Text(voiceClientStatus)
|
||||
.foregroundColor(.white)
|
||||
.font(.headline)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.onChange(of: audioLevel) { oldLevel, newLevel in
|
||||
// The audio level that we receive from the bot is usually too low
|
||||
// so just increasing it so we can see a better graph but
|
||||
// making sure that it is not higher than the maximum 1
|
||||
var audioLevel = audioLevel + 0.4
|
||||
if(audioLevel > 1) {
|
||||
audioLevel = 1
|
||||
}
|
||||
// Update the array and shift values
|
||||
audioLevels.removeFirst()
|
||||
audioLevels.append(newLevel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
WaveformView(audioLevel: 0, isBotReady: false, voiceClientStatus: "idle")
|
||||
}
|
||||
Reference in New Issue
Block a user