import SwiftUI struct PostDetailsView: View { @EnvironmentObject var manager: BooruManager @EnvironmentObject var settings: SettingsManager let post: BooruPost @Binding var navigationPath: NavigationPath @State private var loadingStage: BooruPostLoadingState = .loadingPreview @State private var isTagsSheetPresented = false // swiftlint:disable:next discouraged_optional_collection let posts: [BooruPost]? let baseSearchText: String? private var imageURL: URL? { switch settings.detailViewQuality { case .preview: currentPost.previewURL case .sample: currentPost.sampleURL case .original: currentPost.fileURL } } init( post: BooruPost, navigationPath: Binding, // swiftlint:disable:next discouraged_optional_collection posts: [BooruPost]? = nil, baseSearchText: String? = nil ) { self.post = post self._navigationPath = navigationPath self.posts = posts self.baseSearchText = baseSearchText } var filteredPosts: [BooruPost] { let sourcePosts = posts ?? manager.posts return sourcePosts.filter { settings.displayRatings.contains($0.rating) } } private var currentPost: BooruPost { manager.selectedPost ?? post } var body: some View { VStack(spacing: 0) { #if os(macOS) PostDetailsImageView( url: imageURL, loadingStage: $loadingStage, finalLoadingState: .loaded, post: currentPost ) { PostDetailsImageView( url: currentPost.previewURL, loadingStage: $loadingStage ) .id(currentPost.previewURL) } .id(imageURL) #else PostDetailsCarouselView( posts: filteredPosts, loadingStage: $loadingStage, focusedPost: currentPost ) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) #endif if settings.displayDetailsInformationBar { VStack(spacing: 5) { HStack { Text(currentPost.createdAt.formatted()) .frame(maxWidth: .infinity, alignment: .leading) Group { switch loadingStage { case .loadingPreview: Text("Loading Preview…") case .loadingFile: Text("Loading \(settings.detailViewQuality.rawValue)…") case .loaded: EmptyView() } } .padding(.trailing, 5) } .frame(maxWidth: .infinity, alignment: .leading) .foregroundStyle(.secondary) } .padding(.horizontal, 10) .padding(.vertical, 10 / 1.33) .textSelection(.enabled) .font(.footnote) #if os(iOS) .background(.ultraThinMaterial) #else .background(.opacity(0.1)) #endif .zIndex(1) } } .ifiOS26Unavailable { view in view .navigationTitle("Details") #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif } .toolbar { ToolbarItem { PostGridFavoriteButtonView(post: currentPost) } ToolbarItem { Button(action: { isTagsSheetPresented.toggle() }) { Label("Tags", systemImage: "tag") } } #if os(macOS) if settings.enableShareShortcut { if let imageURL { ToolbarItem { ShareLink(item: imageURL) { Label("Share", systemImage: "square.and.arrow.up") } } } } #endif } .sheet(isPresented: $isTagsSheetPresented) { if #available(macOS 15.0, *) { tagsSheetContent() #if os(macOS) .presentationSizing(.page) #endif } else { tagsSheetContent() } } } @ViewBuilder private func tagsSheetContent() -> some View { PostDetailsTagsView( isPresented: $isTagsSheetPresented, navigationPath: $navigationPath, tags: currentPost.tags, isNestedContext: posts != nil, baseSearchText: baseSearchText ) #if os(macOS) .frame( minHeight: (NSScreen.main?.frame.height ?? 1_080) / 2, maxHeight: .infinity ) #endif } }