mirror of
https://github.com/yattee/yattee.git
synced 2026-05-12 10:25:02 +00:00
Add channel video search on tvOS
Surface the existing in-channel search feature on tvOS as a final tab after Playlists. Selecting it reveals a search field and results area below the tab row, reusing the same API and result views as iOS/macOS.
This commit is contained in:
@@ -8359,6 +8359,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"search.title" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Search"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"search.type.all" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
@@ -14212,17 +14222,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar.mainItem.playlists" : {
|
||||
"comment" : "Sidebar main navigation item for playlists",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Playlists"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar.mainItem.downloads" : {
|
||||
"comment" : "Sidebar main navigation item for downloads",
|
||||
"localizations" : {
|
||||
@@ -14267,6 +14266,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar.mainItem.playlists" : {
|
||||
"comment" : "Sidebar main navigation item for playlists",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Playlists"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar.mainItem.remoteControl" : {
|
||||
"comment" : "Sidebar main navigation item for remote control",
|
||||
"localizations" : {
|
||||
|
||||
@@ -84,7 +84,9 @@ struct ChannelView: View {
|
||||
#if os(tvOS)
|
||||
// tvOS-specific state
|
||||
@State private var isDescriptionScrollLocked = false
|
||||
@State private var tvOSShowSearchTab = false
|
||||
@FocusState private var isSubscribeFocused: Bool
|
||||
@FocusState private var isTVSearchFieldFocused: Bool
|
||||
#endif
|
||||
|
||||
// Header configuration
|
||||
@@ -630,13 +632,43 @@ struct ChannelView: View {
|
||||
ForEach([ChannelTab.videos, .shorts, .streams, .playlists]) { tab in
|
||||
tvOSTabButton(for: tab)
|
||||
}
|
||||
if supportsChannelSearch {
|
||||
tvOSSearchTabButton
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var tvOSSearchTabButton: some View {
|
||||
let isSelected = tvOSShowSearchTab
|
||||
let action = {
|
||||
if !tvOSShowSearchTab {
|
||||
tvOSShowSearchTab = true
|
||||
isSearchActive = true
|
||||
}
|
||||
}
|
||||
let label = Label(String(localized: "search.title"), systemImage: "magnifyingglass")
|
||||
.fontWeight(isSelected ? .bold : .regular)
|
||||
.lineLimit(1)
|
||||
|
||||
if isSelected {
|
||||
Button(action: action) { label }
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(accentColor)
|
||||
} else {
|
||||
Button(action: action) { label }
|
||||
.buttonStyle(.bordered)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func tvOSTabButton(for tab: ChannelTab) -> some View {
|
||||
let isSelected = selectedTab == tab
|
||||
let isSelected = !tvOSShowSearchTab && selectedTab == tab
|
||||
let action = {
|
||||
if tvOSShowSearchTab {
|
||||
tvOSShowSearchTab = false
|
||||
isSearchActive = false
|
||||
}
|
||||
if selectedTab != tab {
|
||||
selectedTab = tab
|
||||
Task {
|
||||
@@ -646,6 +678,7 @@ struct ChannelView: View {
|
||||
}
|
||||
let label = Label(tab.title, systemImage: tab.systemImage)
|
||||
.fontWeight(isSelected ? .bold : .regular)
|
||||
.lineLimit(1)
|
||||
|
||||
if isSelected {
|
||||
Button(action: action) { label }
|
||||
@@ -663,28 +696,57 @@ struct ChannelView: View {
|
||||
tvOSTabButtons
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Group {
|
||||
switch selectedTab {
|
||||
case .videos:
|
||||
videosGrid
|
||||
case .shorts:
|
||||
shortsGrid
|
||||
case .streams:
|
||||
streamsGrid
|
||||
case .playlists:
|
||||
playlistsGrid
|
||||
case .about:
|
||||
videosGrid
|
||||
if tvOSShowSearchTab {
|
||||
tvOSChannelSearchContent
|
||||
} else {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Group {
|
||||
switch selectedTab {
|
||||
case .videos:
|
||||
videosGrid
|
||||
case .shorts:
|
||||
shortsGrid
|
||||
case .streams:
|
||||
streamsGrid
|
||||
case .playlists:
|
||||
playlistsGrid
|
||||
case .about:
|
||||
videosGrid
|
||||
}
|
||||
}
|
||||
.id(selectedTab)
|
||||
}
|
||||
.id(selectedTab)
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
.scrollClipDisabled()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var tvOSChannelSearchContent: some View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
HStack(spacing: 24) {
|
||||
TextField(String(localized: "search.placeholder"), text: $searchText)
|
||||
.textFieldStyle(.plain)
|
||||
.focused($isTVSearchFieldFocused)
|
||||
.onSubmit {
|
||||
Task { await performSearch() }
|
||||
}
|
||||
}
|
||||
.focusSection()
|
||||
|
||||
ScrollView {
|
||||
if hasSearched || isSearchLoading {
|
||||
searchResultsContent
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
.scrollClipDisabled()
|
||||
}
|
||||
.onAppear {
|
||||
isTVSearchFieldFocused = true
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
Reference in New Issue
Block a user