iOS 14/macOS Big Sur Support

This commit is contained in:
Arkadiusz Fal 2021-11-28 15:37:55 +01:00
parent 696751e07c
commit 5ef89ac9f4
57 changed files with 1147 additions and 813 deletions

13
Backports/Backport.swift Normal file
View File

@ -0,0 +1,13 @@
import SwiftUI
public struct Backport<Content> {
public let content: Content
public init(_ content: Content) {
self.content = content
}
}
extension View {
var backport: Backport<Self> { Backport(self) }
}

View File

@ -0,0 +1,11 @@
import SwiftUI
extension Backport where Content: View {
@ViewBuilder func badge(_ count: Text) -> some View {
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
content.badge(count)
} else {
content
}
}
}

View File

@ -0,0 +1,11 @@
import SwiftUI
extension Backport where Content: View {
@ViewBuilder func tint(_ color: Color?) -> some View {
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
content.tint(color)
} else {
content.foregroundColor(color)
}
}
}

View File

@ -0,0 +1,17 @@
import SwiftUI
extension Color {
#if os(macOS)
static let background = Color(NSColor.windowBackgroundColor)
static let secondaryBackground = Color(NSColor.underPageBackgroundColor)
static let tertiaryBackground = Color(NSColor.controlBackgroundColor)
#elseif os(iOS)
static let background = Color(UIColor.systemBackground)
static let secondaryBackground = Color(UIColor.secondarySystemBackground)
static let tertiaryBackground = Color(UIColor.tertiarySystemBackground)
#else
static let background = Color.black
static let secondaryBackground = Color.black
static let tertiaryBackground = Color.black
#endif
}

View File

@ -0,0 +1,8 @@
import AppKit
extension NSTextField {
override open var focusRingType: NSFocusRingType {
get { .none }
set {} // swiftlint:disable:this unused_setter_value
}
}

View File

@ -0,0 +1,10 @@
import Foundation
extension String {
func replacingFirstOccurrence(of target: String, with replacement: String) -> String {
guard let range = range(of: target) else {
return self
}
return replacingCharacters(in: range, with: replacement)
}
}

View File

@ -10,7 +10,21 @@ extension View {
verticalEdgeBorder(.bottom, height: height, color: color) verticalEdgeBorder(.bottom, height: height, color: color)
} }
func borderLeading(width: Double, color: Color = Color(white: 0.7, opacity: 1)) -> some View {
horizontalEdgeBorder(.leading, width: width, color: color)
}
func borderTrailing(width: Double, color: Color = Color(white: 0.7, opacity: 1)) -> some View {
horizontalEdgeBorder(.trailing, width: width, color: color)
}
private func verticalEdgeBorder(_ edge: Alignment, height: Double, color: Color) -> some View { private func verticalEdgeBorder(_ edge: Alignment, height: Double, color: Color) -> some View {
overlay(Rectangle().frame(width: nil, height: height, alignment: .top).foregroundColor(color), alignment: edge) overlay(Rectangle().frame(width: nil, height: height, alignment: .top)
.foregroundColor(color), alignment: edge)
}
private func horizontalEdgeBorder(_ edge: Alignment, width: Double, color: Color) -> some View {
overlay(Rectangle().frame(width: width, height: nil, alignment: .leading)
.foregroundColor(color), alignment: edge)
} }
} }

View File

@ -24,7 +24,7 @@ extension Video {
thumbnails: Thumbnail.fixturesForAllQualities(videoId: id), thumbnails: Thumbnail.fixturesForAllQualities(videoId: id),
live: false, live: false,
upcoming: false, upcoming: false,
publishedAt: Date.now, publishedAt: Date(),
likes: 37333, likes: 37333,
dislikes: 30, dislikes: 30,
keywords: ["very", "cool", "video", "msfs 2020", "757", "747", "A380", "737-900", "MOD", "Zibo", "MD80", "MD11", "Rotate", "Laminar", "787", "A350", "MSFS", "MS2020", "Microsoft Flight Simulator", "Microsoft", "Flight", "Simulator", "SIM", "World", "Ortho", "Flying", "Boeing MAX"] keywords: ["very", "cool", "video", "msfs 2020", "757", "747", "A380", "737-900", "MOD", "Zibo", "MD80", "MD11", "Rotate", "Laminar", "787", "A350", "MSFS", "MS2020", "Microsoft Flight Simulator", "Microsoft", "Flight", "Simulator", "SIM", "World", "Ortho", "Flying", "Boeing MAX"]

View File

@ -45,6 +45,7 @@ final class PlayerModel: ObservableObject {
var accounts: AccountsModel var accounts: AccountsModel
var composition = AVMutableComposition() var composition = AVMutableComposition()
var loadedCompositionAssets = [AVMediaType]()
private var currentArtwork: MPMediaItemArtwork? private var currentArtwork: MPMediaItemArtwork?
private var frequentTimeObserver: Any? private var frequentTimeObserver: Any?
@ -147,9 +148,7 @@ final class PlayerModel: ObservableObject {
logger.info("composition audio asset: \(stream.audioAsset.url)") logger.info("composition audio asset: \(stream.audioAsset.url)")
logger.info("composition video asset: \(stream.videoAsset.url)") logger.info("composition video asset: \(stream.videoAsset.url)")
Task { loadComposition(stream, of: video, preservingTime: preservingTime)
await self.loadComposition(stream, of: video, preservingTime: preservingTime)
}
} }
updateCurrentArtwork() updateCurrentArtwork()
@ -228,46 +227,59 @@ final class PlayerModel: ObservableObject {
_ stream: Stream, _ stream: Stream,
of video: Video, of video: Video,
preservingTime: Bool = false preservingTime: Bool = false
) async { ) {
await loadCompositionAsset(stream.audioAsset, type: .audio, of: video) loadedCompositionAssets = []
await loadCompositionAsset(stream.videoAsset, type: .video, of: video) loadCompositionAsset(stream.audioAsset, stream: stream, type: .audio, of: video, preservingTime: preservingTime)
loadCompositionAsset(stream.videoAsset, stream: stream, type: .video, of: video, preservingTime: preservingTime)
guard streamSelection == stream else {
logger.critical("IGNORING LOADED")
return
}
insertPlayerItem(stream, for: video, preservingTime: preservingTime)
} }
private func loadCompositionAsset( func loadCompositionAsset(
_ asset: AVURLAsset, _ asset: AVURLAsset,
stream: Stream,
type: AVMediaType, type: AVMediaType,
of video: Video of video: Video,
) async { preservingTime: Bool = false
async let assetTracks = asset.loadTracks(withMediaType: type) ) {
asset.loadValuesAsynchronously(forKeys: ["playable"]) { [weak self] in
guard let self = self else {
return
}
self.logger.info("loading \(type.rawValue) track")
logger.info("loading \(type.rawValue) track") let assetTracks = asset.tracks(withMediaType: type)
guard let compositionTrack = composition.addMutableTrack(
withMediaType: type, guard let compositionTrack = self.composition.addMutableTrack(
preferredTrackID: kCMPersistentTrackID_Invalid withMediaType: type,
) else { preferredTrackID: kCMPersistentTrackID_Invalid
logger.critical("composition \(type.rawValue) addMutableTrack FAILED") ) else {
return self.logger.critical("composition \(type.rawValue) addMutableTrack FAILED")
return
}
guard let assetTrack = assetTracks.first else {
self.logger.critical("asset \(type.rawValue) track FAILED")
return
}
try! compositionTrack.insertTimeRange(
CMTimeRange(start: .zero, duration: CMTime.secondsInDefaultTimescale(video.length)),
of: assetTrack,
at: .zero
)
self.logger.critical("\(type.rawValue) LOADED")
guard self.streamSelection == stream else {
self.logger.critical("IGNORING LOADED")
return
}
self.loadedCompositionAssets.append(type)
if self.loadedCompositionAssets.count == 2 {
self.insertPlayerItem(stream, for: video, preservingTime: preservingTime)
}
} }
guard let assetTrack = try? await assetTracks.first else {
logger.critical("asset \(type.rawValue) track FAILED")
return
}
try! compositionTrack.insertTimeRange(
CMTimeRange(start: .zero, duration: CMTime.secondsInDefaultTimescale(video.length)),
of: assetTrack,
at: .zero
)
logger.critical("\(type.rawValue) LOADED")
} }
private func playerItem(_ stream: Stream) -> AVPlayerItem? { private func playerItem(_ stream: Stream) -> AVPlayerItem? {

View File

@ -10,6 +10,8 @@ final class SearchModel: ObservableObject {
@Published var queryText = "" @Published var queryText = ""
@Published var querySuggestions = Store<[String]>() @Published var querySuggestions = Store<[String]>()
@Published var fieldIsFocused = false
private var previousResource: Resource? private var previousResource: Resource?
private var resource: Resource! private var resource: Resource!
@ -80,6 +82,10 @@ final class SearchModel: ObservableObject {
private var suggestionsDebounceTimer: Timer? private var suggestionsDebounceTimer: Timer?
func loadSuggestions(_ query: String) { func loadSuggestions(_ query: String) {
guard !query.isEmpty else {
return
}
suggestionsDebounceTimer?.invalidate() suggestionsDebounceTimer?.invalidate()
suggestionsDebounceTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in suggestionsDebounceTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in

View File

@ -1,11 +1,11 @@
![Yattee Banner](https://r.yattee.stream/icons/yattee-banner.png) ![Yattee Banner](https://r.yattee.stream/icons/yattee-banner.png)
Video player with support for [Invidious](https://github.com/iv-org/invidious) and [Piped](https://github.com/TeamPiped/Piped) instances built for iOS 15, tvOS 15 and macOS Monterey. Video player for [Invidious](https://github.com/iv-org/invidious) and [Piped](https://github.com/TeamPiped/Piped) instances built for iOS, tvOS and macOS.
[![AGPL v3](https://shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.en.html) [![AGPL v3](https://shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.en.html)
![GitHub issues](https://img.shields.io/github/issues/yattee/app) [![GitHub issues](https://img.shields.io/github/issues/yattee/yattee)](https://github.com/yattee/yattee/issues)
![GitHub pull requests](https://img.shields.io/github/issues-pr/yattee/app) [![GitHub pull requests](https://img.shields.io/github/issues-pr/yattee/yattee)](https://github.com/yattee/yattee/pulls)
[![Matrix](https://img.shields.io/matrix/yattee:matrix.org)](https://matrix.to/#/#yattee:matrix.org) [![Matrix](https://img.shields.io/matrix/yattee:matrix.org)](https://matrix.to/#/#yattee:matrix.org)
@ -19,7 +19,7 @@ Video player with support for [Invidious](https://github.com/iv-org/invidious) a
* Fullscreen playback, Picture in Picture and AirPlay support * Fullscreen playback, Picture in Picture and AirPlay support
* Stream quality selection * Stream quality selection
* Favorites: customizable section of channels, playlists, trending, searches and other views * Favorites: customizable section of channels, playlists, trending, searches and other views
* URL Scheme for integrations * `yattee://` URL Scheme for integrations
### Availability ### Availability
| Feature | Invidious | Piped | | Feature | Invidious | Piped |
@ -38,17 +38,24 @@ Video player with support for [Invidious](https://github.com/iv-org/invidious) a
## Installation ## Installation
### Requirements ### Requirements
Only iOS/tvOS 15 and macOS Monterey are supported. System requirements:
* iOS 14 (or newer)
* tvOS 15 (or newer)
* macOS Big Sur (or newer)
### How to install? ### How to install?
#### [AltStore](https://altstore.io/) #### [AltStore](https://altstore.io/) (free)
You can sideload IPA files that you can download from Releases page. You can sideload IPA files downloaded from the [Releases](https://github.com/yattee/yattee/releases) page to your iOS or tvOS device - check [AltStore FAQ](https://altstore.io/faq/) for more information.
Alternatively, if you have to access to the beta AltStore version (v1.5), you can add the following repository in `Browse > Sources` screen:
If you have to access to the beta AltStore version (v1.5, for Patreons only), you can add the following repository in `Browse > Sources` screen:
`https://alt.yattee.stream` `https://alt.yattee.stream`
#### Signing IPA files online (paid)
[UDID Registrations](https://www.udidregistrations.com/) provides services to sign IPA files for your devices. Refer to: ***Break free from the App Store*** section of the website for more information.
#### Manual installation #### Manual installation
Download sources and compile them on a Mac using Xcode, install to your devices. Please note that if you are not registered in Apple Developer Program then the applications will require reinstalling every 7 days. Download sources and compile them on a Mac using Xcode, install to your devices. Please note that if you are not registered in Apple Developer Program you will need to reinstall every 7 days.
## Integrations ## Integrations
### macOS ### macOS
@ -63,14 +70,6 @@ With [Finicky](https://github.com/johnste/finicky) you can configure your system
} }
``` ```
### Experimental: Safari
macOS and iOS apps include Safari extension which will redirect opened YouTube tabs to the app.
### Expermiental: Firefox
You can use [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect) extension to make the videos open in the app. In extension settings put the following URL as Invidious instance:
`https://r.yatte.stream`
## Screenshots ## Screenshots
### iOS ### iOS
| Player | Search | Playlists | | Player | Search | Playlists |
@ -111,6 +110,30 @@ You can use [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect)
* `Command+S` - Play Next * `Command+S` - Play Next
* `Command+O` - Toggle Player * `Command+O` - Toggle Player
## Donations
You can support development of this app with
[Patreon](https://www.patreon.com/arekf) or cryptocurrencies:
**Monero (XMR)**
```
48zfKjLmnXs21PinU2ucMiUPwhiKt5d7WJKiy3ACVS28BKqSn52c1TX8L337oESHJ5TZCyGkozjfWZG11h6C46mN9n4NPrD
```
**Bitcoin (BTC)**
```
bc1qe24zz5a5hm0trc7glwckz93py274eycxzju3mv
```
**Ethereum (ETH)**
```
0xa2f81A58Ec5E550132F03615c8d91954A4E37423
```
Donations will be used to cover development program access and domain renewal costs.
## Contributing
If you're interestred in contributing, you can browse the [issues](https://github.com/yattee/yattee/issues) list or create a new one to discuss your feature idea. Every contribution is very welcome.
## License and Liability ## License and Liability
Yattee and its components is shared on [AGPL v3](https://www.gnu.org/licenses/agpl-3.0.en.html) license. Yattee and its components is shared on [AGPL v3](https://www.gnu.org/licenses/agpl-3.0.en.html) license.

View File

@ -51,7 +51,7 @@ struct FavoritesView: View {
.navigationTitle("Favorites") .navigationTitle("Favorites")
#endif #endif
#if os(macOS) #if os(macOS)
.background() .background(Color.tertiaryBackground)
.frame(minWidth: 360) .frame(minWidth: 360)
#endif #endif
} }

View File

@ -10,7 +10,7 @@ struct MenuCommands: Commands {
} }
private var navigationMenu: some Commands { private var navigationMenu: some Commands {
CommandMenu("Navigation") { CommandGroup(before: .windowSize) {
Button("Favorites") { Button("Favorites") {
model.navigation?.tabSelection = .favorites model.navigation?.tabSelection = .favorites
} }
@ -19,7 +19,7 @@ struct MenuCommands: Commands {
Button("Subscriptions") { Button("Subscriptions") {
model.navigation?.tabSelection = .subscriptions model.navigation?.tabSelection = .subscriptions
} }
.disabled(!(model.accounts?.app.supportsSubscriptions ?? true)) .disabled(subscriptionsDisabled)
.keyboardShortcut("2") .keyboardShortcut("2")
Button("Popular") { Button("Popular") {
@ -37,9 +37,17 @@ struct MenuCommands: Commands {
model.navigation?.tabSelection = .search model.navigation?.tabSelection = .search
} }
.keyboardShortcut("f") .keyboardShortcut("f")
Divider()
} }
} }
private var subscriptionsDisabled: Bool {
!(
(model.accounts?.app.supportsSubscriptions ?? false) && model.accounts?.signedIn ?? false
)
}
private var playbackMenu: some Commands { private var playbackMenu: some Commands {
CommandMenu("Playback") { CommandMenu("Playback") {
Button((model.player?.isPlaying ?? true) ? "Pause" : "Play") { Button((model.player?.isPlaying ?? true) ? "Pause" : "Play") {

View File

@ -1,26 +0,0 @@
import Foundation
import SwiftUI
struct UnsubscribeAlertModifier: ViewModifier {
@EnvironmentObject<NavigationModel> private var navigation
@EnvironmentObject<SubscriptionsModel> private var subscriptions
func body(content: Content) -> some View {
content
.alert(unsubscribeAlertTitle, isPresented: $navigation.presentingUnsubscribeAlert) {
if let channel = navigation.channelToUnsubscribe {
Button("Unsubscribe", role: .destructive) {
subscriptions.unsubscribe(channel.id)
}
}
}
}
var unsubscribeAlertTitle: String {
if let channel = navigation.channelToUnsubscribe {
return "Unsubscribe from \(channel.name)"
}
return "Unknown channel"
}
}

View File

@ -15,13 +15,25 @@ struct AccountsMenuView: View {
} }
} }
} label: { } label: {
Label(model.current?.description ?? "Select Account", systemImage: "person.crop.circle") if #available(iOS 15.0, macOS 12.0, *) {
.labelStyle(.titleAndIcon) label
.labelStyle(.titleAndIcon)
} else {
HStack {
Image(systemName: "person.crop.circle")
label
.labelStyle(.titleOnly)
}
}
} }
.disabled(instances.isEmpty) .disabled(instances.isEmpty)
.transaction { t in t.animation = .none } .transaction { t in t.animation = .none }
} }
private var label: some View {
Label(model.current?.description ?? "Select Account", systemImage: "person.crop.circle")
}
private var allAccounts: [Account] { private var allAccounts: [Account] {
accounts + instances.map(\.anonymousAccount) accounts + instances.map(\.anonymousAccount)
} }

View File

@ -12,6 +12,7 @@ struct AppSidebarPlaylists: View {
LazyView(PlaylistVideosView(playlist)) LazyView(PlaylistVideosView(playlist))
} label: { } label: {
Label(playlist.title, systemImage: AppSidebarNavigation.symbolSystemImage(playlist.title)) Label(playlist.title, systemImage: AppSidebarNavigation.symbolSystemImage(playlist.title))
.backport
.badge(Text("\(playlist.videos.count)")) .badge(Text("\(playlist.videos.count)"))
} }
.id(playlist.id) .id(playlist.id)

View File

@ -18,7 +18,6 @@ struct AppSidebarSubscriptions: View {
navigation.presentUnsubscribeAlert(channel) navigation.presentUnsubscribeAlert(channel)
} }
} }
.modifier(UnsubscribeAlertModifier())
.id("channel\(channel.id)") .id("channel\(channel.id)")
} }
} }

View File

@ -41,6 +41,8 @@ struct AppTabNavigation: View {
} else { } else {
trendingNavigationView trendingNavigationView
} }
} else {
trendingNavigationView
} }
} else { } else {
if accounts.app.supportsPopular { if accounts.app.supportsPopular {
@ -62,26 +64,7 @@ struct AppTabNavigation: View {
} }
NavigationView { NavigationView {
LazyView( LazyView(SearchView())
SearchView()
.toolbar { toolbarContent }
.searchable(text: $search.queryText, placement: .navigationBarDrawer(displayMode: .always)) {
ForEach(search.querySuggestions.collection, id: \.self) { suggestion in
Text(suggestion)
.searchCompletion(suggestion)
}
}
.onChange(of: search.queryText) { query in
search.loadSuggestions(query)
}
.onSubmit(of: .search) {
search.changeQuery { query in
query.query = search.queryText
}
recents.addQuery(search.queryText)
}
)
} }
.tabItem { .tabItem {
Label("Search", systemImage: "magnifyingglass") Label("Search", systemImage: "magnifyingglass")
@ -129,7 +112,7 @@ struct AppTabNavigation: View {
.toolbar { toolbarContent } .toolbar { toolbarContent }
} }
.tabItem { .tabItem {
Label("Popular", systemImage: "chart.bar") Label("Popular", systemImage: "arrow.up.right.circle")
.accessibility(label: Text("Popular")) .accessibility(label: Text("Popular"))
} }
.tag(TabSelection.popular) .tag(TabSelection.popular)
@ -141,7 +124,7 @@ struct AppTabNavigation: View {
.toolbar { toolbarContent } .toolbar { toolbarContent }
} }
.tabItem { .tabItem {
Label("Trending", systemImage: "chart.line.uptrend.xyaxis") Label("Trending", systemImage: "chart.bar")
.accessibility(label: Text("Trending")) .accessibility(label: Text("Trending"))
} }
.tag(TabSelection.trending) .tag(TabSelection.trending)

View File

@ -46,7 +46,7 @@ struct Sidebar: View {
} }
var mainNavigationLinks: some View { var mainNavigationLinks: some View {
Section("Videos") { Section(header: Text("Videos")) {
NavigationLink(destination: LazyView(FavoritesView()), tag: TabSelection.favorites, selection: $navigation.tabSelection) { NavigationLink(destination: LazyView(FavoritesView()), tag: TabSelection.favorites, selection: $navigation.tabSelection) {
Label("Favorites", systemImage: "heart") Label("Favorites", systemImage: "heart")
.accessibility(label: Text("Favorites")) .accessibility(label: Text("Favorites"))
@ -60,13 +60,13 @@ struct Sidebar: View {
if accounts.app.supportsPopular { if accounts.app.supportsPopular {
NavigationLink(destination: LazyView(PopularView()), tag: TabSelection.popular, selection: $navigation.tabSelection) { NavigationLink(destination: LazyView(PopularView()), tag: TabSelection.popular, selection: $navigation.tabSelection) {
Label("Popular", systemImage: "chart.bar") Label("Popular", systemImage: "arrow.up.right.circle")
.accessibility(label: Text("Popular")) .accessibility(label: Text("Popular"))
} }
} }
NavigationLink(destination: LazyView(TrendingView()), tag: TabSelection.trending, selection: $navigation.tabSelection) { NavigationLink(destination: LazyView(TrendingView()), tag: TabSelection.trending, selection: $navigation.tabSelection) {
Label("Trending", systemImage: "chart.line.uptrend.xyaxis") Label("Trending", systemImage: "chart.bar")
.accessibility(label: Text("Trending")) .accessibility(label: Text("Trending"))
} }

View File

@ -3,7 +3,8 @@ import Foundation
import SwiftUI import SwiftUI
struct PlaybackBar: View { struct PlaybackBar: View {
@Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme
@Environment(\.presentationMode) private var presentationMode
@Environment(\.inNavigationView) private var inNavigationView @Environment(\.inNavigationView) private var inNavigationView
@EnvironmentObject<PlayerModel> private var player @EnvironmentObject<PlayerModel> private var player
@ -64,18 +65,20 @@ struct PlaybackBar: View {
Spacer() Spacer()
} }
} }
.alert(player.playerError?.localizedDescription ?? "", isPresented: $player.presentingErrorDetails) { .alert(isPresented: $player.presentingErrorDetails) {
Button("OK") {} Alert(
title: Text("Error"),
message: Text(player.playerError?.localizedDescription ?? "")
)
} }
.environment(\.colorScheme, .dark)
.frame(minWidth: 0, maxWidth: .infinity) .frame(minWidth: 0, maxWidth: .infinity)
.padding(4) .padding(4)
.background(.black) .background(colorScheme == .dark ? Color.black : Color.white)
} }
private var closeButton: some View { private var closeButton: some View {
Button { Button {
dismiss() presentationMode.wrappedValue.dismiss()
} label: { } label: {
Label( Label(
"Close", "Close",
@ -105,10 +108,18 @@ struct PlaybackBar: View {
return "less than a minute" return "less than a minute"
} }
let timeFinishAt = Date.now.addingTimeInterval(remainingSeconds) let timeFinishAt = Date().addingTimeInterval(remainingSeconds)
let timeFinishAtString = timeFinishAt.formatted(date: .omitted, time: .shortened)
return "ends at \(timeFinishAtString)" return "ends at \(formattedTimeFinishAt(timeFinishAt))"
}
private func formattedTimeFinishAt(_ date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .none
dateFormatter.timeStyle = .short
return dateFormatter.string(from: date)
} }
private var rateMenu: some View { private var rateMenu: some View {

View File

@ -44,16 +44,18 @@ struct PlayerQueueView: View {
} }
ForEach(player.queue) { item in ForEach(player.queue) { item in
PlayerQueueRow(item: item, fullScreen: $fullScreen) let row = PlayerQueueRow(item: item, fullScreen: $fullScreen)
.contextMenu { .contextMenu {
removeButton(item, history: false) removeButton(item, history: false)
removeAllButton(history: false) removeAllButton(history: false)
} }
#if os(iOS) if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
.swipeActions(edge: .trailing, allowsFullSwipe: true) { row.swipeActions(edge: .trailing, allowsFullSwipe: true) {
removeButton(item, history: false) removeButton(item, history: false)
} }
#endif } else {
row
}
} }
} }
} }
@ -63,14 +65,18 @@ struct PlayerQueueView: View {
if !player.history.isEmpty { if !player.history.isEmpty {
Section(header: Text("Played Previously")) { Section(header: Text("Played Previously")) {
ForEach(player.history) { item in ForEach(player.history) { item in
PlayerQueueRow(item: item, history: true, fullScreen: $fullScreen) let row = PlayerQueueRow(item: item, history: true, fullScreen: $fullScreen)
.contextMenu { .contextMenu {
removeButton(item, history: true) removeButton(item, history: true)
removeAllButton(history: true) removeAllButton(history: true)
} }
#if os(iOS) #if os(iOS)
.swipeActions(edge: .trailing, allowsFullSwipe: true) { if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
removeButton(item, history: true) row.swipeActions(edge: .trailing, allowsFullSwipe: true) {
removeButton(item, history: true)
}
} else {
row
} }
#endif #endif
} }
@ -100,28 +106,44 @@ struct PlayerQueueView: View {
} }
private func removeButton(_ item: PlayerQueueItem, history: Bool) -> some View { private func removeButton(_ item: PlayerQueueItem, history: Bool) -> some View {
Button(role: .destructive) { if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
if history { return Button(role: .destructive) {
player.removeHistory(item) removeButtonAction(item, history: history)
} else { } label: {
player.remove(item) Label("Remove", systemImage: "trash")
}
} else {
return Button {
removeButtonAction(item, history: history)
} label: {
Label("Remove", systemImage: "trash")
} }
} label: {
Label("Remove", systemImage: "trash")
} }
} }
private func removeButtonAction(_ item: PlayerQueueItem, history: Bool) {
_ = history ? player.removeHistory(item) : player.remove(item)
}
private func removeAllButton(history: Bool) -> some View { private func removeAllButton(history: Bool) -> some View {
Button(role: .destructive) { if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
if history { return Button(role: .destructive) {
player.removeHistoryItems() removeAllButtonAction(history: history)
} else { } label: {
player.removeQueueItems() Label("Remove All", systemImage: "trash.fill")
}
} else {
return Button {
removeAllButtonAction(history: history)
} label: {
Label("Remove All", systemImage: "trash.fill")
} }
} label: {
Label("Remove All", systemImage: "trash.fill")
} }
} }
private func removeAllButtonAction(history: Bool) {
_ = history ? player.removeHistoryItems() : player.removeQueueItems()
}
} }
struct PlayerQueueView_Previews: PreviewProvider { struct PlayerQueueView_Previews: PreviewProvider {

View File

@ -11,14 +11,14 @@ struct VideoDetails: View {
@Binding var fullScreen: Bool @Binding var fullScreen: Bool
@State private var subscribed = false @State private var subscribed = false
@State private var confirmationShown = false @State private var presentingUnsubscribeAlert = false
@State private var presentingAddToPlaylist = false @State private var presentingAddToPlaylist = false
@State private var presentingShareSheet = false @State private var presentingShareSheet = false
@State private var shareURL: URL? @State private var shareURL: URL?
@State private var currentPage = Page.details @State private var currentPage = Page.details
@Environment(\.dismiss) private var dismiss @Environment(\.presentationMode) private var presentationMode
@Environment(\.inNavigationView) private var inNavigationView @Environment(\.inNavigationView) private var inNavigationView
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@ -82,7 +82,7 @@ struct VideoDetails: View {
if fullScreen { if fullScreen {
fullScreen = false fullScreen = false
} else { } else {
self.dismiss() self.presentationMode.wrappedValue.dismiss()
} }
} }
} }
@ -184,19 +184,26 @@ struct VideoDetails: View {
Section { Section {
if subscribed { if subscribed {
Button("Unsubscribe") { Button("Unsubscribe") {
confirmationShown = true presentingUnsubscribeAlert = true
} }
#if os(iOS) #if os(iOS)
.backport
.tint(.gray) .tint(.gray)
#endif #endif
.confirmationDialog("Are you you want to unsubscribe from \(video!.channel.name)?", isPresented: $confirmationShown) { .alert(isPresented: $presentingUnsubscribeAlert) {
Button("Unsubscribe") { Alert(
subscriptions.unsubscribe(video!.channel.id) title: Text(
"Are you you want to unsubscribe from \(video!.channel.name)?"
),
primaryButton: .destructive(Text("Unsubscribe")) {
subscriptions.unsubscribe(video!.channel.id)
withAnimation { withAnimation {
subscribed.toggle() subscribed.toggle()
} }
} },
secondaryButton: .cancel()
)
} }
} else { } else {
Button("Subscribe") { Button("Subscribe") {
@ -206,12 +213,12 @@ struct VideoDetails: View {
subscribed.toggle() subscribed.toggle()
} }
} }
.backport
.tint(.blue) .tint(.blue)
} }
} }
.font(.system(size: 13)) .font(.system(size: 13))
.buttonStyle(.borderless) .buttonStyle(.borderless)
.buttonBorderShape(.roundedRectangle)
} }
} }
} }
@ -241,13 +248,13 @@ struct VideoDetails: View {
Text(published) Text(published)
} }
if let publishedAt = video.publishedAt { if let date = video.publishedAt {
if video.publishedDate != nil { if video.publishedDate != nil {
Text("") Text("")
.foregroundColor(.secondary) .foregroundColor(.secondary)
.opacity(0.3) .opacity(0.3)
} }
Text(publishedAt.formatted(date: .abbreviated, time: .omitted)) Text(formattedPublishedAt(date))
} }
} }
.font(.system(size: 12)) .font(.system(size: 12))
@ -257,6 +264,15 @@ struct VideoDetails: View {
} }
} }
func formattedPublishedAt(_ date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .none
return dateFormatter.string(from: date)
}
var countsSection: some View { var countsSection: some View {
Group { Group {
if let video = player.currentVideo { if let video = player.currentVideo {
@ -338,11 +354,17 @@ struct VideoDetails: View {
VStack(alignment: .leading, spacing: 10) { VStack(alignment: .leading, spacing: 10) {
if let description = video.description { if let description = video.description {
Text(description) Group {
.textSelection(.enabled) if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
.frame(maxWidth: .infinity, alignment: .leading) Text(description)
.font(.caption) .textSelection(.enabled)
.padding(.bottom, 4) } else {
Text(description)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.font(.caption)
.padding(.bottom, 4)
} else { } else {
Text("No description") Text("No description")
.foregroundColor(.secondary) .foregroundColor(.secondary)

View File

@ -16,8 +16,10 @@ struct VideoPlayerView: View {
@State private var playerSize: CGSize = .zero @State private var playerSize: CGSize = .zero
@State private var fullScreen = false @State private var fullScreen = false
@Environment(\.colorScheme) private var colorScheme
#if os(iOS) #if os(iOS)
@Environment(\.dismiss) private var dismiss @Environment(\.presentationMode) private var presentationMode
@Environment(\.horizontalSizeClass) private var horizontalSizeClass @Environment(\.horizontalSizeClass) private var horizontalSizeClass
@Environment(\.verticalSizeClass) private var verticalSizeClass @Environment(\.verticalSizeClass) private var verticalSizeClass
#endif #endif
@ -82,11 +84,11 @@ struct VideoPlayerView: View {
fullScreen = true fullScreen = true
} }
}, },
down: { dismiss() } down: { presentationMode.wrappedValue.dismiss() }
) )
#endif #endif
.background(.black) .background(Color.black)
Group { Group {
#if os(iOS) #if os(iOS)
@ -98,7 +100,7 @@ struct VideoPlayerView: View {
VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreen) VideoDetails(sidebarQueue: sidebarQueueBinding, fullScreen: $fullScreen)
#endif #endif
} }
.background() .background(colorScheme == .dark ? Color.black : Color.white)
.modifier(VideoDetailsPaddingModifier(geometry: geometry, aspectRatio: player.controller?.aspectRatio, fullScreen: fullScreen)) .modifier(VideoDetailsPaddingModifier(geometry: geometry, aspectRatio: player.controller?.aspectRatio, fullScreen: fullScreen))
} }
#endif #endif

View File

@ -7,7 +7,7 @@ struct AddToPlaylistView: View {
@State private var selectedPlaylistID: Playlist.ID = "" @State private var selectedPlaylistID: Playlist.ID = ""
@Environment(\.dismiss) private var dismiss @Environment(\.presentationMode) private var presentationMode
@EnvironmentObject<PlaylistsModel> private var model @EnvironmentObject<PlaylistsModel> private var model
var body: some View { var body: some View {
@ -37,7 +37,7 @@ struct AddToPlaylistView: View {
.padding(.vertical) .padding(.vertical)
#elseif os(tvOS) #elseif os(tvOS)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(.thickMaterial) .background(Color.tertiaryBackground)
#else #else
.padding(.vertical) .padding(.vertical)
#endif #endif
@ -70,7 +70,7 @@ struct AddToPlaylistView: View {
#if !os(tvOS) #if !os(tvOS)
Button("Cancel") { Button("Cancel") {
dismiss() presentationMode.wrappedValue.dismiss()
} }
.keyboardShortcut(.cancelAction) .keyboardShortcut(.cancelAction)
#endif #endif
@ -155,7 +155,7 @@ struct AddToPlaylistView: View {
Defaults[.lastUsedPlaylistID] = id Defaults[.lastUsedPlaylistID] = id
model.addVideo(playlistID: id, videoID: video.videoID) { model.addVideo(playlistID: id, videoID: video.videoID) {
dismiss() presentationMode.wrappedValue.dismiss()
} }
} }

View File

@ -10,9 +10,7 @@ struct PlaylistFormView: View {
@State private var valid = false @State private var valid = false
@State private var showingDeleteConfirmation = false @State private var showingDeleteConfirmation = false
@FocusState private var focused: Bool @Environment(\.presentationMode) private var presentationMode
@Environment(\.dismiss) private var dismiss
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@EnvironmentObject<PlaylistsModel> private var playlists @EnvironmentObject<PlaylistsModel> private var playlists
@ -22,77 +20,68 @@ struct PlaylistFormView: View {
} }
var body: some View { var body: some View {
#if os(macOS) || os(iOS) Group {
VStack(alignment: .leading) { #if os(macOS) || os(iOS)
HStack(alignment: .center) { VStack(alignment: .leading) {
Text(editing ? "Edit Playlist" : "Create Playlist") HStack(alignment: .center) {
.font(.title2.bold()) Text(editing ? "Edit Playlist" : "Create Playlist")
.font(.title2.bold())
Spacer() Spacer()
Button("Cancel") { Button("Cancel") {
dismiss() presentationMode.wrappedValue.dismiss()
}.keyboardShortcut(.cancelAction) }.keyboardShortcut(.cancelAction)
}
.padding(.horizontal)
Form {
TextField("Name", text: $name, onCommit: validate)
.frame(maxWidth: 450)
.padding(.leading, 10)
visibilityFormItem
.pickerStyle(.segmented)
}
#if os(macOS)
.padding(.horizontal)
#endif
HStack {
if editing {
deletePlaylistButton
}
Spacer()
Button("Save", action: submitForm)
.disabled(!valid)
.keyboardShortcut(.defaultAction)
}
.frame(minHeight: 35)
.padding(.horizontal)
} }
.padding(.horizontal)
Form { #if os(iOS)
TextField("Name", text: $name, onCommit: validate) .padding(.vertical)
.frame(maxWidth: 450) #else
.padding(.leading, 10) .frame(width: 400, height: 150)
.focused($focused)
visibilityFormItem
.pickerStyle(.segmented)
}
#if os(macOS)
.padding(.horizontal)
#endif #endif
HStack {
if editing {
deletePlaylistButton
}
Spacer()
Button("Save", action: submitForm)
.disabled(!valid)
.keyboardShortcut(.defaultAction)
}
.frame(minHeight: 35)
.padding(.horizontal)
}
.onChange(of: name) { _ in validate() }
.onAppear(perform: initializeForm)
#if os(iOS)
.padding(.vertical)
#else #else
.frame(width: 400, height: 150) VStack {
Group {
header
form
}
.frame(maxWidth: 1000)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color.tertiaryBackground)
#endif #endif
}
#else .onChange(of: name) { _ in validate() }
VStack { .onAppear(perform: initializeForm)
Group {
header
form
}
.frame(maxWidth: 1000)
}
.onAppear {
guard editing else {
return
}
self.name = self.playlist.title
self.visibility = self.playlist.visibility
validate()
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(.thickMaterial)
#endif
} }
#if os(tvOS) #if os(tvOS)
@ -152,16 +141,16 @@ struct PlaylistFormView: View {
#endif #endif
func initializeForm() { func initializeForm() {
focused = true
guard editing else { guard editing else {
return return
} }
name = playlist.title DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
visibility = playlist.visibility name = playlist.title
visibility = playlist.visibility
validate() validate()
}
} }
func validate() { func validate() {
@ -182,7 +171,7 @@ struct PlaylistFormView: View {
playlists.load(force: true) playlists.load(force: true)
dismiss() presentationMode.wrappedValue.dismiss()
} }
} }
@ -194,7 +183,7 @@ struct PlaylistFormView: View {
#if os(macOS) #if os(macOS)
Picker("Visibility", selection: $visibility) { Picker("Visibility", selection: $visibility) {
ForEach(Playlist.Visibility.allCases) { visibility in ForEach(Playlist.Visibility.allCases) { visibility in
Text(visibility.name) Text(visibility.name).tag(visibility)
} }
} }
#else #else
@ -216,9 +205,10 @@ struct PlaylistFormView: View {
} }
var deletePlaylistButton: some View { var deletePlaylistButton: some View {
Button("Delete", role: .destructive) { Button("Delete") {
showingDeleteConfirmation = true showingDeleteConfirmation = true
}.alert(isPresented: $showingDeleteConfirmation) { }
.alert(isPresented: $showingDeleteConfirmation) {
Alert( Alert(
title: Text("Are you sure you want to delete playlist?"), title: Text("Are you sure you want to delete playlist?"),
message: Text("Playlist \"\(playlist.title)\" will be deleted.\nIt cannot be undone."), message: Text("Playlist \"\(playlist.title)\" will be deleted.\nIt cannot be undone."),
@ -226,16 +216,14 @@ struct PlaylistFormView: View {
secondaryButton: .cancel() secondaryButton: .cancel()
) )
} }
#if os(macOS)
.foregroundColor(.red) .foregroundColor(.red)
#endif
} }
func deletePlaylistAndDismiss() { func deletePlaylistAndDismiss() {
accounts.api.playlist(playlist.id)?.request(.delete).onSuccess { _ in accounts.api.playlist(playlist.id)?.request(.delete).onSuccess { _ in
playlist = nil playlist = nil
playlists.load(force: true) playlists.load(force: true)
dismiss() presentationMode.wrappedValue.dismiss()
} }
} }
} }

View File

@ -71,17 +71,20 @@ struct PlaylistsView: View {
ToolbarItemGroup { ToolbarItemGroup {
#if !os(iOS) #if !os(iOS)
if !model.isEmpty { if !model.isEmpty {
selectPlaylistButton if #available(macOS 12.0, *) {
.prefersDefaultFocus(in: focusNamespace) selectPlaylistButton
.prefersDefaultFocus(in: focusNamespace)
} else {
selectPlaylistButton
}
} }
if currentPlaylist != nil { if currentPlaylist != nil {
editPlaylistButton editPlaylistButton
} }
#endif #endif
FavoriteButton(item: FavoriteItem(section: .playlist(selectedPlaylistID)))
newPlaylistButton FavoriteButton(item: FavoriteItem(section: .playlist(selectedPlaylistID)))
} }
#if os(iOS) #if os(iOS)
@ -99,6 +102,8 @@ struct PlaylistsView: View {
Spacer() Spacer()
newPlaylistButton
if currentPlaylist != nil { if currentPlaylist != nil {
editPlaylistButton editPlaylistButton
} }
@ -168,7 +173,7 @@ struct PlaylistsView: View {
} }
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
#if os(macOS) #if os(macOS)
.background() .background(Color.tertiaryBackground)
#endif #endif
} }

View File

@ -0,0 +1,80 @@
import SwiftUI
struct SearchTextField: View {
@Environment(\.navigationStyle) private var navigationStyle
@EnvironmentObject<RecentsModel> private var recents
@EnvironmentObject<SearchModel> private var state
var body: some View {
ZStack {
#if os(macOS)
fieldBorder
#endif
HStack(spacing: 0) {
#if os(macOS)
Image(systemName: "magnifyingglass")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 12, height: 12)
.padding(.horizontal, 8)
.opacity(0.8)
#endif
TextField("Search...", text: $state.queryText) {
state.changeQuery { query in query.query = state.queryText }
recents.addQuery(state.queryText)
}
.onChange(of: state.queryText) { _ in
if state.query.query.compare(state.queryText, options: .caseInsensitive) == .orderedSame {
state.fieldIsFocused = true
}
}
#if os(macOS)
.textFieldStyle(.plain)
#else
.textFieldStyle(.roundedBorder)
.padding(.leading)
.padding(.trailing, 15)
#endif
if !self.state.queryText.isEmpty {
clearButton
}
}
}
.padding(.top, navigationStyle == .tab ? 10 : 0)
}
private var fieldBorder: some View {
RoundedRectangle(cornerRadius: 5, style: .continuous)
.fill(Color.background)
.frame(width: 250, height: 32)
.overlay(
RoundedRectangle(cornerRadius: 5, style: .continuous)
.stroke(
state.fieldIsFocused ? Color.blue.opacity(0.7) : Color.gray.opacity(0.4),
lineWidth: state.fieldIsFocused ? 3 : 1
)
.frame(width: 250, height: 31)
)
}
private var clearButton: some View {
Button(action: {
self.state.queryText = ""
}) {
Image(systemName: "xmark.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
#if os(macOS)
.frame(width: 14, height: 14)
#else
.frame(width: 18, height: 18)
#endif
.padding(.trailing, 3)
}
.buttonStyle(PlainButtonStyle())
.padding(.trailing, 10)
.opacity(0.7)
}
}

View File

@ -0,0 +1,77 @@
import SwiftUI
struct SearchSuggestions: View {
@EnvironmentObject<RecentsModel> private var recents
@EnvironmentObject<SearchModel> private var state
var body: some View {
List {
Button {
state.changeQuery { query in
query.query = state.queryText
state.fieldIsFocused = false
}
recents.addQuery(state.queryText)
} label: {
HStack(spacing: 5) {
Label(state.queryText, systemImage: "magnifyingglass")
.lineLimit(1)
}
}
#if os(macOS)
.onHover(perform: onHover(_:))
#endif
ForEach(visibleSuggestions, id: \.self) { suggestion in
Button {
state.queryText = suggestion
} label: {
HStack(spacing: 0) {
Label(state.queryText, systemImage: "arrow.up.left.circle")
.lineLimit(1)
.layoutPriority(2)
.foregroundColor(.secondary)
Text(querySuffix(suggestion))
.lineLimit(1)
.layoutPriority(1)
}
}
#if os(macOS)
.onHover(perform: onHover(_:))
#endif
}
}
#if os(macOS)
.buttonStyle(.link)
#endif
}
private var visibleSuggestions: [String] {
state.querySuggestions.collection.filter {
$0.compare(state.queryText, options: .caseInsensitive) != .orderedSame
}
}
private func querySuffix(_ suggestion: String) -> String {
suggestion.replacingFirstOccurrence(of: state.queryText.lowercased(), with: "")
}
#if os(macOS)
private func onHover(_ inside: Bool) {
if inside {
NSCursor.pointingHand.push()
} else {
NSCursor.pop()
}
}
#endif
}
struct SearchSuggestions_Previews: PreviewProvider {
static var previews: some View {
SearchSuggestions()
.injectFixtureEnvironmentObjects()
}
}

View File

@ -9,7 +9,6 @@ struct SearchView: View {
@State private var searchDate = SearchQuery.Date.any @State private var searchDate = SearchQuery.Date.any
@State private var searchDuration = SearchQuery.Duration.any @State private var searchDuration = SearchQuery.Duration.any
@State private var presentingClearConfirmation = false
@State private var recentsChanged = false @State private var recentsChanged = false
#if os(tvOS) #if os(tvOS)
@ -31,50 +30,37 @@ struct SearchView: View {
state.store.collection.sorted { $0 < $1 } state.store.collection.sorted { $0 < $1 }
} }
init(_ query: SearchQuery? = nil, videos: [Video] = [Video]()) { init(_ query: SearchQuery? = nil, videos: [Video] = []) {
self.query = query self.query = query
self.videos = videos self.videos = videos
} }
var body: some View { var body: some View {
PlayerControlsView { PlayerControlsView {
VStack { #if os(iOS)
if showRecentQueries { VStack {
recentQueries SearchTextField()
} else {
#if os(tvOS)
ScrollView(.vertical, showsIndicators: false) {
HStack(spacing: 0) {
if accounts.app.supportsSearchFilters {
filtersHorizontalStack
}
if let favoriteItem = favoriteItem { if state.query.query != state.queryText, !state.queryText.isEmpty, !state.querySuggestions.collection.isEmpty {
FavoriteButton(item: favoriteItem) SearchSuggestions()
.id(favoriteItem.id) } else {
.labelStyle(.iconOnly) results
.font(.system(size: 25))
}
}
HorizontalCells(items: items)
}
.edgesIgnoringSafeArea(.horizontal)
#else
VerticalCells(items: items)
#endif
if noResults {
Text("No results")
if searchFiltersActive {
Button("Reset search filters", action: resetFilters)
}
Spacer()
} }
} }
} #else
ZStack {
results
if state.query.query != state.queryText, !state.queryText.isEmpty, !state.querySuggestions.collection.isEmpty {
HStack {
Spacer()
SearchSuggestions()
.borderLeading(width: 1, color: Color("ControlsBorderColor"))
.frame(maxWidth: 280)
}
}
}
#endif
} }
.toolbar { .toolbar {
#if !os(tvOS) #if !os(tvOS)
@ -118,6 +104,10 @@ struct SearchView: View {
if accounts.app.supportsSearchFilters { if accounts.app.supportsSearchFilters {
filtersMenu filtersMenu
} }
#if os(macOS)
SearchTextField()
#endif
} }
#endif #endif
} }
@ -132,10 +122,11 @@ struct SearchView: View {
state.store.replace(ContentItem.array(of: videos)) state.store.replace(ContentItem.array(of: videos))
} }
} }
.searchable(text: $state.queryText, placement: searchFieldPlacement) { .onChange(of: state.query.query) { newQuery in
ForEach(state.querySuggestions.collection, id: \.self) { suggestion in if newQuery.isEmpty {
Text(suggestion) favoriteItem = nil
.searchCompletion(suggestion) } else {
updateFavoriteItem()
} }
} }
.onChange(of: state.queryText) { newQuery in .onChange(of: state.queryText) { newQuery in
@ -161,11 +152,7 @@ struct SearchView: View {
} }
#endif #endif
} }
.onSubmit(of: .search) {
state.changeQuery { query in query.query = state.queryText }
recents.addQuery(state.queryText)
updateFavoriteItem()
}
.onChange(of: searchSortOrder) { order in .onChange(of: searchSortOrder) { order in
state.changeQuery { query in state.changeQuery { query in
query.sortBy = order query.sortBy = order
@ -185,19 +172,55 @@ struct SearchView: View {
} }
} }
#if !os(tvOS) #if !os(tvOS)
.ignoresSafeArea(.keyboard, edges: .bottom)
.navigationTitle("Search") .navigationTitle("Search")
#endif #endif
}
var searchFieldPlacement: SearchFieldPlacement {
#if os(iOS) #if os(iOS)
.navigationBarDrawer(displayMode: .always) .navigationBarHidden(true)
#else
.automatic
#endif #endif
} }
var toolbarPlacement: ToolbarItemPlacement { private var results: some View {
VStack {
if showRecentQueries {
recentQueries
} else {
#if os(tvOS)
ScrollView(.vertical, showsIndicators: false) {
HStack(spacing: 0) {
if accounts.app.supportsSearchFilters {
filtersHorizontalStack
}
if let favoriteItem = favoriteItem {
FavoriteButton(item: favoriteItem)
.id(favoriteItem.id)
.labelStyle(.iconOnly)
.font(.system(size: 25))
}
}
HorizontalCells(items: items)
}
.edgesIgnoringSafeArea(.horizontal)
#else
VerticalCells(items: items)
#endif
if noResults {
Text("No results")
if searchFiltersActive {
Button("Reset search filters", action: resetFilters)
}
Spacer()
}
}
}
}
private var toolbarPlacement: ToolbarItemPlacement {
#if os(iOS) #if os(iOS)
.bottomBar .bottomBar
#else #else
@ -205,25 +228,25 @@ struct SearchView: View {
#endif #endif
} }
fileprivate var showRecentQueries: Bool { private var showRecentQueries: Bool {
navigationStyle == .tab && state.queryText.isEmpty navigationStyle == .tab && state.queryText.isEmpty
} }
fileprivate var filtersActive: Bool { private var filtersActive: Bool {
searchDuration != .any || searchDate != .any searchDuration != .any || searchDate != .any
} }
fileprivate func resetFilters() { private func resetFilters() {
searchSortOrder = .relevance searchSortOrder = .relevance
searchDate = .any searchDate = .any
searchDuration = .any searchDuration = .any
} }
fileprivate var noResults: Bool { private var noResults: Bool {
items.isEmpty && !state.isLoading && !state.query.isEmpty items.isEmpty && !state.isLoading && !state.query.isEmpty
} }
var recentQueries: some View { private var recentQueries: some View {
VStack { VStack {
List { List {
Section(header: Text("Recents")) { Section(header: Text("Recents")) {
@ -237,22 +260,13 @@ struct SearchView: View {
state.changeQuery { query in query.query = item.title } state.changeQuery { query in query.query = item.title }
updateFavoriteItem() updateFavoriteItem()
} }
#if os(iOS)
.swipeActions(edge: .trailing) {
deleteButton(item)
}
#elseif os(tvOS)
.contextMenu { .contextMenu {
deleteButton(item) deleteButton(item)
deleteAllButton
} }
#endif
} }
} }
.redrawOn(change: recentsChanged) .redrawOn(change: recentsChanged)
if !recentItems.isEmpty {
clearAllButton
}
} }
} }
#if os(iOS) #if os(iOS)
@ -260,37 +274,33 @@ struct SearchView: View {
#endif #endif
} }
#if !os(macOS) private func deleteButton(_ item: RecentItem) -> some View {
func deleteButton(_ item: RecentItem) -> some View { Button {
Button(role: .destructive) { recents.close(item)
recents.close(item) recentsChanged.toggle()
recentsChanged.toggle() } label: {
} label: { Label("Delete", systemImage: "trash")
Label("Delete", systemImage: "trash")
}
}
#endif
var clearAllButton: some View {
Button("Clear All", role: .destructive) {
presentingClearConfirmation = true
}
.confirmationDialog("Clear All", isPresented: $presentingClearConfirmation) {
Button("Clear All", role: .destructive) {
recents.clearQueries()
}
} }
} }
var searchFiltersActive: Bool { private var deleteAllButton: some View {
Button {
recents.clearQueries()
recentsChanged.toggle()
} label: {
Label("Delete All", systemImage: "trash.fill")
}
}
private var searchFiltersActive: Bool {
searchDate != .any || searchDuration != .any searchDate != .any || searchDuration != .any
} }
var recentItems: [RecentItem] { private var recentItems: [RecentItem] {
Defaults[.recentlyOpened].filter { $0.type == .query }.reversed() Defaults[.recentlyOpened].filter { $0.type == .query }.reversed()
} }
var searchSortOrderPicker: some View { private var searchSortOrderPicker: some View {
Picker("Sort", selection: $searchSortOrder) { Picker("Sort", selection: $searchSortOrder) {
ForEach(SearchQuery.SortOrder.allCases) { sortOrder in ForEach(SearchQuery.SortOrder.allCases) { sortOrder in
Text(sortOrder.name).tag(sortOrder) Text(sortOrder.name).tag(sortOrder)
@ -299,7 +309,7 @@ struct SearchView: View {
} }
#if os(tvOS) #if os(tvOS)
var searchSortOrderButton: some View { private var searchSortOrderButton: some View {
Button(action: { self.searchSortOrder = self.searchSortOrder.next() }) { Text(self.searchSortOrder.name) Button(action: { self.searchSortOrder = self.searchSortOrder.next() }) { Text(self.searchSortOrder.name)
.font(.system(size: 30)) .font(.system(size: 30))
.padding(.horizontal) .padding(.horizontal)
@ -315,7 +325,7 @@ struct SearchView: View {
} }
} }
var searchDateButton: some View { private var searchDateButton: some View {
Button(action: { self.searchDate = self.searchDate.next() }) { Button(action: { self.searchDate = self.searchDate.next() }) {
Text(self.searchDate.name) Text(self.searchDate.name)
.font(.system(size: 30)) .font(.system(size: 30))
@ -332,7 +342,7 @@ struct SearchView: View {
} }
} }
var searchDurationButton: some View { private var searchDurationButton: some View {
Button(action: { self.searchDuration = self.searchDuration.next() }) { Button(action: { self.searchDuration = self.searchDuration.next() }) {
Text(self.searchDuration.name) Text(self.searchDuration.name)
.font(.system(size: 30)) .font(.system(size: 30))
@ -349,7 +359,7 @@ struct SearchView: View {
} }
} }
var filtersHorizontalStack: some View { private var filtersHorizontalStack: some View {
HStack { HStack {
HStack(spacing: 30) { HStack(spacing: 30) {
Text("Sort") Text("Sort")
@ -375,7 +385,7 @@ struct SearchView: View {
.font(.system(size: 30)) .font(.system(size: 30))
} }
#else #else
var filtersMenu: some View { private var filtersMenu: some View {
Menu(filtersActive ? "Filter: active" : "Filter") { Menu(filtersActive ? "Filter: active" : "Filter") {
Picker(selection: $searchDuration, label: Text("Duration")) { Picker(selection: $searchDuration, label: Text("Duration")) {
ForEach(SearchQuery.Duration.allCases) { duration in ForEach(SearchQuery.Duration.allCases) { duration in

View File

@ -15,9 +15,7 @@ struct AccountForm: View {
@State private var validationError: String? @State private var validationError: String?
@State private var validationDebounce = Debounce() @State private var validationDebounce = Debounce()
@FocusState private var focused: Bool @Environment(\.presentationMode) private var presentationMode
@Environment(\.dismiss) private var dismiss
var body: some View { var body: some View {
VStack { VStack {
@ -32,7 +30,7 @@ struct AccountForm: View {
.padding(.vertical) .padding(.vertical)
#elseif os(tvOS) #elseif os(tvOS)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(.thickMaterial) .background(Color.tertiaryBackground)
#else #else
.frame(width: 400, height: 145) .frame(width: 400, height: 145)
#endif #endif
@ -46,7 +44,7 @@ struct AccountForm: View {
Spacer() Spacer()
Button("Cancel") { Button("Cancel") {
dismiss() presentationMode.wrappedValue.dismiss()
} }
#if !os(tvOS) #if !os(tvOS)
.keyboardShortcut(.cancelAction) .keyboardShortcut(.cancelAction)
@ -68,7 +66,6 @@ struct AccountForm: View {
formFields formFields
#endif #endif
} }
.onAppear(perform: initializeForm)
.onChange(of: username) { _ in validate() } .onChange(of: username) { _ in validate() }
.onChange(of: password) { _ in validate() } .onChange(of: password) { _ in validate() }
} }
@ -76,24 +73,23 @@ struct AccountForm: View {
var formFields: some View { var formFields: some View {
Group { Group {
if !instance.app.accountsUsePassword { if !instance.app.accountsUsePassword {
TextField("Name", text: $name, prompt: Text("Account Name (optional)")) TextField("Name", text: $name)
.focused($focused)
} }
TextField("Username", text: $username, prompt: usernamePrompt) TextField(usernamePrompt, text: $username)
if instance.app.accountsUsePassword { if instance.app.accountsUsePassword {
SecureField("Password", text: $password, prompt: Text("Password")) SecureField("Password", text: $password)
} }
} }
} }
var usernamePrompt: Text { var usernamePrompt: String {
switch instance.app { switch instance.app {
case .invidious: case .invidious:
return Text("SID Cookie") return "SID Cookie"
default: default:
return Text("Username") return "Username"
} }
} }
@ -121,10 +117,6 @@ struct AccountForm: View {
.padding(.horizontal) .padding(.horizontal)
} }
private func initializeForm() {
focused = true
}
private func validate() { private func validate() {
isValid = false isValid = false
validationDebounce.invalidate() validationDebounce.invalidate()
@ -151,7 +143,7 @@ struct AccountForm: View {
let account = AccountsModel.add(instance: instance, name: name, username: username, password: password) let account = AccountsModel.add(instance: instance, name: name, username: username, password: password)
selectedAccount?.wrappedValue = account selectedAccount?.wrappedValue = account
dismiss() presentationMode.wrappedValue.dismiss()
} }
private var validator: AccountValidator { private var validator: AccountValidator {

View File

@ -0,0 +1,31 @@
import SwiftUI
struct AccountsNavigationLink: View {
@EnvironmentObject<AccountsModel> private var accounts
var instance: Instance
var body: some View {
NavigationLink(instance.longDescription) {
InstanceSettings(instanceID: instance.id)
}
.buttonStyle(.plain)
.contextMenu {
removeInstanceButton(instance)
}
}
private func removeInstanceButton(_ instance: Instance) -> some View {
if #available(iOS 15.0, *) {
return Button("Remove", role: .destructive) { removeAction(instance) }
} else {
return Button("Remove") { removeAction(instance) }
}
}
private func removeAction(_ instance: Instance) {
if accounts.current?.instance == instance {
accounts.setCurrent(nil)
}
InstancesModel.remove(instance)
}
}

View File

@ -1,102 +0,0 @@
import SwiftUI
struct AccountsSettings: View {
let instanceID: Instance.ID?
@State private var accountsChanged = false
@State private var presentingAccountForm = false
@State private var frontendURL = ""
@EnvironmentObject<AccountsModel> private var model
@EnvironmentObject<InstancesModel> private var instances
var instance: Instance! {
InstancesModel.find(instanceID)
}
var body: some View {
List {
if instance.app.hasFrontendURL {
Section(header: Text("Frontend URL")) {
TextField(
"Frontend URL",
text: $frontendURL,
prompt: Text("To enable videos, channels and playlists sharing")
)
.onAppear {
frontendURL = instance.frontendURL ?? ""
}
.onChange(of: frontendURL) { newValue in
InstancesModel.setFrontendURL(instance, newValue)
}
.labelsHidden()
.autocapitalization(.none)
.keyboardType(.URL)
}
}
Section(header: Text("Accounts"), footer: sectionFooter) {
if instance.app.supportsAccounts {
accounts
} else {
Text("Accounts are not supported for the application of this instance")
.foregroundColor(.secondary)
}
}
}
#if os(tvOS)
.frame(maxWidth: 1000)
#endif
.navigationTitle(instance.description)
}
var accounts: some View {
Group {
ForEach(InstancesModel.accounts(instanceID), id: \.self) { account in
#if os(tvOS)
Button(account.description) {}
.contextMenu {
Button("Remove", role: .destructive) { removeAccount(account) }
Button("Cancel", role: .cancel) {}
}
#else
Text(account.description)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button("Remove", role: .destructive) { removeAccount(account) }
}
#endif
}
.redrawOn(change: accountsChanged)
Button("Add account...") {
presentingAccountForm = true
}
}
.sheet(isPresented: $presentingAccountForm, onDismiss: { accountsChanged.toggle() }) {
AccountForm(instance: instance)
}
#if !os(tvOS)
.listStyle(.insetGrouped)
#endif
}
private var sectionFooter: some View {
if !instance.app.supportsAccounts {
return Text("")
}
#if os(iOS)
return Text("Swipe to remove account")
#else
return Text("Tap and hold to remove account")
.foregroundColor(.secondary)
#endif
}
private func removeAccount(_ account: Account) {
AccountsModel.remove(account)
accountsChanged.toggle()
}
}

View File

@ -13,9 +13,7 @@ struct InstanceForm: View {
@State private var validationError: String? @State private var validationError: String?
@State private var validationDebounce = Debounce() @State private var validationDebounce = Debounce()
@FocusState private var nameFieldFocused: Bool @Environment(\.presentationMode) private var presentationMode
@Environment(\.dismiss) private var dismiss
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
@ -30,12 +28,11 @@ struct InstanceForm: View {
} }
.onChange(of: app) { _ in validate() } .onChange(of: app) { _ in validate() }
.onChange(of: url) { _ in validate() } .onChange(of: url) { _ in validate() }
.onAppear(perform: initializeForm)
#if os(iOS) #if os(iOS)
.padding(.vertical) .padding(.vertical)
#elseif os(tvOS) #elseif os(tvOS)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(.thickMaterial) .background(Color.tertiaryBackground)
#else #else
.frame(width: 400, height: 190) .frame(width: 400, height: 190)
#endif #endif
@ -49,7 +46,7 @@ struct InstanceForm: View {
Spacer() Spacer()
Button("Cancel") { Button("Cancel") {
dismiss() presentationMode.wrappedValue.dismiss()
} }
#if !os(tvOS) #if !os(tvOS)
.keyboardShortcut(.cancelAction) .keyboardShortcut(.cancelAction)
@ -80,10 +77,9 @@ struct InstanceForm: View {
} }
.pickerStyle(.segmented) .pickerStyle(.segmented)
TextField("Name", text: $name, prompt: Text("Instance Name (optional)")) TextField("Name", text: $name)
.focused($nameFieldFocused)
TextField("API URL", text: $url, prompt: Text("https://invidious.home.net")) TextField("API URL", text: $url)
#if !os(macOS) #if !os(macOS)
.autocapitalization(.none) .autocapitalization(.none)
@ -138,10 +134,6 @@ struct InstanceForm: View {
} }
} }
func initializeForm() {
nameFieldFocused = true
}
func submitForm() { func submitForm() {
guard isValid else { guard isValid else {
return return
@ -149,7 +141,7 @@ struct InstanceForm: View {
savedInstanceID = InstancesModel.add(app: app, name: name, url: url).id savedInstanceID = InstancesModel.add(app: app, name: name, url: url).id
dismiss() presentationMode.wrappedValue.dismiss()
} }
} }

View File

@ -0,0 +1,104 @@
import SwiftUI
struct InstanceSettings: View {
let instanceID: Instance.ID?
@State private var accountsChanged = false
@State private var presentingAccountForm = false
@State private var frontendURL = ""
@EnvironmentObject<AccountsModel> private var model
@EnvironmentObject<InstancesModel> private var instances
var instance: Instance! {
InstancesModel.find(instanceID)
}
var body: some View {
List {
if instance.app.hasFrontendURL {
Section(header: Text("Frontend URL")) {
TextField(
"Frontend URL",
text: $frontendURL
)
.onAppear {
frontendURL = instance.frontendURL ?? ""
}
.onChange(of: frontendURL) { newValue in
InstancesModel.setFrontendURL(instance, newValue)
}
.labelsHidden()
.autocapitalization(.none)
.keyboardType(.URL)
}
}
Section(header: Text("Accounts"), footer: sectionFooter) {
if instance.app.supportsAccounts {
ForEach(InstancesModel.accounts(instanceID), id: \.self) { account in
#if os(tvOS)
Button(account.description) {}
.contextMenu {
Button("Remove") { removeAccount(account) }
Button("Cancel", role: .cancel) {}
}
#else
ZStack {
NavigationLink(destination: EmptyView()) {
EmptyView()
}
.disabled(true)
.hidden()
HStack {
Text(account.description)
Spacer()
}
.contextMenu {
Button("Remove") { removeAccount(account) }
}
}
#endif
}
.redrawOn(change: accountsChanged)
Button("Add account...") {
presentingAccountForm = true
}
.sheet(isPresented: $presentingAccountForm, onDismiss: { accountsChanged.toggle() }) {
AccountForm(instance: instance)
}
#if !os(tvOS)
.listStyle(.insetGrouped)
#endif
} else {
Text("Accounts are not supported for the application of this instance")
.foregroundColor(.secondary)
}
}
}
#if os(tvOS)
.frame(maxWidth: 1000)
#elseif os(iOS)
.listStyle(.insetGrouped)
#endif
.navigationTitle(instance.description)
}
private var sectionFooter: some View {
if !instance.app.supportsAccounts {
return Text("")
}
return Text("Tap and hold to remove account")
.foregroundColor(.secondary)
}
private func removeAccount(_ account: Account) {
AccountsModel.remove(account)
accountsChanged.toggle()
}
}

View File

@ -1,70 +0,0 @@
import Defaults
import SwiftUI
struct InstancesSettings: View {
@Default(.instances) private var instances
@EnvironmentObject<AccountsModel> private var accounts
@State private var selectedInstanceID: Instance.ID?
@State private var selectedAccount: Account?
@State private var presentingInstanceForm = false
@State private var savedFormInstanceID: Instance.ID?
var body: some View {
Group {
Section(header: SettingsHeader(text: "Instances")) {
ForEach(instances) { instance in
Group {
NavigationLink(instance.longDescription) {
AccountsSettings(instanceID: instance.id)
}
}
#if os(iOS)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
removeInstanceButton(instance)
}
.buttonStyle(.plain)
#else
.contextMenu {
removeInstanceButton(instance)
}
#endif
}
addInstanceButton
}
#if os(iOS)
.listStyle(.insetGrouped)
#endif
}
.sheet(isPresented: $presentingInstanceForm) {
InstanceForm(savedInstanceID: $savedFormInstanceID)
}
}
private var addInstanceButton: some View {
Button("Add Instance...") {
presentingInstanceForm = true
}
}
private func removeInstanceButton(_ instance: Instance) -> some View {
Button("Remove", role: .destructive) {
if accounts.current?.instance == instance {
accounts.setCurrent(nil)
}
InstancesModel.remove(instance)
}
}
}
struct InstancesSettingsView_Previews: PreviewProvider {
static var previews: some View {
VStack {
InstancesSettings()
}
.frame(width: 400, height: 270)
}
}

View File

@ -9,8 +9,7 @@ struct ServicesSettings: View {
Section(header: SettingsHeader(text: "SponsorBlock API")) { Section(header: SettingsHeader(text: "SponsorBlock API")) {
TextField( TextField(
"SponsorBlock API Instance", "SponsorBlock API Instance",
text: $sponsorBlockInstance, text: $sponsorBlockInstance
prompt: Text("SponsorBlock API URL, leave blank to disable")
) )
.labelsHidden() .labelsHidden()
#if !os(macOS) #if !os(macOS)
@ -21,7 +20,7 @@ struct ServicesSettings: View {
Section(header: SettingsHeader(text: "Categories to Skip")) { Section(header: SettingsHeader(text: "Categories to Skip")) {
#if os(macOS) #if os(macOS)
List(SponsorBlockAPI.categories, id: \.self) { category in let list = List(SponsorBlockAPI.categories, id: \.self) { category in
SponsorBlockCategorySelectionRow( SponsorBlockCategorySelectionRow(
title: SponsorBlockAPI.categoryDescription(category) ?? "Unknown", title: SponsorBlockAPI.categoryDescription(category) ?? "Unknown",
selected: sponsorBlockCategories.contains(category) selected: sponsorBlockCategories.contains(category)
@ -29,7 +28,16 @@ struct ServicesSettings: View {
toggleCategory(category, value: value) toggleCategory(category, value: value)
} }
} }
.listStyle(.inset(alternatesRowBackgrounds: true))
Group {
if #available(macOS 12.0, *) {
list
.listStyle(.inset(alternatesRowBackgrounds: true))
} else {
list
.listStyle(.inset)
}
}
Spacer() Spacer()
#else #else
ForEach(SponsorBlockAPI.categories, id: \.self) { category in ForEach(SponsorBlockAPI.categories, id: \.self) { category in

View File

@ -10,11 +10,16 @@ struct SettingsView: View {
#endif #endif
#if os(iOS) #if os(iOS)
@Environment(\.dismiss) private var dismiss @Environment(\.presentationMode) private var presentationMode
#endif #endif
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@State private var presentingInstanceForm = false
@State private var savedFormInstanceID: Instance.ID?
@Default(.instances) private var instances
var body: some View { var body: some View {
#if os(macOS) #if os(macOS)
TabView { TabView {
@ -65,8 +70,14 @@ struct SettingsView: View {
} }
} }
#endif #endif
InstancesSettings()
.environmentObject(accounts) Section(header: Text("Instances")) {
ForEach(instances) { instance in
AccountsNavigationLink(instance: instance)
}
addInstanceButton
}
BrowsingSettings() BrowsingSettings()
PlaybackSettings() PlaybackSettings()
ServicesSettings() ServicesSettings()
@ -76,7 +87,7 @@ struct SettingsView: View {
ToolbarItem(placement: .navigationBarTrailing) { ToolbarItem(placement: .navigationBarTrailing) {
#if !os(tvOS) #if !os(tvOS)
Button("Done") { Button("Done") {
dismiss() presentationMode.wrappedValue.dismiss()
} }
.keyboardShortcut(.cancelAction) .keyboardShortcut(.cancelAction)
#endif #endif
@ -87,11 +98,20 @@ struct SettingsView: View {
.listStyle(.insetGrouped) .listStyle(.insetGrouped)
#endif #endif
} }
.sheet(isPresented: $presentingInstanceForm) {
InstanceForm(savedInstanceID: $savedFormInstanceID)
}
#if os(tvOS) #if os(tvOS)
.background(.black) .background(Color.black)
#endif #endif
#endif #endif
} }
private var addInstanceButton: some View {
Button("Add Instance...") {
presentingInstanceForm = true
}
}
} }
struct SettingsView_Previews: PreviewProvider { struct SettingsView_Previews: PreviewProvider {

View File

@ -9,51 +9,34 @@ struct TrendingCountry: View {
@State private var query: String = "" @State private var query: String = ""
@State private var selection: Country? @State private var selection: Country?
@FocusState var countryIsFocused @Environment(\.presentationMode) private var presentationMode
@Environment(\.dismiss) private var dismiss
var body: some View { var body: some View {
VStack { VStack {
#if os(macOS) #if !os(tvOS)
HStack { HStack {
TextField("Country", text: $query, prompt: Text(TrendingCountry.prompt)) if #available(iOS 15.0, macOS 12.0, *) {
.focused($countryIsFocused) TextField("Country", text: $query, prompt: Text(TrendingCountry.prompt))
} else {
TextField(TrendingCountry.prompt, text: $query)
}
Button("Done") { selectCountryAndDismiss() } Button("Done") { selectCountryAndDismiss() }
.keyboardShortcut(.defaultAction) .keyboardShortcut(.defaultAction)
.keyboardShortcut(.cancelAction) .keyboardShortcut(.cancelAction)
} }
.padding([.horizontal, .top]) .padding([.horizontal, .top])
countriesList
#else
NavigationView {
countriesList
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Button("Done") { selectCountryAndDismiss() }
}
}
#if os(iOS)
.navigationBarTitle("Trending Country", displayMode: .automatic)
#endif
}
#endif #endif
countriesList
} }
.onAppear {
countryIsFocused = true
}
.onSubmit { selectCountryAndDismiss() }
#if !os(macOS)
.searchable(text: $query, placement: searchPlacement, prompt: Text(TrendingCountry.prompt))
#endif
#if os(tvOS) #if os(tvOS)
.background(.thinMaterial) .searchable(text: $query, placement: .automatic, prompt: Text(TrendingCountry.prompt))
.background(Color.black)
#endif #endif
} }
var countriesList: some View { var countriesList: some View {
ScrollViewReader { _ in let list = ScrollViewReader { _ in
List(store.collection, selection: $selection) { country in List(store.collection, selection: $selection) { country in
#if os(macOS) #if os(macOS)
Text(country.name) Text(country.name)
@ -71,29 +54,29 @@ struct TrendingCountry: View {
} }
} }
#if os(macOS) return Group {
.listStyle(.inset(alternatesRowBackgrounds: true)) #if os(macOS)
.padding(.bottom, 5) if #available(macOS 12.0, *) {
list
#endif .listStyle(.inset(alternatesRowBackgrounds: true))
} } else {
list
#if !os(macOS) }
var searchPlacement: SearchFieldPlacement {
#if os(iOS)
.navigationBarDrawer(displayMode: .always)
#else #else
.automatic list
#endif #endif
} }
#endif #if os(macOS)
.padding(.bottom, 5)
#endif
}
func selectCountryAndDismiss(_ country: Country? = nil) { func selectCountryAndDismiss(_ country: Country? = nil) {
if let selected = country ?? selection { if let selected = country ?? selection {
selectedCountry = selected selectedCountry = selected
} }
dismiss() presentationMode.wrappedValue.dismiss()
} }
} }

View File

@ -172,11 +172,12 @@ struct TrendingView: View {
} }
#else #else
Picker("Category", selection: $category) { Picker(category.controlLabel, selection: $category) {
ForEach(TrendingCategory.allCases) { category in ForEach(TrendingCategory.allCases) { category in
Text(category.controlLabel).tag(category) Text(category.controlLabel).tag(category)
} }
} }
.pickerStyle(.menu)
#endif #endif
} }

View File

@ -19,7 +19,7 @@ struct VerticalCells: View {
} }
.edgesIgnoringSafeArea(.horizontal) .edgesIgnoringSafeArea(.horizontal)
#if os(macOS) #if os(macOS)
.background() .background(Color.tertiaryBackground)
.frame(minWidth: 360) .frame(minWidth: 360)
#endif #endif
} }

View File

@ -12,6 +12,7 @@ struct VideoCell: View {
@Environment(\.horizontalCells) private var horizontalCells @Environment(\.horizontalCells) private var horizontalCells
#endif #endif
@EnvironmentObject<AccountsModel> private var accounts
@EnvironmentObject<PlayerModel> private var player @EnvironmentObject<PlayerModel> private var player
@EnvironmentObject<ThumbnailsModel> private var thumbnails @EnvironmentObject<ThumbnailsModel> private var thumbnails
@ -38,7 +39,13 @@ struct VideoCell: View {
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.contentShape(RoundedRectangle(cornerRadius: 12)) .contentShape(RoundedRectangle(cornerRadius: 12))
.contextMenu { VideoContextMenuView(video: video, playerNavigationLinkActive: $player.playerNavigationLinkActive) } .contextMenu {
VideoContextMenuView(
video: video,
playerNavigationLinkActive: $player.playerNavigationLinkActive
)
.environmentObject(accounts)
}
} }
var content: some View { var content: some View {
@ -55,7 +62,7 @@ struct VideoCell: View {
#endif #endif
} }
#if os(macOS) #if os(macOS)
.background() .background(Color.tertiaryBackground)
#endif #endif
} }

View File

@ -83,7 +83,7 @@ struct ChannelPlaylistView: View {
.navigationTitle(playlist.title) .navigationTitle(playlist.title)
#else #else
.background(.thickMaterial) .background(Color.tertiaryBackground)
#endif #endif
} }

View File

@ -9,7 +9,7 @@ struct ChannelVideosView: View {
@StateObject private var store = Store<Channel>() @StateObject private var store = Store<Channel>()
@Environment(\.dismiss) private var dismiss @Environment(\.presentationMode) private var presentationMode
@Environment(\.inNavigationView) private var inNavigationView @Environment(\.inNavigationView) private var inNavigationView
#if os(iOS) #if os(iOS)
@ -43,7 +43,7 @@ struct ChannelVideosView: View {
} }
var content: some View { var content: some View {
VStack { let content = VStack {
#if os(tvOS) #if os(tvOS)
HStack { HStack {
Text(navigationTitle) Text(navigationTitle)
@ -65,40 +65,43 @@ struct ChannelVideosView: View {
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
#endif #endif
VerticalCells(items: videos) #if os(iOS)
VerticalCells(items: videos)
#if !os(iOS) #else
.prefersDefaultFocus(in: focusNamespace) if #available(macOS 12.0, *) {
VerticalCells(items: videos)
.prefersDefaultFocus(in: focusNamespace)
} else {
VerticalCells(items: videos)
}
#endif #endif
} }
.environment(\.inChannelView, true) .environment(\.inChannelView, true)
#if !os(iOS)
.focusScope(focusNamespace)
#endif
#if !os(tvOS) #if !os(tvOS)
.toolbar { .toolbar {
ToolbarItem(placement: .navigation) { ToolbarItem(placement: .navigation) {
ShareButton( ShareButton(
contentItem: contentItem, contentItem: contentItem,
presentingShareSheet: $presentingShareSheet, presentingShareSheet: $presentingShareSheet,
shareURL: $shareURL shareURL: $shareURL
) )
} }
ToolbarItem { ToolbarItem {
HStack { HStack {
Text("**\(store.item?.subscriptionsString ?? "loading")** subscribers") Text("**\(store.item?.subscriptionsString ?? "loading")** subscribers")
.foregroundColor(.secondary) .foregroundColor(.secondary)
.opacity(store.item?.subscriptionsString != nil ? 1 : 0) .opacity(store.item?.subscriptionsString != nil ? 1 : 0)
subscriptionToggleButton subscriptionToggleButton
FavoriteButton(item: FavoriteItem(section: .channel(channel.id, channel.name))) FavoriteButton(item: FavoriteItem(section: .channel(channel.id, channel.name)))
}
} }
} }
}
#else #else
.background(.thickMaterial) .background(Color.tertiaryBackground)
#endif #endif
#if os(iOS) #if os(iOS)
.sheet(isPresented: $presentingShareSheet) { .sheet(isPresented: $presentingShareSheet) {
@ -107,7 +110,6 @@ struct ChannelVideosView: View {
} }
} }
#endif #endif
.modifier(UnsubscribeAlertModifier())
.onAppear { .onAppear {
if store.item.isNil { if store.item.isNil {
resource.addObserver(store) resource.addObserver(store)
@ -115,6 +117,17 @@ struct ChannelVideosView: View {
} }
} }
.navigationTitle(navigationTitle) .navigationTitle(navigationTitle)
return Group {
if #available(macOS 12.0, *) {
content
#if !os(iOS)
.focusScope(focusNamespace)
#endif
} else {
content
}
}
} }
private var resource: Resource { private var resource: Resource {

View File

@ -26,8 +26,13 @@ struct DetailBadge: View {
struct DefaultStyleModifier: ViewModifier { struct DefaultStyleModifier: ViewModifier {
func body(content: Content) -> some View { func body(content: Content) -> some View {
content if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
.background(.thinMaterial) content
.background(.thinMaterial)
} else {
content
.background(Color.background)
}
} }
} }

View File

@ -1,15 +1,15 @@
import SwiftUI import SwiftUI
struct OpenSettingsButton: View { struct OpenSettingsButton: View {
@Environment(\.dismiss) private var dismiss @Environment(\.presentationMode) private var presentationMode
#if !os(macOS) #if !os(macOS)
@EnvironmentObject<NavigationModel> private var navigation @EnvironmentObject<NavigationModel> private var navigation
#endif #endif
var body: some View { var body: some View {
Button { let button = Button {
dismiss() presentationMode.wrappedValue.dismiss()
#if os(macOS) #if os(macOS)
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil) NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
@ -19,7 +19,13 @@ struct OpenSettingsButton: View {
} label: { } label: {
Label("Open Settings", systemImage: "gearshape.2") Label("Open Settings", systemImage: "gearshape.2")
} }
.buttonStyle(.borderedProminent)
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
button
.buttonStyle(.borderedProminent)
} else {
button
}
} }
} }

View File

@ -25,7 +25,7 @@ struct PlayerControlsView<Content: View>: View {
} }
private var controls: some View { private var controls: some View {
HStack { let controls = HStack {
Button(action: { Button(action: {
model.presentingPlayer.toggle() model.presentingPlayer.toggle()
}) { }) {
@ -92,14 +92,23 @@ struct PlayerControlsView<Content: View>: View {
.padding(.horizontal) .padding(.horizontal)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 55) .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 55)
.padding(.vertical, 0) .padding(.vertical, 0)
.background(.ultraThinMaterial) .borderTop(height: 0.4, color: Color("ControlsBorderColor"))
.borderTop(height: 0.4, color: Color("PlayerControlsBorderColor")) .borderBottom(height: navigationStyle == .sidebar ? 0 : 0.4, color: Color("ControlsBorderColor"))
.borderBottom(height: navigationStyle == .sidebar ? 0 : 0.4, color: Color("PlayerControlsBorderColor"))
#if !os(tvOS) #if !os(tvOS)
.onSwipeGesture(up: { .onSwipeGesture(up: {
model.presentingPlayer = true model.presentingPlayer = true
}) })
#endif #endif
return Group {
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
controls
.background(Material.ultraThinMaterial)
} else {
controls
.background(Color.tertiaryBackground)
}
}
} }
private var appVersion: String { private var appVersion: String {

View File

@ -31,9 +31,6 @@ struct SubscriptionsView: View {
FavoriteButton(item: FavoriteItem(section: .subscriptions)) FavoriteButton(item: FavoriteItem(section: .subscriptions))
} }
} }
.refreshable {
loadResources(force: true)
}
} }
fileprivate func loadResources(force: Bool = false) { fileprivate func loadResources(force: Bool = false) {

View File

@ -113,7 +113,7 @@ struct VideoContextMenuView: View {
private var subscriptionButton: some View { private var subscriptionButton: some View {
Group { Group {
if subscriptions.isSubscribing(video.channel.id) { if subscriptions.isSubscribing(video.channel.id) {
Button(role: .destructive) { Button {
#if os(tvOS) #if os(tvOS)
subscriptions.unsubscribe(video.channel.id) subscriptions.unsubscribe(video.channel.id)
#else #else
@ -143,7 +143,7 @@ struct VideoContextMenuView: View {
} }
func removeFromPlaylistButton(playlistID: String) -> some View { func removeFromPlaylistButton(playlistID: String) -> some View {
Button(role: .destructive) { Button {
playlists.removeVideo(videoIndexID: video.indexID!, playlistID: playlistID) playlists.removeVideo(videoIndexID: video.indexID!, playlistID: playlistID)
} label: { } label: {
Label("Remove from playlist", systemImage: "text.badge.minus") Label("Remove from playlist", systemImage: "text.badge.minus")

View File

@ -2,14 +2,14 @@ import Defaults
import SwiftUI import SwiftUI
struct WelcomeScreen: View { struct WelcomeScreen: View {
@Environment(\.dismiss) private var dismiss @Environment(\.presentationMode) private var presentationMode
@EnvironmentObject<AccountsModel> private var accounts @EnvironmentObject<AccountsModel> private var accounts
@Default(.accounts) private var allAccounts @Default(.accounts) private var allAccounts
var body: some View { var body: some View {
VStack { let welcomeScreen = VStack {
Spacer() Spacer()
Text("Welcome") Text("Welcome")
@ -26,7 +26,7 @@ struct WelcomeScreen: View {
AccountSelectionView(showHeader: false) AccountSelectionView(showHeader: false)
Button { Button {
dismiss() presentationMode.wrappedValue.dismiss()
} label: { } label: {
Text("Start") Text("Start")
} }
@ -36,7 +36,7 @@ struct WelcomeScreen: View {
#else #else
AccountsMenuView() AccountsMenuView()
.onChange(of: accounts.current) { _ in .onChange(of: accounts.current) { _ in
dismiss() presentationMode.wrappedValue.dismiss()
} }
#if os(macOS) #if os(macOS)
.frame(maxWidth: 280) .frame(maxWidth: 280)
@ -50,10 +50,16 @@ struct WelcomeScreen: View {
Spacer() Spacer()
} }
.interactiveDismissDisabled()
#if os(macOS) #if os(macOS)
.frame(minWidth: 400, minHeight: 400) .frame(minWidth: 400, minHeight: 400)
#endif #endif
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) {
welcomeScreen
.interactiveDismissDisabled()
} else {
welcomeScreen
}
} }
} }

View File

@ -77,6 +77,8 @@
371F2F1A269B43D300E4A7AB /* NavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationModel.swift */; }; 371F2F1A269B43D300E4A7AB /* NavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationModel.swift */; };
371F2F1B269B43D300E4A7AB /* NavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationModel.swift */; }; 371F2F1B269B43D300E4A7AB /* NavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationModel.swift */; };
371F2F1C269B43D300E4A7AB /* NavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationModel.swift */; }; 371F2F1C269B43D300E4A7AB /* NavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationModel.swift */; };
3722AEBC274DA396005EA4D6 /* Badge+Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3722AEBB274DA396005EA4D6 /* Badge+Backport.swift */; };
3722AEBE274DA401005EA4D6 /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3722AEBD274DA401005EA4D6 /* Backport.swift */; };
3729037E2739E47400EA99F6 /* MenuCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3729037D2739E47400EA99F6 /* MenuCommands.swift */; }; 3729037E2739E47400EA99F6 /* MenuCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3729037D2739E47400EA99F6 /* MenuCommands.swift */; };
3729037F2739E47400EA99F6 /* MenuCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3729037D2739E47400EA99F6 /* MenuCommands.swift */; }; 3729037F2739E47400EA99F6 /* MenuCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3729037D2739E47400EA99F6 /* MenuCommands.swift */; };
372915E42687E33E00F5A35B /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 372915E32687E33E00F5A35B /* Defaults */; }; 372915E42687E33E00F5A35B /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 372915E32687E33E00F5A35B /* Defaults */; };
@ -110,6 +112,8 @@
3743CA52270F284F00E4D32B /* View+Borders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743CA51270F284F00E4D32B /* View+Borders.swift */; }; 3743CA52270F284F00E4D32B /* View+Borders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743CA51270F284F00E4D32B /* View+Borders.swift */; };
3743CA53270F284F00E4D32B /* View+Borders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743CA51270F284F00E4D32B /* View+Borders.swift */; }; 3743CA53270F284F00E4D32B /* View+Borders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743CA51270F284F00E4D32B /* View+Borders.swift */; };
3743CA54270F284F00E4D32B /* View+Borders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743CA51270F284F00E4D32B /* View+Borders.swift */; }; 3743CA54270F284F00E4D32B /* View+Borders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743CA51270F284F00E4D32B /* View+Borders.swift */; };
374710052755291C00CE0F87 /* SearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374710042755291C00CE0F87 /* SearchField.swift */; };
374710062755291C00CE0F87 /* SearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374710042755291C00CE0F87 /* SearchField.swift */; };
3748186626A7627F0084E870 /* Video+Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186526A7627F0084E870 /* Video+Fixtures.swift */; }; 3748186626A7627F0084E870 /* Video+Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186526A7627F0084E870 /* Video+Fixtures.swift */; };
3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186526A7627F0084E870 /* Video+Fixtures.swift */; }; 3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186526A7627F0084E870 /* Video+Fixtures.swift */; };
3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186526A7627F0084E870 /* Video+Fixtures.swift */; }; 3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3748186526A7627F0084E870 /* Video+Fixtures.swift */; };
@ -122,16 +126,14 @@
37484C1926FC837400287258 /* PlaybackSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C1826FC837400287258 /* PlaybackSettings.swift */; }; 37484C1926FC837400287258 /* PlaybackSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C1826FC837400287258 /* PlaybackSettings.swift */; };
37484C1A26FC837400287258 /* PlaybackSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C1826FC837400287258 /* PlaybackSettings.swift */; }; 37484C1A26FC837400287258 /* PlaybackSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C1826FC837400287258 /* PlaybackSettings.swift */; };
37484C1B26FC837400287258 /* PlaybackSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C1826FC837400287258 /* PlaybackSettings.swift */; }; 37484C1B26FC837400287258 /* PlaybackSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C1826FC837400287258 /* PlaybackSettings.swift */; };
37484C1D26FC83A400287258 /* InstancesSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C1C26FC83A400287258 /* InstancesSettings.swift */; };
37484C1F26FC83A400287258 /* InstancesSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C1C26FC83A400287258 /* InstancesSettings.swift */; };
37484C2526FC83E000287258 /* InstanceForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2426FC83E000287258 /* InstanceForm.swift */; }; 37484C2526FC83E000287258 /* InstanceForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2426FC83E000287258 /* InstanceForm.swift */; };
37484C2626FC83E000287258 /* InstanceForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2426FC83E000287258 /* InstanceForm.swift */; }; 37484C2626FC83E000287258 /* InstanceForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2426FC83E000287258 /* InstanceForm.swift */; };
37484C2726FC83E000287258 /* InstanceForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2426FC83E000287258 /* InstanceForm.swift */; }; 37484C2726FC83E000287258 /* InstanceForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2426FC83E000287258 /* InstanceForm.swift */; };
37484C2926FC83FF00287258 /* AccountForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2826FC83FF00287258 /* AccountForm.swift */; }; 37484C2926FC83FF00287258 /* AccountForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2826FC83FF00287258 /* AccountForm.swift */; };
37484C2A26FC83FF00287258 /* AccountForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2826FC83FF00287258 /* AccountForm.swift */; }; 37484C2A26FC83FF00287258 /* AccountForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2826FC83FF00287258 /* AccountForm.swift */; };
37484C2B26FC83FF00287258 /* AccountForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2826FC83FF00287258 /* AccountForm.swift */; }; 37484C2B26FC83FF00287258 /* AccountForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2826FC83FF00287258 /* AccountForm.swift */; };
37484C2D26FC844700287258 /* AccountsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2C26FC844700287258 /* AccountsSettings.swift */; }; 37484C2D26FC844700287258 /* InstanceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2C26FC844700287258 /* InstanceSettings.swift */; };
37484C2F26FC844700287258 /* AccountsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2C26FC844700287258 /* AccountsSettings.swift */; }; 37484C2F26FC844700287258 /* InstanceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C2C26FC844700287258 /* InstanceSettings.swift */; };
37484C3126FCB8F900287258 /* AccountValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C3026FCB8F900287258 /* AccountValidator.swift */; }; 37484C3126FCB8F900287258 /* AccountValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C3026FCB8F900287258 /* AccountValidator.swift */; };
37484C3226FCB8F900287258 /* AccountValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C3026FCB8F900287258 /* AccountValidator.swift */; }; 37484C3226FCB8F900287258 /* AccountValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C3026FCB8F900287258 /* AccountValidator.swift */; };
37484C3326FCB8F900287258 /* AccountValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C3026FCB8F900287258 /* AccountValidator.swift */; }; 37484C3326FCB8F900287258 /* AccountValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37484C3026FCB8F900287258 /* AccountValidator.swift */; };
@ -164,9 +166,6 @@
3761ABFD26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; }; 3761ABFD26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; };
3761ABFE26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; }; 3761ABFE26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; };
3761ABFF26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; }; 3761ABFF26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; };
3761AC0F26F0F9A600AA496F /* UnsubscribeAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761AC0E26F0F9A600AA496F /* UnsubscribeAlertModifier.swift */; };
3761AC1026F0F9A600AA496F /* UnsubscribeAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761AC0E26F0F9A600AA496F /* UnsubscribeAlertModifier.swift */; };
3761AC1126F0F9A600AA496F /* UnsubscribeAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761AC0E26F0F9A600AA496F /* UnsubscribeAlertModifier.swift */; };
3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; }; 3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; };
3763495226DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; }; 3763495226DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.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 */; };
@ -269,12 +268,29 @@
377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27F26737550007FC770 /* SearchView.swift */; }; 377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27F26737550007FC770 /* SearchView.swift */; };
377FC7ED267A0A0800A6BBAF /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7EC267A0A0800A6BBAF /* SwiftyJSON */; }; 377FC7ED267A0A0800A6BBAF /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7EC267A0A0800A6BBAF /* SwiftyJSON */; };
377FC7F3267A0A0800A6BBAF /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7F2267A0A0800A6BBAF /* Logging */; }; 377FC7F3267A0A0800A6BBAF /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7F2267A0A0800A6BBAF /* Logging */; };
3782B94F27553A6700990149 /* SearchSuggestions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782B94E27553A6700990149 /* SearchSuggestions.swift */; };
3782B95027553A6700990149 /* SearchSuggestions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782B94E27553A6700990149 /* SearchSuggestions.swift */; };
3782B9522755667600990149 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782B9512755667600990149 /* String+Format.swift */; };
3782B9532755667600990149 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782B9512755667600990149 /* String+Format.swift */; };
3782B9542755667600990149 /* String+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782B9512755667600990149 /* String+Format.swift */; };
3782B95627557E4E00990149 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27F26737550007FC770 /* SearchView.swift */; };
3782B95727557E6E00990149 /* SearchSuggestions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782B94E27553A6700990149 /* SearchSuggestions.swift */; };
3782B95E2755858100990149 /* NSTextField+FocusRingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782B95C2755858100990149 /* NSTextField+FocusRingType.swift */; };
3784B23B272894DA00B09468 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23A272894DA00B09468 /* ShareSheet.swift */; }; 3784B23B272894DA00B09468 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23A272894DA00B09468 /* ShareSheet.swift */; };
3784B23D2728B85300B09468 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23C2728B85300B09468 /* ShareButton.swift */; }; 3784B23D2728B85300B09468 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23C2728B85300B09468 /* ShareButton.swift */; };
3784B23E2728B85300B09468 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23C2728B85300B09468 /* ShareButton.swift */; }; 3784B23E2728B85300B09468 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23C2728B85300B09468 /* ShareButton.swift */; };
3788AC2726F6840700F6BAA9 /* FavoriteItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */; }; 3788AC2726F6840700F6BAA9 /* FavoriteItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */; };
3788AC2826F6840700F6BAA9 /* FavoriteItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */; }; 3788AC2826F6840700F6BAA9 /* FavoriteItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */; };
3788AC2926F6840700F6BAA9 /* FavoriteItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */; }; 3788AC2926F6840700F6BAA9 /* FavoriteItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */; };
378AE93A274EDFAF006A4EE1 /* Badge+Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3722AEBB274DA396005EA4D6 /* Badge+Backport.swift */; };
378AE93C274EDFB2006A4EE1 /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3722AEBD274DA401005EA4D6 /* Backport.swift */; };
378AE93D274EDFB3006A4EE1 /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3722AEBD274DA401005EA4D6 /* Backport.swift */; };
378AE93E274EDFB4006A4EE1 /* Tint+Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3722AEBF274DAEB8005EA4D6 /* Tint+Backport.swift */; };
378AE93F274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3722AEBF274DAEB8005EA4D6 /* Tint+Backport.swift */; };
378AE940274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3722AEBF274DAEB8005EA4D6 /* Tint+Backport.swift */; };
378AE943274EF00A006A4EE1 /* Color+Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378AE942274EF00A006A4EE1 /* Color+Background.swift */; };
378AE944274EF00A006A4EE1 /* Color+Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378AE942274EF00A006A4EE1 /* Color+Background.swift */; };
378AE945274EF00A006A4EE1 /* Color+Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378AE942274EF00A006A4EE1 /* Color+Background.swift */; };
378E50FB26FE8B9F00F49626 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378E50FA26FE8B9F00F49626 /* Instance.swift */; }; 378E50FB26FE8B9F00F49626 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378E50FA26FE8B9F00F49626 /* Instance.swift */; };
378E50FC26FE8B9F00F49626 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378E50FA26FE8B9F00F49626 /* Instance.swift */; }; 378E50FC26FE8B9F00F49626 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378E50FA26FE8B9F00F49626 /* Instance.swift */; };
378E50FD26FE8B9F00F49626 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378E50FA26FE8B9F00F49626 /* Instance.swift */; }; 378E50FD26FE8B9F00F49626 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378E50FA26FE8B9F00F49626 /* Instance.swift */; };
@ -294,8 +310,6 @@
37A3B15F27255E7F000FB5EE /* images in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B15E27255E7F000FB5EE /* images */; }; 37A3B15F27255E7F000FB5EE /* images in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B15E27255E7F000FB5EE /* images */; };
37A3B16127255E7F000FB5EE /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16027255E7F000FB5EE /* manifest.json */; }; 37A3B16127255E7F000FB5EE /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16027255E7F000FB5EE /* manifest.json */; };
37A3B16527255E7F000FB5EE /* content.js in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16427255E7F000FB5EE /* content.js */; }; 37A3B16527255E7F000FB5EE /* content.js in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16427255E7F000FB5EE /* content.js */; };
37A3B17027255E7F000FB5EE /* Open in Yattee (macOS).appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 37A3B15727255E7F000FB5EE /* Open in Yattee (macOS).appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
37A3B18F2725735F000FB5EE /* Open in Yattee (iOS).appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 37A3B1792725735F000FB5EE /* Open in Yattee (iOS).appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
37A3B194272574FB000FB5EE /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A3B15927255E7F000FB5EE /* SafariWebExtensionHandler.swift */; }; 37A3B194272574FB000FB5EE /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A3B15927255E7F000FB5EE /* SafariWebExtensionHandler.swift */; };
37A3B19627257503000FB5EE /* content.js in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16427255E7F000FB5EE /* content.js */; }; 37A3B19627257503000FB5EE /* content.js in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16427255E7F000FB5EE /* content.js */; };
37A3B1982725750B000FB5EE /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16027255E7F000FB5EE /* manifest.json */; }; 37A3B1982725750B000FB5EE /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16027255E7F000FB5EE /* manifest.json */; };
@ -307,7 +321,6 @@
37A9965F26D6F9B9006E3224 /* FavoritesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A9965D26D6F9B9006E3224 /* FavoritesView.swift */; }; 37A9965F26D6F9B9006E3224 /* FavoritesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A9965D26D6F9B9006E3224 /* FavoritesView.swift */; };
37A9966026D6F9B9006E3224 /* FavoritesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A9965D26D6F9B9006E3224 /* FavoritesView.swift */; }; 37A9966026D6F9B9006E3224 /* FavoritesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A9965D26D6F9B9006E3224 /* FavoritesView.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 */; };
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 */; };
@ -452,6 +465,8 @@
37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; }; 37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
37DD87C8271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; }; 37DD87C8271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; }; 37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; };
37E084AC2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; };
37E084AD2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */; };
37E2EEAB270656EC00170416 /* PlayerControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */; }; 37E2EEAB270656EC00170416 /* PlayerControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */; };
37E2EEAC270656EC00170416 /* PlayerControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */; }; 37E2EEAC270656EC00170416 /* PlayerControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */; };
37E2EEAD270656EC00170416 /* PlayerControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */; }; 37E2EEAD270656EC00170416 /* PlayerControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */; };
@ -510,20 +525,6 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
37A3B16E27255E7F000FB5EE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 37A3B15627255E7F000FB5EE;
remoteInfo = "Open in Yattee";
};
37A3B18D2725735F000FB5EE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 37A3B1782725735F000FB5EE;
remoteInfo = "Open in Yattee";
};
37D4B0D52671614900C925CA /* PBXContainerItemProxy */ = { 37D4B0D52671614900C925CA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 37D4B0BD2671614700C925CA /* Project object */; containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
@ -547,31 +548,6 @@
}; };
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
37A3B17127255E7F000FB5EE /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
37A3B17027255E7F000FB5EE /* Open in Yattee (macOS).appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
37A3B1932725735F000FB5EE /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
37A3B18F2725735F000FB5EE /* Open in Yattee (iOS).appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
3700155A271B0D4D0049C794 /* PipedAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PipedAPI.swift; sourceTree = "<group>"; }; 3700155A271B0D4D0049C794 /* PipedAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PipedAPI.swift; sourceTree = "<group>"; };
3700155E271B12DD0049C794 /* SiestaConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiestaConfiguration.swift; sourceTree = "<group>"; }; 3700155E271B12DD0049C794 /* SiestaConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiestaConfiguration.swift; sourceTree = "<group>"; };
@ -585,6 +561,9 @@
37169AA12729D98A0011DE61 /* InstancesBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesBridge.swift; sourceTree = "<group>"; }; 37169AA12729D98A0011DE61 /* InstancesBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesBridge.swift; sourceTree = "<group>"; };
37169AA52729E2CC0011DE61 /* AccountsBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsBridge.swift; sourceTree = "<group>"; }; 37169AA52729E2CC0011DE61 /* AccountsBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsBridge.swift; sourceTree = "<group>"; };
371F2F19269B43D300E4A7AB /* NavigationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModel.swift; sourceTree = "<group>"; }; 371F2F19269B43D300E4A7AB /* NavigationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModel.swift; sourceTree = "<group>"; };
3722AEBB274DA396005EA4D6 /* Badge+Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Badge+Backport.swift"; sourceTree = "<group>"; };
3722AEBD274DA401005EA4D6 /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
3722AEBF274DAEB8005EA4D6 /* Tint+Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tint+Backport.swift"; sourceTree = "<group>"; };
3729037D2739E47400EA99F6 /* MenuCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuCommands.swift; sourceTree = "<group>"; }; 3729037D2739E47400EA99F6 /* MenuCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuCommands.swift; sourceTree = "<group>"; };
372915E52687E3B900F5A35B /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = "<group>"; }; 372915E52687E3B900F5A35B /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = "<group>"; };
3730D89F2712E2B70020ED53 /* NowPlayingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingView.swift; sourceTree = "<group>"; }; 3730D89F2712E2B70020ED53 /* NowPlayingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingView.swift; sourceTree = "<group>"; };
@ -598,14 +577,14 @@
3743B86727216D3600261544 /* ChannelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelCell.swift; sourceTree = "<group>"; }; 3743B86727216D3600261544 /* ChannelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelCell.swift; sourceTree = "<group>"; };
3743CA4D270EFE3400E4D32B /* PlayerQueueRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueueRow.swift; sourceTree = "<group>"; }; 3743CA4D270EFE3400E4D32B /* PlayerQueueRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueueRow.swift; sourceTree = "<group>"; };
3743CA51270F284F00E4D32B /* View+Borders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Borders.swift"; sourceTree = "<group>"; }; 3743CA51270F284F00E4D32B /* View+Borders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Borders.swift"; sourceTree = "<group>"; };
374710042755291C00CE0F87 /* SearchField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchField.swift; sourceTree = "<group>"; };
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>"; };
37484C1826FC837400287258 /* PlaybackSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackSettings.swift; sourceTree = "<group>"; }; 37484C1826FC837400287258 /* PlaybackSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackSettings.swift; sourceTree = "<group>"; };
37484C1C26FC83A400287258 /* InstancesSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesSettings.swift; sourceTree = "<group>"; };
37484C2426FC83E000287258 /* InstanceForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceForm.swift; sourceTree = "<group>"; }; 37484C2426FC83E000287258 /* InstanceForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceForm.swift; sourceTree = "<group>"; };
37484C2826FC83FF00287258 /* AccountForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountForm.swift; sourceTree = "<group>"; }; 37484C2826FC83FF00287258 /* AccountForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountForm.swift; sourceTree = "<group>"; };
37484C2C26FC844700287258 /* AccountsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsSettings.swift; sourceTree = "<group>"; }; 37484C2C26FC844700287258 /* InstanceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceSettings.swift; sourceTree = "<group>"; };
37484C3026FCB8F900287258 /* AccountValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidator.swift; sourceTree = "<group>"; }; 37484C3026FCB8F900287258 /* AccountValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidator.swift; sourceTree = "<group>"; };
374C053427242D9F009BDDBE /* ServicesSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesSettings.swift; sourceTree = "<group>"; }; 374C053427242D9F009BDDBE /* ServicesSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesSettings.swift; sourceTree = "<group>"; };
374C053A2724614F009BDDBE /* PlayerTVMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerTVMenu.swift; sourceTree = "<group>"; }; 374C053A2724614F009BDDBE /* PlayerTVMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerTVMenu.swift; sourceTree = "<group>"; };
@ -618,7 +597,6 @@
37599F37272B4D740087F250 /* FavoriteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteButton.swift; sourceTree = "<group>"; }; 37599F37272B4D740087F250 /* FavoriteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteButton.swift; sourceTree = "<group>"; };
375DFB5726F9DA010013F468 /* InstancesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesModel.swift; sourceTree = "<group>"; }; 375DFB5726F9DA010013F468 /* InstancesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesModel.swift; sourceTree = "<group>"; };
3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; }; 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; };
3761AC0E26F0F9A600AA496F /* UnsubscribeAlertModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnsubscribeAlertModifier.swift; sourceTree = "<group>"; };
3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarRecents.swift; sourceTree = "<group>"; }; 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarRecents.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>"; };
@ -634,9 +612,13 @@
37732FF32703D32400F04329 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; }; 37732FF32703D32400F04329 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; };
3774122927387B6C00423605 /* InstancesModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesModelTests.swift; sourceTree = "<group>"; }; 3774122927387B6C00423605 /* InstancesModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesModelTests.swift; sourceTree = "<group>"; };
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedContentAccessors.swift; sourceTree = "<group>"; }; 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedContentAccessors.swift; sourceTree = "<group>"; };
3782B94E27553A6700990149 /* SearchSuggestions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSuggestions.swift; sourceTree = "<group>"; };
3782B9512755667600990149 /* String+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Format.swift"; sourceTree = "<group>"; };
3782B95C2755858100990149 /* NSTextField+FocusRingType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextField+FocusRingType.swift"; sourceTree = "<group>"; };
3784B23A272894DA00B09468 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; }; 3784B23A272894DA00B09468 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
3784B23C2728B85300B09468 /* ShareButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = "<group>"; }; 3784B23C2728B85300B09468 /* ShareButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = "<group>"; };
3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteItemView.swift; sourceTree = "<group>"; }; 3788AC2626F6840700F6BAA9 /* FavoriteItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteItemView.swift; sourceTree = "<group>"; };
378AE942274EF00A006A4EE1 /* Color+Background.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Background.swift"; sourceTree = "<group>"; };
378E50FA26FE8B9F00F49626 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = "<group>"; }; 378E50FA26FE8B9F00F49626 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = "<group>"; };
378E50FE26FE8EEE00F49626 /* AccountsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsMenuView.swift; sourceTree = "<group>"; }; 378E50FE26FE8EEE00F49626 /* AccountsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsMenuView.swift; sourceTree = "<group>"; };
37977582268922F600DD52A8 /* InvidiousAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvidiousAPI.swift; sourceTree = "<group>"; }; 37977582268922F600DD52A8 /* InvidiousAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvidiousAPI.swift; sourceTree = "<group>"; };
@ -710,7 +692,7 @@
37D4B0D82671614900C925CA /* Tests_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOS.swift; sourceTree = "<group>"; }; 37D4B0D82671614900C925CA /* Tests_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOS.swift; sourceTree = "<group>"; };
37D4B0DE2671614900C925CA /* Tests (macOS).xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests (macOS).xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 37D4B0DE2671614900C925CA /* Tests (macOS).xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests (macOS).xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
37D4B0E22671614900C925CA /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = "<group>"; }; 37D4B0E22671614900C925CA /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = "<group>"; };
37D4B158267164AE00C925CA /* Yattee (tvOS).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Yattee (tvOS).app"; sourceTree = BUILT_PRODUCTS_DIR; }; 37D4B158267164AE00C925CA /* Yattee.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Yattee.app; sourceTree = BUILT_PRODUCTS_DIR; };
37D4B15E267164AF00C925CA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 37D4B15E267164AF00C925CA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
37D4B171267164B000C925CA /* Tests (tvOS).xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests (tvOS).xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 37D4B171267164B000C925CA /* Tests (tvOS).xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests (tvOS).xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
37D4B175267164B000C925CA /* YatteeUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YatteeUITests.swift; sourceTree = "<group>"; }; 37D4B175267164B000C925CA /* YatteeUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YatteeUITests.swift; sourceTree = "<group>"; };
@ -721,6 +703,7 @@
37D526E22720B4BE00ED2F5E /* View+SwipeGesture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+SwipeGesture.swift"; sourceTree = "<group>"; }; 37D526E22720B4BE00ED2F5E /* View+SwipeGesture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+SwipeGesture.swift"; sourceTree = "<group>"; };
37D9169A27388A81002B1BAA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; 37D9169A27388A81002B1BAA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStreams.swift; sourceTree = "<group>"; }; 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStreams.swift; sourceTree = "<group>"; };
37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsNavigationLink.swift; sourceTree = "<group>"; };
37E2EEAA270656EC00170416 /* PlayerControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsView.swift; sourceTree = "<group>"; }; 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsView.swift; sourceTree = "<group>"; };
37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsModel.swift; sourceTree = "<group>"; }; 37E64DD026D597EB00C71877 /* SubscriptionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsModel.swift; sourceTree = "<group>"; };
37E70922271CD43000D34DDE /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; }; 37E70922271CD43000D34DDE /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
@ -909,7 +892,6 @@
37E2EEAA270656EC00170416 /* PlayerControlsView.swift */, 37E2EEAA270656EC00170416 /* PlayerControlsView.swift */,
37BA793A26DB8EE4002A0235 /* PlaylistVideosView.swift */, 37BA793A26DB8EE4002A0235 /* PlaylistVideosView.swift */,
37AAF27D26737323007FC770 /* PopularView.swift */, 37AAF27D26737323007FC770 /* PopularView.swift */,
37AAF27F26737550007FC770 /* SearchView.swift */,
3784B23C2728B85300B09468 /* ShareButton.swift */, 3784B23C2728B85300B09468 /* ShareButton.swift */,
376B2E0626F920D600B1D64D /* SignInRequiredView.swift */, 376B2E0626F920D600B1D64D /* SignInRequiredView.swift */,
37AAF29F26741C97007FC770 /* SubscriptionsView.swift */, 37AAF29F26741C97007FC770 /* SubscriptionsView.swift */,
@ -919,6 +901,16 @@
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3722AEBA274DA312005EA4D6 /* Backports */ = {
isa = PBXGroup;
children = (
3722AEBD274DA401005EA4D6 /* Backport.swift */,
3722AEBB274DA396005EA4D6 /* Badge+Backport.swift */,
3722AEBF274DAEB8005EA4D6 /* Tint+Backport.swift */,
);
path = Backports;
sourceTree = "<group>";
};
3743B864272169E200261544 /* Applications */ = { 3743B864272169E200261544 /* Applications */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -976,11 +968,11 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
37484C2826FC83FF00287258 /* AccountForm.swift */, 37484C2826FC83FF00287258 /* AccountForm.swift */,
37484C2C26FC844700287258 /* AccountsSettings.swift */, 37E084AB2753D95F00039B7D /* AccountsNavigationLink.swift */,
37732FEF2703A26300F04329 /* AccountValidationStatus.swift */, 37732FEF2703A26300F04329 /* AccountValidationStatus.swift */,
376BE50A27349108009AD608 /* BrowsingSettings.swift */, 376BE50A27349108009AD608 /* BrowsingSettings.swift */,
37484C2426FC83E000287258 /* InstanceForm.swift */, 37484C2426FC83E000287258 /* InstanceForm.swift */,
37484C1C26FC83A400287258 /* InstancesSettings.swift */, 37484C2C26FC844700287258 /* InstanceSettings.swift */,
37484C1826FC837400287258 /* PlaybackSettings.swift */, 37484C1826FC837400287258 /* PlaybackSettings.swift */,
374C053427242D9F009BDDBE /* ServicesSettings.swift */, 374C053427242D9F009BDDBE /* ServicesSettings.swift */,
376BE50627347B57009AD608 /* SettingsHeader.swift */, 376BE50627347B57009AD608 /* SettingsHeader.swift */,
@ -1002,7 +994,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
37F64FE326FE70A60081B69E /* RedrawOnModifier.swift */, 37F64FE326FE70A60081B69E /* RedrawOnModifier.swift */,
3761AC0E26F0F9A600AA496F /* UnsubscribeAlertModifier.swift */,
); );
path = Modifiers; path = Modifiers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1014,6 +1005,16 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3782B95527557A2400990149 /* Search */ = {
isa = PBXGroup;
children = (
374710042755291C00CE0F87 /* SearchField.swift */,
3782B94E27553A6700990149 /* SearchSuggestions.swift */,
37AAF27F26737550007FC770 /* SearchView.swift */,
);
path = Search;
sourceTree = "<group>";
};
3788AC2126F683AB00F6BAA9 /* Favorites */ = { 3788AC2126F683AB00F6BAA9 /* Favorites */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1067,8 +1068,8 @@
37BE0BD826A214500092E2DB /* macOS */ = { 37BE0BD826A214500092E2DB /* macOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
37FD43E1270472060073EE42 /* Settings */,
374C0542272496E4009BDDBE /* AppDelegate.swift */, 374C0542272496E4009BDDBE /* AppDelegate.swift */,
37FD43DB270470B70073EE42 /* InstancesSettings.swift */,
374108D0272B11B2006C5CC8 /* PictureInPictureDelegate.swift */, 374108D0272B11B2006C5CC8 /* PictureInPictureDelegate.swift */,
37BE0BDB26A2367F0092E2DB /* Player.swift */, 37BE0BDB26A2367F0092E2DB /* Player.swift */,
37BE0BD926A214630092E2DB /* PlayerViewController.swift */, 37BE0BD926A214630092E2DB /* PlayerViewController.swift */,
@ -1083,8 +1084,11 @@
379775922689365600DD52A8 /* Array+Next.swift */, 379775922689365600DD52A8 /* Array+Next.swift */,
376578842685429C00D4EA09 /* CaseIterable+Next.swift */, 376578842685429C00D4EA09 /* CaseIterable+Next.swift */,
37C0697D2725C8D400F7F6CB /* CMTime+DefaultTimescale.swift */, 37C0697D2725C8D400F7F6CB /* CMTime+DefaultTimescale.swift */,
378AE942274EF00A006A4EE1 /* Color+Background.swift */,
37C3A240272359900087A57A /* Double+Format.swift */, 37C3A240272359900087A57A /* Double+Format.swift */,
37BA794E26DC3E0E002A0235 /* Int+Format.swift */, 37BA794E26DC3E0E002A0235 /* Int+Format.swift */,
3782B95C2755858100990149 /* NSTextField+FocusRingType.swift */,
3782B9512755667600990149 /* String+Format.swift */,
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */, 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */,
3743CA51270F284F00E4D32B /* View+Borders.swift */, 3743CA51270F284F00E4D32B /* View+Borders.swift */,
); );
@ -1098,6 +1102,7 @@
37BE0BD826A214500092E2DB /* macOS */, 37BE0BD826A214500092E2DB /* macOS */,
37D4B159267164AE00C925CA /* tvOS */, 37D4B159267164AE00C925CA /* tvOS */,
37D4B0C12671614700C925CA /* Shared */, 37D4B0C12671614700C925CA /* Shared */,
3722AEBA274DA312005EA4D6 /* Backports */,
37D4B1B72672CFE300C925CA /* Model */, 37D4B1B72672CFE300C925CA /* Model */,
37C7A9022679058300E721B4 /* Extensions */, 37C7A9022679058300E721B4 /* Extensions */,
3748186426A762300084E870 /* Fixtures */, 3748186426A762300084E870 /* Fixtures */,
@ -1120,6 +1125,7 @@
371AAE2326CEB9E800901972 /* Navigation */, 371AAE2326CEB9E800901972 /* Navigation */,
371AAE2426CEBA4100901972 /* Player */, 371AAE2426CEBA4100901972 /* Player */,
371AAE2626CEBF1600901972 /* Playlists */, 371AAE2626CEBF1600901972 /* Playlists */,
3782B95527557A2400990149 /* Search */,
37484C1726FC836500287258 /* Settings */, 37484C1726FC836500287258 /* Settings */,
371AAE2526CEBF0B00901972 /* Trending */, 371AAE2526CEBF0B00901972 /* Trending */,
371AAE2726CEBF4700901972 /* Videos */, 371AAE2726CEBF4700901972 /* Videos */,
@ -1145,7 +1151,7 @@
37D4B0CF2671614900C925CA /* Yattee.app */, 37D4B0CF2671614900C925CA /* Yattee.app */,
37D4B0D42671614900C925CA /* Tests (iOS).xctest */, 37D4B0D42671614900C925CA /* Tests (iOS).xctest */,
37D4B0DE2671614900C925CA /* Tests (macOS).xctest */, 37D4B0DE2671614900C925CA /* Tests (macOS).xctest */,
37D4B158267164AE00C925CA /* Yattee (tvOS).app */, 37D4B158267164AE00C925CA /* Yattee.app */,
37D4B171267164B000C925CA /* Tests (tvOS).xctest */, 37D4B171267164B000C925CA /* Tests (tvOS).xctest */,
37A3B15727255E7F000FB5EE /* Open in Yattee (macOS).appex */, 37A3B15727255E7F000FB5EE /* Open in Yattee (macOS).appex */,
37A3B1792725735F000FB5EE /* Open in Yattee (iOS).appex */, 37A3B1792725735F000FB5EE /* Open in Yattee (iOS).appex */,
@ -1242,14 +1248,6 @@
path = Search; path = Search;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
37FD43E1270472060073EE42 /* Settings */ = {
isa = PBXGroup;
children = (
37FD43DB270470B70073EE42 /* InstancesSettings.swift */,
);
path = Settings;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -1295,12 +1293,10 @@
37D4B0C52671614900C925CA /* Sources */, 37D4B0C52671614900C925CA /* Sources */,
37D4B0C62671614900C925CA /* Frameworks */, 37D4B0C62671614900C925CA /* Frameworks */,
37D4B0C72671614900C925CA /* Resources */, 37D4B0C72671614900C925CA /* Resources */,
37A3B1932725735F000FB5EE /* Embed App Extensions */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
37A3B18E2725735F000FB5EE /* PBXTargetDependency */,
); );
name = "Yattee (iOS)"; name = "Yattee (iOS)";
packageProductDependencies = ( packageProductDependencies = (
@ -1327,12 +1323,10 @@
37D4B0CB2671614900C925CA /* Sources */, 37D4B0CB2671614900C925CA /* Sources */,
37D4B0CC2671614900C925CA /* Frameworks */, 37D4B0CC2671614900C925CA /* Frameworks */,
37D4B0CD2671614900C925CA /* Resources */, 37D4B0CD2671614900C925CA /* Resources */,
37A3B17127255E7F000FB5EE /* Embed App Extensions */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
37A3B16F27255E7F000FB5EE /* PBXTargetDependency */,
); );
name = "Yattee (macOS)"; name = "Yattee (macOS)";
packageProductDependencies = ( packageProductDependencies = (
@ -1419,7 +1413,7 @@
3765917D27237D2A009F956E /* PINCache */, 3765917D27237D2A009F956E /* PINCache */,
); );
productName = Yattee; productName = Yattee;
productReference = 37D4B158267164AE00C925CA /* Yattee (tvOS).app */; productReference = 37D4B158267164AE00C925CA /* Yattee.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
37D4B170267164B000C925CA /* Tests (tvOS) */ = { 37D4B170267164B000C925CA /* Tests (tvOS) */ = {
@ -1723,6 +1717,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
374710052755291C00CE0F87 /* SearchField.swift in Sources */,
37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */, 37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
37CC3F45270CE30600608308 /* PlayerQueueItem.swift in Sources */, 37CC3F45270CE30600608308 /* PlayerQueueItem.swift in Sources */,
37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */, 37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */,
@ -1737,6 +1732,7 @@
37BD07B52698AA4D003EBB87 /* ContentView.swift in Sources */, 37BD07B52698AA4D003EBB87 /* ContentView.swift in Sources */,
37152EEA26EFEB95004FB96D /* LazyView.swift in Sources */, 37152EEA26EFEB95004FB96D /* LazyView.swift in Sources */,
3761ABFD26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */, 3761ABFD26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */,
3782B94F27553A6700990149 /* SearchSuggestions.swift in Sources */,
378E50FF26FE8EEE00F49626 /* AccountsMenuView.swift in Sources */, 378E50FF26FE8EEE00F49626 /* AccountsMenuView.swift in Sources */,
37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */, 37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */,
37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */, 37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */,
@ -1756,8 +1752,10 @@
37BE0BD326A1D4780092E2DB /* Player.swift in Sources */, 37BE0BD326A1D4780092E2DB /* Player.swift in Sources */,
37A9965E26D6F9B9006E3224 /* FavoritesView.swift in Sources */, 37A9965E26D6F9B9006E3224 /* FavoritesView.swift in Sources */,
37CEE4C12677B697005A1EFE /* Stream.swift in Sources */, 37CEE4C12677B697005A1EFE /* Stream.swift in Sources */,
378AE943274EF00A006A4EE1 /* Color+Background.swift in Sources */,
37F4AE7226828F0900BD60EA /* VerticalCells.swift in Sources */, 37F4AE7226828F0900BD60EA /* VerticalCells.swift in Sources */,
376578852685429C00D4EA09 /* CaseIterable+Next.swift in Sources */, 376578852685429C00D4EA09 /* CaseIterable+Next.swift in Sources */,
3722AEBC274DA396005EA4D6 /* Badge+Backport.swift in Sources */,
3748186626A7627F0084E870 /* Video+Fixtures.swift in Sources */, 3748186626A7627F0084E870 /* Video+Fixtures.swift in Sources */,
37599F34272B44000087F250 /* FavoritesModel.swift in Sources */, 37599F34272B44000087F250 /* FavoritesModel.swift in Sources */,
37BA794726DC2E56002A0235 /* AppSidebarSubscriptions.swift in Sources */, 37BA794726DC2E56002A0235 /* AppSidebarSubscriptions.swift in Sources */,
@ -1765,7 +1763,9 @@
37CC3F4C270CFE1700608308 /* PlayerQueueView.swift in Sources */, 37CC3F4C270CFE1700608308 /* PlayerQueueView.swift in Sources */,
37FFC440272734C3009FFD26 /* Throttle.swift in Sources */, 37FFC440272734C3009FFD26 /* Throttle.swift in Sources */,
3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */, 3705B182267B4E4900704544 /* TrendingCategory.swift in Sources */,
378AE940274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */,
376BE50927347B5F009AD608 /* SettingsHeader.swift in Sources */, 376BE50927347B5F009AD608 /* SettingsHeader.swift in Sources */,
3722AEBE274DA401005EA4D6 /* Backport.swift in Sources */,
3700155F271B12DD0049C794 /* SiestaConfiguration.swift in Sources */, 3700155F271B12DD0049C794 /* SiestaConfiguration.swift in Sources */,
37EAD86F267B9ED100D9E01B /* Segment.swift in Sources */, 37EAD86F267B9ED100D9E01B /* Segment.swift in Sources */,
375168D62700FAFF008F96A6 /* Debounce.swift in Sources */, 375168D62700FAFF008F96A6 /* Debounce.swift in Sources */,
@ -1773,6 +1773,7 @@
376578892685471400D4EA09 /* Playlist.swift in Sources */, 376578892685471400D4EA09 /* Playlist.swift in Sources */,
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */, 373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */, 3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
3782B9522755667600990149 /* String+Format.swift in Sources */,
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */, 373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
37BF661F27308884008CCFB0 /* DropFavoriteOutside.swift in Sources */, 37BF661F27308884008CCFB0 /* DropFavoriteOutside.swift in Sources */,
3700155B271B0D4D0049C794 /* PipedAPI.swift in Sources */, 3700155B271B0D4D0049C794 /* PipedAPI.swift in Sources */,
@ -1786,7 +1787,6 @@
37BA794326DBA973002A0235 /* PlaylistsModel.swift in Sources */, 37BA794326DBA973002A0235 /* PlaylistsModel.swift in Sources */,
37AAF29026740715007FC770 /* Channel.swift in Sources */, 37AAF29026740715007FC770 /* Channel.swift in Sources */,
3748186A26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */, 3748186A26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
3761AC0F26F0F9A600AA496F /* UnsubscribeAlertModifier.swift in Sources */,
37B81AFF26D2CA3700675966 /* VideoDetails.swift in Sources */, 37B81AFF26D2CA3700675966 /* VideoDetails.swift in Sources */,
377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */, 377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */,
376578912685490700D4EA09 /* PlaylistsView.swift in Sources */, 376578912685490700D4EA09 /* PlaylistsView.swift in Sources */,
@ -1795,7 +1795,7 @@
374C053F272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */, 374C053F272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
37FB28412721B22200A57617 /* ContentItem.swift in Sources */, 37FB28412721B22200A57617 /* ContentItem.swift in Sources */,
37C3A24D272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */, 37C3A24D272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */,
37484C2D26FC844700287258 /* AccountsSettings.swift in Sources */, 37484C2D26FC844700287258 /* InstanceSettings.swift in Sources */,
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */, 377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
37484C3126FCB8F900287258 /* AccountValidator.swift in Sources */, 37484C3126FCB8F900287258 /* AccountValidator.swift in Sources */,
37319F0527103F94004ECCD0 /* PlayerQueue.swift in Sources */, 37319F0527103F94004ECCD0 /* PlayerQueue.swift in Sources */,
@ -1830,6 +1830,7 @@
373CFAEB26975CBF003CB2C6 /* PlaylistFormView.swift in Sources */, 373CFAEB26975CBF003CB2C6 /* PlaylistFormView.swift in Sources */,
37B81B0226D2CAE700675966 /* PlaybackBar.swift in Sources */, 37B81B0226D2CAE700675966 /* PlaybackBar.swift in Sources */,
372915E62687E3B900F5A35B /* Defaults.swift in Sources */, 372915E62687E3B900F5A35B /* Defaults.swift in Sources */,
37E084AC2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */,
37D526E32720B4BE00ED2F5E /* View+SwipeGesture.swift in Sources */, 37D526E32720B4BE00ED2F5E /* View+SwipeGesture.swift in Sources */,
37732FF42703D32400F04329 /* Sidebar.swift in Sources */, 37732FF42703D32400F04329 /* Sidebar.swift in Sources */,
37D4B19726717E1500C925CA /* Video.swift in Sources */, 37D4B19726717E1500C925CA /* Video.swift in Sources */,
@ -1842,7 +1843,6 @@
37CC3F50270D010D00608308 /* VideoBanner.swift in Sources */, 37CC3F50270D010D00608308 /* VideoBanner.swift in Sources */,
378E50FB26FE8B9F00F49626 /* Instance.swift in Sources */, 378E50FB26FE8B9F00F49626 /* Instance.swift in Sources */,
37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */, 37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
37484C1D26FC83A400287258 /* InstancesSettings.swift in Sources */,
37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */, 37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */,
37C0697A2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */, 37C0697A2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
37D4B0E42671614900C925CA /* YatteeApp.swift in Sources */, 37D4B0E42671614900C925CA /* YatteeApp.swift in Sources */,
@ -1857,6 +1857,8 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
374710062755291C00CE0F87 /* SearchField.swift in Sources */,
378AE93F274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */,
37C194C826F6A9C8005D3B96 /* RecentsModel.swift in Sources */, 37C194C826F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
37BE0BDC26A2367F0092E2DB /* Player.swift in Sources */, 37BE0BDC26A2367F0092E2DB /* Player.swift in Sources */,
3743CA53270F284F00E4D32B /* View+Borders.swift in Sources */, 3743CA53270F284F00E4D32B /* View+Borders.swift in Sources */,
@ -1870,6 +1872,7 @@
37EAD86C267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */, 37EAD86C267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
37C3A24E272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */, 37C3A24E272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */,
37CEE4C22677B697005A1EFE /* Stream.swift in Sources */, 37CEE4C22677B697005A1EFE /* Stream.swift in Sources */,
3782B95027553A6700990149 /* SearchSuggestions.swift in Sources */,
371F2F1B269B43D300E4A7AB /* NavigationModel.swift in Sources */, 371F2F1B269B43D300E4A7AB /* NavigationModel.swift in Sources */,
37001564271B1F250049C794 /* AccountsModel.swift in Sources */, 37001564271B1F250049C794 /* AccountsModel.swift in Sources */,
3761ABFE26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */, 3761ABFE26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */,
@ -1889,9 +1892,11 @@
37484C1A26FC837400287258 /* PlaybackSettings.swift in Sources */, 37484C1A26FC837400287258 /* PlaybackSettings.swift in Sources */,
37BD07C32698AD4F003EBB87 /* ContentView.swift in Sources */, 37BD07C32698AD4F003EBB87 /* ContentView.swift in Sources */,
37484C3226FCB8F900287258 /* AccountValidator.swift in Sources */, 37484C3226FCB8F900287258 /* AccountValidator.swift in Sources */,
378AE944274EF00A006A4EE1 /* Color+Background.swift in Sources */,
37F49BA426CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */, 37F49BA426CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
37EAD870267B9ED100D9E01B /* Segment.swift in Sources */, 37EAD870267B9ED100D9E01B /* Segment.swift in Sources */,
3788AC2826F6840700F6BAA9 /* FavoriteItemView.swift in Sources */, 3788AC2826F6840700F6BAA9 /* FavoriteItemView.swift in Sources */,
378AE93A274EDFAF006A4EE1 /* Badge+Backport.swift in Sources */,
37599F35272B44000087F250 /* FavoritesModel.swift in Sources */, 37599F35272B44000087F250 /* FavoritesModel.swift in Sources */,
37F64FE526FE70A60081B69E /* RedrawOnModifier.swift in Sources */, 37F64FE526FE70A60081B69E /* RedrawOnModifier.swift in Sources */,
37FFC441272734C3009FFD26 /* Throttle.swift in Sources */, 37FFC441272734C3009FFD26 /* Throttle.swift in Sources */,
@ -1900,6 +1905,7 @@
378E510026FE8EEE00F49626 /* AccountsMenuView.swift in Sources */, 378E510026FE8EEE00F49626 /* AccountsMenuView.swift in Sources */,
37141670267A8ACC006CA35D /* TrendingView.swift in Sources */, 37141670267A8ACC006CA35D /* TrendingView.swift in Sources */,
376BE50727347B57009AD608 /* SettingsHeader.swift in Sources */, 376BE50727347B57009AD608 /* SettingsHeader.swift in Sources */,
378AE93C274EDFB2006A4EE1 /* Backport.swift in Sources */,
37152EEB26EFEB95004FB96D /* LazyView.swift in Sources */, 37152EEB26EFEB95004FB96D /* LazyView.swift in Sources */,
377FC7E2267A084A00A6BBAF /* VideoCell.swift in Sources */, 377FC7E2267A084A00A6BBAF /* VideoCell.swift in Sources */,
37CC3F51270D010D00608308 /* VideoBanner.swift in Sources */, 37CC3F51270D010D00608308 /* VideoBanner.swift in Sources */,
@ -1907,6 +1913,7 @@
37169AA32729D98A0011DE61 /* InstancesBridge.swift in Sources */, 37169AA32729D98A0011DE61 /* InstancesBridge.swift in Sources */,
37B044B826F7AB9000E1419D /* SettingsView.swift in Sources */, 37B044B826F7AB9000E1419D /* SettingsView.swift in Sources */,
3765788A2685471400D4EA09 /* Playlist.swift in Sources */, 3765788A2685471400D4EA09 /* Playlist.swift in Sources */,
3782B9532755667600990149 /* String+Format.swift in Sources */,
37E2EEAC270656EC00170416 /* PlayerControlsView.swift in Sources */, 37E2EEAC270656EC00170416 /* PlayerControlsView.swift in Sources */,
37BF662027308884008CCFB0 /* DropFavoriteOutside.swift in Sources */, 37BF662027308884008CCFB0 /* DropFavoriteOutside.swift in Sources */,
37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */, 37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
@ -1935,7 +1942,6 @@
37B81AFD26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */, 37B81AFD26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
37C0697F2725C8D400F7F6CB /* CMTime+DefaultTimescale.swift in Sources */, 37C0697F2725C8D400F7F6CB /* CMTime+DefaultTimescale.swift in Sources */,
37A9965B26D6F8CA006E3224 /* HorizontalCells.swift in Sources */, 37A9965B26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
3761AC1026F0F9A600AA496F /* UnsubscribeAlertModifier.swift in Sources */,
37732FF52703D32400F04329 /* Sidebar.swift in Sources */, 37732FF52703D32400F04329 /* Sidebar.swift in Sources */,
379775942689365600DD52A8 /* Array+Next.swift in Sources */, 379775942689365600DD52A8 /* Array+Next.swift in Sources */,
3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */, 3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */,
@ -1976,6 +1982,7 @@
37B17DA1268A1F89006AEE9B /* VideoContextMenuView.swift in Sources */, 37B17DA1268A1F89006AEE9B /* VideoContextMenuView.swift in Sources */,
3743B86927216D3600261544 /* ChannelCell.swift in Sources */, 3743B86927216D3600261544 /* ChannelCell.swift in Sources */,
3748186B26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */, 3748186B26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
3782B95E2755858100990149 /* NSTextField+FocusRingType.swift in Sources */,
37C3A252272366440087A57A /* ChannelPlaylistView.swift in Sources */, 37C3A252272366440087A57A /* ChannelPlaylistView.swift in Sources */,
373CFADC269663F1003CB2C6 /* Thumbnail.swift in Sources */, 373CFADC269663F1003CB2C6 /* Thumbnail.swift in Sources */,
37C0697B2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */, 37C0697B2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
@ -2055,7 +2062,6 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
37AAF28026737550007FC770 /* SearchView.swift in Sources */,
37EAD871267B9ED100D9E01B /* Segment.swift in Sources */, 37EAD871267B9ED100D9E01B /* Segment.swift in Sources */,
37CC3F52270D010D00608308 /* VideoBanner.swift in Sources */, 37CC3F52270D010D00608308 /* VideoBanner.swift in Sources */,
37F49BA526CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */, 37F49BA526CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
@ -2071,6 +2077,7 @@
37D4B1802671650A00C925CA /* YatteeApp.swift in Sources */, 37D4B1802671650A00C925CA /* YatteeApp.swift in Sources */,
3748187026A769D60084E870 /* DetailBadge.swift in Sources */, 3748187026A769D60084E870 /* DetailBadge.swift in Sources */,
37A9965C26D6F8CA006E3224 /* HorizontalCells.swift in Sources */, 37A9965C26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
3782B95727557E6E00990149 /* SearchSuggestions.swift in Sources */,
37BD07C92698FBDB003EBB87 /* ContentView.swift in Sources */, 37BD07C92698FBDB003EBB87 /* ContentView.swift in Sources */,
376B2E0926F920D600B1D64D /* SignInRequiredView.swift in Sources */, 376B2E0926F920D600B1D64D /* SignInRequiredView.swift in Sources */,
37141671267A8ACC006CA35D /* TrendingView.swift in Sources */, 37141671267A8ACC006CA35D /* TrendingView.swift in Sources */,
@ -2080,10 +2087,12 @@
37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */, 37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
376BE50D27349108009AD608 /* BrowsingSettings.swift in Sources */, 376BE50D27349108009AD608 /* BrowsingSettings.swift in Sources */,
37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */, 37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */,
378AE93E274EDFB4006A4EE1 /* Tint+Backport.swift in Sources */,
37FFC442272734C3009FFD26 /* Throttle.swift in Sources */, 37FFC442272734C3009FFD26 /* Throttle.swift in Sources */,
375168D82700FDB9008F96A6 /* Debounce.swift in Sources */, 375168D82700FDB9008F96A6 /* Debounce.swift in Sources */,
37BA794126DB8F97002A0235 /* ChannelVideosView.swift in Sources */, 37BA794126DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
37C0697C2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */, 37C0697C2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
378AE93D274EDFB3006A4EE1 /* Backport.swift in Sources */,
37C3A243272359900087A57A /* Double+Format.swift in Sources */, 37C3A243272359900087A57A /* Double+Format.swift in Sources */,
37AAF29226740715007FC770 /* Channel.swift in Sources */, 37AAF29226740715007FC770 /* Channel.swift in Sources */,
37EAD86D267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */, 37EAD86D267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
@ -2098,7 +2107,6 @@
3743B86A27216D3600261544 /* ChannelCell.swift in Sources */, 3743B86A27216D3600261544 /* ChannelCell.swift in Sources */,
37B767DD2677C3CA0098BAA8 /* PlayerModel.swift in Sources */, 37B767DD2677C3CA0098BAA8 /* PlayerModel.swift in Sources */,
373CFAF12697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */, 373CFAF12697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
3761AC1126F0F9A600AA496F /* UnsubscribeAlertModifier.swift in Sources */,
3730D8A02712E2B70020ED53 /* NowPlayingView.swift in Sources */, 3730D8A02712E2B70020ED53 /* NowPlayingView.swift in Sources */,
37169AA42729D98A0011DE61 /* InstancesBridge.swift in Sources */, 37169AA42729D98A0011DE61 /* InstancesBridge.swift in Sources */,
37D4B18E26717B3800C925CA /* VideoCell.swift in Sources */, 37D4B18E26717B3800C925CA /* VideoCell.swift in Sources */,
@ -2112,7 +2120,6 @@
37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */, 37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */,
376A33E62720CB35000C1D6B /* Account.swift in Sources */, 376A33E62720CB35000C1D6B /* Account.swift in Sources */,
37599F3A272B4D740087F250 /* FavoriteButton.swift in Sources */, 37599F3A272B4D740087F250 /* FavoriteButton.swift in Sources */,
37484C1F26FC83A400287258 /* InstancesSettings.swift in Sources */,
37EF5C242739D37B00B03725 /* MenuModel.swift in Sources */, 37EF5C242739D37B00B03725 /* MenuModel.swift in Sources */,
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */, 37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
376578932685490700D4EA09 /* PlaylistsView.swift in Sources */, 376578932685490700D4EA09 /* PlaylistsView.swift in Sources */,
@ -2123,6 +2130,7 @@
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */, 377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */, 3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */,
37C3A253272366440087A57A /* ChannelPlaylistView.swift in Sources */, 37C3A253272366440087A57A /* ChannelPlaylistView.swift in Sources */,
378AE945274EF00A006A4EE1 /* Color+Background.swift in Sources */,
3743CA54270F284F00E4D32B /* View+Borders.swift in Sources */, 3743CA54270F284F00E4D32B /* View+Borders.swift in Sources */,
371F2F1C269B43D300E4A7AB /* NavigationModel.swift in Sources */, 371F2F1C269B43D300E4A7AB /* NavigationModel.swift in Sources */,
37E2EEAD270656EC00170416 /* PlayerControlsView.swift in Sources */, 37E2EEAD270656EC00170416 /* PlayerControlsView.swift in Sources */,
@ -2148,6 +2156,7 @@
37FAE000272ED58000330459 /* EditFavorites.swift in Sources */, 37FAE000272ED58000330459 /* EditFavorites.swift in Sources */,
37599F32272B42810087F250 /* FavoriteItem.swift in Sources */, 37599F32272B42810087F250 /* FavoriteItem.swift in Sources */,
37141675267A8E10006CA35D /* Country.swift in Sources */, 37141675267A8E10006CA35D /* Country.swift in Sources */,
3782B9542755667600990149 /* String+Format.swift in Sources */,
37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */, 37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */,
37484C2726FC83E000287258 /* InstanceForm.swift in Sources */, 37484C2726FC83E000287258 /* InstanceForm.swift in Sources */,
37F49BA826CB0FCE00304AC0 /* PlaylistFormView.swift in Sources */, 37F49BA826CB0FCE00304AC0 /* PlaylistFormView.swift in Sources */,
@ -2158,6 +2167,8 @@
37D526E02720AC4400ED2F5E /* VideosAPI.swift in Sources */, 37D526E02720AC4400ED2F5E /* VideosAPI.swift in Sources */,
37599F36272B44000087F250 /* FavoritesModel.swift in Sources */, 37599F36272B44000087F250 /* FavoritesModel.swift in Sources */,
3705B184267B4E4900704544 /* TrendingCategory.swift in Sources */, 3705B184267B4E4900704544 /* TrendingCategory.swift in Sources */,
37E084AD2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */,
3782B95627557E4E00990149 /* SearchView.swift in Sources */,
3761ABFF26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */, 3761ABFF26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */,
37C3A24F272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */, 37C3A24F272360470087A57A /* ChannelPlaylist+Fixtures.swift in Sources */,
37FB28432721B22200A57617 /* ContentItem.swift in Sources */, 37FB28432721B22200A57617 /* ContentItem.swift in Sources */,
@ -2166,7 +2177,7 @@
372915E82687E3B900F5A35B /* Defaults.swift in Sources */, 372915E82687E3B900F5A35B /* Defaults.swift in Sources */,
37BAB54C269B39FD00E75ED1 /* TVNavigationView.swift in Sources */, 37BAB54C269B39FD00E75ED1 /* TVNavigationView.swift in Sources */,
3797758D2689345500DD52A8 /* Store.swift in Sources */, 3797758D2689345500DD52A8 /* Store.swift in Sources */,
37484C2F26FC844700287258 /* AccountsSettings.swift in Sources */, 37484C2F26FC844700287258 /* InstanceSettings.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -2181,16 +2192,6 @@
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
37A3B16F27255E7F000FB5EE /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 37A3B15627255E7F000FB5EE /* Open in Yattee (macOS) */;
targetProxy = 37A3B16E27255E7F000FB5EE /* PBXContainerItemProxy */;
};
37A3B18E2725735F000FB5EE /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 37A3B1782725735F000FB5EE /* Open in Yattee (iOS) */;
targetProxy = 37A3B18D2725735F000FB5EE /* PBXContainerItemProxy */;
};
37D4B0D62671614900C925CA /* PBXTargetDependency */ = { 37D4B0D62671614900C925CA /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = 37D4B0C82671614900C925CA /* Yattee (iOS) */; target = 37D4B0C82671614900C925CA /* Yattee (iOS) */;
@ -2216,7 +2217,7 @@
CODE_SIGN_ENTITLEMENTS = "Open in Yattee/Open in Yattee.entitlements"; CODE_SIGN_ENTITLEMENTS = "Open in Yattee/Open in Yattee.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -2229,7 +2230,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.2;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -2250,7 +2251,7 @@
CODE_SIGN_ENTITLEMENTS = "Open in Yattee/Open in Yattee.entitlements"; CODE_SIGN_ENTITLEMENTS = "Open in Yattee/Open in Yattee.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -2263,7 +2264,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.2;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -2282,7 +2283,7 @@
buildSettings = { buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Open in Yattee/Info.plist"; INFOPLIST_FILE = "Open in Yattee/Info.plist";
@ -2294,7 +2295,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.2;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -2314,7 +2315,7 @@
buildSettings = { buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Open in Yattee/Info.plist"; INFOPLIST_FILE = "Open in Yattee/Info.plist";
@ -2326,7 +2327,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.2;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"-framework", "-framework",
SafariServices, SafariServices,
@ -2474,11 +2475,10 @@
37D4B0ED2671614900C925CA /* Debug */ = { 37D4B0ED2671614900C925CA /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -2488,12 +2488,12 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.1; MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app; PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app;
PRODUCT_NAME = Yattee; PRODUCT_NAME = Yattee;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -2506,11 +2506,10 @@
37D4B0EE2671614900C925CA /* Release */ = { 37D4B0EE2671614900C925CA /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -2520,12 +2519,12 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.1; MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app; PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app;
PRODUCT_NAME = Yattee; PRODUCT_NAME = Yattee;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -2539,14 +2538,13 @@
37D4B0F02671614900C925CA /* Debug */ = { 37D4B0F02671614900C925CA /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Shared/Yattee.entitlements; CODE_SIGN_ENTITLEMENTS = Shared/Yattee.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_APP_SANDBOX = YES; ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@ -2556,13 +2554,12 @@
INFOPLIST_FILE = macOS/Info.plist; INFOPLIST_FILE = macOS/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.video"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.video";
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
INFOPLIST_KEY_NSMainStoryboardFile = Main;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.1; MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app; PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app;
PRODUCT_NAME = Yattee; PRODUCT_NAME = Yattee;
SDKROOT = macosx; SDKROOT = macosx;
@ -2574,14 +2571,13 @@
37D4B0F12671614900C925CA /* Release */ = { 37D4B0F12671614900C925CA /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Shared/Yattee.entitlements; CODE_SIGN_ENTITLEMENTS = Shared/Yattee.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_APP_SANDBOX = YES; ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@ -2591,13 +2587,12 @@
INFOPLIST_FILE = macOS/Info.plist; INFOPLIST_FILE = macOS/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.video"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.video";
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
INFOPLIST_KEY_NSMainStoryboardFile = Main;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 1.1; MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app; PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app;
PRODUCT_NAME = Yattee; PRODUCT_NAME = Yattee;
SDKROOT = macosx; SDKROOT = macosx;
@ -2713,14 +2708,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = tvOS/Info.plist; INFOPLIST_FILE = tvOS/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Yattee; INFOPLIST_KEY_CFBundleDisplayName = Yattee;
INFOPLIST_KEY_CFBundleExecutable = "Yattee (Apple TV)";
INFOPLIST_KEY_CFBundleName = "Yattee (Apple TV)"; INFOPLIST_KEY_CFBundleName = "Yattee (Apple TV)";
INFOPLIST_KEY_CFBundleVersion = 1; INFOPLIST_KEY_CFBundleVersion = 1;
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@ -2729,9 +2723,9 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.1; MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app; PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = Yattee;
SDKROOT = appletvos; SDKROOT = appletvos;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -2746,14 +2740,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = tvOS/Info.plist; INFOPLIST_FILE = tvOS/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Yattee; INFOPLIST_KEY_CFBundleDisplayName = Yattee;
INFOPLIST_KEY_CFBundleExecutable = "Yattee (Apple TV)";
INFOPLIST_KEY_CFBundleName = "Yattee (Apple TV)"; INFOPLIST_KEY_CFBundleName = "Yattee (Apple TV)";
INFOPLIST_KEY_CFBundleVersion = 1; INFOPLIST_KEY_CFBundleVersion = 1;
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@ -2762,9 +2755,9 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.1; MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app; PRODUCT_BUNDLE_IDENTIFIER = stream.yattee.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = Yattee;
SDKROOT = appletvos; SDKROOT = appletvos;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -2980,7 +2973,7 @@
repositoryURL = "https://github.com/sindresorhus/Defaults"; repositoryURL = "https://github.com/sindresorhus/Defaults";
requirement = { requirement = {
kind = upToNextMajorVersion; kind = upToNextMajorVersion;
minimumVersion = 5.0.0; minimumVersion = 6.0.0;
}; };
}; };
3765917827237D07009F956E /* XCRemoteSwiftPackageReference "PINCache" */ = { 3765917827237D07009F956E /* XCRemoteSwiftPackageReference "PINCache" */ = {

View File

@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/sindresorhus/Defaults", "repositoryURL": "https://github.com/sindresorhus/Defaults",
"state": { "state": {
"branch": null, "branch": null,
"revision": "63d93f97ad545c8bceb125a8a36175ea705f7cf5", "revision": "55f3302c3ab30a8760f10042d0ebc0a6907f865a",
"version": "5.0.0" "version": "6.1.0"
} }
}, },
{ {
@ -33,7 +33,7 @@
"repositoryURL": "https://github.com/pinterest/PINCache", "repositoryURL": "https://github.com/pinterest/PINCache",
"state": { "state": {
"branch": "master", "branch": "master",
"revision": "a16dae6cec4897a7a9710239e0cb50b6de935e7b", "revision": "046f67609085a7d73d27105d2be91729d139208f",
"version": null "version": null
} }
}, },

View File

@ -15,7 +15,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "37D4B157267164AE00C925CA" BlueprintIdentifier = "37D4B157267164AE00C925CA"
BuildableName = "Yattee (tvOS).app" BuildableName = "Yattee.app"
BlueprintName = "Yattee (tvOS)" BlueprintName = "Yattee (tvOS)"
ReferencedContainer = "container:Yattee.xcodeproj"> ReferencedContainer = "container:Yattee.xcodeproj">
</BuildableReference> </BuildableReference>
@ -65,7 +65,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "37D4B157267164AE00C925CA" BlueprintIdentifier = "37D4B157267164AE00C925CA"
BuildableName = "Yattee (tvOS).app" BuildableName = "Yattee.app"
BlueprintName = "Yattee (tvOS)" BlueprintName = "Yattee (tvOS)"
ReferencedContainer = "container:Yattee.xcodeproj"> ReferencedContainer = "container:Yattee.xcodeproj">
</BuildableReference> </BuildableReference>
@ -82,7 +82,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "37D4B157267164AE00C925CA" BlueprintIdentifier = "37D4B157267164AE00C925CA"
BuildableName = "Yattee (tvOS).app" BuildableName = "Yattee.app"
BlueprintName = "Yattee (tvOS)" BlueprintName = "Yattee (tvOS)"
ReferencedContainer = "container:Yattee.xcodeproj"> ReferencedContainer = "container:Yattee.xcodeproj">
</BuildableReference> </BuildableReference>

View File

@ -40,7 +40,7 @@ struct InstancesSettings: View {
if !selectedInstance.isNil, selectedInstance.app.supportsAccounts { if !selectedInstance.isNil, selectedInstance.app.supportsAccounts {
SettingsHeader(text: "Accounts") SettingsHeader(text: "Accounts")
List(selection: $selectedAccount) { let list = List(selection: $selectedAccount) {
if selectedInstanceAccounts.isEmpty { if selectedInstanceAccounts.isEmpty {
Text("You have no accounts for this instance") Text("You have no accounts for this instance")
.foregroundColor(.secondary) .foregroundColor(.secondary)
@ -51,7 +51,7 @@ struct InstancesSettings: View {
Spacer() Spacer()
Button("Remove", role: .destructive) { Button("Remove") {
presentingAccountRemovalConfirmation = true presentingAccountRemovalConfirmation = true
} }
.foregroundColor(.red) .foregroundColor(.red)
@ -60,30 +60,40 @@ struct InstancesSettings: View {
.tag(account) .tag(account)
} }
} }
.confirmationDialog( .alert(isPresented: $presentingAccountRemovalConfirmation) {
"Are you sure you want to remove \(selectedAccount?.description ?? "") account?", Alert(
isPresented: $presentingAccountRemovalConfirmation title: Text(
) { "Are you sure you want to remove \(selectedAccount?.description ?? "") account?"
Button("Remove", role: .destructive) { ),
AccountsModel.remove(selectedAccount!) message: Text("This cannot be undone"),
} primaryButton: .destructive(Text("Delete")) {
AccountsModel.remove(selectedAccount!)
},
secondaryButton: .cancel()
)
}
if #available(macOS 12.0, *) {
list
.listStyle(.inset(alternatesRowBackgrounds: true))
} else {
list
} }
.listStyle(.inset(alternatesRowBackgrounds: true))
} }
if selectedInstance != nil, selectedInstance.app.hasFrontendURL { if selectedInstance != nil, selectedInstance.app.hasFrontendURL {
SettingsHeader(text: "Frontend URL") SettingsHeader(text: "Frontend URL")
TextField("Frontend URL", text: $frontendURL, prompt: Text("Frontend URL")) TextField("Frontend URL", text: $frontendURL)
.onAppear { .onChange(of: selectedInstance) { _ in
frontendURL = selectedInstance.frontendURL ?? "" frontendURL = selectedInstanceFrontendURL
} }
.onChange(of: frontendURL) { newValue in .onChange(of: frontendURL) { newValue in
InstancesModel.setFrontendURL(selectedInstance, newValue) InstancesModel.setFrontendURL(selectedInstance, newValue)
} }
.labelsHidden() .labelsHidden()
Text("If provided, you can copy links from videos, channels and playlist") Text("Used to create links from videos, channels and playlist")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
@ -105,23 +115,26 @@ struct InstancesSettings: View {
Spacer() Spacer()
Button("Remove Instance", role: .destructive) { Button("Remove Instance") {
presentingInstanceRemovalConfirmation = true presentingInstanceRemovalConfirmation = true
} }
.confirmationDialog( .alert(isPresented: $presentingInstanceRemovalConfirmation) {
"Are you sure you want to remove \(selectedInstance!.longDescription) instance?", Alert(
isPresented: $presentingInstanceRemovalConfirmation title: Text(
) { "Are you sure you want to remove \(selectedInstance!.longDescription) instance?"
Button("Remove Instance", role: .destructive) { ),
if accounts.current?.instance == selectedInstance { message: Text("This cannot be undone"),
accounts.setCurrent(nil) primaryButton: .destructive(Text("Remove")) {
} if accounts.current?.instance == selectedInstance {
accounts.setCurrent(nil)
}
InstancesModel.remove(selectedInstance!) InstancesModel.remove(selectedInstance!)
selectedInstanceID = instances.last?.id selectedInstanceID = instances.last?.id
} },
secondaryButton: .cancel()
)
} }
.foregroundColor(.red) .foregroundColor(.red)
} }
} }
@ -134,6 +147,7 @@ struct InstancesSettings: View {
.onAppear { .onAppear {
selectedInstanceID = instances.first?.id selectedInstanceID = instances.first?.id
frontendURL = selectedInstanceFrontendURL
} }
.sheet(isPresented: $presentingAccountForm) { .sheet(isPresented: $presentingAccountForm) {
AccountForm(instance: selectedInstance, selectedAccount: $selectedAccount) AccountForm(instance: selectedInstance, selectedAccount: $selectedAccount)
@ -154,6 +168,10 @@ struct InstancesSettings: View {
InstancesModel.find(selectedInstanceID) InstancesModel.find(selectedInstanceID)
} }
var selectedInstanceFrontendURL: String {
selectedInstance?.frontendURL ?? ""
}
private var selectedInstanceAccounts: [Account] { private var selectedInstanceAccounts: [Account] {
guard selectedInstance != nil else { guard selectedInstance != nil else {
return [] return []

View File

@ -1,10 +1,12 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "TopShelf-Wide.png",
"idiom" : "tv", "idiom" : "tv",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"filename" : "TopShelf-Wide@2x.png",
"idiom" : "tv", "idiom" : "tv",
"scale" : "2x" "scale" : "2x"
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB