129 行
4.3 KiB
Swift
129 行
4.3 KiB
Swift
|
|
import SwiftUI
|
||
|
|
import XuqmSDK
|
||
|
|
|
||
|
|
struct ConversationListView: View {
|
||
|
|
@StateObject private var viewModel = ConversationViewModel()
|
||
|
|
@Binding var path: NavigationPath
|
||
|
|
|
||
|
|
var body: some View {
|
||
|
|
List {
|
||
|
|
if viewModel.totalUnreadCount > 0 {
|
||
|
|
Text("总未读 \(viewModel.totalUnreadCount)")
|
||
|
|
.font(.caption)
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
}
|
||
|
|
|
||
|
|
ForEach(viewModel.filteredConversations, id: \.targetId) { conversation in
|
||
|
|
ConversationRow(conversation: conversation)
|
||
|
|
.contentShape(Rectangle())
|
||
|
|
.onTapGesture {
|
||
|
|
path.append(AppRoute.chat(targetId: conversation.targetId, targetName: conversation.targetId))
|
||
|
|
}
|
||
|
|
.swipeActions(edge: .trailing) {
|
||
|
|
Button(role: .destructive) {
|
||
|
|
viewModel.deleteConversation(conversation)
|
||
|
|
} label: {
|
||
|
|
Text("删除")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.swipeActions(edge: .leading) {
|
||
|
|
Button {
|
||
|
|
viewModel.togglePinned(conversation)
|
||
|
|
} label: {
|
||
|
|
Text(conversation.isPinned ? "取消置顶" : "置顶")
|
||
|
|
}
|
||
|
|
.tint(.orange)
|
||
|
|
|
||
|
|
Button {
|
||
|
|
viewModel.toggleMuted(conversation)
|
||
|
|
} label: {
|
||
|
|
Text(conversation.isMuted ? "取消静音" : "静音")
|
||
|
|
}
|
||
|
|
.tint(.indigo)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.listStyle(.plain)
|
||
|
|
.searchable(text: $viewModel.query, prompt: "搜索会话")
|
||
|
|
.navigationTitle("会话")
|
||
|
|
.toolbar {
|
||
|
|
#if os(iOS)
|
||
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||
|
|
Button {
|
||
|
|
viewModel.refresh()
|
||
|
|
} label: {
|
||
|
|
Image(systemName: "arrow.clockwise")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
.refreshable {
|
||
|
|
viewModel.refresh()
|
||
|
|
}
|
||
|
|
.onAppear {
|
||
|
|
if viewModel.conversations.isEmpty {
|
||
|
|
viewModel.load()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
struct ConversationRow: View {
|
||
|
|
let conversation: ConversationData
|
||
|
|
|
||
|
|
var body: some View {
|
||
|
|
HStack(spacing: 12) {
|
||
|
|
ZStack {
|
||
|
|
Circle()
|
||
|
|
.fill(Color.accentColor.opacity(0.15))
|
||
|
|
.frame(width: 48, height: 48)
|
||
|
|
Text(String(conversation.targetId.prefix(1).uppercased()))
|
||
|
|
.font(.headline)
|
||
|
|
.foregroundStyle(Color.accentColor)
|
||
|
|
}
|
||
|
|
|
||
|
|
VStack(alignment: .leading, spacing: 4) {
|
||
|
|
HStack {
|
||
|
|
Text(conversation.targetId)
|
||
|
|
.font(.subheadline)
|
||
|
|
.fontWeight(.semibold)
|
||
|
|
.lineLimit(1)
|
||
|
|
|
||
|
|
Spacer()
|
||
|
|
|
||
|
|
if conversation.isPinned {
|
||
|
|
Text("置顶")
|
||
|
|
.font(.caption2)
|
||
|
|
.foregroundStyle(.orange)
|
||
|
|
}
|
||
|
|
|
||
|
|
Text(formatConversationTime(conversation.lastMsgTime))
|
||
|
|
.font(.caption)
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
}
|
||
|
|
|
||
|
|
HStack {
|
||
|
|
Text(conversationPreview(conversation))
|
||
|
|
.font(.caption)
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
.lineLimit(1)
|
||
|
|
|
||
|
|
Spacer()
|
||
|
|
|
||
|
|
if conversation.unreadCount > 0 && !conversation.isMuted {
|
||
|
|
Text("\(conversation.unreadCount)")
|
||
|
|
.font(.caption2)
|
||
|
|
.fontWeight(.bold)
|
||
|
|
.foregroundStyle(.white)
|
||
|
|
.padding(.horizontal, 6)
|
||
|
|
.padding(.vertical, 2)
|
||
|
|
.background(Color.red)
|
||
|
|
.clipShape(Capsule())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.padding(.vertical, 4)
|
||
|
|
}
|
||
|
|
}
|