Skip to content

Commit

Permalink
feat: add DVM feeds
Browse files Browse the repository at this point in the history
  • Loading branch information
reyamir committed Nov 7, 2024
1 parent 4b79e55 commit ece6bcc
Show file tree
Hide file tree
Showing 13 changed files with 547 additions and 76 deletions.
14 changes: 14 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ tauri-plugin-theme = "2.1.2"
tauri-plugin-decorum = { git = "https://github.com/clearlysid/tauri-plugin-decorum" }
tauri-specta = { version = "2.0.0-rc.15", features = ["derive", "typescript"] }

nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb"] }
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb", "webln", "all-nips"] }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }

specta = "^2.0.0-rc.20"
Expand Down
167 changes: 167 additions & 0 deletions src-tauri/src/commands/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,173 @@ pub async fn get_all_events_from(
Ok(alt_events)
}

#[tauri::command]
#[specta::specta]
pub async fn get_all_events_by_kind(
kind: u16,
until: Option<String>,
state: State<'_, Nostr>,
) -> Result<Vec<String>, String> {
let client = &state.client;

let as_of = match until {
Some(until) => Timestamp::from_str(&until).map_err(|err| err.to_string())?,
None => Timestamp::now(),
};

let filter = Filter::new()
.kind(Kind::Custom(kind))
.limit(FETCH_LIMIT)
.until(as_of);

let mut events = Events::new(&[filter.clone()]);

let mut rx = client
.stream_events(vec![filter], Some(Duration::from_secs(3)))
.await
.map_err(|e| e.to_string())?;

while let Some(event) = rx.next().await {
events.insert(event);
}

let alt_events: Vec<String> = events.iter().map(|ev| ev.as_json()).collect();

Ok(alt_events)
}

#[tauri::command]
#[specta::specta]
pub async fn get_all_providers(state: State<'_, Nostr>) -> Result<Vec<String>, String> {
let client = &state.client;

let filter = Filter::new()
.kind(Kind::Custom(31990))
.custom_tag(SingleLetterTag::lowercase(Alphabet::K), vec!["5300"]);

let mut events = Events::new(&[filter.clone()]);

let mut rx = client
.stream_events(vec![filter], Some(Duration::from_secs(3)))
.await
.map_err(|e| e.to_string())?;

while let Some(event) = rx.next().await {
events.insert(event);
}

let alt_events: Vec<String> = events.iter().map(|ev| ev.as_json()).collect();

Ok(alt_events)
}

#[tauri::command]
#[specta::specta]
pub async fn request_events_from_provider(
provider: String,
state: State<'_, Nostr>,
) -> Result<String, String> {
let client = &state.client;
let signer = client.signer().await.map_err(|err| err.to_string())?;
let public_key = signer
.get_public_key()
.await
.map_err(|err| err.to_string())?;
let provider = PublicKey::parse(&provider).map_err(|err| err.to_string())?;

// Get current user's relay list
let relay_list = client
.database()
.relay_list(public_key)
.await
.map_err(|err| err.to_string())?;

let relay_list: Vec<String> = relay_list.iter().map(|item| item.0.to_string()).collect();

// Create job request
let builder = EventBuilder::job_request(
Kind::JobRequest(5300),
vec![
Tag::public_key(provider),
Tag::custom(TagKind::Relays, relay_list),
],
)
.map_err(|err| err.to_string())?;

match client.send_event_builder(builder).await {
Ok(output) => {
let filter = Filter::new()
.kind(Kind::JobResult(6300))
.author(provider)
.pubkey(public_key)
.since(Timestamp::now());

let opts = SubscribeAutoCloseOptions::default()
.filter(FilterOptions::WaitDurationAfterEOSE(Duration::from_secs(2)));

let _ = client.subscribe(vec![filter], Some(opts)).await;

Ok(output.val.to_hex())
}
Err(e) => Err(e.to_string()),
}
}

#[tauri::command]
#[specta::specta]
pub async fn get_all_events_by_request(
id: String,
provider: String,
state: State<'_, Nostr>,
) -> Result<Vec<RichEvent>, String> {
let client = &state.client;
let public_key = PublicKey::parse(&id).map_err(|err| err.to_string())?;
let provider = PublicKey::parse(&provider).map_err(|err| err.to_string())?;

let filter = Filter::new()
.kind(Kind::JobResult(6300))
.author(provider)
.pubkey(public_key)
.limit(1);

let events = client
.database()
.query(vec![filter])
.await
.map_err(|err| err.to_string())?;

if let Some(event) = events.first() {
let parsed: Vec<Vec<String>> =
serde_json::from_str(&event.content).map_err(|err| err.to_string())?;

let vec: Vec<Tag> = parsed
.into_iter()
.filter_map(|item| Tag::parse(&item).ok())
.collect::<Vec<_>>();

let tags = Tags::new(vec);
let ids: Vec<EventId> = tags.event_ids().copied().collect();

let filter = Filter::new().ids(ids);
let mut events = Events::new(&[filter.clone()]);

let mut rx = client
.stream_events(vec![filter], Some(Duration::from_secs(3)))
.await
.map_err(|e| e.to_string())?;

while let Some(event) = rx.next().await {
events.insert(event);
}

let alt_events = process_event(client, events, false).await;

Ok(alt_events)
} else {
Err("Job result not found.".into())
}
}

