1
0
Fork 0
forked from alemi/upub

feat(web): added simple config page with filters

This commit is contained in:
əlemi 2024-05-12 00:11:28 +02:00
parent 18f712194c
commit c068822b3c
Signed by: alemi
GPG key ID: A4895B84D311642C
7 changed files with 127 additions and 5 deletions

2
Cargo.lock generated
View file

@ -4510,6 +4510,8 @@ dependencies = [
"mdhtml", "mdhtml",
"reqwest", "reqwest",
"serde", "serde",
"serde-inline-default",
"serde_default",
"serde_json", "serde_json",
"thiserror", "thiserror",
"tracing", "tracing",

View file

@ -19,6 +19,8 @@ console_error_panic_hook = "0.1"
thiserror = "1" thiserror = "1"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
serde_default = "0.1"
serde-inline-default = "0.2"
dashmap = "5.5" dashmap = "5.5"
leptos = { version = "0.6", features = ["csr", "tracing"] } leptos = { version = "0.6", features = ["csr", "tracing"] }
leptos_router = { version = "0.6", features = ["csr"] } leptos_router = { version = "0.6", features = ["csr"] }

View file

@ -2,7 +2,7 @@ use leptos::*;
use leptos_router::*; use leptos_router::*;
use crate::prelude::*; use crate::prelude::*;
use leptos_use::{use_cookie, use_cookie_with_options, utils::FromToStringCodec, UseCookieOptions}; use leptos_use::{storage::use_local_storage, use_cookie, use_cookie_with_options, utils::{FromToStringCodec, JsonCodec}, UseCookieOptions};
#[component] #[component]
@ -12,10 +12,12 @@ pub fn App() -> impl IntoView {
UseCookieOptions::default() UseCookieOptions::default()
.max_age(1000 * 60 * 60 * 6) .max_age(1000 * 60 * 60 * 6)
); );
let (config, set_config, _) = use_local_storage::<crate::Config, JsonCodec>("config");
let (userid, set_userid) = use_cookie::<String, FromToStringCodec>("user_id"); let (userid, set_userid) = use_cookie::<String, FromToStringCodec>("user_id");
let auth = Auth { token, userid }; let auth = Auth { token, userid };
provide_context(auth); provide_context(auth);
provide_context(config);
let username = auth.userid.get_untracked() let username = auth.userid.get_untracked()
.map(|x| x.split('/').last().unwrap_or_default().to_string()) .map(|x| x.split('/').last().unwrap_or_default().to_string())
@ -105,7 +107,7 @@ pub fn App() -> impl IntoView {
<Route path="/web/home" view=move || view! { <TimelinePage name="home" tl=home_tl /> } /> <Route path="/web/home" view=move || view! { <TimelinePage name="home" tl=home_tl /> } />
<Route path="/web/server" view=move || view! { <TimelinePage name="server" tl=server_tl /> } /> <Route path="/web/server" view=move || view! { <TimelinePage name="server" tl=server_tl /> } />
<Route path="/web/config" view=ConfigPage /> <Route path="/web/config" view=move || view! { <ConfigPage setter=set_config /> } />
<Route path="/web/about" view=AboutPage /> <Route path="/web/about" view=AboutPage />
<Route path="/web/users/:id" view=move || view! { <UserPage tl=user_tl /> } /> <Route path="/web/users/:id" view=move || view! { <UserPage tl=user_tl /> } />

47
web/src/config.rs Normal file
View file

@ -0,0 +1,47 @@
#[serde_inline_default::serde_inline_default]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, serde_default::DefaultFromSerde)]
pub struct Config {
#[serde(default)]
pub filters: FiltersConfig,
#[serde_inline_default(true)]
pub collapse_content_warnings: bool,
#[serde_inline_default(true)]
pub loop_videos: bool,
}
#[serde_inline_default::serde_inline_default]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, serde_default::DefaultFromSerde)]
pub struct FiltersConfig {
#[serde_inline_default(false)]
pub likes: bool,
#[serde_inline_default(true)]
pub creates: bool,
#[serde_inline_default(true)]
pub announces: bool,
#[serde_inline_default(true)]
pub follows: bool,
#[serde_inline_default(true)]
pub orphans: bool,
}
impl FiltersConfig {
pub fn visible(&self, object_type: apb::ObjectType) -> bool {
match object_type {
apb::ObjectType::Note | apb::ObjectType::Document(_) => self.orphans,
apb::ObjectType::Activity(apb::ActivityType::Like) => self.likes,
apb::ObjectType::Activity(apb::ActivityType::Create) => self.creates,
apb::ObjectType::Activity(apb::ActivityType::Announce) => self.announces,
apb::ObjectType::Activity(
apb::ActivityType::Follow | apb::ActivityType::Accept(_) | apb::ActivityType::Reject(_)
) => self.follows,
_ => true,
}
}
}

View file

@ -22,6 +22,7 @@ pub fn Navigator() -> impl IntoView {
<tr><td colspan="2"><a href="/web/home"><input class="w-100" type="submit" class:hidden=move || !auth.present() value="home timeline" /></a></td></tr> <tr><td colspan="2"><a href="/web/home"><input class="w-100" type="submit" class:hidden=move || !auth.present() value="home timeline" /></a></td></tr>
<tr><td colspan="2"><a href="/web/server"><input class="w-100" type="submit" value="server timeline" /></a></td></tr> <tr><td colspan="2"><a href="/web/server"><input class="w-100" type="submit" value="server timeline" /></a></td></tr>
<tr><td colspan="2"><a href="/web/about"><input class="w-100" type="submit" value="about" /></a></td></tr> <tr><td colspan="2"><a href="/web/about"><input class="w-100" type="submit" value="about" /></a></td></tr>
<tr><td colspan="2"><a href="/web/config"><input class="w-100" type="submit" value="config" /></a></td></tr>
<tr><td colspan="2"><a href="/web/debug"><input class="w-100" type="submit" value="debug" class:hidden=move|| !auth.present() /></a></td></tr> <tr><td colspan="2"><a href="/web/debug"><input class="w-100" type="submit" value="debug" class:hidden=move|| !auth.present() /></a></td></tr>
</table> </table>
} }

View file

@ -3,8 +3,10 @@ mod auth;
mod components; mod components;
mod page; mod page;
mod control; mod control;
mod config;
pub use app::App; pub use app::App;
pub use config::Config;
pub mod prelude; pub mod prelude;

View file

@ -4,7 +4,7 @@ use apb::{ActivityMut, Actor, Base, Object, ObjectMut};
use leptos::*; use leptos::*;
use leptos_router::*; use leptos_router::*;
use crate::{prelude::*, DEFAULT_AVATAR_URL}; use crate::{prelude::*, Config, DEFAULT_AVATAR_URL};
#[component] #[component]
pub fn AboutPage() -> impl IntoView { pub fn AboutPage() -> impl IntoView {
@ -21,17 +21,83 @@ pub fn AboutPage() -> impl IntoView {
} }
#[component] #[component]
pub fn ConfigPage() -> impl IntoView { pub fn ConfigPage(setter: WriteSignal<Config>) -> impl IntoView {
let config = use_context::<Signal<Config>>().expect("missing config context");
macro_rules! get_cfg {
(filter $field:ident) => {
move || config.get().filters.$field
};
($field:ident) => {
move || config.get().$field
};
}
macro_rules! set_cfg {
($field:ident) => {
move |ev| {
let mut mock = config.get();
mock.$field = event_target_checked(&ev);
setter.set(mock);
}
};
(filter $field:ident) => {
move |ev| {
let mut mock = config.get();
mock.filters.$field = event_target_checked(&ev);
setter.set(mock);
}
};
}
view! { view! {
<div> <div>
<Breadcrumb>config</Breadcrumb> <Breadcrumb>config</Breadcrumb>
<p>
<input type="checkbox" title="likes" class="mr-1"
prop:checked=get_cfg!(loop_videos)
on:input=set_cfg!(loop_videos)
/> loop videos
</p>
<p>
<input type="checkbox" title="likes" class="mr-1"
prop:checked=get_cfg!(collapse_content_warnings)
on:input=set_cfg!(collapse_content_warnings)
/> collapse content warnings
</p>
<div class="mt-s mb-s" > <div class="mt-s mb-s" >
<p><code>"not implemented :("</code></p> <table class="ma-3 center">
<tr>
<th></th>
<th>filters</th>
</tr>
<tr>
<td><input type="checkbox" prop:checked=get_cfg!(filter likes) on:input=set_cfg!(filter likes) /></td>
<td>likes</td>
</tr>
<tr>
<td><input type="checkbox" prop:checked=get_cfg!(filter creates) on:input=set_cfg!(filter creates)/></td>
<td>creates</td>
</tr>
<tr>
<td><input type="checkbox" prop:checked=get_cfg!(filter announces) on:input=set_cfg!(filter announces) /></td>
<td>announces</td>
</tr>
<tr>
<td><input type="checkbox" prop:checked=get_cfg!(filter follows) on:input=set_cfg!(filter follows) /></td>
<td>follows</td>
</tr>
<tr>
<td><input type="checkbox" prop:checked=get_cfg!(filter orphans) on:input=set_cfg!(filter orphans) /></td>
<td>orphans</td>
</tr>
</table>
</div> </div>
</div> </div>
} }
} }
fn send_follow_request(target: String) { fn send_follow_request(target: String) {
let auth = use_context::<Auth>().expect("missing auth context"); let auth = use_context::<Auth>().expect("missing auth context");
spawn_local(async move { spawn_local(async move {