154 lines
4.7 KiB
Swift
154 lines
4.7 KiB
Swift
//
|
|
// 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) 2025 Jellyfin & Jellyfin Contributors
|
|
//
|
|
|
|
import JellyfinAPI
|
|
import SwiftUI
|
|
|
|
struct CulturePicker: View {
|
|
|
|
// MARK: - State Objects
|
|
|
|
@StateObject
|
|
private var viewModel: CulturesViewModel
|
|
|
|
// MARK: - Input Properties
|
|
|
|
private var selectionBinding: Binding<CultureDto?>
|
|
private let title: String
|
|
|
|
@State
|
|
private var selection: CultureDto?
|
|
|
|
// MARK: - Body
|
|
|
|
var body: some View {
|
|
Group {
|
|
#if os(tvOS)
|
|
ListRowMenu(title, subtitle: $selection.wrappedValue?.displayTitle) {
|
|
Picker(title, selection: $selection) {
|
|
Text(CultureDto.none.displayTitle)
|
|
.tag(CultureDto.none as CultureDto?)
|
|
|
|
ForEach(viewModel.value, id: \.self) { value in
|
|
Text(value.displayTitle)
|
|
.tag(value as CultureDto?)
|
|
}
|
|
}
|
|
}
|
|
// TODO: iOS 17+ move this to the Group
|
|
.onChange(of: viewModel.value) {
|
|
updateSelection()
|
|
}
|
|
.onChange(of: selection) { _, newValue in
|
|
selectionBinding.wrappedValue = newValue
|
|
}
|
|
.menuStyle(.borderlessButton)
|
|
.listRowInsets(.zero)
|
|
#else
|
|
Picker(title, selection: $selection) {
|
|
|
|
Text(CultureDto.none.displayTitle)
|
|
.tag(CultureDto.none as CultureDto?)
|
|
|
|
ForEach(viewModel.value, id: \.self) { value in
|
|
Text(value.displayTitle)
|
|
.tag(value as CultureDto?)
|
|
}
|
|
}
|
|
// TODO: iOS 17+ delete this and use the tvOS onChange at the Group level
|
|
.onChange(of: viewModel.value) { _ in
|
|
updateSelection()
|
|
}
|
|
.onChange(of: selection) { newValue in
|
|
selectionBinding.wrappedValue = newValue
|
|
}
|
|
#endif
|
|
}
|
|
.onFirstAppear {
|
|
viewModel.refresh()
|
|
}
|
|
}
|
|
|
|
private func updateSelection() {
|
|
let newValue = viewModel.value.first { value in
|
|
if let selectedTwo = selection?.twoLetterISOLanguageName,
|
|
let candidateTwo = value.twoLetterISOLanguageName,
|
|
selectedTwo == candidateTwo
|
|
{
|
|
return true
|
|
}
|
|
if let selectedThree = selection?.threeLetterISOLanguageName,
|
|
let candidateThree = value.threeLetterISOLanguageName,
|
|
selectedThree == candidateThree
|
|
{
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
selection = newValue ?? CultureDto.none
|
|
}
|
|
}
|
|
|
|
extension CulturePicker {
|
|
|
|
init(_ title: String, twoLetterISOLanguageName: Binding<String?>) {
|
|
self.title = title
|
|
self._selection = State(
|
|
initialValue: twoLetterISOLanguageName.wrappedValue.flatMap {
|
|
CultureDto(twoLetterISOLanguageName: $0)
|
|
} ?? CultureDto.none
|
|
)
|
|
|
|
self.selectionBinding = Binding<CultureDto?>(
|
|
get: {
|
|
guard let code = twoLetterISOLanguageName.wrappedValue else {
|
|
return CultureDto.none
|
|
}
|
|
return CultureDto(twoLetterISOLanguageName: code)
|
|
},
|
|
set: { newCountry in
|
|
twoLetterISOLanguageName.wrappedValue = newCountry?.twoLetterISOLanguageName
|
|
}
|
|
)
|
|
|
|
self._viewModel = StateObject(
|
|
wrappedValue: CulturesViewModel(
|
|
initialValue: [.none]
|
|
)
|
|
)
|
|
}
|
|
|
|
init(_ title: String, threeLetterISOLanguageName: Binding<String?>) {
|
|
self.title = title
|
|
self._selection = State(
|
|
initialValue: threeLetterISOLanguageName.wrappedValue.flatMap {
|
|
CultureDto(threeLetterISOLanguageName: $0)
|
|
} ?? CultureDto.none
|
|
)
|
|
|
|
self.selectionBinding = Binding<CultureDto?>(
|
|
get: {
|
|
guard let code = threeLetterISOLanguageName.wrappedValue else {
|
|
return CultureDto.none
|
|
}
|
|
return CultureDto(threeLetterISOLanguageName: code)
|
|
},
|
|
set: { newCountry in
|
|
threeLetterISOLanguageName.wrappedValue = newCountry?.threeLetterISOLanguageName
|
|
}
|
|
)
|
|
|
|
self._viewModel = StateObject(
|
|
wrappedValue: CulturesViewModel(
|
|
initialValue: [.none]
|
|
)
|
|
)
|
|
}
|
|
}
|