mirror of
https://github.com/yattee/yattee.git
synced 2026-08-06 07:11:28 +00:00
Add search to playlists list view
This commit is contained in:
@@ -8240,6 +8240,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"playlists.search.placeholder" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Search playlists"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"playlists.title" : {
|
"playlists.title" : {
|
||||||
"comment" : "Title for playlists tab in instance browse",
|
"comment" : "Title for playlists tab in instance browse",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import SwiftUI
|
|||||||
struct PlaylistsListView: View {
|
struct PlaylistsListView: View {
|
||||||
@Environment(\.appEnvironment) private var appEnvironment
|
@Environment(\.appEnvironment) private var appEnvironment
|
||||||
@State private var playlists: [LocalPlaylist] = []
|
@State private var playlists: [LocalPlaylist] = []
|
||||||
|
@State private var searchText = ""
|
||||||
@State private var showingNewPlaylist = false
|
@State private var showingNewPlaylist = false
|
||||||
@State private var playlistToEdit: LocalPlaylist?
|
@State private var playlistToEdit: LocalPlaylist?
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
@@ -23,12 +24,22 @@ struct PlaylistsListView: View {
|
|||||||
appEnvironment?.settingsManager.listStyle ?? .inset
|
appEnvironment?.settingsManager.listStyle ?? .inset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Playlists filtered by search.
|
||||||
|
private var filteredPlaylists: [LocalPlaylist] {
|
||||||
|
guard !searchText.isEmpty else { return playlists }
|
||||||
|
let query = searchText.lowercased()
|
||||||
|
return playlists.filter { playlist in
|
||||||
|
playlist.title.lowercased().contains(query) ||
|
||||||
|
(playlist.playlistDescription?.lowercased().contains(query) ?? false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
tvOSContent
|
tvOSContent
|
||||||
#else
|
#else
|
||||||
if playlists.isEmpty {
|
if filteredPlaylists.isEmpty {
|
||||||
emptyView
|
emptyView
|
||||||
} else {
|
} else {
|
||||||
listContent
|
listContent
|
||||||
@@ -38,7 +49,15 @@ struct PlaylistsListView: View {
|
|||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
.navigationTitle(String(localized: "home.playlists.title"))
|
.navigationTitle(String(localized: "home.playlists.title"))
|
||||||
.toolbarTitleDisplayMode(.inlineLarge)
|
.toolbarTitleDisplayMode(.inlineLarge)
|
||||||
|
.searchable(text: $searchText, prompt: Text(String(localized: "playlists.search.placeholder")))
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
#if os(macOS)
|
||||||
|
// Pin the trailing group (search field + toolbar buttons) to the right edge,
|
||||||
|
// matching the global Search view.
|
||||||
|
if #available(macOS 26, *) {
|
||||||
|
ToolbarSpacer(.flexible, placement: .primaryAction)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
Button {
|
Button {
|
||||||
showingNewPlaylist = true
|
showingNewPlaylist = true
|
||||||
@@ -73,8 +92,10 @@ struct PlaylistsListView: View {
|
|||||||
|
|
||||||
private var tvOSContent: some View {
|
private var tvOSContent: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
HStack {
|
HStack(spacing: 24) {
|
||||||
Spacer()
|
TextField(String(localized: "playlists.search.placeholder"), text: $searchText)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
showingNewPlaylist = true
|
showingNewPlaylist = true
|
||||||
} label: {
|
} label: {
|
||||||
@@ -84,9 +105,10 @@ struct PlaylistsListView: View {
|
|||||||
.focusSection()
|
.focusSection()
|
||||||
.padding(.horizontal, 48)
|
.padding(.horizontal, 48)
|
||||||
.padding(.top, 20)
|
.padding(.top, 20)
|
||||||
|
.padding(.bottom, 20)
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
if playlists.isEmpty {
|
if filteredPlaylists.isEmpty {
|
||||||
emptyView
|
emptyView
|
||||||
} else {
|
} else {
|
||||||
listContent
|
listContent
|
||||||
@@ -106,7 +128,17 @@ struct PlaylistsListView: View {
|
|||||||
|
|
||||||
// MARK: - Empty View
|
// MARK: - Empty View
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
private var emptyView: some View {
|
private var emptyView: some View {
|
||||||
|
if !searchText.isEmpty {
|
||||||
|
ContentUnavailableView.search(text: searchText)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
} else {
|
||||||
|
noPlaylistsView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var noPlaylistsView: some View {
|
||||||
ContentUnavailableView {
|
ContentUnavailableView {
|
||||||
Label(String(localized: "home.playlists.title"), systemImage: "list.bullet.rectangle")
|
Label(String(localized: "home.playlists.title"), systemImage: "list.bullet.rectangle")
|
||||||
} description: {
|
} description: {
|
||||||
@@ -129,9 +161,9 @@ struct PlaylistsListView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
.frame(height: 16)
|
.frame(height: 16)
|
||||||
} content: {
|
} content: {
|
||||||
ForEach(Array(playlists.enumerated()), id: \.element.id) { index, playlist in
|
ForEach(Array(filteredPlaylists.enumerated()), id: \.element.id) { index, playlist in
|
||||||
VideoListRow(
|
VideoListRow(
|
||||||
isLast: index == playlists.count - 1,
|
isLast: index == filteredPlaylists.count - 1,
|
||||||
rowStyle: .regular,
|
rowStyle: .regular,
|
||||||
listStyle: listStyle,
|
listStyle: listStyle,
|
||||||
contentWidth: 80 // PlaylistRowView thumbnail width
|
contentWidth: 80 // PlaylistRowView thumbnail width
|
||||||
|
|||||||
Reference in New Issue
Block a user