diff options
| author | Fuwn <[email protected]> | 2025-07-05 06:36:16 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-07-05 06:36:16 -0700 |
| commit | fcf9ea89cf9190d4deb1e8e7223bf32a1f03b44f (patch) | |
| tree | caf89e739b6ce730eb2ff0f0ef060c00cb4f15e9 | |
| parent | feat: Development commit (diff) | |
| download | sora-testing-fcf9ea89cf9190d4deb1e8e7223bf32a1f03b44f.tar.xz sora-testing-fcf9ea89cf9190d4deb1e8e7223bf32a1f03b44f.zip | |
feat: Development commit
| -rw-r--r-- | Sora/Data/CollectionPickerOption.swift | 40 | ||||
| -rw-r--r-- | Sora/Data/Settings/SettingsFolder.swift | 8 | ||||
| -rw-r--r-- | Sora/Extensions/Array+RemovingDuplicates.swift | 9 | ||||
| -rw-r--r-- | Sora/Views/Generic/GenericListView.swift | 91 |
4 files changed, 129 insertions, 19 deletions
diff --git a/Sora/Data/CollectionPickerOption.swift b/Sora/Data/CollectionPickerOption.swift new file mode 100644 index 0000000..442ebdc --- /dev/null +++ b/Sora/Data/CollectionPickerOption.swift @@ -0,0 +1,40 @@ +import Foundation + +enum CollectionPickerOption: Identifiable, Hashable { + case all + case folder(UUID) + case topLevel(String) + case uncategorized + + var id: String { + switch self { + case .all: + return "all" + + case .folder(let id): + return id.uuidString + + case .topLevel(let name): + return name + + case .uncategorized: + return "uncategorized" + } + } + + var name: (_ settings: SettingsManager) -> String { + switch self { + case .all: + return { _ in "All" } + + case .folder(let id): + return { settings in settings.folderName(forID: id) ?? "Unknown Folder" } + + case .topLevel(let name): + return { _ in name } + + case .uncategorized: + return { _ in "Uncategorised" } + } + } +} diff --git a/Sora/Data/Settings/SettingsFolder.swift b/Sora/Data/Settings/SettingsFolder.swift index 84543c1..2464770 100644 --- a/Sora/Data/Settings/SettingsFolder.swift +++ b/Sora/Data/Settings/SettingsFolder.swift @@ -5,6 +5,14 @@ struct SettingsFolder: Codable, Identifiable, Hashable { var name: String let createdAt: Date + var topLevelName: String? { + if name.contains("|") { + return String(name.split(separator: "|")[0]).trimmingCharacters(in: .whitespacesAndNewlines) + } + + return nil + } + init(name: String, id: UUID = UUID()) { createdAt = Date() self.id = id diff --git a/Sora/Extensions/Array+RemovingDuplicates.swift b/Sora/Extensions/Array+RemovingDuplicates.swift new file mode 100644 index 0000000..15fc697 --- /dev/null +++ b/Sora/Extensions/Array+RemovingDuplicates.swift @@ -0,0 +1,9 @@ +import Foundation + +extension Array where Element: Hashable { + func removingDuplicates() -> [Element] { + var seen = Set<Element>() + + return filter { seen.insert($0).inserted } + } +} diff --git a/Sora/Views/Generic/GenericListView.swift b/Sora/Views/Generic/GenericListView.swift index b0ee422..0c8da79 100644 --- a/Sora/Views/Generic/GenericListView.swift +++ b/Sora/Views/Generic/GenericListView.swift @@ -1,6 +1,9 @@ +// swiftlint:disable file_length + import SwiftUI -struct GenericListView<T: Identifiable & Hashable & GenericItem>: View { // swiftlint:disable:this type_body_length +// swiftlint:disable:next type_body_length +struct GenericListView<T: Identifiable & Hashable & GenericItem>: View { @EnvironmentObject private var settings: SettingsManager @EnvironmentObject private var manager: BooruManager @Binding var selectedTab: Int @@ -11,7 +14,7 @@ struct GenericListView<T: Identifiable & Hashable & GenericItem>: View { // swi @State private var newCollectionName = "" @State private var itemPendingCollectionAssignment: UUID? @State private var isCollectionErrorAlertPresented = false - @State private var selectedFolder: UUID? + @State private var selectedCollectionOption: CollectionPickerOption = .all @State private var sort: SettingsBookmarkSort = .dateAdded @State private var isCollectionPickerPresented = false @State private var isProviderPickerPresented = false @@ -31,15 +34,25 @@ struct GenericListView<T: Identifiable & Hashable & GenericItem>: View { // swi var filteredItems: [T] { items.filter { item in let matchesFolder: Bool = { - if selectedFolder == nil { + switch selectedCollectionOption { + case .all: return true - } - if selectedFolder == UUID.nilUUID() { + case .uncategorized: return item.folder == nil - } - return item.folder == selectedFolder + case .topLevel(let topLevelName): + if let folderId = item.folder, + let folder = settings.folders.first(where: { $0.id == folderId }), + let itemTopLevelName = folder.topLevelName + { + return itemTopLevelName == topLevelName + } + return false + + case .folder(let folderId): + return item.folder == folderId + } }() let matchesSearch = searchText.isEmpty @@ -76,7 +89,7 @@ struct GenericListView<T: Identifiable & Hashable & GenericItem>: View { // swi @ViewBuilder private var listContent: some View { List { - if filteredItems.isEmpty && (!searchText.isEmpty || !(selectedFolder == nil)) { + if filteredItems.isEmpty && (!searchText.isEmpty || !(selectedCollectionOption == .all)) { Text("No matching items found") .foregroundColor(.secondary) } @@ -116,16 +129,37 @@ struct GenericListView<T: Identifiable & Hashable & GenericItem>: View { // swi .padding() .pickerStyle(.menu) - Picker("Collection", selection: $selectedFolder) { - Text("All").tag(nil as UUID?) + Picker("Collection", selection: $selectedCollectionOption) { + Text(CollectionPickerOption.all.name(settings)).tag(CollectionPickerOption.all) if items.contains(where: { $0.folder == nil }) { - Text("Uncategorised").tag(UUID.nilUUID() as UUID?) + Text(CollectionPickerOption.uncategorized.name(settings)).tag( + CollectionPickerOption.uncategorized + ) + } + + ForEach( + settings.folders + .filter { $0.topLevelName != nil } + .map { $0.topLevelName! } + .removingDuplicates(), id: \.self + ) { topLevelName in + Text(CollectionPickerOption.topLevel(topLevelName).name(settings)).tag( + CollectionPickerOption.topLevel(topLevelName) + ) } - ForEach(settings.folders, id: \.id) { folder in + ForEach( + settings.folders.filter { folder in + folder.topLevelName == nil + || (selectedCollectionOption == .topLevel(folder.topLevelName!) + && folder.topLevelName != nil) + }, id: \.id + ) { folder in if isCollectionPopulated(folder.id) { - Text(folder.name).tag(folder.id) + Text(CollectionPickerOption.folder(folder.id).name(settings)).tag( + CollectionPickerOption.folder(folder.id) + ) } } } @@ -199,16 +233,35 @@ struct GenericListView<T: Identifiable & Hashable & GenericItem>: View { // swi } Menu { - Picker("Collection", selection: $selectedFolder) { - Text("All").tag(nil as UUID?) + Picker("Collection", selection: $selectedCollectionOption) { + Text(CollectionPickerOption.all.name(settings)).tag(CollectionPickerOption.all) if items.contains(where: { $0.folder == nil }) { - Text("Uncategorised").tag(UUID.nilUUID()) + Text(CollectionPickerOption.uncategorized.name(settings)).tag( + CollectionPickerOption.uncategorized + ) + } + + ForEach( + settings.folders + .filter { $0.topLevelName != nil } + .map { $0.topLevelName! } + .removingDuplicates(), id: \.self + ) { topLevelName in + Text(CollectionPickerOption.topLevel(topLevelName).name(settings)).tag( + CollectionPickerOption.topLevel(topLevelName) + ) } - ForEach(settings.folders, id: \.id) { folder in - Text(folder.name) - .tag(folder.id) + ForEach( + settings.folders.filter { folder in + folder.topLevelName == nil + || (selectedCollectionOption == .topLevel(folder.topLevelName!) + && folder.topLevelName != nil) + }, id: \.id + ) { folder in + Text(CollectionPickerOption.folder(folder.id).name(settings)) + .tag(CollectionPickerOption.folder(folder.id)) .selectionDisabled(!isCollectionPopulated(folder.id)) } } |