Fix API availability issues for macOS 11.0 and tvOS 15.0

This commit resolves multiple build errors caused by using APIs that
require newer OS versions than the deployment targets (macOS 11.0 and
tvOS 15.0).

macOS fixes:
- Add missing init(frame:) initializer to PlayerLayerView
- Add availability checks for textSelection modifier (macOS 12.0+)
- Add availability checks for AttributedString (macOS 12.0+)
- Add availability checks for listStyle.inset(alternatesRowBackgrounds:) (macOS 12.0+)
- Add availability checks for focusScope modifier (macOS 12.0+)
- Correct listRowSeparator availability from macOS 12.0 to 13.0

tvOS fixes:
- Use older onChange(of:) signature compatible with tvOS 15.0
- Add availability check for Menu with primaryAction (tvOS 17.0+)

All changes include appropriate fallbacks for older OS versions to
maintain backward compatibility.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Arkadiusz Fal
2025-11-09 18:53:06 +01:00
parent b0aaf0080b
commit ce7ba207ea
10 changed files with 106 additions and 42 deletions

View File

@@ -5,7 +5,7 @@ extension Backport where Content: View {
@ViewBuilder func listRowSeparator(_ visible: Bool) -> some View { @ViewBuilder func listRowSeparator(_ visible: Bool) -> some View {
#if !os(tvOS) #if !os(tvOS)
// swiftlint:disable:next deployment_target // swiftlint:disable:next deployment_target
if #available(iOS 15.0, macOS 12.0, *) { if #available(iOS 15.0, macOS 13.0, *) {
content content
.listRowSeparator(visible ? .visible : .hidden) .listRowSeparator(visible ? .visible : .hidden)
} else { } else {

View File

@@ -181,15 +181,19 @@ struct ChannelVideosView: View {
.navigationTitle(navigationTitle) .navigationTitle(navigationTitle)
#endif #endif
return Group { #if os(tvOS)
content return content
#if os(tvOS) .background(Color.background(scheme: colorScheme))
.background(Color.background(scheme: colorScheme)) .focusScope(focusNamespace)
#endif #elseif os(macOS)
#if !os(iOS) if #available(macOS 12.0, *) {
.focusScope(focusNamespace) return content.focusScope(focusNamespace)
#endif } else {
} return content
}
#else
return content
#endif
} }
var verticalCellsEdgesIgnoringSafeArea: Edge.Set { var verticalCellsEdgesIgnoringSafeArea: Edge.Set {

View File

@@ -16,6 +16,11 @@ import Foundation
player.avPlayerBackend.playerLayer player.avPlayerBackend.playerLayer
} }
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
wantsLayer = true
}
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
} }

View File

@@ -238,7 +238,7 @@ struct CommentView: View {
.font(.system(size: 14)) .font(.system(size: 14))
.lineSpacing(3) .lineSpacing(3)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.textSelection(.enabled) .modifier(TextSelectionModifier())
#else #else
Text(comment.text) Text(comment.text)
#endif #endif
@@ -253,6 +253,18 @@ struct CommentView: View {
} }
} }
#if os(macOS)
struct TextSelectionModifier: ViewModifier {
func body(content: Content) -> some View {
if #available(macOS 12.0, *) {
content.textSelection(.enabled)
} else {
content
}
}
}
#endif
#if os(iOS) #if os(iOS)
struct ActiveLabelCommentRepresentable: UIViewRepresentable { struct ActiveLabelCommentRepresentable: UIViewRepresentable {
var text: String var text: String

View File

@@ -58,19 +58,30 @@ struct VideoDescription: View {
@ViewBuilder var textDescription: some View { @ViewBuilder var textDescription: some View {
#if canImport(AppKit) #if canImport(AppKit)
DescriptionWithLinks(description: description, detailsSize: detailsSize) if #available(macOS 12.0, *) {
.frame(maxWidth: .infinity, alignment: .leading) DescriptionWithLinks(description: description, detailsSize: detailsSize)
.lineLimit(expand ? 500 : collapsedLinesDescription) .frame(maxWidth: .infinity, alignment: .leading)
.textSelection(.enabled) .lineLimit(expand ? 500 : collapsedLinesDescription)
.multilineTextAlignment(.leading) .textSelection(.enabled)
.font(.system(size: 14)) .multilineTextAlignment(.leading)
.lineSpacing(3) .font(.system(size: 14))
.allowsHitTesting(expand) .lineSpacing(3)
.allowsHitTesting(expand)
} else {
Text(description)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(expand ? 500 : collapsedLinesDescription)
.multilineTextAlignment(.leading)
.font(.system(size: 14))
.lineSpacing(3)
.allowsHitTesting(expand)
}
#endif #endif
} }
// If possibe convert URLs to clickable links // If possibe convert URLs to clickable links
#if canImport(AppKit) #if canImport(AppKit)
@available(macOS 12.0, *)
struct DescriptionWithLinks: View { struct DescriptionWithLinks: View {
let description: String let description: String
let detailsSize: CGSize? let detailsSize: CGSize?

View File

@@ -242,8 +242,11 @@ struct QualityProfileForm: View {
#if os(macOS) #if os(macOS)
let list = filteredFormatList let list = filteredFormatList
list if #available(macOS 12.0, *) {
.listStyle(.inset(alternatesRowBackgrounds: true)) list.listStyle(.inset(alternatesRowBackgrounds: true))
} else {
list.listStyle(.inset)
}
Spacer() Spacer()
#else #else
filteredFormatList filteredFormatList

View File

@@ -183,10 +183,17 @@ struct QualitySettings: View {
} }
#if os(macOS) #if os(macOS)
List { if #available(macOS 12.0, *) {
list List {
list
}
.listStyle(.inset(alternatesRowBackgrounds: true))
} else {
List {
list
}
.listStyle(.inset)
} }
.listStyle(.inset(alternatesRowBackgrounds: true))
#else #else
list list
#endif #endif

View File

@@ -156,7 +156,7 @@ struct FeedView: View {
.focused(self.$focusedChannel, equals: channel.id) .focused(self.$focusedChannel, equals: channel.id)
} }
} }
.onChange(of: self.focusedChannel) { .onChange(of: self.focusedChannel) { _ in
if self.focusedChannel == "all" { if self.focusedChannel == "all" {
withAnimation { withAnimation {
self.selectedChannel = nil self.selectedChannel = nil
@@ -223,21 +223,37 @@ struct FeedView: View {
var header: some View { var header: some View {
HStack(spacing: 16) { HStack(spacing: 16) {
#if os(tvOS) #if os(tvOS)
Menu { if #available(tvOS 17.0, *) {
accountsPicker Menu {
} label: { accountsPicker
Label("Channels", systemImage: "filemenu.and.selection") } label: {
.labelStyle(.iconOnly) Label("Channels", systemImage: "filemenu.and.selection")
.imageScale(.small) .labelStyle(.iconOnly)
.font(.caption) .imageScale(.small)
} primaryAction: { .font(.caption)
withAnimation { } primaryAction: {
self.feedChannelsViewVisible = true withAnimation {
self.focusedChannel = selectedChannel?.id ?? "all" self.feedChannelsViewVisible = true
self.focusedChannel = selectedChannel?.id ?? "all"
}
} }
.opacity(feedChannelsViewVisible ? 0 : 1)
.frame(minWidth: feedChannelsViewVisible ? 0 : nil, maxWidth: feedChannelsViewVisible ? 0 : nil)
} else {
Button {
withAnimation {
self.feedChannelsViewVisible = true
self.focusedChannel = selectedChannel?.id ?? "all"
}
} label: {
Label("Channels", systemImage: "filemenu.and.selection")
.labelStyle(.iconOnly)
.imageScale(.small)
.font(.caption)
}
.opacity(feedChannelsViewVisible ? 0 : 1)
.frame(minWidth: feedChannelsViewVisible ? 0 : nil, maxWidth: feedChannelsViewVisible ? 0 : nil)
} }
.opacity(feedChannelsViewVisible ? 0 : 1)
.frame(minWidth: feedChannelsViewVisible ? 0 : nil, maxWidth: feedChannelsViewVisible ? 0 : nil)
channelHeaderView channelHeaderView
if selectedChannel == nil { if selectedChannel == nil {
Spacer() Spacer()

View File

@@ -58,8 +58,11 @@ struct TrendingCountry: View {
return Group { return Group {
#if os(macOS) #if os(macOS)
list if #available(macOS 12.0, *) {
.listStyle(.inset(alternatesRowBackgrounds: true)) list.listStyle(.inset(alternatesRowBackgrounds: true))
} else {
list.listStyle(.inset)
}
#else #else
list list
#endif #endif

View File

@@ -71,8 +71,11 @@ struct InstancesSettings: View {
} }
} }
list if #available(macOS 12.0, *) {
.listStyle(.inset(alternatesRowBackgrounds: true)) list.listStyle(.inset(alternatesRowBackgrounds: true))
} else {
list.listStyle(.inset)
}
} }
if selectedInstance != nil, selectedInstance.app.hasFrontendURL { if selectedInstance != nil, selectedInstance.app.hasFrontendURL {