+42
parakeet/src/xrpc/app_bsky/graph/lists.rs
+42
parakeet/src/xrpc/app_bsky/graph/lists.rs
···
181
181
lists: mutes,
182
182
}))
183
183
}
184
+
185
+
pub async fn get_list_blocks(
186
+
State(state): State<GlobalState>,
187
+
AtpAcceptLabelers(labelers): AtpAcceptLabelers,
188
+
auth: AtpAuth,
189
+
Query(query): Query<CursorQuery>,
190
+
) -> XrpcResult<Json<GetListMutesRes>> {
191
+
let mut conn = state.pool.get().await?;
192
+
let did = auth.0.clone();
193
+
let hyd = StatefulHydrator::new(&state.dataloaders, &state.cdn, &labelers, Some(auth));
194
+
195
+
let limit = query.limit.unwrap_or(50).clamp(1, 100);
196
+
197
+
let mut blocks_query = schema::list_blocks::table
198
+
.select((schema::list_blocks::created_at, schema::list_blocks::list_uri))
199
+
.filter(schema::list_blocks::did.eq(did))
200
+
.into_boxed();
201
+
202
+
if let Some(cursor) = datetime_cursor(query.cursor.as_ref()) {
203
+
blocks_query = blocks_query.filter(schema::list_blocks::created_at.lt(cursor));
204
+
}
205
+
206
+
let results = blocks_query
207
+
.order(schema::list_blocks::created_at.desc())
208
+
.limit(limit as i64)
209
+
.load::<(chrono::DateTime<chrono::Utc>, String)>(&mut conn)
210
+
.await?;
211
+
212
+
let cursor = results
213
+
.last()
214
+
.map(|(last, _)| last.timestamp_millis().to_string());
215
+
216
+
let uris = results.iter().map(|(_, uri)| uri.clone()).collect();
217
+
218
+
let lists = hyd.hydrate_lists(uris).await;
219
+
let lists = lists.into_values().collect::<Vec<_>>();
220
+
221
+
Ok(Json(GetListMutesRes {
222
+
cursor,
223
+
lists,
224
+
}))
225
+
}
+47
-1
parakeet/src/xrpc/app_bsky/graph/relations.rs
+47
-1
parakeet/src/xrpc/app_bsky/graph/relations.rs
···
1
1
use crate::hydration::StatefulHydrator;
2
2
use crate::xrpc::error::{Error, XrpcResult};
3
3
use crate::xrpc::extract::{AtpAcceptLabelers, AtpAuth};
4
-
use crate::xrpc::{datetime_cursor, get_actor_did, ActorWithCursorQuery};
4
+
use crate::xrpc::{datetime_cursor, get_actor_did, ActorWithCursorQuery, CursorQuery};
5
5
use crate::GlobalState;
6
6
use axum::extract::{Query, State};
7
7
use axum::Json;
···
10
10
use lexica::app_bsky::actor::ProfileView;
11
11
use parakeet_db::schema;
12
12
use serde::Serialize;
13
+
14
+
#[derive(Debug, Serialize)]
15
+
pub struct GetBlocksRes {
16
+
#[serde(skip_serializing_if = "Option::is_none")]
17
+
cursor: Option<String>,
18
+
blocks: Vec<ProfileView>,
19
+
}
20
+
21
+
pub async fn get_blocks(
22
+
State(state): State<GlobalState>,
23
+
AtpAcceptLabelers(labelers): AtpAcceptLabelers,
24
+
auth: AtpAuth,
25
+
Query(query): Query<CursorQuery>,
26
+
) -> XrpcResult<Json<GetBlocksRes>> {
27
+
let mut conn = state.pool.get().await?;
28
+
let did = auth.0.clone();
29
+
let hyd = StatefulHydrator::new(&state.dataloaders, &state.cdn, &labelers, Some(auth));
30
+
31
+
let limit = query.limit.unwrap_or(50).clamp(1, 100);
32
+
33
+
let mut blocked_query = schema::blocks::table
34
+
.select((schema::blocks::created_at, schema::blocks::subject))
35
+
.filter(schema::blocks::did.eq(did))
36
+
.into_boxed();
37
+
38
+
if let Some(cursor) = datetime_cursor(query.cursor.as_ref()) {
39
+
blocked_query = blocked_query.filter(schema::blocks::created_at.lt(cursor));
40
+
}
41
+
42
+
let results = blocked_query
43
+
.order(schema::blocks::created_at.desc())
44
+
.limit(limit as i64)
45
+
.load::<(chrono::DateTime<chrono::Utc>, String)>(&mut conn)
46
+
.await?;
47
+
48
+
let cursor = results
49
+
.last()
50
+
.map(|(last, _)| last.timestamp_millis().to_string());
51
+
52
+
let dids = results.iter().map(|(_, did)| did.clone()).collect();
53
+
54
+
let profiles = hyd.hydrate_profiles(dids).await;
55
+
let blocks = profiles.into_values().collect::<Vec<_>>();
56
+
57
+
Ok(Json(GetBlocksRes { cursor, blocks }))
58
+
}
13
59
14
60
#[derive(Debug, Serialize)]
15
61
pub struct AppBskyGraphGetFollowersRes {
+2
-2
parakeet/src/xrpc/app_bsky/mod.rs
+2
-2
parakeet/src/xrpc/app_bsky/mod.rs
···
30
30
// TODO: app.bsky.feed.getTimeline (complicated)
31
31
// TODO: app.bsky.feed.searchPosts (search)
32
32
.route("/app.bsky.graph.getActorStarterPacks", get(graph::starter_packs::get_actor_starter_packs))
33
-
// TODO: app.bsky.graph.getBlocks
33
+
.route("/app.bsky.graph.getBlocks", get(graph::relations::get_blocks))
34
34
.route("/app.bsky.graph.getFollowers", get(graph::relations::get_followers))
35
35
.route("/app.bsky.graph.getFollows", get(graph::relations::get_follows))
36
36
// TODO: app.bsky.graph.getKnownFollowers
37
37
.route("/app.bsky.graph.getList", get(graph::lists::get_list))
38
-
// TODO: app.bsky.graph.getListBlocks
38
+
.route("/app.bsky.graph.getListBlocks", get(graph::lists::get_list_blocks))
39
39
.route("/app.bsky.graph.getListMutes", get(graph::lists::get_list_mutes))
40
40
.route("/app.bsky.graph.getLists", get(graph::lists::get_lists))
41
41
.route("/app.bsky.graph.getMutes", get(graph::mutes::get_mutes))