column.rs (4646B)
1 use crate::error::FilterError; 2 use crate::filter; 3 use crate::filter::FilterState; 4 use crate::{timeline::Timeline, Error}; 5 use enostr::Pubkey; 6 use nostrdb::{Filter, Ndb, Transaction}; 7 use std::fmt::Display; 8 use tracing::{error, warn}; 9 10 #[derive(Clone, Debug)] 11 pub enum PubkeySource { 12 Explicit(Pubkey), 13 DeckAuthor, 14 } 15 16 #[derive(Debug, Clone)] 17 pub enum ListKind { 18 Contact(PubkeySource), 19 } 20 21 /// 22 /// What kind of column is it? 23 /// - Follow List 24 /// - Notifications 25 /// - DM 26 /// - filter 27 /// - ... etc 28 #[derive(Debug, Clone)] 29 pub enum ColumnKind { 30 List(ListKind), 31 32 Notifications(PubkeySource), 33 34 Profile(PubkeySource), 35 36 Universe, 37 38 /// Generic filter 39 Generic, 40 } 41 42 impl Display for ColumnKind { 43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 44 match self { 45 ColumnKind::List(ListKind::Contact(_src)) => f.write_str("Contacts"), 46 ColumnKind::Generic => f.write_str("Timeline"), 47 ColumnKind::Notifications(_) => f.write_str("Notifications"), 48 ColumnKind::Profile(_) => f.write_str("Profile"), 49 ColumnKind::Universe => f.write_str("Universe"), 50 } 51 } 52 } 53 54 impl ColumnKind { 55 pub fn contact_list(pk: PubkeySource) -> Self { 56 ColumnKind::List(ListKind::Contact(pk)) 57 } 58 59 pub fn profile(pk: PubkeySource) -> Self { 60 ColumnKind::Profile(pk) 61 } 62 63 pub fn notifications(pk: PubkeySource) -> Self { 64 ColumnKind::Notifications(pk) 65 } 66 67 pub fn into_timeline(self, ndb: &Ndb, default_user: Option<&[u8; 32]>) -> Option<Timeline> { 68 match self { 69 ColumnKind::Universe => Some(Timeline::new( 70 ColumnKind::Universe, 71 FilterState::ready(vec![Filter::new() 72 .kinds([1]) 73 .limit(filter::default_limit()) 74 .build()]), 75 )), 76 77 ColumnKind::Generic => { 78 warn!("you can't convert a ColumnKind::Generic to a Timeline"); 79 None 80 } 81 82 ColumnKind::Profile(pk_src) => { 83 let pk = match &pk_src { 84 PubkeySource::DeckAuthor => default_user?, 85 PubkeySource::Explicit(pk) => pk.bytes(), 86 }; 87 88 let filter = Filter::new() 89 .authors([pk]) 90 .kinds([1]) 91 .limit(filter::default_limit()) 92 .build(); 93 94 Some(Timeline::new( 95 ColumnKind::profile(pk_src), 96 FilterState::ready(vec![filter]), 97 )) 98 } 99 100 ColumnKind::Notifications(pk_src) => { 101 let pk = match &pk_src { 102 PubkeySource::DeckAuthor => default_user?, 103 PubkeySource::Explicit(pk) => pk.bytes(), 104 }; 105 106 let notifications_filter = Filter::new() 107 .pubkeys([pk]) 108 .kinds([1]) 109 .limit(filter::default_limit()) 110 .build(); 111 112 Some(Timeline::new( 113 ColumnKind::notifications(pk_src), 114 FilterState::ready(vec![notifications_filter]), 115 )) 116 } 117 118 ColumnKind::List(ListKind::Contact(pk_src)) => { 119 let pk = match &pk_src { 120 PubkeySource::DeckAuthor => default_user?, 121 PubkeySource::Explicit(pk) => pk.bytes(), 122 }; 123 124 let contact_filter = Filter::new().authors([pk]).kinds([3]).limit(1).build(); 125 126 let txn = Transaction::new(ndb).expect("txn"); 127 let results = ndb 128 .query(&txn, &[contact_filter.clone()], 1) 129 .expect("contact query failed?"); 130 131 if results.is_empty() { 132 return Some(Timeline::new( 133 ColumnKind::contact_list(pk_src), 134 FilterState::needs_remote(vec![contact_filter.clone()]), 135 )); 136 } 137 138 match Timeline::contact_list(&results[0].note) { 139 Err(Error::Filter(FilterError::EmptyContactList)) => Some(Timeline::new( 140 ColumnKind::contact_list(pk_src), 141 FilterState::needs_remote(vec![contact_filter]), 142 )), 143 Err(e) => { 144 error!("Unexpected error: {e}"); 145 None 146 } 147 Ok(tl) => Some(tl), 148 } 149 } 150 } 151 } 152 }