Server termination and refactoring message dialogs.

This commit is contained in:
Burak Kaan Köse
2024-08-21 22:42:52 +02:00
parent bab3272970
commit f627226da9
31 changed files with 548 additions and 388 deletions

View File

@@ -0,0 +1,10 @@
namespace Wino.Core.Domain.Enums
{
public enum WinoCustomMessageDialogIcon
{
Information,
Warning,
Error,
Question
}
}

View File

@@ -1,9 +0,0 @@
using System.Threading.Tasks;
namespace Wino.Core.Domain.Interfaces
{
public interface IConfirmationDialog
{
Task<bool> ShowDialogAsync(string title, string message, string approveButtonTitle);
}
}

View File

@@ -15,11 +15,10 @@ namespace Wino.Core.Domain.Interfaces
Task<byte[]> PickWindowsFileContentAsync(params object[] typeFilters);
Task<bool> ShowConfirmationDialogAsync(string question, string title, string confirmationButtonTitle);
Task<bool> ShowHardDeleteConfirmationAsync();
Task<IStoreRatingDialog> ShowRatingDialogAsync();
Task HandleSystemFolderConfigurationDialogAsync(Guid accountId, IFolderService folderService);
Task<bool> ShowCustomThemeBuilderDialogAsync();
Task ShowMessageAsync(string message, string title);
Task ShowMessageAsync(string message, string title, WinoCustomMessageDialogIcon icon);
void InfoBarMessage(string title, string message, InfoBarMessageType messageType);
void InfoBarMessage(string title, string message, InfoBarMessageType messageType, string actionButtonText, Action action);
@@ -53,6 +52,17 @@ namespace Wino.Core.Domain.Interfaces
/// </summary>
/// <returns>Signature information. Null if canceled.</returns>
Task<AccountSignature> ShowSignatureEditorDialog(AccountSignature signatureModel = null);
/// <summary>
/// Presents a dialog to the user for account alias creation/modification.
/// </summary>
/// <returns>Created alias model if not canceled.</returns>
Task<ICreateAccountAliasDialog> ShowCreateAccountAliasDialogAsync();
Task<bool> ShowWinoCustomMessageDialogAsync(string title,
string description,
string approveButtonText,
WinoCustomMessageDialogIcon? icon,
string cancelButtonText = "",
string dontAskAgainConfigurationKey = "");
}
}

View File

@@ -1,8 +0,0 @@
namespace Wino.Core.Domain.Interfaces
{
public interface IStoreRatingDialog
{
bool DontAskAgain { get; }
bool RateWinoClicked { get; }
}
}

View File

