Skip to content

Commit 6a50619

Browse files
committed
Move basic views
1 parent f756f36 commit 6a50619

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2025 LiveKit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import SwiftUI
18+
19+
/// A drop-in replacement `Button` that executes an async action and shows a busy label when in progress.
20+
///
21+
/// - Parameters:
22+
/// - action: The async action to execute.
23+
/// - label: The label to show when not busy.
24+
/// - busyLabel: The label to show when busy. Defaults to an empty view.
25+
public struct AsyncButton<Label: View, BusyLabel: View>: View {
26+
private let action: () async -> Void
27+
28+
@ViewBuilder private let label: Label
29+
@ViewBuilder private let busyLabel: BusyLabel
30+
31+
@State private var isBusy = false
32+
33+
public init(
34+
action: @escaping () async -> Void,
35+
@ViewBuilder label: () -> Label,
36+
@ViewBuilder busyLabel: () -> BusyLabel = EmptyView.init
37+
) {
38+
self.action = action
39+
self.label = label()
40+
self.busyLabel = busyLabel()
41+
}
42+
43+
public var body: some View {
44+
Button {
45+
isBusy = true
46+
Task {
47+
await action()
48+
isBusy = false
49+
}
50+
} label: {
51+
if isBusy {
52+
if busyLabel is EmptyView {
53+
label
54+
} else {
55+
busyLabel
56+
}
57+
} else {
58+
label
59+
}
60+
}
61+
.disabled(isBusy)
62+
}
63+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2025 LiveKit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import LiveKit
18+
import SwiftUI
19+
20+
public struct ChatScrollView<Content: View>: View {
21+
public typealias MessageBuilder = (ReceivedMessage) -> Content
22+
23+
@LKConversation private var conversation
24+
@ViewBuilder private let messageBuilder: MessageBuilder
25+
26+
public init(messageBuilder: @escaping MessageBuilder) {
27+
self.messageBuilder = messageBuilder
28+
}
29+
30+
public var body: some View {
31+
ScrollViewReader { scrollView in
32+
ScrollView {
33+
LazyVStack {
34+
ForEach(conversation.messages.values.reversed()) { message in
35+
messageBuilder(message)
36+
.upsideDown()
37+
.id(message.id)
38+
}
39+
}
40+
}
41+
.onChange(of: conversation.messages.count) { _ in
42+
scrollView.scrollTo(conversation.messages.keys.last)
43+
}
44+
.upsideDown()
45+
.animation(.default, value: conversation.messages)
46+
}
47+
}
48+
}
49+
50+
private struct UpsideDown: ViewModifier {
51+
func body(content: Content) -> some View {
52+
content
53+
.rotationEffect(.radians(Double.pi))
54+
.scaleEffect(x: -1, y: 1, anchor: .center)
55+
}
56+
}
57+
58+
private extension View {
59+
func upsideDown() -> some View {
60+
modifier(UpsideDown())
61+
}
62+
}

Sources/LiveKitComponents/UI/Visualizer/BarAudioVisualizer.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ public struct BarAudioVisualizer: View {
108108
animationProperties = PhaseAnimationProperties(barCount: barCount)
109109
}
110110

111+
public init(agent: Agent?,
112+
barColor: Color = .primary,
113+
barCount: Int = 5,
114+
barCornerRadius: CGFloat = 100,
115+
barSpacingFactor: CGFloat = 0.015,
116+
barMinOpacity: CGFloat = 0.16,
117+
isCentered: Bool = true)
118+
{
119+
self.init(audioTrack: agent?.audioTrack, agentState: agent?.state ?? .listening, barColor: barColor, barCount: barCount, barCornerRadius: barCornerRadius, barSpacingFactor: barSpacingFactor, barMinOpacity: barMinOpacity, isCentered: isCentered)
120+
}
121+
111122
public var body: some View {
112123
GeometryReader { geometry in
113124
let highlightingSequence = animationProperties.highlightingSequence(agentState: agentState)

0 commit comments

Comments
 (0)