diff --git a/src/config.rs b/src/config.rs index 68afdf0..debad15 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,26 +3,18 @@ use serde::Deserialize; #[derive(Deserialize)] pub struct Config { - /// directory, where config is located - #[serde(skip)] - pub conf_path: PathBuf, pub log_path: PathBuf } impl Config { - pub fn new(conf_path: PathBuf) -> Self { - let conf_dir: PathBuf = conf_path.parent().unwrap().into(); - Config { conf_path: conf_dir, log_path: PathBuf::from("./logs") } + pub fn new() -> Self { + Config { log_path: PathBuf::from("./logs") } } pub fn load(path: PathBuf) -> Self { - if let Ok(toml_string) = std::fs::read_to_string(path.clone()) { - let conf = toml::from_str::(&toml_string); - if let Ok(mut conf) = conf { - conf.conf_path = path.parent().unwrap().into(); - return conf; - } + if let Ok(toml_string) = std::fs::read_to_string(path) { + return toml::from_str(&toml_string).unwrap_or(Config::new()); } - Config::new(path) + Config::new() } } diff --git a/src/lib.rs b/src/lib.rs index bdf3eb3..6d93152 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ use config::Config; use std::path::PathBuf; pub mod config; -pub mod log; pub fn load_config() -> Config { if let Ok(path_str) = std::env::var("XDG_CONFIG_HOME") { @@ -11,5 +10,5 @@ pub fn load_config() -> Config { path.push("config.toml"); return Config::load(path); } - Config::new(PathBuf::from("./config.toml")) + Config::new() } diff --git a/src/log.rs b/src/log.rs deleted file mode 100644 index 695f1a4..0000000 --- a/src/log.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::{ - io::{Error, ErrorKind}, - path::PathBuf -}; -use serde::{Deserialize, Serialize}; -use toml::value::{Date, Time}; -use crate::config::Config; - -#[derive(Serialize, Deserialize, Debug)] -pub struct Log { - pub date: Date, - pub events: Vec -} - -impl Log { - pub fn new(date: Date) -> Self { - Log { date, events: Vec::new() } - } - - pub fn load_from(config: &Config, date: Date) -> Self { - let path = Log::get_filepath(&date, config); - if let Ok(log_string) = std::fs::read_to_string(path) { - return toml::from_str(&log_string).unwrap_or(Log::new(date)); - } else { - Log::new(date) - } - } - - pub fn save(&self, config: &Config) -> std::io::Result<()> { - Log::try_create_log_dir(config)?; - let path = Log::get_filepath(&self.date, config); - match toml::to_string_pretty(self) { - Ok(toml_string) => std::fs::write(&path, toml_string), - Err(error) => { - return Err(Error::new(ErrorKind::Other, format!("{error}"))); - } - } - } - - fn get_filepath(date: &Date, config: &Config) -> PathBuf { - let mut path = Log::get_log_dir(&config); - let filename = format!("{}-{}-{}", date.day, date.month, date.year); - path.push(filename); - path.set_extension("toml"); - path - } - - fn get_log_dir(config: &Config) -> PathBuf { - if config.log_path.is_relative() { - let mut path = config.conf_path.clone(); - path.push(&config.log_path); - return path; - } else { - return config.log_path.clone(); - } - } - - fn try_create_log_dir(config: &Config) -> std::io::Result<()> { - let path = Log::get_log_dir(config); - if !std::fs::exists(&path)? { - std::fs::create_dir_all(path)? - } - Ok(()) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct Event { - pub name: String, - pub start: Time, - pub end: Time, - pub finished: bool -} - -impl Event { - pub fn new(name: String, start: i32, end: i32, finished: bool) -> Self { - let start = Time { - hour: (start / 3600) as u8, - minute: ((start / 3600) / 60) as u8, - second: (start % 60) as u8, - nanosecond: 0 - }; - let end = Time { - hour: (end / 3600) as u8, - minute: ((end % 3600) / 60) as u8, - second: (end % 60) as u8, - nanosecond: 0 - }; - Event { name, start, end, finished } - } -} diff --git a/src/main.rs b/src/main.rs index 3e34f4c..2a747ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,149 +1,61 @@ // Prevent console window in addition to Slint window in Windows release builds when, e.g., starting the app via file manager. Ignored on other platforms. #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use std::{error::Error, rc::Rc, sync::{Arc, Mutex}}; +use std::error::Error; -use aliveline::{config::Config, load_config, log::{Event, Log}}; -use chrono::{Datelike, Timelike}; -use slint::{Model, ModelRc, SharedString, ToSharedString, VecModel}; -use toml::value::{Date as TomlDate, Time}; +use aliveline::{config::Config, load_config}; +use chrono::Timelike; +use slint::{Model, SharedString, VecModel}; slint::include_modules!(); -impl From for TimelineEvent { - fn from(event: Event) -> Self { - let start = (event.start.hour as i32) * 3600 + (event.start.minute as i32) * 60 + (event.start.second as i32); - let end = (event.end.hour as i32) * 3600 + (event.end.minute as i32) * 60 + (event.end.second as i32); - TimelineEvent { start, duration: end - start, label: event.name.to_shared_string(), finished: event.finished } - } -} - -impl From for Event { - fn from(event: TimelineEvent) -> Self { - let start = Time { - hour: (event.start / 3600) as u8, - minute: ((event.start % 3600) / 60) as u8, - second: (event.start % 60) as u8, - nanosecond: 0 - }; - let endsecs = event.start + event.duration; - let end = Time { - hour: (endsecs / 3600) as u8, - minute: ((endsecs % 3600) / 60) as u8, - second: (endsecs % 60) as u8, - nanosecond: 0 - }; - Event { start, end, name: event.label.to_string(), finished: event.finished } - } -} - fn main() -> Result<(), Box> { let ui = AppWindow::new()?; let now = chrono::Local::now(); let offset = now.hour() * 3600 + now.minute() * 60 + now.second(); - - let date: TomlDate = TomlDate { - day: now.day() as u8, - month: now.month() as u8, - year: now.year() as u16 - }; - - let config: Arc = Arc::new(load_config()); - let writing_log: Arc> = Arc::new(Mutex::new(Log::load_from(&config, date))); - - { - let ui_weak = ui.as_weak(); - let log = writing_log.clone(); - (move || { - let ui = ui_weak.unwrap(); - let log_guard = log.lock().expect("Log shouldn't be used twice"); - let events: Vec = (*log_guard) - .events - .iter() - .map(|event| TimelineEvent::from((*event).clone())) - .collect(); - let in_progress = events.iter().any(|event| !event.finished); - let model: ModelRc = Rc::new(VecModel::from(events)).into(); - ui.set_record_events(model); - ui.set_in_progress(in_progress); - })() - } - ui.invoke_update_record_offset(offset as i32); - ui.on_save_log({ - let config = config.clone(); - let log = writing_log.clone(); - move || { - let log_guard = log.lock().expect("Log shouldn't be used twice"); - if let Err(error) = (*log_guard).save(&config) { - eprintln!("Error occured while saving log: {error}"); - } - } - }); - - ui.on_update_visible_time({ + ui.on_update_record_visible_time({ let ui_weak = ui.as_weak(); - move |is_record: bool, hours_string: SharedString| { + move |hours_string: SharedString| { let ui = ui_weak.unwrap(); let hours = hours_string.split(' ') .next() .map(|h| h.parse::().unwrap()) .unwrap(); - if is_record { - ui.set_record_visible_time(hours * 3600); - } else { - ui.set_review_visible_time(hours * 3600); - } + ui.set_record_visible_time(hours * 3600); } }); ui.on_start_new_event({ let ui_weak = ui.as_weak(); - let log = writing_log.clone(); move |event_name: SharedString| { let ui = ui_weak.unwrap(); - let events_rc = ui.get_record_events(); let events = events_rc.as_any() .downcast_ref::>() .unwrap(); let offset = ui.get_record_offset(); - - let event = TimelineEvent { + events.push(TimelineEvent { duration: 0, finished: false, label: event_name, start: offset - }; - - { - let mut log_guard = log.lock().expect("Log shouldn't be used twice"); - (*log_guard).events.push(Event::from(event.clone())); - } - - ui.invoke_save_log(); - - events.push(event); + }); } }); ui.on_stop_event({ let ui_weak = ui.as_weak(); - let log = writing_log.clone(); move || { let ui = ui_weak.unwrap(); let events_rc = ui.get_record_events(); - let events = events_rc.as_any() - .downcast_ref::>().unwrap(); + let events = events_rc.as_any().downcast_ref::>().unwrap(); let offset = ui.get_record_offset(); - let event_id = events.iter() - .position(|data| !data.finished) - .unwrap(); - let event = events.row_data(event_id) - .expect("stop-event called without unfinished events"); + let event_id = events.iter().position(|data| !data.finished).unwrap(); + let event = events.row_data(event_id).expect("stop-event called without unfinished events"); let new_event = TimelineEvent { duration: offset - event.start, finished: true, @@ -151,15 +63,6 @@ fn main() -> Result<(), Box> { start: event.start }; - { - let mut log_guard = log.lock().expect("Log shouldn't be used twice"); - (*log_guard).events.push(Event::from(new_event.clone())); - let index = (*log_guard).events.iter().position(|data| !data.finished).unwrap(); - (*log_guard).events.swap_remove(index); - } - - ui.invoke_save_log(); - events.set_row_data(event_id, new_event); } }); @@ -173,6 +76,8 @@ fn main() -> Result<(), Box> { } }); + let config: Config = load_config(); + println!("logs path: {}", config.log_path.to_str().unwrap()); ui.run()?; diff --git a/ui/app-window.slint b/ui/app-window.slint index 89a10b6..a2f44ee 100644 --- a/ui/app-window.slint +++ b/ui/app-window.slint @@ -4,15 +4,11 @@ import { ReviewWidget } from "review.slint"; import { TimelineEvent } from "timeline.slint"; export component AppWindow inherits Window { + callback update-record-visible-time <=> record.update-visible-time; callback start-new-event <=> record.start-new-event; callback stop-event <=> record.stop-event; callback chain-event <=> record.chain-event; callback update-record-offset(int); - callback save-log; - - callback fetch-log <=> review.fetch-log; - - callback update-visible-time(bool, string); update-record-offset(new-offset) => { record.offset = new-offset; @@ -20,13 +16,7 @@ export component AppWindow inherits Window { in-out property record-events <=> record.events; in-out property record-offset <=> record.offset; - in-out property record-visible-time <=> record.visible-time; - in-out property in-progress <=> record.in-progress; - - in-out property review-events <=> review.events; - in-out property review-offset <=> review.offset; - in-out property review-visible-time <=> review.visible-time; - + in-out property record-visible-time <=> record.visible-time; property<[string]> combo-spans: ["1 Hour", "4 Hours", "8 Hours", "24 Hours"]; title: "Aliveline"; @@ -36,19 +26,11 @@ export component AppWindow inherits Window { title: "Record"; record := RecordWidget { combo-spans: combo-spans; - update-visible-time(time) => { - root.update-visible-time(true, time); - } } } Tab { title: "Review"; - review := ReviewWidget { - combo-spans: combo-spans; - update-visible-time(time) => { - root.update-visible-time(false, time) - } - } + review := ReviewWidget {} } } } diff --git a/ui/record.slint b/ui/record.slint index cf195af..0556e37 100644 --- a/ui/record.slint +++ b/ui/record.slint @@ -11,7 +11,7 @@ export component RecordWidget inherits VerticalBox { in-out property offset <=> tl.offset; in-out property events <=> tl.events; in property<[string]> combo-spans: []; - in-out property in-progress: false; + property in-progress: false; property event-name <=> le.text; tl := Timeline { diff --git a/ui/review.slint b/ui/review.slint index 229658b..6d3c7d0 100644 --- a/ui/review.slint +++ b/ui/review.slint @@ -1,31 +1,13 @@ -import { VerticalBox, LineEdit, Button, DatePickerPopup, ComboBox } from "std-widgets.slint"; +import { VerticalBox, LineEdit, Button, DatePickerPopup } from "std-widgets.slint"; import { Timeline } from "timeline.slint"; export component ReviewWidget inherits VerticalBox { - callback update-visible-time(string); - callback fetch-log(int, int, int); - - property max-offset: 24 * 3600; property current-year; property current-month; property current-day; - in property<[string]> combo-spans: []; - in-out property visible-time <=> tl.visible-time; - in-out property offset <=> tl.offset; - in-out property events <=> tl.events; - - tl := Timeline { + + Timeline { updating: false; - TouchArea { - preferred-width: 100%; - preferred-height: 100%; - moved => { - if self.pressed { - root.offset -= (self.mouse-x - self.pressed-x) / 1px; - root.offset = clamp(root.offset, visible-time, max-offset); - } - } - } } GridLayout { spacing-vertical: 8px; @@ -34,30 +16,12 @@ export component ReviewWidget inherits VerticalBox { text: "Day: \{current-day}/\{current-month}/\{current-year}"; font-size: 32px; horizontal-alignment: right; - row: 0; } Button { text: "Select"; clicked => { date-picker.show() } - row: 0; - col: 1; - } - Text { - text: "Span: "; - font-size: 24px; - row: 1; - horizontal-alignment: right; - } - ComboBox { - model: combo-spans; - current-index: 0; - row: 1; - col: 1; - selected(current-value) => { - root.update-visible-time(current-value); - } } } date-picker := DatePickerPopup { @@ -68,7 +32,6 @@ export component ReviewWidget inherits VerticalBox { current-year = date.year; current-month = date.month; current-day = date.day; - root.fetch-log(current-year, current-month, current-day); } } } diff --git a/ui/timeline.slint b/ui/timeline.slint index c6069a0..de530d9 100644 --- a/ui/timeline.slint +++ b/ui/timeline.slint @@ -85,5 +85,4 @@ export component Timeline inherits Rectangle { visible: timeline-event.visible; } } - @children }