diff options
| author | Fuwn <[email protected]> | 2025-03-12 01:49:11 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-03-12 01:49:11 -0700 |
| commit | a30ee55976b5e80f48826e3e8e490761ce0a2410 (patch) | |
| tree | 2c3b27971bff80749a80ee48263b751ec852c747 /Sora/Views/Post | |
| parent | feat: Development commit (diff) | |
| download | sora-testing-a30ee55976b5e80f48826e3e8e490761ce0a2410.tar.xz sora-testing-a30ee55976b5e80f48826e3e8e490761ce0a2410.zip | |
feat: Development commit
Diffstat (limited to 'Sora/Views/Post')
| -rw-r--r-- | Sora/Views/Post/Details/PostDetailsView.swift | 2 | ||||
| -rw-r--r-- | Sora/Views/Post/Grid/PostGridView.swift | 131 | ||||
| -rw-r--r-- | Sora/Views/Post/Grid/Tab/PostGridTabButtonView.swift | 30 | ||||
| -rw-r--r-- | Sora/Views/Post/Grid/Tab/PostGridTabSwitcherContentView.swift | 22 | ||||
| -rw-r--r-- | Sora/Views/Post/Grid/Tab/PostGridTabSwitcherView.swift | 128 |
5 files changed, 254 insertions, 59 deletions
diff --git a/Sora/Views/Post/Details/PostDetailsView.swift b/Sora/Views/Post/Details/PostDetailsView.swift index e040bc3..3b6ef49 100644 --- a/Sora/Views/Post/Details/PostDetailsView.swift +++ b/Sora/Views/Post/Details/PostDetailsView.swift @@ -1,9 +1,9 @@ import SwiftUI struct PostDetailsView: View { - @EnvironmentObject var manager: BooruManager @EnvironmentObject var settings: SettingsManager let post: BooruPost + let manager: BooruManager @State private var loadingStage: BooruPostLoadingState = .loadingPreview private var imageURL: URL? { switch settings.detailViewQuality { diff --git a/Sora/Views/Post/Grid/PostGridView.swift b/Sora/Views/Post/Grid/PostGridView.swift index dc6546d..a03463b 100644 --- a/Sora/Views/Post/Grid/PostGridView.swift +++ b/Sora/Views/Post/Grid/PostGridView.swift @@ -3,9 +3,11 @@ import WaterfallGrid struct PostGridView: View { @EnvironmentObject var settings: SettingsManager - @EnvironmentObject var manager: BooruManager + @ObservedObject var manager: BooruManager @State private var isSearchHistoryPresented = false @Binding var selectedTab: Int + let isActive: Bool + @Binding var selectedPost: (post: BooruPost?, manager: BooruManager?) @Environment(\.isSearching) private var isSearching @@ -29,77 +31,84 @@ struct PostGridView: View { .gridStyle(columns: settings.thumbnailGridColumns) .padding(8) } - .searchable(text: $manager.searchText, prompt: "Tags") - .searchSuggestions { - if settings.searchSuggestionsMode != .disabled { - SearchSuggestionsView( - items: searchSuggestionsItems(), - searchText: $manager.searchText - ) + #if os(macOS) + .searchable(text: $manager.searchText, prompt: "Tags") + .searchSuggestions { + if settings.searchSuggestionsMode != .disabled { + SearchSuggestionsView( + items: searchSuggestionsItems(), + searchText: $manager.searchText + ) + } } - } - .onSubmit(of: .search) { - manager.performSearch(settings: settings) - } - .navigationDestination(for: BooruPost.self) { post in - PostDetailsView(post: post) - } - .onChange(of: manager.searchText) { _, _ in - if manager.searchText.isEmpty, !isSearching { - Task { manager.performSearch() } + .onSubmit(of: .search) { + manager.performSearch(settings: settings) } - } + .navigationDestination(for: BooruPost.self) { post in + PostDetailsView(post: post, manager: manager) + } + .onChange(of: manager.searchText) { _, _ in + if manager.searchText.isEmpty, !isSearching { + Task { manager.performSearch() } + } + } + #endif .toolbar { - #if os(macOS) - ToolbarItem { - Button(action: { - Task { - await manager.fetchPosts(page: 1, tags: manager.tags, replace: true) + if isActive { + #if os(macOS) + ToolbarItem { + Button(action: { + Task { + await manager.fetchPosts(page: 1, tags: manager.tags, replace: true) + } + }) { + Label("Refresh", systemImage: "arrow.clockwise") } - }) { - Label("Refresh", systemImage: "arrow.clockwise") + .disabled(manager.isLoading) } - .disabled(manager.isLoading) - } - #endif + #endif - #if os(macOS) - ToolbarItem { - Button(action: { Task { manager.loadNextPage() } }) { - Label("Manually Load Next Page", systemImage: "arrow.triangle.2.circlepath") + #if os(macOS) + ToolbarItem { + Button(action: { Task { manager.loadNextPage() } }) { + Label("Manually Load Next Page", systemImage: "arrow.triangle.2.circlepath") + } + .disabled(manager.isLoading) } - .disabled(manager.isLoading) - } - #else - ToolbarItem(placement: .bottomBar) { - Button(action: { Task { manager.loadNextPage() } }) { - Label("Manually Load Next Page", systemImage: "arrow.triangle.2.circlepath") + #else + ToolbarItem(placement: .bottomBar) { + Button(action: { Task { manager.loadNextPage() } }) { + Label("Manually Load Next Page", systemImage: "arrow.triangle.2.circlepath") + } + .disabled(manager.isLoading) } - .disabled(manager.isLoading) - } - ToolbarItem(placement: .bottomBar) { - Button(action: { Task { isSearchHistoryPresented.toggle() } }) { - Label("Search History", systemImage: "clock.arrow.circlepath") + ToolbarItem(placement: .bottomBar) { + Button(action: { Task { isSearchHistoryPresented.toggle() } }) { + Label("Search History", systemImage: "clock.arrow.circlepath") + } } - } - if manager.isLoading { - ToolbarItem { - ProgressView() + if manager.isLoading { + ToolbarItem { + ProgressView() + } } - } - #endif - - if !manager.tags.isEmpty { - #if os(macOS) - ToolbarItem { PostGridBookmarkButtonView() } - #else - ToolbarItem(placement: .bottomBar) { PostGridBookmarkButtonView() } #endif + + if !manager.tags.isEmpty { + #if os(macOS) + ToolbarItem { PostGridBookmarkButtonView() } + #else + ToolbarItem(placement: .bottomBar) { PostGridBookmarkButtonView() } + #endif + } } } .navigationTitle("Posts") + #if !os(macOS) + .navigationBarTitleDisplayMode(.inline) + #endif .refreshable { await manager.fetchPosts(page: 1, tags: manager.tags, replace: true) } @@ -116,13 +125,19 @@ struct PostGridView: View { private func waterfallGridContent(post: BooruPost) -> some View { Button { - manager.selectedPost = post + if isActive { + selectedPost.post = post + selectedPost.manager = manager + } } label: { PostGridThumbnailView(post: post, posts: filteredPosts) } .buttonStyle(PlainButtonStyle()) .contextMenu { - Button(action: { manager.selectedPost = post }) { + Button(action: { + selectedPost.post = post + selectedPost.manager = manager + }) { Label("Select Post", systemImage: "arrow.right.circle") } } diff --git a/Sora/Views/Post/Grid/Tab/PostGridTabButtonView.swift b/Sora/Views/Post/Grid/Tab/PostGridTabButtonView.swift new file mode 100644 index 0000000..9162ff0 --- /dev/null +++ b/Sora/Views/Post/Grid/Tab/PostGridTabButtonView.swift @@ -0,0 +1,30 @@ +import SwiftUI + +struct PostGridTabButtonView: View { + let title: String + let isSelected: Bool + let onSelect: () -> Void + let onClose: () -> Void + + var body: some View { + HStack { + Button(action: onClose) { + Image(systemName: "xmark.square.fill") + .foregroundColor(.secondary) + } + .buttonStyle(PlainButtonStyle()) + + Text(title) + .lineLimit(1) + } + .padding(.horizontal, 12) + .padding(.vertical, 8) + #if !os(macOS) + .background(isSelected ? Color(.systemGray5) : Color(.systemGray3)) + #endif + .opacity(isSelected ? 1 : 0.advanced(by: 0.3)) + .clipShape(RoundedRectangle(cornerRadius: 9)) + .onTapGesture(perform: onSelect) + .accessibilityAddTraits(.isButton) + } +} diff --git a/Sora/Views/Post/Grid/Tab/PostGridTabSwitcherContentView.swift b/Sora/Views/Post/Grid/Tab/PostGridTabSwitcherContentView.swift new file mode 100644 index 0000000..6a42460 --- /dev/null +++ b/Sora/Views/Post/Grid/Tab/PostGridTabSwitcherContentView.swift @@ -0,0 +1,22 @@ +import SwiftUI + +struct PostGridTabSwitcherContentView: View { + @Binding var selectedTabID: UUID? + @Binding var selectedPost: (post: BooruPost?, manager: BooruManager?) + @Binding var selectedTab: Int + @Binding var tabs: [PostGridTab] + + var body: some View { + ForEach(tabs) { tab in + PostGridView( + manager: tab.manager, + selectedTab: $selectedTab, + isActive: tab.id == selectedTabID, + selectedPost: $selectedPost + ) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .opacity(tab.id == selectedTabID ? 1 : 0) + .allowsHitTesting(tab.id == selectedTabID) + } + } +} diff --git a/Sora/Views/Post/Grid/Tab/PostGridTabSwitcherView.swift b/Sora/Views/Post/Grid/Tab/PostGridTabSwitcherView.swift new file mode 100644 index 0000000..4f21470 --- /dev/null +++ b/Sora/Views/Post/Grid/Tab/PostGridTabSwitcherView.swift @@ -0,0 +1,128 @@ +import SwiftUI + +struct PostGridTabSwitcherView: View { + @EnvironmentObject var settings: SettingsManager + @State private var tabs: [PostGridTab] = [] + @State private var selectedTabID: UUID? + @Binding private var selectedTab: Int + @State private var searchText: String = "" + @Binding private var selectedPost: (post: BooruPost?, manager: BooruManager?) + + init(selectedTab: Binding<Int>, selectedPost: Binding<(post: BooruPost?, manager: BooruManager?)>) + { + let initialTab = PostGridTab(manager: BooruManager()) + + _tabs = State(initialValue: [initialTab]) + _selectedTabID = State(initialValue: initialTab.id) + _selectedTab = selectedTab + _selectedPost = selectedPost + + Task { await initialTab.manager.fetchPosts(page: 1, tags: [], replace: true) } + } + + private var activeManager: BooruManager? { + if let selectedID = selectedTabID, + let activeTab = tabs.first(where: { $0.id == selectedID }) + { + return activeTab.manager + } + + return nil + } + + var body: some View { + VStack(spacing: 0) { + #if !os(macOS) + ScrollView(.horizontal, showsIndicators: false) { + HStack { + ForEach(tabs) { tab in + PostGridTabButtonView( + title: tab.manager.searchText.isEmpty ? "New Tab" : tab.manager.searchText, + isSelected: tab.id == selectedTabID, + onSelect: { selectedTabID = tab.id }, + onClose: { + if tabs.count > 1 { + tabs.removeAll { $0.id == tab.id } + + if selectedTabID == tab.id { + selectedTabID = tabs.first!.id + } + } + } + ) + } + + Button( + action: { + let newTab = PostGridTab(manager: BooruManager()) + + tabs.append(newTab) + + selectedTabID = newTab.id + + Task { await newTab.manager.fetchPosts(page: 1, tags: [], replace: true) } + } + ) { + Image(systemName: "plus") + } + } + .padding(.horizontal) + } + .background(Color(.systemGray6)) + + HStack { + TextField( + "Tags", + text: $searchText + ) { + if let manager = activeManager { + manager.searchText = searchText + + Task { manager.performSearch(settings: settings) } + } + } + .textFieldStyle(PlainTextFieldStyle()) + .padding(.vertical, 8) + .padding(.horizontal, 12) + .background(Color(.systemGray5)) + .clipShape(RoundedRectangle(cornerRadius: 9)) + + if !searchText.isEmpty { + Button(action: { + searchText = "" + + if let manager = activeManager { + manager.searchText = "" + + Task { manager.performSearch(settings: settings) } + } + }) { + Image(systemName: "xmark.circle.fill") + .foregroundColor(.secondary) + } + .buttonStyle(PlainButtonStyle()) + } + } + .padding(.horizontal) + .padding(.vertical, 12) + .background(Color(.systemGray6)) + #endif + + ZStack { + PostGridTabSwitcherContentView( + selectedTabID: $selectedTabID, + selectedPost: $selectedPost, + selectedTab: $selectedTab, + tabs: $tabs + ) + } + } + .onChange(of: selectedTabID) { _, newValue in + if let selectedID = newValue, + let activeTab = tabs.first(where: { $0.id == selectedID }) + { + searchText = activeTab.manager.searchText + } + } + } +} |