Add bitcoin connect (#215)
* feat: add bitcoin connect * feat: improve zap screen
This commit is contained in:
@@ -3,6 +3,8 @@ use std::str::FromStr;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use cocoa::{appkit::NSApp, base::nil, foundation::NSString};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use tauri::{LogicalPosition, LogicalSize, Manager, State, WebviewUrl};
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::TitleBarStyle;
|
||||
@@ -14,6 +16,17 @@ use url::Url;
|
||||
|
||||
use crate::Nostr;
|
||||
|
||||
#[derive(Serialize, Deserialize, Type)]
|
||||
pub struct Window {
|
||||
label: String,
|
||||
title: String,
|
||||
url: String,
|
||||
width: f64,
|
||||
height: f64,
|
||||
maximizable: bool,
|
||||
minimizable: bool,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn create_column(
|
||||
@@ -121,15 +134,8 @@ pub fn resize_column(
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn open_window(
|
||||
label: &str,
|
||||
title: &str,
|
||||
url: &str,
|
||||
width: f64,
|
||||
height: f64,
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> Result<(), String> {
|
||||
if let Some(window) = app_handle.get_window(label) {
|
||||
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();
|
||||
} else {
|
||||
@@ -138,21 +144,27 @@ pub fn open_window(
|
||||
};
|
||||
} else {
|
||||
#[cfg(target_os = "macos")]
|
||||
let window = WebviewWindowBuilder::new(&app_handle, label, WebviewUrl::App(PathBuf::from(url)))
|
||||
.title(title)
|
||||
.min_inner_size(width, height)
|
||||
.inner_size(width, height)
|
||||
.hidden_title(true)
|
||||
.title_bar_style(TitleBarStyle::Overlay)
|
||||
.transparent(true)
|
||||
.effects(WindowEffectsConfig {
|
||||
state: None,
|
||||
effects: vec![Effect::WindowBackground],
|
||||
radius: None,
|
||||
color: None,
|
||||
})
|
||||
.build()
|
||||
.unwrap();
|
||||
let window = WebviewWindowBuilder::new(
|
||||
&app_handle,
|
||||
&window.label,
|
||||
WebviewUrl::App(PathBuf::from(window.url)),
|
||||
)
|
||||
.title(&window.title)
|
||||
.min_inner_size(window.width, window.height)
|
||||
.inner_size(window.width, window.height)
|
||||
.hidden_title(true)
|
||||
.title_bar_style(TitleBarStyle::Overlay)
|
||||
.transparent(true)
|
||||
.minimizable(window.minimizable)
|
||||
.maximizable(window.maximizable)
|
||||
.effects(WindowEffectsConfig {
|
||||
state: None,
|
||||
effects: vec![Effect::WindowBackground],
|
||||
radius: None,
|
||||
color: None,
|
||||
})
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let window = WebviewWindowBuilder::new(&app_handle, label, WebviewUrl::App(PathBuf::from(url)))
|
||||
@@ -180,10 +192,6 @@ pub fn open_window(
|
||||
#[cfg(target_os = "windows")]
|
||||
// Create a custom titlebar for Windows
|
||||
window.create_overlay_titlebar().unwrap();
|
||||
|
||||
// Set a custom inset to the traffic lights
|
||||
#[cfg(target_os = "macos")]
|
||||
window.set_traffic_lights_inset(8.0, 16.0).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -9,20 +9,20 @@ extern crate cocoa;
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, BufRead},
|
||||
str::FromStr,
|
||||
};
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use tauri::{Manager, path::BaseDirectory};
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::tray::{MouseButtonState, TrayIconEvent};
|
||||
use tauri::{path::BaseDirectory, Manager};
|
||||
use tauri_nspanel::ManagerExt;
|
||||
use tauri_plugin_decorum::WebviewWindowExt;
|
||||
|
||||
@@ -98,9 +98,9 @@ fn main() {
|
||||
nostr::metadata::toggle_contact,
|
||||
nostr::metadata::get_nstore,
|
||||
nostr::metadata::set_nstore,
|
||||
nostr::metadata::set_nwc,
|
||||
nostr::metadata::load_nwc,
|
||||
nostr::metadata::get_balance,
|
||||
nostr::metadata::set_wallet,
|
||||
nostr::metadata::load_wallet,
|
||||
nostr::metadata::remove_wallet,
|
||||
nostr::metadata::zap_profile,
|
||||
nostr::metadata::zap_event,
|
||||
nostr::metadata::friend_to_friend,
|
||||
|
||||
@@ -310,12 +310,12 @@ pub async fn get_nstore(key: &str, state: State<'_, Nostr>) -> Result<String, St
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn set_nwc(uri: &str, state: State<'_, Nostr>) -> Result<bool, String> {
|
||||
pub async fn set_wallet(uri: &str, state: State<'_, Nostr>) -> Result<bool, String> {
|
||||
let client = &state.client;
|
||||
|
||||
if let Ok(nwc_uri) = NostrWalletConnectURI::from_str(uri) {
|
||||
let nwc = NWC::new(nwc_uri);
|
||||
let keyring = Entry::new("Lume Secret Storage", "NWC").map_err(|e| e.to_string())?;
|
||||
let keyring = Entry::new("Lume Secret", "Bitcoin Connect").map_err(|e| e.to_string())?;
|
||||
keyring.set_password(uri).map_err(|e| e.to_string())?;
|
||||
client.set_zapper(nwc).await;
|
||||
|
||||
@@ -327,38 +327,42 @@ pub async fn set_nwc(uri: &str, state: State<'_, Nostr>) -> Result<bool, String>
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn load_nwc(state: State<'_, Nostr>) -> Result<bool, String> {
|
||||
pub async fn load_wallet(state: State<'_, Nostr>) -> Result<String, String> {
|
||||
let client = &state.client;
|
||||
let keyring = Entry::new("Lume Secret Storage", "NWC").map_err(|e| e.to_string())?;
|
||||
let keyring = Entry::new("Lume Secret", "Bitcoin Connect").unwrap();
|
||||
|
||||
match keyring.get_password() {
|
||||
Ok(val) => {
|
||||
let uri = NostrWalletConnectURI::from_str(&val).map_err(|e| e.to_string())?;
|
||||
let uri = NostrWalletConnectURI::from_str(&val).unwrap();
|
||||
let nwc = NWC::new(uri);
|
||||
|
||||
// Get current balance
|
||||
let balance = nwc.get_balance().await;
|
||||
|
||||
// Update zapper
|
||||
client.set_zapper(nwc).await;
|
||||
|
||||
Ok(true)
|
||||
match balance {
|
||||
Ok(val) => Ok(val.to_string()),
|
||||
Err(_) => Err("Get balance failed.".into()),
|
||||
}
|
||||
}
|
||||
Err(_) => Ok(false),
|
||||
Err(_) => Err("NWC not found.".into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn get_balance() -> Result<String, String> {
|
||||
let keyring = Entry::new("Lume Secret Storage", "NWC").map_err(|e| e.to_string())?;
|
||||
pub async fn remove_wallet(state: State<'_, Nostr>) -> Result<(), ()> {
|
||||
let client = &state.client;
|
||||
let keyring = Entry::new("Lume Secret", "Bitcoin Connect").unwrap();
|
||||
|
||||
match keyring.get_password() {
|
||||
Ok(val) => {
|
||||
let uri = NostrWalletConnectURI::from_str(&val).map_err(|e| e.to_string())?;
|
||||
let nwc = NWC::new(uri);
|
||||
nwc
|
||||
.get_balance()
|
||||
.await
|
||||
.map(|balance| balance.to_string())
|
||||
.map_err(|_| "Get balance failed".into())
|
||||
match keyring.delete_password() {
|
||||
Ok(_) => {
|
||||
client.unset_zapper().await;
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err("Something wrong".into()),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,32 +375,28 @@ pub async fn zap_profile(
|
||||
state: State<'_, Nostr>,
|
||||
) -> Result<bool, String> {
|
||||
let client = &state.client;
|
||||
let public_key: Option<PublicKey> = match Nip19::from_bech32(id) {
|
||||
let public_key = match Nip19::from_bech32(id) {
|
||||
Ok(val) => match val {
|
||||
Nip19::Pubkey(key) => Some(key),
|
||||
Nip19::Profile(profile) => Some(profile.public_key),
|
||||
_ => None,
|
||||
Nip19::Pubkey(key) => key,
|
||||
Nip19::Profile(profile) => profile.public_key,
|
||||
_ => return Err("Public Key is not valid.".into()),
|
||||
},
|
||||
Err(_) => match PublicKey::from_str(id) {
|
||||
Ok(val) => Some(val),
|
||||
Err(_) => None,
|
||||
Ok(val) => val,
|
||||
Err(_) => return Err("Public Key is not valid.".into()),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(recipient) = public_key {
|
||||
let details = ZapDetails::new(ZapType::Public).message(message);
|
||||
let num = match amount.parse::<u64>() {
|
||||
Ok(val) => val,
|
||||
Err(_) => return Err("Invalid amount.".into()),
|
||||
};
|
||||
let details = ZapDetails::new(ZapType::Private).message(message);
|
||||
let num = match amount.parse::<u64>() {
|
||||
Ok(val) => val,
|
||||
Err(_) => return Err("Invalid amount.".into()),
|
||||
};
|
||||
|
||||
if client.zap(recipient, num, Some(details)).await.is_ok() {
|
||||
Ok(true)
|
||||
} else {
|
||||
Err("Zap profile failed".into())
|
||||
}
|
||||
if client.zap(public_key, num, Some(details)).await.is_ok() {
|
||||
Ok(true)
|
||||
} else {
|
||||
Err("Parse public key failed".into())
|
||||
Err("Zap profile failed".into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,32 +409,28 @@ pub async fn zap_event(
|
||||
state: State<'_, Nostr>,
|
||||
) -> Result<bool, String> {
|
||||
let client = &state.client;
|
||||
let event_id: Option<EventId> = match Nip19::from_bech32(id) {
|
||||
let event_id = match Nip19::from_bech32(id) {
|
||||
Ok(val) => match val {
|
||||
Nip19::EventId(id) => Some(id),
|
||||
Nip19::Event(event) => Some(event.event_id),
|
||||
_ => None,
|
||||
Nip19::EventId(id) => id,
|
||||
Nip19::Event(event) => event.event_id,
|
||||
_ => return Err("Event ID is invalid.".into()),
|
||||
},
|
||||
Err(_) => match EventId::from_hex(id) {
|
||||
Ok(val) => Some(val),
|
||||
Err(_) => None,
|
||||
Ok(val) => val,
|
||||
Err(_) => return Err("Event ID is invalid.".into()),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(recipient) = event_id {
|
||||
let details = ZapDetails::new(ZapType::Public).message(message);
|
||||
let num = match amount.parse::<u64>() {
|
||||
Ok(val) => val,
|
||||
Err(_) => return Err("Invalid amount.".into()),
|
||||
};
|
||||
let details = ZapDetails::new(ZapType::Private).message(message);
|
||||
let num = match amount.parse::<u64>() {
|
||||
Ok(val) => val,
|
||||
Err(_) => return Err("Invalid amount.".into()),
|
||||
};
|
||||
|
||||
if client.zap(recipient, num, Some(details)).await.is_ok() {
|
||||
Ok(true)
|
||||
} else {
|
||||
Err("Zap event failed".into())
|
||||
}
|
||||
if client.zap(event_id, num, Some(details)).await.is_ok() {
|
||||
Ok(true)
|
||||
} else {
|
||||
Err("Parse event ID failed".into())
|
||||
Err("Zap event failed".into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user