Compare commits

...

10 commits

11 changed files with 643 additions and 605 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

3
.gitignore vendored
View file

@ -27,3 +27,6 @@ dist-ssr
/target /target
.sass-cache/ .sass-cache/
# Manual (nix flake)
.direnv

View file

@ -1 +0,0 @@
nodejs 19.9.0

989
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package] [package]
name = "portfolio" name = "portfolio"
version = "0.6.0" version = "0.6.1"
edition = "2021" edition = "2021"
[lib] [lib]
@ -32,8 +32,8 @@ actix-files = { version = "0.6", optional = true }
actix-web = { version = "4.5", features = ["macros"], optional = true } actix-web = { version = "4.5", features = ["macros"], optional = true }
leptos_actix = { version = "0.6", optional = true } leptos_actix = { version = "0.6", optional = true }
futures = { version = "0.3", optional = true } futures = { version = "0.3", optional = true }
simple_logger = { version = "4.3", optional = true } simple_logger = { version = "5.0", optional = true }
pulldown-cmark = { version = "0.10", optional = true } # Markdown parser pulldown-cmark = { version = "0.11", optional = true } # Markdown parser
gray_matter = { version = "0.2", optional = true } # frontmatter parser gray_matter = { version = "0.2", optional = true } # frontmatter parser
serde_yaml = { version = "0.9", optional = true } serde_yaml = { version = "0.9", optional = true }
anyhow = { version = "1.0", optional = true } anyhow = { version = "1.0", optional = true }

View file

@ -7,8 +7,8 @@ description: Ma première configuration et ma première contribution à l'enviro
project_link: none project_link: none
draft: true draft: true
tags: tags:
- kde - KDE
- linux - Linux
--- ---
## Rapide rappel à propos de KDE ## Rapide rappel à propos de KDE

View file

@ -7,8 +7,8 @@ description: Installation de pmbootstrap et compilation d'un paquet Postmarket O
project_link: none project_link: none
draft: true draft: true
tags: tags:
- pmOS - Postmarket OS
- linux - Linux
--- ---
## À propos de Postmarket OS ## À propos de Postmarket OS
@ -68,11 +68,18 @@ git -C $workdir checkout [branch]
## Compilation de notre paquet Alpine ## Compilation de notre paquet Alpine
Pour compiler le paquet, on utilise la commande `pmbootstrap build [paquet_name]`. Pour compiler le paquet, on utilise la commande `pmbootstrap build [paquet_name]`.
> Avant de compiler depuis le repository Nightly, il faut d'abord rajouter la clef de signature
>
> `wget https://nightly.postmarketos.org/plasma-mobile/pmos@local-662fcd2f.rsa.pub`
>
> `mv pmos@local-662fcd2f.rsa.pub $(pmbootstrap config work)/config_apk_keys/`
>
> Source : https://wiki.postmarketos.org/wiki/Nightly
_Exemple de commande_ _Exemple de commande_
```bash ```bash
pmbootstrap \ pmbootstrap \
-mp https://nightly.postmarketos.org/plasma-mobile/paquets/ \ -mp https://nightly.postmarketos.org/plasma-mobile/packages/ \
-mp http://mirror.postmarketos.org/postmarketos/ \ -mp http://mirror.postmarketos.org/postmarketos/ \
--details-to-stdout \ --details-to-stdout \
-j 32 \ -j 32 \
@ -89,7 +96,7 @@ Dans cet exemple, j'ai utilisé les arguments suivants:
* `-mp` : Permet de définir un repository Alpine pour installer les paquets. * `-mp` : Permet de définir un repository Alpine pour installer les paquets.
> Dans l'exemple, j'utilise deux repos > Dans l'exemple, j'utilise deux repos
> >
> `https://nightly.postmarketos.org/plasma-mobile/paquets/` : Contient la version en cours de développement de KDE Plasma Mobile > `https://nightly.postmarketos.org/plasma-mobile/packages/` : Contient la version en cours de développement de KDE Plasma Mobile
> >
> `http://mirror.postmarketos.org/postmarketos/` : Le miroir officiel de Postmarket OS > `http://mirror.postmarketos.org/postmarketos/` : Le miroir officiel de Postmarket OS

