222 lines
8.3 KiB
Rust
222 lines
8.3 KiB
Rust
use leptos::prelude::*;
|
|
use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
|
|
use leptos_router::{
|
|
components::{Outlet, ParentRoute, Route, Router, Routes, A}, path
|
|
};
|
|
use leptos_i18n_router::I18nRoute;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::i18n::*;
|
|
|
|
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
|
view! {
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
<AutoReload options=options.clone() />
|
|
<HydrationScripts options/>
|
|
<MetaTags/>
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
|
|
</head>
|
|
<body>
|
|
<App/>
|
|
</body>
|
|
</html>
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn App() -> impl IntoView {
|
|
// Provides context that manages stylesheets, titles, meta tags, etc.
|
|
provide_meta_context();
|
|
|
|
view! {
|
|
<I18nContextProvider>
|
|
<Stylesheet href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@100..900&family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap"/>
|
|
|
|
// id=leptos means cargo-leptos will hot-reload this stylesheet
|
|
<Stylesheet id="leptos" href="/pkg/tanguy-gerome-fi.css"/>
|
|
|
|
<Title text="tanguy.gerome.fi"/>
|
|
|
|
// content for this welcome page
|
|
<Router>
|
|
<Header/>
|
|
<Routes fallback=|| "Page not found.".into_view()>
|
|
<I18nRoute<Locale, _, _> view=Outlet>
|
|
<Route path=path!("/") view=HomePage/>
|
|
<Route path=path!("/resume") view=Resume/>
|
|
<ParentRoute path=path!("/blog") view=crate::blog::Blog>
|
|
<Route path=path!(":slug") view=crate::blog::BlogPost/>
|
|
<Route path=path!("") view=|| view! {}/>
|
|
</ParentRoute>
|
|
<ParentRoute path=path!("/gallery") view=crate::gallery::Gallery>
|
|
<Route path=path!(":slug") view=crate::gallery::GalleryEntry/>
|
|
<Route path=path!("") view=|| view! {}/>
|
|
</ParentRoute>
|
|
</I18nRoute<Locale, _, _>>
|
|
</Routes>
|
|
<Footer/>
|
|
</Router>
|
|
</I18nContextProvider>
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn LanguageSwitcher() -> impl IntoView {
|
|
let i18n = use_i18n();
|
|
|
|
view! {
|
|
<div class="language-switcher main-width">
|
|
<p>{t!(i18n, available_in)}</p>
|
|
<button class="link" on:click=move |_| i18n.set_locale(Locale::en)>english</button>
|
|
<button class="link" on:click=move |_| i18n.set_locale(Locale::fr)>français</button>
|
|
<button class="link" on:click=move |_| i18n.set_locale(Locale::fi)>suomi</button>
|
|
// <button class="link" on:click=move |_| i18n.set_locale(Locale::et)>eesti</button>
|
|
// <button class="link" on:click=move |_| i18n.set_locale(Locale::sv)>svenska</button>
|
|
// <button class="link" on:click=move |_| i18n.set_locale(Locale::eo)>esperanto</button>
|
|
// <button class="link" on:click=move |_| i18n.set_locale(Locale::jbo)>lojban</button>
|
|
<p>{t!(i18n, partial_translations)}</p>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn Header() -> impl IntoView {
|
|
let i18n = use_i18n();
|
|
|
|
view! {
|
|
<header>
|
|
<div class="title-links-and-banner">
|
|
<div class="text">
|
|
<h1><span>tanguy</span><span>.gerome</span><span>.fi</span></h1>
|
|
<div class="links">
|
|
<A href="/">{t!(i18n, home)}</A>
|
|
<A href="/blog">{t!(i18n, blog)}</A>
|
|
<A href="/gallery">{t!(i18n, gallery)}</A>
|
|
<A href="/resume">{t!(i18n, resume)}</A>
|
|
</div>
|
|
</div>
|
|
<picture>
|
|
<img
|
|
class="image"
|
|
src="https://directus.gerome.fi/assets/efdc0b69-7a48-4434-bf07-d406e42a971e?width=1600&fit=inside&format=auto&quality=50&withoutEnlargement=true"
|
|
alt="Banner"
|
|
/>
|
|
<source srcset="https://directus.gerome.fi/assets/efdc0b69-7a48-4434-bf07-d406e42a971e?width=1600&fit=inside&format=jpg&quality=50&withoutEnlargement=true"/>
|
|
</picture>
|
|
</div>
|
|
|
|
<LanguageSwitcher/>
|
|
</header>
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn Footer() -> impl IntoView {
|
|
let i18n = use_i18n();
|
|
|
|
view! {
|
|
<footer>
|
|
<div class="name-and-links main-width">
|
|
<span class="name">{t!(i18n, tanguy_gerome)}</span>
|
|
<div class="links">
|
|
<a href="https://www.instagram.com/kapno.cc/" target="_blank" rel="noopener noreferrer">instagram @kapno.cc</a>
|
|
<a href="https://forgejo.juustodiilerit.fi/tanguy" target="_blank" rel="noopener noreferrer">git (forgejo.juustodiilerit.fi)</a>
|
|
<a href="mailto:tanguy@gerome.fi" target="_blank" rel="noopener noreferrer">email tanguy@gerome.fi</a>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
|
pub struct HomePageTranslations {
|
|
content: String
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
|
pub struct HomePageSingleton {
|
|
translations: Option<HomePageTranslations>
|
|
}
|
|
|
|
#[server(GetHomepage, "/api", "GetJson")]
|
|
pub async fn get_homepage(locale: String) -> Result<HomePageSingleton, ServerFnError> {
|
|
crate::services::directus::get_client()
|
|
.get_item::<HomePageSingleton>(&locale, "homepage", "homepage").await
|
|
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
|
}
|
|
|
|
#[component]
|
|
pub fn HomePage() -> impl IntoView {
|
|
let i18n = use_i18n();
|
|
|
|
let homepage = Resource::new(move || i18n.get_locale(), move |locale| get_homepage(locale.to_string()));
|
|
view! {
|
|
<main class="main-width">
|
|
<Suspense
|
|
fallback=move || view! { <div>"Loading..."</div> }
|
|
>
|
|
{move || homepage.get()
|
|
.and_then(|homepage| homepage.ok())
|
|
.and_then(|homepage| {
|
|
let html = markdown::to_html(&homepage.translations.unwrap_or_default().content);
|
|
view! {
|
|
<div class="markdown" inner_html=html/>
|
|
}.into()
|
|
})
|
|
}
|
|
</Suspense>
|
|
</main>
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
|
pub struct ResumeTranslations {
|
|
content: String
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
|
pub struct ResumeSingleton {
|
|
translations: Option<ResumeTranslations>
|
|
}
|
|
|
|
#[server(GetResume, "/api", "GetJson")]
|
|
pub async fn get_resume(locale: String) -> Result<ResumeSingleton, ServerFnError> {
|
|
crate::services::directus::get_client()
|
|
.get_item::<ResumeSingleton>(&locale, "resume", "resume").await
|
|
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
|
}
|
|
|
|
#[component]
|
|
pub fn Resume() -> impl IntoView {
|
|
let i18n = use_i18n();
|
|
|
|
let resume = Resource::new(move || i18n.get_locale(), move |locale| get_resume(locale.to_string()));
|
|
view! {
|
|
<main class="main-width">
|
|
<img
|
|
class="resume-image"
|
|
src="https://directus.gerome.fi/assets/0c33f439-4e1b-4a1f-a1ab-df1cc9b60f23?width=600&height=600&fit=cover&format=auto&quality=90&withoutEnlargement=true"
|
|
alt="Tanguy Gérôme - portait"
|
|
/>
|
|
<Suspense
|
|
fallback=move || view! { <div>"Loading..."</div> }
|
|
>
|
|
{move || resume.get()
|
|
.and_then(|resume| resume.ok())
|
|
.and_then(|resume| {
|
|
let html = markdown::to_html(&resume.translations.unwrap_or_default().content);
|
|
view! {
|
|
<div class="markdown" inner_html=html/>
|
|
}.into()
|
|
})
|
|
}
|
|
</Suspense>
|
|
</main>
|
|
}
|
|
}
|