mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
Search UI fixes
This commit is contained in:
parent
b00b54ad2a
commit
5e403c7f15
@ -50,6 +50,14 @@ final class InvidiousAPI: Service {
|
||||
content.json.arrayValue.map(Video.init)
|
||||
}
|
||||
|
||||
configureTransformer("/search/suggestions", requestMethods: [.get]) { (content: Entity<JSON>) -> [String] in
|
||||
if let suggestions = content.json.dictionaryValue["suggestions"] {
|
||||
return suggestions.arrayValue.map(String.init)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
configureTransformer("/auth/playlists", requestMethods: [.get]) { (content: Entity<JSON>) -> [Playlist] in
|
||||
content.json.arrayValue.map(Playlist.init)
|
||||
}
|
||||
@ -148,6 +156,11 @@ final class InvidiousAPI: Service {
|
||||
return resource
|
||||
}
|
||||
|
||||
func searchSuggestions(query: String) -> Resource {
|
||||
resource("/search/suggestions")
|
||||
.withParam("q", query.lowercased())
|
||||
}
|
||||
|
||||
private func searchQuery(_ query: String) -> String {
|
||||
var searchQuery = query
|
||||
|
||||
|
@ -28,7 +28,6 @@ final class NavigationState: ObservableObject {
|
||||
openChannels.insert(channel)
|
||||
|
||||
isChannelOpen = true
|
||||
tabSelection = .channel(channel.id)
|
||||
}
|
||||
|
||||
func closeChannel(_ channel: Channel) {
|
||||
@ -43,11 +42,6 @@ final class NavigationState: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func closeAllChannels() {
|
||||
isChannelOpen = false
|
||||
openChannels.removeAll()
|
||||
}
|
||||
|
||||
func showOpenChannel(_ id: Channel.ID) -> Bool {
|
||||
if case .channel = tabSelection {
|
||||
return false
|
||||
|
@ -2,6 +2,7 @@ import CoreMedia
|
||||
import Foundation
|
||||
|
||||
final class PlaybackState: ObservableObject {
|
||||
@Published var live = false
|
||||
@Published var stream: Stream?
|
||||
@Published var time: CMTime?
|
||||
|
||||
|
@ -62,7 +62,9 @@ final class PlayerState: ObservableObject {
|
||||
}
|
||||
|
||||
fileprivate func playVideo(_ video: Video) {
|
||||
if video.hlsUrl != nil {
|
||||
playbackState.live = video.live
|
||||
|
||||
if video.live {
|
||||
playHlsUrl()
|
||||
return
|
||||
}
|
||||
|
@ -3,14 +3,16 @@ import Siesta
|
||||
import SwiftUI
|
||||
|
||||
final class SearchState: ObservableObject {
|
||||
@Published var store = Store<[Video]>()
|
||||
@Published var query = SearchQuery()
|
||||
|
||||
@Published var querySuggestions = Store<[String]>()
|
||||
|
||||
@Default(.searchQuery) private var queryText
|
||||
|
||||
private var previousResource: Resource?
|
||||
private var resource: Resource!
|
||||
|
||||
@Published var store = Store<[Video]>()
|
||||
|
||||
init() {
|
||||
let newQuery = query
|
||||
newQuery.query = queryText
|
||||
@ -23,6 +25,23 @@ final class SearchState: ObservableObject {
|
||||
resource.isLoading
|
||||
}
|
||||
|
||||
func loadQuerySuggestions(_ query: String) {
|
||||
let resource = InvidiousAPI.shared.searchSuggestions(query: query)
|
||||
|
||||
resource.addObserver(querySuggestions)
|
||||
resource.loadIfNeeded()
|
||||
|
||||
if let request = resource.loadIfNeeded() {
|
||||
request.onSuccess { response in
|
||||
if let suggestions: [String] = response.typedContent() {
|
||||
self.querySuggestions = Store<[String]>(suggestions)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
querySuggestions = Store<[String]>(querySuggestions.collection)
|
||||
}
|
||||
}
|
||||
|
||||
func changeQuery(_ changeHandler: @escaping (SearchQuery) -> Void = { _ in }) {
|
||||
changeHandler(query)
|
||||
|
||||
|
@ -20,6 +20,9 @@
|
||||
37141673267A8E10006CA35D /* Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37141672267A8E10006CA35D /* Country.swift */; };
|
||||
37141674267A8E10006CA35D /* Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37141672267A8E10006CA35D /* Country.swift */; };
|
||||
37141675267A8E10006CA35D /* Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37141672267A8E10006CA35D /* Country.swift */; };
|
||||
37152EEA26EFEB95004FB96D /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37152EE926EFEB95004FB96D /* LazyView.swift */; };
|
||||
37152EEB26EFEB95004FB96D /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37152EE926EFEB95004FB96D /* LazyView.swift */; };
|
||||
37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37152EE926EFEB95004FB96D /* LazyView.swift */; };
|
||||
371F2F1A269B43D300E4A7AB /* NavigationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationState.swift */; };
|
||||
371F2F1B269B43D300E4A7AB /* NavigationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationState.swift */; };
|
||||
371F2F1C269B43D300E4A7AB /* NavigationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371F2F19269B43D300E4A7AB /* NavigationState.swift */; };
|
||||
@ -191,7 +194,6 @@
|
||||
37D4B15F267164AF00C925CA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37D4B15E267164AF00C925CA /* Assets.xcassets */; };
|
||||
37D4B176267164B000C925CA /* PearvidiousUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B175267164B000C925CA /* PearvidiousUITests.swift */; };
|
||||
37D4B1802671650A00C925CA /* PearvidiousApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B0C22671614700C925CA /* PearvidiousApp.swift */; };
|
||||
37D4B1812671653A00C925CA /* AppTabNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B0C32671614700C925CA /* AppTabNavigation.swift */; };
|
||||
37D4B1862671691600C925CA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37D4B0C42671614800C925CA /* Assets.xcassets */; };
|
||||
37D4B18E26717B3800C925CA /* VideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B18B26717B3800C925CA /* VideoView.swift */; };
|
||||
37D4B19726717E1500C925CA /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B19626717E1500C925CA /* Video.swift */; };
|
||||
@ -253,6 +255,7 @@
|
||||
3711403E26B206A6005B3555 /* SearchState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchState.swift; sourceTree = "<group>"; };
|
||||
3714166E267A8ACC006CA35D /* TrendingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingView.swift; sourceTree = "<group>"; };
|
||||
37141672267A8E10006CA35D /* Country.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Country.swift; sourceTree = "<group>"; };
|
||||
37152EE926EFEB95004FB96D /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
|
||||
371F2F19269B43D300E4A7AB /* NavigationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationState.swift; sourceTree = "<group>"; };
|
||||
372915E52687E3B900F5A35B /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = "<group>"; };
|
||||
373CFABD26966115003CB2C6 /* CoverSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverSectionView.swift; sourceTree = "<group>"; };
|
||||
@ -467,6 +470,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37BA793E26DB8F97002A0235 /* ChannelVideosView.swift */,
|
||||
37152EE926EFEB95004FB96D /* LazyView.swift */,
|
||||
37BA793A26DB8EE4002A0235 /* PlaylistVideosView.swift */,
|
||||
37AAF27D26737323007FC770 /* PopularView.swift */,
|
||||
37AAF27F26737550007FC770 /* SearchView.swift */,
|
||||
@ -964,6 +968,7 @@
|
||||
3763495126DFF59D00B9A393 /* AppSidebarRecentlyOpened.swift in Sources */,
|
||||
37BA793B26DB8EE4002A0235 /* PlaylistVideosView.swift in Sources */,
|
||||
37BD07B52698AA4D003EBB87 /* ContentView.swift in Sources */,
|
||||
37152EEA26EFEB95004FB96D /* LazyView.swift in Sources */,
|
||||
37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */,
|
||||
37977583268922F600DD52A8 /* InvidiousAPI.swift in Sources */,
|
||||
37BE0BD626A1D4A90092E2DB /* PlayerViewController.swift in Sources */,
|
||||
@ -1043,6 +1048,7 @@
|
||||
37EAD870267B9ED100D9E01B /* Segment.swift in Sources */,
|
||||
37BA793C26DB8EE4002A0235 /* PlaylistVideosView.swift in Sources */,
|
||||
37141670267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||
37152EEB26EFEB95004FB96D /* LazyView.swift in Sources */,
|
||||
377FC7E2267A084A00A6BBAF /* VideoView.swift in Sources */,
|
||||
37B81B0626D2CEDA00675966 /* PlaybackState.swift in Sources */,
|
||||
3765788A2685471400D4EA09 /* Playlist.swift in Sources */,
|
||||
@ -1161,6 +1167,7 @@
|
||||
3705B180267B4DFB00704544 /* TrendingCountry.swift in Sources */,
|
||||
373CFACD26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
||||
37141675267A8E10006CA35D /* Country.swift in Sources */,
|
||||
37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */,
|
||||
37F49BA826CB0FCE00304AC0 /* PlaylistFormView.swift in Sources */,
|
||||
373CFAC42696616C003CB2C6 /* CoverSectionRowView.swift in Sources */,
|
||||
37D4B19926717E1500C925CA /* Video.swift in Sources */,
|
||||
@ -1168,7 +1175,6 @@
|
||||
37AAF2A226741C97007FC770 /* SubscriptionsView.swift in Sources */,
|
||||
372915E82687E3B900F5A35B /* Defaults.swift in Sources */,
|
||||
37BAB54C269B39FD00E75ED1 /* TVNavigationView.swift in Sources */,
|
||||
37D4B1812671653A00C925CA /* AppTabNavigation.swift in Sources */,
|
||||
3797758D2689345500DD52A8 /* Store.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -12,6 +12,7 @@ struct UnsubscribeAlertModifier: ViewModifier {
|
||||
Button("Unsubscribe", role: .destructive) {
|
||||
subscriptions.unsubscribe(channel.id) {
|
||||
navigationState.openChannel(channel)
|
||||
navigationState.tabSelection = .channel(channel.id)
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,23 @@ import SwiftUI
|
||||
#endif
|
||||
|
||||
struct AppSidebarNavigation: View {
|
||||
enum SidebarGroup: String, Identifiable {
|
||||
case main
|
||||
|
||||
var id: RawValue {
|
||||
rawValue
|
||||
}
|
||||
}
|
||||
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
@EnvironmentObject<Playlists> private var playlists
|
||||
@EnvironmentObject<SearchState> private var searchState
|
||||
@EnvironmentObject<Subscriptions> private var subscriptions
|
||||
|
||||
@State private var didApplyPrimaryViewWorkAround = false
|
||||
|
||||
@State private var searchQuery = ""
|
||||
|
||||
var selection: Binding<TabSelection?> {
|
||||
navigationState.tabSelectionOptionalBinding
|
||||
}
|
||||
@ -41,61 +52,94 @@ struct AppSidebarNavigation: View {
|
||||
|
||||
Text("Select section")
|
||||
}
|
||||
.searchable(text: $searchQuery, placement: .sidebar) {
|
||||
ForEach(searchState.querySuggestions.collection, id: \.self) { suggestion in
|
||||
Text(suggestion)
|
||||
.searchCompletion(suggestion)
|
||||
}
|
||||
}
|
||||
.onChange(of: searchQuery) { query in
|
||||
searchState.loadQuerySuggestions(query)
|
||||
}
|
||||
.onSubmit(of: .search) {
|
||||
searchState.changeQuery { query in
|
||||
query.query = self.searchQuery
|
||||
}
|
||||
|
||||
navigationState.tabSelection = .search
|
||||
}
|
||||
}
|
||||
|
||||
var sidebar: some View {
|
||||
ScrollViewReader { scrollView in
|
||||
List {
|
||||
mainNavigationLinks
|
||||
|
||||
Group {
|
||||
AppSidebarRecentlyOpened(selection: selection)
|
||||
.id("recentlyOpened")
|
||||
AppSidebarSubscriptions(selection: selection)
|
||||
AppSidebarPlaylists(selection: selection)
|
||||
ForEach(sidebarGroups) { group in
|
||||
sidebarGroupContent(group)
|
||||
.id(group)
|
||||
}
|
||||
|
||||
.onChange(of: navigationState.sidebarSectionChanged) { _ in
|
||||
scrollScrollViewToItem(scrollView: scrollView, for: navigationState.tabSelection)
|
||||
}
|
||||
}
|
||||
.background {
|
||||
NavigationLink(destination: SearchView(), tag: TabSelection.search, selection: selection) {
|
||||
Color.clear
|
||||
}
|
||||
.hidden()
|
||||
}
|
||||
.listStyle(.sidebar)
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
.toolbar {
|
||||
Button(action: toggleSidebar) {
|
||||
Image(systemName: "sidebar.left").help("Toggle Sidebar")
|
||||
.toolbar {
|
||||
#if os(macOS)
|
||||
ToolbarItemGroup {
|
||||
Button(action: toggleSidebar) {
|
||||
Image(systemName: "sidebar.left").help("Toggle Sidebar")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var sidebarGroups: [SidebarGroup] {
|
||||
[.main]
|
||||
}
|
||||
|
||||
func sidebarGroupContent(_ group: SidebarGroup) -> some View {
|
||||
switch group {
|
||||
case .main:
|
||||
return Group {
|
||||
mainNavigationLinks
|
||||
|
||||
AppSidebarRecentlyOpened(selection: selection)
|
||||
.id("recentlyOpened")
|
||||
AppSidebarSubscriptions(selection: selection)
|
||||
AppSidebarPlaylists(selection: selection)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var mainNavigationLinks: some View {
|
||||
Group {
|
||||
NavigationLink(destination: SubscriptionsView(), tag: TabSelection.subscriptions, selection: selection) {
|
||||
Section("Videos") {
|
||||
NavigationLink(destination: LazyView(SubscriptionsView()), tag: TabSelection.subscriptions, selection: selection) {
|
||||
Label("Subscriptions", systemImage: "star.circle.fill")
|
||||
.accessibility(label: Text("Subscriptions"))
|
||||
}
|
||||
|
||||
NavigationLink(destination: PopularView(), tag: TabSelection.popular, selection: selection) {
|
||||
NavigationLink(destination: LazyView(PopularView()), tag: TabSelection.popular, selection: selection) {
|
||||
Label("Popular", systemImage: "chart.bar")
|
||||
.accessibility(label: Text("Popular"))
|
||||
}
|
||||
|
||||
NavigationLink(destination: TrendingView(), tag: TabSelection.trending, selection: selection) {
|
||||
NavigationLink(destination: LazyView(TrendingView()), tag: TabSelection.trending, selection: selection) {
|
||||
Label("Trending", systemImage: "chart.line.uptrend.xyaxis")
|
||||
.accessibility(label: Text("Trending"))
|
||||
}
|
||||
|
||||
NavigationLink(destination: PlaylistsView(), tag: TabSelection.playlists, selection: selection) {
|
||||
NavigationLink(destination: LazyView(PlaylistsView()), tag: TabSelection.playlists, selection: selection) {
|
||||
Label("Playlists", systemImage: "list.and.film")
|
||||
.accessibility(label: Text("Playlists"))
|
||||
}
|
||||
|
||||
NavigationLink(destination: SearchView(), tag: TabSelection.search, selection: selection) {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
.accessibility(label: Text("Search"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ struct AppSidebarPlaylists: View {
|
||||
Section(header: Text("Playlists")) {
|
||||
ForEach(playlists.all) { playlist in
|
||||
NavigationLink(tag: TabSelection.playlist(playlist.id), selection: $selection) {
|
||||
PlaylistVideosView(playlist)
|
||||
LazyView(PlaylistVideosView(playlist))
|
||||
} label: {
|
||||
Label(playlist.title, systemImage: AppSidebarNavigation.symbolSystemImage(playlist.title))
|
||||
.badge(Text("\(playlist.videos.count)"))
|
||||
|
@ -14,7 +14,7 @@ struct AppSidebarRecentlyOpened: View {
|
||||
Section(header: Text("Recently Opened")) {
|
||||
ForEach(recentlyOpened) { channel in
|
||||
NavigationLink(tag: TabSelection.channel(channel.id), selection: $selection) {
|
||||
ChannelVideosView(channel)
|
||||
LazyView(ChannelVideosView(channel))
|
||||
} label: {
|
||||
HStack {
|
||||
Label(channel.name, systemImage: AppSidebarNavigation.symbolSystemImage(channel.name))
|
||||
|
@ -10,7 +10,7 @@ struct AppSidebarSubscriptions: View {
|
||||
Section(header: Text("Subscriptions")) {
|
||||
ForEach(subscriptions.all) { channel in
|
||||
NavigationLink(tag: TabSelection.channel(channel.id), selection: $selection) {
|
||||
ChannelVideosView(channel)
|
||||
LazyView(ChannelVideosView(channel))
|
||||
} label: {
|
||||
Label(channel.name, systemImage: AppSidebarNavigation.symbolSystemImage(channel.name))
|
||||
}
|
||||
|
@ -3,6 +3,9 @@ import SwiftUI
|
||||
|
||||
struct AppTabNavigation: View {
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
@EnvironmentObject<SearchState> private var searchState
|
||||
|
||||
@State private var searchQuery = ""
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $navigationState.tabSelection) {
|
||||
@ -44,6 +47,22 @@ struct AppTabNavigation: View {
|
||||
|
||||
NavigationView {
|
||||
SearchView()
|
||||
.searchable(text: $searchQuery, placement: .navigationBarDrawer(displayMode: .always)) {
|
||||
ForEach(searchState.querySuggestions.collection, id: \.self) { suggestion in
|
||||
Text(suggestion)
|
||||
.searchCompletion(suggestion)
|
||||
}
|
||||
}
|
||||
.onChange(of: searchQuery) { query in
|
||||
searchState.loadQuerySuggestions(query)
|
||||
}
|
||||
.onSubmit(of: .search) {
|
||||
searchState.changeQuery { query in
|
||||
query.query = self.searchQuery
|
||||
}
|
||||
|
||||
navigationState.tabSelection = .search
|
||||
}
|
||||
}
|
||||
.tabItem {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
|
@ -12,7 +12,7 @@ struct PlaybackBar: View {
|
||||
closeButton
|
||||
.frame(width: 60, alignment: .leading)
|
||||
|
||||
Text(playbackFinishAtString)
|
||||
Text(playbackStatus)
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption2)
|
||||
.frame(minWidth: 60, maxWidth: .infinity)
|
||||
@ -21,7 +21,11 @@ struct PlaybackBar: View {
|
||||
if playbackState.stream != nil {
|
||||
Text(currentStreamString)
|
||||
} else {
|
||||
Image(systemName: "bolt.horizontal.fill")
|
||||
if video.live {
|
||||
Image(systemName: "dot.radiowaves.left.and.right")
|
||||
} else {
|
||||
Image(systemName: "bolt.horizontal.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
.foregroundColor(.gray)
|
||||
@ -37,9 +41,13 @@ struct PlaybackBar: View {
|
||||
playbackState.stream != nil ? "\(playbackState.stream!.resolution.height)p" : ""
|
||||
}
|
||||
|
||||
var playbackFinishAtString: String {
|
||||
var playbackStatus: String {
|
||||
guard playbackState.time != nil else {
|
||||
return "loading..."
|
||||
if playbackState.live {
|
||||
return "LIVE"
|
||||
} else {
|
||||
return "loading..."
|
||||
}
|
||||
}
|
||||
|
||||
let remainingSeconds = video.length - playbackState.time!.seconds
|
||||
|
@ -119,12 +119,15 @@ struct VideoView: View {
|
||||
#endif
|
||||
.padding(.bottom)
|
||||
|
||||
if additionalDetailsAvailable {
|
||||
additionalDetails
|
||||
.padding(.bottom, 10)
|
||||
} else {
|
||||
Spacer()
|
||||
Group {
|
||||
if additionalDetailsAvailable {
|
||||
additionalDetails
|
||||
} else {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 30, alignment: .top)
|
||||
.padding(.bottom, 10)
|
||||
}
|
||||
#if os(tvOS)
|
||||
.padding(.horizontal, 8)
|
||||
|
@ -90,7 +90,7 @@ struct ChannelVideosView: View {
|
||||
var subscriptionToolbarItemPlacement: ToolbarItemPlacement {
|
||||
#if os(iOS)
|
||||
if horizontalSizeClass == .regular {
|
||||
return .primaryAction
|
||||
return .primaryAction // swiftlint:disable:this implicit_return
|
||||
}
|
||||
#endif
|
||||
|
||||
|
13
Shared/Views/LazyView.swift
Normal file
13
Shared/Views/LazyView.swift
Normal file
@ -0,0 +1,13 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct LazyView<Content: View>: View {
|
||||
let build: () -> Content
|
||||
init(_ build: @autoclosure @escaping () -> Content) {
|
||||
self.build = build
|
||||
}
|
||||
|
||||
var body: Content {
|
||||
build()
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@ struct SearchView: View {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.searchable(text: $queryText)
|
||||
.onAppear {
|
||||
state.changeQuery { query in
|
||||
query.query = queryText
|
||||
@ -35,7 +34,7 @@ struct SearchView: View {
|
||||
query.duration = searchDuration
|
||||
}
|
||||
}
|
||||
.onChange(of: queryText) { queryText in
|
||||
.onChange(of: state.query.query) { queryText in
|
||||
state.changeQuery { query in query.query = queryText }
|
||||
}
|
||||
.onChange(of: searchSortOrder) { order in
|
||||
@ -48,10 +47,14 @@ struct SearchView: View {
|
||||
state.changeQuery { query in query.duration = duration }
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.navigationTitle("Search")
|
||||
.navigationTitle(navigationTitle)
|
||||
#endif
|
||||
}
|
||||
|
||||
var navigationTitle: String {
|
||||
state.query.query.isEmpty ? "Search" : "Search: \"\(state.query.query)\""
|
||||
}
|
||||
|
||||
var searchFiltersActive: Bool {
|
||||
searchDate != nil || searchDuration != nil
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ struct VideoContextMenuView: View {
|
||||
var openChannelButton: some View {
|
||||
Button("\(video.author) Channel") {
|
||||
navigationState.openChannel(video.channel)
|
||||
navigationState.tabSelection = .channel(video.channel.id)
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import SwiftUI
|
||||
struct TVNavigationView: View {
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
@EnvironmentObject<PlaybackState> private var playbackState
|
||||
@EnvironmentObject<SearchState> private var searchState
|
||||
|
||||
@State private var showingOptions = false
|
||||
|
||||
@ -28,6 +29,15 @@ struct TVNavigationView: View {
|
||||
.tag(TabSelection.playlists)
|
||||
|
||||
SearchView()
|
||||
.searchable(text: $searchState.query.query) {
|
||||
ForEach(searchState.querySuggestions.collection, id: \.self) { suggestion in
|
||||
Text(suggestion)
|
||||
.searchCompletion(suggestion)
|
||||
}
|
||||
}
|
||||
.onChange(of: searchState.query.query) { query in
|
||||
searchState.loadQuerySuggestions(query)
|
||||
}
|
||||
.tabItem { Image(systemName: "magnifyingglass") }
|
||||
.tag(TabSelection.search)
|
||||
}
|
||||
|
@ -105,6 +105,7 @@ struct VideoDetailsView: View {
|
||||
|
||||
return Button("Open \(channel.name) channel") {
|
||||
navigationState.openChannel(channel)
|
||||
navigationState.tabSelection = .channel(channel.id)
|
||||
navigationState.returnToDetails = true
|
||||
dismiss()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user