2025-08-24 16:24:21 -07:00
|
|
|
//
|
|
|
|
|
// ConversationListView.swift
|
|
|
|
|
// kordophone2
|
|
|
|
|
//
|
|
|
|
|
// Created by James Magahern on 8/24/25.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
|
|
struct ConversationListView: View
|
|
|
|
|
{
|
|
|
|
|
@Binding var model: ViewModel
|
2025-08-24 18:41:42 -07:00
|
|
|
@Environment(\.xpcClient) private var xpcClient
|
2025-08-24 16:24:21 -07:00
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
|
List($model.conversations, selection: $model.selectedConversations) { conv in
|
2025-08-25 00:37:48 -07:00
|
|
|
let isUnread = conv.wrappedValue.unreadCount > 0
|
|
|
|
|
|
|
|
|
|
HStack(spacing: 0.0) {
|
2025-08-29 18:49:00 -06:00
|
|
|
if isUnread {
|
|
|
|
|
Image(systemName: "circlebadge.fill")
|
|
|
|
|
.foregroundStyle(.tint)
|
|
|
|
|
.frame(width: 10.0)
|
|
|
|
|
} else {
|
|
|
|
|
Rectangle()
|
|
|
|
|
.foregroundStyle(.clear)
|
|
|
|
|
.frame(width: 10.0)
|
|
|
|
|
}
|
2025-08-24 16:24:21 -07:00
|
|
|
|
2025-08-25 00:37:48 -07:00
|
|
|
VStack(alignment: .leading) {
|
|
|
|
|
Text(conv.wrappedValue.displayName)
|
|
|
|
|
.bold()
|
|
|
|
|
|
|
|
|
|
Text(conv.wrappedValue.messagePreview)
|
|
|
|
|
.foregroundStyle(.secondary)
|
|
|
|
|
}
|
|
|
|
|
.padding(8.0)
|
2025-08-24 16:24:21 -07:00
|
|
|
}
|
2025-08-24 18:41:42 -07:00
|
|
|
.id(conv.id)
|
2025-08-24 16:24:21 -07:00
|
|
|
}
|
|
|
|
|
.listStyle(.sidebar)
|
2025-08-24 18:41:42 -07:00
|
|
|
.task { await watchForConversationListChanges() }
|
2025-08-25 00:37:48 -07:00
|
|
|
.task { await model.triggerSync() }
|
2025-08-24 18:41:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func watchForConversationListChanges() async {
|
|
|
|
|
for await event in xpcClient.eventStream() {
|
|
|
|
|
switch event {
|
|
|
|
|
case .conversationsUpdated:
|
|
|
|
|
model.setNeedsReload()
|
|
|
|
|
case .messagesUpdated(_):
|
2025-08-25 00:37:48 -07:00
|
|
|
await model.triggerSync()
|
2025-08-24 18:41:42 -07:00
|
|
|
case .updateStreamReconnected:
|
2025-08-25 00:37:48 -07:00
|
|
|
await model.triggerSync()
|
2025-08-24 18:41:42 -07:00
|
|
|
default:
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-24 16:24:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Types
|
|
|
|
|
|
|
|
|
|
@Observable
|
|
|
|
|
class ViewModel
|
|
|
|
|
{
|
|
|
|
|
var conversations: [Display.Conversation]
|
|
|
|
|
var selectedConversations: Set<Display.Conversation.ID>
|
|
|
|
|
|
2025-08-24 18:41:42 -07:00
|
|
|
private var needsReload: Bool = true
|
2025-08-25 00:37:48 -07:00
|
|
|
private let client = XPCClient()
|
2025-08-24 18:41:42 -07:00
|
|
|
|
2025-08-24 16:24:21 -07:00
|
|
|
public init(conversations: [Display.Conversation] = []) {
|
|
|
|
|
self.conversations = conversations
|
|
|
|
|
self.selectedConversations = Set()
|
2025-08-24 18:41:42 -07:00
|
|
|
setNeedsReload()
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-25 00:37:48 -07:00
|
|
|
func triggerSync() async {
|
|
|
|
|
do {
|
|
|
|
|
try await client.syncConversationList()
|
|
|
|
|
} catch {
|
|
|
|
|
print("Conversation List Sync Error: \(error)")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-24 18:41:42 -07:00
|
|
|
func setNeedsReload() {
|
|
|
|
|
needsReload = true
|
|
|
|
|
Task { @MainActor [weak self] in
|
|
|
|
|
guard let self else { return }
|
|
|
|
|
await reloadConversations()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func reloadConversations() async {
|
|
|
|
|
guard needsReload else { return }
|
|
|
|
|
needsReload = false
|
|
|
|
|
|
|
|
|
|
do {
|
2025-08-25 00:37:48 -07:00
|
|
|
|
2025-08-24 18:41:42 -07:00
|
|
|
let clientConversations = try await client.getConversations()
|
|
|
|
|
.map { Display.Conversation(from: $0) }
|
|
|
|
|
|
|
|
|
|
self.conversations = clientConversations
|
|
|
|
|
} catch {
|
|
|
|
|
print("Error reloading conversations: \(error)")
|
|
|
|
|
}
|
2025-08-24 16:24:21 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#Preview {
|
|
|
|
|
@Previewable @State var viewModel = ConversationListView.ViewModel(conversations: [
|
|
|
|
|
.init(id: "asdf", name: "Cool", participants: ["me"], messagePreview: "Hello there"),
|
|
|
|
|
.init(id: "gjkl", name: "Nice", participants: ["me"], messagePreview: "How are you"),
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
ConversationListView(model: $viewModel)
|
|
|
|
|
}
|