iuna

iuna

iuna - experimental devnet protocol
git clone https://getiuna.org/git/iuna.git
Log | Files | Refs | README | LICENSE

commit dd31eb76fe236f4b5eceb6f17e648b4abb0226eb
parent 76f148eb28456d356780b798d66821892892b6a0
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Thu, 13 Aug 2026 09:29:02 +0200

Move address book form into recipient picker

Diffstat:
Msrc/adapters/http/index_html.rs | 55++++++++++++++++++++++++++-----------------------------
Msrc/adapters/http/tests.rs | 9+++++++++
Mwww/assets/iuna-ui.js | 7++-----
3 files changed, 37 insertions(+), 34 deletions(-)

diff --git a/src/adapters/http/index_html.rs b/src/adapters/http/index_html.rs @@ -264,8 +264,8 @@ pub(super) const INDEX_HTML: &str = r#"<!doctype html> .address-book-name { font-weight: 800; color: #eef6f8; overflow-wrap: anywhere; } .address-book-actions { display: flex; gap: 6px; align-items: center; } .address-book-modal { width: min(560px, 100%); } - .address-book-modal form { width: 100%; } - .address-book-modal form label { flex: 1 0 100%; } + .address-book-modal form { width: 100%; display: grid; gap: 10px; } + .address-book-form { display: grid; gap: 10px; margin-top: 14px; padding-top: 14px; border-top: 1px solid #2f363c; } .address-book-modal-actions { flex: 1 0 100%; width: 100%; display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; } .address-book-picker-list { display: grid; gap: 8px; } .address-book-picker-row { width: 100%; display: grid; gap: 3px; text-align: left; border: 1px solid #2f363c; border-radius: 8px; padding: 10px; background: #111316; color: inherit; } @@ -1378,17 +1378,32 @@ pub(super) const INDEX_HTML: &str = r#"<!doctype html> </div> </section> </div> - <div class="setup-overlay transaction-overlay" x-show="addressBookModalOpen" x-transition.opacity @click.self="closeAddressBookModal()" role="dialog" aria-modal="true" aria-label="Address Book"> + <div class="setup-overlay transaction-overlay" x-show="addressBookPickerOpen" x-transition.opacity @click.self="closeAddressBookPicker()" role="dialog" aria-modal="true" aria-labelledby="address-book-picker-title"> <section class="tx-modal address-book-modal"> <div class="tx-modal-head"> <div class="tx-modal-title"> - <span class="pill">Address Book</span> + <span class="pill">Send</span> + <h2 id="address-book-picker-title">Choose Contact</h2> + </div> + <div class="address-book-actions"> + <button class="icon-button" type="button" x-show="!addressBookModalOpen" @click="openAddressBookModal()" title="Add contact" aria-label="Add contact"> + <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14"></path><path d="M5 12h14"></path></svg> + </button> + <button class="icon-button" type="button" @click="closeAddressBookPicker()" title="Close" aria-label="Close"> + <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg> + </button> </div> - <button class="icon-button" type="button" @click="closeAddressBookModal()" title="Close" aria-label="Close"> - <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg> - </button> </div> - <form @submit.prevent="saveAddressBookEntry"> + <div class="address-book-picker-list" x-show="!addressBookModalOpen"> + <template x-for="entry in addressBookEntries()" :key="entry.address"> + <button class="address-book-picker-row" type="button" @click="selectTransferContact(entry.address)" :title="`Send to ${entry.name}`"> + <span class="address-book-name" x-text="entry.name"></span> + <code class="tx-value hash" x-text="short(entry.address)"></code> + </button> + </template> + <div class="tx-modal-empty" x-show="addressBookEntries().length === 0">No saved addresses</div> + </div> + <form class="address-book-form" x-show="addressBookModalOpen" @submit.prevent="saveAddressBookEntry"> <label>Name<input x-model="addressBookDraftName" autocomplete="off" required></label> <label>Address<input x-model="addressBookDraftAddress" autocomplete="off" required :class="{ invalid: addressBookDraftAddress && !validAddressBookAddress(addressBookDraftAddress) }"></label> <div class="setup-feedback error" x-show="addressBookDraftAddress && !validAddressBookAddress(addressBookDraftAddress)">Address must be a 64 character hex public key</div> @@ -1396,6 +1411,9 @@ pub(super) const INDEX_HTML: &str = r#"<!doctype html> <button class="icon-button modal-delete-button" type="button" x-show="addressBookEditingAddress" @click="removeAddressBookEntry({ address: addressBookEditingAddress, name: addressBookDraftName || addressBookEditingAddress })" title="Delete contact" aria-label="Delete contact"> <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16"></path><path d="M10 11v6"></path><path d="M14 11v6"></path><path d="M6 7l1 14h10l1-14"></path><path d="M9 7V4h6v3"></path></svg> </button> + <button class="icon-button" type="button" @click="closeAddressBookModal()" title="Back to contacts" aria-label="Back to contacts"> + <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 18l-6-6 6-6"></path></svg> + </button> <button class="primary icon-button" type="submit" title="Save contact" aria-label="Save contact"> <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z"></path><path d="M7 3v6h8"></path><path d="M7 21v-8h10v8"></path></svg> </button> @@ -1403,27 +1421,6 @@ pub(super) const INDEX_HTML: &str = r#"<!doctype html> </form> </section> </div> - <div class="setup-overlay transaction-overlay" x-show="addressBookPickerOpen" x-transition.opacity @click.self="closeAddressBookPicker()" role="dialog" aria-modal="true" aria-labelledby="address-book-picker-title"> - <section class="tx-modal address-book-modal"> - <div class="tx-modal-head"> - <div class="tx-modal-title"> - <span class="pill">Send</span> - <h2 id="address-book-picker-title">Choose Contact</h2> - </div> - <button class="icon-button" type="button" @click="closeAddressBookPicker()" title="Close" aria-label="Close"> - <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg> - </button> - </div> - <div class="address-book-picker-list"> - <template x-for="entry in addressBookEntries()" :key="entry.address"> - <button class="address-book-picker-row" type="button" @click="selectTransferContact(entry.address)" :title="`Send to ${entry.name}`"> - <span class="address-book-name" x-text="entry.name"></span> - <code class="tx-value hash" x-text="short(entry.address)"></code> - </button> - </template> - </div> - </section> - </div> <div class="setup-overlay" x-show="showingSetup()" x-transition.opacity role="dialog" aria-modal="true" aria-labelledby="setup-title"> <section class="setup-modal"> <div class="setup-modal-head"> diff --git a/src/adapters/http/tests.rs b/src/adapters/http/tests.rs @@ -1626,6 +1626,13 @@ fn wallet_screen_includes_address_book_alias_controls() { assert!(super::INDEX_HTML.contains("selectTransferContact(entry.address)")); assert!(super::INDEX_HTML.contains("editAddressBookEntry(entry)")); assert!( + super::INDEX_HTML + .contains("x-show=\"!addressBookModalOpen\" @click=\"openAddressBookModal()\"") + ); + assert!( + super::INDEX_HTML.contains("class=\"address-book-form\" x-show=\"addressBookModalOpen\"") + ); + assert!( super::INDEX_HTML.contains("removeAddressBookEntry({ address: addressBookEditingAddress") ); assert!(super::INDEX_HTML.contains("aria-label=\"Choose contact\"")); @@ -1641,6 +1648,8 @@ fn wallet_screen_includes_address_book_alias_controls() { assert!(app_js.contains("Address is already saved")); assert!(app_js.contains("validAddressBookAddress(address)")); assert!(app_js.contains("openAddressBookPicker()")); + assert!(app_js.contains("this.addressBookPickerOpen = true;")); + assert!(!app_js.contains("No contacts saved yet")); assert!(app_js.contains("await this.submitForm(\"/api/address-book\"")); assert!(app_js.contains("\"/api/address-book\"")); assert!(app_js.contains("addressLabel(address)")); diff --git a/www/assets/iuna-ui.js b/www/assets/iuna-ui.js @@ -2223,6 +2223,7 @@ window.iunaApp = function iunaApp() { this.addressBookEditingAddress = entry?.address || null; this.addressBookDraftAddress = entry?.address || ""; this.addressBookDraftName = entry?.name || ""; + this.addressBookPickerOpen = true; this.addressBookModalOpen = true; }, @@ -2234,15 +2235,12 @@ window.iunaApp = function iunaApp() { }, openAddressBookPicker() { - if (this.addressBookEntries().length === 0) { - this.showFlash("No contacts saved yet", "error"); - return; - } this.addressBookPickerOpen = true; }, closeAddressBookPicker() { this.addressBookPickerOpen = false; + this.closeAddressBookModal(); }, async saveAddressBookEntry() { @@ -2290,7 +2288,6 @@ window.iunaApp = function iunaApp() { this.addressBook = nextBook; this.config = { ...this.config, address_book: nextBook }; if (this.addressBookEditingAddress === entry.address) this.closeAddressBookModal(); - if (this.addressBookEntries().length === 0) this.closeAddressBookPicker(); this.showFlash(`Removed ${entry.name}`, "success"); } catch (error) { this.showFlash(error.message, "error");