diff --git a/src/data.rs b/src/data.rs index a5d2fde..cc54daa 100644 --- a/src/data.rs +++ b/src/data.rs @@ -167,6 +167,7 @@ pub struct FishMeta { pub name_en: String, } +#[derive(Clone)] pub struct CombinedFish<'a> { pub entry: &'a FishEntry, pub meta: &'a FishMeta, @@ -176,7 +177,7 @@ pub struct CombinedFish<'a> { pub rarity: f32, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct Window { pub start_time: DateTime, pub duration: Duration, diff --git a/src/main.rs b/src/main.rs index f2c82f6..dad21b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use axum::{ routing::{get, post}, Router, }; -use data::{Data, Filters}; +use data::{CombinedFish, Data, Filters}; use maud::{html, Markup}; use nanoid::nanoid; use serde::Deserialize; @@ -81,7 +81,30 @@ async fn main_handler( // If this is a HTMX-sent request, don't resend the entire page, just the list. let is_htmx = headers.contains_key("HX-Request"); - Ok(templates::main_page(state, caught_fish, acc_id, &filters, is_htmx).into_response()) + + // Extract the list of fish we want to render. + let meta = state.data.fish_with_meta(); + let mut values: Vec<&CombinedFish> = filters.filter(meta.values().collect(), &caught_fish); + values.sort_by(|afish, bfish| { + bfish + .is_up + .cmp(&afish.is_up) + .then(bfish.is_always_up.cmp(&afish.is_always_up)) + .then(bfish.rarity.total_cmp(&afish.rarity).reverse()) + .then(bfish.meta.name_en.cmp(&afish.meta.name_en)) + }); + + let state = state.clone(); + let filters = filters.clone(); + Ok(templates::main_page(templates::ViewData { + state, + fish: values, + caught_fish, + acc_id, + filters, + only_list: is_htmx, + }) + .into_response()) } async fn insert_cf_handler( diff --git a/src/templates.rs b/src/templates.rs index 300e12a..4d39d19 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -9,6 +9,15 @@ use crate::{ AppState, }; +pub struct ViewData<'a> { + pub state: State>, + pub fish: Vec<&'a CombinedFish<'a>>, + pub caught_fish: Vec, + pub acc_id: String, + pub filters: Filters, + pub only_list: bool, +} + pub fn layout(content: Markup) -> Markup { html! { (DOCTYPE) @@ -31,54 +40,51 @@ pub fn layout(content: Markup) -> Markup { } } -pub fn main_page( - state: State>, - caught_fish: Vec, - acc_id: String, - filters: &Filters, - only_list: bool, -) -> Markup { - let meta = state.data.fish_with_meta(); - let mut values: Vec<&CombinedFish> = filters.filter(meta.values().collect(), &caught_fish); - - values.sort_by(|afish, bfish| { - bfish - .is_up - .cmp(&afish.is_up) - .then(bfish.is_always_up.cmp(&afish.is_always_up)) - .then(bfish.rarity.total_cmp(&afish.rarity).reverse()) - .then(bfish.meta.name_en.cmp(&afish.meta.name_en)) - }); - // values.sort_by(|afish, bfish| { - // if !afish.is_up && !bfish.is_up && !afish.windows.is_empty() && !bfish.windows.is_empty() { - // bfish - // .windows - // .first() - // .unwrap() - // .start_time - // .cmp(&afish.windows.first().unwrap().start_time) - // .reverse() - // } else { - // Ordering::Equal - // } - // }); +pub fn main_page(data: ViewData) -> Markup { let list = html! { h2.clock { "ET " (clock::get_current_eorzea_date().format("%H:%M")) } - @for fish in values { + (fish_list(&data)) + script src="/static/scripts/dates.js" type="text/javascript" {} + }; + + let template = html! { + span style="display: none;" id="account-id" { (data.acc_id) } + (header(&data)) + div id="list" hx-get="" hx-trigger="every 10s" hx-swap="innerHTML" hx-target="this" hx-on="changeDates" { + (list) + } + + script src="/static/scripts/save.js" type="text/javascript" {} + }; + + if data.only_list { + list + } else { + layout(html! { + main { + (template) + } + }) + } +} + +pub fn fish_list(data: &ViewData) -> Markup { + html! { + @for fish in data.fish.clone() { section.fish.up[fish.is_up].alwaysup[fish.is_always_up] { .title { div { - @if !caught_fish.contains(&(fish.entry.id as i32)) { - form action=(format!("/{}/catch/{}", acc_id, fish.entry.id)) method="post" { + @if !data.caught_fish.contains(&(fish.entry.id as i32)) { + form action=(format!("/{}/catch/{}", data.acc_id, fish.entry.id)) method="post" { button.catch-button type="submit" { (PreEscaped("✓")) } } } } div { h3 { (fish.meta.name_en) } - .subtitle { - "Patch " (fish.entry.patch) - } + .subtitle { + "Patch " (fish.entry.patch) + } } } .when { @@ -93,22 +99,22 @@ pub fn main_page( .date data-ts=(clock::to_earth_time(window.start_time + window.duration).timestamp_millis()) { .inner id=(format!("date-{}", fish.entry.id)) hx-preserve { (clock::to_earth_time(window.start_time + window.duration).format("%c %Z")) - } + } } @if let Some(window2) = fish.windows.get(1) { - .date.tiny data-ts=(clock::to_earth_time(window2.start_time).timestamp_millis()) { - "next: " + .date.tiny data-ts=(clock::to_earth_time(window2.start_time).timestamp_millis()) { + "next: " .inner id=(format!("nextwindow-{}", fish.entry.id)) hx-preserve { (clock::to_earth_time(window2.start_time).format("%c %Z")) - } } + } } } @else { .date data-ts=(clock::to_earth_time(window.start_time).timestamp_millis()) { .inner id=(format!("date-{}", fish.entry.id)) hx-preserve { (clock::to_earth_time(window.start_time).format("%c %Z")) - } + } } } } @@ -116,15 +122,15 @@ pub fn main_page( } .how { @for item_id in &fish.entry.best_catch_path { - @if let Some(item) = state.data.db_data.items.get(item_id) { + @if let Some(item) = data.state.data.db_data.items.get(item_id) { span.catchpath title=(item.name_en) { img src=(item.get_icon_url()) width="35"; - @if let Some(hookset) = item.get_hookset(&state.data) { + @if let Some(hookset) = item.get_hookset(&data.state.data) { img.hookset src=(hookset) width="20"; } - @if let Some(tug) = item.get_tug(&state.data) { + @if let Some(tug) = item.get_tug(&data.state.data) { span.tug { (tug) } } } @@ -147,7 +153,7 @@ pub fn main_page( @if !fish.is_always_up { (clock::display_eorzea_time(&clock::set_hm_from_float(&clock::get_current_eorzea_date(), fish.entry.start_hour.unwrap()))) " to " - (clock::display_eorzea_time(&clock::set_hm_from_float(&clock::get_current_eorzea_date(), fish.entry.end_hour.unwrap()))) + (clock::display_eorzea_time(&clock::set_hm_from_float(&clock::get_current_eorzea_date(), fish.entry.end_hour.unwrap()))) } @else { "always up!" } @@ -157,12 +163,11 @@ pub fn main_page( } } } + } +} - script src="/static/scripts/dates.js" type="text/javascript" {} - }; - - let template = html! { - span style="display: none;" id="account-id" { (acc_id) } +pub fn header(data: &ViewData) -> Markup { + html! { .header { div {} .side { @@ -176,7 +181,7 @@ pub fn main_page( summary { "Filters" } form { fieldset { - @if filters.non_big_fish { + @if data.filters.non_big_fish { input name="big" id="big" type="checkbox" checked; } @else { input name="big" id="big" type="checkbox"; @@ -185,7 +190,7 @@ pub fn main_page( } fieldset { - @if filters.include_caught { + @if data.filters.include_caught { input name="caught" id="caught" type="checkbox" checked; } @else { input name="caught" id="caught" type="checkbox"; @@ -195,38 +200,23 @@ pub fn main_page( fieldset { label for="patches" { "Filter by patch" } - br; - select name="patches" id="patches" multiple { - @for patch in data::PATCHES { - @if filters.patches.contains(&patch) { - option value=(patch) selected { (format!("{:.1}", patch)) } - } @else { - option value=(patch) { (format!("{:.1}", patch)) } - } + br; + select name="patches" id="patches" multiple { + @for patch in data::PATCHES { + @if data.filters.patches.contains(&patch) { + option value=(patch) selected { (format!("{:.1}", patch)) } + } @else { + option value=(patch) { (format!("{:.1}", patch)) } } } } + } button type="submit" { "Apply" } } } } } - div id="list" hx-get="" hx-trigger="every 10s" hx-swap="innerHTML" hx-target="this" hx-on="changeDates" { - (list) - } - - script src="/static/scripts/save.js" type="text/javascript" {} - }; - - if only_list { - list - } else { - layout(html! { - main { - (template) - } - }) } }