133 lines
4.8 KiB
Swift
133 lines
4.8 KiB
Swift
import SwiftUI
|
|
|
|
struct MessagesView: View {
|
|
@StateObject private var model: MessagesViewModel
|
|
@State private var pendingDeleteMessage: SMSMessage?
|
|
|
|
init(model: MessagesViewModel? = nil) {
|
|
_model = StateObject(wrappedValue: model ?? DefaultContainer.makeMessagesModel())
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
AITheme.canvas.ignoresSafeArea()
|
|
ScrollView {
|
|
LazyVStack(spacing: 12) {
|
|
filterBar
|
|
if let errorMessage = model.errorMessage {
|
|
AIStatusBanner(errorMessage, tint: AITheme.warning, systemImage: "exclamationmark.triangle")
|
|
}
|
|
if model.isLoading && model.filteredRows.isEmpty {
|
|
AILoadingCard("正在同步消息")
|
|
}
|
|
if model.filteredRows.isEmpty && !model.isLoading {
|
|
AICard {
|
|
AIEmptyState(
|
|
systemImage: "message",
|
|
title: "暂无消息",
|
|
message: "刷新后从实例同步短信。"
|
|
)
|
|
}
|
|
}
|
|
ForEach(model.filteredRows) { message in
|
|
messageCard(message)
|
|
}
|
|
}
|
|
.padding(16)
|
|
}
|
|
}
|
|
.navigationTitle("消息中心")
|
|
.aiBottomActionBar {
|
|
Button {
|
|
model.showingSendForm = true
|
|
} label: {
|
|
Label("发送消息", systemImage: "paperplane")
|
|
}
|
|
.buttonStyle(AIButtonStyle(kind: .primary, size: .regular, fillsWidth: true))
|
|
}
|
|
.confirmationDialog(
|
|
"删除这条消息?操作不可恢复。",
|
|
isPresented: Binding(
|
|
get: { pendingDeleteMessage != nil },
|
|
set: { if !$0 { pendingDeleteMessage = nil } }
|
|
),
|
|
titleVisibility: .visible
|
|
) {
|
|
Button("删除消息", role: .destructive) {
|
|
guard let message = pendingDeleteMessage else { return }
|
|
Task { await model.delete(message) }
|
|
}
|
|
Button("取消", role: .cancel) {}
|
|
}
|
|
.refreshable {
|
|
await model.refresh()
|
|
}
|
|
.task {
|
|
await model.refresh()
|
|
}
|
|
.sheet(isPresented: $model.showingSendForm) {
|
|
SendMessageFormView(model: model)
|
|
}
|
|
}
|
|
|
|
private var filterBar: some View {
|
|
AICard {
|
|
VStack(spacing: 10) {
|
|
Picker("方向", selection: $model.direction) {
|
|
ForEach(MessagesViewModel.DirectionFilter.allCases) { item in
|
|
Text(item.rawValue).tag(item)
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
|
|
Picker("实例", selection: $model.selectedInstanceID) {
|
|
Text("全部实例").tag(Optional<UUID>.none)
|
|
ForEach(model.instances) { instance in
|
|
Text(instance.name).tag(Optional(instance.id))
|
|
}
|
|
}
|
|
.pickerStyle(.menu)
|
|
.aiSelectSurface()
|
|
|
|
AISearchField("搜索号码或内容", text: $model.searchText)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func messageCard(_ message: SMSMessage) -> some View {
|
|
AICard {
|
|
VStack(alignment: .leading, spacing: 10) {
|
|
HStack(spacing: 8) {
|
|
Text(message.phoneNumber.isEmpty ? "未知号码" : message.phoneNumber)
|
|
.font(.headline)
|
|
Spacer()
|
|
AITag(message.direction.label, tint: message.direction == .outgoing ? AITheme.primary : AITheme.success)
|
|
}
|
|
Text(message.content)
|
|
.font(.subheadline)
|
|
.foregroundStyle(AITheme.ink)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
HStack {
|
|
Text(message.instanceName)
|
|
.font(.caption)
|
|
.foregroundStyle(AITheme.muted)
|
|
.lineLimit(1)
|
|
.truncationMode(.middle)
|
|
.layoutPriority(1)
|
|
Spacer()
|
|
Button("删除") {
|
|
pendingDeleteMessage = message
|
|
}
|
|
.buttonStyle(
|
|
AIButtonStyle(
|
|
kind: .danger,
|
|
size: .compact,
|
|
fixedWidth: AITheme.listActionButtonWidth
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|