2022-12-21 04:37:12 +00:00
|
|
|
//
|
|
|
|
// AddHashtagPinnedTimelineView.swift
|
|
|
|
// Tusker
|
|
|
|
//
|
|
|
|
// Created by Shadowfacts on 12/20/22.
|
|
|
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import SwiftUI
|
|
|
|
import Pachyderm
|
|
|
|
|
2023-02-03 04:02:11 +00:00
|
|
|
@available(iOS, obsoleted: 16.0)
|
|
|
|
struct AddHashtagPinnedTimelineRepresentable: UIViewControllerRepresentable {
|
|
|
|
typealias UIViewControllerType = UIHostingController<AddHashtagPinnedTimelineView>
|
|
|
|
|
|
|
|
@Binding var pinnedTimelines: [PinnedTimeline]
|
|
|
|
|
|
|
|
func makeUIViewController(context: Context) -> UIHostingController<AddHashtagPinnedTimelineView> {
|
|
|
|
return UIHostingController(rootView: AddHashtagPinnedTimelineView(pinnedTimelines: $pinnedTimelines))
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateUIViewController(_ uiViewController: UIHostingController<AddHashtagPinnedTimelineView>, context: Context) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-21 04:37:12 +00:00
|
|
|
struct AddHashtagPinnedTimelineView: View {
|
|
|
|
@EnvironmentObject private var mastodonController: MastodonController
|
|
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
|
2023-01-27 23:12:54 +00:00
|
|
|
@Binding var pinnedTimelines: [PinnedTimeline]
|
2022-12-21 04:37:12 +00:00
|
|
|
@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
|
2023-02-06 23:42:55 +00:00
|
|
|
.listStyle(.grouped)
|
|
|
|
.appGroupedListBackground(container: AddHashtagPinnedTimelineRepresentable.UIViewControllerType.self)
|
2023-01-27 23:12:54 +00:00
|
|
|
.navigationTitle("Add Hashtag")
|
2022-12-21 04:37:12 +00:00
|
|
|
.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()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-02-03 04:02:11 +00:00
|
|
|
@ViewBuilder
|
2022-12-21 04:37:12 +00:00
|
|
|
private var list: some View {
|
2023-02-03 04:02:11 +00:00
|
|
|
let list = List {
|
2022-12-21 04:37:12 +00:00
|
|
|
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)
|
|
|
|
}
|
2023-02-06 23:42:55 +00:00
|
|
|
.appGroupedListRowBackground()
|
2022-12-21 04:37:12 +00:00
|
|
|
}
|
|
|
|
.listStyle(.grouped)
|
2023-02-03 04:02:11 +00:00
|
|
|
|
|
|
|
if #available(iOS 16.0, *) {
|
|
|
|
list
|
|
|
|
.scrollContentBackground(.hidden)
|
|
|
|
.background(Color.appGroupedBackground)
|
|
|
|
} else {
|
|
|
|
list
|
|
|
|
}
|
2022-12-21 04:37:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
// }
|
|
|
|
//}
|