Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9365bd0
liana-ui: fix primary text color
pythcoiner May 28, 2026
1419e58
liana-ui: move receive address card into component::receive
pythcoiner May 28, 2026
b84ae45
liana-ui: move previous addresses header into component::receive
pythcoiner May 28, 2026
77fe49a
liana-ui: group panel components under a panels submodule
pythcoiner May 28, 2026
c4d2955
gui: use component::list::see_more in receive view
pythcoiner May 28, 2026
e02f855
liana-ui: move receive modal display into component::receive
pythcoiner May 28, 2026
7b727cc
gui: mark text as legacy in receive view
pythcoiner May 28, 2026
e8662c4
liana-ui: rework address card
pythcoiner May 28, 2026
48a6972
liana-ui: rework receive panel prev addresses header
pythcoiner May 28, 2026
5fae4ed
gui: split receive view into module with modals submodule
pythcoiner May 28, 2026
ca470dd
liana-ui: move receive modal to their own submodule
pythcoiner May 28, 2026
9f61680
liana-ui: rework modal::modal_view()
pythcoiner May 28, 2026
6e1da42
liana-ui: implem receive::modal::show_address_modal()
pythcoiner May 28, 2026
e94e733
liana-ui: rework form
pythcoiner May 29, 2026
732643f
liana-ui: add edit_label_modal() component
pythcoiner May 29, 2026
7352f8f
debugger: aggregate all #[cfg(feature = "debugger")] code
pythcoiner May 19, 2026
5549f17
gui: open edit label modal from address card
pythcoiner Jun 3, 2026
64d5281
gui: rework receive flow with label-first address generation
pythcoiner Jun 3, 2026
a6ae00a
gui: fix receive view and release shasums
ws-plaude Jun 5, 2026
34ac5e9
Business installer Signet switch
ws-plaude Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion contrib/release/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ else
)
fi

find "$RELEASE_DIR" -type f ! -name "$LIANA_PREFIX-shasums.txt" -exec sha256sum {} + | sed "s|$RELEASE_DIR/||" | tee "$RELEASE_DIR/$LIANA_PREFIX-shasums.txt"
find "$RELEASE_DIR" -type f ! -name "$LIANA_PREFIX-shasums.txt" -exec sha256sum {} + \
| sed "s|$RELEASE_DIR/||" \
| LC_ALL=C sort -k2,2 \
| tee "$RELEASE_DIR/$LIANA_PREFIX-shasums.txt"

set +ex
2 changes: 1 addition & 1 deletion liana-business/FLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ State::update() (state/update.rs)
| | LoginResendToken, LoginSendAuthCode, Logout |
+---------------------+----------------------------------------------------------+
| Account Select | AccountSelectConnect, AccountSelectDelete, |
| | AccountSelectNewEmail |
| | AccountSelectNewEmail, AccountSelectSwitchSignet |
+---------------------+----------------------------------------------------------+
| Org Management | OrgSelected, OrgWalletSelected, OrgCreateNewWallet, |
| | OrgSelectUpdateSearchFilter |
Expand Down
202 changes: 202 additions & 0 deletions liana-business/business-installer/src/debug/descriptor_registration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
//! Final wallet-descriptor registration step: `registration_view` plus
//! the per-device registration modal.

use std::sync::OnceLock;

use async_hwi::DeviceKind;
use iced::widget::Column;
use liana_gui::debug::{installer_chrome, installer_with_modal, DebugMessage, DebugPageEntry};
use liana_ui::widget::Element;
use miniscript::bitcoin::bip32::Fingerprint;

use crate::state::{
views::registration::{RegistrationModalState, RegistrationModalStep},
State, View,
};
use crate::views::registration::{
modal::registration_modal_view, registration_key_entry, registration_view_with_cards,
};
use crate::views::registration_view;

use super::{build_state, StateCell};

const REGISTRATION_PATH: &str = "business_installer::views::registration::registration_view";
const REGISTRATION_MODAL_PATH: &str =
"business_installer::views::registration::modal::registration_modal_view";

pub static ENTRY_REGISTRATION: DebugPageEntry = DebugPageEntry {
view: render_registration,
};
pub static ENTRY_REGISTRATION_WITH_DEVICES: DebugPageEntry = DebugPageEntry {
view: render_registration_with_devices,
};
pub static ENTRY_REGISTRATION_MODAL_REGISTERING: DebugPageEntry = DebugPageEntry {
view: render_registration_modal_registering,
};
pub static ENTRY_REGISTRATION_MODAL_CONFIRM_COLDCARD: DebugPageEntry = DebugPageEntry {
view: render_registration_modal_confirm_coldcard,
};
pub static ENTRY_REGISTRATION_MODAL_ERROR: DebugPageEntry = DebugPageEntry {
view: render_registration_modal_error,
};

// ---- registration view --------------------------------------------------

