diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift index b43b67143..54acbf4fe 100644 --- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift +++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift @@ -84,7 +84,7 @@ final class HashtagTimelineViewModel: NSObject { guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { 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) .sink { _ in diff --git a/Mastodon/Scene/Search/SearchViewController.swift b/Mastodon/Scene/Search/SearchViewController.swift index 770fb1da7..357f142c8 100644 --- a/Mastodon/Scene/Search/SearchViewController.swift +++ b/Mastodon/Scene/Search/SearchViewController.swift @@ -217,11 +217,11 @@ extension SearchViewController: UISearchBarDelegate { func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) { switch selectedScope { case 0: - viewModel.searchScope.value = Mastodon.API.Search.SearchType.default + viewModel.searchScope.value = Mastodon.API.V2.Search.SearchType.default case 1: - viewModel.searchScope.value = Mastodon.API.Search.SearchType.accounts + viewModel.searchScope.value = Mastodon.API.V2.Search.SearchType.accounts case 2: - viewModel.searchScope.value = Mastodon.API.Search.SearchType.hashtags + viewModel.searchScope.value = Mastodon.API.V2.Search.SearchType.hashtags default: break } diff --git a/Mastodon/Scene/Search/SearchViewModel+LoadOldestState.swift b/Mastodon/Scene/Search/SearchViewModel+LoadOldestState.swift index b486df774..4fe68e47d 100644 --- a/Mastodon/Scene/Search/SearchViewModel+LoadOldestState.swift +++ b/Mastodon/Scene/Search/SearchViewModel+LoadOldestState.swift @@ -53,14 +53,14 @@ extension SearchViewModel.LoadOldestState { } var offset = 0 switch viewModel.searchScope.value { - case Mastodon.API.Search.SearchType.accounts: + case Mastodon.API.V2.Search.SearchType.accounts: offset = oldSearchResult.accounts.count - case Mastodon.API.Search.SearchType.hashtags: + case Mastodon.API.V2.Search.SearchType.hashtags: offset = oldSearchResult.hashtags.count default: 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, accountID: nil, maxID: nil, @@ -82,7 +82,7 @@ extension SearchViewModel.LoadOldestState { } } receiveValue: { result in switch viewModel.searchScope.value { - case Mastodon.API.Search.SearchType.accounts: + case Mastodon.API.V2.Search.SearchType.accounts: if result.value.accounts.isEmpty { stateMachine.enter(NoMore.self) } else { @@ -93,7 +93,7 @@ extension SearchViewModel.LoadOldestState { viewModel.searchResult.value = Mastodon.Entity.SearchResult(accounts: newAccounts, statuses: oldSearchResult.statuses, hashtags: oldSearchResult.hashtags) stateMachine.enter(Idle.self) } - case Mastodon.API.Search.SearchType.hashtags: + case Mastodon.API.V2.Search.SearchType.hashtags: if result.value.hashtags.isEmpty { stateMachine.enter(NoMore.self) } else { diff --git a/Mastodon/Scene/Search/SearchViewModel.swift b/Mastodon/Scene/Search/SearchViewModel.swift index 27c322c88..afdc1a9f4 100644 --- a/Mastodon/Scene/Search/SearchViewModel.swift +++ b/Mastodon/Scene/Search/SearchViewModel.swift @@ -26,7 +26,7 @@ final class SearchViewModel: NSObject { // output let searchText = CurrentValueSubject("") - let searchScope = CurrentValueSubject(Mastodon.API.Search.SearchType.default) + let searchScope = CurrentValueSubject(Mastodon.API.V2.Search.SearchType.default) let isSearching = CurrentValueSubject(false) @@ -86,7 +86,7 @@ final class SearchViewModel: NSObject { } .flatMap { (text, scope) -> AnyPublisher, Error> in - let query = Mastodon.API.Search.Query(q: text, + let query = Mastodon.API.V2.Search.Query(q: text, type: scope, accountID: nil, maxID: nil, @@ -130,8 +130,8 @@ final class SearchViewModel: NSObject { snapshot.appendSections([.mixed]) searchHistories.forEach { searchHistory in - let containsAccount = scope == Mastodon.API.Search.SearchType.accounts || scope == Mastodon.API.Search.SearchType.default - let containsHashTag = scope == Mastodon.API.Search.SearchType.hashtags || 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.V2.Search.SearchType.hashtags || scope == Mastodon.API.V2.Search.SearchType.default if let mastodonUser = searchHistory.account, containsAccount { let item = SearchResultItem.accountObjectID(accountObjectID: mastodonUser.objectID) snapshot.appendItems([item], toSection: .mixed) @@ -186,7 +186,7 @@ final class SearchViewModel: NSObject { snapshot.appendSections([.account]) let items = accounts.compactMap { SearchResultItem.account(account: $0) } 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) } } @@ -194,7 +194,7 @@ final class SearchViewModel: NSObject { snapshot.appendSections([.hashtag]) let items = tags.compactMap { SearchResultItem.hashtag(tag: $0) } 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) } } @@ -245,7 +245,7 @@ final class SearchViewModel: NSObject { } } receiveValue: { [weak self] accounts in 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 userFetchRequest.predicate = MastodonUser.predicate(domain: activeMastodonAuthenticationBox.domain, ids: ids) let mastodonUsers: [MastodonUser]? = { diff --git a/Mastodon/Service/APIService/APIService+Recommend.swift b/Mastodon/Service/APIService/APIService+Recommend.swift index 1c58fc575..134d43fab 100644 --- a/Mastodon/Service/APIService/APIService+Recommend.swift +++ b/Mastodon/Service/APIService/APIService+Recommend.swift @@ -17,21 +17,22 @@ extension APIService { domain: String, query: Mastodon.API.Suggestions.Query?, mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox - ) -> AnyPublisher, Error> { + ) -> AnyPublisher, Error> { let authorization = mastodonAuthenticationBox.userAuthorization - return Mastodon.API.Suggestions.get(session: session, domain: domain, query: query, authorization: authorization) - .flatMap { response -> AnyPublisher, Error> in + return Mastodon.API.V2.Suggestions.get(session: session, domain: domain, query: query, authorization: authorization) + .flatMap { response -> AnyPublisher, Error> in let log = OSLog.api 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 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) } } .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 { case .success: return response diff --git a/Mastodon/Service/APIService/APIService+Search.swift b/Mastodon/Service/APIService/APIService+Search.swift index ba40aa5de..986e3d931 100644 --- a/Mastodon/Service/APIService/APIService+Search.swift +++ b/Mastodon/Service/APIService/APIService+Search.swift @@ -13,11 +13,11 @@ extension APIService { func search( domain: String, - query: Mastodon.API.Search.Query, + query: Mastodon.API.V2.Search.Query, mastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox ) -> AnyPublisher, Error> { 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) } } diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Search.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+V2+Search.swift similarity index 96% rename from MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Search.swift rename to MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+V2+Search.swift index be8bb2607..c0a687f17 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Search.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+V2+Search.swift @@ -8,7 +8,7 @@ import Combine import Foundation -extension Mastodon.API.Search { +extension Mastodon.API.V2.Search { static func searchURL(domain: String) -> URL { Mastodon.API.endpointV2URL(domain: domain).appendingPathComponent("search") } @@ -32,7 +32,7 @@ extension Mastodon.API.Search { public static func search( session: URLSession, domain: String, - query: Mastodon.API.Search.Query, + query: Mastodon.API.V2.Search.Query, authorization: Mastodon.API.OAuth.Authorization ) -> AnyPublisher, Error> { 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 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 { case accounts case hashtags diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+V2+Suggestions.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+V2+Suggestions.swift new file mode 100644 index 000000000..9e6876b41 --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+V2+Suggestions.swift @@ -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, 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() + } +} diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift index 1a4496ed3..921cc9ed3 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API.swift @@ -99,6 +99,7 @@ extension Mastodon.API { } extension Mastodon.API { + public enum V2 { } public enum Account { } public enum App { } public enum CustomEmojis { } @@ -111,13 +112,17 @@ extension Mastodon.API { public enum Reblog { } public enum Statuses { } public enum Timeline { } - public enum Search { } public enum Trends { } public enum Suggestions { } public enum Notifications { } public enum Subscriptions { } } +extension Mastodon.API.V2 { + public enum Search { } + public enum Suggestions { } +} + extension Mastodon.API { static func get( diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Suggestion.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Suggestion.swift new file mode 100644 index 000000000..98d67d5fa --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Suggestion.swift @@ -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 + } + } +}