mirror of
https://github.com/yattee/yattee.git
synced 2026-05-14 11:25:02 +00:00
Add tvOS Top Shelf extension
Surfaces Continue Watching, Recent Feed, and Recent Bookmarks in the
Apple TV Home top shelf when Yattee is focused. Tapping a tile opens
the video via the existing yattee://video/{id} deep link.
- New YatteeTopShelf app extension target (tvOS only). LD_ENTRY_POINT is
overridden to _NSExtensionMain; the tv-app-extension product type
defaults to _TVExtensionMain which is for the pre-tvOS-13 legacy API
and crashes modern TVTopShelfContentProvider subclasses at launch.
- Main app writes per-section JSON snapshots (capped at 10 items each)
to a shared App Group UserDefaults suite after bookmark, watch-history,
and feed-cache changes, plus an initial write on launch.
- Enabled-sections list is mirrored to the same App Group so the
extension can respect the user's selection without touching SwiftData.
- Settings → Top Shelf (tvOS only) lets the user toggle sections.
- Deep link playback shows a loading toast while video details are
fetched, and an error toast if no source is configured.
This commit is contained in:
29
YatteeTopShelf/TopShelfSnapshot.swift
Normal file
29
YatteeTopShelf/TopShelfSnapshot.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
import Foundation
|
||||
|
||||
// Must stay in sync with Yattee/Services/TopShelfSnapshot.swift.
|
||||
struct TopShelfItem: Codable, Hashable, Sendable {
|
||||
let videoID: String
|
||||
let title: String
|
||||
let authorName: String
|
||||
let duration: TimeInterval
|
||||
let thumbnailURL: String?
|
||||
let deepLinkURL: String
|
||||
let progressSeconds: TimeInterval?
|
||||
}
|
||||
|
||||
enum TopShelfSnapshot {
|
||||
static func read(section: TopShelfSection, from defaults: UserDefaults = AppGroup.defaults) -> [TopShelfItem] {
|
||||
guard let data = defaults.data(forKey: section.snapshotKey),
|
||||
let items = try? JSONDecoder().decode([TopShelfItem].self, from: data) else {
|
||||
return []
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
static func enabledSections(from defaults: UserDefaults = AppGroup.defaults) -> [TopShelfSection] {
|
||||
guard let raw = defaults.array(forKey: AppGroup.enabledSectionsKey) as? [String] else {
|
||||
return TopShelfSection.defaultOrder
|
||||
}
|
||||
return raw.compactMap { TopShelfSection(rawValue: $0) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user