add SearchablePickerView
This commit is contained in:
parent
f308219c34
commit
c27ebe5ad2
|
@ -108,6 +108,8 @@
|
|||
621C638026672A30004216EA /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 621C637F26672A30004216EA /* NukeUI */; };
|
||||
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */; };
|
||||
6228B1C22670EB010067FD35 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBFD263B596B003A4E83 /* PersistenceController.swift */; };
|
||||
624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; };
|
||||
624C21762685CF60007F1390 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; };
|
||||
625CB5682678B6FB00530A6E /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5672678B6FB00530A6E /* SplashView.swift */; };
|
||||
625CB56A2678B71200530A6E /* SplashViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5692678B71200530A6E /* SplashViewModel.swift */; };
|
||||
625CB56C2678C0FD00530A6E /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB56B2678C0FD00530A6E /* MainTabView.swift */; };
|
||||
|
@ -289,6 +291,7 @@
|
|||
621338922660107500A81A2A /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = "<group>"; };
|
||||
621338B22660A07800A81A2A /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
|
||||
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallaxHeader.swift; sourceTree = "<group>"; };
|
||||
624C21742685CF60007F1390 /* SearchablePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchablePickerView.swift; sourceTree = "<group>"; };
|
||||
625CB5672678B6FB00530A6E /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
|
||||
625CB5692678B71200530A6E /* SplashViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashViewModel.swift; sourceTree = "<group>"; };
|
||||
625CB56B2678C0FD00530A6E /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
|
||||
|
@ -599,6 +602,7 @@
|
|||
6267B3D92671138200A7371D /* ImageExtensions.swift */,
|
||||
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */,
|
||||
62CB3F4A2685BB77003D0A6F /* DefaultsExtension.swift */,
|
||||
624C21742685CF60007F1390 /* SearchablePickerView.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
@ -952,6 +956,7 @@
|
|||
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
|
||||
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */,
|
||||
5310695B2684E7EE00CFFDBA /* AudioView.swift in Sources */,
|
||||
624C21762685CF60007F1390 /* SearchablePickerView.swift in Sources */,
|
||||
091B5A8D268315D400D78B61 /* ServerDiscovery.swift in Sources */,
|
||||
53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */,
|
||||
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */,
|
||||
|
@ -1021,6 +1026,7 @@
|
|||
621338B32660A07800A81A2A /* LazyView.swift in Sources */,
|
||||
531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */,
|
||||
62E632E0267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
|
||||
624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */,
|
||||
62EC352F267666A5000E9F2D /* SessionManager.swift in Sources */,
|
||||
62E632E3267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
|
||||
091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */,
|
||||
|
|
|
@ -50,22 +50,22 @@ struct SettingsView: View {
|
|||
|
||||
Section(header: Text("Accessibility")) {
|
||||
Toggle("Automatically show subtitles", isOn: $isAutoSelectSubtitles)
|
||||
Picker("Subtitles Language preferences", selection: $autoSelectSubtitlesLangcode) {
|
||||
Text("Auto")
|
||||
.tag("Auto")
|
||||
ForEach(viewModel.isoLanguageCodesPair, id: \.1) {
|
||||
Text($0.0)
|
||||
.tag($0.1)
|
||||
}
|
||||
}
|
||||
Picker("Audio Language preferences", selection: $autoSelectAudioLangcode) {
|
||||
Text("Auto")
|
||||
.tag("Auto")
|
||||
ForEach(viewModel.isoLanguageCodesPair, id: \.1) {
|
||||
Text($0.0)
|
||||
.tag($0.1)
|
||||
}
|
||||
}
|
||||
SearchablePicker(label: "Subtitles Language preferences",
|
||||
options: viewModel.langs,
|
||||
optionToString: { $0.name },
|
||||
selected:Binding<Lang>(
|
||||
get: { viewModel.langs.first(where: { $0.isoCode == autoSelectSubtitlesLangcode }) ?? .auto },
|
||||
set: {autoSelectSubtitlesLangcode = $0.isoCode}
|
||||
)
|
||||
)
|
||||
SearchablePicker(label: "Audio Language preferences",
|
||||
options: viewModel.langs,
|
||||
optionToString: { $0.name },
|
||||
selected: Binding<Lang>(
|
||||
get: { viewModel.langs.first(where: { $0.isoCode == autoSelectAudioLangcode }) ?? .auto },
|
||||
set: { autoSelectAudioLangcode = $0.isoCode}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Section {
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
/*
|
||||
* 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 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
private struct SearchablePickerView<Selectable: Hashable>: View {
|
||||
@Environment(\.presentationMode)
|
||||
var presentationMode
|
||||
|
||||
let options: [Selectable]
|
||||
let optionToString: (Selectable) -> String
|
||||
let label: String
|
||||
|
||||
@State var text = ""
|
||||
@Binding var selected: Selectable
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
SearchBar(text: $text)
|
||||
List(options.filter {
|
||||
guard !text.isEmpty else { return true }
|
||||
return optionToString($0).lowercased().contains(text.lowercased())
|
||||
}, id: \.self) { selectable in
|
||||
Button(action: {
|
||||
selected = selectable
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}) {
|
||||
HStack {
|
||||
Text(optionToString(selectable)).foregroundColor(Color.primary)
|
||||
Spacer()
|
||||
if selected == selectable {
|
||||
Image(systemName: "checkmark").foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.listStyle(GroupedListStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SearchablePicker<Selectable: Hashable>: View {
|
||||
let label: String
|
||||
let options: [Selectable]
|
||||
let optionToString: (Selectable) -> String
|
||||
|
||||
@Binding var selected: Selectable
|
||||
|
||||
var body: some View {
|
||||
NavigationLink(destination: searchablePickerView()) {
|
||||
HStack {
|
||||
Text(label)
|
||||
Spacer()
|
||||
Text(optionToString(selected))
|
||||
.foregroundColor(.gray)
|
||||
.multilineTextAlignment(.trailing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func searchablePickerView() -> some View {
|
||||
SearchablePickerView(options: options,
|
||||
optionToString: optionToString,
|
||||
label: label,
|
||||
selected: $selected)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
//
|
||||
/*
|
||||
* 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 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
/*
|
||||
* 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 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
|
@ -23,10 +23,17 @@ struct Bitrates: Codable, Hashable {
|
|||
public var value: Int
|
||||
}
|
||||
|
||||
struct Lang: Hashable {
|
||||
var name: String
|
||||
var isoCode: String
|
||||
|
||||
static let auto = Lang(name: "Auto", isoCode: "Auto")
|
||||
}
|
||||
|
||||
final class SettingsViewModel: ObservableObject {
|
||||
let currentLocale = Locale.current
|
||||
var bitrates: [Bitrates] = []
|
||||
var isoLanguageCodesPair = [(name: String, isoCode: String)]()
|
||||
var langs = [Lang]()
|
||||
|
||||
init() {
|
||||
let url = Bundle.main.url(forResource: "bitrates", withExtension: "json")!
|
||||
|
@ -41,10 +48,11 @@ final class SettingsViewModel: ObservableObject {
|
|||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
||||
isoLanguageCodesPair = Locale.isoLanguageCodes.compactMap {
|
||||
|
||||
self.langs = Locale.isoLanguageCodes.compactMap {
|
||||
guard let name = currentLocale.localizedString(forLanguageCode: $0) else { return nil }
|
||||
return (name, $0)
|
||||
return Lang(name: name, isoCode: $0)
|
||||
}.sorted(by: { $0.name < $1.name })
|
||||
self.langs.insert(.auto, at: 0)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue