mirror of
https://github.com/yattee/yattee.git
synced 2025-12-16 13:08:14 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
792a2c1c6c | ||
|
|
12ef9e091c | ||
|
|
101ecb6892 | ||
|
|
be2e4acedd | ||
|
|
c6cff4dee4 | ||
|
|
eaeaa45422 | ||
|
|
6ddf1113bf | ||
|
|
15f3e11a78 | ||
|
|
713570dfd6 | ||
|
|
50eb0be1d7 | ||
|
|
3adbea1897 | ||
|
|
80a644eb7a | ||
|
|
b1637c5ef1 | ||
|
|
166482601d | ||
|
|
1d61dec8eb | ||
|
|
ca7195caba | ||
|
|
947f216fac | ||
|
|
a054d343a9 | ||
|
|
48263ae7db | ||
|
|
562df2d9ba | ||
|
|
e5f137a2d2 | ||
|
|
6856506834 | ||
|
|
a604382a3d | ||
|
|
0bbbf0907d | ||
|
|
921987be5d | ||
|
|
b47156ba5e | ||
|
|
5ab06d0e09 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,4 +1,18 @@
|
|||||||
## Build 151
|
## Build 152
|
||||||
|
* Tapping second time on search tab button focuses the input field and selects entered query text (iOS)
|
||||||
|
* Added Browsing setting "Keep channels with unwatched videos on top of subscriptions list"
|
||||||
|
* Improved buttons and layout on tvOS
|
||||||
|
* Fixed issue with trending categories (Invidious) not working when using non-English language
|
||||||
|
* Fixed issue with search query suggestions not being displayed properly in some languages
|
||||||
|
* Changed subscriptions page picker label from icon to text
|
||||||
|
* Views will display information if there is no videos to show instead of always showing placeholders
|
||||||
|
* Fixed AVPlayer issue with music mode playing video track
|
||||||
|
* Added remove context menu option for all types of recent items in Search
|
||||||
|
* Added advanced setting "Show video context menu options to force selected backend"
|
||||||
|
* Fixed reported crashes
|
||||||
|
* Other minor fixes and improvements
|
||||||
|
|
||||||
|
## Previous Builds
|
||||||
* Improved Home
|
* Improved Home
|
||||||
- Added menu with view options on iOS and toolbar buttons on macOS/tvOS
|
- Added menu with view options on iOS and toolbar buttons on macOS/tvOS
|
||||||
- Added Home Settings
|
- Added Home Settings
|
||||||
@@ -20,9 +34,7 @@
|
|||||||
* Fixed issue with playlists view showing duplicated buttons when "Show cache status" is enabled
|
* Fixed issue with playlists view showing duplicated buttons when "Show cache status" is enabled
|
||||||
* Fixed issue where navigating to channel from list view in Playlists and Search would immediately go back
|
* Fixed issue where navigating to channel from list view in Playlists and Search would immediately go back
|
||||||
* Fixed issue where first URL would fail to open
|
* Fixed issue where first URL would fail to open
|
||||||
* Other minor fixes and improvements
|
|
||||||
|
|
||||||
## Previous Builds
|
|
||||||
* Added support for AVPlayer native system controls on iOS and macOS
|
* Added support for AVPlayer native system controls on iOS and macOS
|
||||||
- Use system features such as AirPlay, subtitles switching (Piped with HLS), text detection and copy and more
|
- Use system features such as AirPlay, subtitles switching (Piped with HLS), text detection and copy and more
|
||||||
- Added Controls setting: "Use system controls with AVPlayer"
|
- Added Controls setting: "Use system controls with AVPlayer"
|
||||||
|
|||||||
14
Extensions/String+ReplacingHTMLEntities.swift
Normal file
14
Extensions/String+ReplacingHTMLEntities.swift
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
var replacingHTMLEntities: String {
|
||||||
|
do {
|
||||||
|
return try NSAttributedString(data: Data(utf8), options: [
|
||||||
|
.documentType: NSAttributedString.DocumentType.html,
|
||||||
|
.characterEncoding: String.Encoding.utf8.rawValue
|
||||||
|
], documentAttributes: nil).string
|
||||||
|
} catch {
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,7 +79,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
|
|
||||||
configureTransformer(pathPattern("search/suggestions"), requestMethods: [.get]) { (content: Entity<JSON>) -> [String] in
|
configureTransformer(pathPattern("search/suggestions"), requestMethods: [.get]) { (content: Entity<JSON>) -> [String] in
|
||||||
if let suggestions = content.json.dictionaryValue["suggestions"] {
|
if let suggestions = content.json.dictionaryValue["suggestions"] {
|
||||||
return suggestions.arrayValue.map(String.init)
|
return suggestions.arrayValue.map { $0.stringValue.replacingHTMLEntities }
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
return []
|
||||||
@@ -236,7 +236,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
|
|
||||||
func trending(country: Country, category: TrendingCategory?) -> Resource {
|
func trending(country: Country, category: TrendingCategory?) -> Resource {
|
||||||
resource(baseURL: account.url, path: "\(Self.basePath)/trending")
|
resource(baseURL: account.url, path: "\(Self.basePath)/trending")
|
||||||
.withParam("type", category?.name)
|
.withParam("type", category?.type)
|
||||||
.withParam("region", country.rawValue)
|
.withParam("region", country.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ final class SubscribedChannelsModel: ObservableObject, CacheModel {
|
|||||||
@Published var error: RequestError?
|
@Published var error: RequestError?
|
||||||
|
|
||||||
var accounts: AccountsModel { .shared }
|
var accounts: AccountsModel { .shared }
|
||||||
|
var unwatchedFeedCount: UnwatchedFeedCountModel { .shared }
|
||||||
|
|
||||||
var resource: Resource? {
|
var resource: Resource? {
|
||||||
accounts.api.subscriptions
|
accounts.api.subscriptions
|
||||||
@@ -32,6 +33,19 @@ final class SubscribedChannelsModel: ObservableObject, CacheModel {
|
|||||||
channels.sorted { $0.name.lowercased() < $1.name.lowercased() }
|
channels.sorted { $0.name.lowercased() < $1.name.lowercased() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var allByUnwatchedCount: [Channel] {
|
||||||
|
if let account = accounts.current {
|
||||||
|
return all.sorted { c1, c2 in
|
||||||
|
let c1HasUnwatched = (unwatchedFeedCount.unwatchedByChannel[account]?[c1.id] ?? -1) > 0
|
||||||
|
let c2HasUnwatched = (unwatchedFeedCount.unwatchedByChannel[account]?[c2.id] ?? -1) > 0
|
||||||
|
let nameIncreasing = c1.name.lowercased() < c2.name.lowercased()
|
||||||
|
|
||||||
|
return c1HasUnwatched ? (c2HasUnwatched ? nameIncreasing : true) : (c2HasUnwatched ? false : nameIncreasing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
|
||||||
func subscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
|
func subscribe(_ channelID: String, onSuccess: @escaping () -> Void = {}) {
|
||||||
accounts.api.subscribe(channelID) {
|
accounts.api.subscribe(channelID) {
|
||||||
self.scheduleLoad(onSuccess: onSuccess)
|
self.scheduleLoad(onSuccess: onSuccess)
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ final class NavigationModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Published var tabSelection: TabSelection!
|
@Published var tabSelection: TabSelection! { didSet {
|
||||||
|
if oldValue == tabSelection { multipleTapHandler() }
|
||||||
|
}}
|
||||||
|
|
||||||
@Published var presentingAddToPlaylist = false
|
@Published var presentingAddToPlaylist = false
|
||||||
@Published var videoToAddToPlaylist: Video!
|
@Published var videoToAddToPlaylist: Video!
|
||||||
@@ -295,6 +297,15 @@ final class NavigationModel: ObservableObject {
|
|||||||
channelPresentedInSheet = channel
|
channelPresentedInSheet = channel
|
||||||
presentingChannelSheet = true
|
presentingChannelSheet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func multipleTapHandler() {
|
||||||
|
switch tabSelection {
|
||||||
|
case .search:
|
||||||
|
self.search.focused = true
|
||||||
|
default:
|
||||||
|
print("not implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias TabSelection = NavigationModel.TabSelection
|
typealias TabSelection = NavigationModel.TabSelection
|
||||||
|
|||||||
@@ -333,6 +333,10 @@ final class AVPlayerBackend: PlayerBackend {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.model.musicMode {
|
||||||
|
self.startMusicMode()
|
||||||
|
}
|
||||||
|
|
||||||
if !preservingTime,
|
if !preservingTime,
|
||||||
!self.model.transitioningToPiP,
|
!self.model.transitioningToPiP,
|
||||||
let segment = self.model.sponsorBlock.segments.first,
|
let segment = self.model.sponsorBlock.segments.first,
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
|
|
||||||
@Published var presentingPlayer = false { didSet { handlePresentationChange() } }
|
@Published var presentingPlayer = false { didSet { handlePresentationChange() } }
|
||||||
@Published var activeBackend = PlayerBackendType.mpv
|
@Published var activeBackend = PlayerBackendType.mpv
|
||||||
|
@Published var forceBackendOnPlay: PlayerBackendType?
|
||||||
|
|
||||||
var avPlayerBackend = AVPlayerBackend()
|
var avPlayerBackend = AVPlayerBackend()
|
||||||
var mpvBackend = MPVBackend()
|
var mpvBackend = MPVBackend()
|
||||||
@@ -235,16 +236,11 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
Delay.by(0.3) {
|
||||||
self?.exitFullScreen(showControls: false)
|
self?.exitFullScreen(showControls: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
|
||||||
if Defaults[.lockPortraitWhenBrowsing] {
|
|
||||||
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
|
||||||
} else {
|
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
Windows.player.hide()
|
Windows.player.hide()
|
||||||
#endif
|
#endif
|
||||||
@@ -387,7 +383,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
if !upgrading, !video.isLocal {
|
if !upgrading, !video.isLocal {
|
||||||
resetSegments()
|
resetSegments()
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.sponsorBlock.loadSegments(
|
self?.sponsorBlock.loadSegments(
|
||||||
videoID: video.videoID,
|
videoID: video.videoID,
|
||||||
categories: Defaults[.sponsorBlockCategories]
|
categories: Defaults[.sponsorBlockCategories]
|
||||||
@@ -407,19 +403,19 @@ final class PlayerModel: ObservableObject {
|
|||||||
resetSegments()
|
resetSegments()
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 0.25) {
|
(withBackend ?? backend).playStream(
|
||||||
(withBackend ?? self.backend).playStream(
|
|
||||||
stream,
|
stream,
|
||||||
of: video,
|
of: video,
|
||||||
preservingTime: preservingTime,
|
preservingTime: preservingTime,
|
||||||
upgrading: upgrading
|
upgrading: upgrading
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.forceBackendOnPlay = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !upgrading {
|
if !upgrading {
|
||||||
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 0.5) {
|
updateCurrentArtwork()
|
||||||
self.updateCurrentArtwork()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +448,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let backend = (live && forceAVPlayerForLiveStreams) ? PlayerBackendType.appleAVPlayer : qualityProfile?.backend,
|
if let backend = forceBackendOnPlay ?? ((live && forceAVPlayerForLiveStreams) ? PlayerBackendType.appleAVPlayer : qualityProfile?.backend),
|
||||||
backend != activeBackend,
|
backend != activeBackend,
|
||||||
backend == .appleAVPlayer || !(avPlayerBackend.startPictureInPictureOnPlay || playingInPictureInPicture)
|
backend == .appleAVPlayer || !(avPlayerBackend.startPictureInPictureOnPlay || playingInPictureInPicture)
|
||||||
{
|
{
|
||||||
@@ -621,6 +617,8 @@ final class PlayerModel: ObservableObject {
|
|||||||
func closeCurrentItem(finished: Bool = false) {
|
func closeCurrentItem(finished: Bool = false) {
|
||||||
pause()
|
pause()
|
||||||
videoBeingOpened = nil
|
videoBeingOpened = nil
|
||||||
|
advancing = false
|
||||||
|
forceBackendOnPlay = nil
|
||||||
|
|
||||||
closing = true
|
closing = true
|
||||||
controls.presentingControls = false
|
controls.presentingControls = false
|
||||||
|
|||||||
@@ -364,10 +364,7 @@ extension PlayerModel {
|
|||||||
message: Text(message),
|
message: Text(message),
|
||||||
primaryButton: .cancel { [weak self] in
|
primaryButton: .cancel { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.advancing = false
|
self.closeCurrentItem()
|
||||||
self.videoBeingOpened = nil
|
|
||||||
self.currentItem = nil
|
|
||||||
self.hide()
|
|
||||||
},
|
},
|
||||||
secondaryButton: retryButton
|
secondaryButton: retryButton
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ final class RecentsModel: ObservableObject {
|
|||||||
|
|
||||||
func addQuery(_ query: String) {
|
func addQuery(_ query: String) {
|
||||||
if !query.isEmpty {
|
if !query.isEmpty {
|
||||||
|
if NavigationModel.shared.tabSelection != .search {
|
||||||
NavigationModel.shared.tabSelection = .search
|
NavigationModel.shared.tabSelection = .search
|
||||||
|
}
|
||||||
add(.init(from: query))
|
add(.init(from: query))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,23 @@ final class SearchModel: ObservableObject {
|
|||||||
@Published var querySuggestions = [String]()
|
@Published var querySuggestions = [String]()
|
||||||
private var suggestionsDebouncer = Debouncer(.milliseconds(200))
|
private var suggestionsDebouncer = Debouncer(.milliseconds(200))
|
||||||
|
|
||||||
|
@Published var focused = false
|
||||||
|
|
||||||
var accounts: AccountsModel { .shared }
|
var accounts: AccountsModel { .shared }
|
||||||
private var resource: Resource!
|
private var resource: Resource!
|
||||||
|
|
||||||
|
init() {
|
||||||
|
#if os(iOS)
|
||||||
|
addKeyboardDidHideNotificationObserver()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
#if os(iOS)
|
||||||
|
removeKeyboardDidHideNotificationObserver()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
var isLoading: Bool {
|
var isLoading: Bool {
|
||||||
resource?.isLoading ?? false
|
resource?.isLoading ?? false
|
||||||
}
|
}
|
||||||
@@ -136,4 +150,18 @@ final class SearchModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
private func addKeyboardDidHideNotificationObserver() {
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardDidHide), name: UIResponder.keyboardDidHideNotification, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func onKeyboardDidHide() {
|
||||||
|
focused = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeKeyboardDidHideNotificationObserver() {
|
||||||
|
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidHideNotification, object: nil)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ final class SponsorBlockAPI: ObservableObject {
|
|||||||
|
|
||||||
self.videoID = videoID
|
self.videoID = videoID
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.requestSegments(categories: categories, completionHandler: completionHandler)
|
self?.requestSegments(categories: categories, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,8 @@ enum TrendingCategory: String, CaseIterable, Identifiable, Defaults.Serializable
|
|||||||
var controlLabel: String {
|
var controlLabel: String {
|
||||||
id == "default" ? "All".localized() : title
|
id == "default" ? "All".localized() : title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var type: String {
|
||||||
|
rawValue.capitalized
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Siesta
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ChannelPlaylistView: View {
|
struct ChannelPlaylistView: View {
|
||||||
var playlist: ChannelPlaylist?
|
var playlist: ChannelPlaylist
|
||||||
var showCloseButton = false
|
var showCloseButton = false
|
||||||
|
|
||||||
@StateObject private var store = Store<ChannelPlaylist>()
|
@StateObject private var store = Store<ChannelPlaylist>()
|
||||||
@@ -15,30 +15,20 @@ struct ChannelPlaylistView: View {
|
|||||||
var player = PlayerModel.shared
|
var player = PlayerModel.shared
|
||||||
@ObservedObject private var recents = RecentsModel.shared
|
@ObservedObject private var recents = RecentsModel.shared
|
||||||
|
|
||||||
|
@State private var isLoading = false
|
||||||
|
|
||||||
private var items: [ContentItem] {
|
private var items: [ContentItem] {
|
||||||
ContentItem.array(of: store.item?.videos ?? [])
|
ContentItem.array(of: store.item?.videos ?? [])
|
||||||
}
|
}
|
||||||
|
|
||||||
private var presentedPlaylist: ChannelPlaylist? {
|
|
||||||
playlist ?? recents.presentedPlaylist
|
|
||||||
}
|
|
||||||
|
|
||||||
private var resource: Resource? {
|
private var resource: Resource? {
|
||||||
guard let playlist = presentedPlaylist else {
|
accounts.api.channelPlaylist(playlist.id)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let resource = accounts.api.channelPlaylist(playlist.id)
|
|
||||||
resource?.addObserver(store)
|
|
||||||
|
|
||||||
return resource
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
HStack {
|
HStack {
|
||||||
if let playlist = presentedPlaylist {
|
|
||||||
ThumbnailView(url: store.item?.thumbnailURL ?? playlist.thumbnailURL)
|
ThumbnailView(url: store.item?.thumbnailURL ?? playlist.thumbnailURL)
|
||||||
.frame(width: 140, height: 80)
|
.frame(width: 140, height: 80)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 2))
|
.clipShape(RoundedRectangle(cornerRadius: 2))
|
||||||
@@ -52,27 +42,29 @@ struct ChannelPlaylistView: View {
|
|||||||
|
|
||||||
FavoriteButton(item: FavoriteItem(section: .channelPlaylist(accounts.app.appType.rawValue, playlist.id, playlist.title)))
|
FavoriteButton(item: FavoriteItem(section: .channelPlaylist(accounts.app.appType.rawValue, playlist.id, playlist.title)))
|
||||||
.labelStyle(.iconOnly)
|
.labelStyle(.iconOnly)
|
||||||
}
|
|
||||||
|
|
||||||
playButtons
|
playButtons
|
||||||
.labelStyle(.iconOnly)
|
.labelStyle(.iconOnly)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
VerticalCells(items: items)
|
VerticalCells(items: items, isLoading: isLoading)
|
||||||
.environment(\.inChannelPlaylistView, true)
|
.environment(\.inChannelPlaylistView, true)
|
||||||
}
|
}
|
||||||
.environment(\.listingStyle, channelPlaylistListingStyle)
|
.environment(\.listingStyle, channelPlaylistListingStyle)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if let playlist = presentedPlaylist,
|
if let cache = ChannelPlaylistsCacheModel.shared.retrievePlaylist(playlist) {
|
||||||
let cache = ChannelPlaylistsCacheModel.shared.retrievePlaylist(playlist)
|
|
||||||
{
|
|
||||||
store.replace(cache)
|
store.replace(cache)
|
||||||
}
|
}
|
||||||
resource?.loadIfNeeded()?.onSuccess { response in
|
isLoading = true
|
||||||
|
resource?
|
||||||
|
.load()
|
||||||
|
.onSuccess { response in
|
||||||
if let playlist: ChannelPlaylist = response.typedContent() {
|
if let playlist: ChannelPlaylist = response.typedContent() {
|
||||||
ChannelPlaylistsCacheModel.shared.storePlaylist(playlist: playlist)
|
ChannelPlaylistsCacheModel.shared.storePlaylist(playlist: playlist)
|
||||||
|
store.replace(playlist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onCompletion { _ in isLoading = false }
|
||||||
}
|
}
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
.background(Color.background(scheme: colorScheme))
|
.background(Color.background(scheme: colorScheme))
|
||||||
@@ -111,15 +103,13 @@ struct ChannelPlaylistView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(label)
|
.navigationTitle(playlist.title)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private var favoriteButton: some View {
|
@ViewBuilder private var favoriteButton: some View {
|
||||||
if let playlist = presentedPlaylist {
|
|
||||||
FavoriteButton(item: FavoriteItem(section: .channelPlaylist(accounts.app.appType.rawValue, playlist.id, playlist.title)))
|
FavoriteButton(item: FavoriteItem(section: .channelPlaylist(accounts.app.appType.rawValue, playlist.id, playlist.title)))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
private var playlistMenu: some View {
|
private var playlistMenu: some View {
|
||||||
@@ -140,13 +130,13 @@ struct ChannelPlaylistView: View {
|
|||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
if let url = store.item?.thumbnailURL ?? playlist?.thumbnailURL {
|
if let url = store.item?.thumbnailURL ?? playlist.thumbnailURL {
|
||||||
ThumbnailView(url: url)
|
ThumbnailView(url: url)
|
||||||
.frame(width: 60, height: 30)
|
.frame(width: 60, height: 30)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 2))
|
.clipShape(RoundedRectangle(cornerRadius: 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(label)
|
Text(playlist.title)
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundColor(.primary)
|
.foregroundColor(.primary)
|
||||||
|
|
||||||
@@ -160,10 +150,6 @@ struct ChannelPlaylistView: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private var label: String {
|
|
||||||
presentedPlaylist?.title ?? ""
|
|
||||||
}
|
|
||||||
|
|
||||||
private var playlistButtonsPlacement: ToolbarItemPlacement {
|
private var playlistButtonsPlacement: ToolbarItemPlacement {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.navigationBarTrailing
|
.navigationBarTrailing
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Siesta
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ChannelVideosView: View {
|
struct ChannelVideosView: View {
|
||||||
var channel: Channel?
|
var channel: Channel
|
||||||
var showCloseButton = false
|
var showCloseButton = false
|
||||||
var inNavigationView = true
|
var inNavigationView = true
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ struct ChannelVideosView: View {
|
|||||||
@Default(.expandChannelDescription) private var expandChannelDescription
|
@Default(.expandChannelDescription) private var expandChannelDescription
|
||||||
|
|
||||||
var presentedChannel: Channel? {
|
var presentedChannel: Channel? {
|
||||||
store.item?.channel ?? channel ?? recents.presentedChannel
|
store.item?.channel ?? channel
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentItems: [ContentItem] {
|
var contentItems: [ContentItem] {
|
||||||
@@ -65,7 +65,7 @@ struct ChannelVideosView: View {
|
|||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VerticalCells(items: contentItems, edgesIgnoringSafeArea: verticalCellsEdgesIgnoringSafeArea) {
|
VerticalCells(items: contentItems, isLoading: resource?.isLoading ?? false, edgesIgnoringSafeArea: verticalCellsEdgesIgnoringSafeArea) {
|
||||||
if let description = presentedChannel?.description, !description.isEmpty {
|
if let description = presentedChannel?.description, !description.isEmpty {
|
||||||
Button {
|
Button {
|
||||||
withAnimation(.spring()) {
|
withAnimation(.spring()) {
|
||||||
@@ -165,10 +165,7 @@ struct ChannelVideosView: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
descriptionExpanded = expandChannelDescription
|
descriptionExpanded = expandChannelDescription
|
||||||
|
|
||||||
if let channel,
|
if let cache = ChannelsCacheModel.shared.retrieve(channel.cacheKey), store.item.isNil {
|
||||||
let cache = ChannelsCacheModel.shared.retrieve(channel.cacheKey),
|
|
||||||
store.item.isNil
|
|
||||||
{
|
|
||||||
store.replace(cache)
|
store.replace(cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ extension Defaults.Keys {
|
|||||||
static let lockPortraitWhenBrowsing = Key<Bool>("lockPortraitWhenBrowsing", default: UIDevice.current.userInterfaceIdiom == .phone)
|
static let lockPortraitWhenBrowsing = Key<Bool>("lockPortraitWhenBrowsing", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||||
#endif
|
#endif
|
||||||
static let showUnwatchedFeedBadges = Key<Bool>("showUnwatchedFeedBadges", default: false)
|
static let showUnwatchedFeedBadges = Key<Bool>("showUnwatchedFeedBadges", default: false)
|
||||||
|
static let keepChannelsWithUnwatchedFeedOnTop = Key<Bool>("keepChannelsWithUnwatchedFeedOnTop", default: true)
|
||||||
static let showToggleWatchedStatusButton = Key<Bool>("showToggleWatchedStatusButton", default: false)
|
static let showToggleWatchedStatusButton = Key<Bool>("showToggleWatchedStatusButton", default: false)
|
||||||
static let expandChannelDescription = Key<Bool>("expandChannelDescription", default: false)
|
static let expandChannelDescription = Key<Bool>("expandChannelDescription", default: false)
|
||||||
static let channelOnThumbnail = Key<Bool>("channelOnThumbnail", default: false)
|
static let channelOnThumbnail = Key<Bool>("channelOnThumbnail", default: false)
|
||||||
@@ -197,6 +198,7 @@ extension Defaults.Keys {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
||||||
|
static let showPlayNowInBackendContextMenu = Key<Bool>("showPlayNowInBackendContextMenu", default: false)
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
static let playerDetailsPageButtonLabelStyleDefault = ButtonLabelStyle.iconAndText
|
static let playerDetailsPageButtonLabelStyleDefault = ButtonLabelStyle.iconAndText
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ struct FavoriteItemView: View {
|
|||||||
@Default(.hideShorts) private var hideShorts
|
@Default(.hideShorts) private var hideShorts
|
||||||
@Default(.hideWatched) private var hideWatched
|
@Default(.hideWatched) private var hideWatched
|
||||||
@Default(.widgetsSettings) private var widgetsSettings
|
@Default(.widgetsSettings) private var widgetsSettings
|
||||||
|
@Default(.visibleSections) private var visibleSections
|
||||||
|
|
||||||
init(item: FavoriteItem) {
|
init(item: FavoriteItem) {
|
||||||
self.item = item
|
self.item = item
|
||||||
@@ -41,20 +42,8 @@ struct FavoriteItemView: View {
|
|||||||
.padding(.leading, 15)
|
.padding(.leading, 15)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if limitedItems.isEmpty, !(resource?.isLoading ?? false) {
|
if limitedItems.isEmpty {
|
||||||
VStack(alignment: .leading) {
|
EmptyItems(isLoading: resource?.isLoading ?? false) { reloadVisibleWatches() }
|
||||||
Text(emptyItemsText)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
|
|
||||||
if hideShorts || hideWatched {
|
|
||||||
AccentButton(text: "Disable filters", maxWidth: nil, verticalPadding: 0, minHeight: 30) {
|
|
||||||
hideShorts = false
|
|
||||||
hideWatched = false
|
|
||||||
reloadVisibleWatches()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
.padding(.vertical, 10)
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
.padding(.horizontal, 40)
|
.padding(.horizontal, 40)
|
||||||
@@ -112,19 +101,6 @@ struct FavoriteItemView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var emptyItemsText: String {
|
|
||||||
var filterText = ""
|
|
||||||
if hideShorts && hideWatched {
|
|
||||||
filterText = "(watched and shorts hidden)"
|
|
||||||
} else if hideShorts {
|
|
||||||
filterText = "(shorts hidden)"
|
|
||||||
} else if hideWatched {
|
|
||||||
filterText = "(watched hidden)"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "No videos to show".localized() + " " + filterText.localized()
|
|
||||||
}
|
|
||||||
|
|
||||||
var contextMenu: some View {
|
var contextMenu: some View {
|
||||||
Group {
|
Group {
|
||||||
if item.section == .history {
|
if item.section == .history {
|
||||||
@@ -285,7 +261,18 @@ struct FavoriteItemView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var navigatableItem: Bool {
|
var navigatableItem: Bool {
|
||||||
item.section != .history
|
switch item.section {
|
||||||
|
case .history:
|
||||||
|
return false
|
||||||
|
case .trending:
|
||||||
|
return visibleSections.contains(.trending)
|
||||||
|
case .subscriptions:
|
||||||
|
return visibleSections.contains(.subscriptions) && accounts.signedIn
|
||||||
|
case .popular:
|
||||||
|
return visibleSections.contains(.popular) && accounts.app.supportsPopular
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var inChannelView: Bool {
|
var inChannelView: Bool {
|
||||||
@@ -321,7 +308,9 @@ struct FavoriteItemView: View {
|
|||||||
itemLabel
|
itemLabel
|
||||||
.foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
}
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemNavigationLink: some View {
|
var itemNavigationLink: some View {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ struct AppTabNavigation: View {
|
|||||||
private var player = PlayerModel.shared
|
private var player = PlayerModel.shared
|
||||||
@ObservedObject private var feed = FeedModel.shared
|
@ObservedObject private var feed = FeedModel.shared
|
||||||
@ObservedObject private var feedCount = UnwatchedFeedCountModel.shared
|
@ObservedObject private var feedCount = UnwatchedFeedCountModel.shared
|
||||||
|
private var recents = RecentsModel.shared
|
||||||
|
|
||||||
@Default(.showHome) private var showHome
|
@Default(.showHome) private var showHome
|
||||||
@Default(.showDocuments) private var showDocuments
|
@Default(.showDocuments) private var showDocuments
|
||||||
@@ -175,9 +176,9 @@ struct AppTabNavigation: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private var channelView: some View {
|
@ViewBuilder private var channelView: some View {
|
||||||
if navigation.presentingChannel {
|
if navigation.presentingChannel, let channel = recents.presentedChannel {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
ChannelVideosView(showCloseButton: true)
|
ChannelVideosView(channel: channel, showCloseButton: true)
|
||||||
}
|
}
|
||||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||||
.environment(\.inChannelView, true)
|
.environment(\.inChannelView, true)
|
||||||
@@ -189,9 +190,9 @@ struct AppTabNavigation: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private var playlistView: some View {
|
@ViewBuilder private var playlistView: some View {
|
||||||
if navigation.presentingPlaylist {
|
if navigation.presentingPlaylist, let playlist = recents.presentedPlaylist {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
ChannelPlaylistView(showCloseButton: true)
|
ChannelPlaylistView(playlist: playlist, showCloseButton: true)
|
||||||
}
|
}
|
||||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||||
.id("channelPlaylist")
|
.id("channelPlaylist")
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ struct PlaylistVideosView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VerticalCells(items: contentItems)
|
VerticalCells(items: contentItems, isLoading: resource?.isLoading ?? false)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
guard contentItems.isEmpty else { return }
|
guard contentItems.isEmpty else { return }
|
||||||
loadResource()
|
loadResource()
|
||||||
|
|||||||
@@ -63,13 +63,11 @@ struct PlaylistsView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
SignInRequiredView(title: "Playlists".localized()) {
|
SignInRequiredView(title: "Playlists".localized()) {
|
||||||
VStack {
|
VStack {
|
||||||
VerticalCells(items: items, allowEmpty: true) { if shouldDisplayHeader { header } }
|
VerticalCells(items: items, isLoading: resource?.isLoading ?? false) { if shouldDisplayHeader { header } }
|
||||||
.environment(\.currentPlaylistID, currentPlaylist?.id)
|
.environment(\.currentPlaylistID, currentPlaylist?.id)
|
||||||
.environment(\.listingStyle, playlistListingStyle)
|
.environment(\.listingStyle, playlistListingStyle)
|
||||||
|
|
||||||
if currentPlaylist != nil, items.isEmpty {
|
if model.all.isEmpty {
|
||||||
hintText("Playlist is empty\n\nTap and hold on a video and then \n\"Add to Playlist\"".localized())
|
|
||||||
} else if model.all.isEmpty {
|
|
||||||
hintText("You have no playlists\n\nTap on \"New Playlist\" to create one".localized())
|
hintText("You have no playlists\n\nTap on \"New Playlist\" to create one".localized())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
Shared/Search/FocusableSearchTextField.swift
Normal file
29
Shared/Search/FocusableSearchTextField.swift
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import Introspect
|
||||||
|
import Repeat
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@available(iOS 15.0, macOS 12, *)
|
||||||
|
struct FocusableSearchTextField: View {
|
||||||
|
@ObservedObject private var state = SearchModel.shared
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@State private var textField: UITextField?
|
||||||
|
#elseif os(macOS)
|
||||||
|
@State private var textField: NSTextField?
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SearchTextField()
|
||||||
|
#if os(iOS)
|
||||||
|
.introspectTextField { field in
|
||||||
|
textField = field
|
||||||
|
}
|
||||||
|
.onChange(of: state.focused) { newValue in
|
||||||
|
if newValue, let textField, !textField.isFirstResponder {
|
||||||
|
textField.becomeFirstResponder()
|
||||||
|
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,8 +95,12 @@ struct SearchView: View {
|
|||||||
filtersMenu
|
filtersMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if #available(macOS 12, *) {
|
||||||
|
FocusableSearchTextField()
|
||||||
|
} else {
|
||||||
SearchTextField()
|
SearchTextField()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
@@ -175,9 +179,13 @@ struct SearchView: View {
|
|||||||
searchMenu
|
searchMenu
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .principal) {
|
ToolbarItem(placement: .principal) {
|
||||||
|
if #available(iOS 15, *) {
|
||||||
|
FocusableSearchTextField()
|
||||||
|
} else {
|
||||||
SearchTextField()
|
SearchTextField()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -236,22 +244,12 @@ struct SearchView: View {
|
|||||||
if showRecentQueries {
|
if showRecentQueries {
|
||||||
recentQueries
|
recentQueries
|
||||||
} else {
|
} else {
|
||||||
VerticalCells(items: state.store.collection, allowEmpty: state.query.isEmpty) {
|
VerticalCells(items: state.store.collection, isLoading: state.isLoading) {
|
||||||
if shouldDisplayHeader {
|
if shouldDisplayHeader {
|
||||||
header
|
header
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.environment(\.loadMoreContentHandler) { state.loadNextPage() }
|
.environment(\.loadMoreContentHandler) { state.loadNextPage() }
|
||||||
|
|
||||||
if noResults {
|
|
||||||
Text("No results")
|
|
||||||
|
|
||||||
if searchFiltersActive {
|
|
||||||
Button("Reset search filters", action: resetFilters)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,12 +270,6 @@ struct SearchView: View {
|
|||||||
searchDuration != .any || searchDate != .any
|
searchDuration != .any || searchDate != .any
|
||||||
}
|
}
|
||||||
|
|
||||||
private func resetFilters() {
|
|
||||||
searchSortOrder = .relevance
|
|
||||||
searchDate = .any
|
|
||||||
searchDuration = .any
|
|
||||||
}
|
|
||||||
|
|
||||||
private var noResults: Bool {
|
private var noResults: Bool {
|
||||||
state.store.collection.isEmpty && !state.isLoading && !state.query.isEmpty
|
state.store.collection.isEmpty && !state.isLoading && !state.query.isEmpty
|
||||||
}
|
}
|
||||||
@@ -325,6 +317,7 @@ struct SearchView: View {
|
|||||||
NavigationLink(destination: recentItemNavigationLinkDestination(item)) {
|
NavigationLink(destination: recentItemNavigationLinkDestination(item)) {
|
||||||
recentItemLabel(item)
|
recentItemLabel(item)
|
||||||
}
|
}
|
||||||
|
.contextMenu { recentItemContextMenu(item) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private func recentItemNavigationLinkDestination(_ item: RecentItem) -> some View {
|
@ViewBuilder private func recentItemNavigationLinkDestination(_ item: RecentItem) -> some View {
|
||||||
@@ -385,7 +378,11 @@ struct SearchView: View {
|
|||||||
} label: {
|
} label: {
|
||||||
recentItemLabel(item)
|
recentItemLabel(item)
|
||||||
}
|
}
|
||||||
.contextMenu {
|
.contextMenu { recentItemContextMenu(item) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func recentItemContextMenu(_ item: RecentItem) -> some View {
|
||||||
|
Group {
|
||||||
removeButton(item)
|
removeButton(item)
|
||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ struct AdvancedSettings: View {
|
|||||||
@Default(.mpvEnableLogging) private var mpvEnableLogging
|
@Default(.mpvEnableLogging) private var mpvEnableLogging
|
||||||
@Default(.showCacheStatus) private var showCacheStatus
|
@Default(.showCacheStatus) private var showCacheStatus
|
||||||
@Default(.feedCacheSize) private var feedCacheSize
|
@Default(.feedCacheSize) private var feedCacheSize
|
||||||
|
@Default(.showPlayNowInBackendContextMenu) private var showPlayNowInBackendContextMenu
|
||||||
|
|
||||||
@State private var filesToShare = [MPVClient.logFile]
|
@State private var filesToShare = [MPVClient.logFile]
|
||||||
@State private var presentingShareSheet = false
|
@State private var presentingShareSheet = false
|
||||||
@@ -56,6 +57,10 @@ struct AdvancedSettings: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder var advancedSettings: some View {
|
@ViewBuilder var advancedSettings: some View {
|
||||||
|
Section(header: SettingsHeader(text: "Advanced")) {
|
||||||
|
showPlayNowInBackendButtonsToggle
|
||||||
|
}
|
||||||
|
|
||||||
Section(header: SettingsHeader(text: "MPV"), footer: mpvFooter) {
|
Section(header: SettingsHeader(text: "MPV"), footer: mpvFooter) {
|
||||||
showMPVPlaybackStatsToggle
|
showMPVPlaybackStatsToggle
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
@@ -115,6 +120,10 @@ struct AdvancedSettings: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var showPlayNowInBackendButtonsToggle: some View {
|
||||||
|
Toggle("Show video context menu options to force selected backend", isOn: $showPlayNowInBackendContextMenu)
|
||||||
|
}
|
||||||
|
|
||||||
var showMPVPlaybackStatsToggle: some View {
|
var showMPVPlaybackStatsToggle: some View {
|
||||||
Toggle("Show playback statistics", isOn: $showMPVPlaybackStats)
|
Toggle("Show playback statistics", isOn: $showMPVPlaybackStats)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ struct BrowsingSettings: View {
|
|||||||
#endif
|
#endif
|
||||||
@Default(.accountPickerDisplaysAnonymousAccounts) private var accountPickerDisplaysAnonymousAccounts
|
@Default(.accountPickerDisplaysAnonymousAccounts) private var accountPickerDisplaysAnonymousAccounts
|
||||||
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
|
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
|
||||||
|
@Default(.keepChannelsWithUnwatchedFeedOnTop) private var keepChannelsWithUnwatchedFeedOnTop
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
|
@Default(.lockPortraitWhenBrowsing) private var lockPortraitWhenBrowsing
|
||||||
@Default(.showDocuments) private var showDocuments
|
@Default(.showDocuments) private var showDocuments
|
||||||
@@ -180,10 +181,12 @@ struct BrowsingSettings: View {
|
|||||||
FeedModel.shared.calculateUnwatchedFeed()
|
FeedModel.shared.calculateUnwatchedFeed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Toggle("Open channels with description expanded", isOn: $expandChannelDescription)
|
Toggle("Open channels with description expanded", isOn: $expandChannelDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Toggle("Keep channels with unwatched videos on top of subscriptions list", isOn: $keepChannelsWithUnwatchedFeedOnTop)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var thumbnailsSettings: some View {
|
private var thumbnailsSettings: some View {
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ struct FavoriteItemEditor: View {
|
|||||||
|
|
||||||
@State private var presentingRemoveAlert = false
|
@State private var presentingRemoveAlert = false
|
||||||
|
|
||||||
|
@Default(.favorites) private var favorites
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack {
|
HStack {
|
||||||
@@ -132,13 +134,13 @@ struct FavoriteItemEditor: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
HStack(spacing: 10) {
|
HStack(spacing: 10) {
|
||||||
FavoriteItemEditorButton {
|
FavoriteItemEditorButton(color: model.canMoveUp(item) ? .accentColor : .secondary) {
|
||||||
Label("Move Up", systemImage: "arrow.up")
|
Label("Move Up", systemImage: "arrow.up")
|
||||||
} onTapGesture: {
|
} onTapGesture: {
|
||||||
model.moveUp(item)
|
model.moveUp(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
FavoriteItemEditorButton {
|
FavoriteItemEditorButton(color: model.canMoveDown(item) ? .accentColor : .secondary) {
|
||||||
Label("Move Down", systemImage: "arrow.down")
|
Label("Move Down", systemImage: "arrow.down")
|
||||||
} onTapGesture: {
|
} onTapGesture: {
|
||||||
model.moveDown(item)
|
model.moveDown(item)
|
||||||
|
|||||||
@@ -11,20 +11,28 @@ struct ChannelsView: View {
|
|||||||
|
|
||||||
@Default(.showCacheStatus) private var showCacheStatus
|
@Default(.showCacheStatus) private var showCacheStatus
|
||||||
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
|
@Default(.showUnwatchedFeedBadges) private var showUnwatchedFeedBadges
|
||||||
|
@Default(.keepChannelsWithUnwatchedFeedOnTop) private var keepChannelsWithUnwatchedFeedOnTop
|
||||||
|
|
||||||
|
@State private var channelLinkActive = false
|
||||||
|
@State private var channelForLink: Channel?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
Section(header: header) {
|
Section(header: header) {
|
||||||
ForEach(subscriptions.all) { channel in
|
ForEach(channels) { channel in
|
||||||
let label = HStack {
|
let label = HStack {
|
||||||
if let url = channel.thumbnailURLOrCached {
|
if let url = channel.thumbnailURLOrCached {
|
||||||
ThumbnailView(url: url)
|
ThumbnailView(url: url)
|
||||||
.frame(width: 35, height: 35)
|
.frame(width: 35, height: 35)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 35))
|
.clipShape(RoundedRectangle(cornerRadius: 35))
|
||||||
Text(channel.name)
|
|
||||||
} else {
|
} else {
|
||||||
Label(channel.name, systemImage: RecentsModel.symbolSystemImage(channel.name))
|
Image(systemName: RecentsModel.symbolSystemImage(channel.name))
|
||||||
|
.imageScale(.large)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.frame(width: 35, height: 35)
|
||||||
}
|
}
|
||||||
|
Text(channel.name)
|
||||||
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
.backport
|
.backport
|
||||||
.badge(showUnwatchedFeedBadges ? feedCount.unwatchedByChannelText(channel) : nil)
|
.badge(showUnwatchedFeedBadges ? feedCount.unwatchedByChannelText(channel) : nil)
|
||||||
@@ -37,9 +45,15 @@ struct ChannelsView: View {
|
|||||||
label
|
label
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
NavigationLink(destination: ChannelVideosView(channel: channel)) {
|
Button {
|
||||||
|
channelForLink = channel
|
||||||
|
channelLinkActive = channelForLink != nil
|
||||||
|
} label: {
|
||||||
label
|
label
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.foregroundColor(.primary)
|
||||||
}
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
.contextMenu {
|
.contextMenu {
|
||||||
@@ -63,6 +77,9 @@ struct ChannelsView: View {
|
|||||||
.listRowSeparator(false)
|
.listRowSeparator(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.background(
|
||||||
|
NavigationLink(destination: ChannelVideosView(channel: channelForLink ?? Video.fixture.channel), isActive: $channelLinkActive, label: EmptyView.init)
|
||||||
|
)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
subscriptions.load()
|
subscriptions.load()
|
||||||
}
|
}
|
||||||
@@ -99,6 +116,10 @@ struct ChannelsView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var channels: [Channel] {
|
||||||
|
keepChannelsWithUnwatchedFeedOnTop ? subscriptions.allByUnwatchedCount : subscriptions.all
|
||||||
|
}
|
||||||
|
|
||||||
var header: some View {
|
var header: some View {
|
||||||
HStack {
|
HStack {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ struct FeedView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VerticalCells(items: videos) { if shouldDisplayHeader { header } }
|
VerticalCells(items: videos, isLoading: feed.isLoading) { if shouldDisplayHeader { header } }
|
||||||
.environment(\.loadMoreContentHandler) { feed.loadNextPage() }
|
.environment(\.loadMoreContentHandler) { feed.loadNextPage() }
|
||||||
.onAppear {
|
.onAppear {
|
||||||
feed.loadResources()
|
feed.loadResources()
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ struct SubscriptionsView: View {
|
|||||||
Label("Channels", systemImage: "person.3.fill").tag(Page.channels)
|
Label("Channels", systemImage: "person.3.fill").tag(Page.channels)
|
||||||
}
|
}
|
||||||
.pickerStyle(.segmented)
|
.pickerStyle(.segmented)
|
||||||
|
.labelStyle(.titleOnly)
|
||||||
|
|
||||||
subscriptionsMenu
|
subscriptionsMenu
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,26 +22,16 @@ struct TrendingView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@State private var error: RequestError?
|
@State private var error: RequestError?
|
||||||
|
@State private var resource: Resource?
|
||||||
|
@State private var isLoading = false
|
||||||
|
|
||||||
init(_ videos: [Video] = [Video]()) {
|
init(_ videos: [Video] = [Video]()) {
|
||||||
self.videos = videos
|
self.videos = videos
|
||||||
}
|
}
|
||||||
|
|
||||||
var resource: Resource {
|
|
||||||
let newResource: Resource
|
|
||||||
|
|
||||||
newResource = accounts.api.trending(country: country, category: category)
|
|
||||||
newResource.addObserver(store)
|
|
||||||
|
|
||||||
return newResource
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
VerticalCells(items: trending, isLoading: isLoading) { if shouldDisplayHeader { header } }
|
||||||
VerticalCells(items: trending) { if shouldDisplayHeader { header } }
|
|
||||||
.environment(\.listingStyle, trendingListingStyle)
|
.environment(\.listingStyle, trendingListingStyle)
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem {
|
ToolbarItem {
|
||||||
RequestErrorButton(error: error)
|
RequestErrorButton(error: error)
|
||||||
@@ -58,18 +48,17 @@ struct TrendingView: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
.onChange(of: category) { _ in updateResource() }
|
||||||
|
.onChange(of: country) { _ in updateResource() }
|
||||||
|
.onChange(of: accounts.current) { _ in updateResource() }
|
||||||
.onChange(of: resource) { _ in
|
.onChange(of: resource) { _ in
|
||||||
resource.load()
|
isLoading = true
|
||||||
|
resource?.load()
|
||||||
.onFailure { self.error = $0 }
|
.onFailure { self.error = $0 }
|
||||||
.onSuccess { _ in self.error = nil }
|
.onSuccess { _ in self.error = nil }
|
||||||
updateFavoriteItem()
|
.onCompletion { _ in self.isLoading = false }
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear { updateResource()
|
||||||
resource.loadIfNeeded()?
|
|
||||||
.onFailure { self.error = $0 }
|
|
||||||
.onSuccess { _ in self.error = nil }
|
|
||||||
|
|
||||||
updateFavoriteItem()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
@@ -85,9 +74,11 @@ struct TrendingView: View {
|
|||||||
}
|
}
|
||||||
.background(
|
.background(
|
||||||
Button("Refresh") {
|
Button("Refresh") {
|
||||||
resource.load()
|
isLoading = true
|
||||||
|
resource?.load()
|
||||||
.onFailure { self.error = $0 }
|
.onFailure { self.error = $0 }
|
||||||
.onSuccess { _ in self.error = nil }
|
.onSuccess { _ in self.error = nil }
|
||||||
|
.onCompletion { _ in self.isLoading = false }
|
||||||
}
|
}
|
||||||
.keyboardShortcut("r")
|
.keyboardShortcut("r")
|
||||||
.opacity(0)
|
.opacity(0)
|
||||||
@@ -96,16 +87,18 @@ struct TrendingView: View {
|
|||||||
#endif
|
#endif
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.refreshControl { refreshControl in
|
.refreshControl { refreshControl in
|
||||||
resource.load().onCompletion { _ in
|
resource?.load().onCompletion { _ in
|
||||||
refreshControl.endRefreshing()
|
refreshControl.endRefreshing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.backport
|
.backport
|
||||||
.refreshable {
|
.refreshable {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
resource.load()
|
isLoading = true
|
||||||
|
resource?.load()
|
||||||
.onFailure { self.error = $0 }
|
.onFailure { self.error = $0 }
|
||||||
.onSuccess { _ in self.error = nil }
|
.onSuccess { _ in self.error = nil }
|
||||||
|
.onCompletion { _ in self.isLoading = false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
@@ -131,9 +124,13 @@ struct TrendingView: View {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
|
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
|
||||||
resource.loadIfNeeded()?
|
let request = resource?.loadIfNeeded()
|
||||||
.onFailure { self.error = $0 }
|
if request != nil {
|
||||||
|
isLoading = true
|
||||||
|
}
|
||||||
|
request?.onFailure { self.error = $0 }
|
||||||
.onSuccess { _ in self.error = nil }
|
.onSuccess { _ in self.error = nil }
|
||||||
|
.onCompletion { _ in self.isLoading = false }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -225,7 +222,7 @@ struct TrendingView: View {
|
|||||||
private var countryButton: some View {
|
private var countryButton: some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
presentingCountrySelection.toggle()
|
presentingCountrySelection.toggle()
|
||||||
resource.removeObservers(ownedBy: store)
|
resource?.removeObservers(ownedBy: store)
|
||||||
}) {
|
}) {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
Label("Country", systemImage: "flag")
|
Label("Country", systemImage: "flag")
|
||||||
@@ -236,6 +233,13 @@ struct TrendingView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateResource() {
|
||||||
|
let resource = accounts.api.trending(country: country, category: category)
|
||||||
|
resource.addObserver(store)
|
||||||
|
self.resource = resource
|
||||||
|
updateFavoriteItem()
|
||||||
|
}
|
||||||
|
|
||||||
private func updateFavoriteItem() {
|
private func updateFavoriteItem() {
|
||||||
favoriteItem = FavoriteItem(section: .trending(country.rawValue, category.rawValue))
|
favoriteItem = FavoriteItem(section: .trending(country.rawValue, category.rawValue))
|
||||||
}
|
}
|
||||||
@@ -254,7 +258,7 @@ struct TrendingView: View {
|
|||||||
HideShortsButtons()
|
HideShortsButtons()
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
resource.load()
|
resource?.load()
|
||||||
.onFailure { self.error = $0 }
|
.onFailure { self.error = $0 }
|
||||||
.onSuccess { _ in self.error = nil }
|
.onSuccess { _ in self.error = nil }
|
||||||
} label: {
|
} label: {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ struct VerticalCells<Header: View>: View {
|
|||||||
@Environment(\.listingStyle) private var listingStyle
|
@Environment(\.listingStyle) private var listingStyle
|
||||||
|
|
||||||
var items = [ContentItem]()
|
var items = [ContentItem]()
|
||||||
var allowEmpty = false
|
var isLoading: Bool
|
||||||
var edgesIgnoringSafeArea = Edge.Set.horizontal
|
var edgesIgnoringSafeArea = Edge.Set.horizontal
|
||||||
|
|
||||||
let header: Header?
|
let header: Header?
|
||||||
@@ -19,25 +19,26 @@ struct VerticalCells<Header: View>: View {
|
|||||||
|
|
||||||
init(
|
init(
|
||||||
items: [ContentItem],
|
items: [ContentItem],
|
||||||
allowEmpty: Bool = false,
|
isLoading: Bool,
|
||||||
edgesIgnoringSafeArea: Edge.Set = .horizontal,
|
edgesIgnoringSafeArea: Edge.Set = .horizontal,
|
||||||
@ViewBuilder header: @escaping () -> Header? = { nil }
|
@ViewBuilder header: @escaping () -> Header? = { nil }
|
||||||
) {
|
) {
|
||||||
self.items = items
|
self.items = items
|
||||||
self.allowEmpty = allowEmpty
|
self.isLoading = isLoading
|
||||||
self.edgesIgnoringSafeArea = edgesIgnoringSafeArea
|
self.edgesIgnoringSafeArea = edgesIgnoringSafeArea
|
||||||
self.header = header()
|
self.header = header()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(
|
init(
|
||||||
items: [ContentItem],
|
items: [ContentItem],
|
||||||
allowEmpty: Bool = false
|
isLoading: Bool
|
||||||
) where Header == EmptyView {
|
) where Header == EmptyView {
|
||||||
self.init(items: items, allowEmpty: allowEmpty) { EmptyView() }
|
self.init(items: items, isLoading: isLoading) { EmptyView() }
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(.vertical, showsIndicators: scrollViewShowsIndicators) {
|
ScrollView(.vertical, showsIndicators: scrollViewShowsIndicators) {
|
||||||
|
Group {
|
||||||
LazyVGrid(columns: adaptiveItem, alignment: .center) {
|
LazyVGrid(columns: adaptiveItem, alignment: .center) {
|
||||||
Section(header: header) {
|
Section(header: header) {
|
||||||
ForEach(contentItems) { item in
|
ForEach(contentItems) { item in
|
||||||
@@ -46,6 +47,21 @@ struct VerticalCells<Header: View>: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.overlay(
|
||||||
|
GeometryReader { proxy in
|
||||||
|
Color.clear
|
||||||
|
.onAppear {
|
||||||
|
gridSize = proxy.size
|
||||||
|
}
|
||||||
|
.onChange(of: proxy.size) { newValue in
|
||||||
|
gridSize = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if !isLoading && gridSize.height < 50 {
|
||||||
|
EmptyItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
.animation(nil)
|
.animation(nil)
|
||||||
@@ -57,7 +73,7 @@ struct VerticalCells<Header: View>: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var contentItems: [ContentItem] {
|
var contentItems: [ContentItem] {
|
||||||
items.isEmpty ? (allowEmpty ? items : ContentItem.placeholders) : items.sorted { $0 < $1 }
|
items.isEmpty && isLoading ? (ContentItem.placeholders) : items.sorted { $0 < $1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMoreContentItemsIfNeeded(current item: ContentItem) {
|
func loadMoreContentItemsIfNeeded(current item: ContentItem) {
|
||||||
@@ -104,7 +120,7 @@ struct VerticalCells<Header: View>: View {
|
|||||||
|
|
||||||
struct VeticalCells_Previews: PreviewProvider {
|
struct VeticalCells_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
VerticalCells(items: ContentItem.array(of: Array(repeating: Video.fixture, count: 30)))
|
VerticalCells(items: ContentItem.array(of: Array(repeating: Video.fixture, count: 30)), isLoading: false)
|
||||||
.injectFixtureEnvironmentObjects()
|
.injectFixtureEnvironmentObjects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,16 +37,9 @@ struct VideoBanner: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .top, spacing: 12) {
|
HStack(alignment: .top, spacing: 12) {
|
||||||
ZStack(alignment: .bottom) {
|
|
||||||
VStack(alignment: .trailing, spacing: 2) {
|
VStack(alignment: .trailing, spacing: 2) {
|
||||||
|
ZStack(alignment: .bottom) {
|
||||||
smallThumbnail
|
smallThumbnail
|
||||||
|
|
||||||
if !timeOnThumbnail, let timeLabel {
|
|
||||||
Text(timeLabel)
|
|
||||||
.font(.caption.monospacedDigit())
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.layoutPriority(1)
|
.layoutPriority(1)
|
||||||
|
|
||||||
ProgressView(value: watch?.progress ?? 44, total: 100)
|
ProgressView(value: watch?.progress ?? 44, total: 100)
|
||||||
@@ -55,6 +48,13 @@ struct VideoBanner: View {
|
|||||||
.opacity(watch?.isShowingProgress ?? false ? 1 : 0)
|
.opacity(watch?.isShowingProgress ?? false ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !timeOnThumbnail, let timeLabel {
|
||||||
|
Text(timeLabel)
|
||||||
|
.font(.caption.monospacedDigit())
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
Group {
|
Group {
|
||||||
if let video {
|
if let video {
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ struct AccentButton: View {
|
|||||||
.frame(maxWidth: maxWidth)
|
.frame(maxWidth: maxWidth)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
.foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.background(buttonBackground)
|
.background(buttonBackground)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var buttonBackground: some View {
|
var buttonBackground: some View {
|
||||||
@@ -40,6 +42,11 @@ struct AccentButton: View {
|
|||||||
|
|
||||||
struct OpenVideosButton_Previews: PreviewProvider {
|
struct OpenVideosButton_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
|
VStack {
|
||||||
AccentButton(text: "Open Videos", imageSystemName: "play.circle.fill")
|
AccentButton(text: "Open Videos", imageSystemName: "play.circle.fill")
|
||||||
|
.padding(.horizontal, 100)
|
||||||
|
AccentButton(text: "Open Videos", imageSystemName: "play.circle.fill")
|
||||||
|
.padding(.horizontal, 100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
62
Shared/Views/EmptyItems.swift
Normal file
62
Shared/Views/EmptyItems.swift
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import Defaults
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct EmptyItems: View {
|
||||||
|
@Default(.hideShorts) private var hideShorts
|
||||||
|
@Default(.hideWatched) private var hideWatched
|
||||||
|
|
||||||
|
var isLoading = false
|
||||||
|
var onDisableFilters: () -> Void = {}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Group {
|
||||||
|
if isLoading {
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
ProgressView()
|
||||||
|
.progressViewStyle(.circular)
|
||||||
|
Text("Loading...")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(emptyItemsText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
if hideShorts || hideWatched {
|
||||||
|
AccentButton(text: "Disable filters", maxWidth: nil, verticalPadding: 0, minHeight: 30) {
|
||||||
|
hideShorts = false
|
||||||
|
hideWatched = false
|
||||||
|
onDisableFilters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyItemsText: String {
|
||||||
|
var filterText = ""
|
||||||
|
if hideShorts && hideWatched {
|
||||||
|
filterText = "(watched and shorts hidden)"
|
||||||
|
} else if hideShorts {
|
||||||
|
filterText = "(shorts hidden)"
|
||||||
|
} else if hideWatched {
|
||||||
|
filterText = "(watched hidden)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "No videos to show".localized() + " " + filterText.localized()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EmptyItems_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
EmptyItems()
|
||||||
|
Spacer()
|
||||||
|
EmptyItems(isLoading: true)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ struct PopularView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VerticalCells(items: videos) { if shouldDisplayHeader { header } }
|
VerticalCells(items: videos, isLoading: resource?.isLoading ?? false) { if shouldDisplayHeader { header } }
|
||||||
.onAppear {
|
.onAppear {
|
||||||
resource?.addObserver(store)
|
resource?.addObserver(store)
|
||||||
resource?.loadIfNeeded()?
|
resource?.loadIfNeeded()?
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ struct VideoContextMenuView: View {
|
|||||||
@FetchRequest private var watchRequest: FetchedResults<Watch>
|
@FetchRequest private var watchRequest: FetchedResults<Watch>
|
||||||
|
|
||||||
@Default(.saveHistory) private var saveHistory
|
@Default(.saveHistory) private var saveHistory
|
||||||
|
@Default(.showPlayNowInBackendContextMenu) private var showPlayNowInBackendContextMenu
|
||||||
|
|
||||||
private var backgroundContext = PersistenceController.shared.container.newBackgroundContext()
|
private var backgroundContext = PersistenceController.shared.container.newBackgroundContext()
|
||||||
|
|
||||||
@@ -71,6 +72,14 @@ struct VideoContextMenuView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if showPlayNowInBackendContextMenu {
|
||||||
|
Section {
|
||||||
|
ForEach(PlayerBackendType.allCases, id: \.self) { backend in
|
||||||
|
playNowInBackendButton(backend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
playNextButton
|
playNextButton
|
||||||
addToQueueButton
|
addToQueueButton
|
||||||
@@ -187,6 +196,20 @@ struct VideoContextMenuView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func playNowInBackendButton(_ backend: PlayerBackendType) -> some View {
|
||||||
|
Button {
|
||||||
|
if player.musicMode {
|
||||||
|
player.toggleMusicMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
player.forceBackendOnPlay = backend
|
||||||
|
|
||||||
|
player.play(video)
|
||||||
|
} label: {
|
||||||
|
Label("Play Now in \(backend.label)", systemImage: "play")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var playNowInPictureInPictureButton: some View {
|
private var playNowInPictureInPictureButton: some View {
|
||||||
Button {
|
Button {
|
||||||
player.avPlayerBackend.startPictureInPictureOnPlay = true
|
player.avPlayerBackend.startPictureInPictureOnPlay = true
|
||||||
|
|||||||
@@ -579,3 +579,15 @@
|
|||||||
"Landscape left" = "Querformat links";
|
"Landscape left" = "Querformat links";
|
||||||
"Landscape right" = "Querformat rechts";
|
"Landscape right" = "Querformat rechts";
|
||||||
"No rotation" = "Keine Drehung";
|
"No rotation" = "Keine Drehung";
|
||||||
|
"Available" = "Verfügbar";
|
||||||
|
"Startup section" = "Bereich Startup";
|
||||||
|
"Home Settings" = "Startseite Einstellungen";
|
||||||
|
"Watched: hidden" = "Beobachtet: versteckt";
|
||||||
|
"(watched and shorts hidden)" = "(beobachtet und shorts versteckt)";
|
||||||
|
"Watched: visible" = "Beobachtet: sichtbar";
|
||||||
|
"No videos to show" = "Keine Videos zu zeigen";
|
||||||
|
"(watched hidden)" = "(versteckt beobachtet)";
|
||||||
|
"(shorts hidden)" = "(shorts versteckt)";
|
||||||
|
"Disable filters" = "Filter deaktivieren";
|
||||||
|
"Limit" = "Grenze";
|
||||||
|
"Are you sure you want to remove %@ from Favorites?" = "Möchten Sie %@ wirklich aus den Favoriten entfernen?";
|
||||||
|
|||||||
@@ -579,3 +579,15 @@
|
|||||||
"Landscape right" = "Paysage droit";
|
"Landscape right" = "Paysage droit";
|
||||||
"No rotation" = "Aucune rotation";
|
"No rotation" = "Aucune rotation";
|
||||||
"Use system controls with AVPlayer" = "Utiliser les contrôles du système avec AVPlayer";
|
"Use system controls with AVPlayer" = "Utiliser les contrôles du système avec AVPlayer";
|
||||||
|
"Available" = "Disponible";
|
||||||
|
"Startup section" = "Section de démarrage";
|
||||||
|
"Home Settings" = "Paramètres de l'accueil";
|
||||||
|
"Watched: hidden" = "Regardées : masquées";
|
||||||
|
"Watched: visible" = "Regardées : visibles";
|
||||||
|
"(watched and shorts hidden)" = "(regardées et shorts masqués)";
|
||||||
|
"Disable filters" = "Désactiver les filtres";
|
||||||
|
"(shorts hidden)" = "(shorts masqués)";
|
||||||
|
"No videos to show" = "Aucune vidéo à afficher";
|
||||||
|
"(watched hidden)" = "(regardées masquées)";
|
||||||
|
"Limit" = "Limite";
|
||||||
|
"Are you sure you want to remove %@ from Favorites?" = "Êtes-vous sûr de vouloir supprimer %@ des favoris ?";
|
||||||
|
|||||||
@@ -202,7 +202,7 @@
|
|||||||
"Translations" = "翻訳";
|
"Translations" = "翻訳";
|
||||||
"Could not refresh Playlists" = "再生リストを更新できません";
|
"Could not refresh Playlists" = "再生リストを更新できません";
|
||||||
"No locations available at the moment" = "現時点で利用可能な場所がありません";
|
"No locations available at the moment" = "現時点で利用可能な場所がありません";
|
||||||
"Show Open Videos quick actions" = "クイック操作に動画を開くを表示";
|
"Show Open Videos quick actions" = "動画を開くクイック操作を表示";
|
||||||
"Show Documents" = "文書を表示";
|
"Show Documents" = "文書を表示";
|
||||||
"Pages toolbar position" = "ページのツールバー位置";
|
"Pages toolbar position" = "ページのツールバー位置";
|
||||||
"Buttons labels" = "ボタンのラベル";
|
"Buttons labels" = "ボタンのラベル";
|
||||||
@@ -513,9 +513,9 @@
|
|||||||
"Typically near or at the end of the video when the credits pop up and/or endcards are shown." = "クレジットのポップアップやエンドカードが表示される、映像の最後のあたりに表示されます。";
|
"Typically near or at the end of the video when the credits pop up and/or endcards are shown." = "クレジットのポップアップやエンドカードが表示される、映像の最後のあたりに表示されます。";
|
||||||
"Verified" = "認証済み";
|
"Verified" = "認証済み";
|
||||||
"Mark channel feed as unwatched" = "チャンネルフィードを未視聴にする";
|
"Mark channel feed as unwatched" = "チャンネルフィードを未視聴にする";
|
||||||
"Open expanded" = "展開時に";
|
"Open expanded" = "展開したまま開く";
|
||||||
"Short videos: hidden" = "ショート動画: 非表示";
|
"Short videos: hidden" = "ショート動画: 非表示";
|
||||||
"Player Bar" = "プレイヤーのバー";
|
"Player Bar" = "プレイヤーバー";
|
||||||
"Play all unwatched" = "未視聴をすべて再生";
|
"Play all unwatched" = "未視聴をすべて再生";
|
||||||
"Double tap gesture" = "ダブルタップ";
|
"Double tap gesture" = "ダブルタップ";
|
||||||
"Single tap gesture" = "シングルタップ";
|
"Single tap gesture" = "シングルタップ";
|
||||||
@@ -531,7 +531,7 @@
|
|||||||
"Switch to public locations" = "公開された場所に切り替え";
|
"Switch to public locations" = "公開された場所に切り替え";
|
||||||
"Explicit reminders to like, subscribe or interact with them on any paid or free platform(s) (e.g. click on a video)." = "有料/無料のプラットフォームかを問わず、いいね、登録などを明示的に操作を促す(例: 動画をクリック)。";
|
"Explicit reminders to like, subscribe or interact with them on any paid or free platform(s) (e.g. click on a video)." = "有料/無料のプラットフォームかを問わず、いいね、登録などを明示的に操作を促す(例: 動画をクリック)。";
|
||||||
"Proxy videos" = "動画閲覧にプロキシ使用";
|
"Proxy videos" = "動画閲覧にプロキシ使用";
|
||||||
"Sections" = "表示部分";
|
"Sections" = "表示する部分";
|
||||||
"System controls show buttons for %@" = "システム制御「%@」用のボタンを表示";
|
"System controls show buttons for %@" = "システム制御「%@」用のボタンを表示";
|
||||||
"You need to create an instance and accounts\nto access %@ section" = "%@ セクションの利用には\nインスタンスとアカウントの作成が必要";
|
"You need to create an instance and accounts\nto access %@ section" = "%@ セクションの利用には\nインスタンスとアカウントの作成が必要";
|
||||||
"You need to select an account\nto access %@ section" = "%@ セクションの利用には\nアカウントの選択が必要";
|
"You need to select an account\nto access %@ section" = "%@ セクションの利用には\nアカウントの選択が必要";
|
||||||
@@ -577,3 +577,14 @@
|
|||||||
"Show Next in Queue" = "「次の再生キュー」を表示";
|
"Show Next in Queue" = "「次の再生キュー」を表示";
|
||||||
"Next in Queue" = "次の再生キュー";
|
"Next in Queue" = "次の再生キュー";
|
||||||
"Inspector" = "ファイルの詳細情報";
|
"Inspector" = "ファイルの詳細情報";
|
||||||
|
"Available" = "選択候補";
|
||||||
|
"Startup section" = "起動時の表示";
|
||||||
|
"Home Settings" = "ホームの設定";
|
||||||
|
"Watched: hidden" = "視聴済み: 隠す";
|
||||||
|
"Watched: visible" = "視聴済み: 表示";
|
||||||
|
"No videos to show" = "表示する動画なし";
|
||||||
|
"(watched and shorts hidden)" = "(視聴済み/ショート非表示)";
|
||||||
|
"(shorts hidden)" = "(ショート非表示)";
|
||||||
|
"(watched hidden)" = "(視聴済み非表示)";
|
||||||
|
"Disable filters" = "絞り込み解除";
|
||||||
|
"Are you sure you want to remove %@ from Favorites?" = "お気に入りから %@ を除去しますか?";
|
||||||
|
|||||||
@@ -579,3 +579,15 @@
|
|||||||
"Close video and player on end" = "Fechar vídeo e player ao final";
|
"Close video and player on end" = "Fechar vídeo e player ao final";
|
||||||
"Your Accounts" = "Suas Contas";
|
"Your Accounts" = "Suas Contas";
|
||||||
"Use system controls with AVPlayer" = "Usar controles do sistema com o AVPlayer";
|
"Use system controls with AVPlayer" = "Usar controles do sistema com o AVPlayer";
|
||||||
|
"Available" = "Disponível";
|
||||||
|
"Startup section" = "Seção ao iniciar";
|
||||||
|
"Home Settings" = "Ajustes da tela Início";
|
||||||
|
"Watched: hidden" = "Assistidos: ocultos";
|
||||||
|
"Watched: visible" = "Assistidos: visíveis";
|
||||||
|
"Disable filters" = "Desativar filtros";
|
||||||
|
"(watched hidden)" = "(assistidos ocultos)";
|
||||||
|
"(shorts hidden)" = "(shorts ocultos)";
|
||||||
|
"Limit" = "Limite";
|
||||||
|
"Are you sure you want to remove %@ from Favorites?" = "Tem certeza que deseja remover %@ dos Favoritos?";
|
||||||
|
"No videos to show" = "Nenhum vídeo para mostrar";
|
||||||
|
"(watched and shorts hidden)" = "(assistidos e shorts ocultos)";
|
||||||
|
|||||||
@@ -579,3 +579,15 @@
|
|||||||
"Your Accounts" = "Conturi tale";
|
"Your Accounts" = "Conturi tale";
|
||||||
"Browse without account" = "Navigați fără cont";
|
"Browse without account" = "Navigați fără cont";
|
||||||
"Close video and player on end" = "Închideți videoclipul și player-ul la sfârșit";
|
"Close video and player on end" = "Închideți videoclipul și player-ul la sfârșit";
|
||||||
|
"Available" = "Disponibil";
|
||||||
|
"Startup section" = "Secțiunea de pornire";
|
||||||
|
"Watched: hidden" = "Vizionat: ascuns";
|
||||||
|
"Watched: visible" = "Vizionat: vizibil";
|
||||||
|
"Disable filters" = "Dezactivează filtrele";
|
||||||
|
"Home Settings" = "Setări de acasă";
|
||||||
|
"(watched and shorts hidden)" = "(vizionat și shorts ascunse)";
|
||||||
|
"No videos to show" = "fără videoclipuri de afișat";
|
||||||
|
"(watched hidden)" = "(vizionat ascuns)";
|
||||||
|
"(shorts hidden)" = "(shorts ascunse)";
|
||||||
|
"Limit" = "Limită";
|
||||||
|
"Are you sure you want to remove %@ from Favorites?" = "Sigur doriți să eliminați %@ din Favorite?";
|
||||||
|
|||||||
@@ -568,6 +568,10 @@
|
|||||||
3776ADD6287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
3776ADD6287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
||||||
3776ADD7287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
3776ADD7287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
||||||
3776ADD8287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
3776ADD8287381240078EBC4 /* Captions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776ADD5287381240078EBC4 /* Captions.swift */; };
|
||||||
|
37772E0D2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37772E0C2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift */; };
|
||||||
|
37772E0E2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37772E0C2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift */; };
|
||||||
|
37772E0F2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37772E0C2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift */; };
|
||||||
|
37772E102A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37772E0C2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift */; };
|
||||||
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
377A20A92693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
||||||
377A20AA2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
377A20AA2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
||||||
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */; };
|
||||||
@@ -686,6 +690,12 @@
|
|||||||
379DC3D128BA4EB400B09677 /* Seek.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC3D028BA4EB400B09677 /* Seek.swift */; };
|
379DC3D128BA4EB400B09677 /* Seek.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC3D028BA4EB400B09677 /* Seek.swift */; };
|
||||||
379DC3D228BA4EB400B09677 /* Seek.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC3D028BA4EB400B09677 /* Seek.swift */; };
|
379DC3D228BA4EB400B09677 /* Seek.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC3D028BA4EB400B09677 /* Seek.swift */; };
|
||||||
379DC3D328BA4EB400B09677 /* Seek.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC3D028BA4EB400B09677 /* Seek.swift */; };
|
379DC3D328BA4EB400B09677 /* Seek.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379DC3D028BA4EB400B09677 /* Seek.swift */; };
|
||||||
|
379E7C2F2A20AF0A00AF8118 /* EmptyItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379E7C2E2A20AF0A00AF8118 /* EmptyItems.swift */; };
|
||||||
|
379E7C302A20AF0A00AF8118 /* EmptyItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379E7C2E2A20AF0A00AF8118 /* EmptyItems.swift */; };
|
||||||
|
379E7C312A20AF0A00AF8118 /* EmptyItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379E7C2E2A20AF0A00AF8118 /* EmptyItems.swift */; };
|
||||||
|
379E7C332A20FE3900AF8118 /* FocusableSearchTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379E7C322A20FE3900AF8118 /* FocusableSearchTextField.swift */; };
|
||||||
|
379E7C342A20FE3900AF8118 /* FocusableSearchTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379E7C322A20FE3900AF8118 /* FocusableSearchTextField.swift */; };
|
||||||
|
379E7C362A2105B900AF8118 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 379E7C352A2105B900AF8118 /* Introspect */; };
|
||||||
379EF9E029AA585F009FE6C6 /* HideShortsButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */; };
|
379EF9E029AA585F009FE6C6 /* HideShortsButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */; };
|
||||||
379EF9E129AA585F009FE6C6 /* HideShortsButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */; };
|
379EF9E129AA585F009FE6C6 /* HideShortsButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */; };
|
||||||
379EF9E229AA585F009FE6C6 /* HideShortsButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */; };
|
379EF9E229AA585F009FE6C6 /* HideShortsButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */; };
|
||||||
@@ -1350,6 +1360,7 @@
|
|||||||
3776925129463C310055EC18 /* PlaylistsCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistsCacheModel.swift; sourceTree = "<group>"; };
|
3776925129463C310055EC18 /* PlaylistsCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistsCacheModel.swift; sourceTree = "<group>"; };
|
||||||
377692552946476F0055EC18 /* ChannelPlaylistsCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelPlaylistsCacheModel.swift; sourceTree = "<group>"; };
|
377692552946476F0055EC18 /* ChannelPlaylistsCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelPlaylistsCacheModel.swift; sourceTree = "<group>"; };
|
||||||
3776ADD5287381240078EBC4 /* Captions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Captions.swift; path = Model/Captions.swift; sourceTree = SOURCE_ROOT; };
|
3776ADD5287381240078EBC4 /* Captions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Captions.swift; path = Model/Captions.swift; sourceTree = SOURCE_ROOT; };
|
||||||
|
37772E0C2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+ReplacingHTMLEntities.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>"; };
|
||||||
377ABC3F286E4AD5009C986F /* InstancesManifest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstancesManifest.swift; sourceTree = "<group>"; };
|
377ABC3F286E4AD5009C986F /* InstancesManifest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstancesManifest.swift; sourceTree = "<group>"; };
|
||||||
377ABC43286E4B74009C986F /* ManifestedInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManifestedInstance.swift; sourceTree = "<group>"; };
|
377ABC43286E4B74009C986F /* ManifestedInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManifestedInstance.swift; sourceTree = "<group>"; };
|
||||||
@@ -1386,6 +1397,8 @@
|
|||||||
379ACB502A1F8DB000E01914 /* HomeSettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeSettingsButton.swift; sourceTree = "<group>"; };
|
379ACB502A1F8DB000E01914 /* HomeSettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeSettingsButton.swift; sourceTree = "<group>"; };
|
||||||
379B0252287A1CDF001015B5 /* OrientationTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrientationTracker.swift; sourceTree = "<group>"; };
|
379B0252287A1CDF001015B5 /* OrientationTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrientationTracker.swift; sourceTree = "<group>"; };
|
||||||
379DC3D028BA4EB400B09677 /* Seek.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Seek.swift; sourceTree = "<group>"; };
|
379DC3D028BA4EB400B09677 /* Seek.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Seek.swift; sourceTree = "<group>"; };
|
||||||
|
379E7C2E2A20AF0A00AF8118 /* EmptyItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyItems.swift; sourceTree = "<group>"; };
|
||||||
|
379E7C322A20FE3900AF8118 /* FocusableSearchTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusableSearchTextField.swift; sourceTree = "<group>"; };
|
||||||
379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */ = {isa = PBXFileReference; indentWidth = 3; lastKnownFileType = sourcecode.swift; path = HideShortsButtons.swift; sourceTree = "<group>"; };
|
379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */ = {isa = PBXFileReference; indentWidth = 3; lastKnownFileType = sourcecode.swift; path = HideShortsButtons.swift; sourceTree = "<group>"; };
|
||||||
379F141E289ECE7F00DE48B5 /* QualitySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QualitySettings.swift; sourceTree = "<group>"; };
|
379F141E289ECE7F00DE48B5 /* QualitySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QualitySettings.swift; sourceTree = "<group>"; };
|
||||||
37A2B345294723850050933E /* CacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheModel.swift; sourceTree = "<group>"; };
|
37A2B345294723850050933E /* CacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheModel.swift; sourceTree = "<group>"; };
|
||||||
@@ -1626,6 +1639,7 @@
|
|||||||
37F7AB5228A94EB900FB46B5 /* IOKit.framework in Frameworks */,
|
37F7AB5228A94EB900FB46B5 /* IOKit.framework in Frameworks */,
|
||||||
370F4FDF27CC16CB001B35DC /* libxcb-shape.0.0.0.dylib in Frameworks */,
|
370F4FDF27CC16CB001B35DC /* libxcb-shape.0.0.0.dylib in Frameworks */,
|
||||||
370F4FE127CC16CB001B35DC /* libuchardet.0.0.7.dylib in Frameworks */,
|
370F4FE127CC16CB001B35DC /* libuchardet.0.0.7.dylib in Frameworks */,
|
||||||
|
379E7C362A2105B900AF8118 /* Introspect in Frameworks */,
|
||||||
370F4FDB27CC16CB001B35DC /* libswscale.6.4.100.dylib in Frameworks */,
|
370F4FDB27CC16CB001B35DC /* libswscale.6.4.100.dylib in Frameworks */,
|
||||||
370F4FDC27CC16CB001B35DC /* libavutil.57.17.100.dylib in Frameworks */,
|
370F4FDC27CC16CB001B35DC /* libavutil.57.17.100.dylib in Frameworks */,
|
||||||
370F4FE327CC16CB001B35DC /* libbrotlicommon.1.dylib in Frameworks */,
|
370F4FE327CC16CB001B35DC /* libbrotlicommon.1.dylib in Frameworks */,
|
||||||
@@ -1897,6 +1911,7 @@
|
|||||||
37FB285D272225E800A57617 /* ContentItemView.swift */,
|
37FB285D272225E800A57617 /* ContentItemView.swift */,
|
||||||
372CFD14285F2E2A00B0B54B /* ControlsBar.swift */,
|
372CFD14285F2E2A00B0B54B /* ControlsBar.swift */,
|
||||||
3748186D26A769D60084E870 /* DetailBadge.swift */,
|
3748186D26A769D60084E870 /* DetailBadge.swift */,
|
||||||
|
379E7C2E2A20AF0A00AF8118 /* EmptyItems.swift */,
|
||||||
37599F37272B4D740087F250 /* FavoriteButton.swift */,
|
37599F37272B4D740087F250 /* FavoriteButton.swift */,
|
||||||
379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */,
|
379EF9DF29AA585F009FE6C6 /* HideShortsButtons.swift */,
|
||||||
37758C0A2A1D1C8B001FD900 /* HideWatchedButtons.swift */,
|
37758C0A2A1D1C8B001FD900 /* HideWatchedButtons.swift */,
|
||||||
@@ -2187,6 +2202,7 @@
|
|||||||
3782B95527557A2400990149 /* Search */ = {
|
3782B95527557A2400990149 /* Search */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
379E7C322A20FE3900AF8118 /* FocusableSearchTextField.swift */,
|
||||||
3782B94E27553A6700990149 /* SearchSuggestions.swift */,
|
3782B94E27553A6700990149 /* SearchSuggestions.swift */,
|
||||||
374710042755291C00CE0F87 /* SearchTextField.swift */,
|
374710042755291C00CE0F87 /* SearchTextField.swift */,
|
||||||
37AAF27F26737550007FC770 /* SearchView.swift */,
|
37AAF27F26737550007FC770 /* SearchView.swift */,
|
||||||
@@ -2280,6 +2296,7 @@
|
|||||||
377ABC47286E5887009C986F /* Sequence+Unique.swift */,
|
377ABC47286E5887009C986F /* Sequence+Unique.swift */,
|
||||||
3782B9512755667600990149 /* String+Format.swift */,
|
3782B9512755667600990149 /* String+Format.swift */,
|
||||||
37270F1B28E06E3E00856150 /* String+Localizable.swift */,
|
37270F1B28E06E3E00856150 /* String+Localizable.swift */,
|
||||||
|
37772E0C2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift */,
|
||||||
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */,
|
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */,
|
||||||
37F7AB4C28A9361F00FB46B5 /* UIDevice+Cellular.swift */,
|
37F7AB4C28A9361F00FB46B5 /* UIDevice+Cellular.swift */,
|
||||||
370B79CB286279BA0045DB77 /* UIViewController+HideHomeIndicator.swift */,
|
370B79CB286279BA0045DB77 /* UIViewController+HideHomeIndicator.swift */,
|
||||||
@@ -2686,6 +2703,7 @@
|
|||||||
374D11E62943C56300CB4350 /* Cache */,
|
374D11E62943C56300CB4350 /* Cache */,
|
||||||
371AC0B1294D1C230085989E /* CachedAsyncImage */,
|
371AC0B1294D1C230085989E /* CachedAsyncImage */,
|
||||||
379325D629A265AE00181CF1 /* Logging */,
|
379325D629A265AE00181CF1 /* Logging */,
|
||||||
|
379E7C352A2105B900AF8118 /* Introspect */,
|
||||||
);
|
);
|
||||||
productName = "Yattee (macOS)";
|
productName = "Yattee (macOS)";
|
||||||
productReference = 37D4B0CF2671614900C925CA /* Yattee.app */;
|
productReference = 37D4B0CF2671614900C925CA /* Yattee.app */;
|
||||||
@@ -3122,6 +3140,7 @@
|
|||||||
374924DA2921050B0017D862 /* LocationsSettings.swift in Sources */,
|
374924DA2921050B0017D862 /* LocationsSettings.swift in Sources */,
|
||||||
378FFBC428660172009E3FBE /* URLParser.swift in Sources */,
|
378FFBC428660172009E3FBE /* URLParser.swift in Sources */,
|
||||||
3784B23D2728B85300B09468 /* ShareButton.swift in Sources */,
|
3784B23D2728B85300B09468 /* ShareButton.swift in Sources */,
|
||||||
|
379E7C332A20FE3900AF8118 /* FocusableSearchTextField.swift in Sources */,
|
||||||
37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||||
3743CA52270F284F00E4D32B /* View+Borders.swift in Sources */,
|
3743CA52270F284F00E4D32B /* View+Borders.swift in Sources */,
|
||||||
3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */,
|
3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */,
|
||||||
@@ -3171,6 +3190,7 @@
|
|||||||
374AB3DB28BCAF7E00DF56FB /* SeekType.swift in Sources */,
|
374AB3DB28BCAF7E00DF56FB /* SeekType.swift in Sources */,
|
||||||
37192D5728B179D60012EEDD /* ChaptersView.swift in Sources */,
|
37192D5728B179D60012EEDD /* ChaptersView.swift in Sources */,
|
||||||
37D836BC294927E700005E5E /* ChannelsCacheModel.swift in Sources */,
|
37D836BC294927E700005E5E /* ChannelsCacheModel.swift in Sources */,
|
||||||
|
379E7C2F2A20AF0A00AF8118 /* EmptyItems.swift in Sources */,
|
||||||
37B81AF926D2C9A700675966 /* VideoPlayerSizeModifier.swift in Sources */,
|
37B81AF926D2C9A700675966 /* VideoPlayerSizeModifier.swift in Sources */,
|
||||||
37C0698227260B2100F7F6CB /* ThumbnailsModel.swift in Sources */,
|
37C0698227260B2100F7F6CB /* ThumbnailsModel.swift in Sources */,
|
||||||
37BC50A82778A84700510953 /* HistorySettings.swift in Sources */,
|
37BC50A82778A84700510953 /* HistorySettings.swift in Sources */,
|
||||||
@@ -3225,6 +3245,7 @@
|
|||||||
37B4E803277D0A72004BF56A /* AppDelegate.swift in Sources */,
|
37B4E803277D0A72004BF56A /* AppDelegate.swift in Sources */,
|
||||||
37FD77002932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */,
|
37FD77002932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */,
|
||||||
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
||||||
|
37772E0D2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */,
|
||||||
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||||
37DCD3112A18E8150059A470 /* OrientationModel.swift in Sources */,
|
37DCD3112A18E8150059A470 /* OrientationModel.swift in Sources */,
|
||||||
3782B9522755667600990149 /* String+Format.swift in Sources */,
|
3782B9522755667600990149 /* String+Format.swift in Sources */,
|
||||||
@@ -3440,6 +3461,7 @@
|
|||||||
377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */,
|
377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */,
|
||||||
374924DB2921050B0017D862 /* LocationsSettings.swift in Sources */,
|
374924DB2921050B0017D862 /* LocationsSettings.swift in Sources */,
|
||||||
371AC0A0294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */,
|
371AC0A0294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */,
|
||||||
|
379E7C342A20FE3900AF8118 /* FocusableSearchTextField.swift in Sources */,
|
||||||
37F5C7E12A1E2AF300927B73 /* ListView.swift in Sources */,
|
37F5C7E12A1E2AF300927B73 /* ListView.swift in Sources */,
|
||||||
37192D5828B179D60012EEDD /* ChaptersView.swift in Sources */,
|
37192D5828B179D60012EEDD /* ChaptersView.swift in Sources */,
|
||||||
3784CDE327772EE40055BBF2 /* Watch.swift in Sources */,
|
3784CDE327772EE40055BBF2 /* Watch.swift in Sources */,
|
||||||
@@ -3479,8 +3501,10 @@
|
|||||||
37599F35272B44000087F250 /* FavoritesModel.swift in Sources */,
|
37599F35272B44000087F250 /* FavoritesModel.swift in Sources */,
|
||||||
376527BC285F60F700102284 /* PlayerTimeModel.swift in Sources */,
|
376527BC285F60F700102284 /* PlayerTimeModel.swift in Sources */,
|
||||||
37F64FE526FE70A60081B69E /* RedrawOnModifier.swift in Sources */,
|
37F64FE526FE70A60081B69E /* RedrawOnModifier.swift in Sources */,
|
||||||
|
379E7C302A20AF0A00AF8118 /* EmptyItems.swift in Sources */,
|
||||||
377ABC45286E4B74009C986F /* ManifestedInstance.swift in Sources */,
|
377ABC45286E4B74009C986F /* ManifestedInstance.swift in Sources */,
|
||||||
3795593727B08538007FF8F4 /* StreamControl.swift in Sources */,
|
3795593727B08538007FF8F4 /* StreamControl.swift in Sources */,
|
||||||
|
37772E0E2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */,
|
||||||
375B8AB428B580D300397B31 /* KeychainModel.swift in Sources */,
|
375B8AB428B580D300397B31 /* KeychainModel.swift in Sources */,
|
||||||
37F7AB5528A951B200FB46B5 /* Power.swift in Sources */,
|
37F7AB5528A951B200FB46B5 /* Power.swift in Sources */,
|
||||||
372CFD16285F2E2A00B0B54B /* ControlsBar.swift in Sources */,
|
372CFD16285F2E2A00B0B54B /* ControlsBar.swift in Sources */,
|
||||||
@@ -3681,6 +3705,7 @@
|
|||||||
3774124B27387D2300423605 /* ThumbnailsModel.swift in Sources */,
|
3774124B27387D2300423605 /* ThumbnailsModel.swift in Sources */,
|
||||||
3774125427387D2300423605 /* Store.swift in Sources */,
|
3774125427387D2300423605 /* Store.swift in Sources */,
|
||||||
37DCD31A2A191A180059A470 /* AVPlayerViewController+FullScreen.swift in Sources */,
|
37DCD31A2A191A180059A470 /* AVPlayerViewController+FullScreen.swift in Sources */,
|
||||||
|
37772E102A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */,
|
||||||
3774125027387D2300423605 /* Video.swift in Sources */,
|
3774125027387D2300423605 /* Video.swift in Sources */,
|
||||||
37EF9A79275BEB8E0043B585 /* CommentView.swift in Sources */,
|
37EF9A79275BEB8E0043B585 /* CommentView.swift in Sources */,
|
||||||
3774125327387D2300423605 /* Country.swift in Sources */,
|
3774125327387D2300423605 /* Country.swift in Sources */,
|
||||||
@@ -3771,6 +3796,7 @@
|
|||||||
37C3A24727235DA70087A57A /* ChannelPlaylist.swift in Sources */,
|
37C3A24727235DA70087A57A /* ChannelPlaylist.swift in Sources */,
|
||||||
3788AC2926F6840700F6BAA9 /* FavoriteItemView.swift in Sources */,
|
3788AC2926F6840700F6BAA9 /* FavoriteItemView.swift in Sources */,
|
||||||
37319F0727103F94004ECCD0 /* PlayerQueue.swift in Sources */,
|
37319F0727103F94004ECCD0 /* PlayerQueue.swift in Sources */,
|
||||||
|
379E7C312A20AF0A00AF8118 /* EmptyItems.swift in Sources */,
|
||||||
3718B9A52921A97F0003DB2E /* InspectorView.swift in Sources */,
|
3718B9A52921A97F0003DB2E /* InspectorView.swift in Sources */,
|
||||||
37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
|
37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
|
||||||
376BE50D27349108009AD608 /* BrowsingSettings.swift in Sources */,
|
376BE50D27349108009AD608 /* BrowsingSettings.swift in Sources */,
|
||||||
@@ -3866,6 +3892,7 @@
|
|||||||
3748186C26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
3748186C26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
||||||
3744A96228B99ADD005DE0A7 /* PlayerControlsLayout.swift in Sources */,
|
3744A96228B99ADD005DE0A7 /* PlayerControlsLayout.swift in Sources */,
|
||||||
3710A55729488C7D006F8025 /* PlaceholderListItem.swift in Sources */,
|
3710A55729488C7D006F8025 /* PlaceholderListItem.swift in Sources */,
|
||||||
|
37772E0F2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */,
|
||||||
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
377A20AB2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
||||||
3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
3748186826A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
||||||
377F9F7D294403F20043F856 /* VideosCacheModel.swift in Sources */,
|
377F9F7D294403F20043F856 /* VideosCacheModel.swift in Sources */,
|
||||||
@@ -4034,7 +4061,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 = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "Open in Yattee/Info.plist";
|
INFOPLIST_FILE = "Open in Yattee/Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Open in Yattee";
|
INFOPLIST_KEY_CFBundleDisplayName = "Open in Yattee";
|
||||||
@@ -4065,7 +4092,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = "Open in Yattee/Info.plist";
|
INFOPLIST_FILE = "Open in Yattee/Info.plist";
|
||||||
@@ -4096,7 +4123,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 = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
@@ -4116,7 +4143,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 = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
@@ -4276,7 +4303,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "iOS/Yattee (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "iOS/Yattee (iOS).entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
@@ -4329,7 +4356,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
|
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION=1";
|
GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION=1";
|
||||||
@@ -4381,7 +4408,7 @@
|
|||||||
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 = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -4423,7 +4450,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = 78Z5H3M6RJ;
|
"DEVELOPMENT_TEAM[sdk=macosx*]" = 78Z5H3M6RJ;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
@@ -4461,7 +4488,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -4485,7 +4512,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -4511,7 +4538,7 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -4536,7 +4563,7 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -4562,7 +4589,7 @@
|
|||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -4602,7 +4629,7 @@
|
|||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "iPhone Distribution";
|
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "iPhone Distribution";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
"DEVELOPMENT_TEAM[sdk=appletvos*]" = 78Z5H3M6RJ;
|
"DEVELOPMENT_TEAM[sdk=appletvos*]" = 78Z5H3M6RJ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -4643,7 +4670,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -4667,7 +4694,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 151;
|
CURRENT_PROJECT_VERSION = 152;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -5166,6 +5193,11 @@
|
|||||||
package = 3799AC0728B03CEC001376F9 /* XCRemoteSwiftPackageReference "ActiveLabel.swift" */;
|
package = 3799AC0728B03CEC001376F9 /* XCRemoteSwiftPackageReference "ActiveLabel.swift" */;
|
||||||
productName = ActiveLabel;
|
productName = ActiveLabel;
|
||||||
};
|
};
|
||||||
|
379E7C352A2105B900AF8118 /* Introspect */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 37BD07C52698B27B003EBB87 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */;
|
||||||
|
productName = Introspect;
|
||||||
|
};
|
||||||
37BADCA42699FB72009BE4FB /* Alamofire */ = {
|
37BADCA42699FB72009BE4FB /* Alamofire */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 37BADCA32699FB72009BE4FB /* XCRemoteSwiftPackageReference "Alamofire" */;
|
package = 37BADCA32699FB72009BE4FB /* XCRemoteSwiftPackageReference "Alamofire" */;
|
||||||
|
|||||||
Reference in New Issue
Block a user