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,6 +158,15 @@ struct MaterialReaderView: View {
.toolbarBackground(.hidden, for: .navigationBar) .toolbarBackground(.hidden, for: .navigationBar)
.toolbar { .toolbar {
ToolbarItem(placement: .topBarTrailing) { ToolbarItem(placement: .topBarTrailing) {
HStack(spacing: 16) {
Button {
markAsRead()
} label: {
Image(systemName: isMarkedRead ? "checkmark.circle.fill" : "checkmark.circle")
.font(.system(size: 16))
.foregroundColor(isMarkedRead ? Color.green : Color.zxF05)
}
NavigationLink(value: Route.aiChat(context: ChatEntryContext( NavigationLink(value: Route.aiChat(context: ChatEntryContext(
scopeType: .material, scopeType: .material,
scopeId: vm.materialId, scopeId: vm.materialId,
@ -170,6 +180,7 @@ struct MaterialReaderView: View {
} }
} }
} }
}
.task { await vm.load() } .task { await vm.load() }
.sheet(isPresented: $showQuickLook) { .sheet(isPresented: $showQuickLook) {
QuickLookPreview(url: URL(fileURLWithPath: vm.filePath)) QuickLookPreview(url: URL(fileURLWithPath: vm.filePath))
@ -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