feat: Add support for NIP-51 (#236)
* feat: and follow and interest sets * feat: improve query * feat: improve
This commit is contained in:
@@ -271,6 +271,15 @@ pub async fn login(
|
||||
// NIP-65: Connect to user's relay list
|
||||
init_nip65(client, &public_key).await;
|
||||
|
||||
// NIP-03: Get user's contact list
|
||||
let contact_list = {
|
||||
let contacts = client.get_contact_list(None).await.unwrap();
|
||||
// Update app's state
|
||||
state.contact_list.lock().await.clone_from(&contacts);
|
||||
// Return
|
||||
contacts
|
||||
};
|
||||
|
||||
// Run seperate thread for syncing data
|
||||
let pk = public_key.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
@@ -292,7 +301,10 @@ pub async fn login(
|
||||
Kind::MuteList,
|
||||
Kind::Bookmarks,
|
||||
Kind::Interests,
|
||||
Kind::InterestSet,
|
||||
Kind::FollowSet,
|
||||
Kind::PinList,
|
||||
Kind::EventDeletion,
|
||||
])
|
||||
.limit(1000),
|
||||
NegentropyOptions::default(),
|
||||
@@ -355,15 +367,6 @@ pub async fn login(
|
||||
println!("Subscribed: {}", e.success.len())
|
||||
}
|
||||
|
||||
// Get user's contact list
|
||||
let contact_list = {
|
||||
let contacts = client.get_contact_list(None).await.unwrap();
|
||||
// Update app's state
|
||||
state.contact_list.lock().await.clone_from(&contacts);
|
||||
// Return
|
||||
contacts
|
||||
};
|
||||
|
||||
// Get events from contact list
|
||||
if !contact_list.is_empty() {
|
||||
let authors: Vec<PublicKey> = contact_list.iter().map(|f| f.public_key).collect();
|
||||
|
||||
@@ -198,7 +198,7 @@ pub async fn subscribe_to(id: String, state: State<'_, Nostr>) -> Result<(), Str
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_events_by(
|
||||
pub async fn get_all_events_by_author(
|
||||
public_key: String,
|
||||
limit: i32,
|
||||
state: State<'_, Nostr>,
|
||||
@@ -237,14 +237,111 @@ pub async fn get_events_by(
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_local_events(
|
||||
until: Option<&str>,
|
||||
pub async fn get_all_events_by_authors(
|
||||
public_keys: Vec<String>,
|
||||
until: Option<String>,
|
||||
state: State<'_, Nostr>,
|
||||
) -> Result<Vec<RichEvent>, String> {
|
||||
let client = &state.client;
|
||||
|
||||
let as_of = match until {
|
||||
Some(until) => Timestamp::from_str(until).map_err(|err| err.to_string())?,
|
||||
Some(until) => Timestamp::from_str(&until).map_err(|err| err.to_string())?,
|
||||
None => Timestamp::now(),
|
||||
};
|
||||
|
||||
let authors: Vec<PublicKey> = public_keys
|
||||
.iter()
|
||||
.map(|pk| PublicKey::from_str(pk).map_err(|err| err.to_string()))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.limit(FETCH_LIMIT)
|
||||
.until(as_of)
|
||||
.authors(authors);
|
||||
|
||||
match client
|
||||
.get_events_of(vec![filter], EventSource::Database)
|
||||
.await
|
||||
{
|
||||
Ok(events) => {
|
||||
let fils = filter_converstation(events);
|
||||
let futures = fils.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)
|
||||
}
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_all_events_by_hashtags(
|
||||
hashtags: Vec<String>,
|
||||
until: Option<String>,
|
||||
state: State<'_, Nostr>,
|
||||
) -> Result<Vec<RichEvent>, 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()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.limit(FETCH_LIMIT)
|
||||
.until(as_of)
|
||||
.hashtags(hashtags);
|
||||
|
||||
match client
|
||||
.get_events_of(
|
||||
vec![filter],
|
||||
EventSource::both(Some(Duration::from_secs(5))),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(events) => {
|
||||
let fils = filter_converstation(events);
|
||||
let futures = fils.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)
|
||||
}
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_local_events(
|
||||
until: Option<String>,
|
||||
state: State<'_, Nostr>,
|
||||
) -> Result<Vec<RichEvent>, 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(),
|
||||
};
|
||||
|
||||
@@ -274,75 +371,22 @@ pub async fn get_local_events(
|
||||
}
|
||||
}
|
||||
|
||||
#[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).map_err(|err| err.to_string())?,
|
||||
None => Timestamp::now(),
|
||||
};
|
||||
|
||||
let authors: Vec<PublicKey> = public_keys
|
||||
.iter()
|
||||
.map(|p| PublicKey::from_str(p).map_err(|err| err.to_string()))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.limit(20)
|
||||
.until(as_of)
|
||||
.authors(authors);
|
||||
|
||||
match client
|
||||
.get_events_of(
|
||||
vec![filter],
|
||||
EventSource::both(Some(Duration::from_secs(5))),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(events) => {
|
||||
let fils = filter_converstation(events);
|
||||
let futures = fils.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)
|
||||
}
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_global_events(
|
||||
until: Option<&str>,
|
||||
until: Option<String>,
|
||||
state: State<'_, Nostr>,
|
||||
) -> Result<Vec<RichEvent>, String> {
|
||||
let client = &state.client;
|
||||
|
||||
let as_of = match until {
|
||||
Some(until) => Timestamp::from_str(until).map_err(|err| err.to_string())?,
|
||||
Some(until) => Timestamp::from_str(&until).map_err(|err| err.to_string())?,
|
||||
None => Timestamp::now(),
|
||||
};
|
||||
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.limit(20)
|
||||
.limit(FETCH_LIMIT)
|
||||
.until(as_of);
|
||||
|
||||
match client
|
||||
@@ -372,51 +416,6 @@ pub async fn get_global_events(
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_hashtag_events(
|
||||
hashtags: 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).map_err(|err| err.to_string())?,
|
||||
None => Timestamp::now(),
|
||||
};
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.limit(20)
|
||||
.until(as_of)
|
||||
.hashtags(hashtags);
|
||||
|
||||
match client
|
||||
.get_events_of(
|
||||
vec![filter],
|
||||
EventSource::both(Some(Duration::from_secs(5))),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(events) => {
|
||||
let fils = filter_converstation(events);
|
||||
let futures = fils.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)
|
||||
}
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn publish(
|
||||
@@ -534,7 +533,7 @@ pub async fn reply(
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn repost(raw: &str, state: State<'_, Nostr>) -> Result<String, String> {
|
||||
pub async fn repost(raw: String, state: State<'_, Nostr>) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let event = Event::from_json(raw).map_err(|err| err.to_string())?;
|
||||
|
||||
@@ -587,7 +586,7 @@ pub async fn event_to_bech32(id: String, state: State<'_, Nostr>) -> Result<Stri
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn user_to_bech32(user: &str, state: State<'_, Nostr>) -> Result<String, String> {
|
||||
pub async fn user_to_bech32(user: String, state: State<'_, Nostr>) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let public_key = PublicKey::parse(user).map_err(|err| err.to_string())?;
|
||||
|
||||
@@ -672,3 +671,21 @@ pub async fn search(
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn is_deleted_event(id: String, state: State<'_, Nostr>) -> Result<bool, String> {
|
||||
let client = &state.client;
|
||||
let signer = client.signer().await.map_err(|err| err.to_string())?;
|
||||
let public_key = signer.public_key().await.map_err(|err| err.to_string())?;
|
||||
let event_id = EventId::from_str(&id).map_err(|err| err.to_string())?;
|
||||
let filter = Filter::new()
|
||||
.author(public_key)
|
||||
.event(event_id)
|
||||
.kind(Kind::EventDeletion);
|
||||
|
||||
match client.database().query(vec![filter]).await {
|
||||
Ok(events) => Ok(!events.is_empty()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use nostr_sdk::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use tauri::State;
|
||||
use tauri::{Emitter, Manager, State};
|
||||
use tauri_specta::Event;
|
||||
|
||||
use crate::{common::get_latest_event, NewSettings, Nostr, Settings};
|
||||
@@ -105,18 +105,14 @@ pub async fn set_contact_list(
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_contact_list(state: State<'_, Nostr>) -> Result<Vec<String>, String> {
|
||||
let client = &state.client;
|
||||
let contact_list = state.contact_list.lock().await.clone();
|
||||
println!("Total contacts: {}", contact_list.len());
|
||||
let vec: Vec<String> = contact_list
|
||||
.into_iter()
|
||||
.map(|f| f.public_key.to_hex())
|
||||
.collect();
|
||||
|
||||
match client.get_contact_list(Some(Duration::from_secs(5))).await {
|
||||
Ok(contact_list) => {
|
||||
let list = contact_list
|
||||
.into_iter()
|
||||
.map(|f| f.public_key.to_hex())
|
||||
.collect();
|
||||
Ok(list)
|
||||
}
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -200,6 +196,194 @@ pub async fn toggle_contact(
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn set_group(
|
||||
title: String,
|
||||
description: Option<String>,
|
||||
image: Option<String>,
|
||||
users: Vec<String>,
|
||||
state: State<'_, Nostr>,
|
||||
handle: tauri::AppHandle,
|
||||
) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let public_keys: Vec<PublicKey> = users
|
||||
.iter()
|
||||
.map(|u| PublicKey::from_str(u).unwrap())
|
||||
.collect();
|
||||
let label = title.to_lowercase().replace(" ", "-");
|
||||
let mut tags: Vec<Tag> = vec![Tag::title(title)];
|
||||
|
||||
if let Some(desc) = description {
|
||||
tags.push(Tag::description(desc))
|
||||
};
|
||||
|
||||
if let Some(img) = image {
|
||||
let url = UncheckedUrl::new(img);
|
||||
tags.push(Tag::image(url, None));
|
||||
}
|
||||
|
||||
let builder = EventBuilder::follow_set(label, public_keys.clone()).add_tags(tags);
|
||||
|
||||
match client.send_event_builder(builder).await {
|
||||
Ok(report) => {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let state = handle.state::<Nostr>();
|
||||
let client = &state.client;
|
||||
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.authors(public_keys)
|
||||
.limit(500);
|
||||
|
||||
if let Ok(report) = client.reconcile(filter, NegentropyOptions::default()).await {
|
||||
println!("Received: {}", report.received.len());
|
||||
handle.emit("synchronized", ()).unwrap();
|
||||
};
|
||||
});
|
||||
|
||||
Ok(report.id().to_hex())
|
||||
}
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_group(id: String, state: State<'_, Nostr>) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let event_id = EventId::from_str(&id).map_err(|e| e.to_string())?;
|
||||
let filter = Filter::new().kind(Kind::FollowSet).id(event_id);
|
||||
|
||||
match client
|
||||
.get_events_of(
|
||||
vec![filter],
|
||||
EventSource::both(Some(Duration::from_secs(5))),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(events) => match get_latest_event(&events) {
|
||||
Some(ev) => Ok(ev.as_json()),
|
||||
None => Err("Not found.".to_string()),
|
||||
},
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_all_groups(state: State<'_, Nostr>) -> Result<Vec<String>, String> {
|
||||
let client = &state.client;
|
||||
let signer = client.signer().await.map_err(|e| e.to_string())?;
|
||||
let public_key = signer.public_key().await.map_err(|e| e.to_string())?;
|
||||
let filter = Filter::new().kind(Kind::FollowSet).author(public_key);
|
||||
|
||||
match client
|
||||
.get_events_of(vec![filter], EventSource::Database)
|
||||
.await
|
||||
{
|
||||
Ok(events) => {
|
||||
let data: Vec<String> = events.iter().map(|ev| ev.as_json()).collect();
|
||||
Ok(data)
|
||||
}
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn set_interest(
|
||||
title: String,
|
||||
description: Option<String>,
|
||||
image: Option<String>,
|
||||
hashtags: Vec<String>,
|
||||
state: State<'_, Nostr>,
|
||||
handle: tauri::AppHandle,
|
||||
) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let label = title.to_lowercase().replace(" ", "-");
|
||||
let mut tags: Vec<Tag> = vec![Tag::title(title)];
|
||||
|
||||
if let Some(desc) = description {
|
||||
tags.push(Tag::description(desc))
|
||||
};
|
||||
|
||||
if let Some(img) = image {
|
||||
let url = UncheckedUrl::new(img);
|
||||
tags.push(Tag::image(url, None));
|
||||
}
|
||||
|
||||
let builder = EventBuilder::interest_set(label, hashtags.clone()).add_tags(tags);
|
||||
|
||||
match client.send_event_builder(builder).await {
|
||||
Ok(report) => {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let state = handle.state::<Nostr>();
|
||||
let client = &state.client;
|
||||
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::TextNote, Kind::Repost])
|
||||
.hashtags(hashtags)
|
||||
.limit(500);
|
||||
|
||||
if let Ok(report) = client.reconcile(filter, NegentropyOptions::default()).await {
|
||||
println!("Received: {}", report.received.len());
|
||||
handle.emit("synchronized", ()).unwrap();
|
||||
};
|
||||
});
|
||||
|
||||
Ok(report.id().to_hex())
|
||||
}
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_interest(id: String, state: State<'_, Nostr>) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let event_id = EventId::from_str(&id).map_err(|e| e.to_string())?;
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::Interests, Kind::InterestSet])
|
||||
.id(event_id);
|
||||
|
||||
match client
|
||||
.get_events_of(
|
||||
vec![filter],
|
||||
EventSource::both(Some(Duration::from_secs(5))),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(events) => match get_latest_event(&events) {
|
||||
Some(ev) => Ok(ev.as_json()),
|
||||
None => Err("Not found.".to_string()),
|
||||
},
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_all_interests(state: State<'_, Nostr>) -> Result<Vec<String>, String> {
|
||||
let client = &state.client;
|
||||
let signer = client.signer().await.map_err(|e| e.to_string())?;
|
||||
let public_key = signer.public_key().await.map_err(|e| e.to_string())?;
|
||||
let filter = Filter::new()
|
||||
.kinds(vec![Kind::InterestSet, Kind::Interests])
|
||||
.author(public_key);
|
||||
|
||||
match client
|
||||
.get_events_of(vec![filter], EventSource::Database)
|
||||
.await
|
||||
{
|
||||
Ok(events) => {
|
||||
let data: Vec<String> = events.iter().map(|ev| ev.as_json()).collect();
|
||||
Ok(data)
|
||||
}
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_mention_list(state: State<'_, Nostr>) -> Result<Vec<Mention>, String> {
|
||||
@@ -230,61 +414,6 @@ pub async fn get_mention_list(state: State<'_, Nostr>) -> Result<Vec<Mention>, S
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn set_lume_store(
|
||||
key: String,
|
||||
content: String,
|
||||
state: State<'_, Nostr>,
|
||||
) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let signer = client.signer().await.map_err(|e| e.to_string())?;
|
||||
let public_key = signer.public_key().await.map_err(|e| e.to_string())?;
|
||||
|
||||
let encrypted = signer
|
||||
.nip44_encrypt(&public_key, content)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let tag = Tag::identifier(key);
|
||||
let builder = EventBuilder::new(Kind::ApplicationSpecificData, encrypted, vec![tag]);
|
||||
|
||||
match client.send_event_builder(builder).await {
|
||||
Ok(event_id) => Ok(event_id.to_string()),
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_lume_store(key: String, state: State<'_, Nostr>) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let signer = client.signer().await.map_err(|e| e.to_string())?;
|
||||
let public_key = signer.public_key().await.map_err(|e| e.to_string())?;
|
||||
|
||||
let filter = Filter::new()
|
||||
.author(public_key)
|
||||
.kind(Kind::ApplicationSpecificData)
|
||||
.identifier(key)
|
||||
.limit(10);
|
||||
|
||||
match client
|
||||
.get_events_of(vec![filter], EventSource::Database)
|
||||
.await
|
||||
{
|
||||
Ok(events) => {
|
||||
if let Some(event) = get_latest_event(&events) {
|
||||
match signer.nip44_decrypt(&public_key, &event.content).await {
|
||||
Ok(decrypted) => Ok(decrypted),
|
||||
Err(_) => Err(event.content.to_string()),
|
||||
}
|
||||
} else {
|
||||
Err("Not found".into())
|
||||
}
|
||||
}
|
||||
Err(err) => Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn set_wallet(uri: &str, state: State<'_, Nostr>) -> Result<bool, String> {
|
||||
|
||||
@@ -113,15 +113,15 @@ pub fn reload_column(label: String, app_handle: tauri::AppHandle) -> Result<bool
|
||||
#[specta::specta]
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), String> {
|
||||
if let Some(window) = app_handle.get_window(&window.label) {
|
||||
if window.is_visible().unwrap_or_default() {
|
||||
let _ = window.set_focus();
|
||||
if let Some(current_window) = app_handle.get_window(&window.label) {
|
||||
if current_window.is_visible().unwrap_or_default() {
|
||||
let _ = current_window.set_focus();
|
||||
} else {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
let _ = current_window.show();
|
||||
let _ = current_window.set_focus();
|
||||
};
|
||||
} else {
|
||||
let window = WebviewWindowBuilder::new(
|
||||
let new_window = WebviewWindowBuilder::new(
|
||||
&app_handle,
|
||||
&window.label,
|
||||
WebviewUrl::App(PathBuf::from(window.url)),
|
||||
@@ -129,7 +129,7 @@ pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), S
|
||||
.title(&window.title)
|
||||
.min_inner_size(window.width, window.height)
|
||||
.inner_size(window.width, window.height)
|
||||
.hidden_title(true)
|
||||
.hidden_title(window.hidden_title)
|
||||
.title_bar_style(TitleBarStyle::Overlay)
|
||||
.minimizable(window.minimizable)
|
||||
.maximizable(window.maximizable)
|
||||
@@ -144,7 +144,7 @@ pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), S
|
||||
.unwrap();
|
||||
|
||||
// Restore native border
|
||||
window.add_border(None);
|
||||
new_window.add_border(None);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -217,7 +217,7 @@ pub fn reopen_lume(app: tauri::AppHandle) {
|
||||
|
||||
// Set a custom inset to the traffic lights
|
||||
#[cfg(target_os = "macos")]
|
||||
window.set_traffic_lights_inset(7.0, 13.0).unwrap();
|
||||
window.set_traffic_lights_inset(7.0, 10.0).unwrap();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let win = window.clone();
|
||||
@@ -225,7 +225,7 @@ pub fn reopen_lume(app: tauri::AppHandle) {
|
||||
#[cfg(target_os = "macos")]
|
||||
window.on_window_event(move |event| {
|
||||
if let tauri::WindowEvent::ThemeChanged(_) = event {
|
||||
win.set_traffic_lights_inset(7.0, 13.0).unwrap();
|
||||
win.set_traffic_lights_inset(7.0, 10.0).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,14 +52,23 @@ pub fn filter_converstation(events: Vec<Event>) -> Vec<Event> {
|
||||
events
|
||||
.into_iter()
|
||||
.filter_map(|ev| {
|
||||
let tags = ev.get_tags_content(TagKind::SingleLetter(SingleLetterTag::lowercase(
|
||||
Alphabet::E,
|
||||
)));
|
||||
if ev.kind == Kind::TextNote {
|
||||
let tags: Vec<&str> = ev
|
||||
.tags
|
||||
.iter()
|
||||
.filter(|t| {
|
||||
t.kind() == TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E))
|
||||
})
|
||||
.filter_map(|t| t.content())
|
||||
.collect();
|
||||
|
||||
if tags.is_empty() {
|
||||
Some(ev)
|
||||
if tags.is_empty() {
|
||||
Some(ev)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
Some(ev)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Event>>()
|
||||
|
||||
@@ -116,8 +116,12 @@ fn main() {
|
||||
check_contact,
|
||||
toggle_contact,
|
||||
get_mention_list,
|
||||
get_lume_store,
|
||||
set_lume_store,
|
||||
set_group,
|
||||
get_group,
|
||||
get_all_groups,
|
||||
set_interest,
|
||||
get_interest,
|
||||
get_all_interests,
|
||||
set_wallet,
|
||||
load_wallet,
|
||||
remove_wallet,
|
||||
@@ -134,11 +138,12 @@ fn main() {
|
||||
get_event_from,
|
||||
get_replies,
|
||||
subscribe_to,
|
||||
get_events_by,
|
||||
get_all_events_by_author,
|
||||
get_all_events_by_authors,
|
||||
get_all_events_by_hashtags,
|
||||
get_local_events,
|
||||
get_group_events,
|
||||
get_global_events,
|
||||
get_hashtag_events,
|
||||
is_deleted_event,
|
||||
search,
|
||||
publish,
|
||||
reply,
|
||||
|
||||
Reference in New Issue
Block a user