diff --git a/Cargo.lock b/Cargo.lock index ec8eb48..29eda49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -552,6 +552,7 @@ name = "hemma_web" version = "0.1.0" dependencies = [ "anyhow", + "chrono", "common", "css_typegen", "lighter_lib", diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 6ac585b..3596bd4 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -14,6 +14,7 @@ serde = { version = "1", features = ['derive'] } serde_json = "1" anyhow = "*" ron = "0.7.1" +chrono = { version = "0.4.20", features = ["serde"] } [dependencies.css_typegen] git = "https://github.com/hulthe/css_typegen.git" diff --git a/frontend/src/page/lights.rs b/frontend/src/page/lights.rs index e30e230..93b65ac 100644 --- a/frontend/src/page/lights.rs +++ b/frontend/src/page/lights.rs @@ -1,9 +1,10 @@ use crate::components::color_picker::{ColorPicker, ColorPickerMsg}; use crate::css::C; +use chrono::{NaiveTime, Weekday}; use common::{BulbGroup, BulbGroupShape, BulbMap, ClientMessage, ServerMessage}; use lighter_lib::{BulbId, BulbMode}; use seed::prelude::*; -use seed::{attrs, button, div, C}; +use seed::{attrs, button, div, input, C}; use seed_router::Page; use std::collections::{BTreeMap, HashSet}; use std::fmt::Write; @@ -32,6 +33,7 @@ pub enum Msg { DeselectGroups, ColorPicker(ColorPickerMsg), SetBulbPower(bool), + LightTime(String, Weekday), } impl Page for Model { @@ -47,7 +49,7 @@ impl Page for Model { fn update(&mut self, msg: Self::Msg, orders: &mut impl Orders) { match msg { Msg::ServerMessage(msg) => match msg { - ServerMessage::BulbMode { id, mode: new_mode } => { + ServerMessage::BulbState { id, mode: new_mode, wake_schedule } => { *self.bulb_states.entry(id).or_default() = new_mode //color_picker.set_color(mode.color); } @@ -108,124 +110,159 @@ impl Page for Model { orders.notify(message); }); } + Msg::LightTime(time, day) => { + if let Ok(time) = NaiveTime::parse_from_str(&time, "%H:%M") { + self.for_selected_bulbs(|id, _| { + let message = ClientMessage::SetBulbWakeTime { + id: id.clone(), + day, + time, + }; + orders.notify(message); + }); + } + } } } - fn view(&self) -> Node { - //let view_bulb = |(id, (mode, color_picker)): (&BulbId, &(BulbMode, ColorPicker))| { - // div![ - // C![C.bulb_box], - // h1![id], - // div![ - // C![C.bulb_controls], - // { - // let id = id.clone(); - // color_picker.view().map_msg(|msg| Msg::ColorPicker(id, msg)) - // }, - // button![ - // if mode.power { - // C![C.bulb_power_button, C.bulb_power_button_on] - // } else { - // C![C.bulb_power_button] - // }, - // { - // let id = id.clone(); - // let power = !mode.power; - // ev(Ev::Click, move |_| Msg::SetBulbPower(id, power)) - // }, - // ], - // ], - // ] - //}; - let bulb_map_width = self - .bulb_map - .groups - .iter() - .map(|group| group.x + group.shape.width()) - .max() - .unwrap_or(0); +fn view(&self) -> Node { + //let view_bulb = |(id, (mode, color_picker)): (&BulbId, &(BulbMode, ColorPicker))| { + // div![ + // C![C.bulb_box], + // h1![id], + // div![ + // C![C.bulb_controls], + // { + // let id = id.clone(); + // color_picker.view().map_msg(|msg| Msg::ColorPicker(id, msg)) + // }, + // button![ + // if mode.power { + // C![C.bulb_power_button, C.bulb_power_button_on] + // } else { + // C![C.bulb_power_button] + // }, + // { + // let id = id.clone(); + // let power = !mode.power; + // ev(Ev::Click, move |_| Msg::SetBulbPower(id, power)) + // }, + // ], + // ], + // ] + //}; - let bulb_map_height = self - .bulb_map - .groups - .iter() - .map(|group| group.y + group.shape.height()) - .max() - .unwrap_or(0); + let bulb_map_width = self + .bulb_map + .groups + .iter() + .map(|group| group.x + group.shape.width()) + .max() + .unwrap_or(0); - let view_bulb_group = |(i, group): (usize, &BulbGroup)| { - let (w, h) = (group.shape.width(), group.shape.height()); - let mut style = String::new(); - write!( - &mut style, - "margin-left: {}rem; margin-top: {}rem; width: {}rem; height: {}rem;", - group.x, group.y, w, h - ) - .ok(); + let bulb_map_height = self + .bulb_map + .groups + .iter() + .map(|group| group.y + group.shape.height()) + .max() + .unwrap_or(0); - if let BulbGroupShape::Circle { r } = group.shape { - write!(&mut style, " border-radius: {r}rem;").ok(); - } + let view_bulb_group = |(i, group): (usize, &BulbGroup)| { + let (w, h) = (group.shape.width(), group.shape.height()); + let mut style = String::new(); + write!( + &mut style, + "margin-left: {}rem; margin-top: {}rem; width: {}rem; height: {}rem;", + group.x, group.y, w, h + ) + .ok(); - div![ - &group.name[..1], - if self.selected_groups.contains(&i) { - C![C.bulb_group, C.bulb_group_selected] - } else { - C![C.bulb_group] - }, - attrs! { - At::Style => style, - }, - ev(Ev::Click, move |event| { - event.stop_propagation(); - Msg::SelectGroup(i) - }), - ] - }; - - let (_color, power) = self - .selected_groups - .iter() - .next() - .and_then(|&index| self.bulb_map.groups.get(index)) - .and_then(|group| group.bulbs.first()) - .and_then(|id| self.bulb_states.get(id)) - .map(|bulb| (bulb.color, bulb.power)) - .unwrap_or_default(); + if let BulbGroupShape::Circle { r } = group.shape { + write!(&mut style, " border-radius: {r}rem;").ok(); + } div![ - C![C.bulb_box], - div![ - C![C.bulb_map], - attrs! { - At::Style => format!("width: {}rem; height: {}rem;", bulb_map_width, bulb_map_height), - }, - ev(Ev::Click, |_| Msg::DeselectGroups), - self.bulb_map.groups.iter().enumerate().map(view_bulb_group), - ], - div![ - C![C.bulb_controls], - self.color_picker - .view() - .map_msg(|msg| Msg::ColorPicker(msg)), - button![ - if power { - C![C.bulb_power_button, C.bulb_power_button_on] - } else { - C![C.bulb_power_button] - }, - ev(Ev::Click, move |_| Msg::SetBulbPower(!power)), - div![attrs! { At::Id => "switch_socket" }], - div![attrs! { At::Id => "off_label" }, "Off"], - div![attrs! { At::Id => "on_label" }, "On"], - div![attrs! { At::Id => "lever_stem" }], - div![attrs! { At::Id => "lever_face" }], - ], + &group.name[..1], + if self.selected_groups.contains(&i) { + C![C.bulb_group, C.bulb_group_selected] + } else { + C![C.bulb_group] + }, + attrs! { + At::Style => style, + }, + ev(Ev::Click, move |event| { + event.stop_propagation(); + Msg::SelectGroup(i) + }), + ] + }; + + let calendar_day = |day: Weekday| { + div![ + C![C.calendar_day], + day.to_string(), + input![ + C![C.calendar_time_input], + attrs! {At::Placeholder => "7:30"}, + input_ev(Ev::Input, move |input| Msg::LightTime(input, day)) ], ] - } + }; + + let (_color, power) = self + .selected_groups + .iter() + .next() + .and_then(|&index| self.bulb_map.groups.get(index)) + .and_then(|group| group.bulbs.first()) + .and_then(|id| self.bulb_states.get(id)) + .map(|bulb| (bulb.color, bulb.power)) + .unwrap_or_default(); + + div![ + C![C.bulb_box], + div![ + C![C.bulb_map], + attrs! { + At::Style => format!("width: {}rem; height: {}rem;", bulb_map_width, bulb_map_height), + }, + ev(Ev::Click, |_| Msg::DeselectGroups), + self.bulb_map.groups.iter().enumerate().map(view_bulb_group), + ], + div![ + C![C.bulb_controls], + self.color_picker + .view() + .map_msg(|msg| Msg::ColorPicker(msg)), + button![ + if power { + C![C.bulb_power_button, C.bulb_power_button_on] + } else { + C![C.bulb_power_button] + }, + ev(Ev::Click, move |_| Msg::SetBulbPower(!power)), + div![attrs! { At::Id => "switch_socket" }], + div![attrs! { At::Id => "off_label" }, "Off"], + div![attrs! { At::Id => "on_label" }, "On"], + div![attrs! { At::Id => "lever_stem" }], + div![attrs! { At::Id => "lever_face" }], + ], + ], + div![ + C![C.calendar_box], + calendar_day(Weekday::Mon), + calendar_day(Weekday::Tue), + calendar_day(Weekday::Wed), + calendar_day(Weekday::Thu), + calendar_day(Weekday::Fri), + calendar_day(Weekday::Sat), + calendar_day(Weekday::Sun), + ], + ] +} } impl Model { diff --git a/frontend/static/styles/common.scss b/frontend/static/styles/common.scss index 34816ad..00c9ae6 100644 --- a/frontend/static/styles/common.scss +++ b/frontend/static/styles/common.scss @@ -259,3 +259,25 @@ body { transition: margin 0.1s ease-out; } +.calendar_day { + display: flex; + flex-direction: row; + justify-content: space-between; + margin-top: .3em; +} + +.calendar_time_input { + background: #453f4b; + border: solid 0.35em #5b3f63; + border-radius: .3em; + color: white; + height: 15; + width: 5em; + align: right; + //margin-bottom: .7em; + margin-left: .5em; +} +.calendar_box { + with: 10em; +} +