Resolves the two deviations from the episode detail sheet: - AI summary now shows in the detail sheet. Added a URL-keyed AISummaryStore that BookmarksViewModel populates as it enriches/loads bookmarks, so the decoupled Podcasts tab can surface a summary from just the episode URL — without recoupling to BookmarksViewModel. - "Open Bookmark" opens the in-app BrowserView as a nested sheet (generator threaded through PodcastLibraryView / EpisodePickerView), alongside the existing "Open in Safari" and Share. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
28 lines
1.1 KiB
Swift
28 lines
1.1 KiB
Swift
import Foundation
|
|
|
|
/// URL-keyed cache of AI summaries so views that only know a URL — e.g. the
|
|
/// deliberately-decoupled Podcasts tab — can show a bookmark's summary without
|
|
/// holding the full `Bookmark`. Written by `BookmarksViewModel` as it enriches
|
|
/// and loads bookmarks; read by the episode detail sheet.
|
|
enum AISummaryStore {
|
|
private static let key = "aiSummaryByURL"
|
|
|
|
static func summary(for url: String) -> String? {
|
|
let store = UserDefaults.standard.dictionary(forKey: key) as? [String: String] ?? [:]
|
|
let value = store[url]
|
|
return (value?.isEmpty ?? true) ? nil : value
|
|
}
|
|
|
|
static func set(_ summary: String?, for url: String) {
|
|
var store = UserDefaults.standard.dictionary(forKey: key) as? [String: String] ?? [:]
|
|
if let summary, !summary.isEmpty {
|
|
guard store[url] != summary else { return } // no-op if unchanged
|
|
store[url] = summary
|
|
} else {
|
|
guard store[url] != nil else { return }
|
|
store.removeValue(forKey: url)
|
|
}
|
|
UserDefaults.standard.set(store, forKey: key)
|
|
}
|
|
}
|