- Unread badge on the Podcasts tab via a new observable PodcastLibrary store that mirrors PodcastIndex; all played/remove/generation mutations route through it so the count stays live. - Persist playback speed across sessions (UserDefaults), applied on setup. - Sleep timer (15/30/45 min or end-of-episode) with a moon menu in the full player; end-of-episode stops instead of auto-advancing. - Persist the current episode + Up Next queue and restore a *paused* session on launch; save position when backgrounding. Audio session is only claimed on actual playback so a restored session never interrupts other audio. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
32 lines
1017 B
Swift
32 lines
1017 B
Swift
import Foundation
|
|
import Observation
|
|
|
|
/// Observable, in-memory mirror of `PodcastIndex` so SwiftUI updates immediately
|
|
/// on changes — the Podcasts tab badge and the library list both read from here.
|
|
/// Route podcast mutations through this store so the unplayed badge stays in sync.
|
|
@Observable
|
|
@MainActor
|
|
final class PodcastLibrary {
|
|
static let shared = PodcastLibrary()
|
|
|
|
private(set) var entries: [PodcastEntry] = []
|
|
|
|
var unplayed: [PodcastEntry] { entries.filter { !$0.isPlayed } }
|
|
var played: [PodcastEntry] { entries.filter { $0.isPlayed } }
|
|
var unplayedCount: Int { entries.reduce(0) { $0 + ($1.isPlayed ? 0 : 1) } }
|
|
|
|
private init() { entries = PodcastIndex.all() }
|
|
|
|
func reload() { entries = PodcastIndex.all() }
|
|
|
|
func setPlayed(_ articleUrl: String, _ played: Bool) {
|
|
PodcastIndex.setPlayed(articleUrl: articleUrl, played)
|
|
reload()
|
|
}
|
|
|
|
func remove(_ articleUrl: String) {
|
|
PodcastIndex.remove(articleUrl: articleUrl)
|
|
reload()
|
|
}
|
|
}
|