Some Cleanup (#1216)
This commit is contained in:
parent
556696b90f
commit
a199d69a31
|
@ -22,8 +22,20 @@ struct SelectorView<Element: Displayable & Hashable, Label: View>: View {
|
|||
@Default(.accentColor)
|
||||
private var accentColor
|
||||
|
||||
@Binding
|
||||
private var selection: Set<Element>
|
||||
@StateObject
|
||||
private var selection: BindingBox<Set<Element>>
|
||||
|
||||
private init(
|
||||
selection: Binding<Set<Element>>,
|
||||
sources: [Element],
|
||||
label: @escaping (Element) -> Label,
|
||||
type: SelectorType
|
||||
) {
|
||||
self._selection = StateObject(wrappedValue: BindingBox(source: selection))
|
||||
self.sources = sources
|
||||
self.label = label
|
||||
self.type = type
|
||||
}
|
||||
|
||||
private let sources: [Element]
|
||||
private var label: (Element) -> Label
|
||||
|
@ -44,7 +56,7 @@ struct SelectorView<Element: Displayable & Hashable, Label: View>: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
if selection.contains(element) {
|
||||
if selection.value.contains(element) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.resizable()
|
||||
.backport
|
||||
|
@ -60,14 +72,14 @@ struct SelectorView<Element: Displayable & Hashable, Label: View>: View {
|
|||
}
|
||||
|
||||
private func handleSingleSelect(with element: Element) {
|
||||
selection = [element]
|
||||
selection.value = [element]
|
||||
}
|
||||
|
||||
private func handleMultiSelect(with element: Element) {
|
||||
if selection.contains(element) {
|
||||
selection.remove(element)
|
||||
if selection.value.contains(element) {
|
||||
selection.value.remove(element)
|
||||
} else {
|
||||
selection.insert(element)
|
||||
selection.value.insert(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// 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) 2024 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
/// Utility for views that are passed a `Binding` that
|
||||
/// may not be able to respond to view updates from
|
||||
/// the source
|
||||
class BindingBox<Wrapped>: ObservableObject {
|
||||
|
||||
@Published
|
||||
var value: Wrapped
|
||||
|
||||
private let source: Binding<Wrapped>
|
||||
private var valueObserver: AnyCancellable!
|
||||
|
||||
init(source: Binding<Wrapped>) {
|
||||
self.source = source
|
||||
self.value = source.wrappedValue
|
||||
valueObserver = nil
|
||||
|
||||
valueObserver = $value.sink { [weak self] in
|
||||
self?.source.wrappedValue = $0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -273,6 +273,8 @@
|
|||
E1153DCD2BBB633B00424D36 /* FastSVGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1153DCB2BBB633B00424D36 /* FastSVGView.swift */; };
|
||||
E1153DD02BBB634F00424D36 /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DCF2BBB634F00424D36 /* SVGKit */; };
|
||||
E1153DD22BBB649C00424D36 /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DD12BBB649C00424D36 /* SVGKit */; };
|
||||
E11562952C818CB2001D5DE4 /* BindingBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11562942C818CB2001D5DE4 /* BindingBox.swift */; };
|
||||
E11562962C818CB2001D5DE4 /* BindingBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11562942C818CB2001D5DE4 /* BindingBox.swift */; };
|
||||
E118959D289312020042947B /* BaseItemPerson+Poster.swift in Sources */ = {isa = PBXBuildFile; fileRef = E118959C289312020042947B /* BaseItemPerson+Poster.swift */; };
|
||||
E118959E289312020042947B /* BaseItemPerson+Poster.swift in Sources */ = {isa = PBXBuildFile; fileRef = E118959C289312020042947B /* BaseItemPerson+Poster.swift */; };
|
||||
E11895A9289383BC0042947B /* ScrollViewOffsetModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */; };
|
||||
|
@ -1143,6 +1145,7 @@
|
|||
E1153D9B2BBA3E9D00424D36 /* LoadingCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingCard.swift; sourceTree = "<group>"; };
|
||||
E1153DB22BBA80B400424D36 /* EmptyCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCard.swift; sourceTree = "<group>"; };
|
||||
E1153DCB2BBB633B00424D36 /* FastSVGView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FastSVGView.swift; sourceTree = "<group>"; };
|
||||
E11562942C818CB2001D5DE4 /* BindingBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingBox.swift; sourceTree = "<group>"; };
|
||||
E1171A1828A2212600FA1AF5 /* QuickConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectView.swift; sourceTree = "<group>"; };
|
||||
E118959C289312020042947B /* BaseItemPerson+Poster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseItemPerson+Poster.swift"; sourceTree = "<group>"; };
|
||||
E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewOffsetModifier.swift; sourceTree = "<group>"; };
|
||||
|
@ -1817,6 +1820,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
E1D4BF802719D22800A11E64 /* AppAppearance.swift */,
|
||||
E11562942C818CB2001D5DE4 /* BindingBox.swift */,
|
||||
E129429728F4785200796AC6 /* CaseIterablePicker.swift */,
|
||||
E10231432BCF8A51009D71FC /* ChannelProgram.swift */,
|
||||
E17FB55128C119D400311DFE /* Displayable.swift */,
|
||||
|
@ -4216,6 +4220,7 @@
|
|||
E1575EA3293E7B1E001665B1 /* UIDevice.swift in Sources */,
|
||||
E193D547271941C500900D82 /* SelectUserView.swift in Sources */,
|
||||
E1BDF2E62951475300CC0294 /* VideoPlayerActionButton.swift in Sources */,
|
||||
E11562962C818CB2001D5DE4 /* BindingBox.swift in Sources */,
|
||||
E10231592BCF8AF8009D71FC /* ChannelLibraryView.swift in Sources */,
|
||||
E1E6C44929AECEE70064123F /* AutoPlayActionButton.swift in Sources */,
|
||||
E1C926152887565C002A7A66 /* EpisodeCard.swift in Sources */,
|
||||
|
@ -4432,6 +4437,7 @@
|
|||
6334175B287DDFB9000603CE /* QuickConnectAuthorizeView.swift in Sources */,
|
||||
E13F05F128BC9016003499D2 /* LibraryRow.swift in Sources */,
|
||||
E168BD10289A4162001A6922 /* HomeView.swift in Sources */,
|
||||
E11562952C818CB2001D5DE4 /* BindingBox.swift in Sources */,
|
||||
4E16FD532C01840C00110147 /* LetterPickerBar.swift in Sources */,
|
||||
E1BE1CEA2BDB5AFE008176A9 /* UserGridButton.swift in Sources */,
|
||||
E1401CB129386C9200E8B599 /* UIColor.swift in Sources */,
|
||||
|
|
|
@ -13,43 +13,44 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
|
|||
@Environment(\.editMode)
|
||||
private var editMode
|
||||
|
||||
@Binding
|
||||
private var selection: [Element]
|
||||
|
||||
@State
|
||||
private var updateSelection: [Element]
|
||||
@StateObject
|
||||
private var selection: BindingBox<[Element]>
|
||||
|
||||
private var disabledSelection: [Element] {
|
||||
sources.subtracting(updateSelection)
|
||||
sources.subtracting(selection.value)
|
||||
}
|
||||
|
||||
private var label: (Element) -> any View
|
||||
private let sources: [Element]
|
||||
|
||||
private func move(from source: IndexSet, to destination: Int) {
|
||||
updateSelection.move(fromOffsets: source, toOffset: destination)
|
||||
selection.value.move(fromOffsets: source, toOffset: destination)
|
||||
}
|
||||
|
||||
private func select(element: Element) {
|
||||
if updateSelection.contains(element) {
|
||||
updateSelection.removeAll(where: { $0 == element })
|
||||
if selection.value.contains(element) {
|
||||
selection.value.removeAll(where: { $0 == element })
|
||||
} else {
|
||||
updateSelection.append(element)
|
||||
selection.value.append(element)
|
||||
}
|
||||
}
|
||||
|
||||
private var isReordering: Bool {
|
||||
editMode?.wrappedValue.isEditing ?? false
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(L10n.enabled) {
|
||||
|
||||
if updateSelection.isEmpty {
|
||||
if selection.value.isEmpty {
|
||||
L10n.none.text
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
ForEach(updateSelection, id: \.self) { element in
|
||||
ForEach(selection.value, id: \.self) { element in
|
||||
Button {
|
||||
if !(editMode?.wrappedValue.isEditing ?? true) {
|
||||
if !isReordering {
|
||||
select(element: element)
|
||||
}
|
||||
} label: {
|
||||
|
@ -59,7 +60,7 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
if !(editMode?.wrappedValue.isEditing ?? false) {
|
||||
if !isReordering {
|
||||
Image(systemName: "minus.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
|
@ -79,7 +80,7 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
|
|||
|
||||
ForEach(disabledSelection, id: \.self) { element in
|
||||
Button {
|
||||
if !(editMode?.wrappedValue.isEditing ?? true) {
|
||||
if !isReordering {
|
||||
select(element: element)
|
||||
}
|
||||
} label: {
|
||||
|
@ -89,7 +90,7 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
if !(editMode?.wrappedValue.isEditing ?? false) {
|
||||
if !isReordering {
|
||||
Image(systemName: "plus.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
|
@ -99,21 +100,17 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.animation(.linear(duration: 0.2), value: updateSelection)
|
||||
.animation(.linear(duration: 0.2), value: selection.value)
|
||||
.toolbar {
|
||||
EditButton()
|
||||
}
|
||||
.onChange(of: updateSelection) { newValue in
|
||||
selection = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension OrderedSectionSelectorView {
|
||||
|
||||
init(selection: Binding<[Element]>, sources: [Element]) {
|
||||
self._selection = selection
|
||||
self._updateSelection = State(initialValue: selection.wrappedValue)
|
||||
self._selection = StateObject(wrappedValue: BindingBox(source: selection))
|
||||
self.sources = sources
|
||||
self.label = { Text($0.displayTitle).foregroundColor(.primary) }
|
||||
}
|
||||
|
|
|
@ -6,26 +6,17 @@
|
|||
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct FontPickerView: View {
|
||||
|
||||
@Binding
|
||||
private var selection: String
|
||||
|
||||
@State
|
||||
private var updateSelection: String
|
||||
|
||||
init(selection: Binding<String>) {
|
||||
self._selection = selection
|
||||
self.updateSelection = selection.wrappedValue
|
||||
}
|
||||
var selection: String
|
||||
|
||||
var body: some View {
|
||||
SelectorView(
|
||||
selection: $updateSelection,
|
||||
selection: $selection,
|
||||
sources: UIFont.familyNames
|
||||
)
|
||||
.label { fontFamily in
|
||||
|
@ -33,9 +24,6 @@ struct FontPickerView: View {
|
|||
.foregroundColor(.primary)
|
||||
.font(.custom(fontFamily, size: 18))
|
||||
}
|
||||
.onChange(of: updateSelection) { newValue in
|
||||
selection = newValue
|
||||
}
|
||||
.navigationTitle("Font")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue