mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-23 06:35:13 +01:00
* Wallet Transactions Export: Add BIP-329 support * Adjust wording * Export one line per label * Join labels, fix type * Rewrite the ProcessBip329 function to be more performant * Add nullable on all TransactionsExport --------- Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
229 lines
9.6 KiB
Text
229 lines
9.6 KiB
Text
@model ListTransactionsViewModel
|
|
|
|
@{
|
|
var walletId = Context.GetRouteValue("walletId").ToString();
|
|
var labelFilter = Context.Request.Query["labelFilter"].ToString();
|
|
|
|
Layout = "../Shared/_NavLayout.cshtml";
|
|
ViewData.SetActivePage(WalletsNavPages.Transactions, $"{Model.CryptoCode} Transactions", walletId);
|
|
}
|
|
|
|
@section PageHeadContent {
|
|
<script src="~/vendor/tom-select/tom-select.complete.min.js" asp-append-version="true"></script>
|
|
<link href="~/vendor/tom-select/tom-select.bootstrap5.min.css" asp-append-version="true" rel="stylesheet">
|
|
<style>
|
|
.smMaxWidth {
|
|
max-width: 125px;
|
|
}
|
|
|
|
@@media (min-width: 990px) {
|
|
.smMaxWidth {
|
|
max-width: 200px;
|
|
}
|
|
}
|
|
|
|
/* pull actions area, so that it is besides the search form */
|
|
@@media (min-width: 1200px) {
|
|
#Filter + #Dropdowns {
|
|
margin-top: -4rem;
|
|
float: right;
|
|
}
|
|
|
|
#Filter + #Dropdowns #Actions {
|
|
order: 1;
|
|
}
|
|
}
|
|
|
|
.unconf > * {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
#LoadingIndicator {
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
</style>
|
|
}
|
|
|
|
@section PageFootContent {
|
|
@*Without async, somehow selenium do not manage to click on links in this page*@
|
|
<script src="~/modal/btcpay.js" asp-append-version="true" async></script>
|
|
@* Custom Range Modal *@
|
|
<script>
|
|
let observer = null;
|
|
let $loadMore = document.getElementById('LoadMore');
|
|
const $actions = document.getElementById('ListActions');
|
|
const $list = document.getElementById('WalletTransactionsList');
|
|
const $indicator = document.getElementById('LoadingIndicator');
|
|
|
|
delegate('click', '#selectAllCheckbox', e => {
|
|
document.querySelectorAll(".selector").forEach(checkbox => {
|
|
checkbox.checked = e.target.checked;
|
|
});
|
|
});
|
|
|
|
delegate('click', '#GoToTop', () => {
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
});
|
|
|
|
delegate('click', '#LoadMore', async () => {
|
|
$loadMore.setAttribute('disabled', 'disabled');
|
|
await loadMoreTransactions();
|
|
});
|
|
|
|
if ($actions && $actions.offsetTop - window.innerHeight > 0) {
|
|
document.getElementById('GoToTop').classList.remove('d-none');
|
|
}
|
|
|
|
const count = @Safe.Json(Model.Count);
|
|
const skipInitial = @Safe.Json(Model.Skip);
|
|
const loadMoreUrl = @Safe.Json(Url.Action("WalletTransactions", new { walletId, labelFilter, skip = Model.Skip, count = Model.Count }));
|
|
let skip = @Safe.Json(Model.Skip);
|
|
|
|
async function loadMoreTransactions() {
|
|
$indicator.classList.remove('d-none');
|
|
|
|
const skipNext = skip + count;
|
|
const url = loadMoreUrl.replace(`skip=${skipInitial}`, `skip=${skipNext}`)
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
'Accept': 'text/html',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
});
|
|
|
|
if (response.ok) {
|
|
const html = await response.text();
|
|
$list.insertAdjacentHTML('beforeend', html);
|
|
skip = skipNext;
|
|
|
|
if ($loadMore) {
|
|
// remove load more button
|
|
$loadMore.remove();
|
|
$loadMore = null;
|
|
|
|
// switch to infinite scroll mode
|
|
observer = new IntersectionObserver(async entries => {
|
|
const { isIntersecting } = entries[0];
|
|
if (isIntersecting) {
|
|
await loadMoreTransactions();
|
|
}
|
|
}, { rootMargin: '128px' });
|
|
|
|
// the actions div marks the end of the list table
|
|
observer.observe($actions);
|
|
}
|
|
|
|
if (html.trim() === '') {
|
|
// in case the response html was empty, remove the observer and stop loading
|
|
observer.unobserve($actions);
|
|
}
|
|
} else if ($loadMore) {
|
|
$loadMore.removeAttribute('disabled');
|
|
}
|
|
|
|
$indicator.classList.add('d-none');
|
|
formatDateTimes(document.getElementById('switchTimeFormat').dataset.mode);
|
|
initLabelManagers();
|
|
}
|
|
</script>
|
|
}
|
|
|
|
@if (Model.Labels.Any())
|
|
{
|
|
<div class="col-xl-7 col-xxl-8 mb-4" id="Filter">
|
|
<div class="input-group">
|
|
<span class="input-group-text">Filter</span>
|
|
<div class="form-control d-flex flex-wrap gap-2 align-items-center">
|
|
@foreach (var label in Model.Labels)
|
|
{
|
|
<a asp-route-labelFilter="@label.Text" class="transaction-label transaction-label-text@(labelFilter == label.Text ? " active" : string.Empty)" style="--label-bg:@label.Color;--label-fg:@label.TextColor;">@label.Text</a>
|
|
}
|
|
</div>
|
|
@if (labelFilter != null)
|
|
{
|
|
<a asp-route-labelFilter="" class="btn btn-secondary d-flex align-items-center">Clear filter</a>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (Model.Transactions.Any())
|
|
{
|
|
<div class="d-inline-flex align-items-center gap-3" id="Dropdowns">
|
|
<div class="dropdown ms-auto" id="Actions">
|
|
<button class="btn btn-secondary dropdown-toggle" type="button" id="ActionsDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
Actions
|
|
</button>
|
|
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="ActionsDropdownToggle">
|
|
<form id="WalletActions" method="post" asp-action="WalletActions" asp-route-walletId="@walletId">
|
|
<button id="BumpFee" name="command" type="submit" class="dropdown-item" value="cpfp">Bump fee (CPFP)</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="dropdown d-inline-flex align-items-center gap-3" id="Export">
|
|
<button class="btn btn-secondary dropdown-toggle" type="button" id="ExportDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
Export
|
|
</button>
|
|
<div class="dropdown-menu" aria-labelledby="ExportDropdownToggle">
|
|
<a asp-action="Export" asp-route-walletId="@walletId" asp-route-format="csv" asp-route-labelFilter="@labelFilter" class="dropdown-item export-link" target="_blank" id="ExportCSV">CSV</a>
|
|
<a asp-action="Export" asp-route-walletId="@walletId" asp-route-format="json" asp-route-labelFilter="@labelFilter" class="dropdown-item export-link" target="_blank" id="ExportJSON">JSON</a>
|
|
<a asp-action="Export" asp-route-walletId="@walletId" asp-route-format="bip329" asp-route-labelFilter="@labelFilter" class="dropdown-item export-link" target="_blank" id="ExportBIP329">Wallet Labels (BIP-329)</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="clear:both"></div>
|
|
|
|
<div class="row">
|
|
<div class="col table-responsive-md" id="walletTable">
|
|
<table class="table table-hover">
|
|
<thead class="thead-inverse">
|
|
<tr>
|
|
<th style="width:2rem;" class="only-for-js">
|
|
<input id="selectAllCheckbox" type="checkbox" class="form-check-input" />
|
|
</th>
|
|
<th class="w-150px">
|
|
<div class="d-flex align-items-center gap-1">
|
|
Date
|
|
<button type="button" class="btn btn-link p-0 fa fa-clock-o switch-time-format" title="Switch date format" id="switchTimeFormat"></button>
|
|
</div>
|
|
</th>
|
|
<th class="text-start">Label</th>
|
|
<th>Transaction Id</th>
|
|
<th class="text-end">Amount</th>
|
|
<th class="text-end" style="min-width:60px"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="WalletTransactionsList">
|
|
<partial name="_WalletTransactionsList" model="Model" />
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<noscript>
|
|
<vc:pager view-model="Model"/>
|
|
</noscript>
|
|
|
|
<div class="text-center only-for-js d-none" id="LoadingIndicator">
|
|
<div class="spinner-border spinner-border-sm text-secondary ms-2" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 mb-5 only-for-js" id="ListActions">
|
|
<button type="button" class="btn btn-secondary d-flex align-items-center" id="LoadMore">
|
|
Load more
|
|
</button>
|
|
<button type="button" class="btn btn-secondary d-none" id="GoToTop">Go to top</button>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<p class="text-secondary mt-3">
|
|
There are no transactions @(string.IsNullOrEmpty(labelFilter) ? "yet" : $"labeled with \"{labelFilter}\"").
|
|
</p>
|
|
}
|
|
|
|
<p class="mt-4 mb-0">
|
|
If BTCPay Server shows you an invalid balance, <a asp-action="WalletRescan" asp-route-walletId="@Context.GetRouteValue("walletId")">rescan your wallet</a>.
|
|
<br />
|
|
If some transactions appear in BTCPay Server, but are missing in another wallet, <a href="https://docs.btcpayserver.org/FAQ/Wallet/#missing-payments-in-my-software-or-hardware-wallet" rel="noreferrer noopener">follow these instructions</a>.
|
|
</p>
|