mirror of
https://github.com/Nutomic/ibis.git
synced 2024-11-22 18:51:08 +00:00
Merge branch 'dessalines-add_leptosfmt'
This commit is contained in:
commit
706713be9d
25 changed files with 703 additions and 482 deletions
2
.leptosfmt.toml
Normal file
2
.leptosfmt.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
max_width = 100
|
||||||
|
attr_value_brace_style = "WhenRequired" # "Always", "AlwaysUnlessLit", "WhenRequired" or "Preserve"
|
|
@ -1,5 +1,6 @@
|
||||||
variables:
|
variables:
|
||||||
- &rust_image "rust:1.75"
|
- &rust_image "rust:1.75"
|
||||||
|
- &install_binstall "wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz && tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz && cp cargo-binstall /usr/local/cargo/bin"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
cargo_fmt:
|
cargo_fmt:
|
||||||
|
@ -10,6 +11,20 @@ steps:
|
||||||
commands:
|
commands:
|
||||||
- rustup component add rustfmt
|
- rustup component add rustfmt
|
||||||
- cargo +nightly fmt -- --check
|
- cargo +nightly fmt -- --check
|
||||||
|
|
||||||
|
leptos_fmt:
|
||||||
|
image: *rust_image
|
||||||
|
commands:
|
||||||
|
- *install_binstall
|
||||||
|
- cargo binstall -y leptosfmt
|
||||||
|
- leptosfmt -c .leptosfmt.toml --check src
|
||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
|
||||||
|
toml_fmt:
|
||||||
|
image: tamasfe/taplo:0.8.1
|
||||||
|
commands:
|
||||||
|
- taplo format --check
|
||||||
when:
|
when:
|
||||||
- event: pull_request
|
- event: pull_request
|
||||||
|
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1587,7 +1587,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ibis"
|
name = "ibis"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
|
@ -45,7 +45,7 @@ diesel = { version = "2.1.4", features = [
|
||||||
"postgres",
|
"postgres",
|
||||||
"chrono",
|
"chrono",
|
||||||
"uuid",
|
"uuid",
|
||||||
"r2d2"
|
"r2d2",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
diesel-derive-newtype = { version = "2.1.0", optional = true }
|
diesel-derive-newtype = { version = "2.1.0", optional = true }
|
||||||
diesel_migrations = { version = "2.1.0", optional = true }
|
diesel_migrations = { version = "2.1.0", optional = true }
|
||||||
|
|
|
@ -94,7 +94,7 @@ pub fn App() -> impl IntoView {
|
||||||
<Stylesheet id="simple" href="/assets/simple.css"/>
|
<Stylesheet id="simple" href="/assets/simple.css"/>
|
||||||
<Stylesheet id="ibis" href="/assets/ibis.css"/>
|
<Stylesheet id="ibis" href="/assets/ibis.css"/>
|
||||||
<Router>
|
<Router>
|
||||||
<Nav />
|
<Nav/>
|
||||||
<main>
|
<main>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" view=ReadArticle/>
|
<Route path="/" view=ReadArticle/>
|
||||||
|
|
|
@ -13,41 +13,60 @@ use leptos_router::*;
|
||||||
pub fn ArticleNav(article: Resource<Option<String>, ArticleView>) -> impl IntoView {
|
pub fn ArticleNav(article: Resource<Option<String>, ArticleView>) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<Suspense>
|
<Suspense>
|
||||||
{move || article.get().map(|article_| {
|
{move || {
|
||||||
let instance = create_local_resource(move || article_.article.instance_id, move |instance_id| async move {
|
article
|
||||||
|
.get()
|
||||||
|
.map(|article_| {
|
||||||
|
let instance = create_local_resource(
|
||||||
|
move || article_.article.instance_id,
|
||||||
|
move |instance_id| async move {
|
||||||
let form = GetInstance {
|
let form = GetInstance {
|
||||||
id: Some(instance_id)
|
id: Some(instance_id),
|
||||||
};
|
};
|
||||||
GlobalState::api_client()
|
GlobalState::api_client().get_instance(&form).await.unwrap()
|
||||||
.get_instance(&form)
|
},
|
||||||
.await
|
);
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
|
let global_state = use_context::<RwSignal<GlobalState>>().unwrap();
|
||||||
let article_link = article_link(&article_.article);
|
let article_link = article_link(&article_.article);
|
||||||
let article_link_ = article_link.clone();
|
let article_link_ = article_link.clone();
|
||||||
let protected = article_.article.protected;
|
let protected = article_.article.protected;
|
||||||
view!{
|
view! {
|
||||||
<nav class="inner">
|
<nav class="inner">
|
||||||
<A href=article_link.clone()>"Read"</A>
|
<A href=article_link.clone()>"Read"</A>
|
||||||
<A href={format!("{article_link}/history")}>"History"</A>
|
<A href=format!("{article_link}/history")>"History"</A>
|
||||||
<Show when=move || global_state.with(|state| {
|
<Show when=move || {
|
||||||
let is_admin = state.my_profile.as_ref().map(|p| p.local_user.admin).unwrap_or(false);
|
global_state
|
||||||
state.my_profile.is_some() && can_edit_article(&article_.article, is_admin).is_ok()
|
.with(|state| {
|
||||||
})>
|
let is_admin = state
|
||||||
<A href={format!("{article_link}/edit")}>"Edit"</A>
|
.my_profile
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.local_user.admin)
|
||||||
|
.unwrap_or(false);
|
||||||
|
state.my_profile.is_some()
|
||||||
|
&& can_edit_article(&article_.article, is_admin).is_ok()
|
||||||
|
})
|
||||||
|
}>
|
||||||
|
<A href=format!("{article_link}/edit")>"Edit"</A>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when=move || global_state.with(|state| state.my_profile.is_some())>
|
<Show when=move || global_state.with(|state| state.my_profile.is_some())>
|
||||||
<A href={format!("{article_link_}/actions")}>"Actions"</A>
|
<A href=format!("{article_link_}/actions")>"Actions"</A>
|
||||||
{instance.get().map(|i|
|
{instance
|
||||||
view!{ <InstanceFollowButton instance=i.instance.clone() /> }
|
.get()
|
||||||
)}
|
.map(|i| {
|
||||||
|
view! { <InstanceFollowButton instance=i.instance.clone()/> }
|
||||||
|
})}
|
||||||
|
|
||||||
</Show>
|
</Show>
|
||||||
<Show when=move || protected>
|
<Show when=move || protected>
|
||||||
<span title="Article can only be edited by local admins">"Protected"</span>
|
<span title="Article can only be edited by local admins">
|
||||||
|
"Protected"
|
||||||
|
</span>
|
||||||
</Show>
|
</Show>
|
||||||
</nav>
|
</nav>
|
||||||
}})}
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub fn CredentialsForm(
|
||||||
view! { <p style="color:red;">{err}</p> }
|
view! { <p style="color:red;">{err}</p> }
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
|
@ -36,11 +37,13 @@ pub fn CredentialsForm(
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_username.update(|v| *v = val);
|
set_username.update(|v| *v = val);
|
||||||
}
|
}
|
||||||
|
|
||||||
on:change=move |ev| {
|
on:change=move |ev| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_username.update(|v| *v = val);
|
set_username.update(|v| *v = val);
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
required
|
required
|
||||||
|
@ -57,15 +60,18 @@ pub fn CredentialsForm(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
on:change=move |ev| {
|
on:change=move |ev| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_password.update(|p| *p = val);
|
set_password.update(|p| *p = val);
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
prop:disabled=move || button_is_disabled.get()
|
prop:disabled=move || button_is_disabled.get()
|
||||||
on:click=move |_| dispatch_action()>
|
on:click=move |_| dispatch_action()
|
||||||
|
>
|
||||||
{action_label}
|
{action_label}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,9 +30,11 @@ pub fn InstanceFollowButton(instance: DbInstance) -> impl IntoView {
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<button on:click=move |_| follow_action.dispatch(instance.id)
|
<button
|
||||||
|
on:click=move |_| follow_action.dispatch(instance.id)
|
||||||
prop:disabled=move || is_following
|
prop:disabled=move || is_following
|
||||||
prop:hidden=move || instance.local>
|
prop:hidden=move || instance.local
|
||||||
|
>
|
||||||
{follow_text}
|
{follow_text}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,7 @@ pub fn Nav() -> impl IntoView {
|
||||||
<li>
|
<li>
|
||||||
<A href="/article/list">"List Articles"</A>
|
<A href="/article/list">"List Articles"</A>
|
||||||
</li>
|
</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>
|
||||||
<A href="/article/create">"Create Article"</A>
|
<A href="/article/create">"Create Article"</A>
|
||||||
</li>
|
</li>
|
||||||
|
@ -47,12 +46,16 @@ pub fn Nav() -> impl IntoView {
|
||||||
navigate(&format!("/search?query={query}"), Default::default());
|
navigate(&format!("/search?query={query}"), Default::default());
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
<input type="text" placeholder="Search"
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
prop:value=search_query
|
prop:value=search_query
|
||||||
on:keyup=move |ev: ev::KeyboardEvent| {
|
on:keyup=move |ev: ev::KeyboardEvent| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_search_query.update(|v| *v = val);
|
set_search_query.update(|v| *v = val);
|
||||||
} />
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<button>Go</button>
|
<button>Go</button>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
|
@ -71,20 +74,24 @@ pub fn Nav() -> impl IntoView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
{
|
{
|
||||||
let my_profile = global_state.with(|state| state.my_profile.clone().unwrap());
|
let my_profile = global_state.with(|state| state.my_profile.clone().unwrap());
|
||||||
let profile_link = format!("/user/{}", my_profile.person.username);
|
let profile_link = format!("/user/{}", my_profile.person.username);
|
||||||
view ! {
|
view! {
|
||||||
<p>"Logged in as "
|
<p>
|
||||||
<a href=profile_link style="border: none; padding: 0; color: var(--accent) !important;">
|
"Logged in as "
|
||||||
|
<a
|
||||||
|
href=profile_link
|
||||||
|
style="border: none; padding: 0; color: var(--accent) !important;"
|
||||||
|
>
|
||||||
{my_profile.person.username}
|
{my_profile.person.username}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<button on:click=move |_| logout_action.dispatch(())>
|
<button on:click=move |_| logout_action.dispatch(())>Logout</button>
|
||||||
Logout
|
|
||||||
</button>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</Show>
|
</Show>
|
||||||
</nav>
|
</nav>
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,5 @@ fn user_title(person: &DbPerson) -> String {
|
||||||
|
|
||||||
fn user_link(person: &DbPerson) -> impl IntoView {
|
fn user_link(person: &DbPerson) -> impl IntoView {
|
||||||
let creator_path = format!("/user/{}", person.username);
|
let creator_path = format!("/user/{}", person.username);
|
||||||
view! {
|
view! { <a href=creator_path>{user_title(person)}</a> }
|
||||||
<a href={creator_path}>{user_title(person)}</a>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,13 @@ pub fn ArticleActions() -> impl IntoView {
|
||||||
});
|
});
|
||||||
view! {
|
view! {
|
||||||
<ArticleNav article=article/>
|
<ArticleNav article=article/>
|
||||||
<Suspense fallback=|| view! { "Loading..." }> {
|
<Suspense fallback=|| {
|
||||||
move || article.get().map(|article|
|
view! { "Loading..." }
|
||||||
|
}>
|
||||||
|
{move || {
|
||||||
|
article
|
||||||
|
.get()
|
||||||
|
.map(|article| {
|
||||||
view! {
|
view! {
|
||||||
<div class="item-view">
|
<div class="item-view">
|
||||||
<h1>{article_title(&article.article)}</h1>
|
<h1>{article_title(&article.article)}</h1>
|
||||||
|
@ -65,13 +70,21 @@ pub fn ArticleActions() -> impl IntoView {
|
||||||
view! { <p style="color:red;">{err}</p> }
|
view! { <p style="color:red;">{err}</p> }
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
<Show
|
|
||||||
when=move || global_state.with(|state| {
|
<Show when=move || {
|
||||||
state.my_profile.as_ref().map(|p| p.local_user.admin).unwrap_or_default()
|
global_state
|
||||||
&& article.article.local
|
.with(|state| {
|
||||||
})>
|
state
|
||||||
<button
|
.my_profile
|
||||||
on:click=move |_| protect_action.dispatch((article.article.id, article.article.protected))>Toggle Article Protection</button>
|
.as_ref()
|
||||||
|
.map(|p| p.local_user.admin)
|
||||||
|
.unwrap_or_default() && article.article.local
|
||||||
|
})
|
||||||
|
}>
|
||||||
|
<button on:click=move |_| {
|
||||||
|
protect_action
|
||||||
|
.dispatch((article.article.id, article.article.protected))
|
||||||
|
}>Toggle Article Protection</button>
|
||||||
<p>"Protect a local article so that only admins can edit it"</p>
|
<p>"Protect a local article so that only admins can edit it"</p>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when=move || !article.article.local>
|
<Show when=move || !article.article.local>
|
||||||
|
@ -80,21 +93,31 @@ pub fn ArticleActions() -> impl IntoView {
|
||||||
on:keyup=move |ev: ev::KeyboardEvent| {
|
on:keyup=move |ev: ev::KeyboardEvent| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_new_title.update(|v| *v = val);
|
set_new_title.update(|v| *v = val);
|
||||||
} />
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled=move || new_title.get().is_empty()
|
disabled=move || new_title.get().is_empty()
|
||||||
on:click=move |_| fork_action.dispatch((article.article.id, new_title.get()))>Fork Article</button>
|
on:click=move |_| {
|
||||||
|
fork_action.dispatch((article.article.id, new_title.get()))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
Fork Article
|
||||||
|
</button>
|
||||||
<p>
|
<p>
|
||||||
"You can fork a remote article to the local instance. This is useful if the original
|
"You can fork a remote article to the local instance. This is useful if the original
|
||||||
instance is dead, or if there are disagreements how the article should be written."
|
instance is dead, or if there are disagreements how the article should be written."
|
||||||
</p>
|
</p>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<Show when=move || fork_response.get().is_some()>
|
<Show when=move || fork_response.get().is_some()>
|
||||||
<Redirect path={article_link(&fork_response.get().unwrap())}/>
|
<Redirect path=article_link(&fork_response.get().unwrap())/>
|
||||||
</Show>
|
</Show>
|
||||||
<p>"TODO: add option for admin to delete article etc"</p>
|
<p>"TODO: add option for admin to delete article etc"</p>
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,12 +56,21 @@ pub fn CreateArticle() -> impl IntoView {
|
||||||
set_title.update(|v| *v = val);
|
set_title.update(|v| *v = val);
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<textarea placeholder="Article text..." on:keyup=move |ev| {
|
|
||||||
|
<textarea
|
||||||
|
placeholder="Article text..."
|
||||||
|
on:keyup=move |ev| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_text.update(|p| *p = val);
|
set_text.update(|p| *p = val);
|
||||||
} >
|
}
|
||||||
|
>
|
||||||
</textarea>
|
</textarea>
|
||||||
<div><a href="https://commonmark.org/help/" target="blank_">Markdown</a>" formatting is supported"</div>
|
<div>
|
||||||
|
<a href="https://commonmark.org/help/" target="blank_">
|
||||||
|
Markdown
|
||||||
|
</a>
|
||||||
|
" formatting is supported"
|
||||||
|
</div>
|
||||||
{move || {
|
{move || {
|
||||||
create_error
|
create_error
|
||||||
.get()
|
.get()
|
||||||
|
@ -69,21 +78,28 @@ pub fn CreateArticle() -> impl IntoView {
|
||||||
view! { <p style="color:red;">{err}</p> }
|
view! { <p style="color:red;">{err}</p> }
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
<input type="text"
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
placeholder="Edit summary"
|
placeholder="Edit summary"
|
||||||
on:keyup=move |ev| {
|
on:keyup=move |ev| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_summary.update(|p| *p = val);
|
set_summary.update(|p| *p = val);
|
||||||
}/>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
prop:disabled=move || button_is_disabled.get()
|
prop:disabled=move || button_is_disabled.get()
|
||||||
on:click=move |_| submit_action.dispatch((title.get(), text.get(), summary.get()))>
|
on:click=move |_| submit_action.dispatch((title.get(), text.get(), summary.get()))
|
||||||
|
>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}>
|
}
|
||||||
<Redirect path={format!("/article/{}", title.get().replace(' ', "_"))} />
|
>
|
||||||
|
|
||||||
|
<Redirect path=format!("/article/{}", title.get().replace(' ', "_"))/>
|
||||||
</Show>
|
</Show>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,16 +105,21 @@ pub fn EditArticle() -> impl IntoView {
|
||||||
when=move || edit_response.get() == EditResponse::Success
|
when=move || edit_response.get() == EditResponse::Success
|
||||||
fallback=move || {
|
fallback=move || {
|
||||||
view! {
|
view! {
|
||||||
<Suspense fallback=|| view! { "Loading..." }> {
|
<Suspense fallback=|| {
|
||||||
move || article.get().map(|mut article| {
|
view! { "Loading..." }
|
||||||
|
}>
|
||||||
|
{move || {
|
||||||
|
article
|
||||||
|
.get()
|
||||||
|
.map(|mut article| {
|
||||||
if let EditResponse::Conflict(conflict) = edit_response.get() {
|
if let EditResponse::Conflict(conflict) = edit_response.get() {
|
||||||
article.article.text = conflict.three_way_merge;
|
article.article.text = conflict.three_way_merge;
|
||||||
set_summary.set(conflict.summary);
|
set_summary.set(conflict.summary);
|
||||||
}
|
}
|
||||||
// set initial text, otherwise submit with no changes results in empty text
|
|
||||||
set_text.set(article.article.text.clone());
|
set_text.set(article.article.text.clone());
|
||||||
let article_ = article.clone();
|
let article_ = article.clone();
|
||||||
view! {
|
view! {
|
||||||
|
// set initial text, otherwise submit with no changes results in empty text
|
||||||
<div class="item-view">
|
<div class="item-view">
|
||||||
<h1>{article_title(&article.article)}</h1>
|
<h1>{article_title(&article.article)}</h1>
|
||||||
{move || {
|
{move || {
|
||||||
|
@ -124,31 +129,52 @@ pub fn EditArticle() -> impl IntoView {
|
||||||
view! { <p style="color:red;">{err}</p> }
|
view! { <p style="color:red;">{err}</p> }
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
|
||||||
<textarea on:keyup=move |ev| {
|
<textarea on:keyup=move |ev| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_text.update(|p| *p = val);
|
set_text.update(|p| *p = val);
|
||||||
}>
|
}>{article.article.text.clone()}</textarea>
|
||||||
{article.article.text.clone()}
|
<div>
|
||||||
</textarea>
|
<a href="https://commonmark.org/help/" target="blank_">
|
||||||
<div><a href="https://commonmark.org/help/" target="blank_">Markdown</a>" formatting is supported"</div>
|
Markdown
|
||||||
<input type="text"
|
</a>
|
||||||
|
" formatting is supported"
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
placeholder="Edit summary"
|
placeholder="Edit summary"
|
||||||
value={summary.get_untracked()}
|
value=summary.get_untracked()
|
||||||
on:keyup=move |ev| {
|
on:keyup=move |ev| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
set_summary.update(|p| *p = val);
|
set_summary.update(|p| *p = val);
|
||||||
}/>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
prop:disabled=move || button_is_disabled.get()
|
prop:disabled=move || button_is_disabled.get()
|
||||||
on:click=move |_| submit_action.dispatch((text.get(), summary.get(), article_.clone(), edit_response.get()))>
|
on:click=move |_| {
|
||||||
|
submit_action
|
||||||
|
.dispatch((
|
||||||
|
text.get(),
|
||||||
|
summary.get(),
|
||||||
|
article_.clone(),
|
||||||
|
edit_response.get(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}}
|
||||||
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}}>
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
Edit successful!
|
Edit successful!
|
||||||
</Show>
|
</Show>
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,22 +13,48 @@ pub fn ArticleHistory() -> impl IntoView {
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<ArticleNav article=article/>
|
<ArticleNav article=article/>
|
||||||
<Suspense fallback=|| view! { "Loading..." }> {
|
<Suspense fallback=|| {
|
||||||
move || article.get().map(|article| {
|
view! { "Loading..." }
|
||||||
|
}>
|
||||||
|
{move || {
|
||||||
|
article
|
||||||
|
.get()
|
||||||
|
.map(|article| {
|
||||||
view! {
|
view! {
|
||||||
<div class="item-view">
|
<div class="item-view">
|
||||||
<h1>{article_title(&article.article)}</h1>
|
<h1>{article_title(&article.article)}</h1>
|
||||||
{
|
|
||||||
article.edits.into_iter().rev().map(|edit| {
|
{article
|
||||||
let path = format!("/article/{}@{}/diff/{}", article.article.title, extract_domain(&article.article.ap_id), edit.edit.hash.0);
|
.edits
|
||||||
let label = format!("{} ({})", edit.edit.summary, edit.edit.created.to_rfc2822());
|
.into_iter()
|
||||||
view! {<li><a href={path}>{label}</a>" by "{user_link(&edit.creator)}</li> }
|
.rev()
|
||||||
}).collect::<Vec<_>>()
|
.map(|edit| {
|
||||||
|
let path = format!(
|
||||||
|
"/article/{}@{}/diff/{}",
|
||||||
|
article.article.title,
|
||||||
|
extract_domain(&article.article.ap_id),
|
||||||
|
edit.edit.hash.0,
|
||||||
|
);
|
||||||
|
let label = format!(
|
||||||
|
"{} ({})",
|
||||||
|
edit.edit.summary,
|
||||||
|
edit.edit.created.to_rfc2822(),
|
||||||
|
);
|
||||||
|
view! {
|
||||||
|
<li>
|
||||||
|
<a href=path>{label}</a>
|
||||||
|
" by "
|
||||||
|
{user_link(&edit.creator)}
|
||||||
|
</li>
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}}
|
||||||
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,25 +24,33 @@ pub fn ListArticles() -> impl IntoView {
|
||||||
<h1>Most recently edited Articles</h1>
|
<h1>Most recently edited Articles</h1>
|
||||||
<Suspense fallback=|| view! { "Loading..." }>
|
<Suspense fallback=|| view! { "Loading..." }>
|
||||||
<fieldset on:input=move |ev| {
|
<fieldset on:input=move |ev| {
|
||||||
let val = ev
|
let val = ev.target().unwrap().unchecked_into::<web_sys::HtmlInputElement>().id();
|
||||||
.target()
|
|
||||||
.unwrap()
|
|
||||||
.unchecked_into::<web_sys::HtmlInputElement>()
|
|
||||||
.id();
|
|
||||||
let is_local_only = val == "only-local";
|
let is_local_only = val == "only-local";
|
||||||
set_only_local.update(|p| *p = is_local_only);
|
set_only_local.update(|p| *p = is_local_only);
|
||||||
}>
|
}>
|
||||||
<input type="radio" name="listing-type" id="only-local" />
|
<input type="radio" name="listing-type" id="only-local"/>
|
||||||
<label for="only-local">Only Local</label>
|
<label for="only-local">Only Local</label>
|
||||||
<input type="radio" name="listing-type" id="all" checked/>
|
<input type="radio" name="listing-type" id="all" checked/>
|
||||||
<label for="all">All</label>
|
<label for="all">All</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<ul> {
|
<ul>
|
||||||
move || articles.get().map(|a|
|
{move || {
|
||||||
a.into_iter().map(|a| view! {
|
articles
|
||||||
<li><a href=article_link(&a)>{article_title(&a)}</a></li>
|
.get()
|
||||||
}).collect::<Vec<_>>())
|
.map(|a| {
|
||||||
} </ul>
|
a.into_iter()
|
||||||
|
.map(|a| {
|
||||||
|
view! {
|
||||||
|
<li>
|
||||||
|
<a href=article_link(&a)>{article_title(&a)}</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
|
||||||
|
</ul>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,26 @@ pub fn ReadArticle() -> impl IntoView {
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<ArticleNav article=article/>
|
<ArticleNav article=article/>
|
||||||
<Suspense fallback=|| view! { "Loading..." }> {
|
<Suspense fallback=|| {
|
||||||
|
view! { "Loading..." }
|
||||||
|
}>
|
||||||
|
|
||||||
|
{
|
||||||
let parser = markdown_parser();
|
let parser = markdown_parser();
|
||||||
move || article.get().map(|article|
|
move || {
|
||||||
|
article
|
||||||
|
.get()
|
||||||
|
.map(|article| {
|
||||||
view! {
|
view! {
|
||||||
<div class="item-view">
|
<div class="item-view">
|
||||||
<h1>{article_title(&article.article)}</h1>
|
<h1>{article_title(&article.article)}</h1>
|
||||||
<div inner_html={parser.parse(&article.article.text).render()}/>
|
<div inner_html=parser.parse(&article.article.text).render()></div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,14 +11,25 @@ pub fn Conflicts() -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<h1>Your unresolved edit conflicts</h1>
|
<h1>Your unresolved edit conflicts</h1>
|
||||||
<Suspense fallback=|| view! { "Loading..." }>
|
<Suspense fallback=|| view! { "Loading..." }>
|
||||||
<ul> {
|
<ul>
|
||||||
move || conflicts.get().map(|c|
|
{move || {
|
||||||
c.into_iter().map(|c| {
|
conflicts
|
||||||
|
.get()
|
||||||
|
.map(|c| {
|
||||||
|
c.into_iter()
|
||||||
|
.map(|c| {
|
||||||
let link = format!("{}/edit/{}", article_link(&c.article), c.id);
|
let link = format!("{}/edit/{}", article_link(&c.article), c.id);
|
||||||
view! {
|
view! {
|
||||||
<li><a href=link>{article_title(&c.article)}" - "{c.summary}</a></li>
|
<li>
|
||||||
}}).collect::<Vec<_>>())
|
<a href=link>{article_title(&c.article)} " - " {c.summary}</a>
|
||||||
} </ul>
|
</li>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
|
||||||
|
</ul>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,25 +9,35 @@ pub fn EditDiff() -> impl IntoView {
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<ArticleNav article=article/>
|
<ArticleNav article=article/>
|
||||||
<Suspense fallback=|| view! { "Loading..." }> {
|
<Suspense fallback=|| {
|
||||||
move || article.get().map(|article| {
|
view! { "Loading..." }
|
||||||
let hash = params
|
}>
|
||||||
.get_untracked()
|
{move || {
|
||||||
.get("hash")
|
article
|
||||||
.cloned().unwrap();
|
.get()
|
||||||
let edit = article.edits.iter().find(|e| e.edit.hash.0.to_string() == hash).unwrap();
|
.map(|article| {
|
||||||
let label = format!("{} ({})", edit.edit.summary, edit.edit.created.to_rfc2822());
|
let hash = params.get_untracked().get("hash").cloned().unwrap();
|
||||||
|
let edit = article
|
||||||
|
.edits
|
||||||
|
.iter()
|
||||||
|
.find(|e| e.edit.hash.0.to_string() == hash)
|
||||||
|
.unwrap();
|
||||||
|
let label = format!(
|
||||||
|
"{} ({})",
|
||||||
|
edit.edit.summary,
|
||||||
|
edit.edit.created.to_rfc2822(),
|
||||||
|
);
|
||||||
view! {
|
view! {
|
||||||
<div class="item-view">
|
<div class="item-view">
|
||||||
<h1>{article.article.title.replace('_', " ")}</h1>
|
<h1>{article.article.title.replace('_', " ")}</h1>
|
||||||
<h2>{label}</h2>
|
<h2>{label}</h2>
|
||||||
<p>"by "{user_link(&edit.creator)}</p>
|
<p>"by " {user_link(&edit.creator)}</p>
|
||||||
<pre>{edit.edit.diff.clone()}</pre>
|
<pre>{edit.edit.diff.clone()}</pre>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}}
|
||||||
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,22 +20,32 @@ pub fn InstanceDetails() -> impl IntoView {
|
||||||
});
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Suspense fallback=|| view! { "Loading..." }> {
|
<Suspense fallback=|| {
|
||||||
move || instance_profile.get().map(|instance: DbInstance| {
|
view! { "Loading..." }
|
||||||
|
}>
|
||||||
|
{move || {
|
||||||
|
instance_profile
|
||||||
|
.get()
|
||||||
|
.map(|instance: DbInstance| {
|
||||||
let instance_ = instance.clone();
|
let instance_ = instance.clone();
|
||||||
view! {
|
view! {
|
||||||
<h1>{instance.domain}</h1>
|
<h1>{instance.domain}</h1>
|
||||||
|
|
||||||
<Show when=move || global_state.with(|state| state.my_profile.is_some())>
|
<Show when=move || global_state.with(|state| state.my_profile.is_some())>
|
||||||
<InstanceFollowButton instance=instance_.clone() />
|
<InstanceFollowButton instance=instance_.clone()/>
|
||||||
</Show>
|
</Show>
|
||||||
<p>Follow the instance so that new edits are federated to your instance.</p>
|
<p>Follow the instance so that new edits are federated to your instance.</p>
|
||||||
<p>"TODO: show a list of articles from the instance. For now you can use the "<a href="/article/list">Article list</a>.</p>
|
<p>
|
||||||
|
"TODO: show a list of articles from the instance. For now you can use the "
|
||||||
|
<a href="/article/list">Article list</a> .
|
||||||
|
</p>
|
||||||
<hr/>
|
<hr/>
|
||||||
<h2>"Description:"</h2>
|
<h2>"Description:"</h2>
|
||||||
<div>{instance.description}</div>
|
<div>{instance.description}</div>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}</Suspense>
|
}}
|
||||||
|
|
||||||
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ pub fn Login() -> impl IntoView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Redirect path="/"/>
|
<Redirect path="/"/>
|
||||||
</Show>
|
</Show>
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ pub fn Register() -> impl IntoView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
<p>"You have successfully registered."</p>
|
<p>"You have successfully registered."</p>
|
||||||
</Show>
|
</Show>
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,45 +52,67 @@ pub fn Search() -> impl IntoView {
|
||||||
});
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<h1>"Search results for "{query}</h1>
|
<h1>"Search results for " {query}</h1>
|
||||||
<Suspense fallback=|| view! { "Loading..." }> {
|
<Suspense fallback=|| {
|
||||||
move || search_results.get().map(move |search_results| {
|
view! { "Loading..." }
|
||||||
|
}>
|
||||||
|
{move || {
|
||||||
|
search_results
|
||||||
|
.get()
|
||||||
|
.map(move |search_results| {
|
||||||
let is_empty = search_results.is_empty();
|
let is_empty = search_results.is_empty();
|
||||||
view! {
|
view! {
|
||||||
<Show when=move || !is_empty
|
<Show
|
||||||
|
when=move || !is_empty
|
||||||
fallback=move || {
|
fallback=move || {
|
||||||
let error_view = move || {
|
let error_view = move || {
|
||||||
error.get().map(|err| {
|
error
|
||||||
|
.get()
|
||||||
|
.map(|err| {
|
||||||
view! { <p style="color:red;">{err}</p> }
|
view! { <p style="color:red;">{err}</p> }
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
view! {
|
view! {
|
||||||
{error_view}
|
{error_view}
|
||||||
<p>No results found</p>
|
<p>No results found</p>
|
||||||
}}>
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{
|
|
||||||
// render resolved instance
|
// render resolved instance
|
||||||
if let Some(instance) = &search_results.instance {
|
{if let Some(instance) = &search_results.instance {
|
||||||
let domain = &instance.domain;
|
let domain = &instance.domain;
|
||||||
vec![view! { <li>
|
vec![
|
||||||
<a href={format!("/instance/{domain}")}>{domain}</a>
|
view! {
|
||||||
</li>}]
|
<li>
|
||||||
} else { vec![] }
|
<a href=format!("/instance/{domain}")>{domain}</a>
|
||||||
}
|
</li>
|
||||||
{
|
},
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}}
|
||||||
// render articles from resolve/search
|
// render articles from resolve/search
|
||||||
search_results.articles
|
{search_results
|
||||||
|
.articles
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| view! { <li>
|
.map(|a| {
|
||||||
<a href={article_link(a)}>{article_title(a)}</a>
|
view! {
|
||||||
</li>})
|
<li>
|
||||||
.collect::<Vec<_>>()
|
<a href=article_link(a)>{article_title(a)}</a>
|
||||||
|
</li>
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()}
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</Show>
|
</Show>
|
||||||
}})
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,21 @@ pub fn UserProfile() -> impl IntoView {
|
||||||
view! { <p style="color:red;">{err}</p> }
|
view! { <p style="color:red;">{err}</p> }
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
<Suspense fallback=|| view! { "Loading..." }> {
|
|
||||||
move || user_profile.get().map(|person: DbPerson| {
|
<Suspense fallback=|| {
|
||||||
|
view! { "Loading..." }
|
||||||
|
}>
|
||||||
|
{move || {
|
||||||
|
user_profile
|
||||||
|
.get()
|
||||||
|
.map(|person: DbPerson| {
|
||||||
view! {
|
view! {
|
||||||
<h1>{user_title(&person)}</h1>
|
<h1>{user_title(&person)}</h1>
|
||||||
<p>TODO: create actual user profile</p>
|
<p>TODO: create actual user profile</p>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}</Suspense>
|
}}
|
||||||
|
|
||||||
|
</Suspense>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue