Add widescreen setting and universal time
This commit is contained in:
parent
775d6c2899
commit
ce912d4b85
7 changed files with 295 additions and 64 deletions
148
api/Cargo.lock
generated
148
api/Cargo.lock
generated
|
|
@ -67,7 +67,7 @@ dependencies = [
|
|||
"actix-rt",
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.9.1",
|
||||
"brotli",
|
||||
"bytes",
|
||||
|
|
@ -158,6 +158,23 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-session"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "400c27fd4cdbe0082b7bbd29ac44a3070cbda1b2114138dc106ba39fe2f90dff"
|
||||
dependencies = [
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"derive_more 2.0.1",
|
||||
"rand 0.9.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-utils"
|
||||
version = "3.0.1"
|
||||
|
|
@ -238,6 +255,41 @@ version = "2.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"ghash",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
|
|
@ -574,6 +626,12 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
|
|
@ -753,6 +811,16 @@ dependencies = [
|
|||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.47"
|
||||
|
|
@ -849,7 +917,14 @@ version = "0.16.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64 0.20.0",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
|
@ -925,9 +1000,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core 0.6.4",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
|
|
@ -1374,6 +1459,16 @@ dependencies = [
|
|||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
||||
dependencies = [
|
||||
"opaque-debug",
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
|
|
@ -1628,7 +1723,7 @@ version = "0.1.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
|
@ -1795,6 +1890,15 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
|
|
@ -2169,6 +2273,12 @@ version = "1.70.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.73"
|
||||
|
|
@ -2356,6 +2466,18 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
|
|
@ -2528,7 +2650,7 @@ version = "0.12.22"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-channel",
|
||||
|
|
@ -2656,7 +2778,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2757,6 +2879,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"actix-cors",
|
||||
"actix-files",
|
||||
"actix-session",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"askama",
|
||||
|
|
@ -2764,6 +2887,7 @@ dependencies = [
|
|||
"chrono-tz",
|
||||
"dotenv",
|
||||
"env_logger",
|
||||
"futures-util",
|
||||
"libseptastic",
|
||||
"log",
|
||||
"reqwest",
|
||||
|
|
@ -2974,7 +3098,7 @@ checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
|
|||
dependencies = [
|
||||
"async-io 1.13.0",
|
||||
"async-std",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
|
|
@ -3049,7 +3173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.9.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
|
|
@ -3091,7 +3215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bitflags 2.9.1",
|
||||
"byteorder",
|
||||
"crc",
|
||||
|
|
@ -3554,6 +3678,16 @@ version = "0.2.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
|
|
|
|||
|
|
@ -20,3 +20,5 @@ chrono-tz = "0.10.4"
|
|||
actix-cors = "0.7.1"
|
||||
reqwest = { version = "0.12.22", features = [ "json", "blocking" ] }
|
||||
sqlx-cli = "0.8.6"
|
||||
futures-util = "0.3.31"
|
||||
actix-session = { version = "0.11.0", features = ["cookie-session"] }
|
||||
|
|
|
|||
|
|
@ -22,10 +22,13 @@ body {
|
|||
.body {
|
||||
background-color: #ffffff;
|
||||
margin: 10px auto;
|
||||
max-width: 750px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.body-small {
|
||||
max-width: 750px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #114488;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use actix_web::{get, web::{Data, self}, HttpResponse, Responder};
|
||||
use actix_web::{get, web::{self, Data}, HttpRequest, HttpResponse, Responder};
|
||||
use anyhow::anyhow;
|
||||
use std::{time::Instant, sync::Arc};
|
||||
use libseptastic::{route::RouteType, stop_schedule::Trip};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
|
@ -8,27 +9,33 @@ use crate::AppState;
|
|||
use crate::database;
|
||||
|
||||
#[get("/routes")]
|
||||
async fn get_routes_html(state: Data<Arc<AppState>>) -> impl Responder {
|
||||
let start_time = Instant::now();
|
||||
async fn get_routes_html(req: HttpRequest, state: Data<Arc<AppState>>) -> impl Responder {
|
||||
crate::perform_action(req, move || {
|
||||
let statex = state.clone();
|
||||
async move {
|
||||
let start_time = Instant::now();
|
||||
|
||||
let all_routes = database::get_all_routes(&mut state.database.begin().await.unwrap()).await.unwrap();
|
||||
let all_routes = database::get_all_routes(&mut statex.database.begin().await?).await?;
|
||||
|
||||
let rr_routes = all_routes.clone().into_iter().filter(|x| x.route_type == RouteType::RegionalRail).collect();
|
||||
let subway_routes = all_routes.clone().into_iter().filter(|x| x.route_type == RouteType::SubwayElevated).collect();
|
||||
let trolley_routes = all_routes.clone().into_iter().filter(|x| x.route_type == RouteType::Trolley).collect();
|
||||
let bus_routes = all_routes.into_iter().filter(|x| x.route_type == RouteType::TracklessTrolley || x.route_type == RouteType::Bus).collect();
|
||||
let rr_routes = all_routes.clone().into_iter().filter(|x| x.route_type == RouteType::RegionalRail).collect();
|
||||
let subway_routes = all_routes.clone().into_iter().filter(|x| x.route_type == RouteType::SubwayElevated).collect();
|
||||
let trolley_routes = all_routes.clone().into_iter().filter(|x| x.route_type == RouteType::Trolley).collect();
|
||||
let bus_routes = all_routes.into_iter().filter(|x| x.route_type == RouteType::TracklessTrolley || x.route_type == RouteType::Bus).collect();
|
||||
|
||||
HttpResponse::Ok().body(crate::templates::ContentTemplate {
|
||||
page_title: Some(String::from("SEPTASTIC | Routes")),
|
||||
page_desc: Some(String::from("All SEPTA routes.")),
|
||||
content: crate::templates::RoutesTemplate {
|
||||
rr_routes,
|
||||
subway_routes,
|
||||
trolley_routes,
|
||||
bus_routes,
|
||||
},
|
||||
load_time_ms: Some(start_time.elapsed().as_millis())
|
||||
}.render().unwrap())
|
||||
Ok(crate::templates::ContentTemplate {
|
||||
page_title: Some(String::from("SEPTASTIC | Routes")),
|
||||
page_desc: Some(String::from("All SEPTA routes.")),
|
||||
widescreen: false,
|
||||
content: crate::templates::RoutesTemplate {
|
||||
rr_routes,
|
||||
subway_routes,
|
||||
trolley_routes,
|
||||
bus_routes,
|
||||
},
|
||||
load_time_ms: Some(start_time.elapsed().as_millis())
|
||||
})
|
||||
}
|
||||
}).await
|
||||
}
|
||||
|
||||
#[get("/routes.json")]
|
||||
|
|
@ -37,7 +44,7 @@ async fn get_routes_json(state: Data<Arc<AppState>>) -> impl Responder {
|
|||
HttpResponse::Ok().json(all_routes)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct RouteQueryParams {
|
||||
#[serde(default)] // Optional: handle missing parameters with a default value
|
||||
stops: Option<String>,
|
||||
|
|
@ -67,39 +74,43 @@ async fn get_route_info(route_id: String, state: Data<Arc<AppState>>) -> ::anyho
|
|||
}
|
||||
|
||||
#[get("/route/{route_id}")]
|
||||
async fn get_route(state: Data<Arc<AppState>>, info: web::Query<RouteQueryParams>, path: web::Path<String>) -> impl Responder {
|
||||
let start_time = Instant::now();
|
||||
async fn get_route(state: Data<Arc<AppState>>, req: HttpRequest, info: web::Query<RouteQueryParams>, path: web::Path<String>) -> impl Responder {
|
||||
crate::perform_action(req, move || {
|
||||
let pathx = path.clone();
|
||||
let infox = info.clone();
|
||||
let statex = state.clone();
|
||||
async move {
|
||||
let mut filters: Option<Vec<i64>> = None;
|
||||
if let Some (stops_v) = infox.stops.clone() {
|
||||
let mut items = Vec::new();
|
||||
|
||||
let mut filters: Option<Vec<i64>> = None;
|
||||
if let Some (stops_v) = info.stops.clone() {
|
||||
let mut items = Vec::new();
|
||||
|
||||
for sid in stops_v.split(",") {
|
||||
items.push(sid.parse::<i64>().unwrap());
|
||||
for sid in stops_v.split(",") {
|
||||
items.push(sid.parse::<i64>().unwrap());
|
||||
}
|
||||
filters = Some(items);
|
||||
}
|
||||
filters = Some(items);
|
||||
}
|
||||
|
||||
let route_id = path.into_inner();
|
||||
let route_info_r = get_route_info(route_id.clone(), state).await;
|
||||
let load_time_ms = Some(start_time.elapsed().as_millis());
|
||||
let route_id = pathx;
|
||||
let route_info_r = get_route_info(route_id.clone(), statex.clone()).await;
|
||||
|
||||
if let Ok(route_info) = route_info_r {
|
||||
let timetables = crate::templates::build_timetables(route_info.directions, route_info.schedule);
|
||||
if let Ok(route_info) = route_info_r {
|
||||
let timetables = crate::templates::build_timetables(route_info.directions, route_info.schedule);
|
||||
|
||||
HttpResponse::Ok().body(crate::templates::ContentTemplate {
|
||||
page_title: Some(format!("SEPTASTIC | Schedules for {}", route_id.clone())),
|
||||
page_desc: Some(format!("Schedule information for {}", route_id.clone())),
|
||||
content: crate::templates::RouteTemplate {
|
||||
route: route_info.route,
|
||||
timetables,
|
||||
filter_stops: filters.clone()
|
||||
},
|
||||
load_time_ms
|
||||
}.render().unwrap())
|
||||
} else {
|
||||
HttpResponse::InternalServerError().body("Error")
|
||||
}
|
||||
Ok(crate::templates::ContentTemplate {
|
||||
widescreen: false,
|
||||
page_title: Some(format!("SEPTASTIC | Schedules for {}", route_id.clone())),
|
||||
page_desc: Some(format!("Schedule information for {}", route_id.clone())),
|
||||
content: crate::templates::RouteTemplate {
|
||||
route: route_info.route,
|
||||
timetables,
|
||||
filter_stops: filters.clone()
|
||||
},
|
||||
load_time_ms: None
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("test"))
|
||||
}
|
||||
}}).await
|
||||
}
|
||||
|
||||
#[get("/route/{route_id}.json")]
|
||||
|
|
|
|||
|
|
@ -1,29 +1,75 @@
|
|||
use actix_web::{get, web::Data, App, HttpResponse, HttpServer, Responder};
|
||||
use actix_web::{cookie::Cookie, get, web::Data, App, HttpRequest, HttpResponse, HttpServer, Responder};
|
||||
use env_logger::Env;
|
||||
use log::*;
|
||||
use dotenv::dotenv;
|
||||
use serde::Deserialize;
|
||||
use services::trip_tracking::{self};
|
||||
use std::sync::Arc;
|
||||
use templates::ContentTemplate;
|
||||
use std::{sync::Arc, time::Instant};
|
||||
use askama::Template;
|
||||
|
||||
mod database;
|
||||
mod services;
|
||||
mod controllers;
|
||||
mod templates;
|
||||
mod middleware;
|
||||
|
||||
pub struct AppState {
|
||||
database: ::sqlx::postgres::PgPool,
|
||||
trip_tracking_service: services::trip_tracking::TripTrackingService
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct LocalStateQuery {
|
||||
pub widescreen: Option<bool>
|
||||
}
|
||||
|
||||
pub async fn perform_action<F, Fut, T >(req: HttpRequest, func: F) -> impl Responder
|
||||
where T: Template,
|
||||
F: Fn() -> Fut,
|
||||
Fut: Future<Output = anyhow::Result<ContentTemplate<T>>> + 'static {
|
||||
|
||||
let start_time = Instant::now();
|
||||
let mut enable_widescreen = false;
|
||||
if let Some(widescreen_set) = req.cookie("widescreen") {
|
||||
enable_widescreen = widescreen_set.value() == "true";
|
||||
}
|
||||
|
||||
let query_params = actix_web::web::Query::<LocalStateQuery>::from_query(req.query_string()).unwrap();
|
||||
|
||||
if let Some(set_widescreen) = query_params.widescreen {
|
||||
enable_widescreen = set_widescreen;
|
||||
}
|
||||
|
||||
let x = func().await;
|
||||
|
||||
match x {
|
||||
Ok(mut y) => {
|
||||
y.widescreen = enable_widescreen;
|
||||
y.load_time_ms = Some(start_time.elapsed().as_nanos());
|
||||
let mut cookie = Cookie::new("widescreen", y.widescreen.to_string());
|
||||
cookie.set_path("/");
|
||||
HttpResponse::Ok()
|
||||
.cookie(cookie)
|
||||
.body(y.render().unwrap())
|
||||
},
|
||||
Err(_) => {
|
||||
HttpResponse::InternalServerError().body("Error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn get_index() -> impl Responder {
|
||||
HttpResponse::Ok().body(templates::ContentTemplate {
|
||||
async fn get_index(req: HttpRequest) -> impl Responder {
|
||||
perform_action(req, move || async {
|
||||
Ok(templates::ContentTemplate {
|
||||
page_title: None,
|
||||
page_desc: None,
|
||||
content: templates::IndexTemplate {},
|
||||
load_time_ms: None
|
||||
}.render().unwrap())
|
||||
load_time_ms: None,
|
||||
widescreen: false
|
||||
})
|
||||
}).await
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
|
|
@ -53,6 +99,7 @@ async fn main() -> ::anyhow::Result<()> {
|
|||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(actix_cors::Cors::permissive())
|
||||
.wrap(actix_web::middleware::from_fn(middleware::local_state::local_state))
|
||||
.app_data(Data::new(state.clone()))
|
||||
.service(controllers::route::api_get_route)
|
||||
.service(controllers::route::api_get_schedule)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ pub struct ContentTemplate<T: askama::Template> {
|
|||
pub content: T,
|
||||
pub page_title: Option<String>,
|
||||
pub page_desc: Option<String>,
|
||||
pub load_time_ms: Option<u128>
|
||||
pub load_time_ms: Option<u128>,
|
||||
pub widescreen: bool
|
||||
}
|
||||
|
||||
#[derive(askama::Template)]
|
||||
|
|
@ -127,6 +128,20 @@ pub fn build_timetables(
|
|||
}
|
||||
|
||||
mod filters {
|
||||
pub fn format_load_time(
|
||||
nanos: &u128,
|
||||
_: &dyn askama::Values,
|
||||
) -> askama::Result<String> {
|
||||
if *nanos >= 1000000000 {
|
||||
return Ok(format!("{}s", (nanos/1000000000)));
|
||||
} else if *nanos >= 1000000 {
|
||||
return Ok(format!("{}ms", nanos/1000000));
|
||||
} if *nanos >= 1000 {
|
||||
return Ok(format!("{}us", nanos/1000));
|
||||
} else {
|
||||
return Ok(format!("{}ns", nanos));
|
||||
}
|
||||
}
|
||||
pub fn format_time(
|
||||
seconds_since_midnight: &i64,
|
||||
_: &dyn askama::Values,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@
|
|||
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<noscript>
|
||||
<style>
|
||||
.js-only {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
<style>
|
||||
.silverliner-svg {
|
||||
display: block;
|
||||
|
|
@ -26,7 +33,11 @@
|
|||
}
|
||||
</style>
|
||||
<body>
|
||||
{% if widescreen %}
|
||||
<div class="body">
|
||||
{% else %}
|
||||
<div class="body body-small">
|
||||
{% endif %}
|
||||
<div style="background-color: #ff0000; color: #ffffff; font-size: .7em; padding: 5px; margin-bottom: 10px; margin-top: 10px;">
|
||||
This website is not run by SEPTA. Data may be inaccurate.
|
||||
</div>
|
||||
|
|
@ -54,10 +65,18 @@
|
|||
<small>Copyright © <a href="https://nickorlow.com">Nicholas Orlowsky</a> 2025</small>
|
||||
</p>
|
||||
{% if let Some(load_time) = load_time_ms %}
|
||||
<p style="marin-top: 5px; color: #555555;"><small><i>Data loaded in {{ load_time }}ms</i><small></p>
|
||||
<p style="marin-top: 5px; color: #555555;"><small><i>Data loaded in {{ *load_time | format_load_time }}</i></small></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{% if widescreen %}
|
||||
<a href="?widescreen=false"><small>[ disable widescreen ]</small></a>
|
||||
{% else %}
|
||||
<a href="?widescreen=true"><small>[ enable widescreen ]</small></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<noscript><p style="margin-top: 10px;"><small>[!] You do not have JavaScript enabled. Some features will be missing.</small></p></noscript>
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue