diff options
Diffstat (limited to 'server/src/routes/ui')
-rw-r--r-- | server/src/routes/ui/account/mod.rs | 19 | ||||
-rw-r--r-- | server/src/routes/ui/error.rs | 5 | ||||
-rw-r--r-- | server/src/routes/ui/home.rs | 3 | ||||
-rw-r--r-- | server/src/routes/ui/layout.rs | 6 | ||||
-rw-r--r-- | server/src/routes/ui/node.rs | 65 | ||||
-rw-r--r-- | server/src/routes/ui/style/layout.css | 67 |
6 files changed, 142 insertions, 23 deletions
diff --git a/server/src/routes/ui/account/mod.rs b/server/src/routes/ui/account/mod.rs index 86e0f18..7e329a1 100644 --- a/server/src/routes/ui/account/mod.rs +++ b/server/src/routes/ui/account/mod.rs @@ -1,6 +1,8 @@ use super::HtmlTemplate; +use crate::database::User; +use crate::{AppState, CONF}; use rocket::form::Form; -use rocket::{get, post, FromForm}; +use rocket::{get, post, FromForm, State}; #[derive(FromForm)] pub struct RegisterForm { @@ -17,7 +19,7 @@ pub fn r_account_register() -> HtmlTemplate<markup::DynRender<'static>> { HtmlTemplate( "Register".to_string(), markup::new! { - h1 { "Register for Jellything" } + h1 { "Register for " @CONF.brand } form[method="POST", action=""] { label[for="inp-invitation"] { "Invite Code: " } input[type="text", id="inp-invitation", name="invitation"]; br; @@ -46,8 +48,21 @@ pub fn r_account_login() -> HtmlTemplate<markup::DynRender<'static>> { #[post("/account/register", data = "<form>")] pub fn r_account_register_post( + state: &State<AppState>, form: Form<RegisterForm>, ) -> HtmlTemplate<markup::DynRender<'static>> { + state + .database + .users + .insert( + &form.username, + &User { + display_name: form.username.clone(), + name: form.username.clone(), + password: form.password.clone().into(), // TODO hash it + }, + ) + .unwrap(); HtmlTemplate( "Registration successful".to_string(), markup::new! { diff --git a/server/src/routes/ui/error.rs b/server/src/routes/ui/error.rs index 0b24b62..1a50796 100644 --- a/server/src/routes/ui/error.rs +++ b/server/src/routes/ui/error.rs @@ -59,3 +59,8 @@ impl From<std::fmt::Error> for MyError { MyError(anyhow::anyhow!("{err}")) } } +impl From<std::io::Error> for MyError { + fn from(err: std::io::Error) -> Self { + MyError(anyhow::anyhow!("{err}")) + } +} diff --git a/server/src/routes/ui/home.rs b/server/src/routes/ui/home.rs index df95665..04a4c7d 100644 --- a/server/src/routes/ui/home.rs +++ b/server/src/routes/ui/home.rs @@ -1,4 +1,5 @@ use crate::routes::ui::node::NodePage; +use crate::CONF; use crate::{routes::ui::HtmlTemplate, AppState}; use rocket::{get, State}; @@ -7,7 +8,7 @@ pub async fn r_home(state: &State<AppState>) -> HtmlTemplate<markup::DynRender> HtmlTemplate( "Home".to_string(), markup::new! { - p { "Welcome to Jellything" } + p { "Welcome to " @CONF.brand } @NodePage { node: state.library.root.clone() } }, ) diff --git a/server/src/routes/ui/layout.rs b/server/src/routes/ui/layout.rs index f333fa1..51905db 100644 --- a/server/src/routes/ui/layout.rs +++ b/server/src/routes/ui/layout.rs @@ -1,16 +1,18 @@ use markup::Render; +use crate::CONF; + markup::define! { Layout<Main: Render>(title: String, main: Main) { @markup::doctype() html { head { - title { @title " - Jellything" } + title { @title " - " @CONF.brand } link[rel="stylesheet", href="/assets/style.css"]; } body { nav { - h1 { a[href="/"] { "Jellything" } } + h1 { a[href="/"] { @CONF.brand } } a[href="/library"] { "My Library" } } #main { @main } diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs index 64f5e84..9162982 100644 --- a/server/src/routes/ui/node.rs +++ b/server/src/routes/ui/node.rs @@ -5,9 +5,11 @@ use crate::{ routes::ui::HtmlTemplate, AppState, }; -use anyhow::Context; +use anyhow::{anyhow, Context}; +use log::info; use rocket::{get, uri, State}; use std::{ops::Deref, path::PathBuf, sync::Arc}; +use tokio::fs::File; #[get("/library/<path..>")] pub async fn r_library_node( @@ -35,27 +37,66 @@ markup::define! { } } DirectoryCard(dir: Arc<Directory>) { - span { a[href=&uri!(r_library_node(&dir.lib_path)).to_string()] { @dir.data.name } } + div.card.dir { a[href=&uri!(r_library_node(&dir.lib_path)).to_string()] { @dir.data.name } } } DirectoryPage(dir: Arc<Directory>) { - h1 { @dir.data.name } - ul.directorylisting { - @for el in &dir.children { - li { - span.title { @match el.deref().to_owned() { + div.page.dir { + h1 { @dir.data.name } + ul.directorylisting { + @for el in &dir.children { + li { @match el.deref().to_owned() { Node::Directory(dir) => { @DirectoryCard { dir } } Node::Item(item) => { @ItemCard { item } } - }} + } } } } } } ItemCard(item: Arc<Item>) { - span { a[href=&uri!(r_library_node(&item.lib_path)).to_string()] { @item.info.title } } + div.card.item { + a[href=&uri!(r_library_node(&item.lib_path)).to_string()] { + img[src=uri!(r_item_assets(&item.lib_path)).to_string()]; + } + a[href=&uri!(r_library_node(&item.lib_path)).to_string()] { + p.title { @item.info.title } + } + } } ItemPage(item: Arc<Item>) { - h1 { @item.info.title } - a[href=&player_uri(&item.lib_path)] { "Watch now" } - p { "Lorem ipsum dolor sit amed...." } + div.page.item { + div.backdrop { + // TODO different image here + img[src=uri!(r_item_assets(&item.lib_path)).to_string()]; + } + div.banner { + img[src=uri!(r_item_assets(&item.lib_path)).to_string()]; + } + div.title { + h1 { @item.info.title } + a[href=&player_uri(&item.lib_path)] { "Watch now" } + } + div.details { + h3 { "Lorem Ipsum!" } + p { "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." } + } + } } } + +#[get("/item_assets/<path..>")] +pub async fn r_item_assets(path: PathBuf, state: &State<AppState>) -> Result<File, MyError> { + let node = state + .library + .nested_path(&path) + .context("retrieving library node")? + .get_item()? + .clone(); + let path = node.fs_path.parent().unwrap().join( + node.info + .banner + .clone() + .ok_or(anyhow!("no banner available"))?, + ); + info!("loading asset from {path:?}"); + Ok(File::open(path).await?) +} diff --git a/server/src/routes/ui/style/layout.css b/server/src/routes/ui/style/layout.css index 078dd4e..6a63ccc 100644 --- a/server/src/routes/ui/style/layout.css +++ b/server/src/routes/ui/style/layout.css @@ -3,6 +3,10 @@ src: url(/assets/cantarell.woff2) format("woff2"); } +:root { + --card-size: 12em; +} + * { color: rgb(218, 218, 218); font-family: "Cantarell", sans-serif; @@ -10,8 +14,16 @@ } body { - background-color: #0f0f0f; + background-color: #070707; width: 100vw; + margin: 0px; + padding: 0px; +} + +img { + width: 100%; + height: 100%; + object-fit: cover; } nav { @@ -21,7 +33,7 @@ nav { padding: 1em; width: calc(100vw - 2em); height: 2em; - background-color: #27272744; + background-color: #1c1c1c9a; } nav h1 { @@ -32,10 +44,6 @@ nav h1 { } #main { - margin-top: 5em; - padding: 1em; - padding-left: 3em; - padding-right: 3em; } .error { @@ -52,3 +60,50 @@ input { option { font-family: "Cantarell", sans-serif; } + +.page.dir { + margin-top: 5em; + padding: 1em; + padding-left: 3em; + padding-right: 3em; +} +.directorylisting { + list-style: none; + display: flex; + flex-wrap: wrap; +} +.directorylisting li { + display: block; +} + +.card.item { + width: var(--card-size); + height: calc(var(--card-size) * 1.41); + padding: 1em; +} +.card.dir { + width: calc(var(--card-size) * 2); + height: calc(var(--card-size) * 1.41); +} +.card a { + width: 100%; + height: 100%; +} + +.card.item .title { + margin-top: 0.1em; + text-align: center; + text-overflow: ellipsis; +} + +.page.item .backdrop { + width: 100%; + margin-bottom: calc(-100% * 1.41 + 18em); +} +.page.item .backdrop img { + mask-mode: alpha; + mask-image: linear-gradient(rgba(0, 0, 0, 1), transparent 30%); +} +.page.item .banner { + width: max(8em, 20%); +} |