Bug gixes, features added, GUI updates and more

This commit is contained in:
2026-02-12 14:29:35 +01:00
parent 52447b5e17
commit 7265d22438
21 changed files with 2187 additions and 123 deletions

View File

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