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