summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Localizable.xcstrings12
-rw-r--r--Sora/Data/GenericItem.swift11
-rw-r--r--Sora/Views/BookmarksView.swift90
-rw-r--r--Sora/Views/Generic/GenericItemView.swift (renamed from Sora/Views/GenericItemView.swift)0
-rw-r--r--Sora/Views/Generic/GenericListView.swift105
-rw-r--r--Sora/Views/Post/Grid/PostGridSearchHistoryView.swift97
6 files changed, 148 insertions, 167 deletions
diff --git a/Localizable.xcstrings b/Localizable.xcstrings
index 0be1197..f263594 100644
--- a/Localizable.xcstrings
+++ b/Localizable.xcstrings
@@ -119,16 +119,10 @@
}
}
},
- "No bookmarks match your search" : {
-
- },
"No History" : {
},
- "No matching %@ found" : {
-
- },
- "No matching history found" : {
+ "No matching items found" : {
},
"On %@ from %@" : {
@@ -199,10 +193,10 @@
"Remove" : {
},
- "Remove All Bookmarks" : {
+ "Remove All" : {
},
- "Remove All Search History" : {
+ "Remove All Bookmarks" : {
},
"Remove All Searches" : {
diff --git a/Sora/Data/GenericItem.swift b/Sora/Data/GenericItem.swift
new file mode 100644
index 0000000..af15e55
--- /dev/null
+++ b/Sora/Data/GenericItem.swift
@@ -0,0 +1,11 @@
+import Foundation
+
+protocol GenericItem {
+ var id: UUID { get }
+ var tags: [String] { get }
+ var date: Date { get }
+ var provider: BooruProvider { get }
+}
+
+extension SettingsBookmark: GenericItem {}
+extension BooruSearchQuery: GenericItem {}
diff --git a/Sora/Views/BookmarksView.swift b/Sora/Views/BookmarksView.swift
index ccef3f1..fa7734e 100644
--- a/Sora/Views/BookmarksView.swift
+++ b/Sora/Views/BookmarksView.swift
@@ -2,85 +2,23 @@ import SwiftUI
struct BookmarksView: View {
@EnvironmentObject var settings: SettingsManager
- @EnvironmentObject var manager: BooruManager
@Binding var selectedTab: Int
- @State private var bookmarksSearchText: String = ""
- @State private var isShowingRemoveAllConfirmation = false
-
- var filteredBookmarks: [SettingsBookmark] {
- guard !bookmarksSearchText.isEmpty else {
- return settings.bookmarks
- }
-
- return settings.bookmarks
- .filter { bookmark in
- bookmark.tags.joined(separator: " ").lowercased().contains(bookmarksSearchText.lowercased())
- }
- }
var body: some View {
- NavigationStack {
- VStack {
- if settings.bookmarks.isEmpty {
- ContentUnavailableView(
- "No Bookmarks",
- systemImage: "bookmark",
- description: Text("Tap the bookmark button on a search page to add a bookmark.")
- )
- } else {
- List {
- if filteredBookmarks.isEmpty, !bookmarksSearchText.isEmpty {
- Text("No bookmarks match your search")
- }
-
- ForEach(
- filteredBookmarks.sorted { $0.date > $1.date },
- id: \.self
- ) { bookmark in
- Button(action: {
- let previousProvider = settings.preferredBooru
-
- settings.preferredBooru = bookmark.provider
- manager.searchText = bookmark.tags.joined(separator: " ")
- selectedTab = 0
-
- if previousProvider == settings.preferredBooru {
- manager.performSearch(settings: settings)
- }
- }) {
- GenericItemView(
- item: bookmark,
- removeAction: settings.removeBookmark
- )
- }
- #if os(macOS)
- .buttonStyle(.plain)
- #endif
- }
- .onDelete(perform: settings.removeBookmark)
- }
- }
- }
- }
- .navigationTitle("Bookmarks")
- .searchable(text: $bookmarksSearchText)
- .toolbar {
- ToolbarItem {
- Button(action: {
- isShowingRemoveAllConfirmation = true
- }) {
- Label("Remove All Bookmarks", systemImage: "trash")
- }
- }
- }
- .confirmationDialog(
- "Are you sure you want to remove all bookmarks? This action cannot be undone.",
- isPresented: $isShowingRemoveAllConfirmation
- ) {
- Button("Remove All Bookmarks") {
- settings.bookmarks.removeAll()
- }
- }
+ GenericListView(
+ selectedTab: $selectedTab,
+ isPresented: .constant(false),
+ title: "Bookmarks",
+ emptyMessage: "No Bookmarks",
+ emptyIcon: "bookmark",
+ emptyDescription: "Tap the bookmark button on a search page to add a bookmark.",
+ removeAllMessage:
+ "Are you sure you want to remove all bookmarks? This action cannot be undone.",
+ removeAllButtonText: "Remove All Bookmarks",
+ items: settings.bookmarks,
+ removeAction: settings.removeBookmark,
+ removeActionUUID: settings.removeBookmark
+ ) { settings.bookmarks.removeAll() }
}
}
diff --git a/Sora/Views/GenericItemView.swift b/Sora/Views/Generic/GenericItemView.swift
index ec8deaa..ec8deaa 100644
--- a/Sora/Views/GenericItemView.swift
+++ b/Sora/Views/Generic/GenericItemView.swift
diff --git a/Sora/Views/Generic/GenericListView.swift b/Sora/Views/Generic/GenericListView.swift
new file mode 100644
index 0000000..0642c6e
--- /dev/null
+++ b/Sora/Views/Generic/GenericListView.swift
@@ -0,0 +1,105 @@
+import SwiftUI
+
+struct GenericListView<T: Identifiable & Hashable & GenericItem & ItemViewModel>: View {
+ @EnvironmentObject private var settings: SettingsManager
+ @EnvironmentObject private var manager: BooruManager
+ @Binding var selectedTab: Int
+ @State private var searchText: String = ""
+ @State private var isShowingRemoveAllConfirmation = false
+ @Binding var isPresented: Bool
+
+ let title: String
+ let emptyMessage: String
+ let emptyIcon: String
+ let emptyDescription: String
+ let removeAllMessage: String
+ let removeAllButtonText: String
+ let items: [T]
+ let removeAction: (IndexSet) -> Void
+ let removeActionUUID: (UUID) -> Void
+ let removeAllAction: () -> Void
+
+ var filteredItems: [T] {
+ guard !searchText.isEmpty else {
+ return items
+ }
+
+ return items.filter { item in
+ item.tags
+ .joined(separator: " ")
+ .lowercased()
+ .contains(searchText.lowercased())
+ }
+ }
+
+ var body: some View {
+ NavigationStack {
+ VStack {
+ if items.isEmpty {
+ ContentUnavailableView(
+ emptyMessage,
+ systemImage: emptyIcon,
+ description: Text(emptyDescription)
+ )
+ } else {
+ List {
+ if filteredItems.isEmpty, !searchText.isEmpty {
+ Text("No matching items found")
+ }
+
+ ForEach(
+ filteredItems.sorted { $0.date > $1.date },
+ id: \.id
+ ) { item in
+ itemButtonContent(item: item)
+ }
+ .onDelete(perform: removeAction)
+ }
+ }
+ }
+ }
+ .navigationTitle(title)
+ .searchable(text: $searchText)
+ .toolbar {
+ ToolbarItem {
+ Button(action: {
+ isShowingRemoveAllConfirmation = true
+ }) {
+ Label("Remove All", systemImage: "trash")
+ }
+ }
+ }
+ .confirmationDialog(
+ removeAllMessage,
+ isPresented: $isShowingRemoveAllConfirmation
+ ) {
+ Button(removeAllButtonText) {
+ removeAllAction()
+ }
+ }
+ }
+
+ func itemButtonContent(item: T) -> some View {
+ Button(action: {
+ let previousProvider = settings.preferredBooru
+
+ settings.preferredBooru = item.provider
+ manager.searchText = item.tags.joined(separator: " ")
+ selectedTab = 0
+
+ isPresented.toggle()
+
+ if previousProvider == settings.preferredBooru {
+ manager.performSearch(settings: settings)
+ }
+ }) {
+ GenericItemView(
+ item: item,
+ removeAction: removeActionUUID
+ )
+ }
+ #if os(macOS)
+ .buttonStyle(.plain)
+ #endif
+ }
+}
diff --git a/Sora/Views/Post/Grid/PostGridSearchHistoryView.swift b/Sora/Views/Post/Grid/PostGridSearchHistoryView.swift
index 30f7808..ae33c53 100644
--- a/Sora/Views/Post/Grid/PostGridSearchHistoryView.swift
+++ b/Sora/Views/Post/Grid/PostGridSearchHistoryView.swift
@@ -1,92 +1,25 @@
import SwiftUI
struct PostGridSearchHistoryView: View {
- @EnvironmentObject private var manager: BooruManager
- @EnvironmentObject private var settings: SettingsManager
- @State private var searchText: String = ""
+ @EnvironmentObject var settings: SettingsManager
@Binding var selectedTab: Int
@Binding var isPresented: Bool
- @State private var isShowingRemoveAllConfirmation = false
-
- var filteredHistory: [BooruSearchQuery] {
- guard !searchText.isEmpty else {
- return settings.searchHistory
- }
-
- return settings.searchHistory
- .filter { query in
- query.tags
- .joined(separator: " ")
- .lowercased()
- .contains(searchText.lowercased())
- }
- }
var body: some View {
- NavigationStack {
- VStack {
- if settings.searchHistory.isEmpty {
- ContentUnavailableView(
- "No History",
- systemImage: "magnifyingglass",
- description: Text("Recent searches will appear here.")
- )
- } else {
- List {
- if filteredHistory.isEmpty, !searchText.isEmpty {
- Text("No matching history found")
- }
-
- ForEach(
- filteredHistory.sorted { $0.date > $1.date },
- id: \.id
- ) { query in
- Button(action: {
- let previousProvider = settings.preferredBooru
-
- settings.preferredBooru = query.provider
- manager.searchText = query.tags.joined(separator: " ")
- selectedTab = 0
-
- isPresented.toggle()
-
- if previousProvider == settings.preferredBooru {
- manager.performSearch()
- }
- }) {
- GenericItemView(
- item: query,
- removeAction: settings.removeSearchHistoryEntry
- )
- }
- #if os(macOS)
- .buttonStyle(.plain)
- #endif
- }
- .onDelete(perform: settings.removeSearchHistoryEntry)
- }
- }
- }
- }
- .navigationTitle("Search History")
- .searchable(text: $searchText)
- .toolbar {
- ToolbarItem {
- Button(action: {
- isShowingRemoveAllConfirmation = true
- }) {
- Label("Remove All Search History", systemImage: "trash")
- }
- }
- }
- .confirmationDialog(
- "Are you sure you want to remove all searches? This action cannot be undone.",
- isPresented: $isShowingRemoveAllConfirmation
- ) {
- Button("Remove All Searches") {
- settings.bookmarks.removeAll()
- }
- }
+ GenericListView(
+ selectedTab: $selectedTab,
+ isPresented: $isPresented,
+ title: "Search History",
+ emptyMessage: "No History",
+ emptyIcon: "magnifyingglass",
+ emptyDescription: "Recent searches will appear here.",
+ removeAllMessage:
+ "Are you sure you want to remove all searches? This action cannot be undone.",
+ removeAllButtonText: "Remove All Searches",
+ items: settings.searchHistory,
+ removeAction: settings.removeSearchHistoryEntry,
+ removeActionUUID: settings.removeSearchHistoryEntry
+ ) { settings.searchHistory.removeAll() }
}
}