mirror of
https://github.com/Nutomic/ibis.git
synced 2025-02-03 18:51:35 +00:00
Move frontend stuff into subfolders
This commit is contained in:
parent
ae44c169ba
commit
5229ec34a7
37 changed files with 251 additions and 229 deletions
|
@ -1,4 +1,7 @@
|
||||||
use crate::backend::{config::IbisConfig, database::schema::jwt_secret, utils::error::MyResult};
|
use crate::backend::{
|
||||||
|
database::schema::jwt_secret,
|
||||||
|
utils::{config::IbisConfig, error::MyResult},
|
||||||
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
r2d2::{ConnectionManager, Pool},
|
r2d2::{ConnectionManager, Pool},
|
||||||
PgConnection,
|
PgConnection,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::utils::error::MyResult;
|
use super::utils::error::MyResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{config::IbisConfig, database::IbisContext},
|
backend::{database::IbisContext, utils::config::IbisConfig},
|
||||||
common::{instance::DbInstance, user::DbPerson},
|
common::{instance::DbInstance, user::DbPerson},
|
||||||
};
|
};
|
||||||
use activities::announce::AnnounceActivity;
|
use activities::announce::AnnounceActivity;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{
|
backend::{
|
||||||
config::IbisConfig,
|
|
||||||
database::{article::DbArticleForm, instance::DbInstanceForm, IbisContext},
|
database::{article::DbArticleForm, instance::DbInstanceForm, IbisContext},
|
||||||
federation::{activities::submit_article_update, VerifyUrlData},
|
federation::{activities::submit_article_update, VerifyUrlData},
|
||||||
utils::{
|
utils::{
|
||||||
|
config::IbisConfig,
|
||||||
error::{Error, MyResult},
|
error::{Error, MyResult},
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
},
|
},
|
||||||
|
@ -37,7 +37,6 @@ use tokio::sync::oneshot;
|
||||||
use utils::{generate_keypair, scheduled_tasks};
|
use utils::{generate_keypair, scheduled_tasks};
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod config;
|
|
||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod federation;
|
pub mod federation;
|
||||||
mod server;
|
mod server;
|
||||||
|
|
|
@ -15,6 +15,7 @@ use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub(super) mod scheduled_tasks;
|
pub(super) mod scheduled_tasks;
|
||||||
pub(super) mod validate;
|
pub(super) mod validate;
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
use crate::{
|
use crate::frontend::{
|
||||||
common::instance::SiteView,
|
api::CLIENT,
|
||||||
frontend::{
|
components::{nav::Nav, protected_route::IbisProtectedRoute},
|
||||||
api::CLIENT,
|
pages::{
|
||||||
components::{nav::Nav, protected_route::IbisProtectedRoute},
|
article::{
|
||||||
dark_mode::DarkMode,
|
actions::ArticleActions,
|
||||||
instance_title,
|
create::CreateArticle,
|
||||||
pages::{
|
|
||||||
article::{
|
|
||||||
actions::ArticleActions,
|
|
||||||
create::CreateArticle,
|
|
||||||
discussion::ArticleDiscussion,
|
|
||||||
edit::EditArticle,
|
|
||||||
history::ArticleHistory,
|
|
||||||
list::ListArticles,
|
|
||||||
read::ReadArticle,
|
|
||||||
},
|
|
||||||
diff::EditDiff,
|
diff::EditDiff,
|
||||||
instance::{details::InstanceDetails, list::ListInstances, settings::InstanceSettings},
|
discussion::ArticleDiscussion,
|
||||||
|
edit::EditArticle,
|
||||||
|
history::ArticleHistory,
|
||||||
|
list::ListArticles,
|
||||||
|
read::ReadArticle,
|
||||||
|
},
|
||||||
|
instance::{
|
||||||
|
details::InstanceDetails,
|
||||||
|
list::ListInstances,
|
||||||
|
search::Search,
|
||||||
|
settings::InstanceSettings,
|
||||||
|
},
|
||||||
|
user::{
|
||||||
|
edit_profile::UserEditProfile,
|
||||||
login::Login,
|
login::Login,
|
||||||
notifications::Notifications,
|
notifications::Notifications,
|
||||||
|
profile::UserProfile,
|
||||||
register::Register,
|
register::Register,
|
||||||
search::Search,
|
|
||||||
user_edit_profile::UserEditProfile,
|
|
||||||
user_profile::UserProfile,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
utils::{dark_mode::DarkMode, formatting::instance_title},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_meta::{provide_meta_context, *};
|
use leptos_meta::{provide_meta_context, *};
|
||||||
|
@ -33,40 +35,6 @@ use leptos_router::{
|
||||||
path,
|
path,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn site() -> Resource<SiteView> {
|
|
||||||
use_context::<Resource<SiteView>>().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_logged_in() -> bool {
|
|
||||||
site().with_default(|site| site.my_profile.is_some())
|
|
||||||
}
|
|
||||||
pub fn is_admin() -> bool {
|
|
||||||
site().with_default(|site| {
|
|
||||||
site.my_profile
|
|
||||||
.as_ref()
|
|
||||||
.map(|p| p.local_user.admin)
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub trait DefaultResource<T> {
|
|
||||||
fn with_default<O>(&self, f: impl FnOnce(&T) -> O) -> O;
|
|
||||||
fn get_default(&self) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Default + Send + Sync + Clone> DefaultResource<T> for Resource<T> {
|
|
||||||
fn with_default<O>(&self, f: impl FnOnce(&T) -> O) -> O {
|
|
||||||
self.with(|x| match x {
|
|
||||||
Some(x) => f(x),
|
|
||||||
None => f(&T::default()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn get_default(&self) -> T {
|
|
||||||
match self.get() {
|
|
||||||
Some(x) => x.clone(),
|
|
||||||
None => T::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::frontend::{markdown::render_article_markdown, use_cookie};
|
use crate::frontend::{markdown::render_article_markdown, utils::use_cookie};
|
||||||
use leptos::{ev::beforeunload, html::Textarea, prelude::*};
|
use leptos::{ev::beforeunload, html::Textarea, prelude::*};
|
||||||
use leptos_use::{use_event_listener, use_window};
|
use leptos_use::{use_event_listener, use_window};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{article::DbArticleView, validation::can_edit_article},
|
common::{article::DbArticleView, validation::can_edit_article},
|
||||||
frontend::{
|
frontend::utils::{
|
||||||
app::{is_admin, is_logged_in},
|
formatting::{article_path, article_title},
|
||||||
article_path,
|
resources::{is_admin, is_logged_in},
|
||||||
article_title,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
|
|
|
@ -6,11 +6,12 @@ use crate::{
|
||||||
},
|
},
|
||||||
frontend::{
|
frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
app::{site, DefaultResource},
|
|
||||||
components::comment_editor::{CommentEditorView, EditParams},
|
components::comment_editor::{CommentEditorView, EditParams},
|
||||||
markdown::render_comment_markdown,
|
markdown::render_comment_markdown,
|
||||||
time_ago,
|
utils::{
|
||||||
user_link,
|
formatting::{time_ago, user_link},
|
||||||
|
resources::{site, DefaultResource},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{article::EditView, utils::extract_domain},
|
common::{article::EditView, utils::extract_domain},
|
||||||
frontend::{article_link, render_date_time, user_link},
|
frontend::utils::formatting::{article_link, render_date_time, user_link},
|
||||||
};
|
};
|
||||||
use leptos::{either::Either, prelude::*};
|
use leptos::{either::Either, prelude::*};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
frontend::{
|
frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
app::{site, DefaultResource},
|
utils::resources::{site, DefaultResource},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::frontend::{
|
use crate::frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
app::{is_admin, is_logged_in, site, DefaultResource},
|
utils::{
|
||||||
dark_mode::DarkMode,
|
dark_mode::DarkMode,
|
||||||
instance_title,
|
formatting::instance_title,
|
||||||
|
resources::{is_admin, is_logged_in, site, DefaultResource},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use leptos::{component, prelude::*, view, IntoView, *};
|
use leptos::{component, prelude::*, view, IntoView, *};
|
||||||
use leptos_router::hooks::use_navigate;
|
use leptos_router::hooks::use_navigate;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::frontend::app::is_logged_in;
|
use crate::frontend::utils::resources::is_logged_in;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_router::{
|
use leptos_router::{
|
||||||
components::{ProtectedRoute, ProtectedRouteProps},
|
components::{ProtectedRoute, ProtectedRouteProps},
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
use crate::common::{
|
use crate::common::article::DbArticle;
|
||||||
article::DbArticle,
|
|
||||||
instance::DbInstance,
|
|
||||||
user::DbPerson,
|
|
||||||
utils::extract_domain,
|
|
||||||
};
|
|
||||||
use chrono::{DateTime, Duration, Local, Utc};
|
|
||||||
use codee::string::FromToStringCodec;
|
|
||||||
use leptos::prelude::*;
|
|
||||||
use leptos_use::{use_cookie_with_options, SameSite, UseCookieOptions};
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
use timeago::Formatter;
|
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod app;
|
pub mod app;
|
||||||
mod components;
|
mod components;
|
||||||
pub mod dark_mode;
|
mod markdown;
|
||||||
pub mod markdown;
|
mod pages;
|
||||||
pub mod pages;
|
mod utils;
|
||||||
|
|
||||||
#[cfg(feature = "hydrate")]
|
#[cfg(feature = "hydrate")]
|
||||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||||
|
@ -26,107 +15,3 @@ pub fn hydrate() {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
leptos::mount::hydrate_body(App);
|
leptos::mount::hydrate_body(App);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn article_path(article: &DbArticle) -> String {
|
|
||||||
if article.local {
|
|
||||||
format!("/article/{}", article.title)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"/article/{}@{}",
|
|
||||||
article.title,
|
|
||||||
extract_domain(&article.ap_id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn article_link(article: &DbArticle) -> impl IntoView {
|
|
||||||
let article_path = article_path(article);
|
|
||||||
view! {
|
|
||||||
<a class="link" href=article_path>
|
|
||||||
{article.title.clone()}
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn article_title(article: &DbArticle) -> String {
|
|
||||||
let title = article.title.replace('_', " ");
|
|
||||||
if article.local {
|
|
||||||
title
|
|
||||||
} else {
|
|
||||||
format!("{}@{}", title, extract_domain(&article.ap_id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn user_title(person: &DbPerson) -> String {
|
|
||||||
let name = person
|
|
||||||
.display_name
|
|
||||||
.clone()
|
|
||||||
.unwrap_or(person.username.clone());
|
|
||||||
if person.local {
|
|
||||||
format!("@{name}")
|
|
||||||
} else {
|
|
||||||
format!("@{}@{}", name, extract_domain(&person.ap_id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn user_link(person: &DbPerson) -> impl IntoView {
|
|
||||||
let creator_path = if person.local {
|
|
||||||
format!("/user/{}", person.username)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"/user/{}@{}",
|
|
||||||
person.username,
|
|
||||||
extract_domain(&person.ap_id)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
view! {
|
|
||||||
<a class="link" href=creator_path>
|
|
||||||
{user_title(person)}
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_date_time(date_time: DateTime<Utc>) -> String {
|
|
||||||
date_time
|
|
||||||
.with_timezone(&Local)
|
|
||||||
.format("%Y-%m-%d %H:%M:%S")
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn use_cookie(name: &str) -> (Signal<Option<bool>>, WriteSignal<Option<bool>>) {
|
|
||||||
let expires = (Local::now() + Duration::days(356)).timestamp();
|
|
||||||
let cookie_options = UseCookieOptions::default()
|
|
||||||
.path("/")
|
|
||||||
.expires(expires)
|
|
||||||
.same_site(SameSite::Strict);
|
|
||||||
use_cookie_with_options::<bool, FromToStringCodec>(name, cookie_options)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn time_ago(time: DateTime<Utc>) -> String {
|
|
||||||
static INSTANCE: OnceLock<Formatter> = OnceLock::new();
|
|
||||||
let secs = Utc::now().signed_duration_since(time).num_seconds();
|
|
||||||
let duration = std::time::Duration::from_secs(secs.try_into().unwrap_or_default());
|
|
||||||
INSTANCE.get_or_init(Formatter::new).convert(duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instance_title_with_domain(instance: &DbInstance) -> String {
|
|
||||||
let name = instance.name.clone();
|
|
||||||
let domain = instance.domain.clone();
|
|
||||||
if let Some(name) = name {
|
|
||||||
format!("{name} ({domain})")
|
|
||||||
} else {
|
|
||||||
domain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instance_title(instance: &DbInstance) -> String {
|
|
||||||
instance.name.clone().unwrap_or(instance.domain.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instance_updated(instance: &DbInstance) -> String {
|
|
||||||
if instance.local {
|
|
||||||
"Local".to_string()
|
|
||||||
} else {
|
|
||||||
format!("Updated {}", time_ago(instance.last_refreshed_at))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,10 +5,9 @@ use crate::{
|
||||||
},
|
},
|
||||||
frontend::{
|
frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
app::is_admin,
|
|
||||||
article_path,
|
|
||||||
components::article_nav::{ActiveTab, ArticleNav},
|
components::article_nav::{ActiveTab, ArticleNav},
|
||||||
pages::article_resource,
|
pages::article_resource,
|
||||||
|
utils::{formatting::article_path, resources::is_admin},
|
||||||
DbArticle,
|
DbArticle,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,8 @@ use crate::{
|
||||||
common::article::CreateArticleParams,
|
common::article::CreateArticleParams,
|
||||||
frontend::{
|
frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
app::{is_admin, site, DefaultResource},
|
|
||||||
components::article_editor::EditorView,
|
components::article_editor::EditorView,
|
||||||
|
utils::resources::{is_admin, site, DefaultResource},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use leptos::{html::Textarea, prelude::*};
|
use leptos::{html::Textarea, prelude::*};
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::frontend::{
|
use crate::frontend::{
|
||||||
article_title,
|
|
||||||
components::article_nav::{ActiveTab, ArticleNav},
|
components::article_nav::{ActiveTab, ArticleNav},
|
||||||
pages::{article_edits_resource, article_resource},
|
pages::{article_edits_resource, article_resource},
|
||||||
render_date_time,
|
utils::formatting::{article_title, render_date_time, user_link},
|
||||||
user_link,
|
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_meta::Title;
|
use leptos_meta::Title;
|
|
@ -2,10 +2,11 @@ use crate::{
|
||||||
common::article::ListArticlesParams,
|
common::article::ListArticlesParams,
|
||||||
frontend::{
|
frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
app::DefaultResource,
|
|
||||||
article_path,
|
|
||||||
article_title,
|
|
||||||
components::connect::ConnectView,
|
components::connect::ConnectView,
|
||||||
|
utils::{
|
||||||
|
formatting::{article_path, article_title},
|
||||||
|
resources::DefaultResource,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod actions;
|
pub mod actions;
|
||||||
pub mod create;
|
pub mod create;
|
||||||
|
pub mod diff;
|
||||||
pub mod discussion;
|
pub mod discussion;
|
||||||
pub mod edit;
|
pub mod edit;
|
||||||
pub mod history;
|
pub mod history;
|
||||||
|
|
|
@ -2,11 +2,13 @@ use crate::{
|
||||||
common::{article::ListArticlesParams, instance::DbInstance, utils::http_protocol_str},
|
common::{article::ListArticlesParams, instance::DbInstance, utils::http_protocol_str},
|
||||||
frontend::{
|
frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
article_path,
|
|
||||||
article_title,
|
|
||||||
components::instance_follow_button::InstanceFollowButton,
|
components::instance_follow_button::InstanceFollowButton,
|
||||||
instance_title_with_domain,
|
utils::formatting::{
|
||||||
instance_updated,
|
article_path,
|
||||||
|
article_title,
|
||||||
|
instance_title_with_domain,
|
||||||
|
instance_updated,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::frontend::{
|
use crate::frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
components::connect::ConnectView,
|
components::connect::ConnectView,
|
||||||
instance_title_with_domain,
|
utils::formatting::{instance_title_with_domain, instance_updated},
|
||||||
instance_updated,
|
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_meta::Title;
|
use leptos_meta::Title;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
pub(crate) mod details;
|
pub mod details;
|
||||||
pub(crate) mod list;
|
pub mod list;
|
||||||
pub(crate) mod settings;
|
pub mod search;
|
||||||
|
pub mod settings;
|
||||||
|
|
|
@ -3,7 +3,10 @@ use crate::{
|
||||||
article::{DbArticle, SearchArticleParams},
|
article::{DbArticle, SearchArticleParams},
|
||||||
instance::DbInstance,
|
instance::DbInstance,
|
||||||
},
|
},
|
||||||
frontend::{api::CLIENT, article_path, article_title},
|
frontend::{
|
||||||
|
api::CLIENT,
|
||||||
|
utils::formatting::{article_path, article_title},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_meta::Title;
|
use leptos_meta::Title;
|
|
@ -8,15 +8,9 @@ use crate::{
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_router::hooks::use_params_map;
|
use leptos_router::hooks::use_params_map;
|
||||||
|
|
||||||
pub(crate) mod article;
|
pub mod article;
|
||||||
pub(crate) mod diff;
|
pub mod instance;
|
||||||
pub(crate) mod instance;
|
pub mod user;
|
||||||
pub(crate) mod login;
|
|
||||||
pub(crate) mod notifications;
|
|
||||||
pub(crate) mod register;
|
|
||||||
pub(crate) mod search;
|
|
||||||
pub(crate) mod user_edit_profile;
|
|
||||||
pub(crate) mod user_profile;
|
|
||||||
|
|
||||||
fn article_resource() -> Resource<DbArticleView> {
|
fn article_resource() -> Resource<DbArticleView> {
|
||||||
let params = use_params_map();
|
let params = use_params_map();
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
common::user::UpdateUserParams,
|
common::user::UpdateUserParams,
|
||||||
frontend::{
|
frontend::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
app::{site, DefaultResource},
|
utils::resources::{site, DefaultResource},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
common::user::LoginUserParams,
|
common::user::LoginUserParams,
|
||||||
frontend::{api::CLIENT, app::site, components::credentials::*},
|
frontend::{api::CLIENT, components::credentials::*, utils::resources::site},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_meta::Title;
|
use leptos_meta::Title;
|
5
src/frontend/pages/user/mod.rs
Normal file
5
src/frontend/pages/user/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod edit_profile;
|
||||||
|
pub mod login;
|
||||||
|
pub mod notifications;
|
||||||
|
pub mod profile;
|
||||||
|
pub mod register;
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
common::Notification,
|
common::Notification,
|
||||||
frontend::{api::CLIENT, article_path, article_title},
|
frontend::{
|
||||||
|
api::CLIENT,
|
||||||
|
utils::formatting::{article_path, article_title},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_meta::Title;
|
use leptos_meta::Title;
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
api::CLIENT,
|
api::CLIENT,
|
||||||
components::edit_list::EditList,
|
components::edit_list::EditList,
|
||||||
markdown::render_article_markdown,
|
markdown::render_article_markdown,
|
||||||
user_title,
|
utils::formatting::user_title,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
common::user::RegisterUserParams,
|
common::user::RegisterUserParams,
|
||||||
frontend::{api::CLIENT, app::site, components::credentials::*},
|
frontend::{api::CLIENT, components::credentials::*, utils::resources::site},
|
||||||
};
|
};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_meta::Title;
|
use leptos_meta::Title;
|
|
@ -1,4 +1,4 @@
|
||||||
use super::use_cookie;
|
use crate::frontend::utils::use_cookie;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_use::use_preferred_dark;
|
use leptos_use::use_preferred_dark;
|
||||||
|
|
105
src/frontend/utils/formatting.rs
Normal file
105
src/frontend/utils/formatting.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
use crate::common::{
|
||||||
|
article::DbArticle,
|
||||||
|
instance::DbInstance,
|
||||||
|
user::DbPerson,
|
||||||
|
utils::extract_domain,
|
||||||
|
};
|
||||||
|
use chrono::{DateTime, Local, Utc};
|
||||||
|
use leptos::prelude::*;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
use timeago::Formatter;
|
||||||
|
|
||||||
|
pub fn article_path(article: &DbArticle) -> String {
|
||||||
|
if article.local {
|
||||||
|
format!("/article/{}", article.title)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"/article/{}@{}",
|
||||||
|
article.title,
|
||||||
|
extract_domain(&article.ap_id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn article_link(article: &DbArticle) -> impl IntoView {
|
||||||
|
let article_path = article_path(article);
|
||||||
|
view! {
|
||||||
|
<a class="link" href=article_path>
|
||||||
|
{article.title.clone()}
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn article_title(article: &DbArticle) -> String {
|
||||||
|
let title = article.title.replace('_', " ");
|
||||||
|
if article.local {
|
||||||
|
title
|
||||||
|
} else {
|
||||||
|
format!("{}@{}", title, extract_domain(&article.ap_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_title(person: &DbPerson) -> String {
|
||||||
|
let name = person
|
||||||
|
.display_name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(person.username.clone());
|
||||||
|
if person.local {
|
||||||
|
format!("@{name}")
|
||||||
|
} else {
|
||||||
|
format!("@{}@{}", name, extract_domain(&person.ap_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_link(person: &DbPerson) -> impl IntoView {
|
||||||
|
let creator_path = if person.local {
|
||||||
|
format!("/user/{}", person.username)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"/user/{}@{}",
|
||||||
|
person.username,
|
||||||
|
extract_domain(&person.ap_id)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
view! {
|
||||||
|
<a class="link" href=creator_path>
|
||||||
|
{user_title(person)}
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_date_time(date_time: DateTime<Utc>) -> String {
|
||||||
|
date_time
|
||||||
|
.with_timezone(&Local)
|
||||||
|
.format("%Y-%m-%d %H:%M:%S")
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time_ago(time: DateTime<Utc>) -> String {
|
||||||
|
static INSTANCE: OnceLock<Formatter> = OnceLock::new();
|
||||||
|
let secs = Utc::now().signed_duration_since(time).num_seconds();
|
||||||
|
let duration = std::time::Duration::from_secs(secs.try_into().unwrap_or_default());
|
||||||
|
INSTANCE.get_or_init(Formatter::new).convert(duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instance_title_with_domain(instance: &DbInstance) -> String {
|
||||||
|
let name = instance.name.clone();
|
||||||
|
let domain = instance.domain.clone();
|
||||||
|
if let Some(name) = name {
|
||||||
|
format!("{name} ({domain})")
|
||||||
|
} else {
|
||||||
|
domain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instance_title(instance: &DbInstance) -> String {
|
||||||
|
instance.name.clone().unwrap_or(instance.domain.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instance_updated(instance: &DbInstance) -> String {
|
||||||
|
if instance.local {
|
||||||
|
"Local".to_string()
|
||||||
|
} else {
|
||||||
|
format!("Updated {}", time_ago(instance.last_refreshed_at))
|
||||||
|
}
|
||||||
|
}
|
17
src/frontend/utils/mod.rs
Normal file
17
src/frontend/utils/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use chrono::{Duration, Local};
|
||||||
|
use codee::string::FromToStringCodec;
|
||||||
|
use leptos::prelude::*;
|
||||||
|
use leptos_use::{use_cookie_with_options, SameSite, UseCookieOptions};
|
||||||
|
|
||||||
|
pub mod dark_mode;
|
||||||
|
pub mod formatting;
|
||||||
|
pub mod resources;
|
||||||
|
|
||||||
|
pub fn use_cookie(name: &str) -> (Signal<Option<bool>>, WriteSignal<Option<bool>>) {
|
||||||
|
let expires = (Local::now() + Duration::days(356)).timestamp();
|
||||||
|
let cookie_options = UseCookieOptions::default()
|
||||||
|
.path("/")
|
||||||
|
.expires(expires)
|
||||||
|
.same_site(SameSite::Strict);
|
||||||
|
use_cookie_with_options::<bool, FromToStringCodec>(name, cookie_options)
|
||||||
|
}
|
37
src/frontend/utils/resources.rs
Normal file
37
src/frontend/utils/resources.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use crate::common::instance::SiteView;
|
||||||
|
use leptos::prelude::*;
|
||||||
|
|
||||||
|
pub fn site() -> Resource<SiteView> {
|
||||||
|
use_context::<Resource<SiteView>>().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_logged_in() -> bool {
|
||||||
|
site().with_default(|site| site.my_profile.is_some())
|
||||||
|
}
|
||||||
|
pub fn is_admin() -> bool {
|
||||||
|
site().with_default(|site| {
|
||||||
|
site.my_profile
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.local_user.admin)
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub trait DefaultResource<T> {
|
||||||
|
fn with_default<O>(&self, f: impl FnOnce(&T) -> O) -> O;
|
||||||
|
fn get_default(&self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Send + Sync + Clone> DefaultResource<T> for Resource<T> {
|
||||||
|
fn with_default<O>(&self, f: impl FnOnce(&T) -> O) -> O {
|
||||||
|
self.with(|x| match x {
|
||||||
|
Some(x) => f(x),
|
||||||
|
None => f(&T::default()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn get_default(&self) -> T {
|
||||||
|
match self.get() {
|
||||||
|
Some(x) => x.clone(),
|
||||||
|
None => T::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() -> ibis::backend::utils::error::MyResult<()> {
|
pub async fn main() -> ibis::backend::utils::error::MyResult<()> {
|
||||||
use ibis::backend::config::IbisConfig;
|
use ibis::backend::utils::config::IbisConfig;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
|
|
||||||
if std::env::args().collect::<Vec<_>>().get(1) == Some(&"--print-config".to_string()) {
|
if std::env::args().collect::<Vec<_>>().get(1) == Some(&"--print-config".to_string()) {
|
||||||
|
|
|
@ -11,7 +11,6 @@ module.exports = {
|
||||||
require('@tailwindcss/typography')
|
require('@tailwindcss/typography')
|
||||||
],
|
],
|
||||||
daisyui: {
|
daisyui: {
|
||||||
//themes: ["emerald", "dim"]
|
|
||||||
themes: [
|
themes: [
|
||||||
{
|
{
|
||||||
emerald: {
|
emerald: {
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ibis::{
|
use ibis::{
|
||||||
backend::{
|
backend::{
|
||||||
config::{IbisConfig, IbisConfigDatabase, IbisConfigFederation},
|
|
||||||
start,
|
start,
|
||||||
|
utils::config::{IbisConfig, IbisConfigDatabase, IbisConfigFederation},
|
||||||
},
|
},
|
||||||
common::{instance::Options, user::RegisterUserParams},
|
common::{instance::Options, user::RegisterUserParams},
|
||||||
frontend::api::ApiClient,
|
frontend::api::ApiClient,
|
||||||
|
|
Loading…
Reference in a new issue