Some Cleanup (#1216)

This commit is contained in:
Ethan Pippin 2024-08-30 09:05:56 -06:00 committed by GitHub
parent 556696b90f
commit a199d69a31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 78 additions and 43 deletions

View File

@ -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)
}
}
}

View File

@ -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
}
}
}

View File

@ -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 */,

View File

@ -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) }
}

View File

@ -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")
}
}