A personal app view to see Bsky posts of your followers (for when their app view goes down)
17
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add the new files

Signed-off-by: Will Andrews <did:plc:dadhhalkfcq3gucaq25hjqon>

+116
+102
src/store.rs
··· 1 + use sqlx::{FromRow, Row, types::chrono}; 2 + 3 + #[derive(Debug, FromRow)] 4 + pub struct PostRecord { 5 + pub created: chrono::DateTime<chrono::Utc>, 6 + pub indexed: chrono::DateTime<chrono::Utc>, 7 + pub author: String, 8 + pub rkey: String, 9 + pub cid: String, 10 + // TODO: other fields like reply to etc 11 + } 12 + 13 + #[derive(Debug, FromRow)] 14 + pub struct RepostRecord { 15 + pub created: chrono::DateTime<chrono::Utc>, 16 + pub indexed: chrono::DateTime<chrono::Utc>, 17 + pub author: String, 18 + pub rkey: String, 19 + pub subject: String, 20 + pub cid: String, 21 + // TODO: other fields like reply to etc 22 + } 23 + 24 + pub async fn insert_post(post: PostRecord, pool: &sqlx::SqlitePool) { 25 + let result = sqlx::query( 26 + "INSERT INTO posts (created, indexed, author, rkey, cid) VALUES ($1, $2, $3, $4, $5)", 27 + ) 28 + .bind(post.created) 29 + .bind(post.indexed) 30 + .bind(post.author) 31 + .bind(post.rkey) 32 + .bind(post.cid) 33 + .execute(pool) 34 + .await; 35 + 36 + if result.is_err() { 37 + println!("Error inserting post into the database: {result:?}"); 38 + } 39 + } 40 + 41 + pub async fn insert_repost(repost: RepostRecord, pool: &sqlx::SqlitePool) { 42 + let result = sqlx::query( 43 + "INSERT INTO reposts (created, indexed, author, rkey, subject, cid) VALUES ($1, $2, $3, $4, $5, $6)", 44 + ) 45 + .bind(repost.created) 46 + .bind(repost.indexed) 47 + .bind(repost.author) 48 + .bind(repost.rkey) 49 + .bind(repost.subject) 50 + .bind(repost.cid) 51 + .execute(pool) 52 + .await; 53 + 54 + if result.is_err() { 55 + println!("Error inserting repost into the database: {result:?}"); 56 + } 57 + } 58 + 59 + pub async fn delete_post(rkey: String, pool: &sqlx::SqlitePool) { 60 + let result = sqlx::query("DELETE FROM posts WHERE rkey = $1") 61 + .bind(rkey) 62 + .execute(pool) 63 + .await; 64 + 65 + if result.is_err() { 66 + println!("Error deleting post from the database: {result:?}"); 67 + } 68 + } 69 + 70 + pub async fn delete_repost(rkey: String, pool: &sqlx::SqlitePool) { 71 + let result = sqlx::query("DELETE FROM reposts WHERE rkey = $1") 72 + .bind(rkey) 73 + .execute(pool) 74 + .await; 75 + 76 + if result.is_err() { 77 + println!("Error deleting repost from the database: {result:?}"); 78 + } 79 + } 80 + 81 + pub async fn get_follow_by_rkey(rkey: &String, pool: &sqlx::SqlitePool) -> String { 82 + let result = sqlx::query("SELECT subject FROM follows where rkey = ? LIMIT 1") 83 + .bind(rkey) 84 + .fetch_one(pool) 85 + .await; 86 + 87 + match result { 88 + Ok(row) => { 89 + if row.len() == 0 { 90 + println!("did not find subject in follows with rkey: {rkey}"); 91 + return "".to_string(); 92 + } 93 + let subject = row.get::<String, _>(0); 94 + 95 + return subject; 96 + } 97 + Err(e) => { 98 + println!("error getting follow {e}"); 99 + return "".to_string(); 100 + } 101 + } 102 + }
+1
src/xrpc/mod.rs
··· 1 + pub mod routes;
+13
src/xrpc/routes.rs
··· 1 + use axum::{ 2 + Json, 3 + response::{IntoResponse, Response}, 4 + }; 5 + use jacquard_axum::service_auth::ExtractServiceAuth; 6 + 7 + use serde_json::json; 8 + 9 + pub async fn app_bsky_feed_get_timeline(ExtractServiceAuth(auth): ExtractServiceAuth) -> Response { 10 + let did = auth.did().to_string(); 11 + println!("{did}"); 12 + Json(json!({})).into_response() 13 + }