mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-29 08:21:47 +00:00
BookmarkList
This commit is contained in:
parent
87347bb307
commit
0e3b06e24f
176
gossip-lib/src/bookmarks.rs
Normal file
176
gossip-lib/src/bookmarks.rs
Normal file
@ -0,0 +1,176 @@
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::globals::GLOBALS;
|
||||
use nostr_types::{
|
||||
ContentEncryptionAlgorithm, Event, EventKind, EventReference, Id, PreEvent, RelayUrl, Tag,
|
||||
Unixtime,
|
||||
};
|
||||
|
||||
pub struct BookmarkList(Vec<(EventReference, bool)>);
|
||||
|
||||
impl BookmarkList {
|
||||
pub fn empty() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.0 = Vec::new();
|
||||
}
|
||||
|
||||
fn add_tags(&mut self, tags: &[Tag], private: bool) -> Result<(), Error> {
|
||||
for tag in tags.iter() {
|
||||
let bookmark = match tag.tagname() {
|
||||
"e" => {
|
||||
let (id, opturl, optmarker) = tag.parse_event()?;
|
||||
let relays = match opturl {
|
||||
Some(url) => match RelayUrl::try_from_unchecked_url(&url) {
|
||||
Ok(rurl) => vec![rurl],
|
||||
Err(_) => vec![],
|
||||
},
|
||||
None => vec![],
|
||||
};
|
||||
EventReference::Id {
|
||||
id,
|
||||
author: None,
|
||||
relays,
|
||||
marker: optmarker,
|
||||
}
|
||||
}
|
||||
"a" => {
|
||||
let (addr, _optmarker) = tag.parse_address()?;
|
||||
EventReference::Addr(addr)
|
||||
}
|
||||
// We don't support other tags (but we have to preserve them)
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
self.0.push((bookmark, private));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add(&mut self, er: EventReference, private: bool) -> Result<bool, Error> {
|
||||
let index = self.0.iter().position(|(thiser, _)| *thiser == er);
|
||||
if index.is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
self.0.push((er, private));
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, er: EventReference) -> Result<bool, Error> {
|
||||
let index = self.0.iter().position(|(thiser, _)| *thiser == er);
|
||||
match index {
|
||||
None => return Ok(false),
|
||||
Some(index) => {
|
||||
self.0.remove(index);
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_event(event: &Event) -> Result<Self, Error> {
|
||||
let public_key = match GLOBALS.identity.public_key() {
|
||||
None => return Err(ErrorKind::NoPublicKey.into()),
|
||||
Some(pk) => pk,
|
||||
};
|
||||
|
||||
if event.kind != EventKind::BookmarkList {
|
||||
return Err(ErrorKind::WrongEventKind.into());
|
||||
}
|
||||
|
||||
if event.pubkey != public_key {
|
||||
return Err(ErrorKind::General("Event by wrong author".to_string()).into());
|
||||
}
|
||||
|
||||
let mut bml = Self::empty();
|
||||
bml.add_tags(event.tags.as_ref(), false)?;
|
||||
if let Ok(bytes) = GLOBALS.identity.decrypt(&public_key, &event.content) {
|
||||
if let Ok(vectags) = serde_json::from_str::<Vec<Tag>>(&bytes) {
|
||||
bml.add_tags(vectags.as_ref(), true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bml)
|
||||
}
|
||||
|
||||
pub fn into_event(&self) -> Result<Event, Error> {
|
||||
let public_key = match GLOBALS.identity.public_key() {
|
||||
None => return Err(ErrorKind::NoPublicKey.into()),
|
||||
Some(pk) => pk,
|
||||
};
|
||||
|
||||
let er_to_tag = |er: &EventReference| -> Tag {
|
||||
match er {
|
||||
EventReference::Id { id, relays, .. } => {
|
||||
Tag::new_event(*id, relays.first().map(|r| r.to_unchecked_url()), None)
|
||||
}
|
||||
EventReference::Addr(ea) => Tag::new_address(ea, None),
|
||||
}
|
||||
};
|
||||
|
||||
let tags: Vec<Tag> = self
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(
|
||||
|(er, private)| {
|
||||
if *private {
|
||||
None
|
||||
} else {
|
||||
Some(er_to_tag(er))
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
|
||||
let content = {
|
||||
let private: Vec<Tag> = self
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(
|
||||
|(er, private)| {
|
||||
if *private {
|
||||
Some(er_to_tag(er))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
let private_json = serde_json::to_string(&private)?;
|
||||
GLOBALS.identity.encrypt(
|
||||
&public_key,
|
||||
&private_json,
|
||||
ContentEncryptionAlgorithm::Nip04,
|
||||
)?
|
||||
};
|
||||
|
||||
let pre_event = PreEvent {
|
||||
pubkey: public_key,
|
||||
created_at: Unixtime::now().unwrap(),
|
||||
kind: EventKind::BookmarkList,
|
||||
tags,
|
||||
content,
|
||||
};
|
||||
|
||||
GLOBALS.identity.sign_event(pre_event)
|
||||
}
|
||||
|
||||
pub fn get_bookmark_feed(&self) -> Result<Vec<Id>, Error> {
|
||||
let mut feed: Vec<Id> = Vec::new();
|
||||
for (eref, _) in &self.0 {
|
||||
match eref {
|
||||
EventReference::Id { id, .. } => feed.push(*id),
|
||||
EventReference::Addr(ea) => {
|
||||
if let Some(event) = GLOBALS
|
||||
.storage
|
||||
.get_replaceable_event(ea.kind, ea.author, &ea.d)?
|
||||
{
|
||||
feed.push(event.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(feed)
|
||||
}
|
||||
}
|
@ -64,6 +64,9 @@
|
||||
//! with the storage engine. In some cases, the `Overlord` has more complex code for doing this,
|
||||
//! but in many cases, you can interact with `GLOBALS.storage` directly.
|
||||
|
||||
pub mod bookmarks;
|
||||
pub use bookmarks::BookmarkList;
|
||||
|
||||
/// Defines messages sent to the overlord
|
||||
pub mod comms;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user