diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..b70e16b
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "src/Miunie"]
+ path = src/Miunie
+ url = git@github.com:control-net/Miunie.git
diff --git a/src/Miunie b/src/Miunie
new file mode 160000
index 0000000..9e3e575
--- /dev/null
+++ b/src/Miunie
@@ -0,0 +1 @@
+Subproject commit 9e3e575a72df76f23f5ab4889321813f4a0ae5cd
diff --git a/src/Miunie.WindowsApp/App.xaml b/src/Miunie.WindowsApp/App.xaml
new file mode 100644
index 0000000..be622f9
--- /dev/null
+++ b/src/Miunie.WindowsApp/App.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Miunie.WindowsApp/App.xaml.cs b/src/Miunie.WindowsApp/App.xaml.cs
new file mode 100644
index 0000000..8304fb4
--- /dev/null
+++ b/src/Miunie.WindowsApp/App.xaml.cs
@@ -0,0 +1,141 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight.Threading;
+using Miunie.WindowsApp.Views;
+using System;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.Activation;
+using Windows.ApplicationModel.Core;
+using Windows.Foundation;
+using Windows.UI.Popups;
+using Windows.UI.ViewManagement;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Navigation;
+
+namespace Miunie.WindowsApp
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ public sealed partial class App : Application
+ {
+ ///
+ /// Initializes a new instance of the class.
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ InitializeComponent();
+ Suspending += OnSuspending;
+ UnhandledException += App_UnhandledException;
+ }
+
+ ///
+ /// Invoked when the application is launched normally by the end user. Other entry points
+ /// will be used such as when the application is launched to open a specific file.
+ ///
+ /// Details about the launch request and process.
+ protected override void OnLaunched(LaunchActivatedEventArgs e)
+ {
+ // Set preferred application window min size
+ ApplicationView.GetForCurrentView().SetPreferredMinSize(new Size(500, 400));
+
+ // Do not repeat app initialization when the Window already has content,
+ // just ensure that the window is active
+ if (!(Window.Current.Content is Frame rootFrame))
+ {
+ // Create a Frame to act as the navigation context and navigate to the first page
+ rootFrame = new Frame();
+
+ rootFrame.NavigationFailed += OnNavigationFailed;
+
+ if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
+ {
+ // TODO: Load state from previously suspended application
+ }
+
+ // Place the frame in the current Window
+ Window.Current.Content = rootFrame;
+ }
+
+ if (e.PrelaunchActivated == false)
+ {
+ if (rootFrame.Content == null)
+ {
+ // When the navigation stack isn't restored navigate to the first page,
+ // configuring the new page by passing required information as a navigation
+ // parameter
+ _ = rootFrame.Navigate(typeof(StartPage), e.Arguments);
+ }
+
+ // Ensure the current window is active
+ Window.Current.Activate();
+ }
+
+ DispatcherHelper.Initialize();
+ }
+
+ private async void App_UnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
+ {
+ e.Handled = true;
+ System.Diagnostics.Debug.WriteLine(e.Exception);
+
+ var confirmRestart = new ContentDialog
+ {
+ Title = "Unexpected Error",
+ Content = "An unexpected error occurred, would you like to restart Miunie, or ignore this problem?",
+ PrimaryButtonText = "Yes, restart Miunie",
+ CloseButtonText = "Ignore and continue"
+ };
+
+ var confirmResult = await confirmRestart.ShowAsync();
+
+ if (confirmResult != ContentDialogResult.Primary) { return; }
+
+ var restartResult = await CoreApplication.RequestRestartAsync($"Restart on Error: {e.Message}");
+
+ var failureAlert = new MessageDialog("Restart Failed", restartResult.ToString());
+ _ = await failureAlert.ShowAsync();
+ }
+
+ ///
+ /// Invoked when Navigation to a certain page fails.
+ ///
+ /// The Frame which failed navigation.
+ /// Details about the navigation failure.
+ private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
+ {
+ throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
+ }
+
+ ///
+ /// Invoked when application execution is being suspended. Application state is saved
+ /// without knowing whether the application will be terminated or resumed with the contents
+ /// of memory still intact.
+ ///
+ /// The source of the suspend request.
+ /// Details about the suspend request.
+ private void OnSuspending(object sender, SuspendingEventArgs e)
+ {
+ var deferral = e.SuspendingOperation.GetDeferral();
+
+ // TODO: Save application state and stop any background activity
+ deferral.Complete();
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Assets/LargeTile.scale-100.png b/src/Miunie.WindowsApp/Assets/LargeTile.scale-100.png
new file mode 100644
index 0000000..d9a367f
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/LargeTile.scale-100.png differ
diff --git a/src/Miunie.WindowsApp/Assets/LargeTile.scale-125.png b/src/Miunie.WindowsApp/Assets/LargeTile.scale-125.png
new file mode 100644
index 0000000..4bab801
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/LargeTile.scale-125.png differ
diff --git a/src/Miunie.WindowsApp/Assets/LargeTile.scale-150.png b/src/Miunie.WindowsApp/Assets/LargeTile.scale-150.png
new file mode 100644
index 0000000..8f9abc8
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/LargeTile.scale-150.png differ
diff --git a/src/Miunie.WindowsApp/Assets/LargeTile.scale-200.png b/src/Miunie.WindowsApp/Assets/LargeTile.scale-200.png
new file mode 100644
index 0000000..8a31425
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/LargeTile.scale-200.png differ
diff --git a/src/Miunie.WindowsApp/Assets/LargeTile.scale-400.png b/src/Miunie.WindowsApp/Assets/LargeTile.scale-400.png
new file mode 100644
index 0000000..b04a27f
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/LargeTile.scale-400.png differ
diff --git a/src/Miunie.WindowsApp/Assets/SmallTile.scale-100.png b/src/Miunie.WindowsApp/Assets/SmallTile.scale-100.png
new file mode 100644
index 0000000..d9bd19a
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/SmallTile.scale-100.png differ
diff --git a/src/Miunie.WindowsApp/Assets/SmallTile.scale-125.png b/src/Miunie.WindowsApp/Assets/SmallTile.scale-125.png
new file mode 100644
index 0000000..634da4f
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/SmallTile.scale-125.png differ
diff --git a/src/Miunie.WindowsApp/Assets/SmallTile.scale-150.png b/src/Miunie.WindowsApp/Assets/SmallTile.scale-150.png
new file mode 100644
index 0000000..9970786
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/SmallTile.scale-150.png differ
diff --git a/src/Miunie.WindowsApp/Assets/SmallTile.scale-200.png b/src/Miunie.WindowsApp/Assets/SmallTile.scale-200.png
new file mode 100644
index 0000000..a623011
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/SmallTile.scale-200.png differ
diff --git a/src/Miunie.WindowsApp/Assets/SmallTile.scale-400.png b/src/Miunie.WindowsApp/Assets/SmallTile.scale-400.png
new file mode 100644
index 0000000..bc8225e
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/SmallTile.scale-400.png differ
diff --git a/src/Miunie.WindowsApp/Assets/SplashScreen.scale-200.png b/src/Miunie.WindowsApp/Assets/SplashScreen.scale-200.png
new file mode 100644
index 0000000..36bfb84
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/SplashScreen.scale-200.png differ
diff --git a/src/Miunie.WindowsApp/Assets/SplashScreen.scale-400.png b/src/Miunie.WindowsApp/Assets/SplashScreen.scale-400.png
new file mode 100644
index 0000000..a9d9871
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/SplashScreen.scale-400.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-100.png b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-100.png
new file mode 100644
index 0000000..52fd16d
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-100.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-125.png b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-125.png
new file mode 100644
index 0000000..ba79078
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-125.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-150.png b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-150.png
new file mode 100644
index 0000000..bbe98a8
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-150.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-200.png b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..040d447
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-200.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-400.png b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-400.png
new file mode 100644
index 0000000..47edcf6
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square150x150Logo.scale-400.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png
new file mode 100644
index 0000000..8a03c80
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png
new file mode 100644
index 0000000..20edc4a
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png
new file mode 100644
index 0000000..f3a22d8
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png
new file mode 100644
index 0000000..657a8df
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png
new file mode 100644
index 0000000..7eeb020
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png
new file mode 100644
index 0000000..8a03c80
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png
new file mode 100644
index 0000000..f3a22d8
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png
new file mode 100644
index 0000000..657a8df
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png
new file mode 100644
index 0000000..7eeb020
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-100.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-100.png
new file mode 100644
index 0000000..7d22073
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-100.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-125.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-125.png
new file mode 100644
index 0000000..8161d53
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-125.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-150.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-150.png
new file mode 100644
index 0000000..13249b6
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-150.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-200.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..47d7fab
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-200.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-400.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-400.png
new file mode 100644
index 0000000..16e3156
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.scale-400.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-16.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-16.png
new file mode 100644
index 0000000..4000750
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-16.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-24.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-24.png
new file mode 100644
index 0000000..6db7067
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-24.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000..9c569cc
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-256.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-256.png
new file mode 100644
index 0000000..609d734
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-256.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-32.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-32.png
new file mode 100644
index 0000000..a8919f7
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-32.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-48.png b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-48.png
new file mode 100644
index 0000000..51c7f06
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Square44x44Logo.targetsize-48.png differ
diff --git a/src/Miunie.WindowsApp/Assets/StoreLogo.scale-100.png b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-100.png
new file mode 100644
index 0000000..923a3d7
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-100.png differ
diff --git a/src/Miunie.WindowsApp/Assets/StoreLogo.scale-125.png b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-125.png
new file mode 100644
index 0000000..f76dc80
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-125.png differ
diff --git a/src/Miunie.WindowsApp/Assets/StoreLogo.scale-150.png b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-150.png
new file mode 100644
index 0000000..7a63d92
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-150.png differ
diff --git a/src/Miunie.WindowsApp/Assets/StoreLogo.scale-200.png b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-200.png
new file mode 100644
index 0000000..52cbdd5
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-200.png differ
diff --git a/src/Miunie.WindowsApp/Assets/StoreLogo.scale-400.png b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-400.png
new file mode 100644
index 0000000..e6ec154
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/StoreLogo.scale-400.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-100.png b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-100.png
new file mode 100644
index 0000000..dae2da7
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-100.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-125.png b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-125.png
new file mode 100644
index 0000000..969c32e
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-125.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-150.png b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-150.png
new file mode 100644
index 0000000..479f53e
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-150.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-200.png b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000..426cbca
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-400.png b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-400.png
new file mode 100644
index 0000000..0416975
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/Wide310x150Logo.scale-400.png differ
diff --git a/src/Miunie.WindowsApp/Assets/bg-pattern-black.png b/src/Miunie.WindowsApp/Assets/bg-pattern-black.png
new file mode 100644
index 0000000..66abb3c
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/bg-pattern-black.png differ
diff --git a/src/Miunie.WindowsApp/Assets/bg-pattern-white.png b/src/Miunie.WindowsApp/Assets/bg-pattern-white.png
new file mode 100644
index 0000000..08a2843
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/bg-pattern-white.png differ
diff --git a/src/Miunie.WindowsApp/Assets/miunie-scarf-transparent.png b/src/Miunie.WindowsApp/Assets/miunie-scarf-transparent.png
new file mode 100644
index 0000000..fb713dd
Binary files /dev/null and b/src/Miunie.WindowsApp/Assets/miunie-scarf-transparent.png differ
diff --git a/src/Miunie.WindowsApp/Controls/NoContentControl.xaml b/src/Miunie.WindowsApp/Controls/NoContentControl.xaml
new file mode 100644
index 0000000..86094a0
--- /dev/null
+++ b/src/Miunie.WindowsApp/Controls/NoContentControl.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/src/Miunie.WindowsApp/Controls/NoContentControl.xaml.cs b/src/Miunie.WindowsApp/Controls/NoContentControl.xaml.cs
new file mode 100644
index 0000000..b572ebe
--- /dev/null
+++ b/src/Miunie.WindowsApp/Controls/NoContentControl.xaml.cs
@@ -0,0 +1,33 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using Windows.UI.Xaml.Controls;
+
+namespace Miunie.WindowsApp.Controls
+{
+ public sealed partial class NoContentControl : UserControl
+ {
+ public NoContentControl()
+ {
+ InitializeComponent();
+ }
+
+ public string Icon { get; set; }
+
+ public string Title { get; set; }
+
+ public string Description { get; set; }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Converters/IntToVisabilityConverter.cs b/src/Miunie.WindowsApp/Converters/IntToVisabilityConverter.cs
new file mode 100644
index 0000000..3b2433a
--- /dev/null
+++ b/src/Miunie.WindowsApp/Converters/IntToVisabilityConverter.cs
@@ -0,0 +1,39 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using System;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Data;
+
+namespace Miunie.WindowsApp.Converters
+{
+ public class IntToVisabilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if (value is int count)
+ {
+ value = count > 0 ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ return value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Converters/TokenMaskConverter.cs b/src/Miunie.WindowsApp/Converters/TokenMaskConverter.cs
new file mode 100644
index 0000000..5fab025
--- /dev/null
+++ b/src/Miunie.WindowsApp/Converters/TokenMaskConverter.cs
@@ -0,0 +1,44 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using System;
+using System.Linq;
+using Windows.UI.Xaml.Data;
+
+namespace Miunie.WindowsApp.Converters
+{
+ public class TokenMaskConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if (value is string token)
+ {
+ if (token == "[NOT CONFIGURED]")
+ {
+ return value;
+ }
+
+ value = $"{string.Join(string.Empty, token.Take(5))} ****************************** {string.Join(string.Empty, token.TakeLast(5))}";
+ }
+
+ return value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Infrastructure/UwpFileSystem.cs b/src/Miunie.WindowsApp/Infrastructure/UwpFileSystem.cs
new file mode 100644
index 0000000..ae2b62a
--- /dev/null
+++ b/src/Miunie.WindowsApp/Infrastructure/UwpFileSystem.cs
@@ -0,0 +1,31 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using Miunie.Core.Infrastructure;
+using Windows.Storage;
+
+namespace Miunie.WindowsApp.Infrastructure
+{
+ public class UwpFileSystem : IFileSystem
+ {
+ public UwpFileSystem()
+ {
+ var storageFolder = ApplicationData.Current.LocalFolder;
+ DataStoragePath = storageFolder.Path;
+ }
+
+ public string DataStoragePath { get; }
+ }
+}
diff --git a/src/Miunie.WindowsApp/InversionOfControl.cs b/src/Miunie.WindowsApp/InversionOfControl.cs
new file mode 100644
index 0000000..0420c5b
--- /dev/null
+++ b/src/Miunie.WindowsApp/InversionOfControl.cs
@@ -0,0 +1,52 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using Microsoft.Extensions.DependencyInjection;
+using Miunie.Core.Infrastructure;
+using Miunie.Core.Logging;
+using Miunie.Infrastructure.Logging;
+using Miunie.Infrastructure.OS;
+using Miunie.InversionOfControl;
+using Miunie.WindowsApp.Infrastructure;
+
+namespace Miunie.WindowsApp
+{
+ public static class InversionOfControl
+ {
+ private static ServiceProvider _provider;
+
+ public static ServiceProvider Provider => GetOrInitProvider();
+
+ private static ServiceProvider GetOrInitProvider()
+ {
+ if (_provider is null)
+ {
+ InitializeProvider();
+ }
+
+ return _provider;
+ }
+
+ private static void InitializeProvider()
+ => _provider = new ServiceCollection()
+ .AddSingleton()
+ .AddSingleton(s => s.GetRequiredService())
+ .AddSingleton(s => s.GetRequiredService())
+ .AddTransient()
+ .AddSingleton()
+ .AddMiunieTypes()
+ .BuildServiceProvider();
+ }
+}
diff --git a/src/Miunie.WindowsApp/Miunie.WindowsApp.csproj b/src/Miunie.WindowsApp/Miunie.WindowsApp.csproj
new file mode 100644
index 0000000..4067742
--- /dev/null
+++ b/src/Miunie.WindowsApp/Miunie.WindowsApp.csproj
@@ -0,0 +1,292 @@
+
+
+
+
+ Debug
+ x86
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}
+ AppContainerExe
+ Properties
+ Miunie.WindowsApp
+ Miunie.WindowsApp
+ en-US
+ UAP
+ 10.0.18362.0
+ 10.0.17134.0
+ 14
+ 512
+ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ true
+ Miunie.WindowsApp_TemporaryKey.pfx
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x86
+ false
+ prompt
+ true
+
+
+ bin\x86\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x86
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM
+ false
+ prompt
+ true
+
+
+ bin\ARM\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM64
+ false
+ prompt
+ true
+ true
+
+
+ bin\ARM64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM64
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x64
+ false
+ prompt
+ true
+
+
+ bin\x64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+ true
+ true
+
+
+ PackageReference
+
+
+
+ App.xaml
+
+
+ NoContentControl.xaml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ImpersonationChatPage.xaml
+
+
+ NotConnectedPage.xaml
+
+
+ ServersPage.xaml
+
+
+ SettingsPage.xaml
+
+
+ StartPage.xaml
+
+
+ StatusPage.xaml
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+ 2.2.0
+
+
+ 3.1.2
+
+
+ 6.2.9
+
+
+ 2.3.200213001
+
+
+ 5.4.1.1
+
+
+
+
+ {0E0150E2-6A28-4936-81B2-EB5A7349EFFF}
+ Miunie.Core
+
+
+ {9431ea7a-3676-4229-b8eb-650005c9734a}
+ Miunie.InversionOfControl
+
+
+ {f116c0fa-e325-45c4-9071-3080559a8822}
+ Miunie.Infrastructure
+
+
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+
+
+ 14.0
+
+
+
+
\ No newline at end of file
diff --git a/src/Miunie.WindowsApp/Models/ObservableImage.cs b/src/Miunie.WindowsApp/Models/ObservableImage.cs
new file mode 100644
index 0000000..5f47a75
--- /dev/null
+++ b/src/Miunie.WindowsApp/Models/ObservableImage.cs
@@ -0,0 +1,56 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight;
+
+namespace Miunie.WindowsApp.Models
+{
+ public class ObservableImage : ObservableObject
+ {
+ private string _proxyUrl;
+ private int? _width;
+ private int? _height;
+
+ public string ProxyUrl
+ {
+ get => _proxyUrl;
+ set
+ {
+ _proxyUrl = value;
+ RaisePropertyChanged(nameof(ProxyUrl));
+ }
+ }
+
+ public int? Width
+ {
+ get => _width;
+ set
+ {
+ _width = value;
+ RaisePropertyChanged(nameof(Width));
+ }
+ }
+
+ public int? Height
+ {
+ get => _height;
+ set
+ {
+ _height = value;
+ RaisePropertyChanged(nameof(Height));
+ }
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Models/ObservableMessageView.cs b/src/Miunie.WindowsApp/Models/ObservableMessageView.cs
new file mode 100644
index 0000000..74022d4
--- /dev/null
+++ b/src/Miunie.WindowsApp/Models/ObservableMessageView.cs
@@ -0,0 +1,85 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight;
+using System;
+using System.Collections.ObjectModel;
+
+namespace Miunie.WindowsApp.Models
+{
+ public class ObservableMessageView : ObservableObject
+ {
+ private string _authorName;
+ private string _authorAvatarUrl;
+ private string _content;
+ private DateTimeOffset _timeStamp;
+ private ObservableCollection _images;
+
+ public ObservableMessageView()
+ {
+ Images = new ObservableCollection();
+ }
+
+ public string AuthorName
+ {
+ get => _authorName;
+ set
+ {
+ _authorName = value;
+ RaisePropertyChanged(nameof(AuthorName));
+ }
+ }
+
+ public string AuthorAvatarUrl
+ {
+ get => _authorAvatarUrl;
+ set
+ {
+ _authorAvatarUrl = value;
+ RaisePropertyChanged(nameof(AuthorAvatarUrl));
+ }
+ }
+
+ public string Content
+ {
+ get => _content;
+ set
+ {
+ _content = value;
+ RaisePropertyChanged(nameof(Content));
+ }
+ }
+
+ public DateTimeOffset TimeStamp
+ {
+ get => _timeStamp;
+ set
+ {
+ _timeStamp = value;
+ RaisePropertyChanged(nameof(TimeStamp));
+ }
+ }
+
+ public ObservableCollection Images
+ {
+ get => _images;
+ set
+ {
+ _images = value;
+ RaisePropertyChanged(nameof(Images));
+ }
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Package.appxmanifest b/src/Miunie.WindowsApp/Package.appxmanifest
new file mode 100644
index 0000000..83e2ca9
--- /dev/null
+++ b/src/Miunie.WindowsApp/Package.appxmanifest
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+ Miunie
+ Spelos Software
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Miunie.WindowsApp/Properties/AssemblyInfo.cs b/src/Miunie.WindowsApp/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5c28f4d
--- /dev/null
+++ b/src/Miunie.WindowsApp/Properties/AssemblyInfo.cs
@@ -0,0 +1,43 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Miunie")]
+[assembly: AssemblyDescription("A community developed bot")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("discord-bot-tutorial")]
+[assembly: AssemblyProduct("Miunie")]
+[assembly: AssemblyCopyright("Copyright © 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: ComVisible(false)]
diff --git a/src/Miunie.WindowsApp/Properties/Default.rd.xml b/src/Miunie.WindowsApp/Properties/Default.rd.xml
new file mode 100644
index 0000000..af00722
--- /dev/null
+++ b/src/Miunie.WindowsApp/Properties/Default.rd.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Miunie.WindowsApp/Utilities/EnterKeyHelpers.cs b/src/Miunie.WindowsApp/Utilities/EnterKeyHelpers.cs
new file mode 100644
index 0000000..0abe4fd
--- /dev/null
+++ b/src/Miunie.WindowsApp/Utilities/EnterKeyHelpers.cs
@@ -0,0 +1,76 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using System.Windows.Input;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Data;
+
+namespace Miunie.WindowsApp.Utilities
+{
+ public static class EnterKeyHelpers
+ {
+ public static readonly DependencyProperty EnterKeyCommandProperty =
+ DependencyProperty.RegisterAttached(
+ "EnterKeyCommand",
+ typeof(ICommand),
+ typeof(EnterKeyHelpers),
+ new PropertyMetadata(null, OnEnterKeyCommandChanged));
+
+ public static ICommand GetEnterKeyCommand(DependencyObject target)
+ {
+ return (ICommand)target.GetValue(EnterKeyCommandProperty);
+ }
+
+ public static void SetEnterKeyCommand(DependencyObject target, ICommand value)
+ {
+ target.SetValue(EnterKeyCommandProperty, value);
+ }
+
+ private static void OnEnterKeyCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
+ {
+ ICommand command = (ICommand)e.NewValue;
+ FrameworkElement fe = (FrameworkElement)target;
+ Control control = (Control)target;
+ control.KeyUp += (s, args) =>
+ {
+ if (args.Key == Windows.System.VirtualKey.Enter)
+ {
+ if (control is TextBox textbox)
+ {
+ BindingExpression b = control.GetBindingExpression(TextBox.TextProperty);
+ if (b != null)
+ {
+ b.UpdateSource();
+ }
+
+ command.Execute(textbox.Text);
+ }
+
+ if (control is PasswordBox password)
+ {
+ BindingExpression b = control.GetBindingExpression(PasswordBox.PasswordProperty);
+ if (b != null)
+ {
+ b.UpdateSource();
+ }
+
+ command.Execute(password.Password);
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Utilities/TokenManager.cs b/src/Miunie.WindowsApp/Utilities/TokenManager.cs
new file mode 100644
index 0000000..8825701
--- /dev/null
+++ b/src/Miunie.WindowsApp/Utilities/TokenManager.cs
@@ -0,0 +1,45 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using Miunie.Core;
+using System.Linq;
+
+namespace Miunie.WindowsApp.Utilities
+{
+ public class TokenManager
+ {
+ public bool StringHasValidTokenStructure(string possibleToken)
+ => possibleToken.Length == 59 && possibleToken.ElementAt(24) == '.' && possibleToken.ElementAt(31) == '.';
+
+ public void ApplyToken(string token, MiunieBot miunie)
+ {
+ miunie.BotConfiguration.DiscordToken = token;
+
+ var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
+ localSettings.Values["MIUNIE_UWP_TOKEN"] = token;
+ }
+
+ public void LoadToken(MiunieBot miunie)
+ {
+ var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
+ var token = localSettings.Values["MIUNIE_UWP_TOKEN"]?.ToString();
+
+ if (!string.IsNullOrWhiteSpace(token))
+ {
+ miunie.BotConfiguration.DiscordToken = token;
+ }
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/ViewModels/ImpersonationChatPageViewModel.cs b/src/Miunie.WindowsApp/ViewModels/ImpersonationChatPageViewModel.cs
new file mode 100644
index 0000000..1f4e65d
--- /dev/null
+++ b/src/Miunie.WindowsApp/ViewModels/ImpersonationChatPageViewModel.cs
@@ -0,0 +1,197 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight;
+using GalaSoft.MvvmLight.Command;
+using GalaSoft.MvvmLight.Threading;
+using Miunie.Core;
+using Miunie.Core.Entities.Views;
+using Miunie.Core.Events;
+using Miunie.WindowsApp.Models;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Miunie.WindowsApp.ViewModels
+{
+ public class ImpersonationChatPageViewModel : ViewModelBase
+ {
+ private readonly MiunieBot _miunie;
+
+ private TextChannelView _selectedChannel;
+ private IEnumerable _channels;
+ private ObservableCollection _messages;
+ private string _messageText;
+ private ulong _currentGuildId;
+
+ public ImpersonationChatPageViewModel(MiunieBot miunie)
+ {
+ _miunie = miunie;
+ _channels = new List();
+ _messages = new ObservableCollection();
+ }
+
+ public TextChannelView SelectedChannel
+ {
+ get => _selectedChannel;
+ set
+ {
+ _selectedChannel = value;
+ RaisePropertyChanged(nameof(SelectedChannel));
+ RaisePropertyChanged(nameof(IsMessageTextboxEnabled));
+ RaisePropertyChanged(nameof(SendMessageInputPlaceholder));
+ LoadMessagesAsync();
+ }
+ }
+
+ public IEnumerable Channels
+ {
+ get => _channels;
+ set
+ {
+ _channels = value;
+ RaisePropertyChanged(nameof(Channels));
+ }
+ }
+
+ public ObservableCollection Messages
+ {
+ get => _messages;
+ set
+ {
+ _messages = value;
+ RaisePropertyChanged(nameof(Messages));
+ }
+ }
+
+ public string MessageText
+ {
+ get => _messageText;
+ set
+ {
+ _messageText = value;
+ RaisePropertyChanged(nameof(MessageText));
+ }
+ }
+
+ public Visibility ContentIsVisible => Channels.Count() > 0
+ ? Visibility.Visible
+ : Visibility.Collapsed;
+
+ public Visibility NoContentIsVisible => Channels.Count() <= 0
+ ? Visibility.Visible
+ : Visibility.Collapsed;
+
+ public bool IsMessageTextboxEnabled => _selectedChannel?.CanSendMessages ?? false;
+
+ public string SendMessageInputPlaceholder => _selectedChannel?.CanSendMessages ?? true ? "Type your message here." : "This channel is read only.";
+
+ public ICommand SendMessageCommand => new RelayCommand(SendMessageAsMiunieAsync, CanSendMessage);
+
+ internal void EnableCommands()
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(
+ async () =>
+ {
+ var enableCommandDialog = new ContentDialog
+ {
+ Title = "Enable Commands?",
+ Content = "Commands have been disabled while you were impersonating, would you like to re-enable them?",
+ PrimaryButtonText = "Sure",
+ CloseButtonText = "No, thanks"
+ };
+
+ if (await enableCommandDialog.ShowAsync() == ContentDialogResult.Primary)
+ {
+ _miunie.BotConfiguration.CommandsEnabled = true;
+ }
+ });
+ }
+
+ internal void DisableCommands()
+ {
+ _miunie.BotConfiguration.CommandsEnabled = false;
+ }
+
+ internal void CleanupHandlers()
+ {
+ _miunie.Impersonation.MessageReceived -= Client_MessageReceivedHandler;
+ _miunie.Impersonation.UnsubscribeForMessages();
+ }
+
+ internal void ConfigureMessagesSubscription()
+ {
+ _miunie.Impersonation.MessageReceived += Client_MessageReceivedHandler;
+ _miunie.Impersonation.SubscribeForMessages();
+ }
+
+ internal async Task FetchInfoAsync(ulong guildId)
+ {
+ _currentGuildId = guildId;
+ Channels = await _miunie.Impersonation.GetAvailableTextChannelsAsync(_currentGuildId);
+ }
+
+ private bool CanSendMessage(string arg)
+ {
+ return !string.IsNullOrWhiteSpace(_messageText) && _selectedChannel != null;
+ }
+
+ private async void SendMessageAsMiunieAsync(string message)
+ {
+ MessageText = string.Empty;
+ await _miunie.Impersonation.SendTextToChannelAsync(message, SelectedChannel.Id);
+ }
+
+ private async void LoadMessagesAsync()
+ {
+ Messages.Clear();
+
+ if (_selectedChannel != null)
+ {
+ var messages = await _miunie.Impersonation.GetMessagesFromTextChannelAsync(_currentGuildId, _selectedChannel.Id);
+
+ Messages = new ObservableCollection(messages
+ .OrderBy(x => x.TimeStamp)
+ .Select(ToObservableMessageView));
+ }
+ }
+
+ private void Client_MessageReceivedHandler(object sender, MessageReceivedEventArgs args)
+ {
+ var m = args.Message;
+
+ if (m.ChannelId == SelectedChannel?.Id)
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(() =>
+ {
+ Messages.Add(ToObservableMessageView(m));
+ });
+ }
+ }
+
+ private ObservableMessageView ToObservableMessageView(MessageView m) => new ObservableMessageView
+ {
+ AuthorAvatarUrl = m.AuthorAvatarUrl,
+ AuthorName = m.AuthorName,
+ Content = m.Content,
+ TimeStamp = m.TimeStamp
+ };
+ }
+}
diff --git a/src/Miunie.WindowsApp/ViewModels/NotConnectedPageViewModel.cs b/src/Miunie.WindowsApp/ViewModels/NotConnectedPageViewModel.cs
new file mode 100644
index 0000000..da4d17c
--- /dev/null
+++ b/src/Miunie.WindowsApp/ViewModels/NotConnectedPageViewModel.cs
@@ -0,0 +1,36 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight;
+using Miunie.Core;
+
+namespace Miunie.WindowsApp.ViewModels
+{
+ public class NotConnectedPageViewModel : ViewModelBase
+ {
+ private readonly MiunieBot _miunie;
+
+ public NotConnectedPageViewModel(MiunieBot miunie)
+ {
+ _miunie = miunie;
+ }
+
+ public bool StartIsEnabled =>
+ !string.IsNullOrWhiteSpace(_miunie.BotConfiguration.DiscordToken);
+
+ public string ConnectionStatus =>
+ _miunie.MiunieDiscord.ConnectionState.ToString();
+ }
+}
diff --git a/src/Miunie.WindowsApp/ViewModels/ServersPageViewModel.cs b/src/Miunie.WindowsApp/ViewModels/ServersPageViewModel.cs
new file mode 100644
index 0000000..59fc891
--- /dev/null
+++ b/src/Miunie.WindowsApp/ViewModels/ServersPageViewModel.cs
@@ -0,0 +1,44 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight;
+using Miunie.Core;
+using Miunie.Core.Entities.Views;
+using System.Collections.Generic;
+using System.Linq;
+using Windows.UI.Xaml;
+
+namespace Miunie.WindowsApp.ViewModels
+{
+ public class ServersPageViewModel : ViewModelBase
+ {
+ private readonly MiunieBot _miunie;
+
+ public ServersPageViewModel(MiunieBot miunie)
+ {
+ _miunie = miunie;
+ }
+
+ public IEnumerable AvailableGuilds => _miunie.Impersonation.GetAvailableGuilds();
+
+ public Visibility ContentIsVisible => AvailableGuilds.Count() > 0
+ ? Visibility.Visible
+ : Visibility.Collapsed;
+
+ public Visibility NoContentIsVisible => AvailableGuilds.Count() <= 0
+ ? Visibility.Visible
+ : Visibility.Collapsed;
+ }
+}
diff --git a/src/Miunie.WindowsApp/ViewModels/SettingsPageViewModel.cs b/src/Miunie.WindowsApp/ViewModels/SettingsPageViewModel.cs
new file mode 100644
index 0000000..9e2e782
--- /dev/null
+++ b/src/Miunie.WindowsApp/ViewModels/SettingsPageViewModel.cs
@@ -0,0 +1,101 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight;
+using GalaSoft.MvvmLight.Command;
+using GalaSoft.MvvmLight.Threading;
+using Miunie.Core;
+using Miunie.Core.Logging;
+using Miunie.WindowsApp.Utilities;
+using System;
+using System.Windows.Input;
+using Windows.UI.Xaml.Controls;
+
+namespace Miunie.WindowsApp.ViewModels
+{
+ public class SettingsPageViewModel : ViewModelBase
+ {
+ private const string DefaultAvatarUrl = "../Assets/miunie-scarf-transparent.png";
+
+ private readonly MiunieBot _miunie;
+ private readonly ILogReader _logReader;
+ private readonly TokenManager _tokenManager;
+
+ public SettingsPageViewModel(MiunieBot miunie, ILogReader logReader, TokenManager tokenManager)
+ {
+ _miunie = miunie;
+ _logReader = logReader;
+ _tokenManager = tokenManager;
+ _logReader.LogRecieved += OnLogRecieved;
+ }
+
+ public event EventHandler TokenApplied;
+
+ public string BotToken => _miunie.BotConfiguration.DiscordToken;
+
+ public string BotAvatar => _miunie.MiunieDiscord.GetBotAvatarUrl() ?? DefaultAvatarUrl;
+
+ public bool CommandsEnabled
+ {
+ get => _miunie.BotConfiguration.CommandsEnabled;
+ set
+ {
+ _miunie.BotConfiguration.CommandsEnabled = value;
+ RaisePropertyChanged(nameof(CommandsEnabled));
+ }
+ }
+
+ public string Logs => string.Join(Environment.NewLine, _logReader.RetrieveLogs(10));
+
+ public ICommand ApplyTokenCommand => new RelayCommand(ApplyToken, CanApplyToken);
+
+ private bool CanApplyToken(string arg)
+ {
+ return !string.IsNullOrEmpty(arg);
+ }
+
+ private async void ApplyToken(string token)
+ {
+ if (!_tokenManager.StringHasValidTokenStructure(token))
+ {
+ var possiblyWrongTokenDialog = new ContentDialog
+ {
+ Title = "That doesn't look like a token.",
+ Content = "The token you provided doesn't follow the basic token length and content structure.",
+ PrimaryButtonText = "Apply anyway",
+ CloseButtonText = "Cancel"
+ };
+
+ var result = await possiblyWrongTokenDialog.ShowAsync();
+
+ if (result != ContentDialogResult.Primary) { return; }
+ }
+
+ _tokenManager.ApplyToken(token, _miunie);
+ RaisePropertyChanged(nameof(BotToken));
+
+ TokenApplied?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void OnLogRecieved(object sender, EventArgs e)
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(
+ () =>
+ {
+ RaisePropertyChanged(nameof(Logs));
+ });
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/ViewModels/StartPageViewModel.cs b/src/Miunie.WindowsApp/ViewModels/StartPageViewModel.cs
new file mode 100644
index 0000000..66e2595
--- /dev/null
+++ b/src/Miunie.WindowsApp/ViewModels/StartPageViewModel.cs
@@ -0,0 +1,35 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight;
+using Miunie.Core.Entities;
+using Miunie.Core.Providers;
+using System;
+
+namespace Miunie.WindowsApp.ViewModels
+{
+ public class StartPageViewModel : ViewModelBase
+ {
+ private readonly ILanguageProvider _lang;
+
+ public StartPageViewModel(ILanguageProvider lang)
+ {
+ _lang = lang;
+ }
+
+ internal string MiunieAboutText =>
+ _lang.GetPhrase(PhraseKey.GPL3_NOTICE.ToString(), DateTime.Now, "https://github.com/control-net/Miunie");
+ }
+}
diff --git a/src/Miunie.WindowsApp/ViewModels/StatusPageViewModel.cs b/src/Miunie.WindowsApp/ViewModels/StatusPageViewModel.cs
new file mode 100644
index 0000000..cf45d40
--- /dev/null
+++ b/src/Miunie.WindowsApp/ViewModels/StatusPageViewModel.cs
@@ -0,0 +1,192 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight;
+using GalaSoft.MvvmLight.Command;
+using GalaSoft.MvvmLight.Threading;
+using Miunie.Core;
+using Miunie.Core.Entities;
+using Miunie.Core.Logging;
+using Miunie.WindowsApp.Utilities;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Windows.ApplicationModel.DataTransfer;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Miunie.WindowsApp.ViewModels
+{
+ public class StatusPageViewModel : ViewModelBase
+ {
+ private const string DefaultAvatarUrl = "../Assets/miunie-scarf-transparent.png";
+
+ private readonly MiunieBot _miunie;
+ private readonly TokenManager _tokenManager;
+ private readonly ILogWriter _logWriter;
+ private string _connectedStatus;
+ private string _errorMessage;
+
+ public StatusPageViewModel(MiunieBot miunie, TokenManager tokenManager, ILogWriter logWriter)
+ {
+ _miunie = miunie;
+ _tokenManager = tokenManager;
+ _logWriter = logWriter;
+ miunie.MiunieDiscord.ConnectionChanged += MiunieOnConnectionStateChanged;
+ ConnectionStatus = "Not connected";
+ _tokenManager.LoadToken(_miunie);
+ CheckForTokenInClipboard();
+ }
+
+ public ICommand ActionCommand => new RelayCommand(ToggleBotStart, CanToggleStart);
+
+ public bool IsConnecting => _miunie.MiunieDiscord.ConnectionState == ConnectionState.CONNECTING;
+
+ public string ConnectionStatus
+ {
+ get => _connectedStatus;
+ set
+ {
+ if (value == _connectedStatus)
+ {
+ return;
+ }
+
+ _connectedStatus = value;
+ RaisePropertyChanged(nameof(ConnectionStatus));
+ }
+ }
+
+ public string ErrorMessage
+ {
+ get => _errorMessage;
+ set
+ {
+ if (value == _errorMessage)
+ {
+ return;
+ }
+
+ _errorMessage = value;
+ RaisePropertyChanged(nameof(ErrorMessage));
+ }
+ }
+
+ public Action AvatarChanged { get; set; }
+
+ public Visibility ActionButtonIsVisible => _miunie.MiunieDiscord.ConnectionState != ConnectionState.CONNECTING
+ ? Visibility.Visible
+ : Visibility.Collapsed;
+
+ public Visibility ProgressBarIsVisible => _miunie.MiunieDiscord.ConnectionState == ConnectionState.CONNECTING
+ ? Visibility.Visible
+ : Visibility.Collapsed;
+
+ public Visibility SettingsButtonIsVisable => string.IsNullOrWhiteSpace(_miunie.BotConfiguration.DiscordToken)
+ ? Visibility.Visible
+ : Visibility.Collapsed;
+
+ public string BotAvatar => _miunie.MiunieDiscord.GetBotAvatarUrl() ?? DefaultAvatarUrl;
+
+ public string ActionButtonText => _miunie.MiunieDiscord.ConnectionState == ConnectionState.CONNECTED ? "Stop" : "Start";
+
+ public async void ToggleBotStart()
+ {
+ if (_miunie.MiunieDiscord.ConnectionState != ConnectionState.CONNECTED)
+ {
+ if (_miunie.BotConfiguration.DiscordToken == null)
+ {
+ ErrorMessage = "No key found, input your key inside Settings!";
+ return;
+ }
+
+ await _miunie?.StartAsync();
+ }
+ else
+ {
+ _miunie.Stop();
+ }
+
+ RaisePropertyChanged(nameof(ActionButtonText));
+ }
+
+ private bool CanToggleStart()
+ {
+ return !string.IsNullOrEmpty(_miunie.BotConfiguration.DiscordToken);
+ }
+
+ private async void CheckForTokenInClipboard()
+ {
+ if (!string.IsNullOrWhiteSpace(_miunie.BotConfiguration.DiscordToken))
+ {
+ RaisePropertyChanged(nameof(SettingsButtonIsVisable));
+ RaisePropertyChanged(nameof(ActionCommand));
+ return;
+ }
+
+ var possibleToken = await TryGetClipboardContents();
+
+ if (!_tokenManager.StringHasValidTokenStructure(possibleToken)) { return; }
+
+ var clipboardTokenDialog = new ContentDialog
+ {
+ Title = "Paste copied bot token?",
+ Content = "It looks like you have a bot token copied.\nDo you want to use it?",
+ PrimaryButtonText = "Sure",
+ CloseButtonText = "No, thanks"
+ };
+
+ if (await clipboardTokenDialog.ShowAsync() == ContentDialogResult.Primary)
+ {
+ _tokenManager.ApplyToken(possibleToken, _miunie);
+ RaisePropertyChanged(nameof(SettingsButtonIsVisable));
+ RaisePropertyChanged(nameof(ActionCommand));
+ }
+ }
+
+ private async Task TryGetClipboardContents()
+ {
+ try
+ {
+ var clipboardContent = Clipboard.GetContent();
+
+ if (!clipboardContent.AvailableFormats.Contains(StandardDataFormats.Text)) { return string.Empty; }
+
+ return await Clipboard.GetContent().GetTextAsync();
+ }
+ catch (Exception ex)
+ {
+ _logWriter.LogError($"Unable to query clipboard: {ex.Message}");
+ return string.Empty;
+ }
+ }
+
+ private void MiunieOnConnectionStateChanged(object sender, EventArgs e)
+ {
+ DispatcherHelper.CheckBeginInvokeOnUI(
+ () =>
+ {
+ ConnectionStatus = _miunie.MiunieDiscord.ConnectionState.ToString();
+ ErrorMessage = string.Empty;
+ RaisePropertyChanged(nameof(ActionButtonText));
+ RaisePropertyChanged(nameof(ActionButtonIsVisible));
+ RaisePropertyChanged(nameof(ProgressBarIsVisible));
+ RaisePropertyChanged(nameof(BotAvatar));
+ AvatarChanged?.Invoke();
+ });
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/ViewModels/ViewModelLocator.cs b/src/Miunie.WindowsApp/ViewModels/ViewModelLocator.cs
new file mode 100644
index 0000000..e962daa
--- /dev/null
+++ b/src/Miunie.WindowsApp/ViewModels/ViewModelLocator.cs
@@ -0,0 +1,74 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using CommonServiceLocator;
+using GalaSoft.MvvmLight;
+using GalaSoft.MvvmLight.Ioc;
+using GalaSoft.MvvmLight.Views;
+using Microsoft.Extensions.DependencyInjection;
+using Miunie.Core;
+using Miunie.Core.Logging;
+using Miunie.Core.Providers;
+using Miunie.WindowsApp.Utilities;
+
+namespace Miunie.WindowsApp.ViewModels
+{
+ public class ViewModelLocator
+ {
+ public ViewModelLocator()
+ {
+ ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
+ if (ViewModelBase.IsInDesignModeStatic)
+ {
+ // Create design time view services and models
+ }
+ else
+ {
+ // Create run time view services and models
+ }
+
+ SimpleIoc.Default.Register();
+ SimpleIoc.Default.Register();
+ SimpleIoc.Default.Register();
+ SimpleIoc.Default.Register();
+ SimpleIoc.Default.Register();
+ SimpleIoc.Default.Register();
+ SimpleIoc.Default.Register();
+ SimpleIoc.Default.Register(() => ActivatorUtilities.CreateInstance(InversionOfControl.Provider));
+ SimpleIoc.Default.Register(() => InversionOfControl.Provider.GetRequiredService());
+ SimpleIoc.Default.Register(() => InversionOfControl.Provider.GetRequiredService());
+ SimpleIoc.Default.Register(() => InversionOfControl.Provider.GetRequiredService());
+ SimpleIoc.Default.Register();
+ }
+
+ public StartPageViewModel StartPageInstance
+ => ServiceLocator.Current.GetInstance();
+
+ public StatusPageViewModel StatusPageInstance
+ => ServiceLocator.Current.GetInstance();
+
+ public SettingsPageViewModel SettingsPageInstance
+ => ServiceLocator.Current.GetInstance();
+
+ public ServersPageViewModel ServersPageInstance
+ => ServiceLocator.Current.GetInstance();
+
+ public ImpersonationChatPageViewModel ImpersonationChatPageInstance
+ => ServiceLocator.Current.GetInstance();
+
+ public NotConnectedPageViewModel NotConnectedPageInstance
+ => ServiceLocator.Current.GetInstance();
+ }
+}
diff --git a/src/Miunie.WindowsApp/Views/ImpersonationChatPage.xaml b/src/Miunie.WindowsApp/Views/ImpersonationChatPage.xaml
new file mode 100644
index 0000000..2d490de
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/ImpersonationChatPage.xaml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Miunie.WindowsApp/Views/ImpersonationChatPage.xaml.cs b/src/Miunie.WindowsApp/Views/ImpersonationChatPage.xaml.cs
new file mode 100644
index 0000000..6c6acb8
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/ImpersonationChatPage.xaml.cs
@@ -0,0 +1,53 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight.Ioc;
+using Miunie.WindowsApp.ViewModels;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Navigation;
+
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+namespace Miunie.WindowsApp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class ImpersonationChatPage : Page
+ {
+ private readonly ImpersonationChatPageViewModel _vm;
+
+ public ImpersonationChatPage()
+ {
+ InitializeComponent();
+ _vm = DataContext as ImpersonationChatPageViewModel;
+ }
+
+ protected override async void OnNavigatedTo(NavigationEventArgs e)
+ {
+ await _vm.FetchInfoAsync((ulong)e.Parameter);
+ _vm.ConfigureMessagesSubscription();
+ _vm.DisableCommands();
+ }
+
+ protected override void OnNavigatedFrom(NavigationEventArgs e)
+ {
+ _vm.CleanupHandlers();
+ _vm.EnableCommands();
+
+ SimpleIoc.Default.Unregister();
+ SimpleIoc.Default.Register();
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Views/NotConnectedPage.xaml b/src/Miunie.WindowsApp/Views/NotConnectedPage.xaml
new file mode 100644
index 0000000..aee346f
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/NotConnectedPage.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Miunie.WindowsApp/Views/NotConnectedPage.xaml.cs b/src/Miunie.WindowsApp/Views/NotConnectedPage.xaml.cs
new file mode 100644
index 0000000..4d0a744
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/NotConnectedPage.xaml.cs
@@ -0,0 +1,42 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+namespace Miunie.WindowsApp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class NotConnectedPage : Page
+ {
+ public NotConnectedPage()
+ {
+ InitializeComponent();
+ }
+
+ private void ConfigBtn_Click(object sender, RoutedEventArgs e)
+ {
+ _ = Frame.Navigate(typeof(SettingsPage), null);
+ }
+
+ private void StartBtn_Click(object sender, RoutedEventArgs e)
+ {
+ _ = Frame.Navigate(typeof(StatusPage), null);
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Views/ServersPage.xaml b/src/Miunie.WindowsApp/Views/ServersPage.xaml
new file mode 100644
index 0000000..ab52570
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/ServersPage.xaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Miunie.WindowsApp/Views/ServersPage.xaml.cs b/src/Miunie.WindowsApp/Views/ServersPage.xaml.cs
new file mode 100644
index 0000000..3031d10
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/ServersPage.xaml.cs
@@ -0,0 +1,42 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using Miunie.Core.Entities.Views;
+using Windows.UI.Xaml.Controls;
+
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+namespace Miunie.WindowsApp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class ServersPage : Page
+ {
+ public ServersPage()
+ {
+ InitializeComponent();
+ }
+
+ private void ListViewBase_OnItemClick(object sender, ItemClickEventArgs e)
+ {
+ if (!(e.ClickedItem is GuildView guild)) { return; }
+
+ // ConnectedAnimationService
+ // .GetForCurrentView()
+ // .PrepareToAnimate("ServerExpandAnimation", (UIElement) e.OriginalSource);
+ _ = Frame.Navigate(typeof(ImpersonationChatPage), guild.Id);
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Views/SettingsPage.xaml b/src/Miunie.WindowsApp/Views/SettingsPage.xaml
new file mode 100644
index 0000000..afafcbc
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/SettingsPage.xaml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Miunie.WindowsApp/Views/SettingsPage.xaml.cs b/src/Miunie.WindowsApp/Views/SettingsPage.xaml.cs
new file mode 100644
index 0000000..423f05a
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/SettingsPage.xaml.cs
@@ -0,0 +1,56 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using Miunie.WindowsApp.ViewModels;
+using System;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media.Animation;
+using Windows.UI.Xaml.Navigation;
+
+namespace Miunie.WindowsApp.Views
+{
+ public sealed partial class SettingsPage : Page
+ {
+ private readonly SettingsPageViewModel _vm;
+
+ public SettingsPage()
+ {
+ InitializeComponent();
+ _vm = DataContext as SettingsPageViewModel;
+ _vm.TokenApplied += TokenAppliedEventHandler;
+ }
+
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("MiunieStatusToSettings");
+ _ = animation?.TryStart(MiunieSettingsAvatar);
+ }
+
+ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
+ {
+ if (e.SourcePageType == typeof(StatusPage))
+ {
+ _ = ConnectedAnimationService
+ .GetForCurrentView()
+ .PrepareToAnimate("MiunieSettingsToStatus", MiunieSettingsAvatar);
+ }
+ }
+
+ private void TokenAppliedEventHandler(object sender, EventArgs e)
+ {
+ _ = Frame.Navigate(typeof(StatusPage), null);
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Views/StartPage.xaml b/src/Miunie.WindowsApp/Views/StartPage.xaml
new file mode 100644
index 0000000..a7f3d2e
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/StartPage.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Miunie.WindowsApp/Views/StartPage.xaml.cs b/src/Miunie.WindowsApp/Views/StartPage.xaml.cs
new file mode 100644
index 0000000..4f0c777
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/StartPage.xaml.cs
@@ -0,0 +1,112 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using GalaSoft.MvvmLight.Ioc;
+using Miunie.Core;
+using Miunie.Core.Entities;
+using Miunie.WindowsApp.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Windows.UI.Popups;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media.Animation;
+using muxc = Microsoft.UI.Xaml.Controls;
+
+namespace Miunie.WindowsApp.Views
+{
+ public sealed partial class StartPage : Page
+ {
+ private readonly List<(string Tag, Type Page)> _navigationPages = new List<(string Tag, Type Page)>
+ {
+ ("home", typeof(StatusPage)),
+ ("servers", typeof(ServersPage)),
+ ("notconnected", typeof(NotConnectedPage))
+ };
+
+ private readonly StartPageViewModel _vm;
+ private readonly MiunieBot _miunie;
+
+ public StartPage()
+ {
+ InitializeComponent();
+ _vm = DataContext as StartPageViewModel;
+ _miunie = SimpleIoc.Default.GetInstance();
+ }
+
+ private void MainNavigationView_OnLoaded(object sender, RoutedEventArgs e)
+ {
+ MainNavigationView.SelectedItem = MainNavigationView.MenuItems[0];
+ NavView_Navigate("home", new EntranceNavigationTransitionInfo());
+ }
+
+ private void MainNavigationView_OnItemInvoked(muxc.NavigationView sender, muxc.NavigationViewItemInvokedEventArgs args)
+ {
+ if (args.IsSettingsInvoked)
+ {
+ NavView_Navigate("settings", args.RecommendedNavigationTransitionInfo);
+ }
+ else if (args.InvokedItemContainer != null)
+ {
+ var navItemTag = args.InvokedItemContainer.Tag.ToString();
+ if (_miunie.MiunieDiscord.ConnectionState == ConnectionState.CONNECTED || navItemTag == "home")
+ {
+ NavView_Navigate(navItemTag, args.RecommendedNavigationTransitionInfo);
+ return;
+ }
+
+ NavView_Navigate("notconnected", args.RecommendedNavigationTransitionInfo);
+ }
+ }
+
+ private void NavView_Navigate(string navItemTag, NavigationTransitionInfo transitionInfo)
+ {
+ var newPage = GetPageTypeByNavItemTag(navItemTag);
+
+ if (newPage is null || MainFrame.CurrentSourcePageType == newPage) { return; }
+
+ _ = MainFrame.Navigate(newPage, null, transitionInfo);
+ }
+
+ private Type GetPageTypeByNavItemTag(string navItemTag)
+ {
+ Type newPage;
+
+ if (navItemTag == "settings")
+ {
+ newPage = typeof(SettingsPage);
+ }
+ else
+ {
+ var (_, type) = _navigationPages.FirstOrDefault(p => p.Tag.Equals(navItemTag));
+ newPage = type;
+ }
+
+ return newPage;
+ }
+
+ private async void AboutView_Navigate(object sender, TappedRoutedEventArgs e)
+ {
+ var aboutDialog = new MessageDialog(_vm.MiunieAboutText, "Miunie");
+
+ aboutDialog.Commands.Add(new UICommand("Close"));
+
+ aboutDialog.CancelCommandIndex = 0;
+ _ = await aboutDialog.ShowAsync();
+ }
+ }
+}
diff --git a/src/Miunie.WindowsApp/Views/StatusPage.xaml b/src/Miunie.WindowsApp/Views/StatusPage.xaml
new file mode 100644
index 0000000..7f3c60f
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/StatusPage.xaml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Miunie.WindowsApp/Views/StatusPage.xaml.cs b/src/Miunie.WindowsApp/Views/StatusPage.xaml.cs
new file mode 100644
index 0000000..0f41b41
--- /dev/null
+++ b/src/Miunie.WindowsApp/Views/StatusPage.xaml.cs
@@ -0,0 +1,74 @@
+// This file is part of Miunie.
+//
+// Miunie is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Miunie is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Miunie. If not, see .
+
+using Miunie.WindowsApp.ViewModels;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media.Animation;
+using Windows.UI.Xaml.Navigation;
+
+namespace Miunie.WindowsApp.Views
+{
+ public sealed partial class StatusPage : Page
+ {
+ private readonly StatusPageViewModel _vm;
+
+ public StatusPage()
+ {
+ InitializeComponent();
+ _vm = DataContext as StatusPageViewModel;
+ NavigationCacheMode = NavigationCacheMode.Enabled;
+ _vm.AvatarChanged = OnAvatarChanged;
+ }
+
+ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
+ {
+ if (e.SourcePageType == typeof(SettingsPage))
+ {
+ _ = ConnectedAnimationService
+ .GetForCurrentView()
+ .PrepareToAnimate("MiunieStatusToSettings", MiunieAvatar);
+ }
+ }
+
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ var animation = ConnectedAnimationService.GetForCurrentView().GetAnimation("MiunieSettingsToStatus");
+ if (animation is null) { return; }
+ _ = animation.TryStart(MiunieAvatar);
+ _vm.RaisePropertyChanged(nameof(_vm.SettingsButtonIsVisable));
+ }
+
+ private void OnAvatarChanged()
+ {
+ FadeOutAvatarStoryboard.Begin();
+
+ if (_vm.IsConnecting)
+ {
+ return;
+ }
+
+ FadeOutAvatarStoryboard.Completed += (object sbs, object sbe) =>
+ {
+ FadeInAvatarStoryboard.Begin();
+ };
+ }
+
+ private void SettingsBtn_OnClick(object sender, RoutedEventArgs e)
+ {
+ _ = Frame.Navigate(typeof(SettingsPage), null);
+ }
+ }
+}
diff --git a/src/MiunieUwp.sln b/src/MiunieUwp.sln
new file mode 100644
index 0000000..4ae3f0e
--- /dev/null
+++ b/src/MiunieUwp.sln
@@ -0,0 +1,55 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30413.136
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Miunie.WindowsApp", "Miunie.WindowsApp\Miunie.WindowsApp.csproj", "{1850BE21-FA48-4E67-8649-A39BAE6947A1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|ARM = Release|ARM
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|ARM.ActiveCfg = Debug|ARM
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|ARM.Build.0 = Debug|ARM
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|ARM.Deploy.0 = Debug|ARM
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|ARM64.Build.0 = Debug|ARM64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|x64.ActiveCfg = Debug|x64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|x64.Build.0 = Debug|x64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|x64.Deploy.0 = Debug|x64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|x86.ActiveCfg = Debug|x86
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|x86.Build.0 = Debug|x86
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Debug|x86.Deploy.0 = Debug|x86
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|Any CPU.ActiveCfg = Release|x86
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|ARM.ActiveCfg = Release|ARM
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|ARM.Build.0 = Release|ARM
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|ARM.Deploy.0 = Release|ARM
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|ARM64.ActiveCfg = Release|ARM64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|ARM64.Build.0 = Release|ARM64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|ARM64.Deploy.0 = Release|ARM64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|x64.ActiveCfg = Release|x64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|x64.Build.0 = Release|x64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|x64.Deploy.0 = Release|x64
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|x86.ActiveCfg = Release|x86
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|x86.Build.0 = Release|x86
+ {1850BE21-FA48-4E67-8649-A39BAE6947A1}.Release|x86.Deploy.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E161BD48-4855-412D-A444-DAC7D0DA98E0}
+ EndGlobalSection
+EndGlobal