mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-06 18:41:12 +01:00
Based on the `ur-registry` upgrade I refactored the `CameraScanner` and `ShowQR` partials: Besides general code changes, the main change is that most of the configuration and result handling now happens on the outer view. Those partials and functions are now generalized and don't know about their purpose (like handling PSBTs): They can be instantiated with simple data (e.g. for displaying a plain QR code) or different modes (like showing a static and the UR version of a QR code) and the result handling is done via callback. The callbacks can now also distinguish between the different results (data as plain string vs. UR-type objects for wallet data or PSBT) and also handle the specific type of data. For instance: Before it wasn't possible to strip the leading derivation path from an xpub when scanning the QR code, because the scanner didn't know about the type of data it was handling. Now that the data is handled in the callback, we can implement that functionality for the scan view only.
220 lines
14 KiB
Text
220 lines
14 KiB
Text
@using NBitcoin.DataEncoders
|
|
@using Newtonsoft.Json
|
|
@using System.Text
|
|
@using BTCPayServer.Abstractions.Models
|
|
@model WalletSettingsViewModel
|
|
@{
|
|
Layout = "../Shared/_NavLayout.cshtml";
|
|
ViewData["NavPartialName"] = "../UIWallets/_Nav";
|
|
ViewData.SetActivePage(StoreNavPages.OnchainSettings, $"{Model.CryptoCode} Wallet Settings", Context.GetStoreData().Id);
|
|
}
|
|
|
|
@section PageHeadContent {
|
|
<bundle name="wwwroot/bundles/camera-bundle.min.js"></bundle>
|
|
<link href="~/vendor/vue-qrcode-reader/vue-qrcode-reader.css" rel="stylesheet" asp-append-version="true"/>
|
|
}
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8 col-xxl-constrain">
|
|
<div class="mb-5">
|
|
<h3 class="mb-3">@ViewData["Title"]</h3>
|
|
<div class="mb-3 d-flex align-items-center">
|
|
<span title="@Model.Source" data-bs-toggle="tooltip" class="me-3">@(Model.IsHotWallet ? "Hot wallet" : "Watch-only wallet")</span>
|
|
<div class="dropdown">
|
|
<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" aria-labelledby="ActionsDropdownToggle">
|
|
<a class="dropdown-item" asp-controller="UIWallets" asp-action="WalletRescan" asp-route-walletId="@Model.WalletId" id="Rescan">Rescan wallet for missing transactions</a>
|
|
<form method="post" asp-controller="UIWallets" asp-action="WalletActions" asp-route-walletId="@Model.WalletId">
|
|
<button name="command" type="submit" class="dropdown-item" value="prune">Prune old transactions from history</button>
|
|
@if (User.IsInRole(Roles.ServerAdmin))
|
|
{
|
|
<button name="command" type="submit" class="dropdown-item" value="clear">Clear all transactions from history</button>
|
|
}
|
|
</form>
|
|
@if (Model.UriScheme == "bitcoin")
|
|
{
|
|
<button type="button" class="dropdown-item" id="RegisterWallet" data-store="@Model.StoreName" data-scheme="@Model.UriScheme" data-url="@Url.Action("WalletSend", "UIWallets", new {walletId = Model.WalletId, bip21 = "%s"})" hidden>Register wallet for payment links</button>
|
|
}
|
|
<div class="dropdown-divider"></div>
|
|
@if (Model.NBXSeedAvailable)
|
|
{
|
|
<a asp-action="WalletSeed" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" class="dropdown-item" id="ViewSeed">View seed</a>
|
|
}
|
|
<a asp-controller="UIStores" asp-action="ReplaceWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode"
|
|
id="ChangeWalletLink"
|
|
class="dropdown-item"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#ConfirmModal"
|
|
data-title="Replace @Model.CryptoCode wallet"
|
|
data-description="@ViewData["ReplaceDescription"]"
|
|
data-confirm="Setup new wallet"
|
|
data-confirm-input="REPLACE">
|
|
Replace wallet
|
|
</a>
|
|
<form method="get" asp-action="DeleteWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" class="d-inline">
|
|
<button type="submit"
|
|
id="Delete"
|
|
class="dropdown-item"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#ConfirmModal"
|
|
data-title="Remove @Model.CryptoCode wallet"
|
|
data-description="@ViewData["RemoveDescription"]"
|
|
data-confirm="Remove"
|
|
data-confirm-input="REMOVE">Remove wallet</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" asp-action="UpdateWalletSettings" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode">
|
|
<div class="form-group my-4">
|
|
<div class="d-flex align-items-center">
|
|
<input asp-for="Enabled" type="checkbox" class="btcpay-toggle me-2"/>
|
|
<label asp-for="Enabled" class="form-label mb-0 me-1"></label>
|
|
</div>
|
|
<span asp-validation-for="PayJoinEnabled" class="text-danger"></span>
|
|
</div>
|
|
<div class="form-group">
|
|
<label asp-for="Label" class="form-label"></label>
|
|
<input asp-for="Label" class="form-control" style="max-width:24em;" />
|
|
<span asp-validation-for="Label" class="text-danger"></span>
|
|
</div>
|
|
<div class="form-group">
|
|
<label asp-for="DerivationScheme" class="form-label"></label>
|
|
<div class="input-group" data-clipboard="@Model.DerivationScheme">
|
|
<input asp-for="DerivationScheme" class="form-control" style="cursor:copy" readonly />
|
|
<button type="button" class="input-group-text btn btn-outline-secondary" data-clipboard-confirm style="min-width:8em;">Copy</button>
|
|
</div>
|
|
</div>
|
|
@if (!string.IsNullOrEmpty(Model.DerivationSchemeInput) && Model.DerivationSchemeInput != Model.DerivationScheme)
|
|
{
|
|
<div class="form-group">
|
|
<label asp-for="DerivationSchemeInput" class="form-label"></label>
|
|
<div class="input-group" data-clipboard="@Model.DerivationSchemeInput">
|
|
<input asp-for="DerivationSchemeInput" class="form-control" style="cursor:copy" readonly/>
|
|
<button type="button" class="input-group-text btn btn-outline-secondary" data-clipboard-confirm style="min-width:8em;">Copy</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
@for (var i = 0; i < Model.AccountKeys.Count; i++)
|
|
{
|
|
<h4 class="mt-5 mb-3">Account Key @i</h4>
|
|
<div class="form-group">
|
|
<div class="d-flex align-items-center justify-content-between">
|
|
<label asp-for="@Model.AccountKeys[i].AccountKey" class="form-label"></label>
|
|
<button type="button" class="d-inline-block ms-2 btn text-primary btn-link p-0 mb-2" data-account-key="@i" title="">
|
|
<span class="fa fa-qrcode"></span> Show export QR
|
|
</button>
|
|
</div>
|
|
<div class="input-group" data-clipboard="@Model.AccountKeys[i].AccountKey">
|
|
<input asp-for="@Model.AccountKeys[i].AccountKey" class="form-control" style="cursor:copy" readonly/>
|
|
<button type="button" class="input-group-text btn btn-outline-secondary" data-clipboard-confirm style="min-width:8em;">Copy</button>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="form-group col-auto">
|
|
<label asp-for="@Model.AccountKeys[i].MasterFingerprint" class="form-label"></label>
|
|
<input asp-for="@Model.AccountKeys[i].MasterFingerprint" class="form-control" style="max-width:16ch;" />
|
|
</div>
|
|
<div class="form-group col-auto">
|
|
<label asp-for="@Model.AccountKeys[i].AccountKeyPath" class="form-label"></label>
|
|
<input asp-for="@Model.AccountKeys[i].AccountKeyPath" class="form-control" style="max-width:16ch;" />
|
|
</div>
|
|
</div>
|
|
@if (Model.IsMultiSig)
|
|
{
|
|
<div class="form-check">
|
|
<input asp-for="SelectedSigningKey" class="form-check-input" type="radio" value="@Model.AccountKeys[i].AccountKey"/>
|
|
<label asp-for="SelectedSigningKey" class="form-check-label"></label>
|
|
</div>
|
|
}
|
|
}
|
|
<button type="submit" class="btn btn-primary mt-2" id="SaveWalletSettings">Save Wallet Settings</button>
|
|
</form>
|
|
|
|
<h3 class="mt-5 mb-4">Payment</h3>
|
|
<form method="post" asp-action="UpdatePaymentSettings" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode">
|
|
@if (Model.CanUsePayJoin)
|
|
{
|
|
<div class="form-group">
|
|
<div class="d-flex align-items-center">
|
|
<input asp-for="PayJoinEnabled" type="checkbox" class="btcpay-toggle me-2"/>
|
|
<label asp-for="PayJoinEnabled" class="form-label mb-0 me-1"></label>
|
|
<a href="https://docs.btcpayserver.org/Payjoin/" target="_blank" rel="noreferrer noopener">
|
|
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
|
</a>
|
|
</div>
|
|
<span asp-validation-for="PayJoinEnabled" class="text-danger"></span>
|
|
</div>
|
|
}
|
|
<div class="form-group">
|
|
<label asp-for="MonitoringExpiration" class="form-label"></label>
|
|
<a href="https://docs.btcpayserver.org/FAQ/Stores/#payment-invalid-if-transactions-fails-to-confirm-minutes-after-invoice-expiration" target="_blank" rel="noreferrer noopener">
|
|
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
|
</a>
|
|
<div class="input-group">
|
|
<input inputmode="numeric" asp-for="MonitoringExpiration" class="form-control" style="max-width:10ch;"/>
|
|
<span class="input-group-text">minutes</span>
|
|
</div>
|
|
<span asp-validation-for="MonitoringExpiration" class="text-danger"></span>
|
|
</div>
|
|
<div class="form-group">
|
|
<label asp-for="SpeedPolicy" class="form-label"></label>
|
|
<a href="https://docs.btcpayserver.org/FAQ/Stores/#consider-the-invoice-confirmed-when-the-payment-transaction" target="_blank" rel="noreferrer noopener">
|
|
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
|
</a>
|
|
<select asp-for="SpeedPolicy" class="form-select w-auto" onchange="document.getElementById('unconfirmed-warning').hidden = this.value !== '0';">
|
|
<option value="0">Is unconfirmed</option>
|
|
<option value="1">Has at least 1 confirmation</option>
|
|
<option value="3">Has at least 2 confirmations</option>
|
|
<option value="2">Has at least 6 confirmations</option>
|
|
</select>
|
|
<div class="alert alert-warning my-2" hidden="@(Model.SpeedPolicy != 0)" id="unconfirmed-warning" role="alert">
|
|
Choosing to accept an unconfirmed invoice can lead to double-spending and is strongly discouraged.
|
|
</div>
|
|
<span asp-validation-for="SpeedPolicy" class="text-danger"></span>
|
|
</div>
|
|
<div class="form-check my-1">
|
|
<input asp-for="ShowRecommendedFee" type="checkbox" class="form-check-input"/>
|
|
<label asp-for="ShowRecommendedFee" class="form-check-label"></label>
|
|
<p class="form-text text-muted mb-0">Fee will be shown for BTC and LTC onchain payments only.</p>
|
|
</div>
|
|
<div class="form-group mt-2">
|
|
<label asp-for="RecommendedFeeBlockTarget" class="form-label"></label>
|
|
<input inputmode="numeric" asp-for="RecommendedFeeBlockTarget" class="form-control" min="1" style="width:8ch" />
|
|
<span asp-validation-for="RecommendedFeeBlockTarget" class="text-danger"></span>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary mt-2" id="SavePaymentSettings">Save Payment Settings</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<partial name="_Confirm" model="@(new ConfirmModel($"{Model.CryptoCode} wallet", "Change", "Update"))" />
|
|
<partial name="ShowQR"/>
|
|
|
|
@section PageFootContent {
|
|
<script>
|
|
const wallets = @Safe.Json(Model.AccountKeys.Select(model => Encoders.Hex.EncodeData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(model, Formatting.None)))))
|
|
const qrApp = initQRShow({ title: "Wallet QR" })
|
|
|
|
delegate('click', '#Delete', event => { event.preventDefault() })
|
|
|
|
delegate('click', 'button[data-account-key]', event => {
|
|
const { accountKey } = event.target.dataset;
|
|
qrApp.showData(wallets[parseInt(accountKey)]);
|
|
})
|
|
|
|
if (navigator.registerProtocolHandler) {
|
|
document.getElementById('RegisterWallet').removeAttribute('hidden');
|
|
delegate('click', '#RegisterWallet', event => {
|
|
const { store, scheme, url } = event.target.dataset
|
|
const uri = decodeURIComponent(url)
|
|
navigator.registerProtocolHandler(scheme, uri, `BTCPay Wallet: ${store}`)
|
|
})
|
|
}
|
|
</script>
|
|
<partial name="_ValidationScriptsPartial"/>
|
|
}
|