···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+ .left_join(schema::posts::table.on(schema::posts::at_uri.eq(schema::author_feeds::post)))
3838+ .filter(
3939+ schema::author_feeds::did
4040+ .eq_any(follows_query)
4141+ .or(schema::author_feeds::did.eq(&did)),
4242+ )
4343+ .into_boxed();
4444+4545+ if let Some(cursor) = datetime_cursor(query.cursor.as_ref()) {
4646+ tl_query = tl_query.filter(schema::author_feeds::sort_at.lt(cursor));
4747+ }
4848+4949+ let results = tl_query
5050+ .order(schema::author_feeds::sort_at.desc())
5151+ .limit(limit as i64)
5252+ .load(&mut conn)
5353+ .await?;
5454+5555+ let cursor = results
5656+ .last()
5757+ .map(|item| item.sort_at.timestamp_millis().to_string());
5858+5959+ let raw_feed = results
6060+ .into_iter()
6161+ .filter_map(|item| match &*item.typ {
6262+ "post" => Some(RawFeedItem::Post {
6363+ uri: item.post,
6464+ context: None,
6565+ }),
6666+ "repost" => Some(RawFeedItem::Repost {
6767+ uri: item.uri,
6868+ post: item.post,
6969+ by: item.did,
7070+ at: item.sort_at,
7171+ context: None,
7272+ }),
7373+ _ => None,
7474+ })
7575+ .collect::<Vec<_>>();
7676+7777+ let feed = hyd.hydrate_feed_posts(raw_feed, false).await;
7878+7979+ Ok(Json(FeedRes { cursor, feed }))
8080+}