1
0
Fork 0
mirror of https://github.com/Nutomic/ibis.git synced 2024-11-22 11:51:09 +00:00

use newtypes for all ids

This commit is contained in:
Felix Ableitner 2024-11-11 23:13:35 +01:00
parent 7d8954ecd1
commit 639ca1704f
14 changed files with 102 additions and 54 deletions

View file

@ -1,2 +1 @@
ALTER TABLE conflict DROP CONSTRAINT conflict_creator_id_fkey; select 1;
ALTER TABLE conflict ADD CONSTRAINT conflict_creator_id_fkey FOREIGN KEY (creator_id) REFERENCES local_user(id) ON UPDATE CASCADE ON DELETE CASCADE;

View file

@ -7,7 +7,13 @@ use crate::{
error::MyResult, error::MyResult,
federation::objects::edits_collection::DbEditCollection, federation::objects::edits_collection::DbEditCollection,
}, },
common::{ArticleView, DbArticle, DbEdit, EditVersion}, common::{
newtypes::{ArticleId, InstanceId},
ArticleView,
DbArticle,
DbEdit,
EditVersion,
},
}; };
use activitypub_federation::fetch::{collection_id::CollectionId, object_id::ObjectId}; use activitypub_federation::fetch::{collection_id::CollectionId, object_id::ObjectId};
use diesel::{ use diesel::{
@ -29,7 +35,7 @@ pub struct DbArticleForm {
pub title: String, pub title: String,
pub text: String, pub text: String,
pub ap_id: ObjectId<DbArticle>, pub ap_id: ObjectId<DbArticle>,
pub instance_id: i32, pub instance_id: InstanceId,
pub local: bool, pub local: bool,
pub protected: bool, pub protected: bool,
} }
@ -59,26 +65,26 @@ impl DbArticle {
.get_result(conn.deref_mut())?) .get_result(conn.deref_mut())?)
} }
pub fn update_text(id: i32, text: &str, data: &IbisData) -> MyResult<Self> { pub fn update_text(id: ArticleId, text: &str, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?; let mut conn = data.db_pool.get()?;
Ok(diesel::update(article::dsl::article.find(id)) Ok(diesel::update(article::dsl::article.find(id))
.set(article::dsl::text.eq(text)) .set(article::dsl::text.eq(text))
.get_result::<Self>(conn.deref_mut())?) .get_result::<Self>(conn.deref_mut())?)
} }
pub fn update_protected(id: i32, locked: bool, data: &IbisData) -> MyResult<Self> { pub fn update_protected(id: ArticleId, locked: bool, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?; let mut conn = data.db_pool.get()?;
Ok(diesel::update(article::dsl::article.find(id)) Ok(diesel::update(article::dsl::article.find(id))
.set(article::dsl::protected.eq(locked)) .set(article::dsl::protected.eq(locked))
.get_result::<Self>(conn.deref_mut())?) .get_result::<Self>(conn.deref_mut())?)
} }
pub fn read(id: i32, data: &IbisData) -> MyResult<Self> { pub fn read(id: ArticleId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?; let mut conn = data.db_pool.get()?;
Ok(article::table.find(id).get_result(conn.deref_mut())?) Ok(article::table.find(id).get_result(conn.deref_mut())?)
} }
pub fn read_view(id: i32, data: &IbisData) -> MyResult<ArticleView> { pub fn read_view(id: ArticleId, data: &IbisData) -> MyResult<ArticleView> {
let mut conn = data.db_pool.get()?; let mut conn = data.db_pool.get()?;
let article: DbArticle = { article::table.find(id).get_result(conn.deref_mut())? }; let article: DbArticle = { article::table.find(id).get_result(conn.deref_mut())? };
let latest_version = article.latest_edit_version(data)?; let latest_version = article.latest_edit_version(data)?;
@ -139,7 +145,7 @@ impl DbArticle {
/// TODO: Should get rid of only_local param and rely on instance_id /// TODO: Should get rid of only_local param and rely on instance_id
pub fn read_all( pub fn read_all(
only_local: Option<bool>, only_local: Option<bool>,
instance_id: Option<i32>, instance_id: Option<InstanceId>,
data: &IbisData, data: &IbisData,
) -> MyResult<Vec<Self>> { ) -> MyResult<Vec<Self>> {
let mut conn = data.db_pool.get()?; let mut conn = data.db_pool.get()?;

View file

@ -5,9 +5,15 @@ use crate::{
federation::activities::submit_article_update, federation::activities::submit_article_update,
utils::generate_article_version, utils::generate_article_version,
}, },
common::{ApiConflict, DbArticle, DbEdit, DbPerson, EditVersion}, common::{
newtypes::{ArticleId, ConflictId, PersonId},
ApiConflict,
DbArticle,
DbEdit,
DbPerson,
EditVersion,
},
}; };
use crate::common::newtypes::PersonId;
use activitypub_federation::config::Data; use activitypub_federation::config::Data;
use diesel::{ use diesel::{
delete, delete,
@ -29,12 +35,12 @@ use std::ops::DerefMut;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Queryable, Selectable, Identifiable)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Queryable, Selectable, Identifiable)]
#[diesel(table_name = conflict, check_for_backend(diesel::pg::Pg), belongs_to(DbArticle, foreign_key = article_id))] #[diesel(table_name = conflict, check_for_backend(diesel::pg::Pg), belongs_to(DbArticle, foreign_key = article_id))]
pub struct DbConflict { pub struct DbConflict {
pub id: i32, pub id: ConflictId,
pub hash: EditVersion, pub hash: EditVersion,
pub diff: String, pub diff: String,
pub summary: String, pub summary: String,
pub creator_id: PersonId, pub creator_id: PersonId,
pub article_id: i32, pub article_id: ArticleId,
pub previous_version_id: EditVersion, pub previous_version_id: EditVersion,
} }
@ -45,7 +51,7 @@ pub struct DbConflictForm {
pub diff: String, pub diff: String,
pub summary: String, pub summary: String,
pub creator_id: PersonId, pub creator_id: PersonId,
pub article_id: i32, pub article_id: ArticleId,
pub previous_version_id: EditVersion, pub previous_version_id: EditVersion,
} }
@ -65,7 +71,7 @@ impl DbConflict {
} }
/// Delete a merge conflict after it is resolved. /// Delete a merge conflict after it is resolved.
pub fn delete(id: i32, data: &IbisData) -> MyResult<Self> { pub fn delete(id: ConflictId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?; let mut conn = data.db_pool.get()?;
Ok(delete(conflict::table.find(id)).get_result(conn.deref_mut())?) Ok(delete(conflict::table.find(id)).get_result(conn.deref_mut())?)
} }

