From f421fbb721d0b8c21ef20cd9586df691acb08e54 Mon Sep 17 00:00:00 2001 From: wangdl Date: Tue, 9 Jun 2026 22:08:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20G1=20context=20registry=20=E2=80=94=20pr?= =?UTF-8?q?event=20empty=20contexts=20from=20dropping=20all=20export=20eve?= =?UTF-8?q?nts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ReadingContextRegistry for SessionManager→Pipeline context sharing - Wire registry into SessionManager (register on open, unregister on close) - Fix Pipeline to auto-pull from registry when contexts not provided - Fix ScenePhase to not pass empty contexts Co-Authored-By: Claude Opus 4.7 --- AIStudyApp/AIStudyApp/AIStudyAppApp.swift | 4 ++-- .../ReadingContextRegistry.swift | 22 +++++++++++++++++++ .../ReadingEventUploadQueue.swift | 9 ++++---- .../ReadingRuntimeSessionManager.swift | 2 ++ 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingContextRegistry.swift diff --git a/AIStudyApp/AIStudyApp/AIStudyAppApp.swift b/AIStudyApp/AIStudyApp/AIStudyAppApp.swift index e627ea4..6902916 100644 --- a/AIStudyApp/AIStudyApp/AIStudyAppApp.swift +++ b/AIStudyApp/AIStudyApp/AIStudyAppApp.swift @@ -71,14 +71,14 @@ struct AIStudyAppApp: App { // Flush events: export → enqueue → (quick upload in background task) Task { let pipeline = ReadingEventUploadPipeline.shared - pipeline.exportAndEnqueue(contexts: [:]) // contexts populated from active sessions + pipeline.exportAndEnqueue() // pulls from ContextRegistry await pipeline.flush() } case .active: // Resume session + reload stale events from Rust ReadingRuntimeSessionManager.shared.resume() Task { - ReadingEventUploadPipeline.shared.reloadOnLaunch(contexts: [:]) + ReadingEventUploadPipeline.shared.reloadOnLaunch() } case .inactive: break diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingContextRegistry.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingContextRegistry.swift new file mode 100644 index 0000000..786dd35 --- /dev/null +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingContextRegistry.swift @@ -0,0 +1,22 @@ +import Foundation + +/// Shared registry of active reading material contexts. +/// The SessionManager registers contexts here; the Pipeline reads them for export. +@MainActor +final class ReadingContextRegistry { + static let shared = ReadingContextRegistry() + + private var contexts: [String: ReadingMaterialContext] = [:] + + func register(_ context: ReadingMaterialContext) { + contexts[context.materialId] = context + } + + func unregister(materialId: String) { + contexts.removeValue(forKey: materialId) + } + + func allContexts() -> [String: ReadingMaterialContext] { + return contexts + } +} diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventUploadQueue.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventUploadQueue.swift index 8a84dd5..be58b9a 100644 --- a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventUploadQueue.swift +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingEventUploadQueue.swift @@ -174,12 +174,13 @@ final class ReadingEventUploadPipeline { private init() {} - /// Full pipeline: export from Rust → enqueue. - func exportAndEnqueue(contexts: [String: ReadingMaterialContext]) { + /// Full pipeline: export from Rust → enqueue. Pulls contexts from registry if not provided. + func exportAndEnqueue(contexts: [String: ReadingMaterialContext] = [:]) { + let effectiveContexts = contexts.isEmpty ? ReadingContextRegistry.shared.allContexts() : contexts let rustEvents = adapter.exportEvents(limit: 100, timestampMs: nowMs()) guard !rustEvents.isEmpty else { return } - let uploadItems = ReadingEventMapper.map(rustEvents: rustEvents, contexts: contexts) + let uploadItems = ReadingEventMapper.map(rustEvents: rustEvents, contexts: effectiveContexts) guard !uploadItems.isEmpty else { return } queue.enqueue(uploadItems) @@ -214,7 +215,7 @@ final class ReadingEventUploadPipeline { } /// Reload on app launch: reload stale Rust events, enqueue, retry failed. - func reloadOnLaunch(contexts: [String: ReadingMaterialContext]) { + func reloadOnLaunch(contexts: [String: ReadingMaterialContext] = [:]) { _ = adapter.reloadStaleEvents() _ = adapter.cleanupStaleSessions(nowMs: nowMs(), maxAgeMs: 30 * 60 * 1000) diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingRuntimeSessionManager.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingRuntimeSessionManager.swift index b9bd4db..e79eb04 100644 --- a/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingRuntimeSessionManager.swift +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/ReadingRuntimeSessionManager.swift @@ -52,6 +52,7 @@ final class ReadingRuntimeSessionManager { activeSessionId = sessionId activeContext = context state = .active + ReadingContextRegistry.shared.register(context) lastPosition = nil lastHeartbeatAtMs = now @@ -74,6 +75,7 @@ final class ReadingRuntimeSessionManager { _ = try? adapter.closeSession(sessionId) activeSessionId = nil + if let ctx = activeContext { ReadingContextRegistry.shared.unregister(materialId: ctx.materialId) } activeContext = nil lastPosition = nil state = .closed