diff options
| -rw-r--r-- | Sora/Views/ContentView.swift | 10 | ||||
| -rw-r--r-- | Sora/Views/Post/Details/Carousel/PostDetailsCarouselItemView.swift | 50 | ||||
| -rw-r--r-- | Sora/Views/Post/Details/Carousel/PostDetailsCarouselView.swift | 15 | ||||
| -rw-r--r-- | Sora/Views/Post/Details/PostDetailsImageView.swift | 44 | ||||
| -rw-r--r-- | Sora/Views/Post/Details/PostDetailsTagsView.swift | 53 | ||||
| -rw-r--r-- | Sora/Views/Post/Details/PostDetailsView.swift | 130 | ||||
| -rw-r--r-- | Sora/Views/Post/Grid/PostGridView.swift | 2 |
7 files changed, 154 insertions, 150 deletions
diff --git a/Sora/Views/ContentView.swift b/Sora/Views/ContentView.swift index 63f128a..85294ee 100644 --- a/Sora/Views/ContentView.swift +++ b/Sora/Views/ContentView.swift @@ -36,12 +36,8 @@ struct ContentView: View { PostGridTabSwitcherView(selectedTab: $selectedTab, selectedPost: $selectedPost) } } detail: { - if let post = selectedPost.post, let manager = selectedPost.manager { - PostDetailsView( - post: post, - manager: manager, - selectedPost: $selectedPost - ) + if selectedPost.post != nil && selectedPost.manager != nil { + PostDetailsView(selectedPost: $selectedPost) } else { Text("Select a Post") .foregroundColor(.secondary) @@ -60,7 +56,7 @@ struct ContentView: View { ) ) { if let post = selectedPost.post, let manager = selectedPost.manager { - PostDetailsView(post: post, manager: manager, selectedPost: $selectedPost) + PostDetailsView(selectedPost: $selectedPost) } } } diff --git a/Sora/Views/Post/Details/Carousel/PostDetailsCarouselItemView.swift b/Sora/Views/Post/Details/Carousel/PostDetailsCarouselItemView.swift index 9d2d95e..fc39ca3 100644 --- a/Sora/Views/Post/Details/Carousel/PostDetailsCarouselItemView.swift +++ b/Sora/Views/Post/Details/Carousel/PostDetailsCarouselItemView.swift @@ -1,8 +1,6 @@ import SwiftUI struct PostDetailsCarouselItemView: View { - var post: BooruPost - var manager: BooruManager var index: Int @Binding var loadingStage: BooruPostLoadingState var imageURL: (BooruPost) -> URL? @@ -15,33 +13,35 @@ struct PostDetailsCarouselItemView: View { return Color.gray #endif } + @Binding var selectedPost: (post: BooruPost?, manager: BooruManager?) var body: some View { - ZStack { - PostDetailsImageView( - url: post.previewURL, - loadingStage: $loadingStage, - manager: manager - ) { - GeometryReader { geometry in - ProgressView() - .frame(width: geometry.size.width, height: geometry.size.height) - .position(x: geometry.size.width / 2, y: geometry.size.height / 2) - .padding() + if let post = selectedPost.post { + ZStack { + PostDetailsImageView( + url: post.previewURL, + loadingStage: $loadingStage, + selectedPost: $selectedPost + ) { + GeometryReader { geometry in + ProgressView() + .frame(width: geometry.size.width, height: geometry.size.height) + .position(x: geometry.size.width / 2, y: geometry.size.height / 2) + .padding() + } } + + PostDetailsImageView( + url: imageURL(post), + loadingStage: $loadingStage, + selectedPost: $selectedPost, + finalLoadingState: .loaded + ) + .background( + loadingStage == .loaded ? systemBackgroundColor : Color.clear + ) } - - PostDetailsImageView( - url: imageURL(post), - loadingStage: $loadingStage, - manager: manager, - finalLoadingState: .loaded, - post: post - ) - .background( - loadingStage == .loaded ? systemBackgroundColor : Color.clear - ) + .tag(index) } - .tag(index) } } diff --git a/Sora/Views/Post/Details/Carousel/PostDetailsCarouselView.swift b/Sora/Views/Post/Details/Carousel/PostDetailsCarouselView.swift index 88714c3..3ead408 100644 --- a/Sora/Views/Post/Details/Carousel/PostDetailsCarouselView.swift +++ b/Sora/Views/Post/Details/Carousel/PostDetailsCarouselView.swift @@ -1,24 +1,24 @@ import SwiftUI struct PostDetailsCarouselView: View { - @ObservedObject var manager: BooruManager @EnvironmentObject var settings: SettingsManager let posts: [BooruPost] let focusedPost: BooruPost? @Binding var loadingStage: BooruPostLoadingState @State private var currentIndex: Int private let cacheManager = ImageCacheManager.shared + @Binding var selectedPost: (post: BooruPost?, manager: BooruManager?) init( posts: [BooruPost], loadingStage: Binding<BooruPostLoadingState>, focusedPost: BooruPost? = nil, - manager: ObservedObject<BooruManager> + selectedPost: Binding<(post: BooruPost?, manager: BooruManager?)> ) { self.posts = posts self.focusedPost = focusedPost _loadingStage = loadingStage - _manager = manager + _selectedPost = selectedPost if let focused = focusedPost, let index = posts.firstIndex(where: { $0.id == focused.id }) @@ -46,18 +46,19 @@ struct PostDetailsCarouselView: View { TabView(selection: $currentIndex) { ForEach(Array(posts.enumerated()), id: \.offset) { index, post in PostDetailsCarouselItemView( - post: post, - manager: manager, index: index, loadingStage: $loadingStage, - imageURL: imageURL + imageURL: imageURL, + selectedPost: $selectedPost ) } } .onChange(of: currentIndex) { + guard let manager = selectedPost.manager else { return } + if currentIndex == posts.count - 1 { manager.loadNextPage() } - manager.selectedPost = posts[currentIndex] + selectedPost.post = posts[currentIndex] preloadNearbyImages() } diff --git a/Sora/Views/Post/Details/PostDetailsImageView.swift b/Sora/Views/Post/Details/PostDetailsImageView.swift index 7d818f2..334dcc8 100644 --- a/Sora/Views/Post/Details/PostDetailsImageView.swift +++ b/Sora/Views/Post/Details/PostDetailsImageView.swift @@ -4,16 +4,15 @@ import UserNotifications struct PostDetailsImageView<Placeholder: View>: View { @EnvironmentObject var settings: SettingsManager - @ObservedObject var manager: BooruManager var url: URL? @Binding var loadingState: BooruPostLoadingState var finalLoadingState: BooruPostLoadingState let placeholder: () -> Placeholder - let post: BooruPost? @State private var currentScale: CGFloat = 1.0 @State private var finalScale: CGFloat = 1.0 @State private var currentOffset: CGSize = .zero @State private var finalOffset: CGSize = .zero + @Binding private var selectedPost: (post: BooruPost?, manager: BooruManager?) #if os(iOS) var keyWindow: UIWindow? { @@ -74,12 +73,12 @@ struct PostDetailsImageView<Placeholder: View>: View { #endif Button { - openURL(postURL(for: post?.id ?? "")) + openURL(postURL(for: selectedPost.post?.id ?? "")) } label: { Label("Open Post in Safari", systemImage: "safari") } - if let source = post?.source { + if let source = selectedPost.post?.source { Button { openURL(URL(string: source)!) } label: { @@ -119,20 +118,20 @@ struct PostDetailsImageView<Placeholder: View>: View { } func movePostCursor(by direction: Int) { - guard let selectedPost = manager.selectedPost, - let index = manager.postIndexMap[selectedPost.id], + guard let post = selectedPost.post, + let manager = selectedPost.manager, + let index = manager.postIndexMap[post.id], (0..<manager.posts.count).contains(index + direction) else { return } - manager.selectedPost = manager.posts[index + direction] + selectedPost.post = manager.posts[index + direction] } init( url: URL?, loadingStage: Binding<BooruPostLoadingState>, - manager: BooruManager, + selectedPost: Binding<(post: BooruPost?, manager: BooruManager?)>, finalLoadingState: BooruPostLoadingState = .loadingFile, - post: BooruPost? = nil, @ViewBuilder placeholder: @escaping () -> Placeholder = { GeometryReader { _ in // ProgressView() @@ -146,31 +145,36 @@ struct PostDetailsImageView<Placeholder: View>: View { _loadingState = loadingStage self.finalLoadingState = finalLoadingState self.placeholder = placeholder - self.post = post - self.manager = manager + _selectedPost = selectedPost } private func postURL(for id: String) -> URL { - switch manager.flavor { - case .moebooru: - return URL(string: "https://\(manager.domain)/post/show/\(id)")! + if let manager = selectedPost.manager { + switch manager.flavor { + case .moebooru: + return URL(string: "https://\(manager.domain)/post/show/\(id)")! - case .gelbooru: - return URL(string: "https://\(manager.domain)/index.php?page=post&s=view&id=\(id)")! + case .gelbooru: + return URL(string: "https://\(manager.domain)/index.php?page=post&s=view&id=\(id)")! - case .danbooru: - return URL(string: "https://\(manager.domain)/posts/\(id)")! + case .danbooru: + return URL(string: "https://\(manager.domain)/posts/\(id)")! + } } + + return URL(string: "#")! } #if os(macOS) private func saveImageToPicturesFolder() { - guard let url = self.url else { return } + guard let url = self.url, let manager = selectedPost.manager else { + return + } let provider = manager.provider URLSession.shared.dataTask(with: url) { data, _, _ in - guard let data, let post else { return } + guard let data, let post = selectedPost.post else { return } let picturesURL = FileManager.default.homeDirectoryForCurrentUser .appendingPathComponent("Pictures/Sora/\(provider.rawValue)") diff --git a/Sora/Views/Post/Details/PostDetailsTagsView.swift b/Sora/Views/Post/Details/PostDetailsTagsView.swift index 107c9f3..cecb63d 100644 --- a/Sora/Views/Post/Details/PostDetailsTagsView.swift +++ b/Sora/Views/Post/Details/PostDetailsTagsView.swift @@ -1,50 +1,53 @@ import SwiftUI struct PostDetailsTagsView: View { - @ObservedObject var manager: BooruManager @EnvironmentObject var settings: SettingsManager @Binding var isPresented: Bool var tags: [String] @Binding var selectedPost: (post: BooruPost?, manager: BooruManager?) var body: some View { - List { - ForEach(tags, id: \.self) { tag in - Button(action: { - manager.searchText = tag - selectedPost = (nil, nil) - - search() - }) { - Text(tag) - } - .contextMenu { + if let manager = selectedPost.manager { + List { + ForEach(tags, id: \.self) { tag in Button(action: { - manager.searchText += " \(tag)" + manager.searchText = tag selectedPost = (nil, nil) search() }) { - Label("Add to Search", systemImage: "plus") + Text(tag) } + .contextMenu { + Button(action: { + manager.searchText += " \(tag)" + selectedPost = (nil, nil) - Button(action: { - settings.addBookmark(provider: settings.preferredBooru, tags: [tag]) - }) { - Label("Bookmark Tag", systemImage: "bookmark") + search() + }) { + Label("Add to Search", systemImage: "plus") + } + + Button(action: { + settings.addBookmark(provider: settings.preferredBooru, tags: [tag]) + }) { + Label("Bookmark Tag", systemImage: "bookmark") + } } } - } - #if os(macOS) +#if os(macOS) .buttonStyle(.plain) - #endif +#endif + } } } private func search() { - manager.selectedPost = nil - - manager.performSearch(settings: settings) - isPresented.toggle() + if let manager = selectedPost.manager { + manager.selectedPost = nil + + manager.performSearch(settings: settings) + isPresented.toggle() + } } } diff --git a/Sora/Views/Post/Details/PostDetailsView.swift b/Sora/Views/Post/Details/PostDetailsView.swift index ba81b65..a7693a5 100644 --- a/Sora/Views/Post/Details/PostDetailsView.swift +++ b/Sora/Views/Post/Details/PostDetailsView.swift @@ -2,116 +2,116 @@ import SwiftUI struct PostDetailsView: View { @EnvironmentObject var settings: SettingsManager - let post: BooruPost - @ObservedObject var manager: BooruManager @State private var loadingStage: BooruPostLoadingState = .loadingPreview private var imageURL: URL? { + guard let post = selectedPost.post else { return nil } + switch settings.detailViewQuality { case .preview: - post.previewURL + return post.previewURL case .sample: - post.sampleURL + return post.sampleURL case .original: - post.fileURL + return post.fileURL } } @State private var isTagsSheetPresented = false @Binding var selectedPost: (post: BooruPost?, manager: BooruManager?) var filteredPosts: [BooruPost] { - manager.posts + selectedPost.manager!.posts .filter { settings.displayRatings.contains($0.rating) } } var body: some View { - VStack(spacing: 0) { - #if os(macOS) + if let post = selectedPost.post { + VStack(spacing: 0) { +#if os(macOS) PostDetailsImageView( url: imageURL, loadingStage: $loadingStage, - manager: manager, - finalLoadingState: .loaded, - post: post + selectedPost: $selectedPost, + finalLoadingState: .loaded ) { PostDetailsImageView( url: post.previewURL, loadingStage: $loadingStage, - manager: manager + selectedPost: $selectedPost ) .id(post.previewURL) } .id(imageURL) - #else +#else PostDetailsCarouselView( posts: filteredPosts, loadingStage: $loadingStage, focusedPost: post, - manager: _manager + selectedPost: $selectedPost ) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - #endif +#endif - if settings.displayDetailsInformationBar { - VStack(spacing: 5) { - HStack { - Text(post.createdAt.formatted()) - .frame(maxWidth: .infinity, alignment: .leading) + if settings.displayDetailsInformationBar { + VStack(spacing: 5) { + HStack { + Text(post.createdAt.formatted()) + .frame(maxWidth: .infinity, alignment: .leading) - Group { - switch loadingStage { - case .loadingPreview: - Text("Loading Preview…") + Group { + switch loadingStage { + case .loadingPreview: + Text("Loading Preview…") - case .loadingFile: - Text("Loading \(settings.detailViewQuality.rawValue)…") + case .loadingFile: + Text("Loading \(settings.detailViewQuality.rawValue)…") - case .loaded: - EmptyView() + case .loaded: + EmptyView() + } } + .padding(.trailing, 5) } - .padding(.trailing, 5) + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundStyle(.secondary) } - .frame(maxWidth: .infinity, alignment: .leading) - .foregroundStyle(.secondary) - } - .padding(.horizontal, 10) - .padding(.vertical, 10 / 1.33) - .textSelection(.enabled) - .font(.footnote) - #if os(iOS) + .padding(.horizontal, 10) + .padding(.vertical, 10 / 1.33) + .textSelection(.enabled) + .font(.footnote) +#if os(iOS) .background(.ultraThinMaterial) - #else +#else .background(.opacity(0.1)) - #endif - .zIndex(1) +#endif + .zIndex(1) + } } - } - .navigationTitle("Details") - #if os(iOS) + .navigationTitle("Details") +#if os(iOS) .navigationBarTitleDisplayMode(.inline) .toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.ultraThinMaterial, for: .navigationBar) - #endif - .toolbar { - #if !os(macOS) - if manager.isLoading || loadingStage != .loaded { +#endif + .toolbar { +#if !os(macOS) + if selectedPost.manager?.isLoading != false || loadingStage != .loaded { ToolbarItem { ProgressView() } } - #endif +#endif - ToolbarItem { - Button(action: { - isTagsSheetPresented.toggle() - }) { - Label("Tags", systemImage: "tag") + ToolbarItem { + Button(action: { + isTagsSheetPresented.toggle() + }) { + Label("Tags", systemImage: "tag") + } } - } - #if os(macOS) +#if os(macOS) if settings.enableShareShortcut { ToolbarItem { ShareLink(item: imageURL!) { @@ -119,16 +119,16 @@ struct PostDetailsView: View { } } } - #endif - } - .sheet(isPresented: $isTagsSheetPresented) { - PostDetailsTagsView( - manager: manager, - isPresented: $isTagsSheetPresented, - tags: post.tags, - selectedPost: $selectedPost - ) - .frame(minHeight: 250) +#endif + } + .sheet(isPresented: $isTagsSheetPresented) { + PostDetailsTagsView( + isPresented: $isTagsSheetPresented, + tags: post.tags, + selectedPost: $selectedPost + ) + .frame(minHeight: 250) + } } } } diff --git a/Sora/Views/Post/Grid/PostGridView.swift b/Sora/Views/Post/Grid/PostGridView.swift index 2b9d29b..0b1d532 100644 --- a/Sora/Views/Post/Grid/PostGridView.swift +++ b/Sora/Views/Post/Grid/PostGridView.swift @@ -45,7 +45,7 @@ struct PostGridView: View { manager.performSearch(settings: settings) } .navigationDestination(for: BooruPost.self) { post in - PostDetailsView(post: post, manager: manager, selectedPost: $selectedPost) + PostDetailsView(selectedPost: $selectedPost) } .onChange(of: manager.searchText) { _, _ in if manager.searchText.isEmpty, !isSearching { |