Private
Public Access
1
0

Adds getting/sending messages

This commit is contained in:
2025-08-24 17:58:37 -07:00
parent b5a2f318b4
commit 3ee94a3bea
6 changed files with 345 additions and 2 deletions

View File

@@ -5,3 +5,127 @@
// Created by James Magahern on 8/24/25.
//
import SwiftUI
struct ChatTranscriptView: View
{
@Binding var model: ViewModel
var body: some View {
ScrollView {
LazyVStack(spacing: 17.0) {
ForEach($model.messages.reversed()) { message in
MessageCellView(message: message)
.id(message.id)
.scaleEffect(CGSize(width: 1.0, height: -1.0))
}
}
.padding()
}
.scaleEffect(CGSize(width: 1.0, height: -1.0))
}
// MARK: - Types
@Observable
class ViewModel
{
var messages: [Display.Message]
var displayedConversation: Display.Conversation.ID? = nil
init(messages: [Display.Message] = []) {
self.messages = messages
observeDisplayedConversation()
}
private func observeDisplayedConversation() {
withObservationTracking {
_ = displayedConversation
} onChange: {
Task { @MainActor [weak self] in
guard let self else { return }
await loadMessages()
observeDisplayedConversation()
}
}
}
private func loadMessages() async {
self.messages = []
guard let displayedConversation else { return }
do {
let client = XPCClient()
let messages = try await client.getMessages(conversationId: displayedConversation)
self.messages = messages.map { Display.Message(from: $0) }
} catch {
print("Message fetch error: \(error)")
}
}
}
}
struct MessageCellView: View
{
@Binding var message: Display.Message
var body: some View {
VStack {
HStack(alignment: .bottom) {
if message.isFromMe { Spacer(minLength: .minimumBubbleHorizontalPadding) }
TextCellContentView(text: message.text, isFromMe: message.isFromMe)
.mask {
UnevenRoundedRectangle(cornerRadii: RectangleCornerRadii(
topLeading: message.isFromMe ? .dominantCornerRadius : .minorCornerRadius,
bottomLeading: .dominantCornerRadius,
bottomTrailing: .dominantCornerRadius,
topTrailing: message.isFromMe ? .minorCornerRadius : .dominantCornerRadius,
))
}
if !message.isFromMe { Spacer(minLength: .minimumBubbleHorizontalPadding) }
}
}
}
// MARK: - Types
private struct TextCellContentView: View
{
let text: String
let isFromMe: Bool
var body: some View {
let bubbleColor: Color = isFromMe ? .blue : Color(.systemGray)
let textColor: Color = isFromMe ? .white : .primary
HStack {
Text(text)
.foregroundStyle(textColor)
.multilineTextAlignment(.leading)
}
.fixedSize(horizontal: false, vertical: true)
.padding(.horizontal, 16.0)
.padding(.vertical, 10.0)
.background(bubbleColor)
}
}
}
fileprivate extension CGFloat {
static let dominantCornerRadius = 16.0
static let minorCornerRadius = 4.0
static let minimumBubbleHorizontalPadding = 80.0
}
#Preview {
@Previewable @State var model = ChatTranscriptView.ViewModel(messages: [
.init(sender: .me, text: "Hello, how are you?"),
.init(sender: .counterpart("Bob"), text: "I am doing fine!")
])
ChatTranscriptView(model: $model)
}