My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: zettel_list uses db as source of truth

+54 -35
+1
crates/dto/src/lib.rs
··· 12 12 pub use sea_orm::EntityTrait; 13 13 pub use sea_orm::IntoActiveModel; 14 14 pub use sea_orm::QueryFilter; 15 + pub use sea_orm::QueryOrder; 15 16 16 17 /// Exporting this as a generic NanoId. 17 18 pub use migration::types::NanoId;
+3 -1
crates/dto/tests/zettel.rs
··· 2 2 ActiveModelTrait, ActiveValue::Set, ColorDTO, TagActiveModel, TagEntity, TagModel, 3 3 ZettelActiveModel, ZettelEntity, ZettelModel, 4 4 }; 5 - use sea_orm::IntoActiveModel; 5 + use sea_orm::{IntoActiveModel, QueryOrder}; 6 6 7 7 mod common; 8 8 ··· 56 56 let zettels_for_tag = TagEntity::load() 57 57 .filter_by_nano_id(tag.nano_id.clone()) 58 58 .with(ZettelEntity) 59 + 59 60 .all(&db) 61 + 60 62 .await 61 63 .unwrap(); 62 64
+39 -15
src/tui/components/zk/mod.rs
··· 1 1 use async_trait::async_trait; 2 2 use color_eyre::eyre::Result; 3 + use dto::{QueryOrder, TagEntity, ZettelColumns, ZettelEntity}; 3 4 use ratatui::{ 4 5 prelude::*, 5 6 widgets::{Block, ListState}, ··· 8 9 9 10 use crate::{ 10 11 tui::{Signal, components::Component}, 11 - types::KastenHandle, 12 + types::{KastenHandle, Zettel}, 12 13 }; 13 14 14 15 mod preview; ··· 24 25 /// initialized) 25 26 pub struct Zk<'text> { 26 27 signal_tx: Option<UnboundedSender<Signal>>, 28 + // TODO: really think whether or not this actually needs a kasten 29 + // handle or is a workspace clone enough? 27 30 kh: KastenHandle, 28 31 layouts: Layouts, 29 32 zettel_list: ZettelList<'text>, ··· 57 60 pub async fn new(kh: KastenHandle) -> Result<Self> { 58 61 let kt = kh.read().await; 59 62 60 - let nodes = kt.graph.nodes_iter().collect::<Vec<_>>(); 63 + let zettels: Vec<Zettel> = ZettelEntity::load() 64 + .with(TagEntity) 65 + .order_by_desc(ZettelColumns::ModifiedAt) 66 + .all(&kt.ws.db) 67 + .await? 68 + .into_iter() 69 + .map(Into::into) 70 + .collect(); 61 71 62 72 // in theory this is wasted compute, we should be initializing all our 63 73 // stuff inside the init function 64 74 let mut l_state = ListState::default(); 65 75 l_state.select_first(); 66 - let zettel_list = ZettelList::new(nodes.as_slice(), l_state, 0); 76 + let zettel_list = ZettelList::new(zettels, l_state, 0); 67 77 68 78 let selected_zettel = zettel_list 69 79 .id_list ··· 138 148 139 149 Ok(()) 140 150 } 151 + 152 + pub async fn get_zettels_by_current_query(&self) -> Result<Vec<Zettel>> { 153 + let kt = self.kh.read().await; 154 + let models = ZettelEntity::load() 155 + .with(TagEntity) 156 + .order_by_desc(ZettelColumns::ModifiedAt) 157 + .all(&kt.ws.db) 158 + .await?; 159 + 160 + // im being a good boy and dropping this as soon as im done with the db 161 + drop(kt); 162 + 163 + let zettels: Vec<Zettel> = models.into_iter().map(Into::into).collect(); 164 + Ok(zettels) 165 + } 141 166 } 142 167 143 168 #[async_trait] ··· 146 171 async fn init(&mut self, area: Size) -> color_eyre::Result<()> { 147 172 let total_width = area.width; 148 173 149 - let kt = self.kh.read().await; 150 - 151 - let nodes = kt.graph.nodes_iter().collect::<Vec<_>>(); 152 - 174 + // in theory this is wasted compute, we should be initializing all our 153 175 let mut l_state = ListState::default(); 154 176 l_state.select_first(); 155 177 156 - let zettel_list = ZettelList::new(nodes.as_slice(), l_state, total_width / 2); 178 + let zettel_list = ZettelList::new( 179 + self.get_zettels_by_current_query().await?, 180 + l_state, 181 + total_width / 2, 182 + ); 157 183 158 184 self.zettel_list = zettel_list; 159 - 160 - drop(kt); 161 185 162 186 Ok(()) 163 187 } ··· 212 236 return Ok(None); 213 237 }; 214 238 215 - let kh = self.kh.read().await; 239 + let kt = self.kh.read().await; 216 240 217 - let node = kh 241 + let node = kt 218 242 .get_node_by_zettel_id(id) 219 243 .expect("Invariant broken, this must exist."); 220 244 ··· 222 246 // the ratatui api doesnt expose a swap function to the inner render 223 247 // list. 224 248 self.zettel_list = ZettelList::new( 225 - kh.graph.nodes_iter().collect::<Vec<_>>().as_slice(), 249 + self.get_zettels_by_current_query().await?, 226 250 self.zettel_list.state, 227 251 self.zettel_list.width, 228 252 ); 229 253 230 254 self.zettel_view = ZettelView::from(node.payload()); 231 - self.preview = Preview::from(node.payload().content(&kh.ws).await?); 232 - drop(kh); 255 + self.preview = Preview::from(node.payload().content(&kt.ws).await?); 256 + drop(kt); 233 257 } 234 258 235 259 _ => {}
+4 -10
src/tui/components/zk/zettel_list.rs
··· 1 - use egui_graphs::{Node, petgraph::graph::NodeIndex}; 2 1 use ratatui::{ 3 2 style::{Color, Modifier, Style, Stylize}, 4 3 text::{Line, Span, Text}, 5 4 widgets::{List, ListState}, 6 5 }; 7 6 8 - use crate::types::{Link, Zettel, ZettelId}; 7 + use crate::types::{Zettel, ZettelId}; 9 8 10 9 pub struct ZettelList<'text> { 11 10 pub render_list: ratatui::widgets::List<'text>, ··· 72 71 } 73 72 74 73 impl ZettelList<'_> { 75 - pub fn new(nodes: &[(NodeIndex, &Node<Zettel, Link>)], state: ListState, width: u16) -> Self { 76 - let render_list = List::new(nodes.iter().map(|(_, n)| { 77 - let z = n.payload(); 74 + pub fn new(zettels: Vec<Zettel>, state: ListState, width: u16) -> Self { 75 + let render_list = List::new(zettels.iter().map(|z| { 78 76 let mut zli: ZettelListItem<'_> = z.into(); 79 77 zli.width = width; 80 - 81 78 Text::from(zli) 82 79 })) 83 80 .style(Color::White) 84 81 .highlight_style(Modifier::REVERSED) 85 82 .highlight_symbol("> "); 86 83 87 - let id_list = nodes 88 - .iter() 89 - .map(|(_, n)| n.payload().id.clone()) 90 - .collect::<Vec<_>>(); 84 + let id_list = zettels.into_iter().map(|z| z.id).collect::<Vec<_>>(); 91 85 92 86 ZettelList { 93 87 render_list,
+7 -9
src/types/kasten.rs
··· 133 133 let zid = zettel.id.clone(); 134 134 let idx = self.graph.add_node(zettel); 135 135 136 - gid = Some( 137 - self.zid_to_gid 138 - .insert(zid.clone(), idx) 139 - .expect("this cannot have existed already"), 140 - ); 136 + self.zid_to_gid.insert(zid.clone(), idx); 137 + 138 + gid = Some(idx); 141 139 142 140 self.get_node_by_zettel_id_mut(&zid) 143 141 .expect("we just inserted it") 144 142 .payload_mut() 145 143 }; 146 144 145 + // and then we sync with the file 146 + zettel.sync_with_file(&ws).await?; 147 + 147 148 // to get past borrowchecker rules 148 - let mut zettel = zettel.clone(); 149 + let zettel = zettel.clone(); 149 150 150 151 // gid must be set 151 152 let gid = gid.unwrap(); 152 - 153 - // and then we sync with the file 154 - zettel.sync_with_file(&ws).await?; 155 153 156 154 // and now we manage the links going out of the file 157 155