85
flake.lock Normal file
View file

@ -0,0 +1,85 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1717112898,
"narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": [
"flake-utils"
],
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1717294752,
"narHash": "sha256-QhlS52cEQyx+iVcgrEoCnEEpWUA6uLdmeLRxk935inI=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "b46857a406d207a1de74e792ef3b83e800c30e08",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

36
flake.nix Normal file
View file

@ -0,0 +1,36 @@
{
description = "Portfolio rust configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-utils.follows = "flake-utils";
};
};
outputs = { self, nixpkgs, flake-utils, rust-overlay }:
flake-utils.lib.eachSystem flake-utils.lib.allSystems (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit system overlays;
};
rust = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
nodejs = pkgs.nodejs_20;
in
{
devShells = {
default = pkgs.mkShell {
buildInputs = [
(rust.override { extensions = ["rust-src" "rust-analyzer"]; })
nodejs
pkgs.cargo-leptos
pkgs.dart-sass
];
};
};
});
}

View file

@ -9,6 +9,7 @@ cfg_if::cfg_if! {
pub posts: Vec<Arc<Post>>, pub posts: Vec<Arc<Post>>,
pub posts_by_slug: HashMap<String, Arc<Post>>, pub posts_by_slug: HashMap<String, Arc<Post>>,
pub posts_by_tag: HashMap<String, Vec<Arc<Post>>>, pub posts_by_tag: HashMap<String, Vec<Arc<Post>>>,
pub tags: Vec<String>,
} }
impl Data { impl Data {
@ -21,6 +22,8 @@ cfg_if::cfg_if! {
.map(Arc::new) .map(Arc::new)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut tags : Vec<String> = Vec::new();
let mut posts_by_slug : HashMap<String, Arc<Post>> = HashMap::new(); let mut posts_by_slug : HashMap<String, Arc<Post>> = HashMap::new();
let mut posts_by_tag : HashMap<String, Vec<Arc<Post>>> = HashMap::new(); let mut posts_by_tag : HashMap<String, Vec<Arc<Post>>> = HashMap::new();
@ -28,19 +31,26 @@ cfg_if::cfg_if! {
posts_by_slug.insert(post.metadata.slug.clone(), post.clone()); posts_by_slug.insert(post.metadata.slug.clone(), post.clone());
for tag in &post.metadata.tags { for tag in &post.metadata.tags {
let tag = tag.to_lowercase(); let tag_lower = tag.to_lowercase();
let posts = posts_by_tag.entry(tag).or_default();
if let None = tags.iter().find(|t| t.to_lowercase().eq(&tag_lower)) {
tags.push(tag.clone());
}
let posts = posts_by_tag.entry(tag_lower).or_default();
posts.push(post.clone()); posts.push(post.clone());
} }
} }
log::info!("Loaded {} posts", posts.len()); log::info!("Loaded {} posts", posts.len());
log::info!("Found {} tags", posts_by_tag.len()); log::info!("Found {} tags in global", tags.len());
log::info!("Found {} tags from posts", posts_by_tag.len());
Ok(Self { Ok(Self {
posts, posts,
posts_by_slug, posts_by_slug,
posts_by_tag, posts_by_tag,
tags
}) })
} }
} }

View file

