Tusker/Tusker/Screens/Customize Timelines/AddHashtagPinnedTimelineVie...

114 lines
3.8 KiB
Swift

//
// 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<Void, Never>?
@State private var isSearching = false
@State private var searchResults: [String] = []
private var savedAndFollowedHashtags: [String] {
var tags = Set<String>()
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()
// }
//}