View file

@ -4,14 +4,19 @@ use crate::{
error::MyResult, error::MyResult,
IbisData, IbisData,
}, },
common::{DbArticle, DbEdit, EditVersion, EditView}, common::{
newtypes::{ArticleId, PersonId},
DbArticle,
DbEdit,
EditVersion,
EditView,
},
}; };
use activitypub_federation::fetch::object_id::ObjectId; use activitypub_federation::fetch::object_id::ObjectId;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use diesel::{insert_into, AsChangeset, ExpressionMethods, Insertable, QueryDsl, RunQueryDsl}; use diesel::{insert_into, AsChangeset, ExpressionMethods, Insertable, QueryDsl, RunQueryDsl};
use diffy::create_patch; use diffy::create_patch;
use std::ops::DerefMut; use std::ops::DerefMut;
use crate::common::newtypes::PersonId;
#[derive(Debug, Clone, Insertable, AsChangeset)] #[derive(Debug, Clone, Insertable, AsChangeset)]
#[diesel(table_name = edit, check_for_backend(diesel::pg::Pg))] #[diesel(table_name = edit, check_for_backend(diesel::pg::Pg))]
@ -21,7 +26,7 @@ pub struct DbEditForm {
pub ap_id: ObjectId<DbEdit>, pub ap_id: ObjectId<DbEdit>,
pub diff: String, pub diff: String,
pub summary: String, pub summary: String,
pub article_id: i32, pub article_id: ArticleId,
pub previous_version_id: EditVersion, pub previous_version_id: EditVersion,
pub created: DateTime<Utc>, pub created: DateTime<Utc>,
} }

View file

