diff --git a/Cargo.lock b/Cargo.lock index 350c091..0679578 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,12 +68,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.2" @@ -113,15 +107,6 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - [[package]] name = "bumpalo" version = "3.13.0" @@ -273,15 +258,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.3.2" @@ -340,16 +316,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - [[package]] name = "cyberpunk-splash" version = "0.1.0" @@ -364,13 +330,15 @@ dependencies = [ name = "dashboard" version = "0.1.0" dependencies = [ + "cairo-rs", "chrono", "fluent", "fluent-ergonomics", "futures", "geo-types", - "horrorshow", - "http", + "gio", + "glib", + "gtk4", "ifc", "lazy_static", "memorycache", @@ -380,17 +348,6 @@ dependencies = [ "serde_json", "tokio", "unic-langid", - "warp", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", ] [[package]] @@ -399,7 +356,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2517b0555262aeeda0d107a40ecfbbcf185921180ffb4acf316ebe0887467e26" dependencies = [ - "generic-array 0.11.2", + "generic-array", "num-traits", "serde", "typenum", @@ -786,16 +743,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "geo-types" version = "0.1.0" @@ -1078,31 +1025,6 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.4.1" @@ -1128,12 +1050,6 @@ dependencies = [ "image", ] -[[package]] -name = "horrorshow" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371fb981840150b1a54f7cb117bf6699f7466a1d4861daac33bc6fe2b5abea0" - [[package]] name = "http" version = "0.2.9" @@ -1413,16 +1329,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1450,24 +1356,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "multer" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http", - "httparse", - "log", - "memchr", - "mime", - "spin", - "version_check", -] - [[package]] name = "nanorand" version = "0.7.0" @@ -1962,7 +1850,7 @@ version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.2", + "base64", "bytes", "encoding_rs", "futures-core", @@ -2027,15 +1915,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.2", -] - [[package]] name = "rusty-fork" version = "0.3.0" @@ -2063,12 +1942,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -2180,17 +2053,6 @@ dependencies = [ "typeshare", ] -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2407,29 +2269,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - [[package]] name = "tokio-util" version = "0.7.8" @@ -2491,7 +2330,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", - "log", "pin-project-lite", "tracing-core", ] @@ -2511,25 +2349,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror", - "url", - "utf-8", -] - [[package]] name = "type-map" version = "0.4.0" @@ -2591,15 +2410,6 @@ dependencies = [ "tinystr", ] -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-bidi" version = "0.3.13" @@ -2632,12 +2442,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "uuid" version = "0.8.2" @@ -2684,37 +2488,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "warp" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "headers", - "http", - "hyper", - "log", - "mime", - "mime_guess", - "multer", - "percent-encoding", - "pin-project", - "rustls-pemfile", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tokio-util", - "tower-service", - "tracing", -] - [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" diff --git a/dashboard/Cargo.toml b/dashboard/Cargo.toml index 668e3a8..56d2f49 100644 --- a/dashboard/Cargo.toml +++ b/dashboard/Cargo.toml @@ -6,21 +6,21 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = { version = "0.4", features = ["serde"] } -fluent = { version = "0.16" } -fluent-ergonomics = { path = "../fluent-ergonomics/" } -futures = { version = "0.3" } -geo-types = { path = "../geo-types/" } -lazy_static = { version = "1.4" } -unic-langid = { version = "0.9" } -horrorshow = { version = "0.8" } -http = { version = "*" } -ifc = { path = "../ifc/" } -memorycache = { path = "../memorycache" } -reqwest = { version = "0.11", features = ["json"] } -serde_derive = { version = "1" } -serde_json = { version = "1" } -serde = { version = "1" } -tokio = { version = "1", features = ["full"] } -warp = { version = "0.3" } - +cairo-rs = { version = "0.17" } +chrono = { version = "0.4", features = ["serde"] } +fluent-ergonomics = { path = "../fluent-ergonomics/" } +fluent = { version = "0.16" } +futures = { version = "0.3" } +geo-types = { path = "../geo-types/" } +gio = { version = "0.17" } +glib = { version = "0.17" } +gtk = { version = "0.6", package = "gtk4" } +ifc = { path = "../ifc/" } +lazy_static = { version = "1.4" } +memorycache = { path = "../memorycache/" } +reqwest = { version = "0.11", features = ["json"] } +serde_derive = { version = "1" } +serde_json = { version = "1" } +serde = { version = "1" } +tokio = { version = "1", features = ["full"] } +unic-langid = { version = "0.9" } diff --git a/dashboard/src/app_window.rs b/dashboard/src/app_window.rs new file mode 100644 index 0000000..66c5dc1 --- /dev/null +++ b/dashboard/src/app_window.rs @@ -0,0 +1,58 @@ +use crate::{ + components::{Date, TransitClock}, + types::State, +}; +use gtk::prelude::*; + +#[derive(Clone)] +pub struct ApplicationWindow { + pub window: gtk::ApplicationWindow, + pub date_label: Date, + pub next_event: gtk::Label, + pub transit_clock: TransitClock, +} + +impl ApplicationWindow { + pub fn new(app: >k::Application) -> Self { + let window = gtk::ApplicationWindow::new(app); + + let layout = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .hexpand(true) + .vexpand(true) + .build(); + + let date_label = Date::new(); + layout.append(&date_label); + + let next_event = gtk::Label::builder() + .margin_bottom(8) + .margin_top(8) + .margin_start(8) + .margin_end(8) + .build(); + next_event.add_css_class("card"); + next_event.add_css_class("activatable"); + layout.append(&next_event); + + let transit_clock = TransitClock::new(); + layout.append(&transit_clock); + + window.set_child(Some(&layout)); + + Self { + window, + date_label, + next_event, + transit_clock, + } + } + + pub fn update_state(&self, state: State) { + self.date_label.update_date(state.date); + self.next_event.set_text(&format!("{:?}", state.next_event)); + if let Some(transit) = state.transit { + self.transit_clock.update_transit(transit); + } + } +} diff --git a/dashboard/src/components/date.rs b/dashboard/src/components/date.rs new file mode 100644 index 0000000..3df0e39 --- /dev/null +++ b/dashboard/src/components/date.rs @@ -0,0 +1,69 @@ +use chrono::Datelike; +use glib::Object; +use gtk::{prelude::*, subclass::prelude::*}; +use ifc::IFC; +use std::{cell::RefCell, rc::Rc}; + +pub struct DatePrivate { + date: Rc>, + label: Rc>, +} + +impl Default for DatePrivate { + fn default() -> Self { + Self { + date: Rc::new(RefCell::new(IFC::from( + chrono::Local::now().date_naive().with_year(12023).unwrap(), + ))), + label: Rc::new(RefCell::new(gtk::Label::new(None))), + } + } +} + +#[glib::object_subclass] +impl ObjectSubclass for DatePrivate { + const NAME: &'static str = "Date"; + type Type = Date; + type ParentType = gtk::Box; +} + +impl ObjectImpl for DatePrivate {} +impl WidgetImpl for DatePrivate {} +impl BoxImpl for DatePrivate {} + +glib::wrapper! { + pub struct Date(ObjectSubclass) @extends gtk::Box, gtk::Widget; +} + +impl Date { + pub fn new() -> Self { + let s: Self = Object::builder().build(); + s.add_css_class("card"); + s.add_css_class("activatable"); + s.set_margin_bottom(8); + s.set_margin_top(8); + s.set_margin_start(8); + s.set_margin_end(8); + + s.append(&*s.imp().label.borrow()); + + s.redraw(); + s + } + + pub fn update_date(&self, date: IFC) { + *self.imp().date.borrow_mut() = date; + self.redraw(); + } + + fn redraw(&self) { + let date = self.imp().date.borrow().clone(); + self.imp().label.borrow_mut().set_text(&format!( + "{:?}, {:?} {}, {}", + date.weekday(), + date.month(), + date.day(), + date.year() + )); + } +} diff --git a/dashboard/src/components/mod.rs b/dashboard/src/components/mod.rs new file mode 100644 index 0000000..a011020 --- /dev/null +++ b/dashboard/src/components/mod.rs @@ -0,0 +1,5 @@ +mod date; +pub use date::Date; + +mod transit_clock; +pub use transit_clock::TransitClock; diff --git a/dashboard/src/components/transit_clock.rs b/dashboard/src/components/transit_clock.rs new file mode 100644 index 0000000..24a0d5b --- /dev/null +++ b/dashboard/src/components/transit_clock.rs @@ -0,0 +1,92 @@ +use crate::{ + drawing::{Color, PieChart, Wedge}, + soluna_client::SunMoon, +}; +use chrono::{Duration, NaiveTime}; +use glib::Object; +use gtk::{prelude::*, subclass::prelude::*}; +use std::{cell::RefCell, f64::consts::PI, rc::Rc}; + +pub struct TransitClockPrivate { + info: Rc>>, +} + +impl Default for TransitClockPrivate { + fn default() -> Self { + Self { + info: Rc::new(RefCell::new(None)), + } + } +} + +#[glib::object_subclass] +impl ObjectSubclass for TransitClockPrivate { + const NAME: &'static str = "TransitClock"; + type Type = TransitClock; + type ParentType = gtk::DrawingArea; +} + +impl ObjectImpl for TransitClockPrivate {} +impl WidgetImpl for TransitClockPrivate {} +impl DrawingAreaImpl for TransitClockPrivate {} + +glib::wrapper! { + pub struct TransitClock(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; +} + +impl TransitClock { + pub fn new() -> Self { + let s: Self = Object::builder().build(); + s.set_width_request(500); + s.set_height_request(500); + + s.set_draw_func({ + let s = s.clone(); + move |_, context, width, height| { + let center_x = width as f64 / 2.; + let center_y = height as f64 / 2.; + let radius = width.min(height) as f64 / 2. * 0.9; + if let Some(ref info) = *s.imp().info.borrow() { + let full_day = Duration::days(1).num_seconds() as f64; + let sunrise = info.sunrise - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let sunset = info.sunset - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + + PieChart::new() + .center(center_x, center_y) + .radius(radius) + .rotation(-PI / 2.) + .wedges( + vec![Wedge { + start_angle: (PI * 2.) * sunset.num_seconds() as f64 / full_day, + end_angle: (PI * 2.) * sunrise.num_seconds() as f64 / full_day, + color: Color { + r: 0.1, + g: 0.1, + b: 0.8, + }, + }] + .into_iter(), + ) + .draw(context); + + (0..24).for_each(|tick| { + context.set_source_rgb(0., 0., 0.); + context.translate(center_x, center_y); + context.rotate(tick as f64 * (PI / 12.)); + context.move_to(radius - 5., 0.); + context.line_to(radius - 10., 0.); + let _ = context.stroke(); + context.identity_matrix(); + }); + } + } + }); + + s + } + + pub fn update_transit(&self, transit_info: SunMoon) { + *self.imp().info.borrow_mut() = Some(transit_info); + self.queue_draw(); + } +} diff --git a/dashboard/src/drawing/mod.rs b/dashboard/src/drawing/mod.rs new file mode 100644 index 0000000..1fa5546 --- /dev/null +++ b/dashboard/src/drawing/mod.rs @@ -0,0 +1,2 @@ +mod pie_chart; +pub use pie_chart::{Color, PieChart, Wedge}; diff --git a/dashboard/src/drawing/pie_chart.rs b/dashboard/src/drawing/pie_chart.rs new file mode 100644 index 0000000..918deb5 --- /dev/null +++ b/dashboard/src/drawing/pie_chart.rs @@ -0,0 +1,88 @@ +use cairo::Context; +use std::f64::consts::PI; + +#[derive(Clone, Debug)] +pub struct Color { + pub r: f64, + pub g: f64, + pub b: f64, +} + +#[derive(Clone, Debug)] +pub struct Wedge { + pub start_angle: f64, + pub end_angle: f64, + pub color: Color, +} + +pub struct PieChart { + rotation: f64, + wedges: Vec, + center_x: f64, + center_y: f64, + radius: f64, +} + +impl PieChart { + pub fn new() -> Self { + Self { + rotation: 0., + wedges: vec![], + center_x: 0., + center_y: 0., + radius: 0., + } + } + + pub fn rotation(mut self, rotation: f64) -> Self { + self.rotation = rotation; + self + } + + pub fn wedges(mut self, wedge: impl Iterator) -> Self { + let mut wedges: Vec = wedge.collect(); + self.wedges.append(&mut wedges); + self + } + + pub fn center(mut self, center_x: f64, center_y: f64) -> Self { + self.center_x = center_x; + self.center_y = center_y; + self + } + + pub fn radius(mut self, radius: f64) -> Self { + self.radius = radius; + self + } + + pub fn draw(self, context: &Context) { + context.set_source_rgba(0., 0., 0., 0.); + let _ = context.paint(); + + context.set_line_width(2.); + + self.wedges.iter().for_each( + |Wedge { + start_angle, + end_angle, + color, + }| { + context.move_to(self.center_x, self.center_y); + context.set_source_rgb(color.r, color.g, color.b); + context.arc( + self.center_x, + self.center_y, + self.radius, + start_angle + self.rotation, + end_angle + self.rotation, + ); + let _ = context.fill(); + }, + ); + + context.set_source_rgb(1., 1., 1.); + context.arc(self.center_x, self.center_y, self.radius, 0., 2. * PI); + let _ = context.stroke(); + } +} diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 1d0b604..3eaa100 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -1,28 +1,28 @@ -#[macro_use] -extern crate serde_derive; -#[macro_use] -extern crate horrorshow; -#[macro_use] -extern crate lazy_static; - -use chrono::{Datelike, Local, TimeZone, Utc}; -use fluent::{FluentArgs, FluentValue}; +use chrono::{Datelike, Local, Utc}; use geo_types::{Latitude, Longitude}; -use horrorshow::helper::doctype; -use horrorshow::prelude::*; -use ifc; +use glib::Sender; +use gtk::prelude::*; +use ifc::IFC; use std::{ - borrow::Cow, - net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::Arc, + env, + sync::{Arc, RwLock}, }; -use warp::{reply::Html, Filter, Rejection}; + +mod app_window; +use app_window::ApplicationWindow; + +mod components; + +mod drawing; mod soluna_client; -use soluna_client::{SolunaClient, SunMoon}; +use soluna_client::SolunaClient; mod solstices; -use solstices::{Event, EVENTS}; +use solstices::EVENTS; + +mod types; +use types::State; /* const EO_TEXT: &'static str = " @@ -57,154 +57,86 @@ summer_solstice = Somera Solstico autumn_equinox = Aŭtuna Ekvinokso winter_solstice = Vintra Solstico "; - -fn date(fluent: Arc, today: ifc::IFC) -> impl Render { - let mut day_args = FluentArgs::new(); - day_args.set( - "day", - FluentValue::String(Cow::Owned(String::from(today.weekday_ifc()))), - ); - let tago = fluent.tr("tago", Some(&day_args)).unwrap(); - - let mut month_args = FluentArgs::new(); - month_args.set( - "month", - FluentValue::String(Cow::Owned(String::from(today.month_ifc()))), - ); - let monato = fluent.tr("month", Some(&month_args)).unwrap(); - - owned_html! {p : format!("{}, {} {}, {}", tago, monato, today.day(), today.year());} -} */ -/* -fn soluna_desegno(sun_moon: SunMoon) -> impl Render { - owned_html! { - table { - tr { - th : "Sunleviĝo"; - th : "Sunfalo"; - th : "Lunleviĝo"; - th : "Lunfalo"; - } - tr { - td : format!("{}", sun_moon.sunrise.format("%H:%M")); - td : format!("{}", sun_moon.sunset.format("%H:%M")); - td : sun_moon.moonrise.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string()); - td : sun_moon.moonset.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string()); - td : format!("{:?}", sun_moon.moon_phase); - } - } - } -} -*/ - -/* -fn astronomia_eventa_desegno( - fluent: Arc, - event: Option, -) -> impl Render { - let s = match event { - None => "".to_owned(), - Some(event) => { - let eventa_str = match event { - Event::SpringEquinox(_) => fluent.tr("spring_equinox", None), - Event::SummerSolstice(_) => fluent.tr("summer_solstice", None), - Event::AutumnEquinox(_) => fluent.tr("autumn_equinox", None), - Event::WinterSolstice(_) => fluent.tr("winter_solstice", None), - } - .unwrap(); - format!("{} {}", eventa_str, event.date().format("%Y-%m-%d")) - } - }; - owned_html! { - div : s.clone(); - } -} -*/ - -/* -fn page_template( - fluent: Arc, - today: ifc::IFC, - sun_moon: SunMoon, - event: Option, -) -> String { - format!( - "{}", - html! { - : doctype::HTML; - html { - head { - title: "Superrigardo"; - } - body { - h1(id="heading", class="title") : date(fluent.clone(), today); - div : soluna_desegno(sun_moon.clone()); - div : astronomia_eventa_desegno(fluent.clone(), event); - // div : cirklo(); - } - } - } - ) +#[derive(Clone, Debug)] +pub enum Message { + Refresh(State), } -async fn page( - fluent: Arc, - solar_client: Arc, - latitude: Latitude, - longitude: Longitude, -) -> Result, Rejection> { - let now = Utc::now(); - let d = ifc::IFC::from(now.with_timezone(&New_York).date()); - let sun_moon = solar_client - .request(latitude, longitude, now.with_timezone(&New_York).date()) - .await; - let next_event = EVENTS.next_event(now); - Ok(warp::reply::html(page_template( - fluent, d, sun_moon, next_event, - ))) +#[derive(Clone)] +pub struct Core { + tx: Arc>>>, } -*/ -#[tokio::main] -pub async fn main() { - let now = Local::now(); - let ifc = ifc::IFC::from(now.date_naive().with_year(12023).unwrap()); - let next_event = EVENTS.next_event(now.with_timezone(&Utc)).unwrap(); - - println!( - "{:?}, {:?} {}, {}", - ifc.weekday(), - ifc.month(), - ifc.day(), - ifc.year() - ); - - println!("{:?}", next_event); +pub fn main() { + let app = gtk::Application::builder() + .application_id("com.luminescent-dreams.dashboard") + .resource_base_path("/com/luminescent-dreams/dashboard") + .build(); let latitude = Latitude::from(41.78); let longitude = Longitude::from(-71.41); - let soluna_client = SolunaClient::new(); - let sun_moon = soluna_client - .request(latitude, longitude, Local::now()) - .await; - println!( - "Solar Transit: {} -> {}", - sun_moon.sunrise.format("%H:%M").to_string(), - sun_moon.sunset.format("%H:%M").to_string() - ); - println!( - "Lunar Transit: {} -> {} [{:?}]", - sun_moon - .moonrise - .map(|time| time.format("%H:%M").to_string()) - .unwrap_or(" -- ".to_owned()), - sun_moon - .moonset - .map(|time| time.format("%H:%M").to_string()) - .unwrap_or(" -- ".to_owned()), - sun_moon.moon_phase + let runtime = Arc::new( + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(), ); + + let core = Core { + tx: Arc::new(RwLock::new(None)), + }; + + let app_handle = runtime.spawn({ + let core = core.clone(); + async move { + let soluna_client = SolunaClient::new(); + + let transit = soluna_client + .request(latitude, longitude, Local::now()) + .await; + + let now = Local::now(); + let state = State { + date: IFC::from(now.date_naive().with_year(12023).unwrap()), + next_event: EVENTS.next_event(now.with_timezone(&Utc)).unwrap(), + transit: Some(transit), + }; + loop { + if let Some(ref gtk_tx) = *core.tx.read().unwrap() { + let _ = gtk_tx.send(Message::Refresh(state.clone())); + } + std::thread::sleep(std::time::Duration::from_secs(300)); + } + } + }); + + app.connect_activate(move |app| { + let (gtk_tx, gtk_rx) = + gtk::glib::MainContext::channel::(gtk::glib::PRIORITY_DEFAULT); + + *core.tx.write().unwrap() = Some(gtk_tx); + + let window = ApplicationWindow::new(app); + window.window.present(); + + gtk_rx.attach(None, { + let window = window.clone(); + move |msg| { + let Message::Refresh(state) = msg; + ApplicationWindow::update_state(&window, state); + + Continue(true) + } + }); + + std::thread::spawn(move || {}); + }); + + let args: Vec = env::args().collect(); + ApplicationExtManual::run_with_args(&app, &args); + + let _ = runtime.block_on(async { app_handle.await }); } diff --git a/dashboard/src/solstices.rs b/dashboard/src/solstices.rs index 048ece7..6a0943b 100644 --- a/dashboard/src/solstices.rs +++ b/dashboard/src/solstices.rs @@ -1,7 +1,7 @@ use chrono; use chrono::prelude::*; +use lazy_static::lazy_static; use serde_derive::{Deserialize, Serialize}; -use serde_json; use std::collections::HashMap; // http://astropixels.com/ephemeris/soleq2001.html @@ -133,9 +133,11 @@ fn parse_events() -> Vec> { pub struct Solstices(HashMap); impl Solstices { - pub fn akiru(&self, year: i32) -> Option { + /* + pub fn acquire(&self, year: i32) -> Option { self.0.get(&year).map(|c| c.clone()) } + */ pub fn next_event(&self, date: chrono::DateTime) -> Option { let year_events = self.0.get(&date.year()); @@ -152,7 +154,7 @@ impl Solstices { } else { self.0 .get(&(date.year() + 1)) - .map(|events| Event::SpringEquinox(year_events.spring_equinox.clone())) + .map(|_| Event::SpringEquinox(year_events.spring_equinox.clone())) } } None => None, diff --git a/dashboard/src/soluna_client.rs b/dashboard/src/soluna_client.rs index 60e3e69..f705043 100644 --- a/dashboard/src/soluna_client.rs +++ b/dashboard/src/soluna_client.rs @@ -1,12 +1,11 @@ // 41.78, -71.41 // https://api.solunar.org/solunar/41.78,-71.41,20211029,-4 -use chrono::{ - DateTime, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike, Utc, -}; +use chrono::{DateTime, Duration, NaiveTime, Offset, TimeZone, Utc}; use geo_types::{Latitude, Longitude}; use memorycache::MemoryCache; use reqwest; +use serde::Deserialize; const ENDPOINT: &str = "https://api.solunar.org/solunar"; diff --git a/dashboard/src/types.rs b/dashboard/src/types.rs new file mode 100644 index 0000000..045d448 --- /dev/null +++ b/dashboard/src/types.rs @@ -0,0 +1,9 @@ +use crate::{solstices::Event, soluna_client::SunMoon}; +use ifc::IFC; + +#[derive(Clone, Debug)] +pub struct State { + pub date: IFC, + pub next_event: Event, + pub transit: Option, +} diff --git a/flake.lock b/flake.lock index 0df1f2e..ca25ad3 100644 --- a/flake.lock +++ b/flake.lock @@ -51,16 +51,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1688392541, - "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", + "lastModified": 1691421349, + "narHash": "sha256-RRJyX0CUrs4uW4gMhd/X4rcDG8PTgaaCQM5rXEJOx6g=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", + "rev": "011567f35433879aae5024fc6ec53f2a0568a6c4", "type": "github" }, "original": { "id": "nixpkgs", - "ref": "nixos-22.11", + "ref": "nixos-23.05", "type": "indirect" } }, diff --git a/flake.nix b/flake.nix index 39da673..9fdfd6e 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Lumenescent Dreams Tools"; inputs = { - nixpkgs.url = "nixpkgs/nixos-22.11"; + nixpkgs.url = "nixpkgs/nixos-23.05"; unstable.url = "nixpkgs/nixos-unstable"; pkgs-cargo2nix.url = "github:cargo2nix/cargo2nix"; typeshare.url = "github:1Password/typeshare";