Browse Source

Merge branch 'master' into dev

# Conflicts:
#	src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj
#	src/Services/Catalog/Catalog.API/Startup.cs
#	src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs
#	src/Web/WebMVC/Startup.cs
pull/173/head
Eduard Tomas 7 years ago
parent
commit
756cb3dafa
58 changed files with 455 additions and 461 deletions
  1. +12
    -9
      README.md
  2. +1
    -0
      docker-compose.override.yml
  3. +10
    -3
      docker-compose.prod.yml
  4. BIN
      docs/Enterprise-Application-Patterns-using-XamarinForms.pdf
  5. BIN
      docs/architecting-and-developing-containerized-and-microservice-based-net-applications-ebook-early-draft.pdf
  6. BIN
      img/eShopOnContainers_Architecture_Diagram.png
  7. BIN
      img/eShopOnContainers_Architecture_Diagram_old.png
  8. BIN
      img/eShopOnContainers_Types_Of_Microservices.png
  9. BIN
      img/ebook_xamarin_patterns_cover.png
  10. BIN
      img/xamarin-enterprise-patterns-ebook-cover-small.png
  11. +16
    -6
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml
  12. +4
    -4
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs
  13. +19
    -25
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Behaviors/LineColorBehavior.cs
  14. +22
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Converters/FirstValidationErrorConverter.cs
  15. +11
    -0
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Effects/EntryLineColorEffect.cs
  16. +0
    -18
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Extensions/AnimationExtension.cs
  17. +3
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/ServicesHelper.cs
  18. +2
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs
  19. +1
    -8
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Navigation/INavigationService.cs
  20. +33
    -90
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Navigation/NavigationService.cs
  21. +16
    -13
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Validations/ValidatableObject.cs
  22. +1
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ExtendedBindableObject.cs
  23. +1
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/MessageKeys.cs
  24. +4
    -5
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelBase.cs
  25. +83
    -81
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs
  26. +12
    -14
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/BasketViewModel.cs
  27. +8
    -19
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CatalogViewModel.cs
  28. +2
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CheckoutViewModel.cs
  29. +20
    -10
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs
  30. +2
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/MainViewModel.cs
  31. +2
    -9
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/OrderDetailViewModel.cs
  32. +1
    -1
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/ProfileViewModel.cs
  33. +2
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs
  34. +4
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/BasketView.xaml
  35. +5
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CatalogView.xaml
  36. +2
    -5
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CatalogView.xaml.cs
  37. +8
    -10
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CheckoutView.xaml
  38. +39
    -7
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml
  39. +16
    -16
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml.cs
  40. +4
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml
  41. +4
    -13
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml.cs
  42. +4
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/OrderDetailView.xaml
  43. +6
    -4
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/ProfileView.xaml
  44. +5
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/SettingsView.xaml
  45. +5
    -2
      src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj
  46. +3
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Effects/EntryLineColorEffect.cs
  47. +3
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Effects/EntryLineColorEffect.cs
  48. +3
    -3
      src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Effects/EntryLineColorEffect.cs
  49. +1
    -0
      src/Services/Catalog/Catalog.API/Startup.cs
  50. +24
    -24
      src/Services/Identity/Identity.API/Configuration/Config.cs
  51. +0
    -1
      src/Services/Identity/Identity.API/Controllers/AccountController.cs
  52. +6
    -6
      src/Services/Identity/Identity.API/Controllers/ConsentController.cs
  53. +2
    -2
      src/Services/Identity/Identity.API/Identity.API.csproj
  54. +13
    -3
      src/Services/Identity/Identity.API/Models/AccountViewModels/ConsentViewModel.cs
  55. +2
    -1
      src/Services/Identity/Identity.API/Startup.cs
  56. +1
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs
  57. +7
    -8
      src/Web/WebMVC/Startup.cs
  58. +0
    -9
      src/Web/WebMonolithic/Readme.txt

+ 12
- 9
README.md View File

@ -14,11 +14,15 @@ Sample .NET Core reference application, powered by Microsoft, based on a simplif
**Architecture overview**: This reference application is cross-platform either at the server and client side, thanks to .NET Core services capable of running on Linux or Windows containers depending on your Docker host, and to Xamarin for mobile apps running on Android, iOS or Windows/UWP plus any browser for the client web apps.
The architecture proposes a simplified microservice oriented architecture implementation with multiple autonomous microservices (each one owning its own data/db) and implementing different approaches within each microservice (simple CRUD vs. DDD/CQRS patterns) using Http as the current communication protocol.
<p>
The plan is to add asynchronous communication for data updates propagation across multiple services based on integration events and an event bus plus other features defined at the <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>roadmap</a>.
It also supports asynchronous communication for data updates propagation across multiple services based on Integration Events and an Event Bus plus other features defined at the <a href='https://github.com/dotnet/eShopOnContainers/wiki/01.-Roadmap-and-Milestones-for-future-releases'>roadmap</a>.
<p>
<img src="img/eshop_logo.png">
<img src="img/eShopOnContainers_Architecture_Diagram.png">
<p>
The microservices are different in type, meaning different internal architecture patterns approaches depending on it purpose, as shown in the image below.
<p>
<img src="img/eShopOnContainers_Types_Of_Microservices.png">
<p>
<p>
Additional miroservice styles with other frameworks and No-SQL databases will be added, eventually. This is a great opportunity for pull requests from the community, like a new microservice using Nancy, or even other languages like Node, Go, Python or data containers with MongoDB with Azure DocDB compatibility, PostgreSQL, RavenDB, Event Store, MySql, etc. You name it! :)
@ -30,15 +34,14 @@ Additional miroservice styles with other frameworks and No-SQL databases will be
## Related documentation and guidance
While developing this reference application, we are creating a reference Guide/eBook named <b>"Architecting and Developing Containerized and Microservice based .NET Applications"</b> which explains in detail how to develop this kind of architectural style (microservices, Docker containers, Domain-Driven Design for certain microservices) plus other simpler architectural styles, like monolithic apps that can also live as Docker containers.
<p>
There's also an additional eBook focusing on Containers/Docker lifecycle (DevOps, CI/CD, etc.) with Microsoft Tools, already published.
You can start reviewing these Guides/eBooks here:
There are also additional eBooks focusing on Containers/Docker lifecycle (DevOps, CI/CD, etc.) with Microsoft Tools, already published plus an additional eBook focusing on Enterprise Apps Patterns with Xamarin.Forms.
You can download them and start reviewing these Guides/eBooks here:
<p>
You can download both eBooks from here:
| Architecting & Developing | Containers Lifecycle & CI/CD |
| ------------ | ------------|
| <a href='docs/architecting-and-developing-containerized-and-microservice-based-net-applications-ebook-early-draft.pdf'><img src="img/ebook_arch_dev_microservices_containers_cover.png"> </a> | <a href='https://aka.ms/dockerlifecycleebook'> <img src="img/ebook_containers_lifecycle.png"> </a> |
| <a href='docs/architecting-and-developing-containerized-and-microservice-based-net-applications-ebook-early-draft.pdf'>**Download** (Early DRAFT, still work in progress)</a> | <a href='https://aka.ms/dockerlifecycleebook'>**Download** - First Edition from late 2016</a> |
| Architecting & Developing | Containers Lifecycle & CI/CD | App patterns with Xamarin.Forms |
| ------------ | ------------| ------------|
| <a href='https://aka.ms/microservicesebook'><img src="img/ebook_arch_dev_microservices_containers_cover.png"> </a> | <a href='https://aka.ms/dockerlifecycleebook'> <img src="img/ebook_containers_lifecycle.png"> </a> | <a href='https://aka.ms/xamarinpatternsebook'> <img src="img/xamarin-enterprise-patterns-ebook-cover-small.png"> </a> |
| <sup> <a href='https://aka.ms/microservicesebook'>**Download** (Early DRAFT, still work in progress)</a> </sup> | <sup> <a href='https://aka.ms/dockerlifecycleebook'>**Download** (First Edition from late 2016) </a> </sup> | <sup> <a href='https://aka.ms/xamarinpatternsebook'>**Download** (Early DRAFT, still work in progress) </a> </sup> |
Send feedback to [cesardl@microsoft.com](cesardl@microsoft.com)
<p>
@ -89,7 +92,7 @@ The app was also partially tested on "Docker for Mac" using a development MacOS
## Sending feedback and pull requests
As mentioned, we'd appreciate to your feedback, improvements and ideas.
You can create new issues at the issues section, do pull requests and/or send emails to eshop_feedback@service.microsoft.com
You can create new issues at the issues section, do pull requests and/or send emails to **eshop_feedback@service.microsoft.com**
## Questions
[QUESTION] Answer +1 if the solution is working for you (Through VS2017 or CLI environment):


+ 1
- 0
docker-compose.override.yml View File

@ -45,6 +45,7 @@ services:
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://basket.api:5103
- EventBusConnection=rabbitmq
ports:
- "5102:5102"


+ 10
- 3
docker-compose.prod.yml View File

@ -19,6 +19,7 @@ services:
- ASPNETCORE_URLS=http://0.0.0.0:5103
- ConnectionString=basket.data
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5103:5103"
@ -28,6 +29,7 @@ services:
- ASPNETCORE_URLS=http://0.0.0.0:5101
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
- ExternalCatalogBaseUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5101 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq
ports:
- "5101:5101"
@ -48,6 +50,8 @@ services:
- ASPNETCORE_URLS=http://0.0.0.0:5102
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word
- identityUrl=http://identity.api:5105 #Local: You need to open your host's firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://basket.api:5103
- EventBusConnection=rabbitmq
ports:
- "5102:5102"
@ -82,10 +86,13 @@ services:
webstatus:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://0.0.0.0:5107
- CatalogUrl=http://catalog.api:5101/hc
- OrderingUrl=http://ordering.api:5102/hc
- BasketUrl=http://basket.api:5103/hc
- IdentityUrl=http://10.0.75.1:5105/hc
- mvc=http://webmvc:5100/hc
- spa=http://webspa:5104/hc
- IdentityUrl=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
ports:
- "5107:5107"
- "5107:5107"

BIN
docs/Developing-Enterprise-Mobile-Applications-with-XamarinForms.pdf → docs/Enterprise-Application-Patterns-using-XamarinForms.pdf View File


BIN
docs/architecting-and-developing-containerized-and-microservice-based-net-applications-ebook-early-draft.pdf View File


BIN
img/eShopOnContainers_Architecture_Diagram.png View File

Before After
Width: 2034  |  Height: 1078  |  Size: 427 KiB Width: 1830  |  Height: 1078  |  Size: 405 KiB

BIN
img/eShopOnContainers_Architecture_Diagram_old.png View File

Before After
Width: 2034  |  Height: 1078  |  Size: 427 KiB

BIN
img/eShopOnContainers_Types_Of_Microservices.png View File

Before After
Width: 1249  |  Height: 581  |  Size: 156 KiB

BIN
img/ebook_xamarin_patterns_cover.png View File

Before After
Width: 310  |  Height: 402  |  Size: 30 KiB

BIN
img/xamarin-enterprise-patterns-ebook-cover-small.png View File

Before After
Width: 260  |  Height: 336  |  Size: 28 KiB

+ 16
- 6
src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml View File

@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:light="clr-namespace:Xamarin.Forms.Themes;assembly=Xamarin.Forms.Theme.Light"
xmlns:converters="clr-namespace:eShopOnContainers.Core.Converters;assembly=eShopOnContainers.Core"
xmlns:effects="clr-namespace:eShopOnContainers.Core.Effects;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
x:Class="eShopOnContainers.App">
<Application.Resources>
<ResourceDictionary MergedWith="light:LightThemeResources">
@ -17,6 +17,7 @@
<Color x:Key="GreenColor">#00A69C</Color>
<Color x:Key="DarkGreenColor">#00857D</Color>
<Color x:Key="GrayColor">#e2e2e2</Color>
<Color x:Key="ErrorColor">#ff5252</Color>
<!-- FONTS -->
<OnPlatform
@ -100,6 +101,7 @@
<!-- CONVERTERS -->
<converters:CountToBoolConverter x:Key="CountToBoolConverter" />
<converters:DatetimeConverter x:Key="DatetimeConverter" />
<converters:FirstValidationErrorConverter x:Key="FirstValidationErrorConverter" />
<converters:ImageConverter x:Key="ImageConverter" />
<converters:ItemTappedEventArgsConverter x:Key="ItemTappedEventArgsConverter" />
<converters:InverseCountToBoolConverter x:Key="InverseCountToBoolConverter" />
@ -110,6 +112,14 @@
<converters:WebNavigatedEventArgsConverter x:Key="WebNavigatedEventArgsConverter" />
<!-- STYLES -->
<Style x:Key="ValidationErrorLabelStyle"
TargetType="{x:Type Label}">
<Setter Property="TextColor"
Value="{StaticResource ErrorColor}" />
<Setter Property="FontSize"
Value="{StaticResource LittleSize}" />
</Style>
<Style x:Key="EntryStyle"
TargetType="{x:Type Entry}">
<Setter Property="FontFamily"
@ -126,9 +136,9 @@
Value="Bold" />
<Setter Property="Opacity"
Value="0.6" />
<Setter Property="effects:LineColorEffect.ApplyLineColor"
<Setter Property="behaviors:LineColorBehavior.ApplyLineColor"
Value="True" />
<Setter Property="effects:LineColorEffect.LineColor"
<Setter Property="behaviors:LineColorBehavior.LineColor"
Value="{StaticResource BlackColor}" />
<Style.Triggers>
<Trigger TargetType="Entry"
@ -157,16 +167,16 @@
Value="Transparent" />
<Setter Property="Opacity"
Value="0.6" />
<Setter Property="effects:LineColorEffect.ApplyLineColor"
<Setter Property="behaviors:LineColorBehavior.ApplyLineColor"
Value="True" />
<Setter Property="effects:LineColorEffect.LineColor"
<Setter Property="behaviors:LineColorBehavior.LineColor"
Value="Gray" />
<Style.Triggers>
<Trigger TargetType="Entry"
Property="IsFocused"
Value="True">
<Setter Property="Opacity" Value="1" />
<Setter Property="effects:LineColorEffect.LineColor"
<Setter Property="behaviors:LineColorBehavior.LineColor"
Value="{StaticResource GreenColor}" />
</Trigger>
</Style.Triggers>


+ 4
- 4
src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs View File

@ -1,6 +1,6 @@
using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Services;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@ -27,13 +27,13 @@ namespace eShopOnContainers
private void InitApp()
{
UseMockServices = Settings.UseMocks;
ViewModelLocator.Instance.UpdateDependencies(UseMockServices);
ViewModelLocator.Initialize();
ViewModelLocator.UpdateDependencies(UseMockServices);
}
private Task InitNavigation()
{
var navigationService = ViewModelLocator.Instance.Resolve<INavigationService>();
var navigationService = ViewModelLocator.Resolve<INavigationService>();
return navigationService.InitializeAsync();
}


src/Mobile/eShopOnContainers/eShopOnContainers.Core/Effects/LineColorEffect.cs → src/Mobile/eShopOnContainers/eShopOnContainers.Core/Behaviors/LineColorBehavior.cs View File

@ -1,14 +1,18 @@
using System.Linq;
using Xamarin.Forms;
using eShopOnContainers.Core.Effects;
namespace eShopOnContainers.Core.Effects
namespace eShopOnContainers.Core.Behaviors
{
public static class LineColorEffect
public static class LineColorBehavior
{
public static readonly BindableProperty ApplyLineColorProperty =
BindableProperty.CreateAttached("ApplyLineColor", typeof(bool), typeof(LineColorEffect), false,
BindableProperty.CreateAttached("ApplyLineColor", typeof(bool), typeof(LineColorBehavior), false,
propertyChanged: OnApplyLineColorChanged);
public static readonly BindableProperty LineColorProperty =
BindableProperty.CreateAttached("LineColor", typeof(Color), typeof(LineColorBehavior), Color.Default);
public static bool GetApplyLineColor(BindableObject view)
{
return (bool)view.GetValue(ApplyLineColorProperty);
@ -19,6 +23,16 @@ namespace eShopOnContainers.Core.Effects
view.SetValue(ApplyLineColorProperty, value);
}
public static Color GetLineColor(BindableObject view)
{
return (Color)view.GetValue(LineColorProperty);
}
public static void SetLineColor(BindableObject view, Color value)
{
view.SetValue(LineColorProperty, value);
}
private static void OnApplyLineColorChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as View;
@ -28,9 +42,9 @@ namespace eShopOnContainers.Core.Effects
return;
}
bool hasShadow = (bool)newValue;
bool hasLine = (bool)newValue;
if (hasShadow)
if (hasLine)
{
view.Effects.Add(new EntryLineColorEffect());
}
@ -43,25 +57,5 @@ namespace eShopOnContainers.Core.Effects
}
}
}
public static readonly BindableProperty LineColorProperty =
BindableProperty.CreateAttached("LineColor", typeof(Color), typeof(LineColorEffect), Color.Default);
public static Color GetLineColor(BindableObject view)
{
return (Color)view.GetValue(LineColorProperty);
}
public static void SetLineColor(BindableObject view, Color value)
{
view.SetValue(LineColorProperty, value);
}
class EntryLineColorEffect : RoutingEffect
{
public EntryLineColorEffect() : base("eShopOnContainers.EntryLineColorEffect")
{
}
}
}
}