@ -10,7 +10,7 @@ use crate::{
instance_collection::DbInstanceCollection, instance_collection::DbInstanceCollection,
}, },
}, },
common::{DbInstance, DbPerson, InstanceView}, common::{newtypes::InstanceId, DbInstance, DbPerson, InstanceView},
}; };
use activitypub_federation::{ use activitypub_federation::{
config::Data, config::Data,
@ -54,7 +54,7 @@ impl DbInstance {
.get_result(conn.deref_mut())?) .get_result(conn.deref_mut())?)
} }
pub fn read(id: i32, data: &IbisData) -> MyResult<Self> { pub fn read(id: InstanceId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?; let mut conn = data.db_pool.get()?;
Ok(instance::table.find(id).get_result(conn.deref_mut())?) Ok(instance::table.find(id).get_result(conn.deref_mut())?)
} }
@ -76,7 +76,7 @@ impl DbInstance {
.get_result(conn.deref_mut())?) .get_result(conn.deref_mut())?)
} }
pub fn read_view(id: Option<i32>, data: &Data<IbisData>) -> MyResult<InstanceView> { pub fn read_view(id: Option<InstanceId>, data: &Data<IbisData>) -> MyResult<InstanceView> {
let instance = match id { let instance = match id {
Some(id) => DbInstance::read(id, data), Some(id) => DbInstance::read(id, data),
None => DbInstance::read_local_instance(data), None => DbInstance::read_local_instance(data),
@ -113,7 +113,7 @@ impl DbInstance {
Ok(()) Ok(())
} }
pub fn read_followers(id_: i32, data: &IbisData) -> MyResult<Vec<DbPerson>> { pub fn read_followers(id_: InstanceId, data: &IbisData) -> MyResult<Vec<DbPerson>> {
use crate::backend::database::schema::person; use crate::backend::database::schema::person;
use instance_follow::dsl::{follower_id, instance_id}; use instance_follow::dsl::{follower_id, instance_id};
let mut conn = data.db_pool.get()?; let mut conn = data.db_pool.get()?;

View file

@ -7,6 +7,7 @@ use crate::{
error::MyResult, error::MyResult,
}, },
common::{ common::{
newtypes::PersonId,
utils::http_protocol_str, utils::http_protocol_str,
DbInstance, DbInstance,
DbLocalUser, DbLocalUser,
@ -14,7 +15,6 @@ use crate::{
LocalUserView, LocalUserView,
}, },
}; };
use crate::common::newtypes::PersonId;
use activitypub_federation::{ use activitypub_federation::{
config::Data, config::Data,
fetch::object_id::ObjectId, fetch::object_id::ObjectId,

View file

@ -7,11 +7,16 @@ use crate::{
update_remote_article::UpdateRemoteArticle, update_remote_article::UpdateRemoteArticle,
}, },
}, },
common::{DbArticle, DbEdit, DbInstance, EditVersion}, common::{
newtypes::{EditId, PersonId},
DbArticle,
DbEdit,
DbInstance,
EditVersion,
},
}; };
use activitypub_federation::config::Data; use activitypub_federation::config::Data;
use chrono::Utc; use chrono::Utc;
use crate::common::newtypes::PersonId;
pub mod accept; pub mod accept;
pub mod create_article; pub mod create_article;
@ -43,7 +48,7 @@ pub async fn submit_article_update(
} else { } else {
// dont insert edit into db, might be invalid in case of conflict // dont insert edit into db, might be invalid in case of conflict
let edit = DbEdit { let edit = DbEdit {
id: -1, id: EditId(-1),
creator_id, creator_id,
hash: form.hash, hash: form.hash,
ap_id: form.ap_id, ap_id: form.ap_id,

View file

@ -46,7 +46,11 @@ pub fn generate_article_version(edits: &Vec<EditView>, version: &EditVersion) ->
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::common::{newtypes::PersonId, DbEdit, DbPerson}; use crate::common::{
newtypes::{ArticleId, EditId, PersonId},
DbEdit,
DbPerson,
};
use activitypub_federation::fetch::object_id::ObjectId; use activitypub_federation::fetch::object_id::ObjectId;
use chrono::Utc; use chrono::Utc;
use diffy::create_patch; use diffy::create_patch;
@ -56,13 +60,13 @@ mod test {
let diff = create_patch(a, b).to_string(); let diff = create_patch(a, b).to_string();
Ok(EditView { Ok(EditView {
edit: DbEdit { edit: DbEdit {
id: 0, id: EditId(0),
creator_id: PersonId(0), creator_id: PersonId(0),
hash: EditVersion::new(&diff), hash: EditVersion::new(&diff),
ap_id: ObjectId::parse("http://example.com")?, ap_id: ObjectId::parse("http://example.com")?,
diff, diff,
summary: String::new(), summary: String::new(),
article_id: 0, article_id: ArticleId(0),
previous_version_id: Default::default(), previous_version_id: Default::default(),
created: Utc::now(), created: Utc::now(),
}, },

View file

@ -3,7 +3,7 @@ pub mod utils;
pub mod validation; pub mod validation;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use newtypes::PersonId; use newtypes::{ArticleId, ConflictId, EditId, InstanceId, PersonId};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use url::Url; use url::Url;
@ -26,13 +26,13 @@ pub const MAIN_PAGE_NAME: &str = "Main_Page";
pub struct GetArticleForm { pub struct GetArticleForm {
pub title: Option<String>, pub title: Option<String>,
pub domain: Option<String>, pub domain: Option<String>,
pub id: Option<i32>, pub id: Option<ArticleId>,
} }
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone)]
pub struct ListArticlesForm { pub struct ListArticlesForm {
pub only_local: Option<bool>, pub only_local: Option<bool>,
pub instance_id: Option<i32>, pub instance_id: Option<InstanceId>,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@ -48,14 +48,14 @@ pub struct ArticleView {
#[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))] #[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))]
#[cfg_attr(feature = "ssr", diesel(table_name = article, check_for_backend(diesel::pg::Pg), belongs_to(DbInstance, foreign_key = instance_id)))] #[cfg_attr(feature = "ssr", diesel(table_name = article, check_for_backend(diesel::pg::Pg), belongs_to(DbInstance, foreign_key = instance_id)))]
pub struct DbArticle { pub struct DbArticle {
pub id: i32, pub id: ArticleId,
pub title: String, pub title: String,
pub text: String, pub text: String,
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
pub ap_id: ObjectId<DbArticle>, pub ap_id: ObjectId<DbArticle>,
#[cfg(not(feature = "ssr"))] #[cfg(not(feature = "ssr"))]
pub ap_id: String, pub ap_id: String,
pub instance_id: i32, pub instance_id: InstanceId,
pub local: bool, pub local: bool,
pub protected: bool, pub protected: bool,
} }
@ -67,7 +67,7 @@ pub struct DbArticle {
pub struct DbEdit { pub struct DbEdit {
// TODO: we could use hash as primary key, but that gives errors on forking because // TODO: we could use hash as primary key, but that gives errors on forking because
// the same edit is used for multiple articles // the same edit is used for multiple articles
pub id: i32, pub id: EditId,
#[serde(skip)] #[serde(skip)]
pub creator_id: PersonId, pub creator_id: PersonId,
/// UUID built from sha224 hash of diff /// UUID built from sha224 hash of diff
@ -78,7 +78,7 @@ pub struct DbEdit {
pub ap_id: String, pub ap_id: String,
pub diff: String, pub diff: String,
pub summary: String, pub summary: String,
pub article_id: i32, pub article_id: ArticleId,
/// First edit of an article always has `EditVersion::default()` here /// First edit of an article always has `EditVersion::default()` here
pub previous_version_id: EditVersion, pub previous_version_id: EditVersion,
pub created: DateTime<Utc>, pub created: DateTime<Utc>,
@ -145,10 +145,10 @@ pub struct LocalUserView {
#[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))] #[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))]
#[cfg_attr(feature = "ssr", diesel(table_name = local_user, check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "ssr", diesel(table_name = local_user, check_for_backend(diesel::pg::Pg)))]
pub struct DbLocalUser { pub struct DbLocalUser {
pub id: i32, pub id: InstanceId,
#[serde(skip)] #[serde(skip)]
pub password_encrypted: String, pub password_encrypted: String,
pub person_id: i32, pub person_id: PersonId,
pub admin: bool, pub admin: bool,
} }
@ -189,7 +189,7 @@ pub struct CreateArticleForm {
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct EditArticleForm { pub struct EditArticleForm {
/// Id of the article to edit /// Id of the article to edit
pub article_id: i32, pub article_id: ArticleId,
/// Full, new text of the article. A diff against `previous_version` is generated on the backend /// Full, new text of the article. A diff against `previous_version` is generated on the backend
/// side to handle conflicts. /// side to handle conflicts.
pub new_text: String, pub new_text: String,
@ -199,29 +199,29 @@ pub struct EditArticleForm {
/// [ApiConflict.previous_version] /// [ApiConflict.previous_version]
pub previous_version_id: EditVersion, pub previous_version_id: EditVersion,
/// If you are resolving a conflict, pass the id to delete conflict from the database /// If you are resolving a conflict, pass the id to delete conflict from the database
pub resolve_conflict_id: Option<i32>, pub resolve_conflict_id: Option<ConflictId>,
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct ProtectArticleForm { pub struct ProtectArticleForm {
pub article_id: i32, pub article_id: ArticleId,
pub protected: bool, pub protected: bool,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct ForkArticleForm { pub struct ForkArticleForm {
pub article_id: i32, pub article_id: ArticleId,
pub new_title: String, pub new_title: String,
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct GetInstance { pub struct GetInstance {
pub id: Option<i32>, pub id: Option<InstanceId>,
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
pub struct FollowInstance { pub struct FollowInstance {
pub id: i32, pub id: InstanceId,
} }
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone)]
@ -236,7 +236,7 @@ pub struct ResolveObject {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ApiConflict { pub struct ApiConflict {
pub id: i32, pub id: ConflictId,
pub hash: EditVersion, pub hash: EditVersion,
pub three_way_merge: String, pub three_way_merge: String,
pub summary: String, pub summary: String,
@ -248,7 +248,7 @@ pub struct ApiConflict {
#[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))] #[cfg_attr(feature = "ssr", derive(Queryable, Selectable, Identifiable))]
#[cfg_attr(feature = "ssr", diesel(table_name = instance, check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "ssr", diesel(table_name = instance, check_for_backend(diesel::pg::Pg)))]
pub struct DbInstance { pub struct DbInstance {
pub id: i32, pub id: InstanceId,
pub domain: String, pub domain: String,
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
pub ap_id: ObjectId<DbInstance>, pub ap_id: ObjectId<DbInstance>,

23
src/common/newtypes.rs Normal file
View file

@ -0,0 +1,23 @@
#[cfg(feature = "ssr")]
use diesel_derive_newtype::DieselNewType;
use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct PersonId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct ArticleId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct EditId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct InstanceId(pub i32);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(DieselNewType))]
pub struct ConflictId(pub i32);

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
common::{DbInstance, FollowInstance}, common::{newtypes::InstanceId, DbInstance, FollowInstance},
frontend::app::GlobalState, frontend::app::GlobalState,
}; };
use leptos::{component, *}; use leptos::{component, *};
@ -7,7 +7,7 @@ use leptos::{component, *};
#[component] #[component]
pub fn InstanceFollowButton(instance: DbInstance) -> impl IntoView { pub fn InstanceFollowButton(instance: DbInstance) -> impl IntoView {
let global_state = use_context::<RwSignal<GlobalState>>().unwrap(); let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
let follow_action = create_action(move |instance_id: &i32| { let follow_action = create_action(move |instance_id: &InstanceId| {
let instance_id = *instance_id; let instance_id = *instance_id;
async move { async move {
let form = FollowInstance { id: instance_id }; let form = FollowInstance { id: instance_id };

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
common::{ForkArticleForm, ProtectArticleForm}, common::{newtypes::ArticleId, ForkArticleForm, ProtectArticleForm},
frontend::{ frontend::{
app::GlobalState, app::GlobalState,
article_link, article_link,
@ -18,7 +18,7 @@ pub fn ArticleActions() -> impl IntoView {
let (new_title, set_new_title) = create_signal(String::new()); let (new_title, set_new_title) = create_signal(String::new());
let (fork_response, set_fork_response) = create_signal(Option::<DbArticle>::None); let (fork_response, set_fork_response) = create_signal(Option::<DbArticle>::None);
let (error, set_error) = create_signal(None::<String>); let (error, set_error) = create_signal(None::<String>);
let fork_action = create_action(move |(article_id, new_title): &(i32, String)| { let fork_action = create_action(move |(article_id, new_title): &(ArticleId, String)| {
let params = ForkArticleForm { let params = ForkArticleForm {
article_id: *article_id, article_id: *article_id,
new_title: new_title.to_string(), new_title: new_title.to_string(),
@ -34,7 +34,7 @@ pub fn ArticleActions() -> impl IntoView {
} }
} }
}); });
let protect_action = create_action(move |(id, protected): &(i32, bool)| { let protect_action = create_action(move |(id, protected): &(ArticleId, bool)| {
let params = ProtectArticleForm { let params = ProtectArticleForm {
article_id: *id, article_id: *id,
protected: !protected, protected: !protected,

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
common::{ApiConflict, ArticleView, EditArticleForm}, common::{newtypes::ConflictId, ApiConflict, ArticleView, EditArticleForm},
frontend::{ frontend::{
app::GlobalState, app::GlobalState,
components::{ components::{
@ -32,7 +32,7 @@ pub fn EditArticle() -> impl IntoView {
let conflict_id = move || use_params_map().get_untracked().get("conflict_id").cloned(); let conflict_id = move || use_params_map().get_untracked().get("conflict_id").cloned();
if let Some(conflict_id) = conflict_id() { if let Some(conflict_id) = conflict_id() {
create_action(move |conflict_id: &String| { create_action(move |conflict_id: &String| {
let conflict_id: i32 = conflict_id.parse().unwrap(); let conflict_id = ConflictId(conflict_id.parse().unwrap());
async move { async move {
let conflict = GlobalState::api_client() let conflict = GlobalState::api_client()
.get_conflicts() .get_conflicts()

View file

@ -21,7 +21,7 @@ pub fn Conflicts() -> impl IntoView {
let link = format!( let link = format!(
"{}/edit/{}", "{}/edit/{}",
article_link(&c.article), article_link(&c.article),
c.id, c.id.0,
); );
view! { view! {
<li> <li>