XuqmGroup-iOSSDK/XuqmDemo/Sources/Views/ChatView.swift
XuqmGroup e7067d03cb feat(sdk): 更新 SDK 设计文档和 API 重构
- 添加 expiresAt 和 refreshUserSig 参数支持自动续签
- 修改 PushSDK 初始化方式,自动完成设备注册和厂商初始化
- 调整过期续签策略,从提前 15 分钟改为提前 5 分钟触发
- 重构 RN SDK 文档结构,简化安装和使用方式
- 更新统一登录流程,支持 profile 信息传递
- 添加 IM 数据库自动隔离功能
- 修复 Android 群消息聚合问题
- 补充自动化测试验证和错误处理机制
2026-05-01 21:27:39 +08:00

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) }
}
}
}