+ 22
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Converters/FirstValidationErrorConverter.cs View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Xamarin.Forms;
namespace eShopOnContainers.Core.Converters
{
public class FirstValidationErrorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ICollection<string> errors = value as ICollection<string>;
return errors != null && errors.Count > 0 ? errors.ElementAt(0) : null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

+ 11
- 0
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Effects/EntryLineColorEffect.cs View File

@ -0,0 +1,11 @@
using Xamarin.Forms;
namespace eShopOnContainers.Core.Effects
{
public class EntryLineColorEffect : RoutingEffect
{
public EntryLineColorEffect() : base("eShopOnContainers.EntryLineColorEffect")
{
}
}
}

+ 0
- 18
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Extensions/AnimationExtension.cs View File

@ -1,18 +0,0 @@
using eShopOnContainers.Core.Animations.Base;
using System;
using Xamarin.Forms;
namespace eShopOnContainers.Core.Extensions
{
public static class AnimationExtension
{
public static async void Animate(this VisualElement visualElement, AnimationBase animation, Action onFinishedCallback = null)
{
animation.Target = visualElement;
await animation.Begin();
onFinishedCallback?.Invoke();
}
}
}

+ 3
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/ServicesHelper.cs View File

@ -1,6 +1,6 @@
using eShopOnContainers.Core.Models.Basket;
using eShopOnContainers.Core.Models.Catalog;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -21,7 +21,7 @@ namespace eShopOnContainers.Core.Helpers
try
{
if (!ViewModelLocator.Instance.UseMockService
if (!ViewModelLocator.UseMockService
&& Settings.UrlBase != GlobalSetting.DefaultEndpoint)
{
foreach (var catalogItem in catalogItems)
@ -54,7 +54,7 @@ namespace eShopOnContainers.Core.Helpers
try
{
if (!ViewModelLocator.Instance.UseMockService
if (!ViewModelLocator.UseMockService
&& Settings.UrlBase != GlobalSetting.DefaultEndpoint)
{
foreach (var basketItem in basketItems)


+ 2
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Helpers/Settings.cs View File

@ -1,4 +1,4 @@
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using Plugin.Settings;
using Plugin.Settings.Abstractions;
@ -27,7 +27,7 @@ namespace eShopOnContainers.Core.Helpers
private const string IdUrlBase = "url_base";
private static readonly string AccessTokenDefault = string.Empty;
private static readonly string IdTokenDefault = string.Empty;
private static readonly bool UseMocksDefault = ViewModelLocator.Instance.UseMockService;
private static readonly bool UseMocksDefault = true;
private static readonly string UrlBaseDefault = GlobalSetting.Instance.BaseEndpoint;
#endregion


+ 1
- 8
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Navigation/INavigationService.cs View File

@ -1,5 +1,4 @@
using eShopOnContainers.ViewModels.Base;
using System;
using eShopOnContainers.Core.ViewModels.Base;
using System.Threading.Tasks;
namespace eShopOnContainers.Services
@ -12,12 +11,6 @@ namespace eShopOnContainers.Services
Task NavigateToAsync<TViewModel>(object parameter) where TViewModel : ViewModelBase;
Task NavigateToAsync(Type viewModelType);
Task NavigateToAsync(Type viewModelType, object parameter);
Task NavigateBackAsync();
Task RemoveLastFromBackStackAsync();
Task RemoveBackStackAsync();


+ 33
- 90
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Navigation/NavigationService.cs View File

@ -1,9 +1,10 @@
using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Core.ViewModels;
using eShopOnContainers.Core.Views;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Threading.Tasks;
using Xamarin.Forms;
@ -11,23 +12,6 @@ namespace eShopOnContainers.Services
{
public class NavigationService : INavigationService
{
protected readonly Dictionary<Type, Type> _mappings;
protected Application CurrentApplication
{
get
{
return Application.Current;
}
}
public NavigationService()
{
_mappings = new Dictionary<Type, Type>();
CreatePageViewModelMappings();
}
public Task InitializeAsync()
{
if(string.IsNullOrEmpty(Settings.AuthAccessToken))
@ -46,32 +30,9 @@ namespace eShopOnContainers.Services
return InternalNavigateToAsync(typeof(TViewModel), parameter);
}
public Task NavigateToAsync(Type viewModelType)
public Task RemoveLastFromBackStackAsync()
{
return InternalNavigateToAsync(viewModelType, null);
}
public Task NavigateToAsync(Type viewModelType, object parameter)
{
return InternalNavigateToAsync(viewModelType, parameter);
}
public async Task NavigateBackAsync()
{
if (CurrentApplication.MainPage is CatalogView)
{
var mainPage = CurrentApplication.MainPage as CatalogView;
await mainPage.Navigation.PopAsync();
}
else if (CurrentApplication.MainPage != null)
{
await CurrentApplication.MainPage.Navigation.PopAsync();
}
}
public virtual Task RemoveLastFromBackStackAsync()
{
var mainPage = CurrentApplication.MainPage as CustomNavigationView;
var mainPage = Application.Current.MainPage as CustomNavigationView;
if (mainPage != null)
{
@ -82,9 +43,9 @@ namespace eShopOnContainers.Services
return Task.FromResult(true);
}
public virtual Task RemoveBackStackAsync()
public Task RemoveBackStackAsync()
{
var mainPage = CurrentApplication.MainPage as CustomNavigationView;
var mainPage = Application.Current.MainPage as CustomNavigationView;
if (mainPage != null)
{
@ -98,67 +59,49 @@ namespace eShopOnContainers.Services
return Task.FromResult(true);
}
protected virtual async Task InternalNavigateToAsync(Type viewModelType, object parameter)
private async Task InternalNavigateToAsync(Type viewModelType, object parameter)
{
Page page = CreateAndBindPage(viewModelType, parameter);
Page page = CreatePage(viewModelType, parameter);
if (page is LoginView)
{
CurrentApplication.MainPage = new CustomNavigationView(page);
Application.Current.MainPage = new CustomNavigationView(page);
}
else
{
var navigationPage = CurrentApplication.MainPage as CustomNavigationView;
{
var navigationPage = Application.Current.MainPage as CustomNavigationView;
if (navigationPage != null)
{
await navigationPage.PushAsync(page);
}
else
{
CurrentApplication.MainPage = new CustomNavigationView(page);
Application.Current.MainPage = new CustomNavigationView(page);
}
}
await (page.BindingContext as ViewModelBase).InitializeAsync(parameter);
}
protected Type GetPageTypeForViewModel(Type viewModelType)
{
if (!_mappings.ContainsKey(viewModelType))
{
throw new KeyNotFoundException($"No map for ${viewModelType} was found on navigation mappings");
}
return _mappings[viewModelType];
}
protected Page CreateAndBindPage(Type viewModelType, object parameter)
{
Type pageType = GetPageTypeForViewModel(viewModelType);
if (pageType == null)
{
throw new Exception($"Mapping type for {viewModelType} is not a page");
}
Page page = Activator.CreateInstance(pageType) as Page;
ViewModelBase viewModel = ViewModelLocator.Instance.Resolve(viewModelType) as ViewModelBase;
page.BindingContext = viewModel;
return page;
}
private void CreatePageViewModelMappings()
{
_mappings.Add(typeof(BasketViewModel), typeof(BasketView));
_mappings.Add(typeof(CatalogViewModel), typeof(CatalogView));
_mappings.Add(typeof(CheckoutViewModel), typeof(CheckoutView));
_mappings.Add(typeof(LoginViewModel), typeof(LoginView));
_mappings.Add(typeof(MainViewModel), typeof(MainView));
_mappings.Add(typeof(OrderDetailViewModel), typeof(OrderDetailView));
_mappings.Add(typeof(ProfileViewModel), typeof(ProfileView));
_mappings.Add(typeof(SettingsViewModel), typeof(SettingsView));
}
private Type GetPageTypeForViewModel(Type viewModelType)
{
var viewName = viewModelType.FullName.Replace("Model", string.Empty);
var viewModelAssemblyName = viewModelType.GetTypeInfo().Assembly.FullName;
var viewAssemblyName = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", viewName, viewModelAssemblyName);
var viewType = Type.GetType(viewAssemblyName);
return viewType;
}
private Page CreatePage(Type viewModelType, object parameter)
{
Type pageType = GetPageTypeForViewModel(viewModelType);
if (pageType == null)
{
throw new Exception($"Cannot locate page type for {viewModelType}");
}
Page page = Activator.CreateInstance(pageType) as Page;
return page;
}
}
}

+ 16
- 13
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Validations/ValidatableObject.cs View File

@ -1,6 +1,5 @@
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace eShopOnContainers.Core.Validations
@ -8,13 +7,24 @@ namespace eShopOnContainers.Core.Validations
public class ValidatableObject<T> : ExtendedBindableObject, IValidity
{
private readonly List<IValidationRule<T>> _validations;
private readonly ObservableCollection<string> _errors;
private List<string> _errors;
private T _value;
private bool _isValid;
public List<IValidationRule<T>> Validations => _validations;
public ObservableCollection<string> Errors => _errors;
public List<string> Errors
{
get
{
return _errors;
}
set
{
_errors = value;
RaisePropertyChanged(() => Errors);
}
}
public T Value
{
@ -22,7 +32,6 @@ namespace eShopOnContainers.Core.Validations
{
return _value;
}
set
{
_value = value;
@ -36,11 +45,9 @@ namespace eShopOnContainers.Core.Validations
{
return _isValid;
}
set
{
_isValid = value;
_errors.Clear();
RaisePropertyChanged(() => IsValid);
}
}
@ -48,7 +55,7 @@ namespace eShopOnContainers.Core.Validations
public ValidatableObject()
{
_isValid = true;
_errors = new ObservableCollection<string>();
_errors = new List<string>();
_validations = new List<IValidationRule<T>>();
}
@ -59,11 +66,7 @@ namespace eShopOnContainers.Core.Validations
IEnumerable<string> errors = _validations.Where(v => !v.Check(Value))
.Select(v => v.ValidationMessage);
foreach (var error in errors)
{
Errors.Add(error);
}
Errors = errors.ToList();
IsValid = !Errors.Any();
return this.IsValid;


+ 1
- 1
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ExtendedBindableObject.cs View File

@ -3,7 +3,7 @@ using System.Linq.Expressions;
using System.Reflection;
using Xamarin.Forms;
namespace eShopOnContainers.ViewModels.Base
namespace eShopOnContainers.Core.ViewModels.Base
{
public abstract class ExtendedBindableObject : BindableObject
{


src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/MessengerKeys.cs → src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/MessageKeys.cs View File

@ -1,6 +1,6 @@
namespace eShopOnContainers.Core.ViewModels.Base
{
public class MessengerKeys
public class MessageKeys
{
// Add product to basket
public const string AddProduct = "AddProduct";

+ 4
- 5
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelBase.cs View File

@ -1,9 +1,8 @@
using eShopOnContainers.Core;
using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Services;
using System.Threading.Tasks;
namespace eShopOnContainers.ViewModels.Base
namespace eShopOnContainers.Core.ViewModels.Base
{
public abstract class ViewModelBase : ExtendedBindableObject
{
@ -28,8 +27,8 @@ namespace eShopOnContainers.ViewModels.Base
public ViewModelBase()
{
DialogService = ViewModelLocator.Instance.Resolve<IDialogService>();
NavigationService = ViewModelLocator.Instance.Resolve<INavigationService>();
DialogService = ViewModelLocator.Resolve<IDialogService>();
NavigationService = ViewModelLocator.Resolve<INavigationService>();
GlobalSetting.Instance.BaseEndpoint = Settings.UrlBase;
}


+ 83
- 81
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/Base/ViewModelLocator.cs View File

@ -1,7 +1,8 @@
using Microsoft.Practices.Unity;
using eShopOnContainers.Core.ViewModels;
using eShopOnContainers.Services;
using System;
using System.Globalization;
using System.Reflection;
using eShopOnContainers.Core.Services.Catalog;
using eShopOnContainers.Core.Services.OpenUrl;
using eShopOnContainers.Core.Services.RequestProvider;
@ -9,100 +10,101 @@ using eShopOnContainers.Core.Services.Basket;
using eShopOnContainers.Core.Services.Identity;
using eShopOnContainers.Core.Services.Order;
using eShopOnContainers.Core.Services.User;
using Xamarin.Forms;
namespace eShopOnContainers.ViewModels.Base
namespace eShopOnContainers.Core.ViewModels.Base
{
public class ViewModelLocator
public static class ViewModelLocator
{
private bool _useMockService;
private readonly IUnityContainer _unityContainer;
private static readonly IUnityContainer _unityContainer = new UnityContainer();
private static readonly ViewModelLocator _instance = new ViewModelLocator();
public static readonly BindableProperty AutoWireViewModelProperty =
BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged);
public static ViewModelLocator Instance
{
get { return _instance; }
}
public static bool GetAutoWireViewModel(BindableObject bindable)
{
return (bool)bindable.GetValue(ViewModelLocator.AutoWireViewModelProperty);
}
public bool UseMockService
{
get { return _useMockService; }
set { _useMockService = value; ; }
}
public static void SetAutoWireViewModel(BindableObject bindable, bool value)
{
bindable.SetValue(ViewModelLocator.AutoWireViewModelProperty, value);
}
protected ViewModelLocator()
{
_unityContainer = new UnityContainer();
public static bool UseMockService { get; set; }
// Services
_unityContainer.RegisterType<IDialogService, DialogService>();
RegisterSingleton<INavigationService, NavigationService>();
_unityContainer.RegisterType<IOpenUrlService, OpenUrlService>();
_unityContainer.RegisterType<IRequestProvider, RequestProvider>();
_unityContainer.RegisterType<IIdentityService, IdentityService>();
public static void Initialize()
{
// Services
_unityContainer.RegisterType<IDialogService, DialogService>();
_unityContainer.RegisterType<INavigationService, NavigationService>(new ContainerControlledLifetimeManager());
_unityContainer.RegisterType<IOpenUrlService, OpenUrlService>();
_unityContainer.RegisterType<IRequestProvider, RequestProvider>();
_unityContainer.RegisterType<IIdentityService, IdentityService>();
_unityContainer.RegisterType<ICatalogService, CatalogMockService>();
_unityContainer.RegisterType<IBasketService, BasketMockService>();
_unityContainer.RegisterType<IUserService, UserMockService>();
_unityContainer.RegisterType<ICatalogService, CatalogMockService>();
_unityContainer.RegisterType<IBasketService, BasketMockService>();
_unityContainer.RegisterType<IUserService, UserMockService>();
// View models
_unityContainer.RegisterType<BasketViewModel>();
_unityContainer.RegisterType<CatalogViewModel>();
_unityContainer.RegisterType<CheckoutViewModel>();
_unityContainer.RegisterType<LoginViewModel>();
_unityContainer.RegisterType<MainViewModel>();
_unityContainer.RegisterType<OrderDetailViewModel>();
_unityContainer.RegisterType<ProfileViewModel>();
_unityContainer.RegisterType<SettingsViewModel>();
}
// View models
_unityContainer.RegisterType<BasketViewModel>();
_unityContainer.RegisterType<CatalogViewModel>();
_unityContainer.RegisterType<CheckoutViewModel>();
_unityContainer.RegisterType<LoginViewModel>();
_unityContainer.RegisterType<MainViewModel>();
_unityContainer.RegisterType<OrderDetailViewModel>();
_unityContainer.RegisterType<ProfileViewModel>();
_unityContainer.RegisterType<SettingsViewModel>();
}
public static void UpdateDependencies(bool useMockServices)
{
// Change injected dependencies
if (useMockServices)
{
_unityContainer.RegisterInstance<ICatalogService>(new CatalogMockService());
_unityContainer.RegisterInstance<IBasketService>(new BasketMockService());
_unityContainer.RegisterInstance<IOrderService>(new OrderMockService());
_unityContainer.RegisterInstance<IUserService>(new UserMockService());
public void UpdateDependencies(bool useMockServices)
{
// Change injected dependencies
if (useMockServices)
{
_unityContainer.RegisterInstance<ICatalogService>(new CatalogMockService());
_unityContainer.RegisterInstance<IBasketService>(new BasketMockService());
_unityContainer.RegisterInstance<IOrderService>(new OrderMockService());
_unityContainer.RegisterInstance<IUserService>(new UserMockService());
UseMockService = true;
}
else
{
var requestProvider = Resolve<IRequestProvider>();
_unityContainer.RegisterInstance<ICatalogService>(new CatalogService(requestProvider));
_unityContainer.RegisterInstance<IBasketService>(new BasketService(requestProvider));
_unityContainer.RegisterInstance<IOrderService>(new OrderService(requestProvider));
_unityContainer.RegisterInstance<IUserService>(new UserService(requestProvider));
UseMockService = true;
}
else
{
var requestProvider = Resolve<IRequestProvider>();
_unityContainer.RegisterInstance<ICatalogService>(new CatalogService(requestProvider));
_unityContainer.RegisterInstance<IBasketService>(new BasketService(requestProvider));
_unityContainer.RegisterInstance<IOrderService>(new OrderService(requestProvider));
_unityContainer.RegisterInstance<IUserService>(new UserService(requestProvider));
UseMockService = false;
}
}
UseMockService = false;
}
}
public T Resolve<T>()
{
return _unityContainer.Resolve<T>();
}
public static T Resolve<T>()
{
return _unityContainer.Resolve<T>();
}
public object Resolve(Type type)
{
return _unityContainer.Resolve(type);
}
private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as Element;
if (view == null)
{
return;
}
public void Register<T>(T instance)
{
_unityContainer.RegisterInstance<T>(instance);
}
var viewType = view.GetType();
var viewName = viewType.FullName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = string.Format(CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName);
public void Register<TInterface, T>() where T : TInterface
{
_unityContainer.RegisterType<TInterface, T>();
}
public void RegisterSingleton<TInterface, T>() where T : TInterface
{
_unityContainer.RegisterType<TInterface, T>(new ContainerControlledLifetimeManager());
}
}
var viewModelType = Type.GetType(viewModelName);
if (viewModelType == null)
{
return;
}
var viewModel = _unityContainer.Resolve(viewModelType);
view.BindingContext = viewModel;
}
}
}

+ 12
- 14
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/BasketViewModel.cs View File

@ -4,8 +4,6 @@ using eShopOnContainers.Core.Models.Catalog;
using eShopOnContainers.Core.Services.Basket;
using eShopOnContainers.Core.Services.User;
using eShopOnContainers.Core.ViewModels.Base;
using eShopOnContainers.ViewModels.Base;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
@ -61,7 +59,7 @@ namespace eShopOnContainers.Core.ViewModels
}
}
public ICommand AddCommand => new Command<BasketItem>(AddItem);
public ICommand AddCommand => new Command<BasketItem>(async (item) => await AddItemAsync(item));
public ICommand CheckoutCommand => new Command(async () => await CheckoutAsync());
@ -84,22 +82,22 @@ namespace eShopOnContainers.Core.ViewModels
foreach (var basketItem in basket.Items)
{
BadgeCount += basketItem.Quantity;
AddBasketItem(basketItem);
await AddBasketItemAsync(basketItem);
}
}
MessagingCenter.Unsubscribe<CatalogViewModel, CatalogItem>(this, MessengerKeys.AddProduct);
MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(this, MessengerKeys.AddProduct, (sender, arg) =>
MessagingCenter.Unsubscribe<CatalogViewModel, CatalogItem>(this, MessageKeys.AddProduct);
MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(this, MessageKeys.AddProduct, async (sender, arg) =>
{
BadgeCount++;
AddCatalogItem(arg);
await AddCatalogItemAsync(arg);
});
await base.InitializeAsync(navigationData);
}
private void AddCatalogItem(CatalogItem item)
private async Task AddCatalogItemAsync(CatalogItem item)
{
BasketItems.Add(new BasketItem
{
@ -110,26 +108,26 @@ namespace eShopOnContainers.Core.ViewModels
Quantity = 1
});
ReCalculateTotal();
await ReCalculateTotalAsync();
}
private void AddItem(BasketItem item)
private async Task AddItemAsync(BasketItem item)
{
BadgeCount++;
AddBasketItem(item);
await AddBasketItemAsync(item);
RaisePropertyChanged(() => BasketItems);
}
private void AddBasketItem(BasketItem item)
private async Task AddBasketItemAsync(BasketItem item)
{
BasketItems.Add(item);
ReCalculateTotal();
await ReCalculateTotalAsync();
}
private async void ReCalculateTotal()
private async Task ReCalculateTotalAsync()
{
Total = 0;


+ 8
- 19
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CatalogViewModel.cs View File

@ -1,14 +1,11 @@
using System.Threading.Tasks;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using System.Collections.ObjectModel;
using Xamarin.Forms;
using eShopOnContainers.Core.ViewModels.Base;
using eShopOnContainers.Core.Models.Catalog;
using eShopOnContainers.Core.Services.Catalog;
using System.Windows.Input;
using System.Linq;
using eShopOnContainers.Core.Services.Basket;
using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Core.Services.User;
namespace eShopOnContainers.Core.ViewModels
@ -20,19 +17,11 @@ namespace eShopOnContainers.Core.ViewModels
private CatalogBrand _brand;
private ObservableCollection<CatalogType> _types;
private CatalogType _type;
private IBasketService _basketService;
private ICatalogService _productsService;
private IUserService _userService;
public CatalogViewModel(
IBasketService basketService,
ICatalogService productsService,
IUserService userService)
public CatalogViewModel(ICatalogService productsService)
{
_basketService = basketService;
_productsService = productsService;
_userService = userService;
}
public ObservableCollection<CatalogItem> Products
@ -91,9 +80,9 @@ namespace eShopOnContainers.Core.ViewModels
public ICommand AddCatalogItemCommand => new Command<CatalogItem>(AddCatalogItem);
public ICommand FilterCommand => new Command(Filter);
public ICommand FilterCommand => new Command(async () => await FilterAsync());
public ICommand ClearFilterCommand => new Command(ClearFilter);
public ICommand ClearFilterCommand => new Command(async () => await ClearFilterAsync());
public override async Task InitializeAsync(object navigationData)
{
@ -110,10 +99,10 @@ namespace eShopOnContainers.Core.ViewModels
private void AddCatalogItem(CatalogItem catalogItem)
{
// Add new item to Basket
MessagingCenter.Send(this, MessengerKeys.AddProduct, catalogItem);
MessagingCenter.Send(this, MessageKeys.AddProduct, catalogItem);
}
private async void Filter()
private async Task FilterAsync()
{
if (Brand == null && Type == null)
{
@ -123,13 +112,13 @@ namespace eShopOnContainers.Core.ViewModels
IsBusy = true;
// Filter catalog products
MessagingCenter.Send(this, MessengerKeys.Filter);
MessagingCenter.Send(this, MessageKeys.Filter);
Products = await _productsService.FilterAsync(Brand.Id, Type.Id);
IsBusy = false;
}
private async void ClearFilter()
private async Task ClearFilterAsync()
{
IsBusy = true;


+ 2
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/CheckoutViewModel.cs View File

@ -1,5 +1,5 @@
using eShopOnContainers.Core.Models.Navigation;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using System.Windows.Input;
using Xamarin.Forms;
using System.Threading.Tasks;
@ -139,7 +139,7 @@ namespace eShopOnContainers.Core.ViewModels
await _basketService.ClearBasketAsync(_shippingAddress.Id.ToString(), authToken);
// Reset Basket badge
var basketViewModel = ViewModelLocator.Instance.Resolve<BasketViewModel>();
var basketViewModel = ViewModelLocator.Resolve<BasketViewModel>();
basketViewModel.BadgeCount = 0;
// Navigate to Orders


+ 20
- 10
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs View File

@ -2,9 +2,8 @@
using eShopOnContainers.Core.Models.User;
using eShopOnContainers.Core.Services.Identity;
using eShopOnContainers.Core.Services.OpenUrl;
using eShopOnContainers.Core.Services.User;
using eShopOnContainers.Core.Validations;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using IdentityModel.Client;
using System;
using System.Diagnostics;
@ -25,16 +24,13 @@ namespace eShopOnContainers.Core.ViewModels
private IOpenUrlService _openUrlService;
private IIdentityService _identityService;
private IUserService _userService;
public LoginViewModel(
IOpenUrlService openUrlService,
IIdentityService identityService,
IUserService userService)
IIdentityService identityService)
{
_openUrlService = openUrlService;
_identityService = identityService;
_userService = userService;
_userName = new ValidatableObject<string>();
_password = new ValidatableObject<string>();
@ -131,6 +127,10 @@ namespace eShopOnContainers.Core.ViewModels
public ICommand SettingsCommand => new Command(async () => await SettingsAsync());
public ICommand ValidateUserNameCommand => new Command(() => ValidateUserName());
public ICommand ValidatePasswordCommand => new Command(() => ValidatePassword());
public override Task InitializeAsync(object navigationData)
{
if(navigationData is LogoutParameter)
@ -255,16 +255,26 @@ namespace eShopOnContainers.Core.ViewModels
private bool Validate()
{
bool isValidUser = _userName.Validate();
bool isValidPassword = _password.Validate();
bool isValidUser = ValidateUserName();
bool isValidPassword = ValidatePassword();
return isValidUser && isValidPassword;
}
private bool ValidateUserName()
{
return _userName.Validate();
}
private bool ValidatePassword()
{
return _password.Validate();
}
private void AddValidations()
{
_userName.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "Username should not be empty" });
_password.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "Password should not be empty" });
_userName.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "A username is required." });
_password.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "A password is required." });
}
public void InvalidateMock()


+ 2
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/MainViewModel.cs View File

@ -1,8 +1,7 @@
using System.Threading.Tasks;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using eShopOnContainers.Core.Models.Navigation;
using Xamarin.Forms;
using eShopOnContainers.Core.ViewModels.Base;
using System.Windows.Input;
namespace eShopOnContainers.Core.ViewModels
@ -19,7 +18,7 @@ namespace eShopOnContainers.Core.ViewModels
{
// Change selected application tab
var tabIndex = ((TabParameter)navigationData).TabIndex;
MessagingCenter.Send(this, MessengerKeys.ChangeTab, tabIndex);
MessagingCenter.Send(this, MessageKeys.ChangeTab, tabIndex);
}
return base.InitializeAsync(navigationData);


+ 2
- 9
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/OrderDetailViewModel.cs View File

@ -1,6 +1,6 @@
using System.Threading.Tasks;
using eShopOnContainers.Core.Models.Orders;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using eShopOnContainers.Core.Services.Catalog;
using eShopOnContainers.Core.Services.Basket;
using eShopOnContainers.Core.Services.Order;
@ -13,17 +13,10 @@ namespace eShopOnContainers.Core.ViewModels
{
private Order _order;
private IBasketService _orderService;
private ICatalogService _catalogService;
private IOrderService _ordersService;
public OrderDetailViewModel(
IBasketService orderService,
ICatalogService catalogService,
IOrderService ordersService)
public OrderDetailViewModel(IOrderService ordersService)
{
_orderService = orderService;
_catalogService = catalogService;
_ordersService = ordersService;
}


+ 1
- 1
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/ProfileViewModel.cs View File

@ -3,7 +3,7 @@ using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Core.Models.Orders;
using eShopOnContainers.Core.Models.User;
using eShopOnContainers.Core.Services.Order;
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;


+ 2
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/SettingsViewModel.cs View File

@ -1,4 +1,4 @@
using eShopOnContainers.ViewModels.Base;
using eShopOnContainers.Core.ViewModels.Base;
using System.Windows.Input;
using Xamarin.Forms;
using System.Threading.Tasks;
@ -71,7 +71,7 @@ namespace eShopOnContainers.Core.ViewModels
private void MockServices()
{
ViewModelLocator.Instance.UpdateDependencies(!UseAzureServices);
ViewModelLocator.UpdateDependencies(!UseAzureServices);
UpdateInfo();
}


+ 4
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/BasketView.xaml View File

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.BasketView"
x:Class="eShopOnContainers.Core.Views.BasketView"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
xmlns:templates="clr-namespace:eShopOnContainers.Core.Views.Templates;assembly=eShopOnContainers.Core"
Title="Cart">
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
Title="Cart">
<ContentPage.Resources>
<ResourceDictionary>


+ 5
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CatalogView.xaml View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.CatalogView"
xmlns:templates="clr-namespace:eShopOnContainers.Core.Views.Templates;assembly=eShopOnContainers.Core"
xmlns:views="clr-namespace:eShopOnContainers.Core.Views;assembly=eShopOnContainers.Core"
xmlns:views="clr-namespace:eShopOnContainers.Core.Views;assembly=eShopOnContainers.Core"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:animations="clr-namespace:eShopOnContainers.Core.Animations;assembly=eShopOnContainers.Core"
xmlns:triggers="clr-namespace:eShopOnContainers.Core.Triggers;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
Title="Catalog">
<ContentPage.Resources>
<ResourceDictionary>


+ 2
- 5
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CatalogView.xaml.cs View File

@ -8,17 +8,15 @@ namespace eShopOnContainers.Core.Views
{
public partial class CatalogView : ContentPage, IMenuContainerPage
{
private FiltersView _filterView;
private FiltersView _filterView = new FiltersView();
public CatalogView()
{
InitializeComponent();
_filterView = new FiltersView();
SlideMenu = _filterView;
MessagingCenter.Subscribe<CatalogViewModel>(this, MessengerKeys.Filter, (sender) =>
MessagingCenter.Subscribe<CatalogViewModel>(this, MessageKeys.Filter, (sender) =>
{
Filter();
});
@ -42,7 +40,6 @@ namespace eShopOnContainers.Core.Views
set;
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();


+ 8
- 10
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/CheckoutView.xaml View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.CheckoutView"
xmlns:templates="clr-namespace:eShopOnContainers.Core.Views.Templates;assembly=eShopOnContainers.Core"
x:Class="eShopOnContainers.Core.Views.CheckoutView"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:templates="clr-namespace:eShopOnContainers.Core.Views.Templates;assembly=eShopOnContainers.Core"
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
Title="Checkout">
<ContentPage.Resources>
<ResourceDictionary>
@ -82,8 +84,7 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- ORDER INFO -->
<Grid
x:Name="OrderInfo">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
@ -139,8 +140,7 @@
</Grid>
<!-- SHIPPING ADDRESS -->
<Grid
x:Name="ShippingAddress"
Grid.Row="1"
Grid.Row="1"
Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@ -167,9 +167,7 @@
</StackLayout>
</Grid>
<!-- ORDER ITEMS -->
<Grid
x:Name="OrderItems"
Grid.Row="2">
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />


+ 39
- 7
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml View File

@ -2,9 +2,11 @@
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.LoginView"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:animations="clr-namespace:eShopOnContainers.Core.Animations;assembly=eShopOnContainers.Core"
xmlns:triggers="clr-namespace:eShopOnContainers.Core.Triggers;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core">
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
viewModelBase:ViewModelLocator.AutoWireViewModel="true">
<ContentPage.Title>
<OnPlatform
x:TypeArguments="x:String"
@ -175,30 +177,60 @@
Margin="24">
<Label
Text="User name or email"
Style="{StaticResource HeaderLabelStyle}"/>
<Entry
Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Style>
Style="{StaticResource HeaderLabelStyle}" />
<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
<Entry.Style>
<OnPlatform x:TypeArguments="Style"
iOS="{StaticResource EntryStyle}"
Android="{StaticResource EntryStyle}"
WinPhone="{StaticResource UwpEntryStyle}"/>
</Entry.Style>
<Entry.Behaviors>
<behaviors:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidateUserNameCommand}" />
</Entry.Behaviors>
<Entry.Triggers>
<DataTrigger
TargetType="Entry"
Binding="{Binding UserName.IsValid}"
Value="False">
<Setter Property="behaviors:LineColorBehavior.LineColor" Value="{StaticResource ErrorColor}" />
</DataTrigger>
</Entry.Triggers>
</Entry>
<Label
Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}"
Style="{StaticResource ValidationErrorLabelStyle}" />
<Label
Text="Password"
Style="{StaticResource HeaderLabelStyle}"/>
<Entry
IsPassword="True"
Text="{Binding Password.Value, Mode=TwoWay}"
Style="{StaticResource EntryStyle}">
Text="{Binding Password.Value, Mode=TwoWay}">
<Entry.Style>
<OnPlatform x:TypeArguments="Style"
iOS="{StaticResource EntryStyle}"
Android="{StaticResource EntryStyle}"
WinPhone="{StaticResource UwpEntryStyle}"/>
</Entry.Style>
<Entry.Behaviors>
<behaviors:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding ValidatePasswordCommand}" />
</Entry.Behaviors>
<Entry.Triggers>
<DataTrigger
TargetType="Entry"
Binding="{Binding Password.IsValid}"
Value="False">
<Setter Property="behaviors:LineColorBehavior.LineColor" Value="{StaticResource ErrorColor}" />
</DataTrigger>
</Entry.Triggers>
</Entry>
<Label
Text="{Binding Password.Errors, Converter={StaticResource FirstValidationErrorConverter}"
Style="{StaticResource ValidationErrorLabelStyle}" />
</StackLayout>
<!-- LOGIN BUTTON -->
<Grid


+ 16
- 16
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/LoginView.xaml.cs View File

@ -1,5 +1,4 @@
using eShopOnContainers.Core.Helpers;
using eShopOnContainers.Core.ViewModels;
using eShopOnContainers.Core.ViewModels;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
@ -22,14 +21,16 @@ namespace eShopOnContainers.Core.Views
this.Content = null;
this.Content = content;
_animate = true;
await AnimateIn();
var vm = BindingContext as LoginViewModel;
if(vm != null)
var vm = BindingContext as LoginViewModel;
if (vm != null)
{
vm.InvalidateMock();
if (!vm.IsMock)
{
_animate = true;
await AnimateIn();
}
}
}
@ -54,14 +55,13 @@ namespace eShopOnContainers.Core.Views
{
while (_animate)
{
await uiElement.ScaleTo(1.05, duration, Easing.SinInOut);
await Task.WhenAll(
uiElement.FadeTo(1, duration, Easing.SinInOut),
uiElement.LayoutTo(new Rectangle(new Point(0, 0), new Size(uiElement.Width, uiElement.Height))),
uiElement.FadeTo(.9, duration, Easing.SinInOut),
uiElement.ScaleTo(1.15, duration, Easing.SinInOut)
);
await uiElement.ScaleTo(1.05, duration, Easing.SinInOut);
await Task.WhenAll(
uiElement.FadeTo(1, duration, Easing.SinInOut),
uiElement.LayoutTo(new Rectangle(new Point(0, 0), new Size(uiElement.Width, uiElement.Height))),
uiElement.FadeTo(.9, duration, Easing.SinInOut),
uiElement.ScaleTo(1.15, duration, Easing.SinInOut)
);
}
}
catch (Exception ex)


+ 4
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.MainView"
xmlns:views="clr-namespace:eShopOnContainers.Core.Views;assembly=eShopOnContainers.Core"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:controls="clr-namespace:eShopOnContainers.Core.Controls;assembly=eShopOnContainers.Core"
BarBackgroundColor="{StaticResource DarkGreenColor}"
BackgroundColor="{StaticResource BackgroundColor}"
BarTextColor="{StaticResource WhiteColor}">
BarTextColor="{StaticResource WhiteColor}"
viewModelBase:ViewModelLocator.AutoWireViewModel="true">
<TabbedPage.Title>
<OnPlatform
x:TypeArguments="x:String"


+ 4
- 13
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/MainView.xaml.cs View File

@ -1,6 +1,5 @@
using eShopOnContainers.Core.ViewModels;
using eShopOnContainers.Core.ViewModels.Base;
using eShopOnContainers.ViewModels.Base;
using Xamarin.Forms;
namespace eShopOnContainers.Core.Views
@ -16,7 +15,7 @@ namespace eShopOnContainers.Core.Views
{
base.OnAppearing();
MessagingCenter.Subscribe<MainViewModel, int>(this, MessengerKeys.ChangeTab, (sender, arg) =>
MessagingCenter.Subscribe<MainViewModel, int>(this, MessageKeys.ChangeTab, (sender, arg) =>
{
switch(arg)
{
@ -32,17 +31,9 @@ namespace eShopOnContainers.Core.Views
}
});
var homeViewModel = ViewModelLocator.Instance.Resolve<CatalogViewModel>();
await homeViewModel.InitializeAsync(null);
HomeView.BindingContext = homeViewModel;
var basketViewModel = ViewModelLocator.Instance.Resolve<BasketViewModel>();
await basketViewModel.InitializeAsync(null);
BasketView.BindingContext = basketViewModel;
var profileViewModel = ViewModelLocator.Instance.Resolve<ProfileViewModel>();
await profileViewModel.InitializeAsync(null);
ProfileView.BindingContext = profileViewModel;
await ((CatalogViewModel)HomeView.BindingContext).InitializeAsync(null);
await ((BasketViewModel)BasketView.BindingContext).InitializeAsync(null);
await ((ProfileViewModel)ProfileView.BindingContext).InitializeAsync(null);
}
protected override async void OnCurrentPageChanged()


+ 4
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/OrderDetailView.xaml View File

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.OrderDetailView"
x:Class="eShopOnContainers.Core.Views.OrderDetailView"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:templates="clr-namespace:eShopOnContainers.Core.Views.Templates;assembly=eShopOnContainers.Core"
xmlns:animations="clr-namespace:eShopOnContainers.Core.Animations;assembly=eShopOnContainers.Core"
xmlns:triggers="clr-namespace:eShopOnContainers.Core.Triggers;assembly=eShopOnContainers.Core"
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
Title="{Binding Order.OrderNumber}">
<ContentPage.Resources>
<ResourceDictionary>


+ 6
- 4
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/ProfileView.xaml View File

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.ProfileView"
xmlns:views="clr-namespace:eShopOnContainers.Core.Views;assembly=eShopOnContainers.Core"
x:Class="eShopOnContainers.Core.Views.ProfileView"
xmlns:views="clr-namespace:eShopOnContainers.Core.Views;assembly=eShopOnContainers.Core"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:templates="clr-namespace:eShopOnContainers.Core.Views.Templates;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
Title="My profile">
<ContentPage.Resources>
<ResourceDictionary>


+ 5
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.Core/Views/SettingsView.xaml View File

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="eShopOnContainers.Core.Views.SettingsView"
x:Class="eShopOnContainers.Core.Views.SettingsView"
xmlns:viewModelBase="clr-namespace:eShopOnContainers.Core.ViewModels.Base;assembly=eShopOnContainers.Core"
xmlns:controls="clr-namespace:eShopOnContainers.Core.Controls;assembly=eShopOnContainers.Core"
xmlns:animations="clr-namespace:eShopOnContainers.Core.Animations;assembly=eShopOnContainers.Core"
xmlns:triggers="clr-namespace:eShopOnContainers.Core.Triggers;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
xmlns:behaviors="clr-namespace:eShopOnContainers.Core.Behaviors;assembly=eShopOnContainers.Core"
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
Title="Settings">
<ContentPage.Resources>
<ResourceDictionary>


+ 5
- 2
src/Mobile/eShopOnContainers/eShopOnContainers.Core/eShopOnContainers.Core.csproj View File

@ -112,7 +112,7 @@
<Compile Include="Validations\IValidity.cs" />
<Compile Include="Validations\ValidatableObject.cs" />
<Compile Include="ViewModels\Base\ExtendedBindableObject.cs" />
<Compile Include="ViewModels\Base\MessengerKeys.cs" />
<Compile Include="ViewModels\Base\MessageKeys.cs" />
<Compile Include="ViewModels\Base\ViewModelBase.cs" />
<Compile Include="ViewModels\Base\ViewModelLocator.cs" />
<Compile Include="ViewModels\BasketViewModel.cs" />
@ -166,6 +166,9 @@
<DependentUpon>ProductTemplate.xaml</DependentUpon>
</Compile>
<Compile Include="Converters\WebNavigatingEventArgsConverter.cs" />
<Compile Include="Converters\FirstValidationErrorConverter.cs" />
<Compile Include="Effects\EntryLineColorEffect.cs" />
<Compile Include="Behaviors\LineColorBehavior.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
@ -352,4 +355,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

+ 3
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.Droid/Effects/EntryLineColorEffect.cs View File

@ -3,7 +3,7 @@ using eShopOnContainers.Droid.Effects;
using Xamarin.Forms.Platform.Android;
using System;
using Android.Widget;
using eShopOnContainers.Core.Effects;
using eShopOnContainers.Core.Behaviors;
using System.ComponentModel;
using System.Diagnostics;
@ -34,7 +34,7 @@ namespace eShopOnContainers.Droid.Effects
protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == LineColorEffect.LineColorProperty.PropertyName)
if (args.PropertyName == LineColorBehavior.LineColorProperty.PropertyName)
{
UpdateLineColor();
}
@ -46,7 +46,7 @@ namespace eShopOnContainers.Droid.Effects
{
if (control != null)
{
control.Background.SetColorFilter(LineColorEffect.GetLineColor(Element).ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop);
control.Background.SetColorFilter(LineColorBehavior.GetLineColor(Element).ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop);
}
}
catch (Exception ex)


+ 3
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Effects/EntryLineColorEffect.cs View File

@ -8,7 +8,7 @@ using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
using Windows.UI.Xaml.Controls;
using eShopOnContainers.Windows.Effects;
using eShopOnContainers.Core.Effects;
using eShopOnContainers.Core.Behaviors;
[assembly: ResolutionGroupName("eShopOnContainers")]
[assembly: ExportEffect(typeof(EntryLineColorEffect), "EntryLineColorEffect")]
@ -38,7 +38,7 @@ namespace eShopOnContainers.Windows.Effects
protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
if (args.PropertyName == LineColorEffect.LineColorProperty.PropertyName)
if (args.PropertyName == LineColorBehavior.LineColorProperty.PropertyName)
{
UpdateLineColor();
}
@ -49,7 +49,7 @@ namespace eShopOnContainers.Windows.Effects
if (control != null)
{
control.BorderThickness = new Xaml.Thickness(0, 0, 0, 1);
var lineColor = XamarinFormColorToWindowsColor(LineColorEffect.GetLineColor(Element));
var lineColor = XamarinFormColorToWindowsColor(LineColorBehavior.GetLineColor(Element));
control.BorderBrush = new Media.SolidColorBrush(lineColor);
var style = Xaml.Application.Current.Resources["FormTextBoxStyle"] as Xaml.Style;


+ 3
- 3
src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Effects/EntryLineColorEffect.cs View File

@ -1,6 +1,6 @@
using CoreAnimation;
using CoreGraphics;
using eShopOnContainers.Core.Effects;
using eShopOnContainers.Core.Behaviors;
using eShopOnContainers.iOS.Effects;
using System;
using System.ComponentModel;
@ -39,7 +39,7 @@ namespace eShopOnContainers.iOS.Effects
{
base.OnElementPropertyChanged(args);
if (args.PropertyName == LineColorEffect.LineColorProperty.PropertyName ||
if (args.PropertyName == LineColorBehavior.LineColorProperty.PropertyName ||
args.PropertyName == "Height")
{
Initialize();
@ -71,7 +71,7 @@ namespace eShopOnContainers.iOS.Effects
}
lineLayer.Frame = new CGRect(0f, Control.Frame.Height - 1f, Control.Bounds.Width, 1f);
lineLayer.BorderColor = LineColorEffect.GetLineColor(Element).ToCGColor();
lineLayer.BorderColor = LineColorBehavior.GetLineColor(Element).ToCGColor();
control.TintColor = control.TextColor;
}


+ 1
- 0
src/Services/Catalog/Catalog.API/Startup.cs View File

@ -21,6 +21,7 @@
using System.Data.Common;
using System.Reflection;
using global::Catalog.API.IntegrationEvents;
using System.Threading.Tasks;
public class Startup
{


+ 24
- 24
src/Services/Identity/Identity.API/Configuration/Config.cs View File

@ -1,31 +1,30 @@
using IdentityServer4.Models;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using IdentityServer4;
namespace Identity.API.Configuration
{
public class Config
{
// scopes define the resources in your system
public static IEnumerable<Scope> GetScopes()
// ApiResources define the apis in your system
public static IEnumerable<ApiResource> GetApis()
{
return new List<Scope>
return new List<ApiResource>
{
//Authentication OpenId uses this scopes;
StandardScopes.OpenId,
StandardScopes.Profile,
new ApiResource("orders", "Orders Service"),
new ApiResource("basket", "Basket Service")
};
}
//Each api we want to securice;
new Scope
{
Name = "orders",
Description = "Orders Service"
},
new Scope
{
Name = "basket",
Description = "Basket Service"
}
// Identity resources are data like user ID, name, or email address of a user
// see: http://docs.identityserver.io/en/release/configuration/resources.html
public static IEnumerable<IdentityResource> GetResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
@ -47,8 +46,8 @@ namespace Identity.API.Configuration
AllowedCorsOrigins = { $"{clientsUrl["Spa"]}" },
AllowedScopes =
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"orders",
"basket"
}
@ -65,8 +64,8 @@ namespace Identity.API.Configuration
AllowedCorsOrigins = { "http://eshopxamarin" },
AllowedScopes =
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"orders",
"basket"
}
@ -82,6 +81,7 @@ namespace Identity.API.Configuration
ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client
AllowedGrantTypes = GrantTypes.Hybrid,
RequireConsent = false,
AllowOfflineAccess = true,
RedirectUris = new List<string>
{
$"{clientsUrl["Mvc"]}/signin-oidc",
@ -96,9 +96,9 @@ namespace Identity.API.Configuration
},
AllowedScopes = new List<string>
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
StandardScopes.OfflineAccess.Name,
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"orders",
"basket",
},


+ 0
- 1
src/Services/Identity/Identity.API/Controllers/AccountController.cs View File

@ -5,7 +5,6 @@
using IdentityModel;
using IdentityServer4.Quickstart.UI.Models;
using IdentityServer4.Services;
using IdentityServer4.Services.InMemory;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Mvc;
using System;


+ 6
- 6
src/Services/Identity/Identity.API/Controllers/ConsentController.cs View File

@ -22,7 +22,7 @@ namespace IdentityServer4.Quickstart.UI.Controllers
{
private readonly ILogger<ConsentController> _logger;
private readonly IClientStore _clientStore;
private readonly IScopeStore _scopeStore;
private readonly IResourceStore _resourceStore;
private readonly IIdentityServerInteractionService _interaction;
@ -30,12 +30,12 @@ namespace IdentityServer4.Quickstart.UI.Controllers
ILogger<ConsentController> logger,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IScopeStore scopeStore)
IResourceStore resourceStore)
{
_logger = logger;
_interaction = interaction;
_clientStore = clientStore;
_scopeStore = scopeStore;
_resourceStore = resourceStore;
}
/// <summary>
@ -120,10 +120,10 @@ namespace IdentityServer4.Quickstart.UI.Controllers
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
if (client != null)
{
var scopes = await _scopeStore.FindEnabledScopesAsync(request.ScopesRequested);
if (scopes != null && scopes.Any())
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any()))
{
return new ConsentViewModel(model, returnUrl, request, client, scopes);
return new ConsentViewModel(model, returnUrl, request, client, resources);
}
else
{


+ 2
- 2
src/Services/Identity/Identity.API/Identity.API.csproj View File

@ -41,8 +41,8 @@
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.1.0-msbuild3-final">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="1.0.0-rc3" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="1.0.0-rc3" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="1.0.0" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="1.0.0" />
</ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">


+ 13
- 3
src/Services/Identity/Identity.API/Models/AccountViewModels/ConsentViewModel.cs View File

@ -10,7 +10,7 @@ namespace Identity.API.Models.AccountViewModels
{
public class ConsentViewModel : ConsentInputModel
{
public ConsentViewModel(ConsentInputModel model, string returnUrl, AuthorizationRequest request, Client client, IEnumerable<Scope> scopes)
public ConsentViewModel(ConsentInputModel model, string returnUrl, AuthorizationRequest request, Client client, Resources resources)
{
RememberConsent = model?.RememberConsent ?? true;
ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>();
@ -22,8 +22,8 @@ namespace Identity.API.Models.AccountViewModels
ClientLogoUrl = client.LogoUri;
AllowRememberConsent = client.AllowRememberConsent;
IdentityScopes = scopes.Where(x => x.Type == ScopeType.Identity).Select(x => new ScopeViewModel(x, ScopesConsented.Contains(x.Name) || model == null)).ToArray();
ResourceScopes = scopes.Where(x => x.Type == ScopeType.Resource).Select(x => new ScopeViewModel(x, ScopesConsented.Contains(x.Name) || model == null)).ToArray();
IdentityScopes = resources.IdentityResources.Select(x => new ScopeViewModel(x, ScopesConsented.Contains(x.Name) || model == null)).ToArray();
ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => new ScopeViewModel(x, ScopesConsented.Contains(x.Name) || model == null)).ToArray();
}
public string ClientName { get; set; }
@ -47,6 +47,16 @@ namespace Identity.API.Models.AccountViewModels
Checked = check || scope.Required;
}
public ScopeViewModel(IdentityResource identity, bool check)
{
Name = identity.Name;
DisplayName = identity.DisplayName;
Description = identity.Description;
Emphasize = identity.Emphasize;
Required = identity.Required;
Checked = check || identity.Required;
}
public string Name { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }


+ 2
- 1
src/Services/Identity/Identity.API/Startup.cs View File

@ -78,7 +78,8 @@ namespace eShopOnContainers.Identity
// Adds IdentityServer
services.AddIdentityServer(x => x.IssuerUri = "null")
.AddSigningCredential(Certificate.Get())
.AddInMemoryScopes(Config.GetScopes())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryIdentityResources(Config.GetResources())
.AddInMemoryClients(Config.GetClients(clientUrls))
.AddAspNetIdentity<ApplicationUser>()
.Services.AddTransient<IProfileService, ProfileService>();


+ 1
- 0
src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs View File

@ -33,6 +33,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof
.Where(i => i.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>)))
.Select(i => new KeyedService("IAsyncNotificationHandler", i)))
.AsImplementedInterfaces();
builder
.RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly)


+ 7
- 8
src/Web/WebMVC/Startup.cs View File

@ -14,6 +14,7 @@ using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http;
using System.Threading;
using Microsoft.Extensions.Options;
using WebMVC.Services.Utilities;
using Microsoft.Extensions.HealthChecks;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.HttpResilience;
@ -48,7 +49,10 @@ namespace Microsoft.eShopOnContainers.WebMVC
services.AddHealthChecks(checks =>
{
checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Ok")));
checks.AddUrlCheck(Configuration["CatalogUrl"]);
checks.AddUrlCheck(Configuration["OrderingUrl"]);
checks.AddUrlCheck(Configuration["BasketUrl"]);
checks.AddUrlCheck(Configuration["IdentityUrl"]);
});
// Add application services.
@ -115,15 +119,10 @@ namespace Microsoft.eShopOnContainers.WebMVC
ResponseType = "code id_token",
SaveTokens = true,
GetClaimsFromUserInfoEndpoint = true,
RequireHttpsMetadata = false,
RequireHttpsMetadata = false,
Scope = { "openid", "profile", "orders", "basket" }
};
oidcOptions.Scope.Clear();
oidcOptions.Scope.Add("openid");
oidcOptions.Scope.Add("profile");
oidcOptions.Scope.Add("orders");
oidcOptions.Scope.Add("basket");
//Wait untill identity service is ready on compose.
app.UseOpenIdConnectAuthentication(oidcOptions);


+ 0
- 9
src/Web/WebMonolithic/Readme.txt View File

@ -1,9 +0,0 @@
Let's create here the simplest monolithic MVC Web app with just the Home Page and ASP.NET Core MVC.
Please, don't say anything about "Containers" here.
From a "branding point of view" it should be named as "eShopWeb"
Cesar.

Loading…
Cancel
Save