feat: use search api to fetch tag info
This commit is contained in:
parent
d548840bd9
commit
b63a5ebe5f
|
@ -240,6 +240,9 @@
|
||||||
"placeholder": "Search hashtags and users",
|
"placeholder": "Search hashtags and users",
|
||||||
"cancel": "Cancel"
|
"cancel": "Cancel"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"hashtag": {
|
||||||
|
"prompt": "%s people talking"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
0F1E2D0B2615C39400C38565 /* HashtagTimelineTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F1E2D0A2615C39400C38565 /* HashtagTimelineTitleView.swift */; };
|
||||||
0F2021FB2613262F000C64BF /* HashtagTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F2021FA2613262F000C64BF /* HashtagTimelineViewController.swift */; };
|
0F2021FB2613262F000C64BF /* HashtagTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F2021FA2613262F000C64BF /* HashtagTimelineViewController.swift */; };
|
||||||
0F202201261326E6000C64BF /* HashtagTimelineViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */; };
|
0F202201261326E6000C64BF /* HashtagTimelineViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */; };
|
||||||
0F20220726134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F20220626134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift */; };
|
0F20220726134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F20220626134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift */; };
|
||||||
|
@ -325,6 +326,7 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
0F1E2D0A2615C39400C38565 /* HashtagTimelineTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineTitleView.swift; sourceTree = "<group>"; };
|
||||||
0F2021FA2613262F000C64BF /* HashtagTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineViewController.swift; sourceTree = "<group>"; };
|
0F2021FA2613262F000C64BF /* HashtagTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineViewController.swift; sourceTree = "<group>"; };
|
||||||
0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineViewModel.swift; sourceTree = "<group>"; };
|
0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineViewModel.swift; sourceTree = "<group>"; };
|
||||||
0F20220626134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HashtagTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
0F20220626134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HashtagTimelineViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -652,9 +654,18 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
0F1E2D102615C39800C38565 /* View */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0F1E2D0A2615C39400C38565 /* HashtagTimelineTitleView.swift */,
|
||||||
|
);
|
||||||
|
path = View;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
0F2021F5261325ED000C64BF /* HashtagTimeline */ = {
|
0F2021F5261325ED000C64BF /* HashtagTimeline */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
0F1E2D102615C39800C38565 /* View */,
|
||||||
0F2021FA2613262F000C64BF /* HashtagTimelineViewController.swift */,
|
0F2021FA2613262F000C64BF /* HashtagTimelineViewController.swift */,
|
||||||
0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */,
|
0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */,
|
||||||
0F20220626134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift */,
|
0F20220626134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift */,
|
||||||
|
@ -1907,6 +1918,7 @@
|
||||||
2D38F1F125CD477D00561493 /* HomeTimelineViewModel+LoadMiddleState.swift in Sources */,
|
2D38F1F125CD477D00561493 /* HomeTimelineViewModel+LoadMiddleState.swift in Sources */,
|
||||||
DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */,
|
DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */,
|
||||||
2D8434FB25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift in Sources */,
|
2D8434FB25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift in Sources */,
|
||||||
|
0F1E2D0B2615C39400C38565 /* HashtagTimelineTitleView.swift in Sources */,
|
||||||
DB9A488A26034D40008B817C /* ComposeViewModel+PublishState.swift in Sources */,
|
DB9A488A26034D40008B817C /* ComposeViewModel+PublishState.swift in Sources */,
|
||||||
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
|
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
|
||||||
2DA504692601ADE7008F4E6C /* SawToothView.swift in Sources */,
|
2DA504692601ADE7008F4E6C /* SawToothView.swift in Sources */,
|
||||||
|
|
|
@ -249,6 +249,12 @@ internal enum L10n {
|
||||||
internal static let title = L10n.tr("Localizable", "Scene.ConfirmEmail.OpenEmailApp.Title")
|
internal static let title = L10n.tr("Localizable", "Scene.ConfirmEmail.OpenEmailApp.Title")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal enum Hashtag {
|
||||||
|
/// %@ people talking
|
||||||
|
internal static func prompt(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "Scene.Hashtag.Prompt", String(describing: p1))
|
||||||
|
}
|
||||||
|
}
|
||||||
internal enum HomeTimeline {
|
internal enum HomeTimeline {
|
||||||
/// Home
|
/// Home
|
||||||
internal static let title = L10n.tr("Localizable", "Scene.HomeTimeline.Title")
|
internal static let title = L10n.tr("Localizable", "Scene.HomeTimeline.Title")
|
||||||
|
|
|
@ -80,6 +80,7 @@ uploaded to Mastodon.";
|
||||||
"Scene.ConfirmEmail.Subtitle" = "We just sent an email to %@,
|
"Scene.ConfirmEmail.Subtitle" = "We just sent an email to %@,
|
||||||
tap the link to confirm your account.";
|
tap the link to confirm your account.";
|
||||||
"Scene.ConfirmEmail.Title" = "One last thing.";
|
"Scene.ConfirmEmail.Title" = "One last thing.";
|
||||||
|
"Scene.Hashtag.Prompt" = "%@ people talking";
|
||||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "See new posts";
|
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "See new posts";
|
||||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Offline";
|
"Scene.HomeTimeline.NavigationBarState.Offline" = "Offline";
|
||||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Published!";
|
"Scene.HomeTimeline.NavigationBarState.Published" = "Published!";
|
||||||
|
|
|
@ -96,12 +96,20 @@ extension HashtagTimelineViewController {
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
viewModel.hashtagEntity
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] tag in
|
||||||
|
self?.updatePromptTitle()
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
viewModel.fetchTag()
|
||||||
guard viewModel.loadLatestStateMachine.currentState is HashtagTimelineViewModel.LoadLatestState.Initial else { return }
|
guard viewModel.loadLatestStateMachine.currentState is HashtagTimelineViewModel.LoadLatestState.Initial else { return }
|
||||||
tableView.setContentOffset(CGPoint(x: 0, y: tableView.contentOffset.y - refreshControl.frame.size.height), animated: true)
|
|
||||||
refreshControl.beginRefreshing()
|
refreshControl.beginRefreshing()
|
||||||
refreshControl.sendActions(for: .valueChanged)
|
refreshControl.sendActions(for: .valueChanged)
|
||||||
}
|
}
|
||||||
|
@ -123,6 +131,24 @@ extension HashtagTimelineViewController {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updatePromptTitle() {
|
||||||
|
guard let histories = viewModel.hashtagEntity.value?.history else {
|
||||||
|
navigationItem.prompt = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if histories.isEmpty {
|
||||||
|
// No tag history, remove the prompt title
|
||||||
|
navigationItem.prompt = nil
|
||||||
|
} else {
|
||||||
|
let sortedHistory = histories.sorted { (h1, h2) -> Bool in
|
||||||
|
return h1.day > h2.day
|
||||||
|
}
|
||||||
|
if let accountsNumber = sortedHistory.first?.accounts {
|
||||||
|
navigationItem.prompt = L10n.Scene.Hashtag.prompt(accountsNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension HashtagTimelineViewController {
|
extension HashtagTimelineViewController {
|
||||||
|
|
|
@ -27,6 +27,7 @@ final class HashtagTimelineViewModel: NSObject {
|
||||||
let fetchedResultsController: NSFetchedResultsController<Toot>
|
let fetchedResultsController: NSFetchedResultsController<Toot>
|
||||||
let isFetchingLatestTimeline = CurrentValueSubject<Bool, Never>(false)
|
let isFetchingLatestTimeline = CurrentValueSubject<Bool, Never>(false)
|
||||||
let timelinePredicate = CurrentValueSubject<NSPredicate?, Never>(nil)
|
let timelinePredicate = CurrentValueSubject<NSPredicate?, Never>(nil)
|
||||||
|
let hashtagEntity = CurrentValueSubject<Mastodon.Entity.Tag?, Never>(nil)
|
||||||
|
|
||||||
weak var contentOffsetAdjustableTimelineViewControllerDelegate: ContentOffsetAdjustableTimelineViewControllerDelegate?
|
weak var contentOffsetAdjustableTimelineViewControllerDelegate: ContentOffsetAdjustableTimelineViewControllerDelegate?
|
||||||
weak var tableView: UITableView?
|
weak var tableView: UITableView?
|
||||||
|
@ -105,6 +106,24 @@ final class HashtagTimelineViewModel: NSObject {
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fetchTag() {
|
||||||
|
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let query = Mastodon.API.Search.Query(q: hashTag, type: .hashtags)
|
||||||
|
context.apiService.search(domain: activeMastodonAuthenticationBox.domain, query: query, mastodonAuthenticationBox: activeMastodonAuthenticationBox)
|
||||||
|
.sink { _ in
|
||||||
|
|
||||||
|
} receiveValue: { [weak self] response in
|
||||||
|
let matchedTag = response.value.hashtags.first { tag -> Bool in
|
||||||
|
return tag.name == self?.hashTag
|
||||||
|
}
|
||||||
|
self?.hashtagEntity.send(matchedTag)
|
||||||
|
}
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
//
|
||||||
|
// HashtagTimelineTitleView.swift
|
||||||
|
// Mastodon
|
||||||
|
//
|
||||||
|
// Created by BradGao on 2021/4/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
final class HashtagTimelineTitleView: UIView {
|
||||||
|
|
||||||
|
let containerView = UIStackView()
|
||||||
|
|
||||||
|
let imageView = UIImageView()
|
||||||
|
let button = RoundedEdgesButton()
|
||||||
|
let label = UILabel()
|
||||||
|
|
||||||
|
// input
|
||||||
|
private var blockingState: HomeTimelineNavigationBarTitleViewModel.State?
|
||||||
|
weak var delegate: HomeTimelineNavigationBarTitleViewDelegate?
|
||||||
|
|
||||||
|
// output
|
||||||
|
private(set) var state: HomeTimelineNavigationBarTitleViewModel.State = .logoImage
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension HomeTimelineNavigationBarTitleView {
|
||||||
|
private func _init() {
|
||||||
|
containerView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
addSubview(containerView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
containerView.topAnchor.constraint(equalTo: topAnchor),
|
||||||
|
containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
containerView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
|
containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||||
|
])
|
||||||
|
|
||||||
|
containerView.addArrangedSubview(imageView)
|
||||||
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
containerView.addArrangedSubview(button)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
button.heightAnchor.constraint(equalToConstant: 24).priority(.defaultHigh)
|
||||||
|
])
|
||||||
|
containerView.addArrangedSubview(label)
|
||||||
|
|
||||||
|
configure(state: .logoImage)
|
||||||
|
button.addTarget(self, action: #selector(HomeTimelineNavigationBarTitleView.buttonDidPressed(_:)), for: .touchUpInside)
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,7 +121,7 @@ extension Mastodon.API.Favorites {
|
||||||
case destroy
|
case destroy
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ListQuery: GetQuery,TimelineQueryType {
|
public struct ListQuery: GetQuery,PagedQueryType {
|
||||||
|
|
||||||
public var limit: Int?
|
public var limit: Int?
|
||||||
public var minID: String?
|
public var minID: String?
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by BradGao on 2021/4/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
extension Mastodon.API.Notifications {
|
||||||
|
static func notificationsEndpointURL(domain: String) -> URL {
|
||||||
|
Mastodon.API.endpointV2URL(domain: domain).appendingPathComponent("notifications")
|
||||||
|
}
|
||||||
|
static func getNotificationEndpointURL(domain: String, notificationID: String) -> URL {
|
||||||
|
notificationsEndpointURL(domain: domain).appendingPathComponent(notificationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all notifications
|
||||||
|
///
|
||||||
|
/// - Since: 0.0.0
|
||||||
|
/// - Version: 3.1.0
|
||||||
|
/// # Last Update
|
||||||
|
/// 2021/4/1
|
||||||
|
/// # Reference
|
||||||
|
/// [Document](https://docs.joinmastodon.org/methods/notifications/)
|
||||||
|
/// - Parameters:
|
||||||
|
/// - session: `URLSession`
|
||||||
|
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||||
|
/// - query: `GetAllNotificationsQuery` with query parameters
|
||||||
|
/// - authorization: User token
|
||||||
|
/// - Returns: `AnyPublisher` contains `Token` nested in the response
|
||||||
|
public static func getAll(
|
||||||
|
session: URLSession,
|
||||||
|
domain: String,
|
||||||
|
query: GetAllNotificationsQuery,
|
||||||
|
authorization: Mastodon.API.OAuth.Authorization?
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Notification]>, Error> {
|
||||||
|
let request = Mastodon.API.get(
|
||||||
|
url: notificationsEndpointURL(domain: domain),
|
||||||
|
query: query,
|
||||||
|
authorization: authorization
|
||||||
|
)
|
||||||
|
return session.dataTaskPublisher(for: request)
|
||||||
|
.tryMap { data, response in
|
||||||
|
let value = try Mastodon.API.decode(type: [Mastodon.Entity.Notification].self, from: data, response: response)
|
||||||
|
return Mastodon.Response.Content(value: value, response: response)
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a single notification
|
||||||
|
///
|
||||||
|
/// - Since: 0.0.0
|
||||||
|
/// - Version: 3.1.0
|
||||||
|
/// # Last Update
|
||||||
|
/// 2021/4/1
|
||||||
|
/// # Reference
|
||||||
|
/// [Document](https://docs.joinmastodon.org/methods/notifications/)
|
||||||
|
/// - Parameters:
|
||||||
|
/// - session: `URLSession`
|
||||||
|
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||||
|
/// - notificationID: ID of the notification.
|
||||||
|
/// - authorization: User token
|
||||||
|
/// - Returns: `AnyPublisher` contains `Token` nested in the response
|
||||||
|
public static func get(
|
||||||
|
session: URLSession,
|
||||||
|
domain: String,
|
||||||
|
notificationID: String,
|
||||||
|
authorization: Mastodon.API.OAuth.Authorization?
|
||||||
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Notification>, Error> {
|
||||||
|
let request = Mastodon.API.get(
|
||||||
|
url: getNotificationEndpointURL(domain: domain, notificationID: notificationID),
|
||||||
|
query: nil,
|
||||||
|
authorization: authorization
|
||||||
|
)
|
||||||
|
return session.dataTaskPublisher(for: request)
|
||||||
|
.tryMap { data, response in
|
||||||
|
let value = try Mastodon.API.decode(type: Mastodon.Entity.Notification.self, from: data, response: response)
|
||||||
|
return Mastodon.Response.Content(value: value, response: response)
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct GetAllNotificationsQuery: Codable, PagedQueryType, GetQuery {
|
||||||
|
public let maxID: Mastodon.Entity.Status.ID?
|
||||||
|
public let sinceID: Mastodon.Entity.Status.ID?
|
||||||
|
public let minID: Mastodon.Entity.Status.ID?
|
||||||
|
public let limit: Int?
|
||||||
|
public let excludeTypes: [String]?
|
||||||
|
public let accountID: String?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
maxID: Mastodon.Entity.Status.ID? = nil,
|
||||||
|
sinceID: Mastodon.Entity.Status.ID? = nil,
|
||||||
|
minID: Mastodon.Entity.Status.ID? = nil,
|
||||||
|
limit: Int? = nil,
|
||||||
|
excludeTypes: [String]? = nil,
|
||||||
|
accountID: String? = nil
|
||||||
|
) {
|
||||||
|
self.maxID = maxID
|
||||||
|
self.sinceID = sinceID
|
||||||
|
self.minID = minID
|
||||||
|
self.limit = limit
|
||||||
|
self.excludeTypes = excludeTypes
|
||||||
|
self.accountID = accountID
|
||||||
|
}
|
||||||
|
|
||||||
|
var queryItems: [URLQueryItem]? {
|
||||||
|
var items: [URLQueryItem] = []
|
||||||
|
maxID.flatMap { items.append(URLQueryItem(name: "max_id", value: $0)) }
|
||||||
|
sinceID.flatMap { items.append(URLQueryItem(name: "since_id", value: $0)) }
|
||||||
|
minID.flatMap { items.append(URLQueryItem(name: "min_id", value: $0)) }
|
||||||
|
limit.flatMap { items.append(URLQueryItem(name: "limit", value: String($0))) }
|
||||||
|
if let excludeTypes = excludeTypes {
|
||||||
|
excludeTypes.forEach {
|
||||||
|
items.append(URLQueryItem(name: "exclude_types[]", value: $0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accountID.flatMap { items.append(URLQueryItem(name: "account_id", value: $0)) }
|
||||||
|
guard !items.isEmpty else { return nil }
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,8 +50,21 @@ extension Mastodon.API.Search {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Mastodon.API.Search {
|
extension Mastodon.API.Search {
|
||||||
|
public enum SearchType: String, Codable {
|
||||||
|
case ccounts, hashtags, statuses
|
||||||
|
}
|
||||||
|
|
||||||
public struct Query: Codable, GetQuery {
|
public struct Query: Codable, GetQuery {
|
||||||
public init(accountID: Mastodon.Entity.Account.ID?, maxID: Mastodon.Entity.Status.ID?, minID: Mastodon.Entity.Status.ID?, type: String?, excludeUnreviewed: Bool?, q: String, resolve: Bool?, limit: Int?, offset: Int?, following: Bool?) {
|
public init(q: String,
|
||||||
|
type: SearchType? = nil,
|
||||||
|
accountID: Mastodon.Entity.Account.ID? = nil,
|
||||||
|
maxID: Mastodon.Entity.Status.ID? = nil,
|
||||||
|
minID: Mastodon.Entity.Status.ID? = nil,
|
||||||
|
excludeUnreviewed: Bool? = nil,
|
||||||
|
resolve: Bool? = nil,
|
||||||
|
limit: Int? = nil,
|
||||||
|
offset: Int? = nil,
|
||||||
|
following: Bool? = nil) {
|
||||||
self.accountID = accountID
|
self.accountID = accountID
|
||||||
self.maxID = maxID
|
self.maxID = maxID
|
||||||
self.minID = minID
|
self.minID = minID
|
||||||
|
@ -67,7 +80,7 @@ extension Mastodon.API.Search {
|
||||||
public let accountID: Mastodon.Entity.Account.ID?
|
public let accountID: Mastodon.Entity.Account.ID?
|
||||||
public let maxID: Mastodon.Entity.Status.ID?
|
public let maxID: Mastodon.Entity.Status.ID?
|
||||||
public let minID: Mastodon.Entity.Status.ID?
|
public let minID: Mastodon.Entity.Status.ID?
|
||||||
public let type: String?
|
public let type: SearchType?
|
||||||
public let excludeUnreviewed: Bool? // Filter out unreviewed tags? Defaults to false. Use true when trying to find trending tags.
|
public let excludeUnreviewed: Bool? // Filter out unreviewed tags? Defaults to false. Use true when trying to find trending tags.
|
||||||
public let q: String
|
public let q: String
|
||||||
public let resolve: Bool? // Attempt WebFinger lookup. Defaults to false.
|
public let resolve: Bool? // Attempt WebFinger lookup. Defaults to false.
|
||||||
|
@ -80,7 +93,7 @@ extension Mastodon.API.Search {
|
||||||
accountID.flatMap { items.append(URLQueryItem(name: "account_id", value: $0)) }
|
accountID.flatMap { items.append(URLQueryItem(name: "account_id", value: $0)) }
|
||||||
maxID.flatMap { items.append(URLQueryItem(name: "max_id", value: $0)) }
|
maxID.flatMap { items.append(URLQueryItem(name: "max_id", value: $0)) }
|
||||||
minID.flatMap { items.append(URLQueryItem(name: "min_id", value: $0)) }
|
minID.flatMap { items.append(URLQueryItem(name: "min_id", value: $0)) }
|
||||||
type.flatMap { items.append(URLQueryItem(name: "type", value: $0)) }
|
type.flatMap { items.append(URLQueryItem(name: "type", value: $0.rawValue)) }
|
||||||
excludeUnreviewed.flatMap { items.append(URLQueryItem(name: "exclude_unreviewed", value: $0.queryItemValue)) }
|
excludeUnreviewed.flatMap { items.append(URLQueryItem(name: "exclude_unreviewed", value: $0.queryItemValue)) }
|
||||||
items.append(URLQueryItem(name: "q", value: q))
|
items.append(URLQueryItem(name: "q", value: q))
|
||||||
resolve.flatMap { items.append(URLQueryItem(name: "resolve", value: $0.queryItemValue)) }
|
resolve.flatMap { items.append(URLQueryItem(name: "resolve", value: $0.queryItemValue)) }
|
||||||
|
|
|
@ -121,14 +121,14 @@ extension Mastodon.API.Timeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol TimelineQueryType {
|
public protocol PagedQueryType {
|
||||||
var maxID: Mastodon.Entity.Status.ID? { get }
|
var maxID: Mastodon.Entity.Status.ID? { get }
|
||||||
var sinceID: Mastodon.Entity.Status.ID? { get }
|
var sinceID: Mastodon.Entity.Status.ID? { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Mastodon.API.Timeline {
|
extension Mastodon.API.Timeline {
|
||||||
|
|
||||||
public typealias TimelineQuery = TimelineQueryType
|
public typealias TimelineQuery = PagedQueryType
|
||||||
|
|
||||||
public struct PublicTimelineQuery: Codable, TimelineQuery, GetQuery {
|
public struct PublicTimelineQuery: Codable, TimelineQuery, GetQuery {
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,7 @@ extension Mastodon.API {
|
||||||
public enum Search { }
|
public enum Search { }
|
||||||
public enum Trends { }
|
public enum Trends { }
|
||||||
public enum Suggestions { }
|
public enum Suggestions { }
|
||||||
|
public enum Notifications { }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Mastodon.API {
|
extension Mastodon.API {
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
extension Mastodon.Entity {
|
extension Mastodon.Entity {
|
||||||
public struct SearchResult: Codable {
|
public struct SearchResult: Codable {
|
||||||
let accounts: [Mastodon.Entity.Account]
|
public let accounts: [Mastodon.Entity.Account]
|
||||||
let statuses: [Mastodon.Entity.Status]
|
public let statuses: [Mastodon.Entity.Status]
|
||||||
let hashtags: [Mastodon.Entity.Tag]
|
public let hashtags: [Mastodon.Entity.Tag]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue