mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-28 23:01:09 +00:00
add list of articles with local/all selection
This commit is contained in:
parent
a12895f9bf
commit
4e53b13b70
13 changed files with 112 additions and 24 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -1523,6 +1523,7 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1653,9 +1654,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.65"
|
version = "0.3.67"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
|
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
@ -3692,9 +3693,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.89"
|
version = "0.2.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
|
@ -3702,9 +3703,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.89"
|
version = "0.2.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
|
@ -3729,9 +3730,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.89"
|
version = "0.2.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
|
@ -3739,9 +3740,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.89"
|
version = "0.2.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3752,9 +3753,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.89"
|
version = "0.2.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-streams"
|
name = "wasm-streams"
|
||||||
|
@ -3771,9 +3772,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.65"
|
version = "0.3.67"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
|
checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|
|
@ -68,6 +68,7 @@ console_log = "1.0.0"
|
||||||
time = "0.3.31"
|
time = "0.3.31"
|
||||||
tower = "0.4.13"
|
tower = "0.4.13"
|
||||||
markdown-it = "0.6.0"
|
markdown-it = "0.6.0"
|
||||||
|
web-sys = "0.3.67"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
|
|
|
@ -6,12 +6,12 @@ use crate::backend::error::MyResult;
|
||||||
use crate::backend::federation::activities::create_article::CreateArticle;
|
use crate::backend::federation::activities::create_article::CreateArticle;
|
||||||
use crate::backend::federation::activities::submit_article_update;
|
use crate::backend::federation::activities::submit_article_update;
|
||||||
use crate::backend::utils::generate_article_version;
|
use crate::backend::utils::generate_article_version;
|
||||||
use crate::common::GetArticleData;
|
|
||||||
use crate::common::LocalUserView;
|
use crate::common::LocalUserView;
|
||||||
use crate::common::{ApiConflict, ResolveObject};
|
use crate::common::{ApiConflict, ResolveObject};
|
||||||
use crate::common::{ArticleView, DbArticle, DbEdit};
|
use crate::common::{ArticleView, DbArticle, DbEdit};
|
||||||
use crate::common::{CreateArticleData, EditArticleData, EditVersion, ForkArticleData};
|
use crate::common::{CreateArticleData, EditArticleData, EditVersion, ForkArticleData};
|
||||||
use crate::common::{DbInstance, SearchArticleData};
|
use crate::common::{DbInstance, SearchArticleData};
|
||||||
|
use crate::common::{GetArticleData, ListArticlesData};
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use activitypub_federation::fetch::object_id::ObjectId;
|
use activitypub_federation::fetch::object_id::ObjectId;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
@ -30,6 +30,10 @@ pub(in crate::backend::api) async fn create_article(
|
||||||
data: Data<MyDataHandle>,
|
data: Data<MyDataHandle>,
|
||||||
Form(create_article): Form<CreateArticleData>,
|
Form(create_article): Form<CreateArticleData>,
|
||||||
) -> MyResult<Json<ArticleView>> {
|
) -> MyResult<Json<ArticleView>> {
|
||||||
|
if create_article.title.is_empty() {
|
||||||
|
return Err(anyhow!("Title must not be empty").into());
|
||||||
|
}
|
||||||
|
|
||||||
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
let local_instance = DbInstance::read_local_instance(&data.db_connection)?;
|
||||||
let ap_id = ObjectId::parse(&format!(
|
let ap_id = ObjectId::parse(&format!(
|
||||||
"http://{}:{}/article/{}",
|
"http://{}:{}/article/{}",
|
||||||
|
@ -147,6 +151,15 @@ pub(in crate::backend::api) async fn get_article(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub(in crate::backend::api) async fn list_articles(
|
||||||
|
Query(query): Query<ListArticlesData>,
|
||||||
|
data: Data<MyDataHandle>,
|
||||||
|
) -> MyResult<Json<Vec<DbArticle>>> {
|
||||||
|
let only_local = query.only_local.unwrap_or(false);
|
||||||
|
Ok(Json(DbArticle::read_all(only_local, &data.db_connection)?))
|
||||||
|
}
|
||||||
|
|
||||||
/// Fork a remote article to local instance. This is useful if there are disagreements about
|
/// Fork a remote article to local instance. This is useful if there are disagreements about
|
||||||
/// how an article should be edited.
|
/// how an article should be edited.
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::backend::api::article::{create_article, resolve_article, search_article};
|
use crate::backend::api::article::{
|
||||||
|
create_article, list_articles, resolve_article, search_article,
|
||||||
|
};
|
||||||
use crate::backend::api::article::{edit_article, fork_article, get_article};
|
use crate::backend::api::article::{edit_article, fork_article, get_article};
|
||||||
use crate::backend::api::instance::get_local_instance;
|
use crate::backend::api::instance::get_local_instance;
|
||||||
use crate::backend::api::instance::{follow_instance, resolve_instance};
|
use crate::backend::api::instance::{follow_instance, resolve_instance};
|
||||||
|
@ -36,6 +38,7 @@ pub fn api_routes() -> Router {
|
||||||
"/article",
|
"/article",
|
||||||
get(get_article).post(create_article).patch(edit_article),
|
get(get_article).post(create_article).patch(edit_article),
|
||||||
)
|
)
|
||||||
|
.route("/article/list", get(list_articles))
|
||||||
.route("/article/fork", post(fork_article))
|
.route("/article/fork", post(fork_article))
|
||||||
.route("/article/resolve", get(resolve_article))
|
.route("/article/resolve", get(resolve_article))
|
||||||
.route("/edit_conflicts", get(edit_conflicts))
|
.route("/edit_conflicts", get(edit_conflicts))
|
||||||
|
|
|
@ -118,11 +118,16 @@ impl DbArticle {
|
||||||
.get_result(conn.deref_mut())?)
|
.get_result(conn.deref_mut())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_all_local(conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
pub fn read_all(only_local: bool, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
||||||
let mut conn = conn.lock().unwrap();
|
let mut conn = conn.lock().unwrap();
|
||||||
Ok(article::table
|
let query = article::table.into_boxed();
|
||||||
|
Ok(if only_local {
|
||||||
|
query
|
||||||
.filter(article::dsl::local.eq(true))
|
.filter(article::dsl::local.eq(true))
|
||||||
.get_results(conn.deref_mut())?)
|
.get_results(conn.deref_mut())?
|
||||||
|
} else {
|
||||||
|
query.get_results(conn.deref_mut())?
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search(query: &str, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
pub fn search(query: &str, conn: &Mutex<PgConnection>) -> MyResult<Vec<Self>> {
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl Collection for DbArticleCollection {
|
||||||
owner: &Self::Owner,
|
owner: &Self::Owner,
|
||||||
data: &Data<Self::DataType>,
|
data: &Data<Self::DataType>,
|
||||||
) -> Result<Self::Kind, Self::Error> {
|
) -> Result<Self::Kind, Self::Error> {
|
||||||
let local_articles = DbArticle::read_all_local(&data.db_connection)?;
|
let local_articles = DbArticle::read_all(true, &data.db_connection)?;
|
||||||
let articles = future::try_join_all(
|
let articles = future::try_join_all(
|
||||||
local_articles
|
local_articles
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -21,6 +21,11 @@ pub struct GetArticleData {
|
||||||
pub id: Option<i32>,
|
pub id: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
pub struct ListArticlesData {
|
||||||
|
pub only_local: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
#[cfg_attr(feature = "ssr", derive(Queryable))]
|
#[cfg_attr(feature = "ssr", derive(Queryable))]
|
||||||
#[cfg_attr(feature = "ssr", diesel(table_name = article, check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "ssr", diesel(table_name = article, check_for_backend(diesel::pg::Pg)))]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::common::ApiConflict;
|
|
||||||
use crate::common::ResolveObject;
|
use crate::common::ResolveObject;
|
||||||
|
use crate::common::{ApiConflict, ListArticlesData};
|
||||||
use crate::common::{ArticleView, LoginUserData, RegisterUserData};
|
use crate::common::{ArticleView, LoginUserData, RegisterUserData};
|
||||||
use crate::common::{CreateArticleData, EditArticleData, ForkArticleData, LocalUserView};
|
use crate::common::{CreateArticleData, EditArticleData, ForkArticleData, LocalUserView};
|
||||||
use crate::common::{DbArticle, GetArticleData};
|
use crate::common::{DbArticle, GetArticleData};
|
||||||
|
@ -36,8 +36,11 @@ impl ApiClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_article(&self, data: GetArticleData) -> MyResult<ArticleView> {
|
pub async fn get_article(&self, data: GetArticleData) -> MyResult<ArticleView> {
|
||||||
self.get_query::<ArticleView, _>("article", Some(data))
|
self.get_query("article", Some(data)).await
|
||||||
.await
|
}
|
||||||
|
|
||||||
|
pub async fn list_articles(&self, data: ListArticlesData) -> MyResult<Vec<DbArticle>> {
|
||||||
|
self.get_query("article/list", Some(data)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn register(&self, register_form: RegisterUserData) -> MyResult<LocalUserView> {
|
pub async fn register(&self, register_form: RegisterUserData) -> MyResult<LocalUserView> {
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::frontend::components::nav::Nav;
|
||||||
use crate::frontend::pages::article::create::CreateArticle;
|
use crate::frontend::pages::article::create::CreateArticle;
|
||||||
use crate::frontend::pages::article::edit::EditArticle;
|
use crate::frontend::pages::article::edit::EditArticle;
|
||||||
use crate::frontend::pages::article::history::ArticleHistory;
|
use crate::frontend::pages::article::history::ArticleHistory;
|
||||||
|
use crate::frontend::pages::article::list::ListArticles;
|
||||||
use crate::frontend::pages::article::read::ReadArticle;
|
use crate::frontend::pages::article::read::ReadArticle;
|
||||||
use crate::frontend::pages::diff::EditDiff;
|
use crate::frontend::pages::diff::EditDiff;
|
||||||
use crate::frontend::pages::login::Login;
|
use crate::frontend::pages::login::Login;
|
||||||
|
@ -76,6 +77,7 @@ pub fn App() -> impl IntoView {
|
||||||
<Route path="/article/:title/history" view=ArticleHistory/>
|
<Route path="/article/:title/history" view=ArticleHistory/>
|
||||||
<Route path="/article/:title/diff/:hash" view=EditDiff/>
|
<Route path="/article/:title/diff/:hash" view=EditDiff/>
|
||||||
<Route path="/article/create" view=CreateArticle/>
|
<Route path="/article/create" view=CreateArticle/>
|
||||||
|
<Route path="/article/list" view=ListArticles/>
|
||||||
<Route path={Page::Login.path()} view=Login/>
|
<Route path={Page::Login.path()} view=Login/>
|
||||||
<Route path={Page::Register.path()} view=Register/>
|
<Route path={Page::Register.path()} view=Register/>
|
||||||
<Route path="/search" view=Search/>
|
<Route path="/search" view=Search/>
|
||||||
|
|
|
@ -18,6 +18,9 @@ pub fn Nav() -> impl IntoView {
|
||||||
<li>
|
<li>
|
||||||
<A href="/">"Main Page"</A>
|
<A href="/">"Main Page"</A>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<A href="/article/list">"List Articles"</A>
|
||||||
|
</li>
|
||||||
<Show
|
<Show
|
||||||
when=move || global_state.with(|state| state.my_profile.is_some())>
|
when=move || global_state.with(|state| state.my_profile.is_some())>
|
||||||
<li>
|
<li>
|
||||||
|
|
45
src/frontend/pages/article/list.rs
Normal file
45
src/frontend/pages/article/list.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use crate::common::ListArticlesData;
|
||||||
|
use crate::frontend::app::GlobalState;
|
||||||
|
use leptos::*;
|
||||||
|
use web_sys::wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn ListArticles() -> impl IntoView {
|
||||||
|
let (only_local, set_only_local) = create_signal(true);
|
||||||
|
let articles = create_resource(
|
||||||
|
move || only_local.get(),
|
||||||
|
|only_local| async move {
|
||||||
|
GlobalState::api_client()
|
||||||
|
.list_articles(ListArticlesData {
|
||||||
|
only_local: Some(only_local),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Suspense fallback=|| view! { "Loading..." }>
|
||||||
|
<fieldset on:input=move |ev| {
|
||||||
|
let val = ev
|
||||||
|
.target()
|
||||||
|
.unwrap()
|
||||||
|
.unchecked_into::<web_sys::HtmlInputElement>()
|
||||||
|
.id();
|
||||||
|
let is_local_only = val == "only-local";
|
||||||
|
set_only_local.update(|p| *p = is_local_only);
|
||||||
|
}>
|
||||||
|
<input type="radio" name="listing-type" id="only-local" checked />
|
||||||
|
<label for="only-local">Only Local</label>
|
||||||
|
<input type="radio" name="listing-type" id="all"/>
|
||||||
|
<label for="all">All</label>
|
||||||
|
</fieldset>
|
||||||
|
<ul> {
|
||||||
|
move || articles.get().map(|a|
|
||||||
|
a.into_iter().map(|a| view! {
|
||||||
|
<li><a href=format!("/article/{}", a.title)>{a.title()}</a></li>
|
||||||
|
}).collect::<Vec<_>>())
|
||||||
|
} </ul>
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod create;
|
pub mod create;
|
||||||
pub mod edit;
|
pub mod edit;
|
||||||
pub mod history;
|
pub mod history;
|
||||||
|
pub mod list;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
|
|
|
@ -59,6 +59,12 @@ async fn test_create_read_and_edit_local_article() -> MyResult<()> {
|
||||||
assert_eq!(1, search_res.len());
|
assert_eq!(1, search_res.len());
|
||||||
assert_eq!(edit_res.article, search_res[0]);
|
assert_eq!(edit_res.article, search_res[0]);
|
||||||
|
|
||||||
|
let list_articles = data.alpha.list_articles().await?;
|
||||||
|
dbg!(&list_articles);
|
||||||
|
// default main page and article created by this test
|
||||||
|
assert_eq!(2, list_articles.len());
|
||||||
|
assert_eq!(edit_res.article, list_articles[1]);
|
||||||
|
|
||||||
data.stop()
|
data.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue