mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
Channels layout improvements, other UI fixes
This commit is contained in:
parent
1651110a5d
commit
b00b54ad2a
29
Extensions/Int+Format.swift
Normal file
29
Extensions/Int+Format.swift
Normal file
@ -0,0 +1,29 @@
|
||||
import Foundation
|
||||
|
||||
extension Int {
|
||||
func formattedAsAbbreviation() -> String {
|
||||
typealias Abbrevation = (threshold: Double, divisor: Double, suffix: String)
|
||||
let abbreviations: [Abbrevation] = [
|
||||
(0, 1, ""), (1000.0, 1000.0, "K"),
|
||||
(999_999.0, 1_000_000.0, "M"), (999_999_999.0, 1_000_000_000.0, "B")
|
||||
]
|
||||
|
||||
let startValue = Double(abs(self))
|
||||
|
||||
guard let nextAbbreviationIndex = abbreviations.firstIndex(where: { startValue < $0.threshold }) else {
|
||||
return String(self)
|
||||
}
|
||||
|
||||
let abbreviation = abbreviations[abbreviations.index(before: nextAbbreviationIndex)]
|
||||
let formatter = NumberFormatter()
|
||||
|
||||
formatter.positiveSuffix = abbreviation.suffix
|
||||
formatter.negativeSuffix = abbreviation.suffix
|
||||
formatter.allowsFloats = true
|
||||
formatter.minimumIntegerDigits = 1
|
||||
formatter.minimumFractionDigits = 0
|
||||
formatter.maximumFractionDigits = 1
|
||||
|
||||
return formatter.string(from: NSNumber(value: Double(self) / abbreviation.divisor))!
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ extension Video {
|
||||
views: 21534,
|
||||
description: "Some relaxing live piano music",
|
||||
genre: "Music",
|
||||
channel: Channel(id: "AbCdEFgHI", name: "The Channel", subscriptionsCount: "2.3K"),
|
||||
channel: Channel(id: "AbCdEFgHI", name: "The Channel", subscriptionsCount: 2300, videos: []),
|
||||
thumbnails: Thumbnail.fixturesForAllQualities(videoId: id),
|
||||
live: false,
|
||||
upcoming: false,
|
||||
|
@ -3,20 +3,41 @@ import Defaults
|
||||
import Foundation
|
||||
import SwiftyJSON
|
||||
|
||||
struct Channel: Codable, Identifiable, Defaults.Serializable {
|
||||
struct Channel: Identifiable, Hashable {
|
||||
var id: String
|
||||
var name: String
|
||||
var subscriptionsCount: String
|
||||
var videos = [Video]()
|
||||
|
||||
private var subscriptionsCount: Int?
|
||||
private var subscriptionsText: String?
|
||||
|
||||
init(json: JSON) {
|
||||
id = json["authorId"].stringValue
|
||||
name = json["author"].stringValue
|
||||
subscriptionsCount = json["subCountText"].stringValue
|
||||
subscriptionsCount = json["subCount"].int
|
||||
subscriptionsText = json["subCountText"].string
|
||||
|
||||
if let channelVideos = json.dictionaryValue["latestVideos"] {
|
||||
videos = channelVideos.arrayValue.map(Video.init)
|
||||
}
|
||||
}
|
||||
|
||||
init(id: String, name: String, subscriptionsCount: String) {
|
||||
init(id: String, name: String, subscriptionsCount: Int? = nil, videos: [Video] = []) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.subscriptionsCount = subscriptionsCount
|
||||
self.videos = videos
|
||||
}
|
||||
|
||||
var subscriptionsString: String? {
|
||||
if subscriptionsCount != nil {
|
||||
return subscriptionsCount!.formattedAsAbbreviation()
|
||||
}
|
||||
|
||||
return subscriptionsText
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
@ -75,12 +75,8 @@ final class InvidiousAPI: Service {
|
||||
content.json.arrayValue.map(Channel.init)
|
||||
}
|
||||
|
||||
configureTransformer("/channels/*", requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
||||
if let channelVideos = content.json.dictionaryValue["latestVideos"] {
|
||||
return channelVideos.arrayValue.map(Video.init)
|
||||
}
|
||||
|
||||
return []
|
||||
configureTransformer("/channels/*", requestMethods: [.get]) { (content: Entity<JSON>) -> Channel in
|
||||
Channel(json: content.json)
|
||||
}
|
||||
|
||||
configureTransformer("/videos/*", requestMethods: [.get]) { (content: Entity<JSON>) -> Video in
|
||||
@ -112,7 +108,7 @@ final class InvidiousAPI: Service {
|
||||
resource("/auth/subscriptions").child(id)
|
||||
}
|
||||
|
||||
func channelVideos(_ id: String) -> Resource {
|
||||
func channel(_ id: String) -> Resource {
|
||||
resource("/channels/\(id)")
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,6 @@ final class NavigationState: ObservableObject {
|
||||
|
||||
@Published var tabSelection: TabSelection = .subscriptions
|
||||
|
||||
@Published var showingChannel = false
|
||||
@Published var channel: Channel?
|
||||
|
||||
@Published var showingVideoDetails = false
|
||||
@Published var showingVideo = false
|
||||
@Published var video: Video?
|
||||
@ -23,15 +20,40 @@ final class NavigationState: ObservableObject {
|
||||
@Published var presentingUnsubscribeAlert = false
|
||||
@Published var channelToUnsubscribe: Channel!
|
||||
|
||||
@Published var openChannels = Set<Channel>()
|
||||
@Published var isChannelOpen = false
|
||||
@Published var sidebarSectionChanged = false
|
||||
|
||||
func openChannel(_ channel: Channel) {
|
||||
returnToDetails = false
|
||||
self.channel = channel
|
||||
showingChannel = true
|
||||
openChannels.insert(channel)
|
||||
|
||||
isChannelOpen = true
|
||||
tabSelection = .channel(channel.id)
|
||||
}
|
||||
|
||||
func closeChannel() {
|
||||
showingChannel = false
|
||||
channel = nil
|
||||
func closeChannel(_ channel: Channel) {
|
||||
guard openChannels.remove(channel) != nil else {
|
||||
return
|
||||
}
|
||||
|
||||
isChannelOpen = !openChannels.isEmpty
|
||||
|
||||
if tabSelection == .channel(channel.id) {
|
||||
tabSelection = .subscriptions
|
||||
}
|
||||
}
|
||||
|
||||
func closeAllChannels() {
|
||||
isChannelOpen = false
|
||||
openChannels.removeAll()
|
||||
}
|
||||
|
||||
func showOpenChannel(_ id: Channel.ID) -> Bool {
|
||||
if case .channel = tabSelection {
|
||||
return false
|
||||
} else {
|
||||
return !openChannels.contains { $0.id == id }
|
||||
}
|
||||
}
|
||||
|
||||
func openVideoDetails(_ video: Video) {
|
||||
@ -59,8 +81,10 @@ final class NavigationState: ObservableObject {
|
||||
get: {
|
||||
self.tabSelection
|
||||
},
|
||||
set: {
|
||||
self.tabSelection = $0 ?? .subscriptions
|
||||
set: { newValue in
|
||||
if newValue != nil {
|
||||
self.tabSelection = newValue!
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -17,29 +17,30 @@ final class Subscriptions: ObservableObject {
|
||||
channels.sorted { $0.name.lowercased() < $1.name.lowercased() }
|
||||
}
|
||||
|
||||
func subscribe(_ channelID: String) {
|
||||
performChannelSubscriptionRequest(channelID, method: .post)
|
||||
func subscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
|
||||
performChannelSubscriptionRequest(channelID, method: .post, onSuccess: onSuccess)
|
||||
}
|
||||
|
||||
func unsubscribe(_ channelID: String) {
|
||||
performChannelSubscriptionRequest(channelID, method: .delete)
|
||||
func unsubscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
|
||||
performChannelSubscriptionRequest(channelID, method: .delete, onSuccess: onSuccess)
|
||||
}
|
||||
|
||||
func subscribed(_ channelID: String) -> Bool {
|
||||
func isSubscribing(_ channelID: String) -> Bool {
|
||||
channels.contains { $0.id == channelID }
|
||||
}
|
||||
|
||||
fileprivate func load() {
|
||||
fileprivate func load(onSuccess: @escaping () -> Void = {}) {
|
||||
resource.load().onSuccess { resource in
|
||||
if let channels: [Channel] = resource.typedContent() {
|
||||
self.channels = channels
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func performChannelSubscriptionRequest(_ channelID: String, method: RequestMethod) {
|
||||
fileprivate func performChannelSubscriptionRequest(_ channelID: String, method: RequestMethod, onSuccess: @escaping () -> Void = {}) {
|
||||
InvidiousAPI.shared.channelSubscription(channelID).request(method).onCompletion { _ in
|
||||
self.load()
|
||||
self.load(onSuccess: onSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,40 +127,15 @@ struct Video: Identifiable, Equatable {
|
||||
}
|
||||
|
||||
var viewsCount: String? {
|
||||
views != 0 ? formattedCount(views) : nil
|
||||
views != 0 ? views.formattedAsAbbreviation() : nil
|
||||
}
|
||||
|
||||
var likesCount: String? {
|
||||
formattedCount(likes)
|
||||
likes?.formattedAsAbbreviation()
|
||||
}
|
||||
|
||||
var dislikesCount: String? {
|
||||
formattedCount(dislikes)
|
||||
}
|
||||
|
||||
func formattedCount(_ count: Int!) -> String? {
|
||||
guard count != nil else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let formatter = NumberFormatter()
|
||||
formatter.numberStyle = .decimal
|
||||
formatter.maximumFractionDigits = 1
|
||||
|
||||
var number: NSNumber
|
||||
var unit: String
|
||||
|
||||
if count < 1000 {
|
||||
return "\(count!)"
|
||||
} else if count < 1_000_000 {
|
||||
number = NSNumber(value: Double(count) / 1000.0)
|
||||
unit = "K"
|
||||
} else {
|
||||
number = NSNumber(value: Double(count) / 1_000_000.0)
|
||||
unit = "M"
|
||||
}
|
||||
|
||||
return "\(formatter.string(from: number)!)\(unit)"
|
||||
dislikes?.formattedAsAbbreviation()
|
||||
}
|
||||
|
||||
var selectableStreams: [Stream] {
|
||||
|
@ -54,6 +54,8 @@
|
||||
3748186E26A769D60084E870 /* DetailBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186D26A769D60084E870 /* DetailBadge.swift */; };
|
||||
3748186F26A769D60084E870 /* DetailBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186D26A769D60084E870 /* DetailBadge.swift */; };
|
||||
3748187026A769D60084E870 /* DetailBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186D26A769D60084E870 /* DetailBadge.swift */; };
|
||||
3763495126DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift */; };
|
||||
3763495226DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift */; };
|
||||
376578852685429C00D4EA09 /* CaseIterable+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376578842685429C00D4EA09 /* CaseIterable+Next.swift */; };
|
||||
376578862685429C00D4EA09 /* CaseIterable+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376578842685429C00D4EA09 /* CaseIterable+Next.swift */; };
|
||||
376578872685429C00D4EA09 /* CaseIterable+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376578842685429C00D4EA09 /* CaseIterable+Next.swift */; };
|
||||
@ -73,8 +75,6 @@
|
||||
377FC7DB267A080300A6BBAF /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7DA267A080300A6BBAF /* Logging */; };
|
||||
377FC7DC267A081800A6BBAF /* PopularView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27D26737323007FC770 /* PopularView.swift */; };
|
||||
377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27D26737323007FC770 /* PopularView.swift */; };
|
||||
377FC7E0267A082600A6BBAF /* ChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF2892673AB89007FC770 /* ChannelView.swift */; };
|
||||
377FC7E1267A082600A6BBAF /* ChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF2892673AB89007FC770 /* ChannelView.swift */; };
|
||||
377FC7E2267A084A00A6BBAF /* VideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B18B26717B3800C925CA /* VideoView.swift */; };
|
||||
377FC7E3267A084A00A6BBAF /* VideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B18B26717B3800C925CA /* VideoView.swift */; };
|
||||
377FC7E4267A084E00A6BBAF /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27F26737550007FC770 /* SearchView.swift */; };
|
||||
@ -91,9 +91,14 @@
|
||||
379775932689365600DD52A8 /* Array+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379775922689365600DD52A8 /* Array+Next.swift */; };
|
||||
379775942689365600DD52A8 /* Array+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379775922689365600DD52A8 /* Array+Next.swift */; };
|
||||
379775952689365600DD52A8 /* Array+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379775922689365600DD52A8 /* Array+Next.swift */; };
|
||||
379DDFEE26DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DDFED26DEDB0E00EA08E7 /* EnvironmentValues.swift */; };
|
||||
379DDFEF26DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DDFED26DEDB0E00EA08E7 /* EnvironmentValues.swift */; };
|
||||
379DDFF026DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DDFED26DEDB0E00EA08E7 /* EnvironmentValues.swift */; };
|
||||
379DDFF326DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DDFF226DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift */; };
|
||||
379DDFF426DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DDFF226DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift */; };
|
||||
379DDFF526DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DDFF226DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift */; };
|
||||
37AAF27E26737323007FC770 /* PopularView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27D26737323007FC770 /* PopularView.swift */; };
|
||||
37AAF28026737550007FC770 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27F26737550007FC770 /* SearchView.swift */; };
|
||||
37AAF28A2673AB89007FC770 /* ChannelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF2892673AB89007FC770 /* ChannelView.swift */; };
|
||||
37AAF29026740715007FC770 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF28F26740715007FC770 /* Channel.swift */; };
|
||||
37AAF29126740715007FC770 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF28F26740715007FC770 /* Channel.swift */; };
|
||||
37AAF29226740715007FC770 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF28F26740715007FC770 /* Channel.swift */; };
|
||||
@ -133,6 +138,11 @@
|
||||
37BA794826DC2E56002A0235 /* AppSidebarSubscriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794626DC2E56002A0235 /* AppSidebarSubscriptions.swift */; };
|
||||
37BA794B26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */; };
|
||||
37BA794C26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */; };
|
||||
37BA794F26DC3E0E002A0235 /* Int+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794E26DC3E0E002A0235 /* Int+Format.swift */; };
|
||||
37BA795026DC3E0E002A0235 /* Int+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794E26DC3E0E002A0235 /* Int+Format.swift */; };
|
||||
37BA795126DC3E0E002A0235 /* Int+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794E26DC3E0E002A0235 /* Int+Format.swift */; };
|
||||
37BA796F26DC412E002A0235 /* Int+FormatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA796D26DC412E002A0235 /* Int+FormatTests.swift */; };
|
||||
37BA797026DC426B002A0235 /* Int+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794E26DC3E0E002A0235 /* Int+Format.swift */; };
|
||||
37BAB54C269B39FD00E75ED1 /* TVNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */; };
|
||||
37BADCA52699FB72009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA42699FB72009BE4FB /* Alamofire */; };
|
||||
37BADCA7269A552E009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA6269A552E009BE4FB /* Alamofire */; };
|
||||
@ -207,6 +217,13 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
37BA796726DC40CB002A0235 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 37D4B0CE2671614900C925CA;
|
||||
remoteInfo = "Pearvidious (macOS)";
|
||||
};
|
||||
37D4B0D52671614900C925CA /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
|
||||
@ -248,6 +265,7 @@
|
||||
3748186526A7627F0084E870 /* Video+Fixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Video+Fixtures.swift"; sourceTree = "<group>"; };
|
||||
3748186926A764FB0084E870 /* Thumbnail+Fixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Thumbnail+Fixtures.swift"; sourceTree = "<group>"; };
|
||||
3748186D26A769D60084E870 /* DetailBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailBadge.swift; sourceTree = "<group>"; };
|
||||
3763495026DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarRecentlyOpened.swift; sourceTree = "<group>"; };
|
||||
376578842685429C00D4EA09 /* CaseIterable+Next.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CaseIterable+Next.swift"; sourceTree = "<group>"; };
|
||||
376578882685471400D4EA09 /* Playlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Playlist.swift; sourceTree = "<group>"; };
|
||||
376578902685490700D4EA09 /* PlaylistsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistsView.swift; sourceTree = "<group>"; };
|
||||
@ -257,9 +275,10 @@
|
||||
3797758A2689345500DD52A8 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
|
||||
379775922689365600DD52A8 /* Array+Next.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Next.swift"; sourceTree = "<group>"; };
|
||||
37992DC726CC50BC003D4C27 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
379DDFED26DEDB0E00EA08E7 /* EnvironmentValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; };
|
||||
379DDFF226DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsubscribeAlertModifier.swift; sourceTree = "<group>"; };
|
||||
37AAF27D26737323007FC770 /* PopularView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopularView.swift; sourceTree = "<group>"; };
|
||||
37AAF27F26737550007FC770 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
||||
37AAF2892673AB89007FC770 /* ChannelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelView.swift; sourceTree = "<group>"; };
|
||||
37AAF28F26740715007FC770 /* Channel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channel.swift; sourceTree = "<group>"; };
|
||||
37AAF29926740A01007FC770 /* VideosListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideosListView.swift; sourceTree = "<group>"; };
|
||||
37AAF29F26741C97007FC770 /* SubscriptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsView.swift; sourceTree = "<group>"; };
|
||||
@ -277,6 +296,9 @@
|
||||
37BA794226DBA973002A0235 /* Playlists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Playlists.swift; sourceTree = "<group>"; };
|
||||
37BA794626DC2E56002A0235 /* AppSidebarSubscriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarSubscriptions.swift; sourceTree = "<group>"; };
|
||||
37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarPlaylists.swift; sourceTree = "<group>"; };
|
||||
37BA794E26DC3E0E002A0235 /* Int+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Format.swift"; sourceTree = "<group>"; };
|
||||
37BA796326DC40CB002A0235 /* Shared Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Shared Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
37BA796D26DC412E002A0235 /* Int+FormatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+FormatTests.swift"; sourceTree = "<group>"; };
|
||||
37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVNavigationView.swift; sourceTree = "<group>"; };
|
||||
37BD07B42698AA4D003EBB87 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
37BD07BA2698AB60003EBB87 /* AppSidebarNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarNavigation.swift; sourceTree = "<group>"; };
|
||||
@ -314,6 +336,13 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
37BA796026DC40CB002A0235 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
37D4B0C62671614900C925CA /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -380,6 +409,7 @@
|
||||
children = (
|
||||
37BD07BA2698AB60003EBB87 /* AppSidebarNavigation.swift */,
|
||||
37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */,
|
||||
3763495026DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift */,
|
||||
37BA794626DC2E56002A0235 /* AppSidebarSubscriptions.swift */,
|
||||
37D4B0C32671614700C925CA /* AppTabNavigation.swift */,
|
||||
37BD07B42698AA4D003EBB87 /* ContentView.swift */,
|
||||
@ -480,6 +510,30 @@
|
||||
path = iOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
379DDFF126DEE2A800EA08E7 /* Modifiers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
379DDFF226DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift */,
|
||||
);
|
||||
path = Modifiers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37BA796426DC40CB002A0235 /* Shared Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37BA796C26DC4105002A0235 /* Extensions */,
|
||||
);
|
||||
path = "Shared Tests";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37BA796C26DC4105002A0235 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37BA796D26DC412E002A0235 /* Int+FormatTests.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37BE0BD826A214500092E2DB /* macOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -494,6 +548,7 @@
|
||||
children = (
|
||||
379775922689365600DD52A8 /* Array+Next.swift */,
|
||||
376578842685429C00D4EA09 /* CaseIterable+Next.swift */,
|
||||
37BA794E26DC3E0E002A0235 /* Int+Format.swift */,
|
||||
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
@ -509,6 +564,7 @@
|
||||
37D4B1B72672CFE300C925CA /* Model */,
|
||||
37C7A9022679058300E721B4 /* Extensions */,
|
||||
3748186426A762300084E870 /* Fixtures */,
|
||||
37BA796426DC40CB002A0235 /* Shared Tests */,
|
||||
377FC7D1267A080300A6BBAF /* Frameworks */,
|
||||
37D4B0CA2671614900C925CA /* Products */,
|
||||
37D4B174267164B000C925CA /* Tests Apple TV */,
|
||||
@ -526,7 +582,9 @@
|
||||
371AAE2526CEBF0B00901972 /* Trending */,
|
||||
371AAE2726CEBF4700901972 /* Videos */,
|
||||
371AAE2826CEC7D900901972 /* Views */,
|
||||
379DDFF126DEE2A800EA08E7 /* Modifiers */,
|
||||
372915E52687E3B900F5A35B /* Defaults.swift */,
|
||||
379DDFED26DEDB0E00EA08E7 /* EnvironmentValues.swift */,
|
||||
37D4B0C22671614700C925CA /* PearvidiousApp.swift */,
|
||||
37D4B0C42671614800C925CA /* Assets.xcassets */,
|
||||
37BD07C42698ADEE003EBB87 /* Pearvidious.entitlements */,
|
||||
@ -543,6 +601,7 @@
|
||||
37D4B0DE2671614900C925CA /* Tests macOS.xctest */,
|
||||
37D4B158267164AE00C925CA /* Pearvidious (Apple TV).app */,
|
||||
37D4B171267164B000C925CA /* Tests Apple TV.xctest */,
|
||||
37BA796326DC40CB002A0235 /* Shared Tests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -568,7 +627,6 @@
|
||||
children = (
|
||||
371AAE2926CF143200901972 /* Options */,
|
||||
373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */,
|
||||
37AAF2892673AB89007FC770 /* ChannelView.swift */,
|
||||
37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */,
|
||||
37B17DA3268A285E006AEE9B /* VideoDetailsView.swift */,
|
||||
37D4B15E267164AF00C925CA /* Assets.xcassets */,
|
||||
@ -616,6 +674,24 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
37BA796226DC40CB002A0235 /* Shared Tests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 37BA796926DC40CB002A0235 /* Build configuration list for PBXNativeTarget "Shared Tests" */;
|
||||
buildPhases = (
|
||||
37BA795F26DC40CB002A0235 /* Sources */,
|
||||
37BA796026DC40CB002A0235 /* Frameworks */,
|
||||
37BA796126DC40CB002A0235 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
37BA796826DC40CB002A0235 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "Shared Tests";
|
||||
productName = "Shared Tests";
|
||||
productReference = 37BA796326DC40CB002A0235 /* Shared Tests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
37D4B0C82671614900C925CA /* Pearvidious (iOS) */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 37D4B0EC2671614900C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (iOS)" */;
|
||||
@ -753,6 +829,10 @@
|
||||
LastSwiftUpdateCheck = 1300;
|
||||
LastUpgradeCheck = 1300;
|
||||
TargetAttributes = {
|
||||
37BA796226DC40CB002A0235 = {
|
||||
CreatedOnToolsVersion = 13.0;
|
||||
TestTargetID = 37D4B0CE2671614900C925CA;
|
||||
};
|
||||
37D4B0C82671614900C925CA = {
|
||||
CreatedOnToolsVersion = 13.0;
|
||||
};
|
||||
@ -803,11 +883,19 @@
|
||||
37D4B0D32671614900C925CA /* Tests iOS */,
|
||||
37D4B0DD2671614900C925CA /* Tests macOS */,
|
||||
37D4B170267164B000C925CA /* Tests Apple TV */,
|
||||
37BA796226DC40CB002A0235 /* Shared Tests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
37BA796126DC40CB002A0235 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
37D4B0C72671614900C925CA /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -857,6 +945,15 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
37BA795F26DC40CB002A0235 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
37BA797026DC426B002A0235 /* Int+Format.swift in Sources */,
|
||||
37BA796F26DC412E002A0235 /* Int+FormatTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
37D4B0C52671614900C925CA /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -864,6 +961,7 @@
|
||||
37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
||||
37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */,
|
||||
37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||
3763495126DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift in Sources */,
|
||||
37BA793B26DB8EE4002A0235 /* PlaylistVideosView.swift in Sources */,
|
||||
37BD07B52698AA4D003EBB87 /* ContentView.swift in Sources */,
|
||||
37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */,
|
||||
@ -888,6 +986,7 @@
|
||||
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
||||
37C7A1DC267CE9D90010EAD6 /* Profile.swift in Sources */,
|
||||
373CFAC026966149003CB2C6 /* CoverSectionView.swift in Sources */,
|
||||
379DDFF326DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */,
|
||||
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
||||
377FC7E3267A084A00A6BBAF /* VideoView.swift in Sources */,
|
||||
@ -900,9 +999,9 @@
|
||||
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
||||
37B17DA2268A1F8A006AEE9B /* VideoContextMenuView.swift in Sources */,
|
||||
379775932689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||
377FC7E1267A082600A6BBAF /* ChannelView.swift in Sources */,
|
||||
37B81AFC26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
||||
37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||
37BA794F26DC3E0E002A0235 /* Int+Format.swift in Sources */,
|
||||
37F49BA326CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
|
||||
37BA794B26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */,
|
||||
37B767DB2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
||||
@ -916,6 +1015,7 @@
|
||||
372915E62687E3B900F5A35B /* Defaults.swift in Sources */,
|
||||
37D4B19726717E1500C925CA /* Video.swift in Sources */,
|
||||
371F2F1A269B43D300E4A7AB /* NavigationState.swift in Sources */,
|
||||
379DDFEE26DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */,
|
||||
37BE0BCF26A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
||||
37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */,
|
||||
37D4B0E42671614900C925CA /* PearvidiousApp.swift in Sources */,
|
||||
@ -934,6 +1034,7 @@
|
||||
37EAD86C267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||
37CEE4C22677B697005A1EFE /* Stream.swift in Sources */,
|
||||
371F2F1B269B43D300E4A7AB /* NavigationState.swift in Sources */,
|
||||
37BA795026DC3E0E002A0235 /* Int+Format.swift in Sources */,
|
||||
377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */,
|
||||
3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
||||
37B81B0026D2CA3700675966 /* VideoDetails.swift in Sources */,
|
||||
@ -959,7 +1060,7 @@
|
||||
376578862685429C00D4EA09 /* CaseIterable+Next.swift in Sources */,
|
||||
37F4AE7326828F0900BD60EA /* VideosCellsView.swift in Sources */,
|
||||
37B81AFD26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
||||
377FC7E0267A082600A6BBAF /* ChannelView.swift in Sources */,
|
||||
379DDFEF26DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */,
|
||||
379775942689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||
3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
||||
37BE0BDA26A214630092E2DB /* PlayerViewController.swift in Sources */,
|
||||
@ -975,6 +1076,7 @@
|
||||
37D4B19826717E1500C925CA /* Video.swift in Sources */,
|
||||
37D4B0E52671614900C925CA /* PearvidiousApp.swift in Sources */,
|
||||
37BD07C12698AD3B003EBB87 /* TrendingCountry.swift in Sources */,
|
||||
379DDFF426DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */,
|
||||
37BA794026DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||
3711404026B206A6005B3555 /* SearchState.swift in Sources */,
|
||||
37BE0BD026A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
||||
@ -984,6 +1086,7 @@
|
||||
3748186B26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
||||
373CFADC269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
||||
373CFAF02697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
||||
3763495226DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift in Sources */,
|
||||
37BA794426DBA973002A0235 /* Playlists.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -1025,6 +1128,7 @@
|
||||
37141671267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||
37BA794126DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||
37AAF29226740715007FC770 /* Channel.swift in Sources */,
|
||||
379DDFF526DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */,
|
||||
37EAD86D267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||
37B81B0726D2D6CF00675966 /* PlaybackState.swift in Sources */,
|
||||
3765788B2685471400D4EA09 /* Playlist.swift in Sources */,
|
||||
@ -1040,6 +1144,7 @@
|
||||
37AAF29A26740A01007FC770 /* VideosListView.swift in Sources */,
|
||||
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||
376578932685490700D4EA09 /* PlaylistsView.swift in Sources */,
|
||||
37BA795126DC3E0E002A0235 /* Int+Format.swift in Sources */,
|
||||
3748186C26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
||||
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
||||
3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
||||
@ -1052,7 +1157,7 @@
|
||||
37BA793D26DB8EE4002A0235 /* PlaylistVideosView.swift in Sources */,
|
||||
3711404126B206A6005B3555 /* SearchState.swift in Sources */,
|
||||
379775952689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||
37AAF28A2673AB89007FC770 /* ChannelView.swift in Sources */,
|
||||
379DDFF026DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */,
|
||||
3705B180267B4DFB00704544 /* TrendingCountry.swift in Sources */,
|
||||
373CFACD26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
||||
37141675267A8E10006CA35D /* Country.swift in Sources */,
|
||||
@ -1079,6 +1184,11 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
37BA796826DC40CB002A0235 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 37D4B0CE2671614900C925CA /* Pearvidious (macOS) */;
|
||||
targetProxy = 37BA796726DC40CB002A0235 /* PBXContainerItemProxy */;
|
||||
};
|
||||
37D4B0D62671614900C925CA /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 37D4B0C82671614900C925CA /* Pearvidious (iOS) */;
|
||||
@ -1097,6 +1207,58 @@
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
37BA796A26DC40CB002A0235 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.arekf.Shared-Tests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Pearvidious.app/Contents/MacOS/Pearvidious";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
37BA796B26DC40CB002A0235 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.arekf.Shared-Tests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Pearvidious.app/Contents/MacOS/Pearvidious";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
37D4B0EA2671614900C925CA /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -1555,6 +1717,15 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
37BA796926DC40CB002A0235 /* Build configuration list for PBXNativeTarget "Shared Tests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
37BA796A26DC40CB002A0235 /* Debug */,
|
||||
37BA796B26DC40CB002A0235 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
37D4B0C02671614700C925CA /* Build configuration list for PBXProject "Pearvidious" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
@ -38,6 +38,16 @@
|
||||
ReferencedContainer = "container:Pearvidious.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "37BA796226DC40CB002A0235"
|
||||
BuildableName = "Shared Tests.xctest"
|
||||
BlueprintName = "Shared Tests"
|
||||
ReferencedContainer = "container:Pearvidious.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
|
19
Shared Tests/Extensions/Int+FormatTests.swift
Normal file
19
Shared Tests/Extensions/Int+FormatTests.swift
Normal file
@ -0,0 +1,19 @@
|
||||
import XCTest
|
||||
|
||||
final class IntFormatTests: XCTestCase {
|
||||
func testFormattedAsAbbreviation() throws {
|
||||
let samples: [Int: String] = [
|
||||
1: "1",
|
||||
999: "999",
|
||||
1000: "1K",
|
||||
1101: "1,1K",
|
||||
12345: "12,3K",
|
||||
123_456: "123,5K",
|
||||
123_626_789: "123,6M"
|
||||
]
|
||||
|
||||
samples.forEach { value, formatted in
|
||||
XCTAssertEqual(value.formattedAsAbbreviation(), formatted)
|
||||
}
|
||||
}
|
||||
}
|
13
Shared/EnvironmentValues.swift
Normal file
13
Shared/EnvironmentValues.swift
Normal file
@ -0,0 +1,13 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
private struct InNavigationViewKey: EnvironmentKey {
|
||||
static let defaultValue = false
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var inNavigationView: Bool {
|
||||
get { self[InNavigationViewKey.self] }
|
||||
set { self[InNavigationViewKey.self] = newValue }
|
||||
}
|
||||
}
|
29
Shared/Modifiers/UnsubscribeAlertModifier.swift
Normal file
29
Shared/Modifiers/UnsubscribeAlertModifier.swift
Normal file
@ -0,0 +1,29 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct UnsubscribeAlertModifier: ViewModifier {
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
@EnvironmentObject<Subscriptions> private var subscriptions
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.alert(unsubscribeAlertTitle, isPresented: $navigationState.presentingUnsubscribeAlert) {
|
||||
if let channel = navigationState.channelToUnsubscribe {
|
||||
Button("Unsubscribe", role: .destructive) {
|
||||
subscriptions.unsubscribe(channel.id) {
|
||||
navigationState.openChannel(channel)
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unsubscribeAlertTitle: String {
|
||||
if let channel = navigationState.channelToUnsubscribe {
|
||||
return "Unsubscribe from \(channel.name)"
|
||||
}
|
||||
|
||||
return "Unknown channel"
|
||||
}
|
||||
}
|
@ -38,17 +38,28 @@ struct AppSidebarNavigation: View {
|
||||
NavigationView {
|
||||
sidebar
|
||||
.frame(minWidth: 180)
|
||||
|
||||
Text("Select section")
|
||||
}
|
||||
}
|
||||
|
||||
var sidebar: some View {
|
||||
ScrollViewReader { scrollView in
|
||||
List {
|
||||
mainNavigationLinks
|
||||
|
||||
Group {
|
||||
AppSidebarRecentlyOpened(selection: selection)
|
||||
.id("recentlyOpened")
|
||||
AppSidebarSubscriptions(selection: selection)
|
||||
AppSidebarPlaylists(selection: selection)
|
||||
}
|
||||
.onChange(of: navigationState.sidebarSectionChanged) { _ in
|
||||
scrollScrollViewToItem(scrollView: scrollView, for: navigationState.tabSelection)
|
||||
}
|
||||
}
|
||||
.listStyle(.sidebar)
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
.toolbar {
|
||||
@ -61,48 +72,51 @@ struct AppSidebarNavigation: View {
|
||||
|
||||
var mainNavigationLinks: some View {
|
||||
Group {
|
||||
NavigationLink(tag: TabSelection.subscriptions, selection: selection) {
|
||||
SubscriptionsView()
|
||||
}
|
||||
label: {
|
||||
NavigationLink(destination: SubscriptionsView(), tag: TabSelection.subscriptions, selection: selection) {
|
||||
Label("Subscriptions", systemImage: "star.circle.fill")
|
||||
.accessibility(label: Text("Subscriptions"))
|
||||
}
|
||||
|
||||
NavigationLink(tag: TabSelection.popular, selection: selection) {
|
||||
PopularView()
|
||||
}
|
||||
label: {
|
||||
NavigationLink(destination: PopularView(), tag: TabSelection.popular, selection: selection) {
|
||||
Label("Popular", systemImage: "chart.bar")
|
||||
.accessibility(label: Text("Popular"))
|
||||
}
|
||||
|
||||
NavigationLink(tag: TabSelection.trending, selection: selection) {
|
||||
TrendingView()
|
||||
}
|
||||
label: {
|
||||
NavigationLink(destination: TrendingView(), tag: TabSelection.trending, selection: selection) {
|
||||
Label("Trending", systemImage: "chart.line.uptrend.xyaxis")
|
||||
.accessibility(label: Text("Trending"))
|
||||
}
|
||||
|
||||
NavigationLink(tag: TabSelection.playlists, selection: selection) {
|
||||
PlaylistsView()
|
||||
}
|
||||
label: {
|
||||
NavigationLink(destination: PlaylistsView(), tag: TabSelection.playlists, selection: selection) {
|
||||
Label("Playlists", systemImage: "list.and.film")
|
||||
.accessibility(label: Text("Playlists"))
|
||||
}
|
||||
|
||||
NavigationLink(tag: TabSelection.search, selection: selection) {
|
||||
SearchView()
|
||||
}
|
||||
label: {
|
||||
NavigationLink(destination: SearchView(), tag: TabSelection.search, selection: selection) {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
.accessibility(label: Text("Search"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func scrollScrollViewToItem(scrollView: ScrollViewProxy, for selection: TabSelection) {
|
||||
if case let .channel(id) = selection {
|
||||
if subscriptions.isSubscribing(id) {
|
||||
scrollView.scrollTo(id)
|
||||
} else {
|
||||
scrollView.scrollTo("recentlyOpened")
|
||||
}
|
||||
} else if case let .playlist(id) = selection {
|
||||
scrollView.scrollTo(id)
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private func toggleSidebar() {
|
||||
NSApp.keyWindow?.contentViewController?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
|
||||
}
|
||||
#endif
|
||||
|
||||
static func symbolSystemImage(_ name: String) -> String {
|
||||
let firstLetter = name.first?.lowercased()
|
||||
let regex = #"^[a-z0-9]$"#
|
||||
@ -111,10 +125,4 @@ struct AppSidebarNavigation: View {
|
||||
|
||||
return "\(symbolName).square"
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private func toggleSidebar() {
|
||||
NSApp.keyWindow?.contentViewController?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ struct AppSidebarPlaylists: View {
|
||||
Label(playlist.title, systemImage: AppSidebarNavigation.symbolSystemImage(playlist.title))
|
||||
.badge(Text("\(playlist.videos.count)"))
|
||||
}
|
||||
.id(playlist.id)
|
||||
.contextMenu {
|
||||
Button("Edit") {
|
||||
navigationState.presentEditPlaylistForm(playlists.find(id: playlist.id))
|
||||
|
54
Shared/Navigation/AppSidebarRecentlyOpened.swift
Normal file
54
Shared/Navigation/AppSidebarRecentlyOpened.swift
Normal file
@ -0,0 +1,54 @@
|
||||
import SwiftUI
|
||||
|
||||
struct AppSidebarRecentlyOpened: View {
|
||||
@Binding var selection: TabSelection?
|
||||
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
@EnvironmentObject<Subscriptions> private var subscriptions
|
||||
|
||||
@State private var subscriptionsChanged = false
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if !recentlyOpened.isEmpty {
|
||||
Section(header: Text("Recently Opened")) {
|
||||
ForEach(recentlyOpened) { channel in
|
||||
NavigationLink(tag: TabSelection.channel(channel.id), selection: $selection) {
|
||||
ChannelVideosView(channel)
|
||||
} label: {
|
||||
HStack {
|
||||
Label(channel.name, systemImage: AppSidebarNavigation.symbolSystemImage(channel.name))
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: { navigationState.closeChannel(channel) }) {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
}
|
||||
.foregroundColor(.secondary)
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
||||
// force recalculating the view on change of subscriptions
|
||||
.opacity(subscriptionsChanged ? 1 : 1)
|
||||
.id(channel.id)
|
||||
.contextMenu {
|
||||
Button("Subscribe") {
|
||||
subscriptions.subscribe(channel.id) {
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: subscriptions.all) { _ in
|
||||
subscriptionsChanged.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var recentlyOpened: [Channel] {
|
||||
navigationState.openChannels.filter { !subscriptions.all.contains($0) }
|
||||
}
|
||||
}
|
@ -19,13 +19,7 @@ struct AppSidebarSubscriptions: View {
|
||||
navigationState.presentUnsubscribeAlert(channel)
|
||||
}
|
||||
}
|
||||
.alert(unsubscribeAlertTitle, isPresented: $navigationState.presentingUnsubscribeAlert) {
|
||||
if let channel = navigationState.channelToUnsubscribe {
|
||||
Button("Unsubscribe", role: .destructive) {
|
||||
subscriptions.unsubscribe(channel.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
.modifier(UnsubscribeAlertModifier())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,10 @@ import Defaults
|
||||
import SwiftUI
|
||||
|
||||
struct AppTabNavigation: View {
|
||||
@State private var tabSelection: TabSelection = .subscriptions
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $tabSelection) {
|
||||
TabView(selection: $navigationState.tabSelection) {
|
||||
NavigationView {
|
||||
SubscriptionsView()
|
||||
}
|
||||
@ -51,5 +51,19 @@ struct AppTabNavigation: View {
|
||||
}
|
||||
.tag(TabSelection.search)
|
||||
}
|
||||
.sheet(isPresented: $navigationState.isChannelOpen, onDismiss: {
|
||||
navigationState.closeChannel(presentedChannel)
|
||||
}) {
|
||||
if presentedChannel != nil {
|
||||
NavigationView {
|
||||
ChannelVideosView(presentedChannel)
|
||||
.environment(\.inNavigationView, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var presentedChannel: Channel! {
|
||||
navigationState.openChannels.first
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ struct PlaybackBar: View {
|
||||
Image(systemName: "chevron.down.circle.fill")
|
||||
}
|
||||
.accessibilityLabel(Text("Close"))
|
||||
.buttonStyle(BorderlessButtonStyle())
|
||||
.buttonStyle(.borderless)
|
||||
.foregroundColor(.gray)
|
||||
.keyboardShortcut(.cancelAction)
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ struct VideoDetails: View {
|
||||
Text(video.channel.name)
|
||||
.font(.system(size: 13))
|
||||
.bold()
|
||||
if !video.channel.subscriptionsCount.isEmpty {
|
||||
Text("\(video.channel.subscriptionsCount) subscribers")
|
||||
if let subscribers = video.channel.subscriptionsString {
|
||||
Text("\(subscribers) subscribers")
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
@ -154,7 +154,7 @@ struct VideoDetails: View {
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
.padding([.horizontal, .bottom])
|
||||
.onAppear {
|
||||
subscribed = subscriptions.subscribed(video.channel.id)
|
||||
subscribed = subscriptions.isSubscribing(video.channel.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,10 +89,9 @@ struct VideoPlayerView: View {
|
||||
navigationState.showingVideoDetails = navigationState.returnToDetails
|
||||
}
|
||||
#if os(macOS)
|
||||
.navigationTitle(video.title)
|
||||
.frame(maxWidth: 1000, minHeight: 700)
|
||||
#elseif os(iOS)
|
||||
.navigationBarTitle(video.title, displayMode: .inline)
|
||||
.navigationBarHidden(true)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ struct PlaylistFormView: View {
|
||||
Text(visibility.name)
|
||||
}
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
Divider()
|
||||
.padding(.vertical, 4)
|
||||
|
@ -8,11 +8,29 @@ struct VideoView: View {
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
#endif
|
||||
|
||||
@Environment(\.inNavigationView) private var inNavigationView
|
||||
|
||||
var video: Video
|
||||
var layout: ListingLayout
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if inNavigationView {
|
||||
NavigationLink(destination: VideoPlayerView(video)) {
|
||||
content
|
||||
}
|
||||
} else {
|
||||
Button(action: { navigationState.playVideo(video) }) {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
.modifier(ButtonStyleModifier(layout: layout))
|
||||
.contentShape(RoundedRectangle(cornerRadius: 12))
|
||||
.contextMenu { VideoContextMenuView(video: video) }
|
||||
}
|
||||
|
||||
var content: some View {
|
||||
VStack {
|
||||
if layout == .cells {
|
||||
#if os(iOS)
|
||||
@ -33,10 +51,6 @@ struct VideoView: View {
|
||||
.background()
|
||||
#endif
|
||||
}
|
||||
.modifier(ButtonStyleModifier(layout: layout))
|
||||
.contentShape(RoundedRectangle(cornerRadius: 12))
|
||||
.contextMenu { VideoContextMenuView(video: video) }
|
||||
}
|
||||
|
||||
var horizontalRow: some View {
|
||||
HStack(alignment: .top, spacing: 2) {
|
||||
@ -228,7 +242,7 @@ struct VideoListRowPreview: PreviewProvider {
|
||||
VideoView(video: video, layout: .list)
|
||||
}
|
||||
}
|
||||
.listStyle(GroupedListStyle())
|
||||
.listStyle(.grouped)
|
||||
|
||||
HStack {
|
||||
ForEach(Video.allFixtures) { video in
|
||||
|
@ -24,7 +24,7 @@ struct VideosListView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(GroupedListStyle())
|
||||
.listStyle(.grouped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ import SwiftUI
|
||||
struct VideosView: View {
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
|
||||
@State private var profile = Profile()
|
||||
|
||||
#if os(tvOS)
|
||||
@Default(.layout) private var layout
|
||||
#endif
|
||||
|
@ -2,26 +2,119 @@ import Siesta
|
||||
import SwiftUI
|
||||
|
||||
struct ChannelVideosView: View {
|
||||
@ObservedObject private var store = Store<[Video]>()
|
||||
|
||||
let channel: Channel
|
||||
|
||||
var resource: Resource {
|
||||
InvidiousAPI.shared.channelVideos(channel.id)
|
||||
}
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
@EnvironmentObject<Subscriptions> private var subscriptions
|
||||
|
||||
@Environment(\.inNavigationView) private var inNavigationView
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
#if os(iOS)
|
||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||
#endif
|
||||
|
||||
@ObservedObject private var store = Store<Channel>()
|
||||
|
||||
@Namespace private var focusNamespace
|
||||
|
||||
init(_ channel: Channel) {
|
||||
self.channel = channel
|
||||
|
||||
resource.addObserver(store)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VideosView(videos: store.collection)
|
||||
#if !os(tvOS)
|
||||
.navigationTitle("\(channel.name) Channel")
|
||||
VStack {
|
||||
#if os(tvOS)
|
||||
HStack {
|
||||
Text(navigationTitle)
|
||||
.font(.title2)
|
||||
.frame(alignment: .leading)
|
||||
|
||||
Spacer()
|
||||
|
||||
if let subscribers = store.item?.subscriptionsString {
|
||||
Text("**\(subscribers)** subscribers")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
subscriptionToggleButton
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
#endif
|
||||
|
||||
VideosView(videos: store.item?.videos ?? [])
|
||||
|
||||
#if !os(iOS)
|
||||
.prefersDefaultFocus(in: focusNamespace)
|
||||
#endif
|
||||
}
|
||||
#if !os(iOS)
|
||||
.focusScope(focusNamespace)
|
||||
#endif
|
||||
#if !os(tvOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: subscriptionToolbarItemPlacement) {
|
||||
HStack {
|
||||
if let channel = store.item, let subscribers = channel.subscriptionsString {
|
||||
Text("**\(subscribers)** subscribers")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
subscriptionToggleButton
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
if inNavigationView {
|
||||
Button("Done") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.modifier(UnsubscribeAlertModifier())
|
||||
.onAppear {
|
||||
resource.loadIfNeeded()
|
||||
}
|
||||
.navigationTitle(navigationTitle)
|
||||
}
|
||||
|
||||
var resource: Resource {
|
||||
InvidiousAPI.shared.channel(channel.id)
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
var subscriptionToolbarItemPlacement: ToolbarItemPlacement {
|
||||
#if os(iOS)
|
||||
if horizontalSizeClass == .regular {
|
||||
return .primaryAction
|
||||
}
|
||||
#endif
|
||||
|
||||
return .status
|
||||
}
|
||||
#endif
|
||||
|
||||
var subscriptionToggleButton: some View {
|
||||
Group {
|
||||
if subscriptions.isSubscribing(channel.id) {
|
||||
Button("Unsubscribe") {
|
||||
navigationState.presentUnsubscribeAlert(channel)
|
||||
}
|
||||
} else {
|
||||
Button("Subscribe") {
|
||||
subscriptions.subscribe(channel.id) {
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var navigationTitle: String {
|
||||
store.item?.name ?? channel.name
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,9 @@ struct VideoContextMenuView: View {
|
||||
|
||||
var body: some View {
|
||||
Section {
|
||||
if navigationState.showOpenChannel(video.channel.id) {
|
||||
openChannelButton
|
||||
}
|
||||
|
||||
subscriptionButton
|
||||
.opacity(subscribed ? 1 : 1)
|
||||
@ -32,18 +34,25 @@ struct VideoContextMenuView: View {
|
||||
var openChannelButton: some View {
|
||||
Button("\(video.author) Channel") {
|
||||
navigationState.openChannel(video.channel)
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
var subscriptionButton: some View {
|
||||
Group {
|
||||
if subscriptions.subscribed(video.channel.id) {
|
||||
if subscriptions.isSubscribing(video.channel.id) {
|
||||
Button("Unsubscribe", role: .destructive) {
|
||||
#if os(tvOS)
|
||||
subscriptions.unsubscribe(video.channel.id)
|
||||
#else
|
||||
navigationState.presentUnsubscribeAlert(video.channel)
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
Button("Subscribe") {
|
||||
subscriptions.subscribe(video.channel.id)
|
||||
subscriptions.subscribe(video.channel.id) {
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
import Siesta
|
||||
import SwiftUI
|
||||
|
||||
struct ChannelView: View {
|
||||
@ObservedObject private var store = Store<[Video]>()
|
||||
|
||||
var id: String
|
||||
|
||||
var resource: Resource {
|
||||
InvidiousAPI.shared.channelVideos(id)
|
||||
}
|
||||
|
||||
init(id: String) {
|
||||
self.id = id
|
||||
resource.addObserver(store)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
VStack {
|
||||
Spacer()
|
||||
VideosView(videos: store.collection)
|
||||
.onAppear {
|
||||
resource.loadIfNeeded()
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.background(.ultraThickMaterial)
|
||||
}
|
||||
}
|
@ -38,21 +38,26 @@ struct TVNavigationView: View {
|
||||
VideoDetailsView(video)
|
||||
}
|
||||
}
|
||||
.fullScreenCover(isPresented: $navigationState.showingChannel, onDismiss: {
|
||||
navigationState.showVideoDetailsIfNeeded()
|
||||
}) {
|
||||
if let channel = navigationState.channel {
|
||||
ChannelView(id: channel.id)
|
||||
}
|
||||
}
|
||||
.fullScreenCover(isPresented: $navigationState.showingVideo) {
|
||||
if let video = navigationState.video {
|
||||
VideoPlayerView(video)
|
||||
.environmentObject(playbackState)
|
||||
}
|
||||
}
|
||||
.fullScreenCover(isPresented: $navigationState.isChannelOpen, onDismiss: {
|
||||
navigationState.closeChannel(presentedChannel)
|
||||
}) {
|
||||
if presentedChannel != nil {
|
||||
ChannelVideosView(presentedChannel)
|
||||
.background(.thickMaterial)
|
||||
}
|
||||
}
|
||||
.onPlayPauseCommand { showingOptions.toggle() }
|
||||
}
|
||||
|
||||
fileprivate var presentedChannel: Channel! {
|
||||
navigationState.openChannels.first
|
||||
}
|
||||
}
|
||||
|
||||
struct TVNavigationView_Previews: PreviewProvider {
|
||||
|
Loading…
Reference in New Issue
Block a user