mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Add device detail screen and navigate to it from notifications and devices
- New /devices/:macAddress route showing all device fields with Register/Forget actions - Device list items are now tappable; popup menu gains a "View details" item - Notification list items with a linked device are tappable; popup menu gains a "View device" item - Backend: new DB migration adds optional mac_address column to notifications - Device-related notifications (NewDeviceFound, DeviceOnlineAfterTime, DeviceChanged) now store the triggering device's MAC address Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
07f7d54531
commit
2da339a2b0
@@ -0,0 +1 @@
|
||||
ALTER TABLE notifications ADD COLUMN mac_address TEXT;
|
||||
@@ -14,7 +14,7 @@ pub fn list(
|
||||
let conn = db::get_db_connection();
|
||||
|
||||
let mut sql_statement =
|
||||
"SELECT id, created_on, notification_type, title, body, is_new FROM notifications WHERE 1=1"
|
||||
"SELECT id, created_on, notification_type, title, body, is_new, mac_address FROM notifications WHERE 1=1"
|
||||
.to_string();
|
||||
|
||||
let mut params: Vec<rusqlite::types::Value> = Vec::new();
|
||||
@@ -53,6 +53,7 @@ pub fn list(
|
||||
title: row.get(3)?,
|
||||
body: row.get(4)?,
|
||||
is_new: row.get(5)?,
|
||||
mac_address: row.get(6)?,
|
||||
})
|
||||
})?
|
||||
.collect::<Result<_, _>>()?;
|
||||
@@ -64,8 +65,8 @@ pub fn insert(notification: Notification) -> Result<i64, DbError> {
|
||||
let conn = db::get_db_connection();
|
||||
|
||||
match conn.execute(
|
||||
"INSERT INTO notifications (created_on, notification_type, title, body, is_new) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
params![notification.created_on, notification.notification_type, notification.title, notification.body, notification.is_new]) {
|
||||
"INSERT INTO notifications (created_on, notification_type, title, body, is_new, mac_address) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
||||
params![notification.created_on, notification.notification_type, notification.title, notification.body, notification.is_new, notification.mac_address]) {
|
||||
Ok(_) => {
|
||||
debug!("Notification inserted into database: {}", notification);
|
||||
Ok(conn.last_insert_rowid())
|
||||
@@ -126,7 +127,7 @@ pub fn read(id: i64) -> Option<Notification> {
|
||||
let conn = db::get_db_connection();
|
||||
|
||||
let result: Result<Notification, rusqlite::Error> = conn.query_one(
|
||||
"SELECT id, created_on, notification_type, title, body, is_new FROM notifications WHERE id=?1",
|
||||
"SELECT id, created_on, notification_type, title, body, is_new, mac_address FROM notifications WHERE id=?1",
|
||||
params![id],
|
||||
|row| {
|
||||
Ok(Notification {
|
||||
@@ -136,6 +137,7 @@ pub fn read(id: i64) -> Option<Notification> {
|
||||
title: row.get(3)?,
|
||||
body: row.get(4)?,
|
||||
is_new: row.get(5)?,
|
||||
mac_address: row.get(6)?,
|
||||
})
|
||||
},
|
||||
);
|
||||
@@ -176,6 +178,7 @@ mod tests {
|
||||
"New notification title".to_string(),
|
||||
"New notification body".to_string(),
|
||||
true,
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
@@ -206,6 +209,7 @@ mod tests {
|
||||
"New notification title".to_string(),
|
||||
"New notification body".to_string(),
|
||||
false,
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
@@ -232,6 +236,7 @@ mod tests {
|
||||
"New notification 1".to_string(),
|
||||
"Body 1".to_string(),
|
||||
true,
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
@@ -241,6 +246,7 @@ mod tests {
|
||||
"New notification 2".to_string(),
|
||||
"Body 2".to_string(),
|
||||
true,
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
@@ -275,6 +281,7 @@ mod tests {
|
||||
"New notification title".to_string(),
|
||||
"New notification body".to_string(),
|
||||
true,
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
@@ -306,6 +313,28 @@ mod tests {
|
||||
inserted_notification.body, "New notification body",
|
||||
"Wrong body (should be 'New notification body')"
|
||||
);
|
||||
assert_eq!(
|
||||
inserted_notification.mac_address, None,
|
||||
"mac_address should be None"
|
||||
);
|
||||
|
||||
// Insert and validate notification with mac_address
|
||||
let mac = "aa:aa:aa:aa:aa:aa".to_string();
|
||||
let inserted_id = insert(Notification::new(
|
||||
Utc::now(),
|
||||
NotificationType::NewDeviceFound,
|
||||
"Device found".to_string(),
|
||||
"Body".to_string(),
|
||||
true,
|
||||
Some(mac.clone()),
|
||||
))
|
||||
.unwrap();
|
||||
let inserted_notification = read(inserted_id).unwrap();
|
||||
assert_eq!(
|
||||
inserted_notification.mac_address,
|
||||
Some(mac),
|
||||
"mac_address should round-trip through insert/read"
|
||||
);
|
||||
|
||||
// Insert and validate notification without title nor body
|
||||
let created_on = Utc::now();
|
||||
@@ -315,6 +344,7 @@ mod tests {
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
false,
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
@@ -398,6 +428,7 @@ mod tests {
|
||||
"New notification title".to_string(),
|
||||
"New notification body".to_string(),
|
||||
true,
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ pub fn trigger_new_device(device: Device) -> Result<(), Box<dyn Error>> {
|
||||
device.mac_address, device.ipv4_address, device.vendor
|
||||
),
|
||||
true,
|
||||
Some(device.mac_address.clone()),
|
||||
);
|
||||
|
||||
send_notification(notification)?;
|
||||
@@ -75,6 +76,7 @@ pub fn trigger_existing_device(
|
||||
)))
|
||||
),
|
||||
true,
|
||||
Some(new_device.mac_address.clone()),
|
||||
);
|
||||
|
||||
send_notification(notification)?;
|
||||
@@ -97,6 +99,7 @@ pub fn trigger_existing_device(
|
||||
new_device.vendor,
|
||||
),
|
||||
true,
|
||||
Some(new_device.mac_address.clone()),
|
||||
);
|
||||
|
||||
send_notification(notification)?;
|
||||
@@ -113,6 +116,7 @@ pub fn trigger_existing_device(
|
||||
new_device.ipv4_address,
|
||||
),
|
||||
true,
|
||||
Some(new_device.mac_address.clone()),
|
||||
);
|
||||
|
||||
send_notification(notification)?;
|
||||
@@ -129,6 +133,7 @@ pub fn trigger_existing_device(
|
||||
new_device.vendor,
|
||||
),
|
||||
true,
|
||||
Some(new_device.mac_address.clone()),
|
||||
);
|
||||
|
||||
send_notification(notification)?;
|
||||
|
||||
@@ -15,6 +15,7 @@ pub struct Notification {
|
||||
pub title: String,
|
||||
pub body: String,
|
||||
pub is_new: bool,
|
||||
pub mac_address: Option<String>,
|
||||
}
|
||||
|
||||
impl Notification {
|
||||
@@ -24,6 +25,7 @@ impl Notification {
|
||||
title: String,
|
||||
body: String,
|
||||
is_new: bool,
|
||||
mac_address: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id: -1,
|
||||
@@ -32,6 +34,7 @@ impl Notification {
|
||||
title,
|
||||
body,
|
||||
is_new,
|
||||
mac_address,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,8 +43,8 @@ impl fmt::Display for Notification {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"id={}, created_on={}, notification_type={}, is_new={}\ntitle={}\nbody={}",
|
||||
self.id, self.created_on, self.notification_type, self.is_new, self.title, self.body
|
||||
"id={}, created_on={}, notification_type={}, is_new={}, mac_address={:?}\ntitle={}\nbody={}",
|
||||
self.id, self.created_on, self.notification_type, self.is_new, self.mac_address, self.title, self.body
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user