From 34884e239f2120c8eaab5acf3e3fd4012d4b15ae Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Thu, 19 Dec 2024 12:07:08 +0100 Subject: [PATCH] User profile with displayname and bio (fixes #90) --- .../2024-12-18-120344_user_bio/down.sql | 2 + migrations/2024-12-18-120344_user_bio/up.sql | 2 + src/backend/api/article.rs | 19 +++- src/backend/api/mod.rs | 3 +- src/backend/api/user.rs | 10 ++ src/backend/database/schema.rs | 4 + src/backend/database/user.rs | 18 ++++ src/backend/federation/objects/user.rs | 15 ++- src/common/mod.rs | 9 ++ src/frontend/api.rs | 10 +- src/frontend/app.rs | 2 + src/frontend/components/credentials.rs | 2 - src/frontend/components/editor.rs | 4 +- src/frontend/components/nav.rs | 5 +- src/frontend/mod.rs | 8 +- src/frontend/pages/mod.rs | 1 + src/frontend/pages/user_edit_profile.rs | 97 +++++++++++++++++++ src/frontend/pages/user_profile.rs | 12 ++- 18 files changed, 206 insertions(+), 17 deletions(-) create mode 100644 migrations/2024-12-18-120344_user_bio/down.sql create mode 100644 migrations/2024-12-18-120344_user_bio/up.sql create mode 100644 src/frontend/pages/user_edit_profile.rs diff --git a/migrations/2024-12-18-120344_user_bio/down.sql b/migrations/2024-12-18-120344_user_bio/down.sql new file mode 100644 index 0000000..bdc89f0 --- /dev/null +++ b/migrations/2024-12-18-120344_user_bio/down.sql @@ -0,0 +1,2 @@ +alter table person drop display_name; +alter table person drop bio; diff --git a/migrations/2024-12-18-120344_user_bio/up.sql b/migrations/2024-12-18-120344_user_bio/up.sql new file mode 100644 index 0000000..e40e899 --- /dev/null +++ b/migrations/2024-12-18-120344_user_bio/up.sql @@ -0,0 +1,2 @@ +alter table person add column display_name varchar(20); +alter table person add column bio varchar(1000); diff --git a/src/backend/api/article.rs b/src/backend/api/article.rs index 09ed6c1..b68c356 100644 --- a/src/backend/api/article.rs +++ b/src/backend/api/article.rs @@ -14,9 +14,22 @@ use crate::{ common::{ utils::{extract_domain, http_protocol_str}, validation::can_edit_article, - ApiConflict, ApproveArticleForm, ArticleView, CreateArticleForm, DbArticle, DbEdit, - DbInstance, DeleteConflictForm, EditArticleForm, EditVersion, ForkArticleForm, - GetArticleForm, ListArticlesForm, LocalUserView, ProtectArticleForm, ResolveObject, + ApiConflict, + ApproveArticleForm, + ArticleView, + CreateArticleForm, + DbArticle, + DbEdit, + DbInstance, + DeleteConflictForm, + EditArticleForm, + EditVersion, + ForkArticleForm, + GetArticleForm, + ListArticlesForm, + LocalUserView, + ProtectArticleForm, + ResolveObject, SearchArticleForm, }, }; diff --git a/src/backend/api/mod.rs b/src/backend/api/mod.rs index 7ed9e3d..552fba1 100644 --- a/src/backend/api/mod.rs +++ b/src/backend/api/mod.rs @@ -37,7 +37,7 @@ use axum::{ use axum_extra::extract::CookieJar; use axum_macros::debug_handler; use instance::list_remote_instances; -use user::{count_notifications, list_notifications}; +use user::{count_notifications, list_notifications, update_user_profile}; pub mod article; pub mod instance; @@ -67,6 +67,7 @@ pub fn api_routes() -> Router<()> { .route("/account/register", post(register_user)) .route("/account/login", post(login_user)) .route("/account/logout", post(logout_user)) + .route("/account/update", post(update_user_profile)) .route("/site", get(site_view)) .route_layer(middleware::from_fn(auth)) } diff --git a/src/backend/api/user.rs b/src/backend/api/user.rs index 27a2575..838f2da 100644 --- a/src/backend/api/user.rs +++ b/src/backend/api/user.rs @@ -13,6 +13,7 @@ use crate::{ Notification, RegisterUserForm, SuccessResponse, + UpdateUserForm, AUTH_COOKIE, }, }; @@ -143,6 +144,15 @@ pub(in crate::backend::api) async fn get_user( )?)) } +#[debug_handler] +pub(in crate::backend::api) async fn update_user_profile( + data: Data, + Form(params): Form, +) -> MyResult> { + DbPerson::update_profile(¶ms, &data)?; + Ok(Json(SuccessResponse::default())) +} + #[debug_handler] pub(crate) async fn list_notifications( Extension(user): Extension, diff --git a/src/backend/database/schema.rs b/src/backend/database/schema.rs index 9468503..e17ec4a 100644 --- a/src/backend/database/schema.rs +++ b/src/backend/database/schema.rs @@ -110,6 +110,10 @@ diesel::table! { private_key -> Nullable, last_refreshed_at -> Timestamptz, local -> Bool, + #[max_length = 20] + display_name -> Nullable, + #[max_length = 1000] + bio -> Nullable, } } diff --git a/src/backend/database/user.rs b/src/backend/database/user.rs index 6c2ab61..6b6203c 100644 --- a/src/backend/database/user.rs +++ b/src/backend/database/user.rs @@ -14,6 +14,7 @@ use crate::{ DbLocalUser, DbPerson, LocalUserView, + UpdateUserForm, }, }; use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; @@ -49,6 +50,8 @@ pub struct DbPersonForm { pub private_key: Option, pub last_refreshed_at: DateTime, pub local: bool, + pub display_name: Option, + pub bio: Option, } impl DbPerson { @@ -89,6 +92,8 @@ impl DbPerson { private_key: Some(keypair.private_key), last_refreshed_at: Utc::now(), local: true, + display_name: None, + bio: None, }; let person = insert_into(person::table) @@ -143,6 +148,17 @@ impl DbPerson { Ok(query.get_result(conn.deref_mut())?) } + pub fn update_profile(params: &UpdateUserForm, data: &Data) -> MyResult<()> { + let mut conn = data.db_pool.get()?; + diesel::update(person::table.find(params.person_id)) + .set(( + person::dsl::display_name.eq(¶ms.display_name), + person::dsl::bio.eq(¶ms.bio), + )) + .execute(conn.deref_mut())?; + Ok(()) + } + pub fn read_local_from_name(username: &str, data: &Data) -> MyResult { let mut conn = data.db_pool.get()?; let (person, local_user) = person::table @@ -191,6 +207,8 @@ impl DbPerson { private_key: Some(keypair.private_key), last_refreshed_at: Utc::now(), local: true, + display_name: None, + bio: None, }; DbPerson::create(&person_form, data) } diff --git a/src/backend/federation/objects/user.rs b/src/backend/federation/objects/user.rs index cd67322..c535515 100644 --- a/src/backend/federation/objects/user.rs +++ b/src/backend/federation/objects/user.rs @@ -24,6 +24,9 @@ pub struct ApubUser { kind: PersonType, id: ObjectId, preferred_username: String, + /// displayname + name: Option, + summary: Option, inbox: Url, public_key: PublicKey, } @@ -48,10 +51,12 @@ impl Object for DbPerson { async fn into_json(self, _data: &Data) -> Result { Ok(ApubUser { kind: Default::default(), - id: self.ap_id.clone(), - preferred_username: self.username.clone(), - inbox: Url::parse(&self.inbox_url)?, - public_key: self.public_key(), + id: __self.ap_id.clone(), + preferred_username: __self.username.clone(), + inbox: Url::parse(&__self.inbox_url)?, + public_key: __self.public_key(), + name: self.display_name, + summary: self.bio, }) } @@ -73,6 +78,8 @@ impl Object for DbPerson { private_key: None, last_refreshed_at: Utc::now(), local: false, + display_name: json.name, + bio: json.summary, }; DbPerson::create(&form, data) } diff --git a/src/common/mod.rs b/src/common/mod.rs index b800852..dad817f 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -186,6 +186,8 @@ pub struct DbPerson { #[serde(skip)] pub last_refreshed_at: DateTime, pub local: bool, + pub display_name: Option, + pub bio: Option, } impl DbPerson { @@ -344,6 +346,13 @@ pub struct GetUserForm { pub domain: Option, } +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct UpdateUserForm { + pub person_id: PersonId, + pub display_name: Option, + pub bio: Option, +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, SmartDefault)] #[serde(default)] #[serde(deny_unknown_fields)] diff --git a/src/frontend/api.rs b/src/frontend/api.rs index 546302b..3ab1a04 100644 --- a/src/frontend/api.rs +++ b/src/frontend/api.rs @@ -217,6 +217,14 @@ impl ApiClient { self.get("/api/v1/user", Some(data)).await } + pub async fn update_user_profile( + &self, + data: UpdateUserForm, + ) -> Result { + log::info!("{:?}", &data); + self.post("/api/v1/account/update", Some(data)).await + } + pub async fn get_article_edits(&self, article_id: ArticleId) -> Option> { let data = GetEditList { article_id: Some(article_id), @@ -344,7 +352,7 @@ impl ApiClient { { let json = serde_json::from_str(&text).map_err(|e| { ServerFnError::::Deserialization(format!( - "Serde error: {e} from {text}on {url}" + "Serde error: {e} from {text} on {url}" )) })?; if status == StatusCode::OK { diff --git a/src/frontend/app.rs b/src/frontend/app.rs index 97f7814..8e6527f 100644 --- a/src/frontend/app.rs +++ b/src/frontend/app.rs @@ -19,6 +19,7 @@ use crate::{ notifications::Notifications, register::Register, search::Search, + user_edit_profile::UserEditProfile, user_profile::UserProfile, }, }, @@ -122,6 +123,7 @@ pub fn App() -> impl IntoView { + diff --git a/src/frontend/components/credentials.rs b/src/frontend/components/credentials.rs index 8dc1dcd..1efe4ed 100644 --- a/src/frontend/components/credentials.rs +++ b/src/frontend/components/credentials.rs @@ -38,7 +38,6 @@ pub fn CredentialsForm( let val = event_target_value(&ev); set_username.update(|v| *v = val); } - on:change=move |ev| { let val = event_target_value(&ev); set_username.update(|v| *v = val); @@ -62,7 +61,6 @@ pub fn CredentialsForm( } } } - on:change=move |ev| { let val = event_target_value(&ev); set_password.update(|p| *p = val); diff --git a/src/frontend/components/editor.rs b/src/frontend/components/editor.rs index 0986eb2..e6972d6 100644 --- a/src/frontend/components/editor.rs +++ b/src/frontend/components/editor.rs @@ -26,7 +26,7 @@ pub fn EditorView( + + + } + } + + + } +} diff --git a/src/frontend/pages/user_profile.rs b/src/frontend/pages/user_profile.rs index a11bd9f..425da99 100644 --- a/src/frontend/pages/user_profile.rs +++ b/src/frontend/pages/user_profile.rs @@ -1,6 +1,11 @@ use crate::{ common::GetUserForm, - frontend::{api::CLIENT, components::edit_list::EditList, user_title}, + frontend::{ + api::CLIENT, + components::edit_list::EditList, + markdown::render_markdown, + user_title, + }, }; use leptos::prelude::*; use leptos_router::hooks::use_params_map; @@ -51,6 +56,11 @@ pub fn UserProfile() -> impl IntoView { {user_title(&person)} +
+

Edits

}