···
use lexica::app_bsky::actor::ProfileViewBasic;
use lexica::app_bsky::embed::Embed;
use lexica::app_bsky::feed::{
6
-
BlockedAuthor, FeedViewPost, PostView, PostViewerState, ReplyRef, ReplyRefPost, ThreadgateView,
6
+
BlockedAuthor, FeedReasonRepost, FeedViewPost, FeedViewPostReason, PostView, PostViewerState,
7
+
ReplyRef, ReplyRefPost, ThreadgateView,
use lexica::app_bsky::graph::ListViewBasic;
use lexica::app_bsky::RecordStats;
···
36
+
type HydratePostsRet = (
41
+
Option<ThreadgateView>,
42
+
Option<PostViewerState>,
37
-
author: ProfileViewBasic,
38
-
labels: Vec<models::Label>,
39
-
embed: Option<Embed>,
40
-
threadgate: Option<ThreadgateView>,
41
-
viewer: Option<PostViewerState>,
42
-
stats: Option<PostStats>,
47
+
(post, author, labels, embed, threadgate, viewer, stats): HydratePostsRet,
.map(|stats| RecordStats {
···
let threadgate = self.hydrate_threadgate(threadgate).await;
let labels = self.get_label(&post.at_uri).await;
138
-
Some(build_postview(
143
+
Some(build_postview((
post, author, labels, embed, threadgate, viewer, stats,
143
-
pub async fn hydrate_posts(&self, posts: Vec<String>) -> HashMap<String, PostView> {
148
+
async fn hydrate_posts_inner(&self, posts: Vec<String>) -> HashMap<String, HydratePostsRet> {
let stats = self.loaders.post_stats.load_many(posts.clone()).await;
let posts = self.loaders.posts.load_many(posts).await;
···
.unzip::<_, _, Vec<_>, Vec<_>>();
let authors = self.hydrate_profiles_basic(authors).await;
153
-
let post_labels = self.get_label_many(&post_uris).await;
154
-
let viewer_data = self.get_post_viewer_states(&post_uris).await;
158
+
let mut post_labels = self.get_label_many(&post_uris).await;
159
+
let mut viewer_data = self.get_post_viewer_states(&post_uris).await;
···
let threadgates = self.hydrate_threadgates(threadgates).await;
162
-
let embeds = self.hydrate_embeds(post_uris).await;
167
+
let mut embeds = self.hydrate_embeds(post_uris).await;
.filter_map(|(uri, (post, threadgate))| {
167
-
let author = authors.get(&post.did)?;
168
-
let embed = embeds.get(&uri).cloned();
172
+
let author = authors.get(&post.did)?.clone();
173
+
let embed = embeds.remove(&uri);
let threadgate = threadgate.and_then(|tg| threadgates.get(&tg.at_uri).cloned());
170
-
let labels = post_labels.get(&uri).cloned().unwrap_or_default();
175
+
let labels = post_labels.remove(&uri).unwrap_or_default();
let stats = stats.get(&uri).cloned();
172
-
let viewer = viewer_data.get(&uri).cloned();
177
+
let viewer = viewer_data.remove(&uri);
181
+
(post, author, labels, embed, threadgate, viewer, stats),
187
+
pub async fn hydrate_posts(&self, posts: Vec<String>) -> HashMap<String, PostView> {
188
+
self.hydrate_posts_inner(posts)
191
+
.map(|(uri, data)| (uri, build_postview(data)))
pub async fn hydrate_feed_posts(
192
-
posts: Vec<String>,
197
+
posts: Vec<RawFeedItem>,
author_threads_only: bool,
194
-
) -> HashMap<String, FeedViewPost> {
195
-
let stats = self.loaders.post_stats.load_many(posts.clone()).await;
196
-
let posts = self.loaders.posts.load_many(posts).await;
198
-
let (authors, post_uris) = posts
200
-
.map(|(post, _)| (post.did.clone(), post.at_uri.clone()))
201
-
.unzip::<_, _, Vec<_>, Vec<_>>();
202
-
let authors = self.hydrate_profiles_basic(authors).await;
204
-
let post_labels = self.get_label_many(&post_uris).await;
205
-
let viewer_data = self.get_post_viewer_states(&post_uris).await;
206
-
let embeds = self.hydrate_embeds(post_uris.clone()).await;
199
+
) -> Vec<FeedViewPost> {
200
+
let post_uris = posts
202
+
.map(|item| item.post_uri().to_string())
203
+
.collect::<Vec<_>>();
204
+
let mut posts_hyd = self.hydrate_posts_inner(post_uris).await;
// we shouldn't show the parent when the post violates a threadgate.
209
-
let reply_refs = posts
207
+
let reply_refs = posts_hyd
211
-
.filter(|(post, _)| !post.violates_threadgate)
212
-
.flat_map(|(post, _)| [post.parent_uri.clone(), post.root_uri.clone()])
209
+
.filter(|(post, ..)| !post.violates_threadgate)
210
+
.flat_map(|(post, ..)| [post.parent_uri.clone(), post.root_uri.clone()])
let reply_posts = self.hydrate_posts(reply_refs).await;
218
-
// hydrate all the posts.
219
-
let mut posts = posts
221
-
.filter_map(|(post_uri, (raw, _))| {
222
-
let root = raw.root_uri.clone();
223
-
let parent = raw.parent_uri.clone();
225
-
let author = authors.get(&raw.did)?;
226
-
let embed = embeds.get(&post_uri).cloned();
227
-
let labels = post_labels.get(&post_uri).cloned().unwrap_or_default();
228
-
let stats = stats.get(&post_uri).cloned();
229
-
let viewer = viewer_data.get(&post_uri).cloned();
231
-
build_postview(raw, author.to_owned(), labels, embed, None, viewer, stats);
233
-
Some((post_uri, (post, root, parent)))
235
-
.collect::<HashMap<_, _>>();
215
+
let repost_profiles = posts
217
+
.filter_map(|item| item.repost_by())
218
+
.collect::<Vec<_>>();
219
+
let profiles_hydrated = self.hydrate_profiles_basic(repost_profiles).await;
239
-
.filter_map(|post_uri| {
240
-
let item = if author_threads_only {
241
-
compile_feed_authors_threads_only(&post_uri, &mut posts)?
223
+
.filter_map(|item| {
224
+
let post = posts_hyd.remove(item.post_uri())?;
225
+
let context = item.context();
227
+
let reply = if let RawFeedItem::Post { .. } = item {
228
+
let root_uri = post.0.root_uri.as_ref();
229
+
let parent_uri = post.0.parent_uri.as_ref();
231
+
let (root, parent) = if author_threads_only {
232
+
if root_uri.is_some() && parent_uri.is_some() {
233
+
let root = root_uri.and_then(|uri| posts_hyd.get(uri))?;
234
+
let parent = parent_uri.and_then(|uri| posts_hyd.get(uri))?;
236
+
let root = build_postview(root.clone());
237
+
let parent = build_postview(parent.clone());
239
+
(Some(root), Some(parent))
244
+
let root = root_uri.and_then(|uri| reply_posts.get(uri)).cloned();
245
+
let parent = parent_uri.and_then(|uri| reply_posts.get(uri)).cloned();
250
+
if root_uri.is_some() || parent_uri.is_some() {
252
+
root: root.map(postview_to_replyref).unwrap_or(
253
+
ReplyRefPost::NotFound {
254
+
uri: root_uri.unwrap().to_owned(),
258
+
parent: parent.map(postview_to_replyref).unwrap_or(
259
+
ReplyRefPost::NotFound {
260
+
uri: parent_uri.unwrap().to_owned(),
264
+
grandparent_author: None,
243
-
compile_feed(&post_uri, &mut posts, &reply_posts)?
246
-
Some((post_uri, item))
273
+
let reason = match item {
274
+
RawFeedItem::Repost { uri, by, at, .. } => {
275
+
Some(FeedViewPostReason::Repost(FeedReasonRepost {
276
+
by: profiles_hydrated.get(&by).cloned()?,
282
+
RawFeedItem::Pin { .. } => Some(FeedViewPostReason::Pin),
286
+
let post = build_postview(post);
288
+
Some(FeedViewPost {
292
+
feed_context: context,
···
291
-
type FeedViewPartData = (PostView, Option<String>, Option<String>);
293
-
// this is the 'normal' one that runs in most places
296
-
posts: &mut HashMap<String, FeedViewPartData>,
297
-
reply_posts: &HashMap<String, PostView>,
298
-
) -> Option<FeedViewPost> {
299
-
let (post, root_uri, parent_uri) = posts.remove(uri)?;
301
-
let root = root_uri.as_ref().and_then(|uri| reply_posts.get(uri));
302
-
let parent = parent_uri.as_ref().and_then(|uri| reply_posts.get(uri));
304
-
let reply = if parent_uri.is_some() && root_uri.is_some() {
308
-
.map(postview_to_replyref)
309
-
.unwrap_or(ReplyRefPost::NotFound {
310
-
uri: root_uri.as_ref().unwrap().clone(),
315
-
.map(postview_to_replyref)
316
-
.unwrap_or(ReplyRefPost::NotFound {
317
-
uri: parent_uri.as_ref().unwrap().clone(),
320
-
grandparent_author: None,
326
-
Some(FeedViewPost {
330
-
feed_context: None,
339
+
pub enum RawFeedItem {
342
+
context: Option<String>,
346
+
context: Option<String>,
352
+
at: chrono::DateTime<chrono::Utc>,
353
+
context: Option<String>,
334
-
// and this one runs in getAuthorFeed when filter=PostsAndAuthorThreads
335
-
fn compile_feed_authors_threads_only(
337
-
posts: &mut HashMap<String, FeedViewPartData>,
338
-
) -> Option<FeedViewPost> {
339
-
let (post, root_uri, parent_uri) = posts.get(uri)?.clone();
341
-
let root = root_uri.as_ref().and_then(|root| posts.get(root));
342
-
let parent = parent_uri.as_ref().and_then(|parent| posts.get(parent));
344
-
let reply = if parent_uri.is_some() && root_uri.is_some() {
348
-
.map(|(post, _, _)| postview_to_replyref(post))?,
351
-
.map(|(post, _, _)| postview_to_replyref(post))?,
352
-
grandparent_author: None,
358
-
Some(FeedViewPost {
362
-
feed_context: None,
358
+
fn post_uri(&self) -> &str {
360
+
RawFeedItem::Pin { uri, .. } => uri,
361
+
RawFeedItem::Post { uri, .. } => uri,
362
+
RawFeedItem::Repost { post, .. } => post,
366
+
fn repost_by(&self) -> Option<String> {
368
+
RawFeedItem::Repost { by, .. } => Some(by.clone()),
373
+
fn context(&self) -> Option<String> {
375
+
RawFeedItem::Pin { context, .. } => context.clone(),
376
+
RawFeedItem::Post { context, .. } => context.clone(),
377
+
RawFeedItem::Repost { context, .. } => context.clone(),