mirror of
				https://github.com/yattee/yattee.git
				synced 2025-10-31 12:41:57 +00:00 
			
		
		
		
	| @@ -11,6 +11,7 @@ final class BrowsingSettingsGroupExporter: SettingsGroupExporter { | ||||
|             "favorites": Defaults[.favorites].compactMap { jsonFromString(FavoriteItem.bridge.serialize($0)) }, | ||||
|             "widgetsSettings": Defaults[.widgetsSettings].compactMap { widgetSettingsJSON($0) }, | ||||
|             "startupSection": Defaults[.startupSection].rawValue, | ||||
|             "showSearchSuggestions": Defaults[.showSearchSuggestions], | ||||
|             "visibleSections": Defaults[.visibleSections].compactMap { $0.rawValue }, | ||||
|             "showOpenActionsToolbarItem": Defaults[.showOpenActionsToolbarItem], | ||||
|             "accountPickerDisplaysAnonymousAccounts": Defaults[.accountPickerDisplaysAnonymousAccounts], | ||||
|   | ||||
| @@ -46,6 +46,10 @@ struct BrowsingSettingsGroupImporter { | ||||
|             Defaults[.startupSection] = startupSection | ||||
|         } | ||||
|  | ||||
|         if let showSearchSuggestions = json["showSearchSuggestions"].bool { | ||||
|             Defaults[.showSearchSuggestions] = showSearchSuggestions | ||||
|         } | ||||
|  | ||||
|         if let visibleSections = json["visibleSections"].array { | ||||
|             let sections = visibleSections.compactMap { visibleSectionJSON in | ||||
|                 if let visibleSectionString = visibleSectionJSON.rawString(options: []), | ||||
|   | ||||
| @@ -18,6 +18,8 @@ final class SearchModel: ObservableObject { | ||||
|  | ||||
|     @Published var focused = false | ||||
|  | ||||
|     @Default(.showSearchSuggestions) private var showSearchSuggestions | ||||
|  | ||||
|     #if os(iOS) | ||||
|         var textField: UITextField! | ||||
|     #elseif os(macOS) | ||||
| @@ -102,7 +104,7 @@ final class SearchModel: ObservableObject { | ||||
|     }} | ||||
|  | ||||
|     func loadSuggestions(_ query: String) { | ||||
|         guard accounts.app.supportsSearchSuggestions else { | ||||
|         guard accounts.app.supportsSearchSuggestions, showSearchSuggestions else { | ||||
|             querySuggestions.removeAll() | ||||
|             return | ||||
|         } | ||||
|   | ||||
| @@ -0,0 +1,38 @@ | ||||
| { | ||||
|   "colors" : [ | ||||
|     { | ||||
|       "color" : { | ||||
|         "color-space" : "srgb", | ||||
|         "components" : { | ||||
|           "alpha" : "1.000", | ||||
|           "blue" : "1.000", | ||||
|           "green" : "1.000", | ||||
|           "red" : "1.000" | ||||
|         } | ||||
|       }, | ||||
|       "idiom" : "universal" | ||||
|     }, | ||||
|     { | ||||
|       "appearances" : [ | ||||
|         { | ||||
|           "appearance" : "luminosity", | ||||
|           "value" : "dark" | ||||
|         } | ||||
|       ], | ||||
|       "color" : { | ||||
|         "color-space" : "srgb", | ||||
|         "components" : { | ||||
|           "alpha" : "1.000", | ||||
|           "blue" : "0.110", | ||||
|           "green" : "0.110", | ||||
|           "red" : "0.118" | ||||
|         } | ||||
|       }, | ||||
|       "idiom" : "universal" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
| @@ -63,6 +63,14 @@ enum Constants { | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|     static var detailsVisibility: Bool { | ||||
|         #if os(iOS) | ||||
|             false | ||||
|         #else | ||||
|             true | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|     static var progressViewScale: Double { | ||||
|         #if os(macOS) | ||||
|             0.4 | ||||
| @@ -95,11 +103,11 @@ enum Constants { | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|     static var detailsVisibility: Bool { | ||||
|         #if os(iOS) | ||||
|             false | ||||
|     static var contentViewMinWidth: Double { | ||||
|         #if os(macOS) | ||||
|             835 | ||||
|         #else | ||||
|             true | ||||
|             0 | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -15,6 +15,7 @@ extension Defaults.Keys { | ||||
|     static let favorites = Key<[FavoriteItem]>("favorites", default: []) | ||||
|     static let widgetsSettings = Key<[WidgetSettings]>("widgetsSettings", default: []) | ||||
|     static let startupSection = Key<StartupSection>("startupSection", default: .home) | ||||
|     static let showSearchSuggestions = Key<Bool>("showSearchSuggestions", default: true) | ||||
|     static let visibleSections = Key<Set<VisibleSection>>("visibleSections", default: [.subscriptions, .trending, .playlists]) | ||||
|  | ||||
|     static let showOpenActionsToolbarItem = Key<Bool>("showOpenActionsToolbarItem", default: false) | ||||
|   | ||||
| @@ -152,7 +152,7 @@ struct HomeView: View { | ||||
|         #endif | ||||
|         #if os(macOS) | ||||
|         .background(Color.secondaryBackground) | ||||
|         .frame(minWidth: 360) | ||||
|         .frame(minWidth: Constants.contentViewMinWidth) | ||||
|         .toolbar { | ||||
|             ToolbarItemGroup(placement: .automatic) { | ||||
|                 HideWatchedButtons() | ||||
|   | ||||
| @@ -169,7 +169,7 @@ struct ContentView: View { | ||||
|             .statusBarHidden(player.playingFullScreen) | ||||
|         #endif | ||||
|         #if os(macOS) | ||||
|         .frame(minWidth: 1200) | ||||
|         .frame(minWidth: 1200, minHeight: 600) | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,64 +1,99 @@ | ||||
| import Repeat | ||||
| import SwiftUI | ||||
|  | ||||
| struct SearchTextField: View { | ||||
|     private var navigation = NavigationModel.shared | ||||
|     @ObservedObject private var state = SearchModel.shared | ||||
|  | ||||
|     var body: some View { | ||||
|         ZStack { | ||||
|             #if os(macOS) | ||||
|     #if os(macOS) | ||||
|         var body: some View { | ||||
|             ZStack { | ||||
|                 fieldBorder | ||||
|             #endif | ||||
|  | ||||
|             HStack(spacing: 0) { | ||||
|                 #if os(macOS) | ||||
|                 HStack(spacing: 0) { | ||||
|                     Image(systemName: "magnifyingglass") | ||||
|                         .resizable() | ||||
|                         .scaledToFill() | ||||
|                         .frame(width: 12, height: 12) | ||||
|                         .padding(.horizontal, 8) | ||||
|                         .padding(.horizontal, 6) | ||||
|                         .opacity(0.8) | ||||
|                 #endif | ||||
|                 TextField("Search...", text: $state.queryText) { | ||||
|                     state.changeQuery { query in | ||||
|                         query.query = state.queryText | ||||
|                         navigation.hideKeyboard() | ||||
|                     } | ||||
|                     RecentsModel.shared.addQuery(state.queryText) | ||||
|                 } | ||||
|                 .disableAutocorrection(true) | ||||
|                 #if os(macOS) | ||||
|                     .frame(maxWidth: 190) | ||||
|                     .textFieldStyle(.plain) | ||||
|                 #else | ||||
|                     .frame(minWidth: 200) | ||||
|                     .textFieldStyle(.roundedBorder) | ||||
|                     .padding(.horizontal, 5) | ||||
|                     .padding(.trailing, state.queryText.isEmpty ? 0 : 10) | ||||
|                 #endif | ||||
|  | ||||
|                 if !state.queryText.isEmpty { | ||||
|                     clearButton | ||||
|                 } else { | ||||
|                     #if os(macOS) | ||||
|                     GeometryReader { geometry in | ||||
|                         TextField("Search...", text: $state.queryText) { | ||||
|                             state.changeQuery { query in | ||||
|                                 query.query = state.queryText | ||||
|                                 navigation.hideKeyboard() | ||||
|                             } | ||||
|                             RecentsModel.shared.addQuery(state.queryText) | ||||
|                         } | ||||
|                         .disableAutocorrection(true) | ||||
|                         .frame(maxWidth: geometry.size.width - 5) | ||||
|                         .textFieldStyle(.plain) | ||||
|                         .padding(.vertical, 8) | ||||
|                         .frame(height: 27, alignment: .center) | ||||
|                     } | ||||
|  | ||||
|                     if !state.queryText.isEmpty { | ||||
|                         clearButton | ||||
|                     } else { | ||||
|                         clearButton | ||||
|                             .opacity(0) | ||||
|                     #endif | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .transaction { t in t.animation = nil } | ||||
|         } | ||||
|         .transaction { t in t.animation = nil } | ||||
|     } | ||||
|     #else | ||||
|         var body: some View { | ||||
|             ZStack { | ||||
|                 HStack { | ||||
|                     HStack(spacing: 0) { | ||||
|                         Image(systemName: "magnifyingglass") | ||||
|                             .foregroundColor(.gray) | ||||
|                             .padding(.leading, 5) | ||||
|                             .padding(.trailing, 5) | ||||
|                             .imageScale(.medium) | ||||
|  | ||||
|                         TextField("Search...", text: $state.queryText) { | ||||
|                             state.changeQuery { query in | ||||
|                                 query.query = state.queryText | ||||
|                                 navigation.hideKeyboard() | ||||
|                             } | ||||
|                             RecentsModel.shared.addQuery(state.queryText) | ||||
|                         } | ||||
|                         .disableAutocorrection(true) | ||||
|                         .textFieldStyle(.plain) | ||||
|                         .padding(.vertical, 7) | ||||
|  | ||||
|                         if !state.queryText.isEmpty { | ||||
|                             clearButton | ||||
|                                 .padding(.leading, 5) | ||||
|                                 .padding(.trailing, 5) | ||||
|                         } | ||||
|                     } | ||||
|                     .background( | ||||
|                         RoundedRectangle(cornerRadius: 8) | ||||
|                             .fill(Color("SearchTextFieldBackground")) | ||||
|                     ) | ||||
|                     .overlay( | ||||
|                         RoundedRectangle(cornerRadius: 8) | ||||
|                             .stroke(Color(UIColor.secondarySystemBackground), lineWidth: 1) | ||||
|                     ) | ||||
|                     .frame(maxWidth: .infinity, alignment: .leading) | ||||
|                 } | ||||
|                 .padding(.horizontal, 0) | ||||
|             } | ||||
|             .transaction { t in t.animation = nil } | ||||
|         } | ||||
|     #endif | ||||
|  | ||||
|     private var fieldBorder: some View { | ||||
|         RoundedRectangle(cornerRadius: 5, style: .continuous) | ||||
|             .fill(Color.background) | ||||
|             .frame(width: 250, height: 32) | ||||
|             .frame(width: 250, height: 27) | ||||
|             .overlay( | ||||
|                 RoundedRectangle(cornerRadius: 5, style: .continuous) | ||||
|                     .stroke(Color.gray.opacity(0.4), lineWidth: 1) | ||||
|                     .frame(width: 250, height: 31) | ||||
|                     .frame(width: 250, height: 27) | ||||
|             ) | ||||
|     } | ||||
|  | ||||
| @@ -67,15 +102,14 @@ struct SearchTextField: View { | ||||
|             self.state.queryText = "" | ||||
|         }) { | ||||
|             Image(systemName: "xmark.circle.fill") | ||||
|             #if os(macOS) | ||||
|                 .imageScale(.small) | ||||
|             #else | ||||
|                 .imageScale(.medium) | ||||
|             #endif | ||||
|         } | ||||
|         .buttonStyle(PlainButtonStyle()) | ||||
|         #if os(macOS) | ||||
|             .padding(.trailing, 10) | ||||
|             .padding(.trailing, 5) | ||||
|         #elseif os(iOS) | ||||
|             .padding(.trailing, 5) | ||||
|             .foregroundColor(.gray) | ||||
|         #endif | ||||
|             .opacity(0.7) | ||||
|     } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ struct SearchView: View { | ||||
|     @Default(.saveRecents) private var saveRecents | ||||
|     @Default(.showHome) private var showHome | ||||
|     @Default(.searchListingStyle) private var searchListingStyle | ||||
|     @Default(.showSearchSuggestions) private var showSearchSuggestions | ||||
|  | ||||
|     private var videos = [Video]() | ||||
|  | ||||
| @@ -38,9 +39,9 @@ struct SearchView: View { | ||||
|         self.videos = videos | ||||
|     } | ||||
|  | ||||
|     var body: some View { | ||||
|         VStack { | ||||
|             #if os(iOS) | ||||
|     #if os(iOS) | ||||
|         var body: some View { | ||||
|             VStack { | ||||
|                 VStack { | ||||
|                     if accounts.app.supportsSearchSuggestions, state.query.query != state.queryText { | ||||
|                         SearchSuggestions() | ||||
| @@ -51,27 +52,155 @@ struct SearchView: View { | ||||
|                 } | ||||
|                 .backport | ||||
|                 .scrollDismissesKeyboardInteractively() | ||||
|             #else | ||||
|             } | ||||
|             .environment(\.listingStyle, searchListingStyle) | ||||
|             .toolbar { | ||||
|                 ToolbarItem(placement: .principal) { | ||||
|                     if #available(iOS 15, *) { | ||||
|                         FocusableSearchTextField() | ||||
|                     } else { | ||||
|                         SearchTextField() | ||||
|                     } | ||||
|                 } | ||||
|                 ToolbarItem(placement: .navigationBarTrailing) { | ||||
|                     searchMenu | ||||
|                 } | ||||
|             } | ||||
|             .navigationBarTitleDisplayMode(.inline) | ||||
|             .ignoresSafeArea(.keyboard, edges: .bottom) | ||||
|             .navigationTitle("Search") | ||||
|             .onAppear { | ||||
|                 if let query { | ||||
|                     state.queryText = query.query | ||||
|                     state.resetQuery(query) | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|  | ||||
|                 if !videos.isEmpty { | ||||
|                     state.store.replace(ContentItem.array(of: videos)) | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: accounts.current) { _ in | ||||
|                 state.reloadQuery() | ||||
|             } | ||||
|             .onChange(of: state.queryText) { newQuery in | ||||
|                 if newQuery.isEmpty { | ||||
|                     favoriteItem = nil | ||||
|                     state.resetQuery() | ||||
|                 } else { | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|                 state.loadSuggestions(newQuery) | ||||
|             } | ||||
|             .onChange(of: searchSortOrder) { order in | ||||
|                 state.changeQuery { query in | ||||
|                     query.sortBy = order | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: searchDate) { date in | ||||
|                 state.changeQuery { query in | ||||
|                     query.date = date | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: searchDuration) { duration in | ||||
|                 state.changeQuery { query in | ||||
|                     query.duration = duration | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     #elseif os(tvOS) | ||||
|         var body: some View { | ||||
|             VStack { | ||||
|                 ZStack { | ||||
|                     results | ||||
|  | ||||
|                     #if os(macOS) | ||||
|                         if accounts.app.supportsSearchSuggestions, state.query.query != state.queryText { | ||||
|                             HStack { | ||||
|                                 Spacer() | ||||
|                                 SearchSuggestions() | ||||
|                                     .borderLeading(width: 1, color: Color("ControlsBorderColor")) | ||||
|                                     .frame(maxWidth: 280) | ||||
|                                     .opacity(state.queryText.isEmpty ? 0 : 1) | ||||
|                             } | ||||
|                         } | ||||
|                     #endif | ||||
|                 } | ||||
|             #endif | ||||
|             } | ||||
|             .environment(\.listingStyle, searchListingStyle) | ||||
|             .onAppear { | ||||
|                 if let query { | ||||
|                     state.queryText = query.query | ||||
|                     state.resetQuery(query) | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|  | ||||
|                 if !videos.isEmpty { | ||||
|                     state.store.replace(ContentItem.array(of: videos)) | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: accounts.current) { _ in | ||||
|                 state.reloadQuery() | ||||
|             } | ||||
|             .onChange(of: state.queryText) { newQuery in | ||||
|                 if newQuery.isEmpty { | ||||
|                     favoriteItem = nil | ||||
|                     state.resetQuery() | ||||
|                 } else { | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|                 if showSearchSuggestions { | ||||
|                     state.loadSuggestions(newQuery) | ||||
|                 } | ||||
|                 searchDebounce.invalidate() | ||||
|                 recentsDebounce.invalidate() | ||||
|  | ||||
|                 searchDebounce.debouncing(2) { | ||||
|                     state.changeQuery { query in | ||||
|                         query.query = newQuery | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 recentsDebounce.debouncing(10) { | ||||
|                     recents.addQuery(newQuery) | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: searchSortOrder) { order in | ||||
|                 state.changeQuery { query in | ||||
|                     query.sortBy = order | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: searchDate) { date in | ||||
|                 state.changeQuery { query in | ||||
|                     query.date = date | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: searchDuration) { duration in | ||||
|                 state.changeQuery { query in | ||||
|                     query.duration = duration | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|             .searchable(text: $state.queryText) { | ||||
|                 if !state.queryText.isEmpty { | ||||
|                     ForEach(state.querySuggestions, id: \.self) { suggestion in | ||||
|                         Text(suggestion) | ||||
|                             .searchCompletion(suggestion) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         .environment(\.listingStyle, searchListingStyle) | ||||
|         .toolbar { | ||||
|             #if os(macOS) | ||||
|  | ||||
|     #elseif os(macOS) | ||||
|         var body: some View { | ||||
|             ZStack { | ||||
|                 results | ||||
|                 if accounts.app.supportsSearchSuggestions, state.query.query != state.queryText, showSearchSuggestions { | ||||
|                     HStack { | ||||
|                         Spacer() | ||||
|                         SearchSuggestions() | ||||
|                             .borderLeading(width: 1, color: Color("ControlsBorderColor")) | ||||
|                             .frame(maxWidth: 262) | ||||
|                             .opacity(state.queryText.isEmpty ? 0 : 1) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .environment(\.listingStyle, searchListingStyle) | ||||
|             .toolbar { | ||||
|                 ToolbarItemGroup(placement: toolbarPlacement) { | ||||
|                     ListingStyleButtons(listingStyle: $searchListingStyle) | ||||
|                     HideWatchedButtons() | ||||
| @@ -84,7 +213,6 @@ struct SearchView: View { | ||||
|                             HStack { | ||||
|                                 Text("Sort:") | ||||
|                                     .foregroundColor(.secondary) | ||||
|  | ||||
|                                 searchSortOrderPicker | ||||
|                             } | ||||
|                         } | ||||
| @@ -101,94 +229,52 @@ struct SearchView: View { | ||||
|                         SearchTextField() | ||||
|                     } | ||||
|                 } | ||||
|             #endif | ||||
|         } | ||||
|         .onAppear { | ||||
|             if let query { | ||||
|                 state.queryText = query.query | ||||
|                 state.resetQuery(query) | ||||
|                 updateFavoriteItem() | ||||
|             } | ||||
|  | ||||
|             if !videos.isEmpty { | ||||
|                 state.store.replace(ContentItem.array(of: videos)) | ||||
|             } | ||||
|         } | ||||
|         .onChange(of: accounts.current) { _ in | ||||
|             state.reloadQuery() | ||||
|         } | ||||
|         .onChange(of: state.queryText) { newQuery in | ||||
|             if newQuery.isEmpty { | ||||
|                 favoriteItem = nil | ||||
|                 state.resetQuery() | ||||
|             } else { | ||||
|                 updateFavoriteItem() | ||||
|             } | ||||
|  | ||||
|             state.loadSuggestions(newQuery) | ||||
|  | ||||
|             #if os(tvOS) | ||||
|                 searchDebounce.invalidate() | ||||
|                 recentsDebounce.invalidate() | ||||
|  | ||||
|                 searchDebounce.debouncing(2) { | ||||
|                     state.changeQuery { query in | ||||
|                         query.query = newQuery | ||||
|                     } | ||||
|             .onAppear { | ||||
|                 if let query { | ||||
|                     state.queryText = query.query | ||||
|                     state.resetQuery(query) | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|  | ||||
|                 recentsDebounce.debouncing(10) { | ||||
|                     recents.addQuery(newQuery) | ||||
|                 } | ||||
|             #endif | ||||
|         } | ||||
|         .onChange(of: searchSortOrder) { order in | ||||
|             state.changeQuery { query in | ||||
|                 query.sortBy = order | ||||
|                 updateFavoriteItem() | ||||
|             } | ||||
|         } | ||||
|         .onChange(of: searchDate) { date in | ||||
|             state.changeQuery { query in | ||||
|                 query.date = date | ||||
|                 updateFavoriteItem() | ||||
|             } | ||||
|         } | ||||
|         .onChange(of: searchDuration) { duration in | ||||
|             state.changeQuery { query in | ||||
|                 query.duration = duration | ||||
|                 updateFavoriteItem() | ||||
|             } | ||||
|         } | ||||
|         #if os(tvOS) | ||||
|         .searchable(text: $state.queryText) { | ||||
|             if !state.queryText.isEmpty { | ||||
|                 ForEach(state.querySuggestions, id: \.self) { suggestion in | ||||
|                     Text(suggestion) | ||||
|                         .searchCompletion(suggestion) | ||||
|                 if !videos.isEmpty { | ||||
|                     state.store.replace(ContentItem.array(of: videos)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         #else | ||||
|         .ignoresSafeArea(.keyboard, edges: .bottom) | ||||
|                 .navigationTitle("Search") | ||||
|         #endif | ||||
|         #if os(iOS) | ||||
|         .toolbar { | ||||
|             ToolbarItem(placement: .navigationBarLeading) { | ||||
|                 searchMenu | ||||
|             .onChange(of: accounts.current) { _ in | ||||
|                 state.reloadQuery() | ||||
|             } | ||||
|             ToolbarItem(placement: .principal) { | ||||
|                 if #available(iOS 15, *) { | ||||
|                     FocusableSearchTextField() | ||||
|             .onChange(of: state.queryText) { newQuery in | ||||
|                 if newQuery.isEmpty { | ||||
|                     favoriteItem = nil | ||||
|                     state.resetQuery() | ||||
|                 } else { | ||||
|                     SearchTextField() | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|                 state.loadSuggestions(newQuery) | ||||
|             } | ||||
|             .onChange(of: searchSortOrder) { order in | ||||
|                 state.changeQuery { query in | ||||
|                     query.sortBy = order | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: searchDate) { date in | ||||
|                 state.changeQuery { query in | ||||
|                     query.date = date | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|             .onChange(of: searchDuration) { duration in | ||||
|                 state.changeQuery { query in | ||||
|                     query.duration = duration | ||||
|                     updateFavoriteItem() | ||||
|                 } | ||||
|             } | ||||
|             .frame(minWidth: Constants.contentViewMinWidth) | ||||
|             .navigationTitle("Search") | ||||
|         } | ||||
|         .navigationBarTitleDisplayMode(.inline) | ||||
|         #endif | ||||
|     } | ||||
|     #endif | ||||
|  | ||||
|     #if os(iOS) | ||||
|         var searchMenu: some View { | ||||
| @@ -230,11 +316,10 @@ struct SearchView: View { | ||||
|                 } | ||||
|             } label: { | ||||
|                 HStack { | ||||
|                     Image(systemName: "magnifyingglass") | ||||
|                     Image(systemName: "chevron.down.circle.fill") | ||||
|                         .foregroundColor(.accentColor) | ||||
|                         .imageScale(.large) | ||||
|                 } | ||||
|                 .foregroundColor(.accentColor) | ||||
|                 .imageScale(.medium) | ||||
|             } | ||||
|         } | ||||
|     #endif | ||||
|   | ||||
| @@ -20,6 +20,7 @@ struct BrowsingSettings: View { | ||||
|     @Default(.showOpenActionsToolbarItem) private var showOpenActionsToolbarItem | ||||
|     @Default(.visibleSections) private var visibleSections | ||||
|     @Default(.startupSection) private var startupSection | ||||
|     @Default(.showSearchSuggestions) private var showSearchSuggestions | ||||
|     @Default(.playerButtonSingleTapGesture) private var playerButtonSingleTapGesture | ||||
|     @Default(.playerButtonDoubleTapGesture) private var playerButtonDoubleTapGesture | ||||
|     @Default(.playerButtonShowsControlButtonsWhenMinimized) private var playerButtonShowsControlButtonsWhenMinimized | ||||
| @@ -67,6 +68,7 @@ struct BrowsingSettings: View { | ||||
|             homeSettings | ||||
|             if !accounts.isEmpty { | ||||
|                 startupSectionPicker | ||||
|                 showSearchSuggestionsToggle | ||||
|                 visibleSectionsSettings | ||||
|             } | ||||
|             let interface = interfaceSettings | ||||
| @@ -246,6 +248,10 @@ struct BrowsingSettings: View { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private var showSearchSuggestionsToggle: some View { | ||||
|         Toggle("Show search suggestions", isOn: $showSearchSuggestions) | ||||
|     } | ||||
|  | ||||
|     private func toggleSection(_ section: VisibleSection, value: Bool) { | ||||
|         if value { | ||||
|             visibleSections.insert(section) | ||||
|   | ||||
| @@ -38,12 +38,14 @@ struct SubscriptionsView: View { | ||||
|                         } | ||||
|                         .pickerStyle(.segmented) | ||||
|                         .labelStyle(.titleOnly) | ||||
|  | ||||
|                         subscriptionsMenu | ||||
|                     } | ||||
|                     .frame(maxWidth: 500) | ||||
|                 } | ||||
|  | ||||
|                 ToolbarItem(placement: .navigationBarTrailing) { | ||||
|                     subscriptionsMenu | ||||
|                 } | ||||
|  | ||||
|                 ToolbarItem { | ||||
|                     RequestErrorButton(error: requestError) | ||||
|                 } | ||||
| @@ -88,7 +90,7 @@ struct SubscriptionsView: View { | ||||
|                     SettingsButtons() | ||||
|                 } | ||||
|             } label: { | ||||
|                 HStack(spacing: 12) { | ||||
|                 HStack { | ||||
|                     Image(systemName: "chevron.down.circle.fill") | ||||
|                         .foregroundColor(.accentColor) | ||||
|                         .imageScale(.large) | ||||
|   | ||||
| @@ -52,7 +52,7 @@ struct VerticalCells<Header: View>: View { | ||||
|         .edgesIgnoringSafeArea(edgesIgnoringSafeArea) | ||||
|         #if os(macOS) | ||||
|             .background(Color.secondaryBackground) | ||||
|             .frame(minWidth: 360) | ||||
|             .frame(minWidth: Constants.contentViewMinWidth) | ||||
|         #endif | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Arkadiusz Fal
					Arkadiusz Fal