aboutsummaryrefslogtreecommitdiffstats
path: root/ui/backend/src
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@elliehuxtable.com>2024-06-18 17:11:24 +0100
committerGitHub <noreply@github.com>2024-06-18 17:11:24 +0100
commitb8be23ee99f47c89d9c9f4ce508b940efc88b1ca (patch)
treead5ba50590f0cdb11b2ea4540795ced931ee7c30 /ui/backend/src
parentfeat(tui): configurable prefix character (#2157) (diff)
downloadatuin-b8be23ee99f47c89d9c9f4ce508b940efc88b1ca.zip
feat(gui): add activity calendar to the homepage (#2160)
* feat(gui): add activity calendar to the homepage * localise week start
Diffstat (limited to 'ui/backend/src')
-rw-r--r--ui/backend/src/db.rs18
-rw-r--r--ui/backend/src/main.rs49
2 files changed, 67 insertions, 0 deletions
diff --git a/ui/backend/src/db.rs b/ui/backend/src/db.rs
index 7e29302a..1015ebf1 100644
--- a/ui/backend/src/db.rs
+++ b/ui/backend/src/db.rs
@@ -174,6 +174,24 @@ impl HistoryDB {
Ok(history)
}
+ pub async fn calendar(&self) -> Result<Vec<(String, u64)>, String> {
+ let query = "select count(1) as count, strftime('%F', datetime(timestamp / 1000000000, 'unixepoch')) as day from history where timestamp > ((unixepoch() - 31536000) * 1000000000) group by day;";
+
+ let calendar: Vec<(String, u64)> = sqlx::query(query)
+ // safe to cast, count(x) is never < 0
+ .map(|row: SqliteRow| {
+ (
+ row.get::<String, _>("day"),
+ row.get::<i64, _>("count") as u64,
+ )
+ })
+ .fetch_all(&self.0.pool)
+ .await
+ .map_err(|e| e.to_string())?;
+
+ Ok(calendar)
+ }
+
pub async fn global_stats(&self) -> Result<GlobalStats, String> {
let day_ago = time::OffsetDateTime::now_utc() - time::Duration::days(1);
let day_ago = day_ago.unix_timestamp_nanos();
diff --git a/ui/backend/src/main.rs b/ui/backend/src/main.rs
index f03bccda..2ba67e50 100644
--- a/ui/backend/src/main.rs
+++ b/ui/backend/src/main.rs
@@ -167,6 +167,54 @@ async fn home_info() -> Result<HomeInfo, String> {
Ok(info)
}
+// Match the format that the frontend library we use expects
+// All the processing in Rust, not JS.
+// Faaaassssssst af ⚡️🦀
+#[derive(Debug, serde::Serialize)]
+pub struct HistoryCalendarDay {
+ pub date: String,
+ pub count: u64,
+ pub level: u8,
+}
+
+#[tauri::command]
+async fn history_calendar() -> Result<Vec<HistoryCalendarDay>, String> {
+ let settings = Settings::new().map_err(|e| e.to_string())?;
+ let db_path = PathBuf::from(settings.db_path.as_str());
+ let db = HistoryDB::new(db_path, settings.local_timeout).await?;
+
+ let calendar = db.calendar().await?;
+
+ // probs don't want to iterate _this_ many times, but it's only the last year. so 365
+ // iterations at max. should be quick.
+
+ let max = calendar
+ .iter()
+ .max_by_key(|d| d.1)
+ .expect("Can't find max count");
+
+ let ret = calendar
+ .iter()
+ .map(|d| {
+ // calculate the "level". we have 5, so figure out which 5th it fits into
+ let percent: f64 = d.1 as f64 / max.1 as f64;
+ let level = if d.1 == 0 {
+ 0.0
+ } else {
+ (percent / 0.2).round() + 1.0
+ };
+
+ HistoryCalendarDay {
+ date: d.0.clone(),
+ count: d.1,
+ level: std::cmp::min(4, level as u8),
+ }
+ })
+ .collect();
+
+ Ok(ret)
+}
+
fn show_window(app: &AppHandle) {
let windows = app.webview_windows();
@@ -190,6 +238,7 @@ fn main() {
session,
login,
register,
+ history_calendar,
install::install_cli,
install::is_cli_installed,
install::setup_cli,