diff --git a/Wino.Mail.WinUI/Wino.Mail.WinUI.csproj b/Wino.Mail.WinUI/Wino.Mail.WinUI.csproj
index 4ac4ae89..94d25b84 100644
--- a/Wino.Mail.WinUI/Wino.Mail.WinUI.csproj
+++ b/Wino.Mail.WinUI/Wino.Mail.WinUI.csproj
@@ -11,25 +11,26 @@
win-$(Platform).pubxml
true
true
- 10.0.19041.35-preview
- True
- False
- True
- False
- SHA256
- False
- C:\Users\bkaan\Desktop\Packages\WinUI\
- True
- True
- Always
- x86|x64|arm64
- 0
+ 10.0.19041.35-preview
+ True
+ False
+ True
+ False
+ SHA256
+ False
+ C:\Users\bkaan\Desktop\Packages\WinUI\
+ True
+ True
+ Always
+ x86|x64|arm64
+ 0
+
diff --git a/Wino.Packaging/Wino.Packaging.wapproj b/Wino.Packaging/Wino.Packaging.wapproj
index 3779c414..0d5d5472 100644
--- a/Wino.Packaging/Wino.Packaging.wapproj
+++ b/Wino.Packaging/Wino.Packaging.wapproj
@@ -56,7 +56,6 @@
en-US
false
$(NoWarn);NU1702
- ..\Wino.Mail\Wino.Mail.csproj
False
SHA256
False
@@ -65,6 +64,7 @@
x64
True
0
+ ..\Wino.Mail.WinUI\Wino.Mail.WinUI.csproj
Always
@@ -157,10 +157,10 @@
-
+
-
-
+
+
\ No newline at end of file
diff --git a/Wino.Server.NET8/App.xaml b/Wino.Server.NET8/App.xaml
index 69096344..5058d6b7 100644
--- a/Wino.Server.NET8/App.xaml
+++ b/Wino.Server.NET8/App.xaml
@@ -1,16 +1,16 @@
-
+
+ xmlns:local="using:Wino.Server.NET8"
+ xmlns:styles="using:Wino.Server.Styles">
-
+
-
diff --git a/Wino.Server.NET8/App.xaml.cs b/Wino.Server.NET8/App.xaml.cs
index 0c435100..1b6b410d 100644
--- a/Wino.Server.NET8/App.xaml.cs
+++ b/Wino.Server.NET8/App.xaml.cs
@@ -1,32 +1,87 @@
-using H.NotifyIcon;
+using System;
+using System.Threading.Tasks;
+using H.NotifyIcon;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
+using Windows.Storage;
+using Wino.Core;
+using Wino.Core.Domain.Interfaces;
+using Wino.Core.Services;
+using Wino.Core.UWP.Services;
+using Wino.Services;
namespace Wino.Server.NET8
{
public partial class App : Application
{
- public TaskbarIcon? TrayIcon { get; private set; }
- public Window? Window { get; set; }
+ public new static App Current => (App)Application.Current;
+ private const string WinoServerAppName = "Wino.Server";
+
+ public TaskbarIcon TrayIcon { get; private set; }
public bool HandleClosedEvents { get; set; } = true;
+ public IServiceProvider Services { get; private set; }
public App()
{
InitializeComponent();
}
- protected override void OnLaunched(LaunchActivatedEventArgs args)
+ private IServiceProvider ConfigureServices()
{
+ var services = new ServiceCollection();
+ services.AddTransient();
+ services.AddTransient();
+
+ services.RegisterCoreServices();
+
+ // Below services belongs to UWP.Core package and some APIs are not available for WPF.
+ // We register them here to avoid compilation errors.
+
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+ return services.BuildServiceProvider();
+ }
+
+ protected override async void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ Services = ConfigureServices();
+
+ await InitializeNewServerAsync();
+ InitializeTrayIcon();
+ }
+
+ private async Task InitializeNewServerAsync()
+ {
+ // TODO: Error handling.
+
+ var databaseService = Services.GetService();
+ var applicationFolderConfiguration = Services.GetService();
+
+ applicationFolderConfiguration.ApplicationDataFolderPath = ApplicationData.Current.LocalFolder.Path;
+ applicationFolderConfiguration.PublisherSharedFolderPath = ApplicationData.Current.GetPublisherCacheFolder(ApplicationConfiguration.SharedFolderName).Path;
+
+ await databaseService.InitializeAsync();
+
+ var serverViewModel = Services.GetRequiredService();
+
+ await serverViewModel.InitializeAsync();
+
+ return serverViewModel;
}
private void InitializeTrayIcon()
{
- var showHideWindowCommand = (XamlUICommand)Resources["ShowHideWindowCommand"];
- // showHideWindowCommand.ExecuteRequested ;
+ var viewModel = Services.GetService();
- var exitApplicationCommand = (XamlUICommand)Resources["ExitApplicationCommand"];
- //exitApplicationCommand.ExecuteRequested += ExitApplicationCommand_ExecuteRequested;
+ var launchCommand = (XamlUICommand)Resources["LaunchCommand"];
+ launchCommand.Command = viewModel.LaunchWinoCommand;
+
+ var exitApplicationCommand = (XamlUICommand)Resources["TerminateCommand"];
+ exitApplicationCommand.Command = viewModel.ExitApplicationCommand;
TrayIcon = (TaskbarIcon)Resources["TrayIcon"];
TrayIcon.ForceCreate();
diff --git a/Wino.Server.NET8/ServerContext.cs b/Wino.Server.NET8/ServerContext.cs
new file mode 100644
index 00000000..5471c501
--- /dev/null
+++ b/Wino.Server.NET8/ServerContext.cs
@@ -0,0 +1,217 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.Messaging;
+using Microsoft.Extensions.DependencyInjection;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.AppService;
+using Windows.Foundation.Collections;
+using Wino.Core.Authenticators;
+using Wino.Core.Domain.Interfaces;
+using Wino.Core.Domain.Models.Synchronization;
+using Wino.Core.Integration.Processors;
+using Wino.Core.Services;
+using Wino.Core.Synchronizers;
+using Wino.Messaging;
+using Wino.Messaging.Enums;
+using Wino.Messaging.Server;
+using Wino.Server.NET8;
+
+namespace Wino.Server
+{
+ public class ServerContext :
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient,
+ IRecipient
+ {
+ private static object connectionLock = new object();
+
+ private AppServiceConnection connection = null;
+
+ private readonly IDatabaseService _databaseService;
+ private readonly IApplicationConfiguration _applicationFolderConfiguration;
+
+ public ServerContext(IDatabaseService databaseService, IApplicationConfiguration applicationFolderConfiguration)
+ {
+ _databaseService = databaseService;
+ _applicationFolderConfiguration = applicationFolderConfiguration;
+
+ WeakReferenceMessenger.Default.RegisterAll(this);
+ }
+
+ #region Message Handlers
+
+ public async void Receive(MailAddedMessage message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(AccountCreatedMessage message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(AccountUpdatedMessage message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(AccountRemovedMessage message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(DraftCreated message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(DraftFailed message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(DraftMapped message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(FolderRenamed message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(FolderSynchronizationEnabled message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(MailDownloadedMessage message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(MailRemovedMessage message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(MailUpdatedMessage message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ public async void Receive(MergedInboxRenamed message) => await SendMessageAsync(MessageType.UIMessage, message);
+
+ #endregion
+ ///
+ /// Open connection to UWP app service
+ ///
+ public async Task InitializeAppServiceConnectionAsync()
+ {
+ if (connection != null) DisposeConnection();
+
+ connection = new AppServiceConnection
+ {
+ AppServiceName = "WinoInteropService",
+ PackageFamilyName = GetAppPackagFamilyName()
+ };
+
+ connection.RequestReceived += OnWinRTMessageReceived;
+ connection.ServiceClosed += OnConnectionClosed;
+
+ AppServiceConnectionStatus status = await connection.OpenAsync();
+
+ if (status != AppServiceConnectionStatus.Success)
+ {
+ // TODO: Handle connection error
+
+ DisposeConnection();
+ }
+ }
+
+ public async Task TestOutlookSynchronizer()
+ {
+ var accountService = App.Current.Services.GetService();
+
+ var accs = await accountService.GetAccountsAsync();
+ var acc = accs.ElementAt(0);
+
+ var authenticator = App.Current.Services.GetService();
+ var processor = App.Current.Services.GetService();
+
+ var sync = new OutlookSynchronizer(acc, authenticator, processor);
+
+ var options = new SynchronizationOptions()
+ {
+ AccountId = acc.Id,
+ Type = Core.Domain.Enums.SynchronizationType.Full
+ };
+
+ var result = await sync.SynchronizeAsync(options);
+ }
+
+ ///
+ /// Disposes current connection to UWP app service.
+ ///
+ private void DisposeConnection()
+ {
+ lock (connectionLock)
+ {
+ if (connection == null) return;
+
+ connection.RequestReceived -= OnWinRTMessageReceived;
+ connection.ServiceClosed -= OnConnectionClosed;
+
+ connection.Dispose();
+ connection = null;
+ }
+ }
+
+ ///
+ /// Sends a serialized object to UWP application if connection exists with given type.
+ ///
+ /// Type of the message.
+ /// IServerMessage object that will be serialized.
+ ///
+ /// When the message is not IServerMessage.
+ private async Task SendMessageAsync(MessageType messageType, object message)
+ {
+ if (connection == null) return;
+
+ if (message is not IServerMessage serverMessage)
+ throw new ArgumentException("Server message must be a type of IServerMessage");
+
+ string json = JsonSerializer.Serialize(message);
+
+ var set = new ValueSet
+ {
+ { MessageConstants.MessageTypeKey, (int)messageType },
+ { MessageConstants.MessageDataKey, json },
+ { MessageConstants.MessageDataTypeKey, message.GetType().Name }
+ };
+
+ Debug.WriteLine($"S: {messageType} ({message.GetType().Name})");
+ await connection.SendMessageAsync(set);
+ }
+
+ private void OnConnectionClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
+ {
+ // TODO: Handle connection closed.
+
+ // UWP app might've been terminated or suspended.
+ // At this point, we must keep active synchronizations going, but connection is lost.
+ // As long as this process is alive, database will be kept updated, but no messages will be sent.
+
+ DisposeConnection();
+ }
+
+ private void OnWinRTMessageReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
+ {
+ // TODO: Handle incoming messages from UWP/WINUI Application.
+
+ }
+
+ #region Init
+
+ private string GetAppPackagFamilyName()
+ {
+ // If running as a standalone app, Package will throw exception.
+ // Return hardcoded value for debugging purposes.
+ // Connection will not be available in this case.
+
+ try
+ {
+ return Package.Current.Id.FamilyName;
+ }
+ catch (Exception)
+ {
+ return "Debug.Wino.Server.FamilyName";
+ }
+ }
+
+ public async Task InitializeAsync()
+ {
+
+ await InitializeAppServiceConnectionAsync();
+ }
+
+ #endregion
+ }
+}
diff --git a/Wino.Server.NET8/ServerViewModel.cs b/Wino.Server.NET8/ServerViewModel.cs
new file mode 100644
index 00000000..c6bbacb0
--- /dev/null
+++ b/Wino.Server.NET8/ServerViewModel.cs
@@ -0,0 +1,37 @@
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Wino.Core.Domain.Interfaces;
+
+namespace Wino.Server
+{
+ public partial class ServerViewModel : ObservableObject, IInitializeAsync
+ {
+ public ServerContext Context { get; }
+
+ public ServerViewModel(ServerContext serverContext)
+ {
+ Context = serverContext;
+ }
+
+ [RelayCommand]
+ public async Task LaunchWinoAsync()
+ {
+ await Context.TestOutlookSynchronizer();
+ // ServerContext.SendTestMessageAsync();
+ }
+
+ ///
+ /// Shuts down the application.
+ ///
+ [RelayCommand]
+ public void ExitApplication()
+ {
+ // TODO: App service send message to UWP app to terminate itself.
+ }
+
+ public async Task ReconnectAsync() => await Context.InitializeAppServiceConnectionAsync();
+
+ public Task InitializeAsync() => Context.InitializeAppServiceConnectionAsync();
+ }
+}
diff --git a/Wino.Server.NET8/TrayIconResources.xaml b/Wino.Server.NET8/Styles/TrayIconResources.xaml
similarity index 86%
rename from Wino.Server.NET8/TrayIconResources.xaml
rename to Wino.Server.NET8/Styles/TrayIconResources.xaml
index b42183ca..f3adbab7 100644
--- a/Wino.Server.NET8/TrayIconResources.xaml
+++ b/Wino.Server.NET8/Styles/TrayIconResources.xaml
@@ -1,13 +1,12 @@
+ xmlns:local="using:Wino.Server"
+ xmlns:tb="using:H.NotifyIcon">
-
+ IconSource="\Assets\Wino_Icon.ico">
diff --git a/Wino.Server.NET8/Styles/TrayIconResources.xaml.cs b/Wino.Server.NET8/Styles/TrayIconResources.xaml.cs
new file mode 100644
index 00000000..07d5ffca
--- /dev/null
+++ b/Wino.Server.NET8/Styles/TrayIconResources.xaml.cs
@@ -0,0 +1,12 @@
+using Microsoft.UI.Xaml;
+
+namespace Wino.Server.Styles
+{
+ partial class TrayIconResources : ResourceDictionary
+ {
+ public TrayIconResources()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Wino.Server.NET8/TrayIconResources.xaml.cs b/Wino.Server.NET8/TrayIconResources.xaml.cs
deleted file mode 100644
index 59014a5c..00000000
--- a/Wino.Server.NET8/TrayIconResources.xaml.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.UI.Xaml;
-
-namespace Wino.Server
-{
- partial class TrayIconResources : ResourceDictionary
- {
- public TrayIconResources()
- {
- this.InitializeComponent();
- }
- }
-}
diff --git a/Wino.Server.NET8/Wino.Server.NET8.csproj b/Wino.Server.NET8/Wino.Server.NET8.csproj
index b1ec355e..7a31aadc 100644
--- a/Wino.Server.NET8/Wino.Server.NET8.csproj
+++ b/Wino.Server.NET8/Wino.Server.NET8.csproj
@@ -6,11 +6,13 @@
Wino.Server
app.manifest
x86;x64;ARM64
+ true
win-x86;win-x64;win-arm64
win10-x86;win10-x64;win10-arm64
win-$(Platform).pubxml
true
true
+ false
@@ -34,12 +36,13 @@
-
+
+
+
-
-
+
@@ -86,6 +89,10 @@
MSBuild:Compile
+
+
+
+