2023-11-02 08:12:28 +01:00
@using BTCPayServer.Client
@using BTCPayServer.Components
2022-02-10 12:24:28 +09:00
@model ListTransactionsViewModel
2022-09-27 05:24:53 -07:00
2018-07-27 00:08:07 +09:00
@{
2021-12-31 08:36:38 +01:00
var walletId = Context.GetRouteValue("walletId").ToString();
2022-05-20 02:35:31 +02:00
var labelFilter = Context.Request.Query["labelFilter"].ToString();
2022-09-27 05:24:53 -07:00
2018-07-27 00:08:07 +09:00
Layout = "../Shared/_NavLayout.cshtml";
2021-12-31 08:36:38 +01:00
ViewData.SetActivePage(WalletsNavPages.Transactions, $"{Model.CryptoCode} Transactions", walletId);
2018-07-27 00:08:07 +09:00
}
2021-10-10 08:52:39 +02:00
@section PageHeadContent {
2023-03-26 13:42:38 +02:00
<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">
2021-10-10 08:52:39 +02:00
<style>
2018-07-27 00:08:07 +09:00
.smMaxWidth {
2021-11-11 06:30:19 +01:00
max-width: 125px;
2018-07-27 00:08:07 +09:00
}
2018-11-05 17:26:49 +09:00
2021-11-11 06:30:19 +01:00
@@media (min-width: 990px) {
2021-10-10 08:52:39 +02:00
.smMaxWidth {
2022-02-17 10:07:41 +01:00
max-width: 200px;
2021-10-10 08:52:39 +02:00
}
}
2022-09-27 05:24:53 -07:00
2022-05-23 03:25:46 +02:00
/* pull actions area, so that it is besides the search form */
@@media (min-width: 1200px) {
2022-05-24 16:13:37 +02:00
#Filter + #Dropdowns {
2022-05-23 03:25:46 +02:00
margin-top: -4rem;
2022-05-24 16:13:37 +02:00
float: right;
}
2022-09-27 05:24:53 -07:00
2022-05-24 16:13:37 +02:00
#Filter + #Dropdowns #Actions {
order: 1;
2022-05-23 03:25:46 +02:00
}
2021-10-10 08:52:39 +02:00
}
2019-09-09 13:34:51 +09:00
2022-09-27 05:24:53 -07:00
.unconf > * {
opacity: 0.5;
2021-10-10 08:52:39 +02:00
}
2022-09-27 05:24:53 -07:00
#LoadingIndicator {
margin-bottom: 1.5rem;
}
2021-10-10 08:52:39 +02:00
</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>
2022-09-27 05:24:53 -07:00
const $actions = document.getElementById('ListActions');
2023-06-22 16:09:53 +09:00
const $transactions = document.getElementById('WalletTransactions');
2022-09-27 05:24:53 -07:00
const $list = document.getElementById('WalletTransactionsList');
2023-06-22 16:09:53 +09:00
const $dropdowns = document.getElementById('Dropdowns');
2022-09-27 05:24:53 -07:00
const $indicator = document.getElementById('LoadingIndicator');
delegate('click', '#GoToTop', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
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);
2023-06-22 16:09:53 +09:00
const loadMoreUrl = @Safe.Json(Url.Action("WalletTransactions", new { walletId, labelFilter, skip = Model.Skip, count = Model.Count, loadTransactions = true }));
// The next time we load transactions, skip will become 0
let skip = @Safe.Json(Model.Skip) - count;
2022-09-27 05:24:53 -07:00
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();
2023-06-22 16:09:53 +09:00
const responseEmpty = html.trim() === '';
2023-03-26 13:42:38 +02:00
$list.insertAdjacentHTML('beforeend', html);
2022-09-27 05:24:53 -07:00
skip = skipNext;
2023-06-22 16:09:53 +09:00
if (responseEmpty) {
2022-09-27 05:24:53 -07:00
// in case the response html was empty, remove the observer and stop loading
observer.unobserve($actions);
}
2023-06-22 16:09:53 +09:00
if (!$transactions.dataset.loaded) {
$transactions.dataset.loaded = 'true';
// replace table and dropdowns if initial response was empty
if (responseEmpty) {
$dropdowns.remove();
$transactions.innerHTML = '<div class="text-secondary" data-loaded="true">There are no transactions yet.</div>';
}
}
2022-09-27 05:24:53 -07:00
}
$indicator.classList.add('d-none');
2023-11-02 08:12:28 +01:00
formatDateTimes(document.querySelector('#WalletTransactions .switch-time-format').dataset.mode);
2023-03-26 13:42:38 +02:00
initLabelManagers();
2022-09-27 05:24:53 -07:00
}
2023-06-22 16:09:53 +09:00
const 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);
2021-10-10 08:52:39 +02:00
</script>
}
2020-04-29 10:11:23 +02:00
2024-03-12 10:48:37 +01:00
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3" id="Dropdowns">
@if (Model.Labels.Any())
{
<div class="btn-group" id="Filter">
<button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
@if (string.IsNullOrEmpty(labelFilter))
{
<span class="text-secondary">Filter by label</span>
}
else
{
<span class="text-secondary">Label:</span>
<span>@labelFilter</span>
}
</button>
<ul class="dropdown-menu">
2023-02-26 03:01:46 +01:00
@foreach (var label in Model.Labels)
2022-02-16 21:07:29 -08:00
{
2024-03-12 10:48:37 +01:00
<li><a asp-route-labelFilter="@label.Text" class="dropdown-item transaction-label-text@(labelFilter == label.Text ? " active" : string.Empty)" style="--btcpay-dropdown-link-active-bg:@label.Color;--btcpay-dropdown-link-active-color:@label.TextColor;">@label.Text</a></li>
2022-02-16 21:07:29 -08:00
}
2024-03-12 10:48:37 +01:00
@if (!string.IsNullOrEmpty(labelFilter))
{
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" asp-route-labelFilter="">Clear filter</a></li>
}
</ul>
2022-05-20 02:35:31 +02:00
</div>
2024-03-12 10:48:37 +01:00
}
<div class="dropdown d-inline-flex align-items-center gap-3 ms-auto" id="Export">
2023-06-22 16:09:53 +09:00
<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>
2022-02-17 10:07:41 +01:00
</div>
2018-07-27 00:08:07 +09:00
</div>
2023-06-22 16:09:53 +09:00
</div>
<div style="clear:both"></div>
<div id="WalletTransactions" class="table-responsive-md">
2023-11-02 08:12:28 +01:00
<table class="table table-hover mass-action">
<thead class="mass-action-head">
<tr>
<th class="only-for-js mass-action-select-col">
<input type="checkbox" class="form-check-input mass-action-select-all" />
</th>
<th class="date-col">
<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 only-for-js" title="Switch date format"></button>
</div>
</th>
<th class="text-start">Label</th>
<th>Transaction Id</th>
<th class="amount-col">Amount</th>
<th></th>
</tr>
</thead>
<thead class="mass-action-actions">
<tr>
<th class="only-for-js mass-action-select-col">
<input type="checkbox" class="form-check-input mass-action-select-all" />
</th>
<th colspan="5">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
<div>
<strong class="mass-action-selected-count">0</strong>
selected
</div>
<form id="WalletActions" method="post" asp-action="WalletActions" asp-route-walletId="@walletId" permission="@Policies.CanModifyStoreSettings" class="d-inline-flex align-items-center gap-3">
<button id="BumpFee" name="command" type="submit" value="cpfp" class="btn btn-link">
<vc:icon symbol="send" />
Bump fee
</button>
</form>
</div>
</th>
</tr>
2023-06-22 16:09:53 +09:00
</thead>
<tbody id="WalletTransactionsList">
<partial name="_WalletTransactionsList" model="Model" />
</tbody>
</table>
</div>
<noscript>
<vc:pager view-model="Model"/>
</noscript>
2022-09-27 05:24:53 -07:00
2023-06-22 16:09:53 +09:00
<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>
2022-09-27 05:24:53 -07:00
</div>
2023-06-22 16:09:53 +09:00
</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-none" id="GoToTop">Go to top</button>
</div>
2022-02-17 10:07:41 +01:00
<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>.
2022-09-27 05:24:53 -07:00
<br />
2022-02-17 10:07:41 +01:00
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>