chore: suggestion use v2 api

This commit is contained in:
sunxiaojian 2021-04-20 15:40:10 +08:00
parent eb89ff6bdc
commit 731b49aaa0
10 changed files with 98 additions and 28 deletions

View File

@ -84,7 +84,7 @@ final class HashtagTimelineViewModel: NSObject {
guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else {
return return
} }
let query = Mastodon.API.Search.Query(q: hashtag, type: .hashtags) let query = Mastodon.API.V2.Search.Query(q: hashtag, type: .hashtags)
context.apiService.search(domain: activeMastodonAuthenticationBox.domain, query: query, mastodonAuthenticationBox: activeMastodonAuthenticationBox) context.apiService.search(domain: activeMastodonAuthenticationBox.domain, query: query, mastodonAuthenticationBox: activeMastodonAuthenticationBox)
.sink { _ in .sink { _ in

View File

@ -217,11 +217,11 @@ extension SearchViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
switch selectedScope { switch selectedScope {
case 0: case 0:
viewModel.searchScope.value = Mastodon.API.Search.SearchType.default viewModel.searchScope.value = Mastodon.API.V2.Search.SearchType.default
case 1: case 1:
viewModel.searchScope.value = Mastodon.API.Search.SearchType.accounts viewModel.searchScope.value = Mastodon.API.V2.Search.SearchType.accounts
case 2: case 2:
viewModel.searchScope.value = Mastodon.API.Search.SearchType.hashtags viewModel.searchScope.value = Mastodon.API.V2.Search.SearchType.hashtags
default: default:
break break
} }

View File

@ -53,14 +53,14 @@ extension SearchViewModel.LoadOldestState {
} }
var offset = 0 var offset = 0
switch viewModel.searchScope.value { switch viewModel.searchScope.value {
case Mastodon.API.Search.SearchType.accounts: case Mastodon.API.V2.Search.SearchType.accounts:
offset = oldSearchResult.accounts.count offset = oldSearchResult.accounts.count
case Mastodon.API.Search.SearchType.hashtags: case Mastodon.API.V2.Search.SearchType.hashtags:
offset = oldSearchResult.hashtags.count offset = oldSearchResult.hashtags.count
default: default:
return return
} }
let query = Mastodon.API.Search.Query(q: viewModel.searchText.value, let query = Mastodon.API.V2.Search.Query(q: viewModel.searchText.value,
type: viewModel.searchScope.value, type: viewModel.searchScope.value,
accountID: nil, accountID: nil,
maxID: nil, maxID: nil,
@ -82,7 +82,7 @@ extension SearchViewModel.LoadOldestState {
} }
} receiveValue: { result in } receiveValue: { result in
switch viewModel.searchScope.value { switch viewModel.searchScope.value {
case Mastodon.API.Search.SearchType.accounts: case Mastodon.API.V2.Search.SearchType.accounts:
if result.value.accounts.isEmpty { if result.value.accounts.isEmpty {
stateMachine.enter(NoMore.self) stateMachine.enter(NoMore.self)
} else { } else {
@ -93,7 +93,7 @@ extension SearchViewModel.LoadOldestState {
viewModel.searchResult.value = Mastodon.Entity.SearchResult(accounts: newAccounts, statuses: oldSearchResult.statuses, hashtags: oldSearchResult.hashtags) viewModel.searchResult.value = Mastodon.Entity.SearchResult(accounts: newAccounts, statuses: oldSearchResult.statuses, hashtags: oldSearchResult.hashtags)
stateMachine.enter(Idle.self) stateMachine.enter(Idle.self)
} }
case Mastodon.API.Search.SearchType.hashtags: case Mastodon.API.V2.Search.SearchType.hashtags:
if result.value.hashtags.isEmpty { if result.value.hashtags.isEmpty {
stateMachine.enter(NoMore.self) stateMachine.enter(NoMore.self)
} else { } else {

View File

@ -26,7 +26,7 @@ final class SearchViewModel: NSObject {
// output // output
let searchText = CurrentValueSubject<String, Never>("") let searchText = CurrentValueSubject<String, Never>("")
let searchScope = CurrentValueSubject<Mastodon.API.Search.SearchType, Never>(Mastodon.API.Search.SearchType.default) let searchScope = CurrentValueSubject<Mastodon.API.V2.Search.SearchType, Never>(Mastodon.API.V2.Search.SearchType.default)
let isSearching = CurrentValueSubject<Bool, Never>(false) let isSearching = CurrentValueSubject<Bool, Never>(false)
@ -86,7 +86,7 @@ final class SearchViewModel: NSObject {
} }
.flatMap { (text, scope) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.SearchResult>, Error> in .flatMap { (text, scope) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.SearchResult>, Error> in
let query = Mastodon.API.Search.Query(q: text, let query = Mastodon.API.V2.Search.Query(q: text,
type: scope, type: scope,
accountID: nil, accountID: nil,
maxID: nil, maxID: nil,
@ -130,8 +130,8 @@ final class SearchViewModel: NSObject {
snapshot.appendSections([.mixed]) snapshot.appendSections([.mixed])
searchHistories.forEach { searchHistory in searchHistories.forEach { searchHistory in
let containsAccount = scope == Mastodon.API.Search.SearchType.accounts || scope == Mastodon.API.Search.SearchType.default let containsAccount = scope == Mastodon.API.V2.Search.SearchType.accounts || scope == Mastodon.API.V2.Search.SearchType.default
let containsHashTag = scope == Mastodon.API.Search.SearchType.hashtags || scope == Mastodon.API.Search.SearchType.default let containsHashTag = scope == Mastodon.API.V2.Search.SearchType.hashtags || scope == Mastodon.API.V2.Search.SearchType.default
if let mastodonUser = searchHistory.account, containsAccount { if let mastodonUser = searchHistory.account, containsAccount {
let item = SearchResultItem.accountObjectID(accountObjectID: mastodonUser.objectID) let item = SearchResultItem.accountObjectID(accountObjectID: mastodonUser.objectID)
snapshot.appendItems([item], toSection: .mixed) snapshot.appendItems([item], toSection: .mixed)
@ -186,7 +186,7 @@ final class SearchViewModel: NSObject {
snapshot.appendSections([.account]) snapshot.appendSections([.account])
let items = accounts.compactMap { SearchResultItem.account(account: $0) } let items = accounts.compactMap { SearchResultItem.account(account: $0) }
snapshot.appendItems(items, toSection: .account) snapshot.appendItems(items, toSection: .account)
if self.searchScope.value == Mastodon.API.Search.SearchType.accounts, !items.isEmpty { if self.searchScope.value == Mastodon.API.V2.Search.SearchType.accounts, !items.isEmpty {
snapshot.appendItems([.bottomLoader], toSection: .account) snapshot.appendItems([.bottomLoader], toSection: .account)
} }
} }
@ -194,7 +194,7 @@ final class SearchViewModel: NSObject {
snapshot.appendSections([.hashtag]) snapshot.appendSections([.hashtag])
let items = tags.compactMap { SearchResultItem.hashtag(tag: $0) } let items = tags.compactMap { SearchResultItem.hashtag(tag: $0) }
snapshot.appendItems(items, toSection: .hashtag) snapshot.appendItems(items, toSection: .hashtag)
if self.searchScope.value == Mastodon.API.Search.SearchType.hashtags, !items.isEmpty { if self.searchScope.value == Mastodon.API.V2.Search.SearchType.hashtags, !items.isEmpty {
snapshot.appendItems([.bottomLoader], toSection: .hashtag) snapshot.appendItems([.bottomLoader], toSection: .hashtag)
} }
} }
@ -245,7 +245,7 @@ final class SearchViewModel: NSObject {
} }
} receiveValue: { [weak self] accounts in } receiveValue: { [weak self] accounts in
guard let self = self else { return } guard let self = self else { return }
let ids = accounts.value.compactMap({$0.id}) let ids = accounts.value.compactMap({$0.account.id})
let userFetchRequest = MastodonUser.sortedFetchRequest let userFetchRequest = MastodonUser.sortedFetchRequest
userFetchRequest.predicate = MastodonUser.predicate(domain: activeMastodonAuthenticationBox.domain, ids: ids) userFetchRequest.predicate = MastodonUser.predicate(domain: activeMastodonAuthenticationBox.domain, ids: ids)
let mastodonUsers: [MastodonUser]? = { let mastodonUsers: [MastodonUser]? = {

View File

@ -17,21 +17,22 @@ extension APIService {
domain: String, domain: String,
query: Mastodon.API.Suggestions.Query?, query: Mastodon.API.Suggestions.Query?,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Account]>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.V2.SuggestionAccount]>, Error> {
let authorization = mastodonAuthenticationBox.userAuthorization let authorization = mastodonAuthenticationBox.userAuthorization
return Mastodon.API.Suggestions.get(session: session, domain: domain, query: query, authorization: authorization) return Mastodon.API.V2.Suggestions.get(session: session, domain: domain, query: query, authorization: authorization)
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Account]>, Error> in .flatMap { response -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.V2.SuggestionAccount]>, Error> in
let log = OSLog.api let log = OSLog.api
return self.backgroundManagedObjectContext.performChanges { return self.backgroundManagedObjectContext.performChanges {
response.value.forEach { user in response.value.forEach { suggestionAccount in
let user = suggestionAccount.account
let (mastodonUser,isCreated) = APIService.CoreData.createOrMergeMastodonUser(into: self.backgroundManagedObjectContext, for: nil, in: domain, entity: user, userCache: nil, networkDate: Date(), log: log) let (mastodonUser,isCreated) = APIService.CoreData.createOrMergeMastodonUser(into: self.backgroundManagedObjectContext, for: nil, in: domain, entity: user, userCache: nil, networkDate: Date(), log: log)
let flag = isCreated ? "+" : "-" let flag = isCreated ? "+" : "-"
os_log(.info, log: log, "%{public}s[%{public}ld], %{public}s: fetch mastodon user [%s](%s)%s", (#file as NSString).lastPathComponent, #line, #function, flag, mastodonUser.id, mastodonUser.username) os_log(.info, log: log, "%{public}s[%{public}ld], %{public}s: fetch mastodon user [%s](%s)%s", (#file as NSString).lastPathComponent, #line, #function, flag, mastodonUser.id, mastodonUser.username)
} }
} }
.setFailureType(to: Error.self) .setFailureType(to: Error.self)
.tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.Account]> in .tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.V2.SuggestionAccount]> in
switch result { switch result {
case .success: case .success:
return response return response

View File

@ -13,11 +13,11 @@ extension APIService {
func search( func search(
domain: String, domain: String,
query: Mastodon.API.Search.Query, query: Mastodon.API.V2.Search.Query,
mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.SearchResult>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.SearchResult>, Error> {
let authorization = mastodonAuthenticationBox.userAuthorization let authorization = mastodonAuthenticationBox.userAuthorization
return Mastodon.API.Search.search(session: session, domain: domain, query: query, authorization: authorization) return Mastodon.API.V2.Search.search(session: session, domain: domain, query: query, authorization: authorization)
} }
} }

View File

@ -8,7 +8,7 @@
import Combine import Combine
import Foundation import Foundation
extension Mastodon.API.Search { extension Mastodon.API.V2.Search {
static func searchURL(domain: String) -> URL { static func searchURL(domain: String) -> URL {
Mastodon.API.endpointV2URL(domain: domain).appendingPathComponent("search") Mastodon.API.endpointV2URL(domain: domain).appendingPathComponent("search")
} }
@ -32,7 +32,7 @@ extension Mastodon.API.Search {
public static func search( public static func search(
session: URLSession, session: URLSession,
domain: String, domain: String,
query: Mastodon.API.Search.Query, query: Mastodon.API.V2.Search.Query,
authorization: Mastodon.API.OAuth.Authorization authorization: Mastodon.API.OAuth.Authorization
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.SearchResult>, Error> { ) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.SearchResult>, Error> {
let request = Mastodon.API.get( let request = Mastodon.API.get(
@ -49,7 +49,7 @@ extension Mastodon.API.Search {
} }
} }
extension Mastodon.API.Search { extension Mastodon.API.V2.Search {
public struct Query: Codable, GetQuery { public struct Query: Codable, GetQuery {
public init(q: String, public init(q: String,
@ -105,7 +105,7 @@ extension Mastodon.API.Search {
} }
} }
public extension Mastodon.API.Search { public extension Mastodon.API.V2.Search {
enum SearchType: String, Codable { enum SearchType: String, Codable {
case accounts case accounts
case hashtags case hashtags

View File

@ -0,0 +1,41 @@
//
// Mastodon+API+V2+Suggestions.swift
//
//
// Created by sxiaojian on 2021/4/20.
//
import Combine
import Foundation
extension Mastodon.API.V2.Suggestions {
static func suggestionsURL(domain: String) -> URL {
Mastodon.API.endpointV2URL(domain: domain).appendingPathComponent("suggestions")
}
/// Follow suggestions, No document for now
/// - Parameters:
/// - session: `URLSession`
/// - domain: Mastodon instance domain. e.g. "example.com"
/// - query: query
/// - authorization: User token.
/// - Returns: `AnyPublisher` contains `AccountsSuggestion` nested in the response
public static func get(
session: URLSession,
domain: String,
query: Mastodon.API.Suggestions.Query?,
authorization: Mastodon.API.OAuth.Authorization
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.V2.SuggestionAccount]>, Error> {
let request = Mastodon.API.get(
url: suggestionsURL(domain: domain),
query: query,
authorization: authorization
)
return session.dataTaskPublisher(for: request)
.tryMap { data, response in
let value = try Mastodon.API.decode(type: [Mastodon.Entity.V2.SuggestionAccount].self, from: data, response: response)
return Mastodon.Response.Content(value: value, response: response)
}
.eraseToAnyPublisher()
}
}

View File

@ -99,6 +99,7 @@ extension Mastodon.API {
} }
extension Mastodon.API { extension Mastodon.API {
public enum V2 { }
public enum Account { } public enum Account { }
public enum App { } public enum App { }
public enum CustomEmojis { } public enum CustomEmojis { }
@ -111,13 +112,17 @@ extension Mastodon.API {
public enum Reblog { } public enum Reblog { }
public enum Statuses { } public enum Statuses { }
public enum Timeline { } public enum Timeline { }
public enum Search { }
public enum Trends { } public enum Trends { }
public enum Suggestions { } public enum Suggestions { }
public enum Notifications { } public enum Notifications { }
public enum Subscriptions { } public enum Subscriptions { }
} }
extension Mastodon.API.V2 {
public enum Search { }
public enum Suggestions { }
}
extension Mastodon.API { extension Mastodon.API {
static func get( static func get(

View File

@ -0,0 +1,23 @@
//
// Mastodon+Entity+Suggestion.swift
//
//
// Created by sxiaojian on 2021/4/20.
//
import Foundation
extension Mastodon.Entity.V2 {
public struct SuggestionAccount: Codable {
public let source: String
public let account: Mastodon.Entity.Account
enum CodingKeys: String, CodingKey {
case source
case account
}
}
}