Revert "Payment redesign" (#1962)

This commit is contained in:
Andrew Camilleri 2020-10-08 08:37:18 +02:00 committed by GitHub
parent 182d67881d
commit 4174fa648d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 555 additions and 566 deletions

View file

@ -806,7 +806,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("20" + Keys.Enter);
s.AssertHappyMessage();
Assert.Contains("Awaiting Approval", s.Driver.PageSource);
Assert.Contains("AwaitingApproval", s.Driver.PageSource);
var viewPullPaymentUrl = s.Driver.Url;
// This one should have nothing
@ -848,7 +848,8 @@ namespace BTCPayServer.Tests
s.Driver.Navigate().GoToUrl(viewPullPaymentUrl);
txs = s.Driver.FindElements(By.ClassName("transaction-link"));
Assert.Equal(2, txs.Count);
Assert.Contains("In Progress", s.Driver.PageSource);
Assert.Contains("InProgress", s.Driver.PageSource);
await s.Server.ExplorerNode.GenerateAsync(1);

View file

@ -1,7 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BTCPayServer;
using BTCPayServer.Data;
@ -70,13 +69,13 @@ namespace BTCPayServer.Controllers
CurrencyData = cd,
LastUpdated = DateTime.Now,
Payouts = payouts
.Select(entity => new ViewPullPaymentModel.PayoutLine
.Select(entity => new ViewPullPaymentModel.PayoutLine()
{
Id = entity.Entity.Id,
Amount = entity.Blob.Amount,
AmountFormatted = _currencyNameTable.FormatCurrency(entity.Blob.Amount, blob.Currency),
Currency = blob.Currency,
Status = Regex.Replace(entity.Entity.State.ToString(), "(\\B[A-Z])", " $1"),
Status = entity.Entity.State.ToString(),
Destination = entity.Blob.Destination.Address.ToString(),
Link = GetTransactionLink(_networkProvider.GetNetwork<BTCPayNetwork>(entity.Entity.GetPaymentMethodId().CryptoCode), entity.TransactionId),
TransactionId = entity.TransactionId

View file

@ -29,9 +29,9 @@
@if (env.OnionUrl != null)
{
<div class="text-center">
<a href="@env.OnionUrl" target="_onion" class="btn btn-sm btn-outline-onion d-inline-flex align-items-center text-nowrap p-2" data-clipboard="@env.OnionUrl" style="min-width:117px;">
<a href="@env.OnionUrl" target="_onion" class="btn btn-sm btn-outline-onion d-inline-flex align-items-center text-nowrap p-2" data-clipboard="@env.OnionUrl">
<img src="~/img/icons/onion-purple.svg" height="20" class="mr-2" asp-append-version="true" />
<span data-clipboard-confirm="Copied URL ✔">Copy Tor URL</span>
Copy Tor URL
</a>
</div>
}

View file

@ -33,9 +33,9 @@
@if (env.OnionUrl != null)
{
<div class="text-center">
<a href="@env.OnionUrl" target="_onion" class="btn btn-sm btn-outline-onion d-inline-flex align-items-center text-nowrap p-2" data-clipboard="@env.OnionUrl" style="min-width:117px;">
<a href="@env.OnionUrl" target="_onion" class="btn btn-sm btn-outline-onion d-inline-flex align-items-center text-nowrap p-2" data-clipboard="@env.OnionUrl">
<img src="~/img/icons/onion-purple.svg" height="20" class="mr-2" asp-append-version="true" />
<span data-clipboard-confirm="Copied URL ✔">Copy Tor URL</span>
Copy Tor URL
</a>
</div>
}

View file

@ -33,8 +33,9 @@
{
<style type="text/css">
body {
background: rgba(25, 25, 25, 0.9);
background: rgba(55, 58, 60, 0.4);
}
.close-icon {
display: flex;
}
@ -43,25 +44,25 @@
</head>
<body>
<noscript>
<div style="padding:2em;text-align:center;">
<center style="padding: 2em">
<h2>Javascript is currently disabled in your browser.</h2>
<h5>Please enable Javascript and refresh this page for the best experience.</h5>
<p>Alternatively, click below to continue to our HTML-only invoice.</p>
<a href="/invoice-noscript?id=@Model.InvoiceId" style="text-decoration:underline;color:blue">
<a href="/invoice-noscript?id=@Model.InvoiceId" style="text-decoration: underline; color: blue">
Continue to javascript-disabled invoice &gt;
</a>
</div>
</center>
</noscript>
<!--[if lte IE 8]>
<div style="padding:2em;text-align:center;">
<center style="padding: 2em">
<form action="/invoice-noscript" method="GET">
<button style="text-decoration: underline; color: blue">Continue to legacy browser compatible invoice page
</button>
</form>
</div>
</center>
<![endif]-->
<invoice>

View file

@ -0,0 +1,175 @@
@model BTCPayServer.Models.PaymentRequestViewModels.ViewPaymentRequestViewModel
<div class="container">
<div class="row w-100 p-0 m-0" style="height: 100vh">
<div class="mx-auto my-auto w-100">
<div class="card">
<h1 class="card-header px-3">
@Model.Title
<span class="text-muted float-right text-center">@Model.Status</span>
</h1>
<div class="card-body px-0 pt-0 pb-0">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<table class="table table-light mb-0">
<tbody>
<tr>
<td class="px-3 h2 text-muted">Request amount:</td>
<td class="px-3 h2 text-nowrap text-right">@Model.AmountFormatted</td>
</tr>
<tr>
<td class="px-3 h2 text-muted">Paid so far:</td>
<td class="px-3 h2 text-nowrap text-right">@Model.AmountCollectedFormatted</td>
</tr>
<tr>
<td class="px-3 h2 text-muted">Amount due:</td>
<td class="px-3 h2 text-nowrap text-right">@Model.AmountDueFormatted</td>
</tr>
</tbody>
</table>
@if (Model.Description != null && Model.Description != "" && Model.Description != "<br>")
{
<div class="w-100 px-3 pt-4 pb-3">@Safe.Raw(Model.Description)</div>
}
</div>
<div class="col-sm-12 col-md-12 col-lg-6 pt-2">
<div class="table-responsive">
<table class="table border-top-0">
<thead>
<tr>
<th class="border-top-0" scope="col">Invoice #</th>
<th class="border-top-0">Price</th>
<th class="border-top-0">Expiry</th>
<th class="border-top-0">Status</th>
</tr>
</thead>
<tbody>
@if (Model.Invoices == null && !Model.Invoices.Any())
{
<tr>
<td colspan="4" class="text-center">No payments made yet</td>
</tr>
}
else
{
foreach (var invoice in Model.Invoices)
{
<tr class="bg-light">
<td scope="row">@invoice.Id</td>
<td>@invoice.Amount @invoice.Currency</td>
<td>@invoice.ExpiryDate.ToString("g")</td>
<td>@invoice.Status</td>
</tr>
if (invoice.Payments != null && invoice.Payments.Any())
{
<tr class="bg-light">
<td colspan="4" class=" px-2 py-1 border-top-0">
<div class="table-responsive">
<table class="table table-bordered">
<tr>
<th class="p-1" style="max-width: 300px">Tx Id</th>
<th class="p-1">Payment Method</th>
<th class="p-1">Amount</th>
<th class="p-1">Link</th>
</tr>
@foreach (var payment in invoice.Payments)
{
<tr class="d-flex">
<td class="p-1 m-0 d-print-none d-block" style="max-width: 300px">
<div style="width: 100%; overflow-x: auto; overflow-wrap: initial;">@payment.Id</div>
</td>
<td class="p-1 m-0 d-none d-print-table-cell" style="max-width: 150px;">
@payment.Id
</td>
<td class="p-1">@payment.PaymentMethod</td>
<td class="p-1">@payment.Amount</td>
<td class="p-1 d-print-none">
@if (!string.IsNullOrEmpty(payment.Link))
{
<a :href="@payment.Link" target="_blank">Link</a>
}
</td>
<td class="p-1 d-none d-print-table-cell" style="max-width: 150px;">
@payment.Link
</td>
</tr>
}
</table>
</div>
</td>
</tr>
}
}
}
@if (Model.IsPending && !Model.Archived)
{
<tr>
<td colspan="4" class="text-center">
@if (Model.AllowCustomPaymentAmounts && !Model.AnyPendingInvoice)
{
<form method="get" asp-action="PayPaymentRequest">
<div class="input-group m-auto" style="max-width: 250px">
<input
class="form-control"
type="number"
name="amount"
value="@Model.AmountDue"
max="@Model.AmountDue"
step="any"
placeholder="Amount"
required>
<div class="input-group-append">
<span class='input-group-text'>@Model.Currency.ToUpper()</span>
<button
class="btn btn-primary"
type="submit">
Pay now
</button>
</div>
</div>
</form>
}
else
{
<a class="btn btn-primary btn-lg d-print-none mt-1" asp-action="PayPaymentRequest">
Pay now
</a>
if (Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)
{
<form method="get" asp-action="CancelUnpaidPendingInvoice">
<button class="btn btn-secondary btn-lg mt-1" type="submit">
Cancel current invoice</button>
</form>
}
}
</td>
</tr>
}else if (Model.Archived)
{
<tr>
<td colspan="4" class="text-center">
<button type="button" class="btn btn-secondary" disabled>Archived</button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="card-footer text-muted d-flex justify-content-between">
<div >Updated @Model.LastUpdated.ToString("g")</div>
<div >
<span class="text-muted">Powered by </span><a href="https://btcpayserver.org" target="_blank">BTCPay Server</a>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,7 +1,6 @@
@model BTCPayServer.Models.PaymentRequestViewModels.ViewPaymentRequestViewModel
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@{
ViewData["Title"] = Model.Title;
@ -9,313 +8,221 @@
}
<!DOCTYPE html>
<html lang="en" @(env.IsDeveloping ? " data-devenv" : "")>
<html class="h-100">
<head>
<title>@Model.Title</title>
<meta charset="utf-8" />
<meta name="robots" content="noindex,nofollow">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.BootstrapUri)" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.ThemeUri)" rel="stylesheet" />
<bundle name="wwwroot/bundles/payment-request-bundle.min.css" asp-append-version="true"></bundle>
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" />
}
<script type="text/javascript">
var srvModel = @Safe.Json(Model);
</script>
<bundle name="wwwroot/bundles/payment-request-bundle.min.js" asp-append-version="true"></bundle>
@*We need to make sure btcpay.js is not bundled, else it will not work if there is a RootPath*@
<script src="~/modal/btcpay.js" asp-append-version="true"></script>
@if (!Context.Request.Query.ContainsKey("simple"))
{
<script type="text/javascript">
var srvModel = @Safe.Json(Model);
</script>
<bundle name="wwwroot/bundles/payment-request-bundle-1.min.js" asp-append-version="true"></bundle>
<bundle name="wwwroot/bundles/payment-request-bundle-2.min.js" asp-append-version="true"></bundle>
@*We need to make sure btcpay.js is not bundled, else it will not work if there is a RootPath*@
<script src="~/modal/btcpay.js" asp-append-version="true"></script>
}
<bundle name="wwwroot/bundles/payment-request-bundle.min.css" asp-append-version="true"></bundle>
@Safe.Raw(Model.EmbeddedCSS)
<noscript>
<style>
.hide-when-js, [v-cloak] { display: block !important; }
.only-for-js { display: none !important; }
</style>
</noscript>
</head>
<body class="h-100">
<div id="app" class="h-100 d-flex flex-column">
<nav id="mainNav" class="navbar sticky-top py-3 py-lg-4 d-print-block">
<div class="container">
<div class="row align-items-center" style="width:calc(100% + 30px)">
<div class="col-12 col-md-8 col-lg-9">
<div class="row">
<div class="col col-12 col-lg-8">
<h1 class="h3" v-text="srvModel.title">@Model.Title</h1>
</div>
<div class="col col-12 col-sm-6 col-lg-8 d-flex align-items-center">
<span class="text-muted text-nowrap">Last Updated</span>
&nbsp;
<span class="text-nowrap" v-text="lastUpdated" v-cloak>@Model.LastUpdated.ToString("g")</span>
<button type="button" class="btn btn-link d-none d-lg-inline-block d-print-none border-0 p-0 ml-4 only-for-js" v-on:click="window.print" v-cloak>
Print
</button>
<button type="button" class="btn btn-link d-none d-lg-inline-block d-print-none border-0 p-0 ml-4 only-for-js" v-on:click="copyLink" v-cloak>
Copy Link
</button>
</div>
<div class="col col-12 col-sm-6 text-sm-right col-lg-4 mt-lg-n4 pt-lg-1 d-print-none">
@if (Model.IsPending && !Model.Archived && Model.ExpiryDate.HasValue)
{
<noscript>@Model.Status</noscript>
}
<template v-if="srvModel.isPending && !srvModel.archived && endDiff">
<span class="text-muted">Expires in</span> {{endDiff}}
</template>
</div>
</div>
</div>
<div class="col-12 pt-3 pb-2 col-md-4 py-md-0 col-lg-3 d-print-none">
<noscript>
@if (Model.IsPending && !Model.Archived)
{
@if (Model.AllowCustomPaymentAmounts && !Model.AnyPendingInvoice)
{
<form method="get" asp-action="PayPaymentRequest" asp-route-id="@Model.Id">
<div class="row">
<div class="col col-12 col-sm-6 col-md-12">
<div class="input-group">
<input type="number" class="form-control text-right hide-number-spin" name="amount" value="@Model.AmountDue" @if (!Model.AllowCustomPaymentAmounts) { @("readonly") } max="@Model.AmountDue" step="any" placeholder="Amount" required>
<div class="input-group-append">
<span class="input-group-text">@Model.Currency.ToUpper()</span>
</div>
</div>
</div>
<div class="col mt-2 col-12 col-sm-6 mt-sm-0 col-md-12 mt-md-2">
<button class="btn btn-primary w-100 text-nowrap" type="submit">Pay Invoice</button>
</div>
</div>
</form>
}
else
{
<a class="btn btn-primary d-inline-block w-100 text-nowrap @if (!(Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)) { @("btn-lg") }" asp-action="PayPaymentRequest" asp-route-id="@Model.Id">
Pay Invoice
</a>
if (Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)
{
<form method="get" asp-action="CancelUnpaidPendingInvoice" asp-route-id="@Model.Id" class="mt-2">
<button class="btn btn-outline-secondary w-100 text-nowrap" type="submit">Cancel Invoice</button>
</form>
}
}
}
else
{
<div class="h2 text-md-right">
<span class="badge @if (Model.Status == "Settled") { @("badge-primary") } else if (Model.Status == "Expired") { @("badge-danger") } else { @("badge-info") }">
@Model.Status
@if (Model.Archived)
{
<span>(archived)</span>
}
</span>
</div>
}
</noscript>
<template v-if="srvModel.isPending && !srvModel.archived" class="d-print-none">
<template v-if="srvModel.allowCustomPaymentAmounts && !srvModel.anyPendingInvoice">
<form v-on:submit="submitCustomAmountForm">
<div class="row">
<div class="col col-12 col-sm-6 col-md-12">
<div class="input-group">
<input type="number" class="form-control text-right hide-number-spin" v-model="customAmount" :readonly="!srvModel.allowCustomPaymentAmounts" :max="srvModel.amountDue" placeholder="Amount" required>
<div class="input-group-append">
<span class="input-group-text">{{currency}}</span>
</div>
</div>
</div>
<div class="col mt-2 col-12 col-sm-6 mt-sm-0 col-md-12 mt-md-2">
<button class="btn btn-primary w-100 text-nowrap" v-bind:class="{ 'btn-disabled': loading}" :disabled="loading" type="submit">
<div v-if="loading" class="spinner-grow spinner-grow-sm" role="status">
<span class="sr-only">Loading...</span>
</div>
Pay Invoice
</button>
</div>
</div>
</form>
</template>
<body>
<partial name="_StatusMessage" />
@if (Context.Request.Query.ContainsKey("simple"))
{
@await Html.PartialAsync("MinimalPaymentRequest", Model)
}
else
{
<noscript>
@await Html.PartialAsync("MinimalPaymentRequest", Model)
</noscript>
<div class="container" id="app" v-cloak>
<div class="row w-100 p-0 m-0" style="height: 100vh">
<div class="mx-auto my-auto w-100">
<div class="card">
<h1 class="card-header px-3">
{{srvModel.title}}
<span class="text-muted float-right text-center">
<template v-if="settled">Settled</template>
<template v-else>
<button class="btn btn-primary w-100 d-flex align-items-center justify-content-center text-nowrap" :class="{ 'btn-lg': !(srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments)}" v-on:click="pay(null)" :disabled="loading">
<div v-if="loading" class="spinner-grow spinner-grow-sm mr-2" role="status">
<span class="sr-only">Loading...</span>
</div>
<span>Pay Invoice</span>
</button>
<button class="btn btn-outline-secondary mt-2 w-100 d-flex align-items-center justify-content-center text-nowrap" v-if="srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments" v-on:click="cancelPayment()" :disabled="loading">
<span v-if="loading" class="spinner-grow spinner-grow-sm mr-2" role="status">
<span class="sr-only">Loading...</span>
</span>
<span>Cancel Invoice</span>
</button>
<template v-if="ended">Request Expired</template>
<template v-else-if="endDiff">Expires in {{endDiff}}</template>
<template v-else>{{srvModel.status}}</template>
</template>
</template>
<template v-else>
<div class="h2 text-md-right">
<span class="badge" :class="{ 'badge-primary': srvModel.status === 'Settled', 'badge-danger': srvModel.status === 'Expired', 'badge-info': (srvModel.status !== 'Settled' && srvModel.status !== 'Expired') }">
{{srvModel.status}}
<span v-if="srvModel.archived">(archived)</span>
</span>
</span>
</h1>
<div class="card-body px-0 pt-0 pb-0">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<table class="table table-light mb-0">
<tbody>
<tr>
<td class="px-3 h2 text-muted">Request amount:</td>
<td class="px-3 h2 text-nowrap text-right">{{srvModel.amountFormatted}}</td>
</tr>
<tr>
<td class="px-3 h2 text-muted">Paid so far:</td>
<td class="px-3 h2 text-nowrap text-right">{{srvModel.amountCollectedFormatted}}</td>
</tr>
<tr>
<td class="px-3 h2 text-muted">Amount due:</td>
<td class="px-3 h2 text-nowrap text-right">{{srvModel.amountDueFormatted}}</td>
</tr>
</tbody>
</table>
<div
v-if="srvModel.description && srvModel.description !== '' && srvModel.description !== '<br>'"
v-html="srvModel.description"
class="w-100 px-3 pt-4 pb-3"
></div>
</div>
</template>
</div>
</div>
</div>
</nav>
<main class="flex-grow-1 py-4">
<div class="container">
@await Html.PartialAsync("_StatusMessage", new ViewDataDictionary(ViewData){ { "Margin", "mb-4" } })
<div class="row">
<div class="col col-12 col-lg-6 mb-4">
<div class="jumbotron h-100 m-0 p-sm-5">
<h2 class="h4 mb-3">Invoice Summary</h2>
<div v-html="srvModel.description">
@if (!string.IsNullOrEmpty(Model.Description) && Model.Description != "<br>")
{
@Safe.Raw(Model.Description)
}
</div>
</div>
</div>
<div class="col col-12 col-lg-6 mb-4">
<div class="jumbotron h-100 m-0 p-sm-5">
<h2 class="h4 mb-3">Payment Details</h2>
<dl class="mb-0 mt-md-4">
<div class="d-flex flex-column mb-4">
<dt class="h4 font-weight-normal text-nowrap text-primary order-2 order-sm-1 mb-0" v-text="srvModel.amountDueFormatted">@Model.AmountDueFormatted</dt>
<dd class="text-muted order-1 order-sm-2 mb-1">Amount due</dd>
</div>
<div class="progress bg-light d-none d-sm-flex mb-sm-4 d-print-none" style="height:5px">
<div class="progress-bar bg-primary" role="progressbar" style="width:@((Model.AmountCollected/Model.Amount)*100)%" v-bind:style="{ width: (srvModel.amountCollected/srvModel.amount*100) + '%' }"></div>
</div>
<div class="d-flex flex-column mb-4 d-sm-inline-flex mb-sm-0">
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0" v-text="srvModel.amountCollectedFormatted">@Model.AmountCollectedFormatted</dt>
<dd class="text-muted order-1 order-sm-2 mb-1">Amount paid</dd>
</div>
<div class="d-flex flex-column mb-0 d-sm-inline-flex float-sm-right">
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0" v-text="srvModel.amountFormatted">@Model.AmountFormatted</dt>
<dd class="text-muted text-sm-right order-1 order-sm-2 mb-1">Total requested</dd>
</div>
</dl>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="jumbotron h-100 m-0 p-sm-5">
<h2 class="h4 mb-3">Payment History</h2>
<div class="table-responsive">
<noscript>
@if (Model.Invoices == null || !Model.Invoices.Any())
{
<p class="text-muted">No payments made yet.</p>
}
else
{
<table class="table my-0">
<thead>
<tr class="table-borderless">
<th class="font-weight-normal text-secondary" scope="col">Invoice Id</th>
<th class="font-weight-normal text-secondary">Price</th>
<th class="font-weight-normal text-secondary">Expiry</th>
<th class="font-weight-normal text-secondary text-right">Status</th>
</tr>
</thead>
<tbody>
@foreach (var invoice in Model.Invoices)
{
<tr>
<td>@invoice.Id</td>
<td>@invoice.Amount @invoice.Currency</td>
<td>@invoice.ExpiryDate.ToString("g")</td>
<td class="text-right">@invoice.Status</td>
</tr>
if (invoice.Payments != null && invoice.Payments.Any())
{
<tr class="table-borderless table-light">
<th colspan="2" class="pl-3 font-weight-normal text-secondary">TX Id</th>
<th class="font-weight-normal text-secondary">Payment Method</th>
<th class="font-weight-normal text-secondary text-right">Amount</th>
</tr>
@foreach (var payment in invoice.Payments)
{
<tr class="table-borderless table-light">
<td colspan="2" class="pl-3 text-break">
@if (!string.IsNullOrEmpty(payment.Link))
{
<a href="@payment.Link" target="_blank">@payment.Id</a>
}
else
{
<span>@payment.Id</span>
}
</td>
<td>@payment.PaymentMethod</td>
<td class="text-right">@payment.Amount</td>
</tr>
}
}
}
</tbody>
</table>
}
</noscript>
<template v-if="!srvModel.invoices || srvModel.invoices.length == 0">
<p class="text-muted">No payments made yet.</p>
</template>
<template v-else v-for="invoice of srvModel.invoices" :key="invoice.id">
<table class="table my-0">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="table-responsive">
<table class="table table-light border-top-0 ">
<thead>
<tr class="table-borderless">
<th class="font-weight-normal text-secondary" scope="col">Invoice Id</th>
<th class="font-weight-normal text-secondary">Price</th>
<th class="font-weight-normal text-secondary">Expiry</th>
<th class="font-weight-normal text-secondary text-right">Status</th>
</tr>
<tr>
<th class="border-top-0" scope="col">Invoice #</th>
<th class="border-top-0">Price</th>
<th class="border-top-0">Expiry</th>
<th class="border-top-0">Status</th>
</tr>
</thead>
<tbody>
<tr v-if="!srvModel.invoices || srvModel.invoices.length == 0">
<td colspan="4" class="text-center">No payments made yet</td>
</tr>
<template v-else v-for="invoice of srvModel.invoices" :key="invoice.id">
<tr>
<td>{{invoice.id}}</td>
<td scope="row">{{invoice.id}}</td>
<td>{{invoice.amountFormatted}}</td>
<td>{{moment(invoice.expiryDate).format('L HH:mm')}}</td>
<td class="text-right">{{invoice.status}}</td>
<td>{{invoice.status}}</td>
</tr>
<template v-if="invoice.payments && invoice.payments.length > 0">
<tr class="table-borderless table-light">
<th colspan="2" class="pl-3 font-weight-normal text-secondary">TX Id</th>
<th class="font-weight-normal text-secondary">Payment Method</th>
<th class="font-weight-normal text-secondary text-right">Amount</th>
</tr>
<tr v-for="payment of invoice.payments" class="table-borderless table-light">
<td colspan="2" class="pl-3 text-break">
<a v-if="payment.link" :href="payment.link" target="_blank">{{payment.id}}</a>
<span v-else>{{payment.id}}</span>
</td>
<td>{{formatPaymentMethod(payment.paymentMethod)}}</td>
<td class="text-right">{{payment.amount.noExponents()}}</td>
</tr>
</template>
<tr v-if="invoice.payments && invoice.payments.length > 0">
<td colspan="4" class=" px-2 py-1 border-top-0">
<div class="table-responsive">
<table class="table table-bordered">
<tr>
<th class="p-1" style="max-width: 300px">Tx Id</th>
<th class="p-1">Payment Method</th>
<th class="p-1">Amount</th>
<th class="p-1">Link</th>
</tr>
<tr v-for="payment of invoice.payments">
<td class="p-1 m-0 d-print-none d-block" style="max-width: 300px">
<div style="width: 100%; overflow-x: auto; overflow-wrap: initial;">{{payment.id}}</div>
</td>
<td class="p-1 m-0 d-none d-print-table-cell" style="max-width: 150px;">
{{payment.id}}
</td>
<td class="p-1">{{formatPaymentMethod(payment.paymentMethod)}}</td>
<td class="p-1">{{payment.amount.noExponents()}}</td>
<td class="p-1 d-print-none">
<a v-if="payment.link" :href="payment.link" target="_blank">Link</a>
</td>
<td class="p-1 d-none d-print-table-cell" style="max-width: 150px;">
{{payment.link}}
</td>
</tr>
</table>
</div>
</td>
</tr>
</template>
<tr v-if="srvModel.archived">
<td colspan="4" class="text-center">
<button type="button" class="btn btn-secondary" disabled>Archived</button>
</td>
</tr>
<tr v-else-if="!ended && (srvModel.amountDue) > 0" class="d-print-none">
<td colspan="4" class="text-center">
<template v-if="srvModel.allowCustomPaymentAmounts && !srvModel.anyPendingInvoice">
<form v-on:submit="submitCustomAmountForm">
<div class="input-group m-auto" style="max-width: 250px">
<input
:readonly="!srvModel.allowCustomPaymentAmounts"
class="form-control"
type="number"
v-model="customAmount"
:max="srvModel.amountDue"
step="any"
placeholder="Amount"
required>
<div class="input-group-append">
<span class='input-group-text'>{{currency}}</span>
<button
class="btn btn-primary"
v-bind:class="{ 'btn-disabled': loading}"
:disabled="loading"
type="submit">
<div v-if="loading" class="spinner-grow spinner-grow-sm" role="status">
<span class="sr-only">Loading...</span>
</div>
Pay now
</button>
</div>
</div>
</form>
</template>
<template v-else>
<button class="btn btn-primary btn-lg mt-1" v-on:click="pay(null)"
:disabled="loading">
<div v-if="loading" class="spinner-grow spinner-grow-sm" role="status">
<span class="sr-only">Loading...</span>
</div>
Pay now
</button>
<button class="btn btn-secondary btn-lg mt-1"
v-if="srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments"
v-on:click="cancelPayment()"
:disabled="loading">
<div v-if="loading" class="spinner-grow spinner-grow-sm" role="status">
<span class="sr-only">Loading...</span>
</div>
Cancel current invoice</button>
</template>
</td>
</tr>
</tbody>
</table>
</template>
</div>
</div>
</div>
</div>
<div class="card-footer text-muted d-flex justify-content-between">
<div>
<span v-on:click="print" class="btn-link d-print-none" style="cursor: pointer"> <span class="fa fa-print"></span> Print</span>
<span>Updated {{lastUpdated}}</span>
</div>
<div>
<span class="text-muted">Powered by </span><a href="https://btcpayserver.org" target="_blank">BTCPay Server</a>
</div>
</div>
</div>
</div>
</main>
<footer class="pt-2 pb-4 d-print-none">
<div class="container text-center">
Powered by <a href="https://btcpayserver.org" target="_blank">BTCPay Server</a>
</div>
</footer>
</div>
</div>
}
</body>
</html>

View file

@ -2,14 +2,14 @@
@model BTCPayServer.Models.ViewPullPaymentModel
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
@{
ViewData["Title"] = Model.Title;
Layout = null;
}
<!DOCTYPE html>
<html lang="en" @(env.IsDeveloping ? " data-devenv" : "")>
<html class="h-100">
<head>
<title>@ViewData["Title"]</title>
<meta charset="utf-8" />
@ -18,189 +18,155 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.BootstrapUri)" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(themeManager.ThemeUri)" rel="stylesheet" />
<bundle name="wwwroot/bundles/payment-request-bundle.min.css" asp-append-version="true"></bundle>
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" />
}
<bundle name="wwwroot/bundles/payment-request-bundle.min.css" asp-append-version="true"></bundle>
@Safe.Raw(Model.EmbeddedCSS)
<noscript>
<style>
.hide-when-js, [v-cloak] { display: block !important; }
.only-for-js { display: none !important; }
</style>
</noscript>
</head>
<body class="h-100">
<div class="h-100 d-flex flex-column">
@if (Model.IsPending)
{
<nav id="mainNav" class="navbar sticky-top py-3 py-lg-4 d-print-block">
<div class="container">
<form asp-action="ClaimPullPayment" asp-route-pullPaymentId="@Model.Id" class="w-100">
<div class="row align-items-center" style="width:calc(100% + 30px)">
<div class="col-12 mb-3 col-lg-6 mb-lg-0">
<input class="form-control form-control-lg text-monospace w-100" asp-for="Destination" placeholder="Enter destination address to claim funds …" required style="font-size:.9rem;height:42px;">
</div>
<div class="col-12 mb-3 col-sm-6 mb-sm-0 col-lg-3">
<div class="input-group">
<input type="number" class="form-control form-control-lg text-right hide-number-spin" asp-for="ClaimedAmount" max="@Model.AmountDue" min="@Model.MinimumClaim" step="any" placeholder="Amount" required>
<div class="input-group-append">
<span class="input-group-text px-3">@Model.Currency.ToUpper()</span>
<body>
<div class="container" style="max-width:960px;">
<div class="row w-100 p-0 m-0" style="height: 100vh">
<div class="mx-auto my-auto w-100">
@if (TempData.HasStatusMessage())
{
<partial name="_StatusMessage" />
}
@if (!this.ViewContext.ModelState.IsValid)
{
@Html.ValidationSummary(string.Empty, new { @class = "alert alert-danger pb-0 text-center" })
}
<div class="card">
@if (!Model.Title.IsNullOrWhiteSpace())
{
<h1 class="card-header px-3">
@Model.Title
<span class="text-muted float-right text-center">@Model.Status</span>
</h1>
}
@if (Model.IsPending)
{
<div class="card-body" style="padding-left:.75rem;padding-right:.75rem;">
<form asp-action="ClaimPullPayment" asp-route-pullPaymentId="@Model.Id">
<div class="row">
<div class="col-12 mb-2 col-lg-6 mb-lg-0">
<input class="form-control text-monospace w-100"
asp-for="Destination"
placeholder="Destination address"
required
style="font-size:.9rem;height:38px;">
</div>
<div class="col-12 mb-2 col-sm-6 mb-sm-0 col-lg-3">
<div class="input-group">
<input class="form-control"
asp-for="ClaimedAmount"
type="number"
max="@Model.AmountDue"
min="@Model.MinimumClaim"
step="any"
placeholder="Amount"
required>
<div class="input-group-append">
<span class='input-group-text'>@Model.Currency.ToUpper()</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-lg-3">
<button class="form-control btn btn-primary" type="submit">Claim now</button>
</div>
</div>
<div class="col-12 col-sm-6 col-lg-3">
<button class="btn btn-lg btn-primary w-100 text-nowrap" type="submit">Claim Funds</button>
</div>
</div>
</form>
</div>
</nav>
}
<main class="flex-grow-1 py-4">
<div class="container">
@await Html.PartialAsync("_StatusMessage", new ViewDataDictionary(ViewData){ { "Margin", "mb-4" } })
@if (!ViewContext.ModelState.IsValid)
{
@Html.ValidationSummary(string.Empty, new { @class = "alert alert-danger mb-4 pb-0 text-center" })
</form>
</div>
}
<div class="row">
<div class="col col-12 col-lg-6 mb-4">
<div class="jumbotron h-100 m-0 p-sm-5">
@if (!Model.Title.IsNullOrWhiteSpace())
{
<h2 class="h4 mb-3">@Model.Title</h2>
}
<div class="d-flex align-items-center">
<span class="text-muted text-nowrap">Last Updated</span>
&nbsp;
<span class="text-nowrap">@Model.LastUpdated.ToString("g")</span>
<button type="button" class="btn btn-link d-none d-lg-inline-block d-print-none border-0 p-0 ml-4 only-for-js" id="copyLink">
Copy Link
</button>
</div>
@if (!string.IsNullOrEmpty(Model.ResetIn))
{
<p>
<span class="text-muted text-nowrap">Reset in</span>
&nbsp;
<span class="text-nowrap">@Model.ResetIn</span>
</p>
}
@if (!string.IsNullOrEmpty(Model.Description) && Model.Description != "<br>")
{
<div>@Safe.Raw(Model.Description)</div>
}
</div>
</div>
<div class="col col-12 col-lg-6 mb-4">
<div class="jumbotron h-100 m-0 p-sm-5">
<h2 class="h4 mb-3">Payment Details</h2>
<dl class="mb-0 mt-md-4">
<div class="d-flex flex-column mb-4">
<dt class="h4 font-weight-normal text-nowrap text-primary order-2 order-sm-1 mb-0">@Model.AmountDueFormatted</dt>
<dd class="text-muted order-1 order-sm-2 mb-1">Available claim</dd>
</div>
<div class="progress bg-light d-none d-sm-flex mb-sm-4 d-print-none" style="height:5px">
<div class="progress-bar bg-primary" role="progressbar" style="width:@((Model.AmountCollected / Model.Amount) * 100)%"></div>
</div>
<div class="d-flex flex-column mb-4 d-sm-inline-flex mb-sm-0">
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0">@Model.AmountCollectedFormatted</dt>
<dd class="text-muted order-1 order-sm-2 mb-1">Already claimed</dd>
</div>
<div class="d-flex flex-column mb-0 d-sm-inline-flex float-sm-right">
<dt class="h4 font-weight-normal text-nowrap order-2 order-sm-1 mb-0">@Model.AmountFormatted</dt>
<dd class="text-muted text-sm-right order-1 order-sm-2 mb-1">Claim limit</dd>
</div>
</dl>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="jumbotron h-100 m-0 p-sm-5">
<h2 class="h4 mb-3">Awaiting Claims</h2>
<div class="table-responsive">
@if (Model.Payouts.Any())
<div class="card-body px-0 pt-0 pb-0">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<h5 style="margin:1rem .75rem;">Pull payment details</h5>
<table class="table mb-lg-0 border-top-0">
<tr class="bg-light">
<td class="font-weight-bold">Claim limit:</td>
<td class="text-right">@Model.AmountFormatted</td>
</tr>
<tr class="bg-light">
<td class="font-weight-bold">Already claimed:</td>
<td class="text-right">@Model.AmountCollectedFormatted</td>
</tr>
<tr class="bg-light">
<td class="font-weight-bold">Available claim:</td>
<td class="text-right">@Model.AmountDueFormatted</td>
</tr>
@if (Model.ResetIn != String.Empty)
{
<table class="table my-0">
<thead>
<tr class="table-borderless">
<th class="font-weight-normal text-secondary" scope="col">Destination</th>
<th class="font-weight-normal text-secondary text-right text-nowrap">Amount requested</th>
<th class="font-weight-normal text-secondary text-right">Status</th>
<tr class="bg-light">
<td class="font-weight-bold">Reset in:</td>
<td class="text-right">@Model.ResetIn</td>
</tr>
}
</table>
@if (Model.Description != null && Model.Description != "" && Model.Description != "<br>")
{
<div class="w-100 px-3 pt-4 pb-3">@Safe.Raw(Model.Description)</div>
}
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<h5 style="margin:1rem .75rem;">Awaiting claims</h5>
@if (Model.Payouts.Any())
{
foreach (var invoice in Model.Payouts)
{
<table class="table border-top-0 mt-3 mb-0">
<tr class="bg-light">
<td class="font-weight-bold">Status</td>
<td class="text-right">@invoice.Status</td>
</tr>
</thead>
<tbody>
@foreach (var invoice in Model.Payouts)
<tr class="bg-light">
<td class="font-weight-bold">Amount&nbsp;claimed</td>
<td class="text-right">@invoice.AmountFormatted</td>
</tr>
<tr class="bg-light">
<td class="font-weight-bold">Destination</td>
<td class="text-right text-break">
<code>@invoice.Destination</code>
</td>
</tr>
@if (!String.IsNullOrEmpty(invoice.Link))
{
<tr>
<td class="text-break">
@invoice.Destination
</td>
<td class="text-right">@invoice.AmountFormatted</td>
<td class="text-right text-nowrap">
@{
string textClass;
switch (invoice.Status)
{
case "Completed":
case "In Progress":
textClass = "text-success";
break;
case "Cancelled":
textClass = "text-danger";
break;
default:
textClass = "text-warning";
break;
}
}
@if (!string.IsNullOrEmpty(invoice.Link))
{
<a class="transaction-link @textClass" href="@invoice.Link">@invoice.Status</a>
}
else
{
<span class="@textClass">@invoice.Status</span>
}
</td>
<tr class="bg-light">
<td class="font-weight-bold">Transaction</td>
<td class="text-right text-truncate" style="max-width: 100px;"><a class="transaction-link" href="@invoice.Link">@invoice.TransactionId</a></td>
</tr>
}
</tbody>
</table>
}
else
{
<p class="text-muted">No claim made yet.</p>
}
</div>
}
else
{
<p class="text-secondary">No claim made yet</p>
}
</div>
</div>
</div>
<div class="card-footer text-muted">
<div class="row">
<div class="col-12 col-sm-6">
Updated @Model.LastUpdated.ToString("g")
</div>
<div class="col-12 col-sm-6 text-sm-right">
Powered by <a href="https://btcpayserver.org" target="_blank">BTCPay Server</a>
</div>
</div>
</div>
</div>
</main>
<footer class="pt-2 pb-4 d-print-none">
<div class="container text-center">
Powered by <a href="https://btcpayserver.org" target="_blank">BTCPay Server</a>
</div>
</footer>
</div>
</div>
<script>
document.getElementById("copyLink").addEventListener("click", function (e) {
if (navigator.clipboard) {
e.preventDefault();
var button = e.currentTarget;
if (!button.dataset.initialText) button.dataset.initialText = button.innerText;
navigator.clipboard.writeText(window.location).then(function () {
button.innerText = 'Copied ✔';
setTimeout(function() { button.innerText = button.dataset.initialText; }, 2500);
});
button.blur();
}
});
</script>
</div>
<style>
tbody + tbody { padding-top: 1.5rem; }
</style>
</body>
</html>

View file

@ -21,10 +21,3 @@
<bundle name="wwwroot/bundles/main-bundle.min.css" asp-append-version="true" />
@* JS *@
<bundle name="wwwroot/bundles/main-bundle.min.js" asp-append-version="true" />
@* Non-JS *@
<noscript>
<style>
.hide-when-js, [v-cloak] { display: block !important; }
.only-for-js { display: none !important; }
</style>
</noscript>

View file

@ -5,13 +5,30 @@
@inject BTCPayServer.HostedServices.CssThemeManager themeManager
<!DOCTYPE html>
<html lang="en"@(env.IsDeveloping ? " data-devenv" : "")>
<html lang="en">
<head>
<partial name="Header" />
@RenderSection("HeadScripts", required: false)
@RenderSection("HeaderContent", false)
<noscript>
<style>
.hide-when-js {
display: block !important;
}
.only-for-js {
display: none !important;
}
[v-cloak]::before {
content: "" !important;
}
</style>
</noscript>
</head>
<body id="page-top">

View file

@ -4,10 +4,10 @@
@if (parsedModel != null)
{
<div class="alert alert-@parsedModel.SeverityCSS @(parsedModel.AllowDismiss? "alert-dismissible":"" ) @(ViewData["Margin"] ?? "mb-5") text-break" role="alert">
<div class="alert alert-@parsedModel.SeverityCSS @(parsedModel.AllowDismiss? "alert-dismissible":"" ) mb-5 text-break" role="alert">
@if (parsedModel.AllowDismiss)
{
<button type="button" class="close only-for-js" data-dismiss="alert" aria-label="Close">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
}

View file

@ -166,7 +166,7 @@
]
},
{
"outputFileName": "wwwroot/bundles/payment-request-bundle.min.js",
"outputFileName": "wwwroot/bundles/payment-request-bundle-1.min.js",
"inputFiles": [
"wwwroot/vendor/vuejs/vue.min.js",
"wwwroot/vendor/babel-polyfill/polyfill.min.js",
@ -174,8 +174,13 @@
"wwwroot/vendor/bootstrap-vue/bootstrap-vue.js",
"wwwroot/vendor/signalr/signalr.js",
"wwwroot/vendor/animejs/anime.min.js",
"wwwroot/vendor/moment/moment.min.js",
"wwwroot/payment-request/**/*.js"
]
},
{
"outputFileName": "wwwroot/bundles/payment-request-bundle-2.min.js",
"inputFiles": [
"wwwroot/vendor/moment/moment.min.js"
],
"minify": {
"enabled": false
@ -186,7 +191,7 @@
"inputFiles": [
"wwwroot/vendor/font-awesome/css/font-awesome.min.css",
"wwwroot/vendor/bootstrap-vue/bootstrap-vue.css",
"wwwroot/main/site.css"
"wwwroot/payment-request/**/*.css"
]
},
{

View file

@ -176,14 +176,8 @@ pre {
background-color: lightgray;
}
/* Fix for small table showing unnecessary scrollbars */
.table-responsive .table-sm {
width: 100% !important;
}
[v-cloak] { display:none }
[v-cloak-loading] > * { display:none }
[v-cloak-loading]::before { content: "loading…" }
[v-cloak] > * { display:none }
[v-cloak]::before { content: "loading…" }
.cursor-pointer{
cursor: pointer;
@ -248,54 +242,3 @@ pre {
display: inline-block;
margin-top: .5rem;
}
/* Chrome, Safari, Edge, Opera */
input[type=number].hide-number-spin::-webkit-outer-spin-button,
input[type=number].hide-number-spin::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number].hide-number-spin {
-moz-appearance: textfield;
}
/* Bootstrap Responsive Helper */
html[data-devenv]:before {
display: inline-block;
position: fixed;
z-index: 1000;
right: 0;
bottom: 0;
background: var(--btcpay-bg-dark);
color: var(--btcpay-color-dark-text);
opacity: .7;
padding: 4px 5px 3px 7px;
font-size: 10px;
border-top-left-radius: 4px;
}
@media (max-width: 575px) { html[data-devenv]:before { content: 'XS'; } }
@media (min-width: 576px) and (max-width: 767px) { html[data-devenv]:before { content: 'SM'; } }
@media (min-width: 768px) and (max-width: 991px) { html[data-devenv]:before { content: 'MD'; } }
@media (min-width: 992px) and (max-width: 1199px) { html[data-devenv]:before { content: 'LG'; } }
@media (min-width: 1200px) { html[data-devenv]:before { content: 'XL'; } }
@media print { html[data-devenv]:before { content: none; } }
/* Print */
@media print {
.jumbotron {
padding: 1rem 0 !important;
}
}
/* Richtext editor */
.note-editor .table.table-sm {
border-collapse: collapse !important;
}
.note-editor .table.table-sm th,
.note-editor .table.table-sm td {
border: 1px dotted var(--btcpay-color-neutral-400);
}

View file

@ -85,17 +85,7 @@ $(function () {
e.preventDefault();
var item = e.currentTarget;
var text = item.getAttribute('data-clipboard');
var confirm = item.querySelector('[data-clipboard-confirm]') || item;
var message = confirm.getAttribute('data-clipboard-confirm') || 'Copied ✔';
if (!confirm.dataset.clipboardInitialText) {
confirm.dataset.clipboardInitialText = confirm.innerText;
console.log(confirm.clientWidth)
confirm.style.minWidth = confirm.clientWidth + 'px';
}
navigator.clipboard.writeText(text).then(function () {
confirm.innerText = message;
setTimeout(function(){ confirm.innerText = confirm.dataset.clipboardInitialText; }, 2500);
});
navigator.clipboard.writeText(text);
item.blur();
}
});

View file

@ -1,11 +1,6 @@
$(document).ready(function() {
$(".richtext").summernote({
minHeight: 300,
tableClassName: 'table table-sm',
insertTableMaxSize: {
col: 5,
row: 10
}
minHeight: 300
});
});

View file

@ -18,6 +18,7 @@ function addLoadEvent(func) {
addLoadEvent(function (ev) {
Vue.use(Toasted);
app = new Vue({
el: '#app',
data: function () {
@ -28,6 +29,7 @@ addLoadEvent(function (ev) {
ended: false,
endDiff: "",
active: true,
lastUpdated: "",
loading: false,
timeoutState: "",
customAmount: null
@ -39,12 +41,6 @@ addLoadEvent(function (ev) {
},
settled: function () {
return this.srvModel.amountDue <= 0;
},
lastUpdated: function () {
return this.srvModel.lastUpdated && moment(this.srvModel.lastUpdated).calendar();
},
active: function () {
return !this.ended;
}
},
methods: {
@ -53,6 +49,7 @@ addLoadEvent(function (ev) {
var endDateM = moment(this.srvModel.expiryDate);
this.endDate = endDateM.format('MMMM Do YYYY');
this.ended = endDateM.isBefore(moment());
} else {
this.ended = false;
this.endDate = null;
@ -67,6 +64,8 @@ addLoadEvent(function (ev) {
this.endDiff = mDiffD > 0 ? mDiffD + " days" : mDiffH > 0 ? mDiffH + " hours" : mDiffM > 0 ? mDiffM + " minutes" : mDiffS > 0 ? mDiffS + " seconds" : "";
}
this.lastUpdated = moment(this.srvModel.lastUpdated).calendar();
this.active = !this.ended;
setTimeout(this.updateComputed, 1000);
},
setLoading: function (val) {
@ -84,18 +83,6 @@ addLoadEvent(function (ev) {
eventAggregator.$emit("pay", amount);
},
copyLink: function (e) {
if (navigator.clipboard) {
e.preventDefault();
var button = e.currentTarget;
if (!button.dataset.initialText) button.dataset.initialText = button.innerText;
navigator.clipboard.writeText(window.location).then(function () {
button.innerText = 'Copied ✔';
setTimeout(function() { button.innerText = button.dataset.initialText; }, 2500);
});
button.blur();
}
},
cancelPayment: function (amount) {
this.setLoading(true);
var self = this;
@ -113,6 +100,9 @@ addLoadEvent(function (ev) {
return str;
},
print:function(){
window.print();
},
submitCustomAmountForm : function(e){
if (e) {
e.preventDefault();
@ -125,10 +115,10 @@ addLoadEvent(function (ev) {
}
},
mounted: function () {
this.customAmount = (this.srvModel.amountDue || 0).noExponents();
hubListener.connect();
var self = this;
eventAggregator.$on("invoice-created", function (invoiceId) {
self.setLoading(false);
btcpay.showInvoice(invoiceId);

View file

@ -0,0 +1,7 @@
[v-cloak] > * {
display: none
}
[v-cloak]::before {
content: "loading…"
}