mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-21 22:11:48 +01:00
add reset every x amount of time feature
This commit is contained in:
parent
c52a49f747
commit
7768f41849
9 changed files with 133 additions and 37 deletions
|
@ -37,6 +37,8 @@ namespace BTCPayServer.Controllers
|
|||
public string DisqusShortname { get; set; }
|
||||
public bool AnimationsEnabled { get; set; }
|
||||
public bool UseInvoiceAmount { get; set; }
|
||||
public int ResetEveryAmount { get; set; }
|
||||
public CrowdfundResetEvery ResetEvery { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,7 +70,9 @@ namespace BTCPayServer.Controllers
|
|||
SoundsEnabled = settings.SoundsEnabled,
|
||||
DisqusShortname = settings.DisqusShortname,
|
||||
AnimationsEnabled = settings.AnimationsEnabled,
|
||||
UseInvoiceAmount = settings.UseInvoiceAmount
|
||||
UseInvoiceAmount = settings.UseInvoiceAmount,
|
||||
ResetEveryAmount = settings.ResetEveryAmount,
|
||||
ResetEvery = Enum.GetName(typeof(CrowdfundResetEvery), settings.ResetEvery),
|
||||
};
|
||||
return View(vm);
|
||||
}
|
||||
|
@ -116,7 +120,8 @@ namespace BTCPayServer.Controllers
|
|||
SoundsEnabled = vm.SoundsEnabled,
|
||||
DisqusShortname = vm.DisqusShortname,
|
||||
AnimationsEnabled = vm.AnimationsEnabled,
|
||||
|
||||
ResetEveryAmount = vm.ResetEveryAmount,
|
||||
ResetEvery = Enum.Parse<CrowdfundResetEvery>(vm.ResetEvery),
|
||||
UseInvoiceAmount = vm.UseInvoiceAmount
|
||||
});
|
||||
await UpdateAppSettings(app);
|
||||
|
|
|
@ -122,12 +122,14 @@ namespace BTCPayServer.Controllers
|
|||
var info = await _CrowdfundHubStreamer.GetCrowdfundInfo(appId);
|
||||
|
||||
if(!isAdmin &&
|
||||
|
||||
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
|
||||
(settings.EndDate.HasValue && DateTime.Now > settings.EndDate) ||
|
||||
(settings.EnforceTargetAmount && (info.Info.PendingProgressPercentage.GetValueOrDefault(0) + info.Info.ProgressPercentage.GetValueOrDefault(0)) >= 100)))
|
||||
(settings.EnforceTargetAmount &&
|
||||
(info.Info.PendingProgressPercentage.GetValueOrDefault(0) +
|
||||
info.Info.ProgressPercentage.GetValueOrDefault(0)) >= 100)))
|
||||
{
|
||||
return NotFound();
|
||||
|
||||
}
|
||||
|
||||
var store = await _AppsHelper.GetStore(app);
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace BTCPayServer.Hubs
|
|||
public const string InvoiceCreated = "InvoiceCreated";
|
||||
public const string PaymentReceived = "PaymentReceived";
|
||||
public const string InfoUpdated = "InfoUpdated";
|
||||
public const string InvoiceError = "InvoiceError";
|
||||
private readonly AppsPublicController _AppsPublicController;
|
||||
|
||||
public CrowdfundHub(AppsPublicController appsPublicController)
|
||||
|
@ -35,7 +36,19 @@ namespace BTCPayServer.Hubs
|
|||
model.RedirectToCheckout = false;
|
||||
_AppsPublicController.ControllerContext.HttpContext = Context.GetHttpContext();
|
||||
var result = await _AppsPublicController.ContributeToCrowdfund(Context.Items["app"].ToString(), model);
|
||||
await Clients.Caller.SendCoreAsync(InvoiceCreated, new[] {(result as OkObjectResult)?.Value.ToString()});
|
||||
switch (result)
|
||||
{
|
||||
case OkObjectResult okObjectResult:
|
||||
await Clients.Caller.SendCoreAsync(InvoiceCreated, new[] {okObjectResult.Value.ToString()});
|
||||
break;
|
||||
case ObjectResult objectResult:
|
||||
await Clients.Caller.SendCoreAsync(InvoiceError, new[] {objectResult.Value});
|
||||
break;
|
||||
default:
|
||||
await Clients.Caller.SendCoreAsync(InvoiceError, System.Array.Empty<object>());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace BTCPayServer.Hubs
|
|||
_InvoiceRepository = invoiceRepository;
|
||||
SubscribeToEvents();
|
||||
}
|
||||
|
||||
|
||||
public Task<ViewCrowdfundViewModel> GetCrowdfundInfo(string appId)
|
||||
{
|
||||
return _MemoryCache.GetOrCreateAsync(GetCacheKey(appId), async entry =>
|
||||
|
@ -178,8 +178,41 @@ namespace BTCPayServer.Hubs
|
|||
private async Task<ViewCrowdfundViewModel> GetInfo(AppData appData, string statusMessage= null)
|
||||
{
|
||||
var settings = appData.GetSettings<AppsController.CrowdfundSettings>();
|
||||
var invoices = await GetInvoicesForApp(appData, _InvoiceRepository);
|
||||
|
||||
var resetEvery = settings.StartDate.HasValue? settings.ResetEvery : CrowdfundResetEvery.Never;
|
||||
DateTime? lastResetDate = null;
|
||||
DateTime? nextResetDate = null;
|
||||
if (resetEvery != CrowdfundResetEvery.Never)
|
||||
{
|
||||
lastResetDate = settings.StartDate.Value;
|
||||
|
||||
nextResetDate = lastResetDate.Value;
|
||||
while (DateTime.Now >= nextResetDate)
|
||||
{
|
||||
lastResetDate = nextResetDate;
|
||||
switch (resetEvery)
|
||||
{
|
||||
case CrowdfundResetEvery.Hour:
|
||||
nextResetDate = lastResetDate.Value.AddHours(settings.ResetEveryAmount);
|
||||
break;
|
||||
case CrowdfundResetEvery.Day:
|
||||
nextResetDate = lastResetDate.Value.AddDays(settings.ResetEveryAmount);
|
||||
break;
|
||||
case CrowdfundResetEvery.Month:
|
||||
|
||||
nextResetDate = lastResetDate.Value.AddMonths(settings.ResetEveryAmount);
|
||||
break;
|
||||
case CrowdfundResetEvery.Year:
|
||||
nextResetDate = lastResetDate.Value.AddYears(settings.ResetEveryAmount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var invoices = await GetInvoicesForApp(appData, lastResetDate);
|
||||
var completeInvoices = invoices.Where(entity => entity.Status == InvoiceStatus.Complete).ToArray();
|
||||
var pendingInvoices = invoices.Where(entity => entity.Status != InvoiceStatus.Complete).ToArray();
|
||||
|
||||
|
@ -190,7 +223,7 @@ namespace BTCPayServer.Hubs
|
|||
|
||||
var currentAmount = await GetCurrentContributionAmount(
|
||||
paymentStats,
|
||||
settings.TargetCurrency, _RateFetcher, rateRules);
|
||||
settings.TargetCurrency, _RateFetcher, rateRules);
|
||||
var currentPendingAmount = await GetCurrentContributionAmount(
|
||||
pendingPaymentStats,
|
||||
settings.TargetCurrency, _RateFetcher, rateRules);
|
||||
|
@ -217,6 +250,8 @@ namespace BTCPayServer.Hubs
|
|||
SoundsEnabled = settings.SoundsEnabled,
|
||||
DisqusShortname = settings.DisqusShortname,
|
||||
AnimationsEnabled = settings.AnimationsEnabled,
|
||||
ResetEveryAmount = settings.ResetEveryAmount,
|
||||
ResetEvery = Enum.GetName(typeof(CrowdfundResetEvery),settings.ResetEvery),
|
||||
Info = new ViewCrowdfundViewModel.CrowdfundInfo()
|
||||
{
|
||||
TotalContributors = invoices.Length,
|
||||
|
@ -226,21 +261,24 @@ namespace BTCPayServer.Hubs
|
|||
PendingProgressPercentage = ( currentPendingAmount/ settings.TargetAmount) * 100,
|
||||
LastUpdated = DateTime.Now,
|
||||
PaymentStats = paymentStats,
|
||||
PendingPaymentStats = pendingPaymentStats
|
||||
PendingPaymentStats = pendingPaymentStats,
|
||||
LastResetDate = lastResetDate,
|
||||
NextResetDate = nextResetDate
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task<InvoiceEntity[]> GetInvoicesForApp(AppData appData, InvoiceRepository invoiceRepository)
|
||||
private async Task<InvoiceEntity[]> GetInvoicesForApp(AppData appData, DateTime? startDate = null)
|
||||
{
|
||||
return await invoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
return await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
{
|
||||
OrderId = $"{CrowdfundInvoiceOrderIdPrefix}{appData.Id}",
|
||||
Status = new string[]{
|
||||
InvoiceState.ToString(InvoiceStatus.New),
|
||||
InvoiceState.ToString(InvoiceStatus.Paid),
|
||||
InvoiceState.ToString(InvoiceStatus.Confirmed),
|
||||
InvoiceState.ToString(InvoiceStatus.Complete)}
|
||||
InvoiceState.ToString(InvoiceStatus.Complete)},
|
||||
StartDate = startDate
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,43 +1,39 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BTCPayServer.Models.AppViewModels
|
||||
{
|
||||
public class UpdateCrowdfundViewModel
|
||||
{
|
||||
[Required]
|
||||
[MaxLength(30)]
|
||||
public string Title { get; set; }
|
||||
|
||||
[MaxLength(50)]
|
||||
public string Tagline { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Description { get; set; }
|
||||
[Required] [MaxLength(30)] public string Title { get; set; }
|
||||
|
||||
[MaxLength(50)] public string Tagline { get; set; }
|
||||
|
||||
[Required] public string Description { get; set; }
|
||||
public string MainImageUrl { get; set; }
|
||||
|
||||
|
||||
public string NotificationUrl { get; set; }
|
||||
|
||||
|
||||
[Required]
|
||||
|
||||
[Display(Name = "Enabled, Allow crowdfund to be publicly visible( still visible to you)")]
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Enable background animations on new payments")]
|
||||
public bool AnimationsEnabled { get; set; } = true;
|
||||
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Enable sounds on new payments")]
|
||||
public bool SoundsEnabled { get; set; } = true;
|
||||
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Enable Disqus Comments")]
|
||||
public bool DisqusEnabled { get; set; } = true;
|
||||
|
||||
[Display(Name = "Disqus Shortname")]
|
||||
public string DisqusShortname { get; set; }
|
||||
|
||||
|
||||
[Display(Name = "Disqus Shortname")] public string DisqusShortname { get; set; }
|
||||
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
|
||||
|
@ -45,17 +41,24 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
[MaxLength(5)]
|
||||
[Display(Name = "The primary currency used for targets and stats")]
|
||||
public string TargetCurrency { get; set; } = "BTC";
|
||||
|
||||
|
||||
[Display(Name = "Set a Target amount ")]
|
||||
public decimal? TargetAmount { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public IEnumerable<string> ResetEveryValues = Enum.GetNames(typeof(CrowdfundResetEvery));
|
||||
|
||||
[Display(Name = "Reset goal every")] public string ResetEvery { get; set; } = nameof(CrowdfundResetEvery.Never);
|
||||
|
||||
public int ResetEveryAmount { get; set; } = 1;
|
||||
|
||||
|
||||
[Display(Name = "Do not allow additional contributions after target has been reached")]
|
||||
public bool EnforceTargetAmount { get; set; }
|
||||
|
||||
[Display(Name = "Contribution Perks Template")]
|
||||
public string PerksTemplate { get; set; }
|
||||
|
||||
|
||||
[MaxLength(500)]
|
||||
[Display(Name = "Custom bootstrap CSS file")]
|
||||
public string CustomCSSLink { get; set; }
|
||||
|
@ -65,4 +68,13 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
[Display(Name = "Base the contributed goal amount on the invoice amount and not actual payments")]
|
||||
public bool UseInvoiceAmount { get; set; } = true;
|
||||
}
|
||||
|
||||
public enum CrowdfundResetEvery
|
||||
{
|
||||
Never,
|
||||
Hour,
|
||||
Day,
|
||||
Month,
|
||||
Year
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
public bool SoundsEnabled { get; set; }
|
||||
public string DisqusShortname { get; set; }
|
||||
public bool AnimationsEnabled { get; set; }
|
||||
public int ResetEveryAmount { get; set; }
|
||||
public string ResetEvery { get; set; }
|
||||
|
||||
|
||||
public class CrowdfundInfo
|
||||
|
@ -41,6 +43,8 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
public DateTime LastUpdated { get; set; }
|
||||
public Dictionary<string, decimal> PaymentStats { get; set; }
|
||||
public Dictionary<string, decimal> PendingPaymentStats { get; set; }
|
||||
public DateTime? LastResetDate { get; set; }
|
||||
public DateTime? NextResetDate { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,6 +74,20 @@
|
|||
<input asp-for="StartDate" class="form-control" />
|
||||
<span asp-validation-for="StartDate" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ResetEvery" class="control-label"></label>
|
||||
<div class="input-group">
|
||||
|
||||
<input type="number" asp-for="ResetEveryAmount" min="1" placeholder="Amount" class="form-control">
|
||||
<select class="custom-select" asp-for="ResetEvery">
|
||||
@foreach (var opt in Model.ResetEveryValues)
|
||||
{
|
||||
<option value="@opt">@opt</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="EndDate" class="control-label"></label>*
|
||||
<input asp-for="EndDate" class="form-control" />
|
||||
|
|
|
@ -50,6 +50,10 @@
|
|||
{
|
||||
<span class="mt-3">
|
||||
<span class="h5">@Model.TargetAmount @Model.TargetCurrency</span>
|
||||
@if (Model.ResetEveryAmount > 0 && Model.ResetEvery != nameof(CrowdfundResetEvery.Never))
|
||||
{
|
||||
<span> Dynamic</span>
|
||||
}
|
||||
@if (Model.EnforceTargetAmount)
|
||||
{
|
||||
<span v-if="srvModel.enforceTargetAmount">Hardcap Goal <span class="fa fa-question-circle" v-b-tooltip title="No contributions allowed after the goal has been reached"></span></span>
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
<span v-if="srvModel.targetAmount" class="mt-3">
|
||||
<span class="h5">{{srvModel.targetAmount}} {{targetCurrency}}</span>
|
||||
|
||||
<span v-if="srvModel.resetEvery !== 'Never'" v-b-tooltip
|
||||
:title="'Goal resets every ' + srvModel.resetEveryAmount + ' ' + srvModel.resetEvery + ((srvModel.resetEveryAmount>1)?'s': '')" >Dynamic </span>
|
||||
<span v-if="srvModel.enforceTargetAmount">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>Softcap Goal <span class="fa fa-question-circle" v-b-tooltip title="Contributions allowed even after goal is reached"></span> </span>
|
||||
</span>
|
||||
|
@ -51,7 +52,7 @@
|
|||
<h5>{{srvModel.info.currentAmount + srvModel.info.currentPendingAmount }} {{targetCurrency}} </h5>
|
||||
<h5 class="text-muted">Raised</h5>
|
||||
</div>
|
||||
<div class="col-sm border-right">
|
||||
<div class="col-sm border-right" id="goal-raised">
|
||||
<h5>{{srvModel.info.progressPercentage + srvModel.info.pendingProgressPercentage }}%</h5>
|
||||
<h5 class="text-muted">Of Goal</h5>
|
||||
</div>
|
||||
|
@ -98,6 +99,9 @@
|
|||
{{stat.label}} {{stat.value}}
|
||||
</li>
|
||||
</ul>
|
||||
</b-tooltip>
|
||||
<b-tooltip target="goal-raised" >
|
||||
Goal resets every {{srvModel.resetEveryAmount}} {{srvModel.resetEvery}} {{srvModel.resetEveryAmount>1?'s': ''}}
|
||||
</b-tooltip>
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue