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 */; };
|
DB66728C25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */; };
|
||||||
DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729525F9F91600D60309 /* ComposeStatusSection.swift */; };
|
DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729525F9F91600D60309 /* ComposeStatusSection.swift */; };
|
||||||
DB66729C25F9F91F00D60309 /* ComposeStatusItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729B25F9F91F00D60309 /* ComposeStatusItem.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 */; };
|
DB68045B2636DC6A00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
|
||||||
DB6804662636DC9000430867 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
|
DB6804662636DC9000430867 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
|
||||||
DB68046C2636DC9E00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
DB6804812637CD4C00430867 /* AppShared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppShared.h; sourceTree = "<group>"; };
|
||||||
|
@ -2326,6 +2328,7 @@
|
||||||
2D34D9DA261494120081BFC0 /* APIService+Search.swift */,
|
2D34D9DA261494120081BFC0 /* APIService+Search.swift */,
|
||||||
0F202212261351F5000C64BF /* APIService+HashtagTimeline.swift */,
|
0F202212261351F5000C64BF /* APIService+HashtagTimeline.swift */,
|
||||||
DB6B74F9272FC2B500C70B6E /* APIService+Follower.swift */,
|
DB6B74F9272FC2B500C70B6E /* APIService+Follower.swift */,
|
||||||
|
DB67D08327312970006A36CF /* APIService+Following.swift */,
|
||||||
DBCC3B9426157E6E0045B23D /* APIService+Relationship.swift */,
|
DBCC3B9426157E6E0045B23D /* APIService+Relationship.swift */,
|
||||||
5B24BBE1262DB19100A9381B /* APIService+Report.swift */,
|
5B24BBE1262DB19100A9381B /* APIService+Report.swift */,
|
||||||
DBAE3F932616E28B004B8251 /* APIService+Follow.swift */,
|
DBAE3F932616E28B004B8251 /* APIService+Follow.swift */,
|
||||||
|
@ -4125,6 +4128,7 @@
|
||||||
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */,
|
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */,
|
||||||
5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */,
|
5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */,
|
||||||
DB73BF43271192BB00781945 /* InstanceService.swift in Sources */,
|
DB73BF43271192BB00781945 /* InstanceService.swift in Sources */,
|
||||||
|
DB67D08427312970006A36CF /* APIService+Following.swift in Sources */,
|
||||||
DBA9443A265CC0FC00C537E1 /* Fields.swift in Sources */,
|
DBA9443A265CC0FC00C537E1 /* Fields.swift in Sources */,
|
||||||
2DE0FAC12615F04D00CDF649 /* RecommendHashTagSection.swift in Sources */,
|
2DE0FAC12615F04D00CDF649 /* RecommendHashTagSection.swift in Sources */,
|
||||||
DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */,
|
DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */,
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
<key>AppShared.xcscheme_^#shared#^_</key>
|
<key>AppShared.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>36</integer>
|
<integer>37</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>35</integer>
|
<integer>36</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>38</integer>
|
<integer>35</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -117,7 +117,7 @@
|
||||||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>37</integer>
|
<integer>38</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
|
|
@ -155,7 +155,7 @@ extension FollowerListViewModel.State {
|
||||||
|
|
||||||
let maxID = response.link?.maxID
|
let maxID = response.link?.maxID
|
||||||
|
|
||||||
if maxID != nil {
|
if hasNewAppend && maxID != nil {
|
||||||
stateMachine.enter(Idle.self)
|
stateMachine.enter(Idle.self)
|
||||||
} else {
|
} else {
|
||||||
stateMachine.enter(NoMore.self)
|
stateMachine.enter(NoMore.self)
|
||||||
|
|
|
@ -128,7 +128,7 @@ extension FollowingListViewModel.State {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.context.apiService.followers(
|
viewModel.context.apiService.following(
|
||||||
userID: userID,
|
userID: userID,
|
||||||
maxID: maxID,
|
maxID: maxID,
|
||||||
authorizationBox: activeMastodonAuthenticationBox
|
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