@ -16,7 +16,7 @@ pub async fn get_posts(
let default = vec![]; let default = vec![];
let posts = match tag { let posts = match tag {
Some(tag) => data.posts_by_tag.get(&tag).unwrap_or(&default), Some(tag) => data.posts_by_tag.get(&tag.to_lowercase()).unwrap_or(&default),
None => &data.posts None => &data.posts
}; };
@ -32,6 +32,16 @@ pub async fn get_posts(
) )
} }
#[server]
pub async fn get_tags() -> Result<Vec<String>, ServerFnError> {
let data : actix_web::web::Data<crate::app::models::Data> = leptos_actix::extract().await?;
let data = data.into_inner();
let mut tags = data.tags.clone();
tags.sort();
Ok(tags)
}
#[server] #[server]
pub async fn get_post( pub async fn get_post(
slug: String slug: String
@ -117,6 +127,7 @@ pub fn PostList() -> impl IntoView {
let query = use_query_map(); let query = use_query_map();
let tag = move || query.with(|query| query.get("tag").cloned()); let tag = move || query.with(|query| query.get("tag").cloned());
let posts = create_resource(move || tag(), move |_| get_posts(tag())); let posts = create_resource(move || tag(), move |_| get_posts(tag()));
let tags = create_resource(|| (), move |_| get_tags());
let posts_view = move || { let posts_view = move || {
posts.and_then(|posts| { posts.and_then(|posts| {
@ -126,18 +137,39 @@ pub fn PostList() -> impl IntoView {
}) })
}; };
let tags_view = move || {
tags.and_then(|tags| {
tags.iter()
.map(|tag| {
let tag = tag.clone();
view! { <A class="tag" href=format!("/posts?tag={}", tag.clone())>{tag}</A> }
})
.collect_view()
})
};
let filter_view = move || { let filter_view = move || {
tag().map(|tag| Some(view! { match tag() {
<div class="mx-auto max-w-3xl mb-5"> Some(tag) => {
Tag sélectionné : <A class="tag" href="/posts".to_string()>{tag}<leptos_icons::Icon icon=icondata::IoClose class="scale-125 ml-1 inline" /></A> view! {
</div> <><A class="tag" href="/posts".to_string()>{tag}<leptos_icons::Icon icon=icondata::IoClose class="scale-125 ml-1 inline" /></A></>
})) }.into_view()
},
None => {
view! {
<>{tags_view}</>
}.into_view()
}
}
}; };
view! { view! {
<Suspense fallback=move || view! { <Loading title="Chargement des posts...".to_string() /> }> <Suspense fallback=move || view! { <Loading title="Chargement des posts...".to_string() /> }>
<Nav/> <Nav/>
<div class="mx-auto max-w-3xl mb-5 flex flex-wrap gap-2">
<span>Filtre : </span>
{filter_view} {filter_view}
</div>
<main class="posts"> <main class="posts">
<div class="posts__cards">{posts_view}</div> <div class="posts__cards">{posts_view}</div>
</main> </main>
@ -152,26 +184,26 @@ pub fn PostElement() -> impl IntoView {
let post = create_resource(|| (), move |_| get_post(slug())); let post = create_resource(|| (), move |_| get_post(slug()));
let post_view = move || { let render_draft_notice = |post: &Post| -> Option<View> {
post.and_then(|post| {
view! {
<>
{
if post.metadata.draft { if post.metadata.draft {
Some(view!{ return Some(view! {
<div class="bg-warning text-on_warning dark:bg-dark_warning dark:text-dart_on_warning rounded-md p-5 mb-5"> <div class="bg-warning text-on_warning dark:bg-dark_warning dark:text-dart_on_warning rounded-md p-5 mb-5">
r#" r#"
L'article est en cours d'écriture. La formulation peut ne pas être exacte et les phrases peuvent contenir des fautes. L'article est en cours d'écriture. La formulation peut ne pas être exacte et les phrases peuvent contenir des fautes.
"# "#
</div> </div>
}) }.into_view());
} else { }
None None
} };
}
let render_post_view = move || {
post.and_then(|post| {
let draft_notice = render_draft_notice(&post);
view! {
<>
{draft_notice}
<PostHeader metadata=post.metadata.clone() full_element=true /> <PostHeader metadata=post.metadata.clone() full_element=true />
<div inner_html={post.content.clone()}></div> <div inner_html={post.content.clone()}></div>
</> </>
} }
@ -182,7 +214,7 @@ pub fn PostElement() -> impl IntoView {
<Suspense fallback=move || view! { <Loading title="Chargement du post...".to_string() /> }> <Suspense fallback=move || view! { <Loading title="Chargement du post...".to_string() /> }>
<Nav/> <Nav/>
<main class="post"> <main class="post">
{post_view} {render_post_view}
<script>load();</script> <script>load();</script>
</main> </main>
</Suspense> </Suspense>