feat: implement following list
This commit is contained in:
parent
07eab320f4
commit
30b2a35b84
|
@ -320,6 +320,7 @@
|
|||
DB66728C25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */; };
|
||||
DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729525F9F91600D60309 /* ComposeStatusSection.swift */; };
|
||||
DB66729C25F9F91F00D60309 /* ComposeStatusItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */; };
|
||||
DB67D08427312970006A36CF /* APIService+Following.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB67D08327312970006A36CF /* APIService+Following.swift */; };
|
||||
DB68045B2636DC6A00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
|
||||
DB6804662636DC9000430867 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
|
||||
DB68046C2636DC9E00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
|
||||
|
@ -1139,6 +1140,7 @@
|
|||
DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ComposeViewModel+DataSource.swift"; sourceTree = "<group>"; };
|
||||
DB66729525F9F91600D60309 /* ComposeStatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusSection.swift; sourceTree = "<group>"; };
|
||||
DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusItem.swift; sourceTree = "<group>"; };
|
||||
DB67D08327312970006A36CF /* APIService+Following.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Following.swift"; sourceTree = "<group>"; };
|
||||
DB68045A2636DC6A00430867 /* MastodonNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonNotification.swift; sourceTree = "<group>"; };
|
||||
DB68047F2637CD4C00430867 /* AppShared.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppShared.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DB6804812637CD4C00430867 /* AppShared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppShared.h; sourceTree = "<group>"; };
|
||||
|
@ -2326,6 +2328,7 @@
|
|||
2D34D9DA261494120081BFC0 /* APIService+Search.swift */,
|
||||
0F202212261351F5000C64BF /* APIService+HashtagTimeline.swift */,
|
||||
DB6B74F9272FC2B500C70B6E /* APIService+Follower.swift */,
|
||||
DB67D08327312970006A36CF /* APIService+Following.swift */,
|
||||
DBCC3B9426157E6E0045B23D /* APIService+Relationship.swift */,
|
||||
5B24BBE1262DB19100A9381B /* APIService+Report.swift */,
|
||||
DBAE3F932616E28B004B8251 /* APIService+Follow.swift */,
|
||||
|
@ -4125,6 +4128,7 @@
|
|||
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */,
|
||||
5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */,
|
||||
DB73BF43271192BB00781945 /* InstanceService.swift in Sources */,
|
||||
DB67D08427312970006A36CF /* APIService+Following.swift in Sources */,
|
||||
DBA9443A265CC0FC00C537E1 /* Fields.swift in Sources */,
|
||||
2DE0FAC12615F04D00CDF649 /* RecommendHashTagSection.swift in Sources */,
|
||||
DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */,
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<key>AppShared.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>36</integer>
|
||||
<integer>37</integer>
|
||||
</dict>
|
||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>35</integer>
|
||||
<integer>36</integer>
|
||||
</dict>
|
||||
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -97,7 +97,7 @@
|
|||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>38</integer>
|
||||
<integer>35</integer>
|
||||
</dict>
|
||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -117,7 +117,7 @@
|
|||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>37</integer>
|
||||
<integer>38</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
@ -155,7 +155,7 @@ extension FollowerListViewModel.State {
|
|||
|
||||
let maxID = response.link?.maxID
|
||||
|
||||
if maxID != nil {
|
||||
if hasNewAppend && maxID != nil {
|
||||
stateMachine.enter(Idle.self)
|
||||
} else {
|
||||
stateMachine.enter(NoMore.self)
|
||||
|
|
|
@ -128,7 +128,7 @@ extension FollowingListViewModel.State {
|
|||
return
|
||||
}
|
||||
|
||||
viewModel.context.apiService.followers(
|
||||
viewModel.context.apiService.following(
|
||||
userID: userID,
|
||||
maxID: maxID,
|
||||
authorizationBox: activeMastodonAuthenticationBox
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// APIService+Following.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-2.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import CommonOSLog
|
||||
import MastodonSDK
|
||||
|
||||
extension APIService {
|
||||
|
||||
func following(
|
||||
userID: Mastodon.Entity.Account.ID,
|
||||
maxID: String?,
|
||||
authorizationBox: MastodonAuthenticationBox
|
||||
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Account]>, Error> {
|
||||
let domain = authorizationBox.domain
|
||||
let authorization = authorizationBox.userAuthorization
|
||||
let requestMastodonUserID = authorizationBox.userID
|
||||
|
||||
let query = Mastodon.API.Account.FollowingQuery(
|
||||
maxID: maxID,
|
||||
limit: nil
|
||||
)
|
||||
return Mastodon.API.Account.following(
|
||||
session: session,
|
||||
domain: domain,
|
||||
userID: userID,
|
||||
query: query,
|
||||
authorization: authorization
|
||||
)
|
||||
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Account]>, Error> in
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
return managedObjectContext.performChanges {
|
||||
let requestMastodonUserRequest = MastodonUser.sortedFetchRequest
|
||||
requestMastodonUserRequest.predicate = MastodonUser.predicate(domain: domain, id: requestMastodonUserID)
|
||||
requestMastodonUserRequest.fetchLimit = 1
|
||||
guard let requestMastodonUser = managedObjectContext.safeFetch(requestMastodonUserRequest).first else { return }
|
||||
|
||||
for entity in response.value {
|
||||
_ = APIService.CoreData.createOrMergeMastodonUser(
|
||||
into: managedObjectContext,
|
||||
for: requestMastodonUser,
|
||||
in: domain,
|
||||
entity: entity,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate,
|
||||
log: .api
|
||||
)
|
||||
}
|
||||
}
|
||||
.tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.Account]> in
|
||||
switch result {
|
||||
case .success:
|
||||
return response
|
||||
case .failure(let error):
|
||||
throw error
|
||||
}
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Mastodon+API+Account+Following.swift
|
||||
//
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-2.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
extension Mastodon.API.Account {
|
||||
|
||||
static func followingEndpointURL(domain: String, userID: Mastodon.Entity.Account.ID) -> URL {
|
||||
return Mastodon.API.endpointURL(domain: domain)
|
||||
.appendingPathComponent("accounts")
|
||||
.appendingPathComponent(userID)
|
||||
.appendingPathComponent("following")
|
||||
}
|
||||
|
||||
/// Following
|
||||
///
|
||||
/// Accounts which the given account is following, if network is not hidden by the account owner.
|
||||
///
|
||||
/// - Since: 0.0.0
|
||||
/// - Version: 3.4.1
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/methods/accounts/)
|
||||
/// - Parameters:
|
||||
/// - session: `URLSession`
|
||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||
/// - userID: ID of the account in the database
|
||||
/// - authorization: User token
|
||||
/// - Returns: `AnyPublisher` contains `[Account]` nested in the response
|
||||
public static func following(
|
||||
session: URLSession,
|
||||
domain: String,
|
||||
userID: Mastodon.Entity.Account.ID,
|
||||
query: FollowingQuery,
|
||||
authorization: Mastodon.API.OAuth.Authorization
|
||||
) -> AnyPublisher<Mastodon.Response.Content<[Mastodon.Entity.Account]>, Error> {
|
||||
let request = Mastodon.API.get(
|
||||
url: followingEndpointURL(domain: domain, userID: userID),
|
||||
query: query,
|
||||
authorization: authorization
|
||||
)
|
||||
return session.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
let value = try Mastodon.API.decode(type: [Mastodon.Entity.Account].self, from: data, response: response)
|
||||
return Mastodon.Response.Content(value: value, response: response)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
public struct FollowingQuery: Codable, GetQuery {
|
||||
|
||||
public let maxID: String?
|
||||
public let limit: Int? // default 40
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case maxID = "max_id"
|
||||
case limit
|
||||
}
|
||||
|
||||
public init(
|
||||
maxID: String?,
|
||||
limit: Int?
|
||||
) {
|
||||
self.maxID = maxID
|
||||
self.limit = limit
|
||||
}
|
||||
|
||||
var queryItems: [URLQueryItem]? {
|
||||
var items: [URLQueryItem] = []
|
||||
maxID.flatMap { items.append(URLQueryItem(name: "max_id", value: $0)) }
|
||||
limit.flatMap { items.append(URLQueryItem(name: "limit", value: String($0))) }
|
||||
guard !items.isEmpty else { return nil }
|
||||
return items
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue