Apps become plugins

Unified navigation for apps that are now plugins. Part of #4744.
This commit is contained in:
Dennis Reimann 2023-03-18 22:36:26 +01:00 committed by d11n
parent d3f5576570
commit 7ad0aa82fc
10 changed files with 77 additions and 66 deletions

View file

@ -77,8 +77,7 @@ namespace BTCPayServer.Tests
s.GenerateWallet(isHotWallet: true);
// Point Of Sale
s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click();
new SelectElement(s.Driver.FindElement(By.Id("SelectedAppType"))).SelectByValue("PointOfSale");
s.Driver.FindElement(By.Id("StoreNav-CreatePointOfSale")).Click();
s.Driver.FindElement(By.Id("AppName")).SendKeys(Guid.NewGuid().ToString());
s.Driver.FindElement(By.Id("Create")).Click();
Assert.Contains("App successfully created", s.FindAlertMessage().Text);
@ -939,9 +938,8 @@ namespace BTCPayServer.Tests
await s.StartAsync();
var userId = s.RegisterNewUser(true);
s.CreateNewStore();
s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click();
s.Driver.FindElement(By.Id("StoreNav-CreatePointOfSale")).Click();
s.Driver.FindElement(By.Name("AppName")).SendKeys("PoS" + Guid.NewGuid());
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Point of Sale");
s.Driver.FindElement(By.Id("Create")).Click();
Assert.Contains("App successfully created", s.FindAlertMessage().Text);
@ -1027,9 +1025,8 @@ namespace BTCPayServer.Tests
s.CreateNewStore();
s.AddDerivationScheme();
s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click();
s.Driver.FindElement(By.Id("StoreNav-CreateCrowdfund")).Click();
s.Driver.FindElement(By.Name("AppName")).SendKeys("CF" + Guid.NewGuid());
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Crowdfund");
s.Driver.FindElement(By.Id("Create")).Click();
Assert.Contains("App successfully created", s.FindAlertMessage().Text);
@ -2006,9 +2003,7 @@ namespace BTCPayServer.Tests
s.AddLightningNode(LightningConnectionType.CLightning, false);
s.GoToLightningSettings();
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click();
s.Driver.FindElement(By.Id("SelectedAppType")).Click();
s.Driver.FindElement(By.CssSelector("option[value='PointOfSale']")).Click();
s.Driver.FindElement(By.Id("StoreNav-CreatePointOfSale")).Click();
s.Driver.FindElement(By.Id("AppName")).SendKeys(Guid.NewGuid().ToString());
s.Driver.FindElement(By.Id("Create")).Click();
TestUtils.Eventually(() => Assert.Contains("App successfully created", s.FindAlertMessage().Text));

View file

@ -155,30 +155,6 @@
</div>
</div>
</div>
<div class="accordion-item" permission="@Policies.CanModifyStoreSettings">
<header class="accordion-header" id="Nav-Apps-Header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Apps" aria-expanded="true" aria-controls="Nav-Apps">
Apps
<vc:icon symbol="caret-down"/>
</button>
</header>
<div id="Nav-Apps" class="accordion-collapse collapse show" aria-labelledby="Nav-Apps-Header">
<div class="accordion-body">
<ul class="navbar-nav">
@foreach (var app in Model.Apps)
{
<vc:ui-extension-point location="apps-nav" model="@app"/>
}
<li class="nav-item">
<a asp-area="" asp-controller="UIApps" asp-action="CreateApp" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(AppsNavPages.Create)" id="StoreNav-CreateApp">
<vc:icon symbol="new"/>
<span>New App</span>
</a>
</li>
</ul>
</div>
</div>
</div>
}
<div class="accordion-item">
<header class="accordion-header" id="Nav-Plugins-Header">
@ -195,9 +171,11 @@
{
<vc:ui-extension-point location="store-integrations-nav" model="@Model"/>
}
</ul>
<ul class="navbar-nav">
<li class="nav-item" permission="@Policies.CanModifyServerSettings">
<a asp-area="" asp-controller="UIServer" asp-action="ListPlugins" class="nav-link @ViewData.IsActivePage(ServerNavPages.Plugins)" id="Nav-ManagePlugins">
<vc:icon symbol="new"/>
<vc:icon symbol="plugin"/>
<span>Manage Plugins</span>
</a>
</li>

View file

@ -111,22 +111,29 @@ namespace BTCPayServer.Controllers
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[HttpGet("/stores/{storeId}/apps/create")]
[HttpGet("/stores/{storeId}/apps/create/{appType?}")]
public IActionResult CreateApp(string storeId, string appType = null)
{
var vm = new CreateAppViewModel (_appService){StoreId = GetCurrentStore().Id, SelectedAppType = appType};
var vm = new CreateAppViewModel(_appService)
{
StoreId = storeId,
AppType = appType,
SelectedAppType = appType
};
return View(vm);
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[HttpPost("/stores/{storeId}/apps/create")]
[HttpPost("/stores/{storeId}/apps/create/{appType?}")]
public async Task<IActionResult> CreateApp(string storeId, CreateAppViewModel vm)
{
var store = GetCurrentStore();
vm.StoreId = store.Id;
var type = _appService.GetAppType(vm.SelectedAppType);
var type = _appService.GetAppType(vm.AppType ?? vm.SelectedAppType);
if (type is null)
{
ModelState.AddModelError(nameof(vm.SelectedAppType), "Invalid App Type");
}
if (!ModelState.IsValid)
{
@ -137,7 +144,7 @@ namespace BTCPayServer.Controllers
{
StoreDataId = store.Id,
Name = vm.AppName,
AppType = vm.SelectedAppType
AppType = type!.Type
};
var defaultCurrency = await GetStoreDefaultCurrentIfEmpty(appData.StoreDataId, null);

View file

@ -30,6 +30,7 @@ namespace BTCPayServer.Models.AppViewModels
public string SelectedAppType { get; set; }
public SelectList AppTypes { get; set; }
public string AppType { get; set; }
private void SetApps(AppService appService)
{

View file

@ -30,7 +30,7 @@ namespace BTCPayServer.Plugins.Crowdfund
public override void Execute(IServiceCollection services)
{
services.AddSingleton<IUIExtension>(new UIExtension("Crowdfund/NavExtension", "apps-nav"));
services.AddSingleton<IUIExtension>(new UIExtension("Crowdfund/NavExtension", "header-nav"));
services.AddSingleton<CrowdfundAppType>();
services.AddSingleton<AppBaseType, CrowdfundAppType>();

View file

@ -28,7 +28,7 @@ namespace BTCPayServer.Plugins.PointOfSale
public override void Execute(IServiceCollection services)
{
services.AddSingleton<IUIExtension>(new UIExtension("PointOfSale/NavExtension", "apps-nav"));
services.AddSingleton<IUIExtension>(new UIExtension("PointOfSale/NavExtension", "header-nav"));
services.AddSingleton<AppBaseType, PointOfSaleAppType>();
base.Execute(services);
}

View file

@ -4,16 +4,28 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.TagHelpers
@using BTCPayServer.Plugins.Crowdfund
@model BTCPayServer.Components.MainNav.StoreApp
@using BTCPayServer.Services.Apps
@inject AppService AppService;
@model BTCPayServer.Components.MainNav.MainNavViewModel
@{
var store = Context.GetStoreData();
}
@{ var store = Context.GetStoreData(); }
@if (store != null && Model.AppType == CrowdfundAppType.AppType)
@if (store != null)
{
var appType = AppService.GetAppType(CrowdfundAppType.AppType)!;
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UICrowdfund" asp-action="UpdateCrowdfund" asp-route-appId="@Model.Id" class="nav-link @ViewData.IsActivePage(AppsNavPages.Update, Model.Id)" id="@($"StoreNav-App-{Model.Id}")">
<vc:icon symbol="@Model.AppType.ToLower()"/>
<span>@Model.AppName</span>
<a asp-area="" asp-controller="UIApps" asp-action="CreateApp" asp-route-storeId="@store.Id" asp-route-appType="@appType.Type" class="nav-link @ViewData.IsActivePage(AppsNavPages.Create, appType.Type)" id="@($"StoreNav-Create{appType.Type}")">
<vc:icon symbol="crowdfund" />
<span>@appType.Description</span>
</a>
</li>
@foreach (var app in Model.Apps.Where(app => app.AppType == appType.Type))
{
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UICrowdfund" asp-action="UpdateCrowdfund" asp-route-appId="@app.Id" class="nav-link @ViewData.IsActivePage(AppsNavPages.Update, app.Id)" id="@($"StoreNav-App-{app.Id}")">
<span>@app.AppName</span>
</a>
</li>
}
}

View file

@ -4,17 +4,28 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.TagHelpers
@using BTCPayServer.Plugins.PointOfSale
@model BTCPayServer.Components.MainNav.StoreApp
@{ var store = Context.GetStoreData(); }
@if (store != null && Model.AppType == PointOfSaleAppType.AppType)
{
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIPointOfSale" asp-action="UpdatePointOfSale" asp-route-appId="@Model.Id" class="nav-link @ViewData.IsActivePage(AppsNavPages.Update, Model.Id)" id="@($"StoreNav-App-{Model.Id}")">
<vc:icon symbol="@Model.AppType.ToLower()"/>
<span>@Model.AppName</span>
</a>
</li>
@using BTCPayServer.Services.Apps
@inject AppService AppService;
@model BTCPayServer.Components.MainNav.MainNavViewModel
@{
var store = Context.GetStoreData();
}
@if (store != null)
{
var appType = AppService.GetAppType(PointOfSaleAppType.AppType)!;
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIApps" asp-action="CreateApp" asp-route-storeId="@store.Id" asp-route-appType="@appType.Type" class="nav-link @ViewData.IsActivePage(AppsNavPages.Create, appType.Type)" id="@($"StoreNav-Create{appType.Type}")">
<vc:icon symbol="pointofsale" />
<span>@appType.Description</span>
</a>
</li>
@foreach (var app in Model.Apps.Where(app => app.AppType == appType.Type))
{
<li class="nav-item nav-item-sub" permission="@Policies.CanModifyStoreSettings">
<a asp-area="" asp-controller="UIPointOfSale" asp-action="UpdatePointOfSale" asp-route-appId="@app.Id" class="nav-link @ViewData.IsActivePage(AppsNavPages.Update, app.Id)" id="@($"StoreNav-App-{app.Id}")">
<span>@app.AppName</span>
</a>
</li>
}
}

View file

@ -1,6 +1,6 @@
@model CreateAppViewModel
@{
ViewData.SetActivePage(AppsNavPages.Create, "Create a new app");
ViewData.SetActivePage(AppsNavPages.Create, $"Create a new {Model.AppType ?? "app"}", Model.AppType);
}
@section PageFootContent {
@ -13,12 +13,15 @@
<div class="row">
<div class="col-xl-8 col-xxl-constrain">
<form asp-action="CreateApp">
<form asp-action="CreateApp" asp-route-appType="@Model.AppType">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="SelectedAppType" class="form-label" data-required></label>
<select asp-for="SelectedAppType" asp-items="Model.AppTypes" class="form-select"></select>
</div>
@if (string.IsNullOrEmpty(Model.AppType))
{
<div class="form-group">
<label asp-for="SelectedAppType" class="form-label" data-required></label>
<select asp-for="SelectedAppType" asp-items="Model.AppTypes" class="form-select"></select>
</div>
}
<div class="form-group">
<label asp-for="AppName" class="form-label" data-required></label>
<input asp-for="AppName" class="form-control" required />

View file

@ -80,6 +80,10 @@
flex-shrink: 0;
}
#mainNav .navbar-nav > li.nav-item-sub {
padding-left:calc(1.5rem + var(--btcpay-space-xs))
}
#mainNav .navbar-nav > li.nav-item .nav-link span {
max-width: 200px;
overflow: hidden;