Filter Toggles [iOS] [iPadOS] (#847)
Co-authored-by: Joe Kribs <joseph@kribs.net>
This commit is contained in:
parent
984134f4b6
commit
48e608e62b
|
@ -34,6 +34,8 @@ final class SettingsCoordinator: NavigationCoordinatable {
|
|||
@Route(.push)
|
||||
var experimentalSettings = makeExperimentalSettings
|
||||
@Route(.push)
|
||||
var filterDrawerButtonSelector = makeFilterDrawerButtonSelector
|
||||
@Route(.push)
|
||||
var indicatorSettings = makeIndicatorSettings
|
||||
@Route(.push)
|
||||
var serverDetail = makeServerDetail
|
||||
|
@ -115,9 +117,14 @@ final class SettingsCoordinator: NavigationCoordinatable {
|
|||
}
|
||||
#endif
|
||||
|
||||
func makeFilterDrawerButtonSelector(selectedButtonsBinding: Binding<[FilterDrawerButtonSelection]>) -> some View {
|
||||
FilterDrawerButtonSelectorView(selectedButtonsBinding: selectedButtonsBinding)
|
||||
}
|
||||
|
||||
func makeVideoPlayerSettings() -> VideoPlayerSettingsCoordinator {
|
||||
VideoPlayerSettingsCoordinator()
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if os(tvOS)
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// Swiftfin is subject to the terms of the Mozilla Public
|
||||
// License, v2.0. If a copy of the MPL was not distributed with this
|
||||
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2023 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import Foundation
|
||||
|
||||
enum FilterDrawerButtonSelection: String, CaseIterable, Defaults.Serializable, Displayable, Identifiable {
|
||||
|
||||
case filters
|
||||
case genres
|
||||
case order
|
||||
case sort
|
||||
|
||||
var displayTitle: String {
|
||||
switch self {
|
||||
case .filters:
|
||||
return L10n.filters
|
||||
case .genres:
|
||||
return L10n.genres
|
||||
case .order:
|
||||
return L10n.order
|
||||
case .sort:
|
||||
return L10n.sort
|
||||
}
|
||||
}
|
||||
|
||||
var id: String {
|
||||
rawValue
|
||||
}
|
||||
|
||||
var itemFilter: WritableKeyPath<ItemFilters, [ItemFilters.Filter]> {
|
||||
switch self {
|
||||
case .filters:
|
||||
return \.filters
|
||||
case .genres:
|
||||
return \.genres
|
||||
case .order:
|
||||
return \.sortOrder
|
||||
case .sort:
|
||||
return \.sortBy
|
||||
}
|
||||
}
|
||||
|
||||
var selectorType: SelectorType {
|
||||
switch self {
|
||||
case .filters, .genres:
|
||||
return .multi
|
||||
case .order, .sort:
|
||||
return .single
|
||||
}
|
||||
}
|
||||
|
||||
var itemFilterDefault: [ItemFilters.Filter] {
|
||||
switch self {
|
||||
case .filters:
|
||||
return []
|
||||
case .genres:
|
||||
return []
|
||||
case .order:
|
||||
return [APISortOrder.ascending.filter]
|
||||
case .sort:
|
||||
return [SortBy.name.filter]
|
||||
}
|
||||
}
|
||||
|
||||
func isItemsFilterActive(activeFilters: ItemFilters) -> Bool {
|
||||
switch self {
|
||||
case .filters:
|
||||
return activeFilters.filters != self.itemFilterDefault
|
||||
case .genres:
|
||||
return activeFilters.genres != self.itemFilterDefault
|
||||
case .order:
|
||||
return activeFilters.sortOrder != self.itemFilterDefault
|
||||
case .sort:
|
||||
return activeFilters.sortBy != self.itemFilterDefault
|
||||
}
|
||||
}
|
||||
|
||||
static var defaultFilterDrawerButtons: [FilterDrawerButtonSelection] {
|
||||
[
|
||||
.filters,
|
||||
.genres,
|
||||
.order,
|
||||
.sort,
|
||||
]
|
||||
}
|
||||
}
|
|
@ -72,6 +72,19 @@ extension Defaults.Keys {
|
|||
static let showFavorites: Key<Bool> = .init("libraryShowFavorites", default: true, suite: .generalSuite)
|
||||
static let viewType = Key<LibraryViewType>("libraryViewType", default: .grid, suite: .generalSuite)
|
||||
}
|
||||
|
||||
enum Filters {
|
||||
static let libraryFilterDrawerButtons: Key<[FilterDrawerButtonSelection]> = .init(
|
||||
"defaultLibraryFilterDrawerButtons",
|
||||
default: FilterDrawerButtonSelection.defaultFilterDrawerButtons,
|
||||
suite: .generalSuite
|
||||
)
|
||||
static let searchFilterDrawerButtons: Key<[FilterDrawerButtonSelection]> = .init(
|
||||
"defaultSearchFilterDrawerButtons",
|
||||
default: FilterDrawerButtonSelection.defaultFilterDrawerButtons,
|
||||
suite: .generalSuite
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum VideoPlayer {
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091B5A872683142E00D78B61 /* ServerDiscovery.swift */; };
|
||||
091B5A8D268315D400D78B61 /* ServerDiscovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091B5A872683142E00D78B61 /* ServerDiscovery.swift */; };
|
||||
4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; };
|
||||
4E8B34EA2AB91B6E0018F305 /* FilterDrawerSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */; };
|
||||
4E8B34EB2AB91B6E0018F305 /* FilterDrawerSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */; };
|
||||
4EAA35BB2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAA35BA2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift */; };
|
||||
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E6267ABD79005D8AB9 /* HomeView.swift */; };
|
||||
53192D5D265AA78A008A4215 /* DeviceProfileBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */; };
|
||||
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531AC8BE26750DE20091C7EB /* ImageView.swift */; };
|
||||
|
@ -622,7 +626,6 @@
|
|||
E1CCF12E28ABF989006CAC9E /* PosterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CCF12D28ABF989006CAC9E /* PosterType.swift */; };
|
||||
E1CCF13128AC07EC006CAC9E /* PosterHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CCF13028AC07EC006CAC9E /* PosterHStack.swift */; };
|
||||
E1CD13EF28EF364100CB46CA /* DetectOrientationModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CD13EE28EF364100CB46CA /* DetectOrientationModifier.swift */; };
|
||||
E1CEFBF527914C7700F60429 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CEFBF427914C7700F60429 /* CustomizeViewsSettings.swift */; };
|
||||
E1CEFBF727914E6400F60429 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CEFBF627914E6400F60429 /* CustomizeViewsSettings.swift */; };
|
||||
E1CFE28028FA606800B7D34C /* ChapterTrack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CFE27F28FA606800B7D34C /* ChapterTrack.swift */; };
|
||||
E1D3043228D175CE00587289 /* StaticLibraryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D3043128D175CE00587289 /* StaticLibraryViewModel.swift */; };
|
||||
|
@ -771,6 +774,9 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
091B5A872683142E00D78B61 /* ServerDiscovery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerDiscovery.swift; sourceTree = "<group>"; };
|
||||
4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = "<group>"; };
|
||||
4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterDrawerSelection.swift; sourceTree = "<group>"; };
|
||||
4EAA35BA2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterDrawerButtonSelectorView.swift; sourceTree = "<group>"; };
|
||||
531690E6267ABD79005D8AB9 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlainNavigationLinkButton.swift; sourceTree = "<group>"; };
|
||||
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileBuilder.swift; sourceTree = "<group>"; };
|
||||
|
@ -1193,7 +1199,6 @@
|
|||
E1CCF12D28ABF989006CAC9E /* PosterType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PosterType.swift; sourceTree = "<group>"; };
|
||||
E1CCF13028AC07EC006CAC9E /* PosterHStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PosterHStack.swift; sourceTree = "<group>"; };
|
||||
E1CD13EE28EF364100CB46CA /* DetectOrientationModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectOrientationModifier.swift; sourceTree = "<group>"; };
|
||||
E1CEFBF427914C7700F60429 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = "<group>"; };
|
||||
E1CEFBF627914E6400F60429 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = "<group>"; };
|
||||
E1CFE27F28FA606800B7D34C /* ChapterTrack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChapterTrack.swift; sourceTree = "<group>"; };
|
||||
E1D3043128D175CE00587289 /* StaticLibraryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLibraryViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -1375,6 +1380,22 @@
|
|||
path = ServerDiscovery;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4EAA35B82AB9694000D840DD /* FilterDrawerSettingsView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4EAA35B92AB9694D00D840DD /* Components */,
|
||||
);
|
||||
path = FilterDrawerSettingsView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4EAA35B92AB9694D00D840DD /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4EAA35BA2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift */,
|
||||
);
|
||||
path = Components;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5310694F2684E7EE00CFFDBA /* VideoPlayer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1503,6 +1524,7 @@
|
|||
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */,
|
||||
E17FB55128C119D400311DFE /* Displayable.swift */,
|
||||
E129429728F4785200796AC6 /* EnumPicker.swift */,
|
||||
4E8B34E92AB91B6E0018F305 /* FilterDrawerSelection.swift */,
|
||||
E1092F4B29106F9F00163F57 /* GestureAction.swift */,
|
||||
E19169CD272514760085832A /* HTTPScheme.swift */,
|
||||
535870AC2669D8DD00D05A09 /* ItemFilters.swift */,
|
||||
|
@ -2694,7 +2716,8 @@
|
|||
E1E5D54A2783E26100692DFE /* SettingsView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E1CEFBF427914C7700F60429 /* CustomizeViewsSettings.swift */,
|
||||
4EAA35B82AB9694000D840DD /* FilterDrawerSettingsView */,
|
||||
4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */,
|
||||
E175AFF2299AC117004DCF52 /* DebugSettingsView.swift */,
|
||||
E1E5D54B2783E27200692DFE /* ExperimentalSettingsView.swift */,
|
||||
E104C86F296E087200C1C3F9 /* IndicatorSettingsView.swift */,
|
||||
|
@ -3279,6 +3302,7 @@
|
|||
C4BE077A2726EE82003F4AD1 /* LiveTVTabCoordinator.swift in Sources */,
|
||||
E193D553271943D500900D82 /* tvOSMainTabCoordinator.swift in Sources */,
|
||||
E1575E83293E784A001665B1 /* MediaItemViewModel.swift in Sources */,
|
||||
4E8B34EB2AB91B6E0018F305 /* FilterDrawerSelection.swift in Sources */,
|
||||
E174121029AE9D94003EF3B5 /* NavigationCoordinatable.swift in Sources */,
|
||||
E154965F296CA2EF00C4EF88 /* DownloadTask.swift in Sources */,
|
||||
E154967E296CCB6C00C4EF88 /* BasicNavigationCoordinator.swift in Sources */,
|
||||
|
@ -3300,6 +3324,7 @@
|
|||
E1B33ECF28EB6EA90073B0FD /* OverlayMenu.swift in Sources */,
|
||||
6220D0B426D5ED8000B8E046 /* LibraryCoordinator.swift in Sources */,
|
||||
E17AC96D2954E9CA003D2BC2 /* DownloadListView.swift in Sources */,
|
||||
4E8B34EA2AB91B6E0018F305 /* FilterDrawerSelection.swift in Sources */,
|
||||
E1A1528828FD229500600579 /* ChevronButton.swift in Sources */,
|
||||
E1B490472967E2E500D3EDCE /* CoreStore.swift in Sources */,
|
||||
6220D0C026D61C5000B8E046 /* ItemCoordinator.swift in Sources */,
|
||||
|
@ -3405,7 +3430,6 @@
|
|||
E1C8CE5B28FE512400DF5D7B /* CGPoint.swift in Sources */,
|
||||
E18ACA922A15A32F00BB4F35 /* (null) in Sources */,
|
||||
E1E1E24D28DF8A2E000DF5FD /* PreferenceKeys.swift in Sources */,
|
||||
E1CEFBF527914C7700F60429 /* CustomizeViewsSettings.swift in Sources */,
|
||||
E1C812BC277A8E5D00918266 /* PlaybackSpeed.swift in Sources */,
|
||||
E15756322935642A00976E1F /* Float.swift in Sources */,
|
||||
E139CC1D28EC836F00688DE2 /* ChapterOverlay.swift in Sources */,
|
||||
|
@ -3434,6 +3458,7 @@
|
|||
E1171A1928A2212600FA1AF5 /* QuickConnectView.swift in Sources */,
|
||||
E1DC9819296DD1CD00982F06 /* CinematicBackgroundView.swift in Sources */,
|
||||
E11CEB8D28999B4A003E74C7 /* Font.swift in Sources */,
|
||||
4EAA35BB2AB9699B00D840DD /* FilterDrawerButtonSelectorView.swift in Sources */,
|
||||
E139CC1F28EC83E400688DE2 /* Int.swift in Sources */,
|
||||
E11895A9289383BC0042947B /* ScrollViewOffsetModifier.swift in Sources */,
|
||||
E1DA656C28E78C1700592A73 /* MenuPosterHStackModel.swift in Sources */,
|
||||
|
@ -3568,6 +3593,7 @@
|
|||
E1937A3E288F0D3D00CB80AA /* UIScreen.swift in Sources */,
|
||||
C4BE076F2720FEFF003F4AD1 /* PlainNavigationLinkButton.swift in Sources */,
|
||||
E1EBCB46278BD595009FE6E9 /* ItemOverviewView.swift in Sources */,
|
||||
4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */,
|
||||
E19F6C5D28F5189300C5197E /* MediaStreamInfoView.swift in Sources */,
|
||||
E1D8429329340B8300D1041A /* Utilities.swift in Sources */,
|
||||
E18CE0B428A22EDA0092E7F1 /* RepeatingTimer.swift in Sources */,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2023 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import JellyfinAPI
|
||||
import SwiftUI
|
||||
|
||||
|
@ -13,7 +14,7 @@ struct FilterDrawerHStack: View {
|
|||
|
||||
@ObservedObject
|
||||
private var viewModel: FilterViewModel
|
||||
|
||||
private var filterDrawerButtonSelection: [FilterDrawerButtonSelection]
|
||||
private var onSelect: (FilterCoordinator.Parameters) -> Void
|
||||
|
||||
var body: some View {
|
||||
|
@ -29,55 +30,29 @@ struct FilterDrawerHStack: View {
|
|||
FilterDrawerButton(systemName: "line.3.horizontal.decrease.circle.fill", activated: true)
|
||||
}
|
||||
}
|
||||
|
||||
FilterDrawerButton(title: L10n.genres, activated: viewModel.currentFilters.genres != [])
|
||||
ForEach(filterDrawerButtonSelection, id: \.self) { button in
|
||||
FilterDrawerButton(title: button.displayTitle, activated: button.isItemsFilterActive(
|
||||
activeFilters: viewModel.currentFilters
|
||||
))
|
||||
.onSelect {
|
||||
onSelect(.init(
|
||||
title: L10n.genres,
|
||||
title: button.displayTitle,
|
||||
viewModel: viewModel,
|
||||
filter: \.genres,
|
||||
selectorType: .multi
|
||||
))
|
||||
}
|
||||
|
||||
FilterDrawerButton(title: L10n.filters, activated: viewModel.currentFilters.filters != [])
|
||||
.onSelect {
|
||||
onSelect(.init(
|
||||
title: L10n.filters,
|
||||
viewModel: viewModel,
|
||||
filter: \.filters,
|
||||
selectorType: .multi
|
||||
))
|
||||
}
|
||||
|
||||
FilterDrawerButton(title: L10n.order, activated: viewModel.currentFilters.sortOrder != [APISortOrder.ascending.filter])
|
||||
.onSelect {
|
||||
onSelect(.init(
|
||||
title: L10n.order,
|
||||
viewModel: viewModel,
|
||||
filter: \.sortOrder,
|
||||
selectorType: .single
|
||||
))
|
||||
}
|
||||
|
||||
FilterDrawerButton(title: L10n.sort, activated: viewModel.currentFilters.sortBy != [SortBy.name.filter])
|
||||
.onSelect {
|
||||
onSelect(.init(
|
||||
title: L10n.sort,
|
||||
viewModel: viewModel,
|
||||
filter: \.sortBy,
|
||||
selectorType: .single
|
||||
filter: button.itemFilter,
|
||||
selectorType: button.selectorType
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FilterDrawerHStack {
|
||||
|
||||
init(viewModel: FilterViewModel) {
|
||||
init(viewModel: FilterViewModel, filterDrawerButtonSelection: [FilterDrawerButtonSelection]) {
|
||||
self.init(
|
||||
viewModel: viewModel,
|
||||
filterDrawerButtonSelection: filterDrawerButtonSelection,
|
||||
onSelect: { _ in }
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ struct LibraryView: View {
|
|||
@Default(.Customization.Library.viewType)
|
||||
private var libraryViewType
|
||||
|
||||
@Default(.Customization.Filters.libraryFilterDrawerButtons)
|
||||
private var filterDrawerButtonSelection
|
||||
|
||||
@EnvironmentObject
|
||||
private var router: LibraryCoordinator.Router
|
||||
|
||||
|
@ -67,14 +70,16 @@ struct LibraryView: View {
|
|||
}
|
||||
.navigationTitle(viewModel.parent?.displayTitle ?? "")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navBarDrawer {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
FilterDrawerHStack(viewModel: viewModel.filterViewModel)
|
||||
.onSelect { filterCoordinatorParameters in
|
||||
router.route(to: \.filter, filterCoordinatorParameters)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 1)
|
||||
.if(!filterDrawerButtonSelection.isEmpty) { view in
|
||||
view.navBarDrawer {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
FilterDrawerHStack(viewModel: viewModel.filterViewModel, filterDrawerButtonSelection: filterDrawerButtonSelection)
|
||||
.onSelect { filterCoordinatorParameters in
|
||||
router.route(to: \.filter, filterCoordinatorParameters)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.toolbar {
|
||||
|
|
|
@ -16,6 +16,9 @@ struct SearchView: View {
|
|||
@Default(.Customization.searchPosterType)
|
||||
private var searchPosterType
|
||||
|
||||
@Default(.Customization.Filters.searchFilterDrawerButtons)
|
||||
private var filterDrawerButtonSelection
|
||||
|
||||
@EnvironmentObject
|
||||
private var router: SearchCoordinator.Router
|
||||
|
||||
|
@ -105,14 +108,16 @@ struct SearchView: View {
|
|||
}
|
||||
.navigationTitle(L10n.search)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navBarDrawer {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
FilterDrawerHStack(viewModel: viewModel.filterViewModel)
|
||||
.onSelect { filterCoordinatorParameters in
|
||||
router.route(to: \.filter, filterCoordinatorParameters)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 1)
|
||||
.if(!filterDrawerButtonSelection.isEmpty) { view in
|
||||
view.navBarDrawer {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
FilterDrawerHStack(viewModel: viewModel.filterViewModel, filterDrawerButtonSelection: filterDrawerButtonSelection)
|
||||
.onSelect { filterCoordinatorParameters in
|
||||
router.route(to: \.filter, filterCoordinatorParameters)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: L10n.search)
|
||||
|
|
|
@ -24,6 +24,11 @@ struct CustomizeViewsSettings: View {
|
|||
@Default(.Customization.shouldShowMissingEpisodes)
|
||||
var shouldShowMissingEpisodes
|
||||
|
||||
@Default(.Customization.Filters.libraryFilterDrawerButtons)
|
||||
var libraryFilterDrawerButtons
|
||||
@Default(.Customization.Filters.searchFilterDrawerButtons)
|
||||
var searchFilterDrawerButtons
|
||||
|
||||
@Default(.Customization.showPosterLabels)
|
||||
var showPosterLabels
|
||||
@Default(.Customization.nextUpPosterType)
|
||||
|
@ -72,12 +77,28 @@ struct CustomizeViewsSettings: View {
|
|||
Section {
|
||||
|
||||
Toggle(L10n.favorites, isOn: $showFavorites)
|
||||
|
||||
Toggle(L10n.randomImage, isOn: $libraryRandomImage)
|
||||
|
||||
} header: {
|
||||
L10n.library.text
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
ChevronButton(title: L10n.library)
|
||||
.onSelect {
|
||||
router.route(to: \.filterDrawerButtonSelector, $libraryFilterDrawerButtons)
|
||||
}
|
||||
|
||||
ChevronButton(title: L10n.search)
|
||||
.onSelect {
|
||||
router.route(to: \.filterDrawerButtonSelector, $searchFilterDrawerButtons)
|
||||
}
|
||||
|
||||
} header: {
|
||||
L10n.filters.text
|
||||
}
|
||||
|
||||
Section {
|
||||
Toggle(L10n.showMissingSeasons, isOn: $shouldShowMissingSeasons)
|
||||
Toggle(L10n.showMissingEpisodes, isOn: $shouldShowMissingEpisodes)
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// Swiftfin is subject to the terms of the Mozilla Public
|
||||
// License, v2.0. If a copy of the MPL was not distributed with this
|
||||
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) 2023 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import SwiftUI
|
||||
|
||||
// TODO: Look at moving across sections
|
||||
// TODO: Look at general implementation in SelectorView
|
||||
struct FilterDrawerButtonSelectorView: View {
|
||||
|
||||
@Binding
|
||||
var selectedButtonsBinding: [FilterDrawerButtonSelection]
|
||||
|
||||
@Environment(\.editMode)
|
||||
private var editMode
|
||||
|
||||
@State
|
||||
private var _selectedButtons: [FilterDrawerButtonSelection]
|
||||
|
||||
private var disabledButtons: [FilterDrawerButtonSelection] {
|
||||
FilterDrawerButtonSelection.allCases.filter { !_selectedButtons.contains($0) }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
ForEach(_selectedButtons) { item in
|
||||
Button {
|
||||
if !(editMode?.wrappedValue.isEditing ?? true) {
|
||||
select(item: item)
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text(item.displayTitle)
|
||||
|
||||
Spacer()
|
||||
|
||||
if !(editMode?.wrappedValue.isEditing ?? false) {
|
||||
Image(systemName: "minus.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
}
|
||||
.onMove(perform: move)
|
||||
|
||||
if _selectedButtons.isEmpty {
|
||||
Text("None")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
} header: {
|
||||
Text("Enabled")
|
||||
}
|
||||
|
||||
Section {
|
||||
ForEach(disabledButtons) { item in
|
||||
Button {
|
||||
if !(editMode?.wrappedValue.isEditing ?? true) {
|
||||
select(item: item)
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text(item.displayTitle)
|
||||
|
||||
Spacer()
|
||||
|
||||
if !(editMode?.wrappedValue.isEditing ?? false) {
|
||||
Image(systemName: "plus.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
}
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
}
|
||||
|
||||
if disabledButtons.isEmpty {
|
||||
Text("None")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
} header: {
|
||||
Text("Disabled")
|
||||
}
|
||||
}
|
||||
.animation(.linear(duration: 0.2), value: _selectedButtons)
|
||||
.toolbar {
|
||||
EditButton()
|
||||
}
|
||||
.onChange(of: _selectedButtons) { newValue in
|
||||
selectedButtonsBinding = newValue
|
||||
}
|
||||
}
|
||||
|
||||
func move(from source: IndexSet, to destination: Int) {
|
||||
_selectedButtons.move(fromOffsets: source, toOffset: destination)
|
||||
}
|
||||
|
||||
private func select(item: FilterDrawerButtonSelection) {
|
||||
if _selectedButtons.contains(item) {
|
||||
_selectedButtons.removeAll(where: { $0.id == item.id })
|
||||
} else {
|
||||
_selectedButtons.append(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FilterDrawerButtonSelectorView {
|
||||
|
||||
init(selectedButtonsBinding: Binding<[FilterDrawerButtonSelection]>) {
|
||||
self.init(
|
||||
selectedButtonsBinding: selectedButtonsBinding,
|
||||
_selectedButtons: selectedButtonsBinding.wrappedValue
|
||||
)
|
||||
// self._selectedButtonsBinding = selectedButtonsBinding
|
||||
// self._selectedButtons = selectedButtonsBinding.wrappedValue
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue