1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import Foundation
// Response types for profile-related XRPC queries.
struct GetFollowersResponse: Codable, Sendable {
var totalCount: Int?
var items: [FollowerItem]?
var cursor: String?
}
struct GetFollowingResponse: Codable, Sendable {
var totalCount: Int?
var items: [FollowingItem]?
var cursor: String?
}
struct GetKnownFollowersResponse: Codable, Sendable {
var items: [FollowerItem]?
}
struct GetGalleryFavoritesResponse: Codable, Sendable {
var totalCount: Int?
var items: [FavoriteItem]?
var cursor: String?
}
struct GetSuggestedFollowsResponse: Codable, Sendable {
var items: [SuggestedItem]?
}
struct SearchProfilesResponse: Codable, Sendable {
var items: [ProfileSearchResult]?
var cursor: String?
}
struct FollowerItem: Codable, Sendable, Identifiable {
let did: String
var handle: String?
var displayName: String?
var description: String?
var avatar: String?
var viewer: ActorViewerState?
var id: String {
did
}
}
struct FollowingItem: Codable, Sendable, Identifiable {
let did: String
var handle: String?
var displayName: String?
var description: String?
var avatar: String?
var viewer: ActorViewerState?
var id: String {
did
}
}
struct FavoriteItem: Codable, Sendable, Identifiable {
let did: String
var handle: String?
var displayName: String?
var description: String?
var avatar: String?
var viewer: ActorViewerState?
var id: String {
did
}
}
struct SuggestedItem: Codable, Sendable, Identifiable {
let did: String
var handle: String?
var displayName: String?
var description: String?
var avatar: String?
var followersCount: Int?
var id: String {
did
}
}
struct ProfileSearchResult: Codable, Sendable, Identifiable {
let did: String
var handle: String?
var displayName: String?
var description: String?
var avatar: String?
var id: String {
did
}
}
// MARK: - Convenience Extensions
extension XRPCClient {
func getActorProfile(actor: String, auth: AuthContext? = nil) async throws -> GrainProfileDetailed {
try await query("social.grain.actor.getProfile", params: ["actor": actor], auth: auth, as: GrainProfileDetailed.self)
}
func getFollowers(actor: String, limit: Int = 50, cursor: String? = nil, auth: AuthContext? = nil) async throws -> GetFollowersResponse {
var params = ["actor": actor, "limit": String(limit)]
if let cursor {
params["cursor"] = cursor
}
return try await query("social.grain.graph.getFollowers", params: params, auth: auth, as: GetFollowersResponse.self)
}
func getFollowing(actor: String, limit: Int = 50, cursor: String? = nil, auth: AuthContext? = nil) async throws -> GetFollowingResponse {
var params = ["actor": actor, "limit": String(limit)]
if let cursor {
params["cursor"] = cursor
}
return try await query("social.grain.graph.getFollowing", params: params, auth: auth, as: GetFollowingResponse.self)
}
/// The accounts the signed-in viewer follows that follow `actor`.
func getKnownFollowers(actor: String, auth: AuthContext? = nil) async throws -> GetKnownFollowersResponse {
try await query("social.grain.graph.getKnownFollowers", params: ["actor": actor], auth: auth, as: GetKnownFollowersResponse.self)
}
func getGalleryFavorites(gallery: String, limit: Int = 50, cursor: String? = nil, auth: AuthContext? = nil) async throws -> GetGalleryFavoritesResponse {
var params = ["gallery": gallery, "limit": String(limit)]
if let cursor {
params["cursor"] = cursor
}
return try await query("social.grain.gallery.getFavorites", params: params, auth: auth, as: GetGalleryFavoritesResponse.self)
}
func getSuggestedFollows(actor: String, limit: Int = 10, auth: AuthContext? = nil) async throws -> GetSuggestedFollowsResponse {
try await query("social.grain.actor.getSuggestedFollows", params: ["actor": actor, "limit": String(limit)], auth: auth, as: GetSuggestedFollowsResponse.self)
}
func searchProfiles(query queryString: String, limit: Int = 30, cursor: String? = nil, auth: AuthContext? = nil) async throws -> SearchProfilesResponse {
var params = ["q": queryString, "limit": String(limit)]
if let cursor {
params["cursor"] = cursor
}
return try await query("social.grain.actor.searchActors", params: params, auth: auth, as: SearchProfilesResponse.self)
}
}