Compare commits

..

17 Commits

Author SHA1 Message Date
Arkadiusz Fal
735e7d62b6 Update changelog for build 210 2025-11-20 18:00:31 +01:00
Arkadiusz Fal
320c16fcc7 Bump build number to 210 2025-11-20 17:56:24 +01:00
Arkadiusz Fal
8c5c503df2 Fix iPad iOS 18 keyboard dismissal issue in search
Removed auto-focus logic that was causing keyboard show/hide loop
on iPad with docked keyboard. The keyboard was repeatedly dismissing
immediately after appearing due to interaction between keyboard
notifications, focus state changes, and view updates.

Changes:
- Removed focused state and keyboard observer from SearchModel
- Removed iOS textField reference (kept macOS only)
- Removed auto-focus logic from FocusableSearchTextField on iOS
- Cleaned up unused focus-related code

The search field now works reliably when tapped manually on iPad.
Auto-focus still works on macOS where it doesn't cause issues.
2025-11-20 17:49:10 +01:00
Arkadiusz Fal
36738572da Fix SwiftFormat and SwiftLint issues
- Fix indentation in AppSidebarNavigation, VideoCell
- Replace && with comma in PlayerModel condition
- Add SwiftLint suppression for necessary tvOS 17.0 availability check
- Update SwiftLint config to use renamed rules and disable false positives
2025-11-20 17:05:22 +01:00
Arkadiusz Fal
9a8ccc366c Clean up trending settings when feature flag is disabled
Add startup cleanup to remove trending-related settings when the feature flag is disabled:
- Remove trending from visible sections
- Reset startup section to home if it was set to trending
- Remove all trending favorite items

This ensures users don't have invalid/broken settings referencing the disabled trending feature.
2025-11-20 13:21:56 +01:00
Arkadiusz Fal
e9ca36f1db Fix Trending menu command to hide instead of disable
Change the Trending menu command to be completely hidden when the feature flag is disabled, rather than just being disabled and still visible in the UI.
2025-11-20 13:18:03 +01:00
Arkadiusz Fal
5b607687d9 Add feature flag to disable Trending functionality
Introduces a feature flag to disable the Trending section across the app. When disabled, all trending-related UI elements, navigation links, and settings are hidden.

Changes:
- Add trendingEnabled feature flag to FeatureFlags.swift (currently disabled)
- Hide Trending tab in AppTabNavigation, Sidebar, and TVNavigationView
- Remove Trending option from visible sections settings
- Remove Trending option from startup section picker
- Disable Trending menu command and keyboard shortcut
- Prevent Trending URL navigation in OpenURLHandler
- Hide Trending in FavoriteItemView navigation
2025-11-20 13:14:31 +01:00
Arkadiusz Fal
e723bb9147 Change Trending icon to arrow.up.right.circle.fill
Replace chart.bar.fill icon with arrow.up.right.circle.fill for the Trending section across tab bar and sidebar navigation.
2025-11-20 13:08:55 +01:00
Arkadiusz Fal
a3747a0975 Change Popular icon to chart.bar.fill
Replace arrow.up.right.circle.fill icon with chart.bar.fill for the Popular section across all navigation contexts (tab bar, sidebar, and view header).
2025-11-20 13:07:32 +01:00
Arkadiusz Fal
bb2bd86c07 Add feature flag to disable hide shorts functionality
The hide shorts feature is no longer working due to API changes that prevent reliable detection of short videos. This commit introduces a feature flag system to disable the functionality while preserving the ability to easily restore it if the API issue is resolved.

Changes:
- Add FeatureFlags.swift with hideShortsEnabled flag (currently disabled)
- Hide all HideShortsButtons UI elements when flag is disabled
- Disable shorts filtering logic in ContentItemView, FavoriteItemView, and FeedModel
- Preserve hideShorts user preference for future restoration
2025-11-20 13:05:12 +01:00
Arkadiusz Fal
680ac9a8a0 Fix keyboard shortcut conflict for Show Player command
Changed Show Player shortcut from Cmd+O to Cmd+Shift+P to avoid
conflict with system Open command.
2025-11-20 00:23:27 +01:00
Arkadiusz Fal
c1b23d20f2 Fix tab selection timing to wait for account sign-in
Tab selection was being set immediately during app configuration, before
the user account had completed sign-in. This caused tabs that require
authentication (like Subscriptions and Playlists) to not be properly
selected on startup since they weren't visible yet.

Changes:
- Add notification system for account configuration completion
- Post notification after all account types finish configuration:
  * Accounts with existing tokens
  * Accounts requiring sign-in (after network request completes)
  * Anonymous/public accounts
  * Error cases (missing credentials, network failures)
- Set up observer before account configuration to ensure notification
  is received
- Set tab selection only when account is fully configured
2025-11-19 23:24:21 +01:00
Arkadiusz Fal
b8f6dabbc9 Update SwiftUI-Introspect to support iOS 26
Upgrade SwiftUI-Introspect dependency from 1.3.0 to 26.0.0 and add iOS 26 support to the introspect modifier in AppSidebarNavigation.
2025-11-19 23:09:18 +01:00
Arkadiusz Fal
1c168bd982 Fix thumbnail aspect ratio to prevent stretching and layout jumps
Fixed issues with thumbnails being stretched vertically and layout jumping during image loading:
- Simplified VideoCellThumbnail to always use 16:9 aspect ratio with .fill mode
- Added matching 16:9 aspect ratio to placeholder with .fill mode to prevent layout shifts
- Removed quality-based aspect ratio selection (4:3 vs 16:9) in favor of consistent 16:9
- Ensures thumbnails maintain proper proportions on both iOS and macOS

This provides consistent sizing across platforms and eliminates the jump when images finish loading.
2025-11-19 23:01:52 +01:00
Arkadiusz Fal
42d53c30db Fix thumbnail aspect ratio in video grid cells
Thumbnails were being stretched vertically due to incorrect aspect ratio handling. Fixed by:
- Using .scaledToFill() on thumbnails to fill the container width
- Constraining container to 16:9 aspect ratio with .fit mode
- Adding matching aspect ratio to placeholder to prevent layout shift during loading

This ensures thumbnails maintain proper proportions while filling the full cell width.
2025-11-19 22:37:05 +01:00
Arkadiusz Fal
a55adb2e65 Fix thumbnail loading for video details
Explicitly specify thumbnail quality order instead of using Thumbnail.Quality.allCases to ensure proper thumbnail URL generation and loading priority.
2025-11-19 22:05:55 +01:00
Arkadiusz Fal
cea296c4b7 Fix audio session interrupting other apps on launch
Previously, the audio session was initialized immediately when the app launched, causing audio from other apps (like Music) to stop even when no video was playing in Yattee.

Changes:
- Remove audio session initialization from AppDelegate launch
- Remove audio session setup from MPVClient initialization
- Update setAudioSessionActive() to configure audio session category before activation

The audio session is now lazily initialized only when playback actually starts:
- For MPV backend: triggered by FILE_LOADED, PLAYBACK_RESTART, AUDIO_RECONFIG events
- For AVPlayer backend: triggered when play() is called

This allows music from other apps to continue playing until a video is actually played in Yattee.
2025-11-19 21:45:56 +01:00
33 changed files with 249 additions and 150 deletions

View File

@@ -8,6 +8,15 @@ disabled_rules:
- multiline_arguments
- implicit_return
- closure_end_indentation
- discarded_notification_center_observer # Observer intentionally lives for app lifetime
# Disable deprecated rules in favor of their renamed versions
- operator_whitespace # renamed to function_name_whitespace
- redundant_optional_initialization # renamed to implicit_optional_initialization
opt_in_rules:
- function_name_whitespace
- implicit_optional_initialization
excluded:
- Vendor
- Tests Apple TV

View File

@@ -1,7 +1,17 @@
## Build 209
## Build 210
## What's Changed
* Trending and Hide Shorts was disabled due to changes in the video apps API
* Fix iPad iOS 18 keyboard dismissal issue in search
* Fix audio session interrupting other apps on launch
* Fix thumbnail loading for video details
* Fix thumbnail aspect ratio to prevent stretching and layout jumps
* Fix keyboard shortcut conflict for Show Player command
## Previous builds
**Build 209:**
* Fix Now Playing controls for both MPV and AVPlayer backends
* Fix thumbnail sizing and aspect ratio issues in video cells (#896)
* Adjust tvOS video cell dimensions for better layout
@@ -10,8 +20,6 @@
* Simplify fullscreen handling for iOS
* Add macOS-specific entitlements for MPV backend
## Previous builds
**Build 208:**
* Enable resizable windows on iPad
* Improve iPad UI behavior and settings layout

View File

@@ -0,0 +1,5 @@
import Foundation
extension Notification.Name {
static let accountConfigurationComplete = Notification.Name("accountConfigurationComplete")
}

View File

@@ -152,6 +152,10 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
FeedModel.shared.onAccountChange()
SubscribedChannelsModel.shared.onAccountChange()
PlaylistsModel.shared.onAccountChange()
DispatchQueue.main.async {
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
}
}
@@ -160,6 +164,9 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
guard !account.anonymous,
(account.token?.isEmpty ?? true) || force
else {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
return
}
@@ -172,6 +179,9 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
title: "Account Error",
message: "Remove and add your account again in Settings."
)
DispatchQueue.main.async {
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
return
}
@@ -212,6 +222,8 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
}
self.configure()
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
}

View File

@@ -174,6 +174,9 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
guard !account.anonymous,
(account.token?.isEmpty ?? true) || force
else {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
return
}
@@ -186,6 +189,9 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
title: "Account Error",
message: "Remove and add your account again in Settings."
)
DispatchQueue.main.async {
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
return
}
@@ -194,6 +200,7 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
title: "Account Error",
message: message ?? "\(response?.response?.statusCode ?? -1) - \(response?.error?.errorDescription ?? "unknown")\nIf this issue persists, try removing and adding your account again in Settings."
)
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
AF
@@ -226,6 +233,8 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
}
self.configure()
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
}

View File

