Imported Upstream version 6.6.0.89

Former-commit-id: b39a328747c2f3414dc52e009fb6f0aa80ca2492
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-09-24 08:53:40 +00:00
parent cf815e07e0
commit 95fdb59ea6
2556 changed files with 138145 additions and 47453 deletions

View File

@@ -0,0 +1,17 @@
<CascadingAuthenticationState>
<Router AppAssembly="typeof(Program).Assembly">
<NotFoundContent>Page not found</NotFoundContent>
<NotAuthorizedContent>
<div class="main">
<h2>You're signed out</h2>
<p>To continue, please sign in.</p>
<a class="btn btn-danger" href="user/signin">Sign in</a>
</div>
</NotAuthorizedContent>
<AuthorizingContent>
Please wait...
</AuthorizingContent>
</Router>
</CascadingAuthenticationState>

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.3</LangVersion>
<RazorLangVersion>3.0</RazorLangVersion>
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Blazor" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="$(AspNetCoreVersion)" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BlazingComponents\BlazingComponents.csproj" />
<ProjectReference Include="..\BlazingPizza.ComponentsLibrary\BlazingPizza.ComponentsLibrary.csproj" />
<ProjectReference Include="..\BlazingPizza.Shared\BlazingPizza.Shared.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
using Microsoft.JSInterop;
using System.Threading.Tasks;
namespace BlazingPizza.Client
{
public static class JSRuntimeExtensions
{
public static Task<bool> Confirm(this IJSRuntime jsRuntime, string message)
{
return jsRuntime.InvokeAsync<bool>("confirm", message);
}
}
}

View File

@@ -0,0 +1,56 @@
using System.Collections.Generic;
namespace BlazingPizza.Client
{
public class OrderState
{
public bool ShowingConfigureDialog { get; private set; }
public Pizza ConfiguringPizza { get; private set; }
public Order Order { get; private set; } = new Order();
public void ShowConfigurePizzaDialog(PizzaSpecial special)
{
ConfiguringPizza = new Pizza()
{
Special = special,
SpecialId = special.Id,
Size = Pizza.DefaultSize,
Toppings = new List<PizzaTopping>(),
};
ShowingConfigureDialog = true;
}
public void CancelConfigurePizzaDialog()
{
ConfiguringPizza = null;
ShowingConfigureDialog = false;
}
public void ConfirmConfigurePizzaDialog()
{
Order.Pizzas.Add(ConfiguringPizza);
ConfiguringPizza = null;
ShowingConfigureDialog = false;
}
public void RemoveConfiguredPizza(Pizza pizza)
{
Order.Pizzas.Remove(pizza);
}
public void ResetOrder()
{
Order = new Order();
}
public void ReplaceOrder(Order order)
{
Order = order;
}
}
}

View File

@@ -0,0 +1,83 @@
@page "/checkout"
@inject OrderState OrderState
@inject HttpClient HttpClient
@inject IUriHelper UriHelper
@inject IJSRuntime JSRuntime
<div class="main">
<AuthorizeView Context="authContext">
<NotAuthorized>
<h2>Redirecting you...</h2>
</NotAuthorized>
<Authorized>
<EditForm Model="@OrderState.Order.DeliveryAddress" OnValidSubmit="@PlaceOrder">
<div class="checkout-cols">
<div class="checkout-order-details">
<h4>Review order</h4>
<OrderReview Order="@OrderState.Order" />
</div>
<div class="checkout-delivery-address">
<h4>Deliver to...</h4>
<AddressEditor Address="@OrderState.Order.DeliveryAddress" />
</div>
</div>
<button type="submit" class="checkout-button btn btn-warning" disabled="@isSubmitting">
Place order
</button>
<DataAnnotationsValidator />
</EditForm>
</Authorized>
</AuthorizeView>
</div>
@functions {
bool isSubmitting;
[CascadingParameter] Task<AuthenticationState> AuthenticationStateTask { get; set; }
protected override async Task OnInitAsync()
{
var authState = await AuthenticationStateTask;
if (!authState.User.Identity.IsAuthenticated)
{
// The server won't accept orders from unauthenticated users, so avoid
// an error by making them log in at this point
await LocalStorage.SetAsync(JSRuntime, "currentorder", OrderState.Order);
UriHelper.NavigateTo("user/signin?redirectUri=/checkout", true);
}
// Try to recover any temporary saved order
if (!OrderState.Order.Pizzas.Any())
{
var savedOrder = await LocalStorage.GetAsync<Order>(JSRuntime, "currentorder");
if (savedOrder != null)
{
OrderState.ReplaceOrder(savedOrder);
await LocalStorage.DeleteAsync(JSRuntime, "currentorder");
}
else
{
// There's nothing check out - go to home
UriHelper.NavigateTo("");
}
}
}
async Task PlaceOrder()
{
isSubmitting = true;
try
{
var newOrderId = await HttpClient.PostJsonAsync<int>("orders", OrderState.Order);
OrderState.ResetOrder();
UriHelper.NavigateTo($"myorders/{newOrderId}");
}
finally
{
isSubmitting = false;
}
}
}

View File

@@ -0,0 +1,74 @@
@page "/"
@inject HttpClient HttpClient
@inject OrderState OrderState
@inject IUriHelper UriHelper
@inject IJSRuntime JS
<div class="main">
<ul class="pizza-cards">
@if (specials != null)
{
@foreach (var special in specials)
{
<li @onclick="@(() => OrderState.ShowConfigurePizzaDialog(special))" style="background-image: url('@special.ImageUrl')">
<div class="pizza-info">
<span class="title">@special.Name</span>
@special.Description
<span class="price">@special.GetFormattedBasePrice()</span>
</div>
</li>
}
}
</ul>
</div>
<div class="sidebar">
@if (Order.Pizzas.Any())
{
<div class="order-contents">
<h2>Your order</h2>
@foreach (var configuredPizza in Order.Pizzas)
{
<ConfiguredPizzaItem Pizza="configuredPizza" OnRemoved="@(() => RemovePizza(configuredPizza))" />
}
</div>
}
else
{
<div class="empty-cart">Choose a pizza<br>to get started</div>
}
<div class="order-total @(Order.Pizzas.Any() ? "" : "hidden")">
Total:
<span class="total-price">@Order.GetFormattedTotalPrice()</span>
<a href="checkout" class="btn btn-warning" disabled="@(Order.Pizzas.Count == 0)">
Order >
</a>
</div>
</div>
<TemplatedDialog Show="OrderState.ShowingConfigureDialog">
<ConfigurePizzaDialog
Pizza="OrderState.ConfiguringPizza"
OnCancel="OrderState.CancelConfigurePizzaDialog"
OnConfirm="OrderState.ConfirmConfigurePizzaDialog" />
</TemplatedDialog>
@functions {
List<PizzaSpecial> specials;
Order Order => OrderState.Order;
protected async override Task OnInitAsync()
{
specials = await HttpClient.GetJsonAsync<List<PizzaSpecial>>("specials");
}
async Task RemovePizza(Pizza configuredPizza)
{
if (await JS.Confirm($"Remove {configuredPizza.Special.Name} pizza from the order?"))
{
OrderState.RemoveConfiguredPizza(configuredPizza);
}
}
}

View File

@@ -0,0 +1,37 @@
@page "/myorders"
@inject HttpClient HttpClient
@attribute [Authorize]
<div class="main">
<TemplatedList Loader="@LoadOrders" ListGroupClass="orders-list">
<LoadingContent>Loading...</LoadingContent>
<EmptyContent>
<h2>No orders placed</h2>
<a class="btn btn-success" href="">Order some pizza</a>
</EmptyContent>
<ItemContent Context="item">
<div class="col">
<h5>@item.Order.CreatedTime.ToLongDateString()</h5>
Items:
<strong>@item.Order.Pizzas.Count()</strong>;
Total price:
<strong>£@item.Order.GetFormattedTotalPrice()</strong>
</div>
<div class="col">
Status: <strong>@item.StatusText</strong>
</div>
<div class="col flex-grow-0">
<a href="myorders/@item.Order.OrderId" class="btn btn-success">
Track &gt;
</a>
</div>
</ItemContent>
</TemplatedList>
</div>
@functions {
async Task<List<OrderWithStatus>> LoadOrders()
{
return await HttpClient.GetJsonAsync<List<OrderWithStatus>>("orders");
}
}

View File

