Make Lume Faster (#208)

* chore: fix some lint issues

* feat: refactor contact list

* feat: refactor relay hint

* feat: add missing commands

* feat: use new cache layer for react query

* feat: refactor column

* feat: improve relay hint

* fix: replace break with continue in parser

* refactor: publish function

* feat: add reply command

* feat: improve editor

* fix: quote

* chore: update deps

* refactor: note component

* feat: improve repost

* feat: improve cache

* fix: backup screen

* refactor: column manager
This commit is contained in:
雨宮蓮
2024-06-17 13:52:06 +07:00
committed by GitHub
parent 7c99ed39e4
commit 843895d876
79 changed files with 1738 additions and 1975 deletions

View File

@@ -7,7 +7,7 @@ use specta::Type;
use tauri::State;
use crate::Nostr;
use crate::nostr::utils::{dedup_event, Meta, parse_event};
use crate::nostr::utils::{create_event_tags, dedup_event, Meta, parse_event};
#[derive(Debug, Serialize, Type)]
pub struct RichEvent {
@@ -15,6 +15,13 @@ pub struct RichEvent {
pub parsed: Option<Meta>,
}
#[tauri::command]
#[specta::specta]
pub async fn get_event_meta(content: &str) -> Result<Meta, ()> {
let meta = parse_event(content).await;
Ok(meta)
}
#[tauri::command]
#[specta::specta]
pub async fn get_event(id: &str, state: State<'_, Nostr>) -> Result<RichEvent, String> {
@@ -22,14 +29,7 @@ pub async fn get_event(id: &str, state: State<'_, Nostr>) -> Result<RichEvent, S
let event_id: Option<EventId> = match Nip19::from_bech32(id) {
Ok(val) => match val {
Nip19::EventId(id) => Some(id),
Nip19::Event(event) => {
let relays = event.relays;
for relay in relays.into_iter() {
let _ = client.add_relay(&relay).await.unwrap_or_default();
client.connect_relay(&relay).await.unwrap_or_default();
}
Some(event.event_id)
}
Nip19::Event(event) => Some(event.event_id),
_ => None,
},
Err(_) => match EventId::from_hex(id) {
@@ -40,10 +40,8 @@ pub async fn get_event(id: &str, state: State<'_, Nostr>) -> Result<RichEvent, S
match event_id {
Some(id) => {
let filter = Filter::new().id(id);
match client
.get_events_of(vec![filter], Some(Duration::from_secs(10)))
.get_events_of(vec![Filter::new().id(id)], Some(Duration::from_secs(10)))
.await
{
Ok(events) => {
@@ -67,6 +65,63 @@ pub async fn get_event(id: &str, state: State<'_, Nostr>) -> Result<RichEvent, S
}
}
#[tauri::command]
#[specta::specta]
pub async fn get_event_from(
id: &str,
relay_hint: &str,
state: State<'_, Nostr>,
) -> Result<RichEvent, String> {
let client = &state.client;
let event_id: Option<EventId> = match Nip19::from_bech32(id) {
Ok(val) => match val {
Nip19::EventId(id) => Some(id),
Nip19::Event(event) => Some(event.event_id),
_ => None,
},
Err(_) => match EventId::from_hex(id) {
Ok(val) => Some(val),
Err(_) => None,
},
};
// Add relay hint to relay pool
if let Err(err) = client.add_relay(relay_hint).await {
return Err(err.to_string());
}
if (client.connect_relay(relay_hint).await).is_ok() {
match event_id {
Some(id) => {
match client
.get_events_from(vec![relay_hint], vec![Filter::new().id(id)], None)
.await
{
Ok(events) => {
if let Some(event) = events.first() {
let raw = event.as_json();
let parsed = if event.kind == Kind::TextNote {
Some(parse_event(&event.content).await)
} else {
None
};
Ok(RichEvent { raw, parsed })
} else {
Err("Cannot found this event with current relay list".into())
}
}
Err(err) => Err(err.to_string()),
}
}
None => Err("Event ID is not valid.".into()),
}
} else {
Err("Relay connection failed.".into())
}
}
#[tauri::command]
#[specta::specta]
pub async fn get_replies(id: &str, state: State<'_, Nostr>) -> Result<Vec<RichEvent>, String> {
@@ -146,25 +201,19 @@ pub async fn get_events_by(
#[tauri::command]
#[specta::specta]
pub async fn get_local_events(
pubkeys: Vec<String>,
until: Option<&str>,
state: State<'_, Nostr>,
) -> Result<Vec<RichEvent>, String> {
let client = &state.client;
let contact_list = state.contact_list.lock().unwrap().clone();
let as_of = match until {
Some(until) => Timestamp::from_str(until).unwrap(),
None => Timestamp::now(),
};
let authors: Vec<PublicKey> = pubkeys
.into_iter()
.map(|p| {
if p.starts_with("npub1") {
PublicKey::from_bech32(p).unwrap()
} else {
PublicKey::from_hex(p).unwrap()
}
})
.collect();
let authors: Vec<PublicKey> = contact_list.into_iter().map(|f| f.public_key).collect();
let filter = Filter::new()
.kinds(vec![Kind::TextNote, Kind::Repost])
.limit(20)
@@ -177,6 +226,7 @@ pub async fn get_local_events(
{
Ok(events) => {
let dedup = dedup_event(&events, false);
let futures = dedup.into_iter().map(|ev| async move {
let raw = ev.as_json();
let parsed = if ev.kind == Kind::TextNote {
@@ -187,6 +237,64 @@ pub async fn get_local_events(
RichEvent { raw, parsed }
});
let rich_events = join_all(futures).await;
Ok(rich_events)
}
Err(err) => Err(err.to_string()),
}
}
#[tauri::command]
#[specta::specta]
pub async fn get_group_events(
public_keys: Vec<&str>,
until: Option<&str>,
state: State<'_, Nostr>,
) -> Result<Vec<RichEvent>, String> {
let client = &state.client;
let as_of = match until {
Some(until) => Timestamp::from_str(until).unwrap(),
None => Timestamp::now(),
};
let authors: Vec<PublicKey> = public_keys
.into_iter()
.map(|p| {
if p.starts_with("npub1") {
PublicKey::from_bech32(p).unwrap()
} else {
PublicKey::from_hex(p).unwrap()
}
})
.collect();
let filter = Filter::new()
.kinds(vec![Kind::TextNote, Kind::Repost])
.limit(20)
.until(as_of)
.authors(authors);
match client
.get_events_of(vec![filter], Some(Duration::from_secs(10)))
.await
{
Ok(events) => {
let dedup = dedup_event(&events, false);
let futures = dedup.into_iter().map(|ev| async move {
let raw = ev.as_json();
let parsed = if ev.kind == Kind::TextNote {
Some(parse_event(&ev.content).await)
} else {
None
};
RichEvent { raw, parsed }
});
let rich_events = join_all(futures).await;
Ok(rich_events)
@@ -278,14 +386,126 @@ pub async fn get_hashtag_events(
#[tauri::command]
#[specta::specta]
pub async fn publish(
content: &str,
tags: Vec<Vec<&str>>,
content: String,
warning: Option<String>,
difficulty: Option<u8>,
state: State<'_, Nostr>,
) -> Result<String, String> {
let client = &state.client;
let event_tags = tags.into_iter().map(|val| Tag::parse(&val).unwrap());
match client.publish_text_note(content, event_tags).await {
// Create tags from content
let mut tags = create_event_tags(&content);
// Add content-warning tag if present
if let Some(reason) = warning {
let t = TagStandard::ContentWarning {
reason: Some(reason),
};
let tag = Tag::from(t);
tags.push(tag)
};
// Get signer
let signer = match client.signer().await {
Ok(signer) => signer,
Err(_) => return Err("Signer is required.".into()),
};
// Get public key
let public_key = signer.public_key().await.unwrap();
// Create unsigned event
let unsigned_event = match difficulty {
Some(num) => EventBuilder::text_note(content, tags).to_unsigned_pow_event(public_key, num),
None => EventBuilder::text_note(content, tags).to_unsigned_event(public_key),
};
// Publish
match signer.sign_event(unsigned_event).await {
Ok(event) => match client.send_event(event).await {
Ok(event_id) => Ok(event_id.to_bech32().unwrap()),
Err(err) => Err(err.to_string()),
},
Err(err) => Err(err.to_string()),
}
}
#[tauri::command]
#[specta::specta]
pub async fn reply(
content: String,
to: String,
root: Option<String>,
state: State<'_, Nostr>,
) -> Result<String, String> {
let client = &state.client;
let database = client.database();
// Create tags from content
let mut tags = create_event_tags(&content);
let reply_id = match EventId::from_hex(to) {
Ok(val) => val,
Err(_) => return Err("Event is not valid.".into()),
};
match database
.query(vec![Filter::new().id(reply_id)], Order::Desc)
.await
{
Ok(events) => {
if let Some(event) = events.into_iter().next() {
let relay_hint =
if let Some(relays) = database.event_seen_on_relays(event.id).await.unwrap() {
relays.into_iter().next().map(UncheckedUrl::new)
} else {
None
};
let t = TagStandard::Event {
event_id: event.id,
relay_url: relay_hint,
marker: Some(Marker::Reply),
public_key: Some(event.pubkey),
};
let tag = Tag::from(t);
tags.push(tag)
} else {
return Err("Reply event is not found.".into());
}
}
Err(err) => return Err(err.to_string()),
};
if let Some(id) = root {
let root_id = match EventId::from_hex(id) {
Ok(val) => val,
Err(_) => return Err("Event is not valid.".into()),
};
if let Ok(events) = database
.query(vec![Filter::new().id(root_id)], Order::Desc)
.await
{
if let Some(event) = events.into_iter().next() {
let relay_hint =
if let Some(relays) = database.event_seen_on_relays(event.id).await.unwrap() {
relays.into_iter().next().map(UncheckedUrl::new)
} else {
None
};
let t = TagStandard::Event {
event_id: event.id,
relay_url: relay_hint,
marker: Some(Marker::Root),
public_key: Some(event.pubkey),
};
let tag = Tag::from(t);
tags.push(tag)
}
}
};
match client.publish_text_note(content, tags).await {
Ok(event_id) => Ok(event_id.to_bech32().unwrap()),
Err(err) => Err(err.to_string()),
}