@@ -135,6 +135,10 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
FeedModel.shared.onAccountChange()
SubscribedChannelsModel.shared.onAccountChange()
PlaylistsModel.shared.onAccountChange()
DispatchQueue.main.async {
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
}
}
@@ -149,6 +153,9 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
let username,
let password
else {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
return
}
@@ -184,11 +191,14 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
self.configure()
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
case let .failure(error):
NavigationModel.shared.presentAlert(
title: "Account Error",
message: error.localizedDescription
)
NotificationCenter.default.post(name: .accountConfigurationComplete, object: nil)
}
}
}
@@ -532,7 +542,10 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
let channelId = details["uploaderUrl"]?.string?.components(separatedBy: "/").last ?? "unknown"
let thumbnails: [Thumbnail] = Thumbnail.Quality.allCases.compactMap {
let qualities = [
Thumbnail.Quality.maxresdefault, .high, .medium, .default, .start, .middle, .end
]
let thumbnails: [Thumbnail] = qualities.compactMap {
if let url = buildThumbnailURL(from: content, quality: $0) {
return Thumbnail(url: url, quality: $0)
}

View File

@@ -235,7 +235,7 @@ final class FeedModel: ObservableObject, CacheModel {
let watches = watchFetchRequestResult(videos, context: backgroundContext)
let watchesIDs = watches.map(\.videoID)
let unwatched = videos.filter { video in
if Defaults[.hideShorts], video.short {
if FeatureFlags.hideShortsEnabled, Defaults[.hideShorts], video.short {
return false
}

View File

@@ -318,7 +318,7 @@ final class NavigationModel: ObservableObject {
func multipleTapHandler() {
switch tabSelection {
case .search:
search.focused = true
break
default:
print("not implemented")
}

View File

@@ -135,12 +135,6 @@ final class MPVClient: ObservableObject {
checkError(mpv_initialize(mpv))
#if !os(macOS)
// Set up audio session for Now Playing support
backend?.model.setupAudioSessionForNowPlaying()
backend?.model.updateNowPlayingInfo()
#endif
let api = UnsafeMutableRawPointer(mutating: (MPV_RENDER_API_TYPE_OPENGL as NSString).utf8String)
var initParams = mpv_opengl_init_params(
get_proc_address: getProcAddress,

View File

@@ -650,7 +650,7 @@ final class PlayerModel: ObservableObject {
// When switching away from AVPlayer, clear its current item to release Now Playing control
#if !os(macOS)
if from == .appleAVPlayer && to == .mpv {
if from == .appleAVPlayer, to == .mpv {
avPlayerBackend.avPlayer.replaceCurrentItem(with: nil)
// Clear Now Playing info entirely before MPV takes over
@@ -1394,7 +1394,11 @@ final class PlayerModel: ObservableObject {
func setAudioSessionActive(_ setActive: Bool) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
do {
try AVAudioSession.sharedInstance().setActive(setActive)
let audioSession = AVAudioSession.sharedInstance()
if setActive {
try audioSession.setCategory(.playback, mode: .moviePlayback)
}
try audioSession.setActive(setActive)
} catch {
self.logger.error("Error setting audio session to \(setActive): \(error)")
}

View File

@@ -16,13 +16,9 @@ final class SearchModel: ObservableObject {
@Published var querySuggestions = [String]()
private var suggestionsDebouncer = Debouncer(.milliseconds(200))
@Published var focused = false
@Default(.showSearchSuggestions) private var showSearchSuggestions
#if os(iOS)
var textField: UITextField!
#elseif os(macOS)
#if os(macOS)
var textField: NSTextField!
#endif
@@ -30,15 +26,9 @@ final class SearchModel: ObservableObject {
private var resource: Resource!
init() {
#if os(iOS)
addKeyboardDidHideNotificationObserver()
#endif
}
deinit {
#if os(iOS)
removeKeyboardDidHideNotificationObserver()
#endif
}
var isLoading: Bool {
@@ -158,18 +148,4 @@ 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
}

12
Shared/FeatureFlags.swift Normal file
View File

@@ -0,0 +1,12 @@
import Foundation
/// Feature flags for enabling/disabling functionality across the app
enum FeatureFlags {
/// Controls whether the "Hide Shorts" functionality is available
/// Set to false when the API changes prevent reliable detection of short videos
static let hideShortsEnabled = false
/// Controls whether the "Trending" section is available
/// Set to false to disable trending functionality across the app
static let trendingEnabled = false
}

View File

@@ -50,9 +50,11 @@ struct FavoriteItemView: View {
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(.secondary)
if hideShorts || hideWatched {
if (FeatureFlags.hideShortsEnabled && hideShorts) || hideWatched {
AccentButton(text: "Disable filters", maxWidth: nil, verticalPadding: 0, minHeight: 30) {
if FeatureFlags.hideShortsEnabled {
hideShorts = false
}
hideWatched = false
reloadVisibleWatches()
}
@@ -107,7 +109,7 @@ struct FavoriteItemView: View {
resource?.removeObservers(ownedBy: store)
}
.onChange(of: player.currentVideo) { _ in if !player.presentingPlayer { reloadVisibleWatches() } }
.onChange(of: hideShorts) { _ in if !player.presentingPlayer { reloadVisibleWatches() } }
.onChange(of: hideShorts) { _ in if !player.presentingPlayer && FeatureFlags.hideShortsEnabled { reloadVisibleWatches() } }
.onChange(of: hideWatched) { _ in if !player.presentingPlayer { reloadVisibleWatches() } }
// Delay is necessary to update the list with the new items.
.onChange(of: favoritesChanged) { _ in if !player.presentingPlayer { Delay.by(1.0) { reloadVisibleWatches() } } }
@@ -135,9 +137,9 @@ struct FavoriteItemView: View {
var emptyItemsText: String {
var filterText = ""
if hideShorts && hideWatched {
if FeatureFlags.hideShortsEnabled && hideShorts && hideWatched {
filterText = "(watched and shorts hidden)"
} else if hideShorts {
} else if FeatureFlags.hideShortsEnabled && hideShorts {
filterText = "(shorts hidden)"
} else if hideWatched {
filterText = "(watched hidden)"
@@ -227,7 +229,7 @@ struct FavoriteItemView: View {
return false
}
guard hideShorts, item.contentType == .video, let video = item.video else {
guard FeatureFlags.hideShortsEnabled, hideShorts, item.contentType == .video, let video = item.video else {
return true
}
@@ -351,7 +353,7 @@ struct FavoriteItemView: View {
case .history:
return false
case .trending:
return visibleSections.contains(.trending)
return FeatureFlags.trendingEnabled && visibleSections.contains(.trending)
case .subscriptions:
return visibleSections.contains(.subscriptions) && accounts.signedIn
case .popular:

View File

@@ -36,10 +36,12 @@ struct MenuCommands: Commands {
.disabled(!AccountsModel.shared.app.supportsPopular)
.keyboardShortcut("3")
if FeatureFlags.trendingEnabled {
Button("Trending") {
setTabSelection(.trending)
}
.keyboardShortcut("4")
}
Button("Search") {
setTabSelection(.search)
@@ -76,7 +78,7 @@ struct MenuCommands: Commands {
Button(togglePlayerLabel) {
PlayerModel.shared.togglePlayer()
}
.keyboardShortcut("o")
.keyboardShortcut("p", modifiers: [.command, .shift])
}
}

View File

@@ -15,7 +15,7 @@ struct AppSidebarNavigation: View {
var body: some View {
#if os(iOS)
content.introspect(.viewController, on: .iOS(.v15, .v16, .v17, .v18)) { viewController in
content.introspect(.viewController, on: .iOS(.v15, .v16, .v17, .v18, .v26)) { viewController in
// workaround for an empty supplementary view on launch
// the supplementary view is determined by the default selection inside the
// primary view, but the primary view is not loaded so its selection is not read

View File

@@ -37,7 +37,7 @@ struct AppTabNavigation: View {
popularNavigationView
}
if visibleSections.contains(.trending) {
if FeatureFlags.trendingEnabled && visibleSections.contains(.trending) {
trendingNavigationView
}
@@ -115,7 +115,7 @@ struct AppTabNavigation: View {
.toolbar { toolbarContent }
}
.tabItem {
Label("Popular", systemImage: "arrow.up.right.circle.fill")
Label("Popular", systemImage: "chart.bar.fill")
.accessibility(label: Text("Popular"))
}
.tag(TabSelection.popular)
@@ -126,7 +126,7 @@ struct AppTabNavigation: View {
LazyView(TrendingView())
}
.tabItem {
Label("Trending", systemImage: "chart.bar.fill")
Label("Trending", systemImage: "arrow.up.right.circle.fill")
.accessibility(label: Text("Trending"))
}
.tag(TabSelection.trending)

View File

@@ -95,15 +95,15 @@ struct Sidebar: View {
if visibleSections.contains(.popular), accounts.app.supportsPopular {
NavigationLink(destination: LazyView(PopularView()), tag: TabSelection.popular, selection: $navigation.tabSelection) {
Label("Popular", systemImage: "arrow.up.right.circle")
Label("Popular", systemImage: "chart.bar.fill")
.accessibility(label: Text("Popular"))
}
.id("popular")
}
if visibleSections.contains(.trending) {
if FeatureFlags.trendingEnabled && visibleSections.contains(.trending) {
NavigationLink(destination: LazyView(TrendingView()), tag: TabSelection.trending, selection: $navigation.tabSelection) {
Label("Trending", systemImage: "chart.bar")
Label("Trending", systemImage: "arrow.up.right.circle.fill")
.accessibility(label: Text("Trending"))
}
.id("trending")

View File

@@ -74,6 +74,7 @@ struct OpenURLHandler {
focusMainWindow()
#endif
case .trending:
guard FeatureFlags.trendingEnabled else { return }
navigation.hideViewsAboveBrowser()
navigation.tabSelection = .trending
#if os(macOS)

View File

@@ -3,27 +3,15 @@ import SwiftUI
import SwiftUIIntrospect
struct FocusableSearchTextField: View {
@ObservedObject private var state = SearchModel.shared
var body: some View {
SearchTextField()
#if os(macOS)
.introspect(.textField, on: .macOS(.v12, .v13, .v14, .v15)) { textField in
state.textField = textField
SearchModel.shared.textField = textField
}
.onAppear {
DispatchQueue.main.async {
state.textField?.becomeFirstResponder()
}
}
#elseif os(iOS)
.introspect(.textField, on: .iOS(.v15, .v16, .v17, .v18)) { textField in
state.textField = textField
}
.onChange(of: state.focused) { newValue in
if newValue, let textField = state.textField, !textField.isFirstResponder {
textField.becomeFirstResponder()
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
SearchModel.shared.textField?.becomeFirstResponder()
}
}
#endif

View File

@@ -55,9 +55,9 @@ struct SearchTextField: View {
TextField("Search...", text: $state.queryText) {
state.changeQuery { query in
query.query = state.queryText
navigation.hideKeyboard()
}
RecentsModel.shared.addQuery(state.queryText)
navigation.hideKeyboard()
}
.disableAutocorrection(true)
.textFieldStyle(.plain)

View File

@@ -49,11 +49,11 @@ struct SearchView: View {
.opacity(state.queryText.isEmpty ? 0 : 1)
} else {
results
}
}
.backport
.scrollDismissesKeyboardInteractively()
}
}
}
.environment(\.listingStyle, searchListingStyle)
.toolbar {
ToolbarItem(placement: .principal) {

View File

@@ -258,6 +258,7 @@ struct BrowsingSettings: View {
private var visibleSectionsSettings: some View {
Section(header: SettingsHeader(text: "Sections".localized())) {
ForEach(VisibleSection.allCases, id: \.self) { section in
if section != .trending || FeatureFlags.trendingEnabled {
MultiselectRow(
title: section.title,
selected: visibleSections.contains(section)
@@ -267,6 +268,7 @@ struct BrowsingSettings: View {
}
}
}
}
private var startupSectionPicker: some View {
Group {
@@ -279,17 +281,21 @@ struct BrowsingSettings: View {
Spacer()
Picker("Startup section", selection: $startupSection) {
ForEach(StartupSection.allCases, id: \.rawValue) { section in
if section != .trending || FeatureFlags.trendingEnabled {
Text(section.label).tag(section)
}
}
}
.modifier(SettingsPickerModifier())
}
#else
Picker("Startup section", selection: $startupSection) {
ForEach(StartupSection.allCases, id: \.rawValue) { section in
if section != .trending || FeatureFlags.trendingEnabled {
Text(section.label).tag(section)
}
}
}
.modifier(SettingsPickerModifier())
#endif
}

View File

@@ -223,6 +223,7 @@ struct FeedView: View {
var header: some View {
HStack(spacing: 16) {
#if os(tvOS)
// swiftlint:disable:next deployment_target
if #available(tvOS 17.0, *) {
Menu {
accountsPicker

View File

@@ -76,6 +76,8 @@ struct ThumbnailView: View {
}
var placeholder: some View {
Rectangle().fill(Color("PlaceholderColor"))
Rectangle()
.fill(Color("PlaceholderColor"))
.aspectRatio(Constants.aspectRatio16x9, contentMode: .fill)
}
}

View File

@@ -432,13 +432,10 @@ struct VideoCell: View {
}
private var thumbnailImage: some View {
Group {
VideoCellThumbnail(video: video)
#if os(tvOS)
.frame(minHeight: 320)
#endif
}
.mask(RoundedRectangle(cornerRadius: thumbnailRoundingCornerRadius))
}
@@ -477,11 +474,10 @@ struct VideoCellThumbnail: View {
private var thumbnails: ThumbnailsModel { .shared }
var body: some View {
let (url, quality) = thumbnails.best(video)
let aspectRatio = (quality == .default || quality == .high) ? Constants.aspectRatio4x3 : Constants.aspectRatio16x9
let (url, _) = thumbnails.best(video)
ThumbnailView(url: url)
.aspectRatio(aspectRatio, contentMode: .fill)
.aspectRatio(Constants.aspectRatio16x9, contentMode: .fill)
}
}

View File

@@ -54,7 +54,7 @@ struct ContentItemView: View {
return false
}
guard hideShorts, item.contentType == .video, let video = item.video else {
guard FeatureFlags.hideShortsEnabled, hideShorts, item.contentType == .video, let video = item.video else {
return true
}

View File

@@ -5,6 +5,7 @@ struct HideShortsButtons: View {
@Default(.hideShorts) private var hideShorts
var body: some View {
if FeatureFlags.hideShortsEnabled {
Button {
hideShorts.toggle()
} label: {
@@ -24,6 +25,7 @@ struct HideShortsButtons: View {
}
}
}
}
struct HideShortsButtons_Previews: PreviewProvider {
static var previews: some View {

View File

@@ -104,7 +104,7 @@ struct PopularView: View {
} label: {
HStack(spacing: 12) {
HStack(spacing: 6) {
Image(systemName: "arrow.up.right.circle.fill")
Image(systemName: "chart.bar.fill")
.foregroundColor(.primary)
.imageScale(.small)

View File

@@ -162,6 +162,23 @@ struct YatteeApp: App {
SDWebImageManager.defaultImageCache = PINCache(name: "stream.yattee.app")
NotificationCenter.default.addObserver(
forName: .accountConfigurationComplete,
object: nil,
queue: .main
) { _ in
let startupSection = Defaults[.startupSection]
var section: TabSelection? = startupSection.tabSelection
#if os(macOS)
if section == .playlists {
section = .search
}
#endif
NavigationModel.shared.tabSelection = section ?? .search
}
if !Defaults[.lastAccountIsPublic] {
AccountsModel.shared.configureAccount()
}
@@ -180,17 +197,6 @@ struct YatteeApp: App {
}
}
let startupSection = Defaults[.startupSection]
var section: TabSelection? = startupSection.tabSelection
#if os(macOS)
if section == .playlists {
section = .search
}
#endif
NavigationModel.shared.tabSelection = section ?? .search
DispatchQueue.main.async {
playlists.load()
}
@@ -231,6 +237,10 @@ struct YatteeApp: App {
self.migrateQualityProfiles()
}
DispatchQueue.global(qos: .userInitiated).async {
self.cleanupDisabledFeatures()
}
#if os(iOS)
DispatchQueue.global(qos: .userInitiated).async {
self.migrateRotateToLandscapeOnEnterFullScreen()
@@ -285,6 +295,34 @@ struct YatteeApp: App {
}
#endif
func cleanupDisabledFeatures() {
// Remove trending from visible sections if feature flag is disabled
if !FeatureFlags.trendingEnabled {
var visibleSections = Defaults[.visibleSections]
if visibleSections.contains(.trending) {
visibleSections.remove(.trending)
Defaults[.visibleSections] = visibleSections
}
// Reset startup section if set to trending
if Defaults[.startupSection] == .trending {
Defaults[.startupSection] = .home
}
// Remove trending favorites
let trendingFavorites = favorites.all.filter { item in
if case .trending = item.section {
return true
}
return false
}
for favorite in trendingFavorites {
favorites.remove(favorite)
}
}
}
var navigationStyle: NavigationStyle {
#if os(iOS)
return horizontalSizeClass == .compact ? .tab : .sidebar

View File

@@ -43,6 +43,9 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
1B81344D4D2A0B0363850A9E /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D886FD1371688A42060DF82 /* FeatureFlags.swift */; };
2446210B2B03C320154634A5 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D886FD1371688A42060DF82 /* FeatureFlags.swift */; };
3528A0FEB2B02A52B715041C /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D886FD1371688A42060DF82 /* FeatureFlags.swift */; };
3700155B271B0D4D0049C794 /* PipedAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3700155A271B0D4D0049C794 /* PipedAPI.swift */; };
3700155C271B0D4D0049C794 /* PipedAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3700155A271B0D4D0049C794 /* PipedAPI.swift */; };
3700155D271B0D4D0049C794 /* PipedAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3700155A271B0D4D0049C794 /* PipedAPI.swift */; };
@@ -207,6 +210,9 @@
37319F0627103F94004ECCD0 /* PlayerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37319F0427103F94004ECCD0 /* PlayerQueue.swift */; };
37319F0727103F94004ECCD0 /* PlayerQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37319F0427103F94004ECCD0 /* PlayerQueue.swift */; };
3732BFD028B83763009F3F4D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 3732BFCF28B83763009F3F4D /* KeychainAccess */; };
3736882B2ECE7947006B1D1F /* Notification+Names.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3736882A2ECE7947006B1D1F /* Notification+Names.swift */; };
3736882C2ECE7947006B1D1F /* Notification+Names.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3736882A2ECE7947006B1D1F /* Notification+Names.swift */; };
3736882D2ECE7947006B1D1F /* Notification+Names.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3736882A2ECE7947006B1D1F /* Notification+Names.swift */; };
3738535429451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */; };
3738535529451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */; };
3738535629451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */; };
@@ -1071,6 +1077,8 @@
37FFC440272734C3009FFD26 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FFC43F272734C3009FFD26 /* Throttle.swift */; };
37FFC441272734C3009FFD26 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FFC43F272734C3009FFD26 /* Throttle.swift */; };
37FFC442272734C3009FFD26 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FFC43F272734C3009FFD26 /* Throttle.swift */; };
4EDC5582D5232B58E0E6A3CD /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D886FD1371688A42060DF82 /* FeatureFlags.swift */; };
C61471C67790128B7638173B /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D886FD1371688A42060DF82 /* FeatureFlags.swift */; };
E24DC6582BFA124100BF6187 /* UserAgentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24DC6572BFA124100BF6187 /* UserAgentManager.swift */; };
E24DC6592BFA124100BF6187 /* UserAgentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24DC6572BFA124100BF6187 /* UserAgentManager.swift */; };
E24DC65A2BFA124100BF6187 /* UserAgentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24DC6572BFA124100BF6187 /* UserAgentManager.swift */; };
@@ -1086,6 +1094,9 @@
E27568B92BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27568B82BFAAC2000BDF0AF /* LanguageCodes.swift */; };
E27568BA2BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27568B82BFAAC2000BDF0AF /* LanguageCodes.swift */; };
E27568BB2BFAAC2000BDF0AF /* LanguageCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27568B82BFAAC2000BDF0AF /* LanguageCodes.swift */; };
E69D11698A85867A28CD6A5A /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D886FD1371688A42060DF82 /* FeatureFlags.swift */; };
F18DFC08B722DE4D5ACB791A /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D886FD1371688A42060DF82 /* FeatureFlags.swift */; };
F3BFD18BABAA233ADA094AC6 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D886FD1371688A42060DF82 /* FeatureFlags.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -1215,6 +1226,7 @@
373197D82732015300EF734F /* RelatedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelatedView.swift; sourceTree = "<group>"; };
37319F0427103F94004ECCD0 /* PlayerQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueue.swift; sourceTree = "<group>"; };
37367E582B8F63C200436163 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
3736882A2ECE7947006B1D1F /* Notification+Names.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Names.swift"; sourceTree = "<group>"; };
3738535329451DC800D2D0CB /* BookmarksCacheModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksCacheModel.swift; sourceTree = "<group>"; };
373C8FE3275B955100CB5936 /* CommentsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsPage.swift; sourceTree = "<group>"; };
373CFACA26966264003CB2C6 /* SearchQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchQuery.swift; sourceTree = "<group>"; };
@@ -1558,6 +1570,7 @@
3DA101AD287C30F50027D920 /* DEVELOPMENT_TEAM.template.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DEVELOPMENT_TEAM.template.xcconfig; sourceTree = "<group>"; };
3DA101AE287C30F50027D920 /* DEVELOPMENT_TEAM.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DEVELOPMENT_TEAM.xcconfig; sourceTree = "<group>"; };
3DA101AF287C30F50027D920 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
5D886FD1371688A42060DF82 /* FeatureFlags.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = "<group>"; };
E24DC6572BFA124100BF6187 /* UserAgentManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentManager.swift; sourceTree = "<group>"; };
E25028AF2BF790F5002CB9FC /* HTTPStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPStatus.swift; sourceTree = "<group>"; };
E258F3892BF61BD2005B8C28 /* URLTester.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLTester.swift; sourceTree = "<group>"; };
@@ -2240,6 +2253,7 @@
37C7A9022679058300E721B4 /* Extensions */ = {
isa = PBXGroup;
children = (
3736882A2ECE7947006B1D1F /* Notification+Names.swift */,
379775922689365600DD52A8 /* Array+Next.swift */,
37DCD3162A191A180059A470 /* AVPlayerViewController+FullScreen.swift */,
376578842685429C00D4EA09 /* CaseIterable+Next.swift */,
@@ -2325,6 +2339,7 @@
37D4B0C22671614700C925CA /* YatteeApp.swift */,
37D4B0C42671614800C925CA /* Assets.xcassets */,
37BD07C42698ADEE003EBB87 /* Yattee.entitlements */,
5D886FD1371688A42060DF82 /* FeatureFlags.swift */,
);
path = Shared;
sourceTree = "<group>";
@@ -2832,7 +2847,7 @@
3765917827237D07009F956E /* XCRemoteSwiftPackageReference "PINCache" */,
37CF8B8228535E4F00B71E37 /* XCRemoteSwiftPackageReference "SDWebImage" */,
372AA40E286D067B0000B1DC /* XCRemoteSwiftPackageReference "Repeat" */,
37EE6DC328A305AD00BFD632 /* XCRemoteSwiftPackageReference "Reachability" */,
37EE6DC328A305AD00BFD632 /* XCRemoteSwiftPackageReference "Reachability.swift" */,
3799AC0728B03CEC001376F9 /* XCRemoteSwiftPackageReference "ActiveLabel.swift" */,
375B8AAF28B57F4200397B31 /* XCRemoteSwiftPackageReference "KeychainAccess" */,
3797104728D3D10600D5F53C /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */,
@@ -3064,6 +3079,7 @@
3762C46D2BF66CDD008E50B8 /* EnvironmentValues.swift in Sources */,
37095E82291DC85400301883 /* ShareViewController.swift in Sources */,
3762C47A2BF66F04008E50B8 /* Strings.swift in Sources */,
C61471C67790128B7638173B /* FeatureFlags.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3076,6 +3092,7 @@
37C0C0FF28665EAC007F6F78 /* VideosApp.swift in Sources */,
378FFBC92866018A009E3FBE /* URLParserTests.swift in Sources */,
371B88F82A1A310100D57683 /* String+Format.swift in Sources */,
3528A0FEB2B02A52B715041C /* FeatureFlags.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3297,6 +3314,7 @@
37BA794F26DC3E0E002A0235 /* Int+Format.swift in Sources */,
378E9C3C2945565500B2D696 /* SubscriptionsView.swift in Sources */,
37D6025928C17375009E8D98 /* PlaybackStatsView.swift in Sources */,
3736882D2ECE7947006B1D1F /* Notification+Names.swift in Sources */,
37F49BA326CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
374C053B2724614F009BDDBE /* PlayerTVMenu.swift in Sources */,
37A9965A26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
@@ -3403,6 +3421,7 @@
37B795902771DAE0001CF27B /* OpenURLHandler.swift in Sources */,
37732FF02703A26300F04329 /* AccountValidationStatus.swift in Sources */,
37A7D72F2B681011009CB1ED /* OtherDataSettingsGroupImporter.swift in Sources */,
2446210B2B03C320154634A5 /* FeatureFlags.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3644,6 +3663,7 @@
37A81BFA294BD1440081D322 /* WatchView.swift in Sources */,
3700155C271B0D4D0049C794 /* PipedAPI.swift in Sources */,
376BE50C27349108009AD608 /* BrowsingSettings.swift in Sources */,
3736882C2ECE7947006B1D1F /* Notification+Names.swift in Sources */,
3710A55629488C7D006F8025 /* PlaceholderListItem.swift in Sources */,
37EBD8CB27AF26C200F1C24B /* MPVBackend.swift in Sources */,
37D4B19826717E1500C925CA /* Video.swift in Sources */,
@@ -3710,6 +3730,7 @@
3769C02F2779F18600DDB3EA /* PlaceholderProgressView.swift in Sources */,
37BA794426DBA973002A0235 /* PlaylistsModel.swift in Sources */,
37A362BF29537AAA00BDF328 /* PlaybackSettings.swift in Sources */,
4EDC5582D5232B58E0E6A3CD /* FeatureFlags.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3718,6 +3739,7 @@
buildActionMask = 2147483647;
files = (
37D4B0D92671614900C925CA /* Tests_iOS.swift in Sources */,
F3BFD18BABAA233ADA094AC6 /* FeatureFlags.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3771,6 +3793,7 @@
3766AFD2273DA97D00686348 /* Int+FormatTests.swift in Sources */,
3774124F27387D2300423605 /* SubscribedChannelsModel.swift in Sources */,
3774126127387D2D00423605 /* AccountsModel.swift in Sources */,
F18DFC08B722DE4D5ACB791A /* FeatureFlags.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3942,6 +3965,7 @@
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
3756C2A82861131100E4B059 /* NetworkState.swift in Sources */,
376578932685490700D4EA09 /* PlaylistsView.swift in Sources */,
3736882B2ECE7947006B1D1F /* Notification+Names.swift in Sources */,
37E75CCD2B6AEB01003A6237 /* RecentlyOpenedExporter.swift in Sources */,
377FF891291A99580028EB0B /* HistoryView.swift in Sources */,
37CC3F47270CE30600608308 /* PlayerQueueItem.swift in Sources */,
@@ -4064,6 +4088,7 @@
3797758D2689345500DD52A8 /* Store.swift in Sources */,
37484C2F26FC844700287258 /* InstanceSettings.swift in Sources */,
37A7D7312B681011009CB1ED /* OtherDataSettingsGroupImporter.swift in Sources */,
1B81344D4D2A0B0363850A9E /* FeatureFlags.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4072,6 +4097,7 @@
buildActionMask = 2147483647;
files = (
37D4B176267164B000C925CA /* YatteeUITests.swift in Sources */,
E69D11698A85867A28CD6A5A /* FeatureFlags.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4142,7 +4168,7 @@
CODE_SIGN_ENTITLEMENTS = "Open in Yattee/Open in Yattee.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Open in Yattee/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "Open in Yattee";
@@ -4173,7 +4199,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Open in Yattee/Info.plist";
@@ -4204,7 +4230,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MACOSX_DEPLOYMENT_TARGET = 14.0;
@@ -4224,7 +4250,7 @@
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MACOSX_DEPLOYMENT_TARGET = 14.0;
@@ -4388,7 +4414,7 @@
CODE_SIGN_ENTITLEMENTS = "iOS/Yattee (iOS).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -4442,7 +4468,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 78Z5H3M6RJ;
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "GLES_SILENCE_DEPRECATION=1";
@@ -4496,7 +4522,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
DEAD_CODE_STRIPPING = YES;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
@@ -4535,7 +4561,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
DEAD_CODE_STRIPPING = YES;
"DEVELOPMENT_TEAM[sdk=macosx*]" = 78Z5H3M6RJ;
ENABLE_APP_SANDBOX = YES;
@@ -4570,7 +4596,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -4593,7 +4619,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -4618,7 +4644,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
DEAD_CODE_STRIPPING = YES;
GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = (
@@ -4642,7 +4668,7 @@
buildSettings = {
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
DEAD_CODE_STRIPPING = YES;
GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = (
@@ -4668,7 +4694,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
DEVELOPMENT_ASSET_PATHS = "";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -4708,7 +4734,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
DEVELOPMENT_ASSET_PATHS = "";
"DEVELOPMENT_TEAM[sdk=appletvos*]" = 78Z5H3M6RJ;
ENABLE_PREVIEWS = YES;
@@ -4748,7 +4774,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -4771,7 +4797,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 209;
CURRENT_PROJECT_VERSION = 210;
GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -5034,7 +5060,7 @@
repositoryURL = "https://github.com/siteline/SwiftUI-Introspect.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.3.0;
minimumVersion = 26.0.0;
};
};
37CF8B8228535E4F00B71E37 /* XCRemoteSwiftPackageReference "SDWebImage" */ = {
@@ -5053,7 +5079,7 @@
minimumVersion = 5.0.2;
};
};
37EE6DC328A305AD00BFD632 /* XCRemoteSwiftPackageReference "Reachability" */ = {
37EE6DC328A305AD00BFD632 /* XCRemoteSwiftPackageReference "Reachability.swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/ashleymills/Reachability.swift";
requirement = {
@@ -5335,7 +5361,7 @@
};
37EE6DC428A305AD00BFD632 /* Reachability */ = {
isa = XCSwiftPackageProductDependency;
package = 37EE6DC328A305AD00BFD632 /* XCRemoteSwiftPackageReference "Reachability" */;
package = 37EE6DC328A305AD00BFD632 /* XCRemoteSwiftPackageReference "Reachability.swift" */;
productName = Reachability;
};
37FB2848272207F000A57617 /* SDWebImageWebPCoder */ = {

View File

@@ -168,8 +168,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"state" : {
"revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336",
"version" : "1.3.0"
"revision" : "a08b87f96b41055577721a6e397562b21ad52454",
"version" : "26.0.0"
}
},
{

View File

@@ -22,13 +22,6 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
OrientationTracker.shared.startDeviceOrientationTracking()
OrientationModel.shared.startOrientationUpdates()
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
logger.error("Failed to set audio session category: \(error)")
}
UIApplication.shared.beginReceivingRemoteControlEvents()
#endif

View File

@@ -29,7 +29,7 @@ struct TVNavigationView: View {
.tag(TabSelection.popular)
}
if visibleSections.contains(.trending) {
if FeatureFlags.trendingEnabled && visibleSections.contains(.trending) {
LazyView(TrendingView())
.tabItem { Text("Trending") }
.tag(TabSelection.trending)