Initial commit.

This commit is contained in:
Burak Kaan Köse
2024-04-18 01:44:37 +02:00
parent 524ea4c0e1
commit 12d3814626
671 changed files with 77295 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
using System.Threading.Tasks;
namespace Wino.Activation
{
// For more information on understanding and extending activation flow see
// https://github.com/microsoft/TemplateStudio/blob/main/docs/UWP/activation.md
internal abstract class ActivationHandler
{
public abstract bool CanHandle(object args);
public abstract Task HandleAsync(object args);
}
// Extend this class to implement new ActivationHandlers
internal 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);
}
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;
}
}
}

View File

@@ -0,0 +1,174 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp.Notifications;
using Serilog;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Background;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using Windows.UI.Notifications;
using Wino.Core;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Synchronization;
using Wino.Core.UWP.Services;
using Wino.Services;
namespace Wino.Activation
{
internal class BackgroundActivationHandler : ActivationHandler<BackgroundActivatedEventArgs>
{
private const string BackgroundExecutionLogTag = "[BackgroundExecution] ";
private readonly IWinoRequestDelegator _winoRequestDelegator;
private readonly IBackgroundSynchronizer _backgroundSynchronizer;
private readonly INativeAppService _nativeAppService;
private readonly IWinoRequestProcessor _winoRequestProcessor;
private readonly IWinoSynchronizerFactory _winoSynchronizerFactory;
private readonly IMailService _mailService;
private ToastArguments _toastArguments;
BackgroundTaskDeferral _deferral;
public BackgroundActivationHandler(IWinoRequestDelegator winoRequestDelegator,
IBackgroundSynchronizer backgroundSynchronizer,
INativeAppService nativeAppService,
IWinoRequestProcessor winoRequestProcessor,
IWinoSynchronizerFactory winoSynchronizerFactory,
IMailService mailService)
{
_winoRequestDelegator = winoRequestDelegator;
_backgroundSynchronizer = backgroundSynchronizer;
_nativeAppService = nativeAppService;
_winoRequestProcessor = winoRequestProcessor;
_winoSynchronizerFactory = winoSynchronizerFactory;
_mailService = mailService;
}
protected override async Task HandleInternalAsync(BackgroundActivatedEventArgs args)
{
var instance = args.TaskInstance;
var taskName = instance.Task.Name;
instance.Canceled -= OnBackgroundExecutionCanceled;
instance.Canceled += OnBackgroundExecutionCanceled;
_deferral = instance.GetDeferral();
if (taskName == BackgroundTaskService.ToastActivationTaskEx)
{
// ToastNotificationActionTriggerDetail somehow use UI thread.
// Calling this without a dispatcher will result in error.
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
if (instance.TriggerDetails is ToastNotificationActionTriggerDetail toastNotificationActionTriggerDetail)
_toastArguments = ToastArguments.Parse(toastNotificationActionTriggerDetail.Argument);
});
// All toast activation mail actions are handled here like mark as read or delete.
// This should not launch the application on the foreground.
// Get the action and mail item id.
// Prepare package and send to delegator.
if (_toastArguments.TryGetValue(Constants.ToastMailItemIdKey, out string mailItemId) &&
_toastArguments.TryGetValue(Constants.ToastActionKey, out MailOperation action))
{
// TODO: Remote folder id.
var mailItem = await _mailService.GetSingleMailItemAsync(mailItemId, string.Empty);
if (mailItem == null) return;
if (_nativeAppService.IsAppRunning())
{
// Just send the package. We should reflect the UI changes as well.
var package = new MailOperationPreperationRequest(action, mailItem);
await _winoRequestDelegator.ExecuteAsync(package);
}
else
{
// We need to synchronize changes without reflection the UI changes.
var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(mailItem.AssignedAccount.Id);
var prepRequest = new MailOperationPreperationRequest(action, mailItem);
var requests = await _winoRequestProcessor.PrepareRequestsAsync(prepRequest);
foreach (var request in requests)
{
synchronizer.QueueRequest(request);
}
var options = new SynchronizationOptions()
{
Type = SynchronizationType.ExecuteRequests,
AccountId = mailItem.AssignedAccount.Id
};
await synchronizer.SynchronizeAsync(options);
}
}
}
else if (taskName == BackgroundTaskService.BackgroundSynchronizationTimerTaskNameEx)
{
var watch = new Stopwatch();
watch.Start();
// Run timer based background synchronization.
await _backgroundSynchronizer.RunBackgroundSynchronizationAsync(BackgroundSynchronizationReason.Timer);
watch.Stop();
Log.Information($"{BackgroundExecutionLogTag}Background synchronization is completed in {watch.Elapsed.TotalSeconds} seconds.");
}
instance.Canceled -= OnBackgroundExecutionCanceled;
_deferral.Complete();
}
private void OnBackgroundExecutionCanceled(Windows.ApplicationModel.Background.IBackgroundTaskInstance sender, Windows.ApplicationModel.Background.BackgroundTaskCancellationReason reason)
{
Log.Error($"{BackgroundExecutionLogTag} ({sender.Task.Name}) Background task is canceled. Reason -> {reason}");
_deferral?.Complete();
}
protected override bool CanHandleInternal(BackgroundActivatedEventArgs args)
{
var instance = args.TaskInstance;
var taskName = instance.Task.Name;
if (taskName == BackgroundTaskService.ToastActivationTaskEx)
{
// User clicked Mark as Read or Delete in toast notification.
// MailId and Action must present in the arguments.
return true;
//if (instance.TriggerDetails is ToastNotificationActionTriggerDetail toastNotificationActionTriggerDetail)
//{
// _toastArguments = ToastArguments.Parse(toastNotificationActionTriggerDetail.Argument);
// return
// _toastArguments.Contains(Constants.ToastMailItemIdKey) &&
// _toastArguments.Contains(Constants.ToastActionKey);
//}
}
else if (taskName == BackgroundTaskService.BackgroundSynchronizationTimerTaskNameEx)
{
// This is timer based background synchronization.
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,23 @@
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Wino.Views;
namespace Wino.Activation
{
internal class DefaultActivationHandler : ActivationHandler<IActivatedEventArgs>
{
protected override Task HandleInternalAsync(IActivatedEventArgs args)
{
(Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo());
return Task.CompletedTask;
}
// Only navigate if Frame content doesn't exist.
protected override bool CanHandleInternal(IActivatedEventArgs args)
=> (Window.Current?.Content as Frame)?.Content == null;
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Services;
using Wino.Helpers;
using Wino.Views;
namespace Wino.Activation
{
internal class FileActivationHandler : ActivationHandler<FileActivatedEventArgs>
{
private readonly INativeAppService _nativeAppService;
private readonly IMimeFileService _mimeFileService;
private readonly IStatePersistanceService _statePersistanceService;
private readonly IWinoNavigationService _winoNavigationService;
public FileActivationHandler(INativeAppService nativeAppService,
IMimeFileService mimeFileService,
IStatePersistanceService statePersistanceService,
IWinoNavigationService winoNavigationService)
{
_nativeAppService = nativeAppService;
_mimeFileService = mimeFileService;
_statePersistanceService = statePersistanceService;
_winoNavigationService = winoNavigationService;
}
protected override async Task HandleInternalAsync(FileActivatedEventArgs args)
{
// Always handle the last item passed.
// Multiple files are not supported.
var file = args.Files.Last() as StorageFile;
// Only EML files are supported now.
var fileExtension = Path.GetExtension(file.Path);
if (string.Equals(fileExtension, ".eml", StringComparison.OrdinalIgnoreCase))
{
var fileBytes = await file.ReadBytesAsync();
var directoryName = Path.GetDirectoryName(file.Path);
var messageInformation = await _mimeFileService.GetMimeMessageInformationAsync(fileBytes, directoryName).ConfigureAwait(false);
if (_nativeAppService.IsAppRunning())
{
// TODO: Activate another Window and go to mail rendering page.
_winoNavigationService.NavigateRendering(messageInformation);
}
else
{
_statePersistanceService.ShouldShiftMailRenderingDesign = true;
(Window.Current.Content as Frame).Navigate(typeof(MailRenderingPage), messageInformation, new DrillInNavigationTransitionInfo());
}
}
}
protected override bool CanHandleInternal(FileActivatedEventArgs args) => args.Files.Any();
}
}

View File

@@ -0,0 +1,56 @@
using System.Threading.Tasks;
using System.Web;
using CommunityToolkit.Mvvm.Messaging;
using Windows.ApplicationModel.Activation;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Messages.Authorization;
using Wino.Core.Messages.Shell;
namespace Wino.Activation
{
internal class ProtocolActivationHandler : ActivationHandler<ProtocolActivatedEventArgs>
{
private const string GoogleAuthorizationProtocolTag = "google.pw.oauth2";
private const string MailtoProtocolTag = "mailto:";
private readonly INativeAppService _nativeAppService;
private readonly ILaunchProtocolService _launchProtocolService;
public ProtocolActivationHandler(INativeAppService nativeAppService, ILaunchProtocolService launchProtocolService)
{
_nativeAppService = nativeAppService;
_launchProtocolService = launchProtocolService;
}
protected override Task HandleInternalAsync(ProtocolActivatedEventArgs args)
{
// Check URI prefix.
var protocolString = args.Uri.AbsoluteUri;
// Google OAuth Response
if (protocolString.StartsWith(GoogleAuthorizationProtocolTag))
{
// App must be working already. No need to check for running state.
WeakReferenceMessenger.Default.Send(new ProtocolAuthorizationCallbackReceived(args.Uri));
}
else if (protocolString.StartsWith(MailtoProtocolTag))
{
// mailto activation. Try to parse params.
var replaced = protocolString.Replace(MailtoProtocolTag, "mailto=");
replaced = Wino.Core.Extensions.StringExtensions.ReplaceFirst(replaced, "?", "&");
_launchProtocolService.MailtoParameters = HttpUtility.ParseQueryString(replaced);
if (_nativeAppService.IsAppRunning())
{
// Just send publish a message. Shell will continue.
WeakReferenceMessenger.Default.Send(new MailtoProtocolMessageRequested());
}
}
return Task.CompletedTask;
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Toolkit.Uwp.Notifications;
using Serilog;
using Windows.ApplicationModel.Activation;
using Wino.Core.Domain;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Messages.Accounts;
namespace Wino.Activation
{
/// <summary>
/// This handler will only handle the toasts that runs on foreground.
/// Background executions are not handled here like mark as read or delete.
/// </summary>
internal class ToastNotificationActivationHandler : ActivationHandler<ToastNotificationActivatedEventArgs>
{
private readonly INativeAppService _nativeAppService;
private readonly IMailService _mailService;
private readonly IFolderService _folderService;
private ToastArguments _toastArguments;
public ToastNotificationActivationHandler(INativeAppService nativeAppService,
IMailService mailService,
IFolderService folderService)
{
_nativeAppService = nativeAppService;
_mailService = mailService;
_folderService = folderService;
}
protected override async Task HandleInternalAsync(ToastNotificationActivatedEventArgs args)
{
// Create the mail item navigation event.
// If the app is running, it'll be picked up by the Messenger.
// Otherwise we'll save it and handle it when the shell loads all accounts.
// Parse the mail unique id and perform above actions.
if (Guid.TryParse(_toastArguments[Constants.ToastMailItemIdKey], out Guid mailItemUniqueId))
{
var account = await _mailService.GetMailAccountByUniqueIdAsync(mailItemUniqueId).ConfigureAwait(false);
if (account == null) return;
var mailItem = await _mailService.GetSingleMailItemAsync(mailItemUniqueId).ConfigureAwait(false);
if (mailItem == null) return;
var message = new AccountMenuItemExtended(mailItem.AssignedFolder.Id, mailItem);
// Delegate this event to LaunchProtocolService so app shell can pick it up on launch if app doesn't work.
var launchProtocolService = App.Current.Services.GetService<ILaunchProtocolService>();
launchProtocolService.LaunchParameter = message;
// Send the messsage anyways. Launch protocol service will be ignored if the message is picked up by subscriber shell.
WeakReferenceMessenger.Default.Send(message);
}
}
protected override bool CanHandleInternal(ToastNotificationActivatedEventArgs args)
{
try
{
_toastArguments = ToastArguments.Parse(args.Argument);
return
_toastArguments.Contains(Constants.ToastMailItemIdKey) &&
_toastArguments.Contains(Constants.ToastActionKey);
}
catch (Exception ex)
{
Log.Error(ex, "Couldn't handle parsing toast notification arguments for foreground navigate.");
}
return false;
}
}
}

212
Wino.Mail/App.xaml Normal file
View File

@@ -0,0 +1,212 @@
<Application
x:Class="Wino.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Controls"
xmlns:selectors="using:Wino.Selectors"
xmlns:wino="using:Wino">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/Converters.xaml" />
<ResourceDictionary Source="/Styles/FontIcons.xaml" />
<ResourceDictionary Source="/Styles/Colors.xaml" />
<ResourceDictionary Source="/Styles/ContentPresenters.xaml" />
<ResourceDictionary Source="/Styles/ImagePreviewControl.xaml" />
<ResourceDictionary Source="/Styles/CommandBarItems.xaml" />
<ResourceDictionary Source="/Styles/ItemContainerStyles.xaml" />
<ResourceDictionary Source="/Styles/WinoInfoBar.xaml" />
<ResourceDictionary>
<x:Double x:Key="AppBarButtonContentHeight">19</x:Double>
<x:Double x:Key="NavigationViewItemOnLeftIconBoxHeight">19</x:Double>
<Thickness x:Key="ImapSetupDialogSubPagePadding">24,24,24,24</Thickness>
<Style x:Key="PageStyle" TargetType="Page">
<Setter Property="Margin" Value="-1,0,0,0" />
<Setter Property="Padding" Value="12" />
<Setter Property="Background" Value="{ThemeResource AppBarBackgroundColor}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Padding="12">
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Default StackPanel animation. -->
<Style TargetType="StackPanel">
<Setter Property="ChildrenTransitions">
<Setter.Value>
<TransitionCollection>
<EntranceThemeTransition IsStaggeringEnabled="True" />
</TransitionCollection>
</Setter.Value>
</Setter>
</Style>
<!-- Default Style for ContentDialog -->
<Style
x:Key="WinoDialogStyle"
BasedOn="{StaticResource DefaultContentDialogStyle}"
TargetType="ContentDialog" />
<!-- Settings Menu Item Template -->
<Style TargetType="controls:SettingsMenuItemControl">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Padding" Value="0" />
<Setter Property="IsClickable" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:SettingsMenuItemControl">
<Grid>
<Button
Padding="0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
IsEnabled="{TemplateBinding IsEnabled}"
IsHitTestVisible="{TemplateBinding IsClickable}">
<Grid
Height="70"
Padding="0,6,12,6"
CornerRadius="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentControl
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Icon}" />
<Grid
Grid.Column="1"
Margin="4,0"
VerticalAlignment="Center"
RowSpacing="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
VerticalAlignment="Center"
FontWeight="SemiBold"
Style="{StaticResource BodyTextBlockStyle}"
Text="{TemplateBinding Title}" />
<TextBlock
Grid.Row="1"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{TemplateBinding Description}" />
</Grid>
<Viewbox
Grid.Column="0"
Grid.ColumnSpan="2"
Width="16"
Height="16"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=IsNavigateIconVisible}">
<PathIcon Data="F1 M 5.029297 19.091797 L 14.111328 10 L 5.029297 0.908203 L 5.908203 0.029297 L 15.888672 10 L 5.908203 19.970703 Z " />
</Viewbox>
</Grid>
</Button>
<ContentControl
Grid.RowSpan="2"
Margin="0,0,16,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{TemplateBinding SideContent}"
IsHitTestVisible="True"
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=IsNavigateIconVisible, Converter={StaticResource ReverseBooleanToVisibilityConverter}}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Wino Navigation View Item -->
<Style TargetType="controls:WinoNavigationViewItem">
<Setter Property="ContentTransitions">
<Setter.Value>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="NoneTemplate">
<Image Source="/Assets/FileTypes/type_none.png" />
</DataTemplate>
<DataTemplate x:Key="ExecutableTemplate">
<Image Source="/Assets/FileTypes/type_executable.png" />
</DataTemplate>
<DataTemplate x:Key="ImageTemplate">
<Image Source="/Assets/FileTypes/type_image.png" />
</DataTemplate>
<DataTemplate x:Key="VideoTemplate">
<Image Source="/Assets/FileTypes/type_video.png" />
</DataTemplate>
<DataTemplate x:Key="AudioTemplate">
<Image Source="/Assets/FileTypes/type_audio.png" />
</DataTemplate>
<DataTemplate x:Key="PDFTemplate">
<Image Source="/Assets/FileTypes/type_pdf.png" />
</DataTemplate>
<DataTemplate x:Key="HTMLTemplate">
<Image Source="/Assets/FileTypes/type_html.png" />
</DataTemplate>
<DataTemplate x:Key="RarTemplate">
<Image Source="/Assets/FileTypes/type_rar.png" />
</DataTemplate>
<DataTemplate x:Key="ArchiveTemplate">
<Image Source="/Assets/FileTypes/type_archive.png" />
</DataTemplate>
<DataTemplate x:Key="OtherTemplate">
<Image Source="/Assets/FileTypes/type_other.png" />
</DataTemplate>
<selectors:FileAttachmentTypeSelector
x:Key="FileTypeIconSelector"
Archive="{StaticResource ArchiveTemplate}"
Executable="{StaticResource ExecutableTemplate}"
HTML="{StaticResource HTMLTemplate}"
Image="{StaticResource ImageTemplate}"
None="{StaticResource NoneTemplate}"
Other="{StaticResource OtherTemplate}"
PDF="{StaticResource PDFTemplate}"
RarArchive="{StaticResource RarTemplate}"
Video="{StaticResource VideoTemplate}" />
</ResourceDictionary>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Last item must always be the default theme. -->
<ResourceDictionary Source="/AppThemes/Mica.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

334
Wino.Mail/App.xaml.cs Normal file
View File

@@ -0,0 +1,334 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.AppCenter.Crashes;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Core;
using Windows.ApplicationModel.Resources;
using Windows.Foundation.Metadata;
using Windows.Globalization;
using Windows.Storage;
using Windows.System.Profile;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Wino.Activation;
using Wino.Core;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Services;
using Wino.Core.UWP;
using Wino.Core.UWP.Services;
using Wino.Mail.ViewModels;
using Wino.Services;
namespace Wino
{
public sealed partial class App : Application
{
private const string WinoLaunchLogPrefix = "[Wino Launch] ";
private const string AppCenterKey = "90deb1d0-a77f-47d0-8a6b-7eaf111c6b72";
public new static App Current => (App)Application.Current;
public IServiceProvider Services { get; }
private readonly ILogInitializer _logInitializer;
private readonly IThemeService _themeService;
private readonly IDatabaseService _databaseService;
private readonly IAppInitializerService _appInitializerService;
private readonly IWinoSynchronizerFactory _synchronizerFactory;
private readonly ITranslationService _translationService;
// Order matters.
private List<IInitializeAsync> initializeServices => new List<IInitializeAsync>()
{
_translationService,
_databaseService,
_themeService,
_synchronizerFactory
};
public App()
{
InitializeComponent();
UnhandledException += OnAppUnhandledException;
EnteredBackground += OnEnteredBackground;
LeavingBackground += OnLeavingBackground;
Services = ConfigureServices();
_logInitializer = Services.GetService<ILogInitializer>();
ConfigureLogger();
ConfigureAppCenter();
ConfigurePrelaunch();
ConfigureXbox();
_themeService = Services.GetService<IThemeService>();
_databaseService = Services.GetService<IDatabaseService>();
_appInitializerService = Services.GetService<IAppInitializerService>();
_synchronizerFactory = Services.GetService<IWinoSynchronizerFactory>();
_translationService = Services.GetService<ITranslationService>();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
private void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}");
private void OnLeavingBackground(object sender, LeavingBackgroundEventArgs e) => LogActivation($"Wino went foreground.");
private void OnEnteredBackground(object sender, EnteredBackgroundEventArgs e) => LogActivation($"Wino went background.");
private IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.RegisterCoreServices();
services.RegisterCoreUWPServices();
RegisterUWPServices(services);
RegisterViewModels(services);
RegisterActivationHandlers(services);
return services.BuildServiceProvider();
}
#region Dependency Injection
private void RegisterActivationHandlers(IServiceCollection services)
{
services.AddTransient<ProtocolActivationHandler>();
services.AddTransient<BackgroundActivationHandler>();
services.AddTransient<ToastNotificationActivationHandler>();
services.AddTransient<FileActivationHandler>();
}
private void RegisterUWPServices(IServiceCollection services)
{
services.AddSingleton<IApplicationResourceManager<ResourceDictionary>, ApplicationResourceManager>();
services.AddSingleton<IThemeService, ThemeService>();
services.AddSingleton<IThemeService, ThemeService>();
services.AddSingleton<IPreferencesService, PreferencesService>();
services.AddSingleton<IStatePersistanceService, StatePersistenceService>();
services.AddSingleton<ILaunchProtocolService, LaunchProtocolService>();
services.AddSingleton<IWinoNavigationService, WinoNavigationService>();
services.AddSingleton<IDialogService, DialogService>();
}
private void RegisterViewModels(IServiceCollection services)
{
services.AddSingleton(typeof(AppShellViewModel));
services.AddTransient(typeof(SettingsDialogViewModel));
services.AddTransient(typeof(PersonalizationPageViewModel));
services.AddTransient(typeof(SettingOptionsPageViewModel));
services.AddTransient(typeof(MailListPageViewModel));
services.AddTransient(typeof(MailRenderingPageViewModel));
services.AddTransient(typeof(AccountManagementViewModel));
services.AddTransient(typeof(WelcomePageViewModel));
services.AddTransient(typeof(AboutPageViewModel));
services.AddTransient(typeof(ComposePageViewModel));
services.AddTransient(typeof(IdlePageViewModel));
services.AddTransient(typeof(SettingsPageViewModel));
services.AddTransient(typeof(NewAccountManagementPageViewModel));
services.AddTransient(typeof(AccountDetailsPageViewModel));
services.AddTransient(typeof(SignatureManagementPageViewModel));
services.AddTransient(typeof(MessageListPageViewModel));
services.AddTransient(typeof(ReadingPanePageViewModel));
services.AddTransient(typeof(MergedAccountDetailsPageViewModel));
}
#endregion
#region Misc Configuration
private void ConfigureLogger() => _logInitializer.SetupLogger(ApplicationData.Current.LocalFolder.Path);
private void ConfigureAppCenter() => AppCenter.Start(AppCenterKey, typeof(Analytics), typeof(Crashes));
private void ConfigurePrelaunch()
{
if (ApiInformation.IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", "EnablePrelaunch"))
CoreApplication.EnablePrelaunch(true);
}
private void ConfigureXbox()
{
// Xbox users should use Reveal focus.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 6))
{
FocusVisualKind = AnalyticsInfo.VersionInfo.DeviceFamily == "Xbox" ? FocusVisualKind.Reveal : FocusVisualKind.HighVisibility;
}
}
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;
}
#endregion
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
base.OnWindowCreated(args);
LogActivation("Window is created.");
ConfigureTitleBar();
}
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);
Log.Information($"File activation for {args.Files.Count} item(s).");
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);
}
protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
base.OnBackgroundActivated(args);
LogActivation($"OnBackgroundActivated -> {args.GetType().Name}, TaskInstanceIdName -> {args.TaskInstance?.Task?.Name ?? "NA"}");
await ActivateWinoAsync(args);
}
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);
}
private bool IsInteractiveLaunchArgs(object args) => args is IActivatedEventArgs;
private async Task ActivateWinoAsync(object args)
{
await PreInitializationAsync();
if (IsInteractiveLaunchArgs(args))
{
if (Window.Current.Content == null)
{
var mainFrame = new Frame();
Window.Current.Content = mainFrame;
await _themeService.InitializeAsync();
}
}
await HandleActivationAsync(args);
if (IsInteractiveLaunchArgs(args))
{
Window.Current.Activate();
LogActivation("Window activated");
}
}
/// <summary>
/// Tasks that must run before the activation and launch.
/// Regardless of whether it's an interactive launch or not.
/// </summary>
private async Task PreInitializationAsync()
{
// Handle migrations.
// TODO: Automate migration process with more proper way.
if (!ApplicationData.Current.LocalSettings.Values.ContainsKey("Migration_169"))
{
try
{
await _appInitializerService.MigrateAsync();
}
catch (Exception ex)
{
Log.Error(ex, $"{WinoLaunchLogPrefix}Migration_169 failed.");
}
finally
{
ApplicationData.Current.LocalSettings.Values["Migration_169"] = true;
}
}
foreach (var service in initializeServices)
{
await service.InitializeAsync();
}
}
private async Task HandleActivationAsync(object activationArgs)
{
var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs));
if (activationHandler != null)
{
await activationHandler.HandleAsync(activationArgs);
}
if (IsInteractiveLaunchArgs(activationArgs))
{
var defaultHandler = new DefaultActivationHandler();
if (defaultHandler.CanHandle(activationArgs))
{
await defaultHandler.HandleAsync(activationArgs);
}
}
}
private IEnumerable<ActivationHandler> GetActivationHandlers()
{
yield return Services.GetService<ProtocolActivationHandler>();
yield return Services.GetService<BackgroundActivationHandler>();
yield return Services.GetService<ToastNotificationActivationHandler>();
yield return Services.GetService<FileActivationHandler>();
}
}
}

