Add 'osx/' from commit '46755a07ef2e7aa9852d74c30e2c12f9fe8f2278'
git-subtree-dir: osx git-subtree-mainline:034026e88agit-subtree-split:46755a07ef
This commit is contained in:
118
osx/kordophone2/ConversationListView.swift
Normal file
118
osx/kordophone2/ConversationListView.swift
Normal file
@@ -0,0 +1,118 @@
|
||||
//
|
||||
// ConversationListView.swift
|
||||
// kordophone2
|
||||
//
|
||||
// Created by James Magahern on 8/24/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ConversationListView: View
|
||||
{
|
||||
@Binding var model: ViewModel
|
||||
@Environment(\.xpcClient) private var xpcClient
|
||||
|
||||
var body: some View {
|
||||
List($model.conversations, selection: $model.selectedConversations) { conv in
|
||||
let isUnread = conv.wrappedValue.unreadCount > 0
|
||||
|
||||
HStack(spacing: 0.0) {
|
||||
if isUnread {
|
||||
Image(systemName: "circlebadge.fill")
|
||||
.foregroundStyle(.tint)
|
||||
.frame(width: 10.0)
|
||||
} else {
|
||||
Rectangle()
|
||||
.foregroundStyle(.clear)
|
||||
.frame(width: 10.0)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(conv.wrappedValue.displayName)
|
||||
.bold()
|
||||
|
||||
Text(conv.wrappedValue.messagePreview)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(8.0)
|
||||
}
|
||||
.id(conv.id)
|
||||
}
|
||||
.listStyle(.sidebar)
|
||||
.task { await watchForConversationListChanges() }
|
||||
.task { await model.triggerSync() }
|
||||
}
|
||||
|
||||
private func watchForConversationListChanges() async {
|
||||
for await event in xpcClient.eventStream() {
|
||||
switch event {
|
||||
case .conversationsUpdated:
|
||||
model.setNeedsReload()
|
||||
case .messagesUpdated(_):
|
||||
await model.triggerSync()
|
||||
case .updateStreamReconnected:
|
||||
await model.triggerSync()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Types
|
||||
|
||||
@Observable
|
||||
class ViewModel
|
||||
{
|
||||
var conversations: [Display.Conversation]
|
||||
var selectedConversations: Set<Display.Conversation.ID>
|
||||
|
||||
private var needsReload: Bool = true
|
||||
private let client = XPCClient()
|
||||
|
||||
public init(conversations: [Display.Conversation] = []) {
|
||||
self.conversations = conversations
|
||||
self.selectedConversations = Set()
|
||||
setNeedsReload()
|
||||
}
|
||||
|
||||
func triggerSync() async {
|
||||
do {
|
||||
try await client.syncConversationList()
|
||||
} catch {
|
||||
print("Conversation List Sync Error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
let clientConversations = try await client.getConversations()
|
||||
.map { Display.Conversation(from: $0) }
|
||||
|
||||
self.conversations = clientConversations
|
||||
} catch {
|
||||
print("Error reloading conversations: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
}
|
||||
Reference in New Issue
Block a user