@@ -11,7 +11,7 @@ namespace Wino.Core.UWP.Services
{
public class StoreRatingService : IStoreRatingService
{
private const string RatedStorageKey = nameof(RatedStorageKey);
private const string RatedStorageKey = "a"; // nameof(RatedStorageKey);
private const string LatestAskedKey = nameof(LatestAskedKey);
private readonly IConfigurationService _configurationService;
@@ -23,9 +23,6 @@ namespace Wino.Core.UWP.Services
_dialogService = dialogService;
}
private void SetRated()
=> _configurationService.SetRoaming(RatedStorageKey, true);
private bool IsAskingThresholdExceeded()
{
var latestAskedDate = _configurationService.Get(LatestAskedKey, DateTime.MinValue);
@@ -62,15 +59,14 @@ namespace Wino.Core.UWP.Services
{
if (!IsAskingThresholdExceeded()) return;
var ratingDialogResult = await _dialogService.ShowRatingDialogAsync();
var isRateWinoApproved = await _dialogService.ShowWinoCustomMessageDialogAsync(Translator.StoreRatingDialog_Title,
Translator.StoreRatingDialog_MessageFirstLine,
Translator.Buttons_RateWino,
Domain.Enums.WinoCustomMessageDialogIcon.Question,
Translator.Buttons_No,
RatedStorageKey);
if (ratingDialogResult == null)
return;
if (ratingDialogResult.DontAskAgain)
SetRated();
if (ratingDialogResult.RateWinoClicked)
if (isRateWinoApproved)
{
// In case of failure of this call, we will navigate users to Store page directly.
@@ -107,7 +103,7 @@ namespace Wino.Core.UWP.Services
else
_dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewNewMessage, Domain.Enums.InfoBarMessageType.Success);
SetRated();
_configurationService.Set(RatedStorageKey, true);
break;
case StoreRateAndReviewStatus.CanceledByUser:
break;

View File

@@ -95,7 +95,9 @@ namespace Wino.Mail.ViewModels
{
// Discord disclaimer message about server.
if (stringUrl == DiscordChannelUrl)
await DialogService.ShowMessageAsync(Translator.DiscordChannelDisclaimerMessage, Translator.DiscordChannelDisclaimerTitle);
await DialogService.ShowMessageAsync(Translator.DiscordChannelDisclaimerMessage,
Translator.DiscordChannelDisclaimerTitle,
WinoCustomMessageDialogIcon.Warning);
await _nativeAppService.LaunchUriAsync(new Uri(stringUrl));
}

View File

@@ -98,14 +98,18 @@ namespace Wino.Mail.ViewModels
// Check existence.
if (AccountAliases.Any(a => a.AliasAddress == newAlias.AliasAddress))
{
await DialogService.ShowMessageAsync(Translator.DialogMessage_AliasExistsTitle, Translator.DialogMessage_AliasExistsMessage);
await DialogService.ShowMessageAsync(Translator.DialogMessage_AliasExistsTitle,
Translator.DialogMessage_AliasExistsMessage,
WinoCustomMessageDialogIcon.Warning);
return;
}
// Validate all addresses.
if (!EmailValidator.Validate(newAlias.AliasAddress) || (!string.IsNullOrEmpty(newAlias.ReplyToAddress) && !EmailValidator.Validate(newAlias.ReplyToAddress)))
{
await DialogService.ShowMessageAsync(Translator.DialogMessage_InvalidAliasMessage, Translator.DialogMessage_InvalidAliasTitle);
await DialogService.ShowMessageAsync(Translator.DialogMessage_InvalidAliasMessage,
Translator.DialogMessage_InvalidAliasTitle,
WinoCustomMessageDialogIcon.Warning);
return;
}
@@ -125,14 +129,18 @@ namespace Wino.Mail.ViewModels
// Primary aliases can't be deleted.
if (alias.IsPrimary)
{
await DialogService.ShowMessageAsync(Translator.Info_CantDeletePrimaryAliasMessage, Translator.GeneralTitle_Warning);
await DialogService.ShowMessageAsync(Translator.Info_CantDeletePrimaryAliasMessage,
Translator.GeneralTitle_Warning,
WinoCustomMessageDialogIcon.Warning);
return;
}
// Root aliases can't be deleted.
if (alias.IsRootAlias)
{
await DialogService.ShowMessageAsync(Translator.DialogMessage_CantDeleteRootAliasTitle, Translator.DialogMessage_CantDeleteRootAliasMessage);
await DialogService.ShowMessageAsync(Translator.DialogMessage_CantDeleteRootAliasTitle,
Translator.DialogMessage_CantDeleteRootAliasMessage,
WinoCustomMessageDialogIcon.Warning);
return;
}

View File

@@ -37,7 +37,8 @@ namespace Wino.Mail.ViewModels
IRecipient<MergedInboxRenamed>,
IRecipient<LanguageChanged>,
IRecipient<AccountMenuItemsReordered>,
IRecipient<AccountSynchronizationProgressUpdatedMessage>
IRecipient<AccountSynchronizationProgressUpdatedMessage>,
IRecipient<NavigateAppPreferencesRequested>
{
#region Menu Items
@@ -65,6 +66,7 @@ namespace Wino.Mail.ViewModels
public IWinoNavigationService NavigationService { get; }
private readonly IFolderService _folderService;
private readonly IConfigurationService _configurationService;
private readonly IAccountService _accountService;
private readonly IContextMenuItemService _contextMenuItemService;
private readonly IStoreRatingService _storeRatingService;
@@ -98,7 +100,8 @@ namespace Wino.Mail.ViewModels
IWinoRequestDelegator winoRequestDelegator,
IFolderService folderService,
IStatePersistanceService statePersistanceService,
IWinoServerConnectionManager serverConnectionManager) : base(dialogService)
IWinoServerConnectionManager serverConnectionManager,
IConfigurationService configurationService) : base(dialogService)
{
StatePersistenceService = statePersistanceService;
ServerConnectionManager = serverConnectionManager;
@@ -115,6 +118,7 @@ namespace Wino.Mail.ViewModels
PreferencesService = preferencesService;
NavigationService = navigationService;
_configurationService = configurationService;
_backgroundTaskService = backgroundTaskService;
_mimeFileService = mimeFileService;
_nativeAppService = nativeAppService;
@@ -229,7 +233,6 @@ namespace Wino.Mail.ViewModels
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
{
base.OnNavigatedTo(mode, parameters);
await CreateFooterItemsAsync();
await RecreateMenuItemsAsync();
@@ -527,7 +530,7 @@ namespace Wino.Mail.ViewModels
StatePersistenceService.CoreWindowTitle = "Wino Mail";
}
public async Task MenuItemInvokedOrSelectedAsync(IMenuItem clickedMenuItem)
public async Task MenuItemInvokedOrSelectedAsync(IMenuItem clickedMenuItem, object parameter = null)
{
if (clickedMenuItem == null) return;
@@ -561,11 +564,11 @@ namespace Wino.Mail.ViewModels
}
else if (clickedMenuItem is SettingsItem)
{
NavigationService.Navigate(WinoPage.SettingsPage);
NavigationService.Navigate(WinoPage.SettingsPage, parameter, NavigationReferenceFrame.ShellFrame, NavigationTransitionType.None);
}
else if (clickedMenuItem is ManageAccountsMenuItem)
{
NavigationService.Navigate(WinoPage.AccountManagementPage);
NavigationService.Navigate(WinoPage.AccountManagementPage, parameter, NavigationReferenceFrame.ShellFrame, NavigationTransitionType.None);
}
else if (clickedMenuItem is IAccountMenuItem clickedAccountMenuItem && latestSelectedAccountMenuItem != clickedAccountMenuItem)
{
@@ -721,7 +724,19 @@ namespace Wino.Mail.ViewModels
if (!accounts.Any())
{
await DialogService.ShowMessageAsync(Translator.DialogMessage_NoAccountsForCreateMailMessage, Translator.DialogMessage_NoAccountsForCreateMailTitle);
var isManageAccountClicked = await DialogService.ShowWinoCustomMessageDialogAsync(Translator.DialogMessage_NoAccountsForCreateMailMessage,
Translator.DialogMessage_NoAccountsForCreateMailTitle,
Translator.MenuManageAccounts,
WinoCustomMessageDialogIcon.Information,
string.Empty);
if (isManageAccountClicked)
{
SelectedMenuItem = ManageAccountsMenuItem;
}
return;
}
@@ -794,11 +809,16 @@ namespace Wino.Mail.ViewModels
protected override async void OnAccountUpdated(MailAccount updatedAccount)
{
await ExecuteUIThread(() =>
await ExecuteUIThread(async () =>
{
if (MenuItems.TryGetAccountMenuItem(updatedAccount.Id, out IAccountMenuItem foundAccountMenuItem))
{
foundAccountMenuItem.UpdateAccount(updatedAccount);
if (latestSelectedAccountMenuItem == foundAccountMenuItem)
{
await ChangeLoadedAccountAsync(foundAccountMenuItem, false);
}
}
});
}
@@ -850,7 +870,9 @@ namespace Wino.Mail.ViewModels
if (!accounts.Any())
{
await DialogService.ShowMessageAsync(Translator.DialogMessage_NoAccountsForCreateMailMessage, Translator.DialogMessage_NoAccountsForCreateMailTitle);
await DialogService.ShowMessageAsync(Translator.DialogMessage_NoAccountsForCreateMailMessage,
Translator.DialogMessage_NoAccountsForCreateMailTitle,
WinoCustomMessageDialogIcon.Warning);
}
else if (accounts.Count == 1)
{
@@ -961,5 +983,10 @@ namespace Wino.Mail.ViewModels
await ExecuteUIThread(() => { accountMenuItem.SynchronizationProgress = message.Progress; });
}
public async void Receive(NavigateAppPreferencesRequested message)
{
await MenuItemInvokedOrSelectedAsync(SettingsItem, WinoPage.AppPreferencesPage);
}
}
}

View File

@@ -161,7 +161,9 @@ namespace Wino.Mail.ViewModels
if (!ToItems.Any())
{
await DialogService.ShowMessageAsync(Translator.DialogMessage_ComposerMissingRecipientMessage, Translator.DialogMessage_ComposerValidationFailedTitle);
await DialogService.ShowMessageAsync(Translator.DialogMessage_ComposerMissingRecipientMessage,
Translator.DialogMessage_ComposerValidationFailedTitle,
WinoCustomMessageDialogIcon.Warning);
return;
}

View File

@@ -6,13 +6,6 @@ namespace Wino.Mail.ViewModels.Messages
/// When the rendering page is active, but new item is requested to be rendered.
/// To not trigger navigation again and re-use existing Chromium.
/// </summary>
public class NewMailItemRenderingRequestedEvent
{
public NewMailItemRenderingRequestedEvent(MailItemViewModel mailItemViewModel)
{
MailItemViewModel = mailItemViewModel;
}
public MailItemViewModel MailItemViewModel { get; }
}
/// <param name="MailItemViewModel"></param>
public record NewMailItemRenderingRequestedEvent(MailItemViewModel MailItemViewModel);
}

View File

@@ -4,7 +4,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Controls"
xmlns:selectors="using:Wino.Selectors"
xmlns:wino="using:Wino">
xmlns:wino="using:Wino"
xmlns:dialogs="using:Wino.Core.Domain.Models.Dialogs"
xmlns:styles="using:Wino.Styles">
<Application.Resources>
<ResourceDictionary>
@@ -18,6 +20,7 @@
<ResourceDictionary Source="/Styles/CommandBarItems.xaml" />
<ResourceDictionary Source="/Styles/ItemContainerStyles.xaml" />
<ResourceDictionary Source="/Styles/WinoInfoBar.xaml" />
<styles:CustomMessageDialogStyles />
<ResourceDictionary>
@@ -166,6 +169,10 @@
</Setter>
</Style>
<!-- Attachment Icon Templates -->
<DataTemplate x:Key="NoneTemplate">
<Image Source="/Assets/FileTypes/type_none.png" />
</DataTemplate>

View File

@@ -21,6 +21,7 @@ using Windows.Foundation.Metadata;
using Windows.Storage;
using Windows.System.Profile;
using Windows.UI;
using Windows.UI.Core.Preview;
using Windows.UI.Notifications;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
@@ -38,6 +39,7 @@ using Wino.Core.UWP;
using Wino.Core.UWP.Services;
using Wino.Mail.ViewModels;
using Wino.Messaging.Client.Connection;
using Wino.Messaging.Client.Navigation;
using Wino.Messaging.Server;
using Wino.Services;
@@ -107,6 +109,78 @@ namespace Wino
WeakReferenceMessenger.Default.Register(this);
}
private async void ApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
var deferral = e.GetDeferral();
// Wino should notify user on app close if:
// 1. User has at least 1 registered account.
// 2. Startup behavior is not Enabled.
// 3. Server terminate behavior is set to Terminate.
var accountService = Services.GetService<IAccountService>();
var accounts = await accountService.GetAccountsAsync();
if (accounts.Count > 0)
{
// User has some accounts. Check if Wino Server runs on system startup.
var dialogService = Services.GetService<IDialogService>();
var startupBehaviorService = Services.GetService<IStartupBehaviorService>();
var preferencesService = Services.GetService<IPreferencesService>();
var currentStartupBehavior = await startupBehaviorService.GetCurrentStartupBehaviorAsync();
bool? isGoToAppPreferencesRequested = null;
if (preferencesService.ServerTerminationBehavior == ServerBackgroundMode.Terminate)
{
// Starting the server is fine, but check if server termination behavior is set to terminate.
// This state will kill the server once the app is terminated.
isGoToAppPreferencesRequested = await _dialogService.ShowWinoCustomMessageDialogAsync("Background Synchronization",
"You are terminating Wino Mail and your app close behavior is set to 'Terminate'.\nThis will stop all background synchronizations and notifications.\nDo you want to go to App Preferences to set Wino Mail to run minimized or in the background?",
Translator.Buttons_Yes,
WinoCustomMessageDialogIcon.Warning,
Translator.Buttons_No,
"DontAskTerminateServerBehavior");
}
if (isGoToAppPreferencesRequested == null && currentStartupBehavior != StartupBehaviorResult.Enabled)
{
// Startup behavior is not enabled.
isGoToAppPreferencesRequested = await dialogService.ShowWinoCustomMessageDialogAsync("Background Synchronization",
"Application has not been set to launch on Windows startup.\nThis may impact notifications and background synchronization.\nDo you want to go to App Preferences page to enable it?",
Translator.Buttons_Yes,
WinoCustomMessageDialogIcon.Warning,
Translator.Buttons_No,
"DontAskDisabledStartup");
}
if (isGoToAppPreferencesRequested == true)
{
WeakReferenceMessenger.Default.Send(new NavigateAppPreferencesRequested());
e.Handled = true;
}
else if (preferencesService.ServerTerminationBehavior == ServerBackgroundMode.Terminate)
{
try
{
var isServerKilled = await _appServiceConnectionManager.GetResponseAsync<bool, TerminateServerRequested>(new TerminateServerRequested());
Log.Information("Server is killed.");
}
catch (Exception ex)
{
Log.Error(ex, "Failed to kill server.");
}
}
}
deferral.Complete();
}
private async void OnResuming(object sender, object e)
{
// App Service connection was lost on suspension.
@@ -233,6 +307,7 @@ namespace Wino
LogActivation("Window is created.");
ConfigureTitleBar();
TryRegisterAppCloseChange();
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
@@ -245,6 +320,18 @@ namespace Wino
}
}
private void TryRegisterAppCloseChange()
{
try
{
var systemNavigationManagerPreview = SystemNavigationManagerPreview.GetForCurrentView();
systemNavigationManagerPreview.CloseRequested -= ApplicationCloseRequested;
systemNavigationManagerPreview.CloseRequested += ApplicationCloseRequested;
}
catch { }
}
protected override async void OnFileActivated(FileActivatedEventArgs args)
{
base.OnFileActivated(args);

View File

@@ -1,29 +0,0 @@
<ContentDialog
x:Class="Wino.Dialogs.ConfirmationDialog"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:domain="using:Wino.Core.Domain"
Title="{x:Bind DialogTitle, Mode=OneWay}"
Style="{StaticResource WinoDialogStyle}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Closed="DialogClosed"
PrimaryButtonText="{x:Bind ApproveButtonTitle, Mode=OneWay}"
DefaultButton="Primary"
SecondaryButtonText="{x:Bind domain:Translator.Buttons_Cancel}"
PrimaryButtonClick="ApproveClicked"
SecondaryButtonClick="CancelClicked"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMinWidth">250</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">500</x:Double>
<x:Double x:Key="ContentDialogMinHeight">200</x:Double>
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
</ContentDialog.Resources>
<TextBlock Text="{x:Bind Message, Mode=OneWay}" TextWrapping="Wrap" />
</ContentDialog>

View File

@@ -1,80 +0,0 @@
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Interfaces;
namespace Wino.Dialogs
{
public sealed partial class ConfirmationDialog : ContentDialog, IConfirmationDialog
{
private TaskCompletionSource<bool> _completionSource;
#region Dependency Properties
public string DialogTitle
{
get { return (string)GetValue(DialogTitleProperty); }
set { SetValue(DialogTitleProperty, value); }
}
public static readonly DependencyProperty DialogTitleProperty = DependencyProperty.Register(nameof(DialogTitle), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty));
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(nameof(Message), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty));
public string ApproveButtonTitle
{
get { return (string)GetValue(ApproveButtonTitleProperty); }
set { SetValue(ApproveButtonTitleProperty, value); }
}
public static readonly DependencyProperty ApproveButtonTitleProperty = DependencyProperty.Register(nameof(ApproveButtonTitle), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty));
#endregion
private bool _isApproved;
public ConfirmationDialog()
{
InitializeComponent();
}
public async Task<bool> ShowDialogAsync(string title, string message, string approveButtonTitle)
{
_completionSource = new TaskCompletionSource<bool>();
DialogTitle = title;
Message = message;
ApproveButtonTitle = approveButtonTitle;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
ShowAsync();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
return await _completionSource.Task;
}
private void DialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
{
_completionSource.TrySetResult(_isApproved);
}
private void ApproveClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
_isApproved = true;
Hide();
}
private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
_isApproved = false;
Hide();
}
}
}

View File

@@ -0,0 +1,24 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Wino.Core.Domain.Enums;
namespace Wino.Dialogs
{
public partial class CustomMessageDialogInformationContainer : ObservableObject
{
[ObservableProperty]
private bool isDontAskChecked;
public CustomMessageDialogInformationContainer(string title, string description, WinoCustomMessageDialogIcon icon, bool isDontAskAgainEnabled)
{
Title = title;
Description = description;
Icon = icon;
IsDontAskAgainEnabled = isDontAskAgainEnabled;
}
public string Title { get; }
public string Description { get; }
public WinoCustomMessageDialogIcon Icon { get; }
public bool IsDontAskAgainEnabled { get; }
}
}

View File

@@ -1,24 +0,0 @@
<ContentDialog
x:Class="Wino.Dialogs.StoreRatingDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Wino.Dialogs"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:domain="using:Wino.Core.Domain"
PrimaryButtonText="{x:Bind domain:Translator.Buttons_RateWino}"
SecondaryButtonText="{x:Bind domain:Translator.Buttons_No}"
DefaultButton="Primary"
PrimaryButtonClick="RateClicked"
Title="{x:Bind domain:Translator.StoreRatingDialog_Title}"
Style="{StaticResource WinoDialogStyle}">
<Grid>
<StackPanel Spacing="6">
<TextBlock Text="{x:Bind domain:Translator.StoreRatingDialog_MessageFirstLine}" />
<TextBlock Text="{x:Bind domain:Translator.StoreRatingDialog_MessageSecondLine}" />
<CheckBox IsChecked="{x:Bind DontAskAgain, Mode=TwoWay}" Content="{x:Bind domain:Translator.Dialog_DontAskAgain}" />
</StackPanel>
</Grid>
</ContentDialog>

View File

@@ -1,21 +0,0 @@
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Interfaces;
namespace Wino.Dialogs
{
public sealed partial class StoreRatingDialog : ContentDialog, IStoreRatingDialog
{
public bool DontAskAgain { get; set; }
public bool RateWinoClicked { get; set; }
public StoreRatingDialog()
{
this.InitializeComponent();
}
private void RateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
RateWinoClicked = true;
}
}
}

View File

@@ -1,25 +0,0 @@
<ContentDialog
x:Class="Wino.Dialogs.WinoMessageDialog"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{x:Bind DialogTitle, Mode=OneWay}"
xmlns:domain="using:Wino.Core.Domain"
Style="{StaticResource WinoDialogStyle}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
CloseButtonText="{x:Bind domain:Translator.Buttons_Close}"
DefaultButton="Close"
Closed="DialogClosed"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMinWidth">250</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">900</x:Double>
<x:Double x:Key="ContentDialogMinHeight">200</x:Double>
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
</ContentDialog.Resources>
<TextBlock Text="{x:Bind Message, Mode=OneWay}" TextWrapping="Wrap" />
</ContentDialog>

View File

@@ -1,60 +0,0 @@
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace Wino.Dialogs
{
public sealed partial class WinoMessageDialog : ContentDialog
{
private TaskCompletionSource<bool> _completionSource;
#region Dependency Properties
public string DialogTitle
{
get { return (string)GetValue(DialogTitleProperty); }
set { SetValue(DialogTitleProperty, value); }
}
public static readonly DependencyProperty DialogTitleProperty = DependencyProperty.Register(nameof(DialogTitle), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty));
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(nameof(Message), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty));
#endregion
public WinoMessageDialog()
{
InitializeComponent();
}
public async Task<bool> ShowDialogAsync(string title, string message)
{
_completionSource = new TaskCompletionSource<bool>();
DialogTitle = title;
Message = message;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
ShowAsync();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
return await _completionSource.Task;
}
private void ApproveClicked(object sender, RoutedEventArgs e)
{
Hide();
}
private void DialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
{
_completionSource.TrySetResult(true);
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain.Enums;
namespace Wino.Selectors
{
public class CustomWinoMessageDialogIconSelector : DataTemplateSelector
{
public DataTemplate InfoIconTemplate { get; set; }
public DataTemplate WarningIconTemplate { get; set; }
public DataTemplate QuestionIconTemplate { get; set; }
public DataTemplate ErrorIconTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item == null) return null;
if (item is WinoCustomMessageDialogIcon icon)
{
switch (icon)
{
case WinoCustomMessageDialogIcon.Information:
return InfoIconTemplate;
case WinoCustomMessageDialogIcon.Warning:
return WarningIconTemplate;
case WinoCustomMessageDialogIcon.Error:
return ErrorIconTemplate;
case WinoCustomMessageDialogIcon.Question:
return QuestionIconTemplate;
default:
throw new Exception("Unknown custom message dialog icon.");
}
}
return base.SelectTemplateCore(item, container);
}
}
}

View File

