add reset every x amount of time feature

This commit is contained in:
Kukks 2019-01-04 11:42:37 +01:00
parent c52a49f747
commit 7768f41849
9 changed files with 133 additions and 37 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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;
}
}
}

View file

@ -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
});
}

View file

@ -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
}
}

View file

@ -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; }
}
}

View file

@ -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" />

View file

@ -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>

View file

@ -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>