mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
Search improvements for iOS
This commit is contained in:
parent
a7da3b9468
commit
4e0d7b60f7
@ -1,7 +1,7 @@
|
|||||||
import Defaults
|
import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class Recents: ObservableObject {
|
final class RecentsModel: ObservableObject {
|
||||||
@Default(.recentlyOpened) var items
|
@Default(.recentlyOpened) var items
|
||||||
|
|
||||||
var isEmpty: Bool {
|
var isEmpty: Bool {
|
||||||
@ -28,6 +28,10 @@ final class Recents: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addQuery(_ query: String) {
|
||||||
|
open(.init(from: query))
|
||||||
|
}
|
||||||
|
|
||||||
var presentedChannel: Channel? {
|
var presentedChannel: Channel? {
|
||||||
if let recent = items.last(where: { $0.type == .channel }) {
|
if let recent = items.last(where: { $0.type == .channel }) {
|
||||||
return recent.channel
|
return recent.channel
|
@ -14,24 +14,7 @@ final class SearchModel: ObservableObject {
|
|||||||
private var resource: Resource!
|
private var resource: Resource!
|
||||||
|
|
||||||
var isLoading: Bool {
|
var isLoading: Bool {
|
||||||
resource.isLoading
|
resource?.isLoading ?? false
|
||||||
}
|
|
||||||
|
|
||||||
func loadSuggestions(_ query: String) {
|
|
||||||
let resource = api.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 }) {
|
func changeQuery(_ changeHandler: @escaping (SearchQuery) -> Void = { _ in }) {
|
||||||
@ -87,4 +70,27 @@ final class SearchModel: ObservableObject {
|
|||||||
store = Store<[Video]>(videos)
|
store = Store<[Video]>(videos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var suggestionsDebounceTimer: Timer?
|
||||||
|
|
||||||
|
func loadSuggestions(_ query: String) {
|
||||||
|
suggestionsDebounceTimer?.invalidate()
|
||||||
|
|
||||||
|
suggestionsDebounceTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
|
||||||
|
let resource = self.api.searchSuggestions(query: query)
|
||||||
|
|
||||||
|
resource.addObserver(self.querySuggestions)
|
||||||
|
resource.loadIfNeeded()
|
||||||
|
|
||||||
|
if let request = resource.loadIfNeeded() {
|
||||||
|
request.onSuccess { response in
|
||||||
|
if let suggestions: [String] = response.typedContent() {
|
||||||
|
self.querySuggestions = Store<[String]>(suggestions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.querySuggestions = Store<[String]>(self.querySuggestions.collection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,9 +36,9 @@ final class SearchQuery: ObservableObject {
|
|||||||
var name: String {
|
var name: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .uploadDate:
|
case .uploadDate:
|
||||||
return "Upload Date"
|
return "Date"
|
||||||
case .viewCount:
|
case .viewCount:
|
||||||
return "View Count"
|
return "Views"
|
||||||
default:
|
default:
|
||||||
return rawValue.capitalized
|
return rawValue.capitalized
|
||||||
}
|
}
|
||||||
|
@ -221,9 +221,9 @@
|
|||||||
37BE0BDA26A214630092E2DB /* PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE0BD926A214630092E2DB /* PlayerViewController.swift */; };
|
37BE0BDA26A214630092E2DB /* PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE0BD926A214630092E2DB /* PlayerViewController.swift */; };
|
||||||
37BE0BDC26A2367F0092E2DB /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE0BDB26A2367F0092E2DB /* Player.swift */; };
|
37BE0BDC26A2367F0092E2DB /* Player.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE0BDB26A2367F0092E2DB /* Player.swift */; };
|
||||||
37BE0BE526A336910092E2DB /* OptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B76E95268747C900CE5671 /* OptionsView.swift */; };
|
37BE0BE526A336910092E2DB /* OptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B76E95268747C900CE5671 /* OptionsView.swift */; };
|
||||||
37C194C726F6A9C8005D3B96 /* Recents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C194C626F6A9C8005D3B96 /* Recents.swift */; };
|
37C194C726F6A9C8005D3B96 /* RecentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C194C626F6A9C8005D3B96 /* RecentsModel.swift */; };
|
||||||
37C194C826F6A9C8005D3B96 /* Recents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C194C626F6A9C8005D3B96 /* Recents.swift */; };
|
37C194C826F6A9C8005D3B96 /* RecentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C194C626F6A9C8005D3B96 /* RecentsModel.swift */; };
|
||||||
37C194C926F6A9C8005D3B96 /* Recents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C194C626F6A9C8005D3B96 /* Recents.swift */; };
|
37C194C926F6A9C8005D3B96 /* RecentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C194C626F6A9C8005D3B96 /* RecentsModel.swift */; };
|
||||||
37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
||||||
37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
||||||
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
||||||
@ -379,7 +379,7 @@
|
|||||||
37BE0BD526A1D4A90092E2DB /* PlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerViewController.swift; sourceTree = "<group>"; };
|
37BE0BD526A1D4A90092E2DB /* PlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerViewController.swift; sourceTree = "<group>"; };
|
||||||
37BE0BD926A214630092E2DB /* PlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerViewController.swift; sourceTree = "<group>"; };
|
37BE0BD926A214630092E2DB /* PlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerViewController.swift; sourceTree = "<group>"; };
|
||||||
37BE0BDB26A2367F0092E2DB /* Player.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = "<group>"; };
|
37BE0BDB26A2367F0092E2DB /* Player.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Player.swift; sourceTree = "<group>"; };
|
||||||
37C194C626F6A9C8005D3B96 /* Recents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Recents.swift; sourceTree = "<group>"; };
|
37C194C626F6A9C8005D3B96 /* RecentsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentsModel.swift; sourceTree = "<group>"; };
|
||||||
37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockSegment.swift; sourceTree = "<group>"; };
|
37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockSegment.swift; sourceTree = "<group>"; };
|
||||||
37CEE4BC2677B670005A1EFE /* SingleAssetStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleAssetStream.swift; sourceTree = "<group>"; };
|
37CEE4BC2677B670005A1EFE /* SingleAssetStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleAssetStream.swift; sourceTree = "<group>"; };
|
||||||
37CEE4C02677B697005A1EFE /* Stream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stream.swift; sourceTree = "<group>"; };
|
37CEE4C02677B697005A1EFE /* Stream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stream.swift; sourceTree = "<group>"; };
|
||||||
@ -760,7 +760,7 @@
|
|||||||
37B767DA2677C3CA0098BAA8 /* PlayerModel.swift */,
|
37B767DA2677C3CA0098BAA8 /* PlayerModel.swift */,
|
||||||
376578882685471400D4EA09 /* Playlist.swift */,
|
376578882685471400D4EA09 /* Playlist.swift */,
|
||||||
37BA794226DBA973002A0235 /* PlaylistsModel.swift */,
|
37BA794226DBA973002A0235 /* PlaylistsModel.swift */,
|
||||||
37C194C626F6A9C8005D3B96 /* Recents.swift */,
|
37C194C626F6A9C8005D3B96 /* RecentsModel.swift */,
|
||||||
3711403E26B206A6005B3555 /* SearchModel.swift */,
|
3711403E26B206A6005B3555 /* SearchModel.swift */,
|
||||||
373CFACA26966264003CB2C6 /* SearchQuery.swift */,
|
373CFACA26966264003CB2C6 /* SearchQuery.swift */,
|
||||||
37EAD86E267B9ED100D9E01B /* Segment.swift */,
|
37EAD86E267B9ED100D9E01B /* Segment.swift */,
|
||||||
@ -1079,7 +1079,7 @@
|
|||||||
37BE0BD626A1D4A90092E2DB /* PlayerViewController.swift in Sources */,
|
37BE0BD626A1D4A90092E2DB /* PlayerViewController.swift in Sources */,
|
||||||
37BA793F26DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
37BA793F26DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||||
37754C9D26B7500000DBD602 /* VideosView.swift in Sources */,
|
37754C9D26B7500000DBD602 /* VideosView.swift in Sources */,
|
||||||
37C194C726F6A9C8005D3B96 /* Recents.swift in Sources */,
|
37C194C726F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
|
||||||
37484C1926FC837400287258 /* PlaybackSettingsView.swift in Sources */,
|
37484C1926FC837400287258 /* PlaybackSettingsView.swift in Sources */,
|
||||||
3711403F26B206A6005B3555 /* SearchModel.swift in Sources */,
|
3711403F26B206A6005B3555 /* SearchModel.swift in Sources */,
|
||||||
37F64FE426FE70A60081B69E /* RedrawOnViewModifier.swift in Sources */,
|
37F64FE426FE70A60081B69E /* RedrawOnViewModifier.swift in Sources */,
|
||||||
@ -1152,7 +1152,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
37C194C826F6A9C8005D3B96 /* Recents.swift in Sources */,
|
37C194C826F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
|
||||||
37BE0BDC26A2367F0092E2DB /* Player.swift in Sources */,
|
37BE0BDC26A2367F0092E2DB /* Player.swift in Sources */,
|
||||||
3788AC2C26F6842D00F6BAA9 /* WatchNowSectionBody.swift in Sources */,
|
3788AC2C26F6842D00F6BAA9 /* WatchNowSectionBody.swift in Sources */,
|
||||||
37CEE4BE2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
37CEE4BE2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
||||||
@ -1306,7 +1306,7 @@
|
|||||||
37484C3326FCB8F900287258 /* InstanceAccountValidator.swift in Sources */,
|
37484C3326FCB8F900287258 /* InstanceAccountValidator.swift in Sources */,
|
||||||
37CEE4C32677B697005A1EFE /* Stream.swift in Sources */,
|
37CEE4C32677B697005A1EFE /* Stream.swift in Sources */,
|
||||||
37484C2326FC83C400287258 /* AccountSettingsView.swift in Sources */,
|
37484C2326FC83C400287258 /* AccountSettingsView.swift in Sources */,
|
||||||
37C194C926F6A9C8005D3B96 /* Recents.swift in Sources */,
|
37C194C926F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
|
||||||
37F64FE626FE70A60081B69E /* RedrawOnViewModifier.swift in Sources */,
|
37F64FE626FE70A60081B69E /* RedrawOnViewModifier.swift in Sources */,
|
||||||
37BE0BE526A336910092E2DB /* OptionsView.swift in Sources */,
|
37BE0BE526A336910092E2DB /* OptionsView.swift in Sources */,
|
||||||
37484C2B26FC83FF00287258 /* AccountFormView.swift in Sources */,
|
37484C2B26FC83FF00287258 /* AccountFormView.swift in Sources */,
|
||||||
|
@ -7,10 +7,6 @@ extension Defaults.Keys {
|
|||||||
|
|
||||||
static let instances = Key<[Instance]>("instances", default: [])
|
static let instances = Key<[Instance]>("instances", default: [])
|
||||||
|
|
||||||
static let searchSortOrder = Key<SearchQuery.SortOrder>("searchSortOrder", default: .relevance)
|
|
||||||
static let searchDate = Key<SearchQuery.Date?>("searchDate")
|
|
||||||
static let searchDuration = Key<SearchQuery.Duration?>("searchDuration")
|
|
||||||
|
|
||||||
static let selectedPlaylistID = Key<String?>("selectedPlaylistID")
|
static let selectedPlaylistID = Key<String?>("selectedPlaylistID")
|
||||||
static let showingAddToPlaylist = Key<Bool>("showingAddToPlaylist", default: false)
|
static let showingAddToPlaylist = Key<Bool>("showingAddToPlaylist", default: false)
|
||||||
static let videoIDToAddToPlaylist = Key<String?>("videoIDToAddToPlaylist")
|
static let videoIDToAddToPlaylist = Key<String?>("videoIDToAddToPlaylist")
|
||||||
|
@ -16,8 +16,7 @@ struct AppSidebarNavigation: View {
|
|||||||
@EnvironmentObject<InstancesModel> private var instances
|
@EnvironmentObject<InstancesModel> private var instances
|
||||||
@EnvironmentObject<NavigationModel> private var navigation
|
@EnvironmentObject<NavigationModel> private var navigation
|
||||||
@EnvironmentObject<PlaylistsModel> private var playlists
|
@EnvironmentObject<PlaylistsModel> private var playlists
|
||||||
@EnvironmentObject<Recents> private var recents
|
@EnvironmentObject<RecentsModel> private var recents
|
||||||
@EnvironmentObject<SearchModel> private var search
|
|
||||||
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
||||||
|
|
||||||
@State private var didApplyPrimaryViewWorkAround = false
|
@State private var didApplyPrimaryViewWorkAround = false
|
||||||
@ -56,23 +55,6 @@ struct AppSidebarNavigation: View {
|
|||||||
Text("Select section")
|
Text("Select section")
|
||||||
}
|
}
|
||||||
.environment(\.navigationStyle, .sidebar)
|
.environment(\.navigationStyle, .sidebar)
|
||||||
.searchable(text: $search.queryText, placement: .sidebar) {
|
|
||||||
ForEach(search.querySuggestions.collection, id: \.self) { suggestion in
|
|
||||||
Text(suggestion)
|
|
||||||
.searchCompletion(suggestion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onChange(of: search.queryText) { query in
|
|
||||||
search.loadSuggestions(query)
|
|
||||||
}
|
|
||||||
.onSubmit(of: .search) {
|
|
||||||
search.changeQuery { query in
|
|
||||||
query.query = search.queryText
|
|
||||||
}
|
|
||||||
recents.open(RecentItem(from: search.queryText))
|
|
||||||
|
|
||||||
navigation.tabSelection = .search
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sidebar: some View {
|
var sidebar: some View {
|
||||||
@ -87,12 +69,6 @@ struct AppSidebarNavigation: View {
|
|||||||
scrollScrollViewToItem(scrollView: scrollView, for: navigation.tabSelection)
|
scrollScrollViewToItem(scrollView: scrollView, for: navigation.tabSelection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background {
|
|
||||||
NavigationLink(destination: SearchView(), tag: TabSelection.search, selection: selection) {
|
|
||||||
Color.clear
|
|
||||||
}
|
|
||||||
.hidden()
|
|
||||||
}
|
|
||||||
.listStyle(.sidebar)
|
.listStyle(.sidebar)
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
@ -144,6 +120,12 @@ struct AppSidebarNavigation: View {
|
|||||||
Label("Trending", systemImage: "chart.line.uptrend.xyaxis")
|
Label("Trending", systemImage: "chart.line.uptrend.xyaxis")
|
||||||
.accessibility(label: Text("Trending"))
|
.accessibility(label: Text("Trending"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NavigationLink(destination: LazyView(SearchView()), tag: TabSelection.search, selection: selection) {
|
||||||
|
Label("Search", systemImage: "magnifyingglass")
|
||||||
|
.accessibility(label: Text("Search"))
|
||||||
|
}
|
||||||
|
.keyboardShortcut("f")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ struct AppSidebarRecents: View {
|
|||||||
@Binding var selection: TabSelection?
|
@Binding var selection: TabSelection?
|
||||||
|
|
||||||
@EnvironmentObject<NavigationModel> private var navigation
|
@EnvironmentObject<NavigationModel> private var navigation
|
||||||
@EnvironmentObject<Recents> private var recents
|
@EnvironmentObject<RecentsModel> private var recents
|
||||||
|
|
||||||
@Default(.recentlyOpened) private var recentItems
|
@Default(.recentlyOpened) private var recentItems
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ struct AppSidebarRecents: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct RecentNavigationLink<DestinationContent: View>: View {
|
struct RecentNavigationLink<DestinationContent: View>: View {
|
||||||
@EnvironmentObject<Recents> private var recents
|
@EnvironmentObject<RecentsModel> private var recents
|
||||||
|
|
||||||
var recent: RecentItem
|
var recent: RecentItem
|
||||||
@Binding var selection: TabSelection?
|
@Binding var selection: TabSelection?
|
||||||
|
@ -4,7 +4,7 @@ import SwiftUI
|
|||||||
struct AppTabNavigation: View {
|
struct AppTabNavigation: View {
|
||||||
@EnvironmentObject<NavigationModel> private var navigation
|
@EnvironmentObject<NavigationModel> private var navigation
|
||||||
@EnvironmentObject<SearchModel> private var search
|
@EnvironmentObject<SearchModel> private var search
|
||||||
@EnvironmentObject<Recents> private var recents
|
@EnvironmentObject<RecentsModel> private var recents
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TabView(selection: $navigation.tabSelection) {
|
TabView(selection: $navigation.tabSelection) {
|
||||||
@ -78,9 +78,7 @@ struct AppTabNavigation: View {
|
|||||||
query.query = search.queryText
|
query.query = search.queryText
|
||||||
}
|
}
|
||||||
|
|
||||||
recents.open(RecentItem(from: search.queryText))
|
recents.addQuery(search.queryText)
|
||||||
|
|
||||||
navigation.tabSelection = .search
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import SwiftUI
|
|||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@StateObject private var navigation = NavigationModel()
|
@StateObject private var navigation = NavigationModel()
|
||||||
@StateObject private var playback = PlaybackModel()
|
@StateObject private var playback = PlaybackModel()
|
||||||
@StateObject private var recents = Recents()
|
@StateObject private var recents = RecentsModel()
|
||||||
|
|
||||||
@EnvironmentObject<InvidiousAPI> private var api
|
@EnvironmentObject<InvidiousAPI> private var api
|
||||||
@EnvironmentObject<InstancesModel> private var instances
|
@EnvironmentObject<InstancesModel> private var instances
|
||||||
@ -34,6 +34,7 @@ struct ContentView: View {
|
|||||||
.sheet(isPresented: $navigation.showingVideo) {
|
.sheet(isPresented: $navigation.showingVideo) {
|
||||||
if let video = navigation.video {
|
if let video = navigation.video {
|
||||||
VideoPlayerView(video)
|
VideoPlayerView(video)
|
||||||
|
.environmentObject(playback)
|
||||||
|
|
||||||
#if !os(iOS)
|
#if !os(iOS)
|
||||||
.frame(minWidth: 550, minHeight: 720)
|
.frame(minWidth: 550, minHeight: 720)
|
||||||
|
@ -31,15 +31,14 @@ struct PearvidiousApp: App {
|
|||||||
.onAppear(perform: configureAPI)
|
.onAppear(perform: configureAPI)
|
||||||
.environmentObject(api)
|
.environmentObject(api)
|
||||||
.environmentObject(instances)
|
.environmentObject(instances)
|
||||||
.environmentObject(playlists)
|
|
||||||
.environmentObject(subscriptions)
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func configureAPI() {
|
fileprivate func configureAPI() {
|
||||||
subscriptions.api = api
|
|
||||||
playlists.api = api
|
playlists.api = api
|
||||||
|
search.api = api
|
||||||
|
subscriptions.api = api
|
||||||
|
|
||||||
guard api.account == nil, instances.defaultAccount != nil else {
|
guard api.account == nil, instances.defaultAccount != nil else {
|
||||||
return
|
return
|
||||||
|
@ -3,19 +3,19 @@ import Siesta
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct SearchView: View {
|
struct SearchView: View {
|
||||||
@Default(.searchSortOrder) private var searchSortOrder
|
private var query: SearchQuery?
|
||||||
@Default(.searchDate) private var searchDate
|
|
||||||
@Default(.searchDuration) private var searchDuration
|
|
||||||
|
|
||||||
@EnvironmentObject<Recents> private var recents
|
@State private var searchSortOrder: SearchQuery.SortOrder = .relevance
|
||||||
@EnvironmentObject<SearchModel> private var state
|
@State private var searchDate: SearchQuery.Date?
|
||||||
|
@State private var searchDuration: SearchQuery.Duration?
|
||||||
@Environment(\.navigationStyle) private var navigationStyle
|
|
||||||
|
|
||||||
@State private var presentingClearConfirmation = false
|
@State private var presentingClearConfirmation = false
|
||||||
@State private var recentsChanged = false
|
@State private var recentsChanged = false
|
||||||
|
|
||||||
private var query: SearchQuery?
|
@Environment(\.navigationStyle) private var navigationStyle
|
||||||
|
|
||||||
|
@EnvironmentObject<RecentsModel> private var recents
|
||||||
|
@EnvironmentObject<SearchModel> private var state
|
||||||
|
|
||||||
init(_ query: SearchQuery? = nil) {
|
init(_ query: SearchQuery? = nil) {
|
||||||
self.query = query
|
self.query = query
|
||||||
@ -37,7 +37,9 @@ struct SearchView: View {
|
|||||||
|
|
||||||
if searchFiltersActive {
|
if searchFiltersActive {
|
||||||
Button("Reset search filters") {
|
Button("Reset search filters") {
|
||||||
Defaults.reset(.searchDate, .searchDuration)
|
self.searchSortOrder = .relevance
|
||||||
|
self.searchDate = nil
|
||||||
|
self.searchDuration = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,16 +47,74 @@ struct SearchView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.toolbar {
|
||||||
|
#if os(iOS)
|
||||||
|
ToolbarItemGroup(placement: .bottomBar) {
|
||||||
|
Section {
|
||||||
|
if !state.queryText.isEmpty {
|
||||||
|
Text("Sort:")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
Menu(searchSortOrder.name) {
|
||||||
|
ForEach(SearchQuery.SortOrder.allCases) { sortOrder in
|
||||||
|
Button(sortOrder.name) {
|
||||||
|
searchSortOrder = sortOrder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("Filter:")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
Menu(searchDuration?.name ?? "Duration") {
|
||||||
|
Button("All") {
|
||||||
|
searchDuration = nil
|
||||||
|
}
|
||||||
|
ForEach(SearchQuery.Duration.allCases) { duration in
|
||||||
|
Button(duration.name) {
|
||||||
|
searchDuration = duration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundColor(searchDuration.isNil ? .secondary : .accentColor)
|
||||||
|
|
||||||
|
Menu(searchDate?.name ?? "Date") {
|
||||||
|
Button("All") {
|
||||||
|
searchDate = nil
|
||||||
|
}
|
||||||
|
ForEach(SearchQuery.Date.allCases) { date in
|
||||||
|
Button(date.name) {
|
||||||
|
searchDate = date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.foregroundColor(searchDate.isNil ? .secondary : .accentColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transaction { t in t.animation = .none }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if query != nil {
|
if query != nil {
|
||||||
if navigationStyle == .tab {
|
state.queryText = query!.query
|
||||||
state.queryText = query!.query
|
|
||||||
}
|
|
||||||
state.resetQuery(query!)
|
state.resetQuery(query!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: state.query.query) { queryText in
|
.searchable(text: $state.queryText, placement: searchFieldPlacement) {
|
||||||
state.changeQuery { query in query.query = queryText }
|
ForEach(state.querySuggestions.collection, id: \.self) { suggestion in
|
||||||
|
Text(suggestion)
|
||||||
|
.searchCompletion(suggestion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: state.queryText) { query in
|
||||||
|
state.loadSuggestions(query)
|
||||||
|
}
|
||||||
|
.onSubmit(of: .search) {
|
||||||
|
state.changeQuery { query in query.query = state.queryText }
|
||||||
|
recents.addQuery(state.queryText)
|
||||||
}
|
}
|
||||||
.onChange(of: searchSortOrder) { order in
|
.onChange(of: searchSortOrder) { order in
|
||||||
state.changeQuery { query in query.sortBy = order }
|
state.changeQuery { query in query.sortBy = order }
|
||||||
@ -66,7 +126,15 @@ struct SearchView: View {
|
|||||||
state.changeQuery { query in query.duration = duration }
|
state.changeQuery { query in query.duration = duration }
|
||||||
}
|
}
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
.navigationTitle(navigationTitle)
|
.navigationTitle("Search")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchFieldPlacement: SearchFieldPlacement {
|
||||||
|
#if os(iOS)
|
||||||
|
.navigationBarDrawer(displayMode: .always)
|
||||||
|
#else
|
||||||
|
.automatic
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,14 +182,6 @@ struct SearchView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var navigationTitle: String {
|
|
||||||
if state.query.query.isEmpty || (navigationStyle == .tab && state.queryText.isEmpty) {
|
|
||||||
return "Search"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Search: \"\(state.query.query)\""
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchFiltersActive: Bool {
|
var searchFiltersActive: Bool {
|
||||||
searchDate != nil || searchDuration != nil
|
searchDate != nil || searchDuration != nil
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import SwiftUI
|
|||||||
struct VideoContextMenuView: View {
|
struct VideoContextMenuView: View {
|
||||||
@EnvironmentObject<InvidiousAPI> private var api
|
@EnvironmentObject<InvidiousAPI> private var api
|
||||||
@EnvironmentObject<NavigationModel> private var navigation
|
@EnvironmentObject<NavigationModel> private var navigation
|
||||||
@EnvironmentObject<Recents> private var recents
|
@EnvironmentObject<RecentsModel> private var recents
|
||||||
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
||||||
|
|
||||||
let video: Video
|
let video: Video
|
||||||
|
Loading…
Reference in New Issue
Block a user