Bug gixes, features added, GUI updates and more
This commit is contained in:
@@ -12,6 +12,8 @@ struct ConversationListView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@State private var searchText = ""
|
||||
@State private var conversations: [Conversation] = []
|
||||
@State private var selectedConversations: Set<UUID> = []
|
||||
@State private var isSelecting = false
|
||||
var onLoad: ((Conversation) -> Void)?
|
||||
|
||||
private var filteredConversations: [Conversation] {
|
||||
@@ -30,13 +32,42 @@ struct ConversationListView: View {
|
||||
Text("Conversations")
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
Spacer()
|
||||
Button { dismiss() } label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.title2)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
if isSelecting {
|
||||
Button("Cancel") {
|
||||
isSelecting = false
|
||||
selectedConversations.removeAll()
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
if !selectedConversations.isEmpty {
|
||||
Button(role: .destructive) {
|
||||
deleteSelected()
|
||||
} label: {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "trash")
|
||||
Text("Delete (\(selectedConversations.count))")
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
} else {
|
||||
if !conversations.isEmpty {
|
||||
Button("Select") {
|
||||
isSelecting = true
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
Button { dismiss() } label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.title2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.keyboardShortcut(.escape, modifiers: [])
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.keyboardShortcut(.escape, modifiers: [])
|
||||
}
|
||||
.padding(.horizontal, 24)
|
||||
.padding(.top, 20)
|
||||
@@ -81,26 +112,57 @@ struct ConversationListView: View {
|
||||
} else {
|
||||
List {
|
||||
ForEach(filteredConversations) { conversation in
|
||||
ConversationRow(conversation: conversation)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
onLoad?(conversation)
|
||||
dismiss()
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
|
||||
Button(role: .destructive) {
|
||||
deleteConversation(conversation)
|
||||
HStack(spacing: 12) {
|
||||
if isSelecting {
|
||||
Button {
|
||||
toggleSelection(conversation.id)
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
Image(systemName: selectedConversations.contains(conversation.id) ? "checkmark.circle.fill" : "circle")
|
||||
.foregroundStyle(selectedConversations.contains(conversation.id) ? .blue : .secondary)
|
||||
.font(.title2)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
ConversationRow(conversation: conversation)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
if isSelecting {
|
||||
toggleSelection(conversation.id)
|
||||
} else {
|
||||
onLoad?(conversation)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
if !isSelecting {
|
||||
Button {
|
||||
exportConversation(conversation)
|
||||
deleteConversation(conversation)
|
||||
} label: {
|
||||
Label("Export", systemImage: "square.and.arrow.up")
|
||||
Image(systemName: "trash")
|
||||
.foregroundStyle(.red)
|
||||
.font(.system(size: 16))
|
||||
}
|
||||
.tint(.blue)
|
||||
.buttonStyle(.plain)
|
||||
.help("Delete conversation")
|
||||
}
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
|
||||
Button(role: .destructive) {
|
||||
deleteConversation(conversation)
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}
|
||||
|
||||
Button {
|
||||
exportConversation(conversation)
|
||||
} label: {
|
||||
Label("Export", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
.tint(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
@@ -123,7 +185,7 @@ struct ConversationListView: View {
|
||||
.onAppear {
|
||||
loadConversations()
|
||||
}
|
||||
.frame(minWidth: 500, minHeight: 400)
|
||||
.frame(minWidth: 700, idealWidth: 800, minHeight: 500, idealHeight: 600)
|
||||
}
|
||||
|
||||
private func loadConversations() {
|
||||
@@ -135,6 +197,29 @@ struct ConversationListView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func toggleSelection(_ id: UUID) {
|
||||
if selectedConversations.contains(id) {
|
||||
selectedConversations.remove(id)
|
||||
} else {
|
||||
selectedConversations.insert(id)
|
||||
}
|
||||
}
|
||||
|
||||
private func deleteSelected() {
|
||||
for id in selectedConversations {
|
||||
do {
|
||||
let _ = try DatabaseService.shared.deleteConversation(id: id)
|
||||
} catch {
|
||||
Log.db.error("Failed to delete conversation: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
withAnimation {
|
||||
conversations.removeAll { selectedConversations.contains($0.id) }
|
||||
selectedConversations.removeAll()
|
||||
isSelecting = false
|
||||
}
|
||||
}
|
||||
|
||||
private func deleteConversation(_ conversation: Conversation) {
|
||||
do {
|
||||
let _ = try DatabaseService.shared.deleteConversation(id: conversation.id)
|
||||
@@ -167,20 +252,28 @@ struct ConversationListView: View {
|
||||
struct ConversationRow: View {
|
||||
let conversation: Conversation
|
||||
|
||||
private var formattedDate: String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "dd.MM.yyyy HH:mm:ss"
|
||||
return formatter.string(from: conversation.updatedAt)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(conversation.name)
|
||||
.font(.headline)
|
||||
.font(.system(size: 16, weight: .semibold))
|
||||
|
||||
HStack(spacing: 8) {
|
||||
Label("\(conversation.messageCount)", systemImage: "message")
|
||||
.font(.system(size: 13))
|
||||
Text("\u{2022}")
|
||||
Text(conversation.updatedAt, style: .relative)
|
||||
.font(.system(size: 13))
|
||||
Text(formattedDate)
|
||||
.font(.system(size: 13))
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
.padding(.vertical, 6)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user