#[tauri::command]
#[specta::specta]
pub async fn get_local_events(
Expand Down
52 changes: 28 additions & 24 deletions src-tauri/src/commands/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ pub async fn create_column(
if let Ok(public_key) = PublicKey::parse(&id) {
let is_newsfeed = payload.url().to_string().contains("newsfeed");

tauri::async_runtime::spawn(async move {
let state = webview.state::<Nostr>();
let client = &state.client;
if is_newsfeed {
tauri::async_runtime::spawn(async move {
let state = webview.state::<Nostr>();
let client = &state.client;

if is_newsfeed {
if let Ok(contact_list) =
client.database().contacts_public_keys(public_key).await
{
Expand All @@ -102,27 +102,31 @@ pub async fn create_column(
println!("Subscription error: {}", e);
}
}
}
});
});
}
} else if let Ok(event_id) = EventId::parse(&id) {
tauri::async_runtime::spawn(async move {
let state = webview.state::<Nostr>();
let client = &state.client;

let subscription_id = SubscriptionId::new(webview.label());

let filter = Filter::new()
.event(event_id)
.kinds(vec![Kind::TextNote, Kind::Custom(1111)])
.since(Timestamp::now());

if let Err(e) = client
.subscribe_with_id(subscription_id, vec![filter], None)
.await
{
println!("Subscription error: {}", e);
}
});
let is_thread = payload.url().to_string().contains("events");

if is_thread {
tauri::async_runtime::spawn(async move {
let state = webview.state::<Nostr>();
let client = &state.client;

let subscription_id = SubscriptionId::new(webview.label());

let filter = Filter::new()
.event(event_id)
.kinds(vec![Kind::TextNote, Kind::Custom(1111)])
.since(Timestamp::now());

if let Err(e) = client
.subscribe_with_id(subscription_id, vec![filter], None)
.await
{
println!("Subscription error: {}", e);
}
});
}
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ fn main() {
get_all_events_by_authors,
get_all_events_by_hashtags,
get_all_events_from,
get_all_events_by_kind,
get_all_providers,
request_events_from_provider,
get_all_events_by_request,
get_local_events,
get_global_events,
search,
Expand Down Expand Up @@ -232,8 +236,7 @@ fn main() {
// Config
let opts = Options::new()
.gossip(true)
.max_avg_latency(Duration::from_millis(300))
.automatic_authentication(true)
.max_avg_latency(Duration::from_millis(500))
.timeout(Duration::from_secs(5));

// Setup nostr client
Expand Down Expand Up @@ -546,6 +549,8 @@ fn main() {
) {
println!("Emit error: {}", e)
}
} else if event.kind == Kind::JobResult(6300) {
println!("Job result: {}", event.as_json())
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/commands.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,38 @@ async getAllEventsFrom(url: string, until: string | null) : Promise<Result<RichE
else return { status: "error", error: e as any };
}
},
async getAllEventsByKind(kind: number, until: string | null) : Promise<Result<string[], string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("get_all_events_by_kind", { kind, until }) };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async getAllProviders() : Promise<Result<string[], string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("get_all_providers") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async requestEventsFromProvider(provider: string) : Promise<Result<string, string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("request_events_from_provider", { provider }) };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async getAllEventsByRequest(id: string, provider: string) : Promise<Result<RichEvent[], string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("get_all_events_by_request", { id, provider }) };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async getLocalEvents(until: string | null) : Promise<Result<RichEvent[], string>> {
try {
return { status: "ok", data: await TAURI_INVOKE("get_local_events", { until }) };
Expand Down
2 changes: 1 addition & 1 deletion src/components/column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function Column({ column }: { column: LumeColumn }) {
y: rect.y,
width: rect.width,
height: rect.height,
url: `${column.url}?label=${column.label}&name=${column.name}`,
url: `${column.url}?label=${column.label}&name=${column.name}&account=${column.account}`,
});

if (res.status === "error") {
Expand Down
10 changes: 4 additions & 6 deletions src/components/note/buttons/repost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,11 @@ export function NoteRepost({

if (signer.status === "ok") {
if (!signer.data) {
if (!signer.data) {
const res = await commands.setSigner(account);
const res = await commands.setSigner(account);

if (res.status === "error") {
await message(res.error, { kind: "error" });
return;
}
if (res.status === "error") {
await message(res.error, { kind: "error" });
return;
}
}

Expand Down
Loading

0 comments on commit ece6bcc

Please sign in to comment.