mirror of
https://github.com/nullishamy/ferri.git
synced 2025-06-28 00:54:17 +00:00
feat: lots of timeline improvements; icon urls for users
This commit is contained in:
parent
41c0091e98
commit
a924415a74
15 changed files with 379 additions and 43 deletions
|
@ -44,10 +44,10 @@ impl From<db::User> for api::Account {
|
|||
note: "".to_string(),
|
||||
url: val.url,
|
||||
|
||||
avatar: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||
avatar_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||
header: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||
header_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||
avatar: val.icon_url.clone(),
|
||||
avatar_static: val.icon_url.clone(),
|
||||
header: val.icon_url.clone(),
|
||||
header_static: val.icon_url,
|
||||
|
||||
followers_count: 0,
|
||||
following_count: 0,
|
||||
|
@ -79,6 +79,42 @@ impl From<db::User> for ap::Person {
|
|||
owner: format!("https://ferri.amy.mov/users/{}", val.id.0),
|
||||
public_key: include_str!("../../../public.pem").to_string(),
|
||||
}),
|
||||
icon: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<db::Post> for api::Status {
|
||||
fn from(value: db::Post) -> api::Status {
|
||||
api::Status {
|
||||
id: value.id,
|
||||
created_at: value.created_at.to_rfc3339(),
|
||||
in_reply_to_id: None,
|
||||
in_reply_to_account_id: None,
|
||||
sensitive: false,
|
||||
spoiler_text: String::new(),
|
||||
visibility: "Public".to_string(),
|
||||
language: "en-GB".to_string(),
|
||||
uri: value.uri.clone(),
|
||||
url: value.uri.0.to_string(),
|
||||
replies_count: 0,
|
||||
reblogs_count: 0,
|
||||
favourites_count: 0,
|
||||
favourited: false,
|
||||
reblogged: false,
|
||||
muted: false,
|
||||
bookmarked: false,
|
||||
content: value.content,
|
||||
reblog: None,
|
||||
application: None,
|
||||
account: value.user.into(),
|
||||
media_attachments: vec![],
|
||||
mentions: vec![],
|
||||
tags: vec![],
|
||||
emojis: vec![],
|
||||
card: None,
|
||||
poll: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,8 @@ pub async fn user_by_id(id: ObjectUuid, conn: &mut SqliteConnection) -> Result<d
|
|||
u.url,
|
||||
u.acct,
|
||||
u.remote,
|
||||
u.created_at
|
||||
u.created_at,
|
||||
u.icon_url
|
||||
FROM "user" u
|
||||
INNER JOIN "actor" a ON u.actor_id = a.id
|
||||
WHERE u.id = ?1
|
||||
|
@ -101,5 +102,142 @@ pub async fn user_by_id(id: ObjectUuid, conn: &mut SqliteConnection) -> Result<d
|
|||
created_at: user_created,
|
||||
url: record.url,
|
||||
posts: db::UserPosts { last_post_at },
|
||||
icon_url: record.icon_url
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn user_by_actor_uri(uri: ObjectUri, conn: &mut SqliteConnection) -> Result<db::User, DbError> {
|
||||
info!("fetching user by actor_uri '{:?}' from the database", uri);
|
||||
|
||||
let record = sqlx::query!(
|
||||
r#"
|
||||
SELECT
|
||||
u.id as "user_id",
|
||||
u.username,
|
||||
u.actor_id,
|
||||
u.display_name,
|
||||
a.inbox,
|
||||
a.outbox,
|
||||
u.url,
|
||||
u.acct,
|
||||
u.remote,
|
||||
u.created_at,
|
||||
u.icon_url
|
||||
FROM "user" u
|
||||
INNER JOIN "actor" a ON u.actor_id = a.id
|
||||
WHERE u.actor_id = ?1
|
||||
"#,
|
||||
uri.0
|
||||
)
|
||||
.fetch_one(&mut *conn)
|
||||
.await
|
||||
.map_err(|e| DbError::FetchError(e.to_string()))?;
|
||||
|
||||
let follower_count = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT COUNT(follower_id)
|
||||
FROM "follow"
|
||||
WHERE followed_id = ?1
|
||||
"#,
|
||||
record.actor_id
|
||||
)
|
||||
.fetch_one(&mut *conn)
|
||||
.await
|
||||
.map_err(|e| DbError::FetchError(e.to_string()))?;
|
||||
|
||||
let last_post_at = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT datetime(p.created_at)
|
||||
FROM post p
|
||||
WHERE p.user_id = ?1
|
||||
ORDER BY datetime(p.created_at) DESC
|
||||
LIMIT 1
|
||||
"#,
|
||||
record.user_id
|
||||
)
|
||||
.fetch_optional(&mut *conn)
|
||||
.await
|
||||
.map_err(|e| DbError::FetchError(e.to_string()))?
|
||||
.flatten()
|
||||
.and_then(|ts| {
|
||||
info!("parsing timestamp {}", ts);
|
||||
parse_ts(ts)
|
||||
});
|
||||
|
||||
let user_created = parse_ts(record.created_at).expect("no db corruption");
|
||||
|
||||
info!("user {:?} has {} followers", record.user_id, follower_count);
|
||||
info!("user {:?} last posted {:?}", record.user_id, last_post_at);
|
||||
|
||||
Ok(db::User {
|
||||
id: ObjectUuid(record.user_id),
|
||||
actor: db::Actor {
|
||||
id: ObjectUri(record.actor_id),
|
||||
inbox: record.inbox,
|
||||
outbox: record.outbox,
|
||||
},
|
||||
acct: record.acct,
|
||||
remote: record.remote,
|
||||
username: record.username,
|
||||
display_name: record.display_name,
|
||||
created_at: user_created,
|
||||
url: record.url,
|
||||
posts: db::UserPosts { last_post_at },
|
||||
icon_url: record.icon_url
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn posts_for_user_id(
|
||||
id: ObjectUuid,
|
||||
conn: &mut SqliteConnection
|
||||
) -> Result<Vec<db::Post>, DbError> {
|
||||
let mut out = vec![];
|
||||
let posts = sqlx::query!(r#"
|
||||
SELECT
|
||||
p.id as "post_id", u.id as "user_id",
|
||||
p.content, p.uri as "post_uri", u.username, u.display_name,
|
||||
u.actor_id, p.created_at as "post_created",
|
||||
p.boosted_post_id, a.inbox, a.outbox, u.created_at as "user_created",
|
||||
u.acct, u.remote, u.url as "user_url", u.icon_url
|
||||
FROM post p
|
||||
INNER JOIN user u on p.user_id = u.id
|
||||
INNER JOIN actor a ON u.actor_id = a.id
|
||||
WHERE p.user_id = ?
|
||||
"#, id.0)
|
||||
.fetch_all(&mut *conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
for record in posts {
|
||||
let user_created = parse_ts(record.user_created)
|
||||
.expect("no db corruption");
|
||||
|
||||
out.push(db::Post {
|
||||
id: ObjectUuid(record.post_id),
|
||||
uri: ObjectUri(record.post_uri),
|
||||
user: db::User {
|
||||
id: ObjectUuid(record.user_id),
|
||||
actor: db::Actor {
|
||||
id: ObjectUri(record.actor_id),
|
||||
inbox: record.inbox,
|
||||
outbox: record.outbox,
|
||||
},
|
||||
acct: record.acct,
|
||||
remote: record.remote,
|
||||
username: record.username,
|
||||
display_name: record.display_name,
|
||||
created_at: user_created,
|
||||
url: record.user_url,
|
||||
icon_url: record.icon_url,
|
||||
posts: db::UserPosts {
|
||||
last_post_at: None
|
||||
}
|
||||
},
|
||||
content: record.content,
|
||||
created_at: parse_ts(record.post_created).unwrap(),
|
||||
boosted_post: None
|
||||
})
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ pub async fn new_user(user: db::User, conn: &mut SqliteConnection) -> Result<db:
|
|||
let ts = user.created_at.to_rfc3339();
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO user (id, acct, url, created_at, remote, username, actor_id, display_name)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)
|
||||
INSERT INTO user (id, acct, url, created_at, remote,
|
||||
username, actor_id, display_name, icon_url)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
|
||||
"#,
|
||||
user.id.0,
|
||||
user.acct,
|
||||
|
@ -15,7 +16,8 @@ pub async fn new_user(user: db::User, conn: &mut SqliteConnection) -> Result<db:
|
|||
user.remote,
|
||||
user.username,
|
||||
user.actor.id.0,
|
||||
user.display_name
|
||||
user.display_name,
|
||||
user.icon_url
|
||||
)
|
||||
.execute(conn)
|
||||
.await
|
||||
|
|
|
@ -86,9 +86,20 @@ pub mod db {
|
|||
pub remote: bool,
|
||||
pub url: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub icon_url: String,
|
||||
|
||||
pub posts: UserPosts,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct Post {
|
||||
pub id: ObjectUuid,
|
||||
pub uri: ObjectUri,
|
||||
pub user: User,
|
||||
pub content: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub boosted_post: Option<ObjectUuid>
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ap {
|
||||
|
@ -210,6 +221,25 @@ pub mod ap {
|
|||
pub outbox: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub enum IconType {
|
||||
Image
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct PersonIcon {
|
||||
#[serde(rename = "type")]
|
||||
pub ty: IconType,
|
||||
pub url: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub summary: String,
|
||||
#[serde(default)]
|
||||
pub width: i64,
|
||||
#[serde(default)]
|
||||
pub height: i64
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Person {
|
||||
|
@ -227,6 +257,8 @@ pub mod ap {
|
|||
pub name: String,
|
||||
|
||||
pub public_key: Option<UserKey>,
|
||||
|
||||
pub icon: Option<PersonIcon>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
|
@ -271,6 +303,37 @@ pub mod api {
|
|||
pub links: Vec<WebfingerLink>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct Status {
|
||||
pub id: ObjectUuid,
|
||||
pub created_at: String,
|
||||
pub in_reply_to_id: Option<ObjectUri>,
|
||||
pub in_reply_to_account_id: Option<ObjectUri>,
|
||||
pub sensitive: bool,
|
||||
pub spoiler_text: String,
|
||||
pub visibility: String,
|
||||
pub language: String,
|
||||
pub uri: ObjectUri,
|
||||
pub url: String,
|
||||
pub replies_count: i64,
|
||||
pub reblogs_count: i64,
|
||||
pub favourites_count: i64,
|
||||
pub favourited: bool,
|
||||
pub reblogged: bool,
|
||||
pub muted: bool,
|
||||
pub bookmarked: bool,
|
||||
pub content: String,
|
||||
pub reblog: Option<Box<Status>>,
|
||||
pub application: Option<()>,
|
||||
pub account: Account,
|
||||
pub media_attachments: Vec<Option<()>>,
|
||||
pub mentions: Vec<Option<()>>,
|
||||
pub tags: Vec<Option<()>>,
|
||||
pub emojis: Vec<Option<()>>,
|
||||
pub card: Option<()>,
|
||||
pub poll: Option<()>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct Account {
|
||||
pub id: ObjectUuid,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue