Sign in , out ,register.

This commit is contained in:
Burak Kaan Köse
2026-03-16 01:33:27 +01:00
parent 921c3bef93
commit 37c1bd3f62
35 changed files with 1195 additions and 30 deletions
+12
View File
@@ -255,6 +255,7 @@ public partial class App : WinoApplication,
// Initialize theme service after window creation.
// Theme service requires the window to exist to properly load and apply themes.
await NewThemeService.InitializeAsync();
await LoadInitialWinoAccountAsync();
LogActivation("Theme service initialized.");
// If startup task launch, keep window hidden (system tray only).
@@ -826,6 +827,17 @@ public partial class App : WinoApplication,
_autoSynchronizationLoopCts = null;
}
private async Task LoadInitialWinoAccountAsync()
{
var winoAccountProfileService = Services.GetRequiredService<IWinoAccountProfileService>();
var winoAccount = await winoAccountProfileService.GetActiveAccountAsync().ConfigureAwait(false);
if (winoAccount != null)
{
WeakReferenceMessenger.Default.Send(new WinoAccountSignedInMessage(winoAccount));
}
}
private async Task RunAutoSynchronizationLoopAsync(TimeSpan interval, CancellationToken cancellationToken)
{
try
@@ -0,0 +1,51 @@
<ContentDialog
x:Class="Wino.Dialogs.WinoAccountLoginDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{x:Bind domain:Translator.WinoAccount_LoginDialog_Title}"
PrimaryButtonClick="LoginClicked"
PrimaryButtonText="{x:Bind domain:Translator.Buttons_SignIn}"
SecondaryButtonText="{x:Bind domain:Translator.Buttons_Cancel}"
Style="{StaticResource WinoDialogStyle}"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMinWidth">440</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">440</x:Double>
</ContentDialog.Resources>
<StackPanel Spacing="12">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind domain:Translator.WinoAccount_LoginDialog_Description}"
TextWrapping="WrapWholeWords" />
<TextBox
x:Name="EmailTextBox"
Header="{x:Bind domain:Translator.WinoAccount_EmailLabel}"
PlaceholderText="{x:Bind domain:Translator.WinoAccount_EmailPlaceholder}"
TextChanging="InputChanged" />
<PasswordBox
x:Name="PasswordBox"
Header="{x:Bind domain:Translator.WinoAccount_PasswordLabel}"
PasswordChanged="InputChanged" />
<ProgressRing
x:Name="BusyRing"
Width="20"
Height="20"
HorizontalAlignment="Left"
IsActive="False"
Visibility="Collapsed" />
<TextBlock
x:Name="ErrorTextBlock"
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
TextWrapping="WrapWholeWords"
Visibility="Collapsed" />
</StackPanel>
</ContentDialog>
@@ -0,0 +1,97 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces;
using Wino.Mail.WinUI.Services;
namespace Wino.Dialogs;
public sealed partial class WinoAccountLoginDialog : ContentDialog
{
private readonly IWinoAccountProfileService _profileService;
public WinoAccountLoginDialog(IWinoAccountProfileService profileService)
{
_profileService = profileService;
InitializeComponent();
}
public WinoAccount? Result { get; private set; }
private async void LoginClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
args.Cancel = true;
var validationError = ValidateInput();
if (!string.IsNullOrWhiteSpace(validationError))
{
ShowError(validationError);
return;
}
var deferral = args.GetDeferral();
try
{
SetBusyState(true);
HideError();
var result = await _profileService.LoginAsync(EmailTextBox.Text.Trim(), PasswordBox.Password);
if (!result.IsSuccess || result.Account == null)
{
ShowError(WinoAccountAuthErrorTranslator.Translate(result.ErrorCode));
return;
}
Result = result.Account;
args.Cancel = false;
Hide();
}
finally
{
SetBusyState(false);
deferral.Complete();
}
}
private string ValidateInput()
{
if (string.IsNullOrWhiteSpace(EmailTextBox.Text))
{
return Translator.WinoAccount_Validation_EmailRequired;
}
if (string.IsNullOrWhiteSpace(PasswordBox.Password))
{
return Translator.WinoAccount_Validation_PasswordRequired;
}
return string.Empty;
}
private void InputChanged(TextBox sender, TextBoxTextChangingEventArgs args) => HideError();
private void InputChanged(object sender, RoutedEventArgs e) => HideError();
private void SetBusyState(bool isBusy)
{
IsPrimaryButtonEnabled = !isBusy;
IsSecondaryButtonEnabled = !isBusy;
BusyRing.IsActive = isBusy;
BusyRing.Visibility = isBusy ? Visibility.Visible : Visibility.Collapsed;
}
private void ShowError(string message)
{
ErrorTextBlock.Text = message;
ErrorTextBlock.Visibility = Visibility.Visible;
}
private void HideError()
{
ErrorTextBlock.Text = string.Empty;
ErrorTextBlock.Visibility = Visibility.Collapsed;
}
}
@@ -0,0 +1,56 @@
<ContentDialog
x:Class="Wino.Dialogs.WinoAccountRegistrationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{x:Bind domain:Translator.WinoAccount_RegisterDialog_Title}"
PrimaryButtonClick="RegisterClicked"
PrimaryButtonText="{x:Bind domain:Translator.WinoAccount_RegisterDialog_PrimaryButton}"
SecondaryButtonText="{x:Bind domain:Translator.Buttons_Cancel}"
Style="{StaticResource WinoDialogStyle}"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMinWidth">440</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">440</x:Double>
</ContentDialog.Resources>
<StackPanel Spacing="12">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind domain:Translator.WinoAccount_RegisterDialog_Description}"
TextWrapping="WrapWholeWords" />
<TextBox
x:Name="EmailTextBox"
Header="{x:Bind domain:Translator.WinoAccount_EmailLabel}"
PlaceholderText="{x:Bind domain:Translator.WinoAccount_EmailPlaceholder}"
TextChanging="InputChanged" />
<PasswordBox
x:Name="PasswordBox"
Header="{x:Bind domain:Translator.WinoAccount_PasswordLabel}"
PasswordChanged="InputChanged" />
<PasswordBox
x:Name="ConfirmPasswordBox"
Header="{x:Bind domain:Translator.WinoAccount_ConfirmPasswordLabel}"
PasswordChanged="InputChanged" />
<ProgressRing
x:Name="BusyRing"
Width="20"
Height="20"
HorizontalAlignment="Left"
IsActive="False"
Visibility="Collapsed" />
<TextBlock
x:Name="ErrorTextBlock"
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
TextWrapping="WrapWholeWords"
Visibility="Collapsed" />
</StackPanel>
</ContentDialog>
@@ -0,0 +1,103 @@
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces;
using Wino.Mail.WinUI.Services;
namespace Wino.Dialogs;
public sealed partial class WinoAccountRegistrationDialog : ContentDialog
{
private readonly IWinoAccountProfileService _profileService;
public WinoAccountRegistrationDialog(IWinoAccountProfileService profileService)
{
_profileService = profileService;
InitializeComponent();
}
public WinoAccount? Result { get; private set; }
private async void RegisterClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
args.Cancel = true;
var validationError = ValidateInput();
if (!string.IsNullOrWhiteSpace(validationError))
{
ShowError(validationError);
return;
}
var deferral = args.GetDeferral();
try
{
SetBusyState(true);
HideError();
var result = await _profileService.RegisterAsync(EmailTextBox.Text.Trim(), PasswordBox.Password);
if (!result.IsSuccess || result.Account == null)
{
ShowError(WinoAccountAuthErrorTranslator.Translate(result.ErrorCode));
return;
}
Result = result.Account;
args.Cancel = false;
Hide();
}
finally
{
SetBusyState(false);
deferral.Complete();
}
}
private string ValidateInput()
{
if (string.IsNullOrWhiteSpace(EmailTextBox.Text))
{
return Translator.WinoAccount_Validation_EmailRequired;
}
if (string.IsNullOrWhiteSpace(PasswordBox.Password))
{
return Translator.WinoAccount_Validation_PasswordRequired;
}
if (!string.Equals(PasswordBox.Password, ConfirmPasswordBox.Password, StringComparison.Ordinal))
{
return Translator.WinoAccount_Validation_PasswordMismatch;
}
return string.Empty;
}
private void InputChanged(TextBox sender, TextBoxTextChangingEventArgs args) => HideError();
private void InputChanged(object sender, RoutedEventArgs e) => HideError();
private void SetBusyState(bool isBusy)
{
IsPrimaryButtonEnabled = !isBusy;
IsSecondaryButtonEnabled = !isBusy;
BusyRing.IsActive = isBusy;
BusyRing.Visibility = isBusy ? Visibility.Visible : Visibility.Collapsed;
}
private void ShowError(string message)
{
ErrorTextBlock.Text = message;
ErrorTextBlock.Visibility = Visibility.Visible;
}
private void HideError()
{
ErrorTextBlock.Text = string.Empty;
ErrorTextBlock.Visibility = Visibility.Collapsed;
}
}
+33 -1
View File
@@ -27,11 +27,15 @@ namespace Wino.Services;
public class DialogService : DialogServiceBase, IMailDialogService
{
private readonly IWinoAccountProfileService _winoAccountProfileService;
public DialogService(INewThemeService themeService,
IConfigurationService configurationService,
IApplicationResourceManager<ResourceDictionary> applicationResourceManager,
IUpdateManager updateManager) : base(themeService, configurationService, applicationResourceManager, updateManager)
IUpdateManager updateManager,
IWinoAccountProfileService winoAccountProfileService) : base(themeService, configurationService, applicationResourceManager, updateManager)
{
_winoAccountProfileService = winoAccountProfileService;
}
public async Task<ICreateAccountAliasDialog> ShowCreateAccountAliasDialogAsync()
@@ -213,4 +217,32 @@ public class DialogService : DialogServiceBase, IMailDialogService
return null;
}
public async Task<WinoAccount?> ShowWinoAccountRegistrationDialogAsync()
{
var dialog = new WinoAccountRegistrationDialog(_winoAccountProfileService)
{
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
};
var result = await HandleDialogPresentationAsync(dialog);
return result == ContentDialogResult.Primary
? dialog.Result
: null;
}
public async Task<WinoAccount?> ShowWinoAccountLoginDialogAsync()
{
var dialog = new WinoAccountLoginDialog(_winoAccountProfileService)
{
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
};
var result = await HandleDialogPresentationAsync(dialog);
return result == ContentDialogResult.Primary
? dialog.Result
: null;
}
}
@@ -0,0 +1,32 @@
using Wino.Core.Domain;
using Wino.Mail.Api.Contracts.Common;
namespace Wino.Mail.WinUI.Services;
public static class WinoAccountAuthErrorTranslator
{
public static string Translate(string? errorCode)
{
if (string.IsNullOrWhiteSpace(errorCode))
{
return Translator.GeneralTitle_Error;
}
return errorCode switch
{
ApiErrorCodes.InvalidCredentials => Translator.WinoAccount_Error_InvalidCredentials,
ApiErrorCodes.AccountLocked => Translator.WinoAccount_Error_AccountLocked,
ApiErrorCodes.AccountBanned => Translator.WinoAccount_Error_AccountBanned,
ApiErrorCodes.AccountSuspended => Translator.WinoAccount_Error_AccountSuspended,
ApiErrorCodes.RefreshTokenInvalid => Translator.WinoAccount_Error_RefreshTokenInvalid,
ApiErrorCodes.EmailAlreadyRegistered => Translator.WinoAccount_Error_EmailAlreadyRegistered,
ApiErrorCodes.ExternalLoginEmailRequired => Translator.WinoAccount_Error_ExternalLoginEmailRequired,
ApiErrorCodes.ExternalLoginInvalid => Translator.WinoAccount_Error_ExternalLoginInvalid,
ApiErrorCodes.ExternalAuthStateInvalid => Translator.WinoAccount_Error_ExternalAuthStateInvalid,
ApiErrorCodes.ExternalAuthCodeInvalid => Translator.WinoAccount_Error_ExternalAuthCodeInvalid,
ApiErrorCodes.Forbidden => Translator.WinoAccount_Error_Forbidden,
ApiErrorCodes.ValidationFailed => Translator.WinoAccount_Error_ValidationFailed,
_ => errorCode
};
}
}
+62
View File
@@ -86,6 +86,68 @@
</Flyout>
</Button.Flyout>
</Button>
<Button
x:Name="WinoAccountButton"
Background="Transparent"
BorderBrush="Transparent">
<Button.Flyout>
<Flyout Placement="Bottom">
<Grid MinWidth="320" MaxWidth="360">
<StackPanel
x:Name="WinoAccountSignedOutView"
Spacing="12">
<TextBlock
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="{x:Bind domain:Translator.WinoAccount_Titlebar_SignedOutTitle}" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind domain:Translator.WinoAccount_Titlebar_SignedOutDescription}"
TextWrapping="WrapWholeWords" />
<StackPanel Orientation="Horizontal" Spacing="8">
<Button
Click="RegisterWinoAccountClicked"
Content="{x:Bind domain:Translator.WinoAccount_RegisterButton_Action}" />
<Button
Click="LoginWinoAccountClicked"
Content="{x:Bind domain:Translator.WinoAccount_LoginButton_Action}" />
</StackPanel>
</StackPanel>
<StackPanel
x:Name="WinoAccountSignedInView"
Spacing="12"
Visibility="Collapsed">
<StackPanel Orientation="Horizontal" Spacing="12">
<PersonPicture
x:Name="WinoAccountFlyoutPicture"
Width="40"
Height="40"
Initials="W" />
<StackPanel Spacing="2">
<TextBlock
x:Name="WinoAccountFlyoutEmailText"
Style="{StaticResource BodyStrongTextBlockStyle}"
TextWrapping="WrapWholeWords" />
<TextBlock
x:Name="WinoAccountFlyoutStatusText"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="WrapWholeWords" />
</StackPanel>
</StackPanel>
<Button
HorizontalAlignment="Left"
Click="SignOutWinoAccountClicked"
Content="{x:Bind domain:Translator.WinoAccount_SignOutButton_Action}" />
</StackPanel>
</Grid>
</Flyout>
</Button.Flyout>
<PersonPicture
x:Name="WinoAccountButtonPicture"
Width="30"
Height="30"
Initials="W" />
</Button>
</StackPanel>
</TitleBar.RightHeader>
</TitleBar>
+132 -21
View File
@@ -10,11 +10,13 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.UI;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Synchronization;
using Wino.Extensions;
using Wino.Mail.WinUI.Activation;
using Wino.Mail.WinUI.Extensions;
using Wino.Mail.WinUI.Interfaces;
using Wino.Mail.WinUI.Views;
using Wino.Messaging.Client.Shell;
@@ -28,11 +30,15 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
IRecipient<InfoBarMessageRequested>,
IRecipient<TitleBarShellContentUpdated>,
IRecipient<SynchronizationActionsAdded>,
IRecipient<SynchronizationActionsCompleted>
IRecipient<SynchronizationActionsCompleted>,
IRecipient<WinoAccountSignedInMessage>,
IRecipient<WinoAccountSignedOutMessage>
{
public IStatePersistanceService StatePersistanceService { get; } = WinoApplication.Current.Services.GetService<IStatePersistanceService>() ?? throw new Exception("StatePersistanceService not registered in DI container.");
public IPreferencesService PreferencesService { get; } = WinoApplication.Current.Services.GetService<IPreferencesService>() ?? throw new Exception("PreferencesService not registered in DI container.");
public INavigationService NavigationService { get; } = WinoApplication.Current.Services.GetService<INavigationService>() ?? throw new Exception("NavigationService not registered in DI container.");
private IMailDialogService MailDialogService { get; } = WinoApplication.Current.Services.GetRequiredService<IMailDialogService>();
private IWinoAccountProfileService WinoAccountProfileService { get; } = WinoApplication.Current.Services.GetRequiredService<IWinoAccountProfileService>();
public ICommand ShowWinoCommand { get; set; }
public ICommand ShowWinoCalendarCommand { get; set; }
@@ -180,26 +186,7 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
public void Receive(InfoBarMessageRequested message)
{
DispatcherQueue.TryEnqueue(() =>
{
if (string.IsNullOrEmpty(message.ActionButtonTitle) || message.Action == null)
{
ShellInfoBar.ActionButton = null;
}
else
{
ShellInfoBar.ActionButton = new Button()
{
Content = message.ActionButtonTitle,
Command = new RelayCommand(message.Action)
};
}
ShellInfoBar.Message = message.Message;
ShellInfoBar.Title = message.Title;
ShellInfoBar.Severity = message.Severity.AsMUXCInfoBarSeverity();
ShellInfoBar.IsOpen = true;
});
ShowInfoBarMessage(message);
}
public void Receive(SynchronizationActionsAdded message)
@@ -226,6 +213,16 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
});
}
public void Receive(WinoAccountSignedInMessage message)
{
DispatcherQueue.TryEnqueue(() => UpdateWinoAccountState(message.Account));
}
public void Receive(WinoAccountSignedOutMessage message)
{
DispatcherQueue.TryEnqueue(() => UpdateWinoAccountState(null));
}
private void UpdateSyncStatusVisibility()
{
SyncStatusButton.Visibility = SyncActionItems.Any()
@@ -329,6 +326,8 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
WeakReferenceMessenger.Default.Register<InfoBarMessageRequested>(this);
WeakReferenceMessenger.Default.Register<SynchronizationActionsAdded>(this);
WeakReferenceMessenger.Default.Register<SynchronizationActionsCompleted>(this);
WeakReferenceMessenger.Default.Register<WinoAccountSignedInMessage>(this);
WeakReferenceMessenger.Default.Register<WinoAccountSignedOutMessage>(this);
}
private void UnregisterRecipients()
@@ -338,6 +337,118 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow,
WeakReferenceMessenger.Default.Unregister<InfoBarMessageRequested>(this);
WeakReferenceMessenger.Default.Unregister<SynchronizationActionsAdded>(this);
WeakReferenceMessenger.Default.Unregister<SynchronizationActionsCompleted>(this);
WeakReferenceMessenger.Default.Unregister<WinoAccountSignedInMessage>(this);
WeakReferenceMessenger.Default.Unregister<WinoAccountSignedOutMessage>(this);
}
private void ShowInfoBarMessage(InfoBarMessageRequested message)
{
DispatcherQueue.TryEnqueue(() =>
{
if (string.IsNullOrEmpty(message.ActionButtonTitle) || message.Action == null)
{
ShellInfoBar.ActionButton = null;
}
else
{
ShellInfoBar.ActionButton = new Button()
{
Content = message.ActionButtonTitle,
Command = new RelayCommand(message.Action)
};
}
ShellInfoBar.Message = message.Message;
ShellInfoBar.Title = message.Title;
ShellInfoBar.Severity = message.Severity.AsMUXCInfoBarSeverity();
ShellInfoBar.IsOpen = true;
});
}
private void UpdateWinoAccountState(WinoAccount? account)
{
var isSignedIn = account != null;
WinoAccountSignedOutView.Visibility = isSignedIn ? Visibility.Collapsed : Visibility.Visible;
WinoAccountSignedInView.Visibility = isSignedIn ? Visibility.Visible : Visibility.Collapsed;
var initials = GetInitials(account?.Email);
WinoAccountButtonPicture.Initials = initials;
WinoAccountFlyoutPicture.Initials = initials;
WinoAccountButtonPicture.DisplayName = account?.Email ?? Translator.WinoAccount_Titlebar_SignedOutTitle;
WinoAccountFlyoutPicture.DisplayName = account?.Email ?? Translator.WinoAccount_Titlebar_SignedOutTitle;
WinoAccountFlyoutEmailText.Text = account?.Email ?? string.Empty;
WinoAccountFlyoutStatusText.Text = account == null
? string.Empty
: string.Format(Translator.WinoAccount_Titlebar_SignedInStatus, account.AccountStatus);
}
private static string GetInitials(string? email)
{
if (string.IsNullOrWhiteSpace(email))
{
return "W";
}
var localPart = email.Split('@')[0];
var segments = localPart
.Split(['.', '_', '-', ' '], StringSplitOptions.RemoveEmptyEntries)
.Where(segment => !string.IsNullOrWhiteSpace(segment))
.Take(2)
.ToArray();
if (segments.Length == 0)
{
return email[..1].ToUpperInvariant();
}
return string.Concat(segments.Select(segment => char.ToUpperInvariant(segment[0])));
}
private async void RegisterWinoAccountClicked(object sender, RoutedEventArgs e)
{
var account = await MailDialogService.ShowWinoAccountRegistrationDialogAsync();
if (account != null)
{
ShowInfoBarMessage(new InfoBarMessageRequested(
InfoBarMessageType.Success,
Translator.GeneralTitle_Info,
string.Format(Translator.WinoAccount_RegisterSuccessMessage, account.Email)));
}
}
private async void LoginWinoAccountClicked(object sender, RoutedEventArgs e)
{
var account = await MailDialogService.ShowWinoAccountLoginDialogAsync();
if (account != null)
{
ShowInfoBarMessage(new InfoBarMessageRequested(
InfoBarMessageType.Success,
Translator.GeneralTitle_Info,
string.Format(Translator.WinoAccount_LoginSuccessMessage, account.Email)));
}
}
private async void SignOutWinoAccountClicked(object sender, RoutedEventArgs e)
{
var activeAccount = await WinoAccountProfileService.GetActiveAccountAsync();
if (activeAccount == null)
{
ShowInfoBarMessage(new InfoBarMessageRequested(
InfoBarMessageType.Warning,
Translator.GeneralTitle_Info,
Translator.WinoAccount_SignOut_NoAccountMessage));
return;
}
await WinoAccountProfileService.SignOutAsync();
ShowInfoBarMessage(new InfoBarMessageRequested(
InfoBarMessageType.Success,
Translator.GeneralTitle_Info,
string.Format(Translator.WinoAccount_SignOut_SuccessMessage, activeAccount.Email)));
}
}
+1
View File
@@ -226,6 +226,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wino.Calendar.ViewModels\Wino.Calendar.ViewModels.csproj" />
<ProjectReference Include="..\Wino.Mail.Contracts\Wino.Mail.Contracts.csproj" />
<ProjectReference Include="..\Wino.Core.Domain\Wino.Core.Domain.csproj" />
<ProjectReference Include="..\Wino.Core.ViewModels\Wino.Core.ViewModels.csproj" />
<ProjectReference Include="..\Wino.Mail.ViewModels\Wino.Mail.ViewModels.csproj" />