@@ -8,6 +8,7 @@ using Microsoft.Toolkit.Uwp.Helpers;
using Serilog;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities;
@@ -29,26 +30,21 @@ namespace Wino.Services
private SemaphoreSlim _presentationSemaphore = new SemaphoreSlim(1);
private readonly IThemeService _themeService;
private readonly IConfigurationService _configurationService;
public DialogService(IThemeService themeService)
public DialogService(IThemeService themeService, IConfigurationService configurationService)
{
_themeService = themeService;
_configurationService = configurationService;
}
public void ShowNotSupportedMessage()
{
InfoBarMessage(Translator.Info_UnsupportedFunctionalityTitle, Translator.Info_UnsupportedFunctionalityDescription, InfoBarMessageType.Error);
}
=> InfoBarMessage(Translator.Info_UnsupportedFunctionalityTitle,
Translator.Info_UnsupportedFunctionalityDescription,
InfoBarMessageType.Error);
public async Task ShowMessageAsync(string message, string title)
{
var dialog = new WinoMessageDialog()
{
RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme()
};
await HandleDialogPresentation(() => dialog.ShowDialogAsync(title, message));
}
public Task ShowMessageAsync(string message, string title, WinoCustomMessageDialogIcon icon = WinoCustomMessageDialogIcon.Information)
=> ShowWinoCustomMessageDialogAsync(title, message, Translator.Buttons_Close, icon);
/// <summary>
/// Waits for PopupRoot to be available before presenting the dialog and returns the result after presentation.
@@ -75,40 +71,8 @@ namespace Wino.Services
return ContentDialogResult.None;
}
/// <summary>
/// Waits for PopupRoot to be available before executing the given Task that returns customized result.
/// </summary>
/// <param name="executionTask">Task that presents the dialog and returns result.</param>
/// <returns>Dialog result from the custom dialog.</returns>
private async Task<bool> HandleDialogPresentation(Func<Task<bool>> executionTask)
{
await _presentationSemaphore.WaitAsync();
try
{
return await executionTask();
}
catch (Exception ex)
{
Log.Error(ex, "Handling dialog service failed.");
}
finally
{
_presentationSemaphore.Release();
}
return false;
}
public async Task<bool> ShowConfirmationDialogAsync(string question, string title, string confirmationButtonTitle)
{
var dialog = new ConfirmationDialog()
{
RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme()
};
return await HandleDialogPresentation(() => dialog.ShowDialogAsync(title, question, confirmationButtonTitle));
}
public Task<bool> ShowConfirmationDialogAsync(string question, string title, string confirmationButtonTitle)
=> ShowWinoCustomMessageDialogAsync(title, question, confirmationButtonTitle, WinoCustomMessageDialogIcon.Question, Translator.Buttons_Cancel, string.Empty);
public async Task<AccountCreationDialogResult> ShowNewAccountMailProviderDialogAsync(List<IProviderDetail> availableProviders)
{
@@ -204,18 +168,6 @@ namespace Wino.Services
return editAccountDialog.IsSaved ? editAccountDialog.Account : null;
}
public async Task<IStoreRatingDialog> ShowRatingDialogAsync()
{
var storeDialog = new StoreRatingDialog()
{
RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme()
};
await HandleDialogPresentationAsync(storeDialog);
return storeDialog;
}
public async Task<ICreateAccountAliasDialog> ShowCreateAccountAliasDialogAsync()
{
var createAccountAliasDialog = new CreateAccountAliasDialog()
@@ -247,6 +199,8 @@ namespace Wino.Services
{
var updatedAccount = await folderService.UpdateSystemFolderConfigurationAsync(accountId, configuration);
InfoBarMessage(Translator.SystemFolderConfigSetupSuccess_Title, Translator.SystemFolderConfigSetupSuccess_Message, InfoBarMessageType.Success);
// Update account menu item and force re-synchronization.
WeakReferenceMessenger.Default.Send(new AccountUpdatedMessage(updatedAccount));
@@ -258,11 +212,6 @@ namespace Wino.Services
WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options, SynchronizationSource.Client));
}
if (configuration != null)
{
InfoBarMessage(Translator.SystemFolderConfigSetupSuccess_Title, Translator.SystemFolderConfigSetupSuccess_Message, InfoBarMessageType.Success);
}
}
catch (Exception ex)
{
@@ -331,7 +280,12 @@ namespace Wino.Services
return await file.ReadBytesAsync();
}
public Task<bool> ShowHardDeleteConfirmationAsync() => ShowConfirmationDialogAsync(Translator.DialogMessage_HardDeleteConfirmationMessage, Translator.DialogMessage_HardDeleteConfirmationTitle, Translator.Buttons_Yes);
public Task<bool> ShowHardDeleteConfirmationAsync()
=> ShowWinoCustomMessageDialogAsync(Translator.DialogMessage_HardDeleteConfirmationMessage,
Translator.DialogMessage_HardDeleteConfirmationTitle,
Translator.Buttons_Yes,
WinoCustomMessageDialogIcon.Warning,
Translator.Buttons_No);
public async Task<MailAccount> ShowAccountPickerDialogAsync(List<MailAccount> availableAccounts)
{
@@ -377,5 +331,103 @@ namespace Wino.Services
await HandleDialogPresentationAsync(accountReorderDialog);
}
public async Task<bool> ShowWinoCustomMessageDialogAsync(string title,
string description,
string approveButtonText,
WinoCustomMessageDialogIcon? icon,
string cancelButtonText = "",
string dontAskAgainConfigurationKey = "")
{
// This config key has been marked as don't ask again already.
// Return immidiate result without presenting the dialog.
bool isDontAskEnabled = !string.IsNullOrEmpty(dontAskAgainConfigurationKey);
if (isDontAskEnabled && _configurationService.Get(dontAskAgainConfigurationKey, false)) return false;
var informationContainer = new CustomMessageDialogInformationContainer(title, description, icon.Value, isDontAskEnabled);
var dialog = new ContentDialog
{
Style = (Style)App.Current.Resources["WinoDialogStyle"],
RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme(),
DefaultButton = ContentDialogButton.Primary,
PrimaryButtonText = approveButtonText,
ContentTemplate = (DataTemplate)App.Current.Resources["CustomWinoContentDialogContentTemplate"],
Content = informationContainer
};
if (!string.IsNullOrEmpty(cancelButtonText))
{
dialog.SecondaryButtonText = cancelButtonText;
}
var dialogResult = await HandleDialogPresentationAsync(dialog);
// Mark this key to not ask again if user checked the checkbox.
if (informationContainer.IsDontAskChecked)
{
_configurationService.Set(dontAskAgainConfigurationKey, true);
}
return dialogResult == ContentDialogResult.Primary;
}
private object GetDontAskDialogContentWithIcon(string description, WinoCustomMessageDialogIcon icon, string dontAskKey = "")
{
var iconPresenter = new ContentPresenter()
{
ContentTemplate = (DataTemplate)App.Current.Resources[$"WinoCustomMessageDialog{icon}IconTemplate"],
HorizontalAlignment = HorizontalAlignment.Center
};
var viewBox = new Viewbox
{
Child = iconPresenter,
Margin = new Thickness(0, 6, 0, 0)
};
var descriptionTextBlock = new TextBlock()
{
Text = description,
TextWrapping = TextWrapping.WrapWholeWords,
VerticalAlignment = VerticalAlignment.Center
};
var containerGrid = new Grid()
{
Children =
{
viewBox,
descriptionTextBlock
},
RowSpacing = 6,
ColumnSpacing = 12
};
containerGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(32, GridUnitType.Pixel) });
containerGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
Grid.SetColumn(descriptionTextBlock, 1);
// Add don't ask again checkbox if key is provided.
if (!string.IsNullOrEmpty(dontAskKey))
{
var dontAskCheckBox = new CheckBox() { Content = Translator.Dialog_DontAskAgain };
dontAskCheckBox.Checked += (c, r) => { _configurationService.Set(dontAskKey, dontAskCheckBox.IsChecked.GetValueOrDefault()); };
containerGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) });
containerGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) });
Grid.SetRow(dontAskCheckBox, 1);
Grid.SetColumnSpan(dontAskCheckBox, 2);
containerGrid.Children.Add(dontAskCheckBox);
}
return containerGrid;
}
}
}

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Wino.Styles"
x:Class="Wino.Styles.CustomMessageDialogStyles"
xmlns:dialogs="using:Wino.Dialogs"
xmlns:domain="using:Wino.Core.Domain"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:selectors="using:Wino.Selectors">
<!-- Icon templates -->
<DataTemplate x:Key="WinoCustomMessageDialogInformationIconTemplate">
<Path
Fill="#0f80d7"
Data="F1 M 0 9.375 C 0 8.509115 0.110677 7.677409 0.332031 6.879883 C 0.553385 6.082357 0.867513 5.335287 1.274414 4.638672 C 1.681315 3.942059 2.169596 3.30892 2.739258 2.739258 C 3.308919 2.169598 3.942057 1.681316 4.638672 1.274414 C 5.335286 0.867514 6.082356 0.553387 6.879883 0.332031 C 7.677409 0.110678 8.509114 0 9.375 0 C 10.240885 0 11.072591 0.110678 11.870117 0.332031 C 12.667643 0.553387 13.414713 0.867514 14.111328 1.274414 C 14.807942 1.681316 15.44108 2.169598 16.010742 2.739258 C 16.580402 3.30892 17.068684 3.942059 17.475586 4.638672 C 17.882486 5.335287 18.196613 6.082357 18.417969 6.879883 C 18.639322 7.677409 18.75 8.509115 18.75 9.375 C 18.75 10.240886 18.637695 11.072592 18.413086 11.870117 C 18.188477 12.667644 17.872721 13.413086 17.46582 14.106445 C 17.058918 14.799805 16.570637 15.431315 16.000977 16.000977 C 15.431314 16.570639 14.799804 17.05892 14.106445 17.46582 C 13.413085 17.872721 12.666015 18.188477 11.865234 18.413086 C 11.064453 18.637695 10.234375 18.75 9.375 18.75 C 8.509114 18.75 7.675781 18.639322 6.875 18.417969 C 6.074219 18.196615 5.327148 17.882486 4.633789 17.475586 C 3.94043 17.068686 3.308919 16.580404 2.739258 16.010742 C 2.169596 15.441081 1.681315 14.80957 1.274414 14.116211 C 0.867513 13.422852 0.553385 12.675781 0.332031 11.875 C 0.110677 11.074219 0 10.240886 0 9.375 Z M 17.5 9.375 C 17.5 8.626303 17.403971 7.905273 17.211914 7.211914 C 17.019855 6.518556 16.746418 5.87077 16.391602 5.268555 C 16.036783 4.666342 15.613606 4.119467 15.12207 3.62793 C 14.630533 3.136395 14.083658 2.713217 13.481445 2.358398 C 12.879231 2.003582 12.231445 1.730145 11.538086 1.538086 C 10.844727 1.346029 10.123697 1.25 9.375 1.25 C 8.626302 1.25 7.905273 1.346029 7.211914 1.538086 C 6.518555 1.730145 5.870768 2.003582 5.268555 2.358398 C 4.666341 2.713217 4.119466 3.136395 3.62793 3.62793 C 3.136393 4.119467 2.713216 4.666342 2.358398 5.268555 C 2.003581 5.87077 1.730143 6.518556 1.538086 7.211914 C 1.346029 7.905273 1.25 8.626303 1.25 9.375 C 1.25 10.123698 1.346029 10.844727 1.538086 11.538086 C 1.730143 12.231445 2.001953 12.879232 2.353516 13.481445 C 2.705078 14.083659 3.128255 14.632162 3.623047 15.126953 C 4.117838 15.621745 4.666341 16.044922 5.268555 16.396484 C 5.870768 16.748047 6.518555 17.019857 7.211914 17.211914 C 7.905273 17.403971 8.626302 17.5 9.375 17.5 C 10.123697 17.5 10.844727 17.403971 11.538086 17.211914 C 12.231445 17.019857 12.879231 16.748047 13.481445 16.396484 C 14.083658 16.044922 14.63216 15.621745 15.126953 15.126953 C 15.621744 14.632162 16.044922 14.083659 16.396484 13.481445 C 16.748047 12.879232 17.019855 12.231445 17.211914 11.538086 C 17.403971 10.844727 17.5 10.123698 17.5 9.375 Z M 8.4375 5.625 C 8.4375 5.364584 8.528646 5.14323 8.710938 4.960938 C 8.893229 4.778646 9.114583 4.6875 9.375 4.6875 C 9.635416 4.6875 9.856771 4.778646 10.039062 4.960938 C 10.221354 5.14323 10.3125 5.364584 10.3125 5.625 C 10.3125 5.885417 10.221354 6.106771 10.039062 6.289062 C 9.856771 6.471354 9.635416 6.5625 9.375 6.5625 C 9.114583 6.5625 8.893229 6.471354 8.710938 6.289062 C 8.528646 6.106771 8.4375 5.885417 8.4375 5.625 Z M 8.75 13.125 L 8.75 8.125 C 8.75 7.95573 8.811849 7.809246 8.935547 7.685547 C 9.059244 7.56185 9.205729 7.5 9.375 7.5 C 9.544271 7.5 9.690755 7.56185 9.814453 7.685547 C 9.93815 7.809246 10 7.95573 10 8.125 L 10 13.125 C 10 13.294271 9.93815 13.440756 9.814453 13.564453 C 9.690755 13.688151 9.544271 13.75 9.375 13.75 C 9.205729 13.75 9.059244 13.688151 8.935547 13.564453 C 8.811849 13.440756 8.75 13.294271 8.75 13.125 Z "
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="WinoCustomMessageDialogQuestionIconTemplate">
<Path
Fill="#0984e3"
Data="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m8-6.5a6.5 6.5 0 1 0 0 13a6.5 6.5 0 0 0 0-13M6.92 6.085h.001a.749.749 0 1 1-1.342-.67c.169-.339.436-.701.849-.977C6.845 4.16 7.369 4 8 4a2.76 2.76 0 0 1 1.637.525c.503.377.863.965.863 1.725c0 .448-.115.83-.329 1.15c-.205.307-.47.513-.692.662c-.109.072-.22.138-.313.195l-.006.004a6 6 0 0 0-.26.16a1 1 0 0 0-.276.245a.75.75 0 0 1-1.248-.832c.184-.264.42-.489.692-.661q.154-.1.313-.195l.007-.004c.1-.061.182-.11.258-.161a1 1 0 0 0 .277-.245C8.96 6.514 9 6.427 9 6.25a.61.61 0 0 0-.262-.525A1.27 1.27 0 0 0 8 5.5c-.369 0-.595.09-.74.187a1 1 0 0 0-.34.398M9 11a1 1 0 1 1-2 0a1 1 0 0 1 2 0"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="WinoCustomMessageDialogWarningIconTemplate">
<Path
Fill="#fdcb6e"
Data="F1 M 2.021484 18.769531 C 1.767578 18.769531 1.52832 18.720703 1.303711 18.623047 C 1.079102 18.525391 0.880534 18.391928 0.708008 18.222656 C 0.535482 18.053385 0.398763 17.856445 0.297852 17.631836 C 0.19694 17.407227 0.146484 17.167969 0.146484 16.914062 C 0.146484 16.614584 0.211589 16.328125 0.341797 16.054688 L 7.695312 1.347656 C 7.851562 1.035156 8.082682 0.784506 8.388672 0.595703 C 8.694661 0.406902 9.023438 0.3125 9.375 0.3125 C 9.726562 0.3125 10.055338 0.406902 10.361328 0.595703 C 10.667317 0.784506 10.898438 1.035156 11.054688 1.347656 L 18.408203 16.054688 C 18.53841 16.328125 18.603516 16.614584 18.603516 16.914062 C 18.603516 17.167969 18.553059 17.407227 18.452148 17.631836 C 18.351236 17.856445 18.216145 18.053385 18.046875 18.222656 C 17.877604 18.391928 17.679035 18.525391 17.451172 18.623047 C 17.223307 18.720703 16.982422 18.769531 16.728516 18.769531 Z M 16.728516 17.519531 C 16.884766 17.519531 17.027994 17.460938 17.158203 17.34375 C 17.28841 17.226562 17.353516 17.086588 17.353516 16.923828 C 17.353516 16.806641 17.330729 16.702475 17.285156 16.611328 L 9.931641 1.904297 C 9.879557 1.793621 9.80306 1.708984 9.702148 1.650391 C 9.601236 1.591797 9.492188 1.5625 9.375 1.5625 C 9.257812 1.5625 9.148763 1.593426 9.047852 1.655273 C 8.946939 1.717123 8.870442 1.800131 8.818359 1.904297 L 1.464844 16.611328 C 1.419271 16.702475 1.396484 16.803387 1.396484 16.914062 C 1.396484 17.083334 1.459961 17.226562 1.586914 17.34375 C 1.713867 17.460938 1.858724 17.519531 2.021484 17.519531 Z M 8.75 11.875 L 8.75 6.875 C 8.75 6.705729 8.811849 6.559245 8.935547 6.435547 C 9.059244 6.31185 9.205729 6.25 9.375 6.25 C 9.544271 6.25 9.690755 6.31185 9.814453 6.435547 C 9.93815 6.559245 10 6.705729 10 6.875 L 10 11.875 C 10 12.044271 9.93815 12.190756 9.814453 12.314453 C 9.690755 12.438151 9.544271 12.5 9.375 12.5 C 9.205729 12.5 9.059244 12.438151 8.935547 12.314453 C 8.811849 12.190756 8.75 12.044271 8.75 11.875 Z M 8.4375 14.375 C 8.4375 14.114584 8.528646 13.893229 8.710938 13.710938 C 8.893229 13.528646 9.114583 13.4375 9.375 13.4375 C 9.635416 13.4375 9.856771 13.528646 10.039062 13.710938 C 10.221354 13.893229 10.3125 14.114584 10.3125 14.375 C 10.3125 14.635417 10.221354 14.856771 10.039062 15.039062 C 9.856771 15.221354 9.635416 15.3125 9.375 15.3125 C 9.114583 15.3125 8.893229 15.221354 8.710938 15.039062 C 8.528646 14.856771 8.4375 14.635417 8.4375 14.375 Z "
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="WinoCustomMessageDialogErrorIconTemplate">
<Path
Fill="#d63031"
Data="F1 M 18.75 9.375 C 18.75 10.240886 18.639322 11.072592 18.417969 11.870117 C 18.196613 12.667644 17.882486 13.414714 17.475586 14.111328 C 17.068684 14.807943 16.580402 15.441081 16.010742 16.010742 C 15.44108 16.580404 14.807942 17.068686 14.111328 17.475586 C 13.414713 17.882486 12.667643 18.196615 11.870117 18.417969 C 11.072591 18.639322 10.240885 18.75 9.375 18.75 C 8.509114 18.75 7.675781 18.639322 6.875 18.417969 C 6.074219 18.196615 5.327148 17.882486 4.633789 17.475586 C 3.94043 17.068686 3.308919 16.580404 2.739258 16.010742 C 2.169596 15.441081 1.681315 14.80957 1.274414 14.116211 C 0.867513 13.422852 0.553385 12.675781 0.332031 11.875 C 0.110677 11.074219 0 10.240886 0 9.375 C 0 8.509115 0.110677 7.677409 0.332031 6.879883 C 0.553385 6.082357 0.867513 5.335287 1.274414 4.638672 C 1.681315 3.942059 2.169596 3.30892 2.739258 2.739258 C 3.308919 2.169598 3.942057 1.681316 4.638672 1.274414 C 5.335286 0.867514 6.082356 0.553387 6.879883 0.332031 C 7.677409 0.110678 8.509114 0 9.375 0 C 10.234375 0 11.062825 0.112305 11.860352 0.336914 C 12.657877 0.561523 13.404947 0.877279 14.101562 1.28418 C 14.798176 1.691082 15.431314 2.179363 16.000977 2.749023 C 16.570637 3.318686 17.058918 3.951824 17.46582 4.648438 C 17.872721 5.345053 18.188477 6.092123 18.413086 6.889648 C 18.637695 7.687175 18.75 8.515625 18.75 9.375 Z M 17.5 9.375 C 17.5 8.626303 17.403971 7.905273 17.211914 7.211914 C 17.019855 6.518556 16.746418 5.87077 16.391602 5.268555 C 16.036783 4.666342 15.613606 4.119467 15.12207 3.62793 C 14.630533 3.136395 14.083658 2.713217 13.481445 2.358398 C 12.879231 2.003582 12.231445 1.730145 11.538086 1.538086 C 10.844727 1.346029 10.123697 1.25 9.375 1.25 C 8.626302 1.25 7.905273 1.346029 7.211914 1.538086 C 6.518555 1.730145 5.870768 2.003582 5.268555 2.358398 C 4.666341 2.713217 4.119466 3.136395 3.62793 3.62793 C 3.136393 4.119467 2.713216 4.666342 2.358398 5.268555 C 2.003581 5.87077 1.730143 6.518556 1.538086 7.211914 C 1.346029 7.905273 1.25 8.626303 1.25 9.375 C 1.25 10.123698 1.346029 10.844727 1.538086 11.538086 C 1.730143 12.231445 2.001953 12.879232 2.353516 13.481445 C 2.705078 14.083659 3.128255 14.632162 3.623047 15.126953 C 4.117838 15.621745 4.666341 16.044922 5.268555 16.396484 C 5.870768 16.748047 6.518555 17.019857 7.211914 17.211914 C 7.905273 17.403971 8.626302 17.5 9.375 17.5 C 10.123697 17.5 10.844727 17.403971 11.538086 17.211914 C 12.231445 17.019857 12.879231 16.74642 13.481445 16.391602 C 14.083658 16.036783 14.630533 15.613607 15.12207 15.12207 C 15.613606 14.630534 16.036783 14.083659 16.391602 13.481445 C 16.746418 12.879232 17.019855 12.231445 17.211914 11.538086 C 17.403971 10.844727 17.5 10.123698 17.5 9.375 Z M 13.4375 5.9375 C 13.4375 6.106771 13.37565 6.253256 13.251953 6.376953 L 10.253906 9.375 L 13.251953 12.373047 C 13.37565 12.496745 13.4375 12.643229 13.4375 12.8125 C 13.4375 12.981771 13.37565 13.128256 13.251953 13.251953 C 13.128254 13.375651 12.98177 13.4375 12.8125 13.4375 C 12.643229 13.4375 12.496744 13.375651 12.373047 13.251953 L 9.375 10.253906 L 6.376953 13.251953 C 6.253255 13.375651 6.106771 13.4375 5.9375 13.4375 C 5.768229 13.4375 5.621745 13.375651 5.498047 13.251953 C 5.374349 13.128256 5.3125 12.981771 5.3125 12.8125 C 5.3125 12.643229 5.374349 12.496745 5.498047 12.373047 L 8.496094 9.375 L 5.498047 6.376953 C 5.374349 6.253256 5.3125 6.106771 5.3125 5.9375 C 5.3125 5.768229 5.374349 5.621745 5.498047 5.498047 C 5.621745 5.37435 5.768229 5.3125 5.9375 5.3125 C 6.106771 5.3125 6.253255 5.37435 6.376953 5.498047 L 9.375 8.496094 L 12.373047 5.498047 C 12.496744 5.37435 12.643229 5.3125 12.8125 5.3125 C 12.98177 5.3125 13.128254 5.37435 13.251953 5.498047 C 13.37565 5.621745 13.4375 5.768229 13.4375 5.9375 Z "
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</DataTemplate>
<selectors:CustomWinoMessageDialogIconSelector
x:Key="CustomWinoMessageDialogIconSelector"
InfoIconTemplate="{StaticResource WinoCustomMessageDialogInformationIconTemplate}"
QuestionIconTemplate="{StaticResource WinoCustomMessageDialogQuestionIconTemplate}"
WarningIconTemplate="{StaticResource WinoCustomMessageDialogWarningIconTemplate}"
ErrorIconTemplate="{StaticResource WinoCustomMessageDialogErrorIconTemplate}" />
<DataTemplate x:DataType="dialogs:CustomMessageDialogInformationContainer" x:Key="CustomWinoContentDialogTitleTemplate">
<Grid />
</DataTemplate>
<DataTemplate x:DataType="dialogs:CustomMessageDialogInformationContainer" x:Key="CustomWinoContentDialogContentTemplate">
<Grid RowSpacing="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Title -->
<StackPanel Orientation="Horizontal" Spacing="12">
<Viewbox Width="28" HorizontalAlignment="Left">
<ContentControl ContentTemplateSelector="{StaticResource CustomWinoMessageDialogIconSelector}" Content="{x:Bind Icon}" />
</Viewbox>
<TextBlock
Text="{x:Bind Title}"
HorizontalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}"
VerticalAlignment="Center" />
</StackPanel>
<!-- Description -->
<TextBlock
Text="{x:Bind Description}"
Grid.Row="1"
TextWrapping="WrapWholeWords" />
<CheckBox
Grid.Row="2"
Content="{x:Bind domain:Translator.Dialog_DontAskAgain}"
Visibility="{x:Bind IsDontAskAgainEnabled}"
IsChecked="{x:Bind IsDontAskChecked, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</ResourceDictionary>

View File

@@ -0,0 +1,12 @@
using Windows.UI.Xaml;
namespace Wino.Styles
{
partial class CustomMessageDialogStyles : ResourceDictionary
{
public CustomMessageDialogStyles()
{
InitializeComponent();
}
}
}

View File

@@ -31,6 +31,16 @@ namespace Wino.Views
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage);
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
if (e.Parameter is WinoPage parameterPage)
{
switch (parameterPage)
{
case WinoPage.AppPreferencesPage:
WeakReferenceMessenger.Default.Send(new BreadcrumbNavigationRequested(Translator.SettingsAppPreferences_Title, WinoPage.AppPreferencesPage));
break;
}
}
}
public override void OnLanguageChanged()

View File

@@ -3,9 +3,9 @@
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<!-- UWP WAM Authentication on Xbox needs this. -->
<UseDotNetNativeSharedAssemblyFrameworkPackage>false</UseDotNetNativeSharedAssemblyFrameworkPackage>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<!-- UWP WAM Authentication on Xbox needs this. -->
<UseDotNetNativeSharedAssemblyFrameworkPackage>false</UseDotNetNativeSharedAssemblyFrameworkPackage>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -89,7 +89,7 @@
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
<UseDotNetNativeSharedAssemblyFrameworkPackage>false</UseDotNetNativeSharedAssemblyFrameworkPackage>
<Use64BitCompiler>true</Use64BitCompiler>
<ShortcutGenericAnalysis>true</ShortcutGenericAnalysis>
<ShortcutGenericAnalysis>true</ShortcutGenericAnalysis>
<OutOfProcPDB>true</OutOfProcPDB>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
@@ -119,7 +119,6 @@
<Use64BitCompiler>true</Use64BitCompiler>
<OutOfProcPDB>true</OutOfProcPDB>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AdaptiveTriggerLibrary">
<Version>1.2.2</Version>
@@ -230,6 +229,7 @@
<DependentUpon>AccountReorderDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\BaseAccountCreationDialog.cs" />
<Compile Include="Dialogs\CustomMessageDialogInformationContainer.cs" />
<Compile Include="Dialogs\CustomThemeBuilderDialog.xaml.cs">
<DependentUpon>CustomThemeBuilderDialog.xaml</DependentUpon>
</Compile>
@@ -245,15 +245,9 @@
<Compile Include="Dialogs\CreateAccountAliasDialog.xaml.cs">
<DependentUpon>CreateAccountAliasDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\StoreRatingDialog.xaml.cs">
<DependentUpon>StoreRatingDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\SystemFolderConfigurationDialog.xaml.cs">
<DependentUpon>SystemFolderConfigurationDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\WinoMessageDialog.xaml.cs">
<DependentUpon>WinoMessageDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\TextInputDialog.xaml.cs">
<DependentUpon>TextInputDialog.xaml</DependentUpon>
</Compile>
@@ -290,9 +284,6 @@
<Compile Include="Controls\WinoInfoBar.cs" />
<Compile Include="Controls\WinoNavigationViewItem.cs" />
<Compile Include="Converters\ReverseBooleanToVisibilityConverter.cs" />
<Compile Include="Dialogs\ConfirmationDialog.xaml.cs">
<DependentUpon>ConfirmationDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\NewAccountDialog.xaml.cs">
<DependentUpon>NewAccountDialog.xaml</DependentUpon>
</Compile>
@@ -302,6 +293,7 @@
<Compile Include="Selectors\AccountProviderViewModelTemplateSelector.cs" />
<Compile Include="Selectors\AccountReorderTemplateSelector.cs" />
<Compile Include="Selectors\AppThemePreviewTemplateSelector.cs" />
<Compile Include="Selectors\CustomWinoMessageDialogIconSelector.cs" />
<Compile Include="Selectors\FileAttachmentTypeSelector.cs" />
<Compile Include="Selectors\MailItemContainerStyleSelector.cs" />
<Compile Include="Selectors\MailItemDisplayModePreviewTemplateSelector.cs" />
@@ -314,6 +306,9 @@
<Compile Include="Styles\CommandBarItems.xaml.cs">
<DependentUpon>CommandBarItems.xaml</DependentUpon>
</Compile>
<Compile Include="Styles\CustomMessageDialogStyles.xaml.cs">
<DependentUpon>CustomMessageDialogStyles.xaml</DependentUpon>
</Compile>
<Compile Include="Views\Abstract\AboutPageAbstract.cs" />
<Compile Include="Views\Abstract\AccountDetailsPageAbstract.cs" />
<Compile Include="Views\Abstract\AccountManagementPageAbstract.cs" />
@@ -494,26 +489,14 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Dialogs\StoreRatingDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Dialogs\SystemFolderConfigurationDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Dialogs\WinoMessageDialog.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Dialogs\TextInputDialog.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Dialogs\ConfirmationDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Dialogs\NewAccountDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -530,6 +513,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\CustomMessageDialogStyles.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\FontIcons.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -814,4 +801,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@@ -0,0 +1,7 @@
namespace Wino.Messaging.Client.Navigation
{
/// <summary>
/// Navigates user to Settings -> App Preferences.
/// </summary>
public record NavigateAppPreferencesRequested;
}

