From 62f9a2503a4f07c685c44ec480c826118d1e4210 Mon Sep 17 00:00:00 2001 From: Yusuf Suleman Date: Sat, 4 Apr 2026 00:35:53 -0500 Subject: [PATCH] debug: timestamped logging for first-article-open stall investigation --- .../Features/Reader/Views/ArticleView.swift | 22 ++++++++++++------- .../Reader/Views/ArticleWebView.swift | 14 ++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/ios/Platform/Platform/Features/Reader/Views/ArticleView.swift b/ios/Platform/Platform/Features/Reader/Views/ArticleView.swift index 5400642..f425ec0 100644 --- a/ios/Platform/Platform/Features/Reader/Views/ArticleView.swift +++ b/ios/Platform/Platform/Features/Reader/Views/ArticleView.swift @@ -13,6 +13,7 @@ struct ArticleView: View { self.vm = vm _currentEntry = State(initialValue: entry) _articleContent = State(initialValue: entry.articleHTML) + print("[ART-OPEN] init contentLen=\(entry.articleHTML.count)") } var body: some View { @@ -59,33 +60,37 @@ struct ArticleView: View { } } .task { - // 1. Synchronous local mutation — runs before any async work. - // The @Observable array mutation triggers ForEach row re-render - // immediately, so the list shows "read" even during push animation. + let t0 = CFAbsoluteTimeGetCurrent() + print("[ART-OPEN] .task started") + let entryId = entry.id if let idx = vm.entries.firstIndex(where: { $0.id == entryId }), !vm.entries[idx].isRead { vm.entries[idx].status = "read" } currentEntry = vm.entries.first(where: { $0.id == entryId }) ?? currentEntry + print("[ART-OPEN] mark-read done +\(Int((CFAbsoluteTimeGetCurrent()-t0)*1000))ms") - // 2. API sync + counter refresh — background, fire-and-forget Task { let api = ReaderAPI() try? await api.markEntries(ids: [entryId], status: "read") vm.counters = try? await api.getCounters() } - // 3. Fetch full content — update when ready + let t1 = CFAbsoluteTimeGetCurrent() do { let fullEntry = try await ReaderAPI().getEntry(id: entryId) - let fullHTML = fullEntry.articleHTML + let t2 = CFAbsoluteTimeGetCurrent() + print("[ART-OPEN] getEntry returned +\(Int((t2-t0)*1000))ms contentLen=\(fullEntry.articleHTML.count)") + let fullHTML = fullEntry.articleHTML if !fullHTML.isEmpty && fullHTML.count > articleContent.count { articleContent = fullHTML + print("[ART-OPEN] articleContent updated +\(Int((CFAbsoluteTimeGetCurrent()-t0)*1000))ms") + } else { + print("[ART-OPEN] content NOT upgraded (existing=\(articleContent.count) new=\(fullHTML.count))") } - // Preserve local status/starred (may differ from server due to race) var merged = fullEntry if let local = vm.entries.first(where: { $0.id == entryId }) { merged.status = local.status @@ -93,8 +98,9 @@ struct ArticleView: View { } currentEntry = merged } catch { - // Keep whatever content we already have + print("[ART-OPEN] getEntry FAILED: \(error)") } + print("[ART-OPEN] .task complete +\(Int((CFAbsoluteTimeGetCurrent()-t0)*1000))ms") } } diff --git a/ios/Platform/Platform/Features/Reader/Views/ArticleWebView.swift b/ios/Platform/Platform/Features/Reader/Views/ArticleWebView.swift index 900a508..8d1285f 100644 --- a/ios/Platform/Platform/Features/Reader/Views/ArticleWebView.swift +++ b/ios/Platform/Platform/Features/Reader/Views/ArticleWebView.swift @@ -49,6 +49,7 @@ struct ArticleWebView: UIViewRepresentable { let html: String func makeUIView(context: Context) -> UIView { + print("[ART-WV] makeUIView") let container = UIView() container.backgroundColor = .clear @@ -74,6 +75,8 @@ struct ArticleWebView: UIViewRepresentable { let isUpgrade = context.coordinator.lastHTML != nil context.coordinator.lastHTML = newHTML + print("[ART-WV] updateUIView isUpgrade=\(isUpgrade) htmlLen=\(newHTML.count)") + if isUpgrade { // Content upgrade (partial → full): swap only #article-body contents. // Header + outer document structure stay intact. Scroll preserved. @@ -135,6 +138,7 @@ struct ArticleWebView: UIViewRepresentable { class Coordinator: NSObject, WKNavigationDelegate { var lastHTML: String? + var loadStart: CFAbsoluteTime = 0 func webView( _ webView: WKWebView, @@ -149,5 +153,15 @@ struct ArticleWebView: UIViewRepresentable { } decisionHandler(.allow) } + + func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + loadStart = CFAbsoluteTimeGetCurrent() + print("[ART-WV] didStartNavigation") + } + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + let elapsed = Int((CFAbsoluteTimeGetCurrent() - loadStart) * 1000) + print("[ART-WV] didFinish +\(elapsed)ms") + } } }