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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +