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) @Default(.accentColor)
private var accentColor private var accentColor
@Binding @StateObject
private var selection: Set<Element> 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 let sources: [Element]
private var label: (Element) -> Label private var label: (Element) -> Label
@ -44,7 +56,7 @@ struct SelectorView<Element: Displayable & Hashable, Label: View>: View {
Spacer() Spacer()
if selection.contains(element) { if selection.value.contains(element) {
Image(systemName: "checkmark.circle.fill") Image(systemName: "checkmark.circle.fill")
.resizable() .resizable()
.backport .backport
@ -60,14 +72,14 @@ struct SelectorView<Element: Displayable & Hashable, Label: View>: View {
} }
private func handleSingleSelect(with element: Element) { private func handleSingleSelect(with element: Element) {
selection = [element] selection.value = [element]
} }
private func handleMultiSelect(with element: Element) { private func handleMultiSelect(with element: Element) {
if selection.contains(element) { if selection.value.contains(element) {
selection.remove(element) selection.value.remove(element)
} else { } 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 */; }; E1153DCD2BBB633B00424D36 /* FastSVGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1153DCB2BBB633B00424D36 /* FastSVGView.swift */; };
E1153DD02BBB634F00424D36 /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DCF2BBB634F00424D36 /* SVGKit */; }; E1153DD02BBB634F00424D36 /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DCF2BBB634F00424D36 /* SVGKit */; };
E1153DD22BBB649C00424D36 /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = E1153DD12BBB649C00424D36 /* 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 */; }; 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 */; }; E118959E289312020042947B /* BaseItemPerson+Poster.swift in Sources */ = {isa = PBXBuildFile; fileRef = E118959C289312020042947B /* BaseItemPerson+Poster.swift */; };
E11895A9289383BC0042947B /* ScrollViewOffsetModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E11895A8289383BC0042947B /* ScrollViewOffsetModifier.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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; E11895A8289383BC0042947B /* ScrollViewOffsetModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewOffsetModifier.swift; sourceTree = "<group>"; };
@ -1817,6 +1820,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E1D4BF802719D22800A11E64 /* AppAppearance.swift */, E1D4BF802719D22800A11E64 /* AppAppearance.swift */,
E11562942C818CB2001D5DE4 /* BindingBox.swift */,
E129429728F4785200796AC6 /* CaseIterablePicker.swift */, E129429728F4785200796AC6 /* CaseIterablePicker.swift */,
E10231432BCF8A51009D71FC /* ChannelProgram.swift */, E10231432BCF8A51009D71FC /* ChannelProgram.swift */,
E17FB55128C119D400311DFE /* Displayable.swift */, E17FB55128C119D400311DFE /* Displayable.swift */,
@ -4216,6 +4220,7 @@
E1575EA3293E7B1E001665B1 /* UIDevice.swift in Sources */, E1575EA3293E7B1E001665B1 /* UIDevice.swift in Sources */,
E193D547271941C500900D82 /* SelectUserView.swift in Sources */, E193D547271941C500900D82 /* SelectUserView.swift in Sources */,
E1BDF2E62951475300CC0294 /* VideoPlayerActionButton.swift in Sources */, E1BDF2E62951475300CC0294 /* VideoPlayerActionButton.swift in Sources */,
E11562962C818CB2001D5DE4 /* BindingBox.swift in Sources */,
E10231592BCF8AF8009D71FC /* ChannelLibraryView.swift in Sources */, E10231592BCF8AF8009D71FC /* ChannelLibraryView.swift in Sources */,
E1E6C44929AECEE70064123F /* AutoPlayActionButton.swift in Sources */, E1E6C44929AECEE70064123F /* AutoPlayActionButton.swift in Sources */,
E1C926152887565C002A7A66 /* EpisodeCard.swift in Sources */, E1C926152887565C002A7A66 /* EpisodeCard.swift in Sources */,
@ -4432,6 +4437,7 @@
6334175B287DDFB9000603CE /* QuickConnectAuthorizeView.swift in Sources */, 6334175B287DDFB9000603CE /* QuickConnectAuthorizeView.swift in Sources */,
E13F05F128BC9016003499D2 /* LibraryRow.swift in Sources */, E13F05F128BC9016003499D2 /* LibraryRow.swift in Sources */,
E168BD10289A4162001A6922 /* HomeView.swift in Sources */, E168BD10289A4162001A6922 /* HomeView.swift in Sources */,
E11562952C818CB2001D5DE4 /* BindingBox.swift in Sources */,
4E16FD532C01840C00110147 /* LetterPickerBar.swift in Sources */, 4E16FD532C01840C00110147 /* LetterPickerBar.swift in Sources */,
E1BE1CEA2BDB5AFE008176A9 /* UserGridButton.swift in Sources */, E1BE1CEA2BDB5AFE008176A9 /* UserGridButton.swift in Sources */,
E1401CB129386C9200E8B599 /* UIColor.swift in Sources */, E1401CB129386C9200E8B599 /* UIColor.swift in Sources */,

View File

@ -13,43 +13,44 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
@Environment(\.editMode) @Environment(\.editMode)
private var editMode private var editMode
@Binding @StateObject
private var selection: [Element] private var selection: BindingBox<[Element]>
@State
private var updateSelection: [Element]
private var disabledSelection: [Element] { private var disabledSelection: [Element] {
sources.subtracting(updateSelection) sources.subtracting(selection.value)
} }
private var label: (Element) -> any View private var label: (Element) -> any View
private let sources: [Element] private let sources: [Element]
private func move(from source: IndexSet, to destination: Int) { 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) { private func select(element: Element) {
if updateSelection.contains(element) { if selection.value.contains(element) {
updateSelection.removeAll(where: { $0 == element }) selection.value.removeAll(where: { $0 == element })
} else { } else {
updateSelection.append(element) selection.value.append(element)
} }
} }
private var isReordering: Bool {
editMode?.wrappedValue.isEditing ?? false
}
var body: some View { var body: some View {
List { List {
Section(L10n.enabled) { Section(L10n.enabled) {
if updateSelection.isEmpty { if selection.value.isEmpty {
L10n.none.text L10n.none.text
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
ForEach(updateSelection, id: \.self) { element in ForEach(selection.value, id: \.self) { element in
Button { Button {
if !(editMode?.wrappedValue.isEditing ?? true) { if !isReordering {
select(element: element) select(element: element)
} }
} label: { } label: {
@ -59,7 +60,7 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
Spacer() Spacer()
if !(editMode?.wrappedValue.isEditing ?? false) { if !isReordering {
Image(systemName: "minus.circle.fill") Image(systemName: "minus.circle.fill")
.foregroundColor(.red) .foregroundColor(.red)
} }
@ -79,7 +80,7 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
ForEach(disabledSelection, id: \.self) { element in ForEach(disabledSelection, id: \.self) { element in
Button { Button {
if !(editMode?.wrappedValue.isEditing ?? true) { if !isReordering {
select(element: element) select(element: element)
} }
} label: { } label: {
@ -89,7 +90,7 @@ struct OrderedSectionSelectorView<Element: Displayable & Hashable>: View {
Spacer() Spacer()
if !(editMode?.wrappedValue.isEditing ?? false) { if !isReordering {
Image(systemName: "plus.circle.fill") Image(systemName: "plus.circle.fill")
.foregroundColor(.green) .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 { .toolbar {
EditButton() EditButton()
} }
.onChange(of: updateSelection) { newValue in
selection = newValue
}
} }
} }
extension OrderedSectionSelectorView { extension OrderedSectionSelectorView {
init(selection: Binding<[Element]>, sources: [Element]) { init(selection: Binding<[Element]>, sources: [Element]) {
self._selection = selection self._selection = StateObject(wrappedValue: BindingBox(source: selection))
self._updateSelection = State(initialValue: selection.wrappedValue)
self.sources = sources self.sources = sources
self.label = { Text($0.displayTitle).foregroundColor(.primary) } self.label = { Text($0.displayTitle).foregroundColor(.primary) }
} }

View File

@ -6,26 +6,17 @@
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors // Copyright (c) 2024 Jellyfin & Jellyfin Contributors
// //
import Defaults
import SwiftUI import SwiftUI
import UIKit import UIKit
struct FontPickerView: View { struct FontPickerView: View {
@Binding @Binding
private var selection: String var selection: String
@State
private var updateSelection: String
init(selection: Binding<String>) {
self._selection = selection
self.updateSelection = selection.wrappedValue
}
var body: some View { var body: some View {
SelectorView( SelectorView(
selection: $updateSelection, selection: $selection,
sources: UIFont.familyNames sources: UIFont.familyNames
) )
.label { fontFamily in .label { fontFamily in
@ -33,9 +24,6 @@ struct FontPickerView: View {
.foregroundColor(.primary) .foregroundColor(.primary)
.font(.custom(fontFamily, size: 18)) .font(.custom(fontFamily, size: 18))
} }
.onChange(of: updateSelection) { newValue in
selection = newValue
}
.navigationTitle("Font") .navigationTitle("Font")
} }
} }