diff --git a/Unige.sln b/Unige.sln
index 9de7b7e..35d8d71 100644
--- a/Unige.sln
+++ b/Unige.sln
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OgeSharp", "OgeSharp\OgeSha
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnigeWebUtility", "UnigeWebUtility\UnigeWebUtility.csproj", "{3568E75E-90CB-4A03-B2E3-D8F413ABF68E}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnigeSoftware", "UnigeSoftware\UnigeSoftware.csproj", "{991CA739-1B61-4D43-B7BF-88840944C08C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{3568E75E-90CB-4A03-B2E3-D8F413ABF68E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3568E75E-90CB-4A03-B2E3-D8F413ABF68E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3568E75E-90CB-4A03-B2E3-D8F413ABF68E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {991CA739-1B61-4D43-B7BF-88840944C08C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {991CA739-1B61-4D43-B7BF-88840944C08C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {991CA739-1B61-4D43-B7BF-88840944C08C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {991CA739-1B61-4D43-B7BF-88840944C08C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/UnigeSoftware/App.axaml b/UnigeSoftware/App.axaml
new file mode 100644
index 0000000..d94f412
--- /dev/null
+++ b/UnigeSoftware/App.axaml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/UnigeSoftware/App.axaml.cs b/UnigeSoftware/App.axaml.cs
new file mode 100644
index 0000000..6dc1881
--- /dev/null
+++ b/UnigeSoftware/App.axaml.cs
@@ -0,0 +1,21 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using UnigeSoftware.ViewModels;
+using UnigeSoftware.Views;
+
+namespace UnigeSoftware {
+ public class App : Application {
+ public override void Initialize() => AvaloniaXamlLoader.Load(this);
+
+ public override void OnFrameworkInitializationCompleted() {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
+ desktop.MainWindow = new MainWindow {
+ DataContext = new MainWindowViewModel(),
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+ }
+}
diff --git a/UnigeSoftware/Assets/avalonia-logo.ico b/UnigeSoftware/Assets/avalonia-logo.ico
new file mode 100644
index 0000000..da8d49f
Binary files /dev/null and b/UnigeSoftware/Assets/avalonia-logo.ico differ
diff --git a/UnigeSoftware/Assets/discord-mark-light.png b/UnigeSoftware/Assets/discord-mark-light.png
new file mode 100644
index 0000000..25d8f2b
Binary files /dev/null and b/UnigeSoftware/Assets/discord-mark-light.png differ
diff --git a/UnigeSoftware/Assets/github-mark-light.png b/UnigeSoftware/Assets/github-mark-light.png
new file mode 100644
index 0000000..ea6ff54
Binary files /dev/null and b/UnigeSoftware/Assets/github-mark-light.png differ
diff --git a/UnigeSoftware/FodyWeavers.xml b/UnigeSoftware/FodyWeavers.xml
new file mode 100644
index 0000000..63fc148
--- /dev/null
+++ b/UnigeSoftware/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/UnigeSoftware/Program.cs b/UnigeSoftware/Program.cs
new file mode 100644
index 0000000..871a896
--- /dev/null
+++ b/UnigeSoftware/Program.cs
@@ -0,0 +1,22 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.ReactiveUI;
+using System;
+
+namespace UnigeSoftware {
+ class Program {
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .LogToTrace()
+ .UseReactiveUI();
+ }
+}
diff --git a/UnigeSoftware/UnigeSoftware.csproj b/UnigeSoftware/UnigeSoftware.csproj
new file mode 100644
index 0000000..39bf9e3
--- /dev/null
+++ b/UnigeSoftware/UnigeSoftware.csproj
@@ -0,0 +1,32 @@
+
+
+ WinExe
+ net5.0
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UnigeSoftware/ViewLocator.cs b/UnigeSoftware/ViewLocator.cs
new file mode 100644
index 0000000..2a6025c
--- /dev/null
+++ b/UnigeSoftware/ViewLocator.cs
@@ -0,0 +1,22 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using ReactiveUI;
+using System;
+
+namespace UnigeSoftware {
+ public class ViewLocator : IDataTemplate {
+ public IControl Build(object data) {
+ var name = data.GetType().FullName!.Replace("ViewModel", "View");
+ var type = Type.GetType(name);
+
+ if (type != null) {
+ return (Control)Activator.CreateInstance(type)!;
+ } else {
+ return new TextBlock { Text = "Not Found: " + name };
+ }
+ }
+
+ public bool Match(object data) => data is ReactiveObject;
+
+ }
+}
diff --git a/UnigeSoftware/ViewModels/ConnectViewModel.cs b/UnigeSoftware/ViewModels/ConnectViewModel.cs
new file mode 100644
index 0000000..5ac784f
--- /dev/null
+++ b/UnigeSoftware/ViewModels/ConnectViewModel.cs
@@ -0,0 +1,63 @@
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+using ReactiveUI.Validation.Abstractions;
+using ReactiveUI.Validation.Contexts;
+using ReactiveUI.Validation.Extensions;
+using System;
+using System.Diagnostics;
+using System.Reactive;
+using System.Reactive.Linq;
+
+namespace UnigeSoftware.ViewModels {
+
+ public class ConnectViewModel : ReactiveObject, IValidatableViewModel {
+
+ private readonly MainWindowViewModel MainWindow;
+ public ValidationContext ValidationContext { get; } = new ValidationContext();
+
+ [Reactive] public string Username { get; set; }
+ [Reactive] public string Password { get; set; }
+
+ public ConnectViewModel(MainWindowViewModel mainWindow) {
+
+ // Save the main window view model
+ MainWindow = mainWindow;
+
+ // Create the commands
+ ConnectCommand = ReactiveCommand.CreateFromObservable(DoConnect);
+ OpenFromExplorerCommand = ReactiveCommand.Create(DoOpenWebpage);
+
+ // Username and password cannot be empty
+ this.ValidationRule(viewModel => viewModel.Username, username => !string.IsNullOrEmpty(username), "Le nom d'utilisateur ne peut pas être vide");
+ this.ValidationRule(viewModel => viewModel.Password, password => !string.IsNullOrEmpty(password), "Le mot de passe ne peut pas être vide");
+
+ }
+
+ #region Commands
+
+ public ReactiveCommand ConnectCommand { get; }
+ public ReactiveCommand OpenFromExplorerCommand { get; }
+
+ // TODO: Maybe not working on other OS
+ private void DoOpenWebpage(string url) => Process.Start("explorer", url);
+
+ private IObservable DoConnect() => Observable.Start(() => {
+
+ // If the fields are not validated
+ if (!ValidationContext.GetIsValid()) return;
+
+ // If the credentials are correct
+ if (MainWindow.Oge.Login(Username, Password)) {
+
+ // Tell the main window that we are connected
+ MainWindow.OnConnected();
+
+ }
+
+ });
+
+ #endregion
+
+ }
+
+}
diff --git a/UnigeSoftware/ViewModels/HomeViewModel.cs b/UnigeSoftware/ViewModels/HomeViewModel.cs
new file mode 100644
index 0000000..27cbee0
--- /dev/null
+++ b/UnigeSoftware/ViewModels/HomeViewModel.cs
@@ -0,0 +1,16 @@
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+
+namespace UnigeSoftware.ViewModels {
+
+ public class HomeViewModel : ReactiveObject {
+
+ [Reactive] public string Note { get; set; }
+
+ public HomeViewModel(MainWindowViewModel mainWindow) {
+
+ }
+
+ }
+
+}
diff --git a/UnigeSoftware/ViewModels/MainWindowViewModel.cs b/UnigeSoftware/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..edf91a1
--- /dev/null
+++ b/UnigeSoftware/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,22 @@
+using OgeSharp;
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+
+namespace UnigeSoftware.ViewModels {
+ public class MainWindowViewModel : ReactiveObject {
+
+ public Oge Oge = new Oge();
+
+ [Reactive] public ReactiveObject Content { get; set; }
+
+ public MainWindowViewModel() {
+
+ // Initialize the connect view model
+ Content = new ConnectViewModel(this);
+
+ }
+
+ public void OnConnected() => Content = new HomeViewModel(this);
+
+ }
+}
diff --git a/UnigeSoftware/Views/ConnectView.axaml b/UnigeSoftware/Views/ConnectView.axaml
new file mode 100644
index 0000000..1a754e1
--- /dev/null
+++ b/UnigeSoftware/Views/ConnectView.axaml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ Connexion à OGE
+
+
+ Nom d'utilisateur:
+
+
+
+
+ Mot de passe:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UnigeSoftware/Views/ConnectView.axaml.cs b/UnigeSoftware/Views/ConnectView.axaml.cs
new file mode 100644
index 0000000..7b2f2cf
--- /dev/null
+++ b/UnigeSoftware/Views/ConnectView.axaml.cs
@@ -0,0 +1,25 @@
+using Avalonia.ReactiveUI;
+using ReactiveUI;
+using ReactiveUI.Validation.Extensions;
+using System.Reactive.Disposables;
+using UnigeSoftware.ViewModels;
+
+namespace UnigeSoftware.Views {
+
+ public partial class ConnectView : ReactiveUserControl {
+
+ public ConnectView() {
+ InitializeComponent();
+
+ // Input validation
+ this.WhenActivated(disposables => {
+
+ this.BindValidation(ViewModel, viewModel => viewModel.Username, view => view.UsernameError.Text).DisposeWith(disposables);
+ this.BindValidation(ViewModel, viewModel => viewModel.Password, view => view.PasswordError.Text).DisposeWith(disposables);
+
+ });
+
+ }
+
+ }
+}
diff --git a/UnigeSoftware/Views/HomeView.axaml b/UnigeSoftware/Views/HomeView.axaml
new file mode 100644
index 0000000..677f926
--- /dev/null
+++ b/UnigeSoftware/Views/HomeView.axaml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/UnigeSoftware/Views/HomeView.axaml.cs b/UnigeSoftware/Views/HomeView.axaml.cs
new file mode 100644
index 0000000..90200ff
--- /dev/null
+++ b/UnigeSoftware/Views/HomeView.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace UnigeSoftware.Views {
+ public partial class HomeView : UserControl {
+
+ public HomeView() => InitializeComponent();
+ private void InitializeComponent() => AvaloniaXamlLoader.Load(this);
+
+ }
+}
diff --git a/UnigeSoftware/Views/MainWindow.axaml b/UnigeSoftware/Views/MainWindow.axaml
new file mode 100644
index 0000000..16f271d
--- /dev/null
+++ b/UnigeSoftware/Views/MainWindow.axaml
@@ -0,0 +1,20 @@
+
+
+
diff --git a/UnigeSoftware/Views/MainWindow.axaml.cs b/UnigeSoftware/Views/MainWindow.axaml.cs
new file mode 100644
index 0000000..1d3a687
--- /dev/null
+++ b/UnigeSoftware/Views/MainWindow.axaml.cs
@@ -0,0 +1,18 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace UnigeSoftware.Views {
+ public partial class MainWindow : Window {
+ public MainWindow() {
+ InitializeComponent();
+#if DEBUG
+ this.AttachDevTools();
+#endif
+ }
+
+ private void InitializeComponent() {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}