613
Wino.Mail/AppShell.xaml Normal file

File diff suppressed because one or more lines are too long

303
Wino.Mail/AppShell.xaml.cs Normal file
View File

@@ -0,0 +1,303 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
using Wino.Controls;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.Messages.Accounts;
using Wino.Core.Messages.Mails;
using Wino.Core.Messages.Shell;
using Wino.Extensions;
using Wino.Mail.ViewModels.Data;
using Wino.MenuFlyouts;
using Wino.MenuFlyouts.Context;
using Wino.Views.Abstract;
namespace Wino.Views
{
public sealed partial class AppShell : AppShellAbstract,
IRecipient<AccountMenuItemExtended>,
IRecipient<NavigateMailFolderEvent>,
IRecipient<CreateNewMailWithMultipleAccountsRequested>,
IRecipient<InfoBarMessageRequested>
{
public AppShell() : base()
{
InitializeComponent();
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.LayoutMetricsChanged += TitleBarLayoutUpdated;
}
private void TitleBarLayoutUpdated(CoreApplicationViewTitleBar sender, object args) => UpdateTitleBarLayout(sender);
private void UpdateTitleBarLayout(CoreApplicationViewTitleBar coreTitleBar) => RealAppBar.SystemReserved = coreTitleBar.SystemOverlayRightInset;
private async void ItemDroppedOnFolder(object sender, DragEventArgs e)
{
// Validate package content.
if (sender is WinoNavigationViewItem droppedContainer)
{
droppedContainer.IsDraggingItemOver = false;
if (CanContinueDragDrop(droppedContainer, e))
{
if (droppedContainer.DataContext is IBaseFolderMenuItem draggingFolder)
{
var mailCopies = new List<MailCopy>();
var dragPackage = e.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
// Extract mail copies from IMailItem.
// ThreadViewModels will be divided into pieces.
foreach (var item in dragPackage.DraggingMails)
{
if (item is MailItemViewModel singleMailItemViewModel)
{
mailCopies.Add(singleMailItemViewModel.MailCopy);
}
else if (item is ThreadMailItemViewModel threadViewModel)
{
mailCopies.AddRange(threadViewModel.GetMailCopies());
}
}
await ViewModel.PerformMoveOperationAsync(mailCopies, draggingFolder);
}
}
}
}
private void ItemDragLeaveFromFolder(object sender, DragEventArgs e)
{
if (sender is WinoNavigationViewItem leavingContainer)
{
leavingContainer.IsDraggingItemOver = false;
}
}
private bool CanContinueDragDrop(WinoNavigationViewItem interactingContainer, DragEventArgs args)
{
// TODO: Maybe override caption with some information why the validation failed?
// Note: Caption has a max length. It may be trimmed in some languages.
if (interactingContainer == null || !args.DataView.Properties.ContainsKey(nameof(MailDragPackage))) return false;
var dragPackage = args.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
// Invalid package.
if (!dragPackage.DraggingMails.Any()) return false;
// Check whether source and target folder are the same.
if (interactingContainer.IsSelected) return false;
// Check if the interacting container is a folder.
if (!(interactingContainer.DataContext is IBaseFolderMenuItem folderMenuItem)) return false;
// Check if the folder is a move target.
if (!folderMenuItem.IsMoveTarget) return false;
// Check whether the moving item's account has at least one same as the target folder's account.
var draggedAccountIds = folderMenuItem.HandlingFolders.Select(a => a.MailAccountId);
if (!dragPackage.DraggingMails.Any(a => draggedAccountIds.Contains(a.AssignedAccount.Id))) return false;
return true;
}
private void ItemDragEnterOnFolder(object sender, DragEventArgs e)
{
// Validate package content.
if (sender is WinoNavigationViewItem droppedContainer && CanContinueDragDrop(droppedContainer, e))
{
droppedContainer.IsDraggingItemOver = true;
var draggingFolder = droppedContainer.DataContext as IBaseFolderMenuItem;
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
e.DragUIOverride.Caption = string.Format(Translator.DragMoveToFolderCaption, draggingFolder.FolderName);
}
}
public async void Receive(AccountMenuItemExtended message)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () =>
{
if (message.FolderId == default) return;
var menuItem = ViewModel.MenuItems.GetFolderItem(message.FolderId);
if (menuItem == null) return;
menuItem.Expand();
await ViewModel.NavigateFolderAsync(menuItem);
navigationView.SelectedItem = menuItem;
if (message.NavigateMailItem == null) return;
// At this point folder is navigated and items are loaded.
WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId));
});
}
private async void MenuSelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
{
if (args.SelectedItem is IMenuItem invokedMenuItem)
{
await ViewModel.MenuItemInvokedOrSelectedAsync(invokedMenuItem);
}
}
private async void NavigationViewItemInvoked(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs args)
{
// SelectsOnInvoked is handled in MenuSelectionChanged.
// This part is only for the items that are not selectable.
if (args.InvokedItemContainer is WinoNavigationViewItem winoNavigationViewItem)
{
if (winoNavigationViewItem.SelectsOnInvoked) return;
await ViewModel.MenuItemInvokedOrSelectedAsync(winoNavigationViewItem.DataContext as IMenuItem);
}
}
public void Receive(NavigateMailFolderEvent message)
{
if (message.BaseFolderMenuItem == null) return;
if (navigationView.SelectedItem != message.BaseFolderMenuItem)
{
var navigateFolderArgs = new NavigateMailFolderEventArgs(message.BaseFolderMenuItem, message.FolderInitLoadAwaitTask);
ViewModel.NavigationService.NavigateFolder(navigateFolderArgs);
// Prevent double navigation.
navigationView.SelectionChanged -= MenuSelectionChanged;
navigationView.SelectedItem = message.BaseFolderMenuItem;
navigationView.SelectionChanged += MenuSelectionChanged;
}
else
{
// Complete the init task since we are already on the right page.
message.FolderInitLoadAwaitTask?.TrySetResult(true);
}
}
private void ShellFrameContentNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
=> RealAppBar.ShellFrameContent = (e.Content as BasePage).ShellContent;
private void BackButtonClicked(Controls.Advanced.WinoAppTitleBar sender, RoutedEventArgs args)
{
WeakReferenceMessenger.Default.Send(new ClearMailSelectionsRequested());
}
private void OnShellLoaded(object sender, RoutedEventArgs e)
{
}
private async void MenuItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
{
// Delegate this request to ViewModel.
// VM will prepare available actions for this folder and show Menu Flyout.
if (sender is WinoNavigationViewItem menuItem &&
menuItem.DataContext is IBaseFolderMenuItem baseFolderMenuItem &&
baseFolderMenuItem.IsMoveTarget &&
args.TryGetPosition(sender, out Point p))
{
args.Handled = true;
var source = new TaskCompletionSource<FolderOperationMenuItem>();
var actions = ViewModel.GetFolderContextMenuActions(baseFolderMenuItem);
var flyout = new FolderOperationFlyout(actions, source);
flyout.ShowAt(menuItem, new FlyoutShowOptions()
{
ShowMode = FlyoutShowMode.Standard,
Position = new Point(p.X + 30, p.Y - 20)
});
var operation = await source.Task;
flyout.Dispose();
// No action selected.
if (operation == null) return;
await ViewModel.PerformFolderOperationAsync(operation.Operation, baseFolderMenuItem);
}
}
public void Receive(CreateNewMailWithMultipleAccountsRequested message)
{
// Find the NewMail menu item container.
var container = navigationView.ContainerFromMenuItem(ViewModel.CreateMailMenuItem);
var flyout = new AccountSelectorFlyout(message.AllAccounts, ViewModel.CreateNewMailForAsync);
flyout.ShowAt(container, new FlyoutShowOptions()
{
ShowMode = FlyoutShowMode.Auto,
Placement = FlyoutPlacementMode.Right
});
}
private void NavigationPaneOpening(Microsoft.UI.Xaml.Controls.NavigationView sender, object args)
{
// It's annoying that NavigationView doesn't respect expansion state of the items in Minimal display mode.
// Expanded items are collaped, and users need to expand them again.
// Regardless of the reason, we will expand the selected item if it's a folder with parent account for visibility.
if (sender.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal && sender.SelectedItem is IFolderMenuItem selectedFolderMenuItem)
{
selectedFolderMenuItem.Expand();
}
}
/// <summary>
/// InfoBar message is requested.
/// </summary>
public async void Receive(InfoBarMessageRequested message)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
if (string.IsNullOrEmpty(message.ActionButtonTitle) || message.Action == null)
{
ShellInfoBar.ActionButton = null;
}
else
{
ShellInfoBar.ActionButton = new Button()
{
Content = message.ActionButtonTitle,
Command = new RelayCommand(message.Action)
};
}
ShellInfoBar.Message = message.Message;
ShellInfoBar.Title = message.Title;
ShellInfoBar.Severity = message.Severity.AsMUXCInfoBarSeverity();
ShellInfoBar.IsOpen = true;
});
}
}
}

