// // AddHashtagPinnedTimelineView.swift // Tusker // // Created by Shadowfacts on 12/20/22. // Copyright © 2022 Shadowfacts. All rights reserved. // import SwiftUI import Pachyderm struct AddHashtagPinnedTimelineView: View { @EnvironmentObject private var mastodonController: MastodonController @Environment(\.dismiss) private var dismiss @Binding var pinnedTimelines: [PinnedTimeline] @StateObject private var viewModel = SearchViewModel() @State private var searchTask: Task? @State private var isSearching = false @State private var searchResults: [String] = [] private var savedAndFollowedHashtags: [String] { var tags = Set() let req = SavedHashtag.fetchRequest(account: mastodonController.accountInfo!) for saved in (try? mastodonController.persistentContainer.viewContext.fetch(req)) ?? [] { tags.insert(saved.name) } for followed in mastodonController.followedHashtags { tags.insert(followed.name) } return Array(tags).sorted(using: SemiCaseSensitiveComparator()) } var body: some View { NavigationView { list .navigationTitle("Add Hashtag") .navigationBarTitleDisplayMode(.inline) .searchable(text: $viewModel.searchQuery, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Search for hashtags")) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } } } } .navigationViewStyle(.stack) .onReceive(viewModel.$searchQuery, perform: { newValue in isSearching = !newValue.isEmpty }) .onReceive(viewModel.$searchQuery.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main), perform: { _ in searchTask?.cancel() searchTask = Task { try? await updateSearchResults() } }) } private var list: some View { List { Section { if viewModel.searchQuery.isEmpty { forEachTag(savedAndFollowedHashtags) } else { forEachTag(searchResults) } } header: { ProgressView() .progressViewStyle(.circular) .opacity(isSearching ? 1 : 0) .frame(maxWidth: .infinity, alignment: .center) .listRowBackground(EmptyView()) .listRowSeparator(.hidden) } } .listStyle(.grouped) } private func forEachTag(_ tags: [String]) -> some View { ForEach(tags, id: \.self) { tag in Button { pinnedTimelines.append(.tag(hashtag: tag)) dismiss() } label: { Text("#\(tag)") } .tint(.primary) .disabled(pinnedTimelines.contains(.tag(hashtag: tag))) } } private func updateSearchResults() async throws { guard !viewModel.searchQuery.isEmpty else { return } isSearching = true let req = Client.search(query: viewModel.searchQuery, types: [.hashtags]) let (results, _) = try await mastodonController.run(req) searchResults = results.hashtags.map(\.name) isSearching = false } } private class SearchViewModel: ObservableObject { @Published var searchQuery = "" } //struct AddHashtagPinnedTimelineView_Previews: PreviewProvider { // static var previews: some View { // AddHashtagPinnedTimelineView() // } //}