Private
Public Access
1
0

autosyncing, appearance tweaks

This commit is contained in:
2025-08-25 00:37:48 -07:00
parent f0fd738935
commit 402b5a5f80
7 changed files with 64 additions and 11 deletions

View File

@@ -14,17 +14,27 @@ struct ConversationListView: View
var body: some View { var body: some View {
List($model.conversations, selection: $model.selectedConversations) { conv in List($model.conversations, selection: $model.selectedConversations) { conv in
VStack(alignment: .leading) { let isUnread = conv.wrappedValue.unreadCount > 0
Text(conv.wrappedValue.displayName)
.bold() HStack(spacing: 0.0) {
Image(systemName: isUnread ? "circlebadge.fill" : "")
.foregroundStyle(.tint)
.frame(width: 10.0)
Text(conv.wrappedValue.messagePreview) VStack(alignment: .leading) {
Text(conv.wrappedValue.displayName)
.bold()
Text(conv.wrappedValue.messagePreview)
.foregroundStyle(.secondary)
}
.padding(8.0)
} }
.id(conv.id) .id(conv.id)
.padding(8.0)
} }
.listStyle(.sidebar) .listStyle(.sidebar)
.task { await watchForConversationListChanges() } .task { await watchForConversationListChanges() }
.task { await model.triggerSync() }
} }
private func watchForConversationListChanges() async { private func watchForConversationListChanges() async {
@@ -33,9 +43,9 @@ struct ConversationListView: View
case .conversationsUpdated: case .conversationsUpdated:
model.setNeedsReload() model.setNeedsReload()
case .messagesUpdated(_): case .messagesUpdated(_):
model.setNeedsReload() await model.triggerSync()
case .updateStreamReconnected: case .updateStreamReconnected:
model.setNeedsReload() await model.triggerSync()
default: default:
break break
} }
@@ -51,6 +61,7 @@ struct ConversationListView: View
var selectedConversations: Set<Display.Conversation.ID> var selectedConversations: Set<Display.Conversation.ID>
private var needsReload: Bool = true private var needsReload: Bool = true
private let client = XPCClient()
public init(conversations: [Display.Conversation] = []) { public init(conversations: [Display.Conversation] = []) {
self.conversations = conversations self.conversations = conversations
@@ -58,6 +69,14 @@ struct ConversationListView: View
setNeedsReload() setNeedsReload()
} }
func triggerSync() async {
do {
try await client.syncConversationList()
} catch {
print("Conversation List Sync Error: \(error)")
}
}
func setNeedsReload() { func setNeedsReload() {
needsReload = true needsReload = true
Task { @MainActor [weak self] in Task { @MainActor [weak self] in
@@ -71,7 +90,7 @@ struct ConversationListView: View
needsReload = false needsReload = false
do { do {
let client = XPCClient()
let clientConversations = try await client.getConversations() let clientConversations = try await client.getConversations()
.map { Display.Conversation(from: $0) } .map { Display.Conversation(from: $0) }

View File

@@ -28,8 +28,10 @@ struct MessageEntryView: View
.font(.body) .font(.body)
.scrollDisabled(true) .scrollDisabled(true)
.padding(8.0) .padding(8.0)
.disabled(selectedConversation == nil)
.background { .background {
RoundedRectangle(cornerRadius: 6.0) RoundedRectangle(cornerRadius: 6.0)
.stroke(SeparatorShapeStyle())
.fill(.background) .fill(.background)
} }
@@ -41,6 +43,10 @@ struct MessageEntryView: View
} }
.padding(10.0) .padding(10.0)
} }
.onChange(of: selectedConversation) { oldValue, newValue in
viewModel.draftText = ""
}
} }
// MARK: - Types // MARK: - Types

View File

@@ -10,12 +10,13 @@ import XPC
enum Display enum Display
{ {
struct Conversation: Identifiable struct Conversation: Identifiable, Hashable
{ {
let id: String let id: String
let name: String? let name: String?
let participants: [String] let participants: [String]
let messagePreview: String let messagePreview: String
let unreadCount: Int
var displayName: String { var displayName: String {
if let name, name.count > 0 { return name } if let name, name.count > 0 { return name }
@@ -27,6 +28,7 @@ enum Display
self.name = c.displayName self.name = c.displayName
self.participants = c.participants self.participants = c.participants
self.messagePreview = c.lastMessagePreview ?? "" self.messagePreview = c.lastMessagePreview ?? ""
self.unreadCount = c.unreadCount
} }
init(id: String = UUID().uuidString, name: String? = nil, participants: [String], messagePreview: String) { init(id: String = UUID().uuidString, name: String? = nil, participants: [String], messagePreview: String) {
@@ -34,6 +36,7 @@ enum Display
self.name = name self.name = name
self.participants = participants self.participants = participants
self.messagePreview = messagePreview self.messagePreview = messagePreview
self.unreadCount = 0
} }
} }

View File

@@ -44,7 +44,9 @@ struct TextBubbleItemView: View
let isFromMe: Bool let isFromMe: Bool
var body: some View { var body: some View {
let bubbleColor: Color = isFromMe ? .blue : Color(.systemGray) let bubbleColor: Color = isFromMe ? .blue : Color(NSColor(name: "grayish", dynamicProvider: { appearance in
appearance.name == .darkAqua ? .darkGray : NSColor(white: 0.78, alpha: 1.0)
}))
let textColor: Color = isFromMe ? .white : .primary let textColor: Color = isFromMe ? .white : .primary
BubbleView(isFromMe: isFromMe) { BubbleView(isFromMe: isFromMe) {

View File

@@ -32,7 +32,9 @@ extension TranscriptView.ViewModel
} }
} }
displayItems.append(.message(message)) if !message.text.isEmpty {
displayItems.append(.message(message))
}
} }
let animation: Animation? = animated ? .default : nil let animation: Animation? = animated ? .default : nil

View File

@@ -113,6 +113,7 @@ struct TranscriptView: View
Task { @MainActor [weak self] in Task { @MainActor [weak self] in
guard let self else { return } guard let self else { return }
await markAsRead()
await triggerSync() await triggerSync()
setNeedsReload(animated: false) setNeedsReload(animated: false)
@@ -121,6 +122,16 @@ struct TranscriptView: View
} }
} }
func markAsRead() async {
guard let displayedConversation else { return }
do {
try await client.markConversationAsRead(conversationId: displayedConversation)
} catch {
print("Error triggering sync: \(error)")
}
}
func triggerSync() async { func triggerSync() async {
guard let displayedConversation else { return } guard let displayedConversation else { return }

View File

@@ -89,6 +89,16 @@ final class XPCClient
_ = try await sendSync(req) _ = try await sendSync(req)
} }
public func syncConversationList() async throws {
let req = makeRequest(method: "SyncConversationList")
_ = try await sendSync(req)
}
public func markConversationAsRead(conversationId: String) async throws {
let req = makeRequest(method: "MarkConversationAsRead", arguments: ["conversation_id": xpcString(conversationId)])
_ = try await sendSync(req)
}
public func getMessages(conversationId: String, limit: Int = 100, offset: Int = 0) async throws -> [Serialized.Message] { public func getMessages(conversationId: String, limit: Int = 100, offset: Int = 0) async throws -> [Serialized.Message] {
var args: [String: xpc_object_t] = [:] var args: [String: xpc_object_t] = [:]
args["conversation_id"] = xpcString(conversationId) args["conversation_id"] = xpcString(conversationId)