View File

@@ -0,0 +1,43 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="using:Microsoft.UI.Xaml.Media"
xmlns:xaml="using:Windows.UI.Xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">Acrylic</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<Thickness x:Key="RendererGridMargin">0,0,4,0</Thickness>
<xaml:CornerRadius x:Key="MailListGridCornerRadius">6,6,6,6</xaml:CornerRadius>
<!-- Acrylic Template -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Reading Pane Background -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">white</SolidColorBrush>
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
<local:AcrylicBrush
x:Key="WinoApplicationBackgroundColor"
TintColor="#FCFCFC"
TintOpacity="0.75"
FallbackColor="#F9F9F9"
BackgroundSource="HostBackdrop" />
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">Transparent</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#2C2C2C</SolidColorBrush>
<local:AcrylicBrush
x:Key="WinoApplicationBackgroundColor"
TintColor="#2C2C2C"
TintOpacity="0.30"
FallbackColor="#2C2C2C"
BackgroundSource="HostBackdrop" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,27 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xaml="using:Windows.UI.Xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">Clouds</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/Clouds.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<Thickness x:Key="RendererGridMargin">4,0,4,0</Thickness>
<xaml:CornerRadius x:Key="MailListGridCornerRadius">6,6,6,6</xaml:CornerRadius>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#FFFFFF</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3262626</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,53 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xaml="using:Windows.UI.Xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">Custom</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appdata:///local/CustomWallpaper.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush
x:Key="WinoApplicationBackgroundColor"
Stretch="UniformToFill"
ImageSource="{StaticResource ThemeBackgroundImage}" />
<Thickness x:Key="RendererGridMargin">0,0,0,0</Thickness>
<xaml:CornerRadius x:Key="MailListGridCornerRadius">0,6,6,0</xaml:CornerRadius>
<!-- Navigation View Settings -->
<xaml:CornerRadius x:Key="NavigationViewContentGridCornerRadius">0,0,0,0</xaml:CornerRadius>
<xaml:CornerRadius x:Key="OverlayCornerRadius">0,1,0,0</xaml:CornerRadius>
<Thickness x:Key="NavigationViewContentMargin">0,0,0,0</Thickness>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
<Color x:Key="MainCustomThemeColor">#D9FFFFFF</Color>
<!-- Reading Pane Background -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="AppBarBackgroundColor" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewDefaultPaneBackground" Color="{StaticResource MainCustomThemeColor}" />
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush>
<Color x:Key="MainCustomThemeColor">#E61F1F1F</Color>
<!-- Reading Pane Background -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="AppBarBackgroundColor" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent" />
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground" Color="{StaticResource MainCustomThemeColor}" />
<SolidColorBrush x:Key="NavigationViewDefaultPaneBackground" Color="{StaticResource MainCustomThemeColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,27 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xaml="using:Windows.UI.Xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">Forest</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/Forest.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<Thickness x:Key="RendererGridMargin">4,0,4,0</Thickness>
<xaml:CornerRadius x:Key="MailListGridCornerRadius">6,6,6,6</xaml:CornerRadius>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#2900F00A</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#A800D608</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3262626</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#59001C01</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,27 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xaml="using:Windows.UI.Xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">Garden</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/Garden.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<Thickness x:Key="RendererGridMargin">4,0,4,0</Thickness>
<xaml:CornerRadius x:Key="MailListGridCornerRadius">6,6,6,6</xaml:CornerRadius>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#FFFFFF</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3262626</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,28 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="using:Windows.UI.Xaml">
<x:String x:Key="ThemeName">Mica</x:String>
<x:Boolean x:Key="UseMica">True</x:Boolean>
<SolidColorBrush x:Key="WinoApplicationBackgroundColor">Transparent</SolidColorBrush>
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<Thickness x:Key="RendererGridMargin">0,0,4,0</Thickness>
<xaml:CornerRadius x:Key="MailListGridCornerRadius">6,6,6,6</xaml:CornerRadius>
<!-- Mica Template -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Reading Pane Background -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">white</SolidColorBrush>
<!-- Reading Page Date/Name Group Header Background -->
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#ecf0f1</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">Transparent</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#1f1f1f</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,27 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xaml="using:Windows.UI.Xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">Nighty</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/Nighty.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<Thickness x:Key="RendererGridMargin">4,0,4,0</Thickness>
<xaml:CornerRadius x:Key="MailListGridCornerRadius">6,6,6,6</xaml:CornerRadius>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#fdcb6e</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3262626</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,27 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:xaml="using:Windows.UI.Xaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">Snowflake</x:String>
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/Snowflake.jpg</x:String>
<x:Boolean x:Key="UseMica">False</x:Boolean>
<ImageBrush x:Key="WinoApplicationBackgroundColor" ImageSource="{StaticResource ThemeBackgroundImage}" />
<SolidColorBrush x:Key="AppBarBackgroundColor">Transparent</SolidColorBrush>
<Thickness x:Key="RendererGridMargin">4,0,4,0</Thickness>
<xaml:CornerRadius x:Key="MailListGridCornerRadius">6,6,6,6</xaml:CornerRadius>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#FFFFFF</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Brushes -->
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3262626</SolidColorBrush>
<SolidColorBrush x:Key="MailListHeaderBackgroundColor">#5413191F</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,22 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="ThemeName">TestTheme.xaml</x:String>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Light">
<!-- Background Image -->
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/bg6.jpg</x:String>
<SolidColorBrush x:Key="ShellTitleBarBackgroundColorBrush">#A3FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="ShellNavigationViewBackgroundColorBrush">#A3FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#fdcb6e</SolidColorBrush>
</ResourceDictionary>
<ResourceDictionary x:Name="Dark">
<!-- Background Image -->
<x:String x:Key="ThemeBackgroundImage">ms-appx:///BackgroundImages/bg6.jpg</x:String>
<SolidColorBrush x:Key="ShellTitleBarBackgroundColorBrush">#A3000000</SolidColorBrush>
<SolidColorBrush x:Key="ShellNavigationViewBackgroundColorBrush">#A3000000</SolidColorBrush>
<SolidColorBrush x:Key="ReadingPaneBackgroundColorBrush">#A3262626</SolidColorBrush>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,11 @@
# 🚀 Good job on building Wino Mail from the source 🙂
## Once we finish pre-release testing, I will add the 1.7.0 patch notes here.
### Right now, it's pretty lonely here. 🤷‍♂️
- Make sure you read the contributuion guidelines before you start diving into the code. ⚠️
- You may find some redundant methods, interfaces, classes, etc. that are not used in the project. Please ignore them for now 🥺. I will clean them up in the future.
- If you want to disable the unlimited account check, go to AccountManagementViewModel.cs and update the Free Account Count property at the top 😁
- If you have any questions, feel free to ask me on [Twitter 🐦](https://twitter.com/TrayhopeR) or project's Discord channels.
- Good luck! 🍀

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

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