Add created_at/updated_at to Task model, DB, and API
- Add created_at and updated_at fields to Task struct - Preserve existing created_at on upsert in insert_task - Parse updated field from Google Tasks API response - Add created_at column to DB schema with migration
This commit is contained in:
@@ -537,6 +537,8 @@ impl App {
|
|||||||
status: TaskStatus::NeedsAction,
|
status: TaskStatus::NeedsAction,
|
||||||
due: None,
|
due: None,
|
||||||
position: 0,
|
position: 0,
|
||||||
|
created_at: None,
|
||||||
|
updated_at: None,
|
||||||
};
|
};
|
||||||
self.db.insert_task(&task).ok();
|
self.db.insert_task(&task).ok();
|
||||||
self.db.push_sync(
|
self.db.push_sync(
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ pub struct Task {
|
|||||||
pub status: TaskStatus,
|
pub status: TaskStatus,
|
||||||
pub due: Option<NaiveDateTime>,
|
pub due: Option<NaiveDateTime>,
|
||||||
pub position: i64,
|
pub position: i64,
|
||||||
|
pub created_at: Option<NaiveDateTime>,
|
||||||
|
pub updated_at: Option<NaiveDateTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -181,6 +181,18 @@ impl ApiClient {
|
|||||||
.ok()
|
.ok()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let updated = item["updated"].as_str().and_then(|s| {
|
||||||
|
chrono::NaiveDateTime::parse_from_str(
|
||||||
|
&s.replace("T", " ")
|
||||||
|
.replace("Z", "")
|
||||||
|
.chars()
|
||||||
|
.take(19)
|
||||||
|
.collect::<String>(),
|
||||||
|
"%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
});
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
id: item["id"].as_str().unwrap_or("").to_string(),
|
id: item["id"].as_str().unwrap_or("").to_string(),
|
||||||
list_id: list_id.to_string(),
|
list_id: list_id.to_string(),
|
||||||
@@ -193,6 +205,8 @@ impl ApiClient {
|
|||||||
},
|
},
|
||||||
due: due_str,
|
due: due_str,
|
||||||
position: i as i64,
|
position: i as i64,
|
||||||
|
created_at: None,
|
||||||
|
updated_at: updated,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -261,6 +275,18 @@ impl ApiClient {
|
|||||||
.ok()
|
.ok()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let updated = item["updated"].as_str().and_then(|s| {
|
||||||
|
chrono::NaiveDateTime::parse_from_str(
|
||||||
|
&s.replace("T", " ")
|
||||||
|
.replace("Z", "")
|
||||||
|
.chars()
|
||||||
|
.take(19)
|
||||||
|
.collect::<String>(),
|
||||||
|
"%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
});
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
id: item["id"].as_str().unwrap_or("").to_string(),
|
id: item["id"].as_str().unwrap_or("").to_string(),
|
||||||
list_id: list_id.to_string(),
|
list_id: list_id.to_string(),
|
||||||
@@ -273,6 +299,8 @@ impl ApiClient {
|
|||||||
},
|
},
|
||||||
due: due_str,
|
due: due_str,
|
||||||
position: i as i64,
|
position: i as i64,
|
||||||
|
created_at: None,
|
||||||
|
updated_at: updated,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ impl Db {
|
|||||||
due TEXT,
|
due TEXT,
|
||||||
position INTEGER NOT NULL DEFAULT 0,
|
position INTEGER NOT NULL DEFAULT 0,
|
||||||
updated_at TEXT NOT NULL,
|
updated_at TEXT NOT NULL,
|
||||||
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now')),
|
||||||
FOREIGN KEY (list_id) REFERENCES task_lists(id) ON DELETE CASCADE
|
FOREIGN KEY (list_id) REFERENCES task_lists(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -45,6 +46,10 @@ impl Db {
|
|||||||
"ALTER TABLE sync_queue ADD COLUMN retries INTEGER NOT NULL DEFAULT 0;",
|
"ALTER TABLE sync_queue ADD COLUMN retries INTEGER NOT NULL DEFAULT 0;",
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
conn.execute_batch(
|
||||||
|
"ALTER TABLE tasks ADD COLUMN created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now'));",
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
Ok(Self { conn: Mutex::new(conn) })
|
Ok(Self { conn: Mutex::new(conn) })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,13 +89,15 @@ impl Db {
|
|||||||
let conn = self.conn.lock().unwrap();
|
let conn = self.conn.lock().unwrap();
|
||||||
let mut stmt = conn
|
let mut stmt = conn
|
||||||
.prepare(
|
.prepare(
|
||||||
"SELECT id, list_id, title, notes, status, due, position
|
"SELECT id, list_id, title, notes, status, due, position, created_at, updated_at
|
||||||
FROM tasks WHERE list_id = ?1 ORDER BY position ASC",
|
FROM tasks WHERE list_id = ?1 ORDER BY position ASC",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
stmt.query_map(params![list_id], |row| {
|
stmt.query_map(params![list_id], |row| {
|
||||||
let due_str: Option<String> = row.get(5)?;
|
let due_str: Option<String> = row.get(5)?;
|
||||||
let due = due_str.and_then(|s| NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M").ok());
|
let due = due_str.and_then(|s| NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M").ok());
|
||||||
|
let created_str: String = row.get(7)?;
|
||||||
|
let updated_str: String = row.get(8)?;
|
||||||
Ok(Task {
|
Ok(Task {
|
||||||
id: row.get(0)?,
|
id: row.get(0)?,
|
||||||
list_id: row.get(1)?,
|
list_id: row.get(1)?,
|
||||||
@@ -102,6 +109,8 @@ impl Db {
|
|||||||
},
|
},
|
||||||
due,
|
due,
|
||||||
position: row.get(6)?,
|
position: row.get(6)?,
|
||||||
|
created_at: NaiveDateTime::parse_from_str(&created_str, "%Y-%m-%d %H:%M:%S").ok(),
|
||||||
|
updated_at: NaiveDateTime::parse_from_str(&updated_str, "%Y-%m-%d %H:%M:%S").ok(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -126,9 +135,26 @@ impl Db {
|
|||||||
} else {
|
} else {
|
||||||
task.position
|
task.position
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Preserve existing created_at if the task already exists
|
||||||
|
let created_at = task.created_at.unwrap_or_else(|| {
|
||||||
|
conn.query_row(
|
||||||
|
"SELECT created_at FROM tasks WHERE id = ?1",
|
||||||
|
params![task.id],
|
||||||
|
|row| row.get::<_, String>(0),
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S").ok())
|
||||||
|
.unwrap_or_else(|| chrono::Utc::now().naive_utc())
|
||||||
|
});
|
||||||
|
|
||||||
|
let updated_at = task
|
||||||
|
.updated_at
|
||||||
|
.unwrap_or_else(|| chrono::Utc::now().naive_utc());
|
||||||
|
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT OR REPLACE INTO tasks (id, list_id, title, notes, status, due, position, updated_at)
|
"INSERT OR REPLACE INTO tasks (id, list_id, title, notes, status, due, position, updated_at, created_at)
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
|
||||||
params![
|
params![
|
||||||
task.id,
|
task.id,
|
||||||
task.list_id,
|
task.list_id,
|
||||||
@@ -137,7 +163,8 @@ impl Db {
|
|||||||
status_str,
|
status_str,
|
||||||
due_str,
|
due_str,
|
||||||
position,
|
position,
|
||||||
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
|
updated_at.format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||||
|
created_at.format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -149,6 +176,9 @@ impl Db {
|
|||||||
TaskStatus::Completed => "completed",
|
TaskStatus::Completed => "completed",
|
||||||
TaskStatus::NeedsAction => "needsAction",
|
TaskStatus::NeedsAction => "needsAction",
|
||||||
};
|
};
|
||||||
|
let updated_at = task
|
||||||
|
.updated_at
|
||||||
|
.unwrap_or_else(|| chrono::Utc::now().naive_utc());
|
||||||
let conn = self.conn.lock().unwrap();
|
let conn = self.conn.lock().unwrap();
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"UPDATE tasks SET title=?1, notes=?2, status=?3, due=?4, position=?5, updated_at=?6
|
"UPDATE tasks SET title=?1, notes=?2, status=?3, due=?4, position=?5, updated_at=?6
|
||||||
@@ -159,7 +189,7 @@ impl Db {
|
|||||||
status_str,
|
status_str,
|
||||||
due_str,
|
due_str,
|
||||||
task.position,
|
task.position,
|
||||||
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
|
updated_at.format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||||
task.id,
|
task.id,
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
@@ -255,6 +255,8 @@ async fn push_sync(
|
|||||||
status: TaskStatus::NeedsAction,
|
status: TaskStatus::NeedsAction,
|
||||||
due: None,
|
due: None,
|
||||||
position: 0,
|
position: 0,
|
||||||
|
created_at: None,
|
||||||
|
updated_at: None,
|
||||||
});
|
});
|
||||||
api.create_task(&item.list_id, &task).await
|
api.create_task(&item.list_id, &task).await
|
||||||
}
|
}
|
||||||
@@ -267,6 +269,8 @@ async fn push_sync(
|
|||||||
status: TaskStatus::NeedsAction,
|
status: TaskStatus::NeedsAction,
|
||||||
due: None,
|
due: None,
|
||||||
position: 0,
|
position: 0,
|
||||||
|
created_at: None,
|
||||||
|
updated_at: None,
|
||||||
});
|
});
|
||||||
api.update_task(&item.list_id, &task).await
|
api.update_task(&item.list_id, &task).await
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user