mirror of
https://github.com/yattee/yattee.git
synced 2024-11-09 15:58:20 +00:00
Feed cache
This commit is contained in:
parent
eae04c9382
commit
971beddc8d
@ -4,81 +4,6 @@ import SwiftUI
|
|||||||
struct FixtureEnvironmentObjectsModifier: ViewModifier {
|
struct FixtureEnvironmentObjectsModifier: ViewModifier {
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
.environmentObject(AccountsModel())
|
|
||||||
.environmentObject(InstancesManifest())
|
|
||||||
.environmentObject(invidious)
|
|
||||||
.environmentObject(NavigationModel())
|
|
||||||
.environmentObject(NetworkStateModel())
|
|
||||||
.environmentObject(PipedAPI())
|
|
||||||
.environmentObject(player)
|
|
||||||
.environmentObject(playerControls)
|
|
||||||
.environmentObject(PlayerTimeModel())
|
|
||||||
.environmentObject(PlaylistsModel())
|
|
||||||
.environmentObject(RecentsModel())
|
|
||||||
.environmentObject(SettingsModel())
|
|
||||||
.environmentObject(subscriptions)
|
|
||||||
.environmentObject(ThumbnailsModel())
|
|
||||||
}
|
|
||||||
|
|
||||||
private var invidious: InvidiousAPI {
|
|
||||||
let api = InvidiousAPI()
|
|
||||||
|
|
||||||
api.validInstance = true
|
|
||||||
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
private var player: PlayerModel {
|
|
||||||
let player = PlayerModel()
|
|
||||||
|
|
||||||
player.currentItem = PlayerQueueItem(
|
|
||||||
Video(
|
|
||||||
app: .invidious,
|
|
||||||
videoID: "https://a/b/c",
|
|
||||||
title: "Video Name",
|
|
||||||
author: "",
|
|
||||||
length: 0,
|
|
||||||
published: "2 days ago",
|
|
||||||
views: 43434,
|
|
||||||
description: "The 14\" and 16\" MacBook Pros are incredible. I can finally retire the travel iMac.\nThat shirt! http://shop.MKBHD.com\nMacBook Pro skins: https://dbrand.com/macbooks\n\n0:00 Intro\n1:38 Top Notch Design\n2:27 Let's Talk Ports\n7:11 RIP Touchbar\n8:20 The new displays\n10:12 Living with the notch\n12:37 Performance\n19:39 Battery\n20:30 So should you get it?\n\nThe Verge Review: https://youtu.be/ftU1HzBKd5Y\nTyler Stalman Review: https://youtu.be/I10WMJV96ns\nDeveloper's tweet: https://twitter.com/softwarejameson/status/1455971162060697613?s=09&t=WbOkVKgDdcegIdyOdurSNQ&utm_source=pocket_mylist\n\nTech I'm using right now: https://www.amazon.com/shop/MKBHD\n\nIntro Track: http://youtube.com/20syl\nPlaylist of MKBHD Intro music: https://goo.gl/B3AWV5\n\nLaptop provided by Apple for review.\n\n~\nhttp://twitter.com/MKBHD\nhttp://instagram.com/MKBHD\nhttp://facebook.com/MKBHD",
|
|
||||||
channel: .init(id: "", name: "Channel Name"),
|
|
||||||
likes: 2332,
|
|
||||||
dislikes: 30,
|
|
||||||
keywords: ["Video", "Computer", "Long Long Keyword"],
|
|
||||||
chapters: [
|
|
||||||
.init(
|
|
||||||
title: "Abc",
|
|
||||||
image: URL(string: "https://pipedproxy.kavin.rocks/vi/rr2XfL_df3o/hqdefault_29633.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg%3D%3D&rs=AOn4CLDFDm9D5SvsIA7D3v5n5KZahLs_UA&host=i.ytimg.com")!,
|
|
||||||
start: 3
|
|
||||||
),
|
|
||||||
.init(
|
|
||||||
title: "Def",
|
|
||||||
image: URL(string: "https://pipedproxy.kavin.rocks/vi/rr2XfL_df3o/hqdefault_98900.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg%3D%3D&rs=AOn4CLCfjXJBJb2O2q0jT0RHIi7hARVahw&host=i.ytimg.com")!,
|
|
||||||
start: 33
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
#if os(iOS)
|
|
||||||
player.playerSize = .init(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
|
|
||||||
#endif
|
|
||||||
let local = (1 ... 10).map { Video.local(URL(string: "https://\($0)")!) }
|
|
||||||
let videos = Video.allFixtures + local
|
|
||||||
player.queue = videos.map { PlayerQueueItem($0) }
|
|
||||||
|
|
||||||
return player
|
|
||||||
}
|
|
||||||
|
|
||||||
private var playerControls: PlayerControlsModel {
|
|
||||||
PlayerControlsModel(presentingControls: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var subscriptions: SubscriptionsModel {
|
|
||||||
let subscriptions = SubscriptionsModel()
|
|
||||||
|
|
||||||
subscriptions.channels = Video.allFixtures.map { $0.channel }
|
|
||||||
|
|
||||||
return subscriptions
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,4 +84,8 @@ struct Account: Defaults.Serializable, Hashable, Identifiable {
|
|||||||
func hash(into hasher: inout Hasher) {
|
func hash(into hasher: inout Hasher) {
|
||||||
hasher.combine(username)
|
hasher.combine(username)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var feedCacheKey: String {
|
||||||
|
"feed-\(id)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
static let basePath = "/api/v1"
|
static let basePath = "/api/v1"
|
||||||
|
|
||||||
@Published var account: Account!
|
@Published var account: Account!
|
||||||
@Published var validInstance = true
|
|
||||||
|
|
||||||
static func withAnonymousAccountForInstanceURL(_ url: URL) -> InvidiousAPI {
|
static func withAnonymousAccountForInstanceURL(_ url: URL) -> InvidiousAPI {
|
||||||
.init(account: Instance(app: .invidious, apiURLString: url.absoluteString).anonymousAccount)
|
.init(account: Instance(app: .invidious, apiURLString: url.absoluteString).anonymousAccount)
|
||||||
@ -35,8 +34,6 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
func setAccount(_ account: Account) {
|
func setAccount(_ account: Account) {
|
||||||
self.account = account
|
self.account = account
|
||||||
|
|
||||||
validInstance = account.anonymous
|
|
||||||
|
|
||||||
configure()
|
configure()
|
||||||
|
|
||||||
if !account.anonymous {
|
if !account.anonymous {
|
||||||
@ -45,31 +42,15 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validate() {
|
func validate() {
|
||||||
validateInstance()
|
|
||||||
validateSID()
|
validateSID()
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateInstance() {
|
|
||||||
guard !validInstance else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
home?
|
|
||||||
.load()
|
|
||||||
.onSuccess { _ in
|
|
||||||
self.validInstance = true
|
|
||||||
}
|
|
||||||
.onFailure { _ in
|
|
||||||
self.validInstance = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateSID() {
|
func validateSID() {
|
||||||
guard signedIn, !(account.token?.isEmpty ?? true) else {
|
guard signedIn, !(account.token?.isEmpty ?? true) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
feed?
|
notifications?
|
||||||
.load()
|
.load()
|
||||||
.onFailure { _ in
|
.onFailure { _ in
|
||||||
self.updateToken(force: true)
|
self.updateToken(force: true)
|
||||||
@ -273,8 +254,17 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
resource(baseURL: account.url, path: "/feed/subscriptions")
|
resource(baseURL: account.url, path: "/feed/subscriptions")
|
||||||
}
|
}
|
||||||
|
|
||||||
var feed: Resource? {
|
func feed(_ page: Int?) -> Resource? {
|
||||||
resource(baseURL: account.url, path: "\(Self.basePath)/auth/feed")
|
resource(baseURL: account.url, path: "\(Self.basePath)/auth/feed")
|
||||||
|
.withParam("page", String(page ?? 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
var feed: Resource? {
|
||||||
|
resource(baseURL: account.url, path: basePathAppending("auth/feed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var notifications: Resource? {
|
||||||
|
resource(baseURL: account.url, path: basePathAppending("auth/notifications"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscriptions: Resource? {
|
var subscriptions: Resource? {
|
||||||
|
@ -70,7 +70,7 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
feed?
|
feed(1)?
|
||||||
.load()
|
.load()
|
||||||
.onFailure { _ in
|
.onFailure { _ in
|
||||||
self.updateToken(force: true)
|
self.updateToken(force: true)
|
||||||
@ -262,8 +262,9 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
|
|||||||
resource(baseURL: account.url, path: "/feed/subscriptions")
|
resource(baseURL: account.url, path: "/feed/subscriptions")
|
||||||
}
|
}
|
||||||
|
|
||||||
var feed: Resource? {
|
func feed(_ page: Int?) -> Resource? {
|
||||||
resource(baseURL: account.url, path: "\(Self.basePath)/auth/feed")
|
resource(baseURL: account.url, path: "\(Self.basePath)/auth/feed")
|
||||||
|
.withParam("page", String(page ?? 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscriptions: Resource? {
|
var subscriptions: Resource? {
|
||||||
|
@ -220,7 +220,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
|||||||
resource(baseURL: account.instance.apiURL, path: "subscriptions")
|
resource(baseURL: account.instance.apiURL, path: "subscriptions")
|
||||||
}
|
}
|
||||||
|
|
||||||
var feed: Resource? {
|
func feed(_: Int?) -> Resource? {
|
||||||
resource(baseURL: account.instance.apiURL, path: "feed")
|
resource(baseURL: account.instance.apiURL, path: "feed")
|
||||||
.withParam("authToken", account.token)
|
.withParam("authToken", account.token)
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ protocol VideosAPI {
|
|||||||
|
|
||||||
func video(_ id: Video.ID) -> Resource
|
func video(_ id: Video.ID) -> Resource
|
||||||
|
|
||||||
|
func feed(_ page: Int?) -> Resource?
|
||||||
var subscriptions: Resource? { get }
|
var subscriptions: Resource? { get }
|
||||||
var feed: Resource? { get }
|
|
||||||
var home: Resource? { get }
|
var home: Resource? { get }
|
||||||
var popular: Resource? { get }
|
var popular: Resource? { get }
|
||||||
var playlists: Resource? { get }
|
var playlists: Resource? { get }
|
||||||
|
@ -30,6 +30,10 @@ enum VideosApp: String, CaseIterable {
|
|||||||
supportsAccounts
|
supportsAccounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var paginatesSubscriptions: Bool {
|
||||||
|
self == .invidious
|
||||||
|
}
|
||||||
|
|
||||||
var supportsTrendingCategories: Bool {
|
var supportsTrendingCategories: Bool {
|
||||||
self == .invidious
|
self == .invidious
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,40 @@
|
|||||||
|
import Cache
|
||||||
import Foundation
|
import Foundation
|
||||||
import Logging
|
import Logging
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
struct CacheModel {
|
struct CacheModel {
|
||||||
static var shared = CacheModel()
|
static var shared = CacheModel()
|
||||||
|
|
||||||
|
static let jsonToDataTransformer: (JSON) -> Data = { try! $0.rawData() }
|
||||||
|
static let jsonFromDataTransformer: (Data) -> JSON = { try! JSON(data: $0) }
|
||||||
|
static let jsonTransformer = Transformer(toData: jsonToDataTransformer, fromData: jsonFromDataTransformer)
|
||||||
|
|
||||||
let logger = Logger(label: "stream.yattee.cache")
|
let logger = Logger(label: "stream.yattee.cache")
|
||||||
|
|
||||||
static let bookmarksGroup = "group.stream.yattee.app.bookmarks"
|
static let bookmarksGroup = "group.stream.yattee.app.bookmarks"
|
||||||
let bookmarksDefaults = UserDefaults(suiteName: Self.bookmarksGroup)
|
let bookmarksDefaults = UserDefaults(suiteName: Self.bookmarksGroup)
|
||||||
|
|
||||||
func removeAll() {
|
func clearBookmarks() {
|
||||||
guard let bookmarksDefaults else { return }
|
guard let bookmarksDefaults else { return }
|
||||||
bookmarksDefaults.dictionaryRepresentation().keys.forEach(bookmarksDefaults.removeObject(forKey:))
|
bookmarksDefaults.dictionaryRepresentation().keys.forEach(bookmarksDefaults.removeObject(forKey:))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clear() {
|
||||||
|
FeedCacheModel.shared.clear()
|
||||||
|
VideosCacheModel.shared.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalSize: Int {
|
||||||
|
(FeedCacheModel.shared.storage.totalDiskStorageSize ?? 0) +
|
||||||
|
(VideosCacheModel.shared.storage.totalDiskStorageSize ?? 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalSizeFormatted: String {
|
||||||
|
totalSizeFormatter.string(fromByteCount: Int64(totalSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var totalSizeFormatter: ByteCountFormatter {
|
||||||
|
.init()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
62
Model/Cache/FeedCacheModel.swift
Normal file
62
Model/Cache/FeedCacheModel.swift
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import Cache
|
||||||
|
import Foundation
|
||||||
|
import Logging
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
|
struct FeedCacheModel {
|
||||||
|
static let shared = FeedCacheModel()
|
||||||
|
let logger = Logger(label: "stream.yattee.cache.feed")
|
||||||
|
|
||||||
|
static let diskConfig = DiskConfig(name: "feed")
|
||||||
|
static let memoryConfig = MemoryConfig()
|
||||||
|
|
||||||
|
let storage = try! Storage<String, JSON>(
|
||||||
|
diskConfig: Self.diskConfig,
|
||||||
|
memoryConfig: Self.memoryConfig,
|
||||||
|
transformer: CacheModel.jsonTransformer
|
||||||
|
)
|
||||||
|
|
||||||
|
func storeFeed(account: Account, videos: [Video]) {
|
||||||
|
let date = dateFormatter.string(from: Date())
|
||||||
|
logger.info("caching feed \(account.feedCacheKey) -- \(date)")
|
||||||
|
let feedTimeObject: JSON = ["date": date]
|
||||||
|
let videosObject: JSON = ["videos": videos.map(\.json).map(\.object)]
|
||||||
|
try? storage.setObject(feedTimeObject, forKey: feedTimeCacheKey(account.feedCacheKey))
|
||||||
|
try? storage.setObject(videosObject, forKey: account.feedCacheKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func retrieveFeed(account: Account) -> [Video] {
|
||||||
|
logger.info("retrieving cache for \(account.feedCacheKey)")
|
||||||
|
|
||||||
|
if let json = try? storage.object(forKey: account.feedCacheKey),
|
||||||
|
let videos = json.dictionaryValue["videos"]
|
||||||
|
{
|
||||||
|
return videos.arrayValue.map { Video.from($0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFeedTime(account: Account) -> Date? {
|
||||||
|
if let json = try? storage.object(forKey: feedTimeCacheKey(account.feedCacheKey)),
|
||||||
|
let string = json.dictionaryValue["date"]?.string,
|
||||||
|
let date = dateFormatter.date(from: string)
|
||||||
|
{
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func clear() {
|
||||||
|
try? storage.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dateFormatter: ISO8601DateFormatter {
|
||||||
|
.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func feedTimeCacheKey(_ feedCacheKey: String) -> String {
|
||||||
|
"\(feedCacheKey)-feedTime"
|
||||||
|
}
|
||||||
|
}
|
@ -7,31 +7,31 @@ struct VideosCacheModel {
|
|||||||
static let shared = VideosCacheModel()
|
static let shared = VideosCacheModel()
|
||||||
let logger = Logger(label: "stream.yattee.cache.videos")
|
let logger = Logger(label: "stream.yattee.cache.videos")
|
||||||
|
|
||||||
static let jsonToDataTransformer: (JSON) -> Data = { try! $0.rawData() }
|
static let diskConfig = DiskConfig(name: "videos")
|
||||||
static let jsonFromDataTransformer: (Data) -> JSON = { try! JSON(data: $0) }
|
static let memoryConfig = MemoryConfig()
|
||||||
static let jsonTransformer = Transformer(toData: jsonToDataTransformer, fromData: jsonFromDataTransformer)
|
|
||||||
|
|
||||||
static let videosStorageDiskConfig = DiskConfig(name: "videos")
|
let storage = try! Storage<String, JSON>(
|
||||||
static let vidoesStorageMemoryConfig = MemoryConfig()
|
diskConfig: Self.diskConfig,
|
||||||
|
memoryConfig: Self.memoryConfig,
|
||||||
let videosStorage = try! Storage<String, JSON>(
|
transformer: CacheModel.jsonTransformer
|
||||||
diskConfig: Self.videosStorageDiskConfig,
|
|
||||||
memoryConfig: Self.vidoesStorageMemoryConfig,
|
|
||||||
transformer: Self.jsonTransformer
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func storeVideo(_ video: Video) {
|
func storeVideo(_ video: Video) {
|
||||||
logger.info("caching \(video.cacheKey)")
|
logger.info("caching \(video.cacheKey)")
|
||||||
try? videosStorage.setObject(video.json, forKey: video.cacheKey)
|
try? storage.setObject(video.json, forKey: video.cacheKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func retrieveVideo(_ cacheKey: String) -> Video? {
|
func retrieveVideo(_ cacheKey: String) -> Video? {
|
||||||
logger.info("retrieving cache for \(cacheKey)")
|
logger.info("retrieving cache for \(cacheKey)")
|
||||||
|
|
||||||
if let json = try? videosStorage.object(forKey: cacheKey) {
|
if let json = try? storage.object(forKey: cacheKey) {
|
||||||
return Video.from(json)
|
return Video.from(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clear() {
|
||||||
|
try? storage.removeAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ extension PlayerModel {
|
|||||||
|
|
||||||
func removeHistory() {
|
func removeHistory() {
|
||||||
removeAllWatches()
|
removeAllWatches()
|
||||||
CacheModel.shared.removeAll()
|
CacheModel.shared.clearBookmarks()
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeWatch(_ watch: Watch) {
|
func removeWatch(_ watch: Watch) {
|
||||||
|
@ -279,7 +279,7 @@ extension PlayerModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadQueueVideoDetails(_ item: PlayerQueueItem) {
|
func loadQueueVideoDetails(_ item: PlayerQueueItem) {
|
||||||
guard !accounts.current.isNil, !item.hasDetailsLoaded else { return }
|
guard !accounts.current.isNil, !item.hasDetailsLoaded, let video = item.video else { return }
|
||||||
|
|
||||||
let videoID = item.video?.videoID ?? item.videoID
|
let videoID = item.video?.videoID ?? item.videoID
|
||||||
|
|
||||||
@ -292,7 +292,7 @@ extension PlayerModel {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
playerAPI(item.video).loadDetails(item, completionHandler: { [weak self] newItem in
|
playerAPI(video)?.loadDetails(item, completionHandler: { [weak self] newItem in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|
||||||
self.queue.filter { $0.videoID == item.videoID }.forEach { item in
|
self.queue.filter { $0.videoID == item.videoID }.forEach { item in
|
||||||
|
@ -4,4 +4,11 @@ import SwiftUI
|
|||||||
struct Constants {
|
struct Constants {
|
||||||
static let yatteeProtocol = "yattee://"
|
static let yatteeProtocol = "yattee://"
|
||||||
static let overlayAnimation = Animation.linear(duration: 0.2)
|
static let overlayAnimation = Animation.linear(duration: 0.2)
|
||||||
|
static var progressViewScale: Double {
|
||||||
|
#if os(macOS)
|
||||||
|
0.4
|
||||||
|
#else
|
||||||
|
0.6
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,13 +53,29 @@ struct FavoriteItemView: View {
|
|||||||
#endif
|
#endif
|
||||||
.onAppear {
|
.onAppear {
|
||||||
resource?.addObserver(store)
|
resource?.addObserver(store)
|
||||||
resource?.loadIfNeeded()
|
if item.section == .subscriptions {
|
||||||
|
cacheFeed(resource?.loadIfNeeded())
|
||||||
|
} else {
|
||||||
|
resource?.loadIfNeeded()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: accounts.current) { _ in
|
.onChange(of: accounts.current) { _ in
|
||||||
resource?.addObserver(store)
|
resource?.addObserver(store)
|
||||||
resource?.load()
|
if item.section == .subscriptions {
|
||||||
|
cacheFeed(resource?.load())
|
||||||
|
} else {
|
||||||
|
resource?.load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func cacheFeed(_ request: Request?) {
|
||||||
|
request?.onSuccess { response in
|
||||||
|
if let videos: [Video] = response.typedContent() {
|
||||||
|
FeedCacheModel.shared.storeFeed(account: accounts.current, videos: videos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +94,7 @@ struct FavoriteItemView: View {
|
|||||||
switch item.section {
|
switch item.section {
|
||||||
case .subscriptions:
|
case .subscriptions:
|
||||||
if accounts.app.supportsSubscriptions {
|
if accounts.app.supportsSubscriptions {
|
||||||
return accounts.api.feed
|
return accounts.api.feed(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .popular:
|
case .popular:
|
||||||
|
@ -82,7 +82,7 @@ struct CommentView: View {
|
|||||||
repliesButton
|
repliesButton
|
||||||
|
|
||||||
ProgressView()
|
ProgressView()
|
||||||
.scaleEffect(progressViewScale, anchor: .center)
|
.scaleEffect(Constants.progressViewScale, anchor: .center)
|
||||||
.opacity(repliesID == comment.id && !comments.repliesLoaded ? 1 : 0)
|
.opacity(repliesID == comment.id && !comments.repliesLoaded ? 1 : 0)
|
||||||
.frame(maxHeight: 0)
|
.frame(maxHeight: 0)
|
||||||
}
|
}
|
||||||
@ -200,14 +200,6 @@ struct CommentView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var progressViewScale: Double {
|
|
||||||
#if os(macOS)
|
|
||||||
0.4
|
|
||||||
#else
|
|
||||||
0.6
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private var repliesList: some View {
|
private var repliesList: some View {
|
||||||
Group {
|
Group {
|
||||||
let last = comments.replies.last
|
let last = comments.replies.last
|
||||||
|
@ -89,7 +89,7 @@ struct SearchView: View {
|
|||||||
filtersMenu
|
filtersMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchTextField(favoriteItem: $favoriteItem)
|
SearchTextField()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ struct AdvancedSettings: View {
|
|||||||
|
|
||||||
@State private var countries = [String]()
|
@State private var countries = [String]()
|
||||||
@State private var filesToShare = [MPVClient.logFile]
|
@State private var filesToShare = [MPVClient.logFile]
|
||||||
@State private var presentingInstanceForm = false
|
|
||||||
@State private var presentingShareSheet = false
|
@State private var presentingShareSheet = false
|
||||||
@State private var savedFormInstanceID: Instance.ID?
|
|
||||||
|
private var settings = SettingsModel.shared
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
@ -36,9 +36,6 @@ struct AdvancedSettings: View {
|
|||||||
.onChange(of: countryOfPublicInstances) { newCountry in
|
.onChange(of: countryOfPublicInstances) { newCountry in
|
||||||
InstancesManifest.shared.setPublicAccount(newCountry, asCurrent: AccountsModel.shared.current?.isPublic ?? true)
|
InstancesManifest.shared.setPublicAccount(newCountry, asCurrent: AccountsModel.shared.current?.isPublic ?? true)
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $presentingInstanceForm) {
|
|
||||||
InstanceForm(savedInstanceID: $savedFormInstanceID)
|
|
||||||
}
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
.frame(maxWidth: 1000)
|
.frame(maxWidth: 1000)
|
||||||
#endif
|
#endif
|
||||||
@ -87,6 +84,11 @@ struct AdvancedSettings: View {
|
|||||||
logButton
|
logButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section(header: SettingsHeader(text: "Cache")) {
|
||||||
|
clearCacheButton
|
||||||
|
cacheSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder var mpvFooter: some View {
|
@ViewBuilder var mpvFooter: some View {
|
||||||
@ -128,13 +130,27 @@ struct AdvancedSettings: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private var addInstanceButton: some View {
|
private var clearCacheButton: some View {
|
||||||
Button {
|
Button {
|
||||||
presentingInstanceForm = true
|
settings.presentAlert(
|
||||||
|
Alert(
|
||||||
|
title: Text(
|
||||||
|
"Are you sure you want to clear cache?"
|
||||||
|
),
|
||||||
|
primaryButton: .destructive(Text("Clear"), action: CacheModel.shared.clear),
|
||||||
|
secondaryButton: .cancel()
|
||||||
|
)
|
||||||
|
)
|
||||||
} label: {
|
} label: {
|
||||||
Label("Add Location...", systemImage: "plus")
|
Text("Clear all")
|
||||||
|
.foregroundColor(.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cacheSize: some View {
|
||||||
|
Text(String(format: "Total size: %@", CacheModel.shared.totalSizeFormatted))
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AdvancedSettings_Previews: PreviewProvider {
|
struct AdvancedSettings_Previews: PreviewProvider {
|
||||||
|
@ -164,7 +164,9 @@ struct HistorySettings: View {
|
|||||||
|
|
||||||
struct HistorySettings_Previews: PreviewProvider {
|
struct HistorySettings_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
HistorySettings()
|
VStack(alignment: .leading) {
|
||||||
.injectFixtureEnvironmentObjects()
|
HistorySettings()
|
||||||
|
}
|
||||||
|
.frame(minHeight: 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,7 +231,7 @@ struct SettingsView: View {
|
|||||||
case .locations:
|
case .locations:
|
||||||
return 600
|
return 600
|
||||||
case .advanced:
|
case .advanced:
|
||||||
return 250
|
return 350
|
||||||
case .help:
|
case .help:
|
||||||
return 650
|
return 650
|
||||||
}
|
}
|
||||||
|
79
Shared/Subscriptions/SubscriptionsView.swift
Normal file
79
Shared/Subscriptions/SubscriptionsView.swift
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import Siesta
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SubscriptionsView: View {
|
||||||
|
@ObservedObject private var model = SubscriptionsViewModel.shared
|
||||||
|
@ObservedObject private var accounts = AccountsModel.shared
|
||||||
|
|
||||||
|
var videos: [ContentItem] {
|
||||||
|
ContentItem.array(of: model.videos)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
BrowserPlayerControls {
|
||||||
|
SignInRequiredView(title: "Subscriptions".localized()) {
|
||||||
|
VerticalCells(items: videos) {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
Button {
|
||||||
|
model.loadResources(force: true)
|
||||||
|
} label: {
|
||||||
|
Label("Refresh", systemImage: "arrow.clockwise")
|
||||||
|
.labelStyle(.iconOnly)
|
||||||
|
.imageScale(.small)
|
||||||
|
.font(.caption2)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CacheStatusHeader(refreshTime: model.formattedFeedTime, isLoading: model.isLoading)
|
||||||
|
}
|
||||||
|
.environment(\.loadMoreContentHandler) { model.loadNextPage() }
|
||||||
|
.onAppear {
|
||||||
|
model.loadResources()
|
||||||
|
}
|
||||||
|
.onChange(of: accounts.current) { _ in
|
||||||
|
model.reset()
|
||||||
|
model.loadResources(force: true)
|
||||||
|
}
|
||||||
|
#if os(iOS)
|
||||||
|
.refreshControl { refreshControl in
|
||||||
|
model.loadResources(force: true) {
|
||||||
|
refreshControl.endRefreshing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.backport
|
||||||
|
.refreshable {
|
||||||
|
await model.loadResources(force: true)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
|
.background(
|
||||||
|
Button("Refresh") {
|
||||||
|
model.loadResources(force: true)
|
||||||
|
}
|
||||||
|
.keyboardShortcut("r")
|
||||||
|
.opacity(0)
|
||||||
|
)
|
||||||
|
#endif
|
||||||
|
#if os(iOS)
|
||||||
|
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
|
||||||
|
#endif
|
||||||
|
#if !os(macOS)
|
||||||
|
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
|
||||||
|
model.loadResources()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SubscriptonsView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
SubscriptionsView()
|
||||||
|
}
|
||||||
|
}
|
148
Shared/Subscriptions/SubscriptionsViewModel.swift
Normal file
148
Shared/Subscriptions/SubscriptionsViewModel.swift
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import Foundation
|
||||||
|
import Siesta
|
||||||
|
|
||||||
|
final class SubscriptionsViewModel: ObservableObject {
|
||||||
|
static let shared = SubscriptionsViewModel()
|
||||||
|
|
||||||
|
@Published var isLoading = false
|
||||||
|
@Published var videos = [Video]()
|
||||||
|
@Published private var page = 1
|
||||||
|
|
||||||
|
private var accounts = AccountsModel.shared
|
||||||
|
|
||||||
|
var feed: Resource? {
|
||||||
|
accounts.api.feed(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadResources(force: Bool = false, onCompletion: @escaping () -> Void = {}) {
|
||||||
|
DispatchQueue.global(qos: .background).async { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
|
||||||
|
if !force {
|
||||||
|
self.loadCachedFeed()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.accounts.app == .invidious {
|
||||||
|
// Invidious for some reason won't refresh feed until homepage is loaded
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self, let home = self.accounts.api.home else { return }
|
||||||
|
self.request(home, force: force)?
|
||||||
|
.onCompletion { _ in
|
||||||
|
self.loadFeed(force: force, onCompletion: onCompletion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.loadFeed(force: force, onCompletion: onCompletion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadFeed(force: Bool = false, paginating: Bool = false, onCompletion: @escaping () -> Void = {}) {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self,
|
||||||
|
!self.isLoading,
|
||||||
|
let account = self.accounts.current
|
||||||
|
else {
|
||||||
|
self?.isLoading = false
|
||||||
|
onCompletion()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if paginating {
|
||||||
|
self.page += 1
|
||||||
|
} else {
|
||||||
|
self.page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let feedBeforeLoad = self.feed
|
||||||
|
var request: Request?
|
||||||
|
if let feedBeforeLoad {
|
||||||
|
request = self.request(feedBeforeLoad, force: force)
|
||||||
|
}
|
||||||
|
if request != nil {
|
||||||
|
self.isLoading = true
|
||||||
|
}
|
||||||
|
|
||||||
|
request?
|
||||||
|
.onCompletion { _ in
|
||||||
|
self.isLoading = false
|
||||||
|
onCompletion()
|
||||||
|
}
|
||||||
|
.onSuccess { response in
|
||||||
|
if let videos: [Video] = response.typedContent() {
|
||||||
|
if paginating {
|
||||||
|
self.videos.append(contentsOf: videos)
|
||||||
|
} else {
|
||||||
|
self.videos = videos
|
||||||
|
FeedCacheModel.shared.storeFeed(account: account, videos: self.videos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onFailure { error in
|
||||||
|
NavigationModel.shared.presentAlert(title: "Could not refresh Subscriptions", message: error.userMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
videos.removeAll()
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadNextPage() {
|
||||||
|
guard accounts.app.paginatesSubscriptions, !isLoading else { return }
|
||||||
|
|
||||||
|
loadFeed(force: true, paginating: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
var feedTime: Date? {
|
||||||
|
if let account = accounts.current {
|
||||||
|
return FeedCacheModel.shared.getFeedTime(account: account)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var formattedFeedTime: String {
|
||||||
|
if let feedTime {
|
||||||
|
let isSameDay = Calendar(identifier: .iso8601).isDate(feedTime, inSameDayAs: Date())
|
||||||
|
let formatter = isSameDay ? dateFormatterForTimeOnly : dateFormatter
|
||||||
|
return formatter.string(from: feedTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadCachedFeed() {
|
||||||
|
let cache = FeedCacheModel.shared.retrieveFeed(account: accounts.current)
|
||||||
|
if !cache.isEmpty {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.videos = cache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dateFormatter: DateFormatter {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateStyle = .short
|
||||||
|
formatter.timeStyle = .medium
|
||||||
|
|
||||||
|
return formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dateFormatterForTimeOnly: DateFormatter {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateStyle = .none
|
||||||
|
formatter.timeStyle = .medium
|
||||||
|
|
||||||
|
return formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
private func request(_ resource: Resource, force: Bool = false) -> Request? {
|
||||||
|
if force {
|
||||||
|
return resource.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource.loadIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
24
Shared/Views/CacheStatusHeader.swift
Normal file
24
Shared/Views/CacheStatusHeader.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CacheStatusHeader: View {
|
||||||
|
var refreshTime: String
|
||||||
|
var isLoading = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 6) {
|
||||||
|
ProgressView()
|
||||||
|
.progressViewStyle(.circular)
|
||||||
|
.scaleEffect(Constants.progressViewScale, anchor: .center)
|
||||||
|
.opacity(isLoading ? 1 : 0)
|
||||||
|
Text(refreshTime)
|
||||||
|
}
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CacheStatusHeader_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
CacheStatusHeader(refreshTime: "15:10:20")
|
||||||
|
}
|
||||||
|
}
|
@ -1,96 +0,0 @@
|
|||||||
import Siesta
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct SubscriptionsView: View {
|
|
||||||
@StateObject private var store = Store<[Video]>()
|
|
||||||
|
|
||||||
@ObservedObject private var accounts = AccountsModel.shared
|
|
||||||
|
|
||||||
var feed: Resource? {
|
|
||||||
accounts.api.feed
|
|
||||||
}
|
|
||||||
|
|
||||||
var videos: [ContentItem] {
|
|
||||||
ContentItem.array(of: store.collection)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
BrowserPlayerControls {
|
|
||||||
SignInRequiredView(title: "Subscriptions".localized()) {
|
|
||||||
VerticalCells(items: videos)
|
|
||||||
.onAppear {
|
|
||||||
loadResources()
|
|
||||||
}
|
|
||||||
.onChange(of: accounts.current) { _ in
|
|
||||||
loadResources(force: true)
|
|
||||||
}
|
|
||||||
#if os(iOS)
|
|
||||||
.refreshControl { refreshControl in
|
|
||||||
loadResources(force: true) {
|
|
||||||
refreshControl.endRefreshing()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.backport
|
|
||||||
.refreshable {
|
|
||||||
await loadResources(force: true)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !os(tvOS)
|
|
||||||
.background(
|
|
||||||
Button("Refresh") {
|
|
||||||
loadResources(force: true)
|
|
||||||
}
|
|
||||||
.keyboardShortcut("r")
|
|
||||||
.opacity(0)
|
|
||||||
)
|
|
||||||
#endif
|
|
||||||
#if os(iOS)
|
|
||||||
.navigationBarTitleDisplayMode(RefreshControl.navigationBarTitleDisplayMode)
|
|
||||||
#endif
|
|
||||||
#if !os(macOS)
|
|
||||||
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
|
|
||||||
loadResources()
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadResources(force: Bool = false, onCompletion: @escaping () -> Void = {}) {
|
|
||||||
feed?.addObserver(store)
|
|
||||||
|
|
||||||
if accounts.app == .invidious {
|
|
||||||
// Invidious for some reason won't refresh feed until homepage is loaded
|
|
||||||
if let request = force ? accounts.api.home?.load() : accounts.api.home?.loadIfNeeded() {
|
|
||||||
request.onSuccess { _ in
|
|
||||||
loadFeed(force: force, onCompletion: onCompletion)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadFeed(force: force, onCompletion: onCompletion)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadFeed(force: force, onCompletion: onCompletion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadFeed(force: Bool = false, onCompletion: @escaping () -> Void = {}) {
|
|
||||||
if let request = force ? feed?.load() : feed?.loadIfNeeded() {
|
|
||||||
request.onCompletion { _ in
|
|
||||||
onCompletion()
|
|
||||||
}
|
|
||||||
.onFailure { error in
|
|
||||||
NavigationModel.shared.presentAlert(title: "Could not refresh Subscriptions", message: error.userMessage)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onCompletion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SubscriptonsView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
SubscriptionsView()
|
|
||||||
.injectFixtureEnvironmentObjects()
|
|
||||||
}
|
|
||||||
}
|
|
@ -550,6 +550,11 @@
|
|||||||
377F9F7B294403F20043F856 /* VideosCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7A294403F20043F856 /* VideosCacheModel.swift */; };
|
377F9F7B294403F20043F856 /* VideosCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7A294403F20043F856 /* VideosCacheModel.swift */; };
|
||||||
377F9F7C294403F20043F856 /* VideosCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7A294403F20043F856 /* VideosCacheModel.swift */; };
|
377F9F7C294403F20043F856 /* VideosCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7A294403F20043F856 /* VideosCacheModel.swift */; };
|
||||||
377F9F7D294403F20043F856 /* VideosCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7A294403F20043F856 /* VideosCacheModel.swift */; };
|
377F9F7D294403F20043F856 /* VideosCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7A294403F20043F856 /* VideosCacheModel.swift */; };
|
||||||
|
377F9F7F2944175F0043F856 /* FeedCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7E2944175F0043F856 /* FeedCacheModel.swift */; };
|
||||||
|
377F9F802944175F0043F856 /* FeedCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7E2944175F0043F856 /* FeedCacheModel.swift */; };
|
||||||
|
377F9F812944175F0043F856 /* FeedCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F9F7E2944175F0043F856 /* FeedCacheModel.swift */; };
|
||||||
|
377F9F83294417B40043F856 /* Cache in Frameworks */ = {isa = PBXBuildFile; productRef = 377F9F82294417B40043F856 /* Cache */; };
|
||||||
|
377F9F85294417FA0043F856 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 377F9F84294417FA0043F856 /* SwiftyJSON */; };
|
||||||
377FC7D5267A080300A6BBAF /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7D4267A080300A6BBAF /* SwiftyJSON */; };
|
377FC7D5267A080300A6BBAF /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7D4267A080300A6BBAF /* SwiftyJSON */; };
|
||||||
377FC7DB267A080300A6BBAF /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7DA267A080300A6BBAF /* Logging */; };
|
377FC7DB267A080300A6BBAF /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7DA267A080300A6BBAF /* Logging */; };
|
||||||
377FC7DC267A081800A6BBAF /* PopularView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27D26737323007FC770 /* PopularView.swift */; };
|
377FC7DC267A081800A6BBAF /* PopularView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27D26737323007FC770 /* PopularView.swift */; };
|
||||||
@ -818,6 +823,12 @@
|
|||||||
37E64DD126D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; };
|
37E64DD126D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; };
|
||||||
37E64DD226D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; };
|
37E64DD226D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; };
|
||||||
37E64DD326D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; };
|
37E64DD326D597EB00C71877 /* SubscriptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */; };
|
||||||
|
37E6D79C2944AE1A00550C3D /* SubscriptionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6D79B2944AE1A00550C3D /* SubscriptionsViewModel.swift */; };
|
||||||
|
37E6D79D2944AE1A00550C3D /* SubscriptionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6D79B2944AE1A00550C3D /* SubscriptionsViewModel.swift */; };
|
||||||
|
37E6D79E2944AE1A00550C3D /* SubscriptionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6D79B2944AE1A00550C3D /* SubscriptionsViewModel.swift */; };
|
||||||
|
37E6D7A02944CD3800550C3D /* CacheStatusHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6D79F2944CD3800550C3D /* CacheStatusHeader.swift */; };
|
||||||
|
37E6D7A12944CD3800550C3D /* CacheStatusHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6D79F2944CD3800550C3D /* CacheStatusHeader.swift */; };
|
||||||
|
37E6D7A22944CD3800550C3D /* CacheStatusHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E6D79F2944CD3800550C3D /* CacheStatusHeader.swift */; };
|
||||||
37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; };
|
37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; };
|
||||||
37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; };
|
37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; };
|
||||||
37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; };
|
37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70922271CD43000D34DDE /* WelcomeScreen.swift */; };
|
||||||
@ -1229,6 +1240,7 @@
|
|||||||
377ABC47286E5887009C986F /* Sequence+Unique.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+Unique.swift"; sourceTree = "<group>"; };
|
377ABC47286E5887009C986F /* Sequence+Unique.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+Unique.swift"; sourceTree = "<group>"; };
|
||||||
377E17132928265900894889 /* ListRowSeparator+Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ListRowSeparator+Backport.swift"; sourceTree = "<group>"; };
|
377E17132928265900894889 /* ListRowSeparator+Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ListRowSeparator+Backport.swift"; sourceTree = "<group>"; };
|
||||||
377F9F7A294403F20043F856 /* VideosCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideosCacheModel.swift; sourceTree = "<group>"; };
|
377F9F7A294403F20043F856 /* VideosCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideosCacheModel.swift; sourceTree = "<group>"; };
|
||||||
|
377F9F7E2944175F0043F856 /* FeedCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedCacheModel.swift; sourceTree = "<group>"; };
|
||||||
377FF88A291A60310028EB0B /* OpenVideosModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenVideosModel.swift; sourceTree = "<group>"; };
|
377FF88A291A60310028EB0B /* OpenVideosModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenVideosModel.swift; sourceTree = "<group>"; };
|
||||||
377FF88E291A99580028EB0B /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = "<group>"; };
|
377FF88E291A99580028EB0B /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = "<group>"; };
|
||||||
37824309291E58D6005DEC1C /* Open in Yattee.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Open in Yattee.entitlements"; sourceTree = "<group>"; };
|
37824309291E58D6005DEC1C /* Open in Yattee.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Open in Yattee.entitlements"; sourceTree = "<group>"; };
|
||||||
@ -1348,6 +1360,8 @@
|
|||||||
37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsNavigationLink.swift; sourceTree = "<group>"; };
|
37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsNavigationLink.swift; sourceTree = "<group>"; };
|
||||||
37E2EEAA270656EC00170416 /* BrowserPlayerControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserPlayerControls.swift; sourceTree = "<group>"; };
|
37E2EEAA270656EC00170416 /* BrowserPlayerControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserPlayerControls.swift; sourceTree = "<group>"; };
|
||||||
37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsModel.swift; sourceTree = "<group>"; };
|
37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsModel.swift; sourceTree = "<group>"; };
|
||||||
|
37E6D79B2944AE1A00550C3D /* SubscriptionsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
37E6D79F2944CD3800550C3D /* CacheStatusHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheStatusHeader.swift; sourceTree = "<group>"; };
|
||||||
37E70922271CD43000D34DDE /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
|
37E70922271CD43000D34DDE /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
|
||||||
37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettingsButton.swift; sourceTree = "<group>"; };
|
37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettingsButton.swift; sourceTree = "<group>"; };
|
||||||
37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDetailsOverlay.swift; sourceTree = "<group>"; };
|
37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDetailsOverlay.swift; sourceTree = "<group>"; };
|
||||||
@ -1397,7 +1411,9 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
377F9F85294417FA0043F856 /* SwiftyJSON in Frameworks */,
|
||||||
37DA0F20291DD6B8009B38CF /* Logging in Frameworks */,
|
37DA0F20291DD6B8009B38CF /* Logging in Frameworks */,
|
||||||
|
377F9F83294417B40043F856 /* Cache in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1744,9 +1760,9 @@
|
|||||||
37F7D82B289EB05F00E2B3D0 /* SettingsPickerModifier.swift */,
|
37F7D82B289EB05F00E2B3D0 /* SettingsPickerModifier.swift */,
|
||||||
3784B23C2728B85300B09468 /* ShareButton.swift */,
|
3784B23C2728B85300B09468 /* ShareButton.swift */,
|
||||||
376B2E0626F920D600B1D64D /* SignInRequiredView.swift */,
|
376B2E0626F920D600B1D64D /* SignInRequiredView.swift */,
|
||||||
37AAF29F26741C97007FC770 /* SubscriptionsView.swift */,
|
|
||||||
37B17D9F268A1F25006AEE9B /* VideoContextMenuView.swift */,
|
37B17D9F268A1F25006AEE9B /* VideoContextMenuView.swift */,
|
||||||
37E70922271CD43000D34DDE /* WelcomeScreen.swift */,
|
37E70922271CD43000D34DDE /* WelcomeScreen.swift */,
|
||||||
|
37E6D79F2944CD3800550C3D /* CacheStatusHeader.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1977,6 +1993,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
37F5E8B9291BEF69006C15F5 /* CacheModel.swift */,
|
37F5E8B9291BEF69006C15F5 /* CacheModel.swift */,
|
||||||
|
377F9F7E2944175F0043F856 /* FeedCacheModel.swift */,
|
||||||
377F9F7A294403F20043F856 /* VideosCacheModel.swift */,
|
377F9F7A294403F20043F856 /* VideosCacheModel.swift */,
|
||||||
);
|
);
|
||||||
path = Cache;
|
path = Cache;
|
||||||
@ -2124,6 +2141,7 @@
|
|||||||
371AAE2626CEBF1600901972 /* Playlists */,
|
371AAE2626CEBF1600901972 /* Playlists */,
|
||||||
3782B95527557A2400990149 /* Search */,
|
3782B95527557A2400990149 /* Search */,
|
||||||
37484C1726FC836500287258 /* Settings */,
|
37484C1726FC836500287258 /* Settings */,
|
||||||
|
37E6D79A2944ADCB00550C3D /* Subscriptions */,
|
||||||
371AAE2526CEBF0B00901972 /* Trending */,
|
371AAE2526CEBF0B00901972 /* Trending */,
|
||||||
371AAE2726CEBF4700901972 /* Videos */,
|
371AAE2726CEBF4700901972 /* Videos */,
|
||||||
371AAE2826CEC7D900901972 /* Views */,
|
371AAE2826CEC7D900901972 /* Views */,
|
||||||
@ -2294,6 +2312,15 @@
|
|||||||
path = Vendor;
|
path = Vendor;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
37E6D79A2944ADCB00550C3D /* Subscriptions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
37AAF29F26741C97007FC770 /* SubscriptionsView.swift */,
|
||||||
|
37E6D79B2944AE1A00550C3D /* SubscriptionsViewModel.swift */,
|
||||||
|
);
|
||||||
|
path = Subscriptions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
37EBD8C227AF0D7C00F1C24B /* Backends */ = {
|
37EBD8C227AF0D7C00F1C24B /* Backends */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -2368,6 +2395,8 @@
|
|||||||
name = "Open in Yattee";
|
name = "Open in Yattee";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
37DA0F1F291DD6B8009B38CF /* Logging */,
|
37DA0F1F291DD6B8009B38CF /* Logging */,
|
||||||
|
377F9F82294417B40043F856 /* Cache */,
|
||||||
|
377F9F84294417FA0043F856 /* SwiftyJSON */,
|
||||||
);
|
);
|
||||||
productName = "Open in Yattee";
|
productName = "Open in Yattee";
|
||||||
productReference = 37095E7F291DC85400301883 /* Open in Yattee.appex */;
|
productReference = 37095E7F291DC85400301883 /* Open in Yattee.appex */;
|
||||||
@ -2872,6 +2901,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
37E6D79C2944AE1A00550C3D /* SubscriptionsViewModel.swift in Sources */,
|
||||||
374710052755291C00CE0F87 /* SearchTextField.swift in Sources */,
|
374710052755291C00CE0F87 /* SearchTextField.swift in Sources */,
|
||||||
37494EA529200B14000DF176 /* DocumentsView.swift in Sources */,
|
37494EA529200B14000DF176 /* DocumentsView.swift in Sources */,
|
||||||
374DE88028BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */,
|
374DE88028BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */,
|
||||||
@ -2920,6 +2950,7 @@
|
|||||||
37D2E0D028B67DBC00F64D52 /* AnimationCompletionObserverModifier.swift in Sources */,
|
37D2E0D028B67DBC00F64D52 /* AnimationCompletionObserverModifier.swift in Sources */,
|
||||||
3727B74A27872A920021C15E /* VisualEffectBlur-iOS.swift in Sources */,
|
3727B74A27872A920021C15E /* VisualEffectBlur-iOS.swift in Sources */,
|
||||||
3709528829283A21001ECA40 /* RecentDocumentsView.swift in Sources */,
|
3709528829283A21001ECA40 /* RecentDocumentsView.swift in Sources */,
|
||||||
|
377F9F7F2944175F0043F856 /* FeedCacheModel.swift in Sources */,
|
||||||
37977583268922F600DD52A8 /* InvidiousAPI.swift in Sources */,
|
37977583268922F600DD52A8 /* InvidiousAPI.swift in Sources */,
|
||||||
374AB3D728BCAF0000DF56FB /* SeekModel.swift in Sources */,
|
374AB3D728BCAF0000DF56FB /* SeekModel.swift in Sources */,
|
||||||
37130A5F277657300033018A /* PersistenceController.swift in Sources */,
|
37130A5F277657300033018A /* PersistenceController.swift in Sources */,
|
||||||
@ -3015,6 +3046,7 @@
|
|||||||
37169AA22729D98A0011DE61 /* InstancesBridge.swift in Sources */,
|
37169AA22729D98A0011DE61 /* InstancesBridge.swift in Sources */,
|
||||||
37C3A24527235DA70087A57A /* ChannelPlaylist.swift in Sources */,
|
37C3A24527235DA70087A57A /* ChannelPlaylist.swift in Sources */,
|
||||||
37030FFF27B04DCC00ECDDAA /* PlayerControls.swift in Sources */,
|
37030FFF27B04DCC00ECDDAA /* PlayerControls.swift in Sources */,
|
||||||
|
37E6D7A02944CD3800550C3D /* CacheStatusHeader.swift in Sources */,
|
||||||
374C053F272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
|
374C053F272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
|
||||||
375F7410289DC35A00747050 /* PlayerBackendView.swift in Sources */,
|
375F7410289DC35A00747050 /* PlayerBackendView.swift in Sources */,
|
||||||
37FB28412721B22200A57617 /* ContentItem.swift in Sources */,
|
37FB28412721B22200A57617 /* ContentItem.swift in Sources */,
|
||||||
@ -3268,6 +3300,7 @@
|
|||||||
37B81AFD26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
37B81AFD26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
||||||
37C0697F2725C8D400F7F6CB /* CMTime+DefaultTimescale.swift in Sources */,
|
37C0697F2725C8D400F7F6CB /* CMTime+DefaultTimescale.swift in Sources */,
|
||||||
37A9965B26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
37A9965B26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
||||||
|
37E6D79D2944AE1A00550C3D /* SubscriptionsViewModel.swift in Sources */,
|
||||||
37732FF52703D32400F04329 /* Sidebar.swift in Sources */,
|
37732FF52703D32400F04329 /* Sidebar.swift in Sources */,
|
||||||
379775942689365600DD52A8 /* Array+Next.swift in Sources */,
|
379775942689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||||
377ABC49286E5887009C986F /* Sequence+Unique.swift in Sources */,
|
377ABC49286E5887009C986F /* Sequence+Unique.swift in Sources */,
|
||||||
@ -3296,6 +3329,7 @@
|
|||||||
37732FF12703A26300F04329 /* AccountValidationStatus.swift in Sources */,
|
37732FF12703A26300F04329 /* AccountValidationStatus.swift in Sources */,
|
||||||
37BA794C26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */,
|
37BA794C26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */,
|
||||||
37CC3F46270CE30600608308 /* PlayerQueueItem.swift in Sources */,
|
37CC3F46270CE30600608308 /* PlayerQueueItem.swift in Sources */,
|
||||||
|
37E6D7A12944CD3800550C3D /* CacheStatusHeader.swift in Sources */,
|
||||||
37B2631B2735EAAB00FE0D40 /* FavoriteResourceObserver.swift in Sources */,
|
37B2631B2735EAAB00FE0D40 /* FavoriteResourceObserver.swift in Sources */,
|
||||||
3700155C271B0D4D0049C794 /* PipedAPI.swift in Sources */,
|
3700155C271B0D4D0049C794 /* PipedAPI.swift in Sources */,
|
||||||
376BE50C27349108009AD608 /* BrowsingSettings.swift in Sources */,
|
376BE50C27349108009AD608 /* BrowsingSettings.swift in Sources */,
|
||||||
@ -3312,6 +3346,7 @@
|
|||||||
374924E4292141320017D862 /* InspectorView.swift in Sources */,
|
374924E4292141320017D862 /* InspectorView.swift in Sources */,
|
||||||
375168D72700FDB8008F96A6 /* Debounce.swift in Sources */,
|
375168D72700FDB8008F96A6 /* Debounce.swift in Sources */,
|
||||||
37D526DF2720AC4400ED2F5E /* VideosAPI.swift in Sources */,
|
37D526DF2720AC4400ED2F5E /* VideosAPI.swift in Sources */,
|
||||||
|
377F9F802944175F0043F856 /* FeedCacheModel.swift in Sources */,
|
||||||
373C8FE5275B955100CB5936 /* CommentsPage.swift in Sources */,
|
373C8FE5275B955100CB5936 /* CommentsPage.swift in Sources */,
|
||||||
37D4B0E52671614900C925CA /* YatteeApp.swift in Sources */,
|
37D4B0E52671614900C925CA /* YatteeApp.swift in Sources */,
|
||||||
37130A60277657300033018A /* PersistenceController.swift in Sources */,
|
37130A60277657300033018A /* PersistenceController.swift in Sources */,
|
||||||
@ -3475,6 +3510,7 @@
|
|||||||
37C0697C2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
|
37C0697C2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
|
||||||
3718B9A12921A9640003DB2E /* VideoDetails.swift in Sources */,
|
3718B9A12921A9640003DB2E /* VideoDetails.swift in Sources */,
|
||||||
378AE93D274EDFB3006A4EE1 /* Backport.swift in Sources */,
|
378AE93D274EDFB3006A4EE1 /* Backport.swift in Sources */,
|
||||||
|
377F9F812944175F0043F856 /* FeedCacheModel.swift in Sources */,
|
||||||
37130A5D277657090033018A /* Yattee.xcdatamodeld in Sources */,
|
37130A5D277657090033018A /* Yattee.xcdatamodeld in Sources */,
|
||||||
37C3A243272359900087A57A /* Double+Format.swift in Sources */,
|
37C3A243272359900087A57A /* Double+Format.swift in Sources */,
|
||||||
37AAF29226740715007FC770 /* Channel.swift in Sources */,
|
37AAF29226740715007FC770 /* Channel.swift in Sources */,
|
||||||
@ -3589,11 +3625,13 @@
|
|||||||
37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */,
|
37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */,
|
||||||
37EF9A78275BEB8E0043B585 /* CommentView.swift in Sources */,
|
37EF9A78275BEB8E0043B585 /* CommentView.swift in Sources */,
|
||||||
37484C2726FC83E000287258 /* InstanceForm.swift in Sources */,
|
37484C2726FC83E000287258 /* InstanceForm.swift in Sources */,
|
||||||
|
37E6D7A22944CD3800550C3D /* CacheStatusHeader.swift in Sources */,
|
||||||
37F49BA826CB0FCE00304AC0 /* PlaylistFormView.swift in Sources */,
|
37F49BA826CB0FCE00304AC0 /* PlaylistFormView.swift in Sources */,
|
||||||
37F0F4F0286F734400C06C2E /* AdvancedSettings.swift in Sources */,
|
37F0F4F0286F734400C06C2E /* AdvancedSettings.swift in Sources */,
|
||||||
373197DA2732060100EF734F /* RelatedView.swift in Sources */,
|
373197DA2732060100EF734F /* RelatedView.swift in Sources */,
|
||||||
37DD9DA52785BBC900539416 /* NoCommentsView.swift in Sources */,
|
37DD9DA52785BBC900539416 /* NoCommentsView.swift in Sources */,
|
||||||
377ABC4A286E5887009C986F /* Sequence+Unique.swift in Sources */,
|
377ABC4A286E5887009C986F /* Sequence+Unique.swift in Sources */,
|
||||||
|
37E6D79E2944AE1A00550C3D /* SubscriptionsViewModel.swift in Sources */,
|
||||||
37D4B19926717E1500C925CA /* Video.swift in Sources */,
|
37D4B19926717E1500C925CA /* Video.swift in Sources */,
|
||||||
378E50FD26FE8B9F00F49626 /* Instance.swift in Sources */,
|
378E50FD26FE8B9F00F49626 /* Instance.swift in Sources */,
|
||||||
37169AA82729E2CC0011DE61 /* AccountsBridge.swift in Sources */,
|
37169AA82729E2CC0011DE61 /* AccountsBridge.swift in Sources */,
|
||||||
@ -3699,6 +3737,7 @@
|
|||||||
MARKETING_VERSION = 1.4.3;
|
MARKETING_VERSION = 1.4.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "stream.yattee.app.Open-in-Yattee";
|
PRODUCT_BUNDLE_IDENTIFIER = "stream.yattee.app.Open-in-Yattee";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@ -4732,6 +4771,16 @@
|
|||||||
package = 374D11E52943C56300CB4350 /* XCRemoteSwiftPackageReference "Cache" */;
|
package = 374D11E52943C56300CB4350 /* XCRemoteSwiftPackageReference "Cache" */;
|
||||||
productName = Cache;
|
productName = Cache;
|
||||||
};
|
};
|
||||||
|
377F9F82294417B40043F856 /* Cache */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 374D11E52943C56300CB4350 /* XCRemoteSwiftPackageReference "Cache" */;
|
||||||
|
productName = Cache;
|
||||||
|
};
|
||||||
|
377F9F84294417FA0043F856 /* SwiftyJSON */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 37D4B19B2671817900C925CA /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
||||||
|
productName = SwiftyJSON;
|
||||||
|
};
|
||||||
377FC7D4267A080300A6BBAF /* SwiftyJSON */ = {
|
377FC7D4267A080300A6BBAF /* SwiftyJSON */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 37D4B19B2671817900C925CA /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
package = 37D4B19B2671817900C925CA /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
||||||
|
Loading…
Reference in New Issue
Block a user