1use crate::{Dialog, DialogError, Note, Result};
2use nostr_sdk::prelude::*;
3
4impl Dialog {
5 pub async fn list_notes(&self, limit: usize) -> Result<Vec<Note>> {
6 eprintln!(
7 "[lib] list_notes: limit={} for pubkey={}",
8 limit,
9 self.keys.public_key()
10 );
11 let filter = Filter::new()
13 .author(self.keys.public_key())
14 .kind(Kind::from(1059))
15 .limit(limit);
16
17 let events = self
18 .client
19 .database()
20 .query(vec![filter])
21 .await
22 .map_err(|e| DialogError::Database(e.to_string()))?;
23
24 let mut notes = Vec::new();
26 for event in events {
27 if let Ok(decrypted) = self.decrypt_event(&event) {
28 let is_read = self.get_read_status(&event.id).await;
29 notes.push(Note {
30 id: event.id,
31 text: decrypted,
32 tags: extract_tags(&event),
33 created_at: event.created_at,
34 is_read,
35 is_synced: true, });
37 }
38 }
39
40 notes.sort_by(|a, b| b.created_at.cmp(&a.created_at));
42
43 eprintln!("[lib] list_notes: returning {} notes", notes.len());
44 Ok(notes)
45 }
46
47 pub async fn list_by_tag(&self, tag: &str, limit: usize) -> Result<Vec<Note>> {
48 let filter = Filter::new()
49 .author(self.keys.public_key())
50 .kind(Kind::from(1059))
51 .hashtag(tag.to_lowercase())
52 .limit(limit);
53
54 let events = self
55 .client
56 .database()
57 .query(vec![filter])
58 .await
59 .map_err(|e| DialogError::Database(e.to_string()))?;
60
61 let mut notes = Vec::new();
63 for event in events {
64 if let Ok(decrypted) = self.decrypt_event(&event) {
65 let is_read = self.get_read_status(&event.id).await;
66 notes.push(Note {
67 id: event.id,
68 text: decrypted,
69 tags: extract_tags(&event),
70 created_at: event.created_at,
71 is_read,
72 is_synced: true, });
74 }
75 }
76
77 notes.sort_by(|a, b| b.created_at.cmp(&a.created_at));
79
80 Ok(notes)
81 }
82
83 pub async fn sync_notes(&self) -> Result<()> {
84 let filter = Filter::new()
86 .author(self.keys.public_key())
87 .kind(Kind::from(1059));
88
89 self.client.sync(filter, &SyncOptions::default()).await?;
90 Ok(())
91 }
92
93 pub async fn sync_notes_plain(&self, limit: Option<usize>) -> Result<()> {
95 let mut filter = Filter::new()
97 .author(self.keys.public_key())
98 .kind(Kind::from(1059));
99 if let Some(lim) = limit {
100 filter = filter.limit(lim);
101 }
102
103 let events = self
106 .client
107 .fetch_events(vec![filter], Some(std::time::Duration::from_secs(10)))
108 .await
109 .map_err(|e| DialogError::Database(e.to_string()))?;
110
111 for event in events {
112 self.client
114 .database()
115 .save_event(&event)
116 .await
117 .map_err(|e| DialogError::Database(e.to_string()))?;
118 }
119 Ok(())
120 }
121}
122
123fn extract_tags(event: &Event) -> Vec<String> {
124 event
125 .tags
126 .iter()
127 .filter_map(|tag| {
128 if let Some(TagStandard::Hashtag(t)) = tag.as_standardized() {
129 Some(t.to_string())
130 } else {
131 None
132 }
133 })
134 .collect()
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_extract_tags() {
143 let test_keys = Keys::generate();
145 let tags = vec![
146 Tag::hashtag("test"),
147 Tag::hashtag("example"),
148 Tag::public_key(test_keys.public_key()),
149 ];
150
151 let sig_bytes = [0u8; 64];
152 let event = Event::new(
153 EventId::all_zeros(),
154 test_keys.public_key(),
155 Timestamp::now(),
156 Kind::from(1059),
157 tags,
158 "encrypted content",
159 Signature::from_slice(&sig_bytes).unwrap(),
160 );
161
162 let extracted = extract_tags(&event);
163 assert_eq!(extracted, vec!["test", "example"]);
164 }
165}