mod.rs (6557B)
1 mod action; 2 mod context; 3 pub mod publish; 4 5 pub use action::{NoteAction, ReactAction, ScrollInfo, ZapAction, ZapTargetAmount}; 6 pub use context::{BroadcastContext, ContextSelection, NoteContextSelection}; 7 pub use publish::{ 8 builder_from_note, send_mute_event, send_people_list_event, send_report_event, 9 send_unmute_event, ReportTarget, ReportType, 10 }; 11 12 use crate::jobs::MediaJobSender; 13 use crate::nip05::Nip05Cache; 14 use crate::Accounts; 15 use crate::GlobalWallet; 16 use crate::Localization; 17 use crate::UnknownIds; 18 use crate::{notecache::NoteCache, zaps::Zaps, Images}; 19 use enostr::NoteId; 20 use nostrdb::{Ndb, Note, NoteKey, QueryResult, Transaction}; 21 use std::borrow::Borrow; 22 use std::cmp::Ordering; 23 use std::fmt; 24 25 /// Aggregates dependencies to reduce the number of parameters 26 /// passed to inner UI elements, minimizing prop drilling. 27 pub struct NoteContext<'d> { 28 pub ndb: &'d Ndb, 29 pub accounts: &'d Accounts, 30 pub global_wallet: &'d GlobalWallet, 31 pub i18n: &'d mut Localization, 32 pub img_cache: &'d mut Images, 33 pub note_cache: &'d mut NoteCache, 34 pub zaps: &'d mut Zaps, 35 pub jobs: &'d MediaJobSender, 36 pub unknown_ids: &'d mut UnknownIds, 37 pub nip05_cache: &'d mut Nip05Cache, 38 pub clipboard: &'d mut egui_winit::clipboard::Clipboard, 39 } 40 41 #[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] 42 pub struct NoteRef { 43 pub key: NoteKey, 44 pub created_at: u64, 45 } 46 47 #[derive(Clone, Copy, Eq, PartialEq, Hash)] 48 pub struct RootNoteIdBuf([u8; 32]); 49 50 impl fmt::Debug for RootNoteIdBuf { 51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 52 write!(f, "RootNoteIdBuf({})", self.hex()) 53 } 54 } 55 56 #[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] 57 pub struct RootNoteId<'a>(&'a [u8; 32]); 58 59 impl RootNoteIdBuf { 60 pub fn to_note_id(self) -> NoteId { 61 NoteId::new(self.0) 62 } 63 64 pub fn bytes(&self) -> &[u8; 32] { 65 &self.0 66 } 67 68 pub fn new( 69 ndb: &Ndb, 70 note_cache: &mut NoteCache, 71 txn: &Transaction, 72 id: &[u8; 32], 73 ) -> Result<RootNoteIdBuf, RootIdError> { 74 root_note_id_from_selected_id(ndb, note_cache, txn, id).map(|rnid| Self(*rnid.bytes())) 75 } 76 77 pub fn hex(&self) -> String { 78 hex::encode(self.bytes()) 79 } 80 81 pub fn new_unsafe(id: [u8; 32]) -> Self { 82 Self(id) 83 } 84 85 pub fn borrow(&self) -> RootNoteId<'_> { 86 RootNoteId(self.bytes()) 87 } 88 } 89 90 impl<'a> RootNoteId<'a> { 91 pub fn to_note_id(self) -> NoteId { 92 NoteId::new(*self.0) 93 } 94 95 pub fn bytes(&self) -> &[u8; 32] { 96 self.0 97 } 98 99 pub fn hex(&self) -> String { 100 hex::encode(self.bytes()) 101 } 102 103 pub fn to_owned(&self) -> RootNoteIdBuf { 104 RootNoteIdBuf::new_unsafe(*self.bytes()) 105 } 106 107 pub fn new( 108 ndb: &Ndb, 109 note_cache: &mut NoteCache, 110 txn: &'a Transaction, 111 id: &'a [u8; 32], 112 ) -> Result<RootNoteId<'a>, RootIdError> { 113 root_note_id_from_selected_id(ndb, note_cache, txn, id) 114 } 115 116 pub fn new_unsafe(id: &'a [u8; 32]) -> Self { 117 Self(id) 118 } 119 } 120 121 impl Borrow<[u8; 32]> for RootNoteIdBuf { 122 fn borrow(&self) -> &[u8; 32] { 123 &self.0 124 } 125 } 126 127 impl Borrow<[u8; 32]> for RootNoteId<'_> { 128 fn borrow(&self) -> &[u8; 32] { 129 self.0 130 } 131 } 132 133 impl NoteRef { 134 pub fn new(key: NoteKey, created_at: u64) -> Self { 135 NoteRef { key, created_at } 136 } 137 138 pub fn from_note(note: &Note<'_>) -> Self { 139 let created_at = note.created_at(); 140 let key = note.key().expect("todo: implement NoteBuf"); 141 NoteRef::new(key, created_at) 142 } 143 144 pub fn from_query_result(qr: QueryResult<'_>) -> Self { 145 NoteRef { 146 key: qr.note_key, 147 created_at: qr.note.created_at(), 148 } 149 } 150 } 151 152 impl Ord for NoteRef { 153 fn cmp(&self, other: &Self) -> Ordering { 154 match self.created_at.cmp(&other.created_at) { 155 Ordering::Equal => self.key.cmp(&other.key), 156 Ordering::Less => Ordering::Greater, 157 Ordering::Greater => Ordering::Less, 158 } 159 } 160 } 161 162 impl PartialOrd for NoteRef { 163 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 164 Some(self.cmp(other)) 165 } 166 } 167 168 #[derive(Debug, Copy, Clone)] 169 pub enum RootIdError { 170 NoteNotFound, 171 NoRootId, 172 } 173 174 pub fn root_note_id_from_selected_id<'txn, 'a>( 175 ndb: &Ndb, 176 note_cache: &mut NoteCache, 177 txn: &'txn Transaction, 178 selected_note_id: &'a [u8; 32], 179 ) -> Result<RootNoteId<'txn>, RootIdError> 180 where 181 'a: 'txn, 182 { 183 let selected_note_key = if let Ok(key) = ndb.get_notekey_by_id(txn, selected_note_id) { 184 key 185 } else { 186 return Err(RootIdError::NoteNotFound); 187 }; 188 189 let note = if let Ok(note) = ndb.get_note_by_key(txn, selected_note_key) { 190 note 191 } else { 192 return Err(RootIdError::NoteNotFound); 193 }; 194 195 note_cache 196 .cached_note_or_insert(selected_note_key, ¬e) 197 .reply 198 .borrow(note.tags()) 199 .root() 200 .map_or_else( 201 || Ok(RootNoteId::new_unsafe(selected_note_id)), 202 |rnid| Ok(RootNoteId::new_unsafe(rnid.id)), 203 ) 204 } 205 206 pub fn event_tag<'a>(ev: &nostrdb::Note<'a>, name: &str) -> Option<&'a str> { 207 ev.tags().iter().find_map(|tag| { 208 if tag.count() < 2 { 209 return None; 210 } 211 212 let cur_name = tag.get_str(0)?; 213 214 if cur_name != name { 215 return None; 216 } 217 218 tag.get_str(1) 219 }) 220 } 221 222 /// Temporary way of checking whether a user has sent a reaction. 223 /// Should be replaced with nostrdb metadata 224 pub fn reaction_sent_id(sender_pk: &enostr::Pubkey, note_reacted_to: &[u8; 32]) -> egui::Id { 225 egui::Id::new(("sent-reaction-id", note_reacted_to, sender_pk)) 226 } 227 228 /// Count the number of hashtags in a note by examining its tags 229 pub fn count_hashtags(note: &Note) -> usize { 230 let mut count = 0; 231 232 for tag in note.tags() { 233 // Early continue if not enough elements 234 if tag.count() < 2 { 235 continue; 236 } 237 238 // Check if this is a hashtag tag (type "t") 239 let Some("t") = tag.get_unchecked(0).variant().str() else { 240 continue; 241 }; 242 243 count += 1; 244 } 245 246 count 247 } 248 249 pub fn get_p_tags<'a>(note: &Note<'a>) -> Vec<&'a [u8; 32]> { 250 let mut items = Vec::new(); 251 for tag in note.tags() { 252 if tag.count() < 2 { 253 continue; 254 } 255 256 if tag.get_str(0) != Some("p") { 257 continue; 258 } 259 260 let Some(item) = tag.get_id(1) else { 261 continue; 262 }; 263 264 items.push(item); 265 } 266 267 items 268 }