@@ -1,10 +1,21 @@ |
| 1 | 1 | //! Notification history management |
| 2 | 2 | |
| 3 | 3 | use std::collections::VecDeque; |
| 4 | | -use tracing::debug; |
| 4 | +use std::fs; |
| 5 | +use std::io::{BufReader, BufWriter}; |
| 6 | +use std::path::PathBuf; |
| 7 | +use tracing::{debug, info, warn}; |
| 5 | 8 | |
| 6 | 9 | use super::types::Notification; |
| 7 | 10 | |
| 11 | +/// Get the history file path |
| 12 | +fn history_file_path() -> PathBuf { |
| 13 | + dirs::data_dir() |
| 14 | + .unwrap_or_else(|| PathBuf::from("~/.local/share")) |
| 15 | + .join("garnotify") |
| 16 | + .join("history.json") |
| 17 | +} |
| 18 | + |
| 8 | 19 | /// Notification history with circular buffer |
| 9 | 20 | pub struct History { |
| 10 | 21 | /// Stored notifications (most recent at back) |
@@ -88,6 +99,58 @@ impl History { |
| 88 | 99 | pub fn list_recent_first(&self) -> Vec<&Notification> { |
| 89 | 100 | self.items.iter().rev().collect() |
| 90 | 101 | } |
| 102 | + |
| 103 | + /// Load history from file |
| 104 | + pub fn load_from_file(&mut self) -> Result<usize, std::io::Error> { |
| 105 | + let path = history_file_path(); |
| 106 | + |
| 107 | + if !path.exists() { |
| 108 | + debug!("No history file found at {}", path.display()); |
| 109 | + return Ok(0); |
| 110 | + } |
| 111 | + |
| 112 | + let file = fs::File::open(&path)?; |
| 113 | + let reader = BufReader::new(file); |
| 114 | + |
| 115 | + let items: Vec<Notification> = serde_json::from_reader(reader) |
| 116 | + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; |
| 117 | + |
| 118 | + let count = items.len(); |
| 119 | + |
| 120 | + // Only keep up to max_length items |
| 121 | + self.items = items |
| 122 | + .into_iter() |
| 123 | + .rev() // Reverse because we want most recent at back |
| 124 | + .take(self.max_length) |
| 125 | + .collect::<Vec<_>>() |
| 126 | + .into_iter() |
| 127 | + .rev() // Reverse back to original order |
| 128 | + .collect(); |
| 129 | + |
| 130 | + info!("Loaded {} notifications from history file", self.items.len()); |
| 131 | + Ok(count) |
| 132 | + } |
| 133 | + |
| 134 | + /// Save history to file |
| 135 | + pub fn save_to_file(&self) -> Result<(), std::io::Error> { |
| 136 | + let path = history_file_path(); |
| 137 | + |
| 138 | + // Create parent directory if needed |
| 139 | + if let Some(parent) = path.parent() { |
| 140 | + fs::create_dir_all(parent)?; |
| 141 | + } |
| 142 | + |
| 143 | + let file = fs::File::create(&path)?; |
| 144 | + let writer = BufWriter::new(file); |
| 145 | + |
| 146 | + // Save as array (oldest first, matching internal order) |
| 147 | + let items: Vec<&Notification> = self.items.iter().collect(); |
| 148 | + serde_json::to_writer_pretty(writer, &items) |
| 149 | + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; |
| 150 | + |
| 151 | + info!("Saved {} notifications to history file", self.items.len()); |
| 152 | + Ok(()) |
| 153 | + } |
| 91 | 154 | } |
| 92 | 155 | |
| 93 | 156 | impl Default for History { |