feat: #80 标记已读 UI + 乐观更新

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-09 20:49:41 +08:00
parent 697220de00
commit 80c1d660cd
2 changed files with 30 additions and 10 deletions

View File

@ -101,6 +101,7 @@ struct MaterialReaderView: View {
@State private var hasRestoredPosition = false @State private var hasRestoredPosition = false
@State private var restoreBlockId: String? @State private var restoreBlockId: String?
@State private var actualContentHeight: CGFloat = 1 @State private var actualContentHeight: CGFloat = 1
@State private var isMarkedRead = false
private let title: String private let title: String
private let knowledgeBaseId: String? private let knowledgeBaseId: String?
@ -157,16 +158,26 @@ struct MaterialReaderView: View {
.toolbarBackground(.hidden, for: .navigationBar) .toolbarBackground(.hidden, for: .navigationBar)
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
NavigationLink(value: Route.aiChat(context: ChatEntryContext( HStack(spacing: 16) {
scopeType: .material, Button {
scopeId: vm.materialId, markAsRead()
scopeName: title, } label: {
parentKnowledgeBaseId: knowledgeBaseId, Image(systemName: isMarkedRead ? "checkmark.circle.fill" : "checkmark.circle")
createdFrom: "material_reader" .font(.system(size: 16))
))) { .foregroundColor(isMarkedRead ? Color.green : Color.zxF05)
Image(systemName: "bubble.left.and.bubble.right") }
.font(.system(size: 16))
.foregroundColor(Color.zxF05) NavigationLink(value: Route.aiChat(context: ChatEntryContext(
scopeType: .material,
scopeId: vm.materialId,
scopeName: title,
parentKnowledgeBaseId: knowledgeBaseId,
createdFrom: "material_reader"
))) {
Image(systemName: "bubble.left.and.bubble.right")
.font(.system(size: 16))
.foregroundColor(Color.zxF05)
}
} }
} }
} }
@ -293,6 +304,15 @@ struct MaterialReaderView: View {
print("[READER] V1 session closed") print("[READER] V1 session closed")
} }
/// Mark the current material as read optimistic update + event push.
private func markAsRead() {
guard !isMarkedRead else { return }
isMarkedRead = true
sessionManager.markAsRead()
// V1 fallback
collector.markAsRead(materialId: vm.materialId)
}
/// Build a NoteAnchor from the current scroll position (for quick note). /// Build a NoteAnchor from the current scroll position (for quick note).
private func buildAnchor() -> NoteAnchor? { private func buildAnchor() -> NoteAnchor? {
let pos = sessionManager.lastPosition ?? collector.lastPosition let pos = sessionManager.lastPosition ?? collector.lastPosition