fn shared_state() -> &'static State {
static S: OnceLock<StateCell<State>> = OnceLock::new();
&S.get_or_init(|| StateCell(build_state(|_| {}))).0
}

fn render_registration() -> Element<'static, DebugMessage> {
let body = registration_view(shared_state()).map(|_| ());
installer_chrome("Business installer: registration", REGISTRATION_PATH, body)
}

fn registration_with_devices_state() -> &'static State {
static S: OnceLock<StateCell<State>> = OnceLock::new();
&S.get_or_init(|| {
StateCell(build_state(|s| {
s.current_view = View::Registration;
}))
})
.0
}

fn render_registration_with_devices() -> Element<'static, DebugMessage> {
let variants: Vec<Element<'_, crate::state::message::Msg>> = vec![
// kind = Some, alias filled: clickable "Register" with kind+fingerprint.
registration_key_entry(
Fingerprint::from([0xAA; 4]),
Some(DeviceKind::BitBox02),
true,
"Alice".to_string(),
),
// kind = Some, alias empty: clickable "Register" with only kind+fingerprint.
registration_key_entry(
Fingerprint::from([0xBB; 4]),
Some(DeviceKind::Ledger),
true,
String::new(),
),
// kind = None, connected = true, alias filled: "Device not supported or locked".
registration_key_entry(
Fingerprint::from([0xCC; 4]),
None,
true,
"Bobjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj".to_string(),
),
// kind = None, connected = true, alias empty.
registration_key_entry(Fingerprint::from([0xDD; 4]), None, true, String::new()),
// kind = None, connected = false, alias filled: "Connect the associated device to register".
registration_key_entry(
Fingerprint::from([0xEE; 4]),
None,
false,
"Carol".to_string(),
),
// kind = None, connected = false, alias empty.
registration_key_entry(Fingerprint::from([0xFF; 4]), None, false, String::new()),
];
let cards: Element<'_, crate::state::message::Msg> = Column::with_children(variants)
.spacing(10)
.padding([0, 20])
.into();
let body =
registration_view_with_cards(registration_with_devices_state(), cards, true).map(|_| ());
installer_chrome(
"Business installer: registration (all key_card variants)",
REGISTRATION_PATH,
body,
)
}

// ---- registration modal -------------------------------------------------

fn registration_modal_state_with(step: RegistrationModalStep, error: Option<String>) -> State {
build_state(|s| {
s.current_view = View::Registration;
s.views.registration.modal = Some(RegistrationModalState {
fingerprint: Fingerprint::from([0xAA; 4]),
device_kind: Some(async_hwi::DeviceKind::Ledger),
step,
error,
});
})
}

fn registration_modal_registering_state() -> &'static State {
static S: OnceLock<StateCell<State>> = OnceLock::new();
&S.get_or_init(|| {
StateCell(registration_modal_state_with(
RegistrationModalStep::Registering,
None,
))
})
.0
}

fn registration_modal_error_state() -> &'static State {
static S: OnceLock<StateCell<State>> = OnceLock::new();
&S.get_or_init(|| {
StateCell(registration_modal_state_with(
RegistrationModalStep::Error,
Some("Could not reach the device. Reconnect and retry.".to_string()),
))
})
.0
}

fn registration_modal_confirm_coldcard_state() -> &'static State {
static S: OnceLock<StateCell<State>> = OnceLock::new();
&S.get_or_init(|| {
StateCell(build_state(|s| {
s.current_view = View::Registration;
// Coldcard doesn't ack the registration on the wire, so the
// wizard pops a dedicated Yes/No confirmation step instead of
// moving straight to "Registered".
s.views.registration.modal = Some(RegistrationModalState {
fingerprint: Fingerprint::from([0xCC; 4]),
device_kind: Some(async_hwi::DeviceKind::Coldcard),
step: RegistrationModalStep::ConfirmColdcard {
hmac: None,
wallet_name: "Acme treasury".to_string(),
},
error: None,
});
}))
})
.0
}

fn render_registration_modal_registering() -> Element<'static, DebugMessage> {
let body = registration_modal_view(registration_modal_registering_state())
.expect("modal state set")
.map(|_| ());
installer_with_modal(
"Business installer: registration modal (registering)",
REGISTRATION_MODAL_PATH,
body,
)
}

fn render_registration_modal_error() -> Element<'static, DebugMessage> {
let body = registration_modal_view(registration_modal_error_state())
.expect("modal state set")
.map(|_| ());
installer_with_modal(
"Business installer: registration modal (error)",
REGISTRATION_MODAL_PATH,
body,
)
}

fn render_registration_modal_confirm_coldcard() -> Element<'static, DebugMessage> {
let body = registration_modal_view(registration_modal_confirm_coldcard_state())
.expect("modal state set")
.map(|_| ());
installer_with_modal(
"Business installer: registration modal (Coldcard Yes/No confirmation)",
REGISTRATION_MODAL_PATH,
body,
)
}
Loading