mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +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,
|
views: 21534,
|
||||||
description: "Some relaxing live piano music",
|
description: "Some relaxing live piano music",
|
||||||
genre: "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),
|
thumbnails: Thumbnail.fixturesForAllQualities(videoId: id),
|
||||||
live: false,
|
live: false,
|
||||||
upcoming: false,
|
upcoming: false,
|
||||||
|
@ -3,20 +3,41 @@ import Defaults
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftyJSON
|
import SwiftyJSON
|
||||||
|
|
||||||
struct Channel: Codable, Identifiable, Defaults.Serializable {
|
struct Channel: Identifiable, Hashable {
|
||||||
var id: String
|
var id: String
|
||||||
var name: String
|
var name: String
|
||||||
var subscriptionsCount: String
|
var videos = [Video]()
|
||||||
|
|
||||||
|
private var subscriptionsCount: Int?
|
||||||
|
private var subscriptionsText: String?
|
||||||
|
|
||||||
init(json: JSON) {
|
init(json: JSON) {
|
||||||
id = json["authorId"].stringValue
|
id = json["authorId"].stringValue
|
||||||
name = json["author"].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.id = id
|
||||||
self.name = name
|
self.name = name
|
||||||
self.subscriptionsCount = subscriptionsCount
|
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)
|
content.json.arrayValue.map(Channel.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
configureTransformer("/channels/*", requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
configureTransformer("/channels/*", requestMethods: [.get]) { (content: Entity<JSON>) -> Channel in
|
||||||
if let channelVideos = content.json.dictionaryValue["latestVideos"] {
|
Channel(json: content.json)
|
||||||
return channelVideos.arrayValue.map(Video.init)
|
|
||||||
}
|
|
||||||
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configureTransformer("/videos/*", requestMethods: [.get]) { (content: Entity<JSON>) -> Video in
|
configureTransformer("/videos/*", requestMethods: [.get]) { (content: Entity<JSON>) -> Video in
|
||||||
@ -112,7 +108,7 @@ final class InvidiousAPI: Service {
|
|||||||
resource("/auth/subscriptions").child(id)
|
resource("/auth/subscriptions").child(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func channelVideos(_ id: String) -> Resource {
|
func channel(_ id: String) -> Resource {
|
||||||
resource("/channels/\(id)")
|
resource("/channels/\(id)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,6 @@ final class NavigationState: ObservableObject {
|
|||||||
|
|
||||||
@Published var tabSelection: TabSelection = .subscriptions
|
@Published var tabSelection: TabSelection = .subscriptions
|
||||||
|
|
||||||
@Published var showingChannel = false
|
|
||||||
@Published var channel: Channel?
|
|
||||||
|
|
||||||
@Published var showingVideoDetails = false
|
@Published var showingVideoDetails = false
|
||||||
@Published var showingVideo = false
|
@Published var showingVideo = false
|
||||||
@Published var video: Video?
|
@Published var video: Video?
|
||||||
@ -23,15 +20,40 @@ final class NavigationState: ObservableObject {
|
|||||||
@Published var presentingUnsubscribeAlert = false
|
@Published var presentingUnsubscribeAlert = false
|
||||||
@Published var channelToUnsubscribe: Channel!
|
@Published var channelToUnsubscribe: Channel!
|
||||||
|
|
||||||
|
@Published var openChannels = Set<Channel>()
|
||||||
|
@Published var isChannelOpen = false
|
||||||
|
@Published var sidebarSectionChanged = false
|
||||||
|
|
||||||
func openChannel(_ channel: Channel) {
|
func openChannel(_ channel: Channel) {
|
||||||
returnToDetails = false
|
openChannels.insert(channel)
|
||||||
self.channel = channel
|
|
||||||
showingChannel = true
|
isChannelOpen = true
|
||||||
|
tabSelection = .channel(channel.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeChannel() {
|
func closeChannel(_ channel: Channel) {
|
||||||
showingChannel = false
|
guard openChannels.remove(channel) != nil else {
|
||||||
channel = nil
|
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) {
|
func openVideoDetails(_ video: Video) {
|
||||||
@ -59,8 +81,10 @@ final class NavigationState: ObservableObject {
|
|||||||
get: {
|
get: {
|
||||||
self.tabSelection
|
self.tabSelection
|
||||||
},
|
},
|
||||||
set: {
|
set: { newValue in
|
||||||
self.tabSelection = $0 ?? .subscriptions
|
if newValue != nil {
|
||||||
|
self.tabSelection = newValue!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -17,29 +17,30 @@ final class Subscriptions: ObservableObject {
|
|||||||
channels.sorted { $0.name.lowercased() < $1.name.lowercased() }
|
channels.sorted { $0.name.lowercased() < $1.name.lowercased() }
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribe(_ channelID: String) {
|
func subscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
|
||||||
performChannelSubscriptionRequest(channelID, method: .post)
|
performChannelSubscriptionRequest(channelID, method: .post, onSuccess: onSuccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsubscribe(_ channelID: String) {
|
func unsubscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
|
||||||
performChannelSubscriptionRequest(channelID, method: .delete)
|
performChannelSubscriptionRequest(channelID, method: .delete, onSuccess: onSuccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribed(_ channelID: String) -> Bool {
|
func isSubscribing(_ channelID: String) -> Bool {
|
||||||
channels.contains { $0.id == channelID }
|
channels.contains { $0.id == channelID }
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func load() {
|
fileprivate func load(onSuccess: @escaping () -> Void = {}) {
|
||||||
resource.load().onSuccess { resource in
|
resource.load().onSuccess { resource in
|
||||||
if let channels: [Channel] = resource.typedContent() {
|
if let channels: [Channel] = resource.typedContent() {
|
||||||
self.channels = channels
|
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
|
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? {
|
var viewsCount: String? {
|
||||||
views != 0 ? formattedCount(views) : nil
|
views != 0 ? views.formattedAsAbbreviation() : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var likesCount: String? {
|
var likesCount: String? {
|
||||||
formattedCount(likes)
|
likes?.formattedAsAbbreviation()
|
||||||
}
|
}
|
||||||
|
|
||||||
var dislikesCount: String? {
|
var dislikesCount: String? {
|
||||||
formattedCount(dislikes)
|
dislikes?.formattedAsAbbreviation()
|
||||||
}
|
|
||||||
|
|
||||||
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)"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectableStreams: [Stream] {
|
var selectableStreams: [Stream] {
|
||||||
|
@ -54,6 +54,8 @@
|
|||||||
3748186E26A769D60084E870 /* DetailBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186D26A769D60084E870 /* DetailBadge.swift */; };
|
3748186E26A769D60084E870 /* DetailBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186D26A769D60084E870 /* DetailBadge.swift */; };
|
||||||
3748186F26A769D60084E870 /* 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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
||||||
377FC7DD267A081A00A6BBAF /* 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 */; };
|
377FC7E2267A084A00A6BBAF /* VideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B18B26717B3800C925CA /* VideoView.swift */; };
|
||||||
377FC7E3267A084A00A6BBAF /* 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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
37AAF27E26737323007FC770 /* PopularView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27D26737323007FC770 /* PopularView.swift */; };
|
||||||
37AAF28026737550007FC770 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27F26737550007FC770 /* SearchView.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 */; };
|
37AAF29026740715007FC770 /* Channel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF28F26740715007FC770 /* Channel.swift */; };
|
||||||
37AAF29126740715007FC770 /* 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 */; };
|
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 */; };
|
37BA794826DC2E56002A0235 /* AppSidebarSubscriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794626DC2E56002A0235 /* AppSidebarSubscriptions.swift */; };
|
||||||
37BA794B26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */; };
|
37BA794B26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */; };
|
||||||
37BA794C26DC30EC002A0235 /* 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 */; };
|
37BAB54C269B39FD00E75ED1 /* TVNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */; };
|
||||||
37BADCA52699FB72009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA42699FB72009BE4FB /* Alamofire */; };
|
37BADCA52699FB72009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA42699FB72009BE4FB /* Alamofire */; };
|
||||||
37BADCA7269A552E009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA6269A552E009BE4FB /* Alamofire */; };
|
37BADCA7269A552E009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA6269A552E009BE4FB /* Alamofire */; };
|
||||||
@ -207,6 +217,13 @@
|
|||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
37BA796726DC40CB002A0235 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 37D4B0CE2671614900C925CA;
|
||||||
|
remoteInfo = "Pearvidious (macOS)";
|
||||||
|
};
|
||||||
37D4B0D52671614900C925CA /* PBXContainerItemProxy */ = {
|
37D4B0D52671614900C925CA /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
|
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
|
||||||
@ -248,6 +265,7 @@
|
|||||||
3748186526A7627F0084E870 /* Video+Fixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Video+Fixtures.swift"; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
37BD07BA2698AB60003EBB87 /* AppSidebarNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarNavigation.swift; sourceTree = "<group>"; };
|
||||||
@ -314,6 +336,13 @@
|
|||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
37BA796026DC40CB002A0235 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
37D4B0C62671614900C925CA /* Frameworks */ = {
|
37D4B0C62671614900C925CA /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -380,6 +409,7 @@
|
|||||||
children = (
|
children = (
|
||||||
37BD07BA2698AB60003EBB87 /* AppSidebarNavigation.swift */,
|
37BD07BA2698AB60003EBB87 /* AppSidebarNavigation.swift */,
|
||||||
37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */,
|
37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */,
|
||||||
|
3763495026DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift */,
|
||||||
37BA794626DC2E56002A0235 /* AppSidebarSubscriptions.swift */,
|
37BA794626DC2E56002A0235 /* AppSidebarSubscriptions.swift */,
|
||||||
37D4B0C32671614700C925CA /* AppTabNavigation.swift */,
|
37D4B0C32671614700C925CA /* AppTabNavigation.swift */,
|
||||||
37BD07B42698AA4D003EBB87 /* ContentView.swift */,
|
37BD07B42698AA4D003EBB87 /* ContentView.swift */,
|
||||||
@ -480,6 +510,30 @@
|
|||||||
path = iOS;
|
path = iOS;
|
||||||
sourceTree = "<group>";
|
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 */ = {
|
37BE0BD826A214500092E2DB /* macOS */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -494,6 +548,7 @@
|
|||||||
children = (
|
children = (
|
||||||
379775922689365600DD52A8 /* Array+Next.swift */,
|
379775922689365600DD52A8 /* Array+Next.swift */,
|
||||||
376578842685429C00D4EA09 /* CaseIterable+Next.swift */,
|
376578842685429C00D4EA09 /* CaseIterable+Next.swift */,
|
||||||
|
37BA794E26DC3E0E002A0235 /* Int+Format.swift */,
|
||||||
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */,
|
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
@ -509,6 +564,7 @@
|
|||||||
37D4B1B72672CFE300C925CA /* Model */,
|
37D4B1B72672CFE300C925CA /* Model */,
|
||||||
37C7A9022679058300E721B4 /* Extensions */,
|
37C7A9022679058300E721B4 /* Extensions */,
|
||||||
3748186426A762300084E870 /* Fixtures */,
|
3748186426A762300084E870 /* Fixtures */,
|
||||||
|
37BA796426DC40CB002A0235 /* Shared Tests */,
|
||||||
377FC7D1267A080300A6BBAF /* Frameworks */,
|
377FC7D1267A080300A6BBAF /* Frameworks */,
|
||||||
37D4B0CA2671614900C925CA /* Products */,
|
37D4B0CA2671614900C925CA /* Products */,
|
||||||
37D4B174267164B000C925CA /* Tests Apple TV */,
|
37D4B174267164B000C925CA /* Tests Apple TV */,
|
||||||
@ -526,7 +582,9 @@
|
|||||||
371AAE2526CEBF0B00901972 /* Trending */,
|
371AAE2526CEBF0B00901972 /* Trending */,
|
||||||
371AAE2726CEBF4700901972 /* Videos */,
|
371AAE2726CEBF4700901972 /* Videos */,
|
||||||
371AAE2826CEC7D900901972 /* Views */,
|
371AAE2826CEC7D900901972 /* Views */,
|
||||||
|
379DDFF126DEE2A800EA08E7 /* Modifiers */,
|
||||||
372915E52687E3B900F5A35B /* Defaults.swift */,
|
372915E52687E3B900F5A35B /* Defaults.swift */,
|
||||||
|
379DDFED26DEDB0E00EA08E7 /* EnvironmentValues.swift */,
|
||||||
37D4B0C22671614700C925CA /* PearvidiousApp.swift */,
|
37D4B0C22671614700C925CA /* PearvidiousApp.swift */,
|
||||||
37D4B0C42671614800C925CA /* Assets.xcassets */,
|
37D4B0C42671614800C925CA /* Assets.xcassets */,
|
||||||
37BD07C42698ADEE003EBB87 /* Pearvidious.entitlements */,
|
37BD07C42698ADEE003EBB87 /* Pearvidious.entitlements */,
|
||||||
@ -543,6 +601,7 @@
|
|||||||
37D4B0DE2671614900C925CA /* Tests macOS.xctest */,
|
37D4B0DE2671614900C925CA /* Tests macOS.xctest */,
|
||||||
37D4B158267164AE00C925CA /* Pearvidious (Apple TV).app */,
|
37D4B158267164AE00C925CA /* Pearvidious (Apple TV).app */,
|
||||||
37D4B171267164B000C925CA /* Tests Apple TV.xctest */,
|
37D4B171267164B000C925CA /* Tests Apple TV.xctest */,
|
||||||
|
37BA796326DC40CB002A0235 /* Shared Tests.xctest */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -568,7 +627,6 @@
|
|||||||
children = (
|
children = (
|
||||||
371AAE2926CF143200901972 /* Options */,
|
371AAE2926CF143200901972 /* Options */,
|
||||||
373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */,
|
373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */,
|
||||||
37AAF2892673AB89007FC770 /* ChannelView.swift */,
|
|
||||||
37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */,
|
37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */,
|
||||||
37B17DA3268A285E006AEE9B /* VideoDetailsView.swift */,
|
37B17DA3268A285E006AEE9B /* VideoDetailsView.swift */,
|
||||||
37D4B15E267164AF00C925CA /* Assets.xcassets */,
|
37D4B15E267164AF00C925CA /* Assets.xcassets */,
|
||||||
@ -616,6 +674,24 @@
|
|||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget 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) */ = {
|
37D4B0C82671614900C925CA /* Pearvidious (iOS) */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 37D4B0EC2671614900C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (iOS)" */;
|
buildConfigurationList = 37D4B0EC2671614900C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (iOS)" */;
|
||||||
@ -753,6 +829,10 @@
|
|||||||
LastSwiftUpdateCheck = 1300;
|
LastSwiftUpdateCheck = 1300;
|
||||||
LastUpgradeCheck = 1300;
|
LastUpgradeCheck = 1300;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
37BA796226DC40CB002A0235 = {
|
||||||
|
CreatedOnToolsVersion = 13.0;
|
||||||
|
TestTargetID = 37D4B0CE2671614900C925CA;
|
||||||
|
};
|
||||||
37D4B0C82671614900C925CA = {
|
37D4B0C82671614900C925CA = {
|
||||||
CreatedOnToolsVersion = 13.0;
|
CreatedOnToolsVersion = 13.0;
|
||||||
};
|
};
|
||||||
@ -803,11 +883,19 @@
|
|||||||
37D4B0D32671614900C925CA /* Tests iOS */,
|
37D4B0D32671614900C925CA /* Tests iOS */,
|
||||||
37D4B0DD2671614900C925CA /* Tests macOS */,
|
37D4B0DD2671614900C925CA /* Tests macOS */,
|
||||||
37D4B170267164B000C925CA /* Tests Apple TV */,
|
37D4B170267164B000C925CA /* Tests Apple TV */,
|
||||||
|
37BA796226DC40CB002A0235 /* Shared Tests */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
37BA796126DC40CB002A0235 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
37D4B0C72671614900C925CA /* Resources */ = {
|
37D4B0C72671614900C925CA /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -857,6 +945,15 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase 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 */ = {
|
37D4B0C52671614900C925CA /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -864,6 +961,7 @@
|
|||||||
37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
||||||
37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */,
|
37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */,
|
||||||
37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||||
|
3763495126DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift in Sources */,
|
||||||
37BA793B26DB8EE4002A0235 /* PlaylistVideosView.swift in Sources */,
|
37BA793B26DB8EE4002A0235 /* PlaylistVideosView.swift in Sources */,
|
||||||
37BD07B52698AA4D003EBB87 /* ContentView.swift in Sources */,
|
37BD07B52698AA4D003EBB87 /* ContentView.swift in Sources */,
|
||||||
37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */,
|
37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */,
|
||||||
@ -888,6 +986,7 @@
|
|||||||
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
||||||
37C7A1DC267CE9D90010EAD6 /* Profile.swift in Sources */,
|
37C7A1DC267CE9D90010EAD6 /* Profile.swift in Sources */,
|
||||||
373CFAC026966149003CB2C6 /* CoverSectionView.swift in Sources */,
|
373CFAC026966149003CB2C6 /* CoverSectionView.swift in Sources */,
|
||||||
|
379DDFF326DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */,
|
||||||
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||||
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
||||||
377FC7E3267A084A00A6BBAF /* VideoView.swift in Sources */,
|
377FC7E3267A084A00A6BBAF /* VideoView.swift in Sources */,
|
||||||
@ -900,9 +999,9 @@
|
|||||||
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
||||||
37B17DA2268A1F8A006AEE9B /* VideoContextMenuView.swift in Sources */,
|
37B17DA2268A1F8A006AEE9B /* VideoContextMenuView.swift in Sources */,
|
||||||
379775932689365600DD52A8 /* Array+Next.swift in Sources */,
|
379775932689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||||
377FC7E1267A082600A6BBAF /* ChannelView.swift in Sources */,
|
|
||||||
37B81AFC26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
37B81AFC26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
||||||
37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||||
|
37BA794F26DC3E0E002A0235 /* Int+Format.swift in Sources */,
|
||||||
37F49BA326CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
|
37F49BA326CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
|
||||||
37BA794B26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */,
|
37BA794B26DC30EC002A0235 /* AppSidebarPlaylists.swift in Sources */,
|
||||||
37B767DB2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
37B767DB2677C3CA0098BAA8 /* PlayerState.swift in Sources */,
|
||||||
@ -916,6 +1015,7 @@
|
|||||||
372915E62687E3B900F5A35B /* Defaults.swift in Sources */,
|
372915E62687E3B900F5A35B /* Defaults.swift in Sources */,
|
||||||
37D4B19726717E1500C925CA /* Video.swift in Sources */,
|
37D4B19726717E1500C925CA /* Video.swift in Sources */,
|
||||||
371F2F1A269B43D300E4A7AB /* NavigationState.swift in Sources */,
|
371F2F1A269B43D300E4A7AB /* NavigationState.swift in Sources */,
|
||||||
|
379DDFEE26DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */,
|
||||||
37BE0BCF26A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
37BE0BCF26A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
||||||
37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */,
|
37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */,
|
||||||
37D4B0E42671614900C925CA /* PearvidiousApp.swift in Sources */,
|
37D4B0E42671614900C925CA /* PearvidiousApp.swift in Sources */,
|
||||||
@ -934,6 +1034,7 @@
|
|||||||
37EAD86C267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
37EAD86C267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||||
37CEE4C22677B697005A1EFE /* Stream.swift in Sources */,
|
37CEE4C22677B697005A1EFE /* Stream.swift in Sources */,
|
||||||
371F2F1B269B43D300E4A7AB /* NavigationState.swift in Sources */,
|
371F2F1B269B43D300E4A7AB /* NavigationState.swift in Sources */,
|
||||||
|
37BA795026DC3E0E002A0235 /* Int+Format.swift in Sources */,
|
||||||
377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */,
|
377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */,
|
||||||
3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
3705B183267B4E4900704544 /* TrendingCategory.swift in Sources */,
|
||||||
37B81B0026D2CA3700675966 /* VideoDetails.swift in Sources */,
|
37B81B0026D2CA3700675966 /* VideoDetails.swift in Sources */,
|
||||||
@ -959,7 +1060,7 @@
|
|||||||
376578862685429C00D4EA09 /* CaseIterable+Next.swift in Sources */,
|
376578862685429C00D4EA09 /* CaseIterable+Next.swift in Sources */,
|
||||||
37F4AE7326828F0900BD60EA /* VideosCellsView.swift in Sources */,
|
37F4AE7326828F0900BD60EA /* VideosCellsView.swift in Sources */,
|
||||||
37B81AFD26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
37B81AFD26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
||||||
377FC7E0267A082600A6BBAF /* ChannelView.swift in Sources */,
|
379DDFEF26DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */,
|
||||||
379775942689365600DD52A8 /* Array+Next.swift in Sources */,
|
379775942689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||||
3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
||||||
37BE0BDA26A214630092E2DB /* PlayerViewController.swift in Sources */,
|
37BE0BDA26A214630092E2DB /* PlayerViewController.swift in Sources */,
|
||||||
@ -975,6 +1076,7 @@
|
|||||||
37D4B19826717E1500C925CA /* Video.swift in Sources */,
|
37D4B19826717E1500C925CA /* Video.swift in Sources */,
|
||||||
37D4B0E52671614900C925CA /* PearvidiousApp.swift in Sources */,
|
37D4B0E52671614900C925CA /* PearvidiousApp.swift in Sources */,
|
||||||
37BD07C12698AD3B003EBB87 /* TrendingCountry.swift in Sources */,
|
37BD07C12698AD3B003EBB87 /* TrendingCountry.swift in Sources */,
|
||||||
|
379DDFF426DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */,
|
||||||
37BA794026DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
37BA794026DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||||
3711404026B206A6005B3555 /* SearchState.swift in Sources */,
|
3711404026B206A6005B3555 /* SearchState.swift in Sources */,
|
||||||
37BE0BD026A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
37BE0BD026A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
||||||
@ -984,6 +1086,7 @@
|
|||||||
3748186B26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
3748186B26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
||||||
373CFADC269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
373CFADC269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
||||||
373CFAF02697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
373CFAF02697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
||||||
|
3763495226DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift in Sources */,
|
||||||
37BA794426DBA973002A0235 /* Playlists.swift in Sources */,
|
37BA794426DBA973002A0235 /* Playlists.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -1025,6 +1128,7 @@
|
|||||||
37141671267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
37141671267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||||
37BA794126DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
37BA794126DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||||
37AAF29226740715007FC770 /* Channel.swift in Sources */,
|
37AAF29226740715007FC770 /* Channel.swift in Sources */,
|
||||||
|
379DDFF526DEE2BA00EA08E7 /* UnsubscribeAlertModifier.swift in Sources */,
|
||||||
37EAD86D267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
37EAD86D267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||||
37B81B0726D2D6CF00675966 /* PlaybackState.swift in Sources */,
|
37B81B0726D2D6CF00675966 /* PlaybackState.swift in Sources */,
|
||||||
3765788B2685471400D4EA09 /* Playlist.swift in Sources */,
|
3765788B2685471400D4EA09 /* Playlist.swift in Sources */,
|
||||||
@ -1040,6 +1144,7 @@
|
|||||||
37AAF29A26740A01007FC770 /* VideosListView.swift in Sources */,
|
37AAF29A26740A01007FC770 /* VideosListView.swift in Sources */,
|
||||||
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||||
376578932685490700D4EA09 /* PlaylistsView.swift in Sources */,
|
376578932685490700D4EA09 /* PlaylistsView.swift in Sources */,
|
||||||
|
37BA795126DC3E0E002A0235 /* Int+Format.swift in Sources */,
|
||||||
3748186C26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
3748186C26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
||||||
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
||||||
3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
||||||
@ -1052,7 +1157,7 @@
|
|||||||
37BA793D26DB8EE4002A0235 /* PlaylistVideosView.swift in Sources */,
|
37BA793D26DB8EE4002A0235 /* PlaylistVideosView.swift in Sources */,
|
||||||
3711404126B206A6005B3555 /* SearchState.swift in Sources */,
|
3711404126B206A6005B3555 /* SearchState.swift in Sources */,
|
||||||
379775952689365600DD52A8 /* Array+Next.swift in Sources */,
|
379775952689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||||
37AAF28A2673AB89007FC770 /* ChannelView.swift in Sources */,
|
379DDFF026DEDB0E00EA08E7 /* EnvironmentValues.swift in Sources */,
|
||||||
3705B180267B4DFB00704544 /* TrendingCountry.swift in Sources */,
|
3705B180267B4DFB00704544 /* TrendingCountry.swift in Sources */,
|
||||||
373CFACD26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
373CFACD26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
||||||
37141675267A8E10006CA35D /* Country.swift in Sources */,
|
37141675267A8E10006CA35D /* Country.swift in Sources */,
|
||||||
@ -1079,6 +1184,11 @@
|
|||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
|
37BA796826DC40CB002A0235 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 37D4B0CE2671614900C925CA /* Pearvidious (macOS) */;
|
||||||
|
targetProxy = 37BA796726DC40CB002A0235 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
37D4B0D62671614900C925CA /* PBXTargetDependency */ = {
|
37D4B0D62671614900C925CA /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 37D4B0C82671614900C925CA /* Pearvidious (iOS) */;
|
target = 37D4B0C82671614900C925CA /* Pearvidious (iOS) */;
|
||||||
@ -1097,6 +1207,58 @@
|
|||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration 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 */ = {
|
37D4B0EA2671614900C925CA /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@ -1555,6 +1717,15 @@
|
|||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList 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" */ = {
|
37D4B0C02671614700C925CA /* Build configuration list for PBXProject "Pearvidious" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
@ -38,6 +38,16 @@
|
|||||||
ReferencedContainer = "container:Pearvidious.xcodeproj">
|
ReferencedContainer = "container:Pearvidious.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "37BA796226DC40CB002A0235"
|
||||||
|
BuildableName = "Shared Tests.xctest"
|
||||||
|
BlueprintName = "Shared Tests"
|
||||||
|
ReferencedContainer = "container:Pearvidious.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<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,16 +38,27 @@ struct AppSidebarNavigation: View {
|
|||||||
NavigationView {
|
NavigationView {
|
||||||
sidebar
|
sidebar
|
||||||
.frame(minWidth: 180)
|
.frame(minWidth: 180)
|
||||||
|
|
||||||
Text("Select section")
|
Text("Select section")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sidebar: some View {
|
var sidebar: some View {
|
||||||
List {
|
ScrollViewReader { scrollView in
|
||||||
mainNavigationLinks
|
List {
|
||||||
|
mainNavigationLinks
|
||||||
|
|
||||||
AppSidebarSubscriptions(selection: selection)
|
Group {
|
||||||
AppSidebarPlaylists(selection: selection)
|
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)
|
#if os(macOS)
|
||||||
@ -61,48 +72,51 @@ struct AppSidebarNavigation: View {
|
|||||||
|
|
||||||
var mainNavigationLinks: some View {
|
var mainNavigationLinks: some View {
|
||||||
Group {
|
Group {
|
||||||
NavigationLink(tag: TabSelection.subscriptions, selection: selection) {
|
NavigationLink(destination: SubscriptionsView(), tag: TabSelection.subscriptions, selection: selection) {
|
||||||
SubscriptionsView()
|
|
||||||
}
|
|
||||||
label: {
|
|
||||||
Label("Subscriptions", systemImage: "star.circle.fill")
|
Label("Subscriptions", systemImage: "star.circle.fill")
|
||||||
.accessibility(label: Text("Subscriptions"))
|
.accessibility(label: Text("Subscriptions"))
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(tag: TabSelection.popular, selection: selection) {
|
NavigationLink(destination: PopularView(), tag: TabSelection.popular, selection: selection) {
|
||||||
PopularView()
|
|
||||||
}
|
|
||||||
label: {
|
|
||||||
Label("Popular", systemImage: "chart.bar")
|
Label("Popular", systemImage: "chart.bar")
|
||||||
.accessibility(label: Text("Popular"))
|
.accessibility(label: Text("Popular"))
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(tag: TabSelection.trending, selection: selection) {
|
NavigationLink(destination: TrendingView(), tag: TabSelection.trending, selection: selection) {
|
||||||
TrendingView()
|
|
||||||
}
|
|
||||||
label: {
|
|
||||||
Label("Trending", systemImage: "chart.line.uptrend.xyaxis")
|
Label("Trending", systemImage: "chart.line.uptrend.xyaxis")
|
||||||
.accessibility(label: Text("Trending"))
|
.accessibility(label: Text("Trending"))
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(tag: TabSelection.playlists, selection: selection) {
|
NavigationLink(destination: PlaylistsView(), tag: TabSelection.playlists, selection: selection) {
|
||||||
PlaylistsView()
|
|
||||||
}
|
|
||||||
label: {
|
|
||||||
Label("Playlists", systemImage: "list.and.film")
|
Label("Playlists", systemImage: "list.and.film")
|
||||||
.accessibility(label: Text("Playlists"))
|
.accessibility(label: Text("Playlists"))
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(tag: TabSelection.search, selection: selection) {
|
NavigationLink(destination: SearchView(), tag: TabSelection.search, selection: selection) {
|
||||||
SearchView()
|
|
||||||
}
|
|
||||||
label: {
|
|
||||||
Label("Search", systemImage: "magnifyingglass")
|
Label("Search", systemImage: "magnifyingglass")
|
||||||
.accessibility(label: Text("Search"))
|
.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 {
|
static func symbolSystemImage(_ name: String) -> String {
|
||||||
let firstLetter = name.first?.lowercased()
|
let firstLetter = name.first?.lowercased()
|
||||||
let regex = #"^[a-z0-9]$"#
|
let regex = #"^[a-z0-9]$"#
|
||||||
@ -111,10 +125,4 @@ struct AppSidebarNavigation: View {
|
|||||||
|
|
||||||
return "\(symbolName).square"
|
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))
|
Label(playlist.title, systemImage: AppSidebarNavigation.symbolSystemImage(playlist.title))
|
||||||
.badge(Text("\(playlist.videos.count)"))
|
.badge(Text("\(playlist.videos.count)"))
|
||||||
}
|
}
|
||||||
|
.id(playlist.id)
|
||||||
.contextMenu {
|
.contextMenu {
|
||||||
Button("Edit") {
|
Button("Edit") {
|
||||||
navigationState.presentEditPlaylistForm(playlists.find(id: playlist.id))
|
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)
|
navigationState.presentUnsubscribeAlert(channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.alert(unsubscribeAlertTitle, isPresented: $navigationState.presentingUnsubscribeAlert) {
|
.modifier(UnsubscribeAlertModifier())
|
||||||
if let channel = navigationState.channelToUnsubscribe {
|
|
||||||
Button("Unsubscribe", role: .destructive) {
|
|
||||||
subscriptions.unsubscribe(channel.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ import Defaults
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct AppTabNavigation: View {
|
struct AppTabNavigation: View {
|
||||||
@State private var tabSelection: TabSelection = .subscriptions
|
@EnvironmentObject<NavigationState> private var navigationState
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TabView(selection: $tabSelection) {
|
TabView(selection: $navigationState.tabSelection) {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
SubscriptionsView()
|
SubscriptionsView()
|
||||||
}
|
}
|
||||||
@ -51,5 +51,19 @@ struct AppTabNavigation: View {
|
|||||||
}
|
}
|
||||||
.tag(TabSelection.search)
|
.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")
|
Image(systemName: "chevron.down.circle.fill")
|
||||||
}
|
}
|
||||||
.accessibilityLabel(Text("Close"))
|
.accessibilityLabel(Text("Close"))
|
||||||
.buttonStyle(BorderlessButtonStyle())
|
.buttonStyle(.borderless)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.keyboardShortcut(.cancelAction)
|
.keyboardShortcut(.cancelAction)
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ struct VideoDetails: View {
|
|||||||
Text(video.channel.name)
|
Text(video.channel.name)
|
||||||
.font(.system(size: 13))
|
.font(.system(size: 13))
|
||||||
.bold()
|
.bold()
|
||||||
if !video.channel.subscriptionsCount.isEmpty {
|
if let subscribers = video.channel.subscriptionsString {
|
||||||
Text("\(video.channel.subscriptionsCount) subscribers")
|
Text("\(subscribers) subscribers")
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ struct VideoDetails: View {
|
|||||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||||
.padding([.horizontal, .bottom])
|
.padding([.horizontal, .bottom])
|
||||||
.onAppear {
|
.onAppear {
|
||||||
subscribed = subscriptions.subscribed(video.channel.id)
|
subscribed = subscriptions.isSubscribing(video.channel.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,10 +89,9 @@ struct VideoPlayerView: View {
|
|||||||
navigationState.showingVideoDetails = navigationState.returnToDetails
|
navigationState.showingVideoDetails = navigationState.returnToDetails
|
||||||
}
|
}
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
.navigationTitle(video.title)
|
|
||||||
.frame(maxWidth: 1000, minHeight: 700)
|
.frame(maxWidth: 1000, minHeight: 700)
|
||||||
#elseif os(iOS)
|
#elseif os(iOS)
|
||||||
.navigationBarTitle(video.title, displayMode: .inline)
|
.navigationBarHidden(true)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ struct PlaylistFormView: View {
|
|||||||
Text(visibility.name)
|
Text(visibility.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pickerStyle(SegmentedPickerStyle())
|
.pickerStyle(.segmented)
|
||||||
}
|
}
|
||||||
Divider()
|
Divider()
|
||||||
.padding(.vertical, 4)
|
.padding(.vertical, 4)
|
||||||
|
@ -8,36 +8,50 @@ struct VideoView: View {
|
|||||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@Environment(\.inNavigationView) private var inNavigationView
|
||||||
|
|
||||||
var video: Video
|
var video: Video
|
||||||
var layout: ListingLayout
|
var layout: ListingLayout
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: { navigationState.playVideo(video) }) {
|
Group {
|
||||||
VStack {
|
if inNavigationView {
|
||||||
if layout == .cells {
|
NavigationLink(destination: VideoPlayerView(video)) {
|
||||||
#if os(iOS)
|
content
|
||||||
if verticalSizeClass == .compact {
|
}
|
||||||
horizontalRow
|
} else {
|
||||||
.padding(.vertical, 4)
|
Button(action: { navigationState.playVideo(video) }) {
|
||||||
} else {
|
content
|
||||||
verticalRow
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
verticalRow
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
horizontalRow
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if os(macOS)
|
|
||||||
.background()
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
.modifier(ButtonStyleModifier(layout: layout))
|
.modifier(ButtonStyleModifier(layout: layout))
|
||||||
.contentShape(RoundedRectangle(cornerRadius: 12))
|
.contentShape(RoundedRectangle(cornerRadius: 12))
|
||||||
.contextMenu { VideoContextMenuView(video: video) }
|
.contextMenu { VideoContextMenuView(video: video) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var content: some View {
|
||||||
|
VStack {
|
||||||
|
if layout == .cells {
|
||||||
|
#if os(iOS)
|
||||||
|
if verticalSizeClass == .compact {
|
||||||
|
horizontalRow
|
||||||
|
.padding(.vertical, 4)
|
||||||
|
} else {
|
||||||
|
verticalRow
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
verticalRow
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
horizontalRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if os(macOS)
|
||||||
|
.background()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
var horizontalRow: some View {
|
var horizontalRow: some View {
|
||||||
HStack(alignment: .top, spacing: 2) {
|
HStack(alignment: .top, spacing: 2) {
|
||||||
Section {
|
Section {
|
||||||
@ -228,7 +242,7 @@ struct VideoListRowPreview: PreviewProvider {
|
|||||||
VideoView(video: video, layout: .list)
|
VideoView(video: video, layout: .list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listStyle(GroupedListStyle())
|
.listStyle(.grouped)
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
ForEach(Video.allFixtures) { video in
|
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 {
|
struct VideosView: View {
|
||||||
@EnvironmentObject<NavigationState> private var navigationState
|
@EnvironmentObject<NavigationState> private var navigationState
|
||||||
|
|
||||||
@State private var profile = Profile()
|
|
||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
@Default(.layout) private var layout
|
@Default(.layout) private var layout
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,26 +2,119 @@ import Siesta
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ChannelVideosView: View {
|
struct ChannelVideosView: View {
|
||||||
@ObservedObject private var store = Store<[Video]>()
|
|
||||||
|
|
||||||
let channel: Channel
|
let channel: Channel
|
||||||
|
|
||||||
var resource: Resource {
|
@EnvironmentObject<NavigationState> private var navigationState
|
||||||
InvidiousAPI.shared.channelVideos(channel.id)
|
@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) {
|
init(_ channel: Channel) {
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
|
||||||
resource.addObserver(store)
|
resource.addObserver(store)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VideosView(videos: store.collection)
|
VStack {
|
||||||
#if !os(tvOS)
|
#if os(tvOS)
|
||||||
.navigationTitle("\(channel.name) Channel")
|
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
|
#endif
|
||||||
.onAppear {
|
#if !os(tvOS)
|
||||||
resource.loadIfNeeded()
|
.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 {
|
var body: some View {
|
||||||
Section {
|
Section {
|
||||||
openChannelButton
|
if navigationState.showOpenChannel(video.channel.id) {
|
||||||
|
openChannelButton
|
||||||
|
}
|
||||||
|
|
||||||
subscriptionButton
|
subscriptionButton
|
||||||
.opacity(subscribed ? 1 : 1)
|
.opacity(subscribed ? 1 : 1)
|
||||||
@ -32,18 +34,25 @@ struct VideoContextMenuView: View {
|
|||||||
var openChannelButton: some View {
|
var openChannelButton: some View {
|
||||||
Button("\(video.author) Channel") {
|
Button("\(video.author) Channel") {
|
||||||
navigationState.openChannel(video.channel)
|
navigationState.openChannel(video.channel)
|
||||||
|
navigationState.sidebarSectionChanged.toggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscriptionButton: some View {
|
var subscriptionButton: some View {
|
||||||
Group {
|
Group {
|
||||||
if subscriptions.subscribed(video.channel.id) {
|
if subscriptions.isSubscribing(video.channel.id) {
|
||||||
Button("Unsubscribe", role: .destructive) {
|
Button("Unsubscribe", role: .destructive) {
|
||||||
subscriptions.unsubscribe(video.channel.id)
|
#if os(tvOS)
|
||||||
|
subscriptions.unsubscribe(video.channel.id)
|
||||||
|
#else
|
||||||
|
navigationState.presentUnsubscribeAlert(video.channel)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Button("Subscribe") {
|
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)
|
VideoDetailsView(video)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fullScreenCover(isPresented: $navigationState.showingChannel, onDismiss: {
|
|
||||||
navigationState.showVideoDetailsIfNeeded()
|
|
||||||
}) {
|
|
||||||
if let channel = navigationState.channel {
|
|
||||||
ChannelView(id: channel.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fullScreenCover(isPresented: $navigationState.showingVideo) {
|
.fullScreenCover(isPresented: $navigationState.showingVideo) {
|
||||||
if let video = navigationState.video {
|
if let video = navigationState.video {
|
||||||
VideoPlayerView(video)
|
VideoPlayerView(video)
|
||||||
.environmentObject(playbackState)
|
.environmentObject(playbackState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.fullScreenCover(isPresented: $navigationState.isChannelOpen, onDismiss: {
|
||||||
|
navigationState.closeChannel(presentedChannel)
|
||||||
|
}) {
|
||||||
|
if presentedChannel != nil {
|
||||||
|
ChannelVideosView(presentedChannel)
|
||||||
|
.background(.thickMaterial)
|
||||||
|
}
|
||||||
|
}
|
||||||
.onPlayPauseCommand { showingOptions.toggle() }
|
.onPlayPauseCommand { showingOptions.toggle() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate var presentedChannel: Channel! {
|
||||||
|
navigationState.openChannels.first
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TVNavigationView_Previews: PreviewProvider {
|
struct TVNavigationView_Previews: PreviewProvider {
|
||||||
|
Loading…
Reference in New Issue
Block a user