File scoped namespaces
This commit is contained in:
@@ -1,37 +1,36 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Activation
|
||||
{
|
||||
public abstract class ActivationHandler
|
||||
{
|
||||
public abstract bool CanHandle(object args);
|
||||
namespace Wino.Activation;
|
||||
|
||||
public abstract Task HandleAsync(object args);
|
||||
public abstract class ActivationHandler
|
||||
{
|
||||
public abstract bool CanHandle(object args);
|
||||
|
||||
public abstract Task HandleAsync(object args);
|
||||
}
|
||||
|
||||
// Extend this class to implement new ActivationHandlers
|
||||
public abstract class ActivationHandler<T> : ActivationHandler
|
||||
where T : class
|
||||
{
|
||||
// Override this method to add the activation logic in your activation handler
|
||||
protected abstract Task HandleInternalAsync(T args);
|
||||
|
||||
public override async Task HandleAsync(object args)
|
||||
{
|
||||
await HandleInternalAsync(args as T);
|
||||
}
|
||||
|
||||
// Extend this class to implement new ActivationHandlers
|
||||
public abstract class ActivationHandler<T> : ActivationHandler
|
||||
where T : class
|
||||
public override bool CanHandle(object args)
|
||||
{
|
||||
// Override this method to add the activation logic in your activation handler
|
||||
protected abstract Task HandleInternalAsync(T args);
|
||||
// CanHandle checks the args is of type you have configured
|
||||
return args is T && CanHandleInternal(args as T);
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(object args)
|
||||
{
|
||||
await HandleInternalAsync(args as T);
|
||||
}
|
||||
|
||||
public override bool CanHandle(object args)
|
||||
{
|
||||
// CanHandle checks the args is of type you have configured
|
||||
return args is T && CanHandleInternal(args as T);
|
||||
}
|
||||
|
||||
// You can override this method to add extra validation on activation args
|
||||
// to determine if your ActivationHandler should handle this activation args
|
||||
protected virtual bool CanHandleInternal(T args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// You can override this method to add extra validation on activation args
|
||||
// to determine if your ActivationHandler should handle this activation args
|
||||
protected virtual bool CanHandleInternal(T args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,81 +8,80 @@ using Windows.UI.Xaml.Navigation;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Messaging.Client.Shell;
|
||||
|
||||
namespace Wino.Core.UWP
|
||||
namespace Wino.Core.UWP;
|
||||
|
||||
public partial class BasePage : Page, IRecipient<LanguageChanged>
|
||||
{
|
||||
public partial class BasePage : Page, IRecipient<LanguageChanged>
|
||||
public UIElement ShellContent
|
||||
{
|
||||
public UIElement ShellContent
|
||||
{
|
||||
get { return (UIElement)GetValue(ShellContentProperty); }
|
||||
set { SetValue(ShellContentProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShellContentProperty = DependencyProperty.Register(nameof(ShellContent), typeof(UIElement), typeof(BasePage), new PropertyMetadata(null));
|
||||
|
||||
public void Receive(LanguageChanged message)
|
||||
{
|
||||
OnLanguageChanged();
|
||||
}
|
||||
|
||||
public virtual void OnLanguageChanged() { }
|
||||
get { return (UIElement)GetValue(ShellContentProperty); }
|
||||
set { SetValue(ShellContentProperty, value); }
|
||||
}
|
||||
|
||||
public abstract class BasePage<T> : BasePage where T : CoreBaseViewModel
|
||||
public static readonly DependencyProperty ShellContentProperty = DependencyProperty.Register(nameof(ShellContent), typeof(UIElement), typeof(BasePage), new PropertyMetadata(null));
|
||||
|
||||
public void Receive(LanguageChanged message)
|
||||
{
|
||||
public T ViewModel { get; } = WinoApplication.Current.Services.GetService<T>();
|
||||
OnLanguageChanged();
|
||||
}
|
||||
|
||||
protected BasePage()
|
||||
{
|
||||
ViewModel.Dispatcher = new UWPDispatcher(Dispatcher);
|
||||
public virtual void OnLanguageChanged() { }
|
||||
}
|
||||
|
||||
Loaded += PageLoaded;
|
||||
Unloaded += PageUnloaded;
|
||||
}
|
||||
public abstract class BasePage<T> : BasePage where T : CoreBaseViewModel
|
||||
{
|
||||
public T ViewModel { get; } = WinoApplication.Current.Services.GetService<T>();
|
||||
|
||||
private void PageUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Loaded -= PageLoaded;
|
||||
Unloaded -= PageUnloaded;
|
||||
}
|
||||
protected BasePage()
|
||||
{
|
||||
ViewModel.Dispatcher = new UWPDispatcher(Dispatcher);
|
||||
|
||||
private void PageLoaded(object sender, RoutedEventArgs e) => ViewModel.OnPageLoaded();
|
||||
Loaded += PageLoaded;
|
||||
Unloaded += PageUnloaded;
|
||||
}
|
||||
|
||||
~BasePage()
|
||||
{
|
||||
Debug.WriteLine($"Disposed {GetType().Name}");
|
||||
}
|
||||
private void PageUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Loaded -= PageLoaded;
|
||||
Unloaded -= PageUnloaded;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
private void PageLoaded(object sender, RoutedEventArgs e) => ViewModel.OnPageLoaded();
|
||||
|
||||
var mode = GetNavigationMode(e.NavigationMode);
|
||||
var parameter = e.Parameter;
|
||||
~BasePage()
|
||||
{
|
||||
Debug.WriteLine($"Disposed {GetType().Name}");
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
ViewModel.OnNavigatedTo(mode, parameter);
|
||||
}
|
||||
var mode = GetNavigationMode(e.NavigationMode);
|
||||
var parameter = e.Parameter;
|
||||
|
||||
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
|
||||
{
|
||||
base.OnNavigatingFrom(e);
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
|
||||
var mode = GetNavigationMode(e.NavigationMode);
|
||||
var parameter = e.Parameter;
|
||||
ViewModel.OnNavigatedTo(mode, parameter);
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
|
||||
{
|
||||
base.OnNavigatingFrom(e);
|
||||
|
||||
ViewModel.OnNavigatedFrom(mode, parameter);
|
||||
var mode = GetNavigationMode(e.NavigationMode);
|
||||
var parameter = e.Parameter;
|
||||
|
||||
GC.Collect();
|
||||
}
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
|
||||
private Domain.Models.Navigation.NavigationMode GetNavigationMode(NavigationMode mode)
|
||||
{
|
||||
return (Domain.Models.Navigation.NavigationMode)mode;
|
||||
}
|
||||
ViewModel.OnNavigatedFrom(mode, parameter);
|
||||
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
private Domain.Models.Navigation.NavigationMode GetNavigationMode(NavigationMode mode)
|
||||
{
|
||||
return (Domain.Models.Navigation.NavigationMode)mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,68 +9,67 @@ using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
|
||||
namespace Wino.Core.UWP.Controls
|
||||
namespace Wino.Core.UWP.Controls;
|
||||
|
||||
public sealed partial class AccountCreationDialogControl : UserControl, IRecipient<CopyAuthURLRequested>
|
||||
{
|
||||
public sealed partial class AccountCreationDialogControl : UserControl, IRecipient<CopyAuthURLRequested>
|
||||
private string copyClipboardURL;
|
||||
|
||||
public event EventHandler CancelClicked;
|
||||
|
||||
public AccountCreationDialogState State
|
||||
{
|
||||
private string copyClipboardURL;
|
||||
|
||||
public event EventHandler CancelClicked;
|
||||
|
||||
public AccountCreationDialogState State
|
||||
{
|
||||
get { return (AccountCreationDialogState)GetValue(StateProperty); }
|
||||
set { SetValue(StateProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(AccountCreationDialogControl), new PropertyMetadata(AccountCreationDialogState.Idle, new PropertyChangedCallback(OnStateChanged)));
|
||||
|
||||
public AccountCreationDialogControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private static void OnStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is AccountCreationDialogControl dialog)
|
||||
{
|
||||
dialog.UpdateVisualStates();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVisualStates() => VisualStateManager.GoToState(this, State.ToString(), false);
|
||||
|
||||
public async void Receive(CopyAuthURLRequested message)
|
||||
{
|
||||
copyClipboardURL = message.AuthURL;
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
AuthHelpDialogButton.Visibility = Windows.UI.Xaml.Visibility.Visible;
|
||||
});
|
||||
}
|
||||
|
||||
private void ControlLoaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Register(this);
|
||||
}
|
||||
|
||||
private void ControlUnloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
}
|
||||
|
||||
private async void CopyClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(copyClipboardURL)) return;
|
||||
|
||||
var clipboardService = WinoApplication.Current.Services.GetService<IClipboardService>();
|
||||
await clipboardService.CopyClipboardAsync(copyClipboardURL);
|
||||
}
|
||||
|
||||
|
||||
private void CancelButtonClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e) => CancelClicked?.Invoke(this, null);
|
||||
get { return (AccountCreationDialogState)GetValue(StateProperty); }
|
||||
set { SetValue(StateProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(AccountCreationDialogControl), new PropertyMetadata(AccountCreationDialogState.Idle, new PropertyChangedCallback(OnStateChanged)));
|
||||
|
||||
public AccountCreationDialogControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private static void OnStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is AccountCreationDialogControl dialog)
|
||||
{
|
||||
dialog.UpdateVisualStates();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVisualStates() => VisualStateManager.GoToState(this, State.ToString(), false);
|
||||
|
||||
public async void Receive(CopyAuthURLRequested message)
|
||||
{
|
||||
copyClipboardURL = message.AuthURL;
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
AuthHelpDialogButton.Visibility = Windows.UI.Xaml.Visibility.Visible;
|
||||
});
|
||||
}
|
||||
|
||||
private void ControlLoaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Register(this);
|
||||
}
|
||||
|
||||
private void ControlUnloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
}
|
||||
|
||||
private async void CopyClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(copyClipboardURL)) return;
|
||||
|
||||
var clipboardService = WinoApplication.Current.Services.GetService<IClipboardService>();
|
||||
await clipboardService.CopyClipboardAsync(copyClipboardURL);
|
||||
}
|
||||
|
||||
|
||||
private void CancelButtonClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e) => CancelClicked?.Invoke(this, null);
|
||||
}
|
||||
|
||||
@@ -1,106 +1,105 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wino.Core.UWP.Controls
|
||||
namespace Wino.Core.UWP.Controls;
|
||||
|
||||
public static class ControlConstants
|
||||
{
|
||||
public static class ControlConstants
|
||||
public static Dictionary<WinoIconGlyph, string> WinoIconFontDictionary = new Dictionary<WinoIconGlyph, string>()
|
||||
{
|
||||
public static Dictionary<WinoIconGlyph, string> WinoIconFontDictionary = new Dictionary<WinoIconGlyph, string>()
|
||||
{
|
||||
{ WinoIconGlyph.None, "\u0020" },
|
||||
{ WinoIconGlyph.Archive, "\uE066" },
|
||||
{ WinoIconGlyph.UnArchive, "\uE06C" },
|
||||
{ WinoIconGlyph.Reply, "\uF176" },
|
||||
{ WinoIconGlyph.ReplyAll, "\uF17A" },
|
||||
{ WinoIconGlyph.Sync, "\uE895" },
|
||||
{ WinoIconGlyph.Send, "\uEA8E" },
|
||||
{ WinoIconGlyph.LightEditor, "\uE1F6" },
|
||||
{ WinoIconGlyph.Delete, "\uEEA6" },
|
||||
{ WinoIconGlyph.DarkEditor, "\uEE44" },
|
||||
{ WinoIconGlyph.Draft, "\uF3BE" },
|
||||
{ WinoIconGlyph.Flag, "\uF40C" },
|
||||
{ WinoIconGlyph.ClearFlag, "\uF40F" },
|
||||
{ WinoIconGlyph.Folder, "\uE643" },
|
||||
{ WinoIconGlyph.Forward, "\uE7AA" },
|
||||
{ WinoIconGlyph.Inbox, "\uF516" },
|
||||
{ WinoIconGlyph.MarkRead, "\uF522" },
|
||||
{ WinoIconGlyph.MarkUnread, "\uF529" },
|
||||
{ WinoIconGlyph.MultiSelect, "\uE84D" },
|
||||
{ WinoIconGlyph.Save, "\uEA43" },
|
||||
{ WinoIconGlyph.CreateFolder, "\uE645" },
|
||||
{ WinoIconGlyph.Pin, "\uF5FF" },
|
||||
{ WinoIconGlyph.UnPin, "\uE985" },
|
||||
{ WinoIconGlyph.Star, "\uE734" },
|
||||
{ WinoIconGlyph.Ignore, "\uF5D0" },
|
||||
{ WinoIconGlyph.Find, "\uEA7D" },
|
||||
{ WinoIconGlyph.Zoom, "\uEE8E" },
|
||||
{ WinoIconGlyph.SpecialFolderInbox, "\uF516" },
|
||||
{ WinoIconGlyph.SpecialFolderStarred, "\uF70D" },
|
||||
{ WinoIconGlyph.SpecialFolderImportant, "\uE2F4" },
|
||||
{ WinoIconGlyph.SpecialFolderSent, "\uEA8E" },
|
||||
{ WinoIconGlyph.SpecialFolderDraft, "\uF3BE" },
|
||||
{ WinoIconGlyph.SpecialFolderArchive, "\uE066" },
|
||||
{ WinoIconGlyph.SpecialFolderDeleted, "\uEEA6" },
|
||||
{ WinoIconGlyph.SpecialFolderJunk, "\uF140" },
|
||||
{ WinoIconGlyph.SpecialFolderChat, "\uE8BD" },
|
||||
{ WinoIconGlyph.SpecialFolderCategory, "\uF599" },
|
||||
{ WinoIconGlyph.SpecialFolderUnread, "\uF529" },
|
||||
{ WinoIconGlyph.SpecialFolderForums, "\uF5B8" },
|
||||
{ WinoIconGlyph.SpecialFolderUpdated, "\uF565" },
|
||||
{ WinoIconGlyph.SpecialFolderPersonal, "\uE25A" },
|
||||
{ WinoIconGlyph.SpecialFolderPromotions, "\uF7B6" },
|
||||
{ WinoIconGlyph.SpecialFolderSocial, "\uEEEB" },
|
||||
{ WinoIconGlyph.SpecialFolderOther, "\uE643" },
|
||||
{ WinoIconGlyph.SpecialFolderMore, "\uF0F4" },
|
||||
{ WinoIconGlyph.Microsoft, "\uE904" },
|
||||
{ WinoIconGlyph.Google, "\uE905" },
|
||||
{ WinoIconGlyph.NewMail, "\uF107" },
|
||||
{ WinoIconGlyph.TurnOfNotifications, "\uF11D" },
|
||||
{ WinoIconGlyph.Rename, "\uF668" },
|
||||
{ WinoIconGlyph.EmptyFolder, "\uE47E" },
|
||||
{ WinoIconGlyph.DontSync, "\uF195" },
|
||||
{ WinoIconGlyph.Move, "\uE7B8" },
|
||||
{ WinoIconGlyph.Mail, "\uF509" },
|
||||
{ WinoIconGlyph.More, "\uE824" },
|
||||
{ WinoIconGlyph.CustomServer, "\uF509" },
|
||||
{ WinoIconGlyph.Print, "\uE922" },
|
||||
{ WinoIconGlyph.Attachment, "\uE723" },
|
||||
{ WinoIconGlyph.SortTextDesc, "\U000F3606" },
|
||||
{ WinoIconGlyph.SortLinesDesc, "\U000F038A" },
|
||||
{ WinoIconGlyph.Certificate, "\uEB95" },
|
||||
{ WinoIconGlyph.OpenInNewWindow, "\uE8A7" },
|
||||
{ WinoIconGlyph.Message, "\uE8BD" },
|
||||
{ WinoIconGlyph.New, "\U000F002A" },
|
||||
{ WinoIconGlyph.Blocked,"\uF140" },
|
||||
{ WinoIconGlyph.IMAP, "\uE715" },
|
||||
{ WinoIconGlyph.Calendar, "\uE912" },
|
||||
{ WinoIconGlyph.CalendarToday, "\uE911" },
|
||||
{ WinoIconGlyph.CalendarDay, "\uE913" },
|
||||
{ WinoIconGlyph.CalendarWeek, "\uE914" },
|
||||
{ WinoIconGlyph.CalendarMonth, "\uE91c" },
|
||||
{ WinoIconGlyph.CalendarYear, "\uE917" },
|
||||
{ WinoIconGlyph.WeatherBlow, "\uE907" },
|
||||
{ WinoIconGlyph.WeatherCloudy, "\uE920" },
|
||||
{ WinoIconGlyph.WeatherSunny, "\uE90e" },
|
||||
{ WinoIconGlyph.WeatherRainy, "\uE908" },
|
||||
{ WinoIconGlyph.WeatherSnowy, "\uE90a" },
|
||||
{ WinoIconGlyph.WeatherSnowShowerAtNight, "\uE90c" },
|
||||
{ WinoIconGlyph.WeatherThunderstorm, "\uE906" },
|
||||
{ WinoIconGlyph.CalendarEventRepeat, "\uE915" },
|
||||
{ WinoIconGlyph.CalendarEventMuiltiDay, "\uE91b" },
|
||||
{ WinoIconGlyph.Reminder, "\uE918" },
|
||||
{ WinoIconGlyph.CalendarAttendee, "\uE91a" },
|
||||
{ WinoIconGlyph.CalendarSync, "\uE91d" },
|
||||
{ WinoIconGlyph.CalendarError, "\uE916" },
|
||||
{ WinoIconGlyph.CalendarAttendees, "\uE929" },
|
||||
{ WinoIconGlyph.EventEditSeries, "\uE92A" },
|
||||
{ WinoIconGlyph.EventTentative, "\uE928" },
|
||||
{ WinoIconGlyph.EventAccept, "\uE925" },
|
||||
{ WinoIconGlyph.EventRespond, "\uE924" },
|
||||
{ WinoIconGlyph.EventReminder, "\uE923" },
|
||||
{ WinoIconGlyph.EventJoinOnline, "\uE926" },
|
||||
{ WinoIconGlyph.ViewMessageSource, "\uE943" },
|
||||
{ WinoIconGlyph.Apple, "\uE92B" },
|
||||
{ WinoIconGlyph.Yahoo, "\uE92C" }
|
||||
};
|
||||
}
|
||||
{ WinoIconGlyph.None, "\u0020" },
|
||||
{ WinoIconGlyph.Archive, "\uE066" },
|
||||
{ WinoIconGlyph.UnArchive, "\uE06C" },
|
||||
{ WinoIconGlyph.Reply, "\uF176" },
|
||||
{ WinoIconGlyph.ReplyAll, "\uF17A" },
|
||||
{ WinoIconGlyph.Sync, "\uE895" },
|
||||
{ WinoIconGlyph.Send, "\uEA8E" },
|
||||
{ WinoIconGlyph.LightEditor, "\uE1F6" },
|
||||
{ WinoIconGlyph.Delete, "\uEEA6" },
|
||||
{ WinoIconGlyph.DarkEditor, "\uEE44" },
|
||||
{ WinoIconGlyph.Draft, "\uF3BE" },
|
||||
{ WinoIconGlyph.Flag, "\uF40C" },
|
||||
{ WinoIconGlyph.ClearFlag, "\uF40F" },
|
||||
{ WinoIconGlyph.Folder, "\uE643" },
|
||||
{ WinoIconGlyph.Forward, "\uE7AA" },
|
||||
{ WinoIconGlyph.Inbox, "\uF516" },
|
||||
{ WinoIconGlyph.MarkRead, "\uF522" },
|
||||
{ WinoIconGlyph.MarkUnread, "\uF529" },
|
||||
{ WinoIconGlyph.MultiSelect, "\uE84D" },
|
||||
{ WinoIconGlyph.Save, "\uEA43" },
|
||||
{ WinoIconGlyph.CreateFolder, "\uE645" },
|
||||
{ WinoIconGlyph.Pin, "\uF5FF" },
|
||||
{ WinoIconGlyph.UnPin, "\uE985" },
|
||||
{ WinoIconGlyph.Star, "\uE734" },
|
||||
{ WinoIconGlyph.Ignore, "\uF5D0" },
|
||||
{ WinoIconGlyph.Find, "\uEA7D" },
|
||||
{ WinoIconGlyph.Zoom, "\uEE8E" },
|
||||
{ WinoIconGlyph.SpecialFolderInbox, "\uF516" },
|
||||
{ WinoIconGlyph.SpecialFolderStarred, "\uF70D" },
|
||||
{ WinoIconGlyph.SpecialFolderImportant, "\uE2F4" },
|
||||
{ WinoIconGlyph.SpecialFolderSent, "\uEA8E" },
|
||||
{ WinoIconGlyph.SpecialFolderDraft, "\uF3BE" },
|
||||
{ WinoIconGlyph.SpecialFolderArchive, "\uE066" },
|
||||
{ WinoIconGlyph.SpecialFolderDeleted, "\uEEA6" },
|
||||
{ WinoIconGlyph.SpecialFolderJunk, "\uF140" },
|
||||
{ WinoIconGlyph.SpecialFolderChat, "\uE8BD" },
|
||||
{ WinoIconGlyph.SpecialFolderCategory, "\uF599" },
|
||||
{ WinoIconGlyph.SpecialFolderUnread, "\uF529" },
|
||||
{ WinoIconGlyph.SpecialFolderForums, "\uF5B8" },
|
||||
{ WinoIconGlyph.SpecialFolderUpdated, "\uF565" },
|
||||
{ WinoIconGlyph.SpecialFolderPersonal, "\uE25A" },
|
||||
{ WinoIconGlyph.SpecialFolderPromotions, "\uF7B6" },
|
||||
{ WinoIconGlyph.SpecialFolderSocial, "\uEEEB" },
|
||||
{ WinoIconGlyph.SpecialFolderOther, "\uE643" },
|
||||
{ WinoIconGlyph.SpecialFolderMore, "\uF0F4" },
|
||||
{ WinoIconGlyph.Microsoft, "\uE904" },
|
||||
{ WinoIconGlyph.Google, "\uE905" },
|
||||
{ WinoIconGlyph.NewMail, "\uF107" },
|
||||
{ WinoIconGlyph.TurnOfNotifications, "\uF11D" },
|
||||
{ WinoIconGlyph.Rename, "\uF668" },
|
||||
{ WinoIconGlyph.EmptyFolder, "\uE47E" },
|
||||
{ WinoIconGlyph.DontSync, "\uF195" },
|
||||
{ WinoIconGlyph.Move, "\uE7B8" },
|
||||
{ WinoIconGlyph.Mail, "\uF509" },
|
||||
{ WinoIconGlyph.More, "\uE824" },
|
||||
{ WinoIconGlyph.CustomServer, "\uF509" },
|
||||
{ WinoIconGlyph.Print, "\uE922" },
|
||||
{ WinoIconGlyph.Attachment, "\uE723" },
|
||||
{ WinoIconGlyph.SortTextDesc, "\U000F3606" },
|
||||
{ WinoIconGlyph.SortLinesDesc, "\U000F038A" },
|
||||
{ WinoIconGlyph.Certificate, "\uEB95" },
|
||||
{ WinoIconGlyph.OpenInNewWindow, "\uE8A7" },
|
||||
{ WinoIconGlyph.Message, "\uE8BD" },
|
||||
{ WinoIconGlyph.New, "\U000F002A" },
|
||||
{ WinoIconGlyph.Blocked,"\uF140" },
|
||||
{ WinoIconGlyph.IMAP, "\uE715" },
|
||||
{ WinoIconGlyph.Calendar, "\uE912" },
|
||||
{ WinoIconGlyph.CalendarToday, "\uE911" },
|
||||
{ WinoIconGlyph.CalendarDay, "\uE913" },
|
||||
{ WinoIconGlyph.CalendarWeek, "\uE914" },
|
||||
{ WinoIconGlyph.CalendarMonth, "\uE91c" },
|
||||
{ WinoIconGlyph.CalendarYear, "\uE917" },
|
||||
{ WinoIconGlyph.WeatherBlow, "\uE907" },
|
||||
{ WinoIconGlyph.WeatherCloudy, "\uE920" },
|
||||
{ WinoIconGlyph.WeatherSunny, "\uE90e" },
|
||||
{ WinoIconGlyph.WeatherRainy, "\uE908" },
|
||||
{ WinoIconGlyph.WeatherSnowy, "\uE90a" },
|
||||
{ WinoIconGlyph.WeatherSnowShowerAtNight, "\uE90c" },
|
||||
{ WinoIconGlyph.WeatherThunderstorm, "\uE906" },
|
||||
{ WinoIconGlyph.CalendarEventRepeat, "\uE915" },
|
||||
{ WinoIconGlyph.CalendarEventMuiltiDay, "\uE91b" },
|
||||
{ WinoIconGlyph.Reminder, "\uE918" },
|
||||
{ WinoIconGlyph.CalendarAttendee, "\uE91a" },
|
||||
{ WinoIconGlyph.CalendarSync, "\uE91d" },
|
||||
{ WinoIconGlyph.CalendarError, "\uE916" },
|
||||
{ WinoIconGlyph.CalendarAttendees, "\uE929" },
|
||||
{ WinoIconGlyph.EventEditSeries, "\uE92A" },
|
||||
{ WinoIconGlyph.EventTentative, "\uE928" },
|
||||
{ WinoIconGlyph.EventAccept, "\uE925" },
|
||||
{ WinoIconGlyph.EventRespond, "\uE924" },
|
||||
{ WinoIconGlyph.EventReminder, "\uE923" },
|
||||
{ WinoIconGlyph.EventJoinOnline, "\uE926" },
|
||||
{ WinoIconGlyph.ViewMessageSource, "\uE943" },
|
||||
{ WinoIconGlyph.Apple, "\uE92B" },
|
||||
{ WinoIconGlyph.Yahoo, "\uE92C" }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,86 +2,85 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Wino.Core.UWP.Controls
|
||||
namespace Wino.Core.UWP.Controls;
|
||||
|
||||
public partial class EqualGridPanel : Panel
|
||||
{
|
||||
public partial class EqualGridPanel : Panel
|
||||
public int Rows
|
||||
{
|
||||
public int Rows
|
||||
get { return (int)GetValue(RowsProperty); }
|
||||
set { SetValue(RowsProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty RowsProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(Rows),
|
||||
typeof(int),
|
||||
typeof(EqualGridPanel),
|
||||
new PropertyMetadata(1, OnLayoutPropertyChanged));
|
||||
|
||||
public int Columns
|
||||
{
|
||||
get { return (int)GetValue(ColumnsProperty); }
|
||||
set { SetValue(ColumnsProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ColumnsProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(Columns),
|
||||
typeof(int),
|
||||
typeof(EqualGridPanel),
|
||||
new PropertyMetadata(1, OnLayoutPropertyChanged));
|
||||
|
||||
private static void OnLayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is EqualGridPanel panel)
|
||||
{
|
||||
get { return (int)GetValue(RowsProperty); }
|
||||
set { SetValue(RowsProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty RowsProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(Rows),
|
||||
typeof(int),
|
||||
typeof(EqualGridPanel),
|
||||
new PropertyMetadata(1, OnLayoutPropertyChanged));
|
||||
|
||||
public int Columns
|
||||
{
|
||||
get { return (int)GetValue(ColumnsProperty); }
|
||||
set { SetValue(ColumnsProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ColumnsProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(Columns),
|
||||
typeof(int),
|
||||
typeof(EqualGridPanel),
|
||||
new PropertyMetadata(1, OnLayoutPropertyChanged));
|
||||
|
||||
private static void OnLayoutPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is EqualGridPanel panel)
|
||||
{
|
||||
panel.InvalidateMeasure();
|
||||
panel.InvalidateArrange();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (Rows <= 0 || Columns <= 0)
|
||||
{
|
||||
return new Size(0, 0);
|
||||
}
|
||||
|
||||
double cellWidth = availableSize.Width / Columns;
|
||||
double cellHeight = availableSize.Height / Rows;
|
||||
|
||||
foreach (UIElement child in Children)
|
||||
{
|
||||
child.Measure(new Size(cellWidth, cellHeight));
|
||||
}
|
||||
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (Rows <= 0 || Columns <= 0)
|
||||
{
|
||||
return new Size(0, 0);
|
||||
}
|
||||
|
||||
double cellWidth = finalSize.Width / Columns;
|
||||
double cellHeight = finalSize.Height / Rows;
|
||||
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
int row = i / Columns;
|
||||
int column = i % Columns;
|
||||
|
||||
double x = column * cellWidth;
|
||||
double y = row * cellHeight;
|
||||
|
||||
Rect rect = new Rect(x, y, cellWidth, cellHeight);
|
||||
Children[i].Arrange(rect);
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
panel.InvalidateMeasure();
|
||||
panel.InvalidateArrange();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (Rows <= 0 || Columns <= 0)
|
||||
{
|
||||
return new Size(0, 0);
|
||||
}
|
||||
|
||||
double cellWidth = availableSize.Width / Columns;
|
||||
double cellHeight = availableSize.Height / Rows;
|
||||
|
||||
foreach (UIElement child in Children)
|
||||
{
|
||||
child.Measure(new Size(cellWidth, cellHeight));
|
||||
}
|
||||
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (Rows <= 0 || Columns <= 0)
|
||||
{
|
||||
return new Size(0, 0);
|
||||
}
|
||||
|
||||
double cellWidth = finalSize.Width / Columns;
|
||||
double cellHeight = finalSize.Height / Rows;
|
||||
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
int row = i / Columns;
|
||||
int column = i % Columns;
|
||||
|
||||
double x = column * cellWidth;
|
||||
double y = row * cellHeight;
|
||||
|
||||
Rect rect = new Rect(x, y, cellWidth, cellHeight);
|
||||
Children[i].Arrange(rect);
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,193 +4,208 @@ using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.UWP.Controls
|
||||
namespace Wino.Core.UWP.Controls;
|
||||
|
||||
public sealed partial class WinoAppTitleBar : UserControl
|
||||
{
|
||||
public sealed partial class WinoAppTitleBar : UserControl
|
||||
public event TypedEventHandler<WinoAppTitleBar, RoutedEventArgs> BackButtonClicked;
|
||||
|
||||
public static readonly DependencyProperty IsRenderingPaneVisibleProperty = DependencyProperty.Register(nameof(IsRenderingPaneVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty IsReaderNarrowedProperty = DependencyProperty.Register(nameof(IsReaderNarrowed), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnIsReaderNarrowedChanged));
|
||||
public static readonly DependencyProperty IsBackButtonVisibleProperty = DependencyProperty.Register(nameof(IsBackButtonVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty OpenPaneLengthProperty = DependencyProperty.Register(nameof(OpenPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0d, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty IsNavigationPaneOpenProperty = DependencyProperty.Register(nameof(IsNavigationPaneOpen), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty NavigationViewDisplayModeProperty = DependencyProperty.Register(nameof(NavigationViewDisplayMode), typeof(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode), typeof(WinoAppTitleBar), new PropertyMetadata(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty ShellFrameContentProperty = DependencyProperty.Register(nameof(ShellFrameContent), typeof(UIElement), typeof(WinoAppTitleBar), new PropertyMetadata(null, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty SystemReservedProperty = DependencyProperty.Register(nameof(SystemReserved), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty CoreWindowTextProperty = DependencyProperty.Register(nameof(CoreWindowText), typeof(string), typeof(WinoAppTitleBar), new PropertyMetadata(string.Empty, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty ReadingPaneLengthProperty = DependencyProperty.Register(nameof(ReadingPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(420d, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty ConnectionStatusProperty = DependencyProperty.Register(nameof(ConnectionStatus), typeof(WinoServerConnectionStatus), typeof(WinoAppTitleBar), new PropertyMetadata(WinoServerConnectionStatus.None, new PropertyChangedCallback(OnConnectionStatusChanged)));
|
||||
public static readonly DependencyProperty ReconnectCommandProperty = DependencyProperty.Register(nameof(ReconnectCommand), typeof(ICommand), typeof(WinoAppTitleBar), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty ShrinkShellContentOnExpansionProperty = DependencyProperty.Register(nameof(ShrinkShellContentOnExpansion), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty IsDragAreaProperty = DependencyProperty.Register(nameof(IsDragArea), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, new PropertyChangedCallback(OnIsDragAreaChanged)));
|
||||
public static readonly DependencyProperty IsShellFrameContentVisibleProperty = DependencyProperty.Register(nameof(IsShellFrameContentVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty IsMenuButtonVisibleProperty = DependencyProperty.Register(nameof(IsMenuButtonVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
|
||||
|
||||
public bool IsShellFrameContentVisible
|
||||
{
|
||||
public event TypedEventHandler<WinoAppTitleBar, RoutedEventArgs> BackButtonClicked;
|
||||
get { return (bool)GetValue(IsShellFrameContentVisibleProperty); }
|
||||
set { SetValue(IsShellFrameContentVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsRenderingPaneVisibleProperty = DependencyProperty.Register(nameof(IsRenderingPaneVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty IsReaderNarrowedProperty = DependencyProperty.Register(nameof(IsReaderNarrowed), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnIsReaderNarrowedChanged));
|
||||
public static readonly DependencyProperty IsBackButtonVisibleProperty = DependencyProperty.Register(nameof(IsBackButtonVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty OpenPaneLengthProperty = DependencyProperty.Register(nameof(OpenPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0d, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty IsNavigationPaneOpenProperty = DependencyProperty.Register(nameof(IsNavigationPaneOpen), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty NavigationViewDisplayModeProperty = DependencyProperty.Register(nameof(NavigationViewDisplayMode), typeof(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode), typeof(WinoAppTitleBar), new PropertyMetadata(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty ShellFrameContentProperty = DependencyProperty.Register(nameof(ShellFrameContent), typeof(UIElement), typeof(WinoAppTitleBar), new PropertyMetadata(null, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty SystemReservedProperty = DependencyProperty.Register(nameof(SystemReserved), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty CoreWindowTextProperty = DependencyProperty.Register(nameof(CoreWindowText), typeof(string), typeof(WinoAppTitleBar), new PropertyMetadata(string.Empty, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty ReadingPaneLengthProperty = DependencyProperty.Register(nameof(ReadingPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(420d, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty ConnectionStatusProperty = DependencyProperty.Register(nameof(ConnectionStatus), typeof(WinoServerConnectionStatus), typeof(WinoAppTitleBar), new PropertyMetadata(WinoServerConnectionStatus.None, new PropertyChangedCallback(OnConnectionStatusChanged)));
|
||||
public static readonly DependencyProperty ReconnectCommandProperty = DependencyProperty.Register(nameof(ReconnectCommand), typeof(ICommand), typeof(WinoAppTitleBar), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty ShrinkShellContentOnExpansionProperty = DependencyProperty.Register(nameof(ShrinkShellContentOnExpansion), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty IsDragAreaProperty = DependencyProperty.Register(nameof(IsDragArea), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, new PropertyChangedCallback(OnIsDragAreaChanged)));
|
||||
public static readonly DependencyProperty IsShellFrameContentVisibleProperty = DependencyProperty.Register(nameof(IsShellFrameContentVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty IsMenuButtonVisibleProperty = DependencyProperty.Register(nameof(IsMenuButtonVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(true));
|
||||
public ICommand ReconnectCommand
|
||||
{
|
||||
get { return (ICommand)GetValue(ReconnectCommandProperty); }
|
||||
set { SetValue(ReconnectCommandProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsShellFrameContentVisible
|
||||
public WinoServerConnectionStatus ConnectionStatus
|
||||
{
|
||||
get { return (WinoServerConnectionStatus)GetValue(ConnectionStatusProperty); }
|
||||
set { SetValue(ConnectionStatusProperty, value); }
|
||||
}
|
||||
|
||||
public string CoreWindowText
|
||||
{
|
||||
get { return (string)GetValue(CoreWindowTextProperty); }
|
||||
set { SetValue(CoreWindowTextProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsDragArea
|
||||
{
|
||||
get { return (bool)GetValue(IsDragAreaProperty); }
|
||||
set { SetValue(IsDragAreaProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
public double SystemReserved
|
||||
{
|
||||
get { return (double)GetValue(SystemReservedProperty); }
|
||||
set { SetValue(SystemReservedProperty, value); }
|
||||
}
|
||||
|
||||
public UIElement ShellFrameContent
|
||||
{
|
||||
get { return (UIElement)GetValue(ShellFrameContentProperty); }
|
||||
set { SetValue(ShellFrameContentProperty, value); }
|
||||
}
|
||||
|
||||
public Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode NavigationViewDisplayMode
|
||||
{
|
||||
get { return (Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode)GetValue(NavigationViewDisplayModeProperty); }
|
||||
set { SetValue(NavigationViewDisplayModeProperty, value); }
|
||||
}
|
||||
|
||||
public bool ShrinkShellContentOnExpansion
|
||||
{
|
||||
get { return (bool)GetValue(ShrinkShellContentOnExpansionProperty); }
|
||||
set { SetValue(ShrinkShellContentOnExpansionProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsNavigationPaneOpen
|
||||
{
|
||||
get { return (bool)GetValue(IsNavigationPaneOpenProperty); }
|
||||
set { SetValue(IsNavigationPaneOpenProperty, value); }
|
||||
}
|
||||
|
||||
public double OpenPaneLength
|
||||
{
|
||||
get { return (double)GetValue(OpenPaneLengthProperty); }
|
||||
set { SetValue(OpenPaneLengthProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool IsMenuButtonVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsMenuButtonVisibleProperty); }
|
||||
set { SetValue(IsMenuButtonVisibleProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
public bool IsBackButtonVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsBackButtonVisibleProperty); }
|
||||
set { SetValue(IsBackButtonVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsReaderNarrowed
|
||||
{
|
||||
get { return (bool)GetValue(IsReaderNarrowedProperty); }
|
||||
set { SetValue(IsReaderNarrowedProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsRenderingPaneVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsRenderingPaneVisibleProperty); }
|
||||
set { SetValue(IsRenderingPaneVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public double ReadingPaneLength
|
||||
{
|
||||
get { return (double)GetValue(ReadingPaneLengthProperty); }
|
||||
set { SetValue(ReadingPaneLengthProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnIsReaderNarrowedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
{
|
||||
get { return (bool)GetValue(IsShellFrameContentVisibleProperty); }
|
||||
set { SetValue(IsShellFrameContentVisibleProperty, value); }
|
||||
bar.DrawTitleBar();
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand ReconnectCommand
|
||||
private static void OnDrawingPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
{
|
||||
get { return (ICommand)GetValue(ReconnectCommandProperty); }
|
||||
set { SetValue(ReconnectCommandProperty, value); }
|
||||
bar.DrawTitleBar();
|
||||
}
|
||||
}
|
||||
|
||||
public WinoServerConnectionStatus ConnectionStatus
|
||||
private static void OnConnectionStatusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
{
|
||||
get { return (WinoServerConnectionStatus)GetValue(ConnectionStatusProperty); }
|
||||
set { SetValue(ConnectionStatusProperty, value); }
|
||||
bar.UpdateConnectionStatus();
|
||||
}
|
||||
}
|
||||
|
||||
public string CoreWindowText
|
||||
private static void OnIsDragAreaChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
{
|
||||
get { return (string)GetValue(CoreWindowTextProperty); }
|
||||
set { SetValue(CoreWindowTextProperty, value); }
|
||||
bar.SetDragArea();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDragArea
|
||||
private void SetDragArea()
|
||||
{
|
||||
if (IsDragArea)
|
||||
{
|
||||
get { return (bool)GetValue(IsDragAreaProperty); }
|
||||
set { SetValue(IsDragAreaProperty, value); }
|
||||
Window.Current.SetTitleBar(dragbar);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateConnectionStatus()
|
||||
{
|
||||
|
||||
public double SystemReserved
|
||||
}
|
||||
|
||||
private void DrawTitleBar()
|
||||
{
|
||||
UpdateLayout();
|
||||
|
||||
CoreWindowTitleTextBlock.Visibility = Visibility.Collapsed;
|
||||
ShellContentContainer.Width = double.NaN;
|
||||
ShellContentContainer.Margin = new Thickness(0, 0, 0, 0);
|
||||
ShellContentContainer.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
|
||||
EmptySpaceWidth.Width = new GridLength(1, GridUnitType.Star);
|
||||
|
||||
// Menu is not visible.
|
||||
if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal)
|
||||
{
|
||||
get { return (double)GetValue(SystemReservedProperty); }
|
||||
set { SetValue(SystemReservedProperty, value); }
|
||||
|
||||
}
|
||||
|
||||
public UIElement ShellFrameContent
|
||||
else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact)
|
||||
{
|
||||
get { return (UIElement)GetValue(ShellFrameContentProperty); }
|
||||
set { SetValue(ShellFrameContentProperty, value); }
|
||||
}
|
||||
// Icons are visible.
|
||||
|
||||
public Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode NavigationViewDisplayMode
|
||||
{
|
||||
get { return (Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode)GetValue(NavigationViewDisplayModeProperty); }
|
||||
set { SetValue(NavigationViewDisplayModeProperty, value); }
|
||||
}
|
||||
|
||||
public bool ShrinkShellContentOnExpansion
|
||||
{
|
||||
get { return (bool)GetValue(ShrinkShellContentOnExpansionProperty); }
|
||||
set { SetValue(ShrinkShellContentOnExpansionProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsNavigationPaneOpen
|
||||
{
|
||||
get { return (bool)GetValue(IsNavigationPaneOpenProperty); }
|
||||
set { SetValue(IsNavigationPaneOpenProperty, value); }
|
||||
}
|
||||
|
||||
public double OpenPaneLength
|
||||
{
|
||||
get { return (double)GetValue(OpenPaneLengthProperty); }
|
||||
set { SetValue(OpenPaneLengthProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool IsMenuButtonVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsMenuButtonVisibleProperty); }
|
||||
set { SetValue(IsMenuButtonVisibleProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
public bool IsBackButtonVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsBackButtonVisibleProperty); }
|
||||
set { SetValue(IsBackButtonVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsReaderNarrowed
|
||||
{
|
||||
get { return (bool)GetValue(IsReaderNarrowedProperty); }
|
||||
set { SetValue(IsReaderNarrowedProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsRenderingPaneVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsRenderingPaneVisibleProperty); }
|
||||
set { SetValue(IsRenderingPaneVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public double ReadingPaneLength
|
||||
{
|
||||
get { return (double)GetValue(ReadingPaneLengthProperty); }
|
||||
set { SetValue(ReadingPaneLengthProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnIsReaderNarrowedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
if (!IsReaderNarrowed && ShrinkShellContentOnExpansion)
|
||||
{
|
||||
bar.DrawTitleBar();
|
||||
ShellContentContainer.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
ShellContentContainer.Width = ReadingPaneLength;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnDrawingPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
if (IsNavigationPaneOpen)
|
||||
{
|
||||
bar.DrawTitleBar();
|
||||
}
|
||||
}
|
||||
CoreWindowTitleTextBlock.Visibility = Visibility.Visible;
|
||||
|
||||
private static void OnConnectionStatusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
{
|
||||
bar.UpdateConnectionStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnIsDragAreaChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
{
|
||||
bar.SetDragArea();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDragArea()
|
||||
{
|
||||
if (IsDragArea)
|
||||
{
|
||||
Window.Current.SetTitleBar(dragbar);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateConnectionStatus()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void DrawTitleBar()
|
||||
{
|
||||
UpdateLayout();
|
||||
|
||||
CoreWindowTitleTextBlock.Visibility = Visibility.Collapsed;
|
||||
ShellContentContainer.Width = double.NaN;
|
||||
ShellContentContainer.Margin = new Thickness(0, 0, 0, 0);
|
||||
ShellContentContainer.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
|
||||
EmptySpaceWidth.Width = new GridLength(1, GridUnitType.Star);
|
||||
|
||||
// Menu is not visible.
|
||||
if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal)
|
||||
{
|
||||
|
||||
}
|
||||
else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact)
|
||||
{
|
||||
// Icons are visible.
|
||||
// LMargin = OpenPaneLength - LeftMenuStackPanel
|
||||
ShellContentContainer.Margin = new Thickness(OpenPaneLength - LeftMenuStackPanel.ActualSize.X, 0, 0, 0);
|
||||
|
||||
if (!IsReaderNarrowed && ShrinkShellContentOnExpansion)
|
||||
{
|
||||
@@ -198,59 +213,43 @@ namespace Wino.Core.UWP.Controls
|
||||
ShellContentContainer.Width = ReadingPaneLength;
|
||||
}
|
||||
}
|
||||
else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded)
|
||||
else
|
||||
{
|
||||
if (IsNavigationPaneOpen)
|
||||
if (ShrinkShellContentOnExpansion)
|
||||
{
|
||||
CoreWindowTitleTextBlock.Visibility = Visibility.Visible;
|
||||
|
||||
// LMargin = OpenPaneLength - LeftMenuStackPanel
|
||||
ShellContentContainer.Margin = new Thickness(OpenPaneLength - LeftMenuStackPanel.ActualSize.X, 0, 0, 0);
|
||||
|
||||
if (!IsReaderNarrowed && ShrinkShellContentOnExpansion)
|
||||
{
|
||||
ShellContentContainer.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
ShellContentContainer.Width = ReadingPaneLength;
|
||||
}
|
||||
EmptySpaceWidth.Width = new GridLength(ReadingPaneLength, GridUnitType.Pixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ShrinkShellContentOnExpansion)
|
||||
{
|
||||
EmptySpaceWidth.Width = new GridLength(ReadingPaneLength, GridUnitType.Pixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmptySpaceWidth.Width = new GridLength(ReadingPaneLength, GridUnitType.Star);
|
||||
}
|
||||
EmptySpaceWidth.Width = new GridLength(ReadingPaneLength, GridUnitType.Star);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WinoAppTitleBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
public WinoAppTitleBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void BackClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BackButtonClicked?.Invoke(this, e);
|
||||
}
|
||||
private void BackClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BackButtonClicked?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void PaneClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsNavigationPaneOpen = !IsNavigationPaneOpen;
|
||||
}
|
||||
private void PaneClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsNavigationPaneOpen = !IsNavigationPaneOpen;
|
||||
}
|
||||
|
||||
private void TitlebarSizeChanged(object sender, SizeChangedEventArgs e) => DrawTitleBar();
|
||||
private void TitlebarSizeChanged(object sender, SizeChangedEventArgs e) => DrawTitleBar();
|
||||
|
||||
private void ReconnectClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Close the popup for reconnect button.
|
||||
ReconnectFlyout.Hide();
|
||||
private void ReconnectClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Close the popup for reconnect button.
|
||||
ReconnectFlyout.Hide();
|
||||
|
||||
// Execute the reconnect command.
|
||||
ReconnectCommand?.Execute(null);
|
||||
}
|
||||
// Execute the reconnect command.
|
||||
ReconnectCommand?.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,136 +1,135 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Wino.Core.UWP.Controls
|
||||
namespace Wino.Core.UWP.Controls;
|
||||
|
||||
public enum WinoIconGlyph
|
||||
{
|
||||
public enum WinoIconGlyph
|
||||
None,
|
||||
NewMail,
|
||||
Google,
|
||||
Microsoft,
|
||||
CustomServer,
|
||||
Archive,
|
||||
UnArchive,
|
||||
Reply,
|
||||
ReplyAll,
|
||||
LightEditor,
|
||||
DarkEditor,
|
||||
Delete,
|
||||
Move,
|
||||
Mail,
|
||||
Draft,
|
||||
Flag,
|
||||
ClearFlag,
|
||||
Folder,
|
||||
Forward,
|
||||
Inbox,
|
||||
MarkRead,
|
||||
MarkUnread,
|
||||
Send,
|
||||
Save,
|
||||
Sync,
|
||||
MultiSelect,
|
||||
Zoom,
|
||||
Pin,
|
||||
UnPin,
|
||||
Ignore,
|
||||
Star,
|
||||
CreateFolder,
|
||||
More,
|
||||
Find,
|
||||
SpecialFolderInbox,
|
||||
SpecialFolderStarred,
|
||||
SpecialFolderImportant,
|
||||
SpecialFolderSent,
|
||||
SpecialFolderDraft,
|
||||
SpecialFolderArchive,
|
||||
SpecialFolderDeleted,
|
||||
SpecialFolderJunk,
|
||||
SpecialFolderChat,
|
||||
SpecialFolderCategory,
|
||||
SpecialFolderUnread,
|
||||
SpecialFolderForums,
|
||||
SpecialFolderUpdated,
|
||||
SpecialFolderPersonal,
|
||||
SpecialFolderPromotions,
|
||||
SpecialFolderSocial,
|
||||
SpecialFolderOther,
|
||||
SpecialFolderMore,
|
||||
TurnOfNotifications,
|
||||
EmptyFolder,
|
||||
Rename,
|
||||
DontSync,
|
||||
Attachment,
|
||||
SortTextDesc,
|
||||
SortLinesDesc,
|
||||
Certificate,
|
||||
OpenInNewWindow,
|
||||
Blocked,
|
||||
Message,
|
||||
New,
|
||||
IMAP,
|
||||
Print,
|
||||
Calendar,
|
||||
CalendarToday,
|
||||
CalendarDay,
|
||||
CalendarWeek,
|
||||
CalendarWorkWeek,
|
||||
CalendarMonth,
|
||||
CalendarYear,
|
||||
WeatherBlow,
|
||||
WeatherCloudy,
|
||||
WeatherSunny,
|
||||
WeatherRainy,
|
||||
WeatherSnowy,
|
||||
WeatherSnowShowerAtNight,
|
||||
WeatherThunderstorm,
|
||||
CalendarEventRepeat,
|
||||
CalendarEventMuiltiDay,
|
||||
CalendarError,
|
||||
Reminder,
|
||||
CalendarAttendee,
|
||||
CalendarAttendees,
|
||||
CalendarSync,
|
||||
EventRespond,
|
||||
EventAccept,
|
||||
EventTentative,
|
||||
EventDecline,
|
||||
EventReminder,
|
||||
EventEditSeries,
|
||||
EventJoinOnline,
|
||||
ViewMessageSource,
|
||||
Apple,
|
||||
Yahoo
|
||||
}
|
||||
|
||||
public partial class WinoFontIcon : FontIcon
|
||||
{
|
||||
public WinoIconGlyph Icon
|
||||
{
|
||||
None,
|
||||
NewMail,
|
||||
Google,
|
||||
Microsoft,
|
||||
CustomServer,
|
||||
Archive,
|
||||
UnArchive,
|
||||
Reply,
|
||||
ReplyAll,
|
||||
LightEditor,
|
||||
DarkEditor,
|
||||
Delete,
|
||||
Move,
|
||||
Mail,
|
||||
Draft,
|
||||
Flag,
|
||||
ClearFlag,
|
||||
Folder,
|
||||
Forward,
|
||||
Inbox,
|
||||
MarkRead,
|
||||
MarkUnread,
|
||||
Send,
|
||||
Save,
|
||||
Sync,
|
||||
MultiSelect,
|
||||
Zoom,
|
||||
Pin,
|
||||
UnPin,
|
||||
Ignore,
|
||||
Star,
|
||||
CreateFolder,
|
||||
More,
|
||||
Find,
|
||||
SpecialFolderInbox,
|
||||
SpecialFolderStarred,
|
||||
SpecialFolderImportant,
|
||||
SpecialFolderSent,
|
||||
SpecialFolderDraft,
|
||||
SpecialFolderArchive,
|
||||
SpecialFolderDeleted,
|
||||
SpecialFolderJunk,
|
||||
SpecialFolderChat,
|
||||
SpecialFolderCategory,
|
||||
SpecialFolderUnread,
|
||||
SpecialFolderForums,
|
||||
SpecialFolderUpdated,
|
||||
SpecialFolderPersonal,
|
||||
SpecialFolderPromotions,
|
||||
SpecialFolderSocial,
|
||||
SpecialFolderOther,
|
||||
SpecialFolderMore,
|
||||
TurnOfNotifications,
|
||||
EmptyFolder,
|
||||
Rename,
|
||||
DontSync,
|
||||
Attachment,
|
||||
SortTextDesc,
|
||||
SortLinesDesc,
|
||||
Certificate,
|
||||
OpenInNewWindow,
|
||||
Blocked,
|
||||
Message,
|
||||
New,
|
||||
IMAP,
|
||||
Print,
|
||||
Calendar,
|
||||
CalendarToday,
|
||||
CalendarDay,
|
||||
CalendarWeek,
|
||||
CalendarWorkWeek,
|
||||
CalendarMonth,
|
||||
CalendarYear,
|
||||
WeatherBlow,
|
||||
WeatherCloudy,
|
||||
WeatherSunny,
|
||||
WeatherRainy,
|
||||
WeatherSnowy,
|
||||
WeatherSnowShowerAtNight,
|
||||
WeatherThunderstorm,
|
||||
CalendarEventRepeat,
|
||||
CalendarEventMuiltiDay,
|
||||
CalendarError,
|
||||
Reminder,
|
||||
CalendarAttendee,
|
||||
CalendarAttendees,
|
||||
CalendarSync,
|
||||
EventRespond,
|
||||
EventAccept,
|
||||
EventTentative,
|
||||
EventDecline,
|
||||
EventReminder,
|
||||
EventEditSeries,
|
||||
EventJoinOnline,
|
||||
ViewMessageSource,
|
||||
Apple,
|
||||
Yahoo
|
||||
get { return (WinoIconGlyph)GetValue(IconProperty); }
|
||||
set { SetValue(IconProperty, value); }
|
||||
}
|
||||
|
||||
public partial class WinoFontIcon : FontIcon
|
||||
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(WinoIconGlyph), typeof(WinoFontIcon), new PropertyMetadata(WinoIconGlyph.Flag, OnIconChanged));
|
||||
|
||||
public WinoFontIcon()
|
||||
{
|
||||
public WinoIconGlyph Icon
|
||||
{
|
||||
get { return (WinoIconGlyph)GetValue(IconProperty); }
|
||||
set { SetValue(IconProperty, value); }
|
||||
}
|
||||
FontFamily = new Windows.UI.Xaml.Media.FontFamily("ms-appx:///Wino.Core.UWP/Assets/WinoIcons.ttf#WinoIcons");
|
||||
FontSize = 32;
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(WinoIconGlyph), typeof(WinoFontIcon), new PropertyMetadata(WinoIconGlyph.Flag, OnIconChanged));
|
||||
|
||||
public WinoFontIcon()
|
||||
private static void OnIconChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoFontIcon fontIcon)
|
||||
{
|
||||
FontFamily = new Windows.UI.Xaml.Media.FontFamily("ms-appx:///Wino.Core.UWP/Assets/WinoIcons.ttf#WinoIcons");
|
||||
FontSize = 32;
|
||||
fontIcon.UpdateGlyph();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnIconChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoFontIcon fontIcon)
|
||||
{
|
||||
fontIcon.UpdateGlyph();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGlyph()
|
||||
{
|
||||
Glyph = ControlConstants.WinoIconFontDictionary[Icon];
|
||||
}
|
||||
private void UpdateGlyph()
|
||||
{
|
||||
Glyph = ControlConstants.WinoIconFontDictionary[Icon];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Core.UWP.Controls;
|
||||
|
||||
namespace Wino.Controls
|
||||
namespace Wino.Controls;
|
||||
|
||||
public partial class WinoFontIconSource : Microsoft.UI.Xaml.Controls.FontIconSource
|
||||
{
|
||||
public partial class WinoFontIconSource : Microsoft.UI.Xaml.Controls.FontIconSource
|
||||
public WinoIconGlyph Icon
|
||||
{
|
||||
public WinoIconGlyph Icon
|
||||
{
|
||||
get { return (WinoIconGlyph)GetValue(IconProperty); }
|
||||
set { SetValue(IconProperty, value); }
|
||||
}
|
||||
get { return (WinoIconGlyph)GetValue(IconProperty); }
|
||||
set { SetValue(IconProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(WinoIconGlyph), typeof(WinoFontIconSource), new PropertyMetadata(WinoIconGlyph.Flag, OnIconChanged));
|
||||
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(WinoIconGlyph), typeof(WinoFontIconSource), new PropertyMetadata(WinoIconGlyph.Flag, OnIconChanged));
|
||||
|
||||
public WinoFontIconSource()
|
||||
{
|
||||
FontFamily = new Windows.UI.Xaml.Media.FontFamily("ms-appx:///Assets/WinoIcons.ttf#WinoIcons");
|
||||
FontSize = 32;
|
||||
}
|
||||
public WinoFontIconSource()
|
||||
{
|
||||
FontFamily = new Windows.UI.Xaml.Media.FontFamily("ms-appx:///Assets/WinoIcons.ttf#WinoIcons");
|
||||
FontSize = 32;
|
||||
}
|
||||
|
||||
private static void OnIconChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
private static void OnIconChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoFontIconSource fontIcon)
|
||||
{
|
||||
if (obj is WinoFontIconSource fontIcon)
|
||||
{
|
||||
fontIcon.UpdateGlyph();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGlyph()
|
||||
{
|
||||
Glyph = ControlConstants.WinoIconFontDictionary[Icon];
|
||||
fontIcon.UpdateGlyph();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGlyph()
|
||||
{
|
||||
Glyph = ControlConstants.WinoIconFontDictionary[Icon];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,87 +5,86 @@ using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.UWP.Controls
|
||||
namespace Wino.Core.UWP.Controls;
|
||||
|
||||
public partial class WinoInfoBar : InfoBar
|
||||
{
|
||||
public partial class WinoInfoBar : InfoBar
|
||||
public static readonly DependencyProperty AnimationTypeProperty = DependencyProperty.Register(nameof(AnimationType), typeof(InfoBarAnimationType), typeof(WinoInfoBar), new PropertyMetadata(InfoBarAnimationType.SlideFromRightToLeft));
|
||||
public static readonly DependencyProperty DismissIntervalProperty = DependencyProperty.Register(nameof(DismissInterval), typeof(int), typeof(WinoInfoBar), new PropertyMetadata(5, new PropertyChangedCallback(OnDismissIntervalChanged)));
|
||||
|
||||
public InfoBarAnimationType AnimationType
|
||||
{
|
||||
public static readonly DependencyProperty AnimationTypeProperty = DependencyProperty.Register(nameof(AnimationType), typeof(InfoBarAnimationType), typeof(WinoInfoBar), new PropertyMetadata(InfoBarAnimationType.SlideFromRightToLeft));
|
||||
public static readonly DependencyProperty DismissIntervalProperty = DependencyProperty.Register(nameof(DismissInterval), typeof(int), typeof(WinoInfoBar), new PropertyMetadata(5, new PropertyChangedCallback(OnDismissIntervalChanged)));
|
||||
get { return (InfoBarAnimationType)GetValue(AnimationTypeProperty); }
|
||||
set { SetValue(AnimationTypeProperty, value); }
|
||||
}
|
||||
|
||||
public InfoBarAnimationType AnimationType
|
||||
public int DismissInterval
|
||||
{
|
||||
get { return (int)GetValue(DismissIntervalProperty); }
|
||||
set { SetValue(DismissIntervalProperty, value); }
|
||||
}
|
||||
|
||||
private readonly DispatcherTimer _dispatcherTimer = new DispatcherTimer();
|
||||
|
||||
public WinoInfoBar()
|
||||
{
|
||||
RegisterPropertyChangedCallback(IsOpenProperty, IsOpenChanged);
|
||||
|
||||
_dispatcherTimer.Interval = TimeSpan.FromSeconds(DismissInterval);
|
||||
}
|
||||
|
||||
private static void OnDismissIntervalChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoInfoBar bar)
|
||||
{
|
||||
get { return (InfoBarAnimationType)GetValue(AnimationTypeProperty); }
|
||||
set { SetValue(AnimationTypeProperty, value); }
|
||||
bar.UpdateInterval(bar.DismissInterval);
|
||||
}
|
||||
}
|
||||
|
||||
public int DismissInterval
|
||||
private void UpdateInterval(int seconds) => _dispatcherTimer.Interval = TimeSpan.FromSeconds(seconds);
|
||||
|
||||
private async void IsOpenChanged(DependencyObject sender, DependencyProperty dp)
|
||||
{
|
||||
if (sender is WinoInfoBar control)
|
||||
{
|
||||
get { return (int)GetValue(DismissIntervalProperty); }
|
||||
set { SetValue(DismissIntervalProperty, value); }
|
||||
}
|
||||
// Message shown.
|
||||
if (!control.IsOpen) return;
|
||||
|
||||
private readonly DispatcherTimer _dispatcherTimer = new DispatcherTimer();
|
||||
Opacity = 1;
|
||||
|
||||
public WinoInfoBar()
|
||||
{
|
||||
RegisterPropertyChangedCallback(IsOpenProperty, IsOpenChanged);
|
||||
|
||||
_dispatcherTimer.Interval = TimeSpan.FromSeconds(DismissInterval);
|
||||
}
|
||||
|
||||
private static void OnDismissIntervalChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoInfoBar bar)
|
||||
{
|
||||
bar.UpdateInterval(bar.DismissInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateInterval(int seconds) => _dispatcherTimer.Interval = TimeSpan.FromSeconds(seconds);
|
||||
|
||||
private async void IsOpenChanged(DependencyObject sender, DependencyProperty dp)
|
||||
{
|
||||
if (sender is WinoInfoBar control)
|
||||
{
|
||||
// Message shown.
|
||||
if (!control.IsOpen) return;
|
||||
|
||||
Opacity = 1;
|
||||
|
||||
_dispatcherTimer.Stop();
|
||||
|
||||
_dispatcherTimer.Tick -= TimerTick;
|
||||
_dispatcherTimer.Tick += TimerTick;
|
||||
|
||||
_dispatcherTimer.Start();
|
||||
|
||||
// Slide from right.
|
||||
if (AnimationType == InfoBarAnimationType.SlideFromRightToLeft)
|
||||
{
|
||||
await AnimationBuilder.Create().Translation(new Vector3(0, 0, 0), new Vector3(150, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
|
||||
}
|
||||
else if (AnimationType == InfoBarAnimationType.SlideFromBottomToTop)
|
||||
{
|
||||
await AnimationBuilder.Create().Translation(new Vector3(0, 0, 0), new Vector3(0, 50, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void TimerTick(object sender, object e)
|
||||
{
|
||||
_dispatcherTimer.Stop();
|
||||
_dispatcherTimer.Tick -= TimerTick;
|
||||
|
||||
_dispatcherTimer.Tick -= TimerTick;
|
||||
_dispatcherTimer.Tick += TimerTick;
|
||||
|
||||
_dispatcherTimer.Start();
|
||||
|
||||
// Slide from right.
|
||||
if (AnimationType == InfoBarAnimationType.SlideFromRightToLeft)
|
||||
{
|
||||
await AnimationBuilder.Create().Translation(new Vector3((float)ActualWidth, 0, 0), new Vector3(0, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
|
||||
await AnimationBuilder.Create().Translation(new Vector3(0, 0, 0), new Vector3(150, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
|
||||
}
|
||||
else if (AnimationType == InfoBarAnimationType.SlideFromBottomToTop)
|
||||
{
|
||||
await AnimationBuilder.Create().Translation(new Vector3(0, (float)ActualHeight, 0), new Vector3(0, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
|
||||
await AnimationBuilder.Create().Translation(new Vector3(0, 0, 0), new Vector3(0, 50, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
|
||||
}
|
||||
|
||||
IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void TimerTick(object sender, object e)
|
||||
{
|
||||
_dispatcherTimer.Stop();
|
||||
_dispatcherTimer.Tick -= TimerTick;
|
||||
|
||||
if (AnimationType == InfoBarAnimationType.SlideFromRightToLeft)
|
||||
{
|
||||
await AnimationBuilder.Create().Translation(new Vector3((float)ActualWidth, 0, 0), new Vector3(0, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
|
||||
}
|
||||
else if (AnimationType == InfoBarAnimationType.SlideFromBottomToTop)
|
||||
{
|
||||
await AnimationBuilder.Create().Translation(new Vector3(0, (float)ActualHeight, 0), new Vector3(0, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this);
|
||||
}
|
||||
|
||||
IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,45 +3,44 @@ using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Hosting;
|
||||
|
||||
namespace Wino.Core.UWP.Controls
|
||||
namespace Wino.Core.UWP.Controls;
|
||||
|
||||
public partial class WinoNavigationViewItem : NavigationViewItem
|
||||
{
|
||||
public partial class WinoNavigationViewItem : NavigationViewItem
|
||||
public bool IsDraggingItemOver
|
||||
{
|
||||
public bool IsDraggingItemOver
|
||||
get { return (bool)GetValue(IsDraggingItemOverProperty); }
|
||||
set { SetValue(IsDraggingItemOverProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsDraggingItemOverProperty = DependencyProperty.Register(nameof(IsDraggingItemOver), typeof(bool), typeof(WinoNavigationViewItem), new PropertyMetadata(false, OnIsDraggingItemOverChanged));
|
||||
|
||||
private static void OnIsDraggingItemOverChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoNavigationViewItem control)
|
||||
control.UpdateDragEnterState();
|
||||
}
|
||||
|
||||
private void UpdateDragEnterState()
|
||||
{
|
||||
// TODO: Add animation. Maybe after overriding DragUI in shell?
|
||||
|
||||
//if (IsDraggingItemOver)
|
||||
//{
|
||||
// ScaleAnimation(new System.Numerics.Vector3(1.2f, 1.2f, 1.2f));
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// ScaleAnimation(new System.Numerics.Vector3(1f, 1f, 1f));
|
||||
//}
|
||||
}
|
||||
|
||||
private void ScaleAnimation(Vector3 vector)
|
||||
{
|
||||
if (Content is UIElement content)
|
||||
{
|
||||
get { return (bool)GetValue(IsDraggingItemOverProperty); }
|
||||
set { SetValue(IsDraggingItemOverProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsDraggingItemOverProperty = DependencyProperty.Register(nameof(IsDraggingItemOver), typeof(bool), typeof(WinoNavigationViewItem), new PropertyMetadata(false, OnIsDraggingItemOverChanged));
|
||||
|
||||
private static void OnIsDraggingItemOverChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoNavigationViewItem control)
|
||||
control.UpdateDragEnterState();
|
||||
}
|
||||
|
||||
private void UpdateDragEnterState()
|
||||
{
|
||||
// TODO: Add animation. Maybe after overriding DragUI in shell?
|
||||
|
||||
//if (IsDraggingItemOver)
|
||||
//{
|
||||
// ScaleAnimation(new System.Numerics.Vector3(1.2f, 1.2f, 1.2f));
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// ScaleAnimation(new System.Numerics.Vector3(1f, 1f, 1f));
|
||||
//}
|
||||
}
|
||||
|
||||
private void ScaleAnimation(Vector3 vector)
|
||||
{
|
||||
if (Content is UIElement content)
|
||||
{
|
||||
var visual = ElementCompositionPreview.GetElementVisual(content);
|
||||
visual.Scale = vector;
|
||||
}
|
||||
var visual = ElementCompositionPreview.GetElementVisual(content);
|
||||
visual.Scale = vector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,25 @@ using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Wino.Converters
|
||||
{
|
||||
public partial class GridLengthConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is double doubleValue)
|
||||
{
|
||||
return new GridLength(doubleValue);
|
||||
}
|
||||
return new GridLength(1, GridUnitType.Auto);
|
||||
}
|
||||
namespace Wino.Converters;
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
public partial class GridLengthConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is double doubleValue)
|
||||
{
|
||||
if (value is GridLength gridLength)
|
||||
{
|
||||
return gridLength.Value;
|
||||
}
|
||||
return 0.0;
|
||||
return new GridLength(doubleValue);
|
||||
}
|
||||
return new GridLength(1, GridUnitType.Auto);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is GridLength gridLength)
|
||||
{
|
||||
return gridLength.Value;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Wino.Converters
|
||||
namespace Wino.Converters;
|
||||
|
||||
public partial class ReverseBooleanConverter : IValueConverter
|
||||
{
|
||||
public partial class ReverseBooleanConverter : IValueConverter
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool boolval)
|
||||
return !boolval;
|
||||
if (value is bool boolval)
|
||||
return !boolval;
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,17 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Wino.Converters
|
||||
{
|
||||
public partial class ReverseBooleanToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
return ((bool)value) ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
namespace Wino.Converters;
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public partial class ReverseBooleanToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
return ((bool)value) ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Wino.Core.UWP
|
||||
namespace Wino.Core.UWP;
|
||||
|
||||
public partial class CoreGeneric : ResourceDictionary
|
||||
{
|
||||
public partial class CoreGeneric : ResourceDictionary
|
||||
{
|
||||
public CoreGeneric() => InitializeComponent();
|
||||
}
|
||||
public CoreGeneric() => InitializeComponent();
|
||||
}
|
||||
|
||||
@@ -6,46 +6,45 @@ using Wino.Core.UWP.Services;
|
||||
using Wino.Core.ViewModels;
|
||||
using Wino.Services;
|
||||
|
||||
namespace Wino.Core.UWP
|
||||
namespace Wino.Core.UWP;
|
||||
|
||||
public static class CoreUWPContainerSetup
|
||||
{
|
||||
public static class CoreUWPContainerSetup
|
||||
public static void RegisterCoreUWPServices(this IServiceCollection services)
|
||||
{
|
||||
public static void RegisterCoreUWPServices(this IServiceCollection services)
|
||||
{
|
||||
var serverConnectionManager = new WinoServerConnectionManager();
|
||||
var serverConnectionManager = new WinoServerConnectionManager();
|
||||
|
||||
services.AddSingleton<IWinoServerConnectionManager>(serverConnectionManager);
|
||||
services.AddSingleton<IWinoServerConnectionManager<AppServiceConnection>>(serverConnectionManager);
|
||||
services.AddSingleton<IApplicationResourceManager<ResourceDictionary>, ApplicationResourceManager>();
|
||||
services.AddSingleton<IWinoServerConnectionManager>(serverConnectionManager);
|
||||
services.AddSingleton<IWinoServerConnectionManager<AppServiceConnection>>(serverConnectionManager);
|
||||
services.AddSingleton<IApplicationResourceManager<ResourceDictionary>, ApplicationResourceManager>();
|
||||
|
||||
services.AddSingleton<IUnderlyingThemeService, UnderlyingThemeService>();
|
||||
services.AddSingleton<INativeAppService, NativeAppService>();
|
||||
services.AddSingleton<IStoreManagementService, StoreManagementService>();
|
||||
services.AddSingleton<IBackgroundTaskService, BackgroundTaskService>();
|
||||
services.AddSingleton<IPreferencesService, PreferencesService>();
|
||||
services.AddSingleton<IThemeService, ThemeService>();
|
||||
services.AddSingleton<IStatePersistanceService, StatePersistenceService>();
|
||||
services.AddSingleton<IUnderlyingThemeService, UnderlyingThemeService>();
|
||||
services.AddSingleton<INativeAppService, NativeAppService>();
|
||||
services.AddSingleton<IStoreManagementService, StoreManagementService>();
|
||||
services.AddSingleton<IBackgroundTaskService, BackgroundTaskService>();
|
||||
services.AddSingleton<IPreferencesService, PreferencesService>();
|
||||
services.AddSingleton<IThemeService, ThemeService>();
|
||||
services.AddSingleton<IStatePersistanceService, StatePersistenceService>();
|
||||
|
||||
services.AddSingleton<IDialogServiceBase, DialogServiceBase>();
|
||||
services.AddTransient<IConfigurationService, ConfigurationService>();
|
||||
services.AddTransient<IFileService, FileService>();
|
||||
services.AddTransient<IStoreRatingService, StoreRatingService>();
|
||||
services.AddTransient<IKeyPressService, KeyPressService>();
|
||||
services.AddTransient<INotificationBuilder, NotificationBuilder>();
|
||||
services.AddTransient<IClipboardService, ClipboardService>();
|
||||
services.AddTransient<IStartupBehaviorService, StartupBehaviorService>();
|
||||
services.AddSingleton<IPrintService, PrintService>();
|
||||
services.AddSingleton<IDialogServiceBase, DialogServiceBase>();
|
||||
services.AddTransient<IConfigurationService, ConfigurationService>();
|
||||
services.AddTransient<IFileService, FileService>();
|
||||
services.AddTransient<IStoreRatingService, StoreRatingService>();
|
||||
services.AddTransient<IKeyPressService, KeyPressService>();
|
||||
services.AddTransient<INotificationBuilder, NotificationBuilder>();
|
||||
services.AddTransient<IClipboardService, ClipboardService>();
|
||||
services.AddTransient<IStartupBehaviorService, StartupBehaviorService>();
|
||||
services.AddSingleton<IPrintService, PrintService>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterCoreViewModels(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient(typeof(SettingsDialogViewModel));
|
||||
services.AddTransient(typeof(PersonalizationPageViewModel));
|
||||
services.AddTransient(typeof(SettingOptionsPageViewModel));
|
||||
services.AddTransient(typeof(AboutPageViewModel));
|
||||
services.AddTransient(typeof(SettingsPageViewModel));
|
||||
services.AddTransient(typeof(ManageAccountsPagePageViewModel));
|
||||
}
|
||||
public static void RegisterCoreViewModels(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient(typeof(SettingsDialogViewModel));
|
||||
services.AddTransient(typeof(PersonalizationPageViewModel));
|
||||
services.AddTransient(typeof(SettingOptionsPageViewModel));
|
||||
services.AddTransient(typeof(AboutPageViewModel));
|
||||
services.AddTransient(typeof(SettingsPageViewModel));
|
||||
services.AddTransient(typeof(ManageAccountsPagePageViewModel));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,67 +5,66 @@ using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Dialogs
|
||||
namespace Wino.Dialogs;
|
||||
|
||||
public sealed partial class AccountCreationDialog : ContentDialog, IAccountCreationDialog
|
||||
{
|
||||
public sealed partial class AccountCreationDialog : ContentDialog, IAccountCreationDialog
|
||||
private TaskCompletionSource<bool> dialogOpened = new TaskCompletionSource<bool>();
|
||||
public CancellationTokenSource CancellationTokenSource { get; private set; }
|
||||
|
||||
public AccountCreationDialogState State
|
||||
{
|
||||
private TaskCompletionSource<bool> dialogOpened = new TaskCompletionSource<bool>();
|
||||
public CancellationTokenSource CancellationTokenSource { get; private set; }
|
||||
get { return (AccountCreationDialogState)GetValue(StateProperty); }
|
||||
set { SetValue(StateProperty, value); }
|
||||
}
|
||||
|
||||
public AccountCreationDialogState State
|
||||
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(AccountCreationDialog), new PropertyMetadata(AccountCreationDialogState.Idle));
|
||||
|
||||
public AccountCreationDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
// Prevent users from dismissing it by ESC key.
|
||||
public void DialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args)
|
||||
{
|
||||
if (args.Result == ContentDialogResult.None)
|
||||
{
|
||||
get { return (AccountCreationDialogState)GetValue(StateProperty); }
|
||||
set { SetValue(StateProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(AccountCreationDialog), new PropertyMetadata(AccountCreationDialogState.Idle));
|
||||
|
||||
public AccountCreationDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
// Prevent users from dismissing it by ESC key.
|
||||
public void DialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args)
|
||||
{
|
||||
if (args.Result == ContentDialogResult.None)
|
||||
{
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Complete(bool cancel)
|
||||
{
|
||||
State = cancel ? AccountCreationDialogState.Canceled : AccountCreationDialogState.Completed;
|
||||
|
||||
// Unregister from closing event.
|
||||
Closing -= DialogClosing;
|
||||
|
||||
if (cancel && !CancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
CancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void CancelClicked(object sender, System.EventArgs e) => Complete(true);
|
||||
|
||||
public async Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource)
|
||||
{
|
||||
CancellationTokenSource = cancellationTokenSource;
|
||||
|
||||
Opened += DialogOpened;
|
||||
_ = ShowAsync();
|
||||
|
||||
await dialogOpened.Task;
|
||||
}
|
||||
|
||||
private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args)
|
||||
{
|
||||
Opened -= DialogOpened;
|
||||
|
||||
dialogOpened?.SetResult(true);
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Complete(bool cancel)
|
||||
{
|
||||
State = cancel ? AccountCreationDialogState.Canceled : AccountCreationDialogState.Completed;
|
||||
|
||||
// Unregister from closing event.
|
||||
Closing -= DialogClosing;
|
||||
|
||||
if (cancel && !CancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
CancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void CancelClicked(object sender, System.EventArgs e) => Complete(true);
|
||||
|
||||
public async Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource)
|
||||
{
|
||||
CancellationTokenSource = cancellationTokenSource;
|
||||
|
||||
Opened += DialogOpened;
|
||||
_ = ShowAsync();
|
||||
|
||||
await dialogOpened.Task;
|
||||
}
|
||||
|
||||
private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args)
|
||||
{
|
||||
Opened -= DialogOpened;
|
||||
|
||||
dialogOpened?.SetResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Dialogs
|
||||
namespace Wino.Dialogs;
|
||||
|
||||
public sealed partial class AccountEditDialog : ContentDialog
|
||||
{
|
||||
public sealed partial class AccountEditDialog : ContentDialog
|
||||
public MailAccount Account { get; private set; }
|
||||
public bool IsSaved { get; set; }
|
||||
|
||||
public AccountEditDialog(MailAccount account)
|
||||
{
|
||||
public MailAccount Account { get; private set; }
|
||||
public bool IsSaved { get; set; }
|
||||
InitializeComponent();
|
||||
Account = account;
|
||||
}
|
||||
|
||||
public AccountEditDialog(MailAccount account)
|
||||
{
|
||||
InitializeComponent();
|
||||
Account = account;
|
||||
}
|
||||
|
||||
private void SaveClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
IsSaved = true;
|
||||
}
|
||||
private void SaveClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
IsSaved = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,25 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Dialogs
|
||||
namespace Wino.Dialogs;
|
||||
|
||||
public sealed partial class AccountPickerDialog : ContentDialog
|
||||
{
|
||||
public sealed partial class AccountPickerDialog : ContentDialog
|
||||
public MailAccount PickedAccount { get; set; }
|
||||
|
||||
public List<MailAccount> AvailableAccounts { get; set; }
|
||||
|
||||
public AccountPickerDialog(List<MailAccount> availableAccounts)
|
||||
{
|
||||
public MailAccount PickedAccount { get; set; }
|
||||
AvailableAccounts = availableAccounts;
|
||||
|
||||
public List<MailAccount> AvailableAccounts { get; set; }
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public AccountPickerDialog(List<MailAccount> availableAccounts)
|
||||
{
|
||||
AvailableAccounts = availableAccounts;
|
||||
private void AccountClicked(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
PickedAccount = e.ClickedItem as MailAccount;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void AccountClicked(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
PickedAccount = e.ClickedItem as MailAccount;
|
||||
|
||||
Hide();
|
||||
}
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Dialogs
|
||||
namespace Wino.Dialogs;
|
||||
|
||||
public partial class CustomMessageDialogInformationContainer : ObservableObject
|
||||
{
|
||||
public partial class CustomMessageDialogInformationContainer : ObservableObject
|
||||
[ObservableProperty]
|
||||
public partial bool IsDontAskChecked { get; set; }
|
||||
|
||||
public CustomMessageDialogInformationContainer(string title, string description, WinoCustomMessageDialogIcon icon, bool isDontAskAgainEnabled)
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial bool IsDontAskChecked { get; set; }
|
||||
|
||||
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; }
|
||||
Title = title;
|
||||
Description = description;
|
||||
Icon = icon;
|
||||
IsDontAskAgainEnabled = isDontAskAgainEnabled;
|
||||
}
|
||||
|
||||
public string Title { get; }
|
||||
public string Description { get; }
|
||||
public WinoCustomMessageDialogIcon Icon { get; }
|
||||
public bool IsDontAskAgainEnabled { get; }
|
||||
}
|
||||
|
||||
@@ -6,60 +6,59 @@ using Windows.UI.Xaml.Media;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Dialogs
|
||||
namespace Wino.Dialogs;
|
||||
|
||||
public sealed partial class CustomThemeBuilderDialog : ContentDialog
|
||||
{
|
||||
public sealed partial class CustomThemeBuilderDialog : ContentDialog
|
||||
public byte[] WallpaperData { get; private set; }
|
||||
public string AccentColor { get; private set; }
|
||||
|
||||
private IThemeService _themeService;
|
||||
|
||||
public CustomThemeBuilderDialog()
|
||||
{
|
||||
public byte[] WallpaperData { get; private set; }
|
||||
public string AccentColor { get; private set; }
|
||||
InitializeComponent();
|
||||
|
||||
private IThemeService _themeService;
|
||||
_themeService = WinoApplication.Current.Services.GetService<IThemeService>();
|
||||
}
|
||||
|
||||
public CustomThemeBuilderDialog()
|
||||
private async void ApplyClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
if (Array.Empty<byte>() == WallpaperData)
|
||||
return;
|
||||
|
||||
var deferal = args.GetDeferral();
|
||||
|
||||
try
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_themeService = WinoApplication.Current.Services.GetService<IThemeService>();
|
||||
await _themeService.CreateNewCustomThemeAsync(ThemeNameBox.Text, AccentColor, WallpaperData);
|
||||
}
|
||||
|
||||
private async void ApplyClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (Array.Empty<byte>() == WallpaperData)
|
||||
return;
|
||||
|
||||
var deferal = args.GetDeferral();
|
||||
|
||||
try
|
||||
{
|
||||
await _themeService.CreateNewCustomThemeAsync(ThemeNameBox.Text, AccentColor, WallpaperData);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ErrorTextBlock.Text = exception.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
deferal.Complete();
|
||||
}
|
||||
ErrorTextBlock.Text = exception.Message;
|
||||
}
|
||||
|
||||
private async void BrowseWallpaperClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
finally
|
||||
{
|
||||
var dialogService = WinoApplication.Current.Services.GetService<IMailDialogService>();
|
||||
|
||||
var pickedFileData = await dialogService.PickWindowsFileContentAsync(".jpg", ".png");
|
||||
|
||||
if (pickedFileData == Array.Empty<byte>()) return;
|
||||
|
||||
IsPrimaryButtonEnabled = true;
|
||||
|
||||
WallpaperData = pickedFileData;
|
||||
}
|
||||
|
||||
private void PickerColorChanged(Microsoft.UI.Xaml.Controls.ColorPicker sender, Microsoft.UI.Xaml.Controls.ColorChangedEventArgs args)
|
||||
{
|
||||
PreviewAccentColorGrid.Background = new SolidColorBrush(args.NewColor);
|
||||
AccentColor = args.NewColor.ToHex();
|
||||
deferal.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
private async void BrowseWallpaperClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
var dialogService = WinoApplication.Current.Services.GetService<IMailDialogService>();
|
||||
|
||||
var pickedFileData = await dialogService.PickWindowsFileContentAsync(".jpg", ".png");
|
||||
|
||||
if (pickedFileData == Array.Empty<byte>()) return;
|
||||
|
||||
IsPrimaryButtonEnabled = true;
|
||||
|
||||
WallpaperData = pickedFileData;
|
||||
}
|
||||
|
||||
private void PickerColorChanged(Microsoft.UI.Xaml.Controls.ColorPicker sender, Microsoft.UI.Xaml.Controls.ColorChangedEventArgs args)
|
||||
{
|
||||
PreviewAccentColorGrid.Background = new SolidColorBrush(args.NewColor);
|
||||
AccentColor = args.NewColor.ToHex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,146 +7,145 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
namespace Wino.Core.UWP.Dialogs
|
||||
namespace Wino.Core.UWP.Dialogs;
|
||||
|
||||
public sealed partial class NewAccountDialog : ContentDialog
|
||||
{
|
||||
public sealed partial class NewAccountDialog : ContentDialog
|
||||
private Dictionary<SpecialImapProvider, string> helpingLinks = new Dictionary<SpecialImapProvider, string>()
|
||||
{
|
||||
private Dictionary<SpecialImapProvider, string> helpingLinks = new Dictionary<SpecialImapProvider, string>()
|
||||
{ SpecialImapProvider.iCloud, "https://support.apple.com/en-us/102654" },
|
||||
{ SpecialImapProvider.Yahoo, "http://help.yahoo.com/kb/SLN15241.html" },
|
||||
};
|
||||
|
||||
public static readonly DependencyProperty IsProviderSelectionVisibleProperty = DependencyProperty.Register(nameof(IsProviderSelectionVisible), typeof(bool), typeof(NewAccountDialog), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty IsSpecialImapServerPartVisibleProperty = DependencyProperty.Register(nameof(IsSpecialImapServerPartVisible), typeof(bool), typeof(NewAccountDialog), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty SelectedMailProviderProperty = DependencyProperty.Register(nameof(SelectedMailProvider), typeof(ProviderDetail), typeof(NewAccountDialog), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedProviderChanged)));
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets current selected mail provider in the dialog.
|
||||
/// </summary>
|
||||
public ProviderDetail SelectedMailProvider
|
||||
{
|
||||
get { return (ProviderDetail)GetValue(SelectedMailProviderProperty); }
|
||||
set { SetValue(SelectedMailProviderProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
public bool IsProviderSelectionVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsProviderSelectionVisibleProperty); }
|
||||
set { SetValue(IsProviderSelectionVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsSpecialImapServerPartVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsSpecialImapServerPartVisibleProperty); }
|
||||
set { SetValue(IsSpecialImapServerPartVisibleProperty, value); }
|
||||
}
|
||||
|
||||
// List of available mail providers for now.
|
||||
|
||||
public List<IProviderDetail> Providers { get; set; }
|
||||
|
||||
public AccountCreationDialogResult Result = null;
|
||||
|
||||
public NewAccountDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// AccountColorPicker.Color = Colors.Blue;
|
||||
}
|
||||
|
||||
private static void OnSelectedProviderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is NewAccountDialog dialog)
|
||||
dialog.Validate();
|
||||
}
|
||||
|
||||
private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void CreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
if (IsSpecialImapServerPartVisible)
|
||||
{
|
||||
{ SpecialImapProvider.iCloud, "https://support.apple.com/en-us/102654" },
|
||||
{ SpecialImapProvider.Yahoo, "http://help.yahoo.com/kb/SLN15241.html" },
|
||||
};
|
||||
// Special imap detail input.
|
||||
|
||||
public static readonly DependencyProperty IsProviderSelectionVisibleProperty = DependencyProperty.Register(nameof(IsProviderSelectionVisible), typeof(bool), typeof(NewAccountDialog), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty IsSpecialImapServerPartVisibleProperty = DependencyProperty.Register(nameof(IsSpecialImapServerPartVisible), typeof(bool), typeof(NewAccountDialog), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty SelectedMailProviderProperty = DependencyProperty.Register(nameof(SelectedMailProvider), typeof(ProviderDetail), typeof(NewAccountDialog), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedProviderChanged)));
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets current selected mail provider in the dialog.
|
||||
/// </summary>
|
||||
public ProviderDetail SelectedMailProvider
|
||||
{
|
||||
get { return (ProviderDetail)GetValue(SelectedMailProviderProperty); }
|
||||
set { SetValue(SelectedMailProviderProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
public bool IsProviderSelectionVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsProviderSelectionVisibleProperty); }
|
||||
set { SetValue(IsProviderSelectionVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsSpecialImapServerPartVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsSpecialImapServerPartVisibleProperty); }
|
||||
set { SetValue(IsSpecialImapServerPartVisibleProperty, value); }
|
||||
}
|
||||
|
||||
// List of available mail providers for now.
|
||||
|
||||
public List<IProviderDetail> Providers { get; set; }
|
||||
|
||||
public AccountCreationDialogResult Result = null;
|
||||
|
||||
public NewAccountDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// AccountColorPicker.Color = Colors.Blue;
|
||||
}
|
||||
|
||||
private static void OnSelectedProviderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is NewAccountDialog dialog)
|
||||
dialog.Validate();
|
||||
}
|
||||
|
||||
private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
var details = new SpecialImapProviderDetails(SpecialImapAddress.Text.Trim(), AppSpecificPassword.Password.Trim(), DisplayNameTextBox.Text.Trim(), SelectedMailProvider.SpecialImapProvider);
|
||||
Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim(), details);
|
||||
Hide();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void CreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
if (IsSpecialImapServerPartVisible)
|
||||
{
|
||||
// Special imap detail input.
|
||||
Validate();
|
||||
|
||||
var details = new SpecialImapProviderDetails(SpecialImapAddress.Text.Trim(), AppSpecificPassword.Password.Trim(), DisplayNameTextBox.Text.Trim(), SelectedMailProvider.SpecialImapProvider);
|
||||
Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim(), details);
|
||||
if (IsSecondaryButtonEnabled)
|
||||
{
|
||||
if (SelectedMailProvider.SpecialImapProvider != SpecialImapProvider.None)
|
||||
{
|
||||
// This step requires app-sepcific password login for some providers.
|
||||
args.Cancel = true;
|
||||
|
||||
IsProviderSelectionVisible = false;
|
||||
IsSpecialImapServerPartVisible = true;
|
||||
|
||||
Validate();
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim(), null);
|
||||
Hide();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Validate();
|
||||
|
||||
if (IsSecondaryButtonEnabled)
|
||||
{
|
||||
if (SelectedMailProvider.SpecialImapProvider != SpecialImapProvider.None)
|
||||
{
|
||||
// This step requires app-sepcific password login for some providers.
|
||||
args.Cancel = true;
|
||||
|
||||
IsProviderSelectionVisible = false;
|
||||
IsSpecialImapServerPartVisible = true;
|
||||
|
||||
Validate();
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = new AccountCreationDialogResult(SelectedMailProvider.Type, AccountNameTextbox.Text.Trim(), null);
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InputChanged(object sender, TextChangedEventArgs e) => Validate();
|
||||
private void SenderNameChanged(object sender, TextChangedEventArgs e) => Validate();
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
ValidateCreateButton();
|
||||
ValidateNames();
|
||||
}
|
||||
|
||||
// Returns whether we can create account or not.
|
||||
private void ValidateCreateButton()
|
||||
{
|
||||
bool shouldEnable = SelectedMailProvider != null
|
||||
&& SelectedMailProvider.IsSupported
|
||||
&& !string.IsNullOrEmpty(AccountNameTextbox.Text)
|
||||
&& (IsSpecialImapServerPartVisible ? (!string.IsNullOrEmpty(AppSpecificPassword.Password)
|
||||
&& !string.IsNullOrEmpty(DisplayNameTextBox.Text)
|
||||
&& EmailValidation.EmailValidator.Validate(SpecialImapAddress.Text)) : true);
|
||||
|
||||
IsPrimaryButtonEnabled = shouldEnable;
|
||||
}
|
||||
|
||||
private void ValidateNames()
|
||||
{
|
||||
AccountNameTextbox.IsEnabled = SelectedMailProvider != null;
|
||||
}
|
||||
|
||||
private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) => Validate();
|
||||
|
||||
private void BackClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsSpecialImapServerPartVisible = false;
|
||||
IsProviderSelectionVisible = true;
|
||||
|
||||
Validate();
|
||||
}
|
||||
|
||||
private void ImapPasswordChanged(object sender, RoutedEventArgs e) => Validate();
|
||||
|
||||
private async void AppSpecificHelpButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var helpUrl = helpingLinks[SelectedMailProvider.SpecialImapProvider];
|
||||
|
||||
await Launcher.LaunchUriAsync(new Uri(helpUrl));
|
||||
}
|
||||
}
|
||||
|
||||
private void InputChanged(object sender, TextChangedEventArgs e) => Validate();
|
||||
private void SenderNameChanged(object sender, TextChangedEventArgs e) => Validate();
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
ValidateCreateButton();
|
||||
ValidateNames();
|
||||
}
|
||||
|
||||
// Returns whether we can create account or not.
|
||||
private void ValidateCreateButton()
|
||||
{
|
||||
bool shouldEnable = SelectedMailProvider != null
|
||||
&& SelectedMailProvider.IsSupported
|
||||
&& !string.IsNullOrEmpty(AccountNameTextbox.Text)
|
||||
&& (IsSpecialImapServerPartVisible ? (!string.IsNullOrEmpty(AppSpecificPassword.Password)
|
||||
&& !string.IsNullOrEmpty(DisplayNameTextBox.Text)
|
||||
&& EmailValidation.EmailValidator.Validate(SpecialImapAddress.Text)) : true);
|
||||
|
||||
IsPrimaryButtonEnabled = shouldEnable;
|
||||
}
|
||||
|
||||
private void ValidateNames()
|
||||
{
|
||||
AccountNameTextbox.IsEnabled = SelectedMailProvider != null;
|
||||
}
|
||||
|
||||
private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) => Validate();
|
||||
|
||||
private void BackClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsSpecialImapServerPartVisible = false;
|
||||
IsProviderSelectionVisible = true;
|
||||
|
||||
Validate();
|
||||
}
|
||||
|
||||
private void ImapPasswordChanged(object sender, RoutedEventArgs e) => Validate();
|
||||
|
||||
private async void AppSpecificHelpButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var helpUrl = helpingLinks[SelectedMailProvider.SpecialImapProvider];
|
||||
|
||||
await Launcher.LaunchUriAsync(new Uri(helpUrl));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,44 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Wino.Dialogs
|
||||
namespace Wino.Dialogs;
|
||||
|
||||
public sealed partial class TextInputDialog : ContentDialog
|
||||
{
|
||||
public sealed partial class TextInputDialog : ContentDialog
|
||||
public bool? HasInput { get; set; }
|
||||
|
||||
public string CurrentInput
|
||||
{
|
||||
public bool? HasInput { get; set; }
|
||||
get { return (string)GetValue(CurrentInputProperty); }
|
||||
set { SetValue(CurrentInputProperty, value); }
|
||||
}
|
||||
|
||||
public string CurrentInput
|
||||
{
|
||||
get { return (string)GetValue(CurrentInputProperty); }
|
||||
set { SetValue(CurrentInputProperty, value); }
|
||||
}
|
||||
public static readonly DependencyProperty CurrentInputProperty = DependencyProperty.Register(nameof(CurrentInput), typeof(string), typeof(TextInputDialog), new PropertyMetadata(string.Empty));
|
||||
|
||||
public static readonly DependencyProperty CurrentInputProperty = DependencyProperty.Register(nameof(CurrentInput), typeof(string), typeof(TextInputDialog), new PropertyMetadata(string.Empty));
|
||||
public TextInputDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public TextInputDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
public void SetDescription(string description)
|
||||
{
|
||||
DialogDescription.Text = description;
|
||||
}
|
||||
|
||||
public void SetDescription(string description)
|
||||
{
|
||||
DialogDescription.Text = description;
|
||||
}
|
||||
public void SetPrimaryButtonText(string text)
|
||||
{
|
||||
PrimaryButtonText = text;
|
||||
}
|
||||
|
||||
public void SetPrimaryButtonText(string text)
|
||||
{
|
||||
PrimaryButtonText = text;
|
||||
}
|
||||
private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
private void UpdateOrCreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
HasInput = true;
|
||||
|
||||
private void UpdateOrCreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
HasInput = true;
|
||||
|
||||
Hide();
|
||||
}
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,18 +3,17 @@ using System.Threading.Tasks;
|
||||
using Windows.UI.Core;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.UWP
|
||||
namespace Wino.Core.UWP;
|
||||
|
||||
public class UWPDispatcher : IDispatcher
|
||||
{
|
||||
public class UWPDispatcher : IDispatcher
|
||||
private readonly CoreDispatcher _coreDispatcher;
|
||||
|
||||
public UWPDispatcher(CoreDispatcher coreDispatcher)
|
||||
{
|
||||
private readonly CoreDispatcher _coreDispatcher;
|
||||
|
||||
public UWPDispatcher(CoreDispatcher coreDispatcher)
|
||||
{
|
||||
_coreDispatcher = coreDispatcher;
|
||||
}
|
||||
|
||||
public Task ExecuteOnUIThread(Action action)
|
||||
=> _coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask();
|
||||
_coreDispatcher = coreDispatcher;
|
||||
}
|
||||
|
||||
public Task ExecuteOnUIThread(Action action)
|
||||
=> _coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask();
|
||||
}
|
||||
|
||||
@@ -8,109 +8,108 @@ using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
|
||||
namespace Wino.Extensions
|
||||
namespace Wino.Extensions;
|
||||
|
||||
public static class AnimationExtensions
|
||||
{
|
||||
public static class AnimationExtensions
|
||||
#region Composition
|
||||
|
||||
public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(this Compositor compositor, float? from, float to,
|
||||
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
|
||||
{
|
||||
#region Composition
|
||||
var animation = compositor.CreateScalarKeyFrameAnimation();
|
||||
|
||||
public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(this Compositor compositor, float? from, float to,
|
||||
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
|
||||
{
|
||||
var animation = compositor.CreateScalarKeyFrameAnimation();
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
if (!delay.Equals(0)) animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
|
||||
animation.InsertKeyFrame(1.0f, to, easing);
|
||||
animation.IterationBehavior = iterationBehavior;
|
||||
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
if (!delay.Equals(0)) animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
|
||||
animation.InsertKeyFrame(1.0f, to, easing);
|
||||
animation.IterationBehavior = iterationBehavior;
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation(this Compositor compositor, Vector2? from, Vector2 to,
|
||||
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
|
||||
{
|
||||
var animation = compositor.CreateVector2KeyFrameAnimation();
|
||||
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
|
||||
animation.InsertKeyFrame(1.0f, to, easing);
|
||||
animation.IterationBehavior = iterationBehavior;
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(this Compositor compositor, Vector2? from, Vector2 to,
|
||||
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
|
||||
{
|
||||
var animation = compositor.CreateVector3KeyFrameAnimation();
|
||||
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
if (from.HasValue) animation.InsertKeyFrame(0.0f, new Vector3(from.Value, 1.0f), easing);
|
||||
animation.InsertKeyFrame(1.0f, new Vector3(to, 1.0f), easing);
|
||||
animation.IterationBehavior = iterationBehavior;
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(this Compositor compositor, Vector3? from, Vector3 to,
|
||||
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
|
||||
{
|
||||
var animation = compositor.CreateVector3KeyFrameAnimation();
|
||||
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
|
||||
animation.InsertKeyFrame(1.0f, to, easing);
|
||||
animation.IterationBehavior = iterationBehavior;
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Xaml Storyboard
|
||||
|
||||
public static void Animate(this DependencyObject target, double? from, double to,
|
||||
string propertyPath, int duration = 400, int startTime = 0,
|
||||
EasingFunctionBase easing = null, Action completed = null, bool enableDependentAnimation = false)
|
||||
{
|
||||
if (easing == null)
|
||||
{
|
||||
easing = new ExponentialEase();
|
||||
}
|
||||
|
||||
var db = new DoubleAnimation
|
||||
{
|
||||
EnableDependentAnimation = enableDependentAnimation,
|
||||
To = to,
|
||||
From = from,
|
||||
EasingFunction = easing,
|
||||
Duration = TimeSpan.FromMilliseconds(duration)
|
||||
};
|
||||
Storyboard.SetTarget(db, target);
|
||||
Storyboard.SetTargetProperty(db, propertyPath);
|
||||
|
||||
var sb = new Storyboard
|
||||
{
|
||||
BeginTime = TimeSpan.FromMilliseconds(startTime)
|
||||
};
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
sb.Completed += (s, e) =>
|
||||
{
|
||||
completed();
|
||||
};
|
||||
}
|
||||
|
||||
sb.Children.Add(db);
|
||||
sb.Begin();
|
||||
}
|
||||
|
||||
#endregion
|
||||
return animation;
|
||||
}
|
||||
|
||||
public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation(this Compositor compositor, Vector2? from, Vector2 to,
|
||||
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
|
||||
{
|
||||
var animation = compositor.CreateVector2KeyFrameAnimation();
|
||||
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
|
||||
animation.InsertKeyFrame(1.0f, to, easing);
|
||||
animation.IterationBehavior = iterationBehavior;
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(this Compositor compositor, Vector2? from, Vector2 to,
|
||||
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
|
||||
{
|
||||
var animation = compositor.CreateVector3KeyFrameAnimation();
|
||||
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
if (from.HasValue) animation.InsertKeyFrame(0.0f, new Vector3(from.Value, 1.0f), easing);
|
||||
animation.InsertKeyFrame(1.0f, new Vector3(to, 1.0f), easing);
|
||||
animation.IterationBehavior = iterationBehavior;
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(this Compositor compositor, Vector3? from, Vector3 to,
|
||||
double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior)
|
||||
{
|
||||
var animation = compositor.CreateVector3KeyFrameAnimation();
|
||||
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing);
|
||||
animation.InsertKeyFrame(1.0f, to, easing);
|
||||
animation.IterationBehavior = iterationBehavior;
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Xaml Storyboard
|
||||
|
||||
public static void Animate(this DependencyObject target, double? from, double to,
|
||||
string propertyPath, int duration = 400, int startTime = 0,
|
||||
EasingFunctionBase easing = null, Action completed = null, bool enableDependentAnimation = false)
|
||||
{
|
||||
if (easing == null)
|
||||
{
|
||||
easing = new ExponentialEase();
|
||||
}
|
||||
|
||||
var db = new DoubleAnimation
|
||||
{
|
||||
EnableDependentAnimation = enableDependentAnimation,
|
||||
To = to,
|
||||
From = from,
|
||||
EasingFunction = easing,
|
||||
Duration = TimeSpan.FromMilliseconds(duration)
|
||||
};
|
||||
Storyboard.SetTarget(db, target);
|
||||
Storyboard.SetTargetProperty(db, propertyPath);
|
||||
|
||||
var sb = new Storyboard
|
||||
{
|
||||
BeginTime = TimeSpan.FromMilliseconds(startTime)
|
||||
};
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
sb.Completed += (s, e) =>
|
||||
{
|
||||
completed();
|
||||
};
|
||||
}
|
||||
|
||||
sb.Children.Add(db);
|
||||
sb.Begin();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -4,70 +4,69 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wino.Extensions
|
||||
namespace Wino.Extensions;
|
||||
|
||||
public enum TransitionDirection
|
||||
{
|
||||
public enum TransitionDirection
|
||||
{
|
||||
TopToBottom,
|
||||
BottomToTop,
|
||||
LeftToRight,
|
||||
RightToLeft
|
||||
}
|
||||
|
||||
public enum ClipAnimationDirection
|
||||
{
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
public enum AnimationAxis
|
||||
{
|
||||
X,
|
||||
Y,
|
||||
Z
|
||||
}
|
||||
|
||||
public enum AnimationType
|
||||
{
|
||||
KeyFrame,
|
||||
Expression
|
||||
}
|
||||
|
||||
public enum FlickDirection
|
||||
{
|
||||
None,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
public enum ViewState
|
||||
{
|
||||
Empty,
|
||||
Small,
|
||||
Big,
|
||||
Full
|
||||
}
|
||||
|
||||
public enum Gesture
|
||||
{
|
||||
Initial,
|
||||
Tap,
|
||||
Swipe
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum VisualPropertyType
|
||||
{
|
||||
None = 0,
|
||||
Opacity = 1 << 0,
|
||||
Offset = 1 << 1,
|
||||
Scale = 1 << 2,
|
||||
Size = 1 << 3,
|
||||
RotationAngleInDegrees = 1 << 4,
|
||||
All = ~0
|
||||
}
|
||||
TopToBottom,
|
||||
BottomToTop,
|
||||
LeftToRight,
|
||||
RightToLeft
|
||||
}
|
||||
|
||||
public enum ClipAnimationDirection
|
||||
{
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
public enum AnimationAxis
|
||||
{
|
||||
X,
|
||||
Y,
|
||||
Z
|
||||
}
|
||||
|
||||
public enum AnimationType
|
||||
{
|
||||
KeyFrame,
|
||||
Expression
|
||||
}
|
||||
|
||||
public enum FlickDirection
|
||||
{
|
||||
None,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
public enum ViewState
|
||||
{
|
||||
Empty,
|
||||
Small,
|
||||
Big,
|
||||
Full
|
||||
}
|
||||
|
||||
public enum Gesture
|
||||
{
|
||||
Initial,
|
||||
Tap,
|
||||
Swipe
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum VisualPropertyType
|
||||
{
|
||||
None = 0,
|
||||
Opacity = 1 << 0,
|
||||
Offset = 1 << 1,
|
||||
Scale = 1 << 2,
|
||||
Size = 1 << 3,
|
||||
RotationAngleInDegrees = 1 << 4,
|
||||
All = ~0
|
||||
}
|
||||
|
||||
@@ -8,193 +8,192 @@ using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Hosting;
|
||||
|
||||
namespace Wino.Extensions
|
||||
namespace Wino.Extensions;
|
||||
|
||||
public static partial class CompositionExtensions
|
||||
{
|
||||
public static partial class CompositionExtensions
|
||||
public static void EnableFluidVisibilityAnimation(this UIElement element, AnimationAxis? axis = null,
|
||||
float showFromOffset = 0.0f, float hideToOffset = 0.0f, Vector3? centerPoint = null,
|
||||
float showFromScale = 1.0f, float hideToScale = 1.0f, float showDuration = 800.0f, float hideDuration = 800.0f,
|
||||
int showDelay = 0, int hideDelay = 0, bool animateOpacity = true)
|
||||
{
|
||||
public static void EnableFluidVisibilityAnimation(this UIElement element, AnimationAxis? axis = null,
|
||||
float showFromOffset = 0.0f, float hideToOffset = 0.0f, Vector3? centerPoint = null,
|
||||
float showFromScale = 1.0f, float hideToScale = 1.0f, float showDuration = 800.0f, float hideDuration = 800.0f,
|
||||
int showDelay = 0, int hideDelay = 0, bool animateOpacity = true)
|
||||
var elementVisual = element.Visual();
|
||||
var compositor = elementVisual.Compositor;
|
||||
ElementCompositionPreview.SetIsTranslationEnabled(element, true);
|
||||
|
||||
ScalarKeyFrameAnimation hideOpacityAnimation = null;
|
||||
ScalarKeyFrameAnimation showOpacityAnimation = null;
|
||||
ScalarKeyFrameAnimation hideOffsetAnimation = null;
|
||||
ScalarKeyFrameAnimation showOffsetAnimation = null;
|
||||
Vector2KeyFrameAnimation hideScaleAnimation = null;
|
||||
Vector2KeyFrameAnimation showeScaleAnimation = null;
|
||||
|
||||
if (animateOpacity)
|
||||
{
|
||||
var elementVisual = element.Visual();
|
||||
var compositor = elementVisual.Compositor;
|
||||
ElementCompositionPreview.SetIsTranslationEnabled(element, true);
|
||||
|
||||
ScalarKeyFrameAnimation hideOpacityAnimation = null;
|
||||
ScalarKeyFrameAnimation showOpacityAnimation = null;
|
||||
ScalarKeyFrameAnimation hideOffsetAnimation = null;
|
||||
ScalarKeyFrameAnimation showOffsetAnimation = null;
|
||||
Vector2KeyFrameAnimation hideScaleAnimation = null;
|
||||
Vector2KeyFrameAnimation showeScaleAnimation = null;
|
||||
|
||||
if (animateOpacity)
|
||||
{
|
||||
hideOpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
hideOpacityAnimation.InsertKeyFrame(1.0f, 0.0f);
|
||||
hideOpacityAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
|
||||
hideOpacityAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
|
||||
hideOpacityAnimation.Target = "Opacity";
|
||||
}
|
||||
|
||||
if (!hideToOffset.Equals(0.0f))
|
||||
{
|
||||
hideOffsetAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
hideOffsetAnimation.InsertKeyFrame(1.0f, hideToOffset);
|
||||
hideOffsetAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
|
||||
hideOffsetAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
|
||||
hideOffsetAnimation.Target = $"Translation.{axis}";
|
||||
}
|
||||
|
||||
if (centerPoint.HasValue)
|
||||
{
|
||||
elementVisual.CenterPoint = centerPoint.Value;
|
||||
}
|
||||
|
||||
if (!hideToScale.Equals(1.0f))
|
||||
{
|
||||
hideScaleAnimation = compositor.CreateVector2KeyFrameAnimation();
|
||||
hideScaleAnimation.InsertKeyFrame(1.0f, new Vector2(hideToScale));
|
||||
hideScaleAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
|
||||
hideScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
|
||||
hideScaleAnimation.Target = "Scale.XY";
|
||||
}
|
||||
|
||||
var hideAnimationGroup = compositor.CreateAnimationGroup();
|
||||
if (hideOpacityAnimation != null)
|
||||
{
|
||||
hideAnimationGroup.Add(hideOpacityAnimation);
|
||||
}
|
||||
if (hideOffsetAnimation != null)
|
||||
{
|
||||
hideAnimationGroup.Add(hideOffsetAnimation);
|
||||
}
|
||||
if (hideScaleAnimation != null)
|
||||
{
|
||||
hideAnimationGroup.Add(hideScaleAnimation);
|
||||
}
|
||||
|
||||
ElementCompositionPreview.SetImplicitHideAnimation(element, hideAnimationGroup);
|
||||
|
||||
if (animateOpacity)
|
||||
{
|
||||
showOpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
showOpacityAnimation.InsertKeyFrame(1.0f, 1.0f);
|
||||
showOpacityAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
|
||||
showOpacityAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
|
||||
showOpacityAnimation.Target = "Opacity";
|
||||
}
|
||||
|
||||
if (!showFromOffset.Equals(0.0f))
|
||||
{
|
||||
showOffsetAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
showOffsetAnimation.InsertKeyFrame(0.0f, showFromOffset);
|
||||
showOffsetAnimation.InsertKeyFrame(1.0f, 0.0f);
|
||||
showOffsetAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
|
||||
showOffsetAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
|
||||
showOffsetAnimation.Target = $"Translation.{axis}";
|
||||
}
|
||||
|
||||
if (!showFromScale.Equals(1.0f))
|
||||
{
|
||||
showeScaleAnimation = compositor.CreateVector2KeyFrameAnimation();
|
||||
showeScaleAnimation.InsertKeyFrame(0.0f, new Vector2(showFromScale));
|
||||
showeScaleAnimation.InsertKeyFrame(1.0f, Vector2.One);
|
||||
showeScaleAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
|
||||
showeScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
|
||||
showeScaleAnimation.Target = "Scale.XY";
|
||||
}
|
||||
|
||||
var showAnimationGroup = compositor.CreateAnimationGroup();
|
||||
if (showOpacityAnimation != null)
|
||||
{
|
||||
showAnimationGroup.Add(showOpacityAnimation);
|
||||
}
|
||||
if (showOffsetAnimation != null)
|
||||
{
|
||||
showAnimationGroup.Add(showOffsetAnimation);
|
||||
}
|
||||
if (showeScaleAnimation != null)
|
||||
{
|
||||
showAnimationGroup.Add(showeScaleAnimation);
|
||||
}
|
||||
|
||||
ElementCompositionPreview.SetImplicitShowAnimation(element, showAnimationGroup);
|
||||
hideOpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
hideOpacityAnimation.InsertKeyFrame(1.0f, 0.0f);
|
||||
hideOpacityAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
|
||||
hideOpacityAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
|
||||
hideOpacityAnimation.Target = "Opacity";
|
||||
}
|
||||
|
||||
public static void EnableImplicitAnimation(this UIElement element, VisualPropertyType typeToAnimate,
|
||||
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
|
||||
if (!hideToOffset.Equals(0.0f))
|
||||
{
|
||||
var visual = element.Visual();
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
var animationCollection = compositor.CreateImplicitAnimationCollection();
|
||||
|
||||
foreach (var type in UtilExtensions.GetValues<VisualPropertyType>())
|
||||
{
|
||||
if (!typeToAnimate.HasFlag(type)) continue;
|
||||
|
||||
var animation = CreateAnimationByType(compositor, type, duration, delay, easing);
|
||||
|
||||
if (animation != null)
|
||||
{
|
||||
animationCollection[type.ToString()] = animation;
|
||||
}
|
||||
}
|
||||
|
||||
visual.ImplicitAnimations = animationCollection;
|
||||
hideOffsetAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
hideOffsetAnimation.InsertKeyFrame(1.0f, hideToOffset);
|
||||
hideOffsetAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
|
||||
hideOffsetAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
|
||||
hideOffsetAnimation.Target = $"Translation.{axis}";
|
||||
}
|
||||
|
||||
public static void EnableImplicitAnimation(this Visual visual, VisualPropertyType typeToAnimate,
|
||||
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
|
||||
if (centerPoint.HasValue)
|
||||
{
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
var animationCollection = compositor.CreateImplicitAnimationCollection();
|
||||
|
||||
foreach (var type in UtilExtensions.GetValues<VisualPropertyType>())
|
||||
{
|
||||
if (!typeToAnimate.HasFlag(type)) continue;
|
||||
|
||||
var animation = CreateAnimationByType(compositor, type, duration, delay, easing);
|
||||
|
||||
if (animation != null)
|
||||
{
|
||||
animationCollection[type.ToString()] = animation;
|
||||
}
|
||||
}
|
||||
|
||||
visual.ImplicitAnimations = animationCollection;
|
||||
elementVisual.CenterPoint = centerPoint.Value;
|
||||
}
|
||||
|
||||
private static KeyFrameAnimation CreateAnimationByType(Compositor compositor, VisualPropertyType type,
|
||||
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
|
||||
if (!hideToScale.Equals(1.0f))
|
||||
{
|
||||
KeyFrameAnimation animation;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case VisualPropertyType.Offset:
|
||||
case VisualPropertyType.Scale:
|
||||
animation = compositor.CreateVector3KeyFrameAnimation();
|
||||
break;
|
||||
case VisualPropertyType.Size:
|
||||
animation = compositor.CreateVector2KeyFrameAnimation();
|
||||
break;
|
||||
case VisualPropertyType.Opacity:
|
||||
case VisualPropertyType.RotationAngleInDegrees:
|
||||
animation = compositor.CreateScalarKeyFrameAnimation();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
animation.InsertExpressionKeyFrame(1.0f, "this.FinalValue", easing);
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
animation.Target = type.ToString();
|
||||
|
||||
return animation;
|
||||
hideScaleAnimation = compositor.CreateVector2KeyFrameAnimation();
|
||||
hideScaleAnimation.InsertKeyFrame(1.0f, new Vector2(hideToScale));
|
||||
hideScaleAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration);
|
||||
hideScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay);
|
||||
hideScaleAnimation.Target = "Scale.XY";
|
||||
}
|
||||
|
||||
var hideAnimationGroup = compositor.CreateAnimationGroup();
|
||||
if (hideOpacityAnimation != null)
|
||||
{
|
||||
hideAnimationGroup.Add(hideOpacityAnimation);
|
||||
}
|
||||
if (hideOffsetAnimation != null)
|
||||
{
|
||||
hideAnimationGroup.Add(hideOffsetAnimation);
|
||||
}
|
||||
if (hideScaleAnimation != null)
|
||||
{
|
||||
hideAnimationGroup.Add(hideScaleAnimation);
|
||||
}
|
||||
|
||||
ElementCompositionPreview.SetImplicitHideAnimation(element, hideAnimationGroup);
|
||||
|
||||
if (animateOpacity)
|
||||
{
|
||||
showOpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
showOpacityAnimation.InsertKeyFrame(1.0f, 1.0f);
|
||||
showOpacityAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
|
||||
showOpacityAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
|
||||
showOpacityAnimation.Target = "Opacity";
|
||||
}
|
||||
|
||||
if (!showFromOffset.Equals(0.0f))
|
||||
{
|
||||
showOffsetAnimation = compositor.CreateScalarKeyFrameAnimation();
|
||||
showOffsetAnimation.InsertKeyFrame(0.0f, showFromOffset);
|
||||
showOffsetAnimation.InsertKeyFrame(1.0f, 0.0f);
|
||||
showOffsetAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
|
||||
showOffsetAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
|
||||
showOffsetAnimation.Target = $"Translation.{axis}";
|
||||
}
|
||||
|
||||
if (!showFromScale.Equals(1.0f))
|
||||
{
|
||||
showeScaleAnimation = compositor.CreateVector2KeyFrameAnimation();
|
||||
showeScaleAnimation.InsertKeyFrame(0.0f, new Vector2(showFromScale));
|
||||
showeScaleAnimation.InsertKeyFrame(1.0f, Vector2.One);
|
||||
showeScaleAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
|
||||
showeScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
|
||||
showeScaleAnimation.Target = "Scale.XY";
|
||||
}
|
||||
|
||||
var showAnimationGroup = compositor.CreateAnimationGroup();
|
||||
if (showOpacityAnimation != null)
|
||||
{
|
||||
showAnimationGroup.Add(showOpacityAnimation);
|
||||
}
|
||||
if (showOffsetAnimation != null)
|
||||
{
|
||||
showAnimationGroup.Add(showOffsetAnimation);
|
||||
}
|
||||
if (showeScaleAnimation != null)
|
||||
{
|
||||
showAnimationGroup.Add(showeScaleAnimation);
|
||||
}
|
||||
|
||||
ElementCompositionPreview.SetImplicitShowAnimation(element, showAnimationGroup);
|
||||
}
|
||||
|
||||
public static void EnableImplicitAnimation(this UIElement element, VisualPropertyType typeToAnimate,
|
||||
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
|
||||
{
|
||||
var visual = element.Visual();
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
var animationCollection = compositor.CreateImplicitAnimationCollection();
|
||||
|
||||
foreach (var type in UtilExtensions.GetValues<VisualPropertyType>())
|
||||
{
|
||||
if (!typeToAnimate.HasFlag(type)) continue;
|
||||
|
||||
var animation = CreateAnimationByType(compositor, type, duration, delay, easing);
|
||||
|
||||
if (animation != null)
|
||||
{
|
||||
animationCollection[type.ToString()] = animation;
|
||||
}
|
||||
}
|
||||
|
||||
visual.ImplicitAnimations = animationCollection;
|
||||
}
|
||||
|
||||
public static void EnableImplicitAnimation(this Visual visual, VisualPropertyType typeToAnimate,
|
||||
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
|
||||
{
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
var animationCollection = compositor.CreateImplicitAnimationCollection();
|
||||
|
||||
foreach (var type in UtilExtensions.GetValues<VisualPropertyType>())
|
||||
{
|
||||
if (!typeToAnimate.HasFlag(type)) continue;
|
||||
|
||||
var animation = CreateAnimationByType(compositor, type, duration, delay, easing);
|
||||
|
||||
if (animation != null)
|
||||
{
|
||||
animationCollection[type.ToString()] = animation;
|
||||
}
|
||||
}
|
||||
|
||||
visual.ImplicitAnimations = animationCollection;
|
||||
}
|
||||
|
||||
private static KeyFrameAnimation CreateAnimationByType(Compositor compositor, VisualPropertyType type,
|
||||
double duration = 800, double delay = 0, CompositionEasingFunction easing = null)
|
||||
{
|
||||
KeyFrameAnimation animation;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case VisualPropertyType.Offset:
|
||||
case VisualPropertyType.Scale:
|
||||
animation = compositor.CreateVector3KeyFrameAnimation();
|
||||
break;
|
||||
case VisualPropertyType.Size:
|
||||
animation = compositor.CreateVector2KeyFrameAnimation();
|
||||
break;
|
||||
case VisualPropertyType.Opacity:
|
||||
case VisualPropertyType.RotationAngleInDegrees:
|
||||
animation = compositor.CreateScalarKeyFrameAnimation();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
animation.InsertExpressionKeyFrame(1.0f, "this.FinalValue", easing);
|
||||
animation.Duration = TimeSpan.FromMilliseconds(duration);
|
||||
animation.DelayTime = TimeSpan.FromMilliseconds(delay);
|
||||
animation.Target = type.ToString();
|
||||
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,124 +4,123 @@ using System.Threading.Tasks;
|
||||
using Windows.UI.Composition;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Wino.Extensions
|
||||
namespace Wino.Extensions;
|
||||
|
||||
public static partial class CompositionExtensions
|
||||
{
|
||||
public static partial class CompositionExtensions
|
||||
public static void StartSizeAnimation(this UIElement element, Vector2? from = null, Vector2? to = null,
|
||||
double duration = 800, int delay = 0, CompositionEasingFunction easing = null, Action completed = null,
|
||||
AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count)
|
||||
{
|
||||
public static void StartSizeAnimation(this UIElement element, Vector2? from = null, Vector2? to = null,
|
||||
double duration = 800, int delay = 0, CompositionEasingFunction easing = null, Action completed = null,
|
||||
AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count)
|
||||
CompositionScopedBatch batch = null;
|
||||
|
||||
var visual = element.Visual();
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
CompositionScopedBatch batch = null;
|
||||
|
||||
var visual = element.Visual();
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
batch.Completed += (s, e) => completed();
|
||||
}
|
||||
|
||||
if (to == null)
|
||||
{
|
||||
to = Vector2.One;
|
||||
}
|
||||
|
||||
visual.StartAnimation("Size",
|
||||
compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior));
|
||||
|
||||
batch?.End();
|
||||
}
|
||||
|
||||
public static void StartSizeAnimation(this Visual visual, Vector2? from = null, Vector2? to = null,
|
||||
double duration = 800, int delay = 0, CompositionEasingFunction easing = null, Action completed = null,
|
||||
AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count)
|
||||
{
|
||||
CompositionScopedBatch batch = null;
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
batch.Completed += (s, e) => completed();
|
||||
}
|
||||
|
||||
if (to == null)
|
||||
{
|
||||
to = Vector2.One;
|
||||
}
|
||||
|
||||
visual.StartAnimation("Size",
|
||||
compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior));
|
||||
|
||||
batch?.End();
|
||||
}
|
||||
|
||||
public static Task StartSizeAnimationAsync(this UIElement element, Vector2? from = null, Vector2? to = null,
|
||||
double duration = 800, int delay = 0, CompositionEasingFunction easing = null,
|
||||
AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count)
|
||||
{
|
||||
CompositionScopedBatch batch;
|
||||
|
||||
var visual = element.Visual();
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
var taskSource = new TaskCompletionSource<bool>();
|
||||
|
||||
void Completed(object o, CompositionBatchCompletedEventArgs e)
|
||||
{
|
||||
batch.Completed -= Completed;
|
||||
taskSource.SetResult(true);
|
||||
}
|
||||
|
||||
batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
batch.Completed += Completed;
|
||||
|
||||
if (to == null)
|
||||
{
|
||||
to = Vector2.One;
|
||||
}
|
||||
|
||||
visual.StartAnimation("Size",
|
||||
compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior));
|
||||
|
||||
batch.End();
|
||||
|
||||
return taskSource.Task;
|
||||
batch.Completed += (s, e) => completed();
|
||||
}
|
||||
|
||||
public static Task StartSizeAnimationAsync(this Visual visual, Vector2? from = null, Vector2? to = null,
|
||||
double duration = 800, int delay = 0, CompositionEasingFunction easing = null,
|
||||
AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count)
|
||||
if (to == null)
|
||||
{
|
||||
CompositionScopedBatch batch;
|
||||
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
var taskSource = new TaskCompletionSource<bool>();
|
||||
|
||||
void Completed(object o, CompositionBatchCompletedEventArgs e)
|
||||
{
|
||||
batch.Completed -= Completed;
|
||||
taskSource.SetResult(true);
|
||||
}
|
||||
|
||||
batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
batch.Completed += Completed;
|
||||
|
||||
if (to == null)
|
||||
{
|
||||
to = Vector2.One;
|
||||
}
|
||||
|
||||
visual.StartAnimation("Size",
|
||||
compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior));
|
||||
|
||||
batch.End();
|
||||
|
||||
return taskSource.Task;
|
||||
to = Vector2.One;
|
||||
}
|
||||
|
||||
visual.StartAnimation("Size",
|
||||
compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior));
|
||||
|
||||
batch?.End();
|
||||
}
|
||||
|
||||
public static void StartSizeAnimation(this Visual visual, Vector2? from = null, Vector2? to = null,
|
||||
double duration = 800, int delay = 0, CompositionEasingFunction easing = null, Action completed = null,
|
||||
AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count)
|
||||
{
|
||||
CompositionScopedBatch batch = null;
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
if (completed != null)
|
||||
{
|
||||
batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
batch.Completed += (s, e) => completed();
|
||||
}
|
||||
|
||||
if (to == null)
|
||||
{
|
||||
to = Vector2.One;
|
||||
}
|
||||
|
||||
visual.StartAnimation("Size",
|
||||
compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior));
|
||||
|
||||
batch?.End();
|
||||
}
|
||||
|
||||
public static Task StartSizeAnimationAsync(this UIElement element, Vector2? from = null, Vector2? to = null,
|
||||
double duration = 800, int delay = 0, CompositionEasingFunction easing = null,
|
||||
AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count)
|
||||
{
|
||||
CompositionScopedBatch batch;
|
||||
|
||||
var visual = element.Visual();
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
var taskSource = new TaskCompletionSource<bool>();
|
||||
|
||||
void Completed(object o, CompositionBatchCompletedEventArgs e)
|
||||
{
|
||||
batch.Completed -= Completed;
|
||||
taskSource.SetResult(true);
|
||||
}
|
||||
|
||||
batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
batch.Completed += Completed;
|
||||
|
||||
if (to == null)
|
||||
{
|
||||
to = Vector2.One;
|
||||
}
|
||||
|
||||
visual.StartAnimation("Size",
|
||||
compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior));
|
||||
|
||||
batch.End();
|
||||
|
||||
return taskSource.Task;
|
||||
}
|
||||
|
||||
public static Task StartSizeAnimationAsync(this Visual visual, Vector2? from = null, Vector2? to = null,
|
||||
double duration = 800, int delay = 0, CompositionEasingFunction easing = null,
|
||||
AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count)
|
||||
{
|
||||
CompositionScopedBatch batch;
|
||||
|
||||
var compositor = visual.Compositor;
|
||||
|
||||
var taskSource = new TaskCompletionSource<bool>();
|
||||
|
||||
void Completed(object o, CompositionBatchCompletedEventArgs e)
|
||||
{
|
||||
batch.Completed -= Completed;
|
||||
taskSource.SetResult(true);
|
||||
}
|
||||
|
||||
batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
|
||||
batch.Completed += Completed;
|
||||
|
||||
if (to == null)
|
||||
{
|
||||
to = Vector2.One;
|
||||
}
|
||||
|
||||
visual.StartAnimation("Size",
|
||||
compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior));
|
||||
|
||||
batch.End();
|
||||
|
||||
return taskSource.Task;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.UWP.Extensions
|
||||
namespace Wino.Core.UWP.Extensions;
|
||||
|
||||
public static class ElementThemeExtensions
|
||||
{
|
||||
public static class ElementThemeExtensions
|
||||
public static ApplicationElementTheme ToWinoElementTheme(this ElementTheme elementTheme)
|
||||
{
|
||||
public static ApplicationElementTheme ToWinoElementTheme(this ElementTheme elementTheme)
|
||||
switch (elementTheme)
|
||||
{
|
||||
switch (elementTheme)
|
||||
{
|
||||
case ElementTheme.Light:
|
||||
return ApplicationElementTheme.Light;
|
||||
case ElementTheme.Dark:
|
||||
return ApplicationElementTheme.Dark;
|
||||
}
|
||||
|
||||
return ApplicationElementTheme.Default;
|
||||
case ElementTheme.Light:
|
||||
return ApplicationElementTheme.Light;
|
||||
case ElementTheme.Dark:
|
||||
return ApplicationElementTheme.Dark;
|
||||
}
|
||||
|
||||
public static ElementTheme ToWindowsElementTheme(this ApplicationElementTheme elementTheme)
|
||||
{
|
||||
switch (elementTheme)
|
||||
{
|
||||
case ApplicationElementTheme.Light:
|
||||
return ElementTheme.Light;
|
||||
case ApplicationElementTheme.Dark:
|
||||
return ElementTheme.Dark;
|
||||
}
|
||||
return ApplicationElementTheme.Default;
|
||||
}
|
||||
|
||||
return ElementTheme.Default;
|
||||
public static ElementTheme ToWindowsElementTheme(this ApplicationElementTheme elementTheme)
|
||||
{
|
||||
switch (elementTheme)
|
||||
{
|
||||
case ApplicationElementTheme.Light:
|
||||
return ElementTheme.Light;
|
||||
case ApplicationElementTheme.Dark:
|
||||
return ElementTheme.Dark;
|
||||
}
|
||||
|
||||
return ElementTheme.Default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using Windows.ApplicationModel;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.UWP.Extensions
|
||||
namespace Wino.Core.UWP.Extensions;
|
||||
|
||||
public static class StartupTaskStateExtensions
|
||||
{
|
||||
public static class StartupTaskStateExtensions
|
||||
public static StartupBehaviorResult AsStartupBehaviorResult(this StartupTaskState state)
|
||||
{
|
||||
public static StartupBehaviorResult AsStartupBehaviorResult(this StartupTaskState state)
|
||||
switch (state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case StartupTaskState.Disabled:
|
||||
case StartupTaskState.DisabledByPolicy:
|
||||
return StartupBehaviorResult.Disabled;
|
||||
case StartupTaskState.DisabledByUser:
|
||||
return StartupBehaviorResult.DisabledByUser;
|
||||
case StartupTaskState.Enabled:
|
||||
case StartupTaskState.EnabledByPolicy:
|
||||
return StartupBehaviorResult.Enabled;
|
||||
default:
|
||||
return StartupBehaviorResult.Fatal;
|
||||
}
|
||||
case StartupTaskState.Disabled:
|
||||
case StartupTaskState.DisabledByPolicy:
|
||||
return StartupBehaviorResult.Disabled;
|
||||
case StartupTaskState.DisabledByUser:
|
||||
return StartupBehaviorResult.DisabledByUser;
|
||||
case StartupTaskState.Enabled:
|
||||
case StartupTaskState.EnabledByPolicy:
|
||||
return StartupBehaviorResult.Enabled;
|
||||
default:
|
||||
return StartupBehaviorResult.Fatal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,28 +4,27 @@ using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Wino.Core.Domain.Models.Common;
|
||||
|
||||
namespace Wino.Core.UWP.Extensions
|
||||
namespace Wino.Core.UWP.Extensions;
|
||||
|
||||
public static class StorageFileExtensions
|
||||
{
|
||||
public static class StorageFileExtensions
|
||||
public static async Task<SharedFile> ToSharedFileAsync(this Windows.Storage.StorageFile storageFile)
|
||||
{
|
||||
public static async Task<SharedFile> ToSharedFileAsync(this Windows.Storage.StorageFile storageFile)
|
||||
var content = await storageFile.ToByteArrayAsync();
|
||||
|
||||
return new SharedFile(storageFile.Path, content);
|
||||
}
|
||||
|
||||
public static async Task<byte[]> ToByteArrayAsync(this StorageFile file)
|
||||
{
|
||||
if (file == null)
|
||||
throw new ArgumentNullException(nameof(file));
|
||||
|
||||
using (var stream = await file.OpenReadAsync())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
var content = await storageFile.ToByteArrayAsync();
|
||||
|
||||
return new SharedFile(storageFile.Path, content);
|
||||
}
|
||||
|
||||
public static async Task<byte[]> ToByteArrayAsync(this StorageFile file)
|
||||
{
|
||||
if (file == null)
|
||||
throw new ArgumentNullException(nameof(file));
|
||||
|
||||
using (var stream = await file.OpenReadAsync())
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await stream.AsStreamForRead().CopyToAsync(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
await stream.AsStreamForRead().CopyToAsync(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Extensions
|
||||
namespace Wino.Extensions;
|
||||
|
||||
public static class UIExtensions
|
||||
{
|
||||
public static class UIExtensions
|
||||
public static InfoBarSeverity AsMUXCInfoBarSeverity(this InfoBarMessageType messageType)
|
||||
{
|
||||
public static InfoBarSeverity AsMUXCInfoBarSeverity(this InfoBarMessageType messageType)
|
||||
return messageType switch
|
||||
{
|
||||
return messageType switch
|
||||
{
|
||||
InfoBarMessageType.Error => InfoBarSeverity.Error,
|
||||
InfoBarMessageType.Warning => InfoBarSeverity.Warning,
|
||||
InfoBarMessageType.Information => InfoBarSeverity.Informational,
|
||||
InfoBarMessageType.Success => InfoBarSeverity.Success,
|
||||
_ => InfoBarSeverity.Informational
|
||||
};
|
||||
}
|
||||
InfoBarMessageType.Error => InfoBarSeverity.Error,
|
||||
InfoBarMessageType.Warning => InfoBarSeverity.Warning,
|
||||
InfoBarMessageType.Information => InfoBarSeverity.Informational,
|
||||
InfoBarMessageType.Success => InfoBarSeverity.Success,
|
||||
_ => InfoBarSeverity.Informational
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,101 +8,100 @@ using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Hosting;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Wino.Extensions
|
||||
namespace Wino.Extensions;
|
||||
|
||||
public static class UtilExtensions
|
||||
{
|
||||
public static class UtilExtensions
|
||||
public static float ToFloat(this double value) => (float)value;
|
||||
|
||||
public static List<FrameworkElement> Children(this DependencyObject parent)
|
||||
{
|
||||
public static float ToFloat(this double value) => (float)value;
|
||||
var list = new List<FrameworkElement>();
|
||||
|
||||
public static List<FrameworkElement> Children(this DependencyObject parent)
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
|
||||
{
|
||||
var list = new List<FrameworkElement>();
|
||||
var child = VisualTreeHelper.GetChild(parent, i);
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
|
||||
if (child is FrameworkElement)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, i);
|
||||
|
||||
if (child is FrameworkElement)
|
||||
{
|
||||
list.Add(child as FrameworkElement);
|
||||
}
|
||||
|
||||
list.AddRange(Children(child));
|
||||
list.Add(child as FrameworkElement);
|
||||
}
|
||||
|
||||
return list;
|
||||
list.AddRange(Children(child));
|
||||
}
|
||||
|
||||
public static T GetChildByName<T>(this DependencyObject parent, string name)
|
||||
{
|
||||
var childControls = Children(parent);
|
||||
var controls = childControls.OfType<FrameworkElement>();
|
||||
|
||||
if (controls == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
var control = controls
|
||||
.Where(x => x.Name.Equals(name))
|
||||
.Cast<T>()
|
||||
.First();
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
public static Visual Visual(this UIElement element) =>
|
||||
ElementCompositionPreview.GetElementVisual(element);
|
||||
|
||||
public static void SetChildVisual(this UIElement element, Visual childVisual) =>
|
||||
ElementCompositionPreview.SetElementChildVisual(element, childVisual);
|
||||
|
||||
public static Point RelativePosition(this UIElement element, UIElement other) =>
|
||||
element.TransformToVisual(other).TransformPoint(new Point(0, 0));
|
||||
|
||||
public static bool IsFullyVisibile(this FrameworkElement element, FrameworkElement parent)
|
||||
{
|
||||
if (element == null || parent == null)
|
||||
return false;
|
||||
|
||||
if (element.Visibility != Visibility.Visible)
|
||||
return false;
|
||||
|
||||
var elementBounds = element.TransformToVisual(parent).TransformBounds(new Windows.Foundation.Rect(0, 0, element.ActualWidth, element.ActualHeight));
|
||||
var containerBounds = new Windows.Foundation.Rect(0, 0, parent.ActualWidth, parent.ActualHeight);
|
||||
|
||||
var originalElementWidth = elementBounds.Width;
|
||||
var originalElementHeight = elementBounds.Height;
|
||||
|
||||
elementBounds.Intersect(containerBounds);
|
||||
|
||||
var newElementWidth = elementBounds.Width;
|
||||
var newElementHeight = elementBounds.Height;
|
||||
|
||||
return originalElementWidth.Equals(newElementWidth) && originalElementHeight.Equals(newElementHeight);
|
||||
}
|
||||
|
||||
public static void ScrollToElement(this ScrollViewer scrollViewer, FrameworkElement element,
|
||||
bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null, bool bringToTopOrLeft = true, bool addMargin = true)
|
||||
{
|
||||
if (!bringToTopOrLeft && element.IsFullyVisibile(scrollViewer))
|
||||
return;
|
||||
|
||||
var contentArea = (FrameworkElement)scrollViewer.Content;
|
||||
var position = element.RelativePosition(contentArea);
|
||||
|
||||
if (isVerticalScrolling)
|
||||
{
|
||||
// Accomodate for additional header.
|
||||
scrollViewer.ChangeView(null, Math.Max(0, position.Y - (addMargin ? 48 : 0)), zoomFactor, !smoothScrolling);
|
||||
}
|
||||
else
|
||||
{
|
||||
scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static T[] GetValues<T>() where T : struct, Enum => Enum.GetValues<T>();
|
||||
return list;
|
||||
}
|
||||
|
||||
public static T GetChildByName<T>(this DependencyObject parent, string name)
|
||||
{
|
||||
var childControls = Children(parent);
|
||||
var controls = childControls.OfType<FrameworkElement>();
|
||||
|
||||
if (controls == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
var control = controls
|
||||
.Where(x => x.Name.Equals(name))
|
||||
.Cast<T>()
|
||||
.First();
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
public static Visual Visual(this UIElement element) =>
|
||||
ElementCompositionPreview.GetElementVisual(element);
|
||||
|
||||
public static void SetChildVisual(this UIElement element, Visual childVisual) =>
|
||||
ElementCompositionPreview.SetElementChildVisual(element, childVisual);
|
||||
|
||||
public static Point RelativePosition(this UIElement element, UIElement other) =>
|
||||
element.TransformToVisual(other).TransformPoint(new Point(0, 0));
|
||||
|
||||
public static bool IsFullyVisibile(this FrameworkElement element, FrameworkElement parent)
|
||||
{
|
||||
if (element == null || parent == null)
|
||||
return false;
|
||||
|
||||
if (element.Visibility != Visibility.Visible)
|
||||
return false;
|
||||
|
||||
var elementBounds = element.TransformToVisual(parent).TransformBounds(new Windows.Foundation.Rect(0, 0, element.ActualWidth, element.ActualHeight));
|
||||
var containerBounds = new Windows.Foundation.Rect(0, 0, parent.ActualWidth, parent.ActualHeight);
|
||||
|
||||
var originalElementWidth = elementBounds.Width;
|
||||
var originalElementHeight = elementBounds.Height;
|
||||
|
||||
elementBounds.Intersect(containerBounds);
|
||||
|
||||
var newElementWidth = elementBounds.Width;
|
||||
var newElementHeight = elementBounds.Height;
|
||||
|
||||
return originalElementWidth.Equals(newElementWidth) && originalElementHeight.Equals(newElementHeight);
|
||||
}
|
||||
|
||||
public static void ScrollToElement(this ScrollViewer scrollViewer, FrameworkElement element,
|
||||
bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null, bool bringToTopOrLeft = true, bool addMargin = true)
|
||||
{
|
||||
if (!bringToTopOrLeft && element.IsFullyVisibile(scrollViewer))
|
||||
return;
|
||||
|
||||
var contentArea = (FrameworkElement)scrollViewer.Content;
|
||||
var position = element.RelativePosition(contentArea);
|
||||
|
||||
if (isVerticalScrolling)
|
||||
{
|
||||
// Accomodate for additional header.
|
||||
scrollViewer.ChangeView(null, Math.Max(0, position.Y - (addMargin ? 48 : 0)), zoomFactor, !smoothScrolling);
|
||||
}
|
||||
else
|
||||
{
|
||||
scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static T[] GetValues<T>() where T : struct, Enum => Enum.GetValues<T>();
|
||||
}
|
||||
|
||||
@@ -2,51 +2,50 @@
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Wino.Helpers
|
||||
namespace Wino.Helpers;
|
||||
|
||||
public static class WinoVisualTreeHelper
|
||||
{
|
||||
public static class WinoVisualTreeHelper
|
||||
public static T GetChildObject<T>(DependencyObject obj, string name) where T : FrameworkElement
|
||||
{
|
||||
public static T GetChildObject<T>(DependencyObject obj, string name) where T : FrameworkElement
|
||||
DependencyObject child = null;
|
||||
T grandChild = null;
|
||||
|
||||
for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
|
||||
{
|
||||
DependencyObject child = null;
|
||||
T grandChild = null;
|
||||
child = VisualTreeHelper.GetChild(obj, i);
|
||||
|
||||
for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
|
||||
if (child is T && (((T)child).Name == name | string.IsNullOrEmpty(name)))
|
||||
{
|
||||
child = VisualTreeHelper.GetChild(obj, i);
|
||||
|
||||
if (child is T && (((T)child).Name == name | string.IsNullOrEmpty(name)))
|
||||
{
|
||||
return (T)child;
|
||||
}
|
||||
else
|
||||
{
|
||||
grandChild = GetChildObject<T>(child, name);
|
||||
}
|
||||
if (grandChild != null)
|
||||
{
|
||||
return grandChild;
|
||||
}
|
||||
return (T)child;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> FindDescendants<T>(this DependencyObject depObj) where T : DependencyObject
|
||||
{
|
||||
if (depObj != null)
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(depObj, i);
|
||||
if (child != null && child is T)
|
||||
{
|
||||
yield return (T)child;
|
||||
}
|
||||
grandChild = GetChildObject<T>(child, name);
|
||||
}
|
||||
if (grandChild != null)
|
||||
{
|
||||
return grandChild;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (T childOfChild in FindDescendants<T>(child))
|
||||
{
|
||||
yield return childOfChild;
|
||||
}
|
||||
public static IEnumerable<T> FindDescendants<T>(this DependencyObject depObj) where T : DependencyObject
|
||||
{
|
||||
if (depObj != null)
|
||||
{
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(depObj, i);
|
||||
if (child != null && child is T)
|
||||
{
|
||||
yield return (T)child;
|
||||
}
|
||||
|
||||
foreach (T childOfChild in FindDescendants<T>(child))
|
||||
{
|
||||
yield return childOfChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,351 +16,350 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.UWP.Controls;
|
||||
|
||||
namespace Wino.Helpers
|
||||
namespace Wino.Helpers;
|
||||
|
||||
public static class XamlHelpers
|
||||
{
|
||||
public static class XamlHelpers
|
||||
private const string TwentyFourHourTimeFormat = "HH:mm";
|
||||
private const string TwelveHourTimeFormat = "hh:mm tt";
|
||||
|
||||
#region Converters
|
||||
|
||||
public static bool IsMultiple(int count) => count > 1;
|
||||
public static bool ReverseIsMultiple(int count) => count < 1;
|
||||
public static PopupPlacementMode GetPlaccementModeForCalendarType(CalendarDisplayType type)
|
||||
{
|
||||
private const string TwentyFourHourTimeFormat = "HH:mm";
|
||||
private const string TwelveHourTimeFormat = "hh:mm tt";
|
||||
|
||||
#region Converters
|
||||
|
||||
public static bool IsMultiple(int count) => count > 1;
|
||||
public static bool ReverseIsMultiple(int count) => count < 1;
|
||||
public static PopupPlacementMode GetPlaccementModeForCalendarType(CalendarDisplayType type)
|
||||
return type switch
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
CalendarDisplayType.Week => PopupPlacementMode.Right,
|
||||
_ => PopupPlacementMode.Bottom,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static Visibility ReverseBoolToVisibilityConverter(bool value) => value ? Visibility.Collapsed : Visibility.Visible;
|
||||
public static Visibility ReverseVisibilityConverter(Visibility visibility) => visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
|
||||
public static bool ReverseBoolConverter(bool value) => !value;
|
||||
public static bool ShouldDisplayPreview(string text) => text.Any(x => char.IsLetter(x));
|
||||
public static bool CountToBooleanConverter(int value) => value > 0;
|
||||
public static bool ObjectEquals(object obj1, object obj2) => object.Equals(obj1, obj2);
|
||||
public static Visibility CountToVisibilityConverter(int value) => value > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
public static Visibility CountToVisibilityConverterWithThreshold(int value, int threshold) => value > threshold ? Visibility.Visible : Visibility.Collapsed;
|
||||
public static InfoBarSeverity InfoBarSeverityConverter(InfoBarMessageType messageType)
|
||||
{
|
||||
return messageType switch
|
||||
{
|
||||
InfoBarMessageType.Information => InfoBarSeverity.Informational,
|
||||
InfoBarMessageType.Success => InfoBarSeverity.Success,
|
||||
InfoBarMessageType.Warning => InfoBarSeverity.Warning,
|
||||
InfoBarMessageType.Error => InfoBarSeverity.Error,
|
||||
_ => InfoBarSeverity.Informational,
|
||||
};
|
||||
}
|
||||
|
||||
public static SolidColorBrush GetReadableTextColor(string backgroundColor)
|
||||
{
|
||||
if (!backgroundColor.StartsWith("#")) throw new ArgumentException("Hex color must start with #.");
|
||||
|
||||
backgroundColor = backgroundColor.TrimStart('#');
|
||||
|
||||
if (backgroundColor.Length == 6)
|
||||
{
|
||||
var r = int.Parse(backgroundColor.Substring(0, 2), NumberStyles.HexNumber);
|
||||
var g = int.Parse(backgroundColor.Substring(2, 2), NumberStyles.HexNumber);
|
||||
var b = int.Parse(backgroundColor.Substring(4, 2), NumberStyles.HexNumber);
|
||||
|
||||
// Calculate relative luminance
|
||||
double luminance = (0.2126 * GetLinearValue(r)) +
|
||||
(0.7152 * GetLinearValue(g)) +
|
||||
(0.0722 * GetLinearValue(b));
|
||||
|
||||
return luminance > 0.5 ? new SolidColorBrush(Colors.Black) : new SolidColorBrush(Colors.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Hex color must be 6 characters long (e.g., #RRGGBB).");
|
||||
}
|
||||
}
|
||||
|
||||
private static double GetLinearValue(int colorComponent)
|
||||
{
|
||||
double sRGB = colorComponent / 255.0;
|
||||
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.Pow((sRGB + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
public static Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode NavigationViewDisplayModeConverter(SplitViewDisplayMode splitViewDisplayMode)
|
||||
{
|
||||
return splitViewDisplayMode switch
|
||||
{
|
||||
SplitViewDisplayMode.CompactOverlay => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact,
|
||||
SplitViewDisplayMode.CompactInline => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal,
|
||||
SplitViewDisplayMode.Overlay => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded,
|
||||
SplitViewDisplayMode.Inline => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded,
|
||||
_ => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal,
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetColorFromHex(Color color) => color.ToHex();
|
||||
public static Color GetWindowsColorFromHex(string hex) => hex.ToColor();
|
||||
|
||||
public static SolidColorBrush GetSolidColorBrushFromHex(string colorHex) => string.IsNullOrEmpty(colorHex) ? new SolidColorBrush(Colors.Transparent) : new SolidColorBrush(colorHex.ToColor());
|
||||
public static Visibility IsSelectionModeMultiple(ListViewSelectionMode mode) => mode == ListViewSelectionMode.Multiple ? Visibility.Visible : Visibility.Collapsed;
|
||||
public static FontWeight GetFontWeightBySyncState(bool isSyncing) => isSyncing ? FontWeights.SemiBold : FontWeights.Normal;
|
||||
public static FontWeight GetFontWeightByChildSelectedState(bool isChildSelected) => isChildSelected ? FontWeights.SemiBold : FontWeights.Normal;
|
||||
public static Visibility StringToVisibilityConverter(string value) => string.IsNullOrWhiteSpace(value) ? Visibility.Collapsed : Visibility.Visible;
|
||||
public static Visibility StringToVisibilityReversedConverter(string value) => string.IsNullOrWhiteSpace(value) ? Visibility.Visible : Visibility.Collapsed;
|
||||
public static string GetMailItemDisplaySummaryForListing(bool isDraft, DateTime receivedDate, bool prefer24HourTime)
|
||||
{
|
||||
if (isDraft)
|
||||
return Translator.Draft;
|
||||
else
|
||||
{
|
||||
var localTime = receivedDate.ToLocalTime();
|
||||
|
||||
return prefer24HourTime ? localTime.ToString(TwentyFourHourTimeFormat) : localTime.ToString(TwelveHourTimeFormat);
|
||||
}
|
||||
}
|
||||
public static string GetCreationDateString(DateTime date, bool prefer24HourTime)
|
||||
{
|
||||
var localTime = date.ToLocalTime();
|
||||
return $"{localTime.ToLongDateString()} {(prefer24HourTime ? localTime.ToString(TwentyFourHourTimeFormat) : localTime.ToString(TwelveHourTimeFormat))}";
|
||||
}
|
||||
public static string GetMailGroupDateString(object groupObject)
|
||||
{
|
||||
if (groupObject is string stringObject)
|
||||
return stringObject;
|
||||
|
||||
object dateObject = null;
|
||||
|
||||
// From regular mail header template
|
||||
if (groupObject is DateTime groupedDate)
|
||||
dateObject = groupedDate;
|
||||
else if (groupObject is IGrouping<object, IMailItem> groupKey)
|
||||
{
|
||||
// From semantic group header.
|
||||
dateObject = groupKey.Key;
|
||||
}
|
||||
|
||||
if (dateObject != null)
|
||||
{
|
||||
if (dateObject is DateTime dateTimeValue)
|
||||
{
|
||||
if (dateTimeValue == DateTime.Today)
|
||||
return Translator.Today;
|
||||
else if (dateTimeValue == DateTime.Today.AddDays(-1))
|
||||
return Translator.Yesterday;
|
||||
else
|
||||
return dateTimeValue.ToLongDateString();
|
||||
}
|
||||
else
|
||||
return dateObject.ToString();
|
||||
}
|
||||
|
||||
return Translator.UnknownDateHeader;
|
||||
}
|
||||
public static bool ConnectionStatusEquals(WinoServerConnectionStatus winoServerConnectionStatus, WinoServerConnectionStatus connectionStatus) => winoServerConnectionStatus == connectionStatus;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wino Font Icon Transformation
|
||||
|
||||
public static WinoIconGlyph GetWinoIconGlyph(FilterOptionType type) => type switch
|
||||
{
|
||||
FilterOptionType.All => WinoIconGlyph.Mail,
|
||||
FilterOptionType.Unread => WinoIconGlyph.MarkUnread,
|
||||
FilterOptionType.Flagged => WinoIconGlyph.Flag,
|
||||
FilterOptionType.Mentions => WinoIconGlyph.NewMail,
|
||||
FilterOptionType.Files => WinoIconGlyph.Attachment,
|
||||
_ => WinoIconGlyph.None,
|
||||
CalendarDisplayType.Week => PopupPlacementMode.Right,
|
||||
_ => PopupPlacementMode.Bottom,
|
||||
};
|
||||
|
||||
public static WinoIconGlyph GetWinoIconGlyph(SortingOptionType type) => type switch
|
||||
{
|
||||
SortingOptionType.Sender => WinoIconGlyph.SortTextDesc,
|
||||
SortingOptionType.ReceiveDate => WinoIconGlyph.SortLinesDesc,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
|
||||
public static WinoIconGlyph GetWinoIconGlyph(MailOperation operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
MailOperation.None => WinoIconGlyph.None,
|
||||
MailOperation.Archive => WinoIconGlyph.Archive,
|
||||
MailOperation.UnArchive => WinoIconGlyph.UnArchive,
|
||||
MailOperation.SoftDelete => WinoIconGlyph.Delete,
|
||||
MailOperation.HardDelete => WinoIconGlyph.Delete,
|
||||
MailOperation.Move => WinoIconGlyph.Forward,
|
||||
MailOperation.MoveToJunk => WinoIconGlyph.Blocked,
|
||||
MailOperation.MoveToFocused => WinoIconGlyph.None,
|
||||
MailOperation.MoveToOther => WinoIconGlyph.None,
|
||||
MailOperation.AlwaysMoveToOther => WinoIconGlyph.None,
|
||||
MailOperation.AlwaysMoveToFocused => WinoIconGlyph.None,
|
||||
MailOperation.SetFlag => WinoIconGlyph.Flag,
|
||||
MailOperation.ClearFlag => WinoIconGlyph.ClearFlag,
|
||||
MailOperation.MarkAsRead => WinoIconGlyph.MarkRead,
|
||||
MailOperation.MarkAsUnread => WinoIconGlyph.MarkUnread,
|
||||
MailOperation.MarkAsNotJunk => WinoIconGlyph.Blocked,
|
||||
MailOperation.Ignore => WinoIconGlyph.Ignore,
|
||||
MailOperation.Reply => WinoIconGlyph.Reply,
|
||||
MailOperation.ReplyAll => WinoIconGlyph.ReplyAll,
|
||||
MailOperation.Zoom => WinoIconGlyph.Zoom,
|
||||
MailOperation.SaveAs => WinoIconGlyph.Save,
|
||||
MailOperation.Print => WinoIconGlyph.Print,
|
||||
MailOperation.Find => WinoIconGlyph.Find,
|
||||
MailOperation.Forward => WinoIconGlyph.Forward,
|
||||
MailOperation.DarkEditor => WinoIconGlyph.DarkEditor,
|
||||
MailOperation.LightEditor => WinoIconGlyph.LightEditor,
|
||||
MailOperation.ViewMessageSource => WinoIconGlyph.ViewMessageSource,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
|
||||
public static WinoIconGlyph GetPathGeometry(FolderOperation operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
FolderOperation.None => WinoIconGlyph.None,
|
||||
FolderOperation.Pin => WinoIconGlyph.Pin,
|
||||
FolderOperation.Unpin => WinoIconGlyph.UnPin,
|
||||
FolderOperation.MarkAllAsRead => WinoIconGlyph.MarkRead,
|
||||
FolderOperation.DontSync => WinoIconGlyph.DontSync,
|
||||
FolderOperation.Empty => WinoIconGlyph.EmptyFolder,
|
||||
FolderOperation.Rename => WinoIconGlyph.Rename,
|
||||
FolderOperation.Delete => WinoIconGlyph.Delete,
|
||||
FolderOperation.Move => WinoIconGlyph.Forward,
|
||||
FolderOperation.TurnOffNotifications => WinoIconGlyph.TurnOfNotifications,
|
||||
FolderOperation.CreateSubFolder => WinoIconGlyph.CreateFolder,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
|
||||
public static WinoIconGlyph GetSpecialFolderPathIconGeometry(SpecialFolderType specialFolderType)
|
||||
{
|
||||
return specialFolderType switch
|
||||
{
|
||||
SpecialFolderType.Inbox => WinoIconGlyph.SpecialFolderInbox,
|
||||
SpecialFolderType.Starred => WinoIconGlyph.SpecialFolderStarred,
|
||||
SpecialFolderType.Important => WinoIconGlyph.SpecialFolderImportant,
|
||||
SpecialFolderType.Sent => WinoIconGlyph.SpecialFolderSent,
|
||||
SpecialFolderType.Draft => WinoIconGlyph.SpecialFolderDraft,
|
||||
SpecialFolderType.Archive => WinoIconGlyph.SpecialFolderArchive,
|
||||
SpecialFolderType.Deleted => WinoIconGlyph.SpecialFolderDeleted,
|
||||
SpecialFolderType.Junk => WinoIconGlyph.SpecialFolderJunk,
|
||||
SpecialFolderType.Chat => WinoIconGlyph.SpecialFolderChat,
|
||||
SpecialFolderType.Category => WinoIconGlyph.SpecialFolderCategory,
|
||||
SpecialFolderType.Unread => WinoIconGlyph.SpecialFolderUnread,
|
||||
SpecialFolderType.Forums => WinoIconGlyph.SpecialFolderForums,
|
||||
SpecialFolderType.Updates => WinoIconGlyph.SpecialFolderUpdated,
|
||||
SpecialFolderType.Personal => WinoIconGlyph.SpecialFolderPersonal,
|
||||
SpecialFolderType.Promotions => WinoIconGlyph.SpecialFolderPromotions,
|
||||
SpecialFolderType.Social => WinoIconGlyph.SpecialFolderSocial,
|
||||
SpecialFolderType.Other => WinoIconGlyph.SpecialFolderOther,
|
||||
SpecialFolderType.More => WinoIconGlyph.SpecialFolderMore,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static WinoIconGlyph GetProviderIcon(MailProviderType providerType, SpecialImapProvider specialImapProvider)
|
||||
{
|
||||
if (specialImapProvider == SpecialImapProvider.None)
|
||||
{
|
||||
return providerType switch
|
||||
{
|
||||
MailProviderType.Outlook => WinoIconGlyph.Microsoft,
|
||||
MailProviderType.Gmail => WinoIconGlyph.Google,
|
||||
MailProviderType.IMAP4 => WinoIconGlyph.IMAP,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return specialImapProvider switch
|
||||
{
|
||||
SpecialImapProvider.iCloud => WinoIconGlyph.Apple,
|
||||
SpecialImapProvider.Yahoo => WinoIconGlyph.Yahoo,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
}
|
||||
public static WinoIconGlyph GetProviderIcon(MailAccount account)
|
||||
=> GetProviderIcon(account.ProviderType, account.SpecialImapProvider);
|
||||
|
||||
public static Geometry GetPathGeometry(string pathMarkup)
|
||||
{
|
||||
string xaml =
|
||||
"<Path " +
|
||||
"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
|
||||
"<Path.Data>" + pathMarkup + "</Path.Data></Path>";
|
||||
var path = XamlReader.Load(xaml) as Windows.UI.Xaml.Shapes.Path;
|
||||
|
||||
Geometry geometry = path.Data;
|
||||
path.Data = null;
|
||||
return geometry;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internationalization
|
||||
|
||||
public static string GetOperationString(MailOperation operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
MailOperation.None => "unknown",
|
||||
MailOperation.Archive => Translator.MailOperation_Archive,
|
||||
MailOperation.UnArchive => Translator.MailOperation_Unarchive,
|
||||
MailOperation.SoftDelete => Translator.MailOperation_Delete,
|
||||
MailOperation.HardDelete => Translator.MailOperation_Delete,
|
||||
MailOperation.Move => Translator.MailOperation_Move,
|
||||
MailOperation.MoveToJunk => Translator.MailOperation_MoveJunk,
|
||||
MailOperation.MoveToFocused => Translator.MailOperation_MoveFocused,
|
||||
MailOperation.MoveToOther => Translator.MailOperation_MoveOther,
|
||||
MailOperation.AlwaysMoveToOther => Translator.MailOperation_AlwaysMoveOther,
|
||||
MailOperation.AlwaysMoveToFocused => Translator.MailOperation_AlwaysMoveFocused,
|
||||
MailOperation.SetFlag => Translator.MailOperation_SetFlag,
|
||||
MailOperation.ClearFlag => Translator.MailOperation_ClearFlag,
|
||||
MailOperation.MarkAsRead => Translator.MailOperation_MarkAsRead,
|
||||
MailOperation.MarkAsUnread => Translator.MailOperation_MarkAsUnread,
|
||||
MailOperation.MarkAsNotJunk => Translator.MailOperation_MarkNotJunk,
|
||||
MailOperation.Seperator => string.Empty,
|
||||
MailOperation.Ignore => Translator.MailOperation_Ignore,
|
||||
MailOperation.Reply => Translator.MailOperation_Reply,
|
||||
MailOperation.ReplyAll => Translator.MailOperation_ReplyAll,
|
||||
MailOperation.Zoom => Translator.MailOperation_Zoom,
|
||||
MailOperation.SaveAs => Translator.MailOperation_SaveAs,
|
||||
MailOperation.Find => Translator.MailOperation_Find,
|
||||
MailOperation.Forward => Translator.MailOperation_Forward,
|
||||
MailOperation.DarkEditor => string.Empty,
|
||||
MailOperation.LightEditor => string.Empty,
|
||||
MailOperation.Print => Translator.MailOperation_Print,
|
||||
MailOperation.ViewMessageSource => Translator.MailOperation_ViewMessageSource,
|
||||
MailOperation.Navigate => Translator.MailOperation_Navigate,
|
||||
_ => "unknown",
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetOperationString(FolderOperation operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
FolderOperation.None => string.Empty,
|
||||
FolderOperation.Pin => Translator.FolderOperation_Pin,
|
||||
FolderOperation.Unpin => Translator.FolderOperation_Unpin,
|
||||
FolderOperation.MarkAllAsRead => Translator.FolderOperation_MarkAllAsRead,
|
||||
FolderOperation.DontSync => Translator.FolderOperation_DontSync,
|
||||
FolderOperation.Empty => Translator.FolderOperation_Empty,
|
||||
FolderOperation.Rename => Translator.FolderOperation_Rename,
|
||||
FolderOperation.Delete => Translator.FolderOperation_Delete,
|
||||
FolderOperation.Move => Translator.FolderOperation_Move,
|
||||
FolderOperation.CreateSubFolder => Translator.FolderOperation_CreateSubFolder,
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static Visibility ReverseBoolToVisibilityConverter(bool value) => value ? Visibility.Collapsed : Visibility.Visible;
|
||||
public static Visibility ReverseVisibilityConverter(Visibility visibility) => visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
|
||||
public static bool ReverseBoolConverter(bool value) => !value;
|
||||
public static bool ShouldDisplayPreview(string text) => text.Any(x => char.IsLetter(x));
|
||||
public static bool CountToBooleanConverter(int value) => value > 0;
|
||||
public static bool ObjectEquals(object obj1, object obj2) => object.Equals(obj1, obj2);
|
||||
public static Visibility CountToVisibilityConverter(int value) => value > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
public static Visibility CountToVisibilityConverterWithThreshold(int value, int threshold) => value > threshold ? Visibility.Visible : Visibility.Collapsed;
|
||||
public static InfoBarSeverity InfoBarSeverityConverter(InfoBarMessageType messageType)
|
||||
{
|
||||
return messageType switch
|
||||
{
|
||||
InfoBarMessageType.Information => InfoBarSeverity.Informational,
|
||||
InfoBarMessageType.Success => InfoBarSeverity.Success,
|
||||
InfoBarMessageType.Warning => InfoBarSeverity.Warning,
|
||||
InfoBarMessageType.Error => InfoBarSeverity.Error,
|
||||
_ => InfoBarSeverity.Informational,
|
||||
};
|
||||
}
|
||||
|
||||
public static SolidColorBrush GetReadableTextColor(string backgroundColor)
|
||||
{
|
||||
if (!backgroundColor.StartsWith("#")) throw new ArgumentException("Hex color must start with #.");
|
||||
|
||||
backgroundColor = backgroundColor.TrimStart('#');
|
||||
|
||||
if (backgroundColor.Length == 6)
|
||||
{
|
||||
var r = int.Parse(backgroundColor.Substring(0, 2), NumberStyles.HexNumber);
|
||||
var g = int.Parse(backgroundColor.Substring(2, 2), NumberStyles.HexNumber);
|
||||
var b = int.Parse(backgroundColor.Substring(4, 2), NumberStyles.HexNumber);
|
||||
|
||||
// Calculate relative luminance
|
||||
double luminance = (0.2126 * GetLinearValue(r)) +
|
||||
(0.7152 * GetLinearValue(g)) +
|
||||
(0.0722 * GetLinearValue(b));
|
||||
|
||||
return luminance > 0.5 ? new SolidColorBrush(Colors.Black) : new SolidColorBrush(Colors.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Hex color must be 6 characters long (e.g., #RRGGBB).");
|
||||
}
|
||||
}
|
||||
|
||||
private static double GetLinearValue(int colorComponent)
|
||||
{
|
||||
double sRGB = colorComponent / 255.0;
|
||||
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.Pow((sRGB + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
public static Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode NavigationViewDisplayModeConverter(SplitViewDisplayMode splitViewDisplayMode)
|
||||
{
|
||||
return splitViewDisplayMode switch
|
||||
{
|
||||
SplitViewDisplayMode.CompactOverlay => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact,
|
||||
SplitViewDisplayMode.CompactInline => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal,
|
||||
SplitViewDisplayMode.Overlay => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded,
|
||||
SplitViewDisplayMode.Inline => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded,
|
||||
_ => Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal,
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetColorFromHex(Color color) => color.ToHex();
|
||||
public static Color GetWindowsColorFromHex(string hex) => hex.ToColor();
|
||||
|
||||
public static SolidColorBrush GetSolidColorBrushFromHex(string colorHex) => string.IsNullOrEmpty(colorHex) ? new SolidColorBrush(Colors.Transparent) : new SolidColorBrush(colorHex.ToColor());
|
||||
public static Visibility IsSelectionModeMultiple(ListViewSelectionMode mode) => mode == ListViewSelectionMode.Multiple ? Visibility.Visible : Visibility.Collapsed;
|
||||
public static FontWeight GetFontWeightBySyncState(bool isSyncing) => isSyncing ? FontWeights.SemiBold : FontWeights.Normal;
|
||||
public static FontWeight GetFontWeightByChildSelectedState(bool isChildSelected) => isChildSelected ? FontWeights.SemiBold : FontWeights.Normal;
|
||||
public static Visibility StringToVisibilityConverter(string value) => string.IsNullOrWhiteSpace(value) ? Visibility.Collapsed : Visibility.Visible;
|
||||
public static Visibility StringToVisibilityReversedConverter(string value) => string.IsNullOrWhiteSpace(value) ? Visibility.Visible : Visibility.Collapsed;
|
||||
public static string GetMailItemDisplaySummaryForListing(bool isDraft, DateTime receivedDate, bool prefer24HourTime)
|
||||
{
|
||||
if (isDraft)
|
||||
return Translator.Draft;
|
||||
else
|
||||
{
|
||||
var localTime = receivedDate.ToLocalTime();
|
||||
|
||||
return prefer24HourTime ? localTime.ToString(TwentyFourHourTimeFormat) : localTime.ToString(TwelveHourTimeFormat);
|
||||
}
|
||||
}
|
||||
public static string GetCreationDateString(DateTime date, bool prefer24HourTime)
|
||||
{
|
||||
var localTime = date.ToLocalTime();
|
||||
return $"{localTime.ToLongDateString()} {(prefer24HourTime ? localTime.ToString(TwentyFourHourTimeFormat) : localTime.ToString(TwelveHourTimeFormat))}";
|
||||
}
|
||||
public static string GetMailGroupDateString(object groupObject)
|
||||
{
|
||||
if (groupObject is string stringObject)
|
||||
return stringObject;
|
||||
|
||||
object dateObject = null;
|
||||
|
||||
// From regular mail header template
|
||||
if (groupObject is DateTime groupedDate)
|
||||
dateObject = groupedDate;
|
||||
else if (groupObject is IGrouping<object, IMailItem> groupKey)
|
||||
{
|
||||
// From semantic group header.
|
||||
dateObject = groupKey.Key;
|
||||
}
|
||||
|
||||
if (dateObject != null)
|
||||
{
|
||||
if (dateObject is DateTime dateTimeValue)
|
||||
{
|
||||
if (dateTimeValue == DateTime.Today)
|
||||
return Translator.Today;
|
||||
else if (dateTimeValue == DateTime.Today.AddDays(-1))
|
||||
return Translator.Yesterday;
|
||||
else
|
||||
return dateTimeValue.ToLongDateString();
|
||||
}
|
||||
else
|
||||
return dateObject.ToString();
|
||||
}
|
||||
|
||||
return Translator.UnknownDateHeader;
|
||||
}
|
||||
public static bool ConnectionStatusEquals(WinoServerConnectionStatus winoServerConnectionStatus, WinoServerConnectionStatus connectionStatus) => winoServerConnectionStatus == connectionStatus;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wino Font Icon Transformation
|
||||
|
||||
public static WinoIconGlyph GetWinoIconGlyph(FilterOptionType type) => type switch
|
||||
{
|
||||
FilterOptionType.All => WinoIconGlyph.Mail,
|
||||
FilterOptionType.Unread => WinoIconGlyph.MarkUnread,
|
||||
FilterOptionType.Flagged => WinoIconGlyph.Flag,
|
||||
FilterOptionType.Mentions => WinoIconGlyph.NewMail,
|
||||
FilterOptionType.Files => WinoIconGlyph.Attachment,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
|
||||
public static WinoIconGlyph GetWinoIconGlyph(SortingOptionType type) => type switch
|
||||
{
|
||||
SortingOptionType.Sender => WinoIconGlyph.SortTextDesc,
|
||||
SortingOptionType.ReceiveDate => WinoIconGlyph.SortLinesDesc,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
|
||||
public static WinoIconGlyph GetWinoIconGlyph(MailOperation operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
MailOperation.None => WinoIconGlyph.None,
|
||||
MailOperation.Archive => WinoIconGlyph.Archive,
|
||||
MailOperation.UnArchive => WinoIconGlyph.UnArchive,
|
||||
MailOperation.SoftDelete => WinoIconGlyph.Delete,
|
||||
MailOperation.HardDelete => WinoIconGlyph.Delete,
|
||||
MailOperation.Move => WinoIconGlyph.Forward,
|
||||
MailOperation.MoveToJunk => WinoIconGlyph.Blocked,
|
||||
MailOperation.MoveToFocused => WinoIconGlyph.None,
|
||||
MailOperation.MoveToOther => WinoIconGlyph.None,
|
||||
MailOperation.AlwaysMoveToOther => WinoIconGlyph.None,
|
||||
MailOperation.AlwaysMoveToFocused => WinoIconGlyph.None,
|
||||
MailOperation.SetFlag => WinoIconGlyph.Flag,
|
||||
MailOperation.ClearFlag => WinoIconGlyph.ClearFlag,
|
||||
MailOperation.MarkAsRead => WinoIconGlyph.MarkRead,
|
||||
MailOperation.MarkAsUnread => WinoIconGlyph.MarkUnread,
|
||||
MailOperation.MarkAsNotJunk => WinoIconGlyph.Blocked,
|
||||
MailOperation.Ignore => WinoIconGlyph.Ignore,
|
||||
MailOperation.Reply => WinoIconGlyph.Reply,
|
||||
MailOperation.ReplyAll => WinoIconGlyph.ReplyAll,
|
||||
MailOperation.Zoom => WinoIconGlyph.Zoom,
|
||||
MailOperation.SaveAs => WinoIconGlyph.Save,
|
||||
MailOperation.Print => WinoIconGlyph.Print,
|
||||
MailOperation.Find => WinoIconGlyph.Find,
|
||||
MailOperation.Forward => WinoIconGlyph.Forward,
|
||||
MailOperation.DarkEditor => WinoIconGlyph.DarkEditor,
|
||||
MailOperation.LightEditor => WinoIconGlyph.LightEditor,
|
||||
MailOperation.ViewMessageSource => WinoIconGlyph.ViewMessageSource,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
|
||||
public static WinoIconGlyph GetPathGeometry(FolderOperation operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
FolderOperation.None => WinoIconGlyph.None,
|
||||
FolderOperation.Pin => WinoIconGlyph.Pin,
|
||||
FolderOperation.Unpin => WinoIconGlyph.UnPin,
|
||||
FolderOperation.MarkAllAsRead => WinoIconGlyph.MarkRead,
|
||||
FolderOperation.DontSync => WinoIconGlyph.DontSync,
|
||||
FolderOperation.Empty => WinoIconGlyph.EmptyFolder,
|
||||
FolderOperation.Rename => WinoIconGlyph.Rename,
|
||||
FolderOperation.Delete => WinoIconGlyph.Delete,
|
||||
FolderOperation.Move => WinoIconGlyph.Forward,
|
||||
FolderOperation.TurnOffNotifications => WinoIconGlyph.TurnOfNotifications,
|
||||
FolderOperation.CreateSubFolder => WinoIconGlyph.CreateFolder,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
|
||||
public static WinoIconGlyph GetSpecialFolderPathIconGeometry(SpecialFolderType specialFolderType)
|
||||
{
|
||||
return specialFolderType switch
|
||||
{
|
||||
SpecialFolderType.Inbox => WinoIconGlyph.SpecialFolderInbox,
|
||||
SpecialFolderType.Starred => WinoIconGlyph.SpecialFolderStarred,
|
||||
SpecialFolderType.Important => WinoIconGlyph.SpecialFolderImportant,
|
||||
SpecialFolderType.Sent => WinoIconGlyph.SpecialFolderSent,
|
||||
SpecialFolderType.Draft => WinoIconGlyph.SpecialFolderDraft,
|
||||
SpecialFolderType.Archive => WinoIconGlyph.SpecialFolderArchive,
|
||||
SpecialFolderType.Deleted => WinoIconGlyph.SpecialFolderDeleted,
|
||||
SpecialFolderType.Junk => WinoIconGlyph.SpecialFolderJunk,
|
||||
SpecialFolderType.Chat => WinoIconGlyph.SpecialFolderChat,
|
||||
SpecialFolderType.Category => WinoIconGlyph.SpecialFolderCategory,
|
||||
SpecialFolderType.Unread => WinoIconGlyph.SpecialFolderUnread,
|
||||
SpecialFolderType.Forums => WinoIconGlyph.SpecialFolderForums,
|
||||
SpecialFolderType.Updates => WinoIconGlyph.SpecialFolderUpdated,
|
||||
SpecialFolderType.Personal => WinoIconGlyph.SpecialFolderPersonal,
|
||||
SpecialFolderType.Promotions => WinoIconGlyph.SpecialFolderPromotions,
|
||||
SpecialFolderType.Social => WinoIconGlyph.SpecialFolderSocial,
|
||||
SpecialFolderType.Other => WinoIconGlyph.SpecialFolderOther,
|
||||
SpecialFolderType.More => WinoIconGlyph.SpecialFolderMore,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static WinoIconGlyph GetProviderIcon(MailProviderType providerType, SpecialImapProvider specialImapProvider)
|
||||
{
|
||||
if (specialImapProvider == SpecialImapProvider.None)
|
||||
{
|
||||
return providerType switch
|
||||
{
|
||||
MailProviderType.Outlook => WinoIconGlyph.Microsoft,
|
||||
MailProviderType.Gmail => WinoIconGlyph.Google,
|
||||
MailProviderType.IMAP4 => WinoIconGlyph.IMAP,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return specialImapProvider switch
|
||||
{
|
||||
SpecialImapProvider.iCloud => WinoIconGlyph.Apple,
|
||||
SpecialImapProvider.Yahoo => WinoIconGlyph.Yahoo,
|
||||
_ => WinoIconGlyph.None,
|
||||
};
|
||||
}
|
||||
}
|
||||
public static WinoIconGlyph GetProviderIcon(MailAccount account)
|
||||
=> GetProviderIcon(account.ProviderType, account.SpecialImapProvider);
|
||||
|
||||
public static Geometry GetPathGeometry(string pathMarkup)
|
||||
{
|
||||
string xaml =
|
||||
"<Path " +
|
||||
"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
|
||||
"<Path.Data>" + pathMarkup + "</Path.Data></Path>";
|
||||
var path = XamlReader.Load(xaml) as Windows.UI.Xaml.Shapes.Path;
|
||||
|
||||
Geometry geometry = path.Data;
|
||||
path.Data = null;
|
||||
return geometry;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internationalization
|
||||
|
||||
public static string GetOperationString(MailOperation operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
MailOperation.None => "unknown",
|
||||
MailOperation.Archive => Translator.MailOperation_Archive,
|
||||
MailOperation.UnArchive => Translator.MailOperation_Unarchive,
|
||||
MailOperation.SoftDelete => Translator.MailOperation_Delete,
|
||||
MailOperation.HardDelete => Translator.MailOperation_Delete,
|
||||
MailOperation.Move => Translator.MailOperation_Move,
|
||||
MailOperation.MoveToJunk => Translator.MailOperation_MoveJunk,
|
||||
MailOperation.MoveToFocused => Translator.MailOperation_MoveFocused,
|
||||
MailOperation.MoveToOther => Translator.MailOperation_MoveOther,
|
||||
MailOperation.AlwaysMoveToOther => Translator.MailOperation_AlwaysMoveOther,
|
||||
MailOperation.AlwaysMoveToFocused => Translator.MailOperation_AlwaysMoveFocused,
|
||||
MailOperation.SetFlag => Translator.MailOperation_SetFlag,
|
||||
MailOperation.ClearFlag => Translator.MailOperation_ClearFlag,
|
||||
MailOperation.MarkAsRead => Translator.MailOperation_MarkAsRead,
|
||||
MailOperation.MarkAsUnread => Translator.MailOperation_MarkAsUnread,
|
||||
MailOperation.MarkAsNotJunk => Translator.MailOperation_MarkNotJunk,
|
||||
MailOperation.Seperator => string.Empty,
|
||||
MailOperation.Ignore => Translator.MailOperation_Ignore,
|
||||
MailOperation.Reply => Translator.MailOperation_Reply,
|
||||
MailOperation.ReplyAll => Translator.MailOperation_ReplyAll,
|
||||
MailOperation.Zoom => Translator.MailOperation_Zoom,
|
||||
MailOperation.SaveAs => Translator.MailOperation_SaveAs,
|
||||
MailOperation.Find => Translator.MailOperation_Find,
|
||||
MailOperation.Forward => Translator.MailOperation_Forward,
|
||||
MailOperation.DarkEditor => string.Empty,
|
||||
MailOperation.LightEditor => string.Empty,
|
||||
MailOperation.Print => Translator.MailOperation_Print,
|
||||
MailOperation.ViewMessageSource => Translator.MailOperation_ViewMessageSource,
|
||||
MailOperation.Navigate => Translator.MailOperation_Navigate,
|
||||
_ => "unknown",
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetOperationString(FolderOperation operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
FolderOperation.None => string.Empty,
|
||||
FolderOperation.Pin => Translator.FolderOperation_Pin,
|
||||
FolderOperation.Unpin => Translator.FolderOperation_Unpin,
|
||||
FolderOperation.MarkAllAsRead => Translator.FolderOperation_MarkAllAsRead,
|
||||
FolderOperation.DontSync => Translator.FolderOperation_DontSync,
|
||||
FolderOperation.Empty => Translator.FolderOperation_Empty,
|
||||
FolderOperation.Rename => Translator.FolderOperation_Rename,
|
||||
FolderOperation.Delete => Translator.FolderOperation_Delete,
|
||||
FolderOperation.Move => Translator.FolderOperation_Move,
|
||||
FolderOperation.CreateSubFolder => Translator.FolderOperation_CreateSubFolder,
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -5,27 +5,26 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Personalization;
|
||||
using Wino.Services;
|
||||
|
||||
namespace Wino.Core.UWP.Models.Personalization
|
||||
namespace Wino.Core.UWP.Models.Personalization;
|
||||
|
||||
/// <summary>
|
||||
/// Custom themes that are generated by users.
|
||||
/// </summary>
|
||||
public class CustomAppTheme : AppThemeBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom themes that are generated by users.
|
||||
/// </summary>
|
||||
public class CustomAppTheme : AppThemeBase
|
||||
public CustomAppTheme(CustomThemeMetadata metadata) : base(metadata.Name, metadata.Id)
|
||||
{
|
||||
public CustomAppTheme(CustomThemeMetadata metadata) : base(metadata.Name, metadata.Id)
|
||||
{
|
||||
AccentColor = metadata.AccentColorHex;
|
||||
}
|
||||
AccentColor = metadata.AccentColorHex;
|
||||
}
|
||||
|
||||
public override AppThemeType AppThemeType => AppThemeType.Custom;
|
||||
public override AppThemeType AppThemeType => AppThemeType.Custom;
|
||||
|
||||
public override string GetBackgroundPreviewImagePath()
|
||||
=> $"ms-appdata:///local/{ThemeService.CustomThemeFolderName}/{Id}_preview.jpg";
|
||||
public override string GetBackgroundPreviewImagePath()
|
||||
=> $"ms-appdata:///local/{ThemeService.CustomThemeFolderName}/{Id}_preview.jpg";
|
||||
|
||||
public override async Task<string> GetThemeResourceDictionaryContentAsync()
|
||||
{
|
||||
var customAppThemeFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Wino.Core.UWP/AppThemes/Custom.xaml"));
|
||||
return await FileIO.ReadTextAsync(customAppThemeFile);
|
||||
}
|
||||
public override async Task<string> GetThemeResourceDictionaryContentAsync()
|
||||
{
|
||||
var customAppThemeFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Wino.Core.UWP/AppThemes/Custom.xaml"));
|
||||
return await FileIO.ReadTextAsync(customAppThemeFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,31 +4,30 @@ using Windows.Storage;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Personalization;
|
||||
|
||||
namespace Wino.Core.UWP.Models.Personalization
|
||||
namespace Wino.Core.UWP.Models.Personalization;
|
||||
|
||||
/// <summary>
|
||||
/// Forest, Nighty, Clouds etc. applies to pre-defined themes in Wino.
|
||||
/// </summary>
|
||||
public class PreDefinedAppTheme : AppThemeBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Forest, Nighty, Clouds etc. applies to pre-defined themes in Wino.
|
||||
/// </summary>
|
||||
public class PreDefinedAppTheme : AppThemeBase
|
||||
public PreDefinedAppTheme(string themeName,
|
||||
Guid id,
|
||||
string accentColor = "",
|
||||
ApplicationElementTheme forcedElementTheme = ApplicationElementTheme.Default) : base(themeName, id)
|
||||
{
|
||||
public PreDefinedAppTheme(string themeName,
|
||||
Guid id,
|
||||
string accentColor = "",
|
||||
ApplicationElementTheme forcedElementTheme = ApplicationElementTheme.Default) : base(themeName, id)
|
||||
{
|
||||
AccentColor = accentColor;
|
||||
ForceElementTheme = forcedElementTheme;
|
||||
}
|
||||
AccentColor = accentColor;
|
||||
ForceElementTheme = forcedElementTheme;
|
||||
}
|
||||
|
||||
public override AppThemeType AppThemeType => AppThemeType.PreDefined;
|
||||
public override AppThemeType AppThemeType => AppThemeType.PreDefined;
|
||||
|
||||
public override string GetBackgroundPreviewImagePath()
|
||||
=> $"ms-appx:///Wino.Core.UWP/BackgroundImages/{ThemeName}.jpg";
|
||||
public override string GetBackgroundPreviewImagePath()
|
||||
=> $"ms-appx:///Wino.Core.UWP/BackgroundImages/{ThemeName}.jpg";
|
||||
|
||||
public override async Task<string> GetThemeResourceDictionaryContentAsync()
|
||||
{
|
||||
var xamlDictionaryFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Wino.Core.UWP/AppThemes/{ThemeName}.xaml"));
|
||||
return await FileIO.ReadTextAsync(xamlDictionaryFile);
|
||||
}
|
||||
public override async Task<string> GetThemeResourceDictionaryContentAsync()
|
||||
{
|
||||
var xamlDictionaryFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Wino.Core.UWP/AppThemes/{ThemeName}.xaml"));
|
||||
return await FileIO.ReadTextAsync(xamlDictionaryFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.UWP.Models.Personalization
|
||||
{
|
||||
// Mica - Acrylic.
|
||||
public class SystemAppTheme : PreDefinedAppTheme
|
||||
{
|
||||
public SystemAppTheme(string themeName, Guid id) : base(themeName, id, "") { }
|
||||
namespace Wino.Core.UWP.Models.Personalization;
|
||||
|
||||
public override AppThemeType AppThemeType => AppThemeType.System;
|
||||
}
|
||||
// Mica - Acrylic.
|
||||
public class SystemAppTheme : PreDefinedAppTheme
|
||||
{
|
||||
public SystemAppTheme(string themeName, Guid id) : base(themeName, id, "") { }
|
||||
|
||||
public override AppThemeType AppThemeType => AppThemeType.System;
|
||||
}
|
||||
|
||||
@@ -2,24 +2,23 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.UWP.Models.Personalization;
|
||||
|
||||
namespace Wino.Core.UWP.Selectors
|
||||
namespace Wino.Core.UWP.Selectors;
|
||||
|
||||
public partial class AppThemePreviewTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public partial class AppThemePreviewTemplateSelector : DataTemplateSelector
|
||||
public DataTemplate SystemThemeTemplate { get; set; }
|
||||
public DataTemplate PreDefinedThemeTemplate { get; set; }
|
||||
public DataTemplate CustomAppTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
public DataTemplate SystemThemeTemplate { get; set; }
|
||||
public DataTemplate PreDefinedThemeTemplate { get; set; }
|
||||
public DataTemplate CustomAppTemplate { get; set; }
|
||||
if (item is SystemAppTheme)
|
||||
return SystemThemeTemplate;
|
||||
else if (item is PreDefinedAppTheme)
|
||||
return PreDefinedThemeTemplate;
|
||||
else if (item is CustomAppTheme)
|
||||
return CustomAppTemplate;
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
if (item is SystemAppTheme)
|
||||
return SystemThemeTemplate;
|
||||
else if (item is PreDefinedAppTheme)
|
||||
return PreDefinedThemeTemplate;
|
||||
else if (item is CustomAppTheme)
|
||||
return CustomAppTemplate;
|
||||
|
||||
return base.SelectTemplateCore(item);
|
||||
}
|
||||
return base.SelectTemplateCore(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,36 +3,35 @@ using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.UWP.Selectors
|
||||
namespace Wino.Core.UWP.Selectors;
|
||||
|
||||
public partial class CustomWinoMessageDialogIconSelector : DataTemplateSelector
|
||||
{
|
||||
public partial 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)
|
||||
{
|
||||
public DataTemplate InfoIconTemplate { get; set; }
|
||||
public DataTemplate WarningIconTemplate { get; set; }
|
||||
public DataTemplate QuestionIconTemplate { get; set; }
|
||||
public DataTemplate ErrorIconTemplate { get; set; }
|
||||
if (item == null) return null;
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
if (item is WinoCustomMessageDialogIcon icon)
|
||||
{
|
||||
if (item == null) return null;
|
||||
|
||||
if (item is WinoCustomMessageDialogIcon icon)
|
||||
switch (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.");
|
||||
}
|
||||
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);
|
||||
}
|
||||
return base.SelectTemplateCore(item, container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,51 +2,50 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.UWP.Selectors
|
||||
namespace Wino.Core.UWP.Selectors;
|
||||
|
||||
public partial class FileAttachmentTypeSelector : DataTemplateSelector
|
||||
{
|
||||
public partial class FileAttachmentTypeSelector : DataTemplateSelector
|
||||
public DataTemplate None { get; set; }
|
||||
public DataTemplate Executable { get; set; }
|
||||
public DataTemplate Image { get; set; }
|
||||
public DataTemplate Audio { get; set; }
|
||||
public DataTemplate Video { get; set; }
|
||||
public DataTemplate PDF { get; set; }
|
||||
public DataTemplate HTML { get; set; }
|
||||
public DataTemplate RarArchive { get; set; }
|
||||
public DataTemplate Archive { get; set; }
|
||||
public DataTemplate Other { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
public DataTemplate None { get; set; }
|
||||
public DataTemplate Executable { get; set; }
|
||||
public DataTemplate Image { get; set; }
|
||||
public DataTemplate Audio { get; set; }
|
||||
public DataTemplate Video { get; set; }
|
||||
public DataTemplate PDF { get; set; }
|
||||
public DataTemplate HTML { get; set; }
|
||||
public DataTemplate RarArchive { get; set; }
|
||||
public DataTemplate Archive { get; set; }
|
||||
public DataTemplate Other { get; set; }
|
||||
if (item == null)
|
||||
return None;
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
var type = (MailAttachmentType)item;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
if (item == null)
|
||||
case MailAttachmentType.None:
|
||||
return None;
|
||||
|
||||
var type = (MailAttachmentType)item;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MailAttachmentType.None:
|
||||
return None;
|
||||
case MailAttachmentType.Executable:
|
||||
return Executable;
|
||||
case MailAttachmentType.Image:
|
||||
return Image;
|
||||
case MailAttachmentType.Audio:
|
||||
return Audio;
|
||||
case MailAttachmentType.Video:
|
||||
return Video;
|
||||
case MailAttachmentType.PDF:
|
||||
return PDF;
|
||||
case MailAttachmentType.HTML:
|
||||
return HTML;
|
||||
case MailAttachmentType.RarArchive:
|
||||
return RarArchive;
|
||||
case MailAttachmentType.Archive:
|
||||
return Archive;
|
||||
default:
|
||||
return Other;
|
||||
}
|
||||
case MailAttachmentType.Executable:
|
||||
return Executable;
|
||||
case MailAttachmentType.Image:
|
||||
return Image;
|
||||
case MailAttachmentType.Audio:
|
||||
return Audio;
|
||||
case MailAttachmentType.Video:
|
||||
return Video;
|
||||
case MailAttachmentType.PDF:
|
||||
return PDF;
|
||||
case MailAttachmentType.HTML:
|
||||
return HTML;
|
||||
case MailAttachmentType.RarArchive:
|
||||
return RarArchive;
|
||||
case MailAttachmentType.Archive:
|
||||
return Archive;
|
||||
default:
|
||||
return Other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,58 +2,57 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.MenuItems;
|
||||
|
||||
namespace Wino.Core.UWP.Selectors
|
||||
{
|
||||
public partial class NavigationMenuTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate MenuItemTemplate { get; set; }
|
||||
public DataTemplate AccountManagementTemplate { get; set; }
|
||||
public DataTemplate ClickableAccountMenuTemplate { get; set; }
|
||||
public DataTemplate MergedAccountTemplate { get; set; }
|
||||
public DataTemplate MergedAccountFolderTemplate { get; set; }
|
||||
public DataTemplate MergedAccountMoreExpansionItemTemplate { get; set; }
|
||||
public DataTemplate FolderMenuTemplate { get; set; }
|
||||
public DataTemplate SettingsItemTemplate { get; set; }
|
||||
public DataTemplate MoreItemsFolderTemplate { get; set; }
|
||||
public DataTemplate RatingItemTemplate { get; set; }
|
||||
public DataTemplate CreateNewFolderTemplate { get; set; }
|
||||
public DataTemplate SeperatorTemplate { get; set; }
|
||||
public DataTemplate NewMailTemplate { get; set; }
|
||||
public DataTemplate CategoryItemsTemplate { get; set; }
|
||||
public DataTemplate FixAuthenticationIssueTemplate { get; set; }
|
||||
public DataTemplate FixMissingFolderConfigTemplate { get; set; }
|
||||
namespace Wino.Core.UWP.Selectors;
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
public partial class NavigationMenuTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate MenuItemTemplate { get; set; }
|
||||
public DataTemplate AccountManagementTemplate { get; set; }
|
||||
public DataTemplate ClickableAccountMenuTemplate { get; set; }
|
||||
public DataTemplate MergedAccountTemplate { get; set; }
|
||||
public DataTemplate MergedAccountFolderTemplate { get; set; }
|
||||
public DataTemplate MergedAccountMoreExpansionItemTemplate { get; set; }
|
||||
public DataTemplate FolderMenuTemplate { get; set; }
|
||||
public DataTemplate SettingsItemTemplate { get; set; }
|
||||
public DataTemplate MoreItemsFolderTemplate { get; set; }
|
||||
public DataTemplate RatingItemTemplate { get; set; }
|
||||
public DataTemplate CreateNewFolderTemplate { get; set; }
|
||||
public DataTemplate SeperatorTemplate { get; set; }
|
||||
public DataTemplate NewMailTemplate { get; set; }
|
||||
public DataTemplate CategoryItemsTemplate { get; set; }
|
||||
public DataTemplate FixAuthenticationIssueTemplate { get; set; }
|
||||
public DataTemplate FixMissingFolderConfigTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
if (item is NewMailMenuItem)
|
||||
return NewMailTemplate;
|
||||
else if (item is SettingsItem)
|
||||
return SettingsItemTemplate;
|
||||
else if (item is SeperatorItem)
|
||||
return SeperatorTemplate;
|
||||
else if (item is AccountMenuItem accountMenuItem)
|
||||
// Merged inbox account menu items must be nested.
|
||||
return ClickableAccountMenuTemplate;
|
||||
else if (item is ManageAccountsMenuItem)
|
||||
return AccountManagementTemplate;
|
||||
else if (item is RateMenuItem)
|
||||
return RatingItemTemplate;
|
||||
else if (item is MergedAccountMenuItem)
|
||||
return MergedAccountTemplate;
|
||||
else if (item is MergedAccountMoreFolderMenuItem)
|
||||
return MergedAccountMoreExpansionItemTemplate;
|
||||
else if (item is MergedAccountFolderMenuItem)
|
||||
return MergedAccountFolderTemplate;
|
||||
else if (item is FolderMenuItem)
|
||||
return FolderMenuTemplate;
|
||||
else if (item is FixAccountIssuesMenuItem fixAccountIssuesMenuItem)
|
||||
return fixAccountIssuesMenuItem.Account.AttentionReason == Domain.Enums.AccountAttentionReason.MissingSystemFolderConfiguration
|
||||
? FixMissingFolderConfigTemplate : FixAuthenticationIssueTemplate;
|
||||
else
|
||||
{
|
||||
if (item is NewMailMenuItem)
|
||||
return NewMailTemplate;
|
||||
else if (item is SettingsItem)
|
||||
return SettingsItemTemplate;
|
||||
else if (item is SeperatorItem)
|
||||
return SeperatorTemplate;
|
||||
else if (item is AccountMenuItem accountMenuItem)
|
||||
// Merged inbox account menu items must be nested.
|
||||
return ClickableAccountMenuTemplate;
|
||||
else if (item is ManageAccountsMenuItem)
|
||||
return AccountManagementTemplate;
|
||||
else if (item is RateMenuItem)
|
||||
return RatingItemTemplate;
|
||||
else if (item is MergedAccountMenuItem)
|
||||
return MergedAccountTemplate;
|
||||
else if (item is MergedAccountMoreFolderMenuItem)
|
||||
return MergedAccountMoreExpansionItemTemplate;
|
||||
else if (item is MergedAccountFolderMenuItem)
|
||||
return MergedAccountFolderTemplate;
|
||||
else if (item is FolderMenuItem)
|
||||
return FolderMenuTemplate;
|
||||
else if (item is FixAccountIssuesMenuItem fixAccountIssuesMenuItem)
|
||||
return fixAccountIssuesMenuItem.Account.AttentionReason == Domain.Enums.AccountAttentionReason.MissingSystemFolderConfiguration
|
||||
? FixMissingFolderConfigTemplate : FixAuthenticationIssueTemplate;
|
||||
else
|
||||
{
|
||||
var type = item.GetType();
|
||||
return null;
|
||||
}
|
||||
var type = item.GetType();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,24 @@ using Windows.UI.Xaml;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.UWP;
|
||||
|
||||
namespace Wino.Services
|
||||
namespace Wino.Services;
|
||||
|
||||
public class ApplicationResourceManager : IApplicationResourceManager<ResourceDictionary>
|
||||
{
|
||||
public class ApplicationResourceManager : IApplicationResourceManager<ResourceDictionary>
|
||||
{
|
||||
public void AddResource(ResourceDictionary resource)
|
||||
=> WinoApplication.Current.Resources.MergedDictionaries.Add(resource);
|
||||
public void RemoveResource(ResourceDictionary resource)
|
||||
=> WinoApplication.Current.Resources.MergedDictionaries.Remove(resource);
|
||||
public void AddResource(ResourceDictionary resource)
|
||||
=> WinoApplication.Current.Resources.MergedDictionaries.Add(resource);
|
||||
public void RemoveResource(ResourceDictionary resource)
|
||||
=> WinoApplication.Current.Resources.MergedDictionaries.Remove(resource);
|
||||
|
||||
public bool ContainsResourceKey(string resourceKey)
|
||||
=> WinoApplication.Current.Resources.ContainsKey(resourceKey);
|
||||
public bool ContainsResourceKey(string resourceKey)
|
||||
=> WinoApplication.Current.Resources.ContainsKey(resourceKey);
|
||||
|
||||
public ResourceDictionary GetLastResource()
|
||||
=> WinoApplication.Current.Resources.MergedDictionaries.LastOrDefault();
|
||||
public ResourceDictionary GetLastResource()
|
||||
=> WinoApplication.Current.Resources.MergedDictionaries.LastOrDefault();
|
||||
|
||||
public void ReplaceResource(string resourceKey, object resource)
|
||||
=> WinoApplication.Current.Resources[resourceKey] = resource;
|
||||
public void ReplaceResource(string resourceKey, object resource)
|
||||
=> WinoApplication.Current.Resources[resourceKey] = resource;
|
||||
|
||||
public TReturnType GetResource<TReturnType>(string resourceKey)
|
||||
=> (TReturnType)WinoApplication.Current.Resources[resourceKey];
|
||||
}
|
||||
public TReturnType GetResource<TReturnType>(string resourceKey)
|
||||
=> (TReturnType)WinoApplication.Current.Resources[resourceKey];
|
||||
}
|
||||
|
||||
@@ -5,59 +5,58 @@ using Serilog;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class BackgroundTaskService : IBackgroundTaskService
|
||||
{
|
||||
public class BackgroundTaskService : IBackgroundTaskService
|
||||
private const string IsBackgroundTasksUnregisteredKey = nameof(IsBackgroundTasksUnregisteredKey);
|
||||
public const string ToastNotificationActivationHandlerTaskName = "ToastNotificationActivationHandlerTask";
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
|
||||
public BackgroundTaskService(IConfigurationService configurationService)
|
||||
{
|
||||
private const string IsBackgroundTasksUnregisteredKey = nameof(IsBackgroundTasksUnregisteredKey);
|
||||
public const string ToastNotificationActivationHandlerTaskName = "ToastNotificationActivationHandlerTask";
|
||||
_configurationService = configurationService;
|
||||
}
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
|
||||
public BackgroundTaskService(IConfigurationService configurationService)
|
||||
public void UnregisterAllBackgroundTask()
|
||||
{
|
||||
if (_configurationService.Get(IsBackgroundTasksUnregisteredKey, false))
|
||||
{
|
||||
_configurationService = configurationService;
|
||||
}
|
||||
|
||||
public void UnregisterAllBackgroundTask()
|
||||
{
|
||||
if (_configurationService.Get(IsBackgroundTasksUnregisteredKey, false))
|
||||
foreach (var task in BackgroundTaskRegistration.AllTasks)
|
||||
{
|
||||
foreach (var task in BackgroundTaskRegistration.AllTasks)
|
||||
{
|
||||
task.Value.Unregister(true);
|
||||
}
|
||||
|
||||
Log.Information("Unregistered all background tasks.");
|
||||
_configurationService.Set(IsBackgroundTasksUnregisteredKey, true);
|
||||
task.Value.Unregister(true);
|
||||
}
|
||||
}
|
||||
|
||||
public Task RegisterBackgroundTasksAsync()
|
||||
{
|
||||
return RegisterToastNotificationHandlerBackgroundTaskAsync();
|
||||
}
|
||||
|
||||
public async Task RegisterToastNotificationHandlerBackgroundTaskAsync()
|
||||
{
|
||||
// If background task is already registered, do nothing.
|
||||
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(ToastNotificationActivationHandlerTaskName)))
|
||||
return;
|
||||
|
||||
// Otherwise request access
|
||||
BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
|
||||
|
||||
// Create the background task
|
||||
BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
|
||||
{
|
||||
Name = ToastNotificationActivationHandlerTaskName
|
||||
};
|
||||
|
||||
// Assign the toast action trigger
|
||||
builder.SetTrigger(new ToastNotificationActionTrigger());
|
||||
|
||||
// And register the task
|
||||
BackgroundTaskRegistration registration = builder.Register();
|
||||
Log.Information("Unregistered all background tasks.");
|
||||
_configurationService.Set(IsBackgroundTasksUnregisteredKey, true);
|
||||
}
|
||||
}
|
||||
|
||||
public Task RegisterBackgroundTasksAsync()
|
||||
{
|
||||
return RegisterToastNotificationHandlerBackgroundTaskAsync();
|
||||
}
|
||||
|
||||
public async Task RegisterToastNotificationHandlerBackgroundTaskAsync()
|
||||
{
|
||||
// If background task is already registered, do nothing.
|
||||
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(ToastNotificationActivationHandlerTaskName)))
|
||||
return;
|
||||
|
||||
// Otherwise request access
|
||||
BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
|
||||
|
||||
// Create the background task
|
||||
BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
|
||||
{
|
||||
Name = ToastNotificationActivationHandlerTaskName
|
||||
};
|
||||
|
||||
// Assign the toast action trigger
|
||||
builder.SetTrigger(new ToastNotificationActionTrigger());
|
||||
|
||||
// And register the task
|
||||
BackgroundTaskRegistration registration = builder.Register();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,17 @@
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class ClipboardService : IClipboardService
|
||||
{
|
||||
public class ClipboardService : IClipboardService
|
||||
public Task CopyClipboardAsync(string text)
|
||||
{
|
||||
public Task CopyClipboardAsync(string text)
|
||||
{
|
||||
var package = new DataPackage();
|
||||
package.SetText(text);
|
||||
var package = new DataPackage();
|
||||
package.SetText(text);
|
||||
|
||||
Clipboard.SetContent(package);
|
||||
Clipboard.SetContent(package);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,48 +5,47 @@ using Windows.Foundation.Collections;
|
||||
using Windows.Storage;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class ConfigurationService : IConfigurationService
|
||||
{
|
||||
public class ConfigurationService : IConfigurationService
|
||||
public T Get<T>(string key, T defaultValue = default)
|
||||
=> GetInternal(key, ApplicationData.Current.LocalSettings.Values, defaultValue);
|
||||
|
||||
public T GetRoaming<T>(string key, T defaultValue = default)
|
||||
=> GetInternal(key, ApplicationData.Current.RoamingSettings.Values, defaultValue);
|
||||
|
||||
public void Set(string key, object value)
|
||||
=> SetInternal(key, value, ApplicationData.Current.LocalSettings.Values);
|
||||
|
||||
public void SetRoaming(string key, object value)
|
||||
=> SetInternal(key, value, ApplicationData.Current.RoamingSettings.Values);
|
||||
|
||||
private static T GetInternal<T>(string key, IPropertySet collection, T defaultValue = default)
|
||||
{
|
||||
public T Get<T>(string key, T defaultValue = default)
|
||||
=> GetInternal(key, ApplicationData.Current.LocalSettings.Values, defaultValue);
|
||||
|
||||
public T GetRoaming<T>(string key, T defaultValue = default)
|
||||
=> GetInternal(key, ApplicationData.Current.RoamingSettings.Values, defaultValue);
|
||||
|
||||
public void Set(string key, object value)
|
||||
=> SetInternal(key, value, ApplicationData.Current.LocalSettings.Values);
|
||||
|
||||
public void SetRoaming(string key, object value)
|
||||
=> SetInternal(key, value, ApplicationData.Current.RoamingSettings.Values);
|
||||
|
||||
private static T GetInternal<T>(string key, IPropertySet collection, T defaultValue = default)
|
||||
if (collection.TryGetValue(key, out object value))
|
||||
{
|
||||
if (collection.TryGetValue(key, out object value))
|
||||
var stringValue = value?.ToString();
|
||||
|
||||
if (typeof(T).IsEnum)
|
||||
return (T)Enum.Parse(typeof(T), stringValue);
|
||||
|
||||
if ((typeof(T) == typeof(Guid?) || typeof(T) == typeof(Guid)) && Guid.TryParse(stringValue, out Guid guidResult))
|
||||
{
|
||||
var stringValue = value?.ToString();
|
||||
|
||||
if (typeof(T).IsEnum)
|
||||
return (T)Enum.Parse(typeof(T), stringValue);
|
||||
|
||||
if ((typeof(T) == typeof(Guid?) || typeof(T) == typeof(Guid)) && Guid.TryParse(stringValue, out Guid guidResult))
|
||||
{
|
||||
return (T)(object)guidResult;
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(TimeSpan))
|
||||
{
|
||||
return (T)(object)TimeSpan.Parse(stringValue);
|
||||
}
|
||||
|
||||
return (T)Convert.ChangeType(stringValue, typeof(T));
|
||||
return (T)(object)guidResult;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
if (typeof(T) == typeof(TimeSpan))
|
||||
{
|
||||
return (T)(object)TimeSpan.Parse(stringValue);
|
||||
}
|
||||
|
||||
return (T)Convert.ChangeType(stringValue, typeof(T));
|
||||
}
|
||||
|
||||
private static void SetInternal(string key, object value, IPropertySet collection)
|
||||
=> collection[key] = value?.ToString();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static void SetInternal(string key, object value, IPropertySet collection)
|
||||
=> collection[key] = value?.ToString();
|
||||
}
|
||||
|
||||
@@ -20,284 +20,283 @@ using Wino.Core.UWP.Extensions;
|
||||
using Wino.Dialogs;
|
||||
using Wino.Messaging.Client.Shell;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class DialogServiceBase : IDialogServiceBase
|
||||
{
|
||||
public class DialogServiceBase : IDialogServiceBase
|
||||
private SemaphoreSlim _presentationSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
protected IThemeService ThemeService { get; }
|
||||
protected IConfigurationService ConfigurationService { get; }
|
||||
|
||||
protected IApplicationResourceManager<ResourceDictionary> ApplicationResourceManager { get; }
|
||||
|
||||
public DialogServiceBase(IThemeService themeService, IConfigurationService configurationService, IApplicationResourceManager<ResourceDictionary> applicationResourceManager)
|
||||
{
|
||||
private SemaphoreSlim _presentationSemaphore = new SemaphoreSlim(1);
|
||||
ThemeService = themeService;
|
||||
ConfigurationService = configurationService;
|
||||
ApplicationResourceManager = applicationResourceManager;
|
||||
}
|
||||
|
||||
protected IThemeService ThemeService { get; }
|
||||
protected IConfigurationService ConfigurationService { get; }
|
||||
|
||||
protected IApplicationResourceManager<ResourceDictionary> ApplicationResourceManager { get; }
|
||||
|
||||
public DialogServiceBase(IThemeService themeService, IConfigurationService configurationService, IApplicationResourceManager<ResourceDictionary> applicationResourceManager)
|
||||
public async Task<MailAccount> ShowEditAccountDialogAsync(MailAccount account)
|
||||
{
|
||||
var editAccountDialog = new AccountEditDialog(account)
|
||||
{
|
||||
ThemeService = themeService;
|
||||
ConfigurationService = configurationService;
|
||||
ApplicationResourceManager = applicationResourceManager;
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
|
||||
};
|
||||
|
||||
await HandleDialogPresentationAsync(editAccountDialog);
|
||||
|
||||
return editAccountDialog.IsSaved ? editAccountDialog.Account : null;
|
||||
}
|
||||
|
||||
public async Task<string> PickFilePathAsync(string saveFileName)
|
||||
{
|
||||
var picker = new FolderPicker()
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.Desktop
|
||||
};
|
||||
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
var folder = await picker.PickSingleFolderAsync();
|
||||
if (folder == null) return string.Empty;
|
||||
|
||||
StorageApplicationPermissions.FutureAccessList.Add(folder);
|
||||
|
||||
return folder.Path;
|
||||
|
||||
//var picker = new FileSavePicker
|
||||
//{
|
||||
// SuggestedStartLocation = PickerLocationId.Desktop,
|
||||
// SuggestedFileName = saveFileName
|
||||
//};
|
||||
|
||||
//picker.FileTypeChoices.Add(Translator.FilteringOption_All, [".*"]);
|
||||
|
||||
//var file = await picker.PickSaveFileAsync();
|
||||
//if (file == null) return string.Empty;
|
||||
|
||||
//StorageApplicationPermissions.FutureAccessList.Add(file);
|
||||
|
||||
//return file.Path;
|
||||
}
|
||||
|
||||
public async Task<List<SharedFile>> PickFilesAsync(params object[] typeFilters)
|
||||
{
|
||||
var returnList = new List<SharedFile>();
|
||||
var picker = new FileOpenPicker
|
||||
{
|
||||
ViewMode = PickerViewMode.Thumbnail,
|
||||
SuggestedStartLocation = PickerLocationId.Desktop
|
||||
};
|
||||
|
||||
foreach (var filter in typeFilters)
|
||||
{
|
||||
picker.FileTypeFilter.Add(filter.ToString());
|
||||
}
|
||||
|
||||
public async Task<MailAccount> ShowEditAccountDialogAsync(MailAccount account)
|
||||
var files = await picker.PickMultipleFilesAsync();
|
||||
if (files == null) return returnList;
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var editAccountDialog = new AccountEditDialog(account)
|
||||
{
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
|
||||
};
|
||||
|
||||
await HandleDialogPresentationAsync(editAccountDialog);
|
||||
|
||||
return editAccountDialog.IsSaved ? editAccountDialog.Account : null;
|
||||
}
|
||||
|
||||
public async Task<string> PickFilePathAsync(string saveFileName)
|
||||
{
|
||||
var picker = new FolderPicker()
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.Desktop
|
||||
};
|
||||
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
var folder = await picker.PickSingleFolderAsync();
|
||||
if (folder == null) return string.Empty;
|
||||
|
||||
StorageApplicationPermissions.FutureAccessList.Add(folder);
|
||||
|
||||
return folder.Path;
|
||||
|
||||
//var picker = new FileSavePicker
|
||||
//{
|
||||
// SuggestedStartLocation = PickerLocationId.Desktop,
|
||||
// SuggestedFileName = saveFileName
|
||||
//};
|
||||
|
||||
//picker.FileTypeChoices.Add(Translator.FilteringOption_All, [".*"]);
|
||||
|
||||
//var file = await picker.PickSaveFileAsync();
|
||||
//if (file == null) return string.Empty;
|
||||
|
||||
//StorageApplicationPermissions.FutureAccessList.Add(file);
|
||||
|
||||
//return file.Path;
|
||||
}
|
||||
|
||||
public async Task<List<SharedFile>> PickFilesAsync(params object[] typeFilters)
|
||||
{
|
||||
var returnList = new List<SharedFile>();
|
||||
var picker = new FileOpenPicker
|
||||
{
|
||||
ViewMode = PickerViewMode.Thumbnail,
|
||||
SuggestedStartLocation = PickerLocationId.Desktop
|
||||
};
|
||||
|
||||
foreach (var filter in typeFilters)
|
||||
{
|
||||
picker.FileTypeFilter.Add(filter.ToString());
|
||||
}
|
||||
|
||||
var files = await picker.PickMultipleFilesAsync();
|
||||
if (files == null) return returnList;
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
StorageApplicationPermissions.FutureAccessList.Add(file);
|
||||
|
||||
var sharedFile = await file.ToSharedFileAsync();
|
||||
returnList.Add(sharedFile);
|
||||
}
|
||||
|
||||
return returnList;
|
||||
}
|
||||
|
||||
private async Task<StorageFile> PickFileAsync(params object[] typeFilters)
|
||||
{
|
||||
var picker = new FileOpenPicker
|
||||
{
|
||||
ViewMode = PickerViewMode.Thumbnail
|
||||
};
|
||||
|
||||
foreach (var filter in typeFilters)
|
||||
{
|
||||
picker.FileTypeFilter.Add(filter.ToString());
|
||||
}
|
||||
|
||||
var file = await picker.PickSingleFileAsync();
|
||||
|
||||
if (file == null) return null;
|
||||
|
||||
StorageApplicationPermissions.FutureAccessList.Add(file);
|
||||
|
||||
return file;
|
||||
var sharedFile = await file.ToSharedFileAsync();
|
||||
returnList.Add(sharedFile);
|
||||
}
|
||||
|
||||
public virtual IAccountCreationDialog GetAccountCreationDialog(AccountCreationDialogResult accountCreationDialogResult)
|
||||
return returnList;
|
||||
}
|
||||
|
||||
private async Task<StorageFile> PickFileAsync(params object[] typeFilters)
|
||||
{
|
||||
var picker = new FileOpenPicker
|
||||
{
|
||||
return new AccountCreationDialog
|
||||
{
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
|
||||
};
|
||||
}
|
||||
ViewMode = PickerViewMode.Thumbnail
|
||||
};
|
||||
|
||||
public async Task<byte[]> PickWindowsFileContentAsync(params object[] typeFilters)
|
||||
foreach (var filter in typeFilters)
|
||||
{
|
||||
var file = await PickFileAsync(typeFilters);
|
||||
|
||||
if (file == null) return [];
|
||||
|
||||
return await file.ToByteArrayAsync();
|
||||
picker.FileTypeFilter.Add(filter.ToString());
|
||||
}
|
||||
|
||||
public Task ShowMessageAsync(string message, string title, WinoCustomMessageDialogIcon icon = WinoCustomMessageDialogIcon.Information)
|
||||
=> ShowWinoCustomMessageDialogAsync(title, message, Translator.Buttons_Close, icon);
|
||||
var file = await picker.PickSingleFileAsync();
|
||||
|
||||
public Task<bool> ShowConfirmationDialogAsync(string question, string title, string confirmationButtonTitle)
|
||||
=> ShowWinoCustomMessageDialogAsync(title, question, confirmationButtonTitle, WinoCustomMessageDialogIcon.Question, Translator.Buttons_Cancel, string.Empty);
|
||||
if (file == null) return null;
|
||||
|
||||
public async Task<bool> ShowWinoCustomMessageDialogAsync(string title,
|
||||
string description,
|
||||
string approveButtonText,
|
||||
WinoCustomMessageDialogIcon? icon,
|
||||
string cancelButtonText = "",
|
||||
string dontAskAgainConfigurationKey = "")
|
||||
StorageApplicationPermissions.FutureAccessList.Add(file);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
public virtual IAccountCreationDialog GetAccountCreationDialog(AccountCreationDialogResult accountCreationDialogResult)
|
||||
{
|
||||
return new AccountCreationDialog
|
||||
{
|
||||
// This config key has been marked as don't ask again already.
|
||||
// Return immidiate result without presenting the dialog.
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
|
||||
};
|
||||
}
|
||||
|
||||
bool isDontAskEnabled = !string.IsNullOrEmpty(dontAskAgainConfigurationKey);
|
||||
public async Task<byte[]> PickWindowsFileContentAsync(params object[] typeFilters)
|
||||
{
|
||||
var file = await PickFileAsync(typeFilters);
|
||||
|
||||
if (isDontAskEnabled && ConfigurationService.Get(dontAskAgainConfigurationKey, false)) return false;
|
||||
if (file == null) return [];
|
||||
|
||||
var informationContainer = new CustomMessageDialogInformationContainer(title, description, icon.Value, isDontAskEnabled);
|
||||
return await file.ToByteArrayAsync();
|
||||
}
|
||||
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
Style = ApplicationResourceManager.GetResource<Style>("WinoDialogStyle"),
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme(),
|
||||
DefaultButton = ContentDialogButton.Primary,
|
||||
PrimaryButtonText = approveButtonText,
|
||||
ContentTemplate = ApplicationResourceManager.GetResource<DataTemplate>("CustomWinoContentDialogContentTemplate"),
|
||||
Content = informationContainer
|
||||
};
|
||||
public Task ShowMessageAsync(string message, string title, WinoCustomMessageDialogIcon icon = WinoCustomMessageDialogIcon.Information)
|
||||
=> ShowWinoCustomMessageDialogAsync(title, message, Translator.Buttons_Close, icon);
|
||||
|
||||
if (!string.IsNullOrEmpty(cancelButtonText))
|
||||
{
|
||||
dialog.SecondaryButtonText = cancelButtonText;
|
||||
}
|
||||
public Task<bool> ShowConfirmationDialogAsync(string question, string title, string confirmationButtonTitle)
|
||||
=> ShowWinoCustomMessageDialogAsync(title, question, confirmationButtonTitle, WinoCustomMessageDialogIcon.Question, Translator.Buttons_Cancel, string.Empty);
|
||||
|
||||
var dialogResult = await HandleDialogPresentationAsync(dialog);
|
||||
public async Task<bool> ShowWinoCustomMessageDialogAsync(string title,
|
||||
string description,
|
||||
string approveButtonText,
|
||||
WinoCustomMessageDialogIcon? icon,
|
||||
string cancelButtonText = "",
|
||||
string dontAskAgainConfigurationKey = "")
|
||||
|
||||
// Mark this key to not ask again if user checked the checkbox.
|
||||
if (informationContainer.IsDontAskChecked)
|
||||
{
|
||||
ConfigurationService.Set(dontAskAgainConfigurationKey, true);
|
||||
}
|
||||
{
|
||||
// This config key has been marked as don't ask again already.
|
||||
// Return immidiate result without presenting the dialog.
|
||||
|
||||
return dialogResult == ContentDialogResult.Primary;
|
||||
}
|
||||
bool isDontAskEnabled = !string.IsNullOrEmpty(dontAskAgainConfigurationKey);
|
||||
|
||||
/// <summary>
|
||||
/// Waits for PopupRoot to be available before presenting the dialog and returns the result after presentation.
|
||||
/// </summary>
|
||||
/// <param name="dialog">Dialog to present and wait for closing.</param>
|
||||
/// <returns>Dialog result from WinRT.</returns>
|
||||
public async Task<ContentDialogResult> HandleDialogPresentationAsync(ContentDialog dialog)
|
||||
if (isDontAskEnabled && ConfigurationService.Get(dontAskAgainConfigurationKey, false)) return false;
|
||||
|
||||
var informationContainer = new CustomMessageDialogInformationContainer(title, description, icon.Value, isDontAskEnabled);
|
||||
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
await _presentationSemaphore.WaitAsync();
|
||||
Style = ApplicationResourceManager.GetResource<Style>("WinoDialogStyle"),
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme(),
|
||||
DefaultButton = ContentDialogButton.Primary,
|
||||
PrimaryButtonText = approveButtonText,
|
||||
ContentTemplate = ApplicationResourceManager.GetResource<DataTemplate>("CustomWinoContentDialogContentTemplate"),
|
||||
Content = informationContainer
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
return await dialog.ShowAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Handling dialog service failed. Dialog was {dialog.GetType().Name}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_presentationSemaphore.Release();
|
||||
}
|
||||
|
||||
return ContentDialogResult.None;
|
||||
}
|
||||
|
||||
|
||||
public void InfoBarMessage(string title, string message, InfoBarMessageType messageType)
|
||||
=> WeakReferenceMessenger.Default.Send(new InfoBarMessageRequested(messageType, title, message));
|
||||
|
||||
public void InfoBarMessage(string title, string message, InfoBarMessageType messageType, string actionButtonText, Action action)
|
||||
=> WeakReferenceMessenger.Default.Send(new InfoBarMessageRequested(messageType, title, message, actionButtonText, action));
|
||||
|
||||
public void ShowNotSupportedMessage()
|
||||
=> InfoBarMessage(Translator.Info_UnsupportedFunctionalityTitle,
|
||||
Translator.Info_UnsupportedFunctionalityDescription,
|
||||
InfoBarMessageType.Error);
|
||||
|
||||
public async Task<string> ShowTextInputDialogAsync(string currentInput, string dialogTitle, string dialogDescription, string primaryButtonText)
|
||||
if (!string.IsNullOrEmpty(cancelButtonText))
|
||||
{
|
||||
var inputDialog = new TextInputDialog()
|
||||
{
|
||||
CurrentInput = currentInput,
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme(),
|
||||
Title = dialogTitle
|
||||
};
|
||||
|
||||
inputDialog.SetDescription(dialogDescription);
|
||||
inputDialog.SetPrimaryButtonText(primaryButtonText);
|
||||
|
||||
await HandleDialogPresentationAsync(inputDialog);
|
||||
|
||||
if (inputDialog.HasInput.GetValueOrDefault() && !currentInput.Equals(inputDialog.CurrentInput))
|
||||
return inputDialog.CurrentInput;
|
||||
|
||||
return string.Empty;
|
||||
dialog.SecondaryButtonText = cancelButtonText;
|
||||
}
|
||||
|
||||
public async Task<string> PickWindowsFolderAsync()
|
||||
var dialogResult = await HandleDialogPresentationAsync(dialog);
|
||||
|
||||
// Mark this key to not ask again if user checked the checkbox.
|
||||
if (informationContainer.IsDontAskChecked)
|
||||
{
|
||||
var picker = new FolderPicker
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
|
||||
};
|
||||
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
var pickedFolder = await picker.PickSingleFolderAsync();
|
||||
|
||||
if (pickedFolder != null)
|
||||
{
|
||||
Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace("FolderPickerToken", pickedFolder);
|
||||
|
||||
return pickedFolder.Path;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
ConfigurationService.Set(dontAskAgainConfigurationKey, true);
|
||||
}
|
||||
|
||||
public async Task<bool> ShowCustomThemeBuilderDialogAsync()
|
||||
return dialogResult == ContentDialogResult.Primary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for PopupRoot to be available before presenting the dialog and returns the result after presentation.
|
||||
/// </summary>
|
||||
/// <param name="dialog">Dialog to present and wait for closing.</param>
|
||||
/// <returns>Dialog result from WinRT.</returns>
|
||||
public async Task<ContentDialogResult> HandleDialogPresentationAsync(ContentDialog dialog)
|
||||
{
|
||||
await _presentationSemaphore.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var themeBuilderDialog = new CustomThemeBuilderDialog()
|
||||
{
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
|
||||
};
|
||||
|
||||
var dialogResult = await HandleDialogPresentationAsync(themeBuilderDialog);
|
||||
|
||||
return dialogResult == ContentDialogResult.Primary;
|
||||
return await dialog.ShowAsync();
|
||||
}
|
||||
|
||||
public async Task<AccountCreationDialogResult> ShowAccountProviderSelectionDialogAsync(List<IProviderDetail> availableProviders)
|
||||
catch (Exception ex)
|
||||
{
|
||||
var dialog = new NewAccountDialog
|
||||
{
|
||||
Providers = availableProviders,
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
|
||||
};
|
||||
|
||||
await HandleDialogPresentationAsync(dialog);
|
||||
|
||||
return dialog.Result;
|
||||
Log.Error(ex, $"Handling dialog service failed. Dialog was {dialog.GetType().Name}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_presentationSemaphore.Release();
|
||||
}
|
||||
|
||||
return ContentDialogResult.None;
|
||||
}
|
||||
|
||||
|
||||
public void InfoBarMessage(string title, string message, InfoBarMessageType messageType)
|
||||
=> WeakReferenceMessenger.Default.Send(new InfoBarMessageRequested(messageType, title, message));
|
||||
|
||||
public void InfoBarMessage(string title, string message, InfoBarMessageType messageType, string actionButtonText, Action action)
|
||||
=> WeakReferenceMessenger.Default.Send(new InfoBarMessageRequested(messageType, title, message, actionButtonText, action));
|
||||
|
||||
public void ShowNotSupportedMessage()
|
||||
=> InfoBarMessage(Translator.Info_UnsupportedFunctionalityTitle,
|
||||
Translator.Info_UnsupportedFunctionalityDescription,
|
||||
InfoBarMessageType.Error);
|
||||
|
||||
public async Task<string> ShowTextInputDialogAsync(string currentInput, string dialogTitle, string dialogDescription, string primaryButtonText)
|
||||
{
|
||||
var inputDialog = new TextInputDialog()
|
||||
{
|
||||
CurrentInput = currentInput,
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme(),
|
||||
Title = dialogTitle
|
||||
};
|
||||
|
||||
inputDialog.SetDescription(dialogDescription);
|
||||
inputDialog.SetPrimaryButtonText(primaryButtonText);
|
||||
|
||||
await HandleDialogPresentationAsync(inputDialog);
|
||||
|
||||
if (inputDialog.HasInput.GetValueOrDefault() && !currentInput.Equals(inputDialog.CurrentInput))
|
||||
return inputDialog.CurrentInput;
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public async Task<string> PickWindowsFolderAsync()
|
||||
{
|
||||
var picker = new FolderPicker
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
|
||||
};
|
||||
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
var pickedFolder = await picker.PickSingleFolderAsync();
|
||||
|
||||
if (pickedFolder != null)
|
||||
{
|
||||
Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace("FolderPickerToken", pickedFolder);
|
||||
|
||||
return pickedFolder.Path;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public async Task<bool> ShowCustomThemeBuilderDialogAsync()
|
||||
{
|
||||
var themeBuilderDialog = new CustomThemeBuilderDialog()
|
||||
{
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
|
||||
};
|
||||
|
||||
var dialogResult = await HandleDialogPresentationAsync(themeBuilderDialog);
|
||||
|
||||
return dialogResult == ContentDialogResult.Primary;
|
||||
}
|
||||
|
||||
public async Task<AccountCreationDialogResult> ShowAccountProviderSelectionDialogAsync(List<IProviderDetail> availableProviders)
|
||||
{
|
||||
var dialog = new NewAccountDialog
|
||||
{
|
||||
Providers = availableProviders,
|
||||
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
|
||||
};
|
||||
|
||||
await HandleDialogPresentationAsync(dialog);
|
||||
|
||||
return dialog.Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,57 +6,56 @@ using Windows.Storage;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class FileService : IFileService
|
||||
{
|
||||
public class FileService : IFileService
|
||||
public async Task<string> CopyFileAsync(string sourceFilePath, string destinationFolderPath)
|
||||
{
|
||||
public async Task<string> CopyFileAsync(string sourceFilePath, string destinationFolderPath)
|
||||
var fileName = Path.GetFileName(sourceFilePath);
|
||||
|
||||
var sourceFileHandle = await StorageFile.GetFileFromPathAsync(sourceFilePath);
|
||||
var destinationFolder = await StorageFolder.GetFolderFromPathAsync(destinationFolderPath);
|
||||
|
||||
var copiedFile = await sourceFileHandle.CopyAsync(destinationFolder, fileName, NameCollisionOption.GenerateUniqueName);
|
||||
|
||||
return copiedFile.Path;
|
||||
}
|
||||
|
||||
public async Task<string> GetFileContentByApplicationUriAsync(string resourcePath)
|
||||
{
|
||||
var releaseNoteFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(resourcePath));
|
||||
|
||||
return await FileIO.ReadTextAsync(releaseNoteFile);
|
||||
}
|
||||
|
||||
public async Task<Stream> GetFileStreamAsync(string folderPath, string fileName)
|
||||
{
|
||||
var folder = await StorageFolder.GetFolderFromPathAsync(folderPath);
|
||||
var createdFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
return await createdFile.OpenStreamForWriteAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> SaveLogsToFolderAsync(string logsFolder, string destinationFolder)
|
||||
{
|
||||
var logFiles = Directory.GetFiles(logsFolder, "*.log");
|
||||
|
||||
if (logFiles.Length == 0) return false;
|
||||
|
||||
using var fileStream = await GetFileStreamAsync(destinationFolder, Constants.LogArchiveFileName);
|
||||
using var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true);
|
||||
|
||||
foreach (var logFile in logFiles)
|
||||
{
|
||||
var fileName = Path.GetFileName(sourceFilePath);
|
||||
using FileStream logFileStream = File.Open(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
var sourceFileHandle = await StorageFile.GetFileFromPathAsync(sourceFilePath);
|
||||
var destinationFolder = await StorageFolder.GetFolderFromPathAsync(destinationFolderPath);
|
||||
var zipArchiveEntry = archive.CreateEntry(Path.GetFileName(logFile), CompressionLevel.Fastest);
|
||||
using var zipStream = zipArchiveEntry.Open();
|
||||
|
||||
var copiedFile = await sourceFileHandle.CopyAsync(destinationFolder, fileName, NameCollisionOption.GenerateUniqueName);
|
||||
|
||||
return copiedFile.Path;
|
||||
await logFileStream.CopyToAsync(zipStream);
|
||||
}
|
||||
|
||||
public async Task<string> GetFileContentByApplicationUriAsync(string resourcePath)
|
||||
{
|
||||
var releaseNoteFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(resourcePath));
|
||||
|
||||
return await FileIO.ReadTextAsync(releaseNoteFile);
|
||||
}
|
||||
|
||||
public async Task<Stream> GetFileStreamAsync(string folderPath, string fileName)
|
||||
{
|
||||
var folder = await StorageFolder.GetFolderFromPathAsync(folderPath);
|
||||
var createdFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
return await createdFile.OpenStreamForWriteAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> SaveLogsToFolderAsync(string logsFolder, string destinationFolder)
|
||||
{
|
||||
var logFiles = Directory.GetFiles(logsFolder, "*.log");
|
||||
|
||||
if (logFiles.Length == 0) return false;
|
||||
|
||||
using var fileStream = await GetFileStreamAsync(destinationFolder, Constants.LogArchiveFileName);
|
||||
using var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true);
|
||||
|
||||
foreach (var logFile in logFiles)
|
||||
{
|
||||
using FileStream logFileStream = File.Open(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
var zipArchiveEntry = archive.CreateEntry(Path.GetFileName(logFile), CompressionLevel.Fastest);
|
||||
using var zipStream = zipArchiveEntry.Open();
|
||||
|
||||
await logFileStream.CopyToAsync(zipStream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@ using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
{
|
||||
public class KeyPressService : IKeyPressService
|
||||
{
|
||||
public bool IsCtrlKeyPressed()
|
||||
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down) ?? false;
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public bool IsShiftKeyPressed()
|
||||
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down) ?? false;
|
||||
}
|
||||
public class KeyPressService : IKeyPressService
|
||||
{
|
||||
public bool IsCtrlKeyPressed()
|
||||
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down) ?? false;
|
||||
|
||||
public bool IsShiftKeyPressed()
|
||||
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down) ?? false;
|
||||
}
|
||||
|
||||
@@ -14,96 +14,95 @@ using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#endif
|
||||
|
||||
namespace Wino.Services
|
||||
namespace Wino.Services;
|
||||
|
||||
public class NativeAppService : INativeAppService
|
||||
{
|
||||
public class NativeAppService : INativeAppService
|
||||
private string _mimeMessagesFolder;
|
||||
private string _editorBundlePath;
|
||||
|
||||
public Func<IntPtr> GetCoreWindowHwnd { get; set; }
|
||||
|
||||
public string GetWebAuthenticationBrokerUri()
|
||||
{
|
||||
private string _mimeMessagesFolder;
|
||||
private string _editorBundlePath;
|
||||
|
||||
public Func<IntPtr> GetCoreWindowHwnd { get; set; }
|
||||
|
||||
public string GetWebAuthenticationBrokerUri()
|
||||
{
|
||||
#if WINDOWS_UWP
|
||||
return WebAuthenticationBroker.GetCurrentApplicationCallbackUri().AbsoluteUri;
|
||||
return WebAuthenticationBroker.GetCurrentApplicationCallbackUri().AbsoluteUri;
|
||||
#endif
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public async Task<string> GetMimeMessageStoragePath()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_mimeMessagesFolder))
|
||||
return _mimeMessagesFolder;
|
||||
|
||||
var localFolder = ApplicationData.Current.LocalFolder;
|
||||
var mimeFolder = await localFolder.CreateFolderAsync("Mime", CreationCollisionOption.OpenIfExists);
|
||||
|
||||
_mimeMessagesFolder = mimeFolder.Path;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public async Task<string> GetMimeMessageStoragePath()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_mimeMessagesFolder))
|
||||
return _mimeMessagesFolder;
|
||||
|
||||
var localFolder = ApplicationData.Current.LocalFolder;
|
||||
var mimeFolder = await localFolder.CreateFolderAsync("Mime", CreationCollisionOption.OpenIfExists);
|
||||
|
||||
_mimeMessagesFolder = mimeFolder.Path;
|
||||
|
||||
return _mimeMessagesFolder;
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> GetEditorBundlePathAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_editorBundlePath))
|
||||
{
|
||||
var editorFileFromBundle = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///JS/editor.html"))
|
||||
.AsTask()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_editorBundlePath = editorFileFromBundle.Path;
|
||||
}
|
||||
|
||||
return _editorBundlePath;
|
||||
}
|
||||
|
||||
public async Task<string> GetEditorBundlePathAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_editorBundlePath))
|
||||
{
|
||||
var editorFileFromBundle = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///JS/editor.html"))
|
||||
.AsTask()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_editorBundlePath = editorFileFromBundle.Path;
|
||||
}
|
||||
|
||||
return _editorBundlePath;
|
||||
}
|
||||
|
||||
[Obsolete("This should be removed. There should be no functionality.")]
|
||||
public bool IsAppRunning()
|
||||
{
|
||||
[Obsolete("This should be removed. There should be no functionality.")]
|
||||
public bool IsAppRunning()
|
||||
{
|
||||
#if WINDOWS_UWP
|
||||
return (Window.Current?.Content as Frame)?.Content != null;
|
||||
return (Window.Current?.Content as Frame)?.Content != null;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public async Task LaunchFileAsync(string filePath)
|
||||
{
|
||||
var file = await StorageFile.GetFileFromPathAsync(filePath);
|
||||
public async Task LaunchFileAsync(string filePath)
|
||||
{
|
||||
var file = await StorageFile.GetFileFromPathAsync(filePath);
|
||||
|
||||
await Launcher.LaunchFileAsync(file);
|
||||
}
|
||||
await Launcher.LaunchFileAsync(file);
|
||||
}
|
||||
|
||||
public Task<bool> LaunchUriAsync(Uri uri) => Launcher.LaunchUriAsync(uri).AsTask();
|
||||
public Task<bool> LaunchUriAsync(Uri uri) => Launcher.LaunchUriAsync(uri).AsTask();
|
||||
|
||||
public string GetFullAppVersion()
|
||||
{
|
||||
Package package = Package.Current;
|
||||
PackageId packageId = package.Id;
|
||||
PackageVersion version = packageId.Version;
|
||||
public string GetFullAppVersion()
|
||||
{
|
||||
Package package = Package.Current;
|
||||
PackageId packageId = package.Id;
|
||||
PackageVersion version = packageId.Version;
|
||||
|
||||
return string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
|
||||
}
|
||||
return string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
|
||||
}
|
||||
|
||||
public async Task PinAppToTaskbarAsync()
|
||||
{
|
||||
// If Start screen manager API's aren't present
|
||||
if (!ApiInformation.IsTypePresent("Windows.UI.Shell.TaskbarManager")) return;
|
||||
public async Task PinAppToTaskbarAsync()
|
||||
{
|
||||
// If Start screen manager API's aren't present
|
||||
if (!ApiInformation.IsTypePresent("Windows.UI.Shell.TaskbarManager")) return;
|
||||
|
||||
// Get the taskbar manager
|
||||
var taskbarManager = TaskbarManager.GetDefault();
|
||||
// Get the taskbar manager
|
||||
var taskbarManager = TaskbarManager.GetDefault();
|
||||
|
||||
// If Taskbar doesn't allow pinning, don't show the tip
|
||||
if (!taskbarManager.IsPinningAllowed) return;
|
||||
// If Taskbar doesn't allow pinning, don't show the tip
|
||||
if (!taskbarManager.IsPinningAllowed) return;
|
||||
|
||||
// If already pinned, don't show the tip
|
||||
if (await taskbarManager.IsCurrentAppPinnedAsync()) return;
|
||||
// If already pinned, don't show the tip
|
||||
if (await taskbarManager.IsCurrentAppPinnedAsync()) return;
|
||||
|
||||
await taskbarManager.RequestPinCurrentAppAsync();
|
||||
}
|
||||
await taskbarManager.RequestPinCurrentAppAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,26 +3,25 @@ using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class NavigationServiceBase
|
||||
{
|
||||
public class NavigationServiceBase
|
||||
public NavigationTransitionInfo GetNavigationTransitionInfo(NavigationTransitionType transition)
|
||||
{
|
||||
public NavigationTransitionInfo GetNavigationTransitionInfo(NavigationTransitionType transition)
|
||||
return transition switch
|
||||
{
|
||||
return transition switch
|
||||
{
|
||||
NavigationTransitionType.DrillIn => new DrillInNavigationTransitionInfo(),
|
||||
NavigationTransitionType.Entrance => new EntranceNavigationTransitionInfo(),
|
||||
_ => new SuppressNavigationTransitionInfo(),
|
||||
};
|
||||
}
|
||||
NavigationTransitionType.DrillIn => new DrillInNavigationTransitionInfo(),
|
||||
NavigationTransitionType.Entrance => new EntranceNavigationTransitionInfo(),
|
||||
_ => new SuppressNavigationTransitionInfo(),
|
||||
};
|
||||
}
|
||||
|
||||
public Type GetCurrentFrameType(ref Frame _frame)
|
||||
{
|
||||
if (_frame != null && _frame.Content != null)
|
||||
return _frame.Content.GetType();
|
||||
public Type GetCurrentFrameType(ref Frame _frame)
|
||||
{
|
||||
if (_frame != null && _frame.Content != null)
|
||||
return _frame.Content.GetType();
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,43 +13,109 @@ using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Core.Services;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
// TODO: Refactor this thing. It's garbage.
|
||||
|
||||
public class NotificationBuilder : INotificationBuilder
|
||||
{
|
||||
// TODO: Refactor this thing. It's garbage.
|
||||
private readonly IUnderlyingThemeService _underlyingThemeService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly IMailService _mailService;
|
||||
|
||||
public class NotificationBuilder : INotificationBuilder
|
||||
public NotificationBuilder(IUnderlyingThemeService underlyingThemeService,
|
||||
IAccountService accountService,
|
||||
IFolderService folderService,
|
||||
IMailService mailService)
|
||||
{
|
||||
private readonly IUnderlyingThemeService _underlyingThemeService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IFolderService _folderService;
|
||||
private readonly IMailService _mailService;
|
||||
_underlyingThemeService = underlyingThemeService;
|
||||
_accountService = accountService;
|
||||
_folderService = folderService;
|
||||
_mailService = mailService;
|
||||
}
|
||||
|
||||
public NotificationBuilder(IUnderlyingThemeService underlyingThemeService,
|
||||
IAccountService accountService,
|
||||
IFolderService folderService,
|
||||
IMailService mailService)
|
||||
public async Task CreateNotificationsAsync(Guid inboxFolderId, IEnumerable<IMailItem> downloadedMailItems)
|
||||
{
|
||||
var mailCount = downloadedMailItems.Count();
|
||||
|
||||
try
|
||||
{
|
||||
_underlyingThemeService = underlyingThemeService;
|
||||
_accountService = accountService;
|
||||
_folderService = folderService;
|
||||
_mailService = mailService;
|
||||
}
|
||||
|
||||
public async Task CreateNotificationsAsync(Guid inboxFolderId, IEnumerable<IMailItem> downloadedMailItems)
|
||||
{
|
||||
var mailCount = downloadedMailItems.Count();
|
||||
|
||||
try
|
||||
// If there are more than 3 mails, just display 1 general toast.
|
||||
if (mailCount > 3)
|
||||
{
|
||||
// If there are more than 3 mails, just display 1 general toast.
|
||||
if (mailCount > 3)
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
|
||||
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
|
||||
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
|
||||
|
||||
builder.AddButton(GetDismissButton());
|
||||
builder.AddAudio(new ToastAudio()
|
||||
{
|
||||
Src = new Uri("ms-winsoundevent:Notification.Mail")
|
||||
});
|
||||
|
||||
builder.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
var validItems = new List<IMailItem>();
|
||||
|
||||
// Fetch mails again to fill up assigned folder data and latest statuses.
|
||||
// They've been marked as read by executing synchronizer tasks until inital sync finishes.
|
||||
|
||||
foreach (var item in downloadedMailItems)
|
||||
{
|
||||
var mailItem = await _mailService.GetSingleMailItemAsync(item.UniqueId);
|
||||
|
||||
if (mailItem != null && mailItem.AssignedFolder != null)
|
||||
{
|
||||
validItems.Add(mailItem);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var mailItem in validItems)
|
||||
{
|
||||
if (mailItem.IsRead)
|
||||
continue;
|
||||
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
|
||||
builder.AddText(Translator.Notifications_MultipleNotificationsTitle);
|
||||
builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsMessage, mailCount));
|
||||
var host = ThumbnailService.GetHost(mailItem.FromAddress);
|
||||
|
||||
var knownTuple = ThumbnailService.CheckIsKnown(host);
|
||||
|
||||
bool isKnown = knownTuple.Item1;
|
||||
host = knownTuple.Item2;
|
||||
|
||||
if (isKnown)
|
||||
builder.AddAppLogoOverride(new System.Uri(ThumbnailService.GetKnownHostImage(host)), hintCrop: ToastGenericAppLogoCrop.Default);
|
||||
else
|
||||
{
|
||||
// TODO: https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=toolkit
|
||||
// Follow official guides for icons/theme.
|
||||
|
||||
bool isOSDarkTheme = _underlyingThemeService.IsUnderlyingThemeDark();
|
||||
string profileLogoName = isOSDarkTheme ? "profile-dark.png" : "profile-light.png";
|
||||
|
||||
builder.AddAppLogoOverride(new System.Uri($"ms-appx:///Assets/NotificationIcons/{profileLogoName}"), hintCrop: ToastGenericAppLogoCrop.Circle);
|
||||
}
|
||||
|
||||
// Override system notification timetamp with received date of the mail.
|
||||
// It may create confusion for some users, but still it's the truth...
|
||||
builder.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime());
|
||||
|
||||
builder.AddText(mailItem.FromName);
|
||||
builder.AddText(mailItem.Subject);
|
||||
builder.AddText(mailItem.PreviewText);
|
||||
|
||||
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
|
||||
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate);
|
||||
|
||||
builder.AddButton(GetMarkedAsRead(mailItem.UniqueId));
|
||||
builder.AddButton(GetDeleteButton(mailItem.UniqueId));
|
||||
builder.AddButton(GetDismissButton());
|
||||
builder.AddAudio(new ToastAudio()
|
||||
{
|
||||
@@ -58,174 +124,107 @@ namespace Wino.Core.UWP.Services
|
||||
|
||||
builder.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
var validItems = new List<IMailItem>();
|
||||
|
||||
// Fetch mails again to fill up assigned folder data and latest statuses.
|
||||
// They've been marked as read by executing synchronizer tasks until inital sync finishes.
|
||||
|
||||
foreach (var item in downloadedMailItems)
|
||||
{
|
||||
var mailItem = await _mailService.GetSingleMailItemAsync(item.UniqueId);
|
||||
|
||||
if (mailItem != null && mailItem.AssignedFolder != null)
|
||||
{
|
||||
validItems.Add(mailItem);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var mailItem in validItems)
|
||||
{
|
||||
if (mailItem.IsRead)
|
||||
continue;
|
||||
|
||||
var builder = new ToastContentBuilder();
|
||||
builder.SetToastScenario(ToastScenario.Default);
|
||||
|
||||
var host = ThumbnailService.GetHost(mailItem.FromAddress);
|
||||
|
||||
var knownTuple = ThumbnailService.CheckIsKnown(host);
|
||||
|
||||
bool isKnown = knownTuple.Item1;
|
||||
host = knownTuple.Item2;
|
||||
|
||||
if (isKnown)
|
||||
builder.AddAppLogoOverride(new System.Uri(ThumbnailService.GetKnownHostImage(host)), hintCrop: ToastGenericAppLogoCrop.Default);
|
||||
else
|
||||
{
|
||||
// TODO: https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=toolkit
|
||||
// Follow official guides for icons/theme.
|
||||
|
||||
bool isOSDarkTheme = _underlyingThemeService.IsUnderlyingThemeDark();
|
||||
string profileLogoName = isOSDarkTheme ? "profile-dark.png" : "profile-light.png";
|
||||
|
||||
builder.AddAppLogoOverride(new System.Uri($"ms-appx:///Assets/NotificationIcons/{profileLogoName}"), hintCrop: ToastGenericAppLogoCrop.Circle);
|
||||
}
|
||||
|
||||
// Override system notification timetamp with received date of the mail.
|
||||
// It may create confusion for some users, but still it's the truth...
|
||||
builder.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime());
|
||||
|
||||
builder.AddText(mailItem.FromName);
|
||||
builder.AddText(mailItem.Subject);
|
||||
builder.AddText(mailItem.PreviewText);
|
||||
|
||||
builder.AddArgument(Constants.ToastMailUniqueIdKey, mailItem.UniqueId.ToString());
|
||||
builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate);
|
||||
|
||||
builder.AddButton(GetMarkedAsRead(mailItem.UniqueId));
|
||||
builder.AddButton(GetDeleteButton(mailItem.UniqueId));
|
||||
builder.AddButton(GetDismissButton());
|
||||
builder.AddAudio(new ToastAudio()
|
||||
{
|
||||
Src = new Uri("ms-winsoundevent:Notification.Mail")
|
||||
});
|
||||
|
||||
builder.Show();
|
||||
}
|
||||
|
||||
await UpdateTaskbarIconBadgeAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to create notifications.");
|
||||
await UpdateTaskbarIconBadgeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private ToastButton GetDismissButton()
|
||||
=> new ToastButton()
|
||||
.SetDismissActivation()
|
||||
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/dismiss.png"));
|
||||
|
||||
private ToastButton GetDeleteButton(Guid mailUniqueId)
|
||||
=> new ToastButton()
|
||||
.SetContent(Translator.MailOperation_Delete)
|
||||
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/delete.png"))
|
||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete)
|
||||
.SetBackgroundActivation();
|
||||
|
||||
private ToastButton GetMarkedAsRead(Guid mailUniqueId)
|
||||
=> new ToastButton()
|
||||
.SetContent(Translator.MailOperation_MarkAsRead)
|
||||
.SetImageUri(new System.Uri("ms-appx:///Assets/NotificationIcons/markread.png"))
|
||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead)
|
||||
.SetBackgroundActivation();
|
||||
|
||||
public async Task UpdateTaskbarIconBadgeAsync()
|
||||
catch (Exception ex)
|
||||
{
|
||||
int totalUnreadCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var badgeUpdater = BadgeUpdateManager.CreateBadgeUpdaterForApplication();
|
||||
var accounts = await _accountService.GetAccountsAsync();
|
||||
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
if (!account.Preferences.IsTaskbarBadgeEnabled) continue;
|
||||
|
||||
var accountInbox = await _folderService.GetSpecialFolderByAccountIdAsync(account.Id, SpecialFolderType.Inbox);
|
||||
|
||||
if (accountInbox == null)
|
||||
continue;
|
||||
|
||||
var inboxUnreadCount = await _folderService.GetFolderNotificationBadgeAsync(accountInbox.Id);
|
||||
|
||||
totalUnreadCount += inboxUnreadCount;
|
||||
}
|
||||
|
||||
if (totalUnreadCount > 0)
|
||||
{
|
||||
// Get the blank badge XML payload for a badge number
|
||||
XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
|
||||
|
||||
// Set the value of the badge in the XML to our number
|
||||
XmlElement badgeElement = badgeXml.SelectSingleNode("/badge") as XmlElement;
|
||||
badgeElement.SetAttribute("value", totalUnreadCount.ToString());
|
||||
|
||||
// Create the badge notification
|
||||
BadgeNotification badge = new BadgeNotification(badgeXml);
|
||||
|
||||
// And update the badge
|
||||
badgeUpdater.Update(badge);
|
||||
}
|
||||
else
|
||||
badgeUpdater.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error while updating taskbar badge.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CreateTestNotificationAsync(string title, string message)
|
||||
{
|
||||
// with args test.
|
||||
await CreateNotificationsAsync(Guid.Parse("28c3c39b-7147-4de3-b209-949bd19eede6"), new List<IMailItem>()
|
||||
{
|
||||
new MailCopy()
|
||||
{
|
||||
Subject = "test subject",
|
||||
PreviewText = "preview html",
|
||||
CreationDate = DateTime.UtcNow,
|
||||
FromAddress = "bkaankose@outlook.com",
|
||||
Id = "AAkALgAAAAAAHYQDEapmEc2byACqAC-EWg0AnMdP0zg8wkS_Ib2Eeh80LAAGq91I3QAA",
|
||||
}
|
||||
});
|
||||
|
||||
//var builder = new ToastContentBuilder();
|
||||
//builder.SetToastScenario(ToastScenario.Default);
|
||||
|
||||
//builder.AddText(title);
|
||||
//builder.AddText(message);
|
||||
|
||||
//builder.Show();
|
||||
|
||||
//await Task.CompletedTask;
|
||||
Log.Error(ex, "Failed to create notifications.");
|
||||
}
|
||||
}
|
||||
|
||||
private ToastButton GetDismissButton()
|
||||
=> new ToastButton()
|
||||
.SetDismissActivation()
|
||||
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/dismiss.png"));
|
||||
|
||||
private ToastButton GetDeleteButton(Guid mailUniqueId)
|
||||
=> new ToastButton()
|
||||
.SetContent(Translator.MailOperation_Delete)
|
||||
.SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/delete.png"))
|
||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete)
|
||||
.SetBackgroundActivation();
|
||||
|
||||
private ToastButton GetMarkedAsRead(Guid mailUniqueId)
|
||||
=> new ToastButton()
|
||||
.SetContent(Translator.MailOperation_MarkAsRead)
|
||||
.SetImageUri(new System.Uri("ms-appx:///Assets/NotificationIcons/markread.png"))
|
||||
.AddArgument(Constants.ToastMailUniqueIdKey, mailUniqueId.ToString())
|
||||
.AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead)
|
||||
.SetBackgroundActivation();
|
||||
|
||||
public async Task UpdateTaskbarIconBadgeAsync()
|
||||
{
|
||||
int totalUnreadCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var badgeUpdater = BadgeUpdateManager.CreateBadgeUpdaterForApplication();
|
||||
var accounts = await _accountService.GetAccountsAsync();
|
||||
|
||||
foreach (var account in accounts)
|
||||
{
|
||||
if (!account.Preferences.IsTaskbarBadgeEnabled) continue;
|
||||
|
||||
var accountInbox = await _folderService.GetSpecialFolderByAccountIdAsync(account.Id, SpecialFolderType.Inbox);
|
||||
|
||||
if (accountInbox == null)
|
||||
continue;
|
||||
|
||||
var inboxUnreadCount = await _folderService.GetFolderNotificationBadgeAsync(accountInbox.Id);
|
||||
|
||||
totalUnreadCount += inboxUnreadCount;
|
||||
}
|
||||
|
||||
if (totalUnreadCount > 0)
|
||||
{
|
||||
// Get the blank badge XML payload for a badge number
|
||||
XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);
|
||||
|
||||
// Set the value of the badge in the XML to our number
|
||||
XmlElement badgeElement = badgeXml.SelectSingleNode("/badge") as XmlElement;
|
||||
badgeElement.SetAttribute("value", totalUnreadCount.ToString());
|
||||
|
||||
// Create the badge notification
|
||||
BadgeNotification badge = new BadgeNotification(badgeXml);
|
||||
|
||||
// And update the badge
|
||||
badgeUpdater.Update(badge);
|
||||
}
|
||||
else
|
||||
badgeUpdater.Clear();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error while updating taskbar badge.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CreateTestNotificationAsync(string title, string message)
|
||||
{
|
||||
// with args test.
|
||||
await CreateNotificationsAsync(Guid.Parse("28c3c39b-7147-4de3-b209-949bd19eede6"), new List<IMailItem>()
|
||||
{
|
||||
new MailCopy()
|
||||
{
|
||||
Subject = "test subject",
|
||||
PreviewText = "preview html",
|
||||
CreationDate = DateTime.UtcNow,
|
||||
FromAddress = "bkaankose@outlook.com",
|
||||
Id = "AAkALgAAAAAAHYQDEapmEc2byACqAC-EWg0AnMdP0zg8wkS_Ib2Eeh80LAAGq91I3QAA",
|
||||
}
|
||||
});
|
||||
|
||||
//var builder = new ToastContentBuilder();
|
||||
//builder.SetToastScenario(ToastScenario.Default);
|
||||
|
||||
//builder.AddText(title);
|
||||
//builder.AddText(message);
|
||||
|
||||
//builder.Show();
|
||||
|
||||
//await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,300 +11,299 @@ using Wino.Core.Domain.Models.Reader;
|
||||
using Wino.Core.Domain.Translations;
|
||||
using Wino.Services;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class PreferencesService : ObservableObject, IPreferencesService
|
||||
{
|
||||
public class PreferencesService : ObservableObject, IPreferencesService
|
||||
private readonly IConfigurationService _configurationService;
|
||||
|
||||
public event EventHandler<string> PreferenceChanged;
|
||||
|
||||
public PreferencesService(IConfigurationService configurationService)
|
||||
{
|
||||
private readonly IConfigurationService _configurationService;
|
||||
_configurationService = configurationService;
|
||||
}
|
||||
|
||||
public event EventHandler<string> PreferenceChanged;
|
||||
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
public PreferencesService(IConfigurationService configurationService)
|
||||
PreferenceChanged?.Invoke(this, e.PropertyName);
|
||||
}
|
||||
|
||||
private void SaveProperty(string propertyName, object value) => _configurationService.Set(propertyName, value);
|
||||
|
||||
private void SetPropertyAndSave(string propertyName, object value)
|
||||
{
|
||||
_configurationService.Set(propertyName, value);
|
||||
|
||||
OnPropertyChanged(propertyName);
|
||||
Debug.WriteLine($"PreferencesService -> {propertyName}:{value?.ToString()}");
|
||||
}
|
||||
|
||||
public MailRenderingOptions GetRenderingOptions()
|
||||
=> new MailRenderingOptions()
|
||||
{
|
||||
_configurationService = configurationService;
|
||||
LoadImages = RenderImages,
|
||||
LoadStyles = RenderStyles,
|
||||
RenderPlaintextLinks = RenderPlaintextLinks
|
||||
};
|
||||
|
||||
public MailListDisplayMode MailItemDisplayMode
|
||||
{
|
||||
get => _configurationService.Get(nameof(MailItemDisplayMode), MailListDisplayMode.Spacious);
|
||||
set => SetPropertyAndSave(nameof(MailItemDisplayMode), value);
|
||||
}
|
||||
|
||||
public bool IsSemanticZoomEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsSemanticZoomEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsSemanticZoomEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsHardDeleteProtectionEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsHardDeleteProtectionEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsHardDeleteProtectionEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsThreadingEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsThreadingEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsThreadingEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsMailListActionBarEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsMailListActionBarEnabled), false);
|
||||
set => SetPropertyAndSave(nameof(IsMailListActionBarEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsShowSenderPicturesEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsShowSenderPicturesEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsShowSenderPicturesEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsShowPreviewEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsShowPreviewEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsShowPreviewEnabled), value);
|
||||
}
|
||||
|
||||
public bool RenderStyles
|
||||
{
|
||||
get => _configurationService.Get(nameof(RenderStyles), true);
|
||||
set => SetPropertyAndSave(nameof(RenderStyles), value);
|
||||
}
|
||||
|
||||
public bool RenderPlaintextLinks
|
||||
{
|
||||
get => _configurationService.Get(nameof(RenderPlaintextLinks), true);
|
||||
set => SetPropertyAndSave(nameof(RenderPlaintextLinks), value);
|
||||
}
|
||||
|
||||
public bool RenderImages
|
||||
{
|
||||
get => _configurationService.Get(nameof(RenderImages), true);
|
||||
set => SetPropertyAndSave(nameof(RenderImages), value);
|
||||
}
|
||||
|
||||
public bool Prefer24HourTimeFormat
|
||||
{
|
||||
get => _configurationService.Get(nameof(Prefer24HourTimeFormat), false);
|
||||
set => SetPropertyAndSave(nameof(Prefer24HourTimeFormat), value);
|
||||
}
|
||||
|
||||
public MailMarkAsOption MarkAsPreference
|
||||
{
|
||||
get => _configurationService.Get(nameof(MarkAsPreference), MailMarkAsOption.WhenSelected);
|
||||
set => SetPropertyAndSave(nameof(MarkAsPreference), value);
|
||||
}
|
||||
|
||||
public int MarkAsDelay
|
||||
{
|
||||
get => _configurationService.Get(nameof(MarkAsDelay), 5);
|
||||
set => SetPropertyAndSave(nameof(MarkAsDelay), value);
|
||||
}
|
||||
|
||||
public MailOperation RightSwipeOperation
|
||||
{
|
||||
get => _configurationService.Get(nameof(RightSwipeOperation), MailOperation.MarkAsRead);
|
||||
set => SetPropertyAndSave(nameof(RightSwipeOperation), value);
|
||||
}
|
||||
|
||||
public MailOperation LeftSwipeOperation
|
||||
{
|
||||
get => _configurationService.Get(nameof(LeftSwipeOperation), MailOperation.SoftDelete);
|
||||
set => SetPropertyAndSave(nameof(LeftSwipeOperation), value);
|
||||
}
|
||||
|
||||
public bool IsHoverActionsEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsHoverActionsEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsHoverActionsEnabled), value);
|
||||
}
|
||||
|
||||
public MailOperation LeftHoverAction
|
||||
{
|
||||
get => _configurationService.Get(nameof(LeftHoverAction), MailOperation.Archive);
|
||||
set => SetPropertyAndSave(nameof(LeftHoverAction), value);
|
||||
}
|
||||
|
||||
public MailOperation CenterHoverAction
|
||||
{
|
||||
get => _configurationService.Get(nameof(CenterHoverAction), MailOperation.SoftDelete);
|
||||
set => SetPropertyAndSave(nameof(CenterHoverAction), value);
|
||||
}
|
||||
|
||||
public MailOperation RightHoverAction
|
||||
{
|
||||
get => _configurationService.Get(nameof(RightHoverAction), MailOperation.SetFlag);
|
||||
set => SetPropertyAndSave(nameof(RightHoverAction), value);
|
||||
}
|
||||
|
||||
public bool IsLoggingEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsLoggingEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsLoggingEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsMailkitProtocolLoggerEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsMailkitProtocolLoggerEnabled), false);
|
||||
set => SetPropertyAndSave(nameof(IsMailkitProtocolLoggerEnabled), value);
|
||||
}
|
||||
|
||||
public Guid? StartupEntityId
|
||||
{
|
||||
get => _configurationService.Get<Guid?>(nameof(StartupEntityId), null);
|
||||
set => SaveProperty(propertyName: nameof(StartupEntityId), value);
|
||||
}
|
||||
|
||||
public AppLanguage CurrentLanguage
|
||||
{
|
||||
get => _configurationService.Get(nameof(CurrentLanguage), TranslationService.DefaultAppLanguage);
|
||||
set => SaveProperty(propertyName: nameof(CurrentLanguage), value);
|
||||
}
|
||||
|
||||
public string ReaderFont
|
||||
{
|
||||
get => _configurationService.Get(nameof(ReaderFont), "Calibri");
|
||||
set => SaveProperty(propertyName: nameof(ReaderFont), value);
|
||||
}
|
||||
|
||||
public int ReaderFontSize
|
||||
{
|
||||
get => _configurationService.Get(nameof(ReaderFontSize), 14);
|
||||
set => SaveProperty(propertyName: nameof(ReaderFontSize), value);
|
||||
}
|
||||
|
||||
public string ComposerFont
|
||||
{
|
||||
get => _configurationService.Get(nameof(ComposerFont), "Calibri");
|
||||
set => SaveProperty(propertyName: nameof(ComposerFont), value);
|
||||
}
|
||||
|
||||
public int ComposerFontSize
|
||||
{
|
||||
get => _configurationService.Get(nameof(ComposerFontSize), 14);
|
||||
set => SaveProperty(propertyName: nameof(ComposerFontSize), value);
|
||||
}
|
||||
|
||||
public bool IsNavigationPaneOpened
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsNavigationPaneOpened), true);
|
||||
set => SaveProperty(propertyName: nameof(IsNavigationPaneOpened), value);
|
||||
}
|
||||
|
||||
public bool AutoSelectNextItem
|
||||
{
|
||||
get => _configurationService.Get(nameof(AutoSelectNextItem), true);
|
||||
set => SaveProperty(propertyName: nameof(AutoSelectNextItem), value);
|
||||
}
|
||||
|
||||
public ServerBackgroundMode ServerTerminationBehavior
|
||||
{
|
||||
get => _configurationService.Get(nameof(ServerTerminationBehavior), ServerBackgroundMode.MinimizedTray);
|
||||
set => SaveProperty(propertyName: nameof(ServerTerminationBehavior), value);
|
||||
}
|
||||
|
||||
public string DiagnosticId
|
||||
{
|
||||
get => _configurationService.Get(nameof(DiagnosticId), Guid.NewGuid().ToString());
|
||||
set => SaveProperty(propertyName: nameof(DiagnosticId), value);
|
||||
}
|
||||
|
||||
public DayOfWeek FirstDayOfWeek
|
||||
{
|
||||
get => _configurationService.Get(nameof(FirstDayOfWeek), DayOfWeek.Monday);
|
||||
set => SaveProperty(propertyName: nameof(FirstDayOfWeek), value);
|
||||
}
|
||||
|
||||
public double HourHeight
|
||||
{
|
||||
get => _configurationService.Get(nameof(HourHeight), 60.0);
|
||||
set => SaveProperty(propertyName: nameof(HourHeight), value);
|
||||
}
|
||||
|
||||
public TimeSpan WorkingHourStart
|
||||
{
|
||||
get => _configurationService.Get(nameof(WorkingHourStart), new TimeSpan(8, 0, 0));
|
||||
set => SaveProperty(propertyName: nameof(WorkingHourStart), value);
|
||||
}
|
||||
|
||||
public TimeSpan WorkingHourEnd
|
||||
{
|
||||
get => _configurationService.Get(nameof(WorkingHourEnd), new TimeSpan(17, 0, 0));
|
||||
set => SaveProperty(propertyName: nameof(WorkingHourEnd), value);
|
||||
}
|
||||
|
||||
public DayOfWeek WorkingDayStart
|
||||
{
|
||||
get => _configurationService.Get(nameof(WorkingDayStart), DayOfWeek.Monday);
|
||||
set => SaveProperty(propertyName: nameof(WorkingDayStart), value);
|
||||
}
|
||||
|
||||
public DayOfWeek WorkingDayEnd
|
||||
{
|
||||
get => _configurationService.Get(nameof(WorkingDayEnd), DayOfWeek.Friday);
|
||||
set => SaveProperty(propertyName: nameof(WorkingDayEnd), value);
|
||||
}
|
||||
|
||||
public CalendarSettings GetCurrentCalendarSettings()
|
||||
{
|
||||
var workingDays = GetDaysBetween(WorkingDayStart, WorkingDayEnd);
|
||||
|
||||
return new CalendarSettings(FirstDayOfWeek,
|
||||
workingDays,
|
||||
WorkingHourStart,
|
||||
WorkingHourEnd,
|
||||
HourHeight,
|
||||
Prefer24HourTimeFormat ? DayHeaderDisplayType.TwentyFourHour : DayHeaderDisplayType.TwelveHour,
|
||||
new CultureInfo(WinoTranslationDictionary.GetLanguageFileNameRelativePath(CurrentLanguage)));
|
||||
}
|
||||
|
||||
private List<DayOfWeek> GetDaysBetween(DayOfWeek startDay, DayOfWeek endDay)
|
||||
{
|
||||
var daysOfWeek = new List<DayOfWeek>();
|
||||
|
||||
int currentDay = (int)startDay;
|
||||
int endDayInt = (int)endDay;
|
||||
|
||||
// If endDay is before startDay in the week, wrap around
|
||||
if (endDayInt < currentDay)
|
||||
{
|
||||
endDayInt += 7;
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
// Collect days from startDay to endDay
|
||||
while (currentDay <= endDayInt)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
|
||||
PreferenceChanged?.Invoke(this, e.PropertyName);
|
||||
daysOfWeek.Add((DayOfWeek)(currentDay % 7));
|
||||
currentDay++;
|
||||
}
|
||||
|
||||
private void SaveProperty(string propertyName, object value) => _configurationService.Set(propertyName, value);
|
||||
|
||||
private void SetPropertyAndSave(string propertyName, object value)
|
||||
{
|
||||
_configurationService.Set(propertyName, value);
|
||||
|
||||
OnPropertyChanged(propertyName);
|
||||
Debug.WriteLine($"PreferencesService -> {propertyName}:{value?.ToString()}");
|
||||
}
|
||||
|
||||
public MailRenderingOptions GetRenderingOptions()
|
||||
=> new MailRenderingOptions()
|
||||
{
|
||||
LoadImages = RenderImages,
|
||||
LoadStyles = RenderStyles,
|
||||
RenderPlaintextLinks = RenderPlaintextLinks
|
||||
};
|
||||
|
||||
public MailListDisplayMode MailItemDisplayMode
|
||||
{
|
||||
get => _configurationService.Get(nameof(MailItemDisplayMode), MailListDisplayMode.Spacious);
|
||||
set => SetPropertyAndSave(nameof(MailItemDisplayMode), value);
|
||||
}
|
||||
|
||||
public bool IsSemanticZoomEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsSemanticZoomEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsSemanticZoomEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsHardDeleteProtectionEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsHardDeleteProtectionEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsHardDeleteProtectionEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsThreadingEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsThreadingEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsThreadingEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsMailListActionBarEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsMailListActionBarEnabled), false);
|
||||
set => SetPropertyAndSave(nameof(IsMailListActionBarEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsShowSenderPicturesEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsShowSenderPicturesEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsShowSenderPicturesEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsShowPreviewEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsShowPreviewEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsShowPreviewEnabled), value);
|
||||
}
|
||||
|
||||
public bool RenderStyles
|
||||
{
|
||||
get => _configurationService.Get(nameof(RenderStyles), true);
|
||||
set => SetPropertyAndSave(nameof(RenderStyles), value);
|
||||
}
|
||||
|
||||
public bool RenderPlaintextLinks
|
||||
{
|
||||
get => _configurationService.Get(nameof(RenderPlaintextLinks), true);
|
||||
set => SetPropertyAndSave(nameof(RenderPlaintextLinks), value);
|
||||
}
|
||||
|
||||
public bool RenderImages
|
||||
{
|
||||
get => _configurationService.Get(nameof(RenderImages), true);
|
||||
set => SetPropertyAndSave(nameof(RenderImages), value);
|
||||
}
|
||||
|
||||
public bool Prefer24HourTimeFormat
|
||||
{
|
||||
get => _configurationService.Get(nameof(Prefer24HourTimeFormat), false);
|
||||
set => SetPropertyAndSave(nameof(Prefer24HourTimeFormat), value);
|
||||
}
|
||||
|
||||
public MailMarkAsOption MarkAsPreference
|
||||
{
|
||||
get => _configurationService.Get(nameof(MarkAsPreference), MailMarkAsOption.WhenSelected);
|
||||
set => SetPropertyAndSave(nameof(MarkAsPreference), value);
|
||||
}
|
||||
|
||||
public int MarkAsDelay
|
||||
{
|
||||
get => _configurationService.Get(nameof(MarkAsDelay), 5);
|
||||
set => SetPropertyAndSave(nameof(MarkAsDelay), value);
|
||||
}
|
||||
|
||||
public MailOperation RightSwipeOperation
|
||||
{
|
||||
get => _configurationService.Get(nameof(RightSwipeOperation), MailOperation.MarkAsRead);
|
||||
set => SetPropertyAndSave(nameof(RightSwipeOperation), value);
|
||||
}
|
||||
|
||||
public MailOperation LeftSwipeOperation
|
||||
{
|
||||
get => _configurationService.Get(nameof(LeftSwipeOperation), MailOperation.SoftDelete);
|
||||
set => SetPropertyAndSave(nameof(LeftSwipeOperation), value);
|
||||
}
|
||||
|
||||
public bool IsHoverActionsEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsHoverActionsEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsHoverActionsEnabled), value);
|
||||
}
|
||||
|
||||
public MailOperation LeftHoverAction
|
||||
{
|
||||
get => _configurationService.Get(nameof(LeftHoverAction), MailOperation.Archive);
|
||||
set => SetPropertyAndSave(nameof(LeftHoverAction), value);
|
||||
}
|
||||
|
||||
public MailOperation CenterHoverAction
|
||||
{
|
||||
get => _configurationService.Get(nameof(CenterHoverAction), MailOperation.SoftDelete);
|
||||
set => SetPropertyAndSave(nameof(CenterHoverAction), value);
|
||||
}
|
||||
|
||||
public MailOperation RightHoverAction
|
||||
{
|
||||
get => _configurationService.Get(nameof(RightHoverAction), MailOperation.SetFlag);
|
||||
set => SetPropertyAndSave(nameof(RightHoverAction), value);
|
||||
}
|
||||
|
||||
public bool IsLoggingEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsLoggingEnabled), true);
|
||||
set => SetPropertyAndSave(nameof(IsLoggingEnabled), value);
|
||||
}
|
||||
|
||||
public bool IsMailkitProtocolLoggerEnabled
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsMailkitProtocolLoggerEnabled), false);
|
||||
set => SetPropertyAndSave(nameof(IsMailkitProtocolLoggerEnabled), value);
|
||||
}
|
||||
|
||||
public Guid? StartupEntityId
|
||||
{
|
||||
get => _configurationService.Get<Guid?>(nameof(StartupEntityId), null);
|
||||
set => SaveProperty(propertyName: nameof(StartupEntityId), value);
|
||||
}
|
||||
|
||||
public AppLanguage CurrentLanguage
|
||||
{
|
||||
get => _configurationService.Get(nameof(CurrentLanguage), TranslationService.DefaultAppLanguage);
|
||||
set => SaveProperty(propertyName: nameof(CurrentLanguage), value);
|
||||
}
|
||||
|
||||
public string ReaderFont
|
||||
{
|
||||
get => _configurationService.Get(nameof(ReaderFont), "Calibri");
|
||||
set => SaveProperty(propertyName: nameof(ReaderFont), value);
|
||||
}
|
||||
|
||||
public int ReaderFontSize
|
||||
{
|
||||
get => _configurationService.Get(nameof(ReaderFontSize), 14);
|
||||
set => SaveProperty(propertyName: nameof(ReaderFontSize), value);
|
||||
}
|
||||
|
||||
public string ComposerFont
|
||||
{
|
||||
get => _configurationService.Get(nameof(ComposerFont), "Calibri");
|
||||
set => SaveProperty(propertyName: nameof(ComposerFont), value);
|
||||
}
|
||||
|
||||
public int ComposerFontSize
|
||||
{
|
||||
get => _configurationService.Get(nameof(ComposerFontSize), 14);
|
||||
set => SaveProperty(propertyName: nameof(ComposerFontSize), value);
|
||||
}
|
||||
|
||||
public bool IsNavigationPaneOpened
|
||||
{
|
||||
get => _configurationService.Get(nameof(IsNavigationPaneOpened), true);
|
||||
set => SaveProperty(propertyName: nameof(IsNavigationPaneOpened), value);
|
||||
}
|
||||
|
||||
public bool AutoSelectNextItem
|
||||
{
|
||||
get => _configurationService.Get(nameof(AutoSelectNextItem), true);
|
||||
set => SaveProperty(propertyName: nameof(AutoSelectNextItem), value);
|
||||
}
|
||||
|
||||
public ServerBackgroundMode ServerTerminationBehavior
|
||||
{
|
||||
get => _configurationService.Get(nameof(ServerTerminationBehavior), ServerBackgroundMode.MinimizedTray);
|
||||
set => SaveProperty(propertyName: nameof(ServerTerminationBehavior), value);
|
||||
}
|
||||
|
||||
public string DiagnosticId
|
||||
{
|
||||
get => _configurationService.Get(nameof(DiagnosticId), Guid.NewGuid().ToString());
|
||||
set => SaveProperty(propertyName: nameof(DiagnosticId), value);
|
||||
}
|
||||
|
||||
public DayOfWeek FirstDayOfWeek
|
||||
{
|
||||
get => _configurationService.Get(nameof(FirstDayOfWeek), DayOfWeek.Monday);
|
||||
set => SaveProperty(propertyName: nameof(FirstDayOfWeek), value);
|
||||
}
|
||||
|
||||
public double HourHeight
|
||||
{
|
||||
get => _configurationService.Get(nameof(HourHeight), 60.0);
|
||||
set => SaveProperty(propertyName: nameof(HourHeight), value);
|
||||
}
|
||||
|
||||
public TimeSpan WorkingHourStart
|
||||
{
|
||||
get => _configurationService.Get(nameof(WorkingHourStart), new TimeSpan(8, 0, 0));
|
||||
set => SaveProperty(propertyName: nameof(WorkingHourStart), value);
|
||||
}
|
||||
|
||||
public TimeSpan WorkingHourEnd
|
||||
{
|
||||
get => _configurationService.Get(nameof(WorkingHourEnd), new TimeSpan(17, 0, 0));
|
||||
set => SaveProperty(propertyName: nameof(WorkingHourEnd), value);
|
||||
}
|
||||
|
||||
public DayOfWeek WorkingDayStart
|
||||
{
|
||||
get => _configurationService.Get(nameof(WorkingDayStart), DayOfWeek.Monday);
|
||||
set => SaveProperty(propertyName: nameof(WorkingDayStart), value);
|
||||
}
|
||||
|
||||
public DayOfWeek WorkingDayEnd
|
||||
{
|
||||
get => _configurationService.Get(nameof(WorkingDayEnd), DayOfWeek.Friday);
|
||||
set => SaveProperty(propertyName: nameof(WorkingDayEnd), value);
|
||||
}
|
||||
|
||||
public CalendarSettings GetCurrentCalendarSettings()
|
||||
{
|
||||
var workingDays = GetDaysBetween(WorkingDayStart, WorkingDayEnd);
|
||||
|
||||
return new CalendarSettings(FirstDayOfWeek,
|
||||
workingDays,
|
||||
WorkingHourStart,
|
||||
WorkingHourEnd,
|
||||
HourHeight,
|
||||
Prefer24HourTimeFormat ? DayHeaderDisplayType.TwentyFourHour : DayHeaderDisplayType.TwelveHour,
|
||||
new CultureInfo(WinoTranslationDictionary.GetLanguageFileNameRelativePath(CurrentLanguage)));
|
||||
}
|
||||
|
||||
private List<DayOfWeek> GetDaysBetween(DayOfWeek startDay, DayOfWeek endDay)
|
||||
{
|
||||
var daysOfWeek = new List<DayOfWeek>();
|
||||
|
||||
int currentDay = (int)startDay;
|
||||
int endDayInt = (int)endDay;
|
||||
|
||||
// If endDay is before startDay in the week, wrap around
|
||||
if (endDayInt < currentDay)
|
||||
{
|
||||
endDayInt += 7;
|
||||
}
|
||||
|
||||
// Collect days from startDay to endDay
|
||||
while (currentDay <= endDayInt)
|
||||
{
|
||||
daysOfWeek.Add((DayOfWeek)(currentDay % 7));
|
||||
currentDay++;
|
||||
}
|
||||
|
||||
return daysOfWeek;
|
||||
}
|
||||
return daysOfWeek;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,254 +12,253 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Printing;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Printer service that uses WinRT APIs to print PDF files.
|
||||
/// Used modified version of the code here:
|
||||
/// https://github.com/microsoft/Win2D-Samples/blob/reunion_master/ExampleGallery/PrintingExample.xaml.cs
|
||||
/// HTML file is saved as PDF to temporary location.
|
||||
/// Then PDF is loaded as PdfDocument and printed using CanvasBitmap for each page.
|
||||
/// </summary>
|
||||
public class PrintService : IPrintService
|
||||
{
|
||||
/// <summary>
|
||||
/// Printer service that uses WinRT APIs to print PDF files.
|
||||
/// Used modified version of the code here:
|
||||
/// https://github.com/microsoft/Win2D-Samples/blob/reunion_master/ExampleGallery/PrintingExample.xaml.cs
|
||||
/// HTML file is saved as PDF to temporary location.
|
||||
/// Then PDF is loaded as PdfDocument and printed using CanvasBitmap for each page.
|
||||
/// </summary>
|
||||
public class PrintService : IPrintService
|
||||
private TaskCompletionSource<PrintingResult> _taskCompletionSource;
|
||||
private CanvasPrintDocument printDocument;
|
||||
private PrintTask printTask;
|
||||
private PdfDocument pdfDocument;
|
||||
|
||||
private List<CanvasBitmap> bitmaps = new();
|
||||
private Vector2 largestBitmap;
|
||||
private Vector2 pageSize;
|
||||
private Vector2 imagePadding = new Vector2(64, 64);
|
||||
private Vector2 cellSize;
|
||||
|
||||
private int bitmapCount;
|
||||
private int columns;
|
||||
private int rows;
|
||||
private int bitmapsPerPage;
|
||||
private int pageCount = -1;
|
||||
|
||||
private PrintInformation _currentPrintInformation;
|
||||
|
||||
public async Task<PrintingResult> PrintPdfFileAsync(string pdfFilePath, string printTitle)
|
||||
{
|
||||
private TaskCompletionSource<PrintingResult> _taskCompletionSource;
|
||||
private CanvasPrintDocument printDocument;
|
||||
private PrintTask printTask;
|
||||
private PdfDocument pdfDocument;
|
||||
|
||||
private List<CanvasBitmap> bitmaps = new();
|
||||
private Vector2 largestBitmap;
|
||||
private Vector2 pageSize;
|
||||
private Vector2 imagePadding = new Vector2(64, 64);
|
||||
private Vector2 cellSize;
|
||||
|
||||
private int bitmapCount;
|
||||
private int columns;
|
||||
private int rows;
|
||||
private int bitmapsPerPage;
|
||||
private int pageCount = -1;
|
||||
|
||||
private PrintInformation _currentPrintInformation;
|
||||
|
||||
public async Task<PrintingResult> PrintPdfFileAsync(string pdfFilePath, string printTitle)
|
||||
if (_taskCompletionSource != null)
|
||||
{
|
||||
if (_taskCompletionSource != null)
|
||||
{
|
||||
_taskCompletionSource.TrySetResult(PrintingResult.Abandoned);
|
||||
_taskCompletionSource = new TaskCompletionSource<PrintingResult>();
|
||||
}
|
||||
|
||||
// Load the PDF file
|
||||
var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(pdfFilePath);
|
||||
pdfDocument = await PdfDocument.LoadFromFileAsync(file);
|
||||
|
||||
_taskCompletionSource ??= new TaskCompletionSource<PrintingResult>();
|
||||
|
||||
_currentPrintInformation = new PrintInformation(pdfFilePath, printTitle);
|
||||
|
||||
printDocument = new CanvasPrintDocument();
|
||||
printDocument.PrintTaskOptionsChanged += OnDocumentTaskOptionsChanged;
|
||||
printDocument.Preview += OnDocumentPreview;
|
||||
printDocument.Print += OnDocumentPrint;
|
||||
|
||||
var printManager = PrintManager.GetForCurrentView();
|
||||
printManager.PrintTaskRequested += PrintingExample_PrintTaskRequested;
|
||||
|
||||
try
|
||||
{
|
||||
await PrintManager.ShowPrintUIAsync();
|
||||
|
||||
var result = await _taskCompletionSource.Task;
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Dispose everything.
|
||||
UnregisterPrintManager(printManager);
|
||||
ClearBitmaps();
|
||||
UnregisterTask();
|
||||
DisposePDFDocument();
|
||||
|
||||
_taskCompletionSource = null;
|
||||
}
|
||||
_taskCompletionSource.TrySetResult(PrintingResult.Abandoned);
|
||||
_taskCompletionSource = new TaskCompletionSource<PrintingResult>();
|
||||
}
|
||||
|
||||
private void DisposePDFDocument()
|
||||
// Load the PDF file
|
||||
var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(pdfFilePath);
|
||||
pdfDocument = await PdfDocument.LoadFromFileAsync(file);
|
||||
|
||||
_taskCompletionSource ??= new TaskCompletionSource<PrintingResult>();
|
||||
|
||||
_currentPrintInformation = new PrintInformation(pdfFilePath, printTitle);
|
||||
|
||||
printDocument = new CanvasPrintDocument();
|
||||
printDocument.PrintTaskOptionsChanged += OnDocumentTaskOptionsChanged;
|
||||
printDocument.Preview += OnDocumentPreview;
|
||||
printDocument.Print += OnDocumentPrint;
|
||||
|
||||
var printManager = PrintManager.GetForCurrentView();
|
||||
printManager.PrintTaskRequested += PrintingExample_PrintTaskRequested;
|
||||
|
||||
try
|
||||
{
|
||||
if (pdfDocument != null)
|
||||
{
|
||||
pdfDocument = null;
|
||||
}
|
||||
await PrintManager.ShowPrintUIAsync();
|
||||
|
||||
var result = await _taskCompletionSource.Task;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void UnregisterTask()
|
||||
{
|
||||
if (printTask != null)
|
||||
{
|
||||
printTask.Completed -= TaskCompleted;
|
||||
printTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterPrintManager(PrintManager manager)
|
||||
{
|
||||
manager.PrintTaskRequested -= PrintingExample_PrintTaskRequested;
|
||||
}
|
||||
|
||||
private void ClearBitmaps()
|
||||
{
|
||||
foreach (var bitmap in bitmaps)
|
||||
{
|
||||
bitmap.Dispose();
|
||||
}
|
||||
|
||||
bitmaps.Clear();
|
||||
}
|
||||
|
||||
private void PrintingExample_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
|
||||
{
|
||||
if (_currentPrintInformation == null) return;
|
||||
|
||||
printTask = args.Request.CreatePrintTask(_currentPrintInformation.PDFTitle, (createPrintTaskArgs) =>
|
||||
{
|
||||
createPrintTaskArgs.SetSource(printDocument);
|
||||
});
|
||||
|
||||
printTask.Completed += TaskCompleted;
|
||||
}
|
||||
|
||||
private void TaskCompleted(PrintTask sender, PrintTaskCompletedEventArgs args)
|
||||
=> _taskCompletionSource?.TrySetResult((PrintingResult)args.Completion);
|
||||
|
||||
private async void OnDocumentTaskOptionsChanged(CanvasPrintDocument sender, CanvasPrintTaskOptionsChangedEventArgs args)
|
||||
{
|
||||
var deferral = args.GetDeferral();
|
||||
|
||||
try
|
||||
{
|
||||
await LoadPDFPageBitmapsAsync(sender);
|
||||
|
||||
var pageDesc = args.PrintTaskOptions.GetPageDescription(1);
|
||||
var newPageSize = pageDesc.PageSize.ToVector2();
|
||||
|
||||
if (pageSize == newPageSize && pageCount != -1)
|
||||
{
|
||||
// We've already figured out the pages and the page size hasn't changed, so there's nothing left for us to do here.
|
||||
return;
|
||||
}
|
||||
|
||||
pageSize = newPageSize;
|
||||
sender.InvalidatePreview();
|
||||
|
||||
// Figure out the bitmap index at the top of the current preview page. We'll request that the preview defaults to showing
|
||||
// the page that still has this bitmap on it in the new layout.
|
||||
int indexOnCurrentPage = 0;
|
||||
if (pageCount != -1)
|
||||
{
|
||||
indexOnCurrentPage = (int)(args.CurrentPreviewPageNumber - 1) * bitmapsPerPage;
|
||||
}
|
||||
|
||||
// Calculate the new layout
|
||||
var printablePageSize = pageSize * 0.9f;
|
||||
|
||||
cellSize = largestBitmap + imagePadding;
|
||||
|
||||
var cellsPerPage = printablePageSize / cellSize;
|
||||
|
||||
columns = Math.Max(1, (int)Math.Floor(cellsPerPage.X));
|
||||
rows = Math.Max(1, (int)Math.Floor(cellsPerPage.Y));
|
||||
|
||||
bitmapsPerPage = columns * rows;
|
||||
|
||||
// Calculate the page count
|
||||
bitmapCount = bitmaps.Count;
|
||||
pageCount = (int)Math.Ceiling(bitmapCount / (double)bitmapsPerPage);
|
||||
sender.SetPageCount((uint)pageCount);
|
||||
|
||||
// Set the preview page to the one that has the item that was currently displayed in the last preview
|
||||
args.NewPreviewPageNumber = (uint)(indexOnCurrentPage / bitmapsPerPage) + 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
deferral.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadPDFPageBitmapsAsync(CanvasPrintDocument sender)
|
||||
finally
|
||||
{
|
||||
// Dispose everything.
|
||||
UnregisterPrintManager(printManager);
|
||||
ClearBitmaps();
|
||||
UnregisterTask();
|
||||
DisposePDFDocument();
|
||||
|
||||
bitmaps ??= new List<CanvasBitmap>();
|
||||
_taskCompletionSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < pdfDocument.PageCount; i++)
|
||||
{
|
||||
var page = pdfDocument.GetPage((uint)i);
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
await page.RenderToStreamAsync(stream);
|
||||
var bitmap = await CanvasBitmap.LoadAsync(sender, stream);
|
||||
bitmaps.Add(bitmap);
|
||||
}
|
||||
private void DisposePDFDocument()
|
||||
{
|
||||
if (pdfDocument != null)
|
||||
{
|
||||
pdfDocument = null;
|
||||
}
|
||||
}
|
||||
|
||||
largestBitmap = Vector2.Zero;
|
||||
private void UnregisterTask()
|
||||
{
|
||||
if (printTask != null)
|
||||
{
|
||||
printTask.Completed -= TaskCompleted;
|
||||
printTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var bitmap in bitmaps)
|
||||
{
|
||||
largestBitmap.X = Math.Max(largestBitmap.X, (float)bitmap.Size.Width);
|
||||
largestBitmap.Y = Math.Max(largestBitmap.Y, (float)bitmap.Size.Height);
|
||||
}
|
||||
private void UnregisterPrintManager(PrintManager manager)
|
||||
{
|
||||
manager.PrintTaskRequested -= PrintingExample_PrintTaskRequested;
|
||||
}
|
||||
|
||||
private void ClearBitmaps()
|
||||
{
|
||||
foreach (var bitmap in bitmaps)
|
||||
{
|
||||
bitmap.Dispose();
|
||||
}
|
||||
|
||||
bitmaps.Clear();
|
||||
}
|
||||
|
||||
private void OnDocumentPreview(CanvasPrintDocument sender, CanvasPreviewEventArgs args)
|
||||
private void PrintingExample_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
|
||||
{
|
||||
if (_currentPrintInformation == null) return;
|
||||
|
||||
printTask = args.Request.CreatePrintTask(_currentPrintInformation.PDFTitle, (createPrintTaskArgs) =>
|
||||
{
|
||||
var ds = args.DrawingSession;
|
||||
var pageNumber = args.PageNumber;
|
||||
createPrintTaskArgs.SetSource(printDocument);
|
||||
});
|
||||
|
||||
DrawPdfPage(sender, ds, pageNumber);
|
||||
}
|
||||
printTask.Completed += TaskCompleted;
|
||||
}
|
||||
|
||||
private void OnDocumentPrint(CanvasPrintDocument sender, CanvasPrintEventArgs args)
|
||||
private void TaskCompleted(PrintTask sender, PrintTaskCompletedEventArgs args)
|
||||
=> _taskCompletionSource?.TrySetResult((PrintingResult)args.Completion);
|
||||
|
||||
private async void OnDocumentTaskOptionsChanged(CanvasPrintDocument sender, CanvasPrintTaskOptionsChangedEventArgs args)
|
||||
{
|
||||
var deferral = args.GetDeferral();
|
||||
|
||||
try
|
||||
{
|
||||
var detailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(args.PrintTaskOptions);
|
||||
await LoadPDFPageBitmapsAsync(sender);
|
||||
|
||||
int pageCountToPrint = (int)pdfDocument.PageCount;
|
||||
var pageDesc = args.PrintTaskOptions.GetPageDescription(1);
|
||||
var newPageSize = pageDesc.PageSize.ToVector2();
|
||||
|
||||
for (uint i = 1; i <= pageCountToPrint; ++i)
|
||||
if (pageSize == newPageSize && pageCount != -1)
|
||||
{
|
||||
using var ds = args.CreateDrawingSession();
|
||||
var imageableRect = args.PrintTaskOptions.GetPageDescription(i).ImageableRect;
|
||||
|
||||
DrawPdfPage(sender, ds, i);
|
||||
// We've already figured out the pages and the page size hasn't changed, so there's nothing left for us to do here.
|
||||
return;
|
||||
}
|
||||
|
||||
pageSize = newPageSize;
|
||||
sender.InvalidatePreview();
|
||||
|
||||
// Figure out the bitmap index at the top of the current preview page. We'll request that the preview defaults to showing
|
||||
// the page that still has this bitmap on it in the new layout.
|
||||
int indexOnCurrentPage = 0;
|
||||
if (pageCount != -1)
|
||||
{
|
||||
indexOnCurrentPage = (int)(args.CurrentPreviewPageNumber - 1) * bitmapsPerPage;
|
||||
}
|
||||
|
||||
// Calculate the new layout
|
||||
var printablePageSize = pageSize * 0.9f;
|
||||
|
||||
cellSize = largestBitmap + imagePadding;
|
||||
|
||||
var cellsPerPage = printablePageSize / cellSize;
|
||||
|
||||
columns = Math.Max(1, (int)Math.Floor(cellsPerPage.X));
|
||||
rows = Math.Max(1, (int)Math.Floor(cellsPerPage.Y));
|
||||
|
||||
bitmapsPerPage = columns * rows;
|
||||
|
||||
// Calculate the page count
|
||||
bitmapCount = bitmaps.Count;
|
||||
pageCount = (int)Math.Ceiling(bitmapCount / (double)bitmapsPerPage);
|
||||
sender.SetPageCount((uint)pageCount);
|
||||
|
||||
// Set the preview page to the one that has the item that was currently displayed in the last preview
|
||||
args.NewPreviewPageNumber = (uint)(indexOnCurrentPage / bitmapsPerPage) + 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
deferral.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadPDFPageBitmapsAsync(CanvasPrintDocument sender)
|
||||
{
|
||||
ClearBitmaps();
|
||||
|
||||
bitmaps ??= new List<CanvasBitmap>();
|
||||
|
||||
for (int i = 0; i < pdfDocument.PageCount; i++)
|
||||
{
|
||||
var page = pdfDocument.GetPage((uint)i);
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
await page.RenderToStreamAsync(stream);
|
||||
var bitmap = await CanvasBitmap.LoadAsync(sender, stream);
|
||||
bitmaps.Add(bitmap);
|
||||
}
|
||||
|
||||
private void DrawPdfPage(CanvasPrintDocument sender, CanvasDrawingSession ds, uint pageNumber)
|
||||
largestBitmap = Vector2.Zero;
|
||||
|
||||
foreach (var bitmap in bitmaps)
|
||||
{
|
||||
if (bitmaps?.Count == 0) return;
|
||||
largestBitmap.X = Math.Max(largestBitmap.X, (float)bitmap.Size.Width);
|
||||
largestBitmap.Y = Math.Max(largestBitmap.Y, (float)bitmap.Size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
var cellAcross = new Vector2(cellSize.X, 0);
|
||||
var cellDown = new Vector2(0, cellSize.Y);
|
||||
|
||||
var totalSize = cellAcross * columns + cellDown * rows;
|
||||
Vector2 topLeft = (pageSize - totalSize) / 2;
|
||||
private void OnDocumentPreview(CanvasPrintDocument sender, CanvasPreviewEventArgs args)
|
||||
{
|
||||
var ds = args.DrawingSession;
|
||||
var pageNumber = args.PageNumber;
|
||||
|
||||
int bitmapIndex = ((int)pageNumber - 1) * bitmapsPerPage;
|
||||
DrawPdfPage(sender, ds, pageNumber);
|
||||
}
|
||||
|
||||
for (int row = 0; row < rows; ++row)
|
||||
private void OnDocumentPrint(CanvasPrintDocument sender, CanvasPrintEventArgs args)
|
||||
{
|
||||
var detailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(args.PrintTaskOptions);
|
||||
|
||||
int pageCountToPrint = (int)pdfDocument.PageCount;
|
||||
|
||||
for (uint i = 1; i <= pageCountToPrint; ++i)
|
||||
{
|
||||
using var ds = args.CreateDrawingSession();
|
||||
var imageableRect = args.PrintTaskOptions.GetPageDescription(i).ImageableRect;
|
||||
|
||||
DrawPdfPage(sender, ds, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPdfPage(CanvasPrintDocument sender, CanvasDrawingSession ds, uint pageNumber)
|
||||
{
|
||||
if (bitmaps?.Count == 0) return;
|
||||
|
||||
var cellAcross = new Vector2(cellSize.X, 0);
|
||||
var cellDown = new Vector2(0, cellSize.Y);
|
||||
|
||||
var totalSize = cellAcross * columns + cellDown * rows;
|
||||
Vector2 topLeft = (pageSize - totalSize) / 2;
|
||||
|
||||
int bitmapIndex = ((int)pageNumber - 1) * bitmapsPerPage;
|
||||
|
||||
for (int row = 0; row < rows; ++row)
|
||||
{
|
||||
for (int column = 0; column < columns; ++column)
|
||||
{
|
||||
for (int column = 0; column < columns; ++column)
|
||||
{
|
||||
var cellTopLeft = topLeft + cellAcross * column + cellDown * row;
|
||||
var bitmapInfo = bitmaps[bitmapIndex % bitmaps.Count];
|
||||
var bitmapPos = cellTopLeft + (cellSize - bitmapInfo.Size.ToVector2()) / 2;
|
||||
var cellTopLeft = topLeft + cellAcross * column + cellDown * row;
|
||||
var bitmapInfo = bitmaps[bitmapIndex % bitmaps.Count];
|
||||
var bitmapPos = cellTopLeft + (cellSize - bitmapInfo.Size.ToVector2()) / 2;
|
||||
|
||||
ds.DrawImage(bitmapInfo, bitmapPos);
|
||||
ds.DrawImage(bitmapInfo, bitmapPos);
|
||||
|
||||
bitmapIndex++;
|
||||
}
|
||||
bitmapIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,51 +6,50 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.UWP.Extensions;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class StartupBehaviorService : IStartupBehaviorService
|
||||
{
|
||||
public class StartupBehaviorService : IStartupBehaviorService
|
||||
private const string WinoServerTaskId = "WinoServer";
|
||||
|
||||
public async Task<StartupBehaviorResult> ToggleStartupBehavior(bool isEnabled)
|
||||
{
|
||||
private const string WinoServerTaskId = "WinoServer";
|
||||
|
||||
public async Task<StartupBehaviorResult> ToggleStartupBehavior(bool isEnabled)
|
||||
try
|
||||
{
|
||||
try
|
||||
var task = await StartupTask.GetAsync(WinoServerTaskId);
|
||||
|
||||
if (isEnabled)
|
||||
{
|
||||
var task = await StartupTask.GetAsync(WinoServerTaskId);
|
||||
|
||||
if (isEnabled)
|
||||
{
|
||||
await task.RequestEnableAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
task.Disable();
|
||||
}
|
||||
await task.RequestEnableAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
else
|
||||
{
|
||||
Log.Error("Error toggling startup behavior");
|
||||
|
||||
task.Disable();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Error("Error toggling startup behavior");
|
||||
|
||||
return await GetCurrentStartupBehaviorAsync();
|
||||
}
|
||||
|
||||
public async Task<StartupBehaviorResult> GetCurrentStartupBehaviorAsync()
|
||||
return await GetCurrentStartupBehaviorAsync();
|
||||
}
|
||||
|
||||
public async Task<StartupBehaviorResult> GetCurrentStartupBehaviorAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var task = await StartupTask.GetAsync(WinoServerTaskId);
|
||||
var task = await StartupTask.GetAsync(WinoServerTaskId);
|
||||
|
||||
return task.State.AsStartupBehaviorResult();
|
||||
return task.State.AsStartupBehaviorResult();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error getting startup behavior");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error getting startup behavior");
|
||||
|
||||
return StartupBehaviorResult.Fatal;
|
||||
}
|
||||
return StartupBehaviorResult.Fatal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,141 +4,140 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Services
|
||||
namespace Wino.Services;
|
||||
|
||||
public class StatePersistenceService : ObservableObject, IStatePersistanceService
|
||||
{
|
||||
public class StatePersistenceService : ObservableObject, IStatePersistanceService
|
||||
public event EventHandler<string> StatePropertyChanged;
|
||||
|
||||
private const string OpenPaneLengthKey = nameof(OpenPaneLengthKey);
|
||||
private const string MailListPaneLengthKey = nameof(MailListPaneLengthKey);
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
|
||||
public StatePersistenceService(IConfigurationService configurationService)
|
||||
{
|
||||
public event EventHandler<string> StatePropertyChanged;
|
||||
_configurationService = configurationService;
|
||||
|
||||
private const string OpenPaneLengthKey = nameof(OpenPaneLengthKey);
|
||||
private const string MailListPaneLengthKey = nameof(MailListPaneLengthKey);
|
||||
_openPaneLength = _configurationService.Get(OpenPaneLengthKey, 320d);
|
||||
_mailListPaneLength = _configurationService.Get(MailListPaneLengthKey, 420d);
|
||||
_calendarDisplayType = _configurationService.Get(nameof(CalendarDisplayType), CalendarDisplayType.Week);
|
||||
_dayDisplayCount = _configurationService.Get(nameof(DayDisplayCount), 1);
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
PropertyChanged += ServicePropertyChanged;
|
||||
}
|
||||
|
||||
public StatePersistenceService(IConfigurationService configurationService)
|
||||
private void ServicePropertyChanged(object sender, PropertyChangedEventArgs e) => StatePropertyChanged?.Invoke(this, e.PropertyName);
|
||||
|
||||
public bool IsBackButtonVisible => IsReadingMail && IsReaderNarrowed;
|
||||
|
||||
private bool isReadingMail;
|
||||
|
||||
public bool IsReadingMail
|
||||
{
|
||||
get => isReadingMail;
|
||||
set
|
||||
{
|
||||
_configurationService = configurationService;
|
||||
|
||||
_openPaneLength = _configurationService.Get(OpenPaneLengthKey, 320d);
|
||||
_mailListPaneLength = _configurationService.Get(MailListPaneLengthKey, 420d);
|
||||
_calendarDisplayType = _configurationService.Get(nameof(CalendarDisplayType), CalendarDisplayType.Week);
|
||||
_dayDisplayCount = _configurationService.Get(nameof(DayDisplayCount), 1);
|
||||
|
||||
PropertyChanged += ServicePropertyChanged;
|
||||
}
|
||||
|
||||
private void ServicePropertyChanged(object sender, PropertyChangedEventArgs e) => StatePropertyChanged?.Invoke(this, e.PropertyName);
|
||||
|
||||
public bool IsBackButtonVisible => IsReadingMail && IsReaderNarrowed;
|
||||
|
||||
private bool isReadingMail;
|
||||
|
||||
public bool IsReadingMail
|
||||
{
|
||||
get => isReadingMail;
|
||||
set
|
||||
if (SetProperty(ref isReadingMail, value))
|
||||
{
|
||||
if (SetProperty(ref isReadingMail, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(IsBackButtonVisible));
|
||||
}
|
||||
OnPropertyChanged(nameof(IsBackButtonVisible));
|
||||
}
|
||||
}
|
||||
|
||||
private bool shouldShiftMailRenderingDesign;
|
||||
|
||||
public bool ShouldShiftMailRenderingDesign
|
||||
{
|
||||
get { return shouldShiftMailRenderingDesign; }
|
||||
set { shouldShiftMailRenderingDesign = value; }
|
||||
}
|
||||
|
||||
private bool isReaderNarrowed;
|
||||
|
||||
public bool IsReaderNarrowed
|
||||
{
|
||||
get => isReaderNarrowed;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref isReaderNarrowed, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(IsBackButtonVisible));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string coreWindowTitle;
|
||||
|
||||
public string CoreWindowTitle
|
||||
{
|
||||
get => coreWindowTitle;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref coreWindowTitle, value))
|
||||
{
|
||||
UpdateAppCoreWindowTitle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double _openPaneLength;
|
||||
public double OpenPaneLength
|
||||
{
|
||||
get => _openPaneLength;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _openPaneLength, value))
|
||||
{
|
||||
_configurationService.Set(OpenPaneLengthKey, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double _mailListPaneLength;
|
||||
public double MailListPaneLength
|
||||
{
|
||||
get => _mailListPaneLength;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _mailListPaneLength, value))
|
||||
{
|
||||
_configurationService.Set(MailListPaneLengthKey, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CalendarDisplayType _calendarDisplayType;
|
||||
public CalendarDisplayType CalendarDisplayType
|
||||
{
|
||||
get => _calendarDisplayType;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _calendarDisplayType, value))
|
||||
{
|
||||
_configurationService.Set(nameof(CalendarDisplayType), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _dayDisplayCount;
|
||||
public int DayDisplayCount
|
||||
{
|
||||
get => _dayDisplayCount;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _dayDisplayCount, value))
|
||||
{
|
||||
_configurationService.Set(nameof(DayDisplayCount), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAppCoreWindowTitle()
|
||||
{
|
||||
var appView = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView();
|
||||
|
||||
if (appView != null)
|
||||
appView.Title = CoreWindowTitle;
|
||||
}
|
||||
}
|
||||
|
||||
private bool shouldShiftMailRenderingDesign;
|
||||
|
||||
public bool ShouldShiftMailRenderingDesign
|
||||
{
|
||||
get { return shouldShiftMailRenderingDesign; }
|
||||
set { shouldShiftMailRenderingDesign = value; }
|
||||
}
|
||||
|
||||
private bool isReaderNarrowed;
|
||||
|
||||
public bool IsReaderNarrowed
|
||||
{
|
||||
get => isReaderNarrowed;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref isReaderNarrowed, value))
|
||||
{
|
||||
OnPropertyChanged(nameof(IsBackButtonVisible));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string coreWindowTitle;
|
||||
|
||||
public string CoreWindowTitle
|
||||
{
|
||||
get => coreWindowTitle;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref coreWindowTitle, value))
|
||||
{
|
||||
UpdateAppCoreWindowTitle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double _openPaneLength;
|
||||
public double OpenPaneLength
|
||||
{
|
||||
get => _openPaneLength;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _openPaneLength, value))
|
||||
{
|
||||
_configurationService.Set(OpenPaneLengthKey, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double _mailListPaneLength;
|
||||
public double MailListPaneLength
|
||||
{
|
||||
get => _mailListPaneLength;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _mailListPaneLength, value))
|
||||
{
|
||||
_configurationService.Set(MailListPaneLengthKey, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CalendarDisplayType _calendarDisplayType;
|
||||
public CalendarDisplayType CalendarDisplayType
|
||||
{
|
||||
get => _calendarDisplayType;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _calendarDisplayType, value))
|
||||
{
|
||||
_configurationService.Set(nameof(CalendarDisplayType), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _dayDisplayCount;
|
||||
public int DayDisplayCount
|
||||
{
|
||||
get => _dayDisplayCount;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _dayDisplayCount, value))
|
||||
{
|
||||
_configurationService.Set(nameof(DayDisplayCount), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAppCoreWindowTitle()
|
||||
{
|
||||
var appView = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView();
|
||||
|
||||
if (appView != null)
|
||||
appView.Title = CoreWindowTitle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,68 +5,67 @@ using Windows.Services.Store;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Store;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class StoreManagementService : IStoreManagementService
|
||||
{
|
||||
public class StoreManagementService : IStoreManagementService
|
||||
private StoreContext CurrentContext { get; }
|
||||
|
||||
private readonly Dictionary<StoreProductType, string> productIds = new Dictionary<StoreProductType, string>()
|
||||
{
|
||||
private StoreContext CurrentContext { get; }
|
||||
{ StoreProductType.UnlimitedAccounts, "UnlimitedAccounts" }
|
||||
};
|
||||
|
||||
private readonly Dictionary<StoreProductType, string> productIds = new Dictionary<StoreProductType, string>()
|
||||
{
|
||||
{ StoreProductType.UnlimitedAccounts, "UnlimitedAccounts" }
|
||||
};
|
||||
private readonly Dictionary<StoreProductType, string> skuIds = new Dictionary<StoreProductType, string>()
|
||||
{
|
||||
{ StoreProductType.UnlimitedAccounts, "9P02MXZ42GSM" }
|
||||
};
|
||||
|
||||
private readonly Dictionary<StoreProductType, string> skuIds = new Dictionary<StoreProductType, string>()
|
||||
{
|
||||
{ StoreProductType.UnlimitedAccounts, "9P02MXZ42GSM" }
|
||||
};
|
||||
public StoreManagementService()
|
||||
{
|
||||
CurrentContext = StoreContext.GetDefault();
|
||||
}
|
||||
|
||||
public StoreManagementService()
|
||||
{
|
||||
CurrentContext = StoreContext.GetDefault();
|
||||
}
|
||||
|
||||
public async Task<bool> HasProductAsync(StoreProductType productType)
|
||||
{
|
||||
var productKey = productIds[productType];
|
||||
var appLicense = await CurrentContext.GetAppLicenseAsync();
|
||||
|
||||
if (appLicense == null)
|
||||
return false;
|
||||
|
||||
// Access the valid licenses for durable add-ons for this app.
|
||||
foreach (KeyValuePair<string, StoreLicense> item in appLicense.AddOnLicenses)
|
||||
{
|
||||
StoreLicense addOnLicense = item.Value;
|
||||
|
||||
if (addOnLicense.InAppOfferToken == productKey)
|
||||
{
|
||||
return addOnLicense.IsActive;
|
||||
}
|
||||
}
|
||||
public async Task<bool> HasProductAsync(StoreProductType productType)
|
||||
{
|
||||
var productKey = productIds[productType];
|
||||
var appLicense = await CurrentContext.GetAppLicenseAsync();
|
||||
|
||||
if (appLicense == null)
|
||||
return false;
|
||||
|
||||
// Access the valid licenses for durable add-ons for this app.
|
||||
foreach (KeyValuePair<string, StoreLicense> item in appLicense.AddOnLicenses)
|
||||
{
|
||||
StoreLicense addOnLicense = item.Value;
|
||||
|
||||
if (addOnLicense.InAppOfferToken == productKey)
|
||||
{
|
||||
return addOnLicense.IsActive;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Domain.Enums.StorePurchaseResult> PurchaseAsync(StoreProductType productType)
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<Domain.Enums.StorePurchaseResult> PurchaseAsync(StoreProductType productType)
|
||||
{
|
||||
if (await HasProductAsync(productType))
|
||||
return Domain.Enums.StorePurchaseResult.AlreadyPurchased;
|
||||
else
|
||||
{
|
||||
if (await HasProductAsync(productType))
|
||||
return Domain.Enums.StorePurchaseResult.AlreadyPurchased;
|
||||
else
|
||||
var productKey = skuIds[productType];
|
||||
|
||||
var result = await CurrentContext.RequestPurchaseAsync(productKey);
|
||||
|
||||
switch (result.Status)
|
||||
{
|
||||
var productKey = skuIds[productType];
|
||||
|
||||
var result = await CurrentContext.RequestPurchaseAsync(productKey);
|
||||
|
||||
switch (result.Status)
|
||||
{
|
||||
case StorePurchaseStatus.Succeeded:
|
||||
return Domain.Enums.StorePurchaseResult.Succeeded;
|
||||
case StorePurchaseStatus.AlreadyPurchased:
|
||||
return Domain.Enums.StorePurchaseResult.AlreadyPurchased;
|
||||
default:
|
||||
return Domain.Enums.StorePurchaseResult.NotPurchased;
|
||||
}
|
||||
case StorePurchaseStatus.Succeeded:
|
||||
return Domain.Enums.StorePurchaseResult.Succeeded;
|
||||
case StorePurchaseStatus.AlreadyPurchased:
|
||||
return Domain.Enums.StorePurchaseResult.AlreadyPurchased;
|
||||
default:
|
||||
return Domain.Enums.StorePurchaseResult.NotPurchased;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,127 +7,126 @@ using Windows.System;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class StoreRatingService : IStoreRatingService
|
||||
{
|
||||
public class StoreRatingService : IStoreRatingService
|
||||
private const string RatedStorageKey = nameof(RatedStorageKey);
|
||||
private const string LatestAskedKey = nameof(LatestAskedKey);
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
private readonly IMailDialogService _dialogService;
|
||||
|
||||
public StoreRatingService(IConfigurationService configurationService, IMailDialogService dialogService)
|
||||
{
|
||||
private const string RatedStorageKey = nameof(RatedStorageKey);
|
||||
private const string LatestAskedKey = nameof(LatestAskedKey);
|
||||
_configurationService = configurationService;
|
||||
_dialogService = dialogService;
|
||||
}
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private bool IsAskingThresholdExceeded()
|
||||
{
|
||||
var latestAskedDate = _configurationService.Get(LatestAskedKey, DateTime.MinValue);
|
||||
|
||||
public StoreRatingService(IConfigurationService configurationService, IMailDialogService dialogService)
|
||||
// Never asked before.
|
||||
// Set the threshold and wait for the next trigger.
|
||||
|
||||
if (latestAskedDate == DateTime.MinValue)
|
||||
{
|
||||
_configurationService = configurationService;
|
||||
_dialogService = dialogService;
|
||||
_configurationService.Set(LatestAskedKey, DateTime.UtcNow);
|
||||
}
|
||||
else if (DateTime.UtcNow >= latestAskedDate.AddMinutes(30))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsAskingThresholdExceeded()
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task PromptRatingDialogAsync()
|
||||
{
|
||||
// Annoying.
|
||||
if (Debugger.IsAttached) return;
|
||||
|
||||
// Swallow all exceptions. App should not crash in any errors.
|
||||
|
||||
try
|
||||
{
|
||||
var latestAskedDate = _configurationService.Get(LatestAskedKey, DateTime.MinValue);
|
||||
bool isRated = _configurationService.GetRoaming(RatedStorageKey, false);
|
||||
|
||||
// Never asked before.
|
||||
// Set the threshold and wait for the next trigger.
|
||||
if (isRated) return;
|
||||
|
||||
if (latestAskedDate == DateTime.MinValue)
|
||||
if (!isRated)
|
||||
{
|
||||
_configurationService.Set(LatestAskedKey, DateTime.UtcNow);
|
||||
}
|
||||
else if (DateTime.UtcNow >= latestAskedDate.AddMinutes(30))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!IsAskingThresholdExceeded()) return;
|
||||
|
||||
return false;
|
||||
}
|
||||
var isRateWinoApproved = await _dialogService.ShowWinoCustomMessageDialogAsync(Translator.StoreRatingDialog_Title,
|
||||
Translator.StoreRatingDialog_MessageFirstLine,
|
||||
Translator.Buttons_RateWino,
|
||||
Domain.Enums.WinoCustomMessageDialogIcon.Question,
|
||||
Translator.Buttons_No,
|
||||
RatedStorageKey);
|
||||
|
||||
public async Task PromptRatingDialogAsync()
|
||||
{
|
||||
// Annoying.
|
||||
if (Debugger.IsAttached) return;
|
||||
|
||||
// Swallow all exceptions. App should not crash in any errors.
|
||||
|
||||
try
|
||||
{
|
||||
bool isRated = _configurationService.GetRoaming(RatedStorageKey, false);
|
||||
|
||||
if (isRated) return;
|
||||
|
||||
if (!isRated)
|
||||
if (isRateWinoApproved)
|
||||
{
|
||||
if (!IsAskingThresholdExceeded()) return;
|
||||
// In case of failure of this call, we will navigate users to Store page directly.
|
||||
|
||||
var isRateWinoApproved = await _dialogService.ShowWinoCustomMessageDialogAsync(Translator.StoreRatingDialog_Title,
|
||||
Translator.StoreRatingDialog_MessageFirstLine,
|
||||
Translator.Buttons_RateWino,
|
||||
Domain.Enums.WinoCustomMessageDialogIcon.Question,
|
||||
Translator.Buttons_No,
|
||||
RatedStorageKey);
|
||||
|
||||
if (isRateWinoApproved)
|
||||
try
|
||||
{
|
||||
// In case of failure of this call, we will navigate users to Store page directly.
|
||||
|
||||
try
|
||||
{
|
||||
await ShowPortableRatingDialogAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri($"ms-windows-store://review/?ProductId=9NCRCVJC50WL"));
|
||||
}
|
||||
await ShowPortableRatingDialogAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri($"ms-windows-store://review/?ProductId=9NCRCVJC50WL"));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
finally
|
||||
{
|
||||
_configurationService.Set(LatestAskedKey, DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowPortableRatingDialogAsync()
|
||||
catch (Exception) { }
|
||||
finally
|
||||
{
|
||||
var _storeContext = StoreContext.GetDefault();
|
||||
|
||||
StoreRateAndReviewResult result = await _storeContext.RequestRateAndReviewAppAsync();
|
||||
|
||||
// Check status
|
||||
switch (result.Status)
|
||||
{
|
||||
case StoreRateAndReviewStatus.Succeeded:
|
||||
if (result.WasUpdated)
|
||||
_dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewUpdatedMessage, Domain.Enums.InfoBarMessageType.Success);
|
||||
else
|
||||
_dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewNewMessage, Domain.Enums.InfoBarMessageType.Success);
|
||||
|
||||
_configurationService.Set(RatedStorageKey, true);
|
||||
break;
|
||||
case StoreRateAndReviewStatus.CanceledByUser:
|
||||
break;
|
||||
|
||||
case StoreRateAndReviewStatus.NetworkError:
|
||||
_dialogService.InfoBarMessage(Translator.Info_ReviewNetworkErrorTitle, Translator.Info_ReviewNetworkErrorMessage, Domain.Enums.InfoBarMessageType.Warning);
|
||||
break;
|
||||
default:
|
||||
_dialogService.InfoBarMessage(Translator.Info_ReviewUnknownErrorTitle, string.Format(Translator.Info_ReviewUnknownErrorMessage, result.ExtendedError.Message), Domain.Enums.InfoBarMessageType.Warning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LaunchStorePageForReviewAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await CoreApplication.GetCurrentView()?.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
|
||||
{
|
||||
// TODO: Get it from package info.
|
||||
await Launcher.LaunchUriAsync(new Uri($"ms-windows-store://review/?ProductId=9NCRCVJC50WL"));
|
||||
});
|
||||
}
|
||||
catch (Exception) { }
|
||||
_configurationService.Set(LatestAskedKey, DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowPortableRatingDialogAsync()
|
||||
{
|
||||
var _storeContext = StoreContext.GetDefault();
|
||||
|
||||
StoreRateAndReviewResult result = await _storeContext.RequestRateAndReviewAppAsync();
|
||||
|
||||
// Check status
|
||||
switch (result.Status)
|
||||
{
|
||||
case StoreRateAndReviewStatus.Succeeded:
|
||||
if (result.WasUpdated)
|
||||
_dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewUpdatedMessage, Domain.Enums.InfoBarMessageType.Success);
|
||||
else
|
||||
_dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewNewMessage, Domain.Enums.InfoBarMessageType.Success);
|
||||
|
||||
_configurationService.Set(RatedStorageKey, true);
|
||||
break;
|
||||
case StoreRateAndReviewStatus.CanceledByUser:
|
||||
break;
|
||||
|
||||
case StoreRateAndReviewStatus.NetworkError:
|
||||
_dialogService.InfoBarMessage(Translator.Info_ReviewNetworkErrorTitle, Translator.Info_ReviewNetworkErrorMessage, Domain.Enums.InfoBarMessageType.Warning);
|
||||
break;
|
||||
default:
|
||||
_dialogService.InfoBarMessage(Translator.Info_ReviewUnknownErrorTitle, string.Format(Translator.Info_ReviewUnknownErrorMessage, result.ExtendedError.Message), Domain.Enums.InfoBarMessageType.Warning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LaunchStorePageForReviewAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await CoreApplication.GetCurrentView()?.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
|
||||
{
|
||||
// TODO: Get it from package info.
|
||||
await Launcher.LaunchUriAsync(new Uri($"ms-windows-store://review/?ProductId=9NCRCVJC50WL"));
|
||||
});
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,437 +26,436 @@ using Wino.Core.UWP.Models.Personalization;
|
||||
using Wino.Core.UWP.Services;
|
||||
using Wino.Messaging.Client.Shell;
|
||||
|
||||
namespace Wino.Services
|
||||
namespace Wino.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Class providing functionality around switching and restoring theme settings
|
||||
/// </summary>
|
||||
public class ThemeService : IThemeService
|
||||
{
|
||||
/// <summary>
|
||||
/// Class providing functionality around switching and restoring theme settings
|
||||
/// </summary>
|
||||
public class ThemeService : IThemeService
|
||||
public const string CustomThemeFolderName = "CustomThemes";
|
||||
|
||||
private static string _micaThemeId = "a160b1b0-2ab8-4e97-a803-f4050f036e25";
|
||||
private static string _acrylicThemeId = "fc08e58c-36fd-46e2-a562-26cf277f1467";
|
||||
private static string _cloudsThemeId = "3b621cc2-e270-4a76-8477-737917cccda0";
|
||||
private static string _forestThemeId = "8bc89b37-a7c5-4049-86e2-de1ae8858dbd";
|
||||
private static string _nightyThemeId = "5b65e04e-fd7e-4c2d-8221-068d3e02d23a";
|
||||
private static string _snowflakeThemeId = "e143ddde-2e28-4846-9d98-dad63d6505f1";
|
||||
private static string _gardenThemeId = "698e4466-f88c-4799-9c61-f0ea1308ed49";
|
||||
|
||||
private Frame mainApplicationFrame = null;
|
||||
|
||||
public event EventHandler<ApplicationElementTheme> ElementThemeChanged;
|
||||
public event EventHandler<string> AccentColorChanged;
|
||||
|
||||
private const string AccentColorKey = nameof(AccentColorKey);
|
||||
private const string CurrentApplicationThemeKey = nameof(CurrentApplicationThemeKey);
|
||||
|
||||
// Custom theme
|
||||
public const string CustomThemeAccentColorKey = nameof(CustomThemeAccentColorKey);
|
||||
|
||||
// Keep reference so it does not get optimized/garbage collected
|
||||
private readonly UISettings uiSettings = new UISettings();
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
private readonly IUnderlyingThemeService _underlyingThemeService;
|
||||
private readonly IApplicationResourceManager<ResourceDictionary> _applicationResourceManager;
|
||||
|
||||
private List<AppThemeBase> preDefinedThemes { get; set; } = new List<AppThemeBase>()
|
||||
{
|
||||
public const string CustomThemeFolderName = "CustomThemes";
|
||||
new SystemAppTheme("Mica", Guid.Parse(_micaThemeId)),
|
||||
new SystemAppTheme("Acrylic", Guid.Parse(_acrylicThemeId)),
|
||||
new PreDefinedAppTheme("Nighty", Guid.Parse(_nightyThemeId), "#e1b12c", ApplicationElementTheme.Dark),
|
||||
new PreDefinedAppTheme("Forest", Guid.Parse(_forestThemeId), "#16a085", ApplicationElementTheme.Dark),
|
||||
new PreDefinedAppTheme("Clouds", Guid.Parse(_cloudsThemeId), "#0984e3", ApplicationElementTheme.Light),
|
||||
new PreDefinedAppTheme("Snowflake", Guid.Parse(_snowflakeThemeId), "#4a69bd", ApplicationElementTheme.Light),
|
||||
new PreDefinedAppTheme("Garden", Guid.Parse(_gardenThemeId), "#05c46b", ApplicationElementTheme.Light),
|
||||
};
|
||||
|
||||
private static string _micaThemeId = "a160b1b0-2ab8-4e97-a803-f4050f036e25";
|
||||
private static string _acrylicThemeId = "fc08e58c-36fd-46e2-a562-26cf277f1467";
|
||||
private static string _cloudsThemeId = "3b621cc2-e270-4a76-8477-737917cccda0";
|
||||
private static string _forestThemeId = "8bc89b37-a7c5-4049-86e2-de1ae8858dbd";
|
||||
private static string _nightyThemeId = "5b65e04e-fd7e-4c2d-8221-068d3e02d23a";
|
||||
private static string _snowflakeThemeId = "e143ddde-2e28-4846-9d98-dad63d6505f1";
|
||||
private static string _gardenThemeId = "698e4466-f88c-4799-9c61-f0ea1308ed49";
|
||||
public ThemeService(IConfigurationService configurationService,
|
||||
IUnderlyingThemeService underlyingThemeService,
|
||||
IApplicationResourceManager<ResourceDictionary> applicationResourceManager)
|
||||
{
|
||||
_configurationService = configurationService;
|
||||
_underlyingThemeService = underlyingThemeService;
|
||||
_applicationResourceManager = applicationResourceManager;
|
||||
}
|
||||
|
||||
private Frame mainApplicationFrame = null;
|
||||
|
||||
public event EventHandler<ApplicationElementTheme> ElementThemeChanged;
|
||||
public event EventHandler<string> AccentColorChanged;
|
||||
|
||||
private const string AccentColorKey = nameof(AccentColorKey);
|
||||
private const string CurrentApplicationThemeKey = nameof(CurrentApplicationThemeKey);
|
||||
|
||||
// Custom theme
|
||||
public const string CustomThemeAccentColorKey = nameof(CustomThemeAccentColorKey);
|
||||
|
||||
// Keep reference so it does not get optimized/garbage collected
|
||||
private readonly UISettings uiSettings = new UISettings();
|
||||
|
||||
private readonly IConfigurationService _configurationService;
|
||||
private readonly IUnderlyingThemeService _underlyingThemeService;
|
||||
private readonly IApplicationResourceManager<ResourceDictionary> _applicationResourceManager;
|
||||
|
||||
private List<AppThemeBase> preDefinedThemes { get; set; } = new List<AppThemeBase>()
|
||||
/// <summary>
|
||||
/// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element.
|
||||
/// </summary>
|
||||
public ApplicationElementTheme RootTheme
|
||||
{
|
||||
get
|
||||
{
|
||||
new SystemAppTheme("Mica", Guid.Parse(_micaThemeId)),
|
||||
new SystemAppTheme("Acrylic", Guid.Parse(_acrylicThemeId)),
|
||||
new PreDefinedAppTheme("Nighty", Guid.Parse(_nightyThemeId), "#e1b12c", ApplicationElementTheme.Dark),
|
||||
new PreDefinedAppTheme("Forest", Guid.Parse(_forestThemeId), "#16a085", ApplicationElementTheme.Dark),
|
||||
new PreDefinedAppTheme("Clouds", Guid.Parse(_cloudsThemeId), "#0984e3", ApplicationElementTheme.Light),
|
||||
new PreDefinedAppTheme("Snowflake", Guid.Parse(_snowflakeThemeId), "#4a69bd", ApplicationElementTheme.Light),
|
||||
new PreDefinedAppTheme("Garden", Guid.Parse(_gardenThemeId), "#05c46b", ApplicationElementTheme.Light),
|
||||
};
|
||||
if (mainApplicationFrame == null) return ApplicationElementTheme.Default;
|
||||
|
||||
public ThemeService(IConfigurationService configurationService,
|
||||
IUnderlyingThemeService underlyingThemeService,
|
||||
IApplicationResourceManager<ResourceDictionary> applicationResourceManager)
|
||||
{
|
||||
_configurationService = configurationService;
|
||||
_underlyingThemeService = underlyingThemeService;
|
||||
_applicationResourceManager = applicationResourceManager;
|
||||
return mainApplicationFrame.RequestedTheme.ToWinoElementTheme();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element.
|
||||
/// </summary>
|
||||
public ApplicationElementTheme RootTheme
|
||||
set
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mainApplicationFrame == null) return ApplicationElementTheme.Default;
|
||||
|
||||
return mainApplicationFrame.RequestedTheme.ToWinoElementTheme();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (mainApplicationFrame == null)
|
||||
return;
|
||||
|
||||
mainApplicationFrame.RequestedTheme = value.ToWindowsElementTheme();
|
||||
|
||||
_configurationService.Set(UnderlyingThemeService.SelectedAppThemeKey, value);
|
||||
|
||||
UpdateSystemCaptionButtonColors();
|
||||
|
||||
// PopupRoot usually needs to react to changes.
|
||||
NotifyThemeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Guid currentApplicationThemeId;
|
||||
|
||||
public Guid CurrentApplicationThemeId
|
||||
{
|
||||
get { return currentApplicationThemeId; }
|
||||
set
|
||||
{
|
||||
currentApplicationThemeId = value;
|
||||
|
||||
_configurationService.Set(CurrentApplicationThemeKey, value);
|
||||
|
||||
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () =>
|
||||
{
|
||||
await ApplyCustomThemeAsync(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string accentColor;
|
||||
|
||||
public string AccentColor
|
||||
{
|
||||
get { return accentColor; }
|
||||
set
|
||||
{
|
||||
accentColor = value;
|
||||
|
||||
UpdateAccentColor(value);
|
||||
|
||||
_configurationService.Set(AccentColorKey, value);
|
||||
AccentColorChanged?.Invoke(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCustomTheme
|
||||
{
|
||||
get
|
||||
{
|
||||
return currentApplicationThemeId != Guid.Parse(_micaThemeId) &&
|
||||
currentApplicationThemeId != Guid.Parse(_acrylicThemeId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Already initialized. There is no need.
|
||||
if (mainApplicationFrame != null)
|
||||
if (mainApplicationFrame == null)
|
||||
return;
|
||||
|
||||
// Save reference as this might be null when the user is in another app
|
||||
mainApplicationFrame.RequestedTheme = value.ToWindowsElementTheme();
|
||||
|
||||
mainApplicationFrame = Window.Current.Content as Frame;
|
||||
_configurationService.Set(UnderlyingThemeService.SelectedAppThemeKey, value);
|
||||
|
||||
if (mainApplicationFrame == null) return;
|
||||
UpdateSystemCaptionButtonColors();
|
||||
|
||||
RootTheme = _configurationService.Get(UnderlyingThemeService.SelectedAppThemeKey, ApplicationElementTheme.Default);
|
||||
AccentColor = _configurationService.Get(AccentColorKey, string.Empty);
|
||||
|
||||
// Set the current theme id. Default to Mica.
|
||||
currentApplicationThemeId = _configurationService.Get(CurrentApplicationThemeKey, Guid.Parse(_micaThemeId));
|
||||
|
||||
await ApplyCustomThemeAsync(true);
|
||||
|
||||
// Registering to color changes, thus we notice when user changes theme system wide
|
||||
uiSettings.ColorValuesChanged -= UISettingsColorChanged;
|
||||
uiSettings.ColorValuesChanged += UISettingsColorChanged;
|
||||
// PopupRoot usually needs to react to changes.
|
||||
NotifyThemeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void NotifyThemeUpdate()
|
||||
|
||||
private Guid currentApplicationThemeId;
|
||||
|
||||
public Guid CurrentApplicationThemeId
|
||||
{
|
||||
get { return currentApplicationThemeId; }
|
||||
set
|
||||
{
|
||||
if (mainApplicationFrame == null || mainApplicationFrame.Dispatcher == null) return;
|
||||
currentApplicationThemeId = value;
|
||||
|
||||
_configurationService.Set(CurrentApplicationThemeKey, value);
|
||||
|
||||
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () =>
|
||||
{
|
||||
await ApplyCustomThemeAsync(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private string accentColor;
|
||||
|
||||
public string AccentColor
|
||||
{
|
||||
get { return accentColor; }
|
||||
set
|
||||
{
|
||||
accentColor = value;
|
||||
|
||||
UpdateAccentColor(value);
|
||||
|
||||
_configurationService.Set(AccentColorKey, value);
|
||||
AccentColorChanged?.Invoke(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCustomTheme
|
||||
{
|
||||
get
|
||||
{
|
||||
return currentApplicationThemeId != Guid.Parse(_micaThemeId) &&
|
||||
currentApplicationThemeId != Guid.Parse(_acrylicThemeId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Already initialized. There is no need.
|
||||
if (mainApplicationFrame != null)
|
||||
return;
|
||||
|
||||
// Save reference as this might be null when the user is in another app
|
||||
|
||||
mainApplicationFrame = Window.Current.Content as Frame;
|
||||
|
||||
if (mainApplicationFrame == null) return;
|
||||
|
||||
RootTheme = _configurationService.Get(UnderlyingThemeService.SelectedAppThemeKey, ApplicationElementTheme.Default);
|
||||
AccentColor = _configurationService.Get(AccentColorKey, string.Empty);
|
||||
|
||||
// Set the current theme id. Default to Mica.
|
||||
currentApplicationThemeId = _configurationService.Get(CurrentApplicationThemeKey, Guid.Parse(_micaThemeId));
|
||||
|
||||
await ApplyCustomThemeAsync(true);
|
||||
|
||||
// Registering to color changes, thus we notice when user changes theme system wide
|
||||
uiSettings.ColorValuesChanged -= UISettingsColorChanged;
|
||||
uiSettings.ColorValuesChanged += UISettingsColorChanged;
|
||||
}
|
||||
|
||||
private void NotifyThemeUpdate()
|
||||
{
|
||||
if (mainApplicationFrame == null || mainApplicationFrame.Dispatcher == null) return;
|
||||
|
||||
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
|
||||
{
|
||||
ElementThemeChanged?.Invoke(this, RootTheme);
|
||||
WeakReferenceMessenger.Default.Send(new ApplicationThemeChanged(_underlyingThemeService.IsUnderlyingThemeDark()));
|
||||
});
|
||||
}
|
||||
|
||||
private void UISettingsColorChanged(UISettings sender, object args)
|
||||
{
|
||||
// Make sure we have a reference to our window so we dispatch a UI change
|
||||
if (mainApplicationFrame != null)
|
||||
{
|
||||
// Dispatch on UI thread so that we have a current appbar to access and change
|
||||
|
||||
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
|
||||
{
|
||||
ElementThemeChanged?.Invoke(this, RootTheme);
|
||||
WeakReferenceMessenger.Default.Send(new ApplicationThemeChanged(_underlyingThemeService.IsUnderlyingThemeDark()));
|
||||
UpdateSystemCaptionButtonColors();
|
||||
|
||||
var accentColor = sender.GetColorValue(UIColorType.Accent);
|
||||
//AccentColorChangedBySystem?.Invoke(this, accentColor.ToHex());
|
||||
});
|
||||
}
|
||||
|
||||
private void UISettingsColorChanged(UISettings sender, object args)
|
||||
NotifyThemeUpdate();
|
||||
}
|
||||
|
||||
public void UpdateSystemCaptionButtonColors()
|
||||
{
|
||||
if (mainApplicationFrame == null) return;
|
||||
|
||||
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
// Make sure we have a reference to our window so we dispatch a UI change
|
||||
if (mainApplicationFrame != null)
|
||||
ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
|
||||
if (titleBar == null) return;
|
||||
|
||||
if (_underlyingThemeService.IsUnderlyingThemeDark())
|
||||
{
|
||||
// Dispatch on UI thread so that we have a current appbar to access and change
|
||||
|
||||
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
|
||||
{
|
||||
UpdateSystemCaptionButtonColors();
|
||||
|
||||
var accentColor = sender.GetColorValue(UIColorType.Accent);
|
||||
//AccentColorChangedBySystem?.Invoke(this, accentColor.ToHex());
|
||||
});
|
||||
}
|
||||
|
||||
NotifyThemeUpdate();
|
||||
}
|
||||
|
||||
public void UpdateSystemCaptionButtonColors()
|
||||
{
|
||||
if (mainApplicationFrame == null) return;
|
||||
|
||||
_ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
|
||||
if (titleBar == null) return;
|
||||
|
||||
if (_underlyingThemeService.IsUnderlyingThemeDark())
|
||||
{
|
||||
titleBar.ButtonForegroundColor = Colors.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
titleBar.ButtonForegroundColor = Colors.Black;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateAccentColor(string hex)
|
||||
{
|
||||
// Change accent color if specified.
|
||||
if (!string.IsNullOrEmpty(hex))
|
||||
{
|
||||
var color = CommunityToolkit.WinUI.Helpers.ColorHelper.ToColor(hex);
|
||||
var brush = new SolidColorBrush(color);
|
||||
|
||||
if (_applicationResourceManager.ContainsResourceKey("SystemAccentColor"))
|
||||
_applicationResourceManager.ReplaceResource("SystemAccentColor", color);
|
||||
|
||||
if (_applicationResourceManager.ContainsResourceKey("NavigationViewSelectionIndicatorForeground"))
|
||||
_applicationResourceManager.ReplaceResource("NavigationViewSelectionIndicatorForeground", brush);
|
||||
|
||||
if (_applicationResourceManager.ContainsResourceKey("SystemControlBackgroundAccentBrush"))
|
||||
_applicationResourceManager.ReplaceResource("SystemControlBackgroundAccentBrush", brush);
|
||||
|
||||
if (_applicationResourceManager.ContainsResourceKey("SystemColorControlAccentBrush"))
|
||||
_applicationResourceManager.ReplaceResource("SystemColorControlAccentBrush", brush);
|
||||
|
||||
|
||||
RefreshThemeResource();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshThemeResource()
|
||||
{
|
||||
if (mainApplicationFrame == null) return;
|
||||
|
||||
if (mainApplicationFrame.RequestedTheme == ElementTheme.Dark)
|
||||
{
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Light;
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Dark;
|
||||
}
|
||||
else if (mainApplicationFrame.RequestedTheme == ElementTheme.Light)
|
||||
{
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Dark;
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Light;
|
||||
titleBar.ButtonForegroundColor = Colors.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
var isUnderlyingDark = _underlyingThemeService.IsUnderlyingThemeDark();
|
||||
|
||||
mainApplicationFrame.RequestedTheme = isUnderlyingDark ? ElementTheme.Light : ElementTheme.Dark;
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Default;
|
||||
titleBar.ButtonForegroundColor = Colors.Black;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ApplyCustomThemeAsync(bool isInitializing)
|
||||
public void UpdateAccentColor(string hex)
|
||||
{
|
||||
// Change accent color if specified.
|
||||
if (!string.IsNullOrEmpty(hex))
|
||||
{
|
||||
AppThemeBase applyingTheme = null;
|
||||
var color = CommunityToolkit.WinUI.Helpers.ColorHelper.ToColor(hex);
|
||||
var brush = new SolidColorBrush(color);
|
||||
|
||||
var controlThemeList = new List<AppThemeBase>(preDefinedThemes);
|
||||
if (_applicationResourceManager.ContainsResourceKey("SystemAccentColor"))
|
||||
_applicationResourceManager.ReplaceResource("SystemAccentColor", color);
|
||||
|
||||
// Don't search for custom themes if applying theme is already in pre-defined templates.
|
||||
// This is important for startup performance because we won't be loading the custom themes on launch.
|
||||
if (_applicationResourceManager.ContainsResourceKey("NavigationViewSelectionIndicatorForeground"))
|
||||
_applicationResourceManager.ReplaceResource("NavigationViewSelectionIndicatorForeground", brush);
|
||||
|
||||
bool isApplyingPreDefinedTheme = preDefinedThemes.Exists(a => a.Id == currentApplicationThemeId);
|
||||
if (_applicationResourceManager.ContainsResourceKey("SystemControlBackgroundAccentBrush"))
|
||||
_applicationResourceManager.ReplaceResource("SystemControlBackgroundAccentBrush", brush);
|
||||
|
||||
if (isApplyingPreDefinedTheme)
|
||||
{
|
||||
applyingTheme = preDefinedThemes.Find(a => a.Id == currentApplicationThemeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// User applied custom theme. Load custom themes and find it there.
|
||||
// Fallback to Mica if nothing found.
|
||||
if (_applicationResourceManager.ContainsResourceKey("SystemColorControlAccentBrush"))
|
||||
_applicationResourceManager.ReplaceResource("SystemColorControlAccentBrush", brush);
|
||||
|
||||
var customThemes = await GetCurrentCustomThemesAsync();
|
||||
|
||||
controlThemeList.AddRange(customThemes.Select(a => new CustomAppTheme(a)));
|
||||
|
||||
applyingTheme = controlThemeList.Find(a => a.Id == currentApplicationThemeId) ?? preDefinedThemes.First(a => a.Id == Guid.Parse(_micaThemeId));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var existingThemeDictionary = _applicationResourceManager.GetLastResource();
|
||||
|
||||
if (existingThemeDictionary != null && existingThemeDictionary.TryGetValue("ThemeName", out object themeNameString))
|
||||
{
|
||||
var themeName = themeNameString.ToString();
|
||||
|
||||
// Applying different theme.
|
||||
if (themeName != applyingTheme.ThemeName)
|
||||
{
|
||||
var resourceDictionaryContent = await applyingTheme.GetThemeResourceDictionaryContentAsync();
|
||||
|
||||
var resourceDictionary = XamlReader.Load(resourceDictionaryContent) as ResourceDictionary;
|
||||
|
||||
// Custom themes require special attention for background image because
|
||||
// they share the same base theme resource dictionary.
|
||||
|
||||
if (applyingTheme is CustomAppTheme)
|
||||
{
|
||||
resourceDictionary["ThemeBackgroundImage"] = $"ms-appdata:///local/{CustomThemeFolderName}/{applyingTheme.Id}.jpg";
|
||||
}
|
||||
|
||||
_applicationResourceManager.RemoveResource(existingThemeDictionary);
|
||||
_applicationResourceManager.AddResource(resourceDictionary);
|
||||
|
||||
bool isSystemTheme = applyingTheme is SystemAppTheme || applyingTheme is CustomAppTheme;
|
||||
|
||||
if (isSystemTheme)
|
||||
{
|
||||
// For system themes, set the RootElement theme from saved values.
|
||||
// Potential bug: When we set it to system default, theme is not applied when system and
|
||||
// app element theme is different :)
|
||||
|
||||
var savedElement = _configurationService.Get(UnderlyingThemeService.SelectedAppThemeKey, ApplicationElementTheme.Default);
|
||||
RootTheme = savedElement;
|
||||
|
||||
// Quickly switch theme to apply theme resource changes.
|
||||
RefreshThemeResource();
|
||||
}
|
||||
else
|
||||
RootTheme = applyingTheme.ForceElementTheme;
|
||||
|
||||
// Theme has accent color. Override.
|
||||
if (!isInitializing)
|
||||
{
|
||||
AccentColor = applyingTheme.AccentColor;
|
||||
}
|
||||
}
|
||||
else
|
||||
UpdateSystemCaptionButtonColors();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Apply theme failed -> {ex.Message}");
|
||||
}
|
||||
RefreshThemeResource();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<AppThemeBase>> GetAvailableThemesAsync()
|
||||
private void RefreshThemeResource()
|
||||
{
|
||||
if (mainApplicationFrame == null) return;
|
||||
|
||||
if (mainApplicationFrame.RequestedTheme == ElementTheme.Dark)
|
||||
{
|
||||
var availableThemes = new List<AppThemeBase>(preDefinedThemes);
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Light;
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Dark;
|
||||
}
|
||||
else if (mainApplicationFrame.RequestedTheme == ElementTheme.Light)
|
||||
{
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Dark;
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Light;
|
||||
}
|
||||
else
|
||||
{
|
||||
var isUnderlyingDark = _underlyingThemeService.IsUnderlyingThemeDark();
|
||||
|
||||
mainApplicationFrame.RequestedTheme = isUnderlyingDark ? ElementTheme.Light : ElementTheme.Dark;
|
||||
mainApplicationFrame.RequestedTheme = ElementTheme.Default;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ApplyCustomThemeAsync(bool isInitializing)
|
||||
{
|
||||
AppThemeBase applyingTheme = null;
|
||||
|
||||
var controlThemeList = new List<AppThemeBase>(preDefinedThemes);
|
||||
|
||||
// Don't search for custom themes if applying theme is already in pre-defined templates.
|
||||
// This is important for startup performance because we won't be loading the custom themes on launch.
|
||||
|
||||
bool isApplyingPreDefinedTheme = preDefinedThemes.Exists(a => a.Id == currentApplicationThemeId);
|
||||
|
||||
if (isApplyingPreDefinedTheme)
|
||||
{
|
||||
applyingTheme = preDefinedThemes.Find(a => a.Id == currentApplicationThemeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// User applied custom theme. Load custom themes and find it there.
|
||||
// Fallback to Mica if nothing found.
|
||||
|
||||
var customThemes = await GetCurrentCustomThemesAsync();
|
||||
|
||||
availableThemes.AddRange(customThemes.Select(a => new CustomAppTheme(a)));
|
||||
controlThemeList.AddRange(customThemes.Select(a => new CustomAppTheme(a)));
|
||||
|
||||
return availableThemes;
|
||||
applyingTheme = controlThemeList.Find(a => a.Id == currentApplicationThemeId) ?? preDefinedThemes.First(a => a.Id == Guid.Parse(_micaThemeId));
|
||||
}
|
||||
|
||||
public async Task<CustomThemeMetadata> CreateNewCustomThemeAsync(string themeName, string accentColor, byte[] wallpaperData)
|
||||
try
|
||||
{
|
||||
if (wallpaperData == null || wallpaperData.Length == 0)
|
||||
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeMissingWallpaper);
|
||||
var existingThemeDictionary = _applicationResourceManager.GetLastResource();
|
||||
|
||||
if (string.IsNullOrEmpty(themeName))
|
||||
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeMissingName);
|
||||
|
||||
var themes = await GetCurrentCustomThemesAsync();
|
||||
|
||||
if (themes.Exists(a => a.Name == themeName))
|
||||
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeExists);
|
||||
|
||||
var newTheme = new CustomThemeMetadata()
|
||||
if (existingThemeDictionary != null && existingThemeDictionary.TryGetValue("ThemeName", out object themeNameString))
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = themeName,
|
||||
AccentColorHex = accentColor
|
||||
};
|
||||
var themeName = themeNameString.ToString();
|
||||
|
||||
// Save wallpaper.
|
||||
// Filename would be the same as metadata id, in jpg format.
|
||||
// Applying different theme.
|
||||
if (themeName != applyingTheme.ThemeName)
|
||||
{
|
||||
var resourceDictionaryContent = await applyingTheme.GetThemeResourceDictionaryContentAsync();
|
||||
|
||||
var themeFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(CustomThemeFolderName, CreationCollisionOption.OpenIfExists);
|
||||
var resourceDictionary = XamlReader.Load(resourceDictionaryContent) as ResourceDictionary;
|
||||
|
||||
var wallpaperFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.jpg", CreationCollisionOption.ReplaceExisting);
|
||||
await FileIO.WriteBytesAsync(wallpaperFile, wallpaperData);
|
||||
// Custom themes require special attention for background image because
|
||||
// they share the same base theme resource dictionary.
|
||||
|
||||
// Generate thumbnail for settings page.
|
||||
if (applyingTheme is CustomAppTheme)
|
||||
{
|
||||
resourceDictionary["ThemeBackgroundImage"] = $"ms-appdata:///local/{CustomThemeFolderName}/{applyingTheme.Id}.jpg";
|
||||
}
|
||||
|
||||
var thumbnail = await wallpaperFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView);
|
||||
var thumbnailFile = await themeFolder.CreateFileAsync($"{newTheme.Id}_preview.jpg", CreationCollisionOption.ReplaceExisting);
|
||||
_applicationResourceManager.RemoveResource(existingThemeDictionary);
|
||||
_applicationResourceManager.AddResource(resourceDictionary);
|
||||
|
||||
using (var readerStream = thumbnail.AsStreamForRead())
|
||||
{
|
||||
byte[] bytes = new byte[readerStream.Length];
|
||||
bool isSystemTheme = applyingTheme is SystemAppTheme || applyingTheme is CustomAppTheme;
|
||||
|
||||
await readerStream.ReadExactlyAsync(bytes);
|
||||
if (isSystemTheme)
|
||||
{
|
||||
// For system themes, set the RootElement theme from saved values.
|
||||
// Potential bug: When we set it to system default, theme is not applied when system and
|
||||
// app element theme is different :)
|
||||
|
||||
var buffer = bytes.AsBuffer();
|
||||
var savedElement = _configurationService.Get(UnderlyingThemeService.SelectedAppThemeKey, ApplicationElementTheme.Default);
|
||||
RootTheme = savedElement;
|
||||
|
||||
await FileIO.WriteBufferAsync(thumbnailFile, buffer);
|
||||
// Quickly switch theme to apply theme resource changes.
|
||||
RefreshThemeResource();
|
||||
}
|
||||
else
|
||||
RootTheme = applyingTheme.ForceElementTheme;
|
||||
|
||||
// Theme has accent color. Override.
|
||||
if (!isInitializing)
|
||||
{
|
||||
AccentColor = applyingTheme.AccentColor;
|
||||
}
|
||||
}
|
||||
else
|
||||
UpdateSystemCaptionButtonColors();
|
||||
}
|
||||
|
||||
// Save metadata.
|
||||
var metadataFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.json", CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
var serialized = JsonSerializer.Serialize(newTheme, DomainModelsJsonContext.Default.CustomThemeMetadata);
|
||||
await FileIO.WriteTextAsync(metadataFile, serialized);
|
||||
|
||||
return newTheme;
|
||||
}
|
||||
|
||||
public async Task<List<CustomThemeMetadata>> GetCurrentCustomThemesAsync()
|
||||
catch (Exception ex)
|
||||
{
|
||||
var results = new List<CustomThemeMetadata>();
|
||||
|
||||
var themeFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(CustomThemeFolderName, CreationCollisionOption.OpenIfExists);
|
||||
|
||||
var allFiles = await themeFolder.GetFilesAsync();
|
||||
|
||||
var themeMetadatas = allFiles.Where(a => a.FileType == ".json");
|
||||
|
||||
foreach (var theme in themeMetadatas)
|
||||
{
|
||||
var metadata = await GetCustomMetadataAsync(theme).ConfigureAwait(false);
|
||||
|
||||
if (metadata == null) continue;
|
||||
|
||||
results.Add(metadata);
|
||||
}
|
||||
|
||||
return results;
|
||||
Debug.WriteLine($"Apply theme failed -> {ex.Message}");
|
||||
}
|
||||
|
||||
private async Task<CustomThemeMetadata> GetCustomMetadataAsync(IStorageFile file)
|
||||
{
|
||||
var fileContent = await FileIO.ReadTextAsync(file);
|
||||
|
||||
return JsonSerializer.Deserialize(fileContent, DomainModelsJsonContext.Default.CustomThemeMetadata);
|
||||
}
|
||||
|
||||
public string GetSystemAccentColorHex()
|
||||
=> uiSettings.GetColorValue(UIColorType.Accent).ToHex();
|
||||
}
|
||||
|
||||
public async Task<List<AppThemeBase>> GetAvailableThemesAsync()
|
||||
{
|
||||
var availableThemes = new List<AppThemeBase>(preDefinedThemes);
|
||||
|
||||
var customThemes = await GetCurrentCustomThemesAsync();
|
||||
|
||||
availableThemes.AddRange(customThemes.Select(a => new CustomAppTheme(a)));
|
||||
|
||||
return availableThemes;
|
||||
}
|
||||
|
||||
public async Task<CustomThemeMetadata> CreateNewCustomThemeAsync(string themeName, string accentColor, byte[] wallpaperData)
|
||||
{
|
||||
if (wallpaperData == null || wallpaperData.Length == 0)
|
||||
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeMissingWallpaper);
|
||||
|
||||
if (string.IsNullOrEmpty(themeName))
|
||||
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeMissingName);
|
||||
|
||||
var themes = await GetCurrentCustomThemesAsync();
|
||||
|
||||
if (themes.Exists(a => a.Name == themeName))
|
||||
throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeExists);
|
||||
|
||||
var newTheme = new CustomThemeMetadata()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = themeName,
|
||||
AccentColorHex = accentColor
|
||||
};
|
||||
|
||||
// Save wallpaper.
|
||||
// Filename would be the same as metadata id, in jpg format.
|
||||
|
||||
var themeFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(CustomThemeFolderName, CreationCollisionOption.OpenIfExists);
|
||||
|
||||
var wallpaperFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.jpg", CreationCollisionOption.ReplaceExisting);
|
||||
await FileIO.WriteBytesAsync(wallpaperFile, wallpaperData);
|
||||
|
||||
// Generate thumbnail for settings page.
|
||||
|
||||
var thumbnail = await wallpaperFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView);
|
||||
var thumbnailFile = await themeFolder.CreateFileAsync($"{newTheme.Id}_preview.jpg", CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
using (var readerStream = thumbnail.AsStreamForRead())
|
||||
{
|
||||
byte[] bytes = new byte[readerStream.Length];
|
||||
|
||||
await readerStream.ReadExactlyAsync(bytes);
|
||||
|
||||
var buffer = bytes.AsBuffer();
|
||||
|
||||
await FileIO.WriteBufferAsync(thumbnailFile, buffer);
|
||||
}
|
||||
|
||||
// Save metadata.
|
||||
var metadataFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.json", CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
var serialized = JsonSerializer.Serialize(newTheme, DomainModelsJsonContext.Default.CustomThemeMetadata);
|
||||
await FileIO.WriteTextAsync(metadataFile, serialized);
|
||||
|
||||
return newTheme;
|
||||
}
|
||||
|
||||
public async Task<List<CustomThemeMetadata>> GetCurrentCustomThemesAsync()
|
||||
{
|
||||
var results = new List<CustomThemeMetadata>();
|
||||
|
||||
var themeFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(CustomThemeFolderName, CreationCollisionOption.OpenIfExists);
|
||||
|
||||
var allFiles = await themeFolder.GetFilesAsync();
|
||||
|
||||
var themeMetadatas = allFiles.Where(a => a.FileType == ".json");
|
||||
|
||||
foreach (var theme in themeMetadatas)
|
||||
{
|
||||
var metadata = await GetCustomMetadataAsync(theme).ConfigureAwait(false);
|
||||
|
||||
if (metadata == null) continue;
|
||||
|
||||
results.Add(metadata);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private async Task<CustomThemeMetadata> GetCustomMetadataAsync(IStorageFile file)
|
||||
{
|
||||
var fileContent = await FileIO.ReadTextAsync(file);
|
||||
|
||||
return JsonSerializer.Deserialize(fileContent, DomainModelsJsonContext.Default.CustomThemeMetadata);
|
||||
}
|
||||
|
||||
public string GetSystemAccentColorHex()
|
||||
=> uiSettings.GetColorValue(UIColorType.Accent).ToHex();
|
||||
}
|
||||
|
||||
@@ -2,63 +2,62 @@
|
||||
using System.Linq;
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public static class ThumbnailService
|
||||
{
|
||||
public static class ThumbnailService
|
||||
private static string[] knownCompanies = new string[]
|
||||
{
|
||||
private static string[] knownCompanies = new string[]
|
||||
"microsoft.com", "apple.com", "google.com", "steampowered.com", "airbnb.com", "youtube.com", "uber.com"
|
||||
};
|
||||
|
||||
public static bool IsKnown(string mailHost) => !string.IsNullOrEmpty(mailHost) && knownCompanies.Contains(mailHost);
|
||||
|
||||
public static string GetHost(string address)
|
||||
{
|
||||
if (string.IsNullOrEmpty(address))
|
||||
return string.Empty;
|
||||
|
||||
if (address.Contains('@'))
|
||||
{
|
||||
"microsoft.com", "apple.com", "google.com", "steampowered.com", "airbnb.com", "youtube.com", "uber.com"
|
||||
};
|
||||
var splitted = address.Split('@');
|
||||
|
||||
public static bool IsKnown(string mailHost) => !string.IsNullOrEmpty(mailHost) && knownCompanies.Contains(mailHost);
|
||||
|
||||
public static string GetHost(string address)
|
||||
{
|
||||
if (string.IsNullOrEmpty(address))
|
||||
return string.Empty;
|
||||
|
||||
if (address.Contains('@'))
|
||||
if (splitted.Length >= 2 && !string.IsNullOrEmpty(splitted[1]))
|
||||
{
|
||||
var splitted = address.Split('@');
|
||||
|
||||
if (splitted.Length >= 2 && !string.IsNullOrEmpty(splitted[1]))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
return new MailAddress(address).Host;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO: Exceptions are ignored for now.
|
||||
}
|
||||
return new MailAddress(address).Host;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO: Exceptions are ignored for now.
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static Tuple<bool, string> CheckIsKnown(string host)
|
||||
{
|
||||
// Check known hosts.
|
||||
// Apply company logo if available.
|
||||
|
||||
try
|
||||
{
|
||||
var last = host.Split('.');
|
||||
|
||||
if (last.Length > 2)
|
||||
host = $"{last[last.Length - 2]}.{last[last.Length - 1]}";
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new Tuple<bool, string>(false, host);
|
||||
}
|
||||
|
||||
return new Tuple<bool, string>(IsKnown(host), host);
|
||||
}
|
||||
|
||||
public static string GetKnownHostImage(string host)
|
||||
=> $"ms-appx:///Assets/Thumbnails/{host}.png";
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static Tuple<bool, string> CheckIsKnown(string host)
|
||||
{
|
||||
// Check known hosts.
|
||||
// Apply company logo if available.
|
||||
|
||||
try
|
||||
{
|
||||
var last = host.Split('.');
|
||||
|
||||
if (last.Length > 2)
|
||||
host = $"{last[last.Length - 2]}.{last[last.Length - 1]}";
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new Tuple<bool, string>(false, host);
|
||||
}
|
||||
|
||||
return new Tuple<bool, string>(IsKnown(host), host);
|
||||
}
|
||||
|
||||
public static string GetKnownHostImage(string host)
|
||||
=> $"ms-appx:///Assets/Thumbnails/{host}.png";
|
||||
}
|
||||
|
||||
@@ -2,31 +2,30 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class UnderlyingThemeService : IUnderlyingThemeService
|
||||
{
|
||||
public class UnderlyingThemeService : IUnderlyingThemeService
|
||||
public const string SelectedAppThemeKey = nameof(SelectedAppThemeKey);
|
||||
|
||||
private readonly UISettings uiSettings = new UISettings();
|
||||
private readonly IConfigurationService _configurationService;
|
||||
|
||||
public UnderlyingThemeService(IConfigurationService configurationService)
|
||||
{
|
||||
public const string SelectedAppThemeKey = nameof(SelectedAppThemeKey);
|
||||
_configurationService = configurationService;
|
||||
}
|
||||
|
||||
private readonly UISettings uiSettings = new UISettings();
|
||||
private readonly IConfigurationService _configurationService;
|
||||
// This should not rely on application window to be present.
|
||||
// Check theme from the settings, rely on UISettings background color if Default.
|
||||
|
||||
public UnderlyingThemeService(IConfigurationService configurationService)
|
||||
{
|
||||
_configurationService = configurationService;
|
||||
}
|
||||
public bool IsUnderlyingThemeDark()
|
||||
{
|
||||
var currentTheme = _configurationService.Get(SelectedAppThemeKey, ApplicationElementTheme.Default);
|
||||
|
||||
// This should not rely on application window to be present.
|
||||
// Check theme from the settings, rely on UISettings background color if Default.
|
||||
|
||||
public bool IsUnderlyingThemeDark()
|
||||
{
|
||||
var currentTheme = _configurationService.Get(SelectedAppThemeKey, ApplicationElementTheme.Default);
|
||||
|
||||
if (currentTheme == ApplicationElementTheme.Default)
|
||||
return uiSettings.GetColorValue(UIColorType.Background).ToString() == "#FF000000";
|
||||
else
|
||||
return currentTheme == ApplicationElementTheme.Dark;
|
||||
}
|
||||
if (currentTheme == ApplicationElementTheme.Default)
|
||||
return uiSettings.GetColorValue(UIColorType.Background).ToString() == "#FF000000";
|
||||
else
|
||||
return currentTheme == ApplicationElementTheme.Dark;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,344 +21,343 @@ using Wino.Messaging.Enums;
|
||||
using Wino.Messaging.Server;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Core.UWP.Services
|
||||
namespace Wino.Core.UWP.Services;
|
||||
|
||||
public class WinoServerConnectionManager :
|
||||
IWinoServerConnectionManager<AppServiceConnection>,
|
||||
IRecipient<WinoServerConnectionEstablished>
|
||||
{
|
||||
public class WinoServerConnectionManager :
|
||||
IWinoServerConnectionManager<AppServiceConnection>,
|
||||
IRecipient<WinoServerConnectionEstablished>
|
||||
private const int ServerConnectionTimeoutMs = 10000;
|
||||
|
||||
public event EventHandler<WinoServerConnectionStatus> StatusChanged;
|
||||
|
||||
public TaskCompletionSource<bool> ConnectingHandle { get; private set; }
|
||||
|
||||
private ILogger Logger => Logger.ForContext<WinoServerConnectionManager>();
|
||||
|
||||
private WinoServerConnectionStatus status;
|
||||
|
||||
public WinoServerConnectionStatus Status
|
||||
{
|
||||
private const int ServerConnectionTimeoutMs = 10000;
|
||||
|
||||
public event EventHandler<WinoServerConnectionStatus> StatusChanged;
|
||||
|
||||
public TaskCompletionSource<bool> ConnectingHandle { get; private set; }
|
||||
|
||||
private ILogger Logger => Logger.ForContext<WinoServerConnectionManager>();
|
||||
|
||||
private WinoServerConnectionStatus status;
|
||||
|
||||
public WinoServerConnectionStatus Status
|
||||
get { return status; }
|
||||
private set
|
||||
{
|
||||
get { return status; }
|
||||
private set
|
||||
{
|
||||
Log.Information("Server connection status changed to {Status}.", value);
|
||||
status = value;
|
||||
StatusChanged?.Invoke(this, value);
|
||||
}
|
||||
Log.Information("Server connection status changed to {Status}.", value);
|
||||
status = value;
|
||||
StatusChanged?.Invoke(this, value);
|
||||
}
|
||||
|
||||
private AppServiceConnection _connection;
|
||||
public AppServiceConnection Connection
|
||||
{
|
||||
get { return _connection; }
|
||||
set
|
||||
{
|
||||
if (_connection != null)
|
||||
{
|
||||
_connection.RequestReceived -= ServerMessageReceived;
|
||||
_connection.ServiceClosed -= ServerDisconnected;
|
||||
}
|
||||
|
||||
_connection = value;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
Status = WinoServerConnectionStatus.Disconnected;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.RequestReceived += ServerMessageReceived;
|
||||
value.ServiceClosed += ServerDisconnected;
|
||||
|
||||
Status = WinoServerConnectionStatus.Connected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
||||
{
|
||||
TypeInfoResolver = new ServerRequestTypeInfoResolver()
|
||||
};
|
||||
|
||||
public WinoServerConnectionManager()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Register(this);
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectAsync()
|
||||
{
|
||||
if (Status == WinoServerConnectionStatus.Connected)
|
||||
{
|
||||
Log.Information("Server is already connected.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Status == WinoServerConnectionStatus.Connecting)
|
||||
{
|
||||
// A connection is already being established at the moment.
|
||||
// No need to run another connection establishment process.
|
||||
// Await the connecting handler if possible.
|
||||
|
||||
if (ConnectingHandle != null)
|
||||
{
|
||||
return await ConnectingHandle.Task;
|
||||
}
|
||||
}
|
||||
|
||||
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
|
||||
{
|
||||
try
|
||||
{
|
||||
ConnectingHandle = new TaskCompletionSource<bool>();
|
||||
|
||||
Status = WinoServerConnectionStatus.Connecting;
|
||||
|
||||
var connectionCancellationToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(ServerConnectionTimeoutMs));
|
||||
|
||||
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("WinoServer");
|
||||
|
||||
// Connection establishment handler is in App.xaml.cs OnBackgroundActivated.
|
||||
// Once the connection is established, the handler will set the Connection property
|
||||
// and WinoServerConnectionEstablished will be fired by the messenger.
|
||||
|
||||
await ConnectingHandle.Task.WaitAsync(connectionCancellationToken.Token);
|
||||
|
||||
Log.Information("Server connection established successfully.");
|
||||
}
|
||||
catch (OperationCanceledException canceledException)
|
||||
{
|
||||
Log.Error(canceledException, $"Server process did not start in {ServerConnectionTimeoutMs} ms. Operation is canceled.");
|
||||
|
||||
ConnectingHandle?.TrySetException(canceledException);
|
||||
|
||||
Status = WinoServerConnectionStatus.Failed;
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to connect to the server.");
|
||||
|
||||
ConnectingHandle?.TrySetException(ex);
|
||||
|
||||
Status = WinoServerConnectionStatus.Failed;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("FullTrustAppContract is not present in the system. Server connection is not possible.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
var isConnectionSuccessfull = await ConnectAsync();
|
||||
|
||||
if (isConnectionSuccessfull)
|
||||
{
|
||||
Log.Information("ServerConnectionManager initialized successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("ServerConnectionManager initialization failed.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ServerMessageReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
||||
{
|
||||
if (args.Request.Message.TryGetValue(MessageConstants.MessageTypeKey, out object messageTypeObject) && messageTypeObject is int messageTypeInt)
|
||||
{
|
||||
var messageType = (MessageType)messageTypeInt;
|
||||
|
||||
if (args.Request.Message.TryGetValue(MessageConstants.MessageDataKey, out object messageDataObject) && messageDataObject is string messageJson)
|
||||
{
|
||||
switch (messageType)
|
||||
{
|
||||
case MessageType.UIMessage:
|
||||
if (!args.Request.Message.TryGetValue(MessageConstants.MessageDataTypeKey, out object dataTypeObject) || dataTypeObject is not string dataTypeName)
|
||||
throw new ArgumentException("Message data type is missing.");
|
||||
|
||||
HandleUIMessage(messageJson, dataTypeName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks IServerMessage objects and delegate it to Messenger for UI to process.
|
||||
/// </summary>
|
||||
/// <param name="messageJson">Message data in json format.</param>
|
||||
private void HandleUIMessage(string messageJson, string typeName)
|
||||
{
|
||||
switch (typeName)
|
||||
{
|
||||
case nameof(MailAddedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MailAddedMessage));
|
||||
break;
|
||||
case nameof(MailDownloadedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MailDownloadedMessage));
|
||||
break;
|
||||
case nameof(MailRemovedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MailRemovedMessage));
|
||||
break;
|
||||
case nameof(MailUpdatedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MailUpdatedMessage));
|
||||
break;
|
||||
case nameof(AccountCreatedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountCreatedMessage));
|
||||
break;
|
||||
case nameof(AccountRemovedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountRemovedMessage));
|
||||
break;
|
||||
case nameof(AccountUpdatedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountUpdatedMessage));
|
||||
break;
|
||||
case nameof(DraftCreated):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.DraftCreated));
|
||||
break;
|
||||
case nameof(DraftFailed):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.DraftFailed));
|
||||
break;
|
||||
case nameof(DraftMapped):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.DraftMapped));
|
||||
break;
|
||||
case nameof(FolderRenamed):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.FolderRenamed));
|
||||
break;
|
||||
case nameof(FolderSynchronizationEnabled):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.FolderSynchronizationEnabled));
|
||||
break;
|
||||
case nameof(MergedInboxRenamed):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MergedInboxRenamed));
|
||||
break;
|
||||
case nameof(AccountSynchronizationCompleted):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountSynchronizationCompleted));
|
||||
break;
|
||||
case nameof(RefreshUnreadCountsMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.RefreshUnreadCountsMessage));
|
||||
break;
|
||||
case nameof(AccountSynchronizerStateChanged):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountSynchronizerStateChanged));
|
||||
break;
|
||||
case nameof(AccountSynchronizationProgressUpdatedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountSynchronizationProgressUpdatedMessage));
|
||||
break;
|
||||
case nameof(AccountFolderConfigurationUpdated):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountFolderConfigurationUpdated));
|
||||
break;
|
||||
case nameof(CopyAuthURLRequested):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.CopyAuthURLRequested));
|
||||
break;
|
||||
case nameof(NewMailSynchronizationRequested):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize<NewMailSynchronizationRequested>(messageJson));
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid data type name passed to client.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ServerDisconnected(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
||||
{
|
||||
Log.Information("Server disconnected.");
|
||||
}
|
||||
|
||||
public async Task QueueRequestAsync(IRequestBase request, Guid accountId)
|
||||
{
|
||||
var queuePackage = new ServerRequestPackage(accountId, request);
|
||||
|
||||
var queueResponse = await GetResponseInternalAsync<bool, ServerRequestPackage>(queuePackage, new Dictionary<string, object>()
|
||||
{
|
||||
{ MessageConstants.MessageDataRequestAccountIdKey, accountId }
|
||||
});
|
||||
|
||||
queueResponse.ThrowIfFailed();
|
||||
}
|
||||
|
||||
public Task<WinoServerResponse<TResponse>> GetResponseAsync<TResponse, TRequestType>(TRequestType message, CancellationToken cancellationToken = default) where TRequestType : IClientMessage
|
||||
=> GetResponseInternalAsync<TResponse, TRequestType>(message, cancellationToken: cancellationToken);
|
||||
|
||||
private async Task<WinoServerResponse<TResponse>> GetResponseInternalAsync<TResponse, TRequestType>(TRequestType message,
|
||||
Dictionary<string, object> parameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (Status != WinoServerConnectionStatus.Connected)
|
||||
await ConnectAsync();
|
||||
|
||||
if (Connection == null) return WinoServerResponse<TResponse>.CreateErrorResponse("Server connection is not established.");
|
||||
|
||||
string serializedMessage = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
serializedMessage = JsonSerializer.Serialize(message, _jsonSerializerOptions);
|
||||
}
|
||||
catch (Exception serializationException)
|
||||
{
|
||||
Logger.Error(serializationException, $"Failed to serialize client message for sending.");
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Failed to serialize message.\n{serializationException.Message}");
|
||||
}
|
||||
|
||||
AppServiceResponse response = null;
|
||||
|
||||
try
|
||||
{
|
||||
var valueSet = new ValueSet
|
||||
{
|
||||
{ MessageConstants.MessageTypeKey, (int)MessageType.ServerMessage },
|
||||
{ MessageConstants.MessageDataKey, serializedMessage },
|
||||
{ MessageConstants.MessageDataTypeKey, message.GetType().Name }
|
||||
};
|
||||
|
||||
// Add additional parameters into ValueSet
|
||||
if (parameters != null)
|
||||
{
|
||||
foreach (var item in parameters)
|
||||
{
|
||||
valueSet.Add(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
response = await Connection.SendMessageAsync(valueSet).AsTask(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Request is canceled by client.");
|
||||
}
|
||||
catch (Exception serverSendException)
|
||||
{
|
||||
Logger.Error(serverSendException, $"Failed to send message to server.");
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Failed to send message to server.\n{serverSendException.Message}");
|
||||
}
|
||||
|
||||
// It should be always Success.
|
||||
if (response.Status != AppServiceResponseStatus.Success)
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Wino Server responded with '{response.Status}' status to message delivery.");
|
||||
|
||||
// All responses must contain a message data.
|
||||
if (!(response.Message.TryGetValue(MessageConstants.MessageDataKey, out object messageDataObject) && messageDataObject is string messageJson))
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse("Server response did not contain message data.");
|
||||
|
||||
// Try deserialize the message data.
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<WinoServerResponse<TResponse>>(messageJson);
|
||||
}
|
||||
catch (Exception jsonDeserializationError)
|
||||
{
|
||||
Logger.Error(jsonDeserializationError, $"Failed to deserialize server response message data.");
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Failed to deserialize Wino server response message data.\n{jsonDeserializationError.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(WinoServerConnectionEstablished message)
|
||||
=> ConnectingHandle?.TrySetResult(true);
|
||||
}
|
||||
|
||||
private AppServiceConnection _connection;
|
||||
public AppServiceConnection Connection
|
||||
{
|
||||
get { return _connection; }
|
||||
set
|
||||
{
|
||||
if (_connection != null)
|
||||
{
|
||||
_connection.RequestReceived -= ServerMessageReceived;
|
||||
_connection.ServiceClosed -= ServerDisconnected;
|
||||
}
|
||||
|
||||
_connection = value;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
Status = WinoServerConnectionStatus.Disconnected;
|
||||
}
|
||||
else
|
||||
{
|
||||
value.RequestReceived += ServerMessageReceived;
|
||||
value.ServiceClosed += ServerDisconnected;
|
||||
|
||||
Status = WinoServerConnectionStatus.Connected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
||||
{
|
||||
TypeInfoResolver = new ServerRequestTypeInfoResolver()
|
||||
};
|
||||
|
||||
public WinoServerConnectionManager()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Register(this);
|
||||
}
|
||||
|
||||
public async Task<bool> ConnectAsync()
|
||||
{
|
||||
if (Status == WinoServerConnectionStatus.Connected)
|
||||
{
|
||||
Log.Information("Server is already connected.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Status == WinoServerConnectionStatus.Connecting)
|
||||
{
|
||||
// A connection is already being established at the moment.
|
||||
// No need to run another connection establishment process.
|
||||
// Await the connecting handler if possible.
|
||||
|
||||
if (ConnectingHandle != null)
|
||||
{
|
||||
return await ConnectingHandle.Task;
|
||||
}
|
||||
}
|
||||
|
||||
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
|
||||
{
|
||||
try
|
||||
{
|
||||
ConnectingHandle = new TaskCompletionSource<bool>();
|
||||
|
||||
Status = WinoServerConnectionStatus.Connecting;
|
||||
|
||||
var connectionCancellationToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(ServerConnectionTimeoutMs));
|
||||
|
||||
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("WinoServer");
|
||||
|
||||
// Connection establishment handler is in App.xaml.cs OnBackgroundActivated.
|
||||
// Once the connection is established, the handler will set the Connection property
|
||||
// and WinoServerConnectionEstablished will be fired by the messenger.
|
||||
|
||||
await ConnectingHandle.Task.WaitAsync(connectionCancellationToken.Token);
|
||||
|
||||
Log.Information("Server connection established successfully.");
|
||||
}
|
||||
catch (OperationCanceledException canceledException)
|
||||
{
|
||||
Log.Error(canceledException, $"Server process did not start in {ServerConnectionTimeoutMs} ms. Operation is canceled.");
|
||||
|
||||
ConnectingHandle?.TrySetException(canceledException);
|
||||
|
||||
Status = WinoServerConnectionStatus.Failed;
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to connect to the server.");
|
||||
|
||||
ConnectingHandle?.TrySetException(ex);
|
||||
|
||||
Status = WinoServerConnectionStatus.Failed;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("FullTrustAppContract is not present in the system. Server connection is not possible.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
var isConnectionSuccessfull = await ConnectAsync();
|
||||
|
||||
if (isConnectionSuccessfull)
|
||||
{
|
||||
Log.Information("ServerConnectionManager initialized successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("ServerConnectionManager initialization failed.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ServerMessageReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
||||
{
|
||||
if (args.Request.Message.TryGetValue(MessageConstants.MessageTypeKey, out object messageTypeObject) && messageTypeObject is int messageTypeInt)
|
||||
{
|
||||
var messageType = (MessageType)messageTypeInt;
|
||||
|
||||
if (args.Request.Message.TryGetValue(MessageConstants.MessageDataKey, out object messageDataObject) && messageDataObject is string messageJson)
|
||||
{
|
||||
switch (messageType)
|
||||
{
|
||||
case MessageType.UIMessage:
|
||||
if (!args.Request.Message.TryGetValue(MessageConstants.MessageDataTypeKey, out object dataTypeObject) || dataTypeObject is not string dataTypeName)
|
||||
throw new ArgumentException("Message data type is missing.");
|
||||
|
||||
HandleUIMessage(messageJson, dataTypeName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks IServerMessage objects and delegate it to Messenger for UI to process.
|
||||
/// </summary>
|
||||
/// <param name="messageJson">Message data in json format.</param>
|
||||
private void HandleUIMessage(string messageJson, string typeName)
|
||||
{
|
||||
switch (typeName)
|
||||
{
|
||||
case nameof(MailAddedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MailAddedMessage));
|
||||
break;
|
||||
case nameof(MailDownloadedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MailDownloadedMessage));
|
||||
break;
|
||||
case nameof(MailRemovedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MailRemovedMessage));
|
||||
break;
|
||||
case nameof(MailUpdatedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MailUpdatedMessage));
|
||||
break;
|
||||
case nameof(AccountCreatedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountCreatedMessage));
|
||||
break;
|
||||
case nameof(AccountRemovedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountRemovedMessage));
|
||||
break;
|
||||
case nameof(AccountUpdatedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountUpdatedMessage));
|
||||
break;
|
||||
case nameof(DraftCreated):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.DraftCreated));
|
||||
break;
|
||||
case nameof(DraftFailed):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.DraftFailed));
|
||||
break;
|
||||
case nameof(DraftMapped):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.DraftMapped));
|
||||
break;
|
||||
case nameof(FolderRenamed):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.FolderRenamed));
|
||||
break;
|
||||
case nameof(FolderSynchronizationEnabled):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.FolderSynchronizationEnabled));
|
||||
break;
|
||||
case nameof(MergedInboxRenamed):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.MergedInboxRenamed));
|
||||
break;
|
||||
case nameof(AccountSynchronizationCompleted):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountSynchronizationCompleted));
|
||||
break;
|
||||
case nameof(RefreshUnreadCountsMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.RefreshUnreadCountsMessage));
|
||||
break;
|
||||
case nameof(AccountSynchronizerStateChanged):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountSynchronizerStateChanged));
|
||||
break;
|
||||
case nameof(AccountSynchronizationProgressUpdatedMessage):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountSynchronizationProgressUpdatedMessage));
|
||||
break;
|
||||
case nameof(AccountFolderConfigurationUpdated):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.AccountFolderConfigurationUpdated));
|
||||
break;
|
||||
case nameof(CopyAuthURLRequested):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize(messageJson, CommunicationMessagesContext.Default.CopyAuthURLRequested));
|
||||
break;
|
||||
case nameof(NewMailSynchronizationRequested):
|
||||
WeakReferenceMessenger.Default.Send(JsonSerializer.Deserialize<NewMailSynchronizationRequested>(messageJson));
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid data type name passed to client.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ServerDisconnected(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
||||
{
|
||||
Log.Information("Server disconnected.");
|
||||
}
|
||||
|
||||
public async Task QueueRequestAsync(IRequestBase request, Guid accountId)
|
||||
{
|
||||
var queuePackage = new ServerRequestPackage(accountId, request);
|
||||
|
||||
var queueResponse = await GetResponseInternalAsync<bool, ServerRequestPackage>(queuePackage, new Dictionary<string, object>()
|
||||
{
|
||||
{ MessageConstants.MessageDataRequestAccountIdKey, accountId }
|
||||
});
|
||||
|
||||
queueResponse.ThrowIfFailed();
|
||||
}
|
||||
|
||||
public Task<WinoServerResponse<TResponse>> GetResponseAsync<TResponse, TRequestType>(TRequestType message, CancellationToken cancellationToken = default) where TRequestType : IClientMessage
|
||||
=> GetResponseInternalAsync<TResponse, TRequestType>(message, cancellationToken: cancellationToken);
|
||||
|
||||
private async Task<WinoServerResponse<TResponse>> GetResponseInternalAsync<TResponse, TRequestType>(TRequestType message,
|
||||
Dictionary<string, object> parameters = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (Status != WinoServerConnectionStatus.Connected)
|
||||
await ConnectAsync();
|
||||
|
||||
if (Connection == null) return WinoServerResponse<TResponse>.CreateErrorResponse("Server connection is not established.");
|
||||
|
||||
string serializedMessage = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
serializedMessage = JsonSerializer.Serialize(message, _jsonSerializerOptions);
|
||||
}
|
||||
catch (Exception serializationException)
|
||||
{
|
||||
Logger.Error(serializationException, $"Failed to serialize client message for sending.");
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Failed to serialize message.\n{serializationException.Message}");
|
||||
}
|
||||
|
||||
AppServiceResponse response = null;
|
||||
|
||||
try
|
||||
{
|
||||
var valueSet = new ValueSet
|
||||
{
|
||||
{ MessageConstants.MessageTypeKey, (int)MessageType.ServerMessage },
|
||||
{ MessageConstants.MessageDataKey, serializedMessage },
|
||||
{ MessageConstants.MessageDataTypeKey, message.GetType().Name }
|
||||
};
|
||||
|
||||
// Add additional parameters into ValueSet
|
||||
if (parameters != null)
|
||||
{
|
||||
foreach (var item in parameters)
|
||||
{
|
||||
valueSet.Add(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
response = await Connection.SendMessageAsync(valueSet).AsTask(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Request is canceled by client.");
|
||||
}
|
||||
catch (Exception serverSendException)
|
||||
{
|
||||
Logger.Error(serverSendException, $"Failed to send message to server.");
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Failed to send message to server.\n{serverSendException.Message}");
|
||||
}
|
||||
|
||||
// It should be always Success.
|
||||
if (response.Status != AppServiceResponseStatus.Success)
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Wino Server responded with '{response.Status}' status to message delivery.");
|
||||
|
||||
// All responses must contain a message data.
|
||||
if (!(response.Message.TryGetValue(MessageConstants.MessageDataKey, out object messageDataObject) && messageDataObject is string messageJson))
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse("Server response did not contain message data.");
|
||||
|
||||
// Try deserialize the message data.
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<WinoServerResponse<TResponse>>(messageJson);
|
||||
}
|
||||
catch (Exception jsonDeserializationError)
|
||||
{
|
||||
Logger.Error(jsonDeserializationError, $"Failed to deserialize server response message data.");
|
||||
return WinoServerResponse<TResponse>.CreateErrorResponse($"Failed to deserialize Wino server response message data.\n{jsonDeserializationError.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(WinoServerConnectionEstablished message)
|
||||
=> ConnectingHandle?.TrySetResult(true);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Wino.Core.UWP.Styles
|
||||
namespace Wino.Core.UWP.Styles;
|
||||
|
||||
partial class CustomMessageDialogStyles : ResourceDictionary
|
||||
{
|
||||
partial class CustomMessageDialogStyles : ResourceDictionary
|
||||
public CustomMessageDialogStyles()
|
||||
{
|
||||
public CustomMessageDialogStyles()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Wino.Core.UWP.Styles
|
||||
namespace Wino.Core.UWP.Styles;
|
||||
|
||||
public partial class DataTemplates : ResourceDictionary
|
||||
{
|
||||
public partial class DataTemplates : ResourceDictionary
|
||||
public DataTemplates()
|
||||
{
|
||||
public DataTemplates()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Wino.Core.ViewModels;
|
||||
|
||||
namespace Wino.Core.UWP.Views.Abstract
|
||||
namespace Wino.Core.UWP.Views.Abstract;
|
||||
|
||||
public abstract class ManageAccountsPageAbstract : BasePage<ManageAccountsPagePageViewModel>
|
||||
{
|
||||
public abstract class ManageAccountsPageAbstract : BasePage<ManageAccountsPagePageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Wino.Core.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
namespace Wino.Views.Abstract;
|
||||
|
||||
public abstract class SettingOptionsPageAbstract : SettingsPageBase<SettingOptionsPageViewModel>
|
||||
{
|
||||
public abstract class SettingOptionsPageAbstract : SettingsPageBase<SettingOptionsPageViewModel>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Core.UWP;
|
||||
using Wino.Core.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public abstract class SettingsPageAbstract : BasePage<SettingsPageViewModel> { }
|
||||
}
|
||||
namespace Wino.Views.Abstract;
|
||||
|
||||
public abstract class SettingsPageAbstract : BasePage<SettingsPageViewModel> { }
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
using Wino.Core.UWP;
|
||||
using Wino.Core.ViewModels;
|
||||
|
||||
namespace Wino.Views.Abstract
|
||||
{
|
||||
public partial class SettingsPageBase<T> : BasePage<T> where T : CoreBaseViewModel
|
||||
{
|
||||
public string Title
|
||||
{
|
||||
get { return (string)GetValue(TitleProperty); }
|
||||
set { SetValue(TitleProperty, value); }
|
||||
}
|
||||
namespace Wino.Views.Abstract;
|
||||
|
||||
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(SettingsPageBase<T>), new PropertyMetadata(string.Empty));
|
||||
public partial class SettingsPageBase<T> : BasePage<T> where T : CoreBaseViewModel
|
||||
{
|
||||
public string Title
|
||||
{
|
||||
get { return (string)GetValue(TitleProperty); }
|
||||
set { SetValue(TitleProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(SettingsPageBase<T>), new PropertyMetadata(string.Empty));
|
||||
}
|
||||
|
||||
@@ -11,92 +11,91 @@ using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Messaging.Client.Navigation;
|
||||
using Wino.Messaging.UI;
|
||||
|
||||
namespace Wino.Views
|
||||
namespace Wino.Views;
|
||||
|
||||
public sealed partial class ManageAccountsPage : ManageAccountsPageAbstract,
|
||||
IRecipient<BackBreadcrumNavigationRequested>,
|
||||
IRecipient<BreadcrumbNavigationRequested>,
|
||||
IRecipient<MergedInboxRenamed>
|
||||
{
|
||||
public sealed partial class ManageAccountsPage : ManageAccountsPageAbstract,
|
||||
IRecipient<BackBreadcrumNavigationRequested>,
|
||||
IRecipient<BreadcrumbNavigationRequested>,
|
||||
IRecipient<MergedInboxRenamed>
|
||||
public ObservableCollection<BreadcrumbNavigationItemViewModel> PageHistory { get; set; } = new ObservableCollection<BreadcrumbNavigationItemViewModel>();
|
||||
|
||||
|
||||
public ManageAccountsPage()
|
||||
{
|
||||
public ObservableCollection<BreadcrumbNavigationItemViewModel> PageHistory { get; set; } = new ObservableCollection<BreadcrumbNavigationItemViewModel>();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuManageAccounts, WinoPage.AccountManagementPage);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
||||
|
||||
var accountManagementPageType = ViewModel.NavigationService.GetPageType(WinoPage.AccountManagementPage);
|
||||
|
||||
AccountPagesFrame.Navigate(accountManagementPageType, null, new SuppressNavigationTransitionInfo());
|
||||
}
|
||||
|
||||
|
||||
public ManageAccountsPage()
|
||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
||||
|
||||
if (pageType == null) return;
|
||||
|
||||
AccountPagesFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
||||
}
|
||||
|
||||
private void GoBackFrame()
|
||||
{
|
||||
if (AccountPagesFrame.CanGoBack)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuManageAccounts, WinoPage.AccountManagementPage);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
||||
|
||||
var accountManagementPageType = ViewModel.NavigationService.GetPageType(WinoPage.AccountManagementPage);
|
||||
|
||||
AccountPagesFrame.Navigate(accountManagementPageType, null, new SuppressNavigationTransitionInfo());
|
||||
}
|
||||
|
||||
|
||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
||||
|
||||
if (pageType == null) return;
|
||||
|
||||
AccountPagesFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
||||
}
|
||||
|
||||
private void GoBackFrame()
|
||||
{
|
||||
if (AccountPagesFrame.CanGoBack)
|
||||
{
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
|
||||
AccountPagesFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
}
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedPageHistory = PageHistory[args.Index];
|
||||
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
||||
{
|
||||
AccountPagesFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(BackBreadcrumNavigationRequested message)
|
||||
{
|
||||
GoBackFrame();
|
||||
}
|
||||
|
||||
public void Receive(AccountUpdatedMessage message)
|
||||
{
|
||||
// TODO: Find better way to retrieve page history from the stack for the account.
|
||||
var activePage = PageHistory.LastOrDefault();
|
||||
|
||||
if (activePage == null) return;
|
||||
|
||||
activePage.Title = message.Account.Name;
|
||||
}
|
||||
|
||||
public void Receive(MergedInboxRenamed message)
|
||||
{
|
||||
// TODO: Find better way to retrieve page history from the stack for the merged account.
|
||||
var activePage = PageHistory.LastOrDefault();
|
||||
|
||||
if (activePage == null) return;
|
||||
|
||||
activePage.Title = message.NewName;
|
||||
AccountPagesFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
}
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedPageHistory = PageHistory[args.Index];
|
||||
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
||||
{
|
||||
AccountPagesFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(BackBreadcrumNavigationRequested message)
|
||||
{
|
||||
GoBackFrame();
|
||||
}
|
||||
|
||||
public void Receive(AccountUpdatedMessage message)
|
||||
{
|
||||
// TODO: Find better way to retrieve page history from the stack for the account.
|
||||
var activePage = PageHistory.LastOrDefault();
|
||||
|
||||
if (activePage == null) return;
|
||||
|
||||
activePage.Title = message.Account.Name;
|
||||
}
|
||||
|
||||
public void Receive(MergedInboxRenamed message)
|
||||
{
|
||||
// TODO: Find better way to retrieve page history from the stack for the merged account.
|
||||
var activePage = PageHistory.LastOrDefault();
|
||||
|
||||
if (activePage == null) return;
|
||||
|
||||
activePage.Title = message.NewName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Wino.Views.Abstract;
|
||||
|
||||
namespace Wino.Views.Settings
|
||||
namespace Wino.Views.Settings;
|
||||
|
||||
public sealed partial class SettingOptionsPage : SettingOptionsPageAbstract
|
||||
{
|
||||
public sealed partial class SettingOptionsPage : SettingOptionsPageAbstract
|
||||
public SettingOptionsPage()
|
||||
{
|
||||
public SettingOptionsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,77 +11,76 @@ using Wino.Messaging.Client.Navigation;
|
||||
using Wino.Views.Abstract;
|
||||
using Wino.Views.Settings;
|
||||
|
||||
namespace Wino.Views
|
||||
namespace Wino.Views;
|
||||
|
||||
public sealed partial class SettingsPage : SettingsPageAbstract, IRecipient<BreadcrumbNavigationRequested>
|
||||
{
|
||||
public sealed partial class SettingsPage : SettingsPageAbstract, IRecipient<BreadcrumbNavigationRequested>
|
||||
public ObservableCollection<BreadcrumbNavigationItemViewModel> PageHistory { get; set; } = [];
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
public ObservableCollection<BreadcrumbNavigationItemViewModel> PageHistory { get; set; } = [];
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public SettingsPage()
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo());
|
||||
|
||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
||||
|
||||
if (e.Parameter is WinoPage parameterPage)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo());
|
||||
|
||||
var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage);
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true));
|
||||
|
||||
if (e.Parameter is WinoPage parameterPage)
|
||||
switch (parameterPage)
|
||||
{
|
||||
switch (parameterPage)
|
||||
{
|
||||
case WinoPage.AppPreferencesPage:
|
||||
WeakReferenceMessenger.Default.Send(new BreadcrumbNavigationRequested(Translator.SettingsAppPreferences_Title, WinoPage.AppPreferencesPage));
|
||||
break;
|
||||
case WinoPage.PersonalizationPage:
|
||||
WeakReferenceMessenger.Default.Send(new BreadcrumbNavigationRequested(Translator.SettingsPersonalization_Title, WinoPage.PersonalizationPage));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLanguageChanged()
|
||||
{
|
||||
base.OnLanguageChanged();
|
||||
|
||||
// Update Settings header in breadcrumb.
|
||||
|
||||
var settingsHeader = PageHistory.FirstOrDefault();
|
||||
|
||||
if (settingsHeader == null) return;
|
||||
|
||||
settingsHeader.Title = Translator.MenuSettings;
|
||||
}
|
||||
|
||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
||||
|
||||
if (pageType == null) return;
|
||||
|
||||
SettingsFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedPageHistory = PageHistory[args.Index];
|
||||
var activeIndex = PageHistory.IndexOf(PageHistory.FirstOrDefault(a => a.IsActive));
|
||||
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
||||
{
|
||||
SettingsFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
case WinoPage.AppPreferencesPage:
|
||||
WeakReferenceMessenger.Default.Send(new BreadcrumbNavigationRequested(Translator.SettingsAppPreferences_Title, WinoPage.AppPreferencesPage));
|
||||
break;
|
||||
case WinoPage.PersonalizationPage:
|
||||
WeakReferenceMessenger.Default.Send(new BreadcrumbNavigationRequested(Translator.SettingsPersonalization_Title, WinoPage.PersonalizationPage));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLanguageChanged()
|
||||
{
|
||||
base.OnLanguageChanged();
|
||||
|
||||
// Update Settings header in breadcrumb.
|
||||
|
||||
var settingsHeader = PageHistory.FirstOrDefault();
|
||||
|
||||
if (settingsHeader == null) return;
|
||||
|
||||
settingsHeader.Title = Translator.MenuSettings;
|
||||
}
|
||||
|
||||
void IRecipient<BreadcrumbNavigationRequested>.Receive(BreadcrumbNavigationRequested message)
|
||||
{
|
||||
var pageType = ViewModel.NavigationService.GetPageType(message.PageType);
|
||||
|
||||
if (pageType == null) return;
|
||||
|
||||
SettingsFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
|
||||
PageHistory.ForEach(a => a.IsActive = false);
|
||||
|
||||
PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true));
|
||||
}
|
||||
|
||||
private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args)
|
||||
{
|
||||
var clickedPageHistory = PageHistory[args.Index];
|
||||
var activeIndex = PageHistory.IndexOf(PageHistory.FirstOrDefault(a => a.IsActive));
|
||||
|
||||
while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory)
|
||||
{
|
||||
SettingsFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
PageHistory.RemoveAt(PageHistory.Count - 1);
|
||||
PageHistory[PageHistory.Count - 1].IsActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AppCenter;
|
||||
using Microsoft.AppCenter.Analytics;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Nito.AsyncEx;
|
||||
using Serilog;
|
||||
@@ -22,228 +25,242 @@ using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Services;
|
||||
|
||||
namespace Wino.Core.UWP
|
||||
namespace Wino.Core.UWP;
|
||||
|
||||
public abstract class WinoApplication : Application
|
||||
{
|
||||
public abstract class WinoApplication : Application
|
||||
public new static WinoApplication Current => (WinoApplication)Application.Current;
|
||||
public const string WinoLaunchLogPrefix = "[Wino Launch] ";
|
||||
|
||||
public IServiceProvider Services { get; }
|
||||
protected ILogInitializer LogInitializer { get; }
|
||||
protected IApplicationConfiguration AppConfiguration { get; }
|
||||
protected IWinoServerConnectionManager<AppServiceConnection> AppServiceConnectionManager { get; }
|
||||
protected IThemeService ThemeService { get; }
|
||||
protected IDatabaseService DatabaseService { get; }
|
||||
protected ITranslationService TranslationService { get; }
|
||||
|
||||
// Order matters.
|
||||
private List<IInitializeAsync> initializeServices => new List<IInitializeAsync>()
|
||||
{
|
||||
public new static WinoApplication Current => (WinoApplication)Application.Current;
|
||||
public const string WinoLaunchLogPrefix = "[Wino Launch] ";
|
||||
DatabaseService,
|
||||
TranslationService,
|
||||
ThemeService,
|
||||
};
|
||||
|
||||
public IServiceProvider Services { get; }
|
||||
protected ILogInitializer LogInitializer { get; }
|
||||
protected IApplicationConfiguration AppConfiguration { get; }
|
||||
protected IWinoServerConnectionManager<AppServiceConnection> AppServiceConnectionManager { get; }
|
||||
protected IThemeService ThemeService { get; }
|
||||
protected IDatabaseService DatabaseService { get; }
|
||||
protected ITranslationService TranslationService { get; }
|
||||
public abstract string AppCenterKey { get; }
|
||||
|
||||
protected WinoApplication()
|
||||
protected WinoApplication()
|
||||
{
|
||||
ConfigureAppCenter();
|
||||
ConfigurePrelaunch();
|
||||
|
||||
Services = ConfigureServices();
|
||||
|
||||
UnhandledException += OnAppUnhandledException;
|
||||
Resuming += OnResuming;
|
||||
Suspending += OnSuspending;
|
||||
|
||||
LogInitializer = Services.GetService<ILogInitializer>();
|
||||
AppConfiguration = Services.GetService<IApplicationConfiguration>();
|
||||
|
||||
AppServiceConnectionManager = Services.GetService<IWinoServerConnectionManager<AppServiceConnection>>();
|
||||
ThemeService = Services.GetService<IThemeService>();
|
||||
DatabaseService = Services.GetService<IDatabaseService>();
|
||||
TranslationService = Services.GetService<ITranslationService>();
|
||||
|
||||
// Make sure the paths are setup on app start.
|
||||
AppConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path;
|
||||
AppConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path;
|
||||
AppConfiguration.ApplicationTempFolderPath = ApplicationData.Current.TemporaryFolder.Path;
|
||||
|
||||
ConfigureLogging();
|
||||
}
|
||||
|
||||
protected abstract void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e);
|
||||
protected abstract IEnumerable<ActivationHandler> GetActivationHandlers();
|
||||
protected abstract ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler();
|
||||
protected override void OnWindowCreated(WindowCreatedEventArgs args)
|
||||
{
|
||||
base.OnWindowCreated(args);
|
||||
|
||||
ConfigureTitleBar();
|
||||
|
||||
LogActivation($"OnWindowCreated -> IsWindowNull: {args.Window == null}");
|
||||
|
||||
TryRegisterAppCloseChange();
|
||||
}
|
||||
|
||||
public IEnumerable<IInitializeAsync> GetActivationServices()
|
||||
{
|
||||
yield return DatabaseService;
|
||||
yield return TranslationService;
|
||||
yield return ThemeService;
|
||||
}
|
||||
|
||||
public Task InitializeServicesAsync() => GetActivationServices().Select(a => a.InitializeAsync()).WhenAll();
|
||||
|
||||
public bool IsInteractiveLaunchArgs(object args) => args is IActivatedEventArgs;
|
||||
|
||||
public void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}");
|
||||
|
||||
private void ConfigureTitleBar()
|
||||
{
|
||||
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
|
||||
var applicationViewTitleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
|
||||
// Extend shell content into core window to meet design requirements.
|
||||
coreTitleBar.ExtendViewIntoTitleBar = true;
|
||||
|
||||
// Change system buttons and background colors to meet design requirements.
|
||||
applicationViewTitleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
applicationViewTitleBar.BackgroundColor = Colors.Transparent;
|
||||
applicationViewTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
applicationViewTitleBar.ButtonForegroundColor = Colors.White;
|
||||
}
|
||||
|
||||
public async Task ActivateWinoAsync(object args)
|
||||
{
|
||||
await InitializeServicesAsync();
|
||||
|
||||
if (IsInteractiveLaunchArgs(args))
|
||||
{
|
||||
ConfigurePrelaunch();
|
||||
|
||||
Services = ConfigureServices();
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
|
||||
UnhandledException += OnAppUnhandledException;
|
||||
|
||||
Resuming += OnResuming;
|
||||
Suspending += OnSuspending;
|
||||
|
||||
LogInitializer = Services.GetService<ILogInitializer>();
|
||||
AppConfiguration = Services.GetService<IApplicationConfiguration>();
|
||||
|
||||
AppServiceConnectionManager = Services.GetService<IWinoServerConnectionManager<AppServiceConnection>>();
|
||||
ThemeService = Services.GetService<IThemeService>();
|
||||
DatabaseService = Services.GetService<IDatabaseService>();
|
||||
TranslationService = Services.GetService<ITranslationService>();
|
||||
|
||||
// Make sure the paths are setup on app start.
|
||||
AppConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path;
|
||||
AppConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path;
|
||||
AppConfiguration.ApplicationTempFolderPath = ApplicationData.Current.TemporaryFolder.Path;
|
||||
|
||||
ConfigureLogging();
|
||||
}
|
||||
|
||||
private void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
|
||||
=> Log.Fatal(e.ExceptionObject as Exception, "AppDomain Unhandled Exception");
|
||||
|
||||
private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||||
=> Log.Error(e.Exception, "Unobserved Task Exception");
|
||||
|
||||
private void OnAppUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Log.Fatal(e.Exception, "Unhandled Exception");
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
protected abstract void OnApplicationCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e);
|
||||
protected abstract IEnumerable<ActivationHandler> GetActivationHandlers();
|
||||
protected abstract ActivationHandler<IActivatedEventArgs> GetDefaultActivationHandler();
|
||||
protected override void OnWindowCreated(WindowCreatedEventArgs args)
|
||||
{
|
||||
base.OnWindowCreated(args);
|
||||
|
||||
ConfigureTitleBar();
|
||||
|
||||
LogActivation($"OnWindowCreated -> IsWindowNull: {args.Window == null}");
|
||||
|
||||
TryRegisterAppCloseChange();
|
||||
}
|
||||
|
||||
public IEnumerable<IInitializeAsync> GetActivationServices()
|
||||
{
|
||||
yield return DatabaseService;
|
||||
yield return TranslationService;
|
||||
yield return ThemeService;
|
||||
}
|
||||
|
||||
public Task InitializeServicesAsync() => GetActivationServices().Select(a => a.InitializeAsync()).WhenAll();
|
||||
|
||||
public bool IsInteractiveLaunchArgs(object args) => args is IActivatedEventArgs;
|
||||
|
||||
public void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}");
|
||||
|
||||
private void ConfigureTitleBar()
|
||||
{
|
||||
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
|
||||
var applicationViewTitleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
|
||||
// Extend shell content into core window to meet design requirements.
|
||||
coreTitleBar.ExtendViewIntoTitleBar = true;
|
||||
|
||||
// Change system buttons and background colors to meet design requirements.
|
||||
applicationViewTitleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
applicationViewTitleBar.BackgroundColor = Colors.Transparent;
|
||||
applicationViewTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
applicationViewTitleBar.ButtonForegroundColor = Colors.White;
|
||||
}
|
||||
|
||||
public async Task ActivateWinoAsync(object args)
|
||||
{
|
||||
await InitializeServicesAsync();
|
||||
|
||||
if (IsInteractiveLaunchArgs(args))
|
||||
if (Window.Current.Content == null)
|
||||
{
|
||||
if (Window.Current.Content == null)
|
||||
{
|
||||
var mainFrame = new Frame();
|
||||
var mainFrame = new Frame();
|
||||
|
||||
Window.Current.Content = mainFrame;
|
||||
Window.Current.Content = mainFrame;
|
||||
|
||||
await ThemeService.InitializeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
await HandleActivationAsync(args);
|
||||
|
||||
if (IsInteractiveLaunchArgs(args))
|
||||
{
|
||||
Window.Current.Activate();
|
||||
|
||||
LogActivation("Window activated");
|
||||
await ThemeService.InitializeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task HandleActivationAsync(object activationArgs)
|
||||
await HandleActivationAsync(args);
|
||||
|
||||
if (IsInteractiveLaunchArgs(args))
|
||||
{
|
||||
if (GetActivationHandlers() != null)
|
||||
{
|
||||
var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs)) ?? null;
|
||||
Window.Current.Activate();
|
||||
|
||||
if (activationHandler != null)
|
||||
{
|
||||
await activationHandler.HandleAsync(activationArgs);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsInteractiveLaunchArgs(activationArgs))
|
||||
{
|
||||
var defaultHandler = GetDefaultActivationHandler();
|
||||
|
||||
if (defaultHandler.CanHandle(activationArgs))
|
||||
{
|
||||
await defaultHandler.HandleAsync(activationArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}");
|
||||
|
||||
if (!args.PrelaunchActivated)
|
||||
{
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnFileActivated(FileActivatedEventArgs args)
|
||||
{
|
||||
base.OnFileActivated(args);
|
||||
|
||||
LogActivation($"OnFileActivated -> ItemCount: {args.Files.Count}, Kind: {args.Kind}, PreviousExecutionState: {args.PreviousExecutionState}");
|
||||
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
|
||||
protected override async void OnActivated(IActivatedEventArgs args)
|
||||
{
|
||||
base.OnActivated(args);
|
||||
|
||||
Log.Information($"OnActivated -> {args.GetType().Name}, Kind -> {args.Kind}, Prev Execution State -> {args.PreviousExecutionState}");
|
||||
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
|
||||
private void TryRegisterAppCloseChange()
|
||||
{
|
||||
try
|
||||
{
|
||||
var systemNavigationManagerPreview = SystemNavigationManagerPreview.GetForCurrentView();
|
||||
|
||||
systemNavigationManagerPreview.CloseRequested -= OnApplicationCloseRequested;
|
||||
systemNavigationManagerPreview.CloseRequested += OnApplicationCloseRequested;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void ConfigurePrelaunch()
|
||||
{
|
||||
if (ApiInformation.IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", "EnablePrelaunch"))
|
||||
CoreApplication.EnablePrelaunch(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual async void OnResuming(object sender, object e)
|
||||
{
|
||||
// App Service connection was lost on suspension.
|
||||
// We must restore it.
|
||||
// Server might be running already, but re-launching it will trigger a new connection attempt.
|
||||
|
||||
try
|
||||
{
|
||||
await AppServiceConnectionManager.ConnectAsync();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to connect to server after resuming the app.");
|
||||
}
|
||||
}
|
||||
public virtual void OnSuspending(object sender, SuspendingEventArgs e) { }
|
||||
|
||||
public abstract IServiceProvider ConfigureServices();
|
||||
|
||||
public void ConfigureLogging()
|
||||
{
|
||||
string logFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.ClientLogFile);
|
||||
LogInitializer.SetupLogger(logFilePath);
|
||||
LogActivation("Window activated");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task HandleActivationAsync(object activationArgs)
|
||||
{
|
||||
if (GetActivationHandlers() != null)
|
||||
{
|
||||
var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs)) ?? null;
|
||||
|
||||
if (activationHandler != null)
|
||||
{
|
||||
await activationHandler.HandleAsync(activationArgs);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsInteractiveLaunchArgs(activationArgs))
|
||||
{
|
||||
var defaultHandler = GetDefaultActivationHandler();
|
||||
|
||||
if (defaultHandler.CanHandle(activationArgs))
|
||||
{
|
||||
await defaultHandler.HandleAsync(activationArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}");
|
||||
|
||||
if (!args.PrelaunchActivated)
|
||||
{
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnFileActivated(FileActivatedEventArgs args)
|
||||
{
|
||||
base.OnFileActivated(args);
|
||||
|
||||
LogActivation($"OnFileActivated -> ItemCount: {args.Files.Count}, Kind: {args.Kind}, PreviousExecutionState: {args.PreviousExecutionState}");
|
||||
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
|
||||
protected override async void OnActivated(IActivatedEventArgs args)
|
||||
{
|
||||
base.OnActivated(args);
|
||||
|
||||
Log.Information($"OnActivated -> {args.GetType().Name}, Kind -> {args.Kind}, Prev Execution State -> {args.PreviousExecutionState}");
|
||||
|
||||
await ActivateWinoAsync(args);
|
||||
}
|
||||
|
||||
private void TryRegisterAppCloseChange()
|
||||
{
|
||||
try
|
||||
{
|
||||
var systemNavigationManagerPreview = SystemNavigationManagerPreview.GetForCurrentView();
|
||||
|
||||
systemNavigationManagerPreview.CloseRequested -= OnApplicationCloseRequested;
|
||||
systemNavigationManagerPreview.CloseRequested += OnApplicationCloseRequested;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ConfigurePrelaunch()
|
||||
{
|
||||
if (ApiInformation.IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", "EnablePrelaunch"))
|
||||
CoreApplication.EnablePrelaunch(true);
|
||||
}
|
||||
|
||||
private void OnAppUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var parameters = new Dictionary<string, string>()
|
||||
{
|
||||
{ "BaseMessage", e.Exception.GetBaseException().Message },
|
||||
{ "BaseStackTrace", e.Exception.GetBaseException().StackTrace },
|
||||
{ "StackTrace", e.Exception.StackTrace },
|
||||
{ "Message", e.Exception.Message },
|
||||
};
|
||||
|
||||
Log.Error(e.Exception, "[Wino Crash]");
|
||||
|
||||
Crashes.TrackError(e.Exception, parameters);
|
||||
Analytics.TrackEvent("Wino Crashed", parameters);
|
||||
}
|
||||
|
||||
|
||||
public virtual async void OnResuming(object sender, object e)
|
||||
{
|
||||
// App Service connection was lost on suspension.
|
||||
// We must restore it.
|
||||
// Server might be running already, but re-launching it will trigger a new connection attempt.
|
||||
|
||||
try
|
||||
{
|
||||
await AppServiceConnectionManager.ConnectAsync();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to connect to server after resuming the app.");
|
||||
}
|
||||
}
|
||||
public virtual void OnSuspending(object sender, SuspendingEventArgs e) { }
|
||||
|
||||
public abstract IServiceProvider ConfigureServices();
|
||||
public void ConfigureAppCenter()
|
||||
=> AppCenter.Start(AppCenterKey, typeof(Analytics), typeof(Crashes));
|
||||
|
||||
public void ConfigureLogging()
|
||||
{
|
||||
string logFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.ClientLogFile);
|
||||
LogInitializer.SetupLogger(logFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user