View File

@@ -0,0 +1,9 @@
using Wino.Core.Domain.Interfaces;
namespace Wino.Messaging.Server
{
/// <summary>
/// This message is sent to server to kill itself when UWP app is terminating.
/// </summary>
public record TerminateServerRequested : IClientMessage;
}

View File

@@ -110,5 +110,6 @@
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="confirmAppClose" />
</Capabilities>
</Package>

View File

@@ -21,6 +21,7 @@ namespace Wino.Server.Core
nameof(ProtocolAuthorizationCallbackReceived) => App.Current.Services.GetService<ProtocolAuthActivationHandler>(),
nameof(SynchronizationExistenceCheckRequest) => App.Current.Services.GetService<SyncExistenceHandler>(),
nameof(ServerTerminationModeChanged) => App.Current.Services.GetService<ServerTerminationModeHandler>(),
nameof(TerminateServerRequested) => App.Current.Services.GetService<TerminateServerRequestHandler>(),
_ => throw new Exception($"Server handler for {typeName} is not registered."),
};
}
@@ -36,6 +37,7 @@ namespace Wino.Server.Core
serviceCollection.AddTransient<ProtocolAuthActivationHandler>();
serviceCollection.AddTransient<SyncExistenceHandler>();
serviceCollection.AddTransient<ServerTerminationModeHandler>();
serviceCollection.AddTransient<TerminateServerRequestHandler>();
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Serilog;
using Wino.Core.Domain.Models.Server;
using Wino.Messaging.Server;
using Wino.Server.Core;
namespace Wino.Server.MessageHandlers
{
public class TerminateServerRequestHandler : ServerMessageHandler<TerminateServerRequested, bool>
{
public override WinoServerResponse<bool> FailureDefaultResponse(Exception ex) => WinoServerResponse<bool>.CreateErrorResponse(ex.Message);
protected override Task<WinoServerResponse<bool>> HandleAsync(TerminateServerRequested message, CancellationToken cancellationToken = default)
{
// This handler is only doing the logging right now.
// Client will always expect success response.
// Server will be terminated in the server context once the client gets the response.
Log.Information("Terminate server is requested by client. Killing server.");
return Task.FromResult(WinoServerResponse<bool>.CreateSuccessResponse(true));
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.Messaging;
using Serilog;
using Windows.ApplicationModel;
@@ -308,12 +309,28 @@ namespace Wino.Server
case nameof(ServerTerminationModeChanged):
await ExecuteServerMessageSafeAsync(args, JsonSerializer.Deserialize<ServerTerminationModeChanged>(messageJson, _jsonSerializerOptions));
break;
case nameof(TerminateServerRequested):
await ExecuteServerMessageSafeAsync(args, JsonSerializer.Deserialize<TerminateServerRequested>(messageJson, _jsonSerializerOptions));
KillServer();
break;
default:
Debug.WriteLine($"Missing handler for {typeName} in the server. Check ServerContext.cs - HandleServerMessageAsync.");
break;
}
}
private void KillServer()
{
DisposeConnection();
Application.Current.Dispatcher.Invoke(() =>
{
Application.Current.Shutdown();
});
}
/// <summary>
/// Executes ServerMessage coming from the UWP.
/// These requests are awaited and expected to return a response.