@@ -0,0 +1,83 @@
@page "/myorders/{orderId:int}"
@using System.Threading
@inject HttpClient HttpClient
@implements IDisposable
@attribute [Authorize]
<div class="main">
@if (invalidOrder)
{
<h2>Nope</h2>
<p>Sorry, this order could not be loaded.</p>
}
else if (orderWithStatus == null)
{
<text>Loading...</text>
}
else
{
<div class="track-order">
<div class="track-order-title">
<h2>
Order placed @orderWithStatus.Order.CreatedTime.ToLongDateString()
</h2>
<p class="ml-auto mb-0">
Status: <strong>@orderWithStatus.StatusText</strong>
</p>
</div>
<div class="track-order-body">
<div class="track-order-details">
<OrderReview Order="@orderWithStatus.Order" />
</div>
<div class="track-order-map">
<Map Zoom="13" Markers="@orderWithStatus.MapMarkers" />
</div>
</div>
</div>
}
</div>
@functions {
[Parameter] int OrderId { get; set; }
OrderWithStatus orderWithStatus;
bool invalidOrder;
CancellationTokenSource pollingCancellationToken;
protected override void OnParametersSet()
{
// If we were already polling for a different order, stop doing so
pollingCancellationToken?.Cancel();
// Start a new poll loop
PollForUpdates();
}
void IDisposable.Dispose()
{
pollingCancellationToken?.Cancel();
}
private async void PollForUpdates()
{
pollingCancellationToken = new CancellationTokenSource();
while (!pollingCancellationToken.IsCancellationRequested)
{
try
{
invalidOrder = false;
orderWithStatus = await HttpClient.GetJsonAsync<OrderWithStatus>($"orders/{OrderId}");
}
catch (Exception ex)
{
invalidOrder = true;
pollingCancellationToken.Cancel();
Console.Error.WriteLine(ex);
}
StateHasChanged();
await Task.Delay(4000);
}
}
}

View File

@@ -0,0 +1 @@
@layout MainLayout

View File

@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Blazor.Hosting;
namespace BlazingPizza.Client
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) =>
BlazorWebAssemblyHost.CreateDefaultBuilder()
.UseBlazorStartup<Startup>();
}
}

View File

@@ -0,0 +1,28 @@
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
namespace BlazingPizza.Client
{
public class ServerAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly HttpClient _httpClient;
public ServerAuthenticationStateProvider(HttpClient httpClient)
{
_httpClient = httpClient;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var userInfo = await _httpClient.GetJsonAsync<UserInfo>("user");
var identity = userInfo.IsAuthenticated
? new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, userInfo.Name) }, "serverauth")
: new ClaimsIdentity();
return new AuthenticationState(new ClaimsPrincipal(identity));
}
}
}

View File

@@ -0,0 +1,51 @@
<div class="form-field">
<label>Name:</label>
<div>
<InputText @bind-Value="@Address.Name" />
<ValidationMessage For="@(() => Address.Name)" />
</div>
</div>
<div class="form-field">
<label>Line 1:</label>
<div>
<InputText @bind-Value="@Address.Line1" />
<ValidationMessage For="@(() => Address.Line1)" />
</div>
</div>
<div class="form-field">
<label>Line 2:</label>
<div>
<InputText @bind-Value="@Address.Line2" />
<ValidationMessage For="@(() => Address.Line2)" />
</div>
</div>
<div class="form-field">
<label>City:</label>
<div>
<InputText @bind-Value="@Address.City" />
<ValidationMessage For="@(() => Address.City)" />
</div>
</div>
<div class="form-field">
<label>Region:</label>
<div>
<InputText @bind-Value="@Address.Region" />
<ValidationMessage For="@(() => Address.Region)" />
</div>
</div>
<div class="form-field">
<label>Postal code:</label>
<div>
<InputText @bind-Value="@Address.PostalCode" />
<ValidationMessage For="@(() => Address.PostalCode)" />
</div>
</div>
@functions {
[Parameter] Address Address { get; set; }
}

View File

