/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin */ use super::{ account::session::Session, layout::LayoutPage, node::{DatabaseNodeUserDataExt, NodeCard}, }; use crate::{ database::DataAcid, routes::ui::{error::MyResult, layout::DynLayoutPage}, }; use anyhow::Context; use chrono::{Datelike, Utc}; use jellybase::{ database::{ReadableTable, TableExt, T_NODE, T_USER_NODE}, CONF, }; use jellycommon::user::WatchedState; use rocket::{get, State}; use tokio::fs::read_to_string; #[get("/")] pub fn r_home(sess: Session, db: &State) -> MyResult { let mut items = { let txn = db.begin_read()?; let nodes = txn.open_table(T_NODE)?; let node_users = txn.open_table(T_USER_NODE)?; let i = nodes .iter()? .map(|a| { let (x, y) = a.unwrap(); let (x, y) = (x.value().to_owned(), y.value().0); let z = node_users .get(&(sess.user.name.as_str(), x.as_str())) .unwrap() .map(|z| z.value().0) .unwrap_or_default(); let y = y.public; (x, y, z) }) .collect::>(); drop(nodes); i }; let random = (0..16) .flat_map(|i| Some(items[cheap_daily_random(i).checked_rem(items.len())?].clone())) .collect::>(); let toplevel = T_NODE .get(&db, "library")? .context("root node missing")? .public .children .into_iter() .map(|n| db.get_node_with_userdata(&n, &sess)) .collect::>>()? .into_iter() .collect::>(); items.sort_by_key(|(_, n, _)| n.release_date.map(|d| -d).unwrap_or(i64::MAX)); let latest = items .iter() .take(16) .map(|k| k.to_owned()) .collect::>(); let continue_watching = items .iter() .filter(|(_, _, u)| matches!(u.watched, WatchedState::Progress(_))) .map(|k| k.to_owned()) .collect::>(); let watchlist = items .iter() .filter(|(_, _, u)| matches!(u.watched, WatchedState::Pending)) .map(|k| k.to_owned()) .collect::>(); Ok(LayoutPage { title: "Home".to_string(), content: markup::new! { h2 { "Explore " @CONF.brand } .hlist { ul {@for (id, node, udata) in &toplevel { li { @NodeCard { id, node, udata } } }}} @if !continue_watching.is_empty() { h2 { "Continue Watching" } .hlist { ul {@for (id, node, udata) in &continue_watching { li { @NodeCard { id, node, udata } } }}} } @if !watchlist.is_empty() { h2 { "Watchlist" } .hlist { ul {@for (id, node, udata) in &watchlist { li { @NodeCard { id, node, udata } } }}} } h2 { "Latest Releases" } .hlist { ul {@for (id, node, udata) in &latest { li { @NodeCard { id, node, udata } } }}} h2 { "Today's Picks" } .hlist { ul {@for (id, node, udata) in &random { li { @NodeCard { id, node, udata } } }}} p.error { "TODO: recently added" } p.error { "TODO: best rating" } }, ..Default::default() }) } #[get("/", rank = 2)] pub async fn r_home_unpriv() -> MyResult> { let front = read_to_string(CONF.asset_path.join("front.htm")).await?; Ok(LayoutPage { title: "Home".to_string(), content: markup::new! { @markup::raw(&front) }, ..Default::default() }) } fn cheap_daily_random(i: usize) -> usize { xorshift(xorshift(Utc::now().num_days_from_ce() as u64) + i as u64) as usize } fn xorshift(mut x: u64) -> u64 { x ^= x << 13; x ^= x >> 7; x ^= x << 17; x }