mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 14:22:40 +01:00
Crowdfund public UI re-design (#2918)
* Patch bootstrap-vue modal close button For Bootstrap v5 compatibility, which bootstrap-vue does not have currently. * Crowdfund: Use common global layout head (and themes) * Crowdfund: Display disabled alert also for simple variant * Crowdfund: Unify non-JS/simple and JS-enabled view * Improve fireworks animation * Improve layout Inspired by the Bitcoin Smiles compaign, see #2783 * Cleanup and remove views * Fix typo * Fix test
This commit is contained in:
parent
bd6c7a8c3d
commit
a3afcd2a6e
13 changed files with 452 additions and 575 deletions
|
@ -552,7 +552,7 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.Id("TargetAmount")).SendKeys("700");
|
||||
s.Driver.FindElement(By.Id("SaveSettings")).Click();
|
||||
s.Driver.FindElement(By.Id("ViewApp")).Click();
|
||||
Assert.Equal("Currently Active!", s.Driver.FindElement(By.CssSelector(".h6.text-muted")).Text);
|
||||
Assert.Equal("currently active!", s.Driver.FindElement(By.CssSelector("[data-test='time-state']")).Text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -258,8 +258,8 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
return NotFound("A Target Currency must be set for this app in order to be loadable.");
|
||||
}
|
||||
var appInfo = (ViewCrowdfundViewModel)(await _AppService.GetAppInfo(appId));
|
||||
appInfo.HubPath = AppHub.GetHubPath(this.Request);
|
||||
var appInfo = await GetAppInfo(appId);
|
||||
|
||||
if (settings.Enabled)
|
||||
return View(appInfo);
|
||||
if (!isAdmin)
|
||||
|
@ -287,7 +287,6 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound();
|
||||
var settings = app.GetSettings<CrowdfundSettings>();
|
||||
|
||||
|
||||
var isAdmin = await _AppService.GetAppDataIfOwner(GetUserId(), appId, AppType.Crowdfund) != null;
|
||||
|
||||
if (!settings.Enabled && !isAdmin)
|
||||
|
@ -295,8 +294,7 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound("Crowdfund is not currently active");
|
||||
}
|
||||
|
||||
var info = (ViewCrowdfundViewModel)await _AppService.GetAppInfo(appId);
|
||||
info.HubPath = AppHub.GetHubPath(this.Request);
|
||||
var info = await GetAppInfo(appId);
|
||||
if (!isAdmin &&
|
||||
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
|
||||
(settings.EndDate.HasValue && DateTime.Now > settings.EndDate) ||
|
||||
|
@ -379,9 +377,15 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
return BadRequest(e.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task<ViewCrowdfundViewModel> GetAppInfo(string appId)
|
||||
{
|
||||
var info = (ViewCrowdfundViewModel)await _AppService.GetAppInfo(appId);
|
||||
info.HubPath = AppHub.GetHubPath(Request);
|
||||
info.SimpleDisplay = Request.Query.ContainsKey("simple");
|
||||
return info;
|
||||
}
|
||||
|
||||
private string GetUserId()
|
||||
{
|
||||
|
|
|
@ -96,7 +96,7 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
[Display(Name = "Sounds to play when a payment is made. One sound per line")]
|
||||
public string Sounds { get; set; }
|
||||
|
||||
[Display(Name = "Colors to rotate between with animation when a payment is made. First color is the default background. One color per line. Can be any valid css color value.")]
|
||||
[Display(Name = "Colors to rotate between with animation when a payment is made. One color per line (any valid css color value).")]
|
||||
public string AnimationColors { get; set; }
|
||||
|
||||
// NOTE: Improve validation if needed
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
public CrowdfundInfo Info { get; set; }
|
||||
public string Tagline { get; set; }
|
||||
public ViewPointOfSaleViewModel.Item[] Perks { get; set; }
|
||||
public bool SimpleDisplay { get; set; }
|
||||
public bool DisqusEnabled { get; set; }
|
||||
public bool SoundsEnabled { get; set; }
|
||||
public string DisqusShortname { get; set; }
|
||||
|
|
|
@ -6,65 +6,63 @@
|
|||
<div class="card mb-4 perk expanded" id="@item.Id">
|
||||
@if (Model.ViewCrowdfundViewModel.DisplayPerksRanking && Model.ViewCrowdfundViewModel.PerkCount.ContainsKey(item.Id))
|
||||
{
|
||||
<span class="btn btn-sm rounded-circle px-0 btn-primary perk-badge">#@(Array.IndexOf(Model.ViewCrowdfundViewModel.Perks, item) + 1)</span>
|
||||
<span class="btn btn-sm rounded-circle px-0 btn-primary perk-badge">#@(Array.IndexOf(Model.ViewCrowdfundViewModel.Perks, item) + 1)</span>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(item.Image))
|
||||
{
|
||||
<img class="card-img-top" src="@item.Image" asp-append-version="true" />
|
||||
<img class="card-img-top" src="@item.Image" alt="@item.Title" asp-append-version="true" />
|
||||
}
|
||||
<div class="card-body">
|
||||
<div class="card-title d-flex justify-content-between">
|
||||
<label class="h5">
|
||||
|
||||
<div class="card-title d-flex align-items-center justify-content-between mb-1">
|
||||
<label class="h5 d-flex align-items-center">
|
||||
@if (Model.ViewCrowdfundViewModel.Started && !Model.ViewCrowdfundViewModel.Ended && (item.Price.Value > 0 || item.Custom))
|
||||
{
|
||||
|
||||
<input type="radio" asp-for="ChoiceKey" value="@item.Id"/>
|
||||
<input type="radio" asp-for="ChoiceKey" value="@item.Id" class="form-check-input mt-0 me-2"/>
|
||||
}
|
||||
@(string.IsNullOrEmpty(item.Title) ? item.Id : item.Title)
|
||||
</label>
|
||||
<span class="text-muted">
|
||||
@if (item.Price.Value > 0)
|
||||
{
|
||||
@item.Price.Value
|
||||
<span>@item.Price.Value</span>
|
||||
<span>@Model.ViewCrowdfundViewModel.TargetCurrency</span>
|
||||
|
||||
if (item.Custom)
|
||||
{
|
||||
@Safe.Raw("or more");
|
||||
@Safe.Raw("or more")
|
||||
}
|
||||
}
|
||||
else if (item.Custom)
|
||||
{
|
||||
@Safe.Raw("Any amount");
|
||||
@Safe.Raw("Any amount")
|
||||
}
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<p class="card-text overflow-hidden">@Safe.Raw(item.Description)</p>
|
||||
|
||||
</div>
|
||||
@if (Model.ViewCrowdfundViewModel.PerkCount.ContainsKey(item.Id) || item.Inventory.HasValue)
|
||||
{
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
@switch (item.Inventory)
|
||||
{
|
||||
case null:
|
||||
<span></span>
|
||||
break;
|
||||
case int i when i <= 0:
|
||||
<span>Sold out</span>
|
||||
break;
|
||||
default:
|
||||
<span>@item.Inventory left</span>
|
||||
break;
|
||||
}
|
||||
@if (Model.ViewCrowdfundViewModel.PerkCount.ContainsKey(item.Id))
|
||||
{
|
||||
<span> @Model.ViewCrowdfundViewModel.PerkCount[item.Id] Contributors</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@switch (item.Inventory)
|
||||
{
|
||||
case null:
|
||||
<span></span>
|
||||
}
|
||||
break;
|
||||
case int i when i <= 0:
|
||||
<span>Sold out</span>
|
||||
break;
|
||||
default:
|
||||
<span>@item.Inventory left</span>
|
||||
break;
|
||||
}
|
||||
@if (Model.ViewCrowdfundViewModel.PerkCount.ContainsKey(item.Id))
|
||||
{
|
||||
<span>@Model.ViewCrowdfundViewModel.PerkCount[item.Id] Contributors</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span></span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -72,16 +70,16 @@
|
|||
@if (Model.ViewCrowdfundViewModel.Started && !Model.ViewCrowdfundViewModel.Ended)
|
||||
{
|
||||
<div class="form-group">
|
||||
<label asp-for="Email"></label>
|
||||
<label asp-for="Email" class="form-label"></label>
|
||||
<input asp-for="Email" type="email" class="form-control"/>
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Amount"></label>
|
||||
<div class="input-group mb-3">
|
||||
<input asp-for="Amount" type="number" step="any" class="form-control"/>
|
||||
<span class="input-group-text">@Model.ViewCrowdfundViewModel.TargetCurrency.ToUpperInvariant()</span>
|
||||
</div>
|
||||
<label asp-for="Amount" class="form-label"></label>
|
||||
<div class="input-group mb-3">
|
||||
<input asp-for="Amount" type="number" step="any" class="form-control"/>
|
||||
<span class="input-group-text">@Model.ViewCrowdfundViewModel.TargetCurrency.ToUpperInvariant()</span>
|
||||
</div>
|
||||
<span asp-validation-for="Amount" class="text-danger"></span>
|
||||
</div>
|
||||
<input type="hidden" asp-for="RedirectToCheckout"/>
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
@using BTCPayServer.Models.AppViewModels
|
||||
@using Microsoft.CodeAnalysis.CSharp.Syntax
|
||||
@model BTCPayServer.Models.AppViewModels.ViewCrowdfundViewModel
|
||||
|
||||
|
||||
<div class="container p-0">
|
||||
<div class="row h-100 w-100 py-sm-0 py-md-4 mx-0">
|
||||
<div class="card w-100 p-0 mx-0">
|
||||
@if (!string.IsNullOrEmpty(Model.MainImageUrl))
|
||||
{
|
||||
<img class="card-img-top" src="@Model.MainImageUrl" asp-append-version="true" />
|
||||
}
|
||||
<div class="d-flex justify-content-between px-2">
|
||||
<h1>
|
||||
@Model.Title
|
||||
@if (!Model.Started && Model.StartDate.HasValue)
|
||||
{
|
||||
<span class="h6 text-muted">
|
||||
Starts @Model.StartDate.Value.Subtract(DateTime.Now.ToUniversalTime())
|
||||
</span>
|
||||
}
|
||||
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
|
||||
{
|
||||
<span class="h6 text-muted">
|
||||
Ends @Model.EndDate.Value.Subtract(DateTime.Now.ToUniversalTime())
|
||||
</span>
|
||||
}
|
||||
else if (Model.Started && !Model.Ended && !Model.EndDate.HasValue)
|
||||
{
|
||||
<span class="h6 text-muted" :title="startDate" title="No set end date">
|
||||
Currently Active!
|
||||
</span>
|
||||
}
|
||||
|
||||
</h1>
|
||||
@if (Model.TargetAmount.HasValue)
|
||||
{
|
||||
<span class="mt-3">
|
||||
<span class="h5">@Model.TargetAmount @Model.TargetCurrency</span>
|
||||
@if (Model.ResetEveryAmount > 0 && !Model.NeverReset)
|
||||
{
|
||||
<span> Dynamic</span>
|
||||
}
|
||||
@if (Model.EnforceTargetAmount)
|
||||
{
|
||||
<span >Hardcap Goal <span class="fa fa-question-circle" ></span></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span >Softcap Goal <span class="fa fa-question-circle" ></span> </span>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
@if (Model.TargetAmount.HasValue)
|
||||
{
|
||||
<div class="progress w-100 rounded-0 " >
|
||||
<div class="progress-bar" role="progressbar"
|
||||
style="width:@(Model.Info.ProgressPercentage + "%")"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
<div class="progress-bar bg-warning" role="progressbar"
|
||||
style="width:@(Model.Info.PendingProgressPercentage + "%")"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
|
||||
<div class="row py-2 text-center">
|
||||
<div class="col-sm border-right" id="raised-amount">
|
||||
<h5>@(Model.Info.CurrentAmount + Model.Info.CurrentPendingAmount) @Model.TargetCurrency </h5>
|
||||
<h5 class="text-muted">Raised</h5>
|
||||
</div>
|
||||
<div class="col-sm border-right">
|
||||
<h5>@(Model.Info.PendingProgressPercentage.GetValueOrDefault(0) + Model.Info.ProgressPercentage.GetValueOrDefault(0))%</h5>
|
||||
<h5 class="text-muted">Of Goal</h5>
|
||||
</div>
|
||||
<div class="col-sm text-end">
|
||||
<h5>
|
||||
@Model.Info.TotalContributors
|
||||
</h5>
|
||||
<h5 class="text-muted">Contributors</h5>
|
||||
</div>
|
||||
@if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
|
||||
{
|
||||
<div class="col-sm">
|
||||
<h5>
|
||||
@TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)
|
||||
|
||||
</h5>
|
||||
<h5 class="text-muted">Ends</h5>
|
||||
</div>
|
||||
} else if (!Model.Started)
|
||||
{
|
||||
<div class="col-sm">
|
||||
<h5>
|
||||
@TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)
|
||||
</h5>
|
||||
<h5 class="text-muted">Starts</h5>
|
||||
</div>
|
||||
}else if (Model.Ended)
|
||||
{
|
||||
|
||||
<div class="col-sm" id="inactive-campaign">
|
||||
<h5>
|
||||
Campaign
|
||||
</h5>
|
||||
<h5 >not active</h5>
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card-title">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 ">
|
||||
<h2 class="text-muted" >@Model.Tagline</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-sm-12">
|
||||
<div class="card-text overflow-hidden">@Safe.Raw(Model.Description)</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<partial
|
||||
name="/Views/AppsPublic/Crowdfund/ContributeForm.cshtml"
|
||||
model="@(new ContributeToCrowdfund { ViewCrowdfundViewModel = Model, RedirectToCheckout = true })"></partial>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer text-muted d-flex">
|
||||
<div class="align-self-end pe-4">Updated @Model.Info.LastUpdated</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,324 +0,0 @@
|
|||
<div class="container p-0" id="app" v-cloak>
|
||||
@if(!Model.Enabled)
|
||||
{
|
||||
<div class="alert alert-warning" role="alert" style="text-align: center;">
|
||||
This crowdfund page is not publically viewable!
|
||||
</div>
|
||||
}
|
||||
<div class="row h-100 w-100 py-sm-0 py-md-4 mx-0">
|
||||
<div class="card w-100 p-0 mx-0">
|
||||
<img class="card-img-top" :src="srvModel.mainImageUrl" v-if="srvModel.mainImageUrl" id="crowdfund-main-image">
|
||||
<div class="d-flex justify-content-between px-2" id="crowdfund-header-container">
|
||||
<h1>
|
||||
{{srvModel.title}}
|
||||
<span class="h6 text-muted" v-if="!started && srvModel.startDate" v-b-tooltip :title="startDate" id="crowdfund-header-start-date">
|
||||
Starts in {{startDiff}}
|
||||
</span>
|
||||
<span class="h6 text-muted" v-else-if="started && !ended && srvModel.endDate" v-b-tooltip :title="endDate" id="crowdfund-header-end-date">
|
||||
Ends in {{endDiff}}
|
||||
</span>
|
||||
<span class="h6 text-muted" v-else-if="started && !ended && !srvModel.endDate" v-b-tooltip title="No set end date" id="crowdfund-header-active">
|
||||
Currently Active!
|
||||
</span>
|
||||
|
||||
</h1>
|
||||
|
||||
<span v-if="srvModel.targetAmount" class="mt-3" id="crowdfund-header-target">
|
||||
<span class="h5" id="crowdfund-header-target-amount">{{srvModel.targetAmount}} {{targetCurrency}}</span>
|
||||
<span v-if="srvModel.resetEvery !== 'Never'"
|
||||
id="crowdfund-header-target-dynamic"
|
||||
v-b-tooltip
|
||||
:title="'Goal resets every ' + srvModel.resetEveryAmount + ' ' + srvModel.resetEvery + ((srvModel.resetEveryAmount>1)?'s': '')" >Dynamic </span>
|
||||
<span v-if="srvModel.enforceTargetAmount"
|
||||
id="crowdfund-header-target-softcap">Hardcap Goal <span class="fa fa-question-circle" v-b-tooltip title="No contributions allowed after the goal has been reached"></span></span>
|
||||
<span v-else
|
||||
id="crowdfund-header-target-hardcap">Softcap Goal <span class="fa fa-question-circle" v-b-tooltip title="Contributions allowed even after goal is reached"></span> </span>
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="progress w-100 rounded-0 " v-if="srvModel.targetAmount"
|
||||
id="crowdfund-progress-bar">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
:aria-valuenow="srvModel.info.progressPercentage"
|
||||
v-bind:style="{ width: srvModel.info.progressPercentage + '%' }"
|
||||
aria-valuemin="0"
|
||||
id="crowdfund-progress-bar-confirmed-bar"
|
||||
v-b-tooltip :title="parseFloat(srvModel.info.progressPercentage).toFixed(2) + '% contributions'"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
<div class="progress-bar bg-warning" role="progressbar"
|
||||
id="crowdfund-progress-bar-pending-bar"
|
||||
:aria-valuenow="srvModel.info.pendingProgressPercentage"
|
||||
v-bind:style="{ width: srvModel.info.pendingProgressPercentage + '%' }"
|
||||
v-b-tooltip :title="parseFloat(srvModel.info.pendingProgressPercentage).toFixed(2) + '% contributions pending confirmation'"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row py-2 text-center crowdfund-stats">
|
||||
<div class="col-sm border-right" id="crowdfund-body-raised-amount">
|
||||
<h5>{{ raisedAmount }} {{targetCurrency}} </h5>
|
||||
<h5 class="text-muted">Raised</h5>
|
||||
</div>
|
||||
<div class="col-sm border-right" id="crowdfund-body-goal-raised">
|
||||
<h5>{{ percentageRaisedAmount }}%</h5>
|
||||
<h5 class="text-muted">Of Goal</h5>
|
||||
</div>
|
||||
<div class="col-sm border-right" id="crowdfund-body-total-contributors">
|
||||
<h5>
|
||||
{{srvModel.info.totalContributors}}
|
||||
</h5>
|
||||
<h5 class="text-muted">Contributors</h5>
|
||||
</div>
|
||||
<div class="col-sm border-right" v-if="startDiff" id="crowdfund-body-campaign-dates-not-started">
|
||||
<h5>
|
||||
{{startDiff}}
|
||||
</h5>
|
||||
<h5 class="text-muted">Left to start</h5>
|
||||
|
||||
<b-tooltip target="crowdfund-body-campaign-dates-not-started" >
|
||||
<ul class="p-0">
|
||||
<li v-if="startDate" class="list-unstyled">
|
||||
{{started? "Started" : "Starts"}} {{startDate}}
|
||||
</li>
|
||||
<li v-if="endDate" class="list-unstyled">
|
||||
{{ended? "Ended" : "Ends"}} {{endDate}}
|
||||
</li>
|
||||
</ul>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
<div class="col-sm border-right" v-if="endDiff" id="crowdfund-body-campaign-dates-started">
|
||||
<h5>
|
||||
{{endDiff}}
|
||||
</h5>
|
||||
<h5 class="text-muted">Left</h5>
|
||||
<b-tooltip target="crowdfund-body-campaign-dates-started" >
|
||||
<ul class="p-0">
|
||||
<li v-if="startDate" class="list-unstyled">
|
||||
{{started? "Started" : "Starts"}} {{startDate}}
|
||||
</li>
|
||||
<li v-if="endDate" class="list-unstyled">
|
||||
{{ended? "Ended" : "Ends"}} {{endDate}}
|
||||
</li>
|
||||
</ul>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
<div class="col-sm border-right" v-if="ended" id="crowdfund-body-campaign-dates-not-active">
|
||||
<h5>
|
||||
Campaign
|
||||
</h5>
|
||||
<h5 >not active</h5>
|
||||
|
||||
<b-tooltip target="crowdfund-body-campaign-dates-not-active" >
|
||||
<ul class="p-0">
|
||||
<li v-if="startDate" class="list-unstyled">
|
||||
{{started? "Started" : "Starts"}} {{startDate}}
|
||||
</li>
|
||||
<li v-if="endDate" class="list-unstyled">
|
||||
{{ended? "Ended" : "Ends"}} {{endDate}}
|
||||
</li>
|
||||
</ul>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-tooltip target="crowdfund-body-raised-amount" v-if="paymentStats && paymentStats.length > 0">
|
||||
<ul class="p-0 text-uppercase">
|
||||
<li v-for="stat of paymentStats" class="list-unstyled">
|
||||
{{stat.label}} <span v-if="stat.lightning" class="fa fa-bolt"></span> {{stat.value}}
|
||||
</li>
|
||||
</ul>
|
||||
</b-tooltip>
|
||||
<b-tooltip target="crowdfund-body-goal-raised" v-if="srvModel.resetEvery !== 'Never'">
|
||||
Goal resets every {{srvModel.resetEveryAmount}} {{srvModel.resetEvery}} {{srvModel.resetEveryAmount>1?'s': ''}}
|
||||
</b-tooltip>
|
||||
|
||||
|
||||
<div class="card-title" id="crowdfund-body-header">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-8 col-lg-9" id="crowdfund-body-header-tagline-container">
|
||||
<h2 class="text-muted" v-if="srvModel.tagline" id="crowdfund-body-header-tagline">{{srvModel.tagline}}</h2>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-4 col-lg-3" id="crowdfund-body-header-cta-container">
|
||||
<button v-if="active" id="crowdfund-body-header-cta" class="btn btn-lg btn-primary w-100 fw-bold" v-on:click="contributeModalOpen = true">Contribute</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="srvModel.disqusEnabled">
|
||||
<b-tabs>
|
||||
<b-tab title="Details"active>
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container">
|
||||
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description"></div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
|
||||
<contribute :target-currency="srvModel.targetCurrency"
|
||||
:display-perks-ranking="srvModel.displayPerksRanking"
|
||||
:active="active"
|
||||
:loading="loading"
|
||||
:in-modal="false"
|
||||
:perks="perks">
|
||||
|
||||
</contribute>
|
||||
</div>
|
||||
</div>
|
||||
</b-tab>
|
||||
<b-tab title="Discussion" >
|
||||
<div id="disqus_thread" class=" mt-2"></div>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
|
||||
</template>
|
||||
<template v-else>
|
||||
<hr/>
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container">
|
||||
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description"></div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
|
||||
<contribute :target-currency="srvModel.targetCurrency"
|
||||
:loading="loading"
|
||||
:display-perks-ranking="srvModel.displayPerksRanking"
|
||||
:active="active"
|
||||
:in-modal="false"
|
||||
:perks="perks">
|
||||
|
||||
</contribute>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="card-footer text-muted d-flex" v-if="srvModel.animationsEnabled || srvModel.soundsEnabled">
|
||||
|
||||
<div class="align-self-end pe-4">Updated {{lastUpdated}}</div>
|
||||
<div class="form-check mx-1" v-if="srvModel.animationsEnabled || animation">
|
||||
<input class="form-check-input" type="checkbox" id="cbAnime" v-model="animation">
|
||||
<label class="form-check-label" for="cbAnime">
|
||||
Animations
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mx-1" v-if="srvModel.soundsEnabled|| sound">
|
||||
<input class="form-check-input" type="checkbox" id="cbSounds" v-model="sound">
|
||||
<label class="form-check-label" for="cbSounds">
|
||||
Sounds
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<b-modal title="Contribute" v-model="contributeModalOpen" size="lg" ok-only="true" ok-variant="secondary" ok-title="Close" ref="modalContribute">
|
||||
<contribute v-if="contributeModalOpen"
|
||||
:target-currency="srvModel.targetCurrency"
|
||||
:active="active"
|
||||
:perks="srvModel.perks"
|
||||
:loading="loading"
|
||||
:in-modal="true">
|
||||
</contribute>
|
||||
</b-modal>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/x-template" id="perks-template">
|
||||
<div class="perks-container">
|
||||
<perk v-if="!perks || perks.length ===0"
|
||||
:perk="{title: 'Donate Custom Amount', price: { value: null}, custom: true}"
|
||||
:target-currency="targetCurrency"
|
||||
:active="active"
|
||||
:loading="loading"
|
||||
:in-modal="inModal">
|
||||
</perk>
|
||||
<perk v-for="(perk, index) in perks" :perk="perk" :key="perk.id"
|
||||
:target-currency="targetCurrency"
|
||||
:active="active"
|
||||
:display-perks-ranking="displayPerksRanking"
|
||||
:index="index"
|
||||
:loading="loading"
|
||||
:in-modal="inModal">
|
||||
</perk>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-template" id="perk-template">
|
||||
<div class="card perk" v-bind:class="{ 'expanded': expanded, 'unexpanded': !expanded, 'mb-4':!inModal }" v-on:click="expand" :id="perk.id">
|
||||
<span v-if="displayPerksRanking && perk.sold"
|
||||
class="btn btn-sm rounded-circle px-0 perk-badge"
|
||||
v-bind:class="{ 'btn-primary': index==0, 'btn-secondary': index!=0}">#{{index+1}}</span>
|
||||
|
||||
|
||||
<div class="perk-zoom " v-if="canExpand">
|
||||
<div class="perk-zoom-bg bg-primary"> </div>
|
||||
<div class="perk-zoom-text w-100 text-center text-white fw-bold">
|
||||
Select this contribution perk
|
||||
</div>
|
||||
</div>
|
||||
<form v-on:submit='onContributeFormSubmit'>
|
||||
<input type="hidden" :value="perk.id" id="choiceKey"/>
|
||||
<img v-if="perk.image && perk.image != 'null' " class="card-img-top" :src="perk.image" />
|
||||
<div class="card-body">
|
||||
<div class="card-title d-flex justify-content-between" >
|
||||
<span class="h5">{{perk.title? perk.title : perk.id}} </span>
|
||||
<span class="text-muted" >
|
||||
<template v-if="perk.price.value">{{perk.price.value.noExponents()}}
|
||||
{{targetCurrency}}
|
||||
<template v-if="perk.custom">or more</template>
|
||||
</template>
|
||||
<template v-else-if="!perk.price.value && perk.custom">
|
||||
Any amount
|
||||
</template>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<p class="card-text overflow-hidden" v-if="perk.description" v-html="perk.description"></p>
|
||||
|
||||
<div class="input-group" style="max-width: 500px;" v-if="expanded" :id="'perk-form'+ perk.id">
|
||||
<input
|
||||
:disabled="!active"
|
||||
:readonly="!perk.custom"
|
||||
class="form-control"
|
||||
type="number"
|
||||
v-model="amount"
|
||||
:min="perk.price.value"
|
||||
step="any"
|
||||
placeholder="Contribution Amount"
|
||||
required>
|
||||
<span class='input-group-text'>{{targetCurrency}}</span>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
v-bind:class="{ 'btn-disabled': loading}"
|
||||
:disabled="!active || loading"
|
||||
type="submit">
|
||||
<div v-if="loading" class="spinner-grow spinner-grow-sm" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
{{perk.buyButtonText || 'Continue'}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between" v-if="perk.sold || perk.inventory != null">
|
||||
|
||||
<span v-if="perk.inventory != null && perk.inventory > 0" class="text-center text-muted">{{perk.inventory}} left</span>
|
||||
<span v-if="perk.inventory != null && perk.inventory <= 0" class="text-center text-muted">Sold out</span>
|
||||
<span v-if="perk.sold">{{perk.sold}} Contributor{{perk.sold > 1? "s": ""}}</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/x-template" id="contribute-template">
|
||||
<div>
|
||||
<h3 v-if="!inModal" class="mb-3">Contribute</h3>
|
||||
<perks
|
||||
:perks="perks"
|
||||
:loading="loading"
|
||||
:in-modal="inModal"
|
||||
:display-perks-ranking="displayPerksRanking"
|
||||
:target-currency="targetCurrency"
|
||||
:active="active">
|
||||
</perks>
|
||||
</div>
|
||||
</script>
|
|
@ -1,66 +1,388 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@inject ISettingsRepository _settingsRepository
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Models.AppViewModels
|
||||
@model BTCPayServer.Models.AppViewModels.ViewCrowdfundViewModel
|
||||
@{
|
||||
ViewData["Title"] = Model.Title;
|
||||
Layout = null;
|
||||
var theme = await _settingsRepository.GetTheme();
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html class="h-100">
|
||||
<head>
|
||||
<title>@Model.Title</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<bundle name="wwwroot/bundles/main-bundle.min.css" asp-append-version="true" />
|
||||
<partial name="LayoutHead" />
|
||||
<bundle name="wwwroot/bundles/crowdfund-bundle.min.css" asp-append-version="true"></bundle>
|
||||
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.CssUri)" rel="stylesheet" asp-append-version="true"/>
|
||||
@if (Model.CustomCSSLink != null)
|
||||
@if (!string.IsNullOrEmpty(Model.CustomCSSLink))
|
||||
{
|
||||
<link href="@Model.CustomCSSLink" rel="stylesheet" />
|
||||
}
|
||||
@if (!Context.Request.Query.ContainsKey("simple"))
|
||||
@if (!string.IsNullOrEmpty(Model.EmbeddedCSS))
|
||||
{
|
||||
<script>
|
||||
var srvModel = @Safe.Json(Model);
|
||||
</script>
|
||||
@Safe.Raw($"<style>{Model.EmbeddedCSS}</style>")
|
||||
}
|
||||
@if (!Model.SimpleDisplay)
|
||||
{
|
||||
<script>var srvModel = @Safe.Json(Model);</script>
|
||||
<bundle name="wwwroot/bundles/crowdfund-bundle-1.min.js" asp-append-version="true"></bundle>
|
||||
<bundle name="wwwroot/bundles/crowdfund-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>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.EmbeddedCSS))
|
||||
{
|
||||
@Safe.Raw($"<style>{Model.EmbeddedCSS}</style>");
|
||||
}
|
||||
</head>
|
||||
<body>
|
||||
@if (Context.Request.Query.ContainsKey("simple"))
|
||||
@if (!Model.Enabled)
|
||||
{
|
||||
<partial name="/Views/AppsPublic/Crowdfund/MinimalCrowdfund.cshtml" model="Model" />
|
||||
<div class="alert alert-warning text-center sticky-top mb-0 rounded-0" role="alert">
|
||||
This crowdfund page is not publically viewable!
|
||||
</div>
|
||||
}
|
||||
else
|
||||
@if (Model.AnimationsEnabled)
|
||||
{
|
||||
<noscript>
|
||||
<partial name="/Views/AppsPublic/Crowdfund/MinimalCrowdfund.cshtml" model="Model" />
|
||||
|
||||
<style>
|
||||
/* Hide the below canvas or else user will be staring "Loading..." text when JS is disabled */
|
||||
#fireworks,
|
||||
#app {
|
||||
display:none;
|
||||
<canvas id="fireworks" class="d-none"></canvas>
|
||||
}
|
||||
<div class="container px-0 py-3" id="app" @(Model.SimpleDisplay ? "" : "v-cloak")>
|
||||
<div class="row h-100 w-100 py-sm-0 py-md-4 mx-0">
|
||||
<div class="card w-100 p-0 mx-0">
|
||||
@if (!string.IsNullOrEmpty(Model.MainImageUrl))
|
||||
{
|
||||
<img v-if="srvModel.mainImageUrl" src="@Model.MainImageUrl" :src="srvModel.mainImageUrl" alt="@Model.Title" :alt="srvModel.title" class="card-img-top" id="crowdfund-main-image" asp-append-version="true" />
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
|
||||
if (Model.AnimationsEnabled)
|
||||
{
|
||||
<canvas id="fireworks"></canvas>
|
||||
}
|
||||
<partial name="/Views/AppsPublic/Crowdfund/VueCrowdfund.cshtml" model="Model" />
|
||||
}
|
||||
<div class="d-flex flex-column justify-content-between py-4 px-3 text-center" id="crowdfund-header-container">
|
||||
<h1 v-text="srvModel.title" class="my-3">@Model.Title</h1>
|
||||
@if (!Model.Started && Model.StartDate.HasValue)
|
||||
{
|
||||
<h6 class="text-muted fst-italic" v-if="!started && srvModel.startDate" v-b-tooltip :title="startDate" v-text="`starts in ${startDiff}`" data-test="time-state">
|
||||
starts @TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)
|
||||
</h6>
|
||||
}
|
||||
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
|
||||
{
|
||||
<h6 class="text-muted fst-italic" v-if="started && !ended && srvModel.endDate" v-b-tooltip :title="endDate" v-text="`ends in ${endDiff}`" data-test="time-state">
|
||||
ends @TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)
|
||||
</h6>
|
||||
}
|
||||
else if (Model.Started && !Model.Ended && !Model.EndDate.HasValue)
|
||||
{
|
||||
<h6 class="text-muted fst-italic" v-if="started && !ended && !srvModel.endDate" v-b-tooltip title="No set end date" data-test="time-state">
|
||||
currently active!
|
||||
</h6>
|
||||
}
|
||||
@if (Model.TargetAmount.HasValue)
|
||||
{
|
||||
<span v-if="srvModel.targetAmount" class="mt-3" id="crowdfund-header-target">
|
||||
<h3 class="d-inline-block">
|
||||
<span class="badge bg-info px-3" v-text="`${srvModel.targetAmount} ${targetCurrency}`">@Math.Round(Model.TargetAmount.GetValueOrDefault(0)) @Model.TargetCurrency</span>
|
||||
</h3>
|
||||
@if (Model.ResetEveryAmount > 0 && !Model.NeverReset)
|
||||
{
|
||||
<span v-if="srvModel.resetEvery !== 'Never'"
|
||||
class="h5 ms-2"
|
||||
v-b-tooltip
|
||||
:title="'Goal resets every ' + srvModel.resetEveryAmount + ' ' + srvModel.resetEvery + ((srvModel.resetEveryAmount>1)?'s': '')">Dynamic</span>
|
||||
}
|
||||
@if (Model.EnforceTargetAmount)
|
||||
{
|
||||
<span v-if="srvModel.enforceTargetAmount" class="h5 ms-2">
|
||||
Hardcap Goal
|
||||
<span class="fa fa-question-circle" v-b-tooltip title="No contributions allowed after the goal has been reached"></span>
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span v-if="!srvModel.enforceTargetAmount" class="h5 ms-2">
|
||||
Softcap Goal
|
||||
<span class="fa fa-question-circle" v-b-tooltip title="Contributions allowed even after goal is reached"></span>
|
||||
</span>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (Model.TargetAmount.HasValue)
|
||||
{
|
||||
<div class="progress rounded-pill mx-3" v-if="srvModel.targetAmount" id="crowdfund-progress-bar">
|
||||
<div class="progress-bar bg-primary"
|
||||
role="progressbar"
|
||||
style="width:@(Model.Info.ProgressPercentage + "%")"
|
||||
:aria-valuenow="srvModel.info.progressPercentage"
|
||||
v-bind:style="{ width: srvModel.info.progressPercentage + '%' }"
|
||||
aria-valuemin="0"
|
||||
id="crowdfund-progress-bar-confirmed-bar"
|
||||
v-b-tooltip
|
||||
:title="parseFloat(srvModel.info.progressPercentage).toFixed(2) + '% contributions'"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
<div class="progress-bar bg-warning"
|
||||
role="progressbar"
|
||||
id="crowdfund-progress-bar-pending-bar"
|
||||
style="width:@(Model.Info.PendingProgressPercentage + "%")"
|
||||
:aria-valuenow="srvModel.info.pendingProgressPercentage"
|
||||
v-bind:style="{ width: srvModel.info.pendingProgressPercentage + '%' }"
|
||||
v-b-tooltip
|
||||
:title="parseFloat(srvModel.info.pendingProgressPercentage).toFixed(2) + '% contributions pending confirmation'"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row py-2 text-center crowdfund-stats">
|
||||
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-raised-amount">
|
||||
<h3 v-text="`${raisedAmount} ${targetCurrency}`">@Math.Round(Model.Info.CurrentAmount + Model.Info.CurrentPendingAmount) @Model.TargetCurrency</h3>
|
||||
<h5 class="text-muted fst-italic mb-0">Raised</h5>
|
||||
<b-tooltip target="crowdfund-body-raised-amount" v-if="paymentStats && paymentStats.length > 0" class="only-for-js">
|
||||
<ul class="p-0 text-uppercase">
|
||||
<li v-for="stat of paymentStats" class="list-unstyled">
|
||||
{{stat.label}} <span v-if="stat.lightning" class="fa fa-bolt"></span> {{stat.value}}
|
||||
</li>
|
||||
</ul>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-goal-raised">
|
||||
<h3 v-text="`${percentageRaisedAmount}%`">@Math.Round(Model.Info.PendingProgressPercentage.GetValueOrDefault(0) + Model.Info.ProgressPercentage.GetValueOrDefault(0))%</h3>
|
||||
<h5 class="text-muted fst-italic mb-0">Of Goal</h5>
|
||||
<b-tooltip target="crowdfund-body-goal-raised" v-if="srvModel.resetEvery !== 'Never'" class="only-for-js">
|
||||
Goal resets every {{srvModel.resetEveryAmount}} {{srvModel.resetEvery}} {{srvModel.resetEveryAmount>1?'s': ''}}
|
||||
</b-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-total-contributors">
|
||||
<h3 v-text="srvModel.info.totalContributors">@Model.Info.TotalContributors</h3>
|
||||
<h5 class="text-muted fst-italic mb-0">Contributors</h5>
|
||||
</div>
|
||||
@if (Model.StartDate.HasValue || Model.EndDate.HasValue)
|
||||
{
|
||||
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-campaign-dates">
|
||||
@if (!Model.Started && Model.StartDate.HasValue)
|
||||
{
|
||||
<div v-if="startDiff">
|
||||
<h3 v-text="startDiff">@TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)</h3>
|
||||
<h5 class="text-muted fst-italic mb-0" v-text="'Left to start'">Start Date</h5>
|
||||
</div>
|
||||
}
|
||||
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
|
||||
{
|
||||
<div v-if="!startDiff && endDiff">
|
||||
<h3 v-text="endDiff">@TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)</h3>
|
||||
<h5 class="text-muted fst-italic mb-0" v-text="'Left'">End Date</h5>
|
||||
</div>
|
||||
}
|
||||
else if (Model.Ended)
|
||||
{
|
||||
<div v-if="ended">
|
||||
<h3 class="mb-0">Campaign not active</h3>
|
||||
</div>
|
||||
}
|
||||
<b-tooltip v-if="startDate || endDate" target="crowdfund-body-campaign-dates" class="only-for-js">
|
||||
<ul class="p-0">
|
||||
@if (Model.StartDate.HasValue)
|
||||
{
|
||||
<li v-if="startDate" class="list-unstyled">
|
||||
{{started? "Started" : "Starts"}} {{startDate}}
|
||||
</li>
|
||||
}
|
||||
@if (Model.EndDate.HasValue)
|
||||
{
|
||||
<li v-if="endDate" class="list-unstyled">
|
||||
{{ended? "Ended" : "Ends"}} {{endDate}}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</b-tooltip>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="card-title text-center" id="crowdfund-body-header">
|
||||
@if (!string.IsNullOrEmpty(Model.Tagline))
|
||||
{
|
||||
<h2 class="h3 my-4 fw-normal" v-if="srvModel.tagline" v-text="srvModel.tagline">@Model.Tagline</h2>
|
||||
}
|
||||
<button v-if="active" id="crowdfund-body-header-cta" class="btn btn-lg btn-primary mb-4 py-2 px-5 only-for-js" v-on:click="contributeModalOpen = true">Contribute</button>
|
||||
</div>
|
||||
|
||||
<hr class="w-100"/>
|
||||
|
||||
<template v-if="srvModel.disqusEnabled && srvModel.disqusShortname">
|
||||
<b-tabs>
|
||||
<b-tab title="Details" active>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container">
|
||||
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
|
||||
@Safe.Raw(Model.Description)
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
|
||||
<contribute :target-currency="srvModel.targetCurrency"
|
||||
:display-perks-ranking="srvModel.displayPerksRanking"
|
||||
:active="active"
|
||||
:loading="loading"
|
||||
:in-modal="false"
|
||||
:perks="perks">
|
||||
</contribute>
|
||||
</div>
|
||||
</div>
|
||||
</b-tab>
|
||||
<b-tab title="Discussion">
|
||||
<div id="disqus_thread" class="mt-3"></div>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container">
|
||||
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
|
||||
@Safe.Raw(Model.Description)
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
|
||||
<contribute :target-currency="srvModel.targetCurrency"
|
||||
:loading="loading"
|
||||
:display-perks-ranking="srvModel.displayPerksRanking"
|
||||
:active="active"
|
||||
:in-modal="false"
|
||||
:perks="perks">
|
||||
</contribute>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<noscript>
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-sm-12">
|
||||
<div class="card-text overflow-hidden">@Safe.Raw(Model.Description)</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<partial
|
||||
name="/Views/AppsPublic/Crowdfund/ContributeForm.cshtml"
|
||||
model="@(new ContributeToCrowdfund { ViewCrowdfundViewModel = Model, RedirectToCheckout = true })"></partial>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
<div class="card-footer text-muted d-flex" v-if="srvModel.animationsEnabled || srvModel.soundsEnabled">
|
||||
<div class="align-self-end pe-4" v-text="`Updated ${lastUpdated}`">Updated @Model.Info.LastUpdated</div>
|
||||
<div class="form-check mx-1 only-for-js" v-if="srvModel.animationsEnabled || animation">
|
||||
<input class="form-check-input" type="checkbox" id="cbAnime" v-model="animation">
|
||||
<label class="form-check-label" for="cbAnime">Animations</label>
|
||||
</div>
|
||||
<div class="form-check mx-1 only-for-js" v-if="srvModel.soundsEnabled|| sound">
|
||||
<input class="form-check-input" type="checkbox" id="cbSounds" v-model="sound">
|
||||
<label class="form-check-label" for="cbSounds">Sounds</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<b-modal title="Contribute" v-model="contributeModalOpen" size="lg" ok-only="true" ok-variant="secondary" ok-title="Close" ref="modalContribute">
|
||||
<contribute v-if="contributeModalOpen"
|
||||
:target-currency="srvModel.targetCurrency"
|
||||
:active="active"
|
||||
:perks="srvModel.perks"
|
||||
:loading="loading"
|
||||
:in-modal="true">
|
||||
</contribute>
|
||||
</b-modal>
|
||||
</div>
|
||||
|
||||
<template id="perks-template">
|
||||
<div class="perks-container">
|
||||
<perk v-if="!perks || perks.length ===0"
|
||||
:perk="{title: 'Donate Custom Amount', price: { value: null}, custom: true}"
|
||||
:target-currency="targetCurrency"
|
||||
:active="active"
|
||||
:loading="loading"
|
||||
:in-modal="inModal">
|
||||
</perk>
|
||||
<perk v-for="(perk, index) in perks"
|
||||
:key="perk.id"
|
||||
:perk="perk"
|
||||
:target-currency="targetCurrency"
|
||||
:active="active"
|
||||
:display-perks-ranking="displayPerksRanking"
|
||||
:index="index"
|
||||
:loading="loading"
|
||||
:in-modal="inModal">
|
||||
</perk>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="perk-template">
|
||||
<div class="card perk" v-bind:class="{ 'expanded': expanded, 'unexpanded': !expanded, 'mb-4':!inModal }" v-on:click="expand" :id="perk.id">
|
||||
<span v-if="displayPerksRanking && perk.sold"
|
||||
class="btn btn-sm rounded-circle px-0 perk-badge"
|
||||
v-bind:class="{ 'btn-primary': index==0, 'btn-light': index!=0}">#{{index+1}}</span>
|
||||
<div class="perk-zoom" v-if="canExpand">
|
||||
<div class="perk-zoom-bg"></div>
|
||||
<div class="perk-zoom-text w-100 py-2 px-4 text-center text-primary fw-bold fs-3 lh-sm">
|
||||
Select this contribution perk
|
||||
</div>
|
||||
</div>
|
||||
<form v-on:submit="onContributeFormSubmit">
|
||||
<input type="hidden" :value="perk.id" id="choiceKey"/>
|
||||
<img v-if="perk.image && perk.image != 'null'" class="card-img-top" :src="perk.image" />
|
||||
<div class="card-body">
|
||||
<div class="card-title d-flex justify-content-between">
|
||||
<span class="h5">{{perk.title? perk.title : perk.id}}</span>
|
||||
<span class="text-muted">
|
||||
<template v-if="perk.price.value">
|
||||
{{perk.price.value.noExponents()}}
|
||||
{{targetCurrency}}
|
||||
<template v-if="perk.custom">or more</template>
|
||||
</template>
|
||||
<template v-else-if="!perk.price.value && perk.custom">
|
||||
Any amount
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<p class="card-text overflow-hidden" v-if="perk.description" v-html="perk.description"></p>
|
||||
|
||||
<div class="input-group" style="max-width:500px;" v-if="expanded" :id="'perk-form'+ perk.id">
|
||||
<input
|
||||
:disabled="!active"
|
||||
:readonly="!perk.custom"
|
||||
class="form-control hide-number-spin"
|
||||
type="number"
|
||||
v-model="amount"
|
||||
:min="perk.price.value"
|
||||
step="any"
|
||||
placeholder="Contribution Amount"
|
||||
required>
|
||||
<span class="input-group-text">{{targetCurrency}}</span>
|
||||
<button
|
||||
class="btn btn-primary d-flex align-items-center"
|
||||
v-bind:class="{ 'btn-disabled': loading}"
|
||||
:disabled="!active || loading"
|
||||
type="submit">
|
||||
<div v-if="loading" class="spinner-grow spinner-grow-sm me-2" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
{{perk.buyButtonText || 'Continue'}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-between" v-if="perk.sold || perk.inventory != null">
|
||||
<span v-if="perk.inventory != null && perk.inventory > 0" class="text-center text-muted">{{perk.inventory}} left</span>
|
||||
<span v-if="perk.inventory != null && perk.inventory <= 0" class="text-center text-muted">Sold out</span>
|
||||
<span v-if="perk.sold">{{perk.sold}} Contributor{{perk.sold > 1 ? "s": ""}}</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="contribute-template">
|
||||
<div>
|
||||
<h3 v-if="!inModal" class="mb-3">Contribute</h3>
|
||||
<perks
|
||||
:perks="perks"
|
||||
:loading="loading"
|
||||
:in-modal="inModal"
|
||||
:display-perks-ranking="displayPerksRanking"
|
||||
:target-currency="targetCurrency"
|
||||
:active="active">
|
||||
</perks>
|
||||
</div>
|
||||
</template>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
@{ var policies = await _settingsRepository.GetPolicies(); }
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
@if (policies.DiscourageSearchEngines)
|
||||
{
|
||||
<meta name="robots" content="noindex">
|
||||
|
|
|
@ -165,17 +165,20 @@ var resizeCanvas = function() {
|
|||
window.addEventListener("resize", resizeCanvas);
|
||||
})();
|
||||
|
||||
window.fireworks = function(){
|
||||
window.fireworks = function(count = 0) {
|
||||
const elem = document.getElementById("fireworks");
|
||||
elem.classList.remove("d-none");
|
||||
var fauxClick = new Event("mousedown");
|
||||
fauxClick.pageX = anime.random( 0, cW );
|
||||
fauxClick.pageY = anime.random(0, cH );
|
||||
|
||||
fauxClick.pageX = anime.random(0, cW);
|
||||
fauxClick.pageY = anime.random(0, cH);
|
||||
|
||||
var middleSpaceX = cW * 0.6;
|
||||
var middleSpaceY = cH * 0.6;
|
||||
var middleSpaceX1 = middleSpaceX /2;
|
||||
var middleSpaceX2 = middleSpaceX1 + middleSpaceX;
|
||||
var middleSpaceY1 = middleSpaceY /2;
|
||||
var middleSpaceY2 = middleSpaceY1 + middleSpaceY;
|
||||
|
||||
while(true){
|
||||
if(fauxClick.pageX > middleSpaceX1 && fauxClick.pageX < middleSpaceX2){
|
||||
fauxClick.pageX = anime.random( 0, cW );
|
||||
|
@ -187,6 +190,14 @@ window.fireworks = function(){
|
|||
}
|
||||
break;
|
||||
}
|
||||
handleEvent(fauxClick)
|
||||
handleEvent(fauxClick);
|
||||
|
||||
setTimeout(function () {
|
||||
if (count < 5) {
|
||||
fireworks(count + 1);
|
||||
} else {
|
||||
elem.classList.add("d-none");
|
||||
}
|
||||
}, 750);
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
[v-cloak] > * {
|
||||
display: none
|
||||
}
|
||||
|
||||
[v-cloak]::before {
|
||||
content: "loading…"
|
||||
}
|
||||
|
||||
canvas#fireworks {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.perk-zoom {
|
||||
|
@ -55,7 +47,7 @@ canvas#fireworks {
|
|||
z-index: 1
|
||||
}
|
||||
|
||||
.crowdfund-stats .col-sm:last-child{
|
||||
.crowdfund-stats .col-sm:last-child {
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
|
@ -69,3 +61,26 @@ canvas#fireworks {
|
|||
max-width: 320px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
/* Constrain image width in description */
|
||||
div#crowdfund-body-description img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* HEADER: PROGRESS */
|
||||
div#crowdfund-progress-bar {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
#crowdfund-body-header + hr {
|
||||
height: 1px;
|
||||
opacity: 1;
|
||||
margin-top: 0;
|
||||
background: var(--btcpay-body-border-medium);
|
||||
}
|
||||
|
||||
/* PERKS: HOVER */
|
||||
.perks-container .perk-zoom-bg {
|
||||
background-color: var(--btcpay-bg-tile) !important;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
|
|
@ -9843,15 +9843,15 @@ pre:not(.text-wrap) code {
|
|||
padding: var(--btcpay-space-m) !important;
|
||||
}
|
||||
|
||||
ol {
|
||||
ol:not([class]) {
|
||||
padding-left: 1.125em;
|
||||
}
|
||||
|
||||
ul {
|
||||
ul:not([class]) {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
ul li {
|
||||
ul:not([class]) li {
|
||||
padding-top: .2em;
|
||||
padding-bottom: .2em;
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
slots = _ref.slots;
|
||||
|
||||
var componentData = {
|
||||
staticClass: 'close',
|
||||
staticClass: 'btn-close',
|
||||
class: defineProperty({}, 'text-' + props.textVariant, props.textVariant),
|
||||
attrs: {
|
||||
type: 'button',
|
||||
|
@ -147,7 +147,7 @@
|
|||
}
|
||||
// Careful not to override the slot with innerHTML
|
||||
};if (!slots().default) {
|
||||
componentData.domProps = { innerHTML: '×' };
|
||||
componentData.domProps = { innerHTML: '<svg role="img" class="icon icon-close"><use href="/img/icon-sprite.svg#close"></use></svg>' };
|
||||
}
|
||||
return h('button', mergeData(data, componentData), slots().default);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue