feat(wallet): humanize shared wallet experience

This commit is contained in:
Ricardo Guilherme Schmidt
2026-07-17 20:18:09 -03:00
parent c0947e1917
commit 9b72b08c2d
38 changed files with 4614 additions and 272 deletions
+188 -9
View File
@@ -1,5 +1,13 @@
#include "WalletAccountModel.h"
#include "WalletAccountId.h"
#include <utility>
namespace {
const QString DEFAULT_PROGRAM_OWNER(64, QLatin1Char('0'));
}
WalletAccountModel::WalletAccountModel(QObject* parent)
: QAbstractListModel(parent)
{
@@ -21,10 +29,36 @@ QVariant WalletAccountModel::data(const QModelIndex& index, int role) const
return account.name;
case AddressRole:
return account.address;
case DisplayAddressRole:
return account.displayAddress;
case BalanceRole:
return account.balance;
case IsPublicRole:
return account.isPublic;
case KindRole:
return account.kind;
case SectionRole:
return account.section;
case ProgramOwnerRole:
return account.programOwner;
case ReadStatusRole:
return account.readStatus;
case ProgramNameRole:
return account.programName;
case AccountTypeRole:
return account.accountType;
case VisibilityRole:
return account.isPublic ? QStringLiteral("public") : QStringLiteral("private");
case ControlRole:
return QStringLiteral("wallet");
case CanBePrimaryRole:
return account.canBePrimary;
case IsPrimaryRole:
return account.isPrimary;
case DefinitionIdRole:
return account.definitionId;
case AliasRole:
return account.alias;
default:
return {};
}
@@ -37,25 +71,170 @@ QHash<int, QByteArray> WalletAccountModel::roleNames() const
{ AddressRole, "address" },
{ BalanceRole, "balance" },
{ IsPublicRole, "isPublic" },
{ KindRole, "kind" },
{ SectionRole, "section" },
{ ProgramOwnerRole, "programOwner" },
{ ReadStatusRole, "readStatus" },
{ ProgramNameRole, "programName" },
{ AccountTypeRole, "accountType" },
{ VisibilityRole, "visibility" },
{ ControlRole, "control" },
{ CanBePrimaryRole, "canBePrimary" },
{ IsPrimaryRole, "isPrimary" },
{ DefinitionIdRole, "definitionId" },
{ AliasRole, "alias" },
{ DisplayAddressRole, "displayAddress" },
};
}
void WalletAccountModel::replaceAccounts(const QVector<WalletAccount>& accounts)
void WalletAccountModel::replaceAccounts(const QVector<WalletAccount>& accounts,
const QHash<QString, QString>& aliases,
const QString& primaryAddress)
{
beginResetModel();
const qsizetype oldCount = m_accounts.size();
m_accounts.clear();
m_accounts.reserve(accounts.size());
for (qsizetype index = 0; index < accounts.size(); ++index) {
const WalletAccount& account = accounts.at(index);
m_accounts.append({
QStringLiteral("Account %1").arg(index + 1),
account.address,
account.balance,
account.isPublic,
});
for (const WalletAccount& account : accounts) {
Entry entry;
entry.alias = aliases.value(account.address);
entry.address = account.address;
entry.displayAddress = walletAccountIdToBase58(account.address);
if (entry.displayAddress.isEmpty())
entry.displayAddress = account.address;
entry.balance = account.balance;
entry.isPublic = account.isPublic;
entry.programOwner = account.programOwner;
entry.readStatus = account.readStatus;
if (!account.isPublic) {
entry.kind = QStringLiteral("private");
entry.canBePrimary = true;
} else if (account.readStatus != QStringLiteral("ok")) {
entry.kind = QStringLiteral("unknown");
} else if (account.programOwner == DEFAULT_PROGRAM_OWNER) {
entry.kind = QStringLiteral("user");
entry.canBePrimary = true;
} else {
entry.kind = QStringLiteral("program");
}
entry.section = sectionFor(entry);
entry.isPrimary = account.address == primaryAddress && entry.canBePrimary;
updateEntryName(entry);
m_accounts.append(std::move(entry));
}
endResetModel();
if (oldCount != m_accounts.size())
emit countChanged();
}
void WalletAccountModel::applyPresentations(
const QVector<WalletAccountPresentation>& presentations)
{
for (const WalletAccountPresentation& presentation : presentations) {
const int row = indexOf(presentation.address);
if (row < 0)
continue;
Entry& entry = m_accounts[row];
if (!presentation.kind.isEmpty())
entry.kind = presentation.kind;
entry.programName = presentation.programName;
entry.accountType = presentation.accountType;
entry.definitionId = presentation.definitionId;
entry.semanticName = presentation.semanticName;
entry.section = sectionFor(entry, presentation.hiddenFromAccounts);
entry.canBePrimary = entry.kind == QStringLiteral("user")
|| entry.kind == QStringLiteral("private");
if (!entry.canBePrimary)
entry.isPrimary = false;
updateEntryName(entry);
const QModelIndex changed = index(row);
emit dataChanged(changed, changed);
}
}
void WalletAccountModel::setAlias(const QString& address, const QString& alias)
{
const int row = indexOf(address);
if (row < 0)
return;
Entry& entry = m_accounts[row];
entry.alias = alias;
updateEntryName(entry);
emit dataChanged(index(row), index(row), { NameRole, AliasRole });
}
void WalletAccountModel::setPrimaryAddress(const QString& address)
{
for (int row = 0; row < m_accounts.size(); ++row) {
Entry& entry = m_accounts[row];
const bool next = entry.address == address && entry.canBePrimary;
if (entry.isPrimary == next)
continue;
entry.isPrimary = next;
emit dataChanged(index(row), index(row), { IsPrimaryRole });
}
}
bool WalletAccountModel::contains(const QString& address) const
{
return indexOf(address) >= 0;
}
bool WalletAccountModel::canBePrimary(const QString& address) const
{
const int row = indexOf(address);
return row >= 0 && m_accounts.at(row).canBePrimary;
}
QString WalletAccountModel::firstAutomaticPrimary() const
{
for (const Entry& entry : m_accounts) {
if (entry.kind == QStringLiteral("user"))
return entry.address;
}
return {};
}
int WalletAccountModel::indexOf(const QString& address) const
{
for (int row = 0; row < m_accounts.size(); ++row) {
if (m_accounts.at(row).address == address)
return row;
}
return -1;
}
QString WalletAccountModel::defaultName(const Entry& entry)
{
if (!entry.accountType.isEmpty()) {
QString name = entry.accountType;
for (qsizetype index = 1; index < name.size(); ++index) {
if (name.at(index).isUpper() && name.at(index - 1).isLower())
name.insert(index++, QLatin1Char(' '));
}
return name;
}
if (entry.kind == QStringLiteral("user"))
return QStringLiteral("User account");
if (entry.kind == QStringLiteral("private"))
return QStringLiteral("Private account");
if (entry.kind == QStringLiteral("unknown"))
return QStringLiteral("Unknown account");
return QStringLiteral("Program account");
}
QString WalletAccountModel::sectionFor(const Entry& entry, bool hiddenFromAccounts)
{
if (hiddenFromAccounts || entry.kind == QStringLiteral("token_holding"))
return QStringLiteral("hidden");
if (entry.kind == QStringLiteral("user") || entry.kind == QStringLiteral("private"))
return QStringLiteral("accounts");
return QStringLiteral("advanced");
}
void WalletAccountModel::updateEntryName(Entry& entry)
{
entry.name = !entry.alias.isEmpty()
? entry.alias
: (!entry.semanticName.isEmpty() ? entry.semanticName : defaultName(entry));
}