feat: add support for cashu wallet (nip60 & nip61) (#71)
Reviewed-on: #71
This commit was merged in pull request #71.
This commit is contained in:
@@ -9,11 +9,11 @@ ui = { path = "../ui" }
|
||||
gpui-kit.workspace = true
|
||||
common = { path = "../common" }
|
||||
state = { path = "../state" }
|
||||
nip29 = { path = "../nip29" }
|
||||
nip29_ui = { path = "../nip29_ui" }
|
||||
nip4e = { path = "../nip4e" }
|
||||
chat = { path = "../chat" }
|
||||
chat_ui = { path = "../chat_ui" }
|
||||
nip29 = { path = "../nip29" }
|
||||
nip29_ui = { path = "../nip29_ui" }
|
||||
settings = { path = "../settings" }
|
||||
speech = { path = "../speech" }
|
||||
person = { path = "../person" }
|
||||
@@ -30,3 +30,7 @@ serde.workspace = true
|
||||
log.workspace = true
|
||||
rand.workspace = true
|
||||
smallvec.workspace = true
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
nip60 = { path = "../nip60" }
|
||||
nip60_ui = { path = "../nip60_ui" }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{App, AppContext, ClickEvent, ParentElement, Styled, Window, px};
|
||||
use gpui_kit::assets::IconName;
|
||||
use gpui_kit::component::button::{Button, ButtonVariants};
|
||||
use gpui_kit::component::input::{Input, InputState};
|
||||
use gpui_kit::component::notification::Notification;
|
||||
use gpui_kit::component::{WindowExt, h_flex, v_flex};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{App, AppContext, ClickEvent, ParentElement, Styled, Window, px};
|
||||
use nip29::{GroupReference, GroupsRegistry};
|
||||
|
||||
type SubmitFn = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) -> bool>;
|
||||
|
||||
@@ -41,6 +41,8 @@ pub struct Preferences {
|
||||
radius_input: Entity<InputState>,
|
||||
radius_lg_input: Entity<InputState>,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mint_input: Entity<InputState>,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
_speech_poll: Task<()>,
|
||||
/// Keeps the control subscriptions alive while the dialog is open
|
||||
_subscriptions: Vec<Subscription>,
|
||||
@@ -90,6 +92,9 @@ impl Preferences {
|
||||
.max(RADIUS_RANGE.1 as f64)
|
||||
});
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let mint_input = cx.new(|cx| InputState::new(window, cx).placeholder("https://mint…"));
|
||||
|
||||
let subscriptions = vec![
|
||||
Self::wire_number_input(&font_size_input, FONT_SIZE_RANGE, cx, |value, cx| {
|
||||
if let Some(font_size) = value {
|
||||
@@ -140,6 +145,8 @@ impl Preferences {
|
||||
radius_input,
|
||||
radius_lg_input,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mint_input,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
_speech_poll,
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
@@ -185,6 +192,56 @@ impl Preferences {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the NIP-60 wallet or add a mint to it
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn setup_mint(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let mint = self.mint_input.read(cx).value().trim().to_string();
|
||||
if mint.is_empty() {
|
||||
window.push_notification(Notification::error("Enter a mint URL"), cx);
|
||||
return;
|
||||
}
|
||||
|
||||
nip60::WalletRegistry::global(cx).update(cx, |registry, cx| {
|
||||
registry.setup(mint, cx);
|
||||
});
|
||||
}
|
||||
|
||||
/// The NIP-60 wallet block; absent from the web build
|
||||
fn render_wallet(
|
||||
&mut self,
|
||||
#[cfg_attr(target_arch = "wasm32", allow(unused_variables))] cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
return div().into_any_element();
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
const WALLET: &str = "Cashu mint for your NIP-60 wallet. Set up creates the wallet, Add extends it with another mint.";
|
||||
let has_wallet = nip60::WalletRegistry::global(cx).read(cx).has_wallet();
|
||||
|
||||
settings_block(
|
||||
"Wallet",
|
||||
WALLET,
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(div().flex_1().child(Input::new(&self.mint_input)))
|
||||
.child(
|
||||
Button::new("setup-mint")
|
||||
.label(if has_wallet { "Add" } else { "Set up" })
|
||||
.ghost()
|
||||
.size_8()
|
||||
.on_click(cx.listener(move |this, _ev, window, cx| {
|
||||
this.setup_mint(window, cx);
|
||||
})),
|
||||
),
|
||||
cx,
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the appearance mode (system, light or dark)
|
||||
fn set_appearance_mode(mode: AppearanceMode, cx: &mut App) {
|
||||
AppSettings::update_appearance_mode(mode, cx);
|
||||
@@ -546,5 +603,7 @@ impl Render for Preferences {
|
||||
),
|
||||
cx,
|
||||
))
|
||||
.child(Separator::horizontal())
|
||||
.child(self.render_wallet(cx))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ enum Command {
|
||||
ShowContactList,
|
||||
ShowInbox,
|
||||
ShowBrowse,
|
||||
ShowWallet,
|
||||
NewChat,
|
||||
NewGroup,
|
||||
JoinGroup,
|
||||
@@ -196,6 +197,30 @@ impl Workspace {
|
||||
}),
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let wallet = ::nip60::WalletRegistry::global(cx);
|
||||
|
||||
subscriptions.push(cx.subscribe_in(
|
||||
&wallet,
|
||||
window,
|
||||
|_this, _wallet, event, window, cx| match event {
|
||||
::nip60::WalletEvent::Error(error) => {
|
||||
window.push_notification(Notification::error(error.clone()), cx);
|
||||
}
|
||||
::nip60::WalletEvent::TokenCreated(_) => {
|
||||
window.push_notification(
|
||||
Notification::new()
|
||||
.message("Cashu token created, copy it from the wallet panel")
|
||||
.with_type(NotificationType::Success),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
::nip60::WalletEvent::Updated => {}
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
App::on_action(cx, {
|
||||
let workspace = workspace.clone();
|
||||
move |action, cx| {
|
||||
@@ -307,6 +332,13 @@ impl Workspace {
|
||||
let panel = browse::init(self.dock.downgrade(), window, cx);
|
||||
self.add_panel_to_dock(panel, DockPlacement::Center, window, cx);
|
||||
}
|
||||
Command::ShowWallet => {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let panel = nip60_ui::wallet::init(window, cx);
|
||||
self.add_panel_to_dock(panel, DockPlacement::Center, window, cx);
|
||||
}
|
||||
}
|
||||
Command::NewChat => {
|
||||
new_chat::open(self.dock.downgrade(), window, cx);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ impl Sidebar {
|
||||
let requests_open = self.requests_open;
|
||||
let contacts_open = self.contacts_open && !requests_open;
|
||||
let rows = Rc::new(self.rows_for(cx));
|
||||
|
||||
// Whether to show the create button for the contact picker
|
||||
let create_button = contacts_open && !self.selected_contacts.is_empty();
|
||||
|
||||
@@ -60,12 +59,25 @@ impl Sidebar {
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.relative()
|
||||
.border_l_1()
|
||||
.border_color(cx.theme().sidebar_border)
|
||||
.child(
|
||||
v_flex()
|
||||
.px_2()
|
||||
.gap_1()
|
||||
.items_start()
|
||||
.justify_start()
|
||||
.when(cfg!(not(target_arch = "wasm32")), |this| {
|
||||
this.child(
|
||||
Nav::new("wallet")
|
||||
.prefix(Icon::new(gpui_kit::assets::IconName::Wallet).small())
|
||||
.label("Wallet")
|
||||
.text_sm()
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(Box::new(crate::Command::ShowWallet), cx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
Nav::new("browse")
|
||||
.prefix(Icon::new(IconName::Compass).small())
|
||||
|
||||
Reference in New Issue
Block a user