2020-10-26 20:14:04 +01:00
|
|
|
use std::convert::TryInto;
|
2020-10-26 16:51:25 +01:00
|
|
|
use gotham::{
|
|
|
|
helpers::http::response::create_response,
|
|
|
|
hyper::{Body, Response},
|
|
|
|
state::{FromState, State},
|
|
|
|
};
|
|
|
|
use http::status::StatusCode;
|
|
|
|
use mime::TEXT_HTML;
|
|
|
|
|
2020-10-26 20:14:04 +01:00
|
|
|
use typed_html::{html, text, dom::DOMTree, types::{Class, SpacedSet}};
|
2020-10-26 16:51:25 +01:00
|
|
|
use diesel::prelude::*;
|
2020-10-30 17:01:20 +01:00
|
|
|
use chrono::{offset::Local, Datelike, Duration, NaiveDate};
|
2020-10-26 16:51:25 +01:00
|
|
|
|
|
|
|
use libticker::{
|
|
|
|
schema::{self, events::dsl::events},
|
|
|
|
model::Event,
|
|
|
|
};
|
|
|
|
use crate::AppState;
|
|
|
|
|
|
|
|
|
|
|
|
fn fix_url(s: &str) -> std::borrow::Cow<str> {
|
|
|
|
if s.starts_with("http:") || s.starts_with("https:") {
|
|
|
|
s.into()
|
|
|
|
} else {
|
|
|
|
format!("http://{}", s).into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DayEvents<'e> {
|
|
|
|
date: NaiveDate,
|
|
|
|
events: &'e [Event],
|
|
|
|
}
|
|
|
|
|
|
|
|
/// assumes pre-sorted input
|
|
|
|
fn group_by_day(es: &[Event]) -> Vec<DayEvents> {
|
|
|
|
let mut results = vec![];
|
|
|
|
|
|
|
|
let mut prev_date = None;
|
|
|
|
let mut date_start = 0;
|
|
|
|
for (i, event) in es.iter().enumerate() {
|
|
|
|
if prev_date.is_some() && prev_date != Some(event.dtstart.date()) {
|
|
|
|
if i > date_start {
|
|
|
|
results.push(DayEvents {
|
|
|
|
date: prev_date.unwrap().clone(),
|
|
|
|
events: &es[date_start..i],
|
|
|
|
});
|
|
|
|
date_start = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prev_date = Some(event.dtstart.date());
|
|
|
|
}
|
|
|
|
|
|
|
|
results
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_index(app_state: &AppState) -> String {
|
|
|
|
let db = app_state.db.lock().unwrap();
|
|
|
|
let today = Local::today().naive_local().and_hms(0, 0, 0);
|
2020-10-30 17:01:20 +01:00
|
|
|
let limit = Local::today().naive_local().and_hms(0, 0, 0) +
|
|
|
|
Duration::weeks(2);
|
2020-10-26 16:51:25 +01:00
|
|
|
let es = events
|
2020-10-30 17:21:06 +01:00
|
|
|
.filter(schema::events::dtend.ge(&today))
|
|
|
|
.filter(schema::events::dtend.lt(&limit))
|
2020-10-26 16:51:25 +01:00
|
|
|
.order_by(schema::events::dtstart.asc())
|
|
|
|
.then_order_by(schema::events::dtend.desc())
|
|
|
|
.load::<Event>(&*db)
|
|
|
|
.unwrap();
|
|
|
|
let days = group_by_day(&es);
|
|
|
|
|
2020-10-26 19:48:17 +01:00
|
|
|
let config = &app_state.config;
|
|
|
|
|
2020-10-26 16:51:25 +01:00
|
|
|
let doc: DOMTree<String> = html!(
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>"Ticker"</title>
|
2020-10-27 17:35:44 +01:00
|
|
|
<meta charset="utf-8"/>
|
|
|
|
<meta name="viewport" content="width=device-width"/>
|
2020-10-26 19:48:17 +01:00
|
|
|
<link rel="stylesheet" title="Style" type="text/css" href="static/style.css"/>
|
2020-10-27 17:35:44 +01:00
|
|
|
<link rel="icon" type="image/png" href="static/favicon.png"/>
|
2020-10-26 16:51:25 +01:00
|
|
|
</head>
|
|
|
|
<body>
|
2020-10-26 20:14:04 +01:00
|
|
|
{ days.iter().map(|day| {
|
|
|
|
let mut day_class: SpacedSet<Class> = ["date"].try_into().unwrap();
|
|
|
|
day_class.add(&format!("wd{}", day.date.weekday().num_days_from_monday())[..]);
|
|
|
|
html!(
|
|
|
|
<div>
|
|
|
|
<h2>
|
|
|
|
<span class={ day_class }>
|
|
|
|
<span class="day">
|
|
|
|
{ text!("{}", day.date.day()) }
|
|
|
|
</span>
|
|
|
|
<span class="month">
|
|
|
|
{ text!("{}", &config.months[day.date.month0() as usize]) }
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
|
<span class="weekday">
|
|
|
|
{ text!("{}", &config.weekdays[day.date.weekday().num_days_from_monday() as usize]) }
|
|
|
|
</span>
|
|
|
|
</h2>
|
2020-10-26 16:51:25 +01:00
|
|
|
|
2020-10-26 20:14:04 +01:00
|
|
|
{ day.events.iter().map(|e| html!(
|
2020-10-30 17:01:35 +01:00
|
|
|
<article class="event"
|
|
|
|
style={ format!("border-left: 1.5rem solid {}", &config.calendars.get(&e.calendar).map(|o| &o.color[..]).unwrap_or("white")) }
|
|
|
|
>
|
|
|
|
<p class="dtstart"
|
|
|
|
title={ format!("{}", e.dtstart.format("%c")) }
|
|
|
|
>
|
2020-10-30 17:17:41 +01:00
|
|
|
{ text!("{}", &e.dtstart.format("%H:%M")) }
|
2020-10-30 17:01:35 +01:00
|
|
|
</p>
|
2020-10-26 20:14:04 +01:00
|
|
|
{ match &e.url {
|
|
|
|
None => html!(
|
|
|
|
<h3>{ text!("{}", &e.summary) }</h3>
|
|
|
|
),
|
|
|
|
Some(url) => html!(
|
|
|
|
<h3>
|
|
|
|
<a href={ fix_url(url) }>
|
|
|
|
{ text!("{}", &e.summary) }
|
|
|
|
</a>
|
|
|
|
</h3>
|
|
|
|
),
|
|
|
|
} }
|
2020-10-26 16:51:25 +01:00
|
|
|
|
2020-10-26 20:14:04 +01:00
|
|
|
<p class="location">
|
2020-10-30 17:01:35 +01:00
|
|
|
{ text!("{}", e.location.as_ref().unwrap_or(&"".to_owned())) }
|
|
|
|
</p>
|
2020-10-26 20:14:04 +01:00
|
|
|
</article>
|
|
|
|
)) }
|
|
|
|
</div>)
|
|
|
|
}) }
|
2020-10-30 17:01:03 +01:00
|
|
|
<footer>
|
|
|
|
<p>
|
|
|
|
"Ein Projekt des "
|
|
|
|
<a href="https://www.c3d2.de/">"C3D2"</a>
|
|
|
|
" - "
|
|
|
|
<a href="https://gitea.c3d2.de/astro/ticker">"Code"</a>
|
|
|
|
</p>
|
|
|
|
</footer>
|
2020-10-26 16:51:25 +01:00
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
);
|
|
|
|
format!("<DOCTYPE html>\n{}", doc.to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn index(state: State) -> (State, Response<Body>) {
|
|
|
|
let message = {
|
|
|
|
let app_state = AppState::borrow_from(&state);
|
|
|
|
render_index(app_state)
|
|
|
|
};
|
|
|
|
let res = create_response(&state, StatusCode::OK, TEXT_HTML, message);
|
|
|
|
(state, res)
|
|
|
|
}
|