XuqmGroup-iOSSDK/XuqmDemo/Sources/Views/ChatView.swift

114 行
3.9 KiB
Swift

import SwiftUI
import XuqmSDK
struct ChatView: View {
let targetId: String
let targetName: String
let currentUserId: String
@StateObject private var viewModel = ChatViewModel()
@State private var scrollToBottom = false
var body: some View {
VStack(spacing: 0) {
if viewModel.connectionStatus != "已连接" {
Text(viewModel.connectionStatus)
.font(.caption)
.foregroundStyle(.white)
.padding(.vertical, 4)
.frame(maxWidth: .infinity)
.background(viewModel.connectionStatus.contains("错误") ? Color.red : Color.orange)
}
ScrollViewReader { proxy in
ScrollView {
LazyVStack(spacing: 8) {
ForEach(viewModel.messages.reversed(), id: \.id) { message in
MessageBubble(message: message, currentUserId: currentUserId)
.id(message.id)
.rotationEffect(.degrees(180))
}
if viewModel.isLoading {
ProgressView()
.padding()
.rotationEffect(.degrees(180))
}
}
.padding()
.rotationEffect(.degrees(180))
}
.onChange(of: viewModel.messages.count) { _ in
if let first = viewModel.messages.first {
withAnimation {
proxy.scrollTo(first.id, anchor: .bottom)
}
}
}
}
Divider()
HStack(spacing: 12) {
TextField("输入消息…", text: $viewModel.inputText, axis: .vertical)
.textFieldStyle(.roundedBorder)
.lineLimit(1...4)
Button {
viewModel.sendText()
} label: {
Image(systemName: "paperplane.fill")
.foregroundStyle(viewModel.inputText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? Color.gray : Color.accentColor)
}
.disabled(viewModel.inputText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
}
.padding()
}
.navigationTitle(targetName)
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.onAppear {
viewModel.setup(targetId: targetId, chatType: .single)
viewModel.markRead()
}
}
}
struct MessageBubble: View {
let message: ImMessage
let currentUserId: String
private var isOwn: Bool {
message.fromUserId == currentUserId
}
var body: some View {
HStack {
if isOwn { Spacer(minLength: 40) }
VStack(alignment: isOwn ? .trailing : .leading, spacing: 2) {
Text(parseMessageText(message))
.padding(.horizontal, 12)
.padding(.vertical, 8)
.background(isOwn ? Color.accentColor.opacity(0.15) : Color.gray.opacity(0.2))
.clipShape(RoundedRectangle(cornerRadius: 16))
.foregroundStyle(.primary)
HStack(spacing: 4) {
if isOwn {
Text(statusLabel(message.status))
.font(.caption2)
.foregroundStyle(.secondary)
}
Text(formatConversationTime(message.createdAt))
.font(.caption2)
.foregroundStyle(.secondary)
}
}
if !isOwn { Spacer(minLength: 40) }
}
}
}