Files
ObsidianDragon/src/ui/windows/contact_picker.h
DanS ddd53dc006 feat(send): add a shared contact picker to the recipient field
Phase 0c. The Send recipient field only offered Paste and a tx-history
suggestion list — no way to pick a saved contact. Add a compact contacts
icon button next to Paste that opens a picker popup over the App-owned
address book; selecting a contact fills the recipient (re-validated next
frame).

- New header-only src/ui/windows/contact_picker.h: ContactPickerPopup(id,
  book, outBuf, outSz) — a reusable popup listing contacts ([Z]/[T] tag +
  label + short address) that copies the chosen address into the caller's
  buffer. Kept out of the material layer so the design-system headers stay
  free of data/ deps; reserved for Chat "new conversation" later.
- send_tab: shrink the recipient input to make room for the icon button
  (ICON_MD_CONTACTS), wire the popup to app->addressBook(). Coexists with the
  existing Paste + suggestion affordances.
- i18n: send_contacts_button tooltip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 17:04:58 -05:00

66 lines
2.3 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include "imgui.h"
#include "../../data/address_book.h"
#include "../../util/i18n.h"
#include "../../util/text_format.h"
#include <cstring>
#include <string>
namespace dragonx {
namespace ui {
/**
* @brief Shared address-book contact picker popup.
*
* Header-only so Send (and later Chat "new conversation") can reuse it without
* pulling the address book into the material layer. Trigger with
* ImGui::OpenPopup(id) from a button, then call this every frame. On selection
* it copies the chosen contact's address into outBuf (NUL-terminated) and
* returns true.
*
* @param id Popup id (must match the ImGui::OpenPopup(id) call).
* @param book The App-owned address book (app->addressBook()).
* @param outBuf Destination buffer for the chosen address.
* @param outSz Size of outBuf.
* @return true on the frame a contact is picked.
*/
inline bool ContactPickerPopup(const char* id, const data::AddressBook& book,
char* outBuf, size_t outSz)
{
bool picked = false;
if (ImGui::BeginPopup(id)) {
if (book.empty()) {
ImGui::TextDisabled("%s", TR("address_book_empty"));
} else {
const auto& entries = book.entries();
for (size_t i = 0; i < entries.size(); ++i) {
const auto& e = entries[i];
ImGui::PushID(static_cast<int>(i));
// [Z]/[T] type marker (derived from the address) + label + short address.
const char* tag = (!e.address.empty() && e.address[0] == 'z') ? "[Z] " : "[T] ";
std::string shortAddr = util::truncateMiddle(e.address, 24);
std::string row = std::string(tag) + e.label + " " + shortAddr;
if (ImGui::Selectable(row.c_str())) {
if (outSz > 0) {
std::strncpy(outBuf, e.address.c_str(), outSz - 1);
outBuf[outSz - 1] = '\0';
}
picked = true;
ImGui::CloseCurrentPopup();
}
ImGui::PopID();
}
}
ImGui::EndPopup();
}
return picked;
}
} // namespace ui
} // namespace dragonx