@@ -0,0 +1,92 @@
@inject HttpClient HttpClient
<div class="dialog-title">
<h2>@Pizza.Special.Name</h2>
@Pizza.Special.Description
</div>
<form class="dialog-body">
<div>
<label>Size:</label>
<input type="range" min="@Pizza.MinimumSize" max="@Pizza.MaximumSize" step="1" @bind-value="@Pizza.Size" @bind-value:event="oninput" />
<span class="size-label">
@(Pizza.Size)" (£@(Pizza.GetFormattedTotalPrice()))
</span>
</div>
<div>
<label>Extra Toppings:</label>
@if (toppings == null)
{
<select class="custom-select" disabled>
<option>(loading...)</option>
</select>
}
else if (Pizza.Toppings.Count >= 6)
{
<div>(maximum reached)</div>
}
else
{
<select class="custom-select" @onchange="@ToppingSelected">
<option value="-1" disabled selected>(select)</option>
@for (var i = 0; i < toppings.Count; i++)
{
<option value="@i">@toppings[i].Name - (£@(toppings[i].GetFormattedPrice()))</option>
}
</select>
}
</div>
<div class="toppings">
@foreach (var topping in Pizza.Toppings)
{
<div class="topping">
@topping.Topping.Name
<span class="topping-price">@topping.Topping.GetFormattedPrice()</span>
<button type="button" class="delete-topping" @onclick="@(() => RemoveTopping(topping.Topping))">x</button>
</div>
}
</div>
</form>
<div class="dialog-buttons">
<button class="btn btn-secondary mr-auto" @onclick="@OnCancel">Cancel</button>
<span class="mr-center">
Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>
</span>
<button class="btn btn-success ml-auto" @onclick="@OnConfirm">Order ></button>
</div>
@functions {
List<Topping> toppings;
[Parameter] Pizza Pizza { get; set; }
[Parameter] EventCallback OnCancel { get; set; }
[Parameter] EventCallback OnConfirm { get; set; }
protected async override Task OnInitAsync()
{
toppings = await HttpClient.GetJsonAsync<List<Topping>>("toppings");
}
void ToppingSelected(UIChangeEventArgs e)
{
if (int.TryParse((string)e.Value, out var index) && index >= 0)
{
AddTopping(toppings[index]);
}
}
void AddTopping(Topping topping)
{
if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null)
{
Pizza.Toppings.Add(new PizzaTopping() { Topping = topping });
}
}
void RemoveTopping(Topping topping)
{
Pizza.Toppings.RemoveAll(pt => pt.Topping == topping);
StateHasChanged();
}
}

View File

@@ -0,0 +1,18 @@
<div class="cart-item">
<a @onclick="@OnRemoved" class="delete-item">x</a>
<div class="title">@(Pizza.Size)" @Pizza.Special.Name</div>
<ul>
@foreach (var topping in Pizza.Toppings)
{
<li>+ @topping.Topping.Name</li>
}
</ul>
<div class="item-price">
@Pizza.GetFormattedTotalPrice()
</div>
</div>
@functions {
[Parameter] Pizza Pizza { get; set; }
[Parameter] EventCallback OnRemoved { get; set; }
}

View File

@@ -0,0 +1,17 @@
<div class="user-info">
<AuthorizeView>
<Authorizing>
<text>...</text>
</Authorizing>
<Authorized>
<img src="img/user.svg" />
<div>
<span class="username">@context.User.Identity.Name</span>
<a class="sign-out" href="user/signout">Sign out</a>
</div>
</Authorized>
<NotAuthorized>
<a class="sign-in" href="user/signin">Sign in</a>
</NotAuthorized>
</AuthorizeView>
</div>

View File

@@ -0,0 +1,22 @@
@inherits LayoutComponentBase
<div class="top-bar">
<img class="logo" src="img/logo.svg" />
<NavLink href="" class="nav-tab" Match="NavLinkMatch.All">
<img src="img/pizza-slice.svg" />
<div>Get Pizza</div>
</NavLink>
<NavLink href="myorders" class="nav-tab">
<img src="img/bike.svg" />
<div>My Orders</div>
</NavLink>
<LoginDisplay />
</div>
<div class="content">
@Body
</div>

View File

@@ -0,0 +1,28 @@
@foreach (var pizza in Order.Pizzas)
{
<p>
<strong>
@(pizza.Size)"
@pizza.Special.Name
(£@pizza.GetFormattedTotalPrice())
</strong>
</p>
<ul>
@foreach (var topping in pizza.Toppings)
{
<li>+ @topping.Topping.Name</li>
}
</ul>
}
<p>
<strong>
Total price:
£@Order.GetFormattedTotalPrice()
</strong>
</p>
@functions {
[Parameter] Order Order { get; set; }
}

View File

@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace BlazingPizza.Client
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<OrderState>();
// Add auth services
services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
}
public void Configure(IComponentsApplicationBuilder app)
{
app.AddComponent<App>("app");
}
}
}

View File

@@ -0,0 +1,11 @@
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Layouts
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.JSInterop
@using BlazingPizza.Client
@using BlazingPizza.Client.Shared
@using BlazingPizza.ComponentsLibrary
@using BlazingPizza.ComponentsLibrary.Map
@using BlazingComponents

View File

@@ -0,0 +1,3 @@
<StaticWebAssets Version="1.0">
<ContentRoot BasePath="_content/blazingpizzacomponentslibrary" Path="/Users/lewing/Source/blazor-workshop/src/BlazingPizza.ComponentsLibrary/wwwroot/" />
</StaticWebAssets>

Some files were not shown because too many files have changed in this diff Show More