···11+use super::posts::FeedRes;
22+use crate::hydration::posts::RawFeedItem;
33+use crate::hydration::StatefulHydrator;
44+use crate::xrpc::error::XrpcResult;
55+use crate::xrpc::extract::{AtpAcceptLabelers, AtpAuth};
66+use crate::xrpc::{datetime_cursor, CursorQuery};
77+use crate::GlobalState;
88+use axum::extract::{Query, State};
99+use axum::Json;
1010+use diesel::prelude::*;
1111+use diesel_async::RunQueryDsl;
1212+use parakeet_db::{models, schema};
1313+1414+// okay so there's debate as to if the TL should show all posts from everyone you follow, or
1515+// just where you follow the poster and the person they're replying to. Maybe this could be an
1616+// option in the config?? Currently, this is the "old" behaviour.
1717+// If we want the "new" version, we'll need to add it into hydrate_feed_posts...
1818+// <mia opinion>i like how it works currently on bsky</mia opinion>
1919+pub async fn get_timeline(
2020+ State(state): State<GlobalState>,
2121+ AtpAcceptLabelers(labelers): AtpAcceptLabelers,
2222+ auth: AtpAuth,
2323+ Query(query): Query<CursorQuery>,
2424+) -> XrpcResult<Json<FeedRes>> {
2525+ let mut conn = state.pool.get().await?;
2626+2727+ let did = auth.0.clone();
2828+ let hyd = StatefulHydrator::new(&state.dataloaders, &state.cdn, &labelers, Some(auth));
2929+ let limit = query.limit.unwrap_or(50).clamp(1, 100);
3030+3131+ let follows_query = schema::follows::table
3232+ .select(schema::follows::subject)
3333+ .filter(schema::follows::did.eq(&did));
3434+3535+ let mut tl_query = schema::author_feeds::table
3636+ .select(models::AuthorFeedItem::as_select())
3737+ .filter(
3838+ schema::author_feeds::did
3939+ .eq_any(follows_query)
4040+ .or(schema::author_feeds::did.eq(&did)),
4141+ )
4242+ .into_boxed();
4343+4444+ if let Some(cursor) = datetime_cursor(query.cursor.as_ref()) {
4545+ tl_query = tl_query.filter(schema::author_feeds::sort_at.lt(cursor));
4646+ }
4747+4848+ let results = tl_query
4949+ .order(schema::author_feeds::sort_at.desc())
5050+ .limit(limit as i64)
5151+ .load(&mut conn)
5252+ .await?;
5353+5454+ let cursor = results
5555+ .last()
5656+ .map(|item| item.sort_at.timestamp_millis().to_string());
5757+5858+ let raw_feed = results
5959+ .into_iter()
6060+ .filter_map(|item| match &*item.typ {
6161+ "post" => Some(RawFeedItem::Post {
6262+ uri: item.post,
6363+ context: None,
6464+ }),
6565+ "repost" => Some(RawFeedItem::Repost {
6666+ uri: item.uri,
6767+ post: item.post,
6868+ by: item.did,
6969+ at: item.sort_at,
7070+ context: None,
7171+ }),
7272+ _ => None,
7373+ })
7474+ .collect::<Vec<_>>();
7575+7676+ let feed = hyd.hydrate_feed_posts(raw_feed, false).await;
7777+7878+ Ok(Json(FeedRes { cursor, feed }))
7979+}