diff --git a/Sources/LeagueOfLegends.sln b/Sources/LeagueOfLegends.sln
index 2d87936..8a34ca4 100644
--- a/Sources/LeagueOfLegends.sln
+++ b/Sources/LeagueOfLegends.sln
@@ -1,83 +1,97 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.2.32616.157
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{2960F9BA-49DE-494D-92E3-CE5A794BA1A9}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C76D0C23-1FFA-4963-93CD-E12BD643F030}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTests", "Tests\ConsoleTests\ConsoleTests.csproj", "{1889FA6E-B7C6-416E-8628-9449FB9070B9}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{3B720C0C-53FE-4642-A2DB-87FD8634CD74}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stub", "Stub", "{2C607793-B163-4731-A4D1-AFE8A7C4C170}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StubLib", "StubLib\StubLib.csproj", "{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}"
- ProjectSection(ProjectDependencies) = postProject
- {3B720C0C-53FE-4642-A2DB-87FD8634CD74} = {3B720C0C-53FE-4642-A2DB-87FD8634CD74}
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "apiLOL", "apiLOL\apiLOL.csproj", "{65B824B5-FADB-4C89-8B4B-D541B62B7DCA}"
- ProjectSection(ProjectDependencies) = postProject
- {1889FA6E-B7C6-416E-8628-9449FB9070B9} = {1889FA6E-B7C6-416E-8628-9449FB9070B9}
- {2960F9BA-49DE-494D-92E3-CE5A794BA1A9} = {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}
- {3B720C0C-53FE-4642-A2DB-87FD8634CD74} = {3B720C0C-53FE-4642-A2DB-87FD8634CD74}
- {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB} = {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFLol", "EFLol\EFLol.csproj", "{7AEE66D2-490B-4049-B9D3-C629D7F78DA7}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUnitaire", "TestUnitaire\TestUnitaire.csproj", "{5702F240-97BD-4757-918C-6E485C57D0EC}"
- ProjectSection(ProjectDependencies) = postProject
- {65B824B5-FADB-4C89-8B4B-D541B62B7DCA} = {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}
- {7AEE66D2-490B-4049-B9D3-C629D7F78DA7} = {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}
- {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB} = {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}
- EndProjectSection
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Release|Any CPU.Build.0 = Release|Any CPU
- {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Release|Any CPU.Build.0 = Release|Any CPU
- {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Release|Any CPU.Build.0 = Release|Any CPU
- {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Release|Any CPU.Build.0 = Release|Any CPU
- {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}.Release|Any CPU.Build.0 = Release|Any CPU
- {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}.Release|Any CPU.Build.0 = Release|Any CPU
- {5702F240-97BD-4757-918C-6E485C57D0EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5702F240-97BD-4757-918C-6E485C57D0EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5702F240-97BD-4757-918C-6E485C57D0EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5702F240-97BD-4757-918C-6E485C57D0EC}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {1889FA6E-B7C6-416E-8628-9449FB9070B9} = {C76D0C23-1FFA-4963-93CD-E12BD643F030}
- {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB} = {2C607793-B163-4731-A4D1-AFE8A7C4C170}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {92F3083D-793F-4552-8A9A-0AD6534159C9}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32616.157
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{2960F9BA-49DE-494D-92E3-CE5A794BA1A9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C76D0C23-1FFA-4963-93CD-E12BD643F030}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTests", "Tests\ConsoleTests\ConsoleTests.csproj", "{1889FA6E-B7C6-416E-8628-9449FB9070B9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{3B720C0C-53FE-4642-A2DB-87FD8634CD74}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stub", "Stub", "{2C607793-B163-4731-A4D1-AFE8A7C4C170}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StubLib", "StubLib\StubLib.csproj", "{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {3B720C0C-53FE-4642-A2DB-87FD8634CD74} = {3B720C0C-53FE-4642-A2DB-87FD8634CD74}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "apiLOL", "apiLOL\apiLOL.csproj", "{65B824B5-FADB-4C89-8B4B-D541B62B7DCA}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1889FA6E-B7C6-416E-8628-9449FB9070B9} = {1889FA6E-B7C6-416E-8628-9449FB9070B9}
+ {2960F9BA-49DE-494D-92E3-CE5A794BA1A9} = {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}
+ {3B720C0C-53FE-4642-A2DB-87FD8634CD74} = {3B720C0C-53FE-4642-A2DB-87FD8634CD74}
+ {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB} = {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFLol", "EFLol\EFLol.csproj", "{7AEE66D2-490B-4049-B9D3-C629D7F78DA7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUnitaire", "TestUnitaire\TestUnitaire.csproj", "{5702F240-97BD-4757-918C-6E485C57D0EC}"
+ ProjectSection(ProjectDependencies) = postProject
+ {65B824B5-FADB-4C89-8B4B-D541B62B7DCA} = {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}
+ {7AEE66D2-490B-4049-B9D3-C629D7F78DA7} = {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}
+ {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB} = {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LolApp", "LolApp\LolApp.csproj", "{0C898A04-092A-49AA-BE65-8AE818A2AF50}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ViewModels", "ViewModels\ViewModels.csproj", "{0ECDCB33-39F0-444B-B787-2EFDD9422870}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {65B824B5-FADB-4C89-8B4B-D541B62B7DCA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7AEE66D2-490B-4049-B9D3-C629D7F78DA7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5702F240-97BD-4757-918C-6E485C57D0EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5702F240-97BD-4757-918C-6E485C57D0EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5702F240-97BD-4757-918C-6E485C57D0EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5702F240-97BD-4757-918C-6E485C57D0EC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0C898A04-092A-49AA-BE65-8AE818A2AF50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0C898A04-092A-49AA-BE65-8AE818A2AF50}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0C898A04-092A-49AA-BE65-8AE818A2AF50}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {0C898A04-092A-49AA-BE65-8AE818A2AF50}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0C898A04-092A-49AA-BE65-8AE818A2AF50}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0C898A04-092A-49AA-BE65-8AE818A2AF50}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {0ECDCB33-39F0-444B-B787-2EFDD9422870}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0ECDCB33-39F0-444B-B787-2EFDD9422870}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0ECDCB33-39F0-444B-B787-2EFDD9422870}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0ECDCB33-39F0-444B-B787-2EFDD9422870}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {1889FA6E-B7C6-416E-8628-9449FB9070B9} = {C76D0C23-1FFA-4963-93CD-E12BD643F030}
+ {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB} = {2C607793-B163-4731-A4D1-AFE8A7C4C170}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {92F3083D-793F-4552-8A9A-0AD6534159C9}
+ EndGlobalSection
+EndGlobal
diff --git a/Sources/LolApp/AddChampionPage.xaml b/Sources/LolApp/AddChampionPage.xaml
new file mode 100644
index 0000000..7f38acb
--- /dev/null
+++ b/Sources/LolApp/AddChampionPage.xaml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/AddChampionPage.xaml.cs b/Sources/LolApp/AddChampionPage.xaml.cs
new file mode 100644
index 0000000..8388605
--- /dev/null
+++ b/Sources/LolApp/AddChampionPage.xaml.cs
@@ -0,0 +1,13 @@
+using LolApp.ViewModels;
+using ViewModels;
+
+namespace LolApp;
+
+public partial class AddChampionPage : ContentPage
+{
+ public AddChampionPage(ChampionsMgrVM championsMgrVM, ChampionVM champion = null)
+ {
+ InitializeComponent();
+ BindingContext = new AddChampionPageVM(championsMgrVM, champion);
+ }
+}
diff --git a/Sources/LolApp/AddOrEditSkinPage.xaml b/Sources/LolApp/AddOrEditSkinPage.xaml
new file mode 100644
index 0000000..20071b4
--- /dev/null
+++ b/Sources/LolApp/AddOrEditSkinPage.xaml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/AddOrEditSkinPage.xaml.cs b/Sources/LolApp/AddOrEditSkinPage.xaml.cs
new file mode 100644
index 0000000..8c94ec0
--- /dev/null
+++ b/Sources/LolApp/AddOrEditSkinPage.xaml.cs
@@ -0,0 +1,24 @@
+using LolApp.ViewModels;
+using ViewModels;
+
+namespace LolApp;
+
+public partial class AddOrEditSkinPage : ContentPage
+{
+ AddOrEditSkinPage()
+ {
+ InitializeComponent();
+ }
+
+ public AddOrEditSkinPage(SkinsMgrVM skinsMgrVM, SkinVM skin)
+ :this()
+ {
+ BindingContext = new AddOrEditSkinPageVM(skinsMgrVM, skin);
+ }
+
+ public AddOrEditSkinPage(SkinsMgrVM skinsMgrVM, ChampionVM champion)
+ :this()
+ {
+ BindingContext = new AddOrEditSkinPageVM(skinsMgrVM, champion);
+ }
+}
diff --git a/Sources/LolApp/AddSkill.xaml b/Sources/LolApp/AddSkill.xaml
new file mode 100644
index 0000000..b6f9583
--- /dev/null
+++ b/Sources/LolApp/AddSkill.xaml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/AddSkill.xaml.cs b/Sources/LolApp/AddSkill.xaml.cs
new file mode 100644
index 0000000..b164003
--- /dev/null
+++ b/Sources/LolApp/AddSkill.xaml.cs
@@ -0,0 +1,13 @@
+using LolApp.ViewModels;
+using ViewModels;
+
+namespace LolApp;
+
+public partial class AddSkill : ContentPage
+{
+ public AddSkill(EditableChampionVM champion)
+ {
+ InitializeComponent();
+ BindingContext = new AddSkillVM(champion);
+ }
+}
diff --git a/Sources/LolApp/App.xaml b/Sources/LolApp/App.xaml
new file mode 100644
index 0000000..cdae088
--- /dev/null
+++ b/Sources/LolApp/App.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/App.xaml.cs b/Sources/LolApp/App.xaml.cs
new file mode 100644
index 0000000..276b46e
--- /dev/null
+++ b/Sources/LolApp/App.xaml.cs
@@ -0,0 +1,12 @@
+namespace LolApp;
+
+public partial class App : Application
+{
+ public App()
+ {
+ InitializeComponent();
+
+ MainPage = new AppShell();
+ }
+}
+
diff --git a/Sources/LolApp/AppShell.xaml b/Sources/LolApp/AppShell.xaml
new file mode 100644
index 0000000..a71d8ca
--- /dev/null
+++ b/Sources/LolApp/AppShell.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/AppShell.xaml.cs b/Sources/LolApp/AppShell.xaml.cs
new file mode 100644
index 0000000..bc552b6
--- /dev/null
+++ b/Sources/LolApp/AppShell.xaml.cs
@@ -0,0 +1,10 @@
+namespace LolApp;
+
+public partial class AppShell : Shell
+{
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+}
+
diff --git a/Sources/LolApp/ChampionPage.xaml b/Sources/LolApp/ChampionPage.xaml
new file mode 100644
index 0000000..b0d85f5
--- /dev/null
+++ b/Sources/LolApp/ChampionPage.xaml
@@ -0,0 +1,185 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 120
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/ChampionPage.xaml.cs b/Sources/LolApp/ChampionPage.xaml.cs
new file mode 100644
index 0000000..411e629
--- /dev/null
+++ b/Sources/LolApp/ChampionPage.xaml.cs
@@ -0,0 +1,31 @@
+using CommunityToolkit.Maui.Behaviors;
+using LolApp.ViewModels;
+using ViewModels;
+
+namespace LolApp;
+
+public partial class ChampionPage : ContentPage
+{
+ public ApplicationVM AppVM { get; set; }
+ public ChampionVM Champion { get; }
+
+ public ChampionPage(ChampionVM cvm, ApplicationVM appVM)
+ {
+ AppVM = appVM;
+ BindingContext = Champion = cvm;
+
+ InitializeComponent();
+ }
+
+ void imgClass_PropertyChanged(System.Object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ Image img = sender as Image;
+ if(e.PropertyName == "Source" && img != null && img.Behaviors.Any(b => b is IconTintColorBehavior))
+ {
+ var beh = (img.Behaviors.First(b => b is IconTintColorBehavior) as IconTintColorBehavior);
+ var color = beh.TintColor;
+ img.Behaviors.Remove(beh);
+ img.Behaviors.Add(new IconTintColorBehavior() { TintColor = color});
+ }
+ }
+}
diff --git a/Sources/LolApp/ChampionsPage.xaml b/Sources/LolApp/ChampionsPage.xaml
new file mode 100644
index 0000000..4e8b2c9
--- /dev/null
+++ b/Sources/LolApp/ChampionsPage.xaml
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/ChampionsPage.xaml.cs b/Sources/LolApp/ChampionsPage.xaml.cs
new file mode 100644
index 0000000..6aa38a7
--- /dev/null
+++ b/Sources/LolApp/ChampionsPage.xaml.cs
@@ -0,0 +1,17 @@
+using LolApp.ViewModels;
+using ViewModels;
+
+namespace LolApp;
+
+public partial class ChampionsPage : ContentPage
+{
+ public ApplicationVM AppVM { get; }
+ public ChampionsPageVM VM { get; }
+ public ChampionsPage(ApplicationVM appVM)
+ {
+ InitializeComponent();
+ AppVM = appVM;
+ VM = new ChampionsPageVM(AppVM.ChampionsMgrVM);
+ BindingContext = this;
+ }
+}
diff --git a/Sources/LolApp/ContentViews/ChampionClassSelector.xaml b/Sources/LolApp/ContentViews/ChampionClassSelector.xaml
new file mode 100644
index 0000000..6dc66e4
--- /dev/null
+++ b/Sources/LolApp/ContentViews/ChampionClassSelector.xaml
@@ -0,0 +1,131 @@
+
+
+
+
+ Assassin
+ Fighter
+ Mage
+ Marksman
+ Support
+ Tank
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/ContentViews/ChampionClassSelector.xaml.cs b/Sources/LolApp/ContentViews/ChampionClassSelector.xaml.cs
new file mode 100644
index 0000000..74fb508
--- /dev/null
+++ b/Sources/LolApp/ContentViews/ChampionClassSelector.xaml.cs
@@ -0,0 +1,34 @@
+using Model;
+
+namespace LolApp.ContentViews;
+
+public partial class ChampionClassSelector : ContentView
+{
+ public ChampionClassSelector()
+ {
+ InitializeComponent();
+ }
+
+ public static readonly BindableProperty SelectedValueProperty = BindableProperty.Create(nameof(SelectedValue), typeof(ChampionClass), typeof(ChampionClassSelector), ChampionClass.Unknown, BindingMode.TwoWay);
+ public ChampionClass SelectedValue
+ {
+ get => (ChampionClass)GetValue(SelectedValueProperty);
+ set => SetValue(SelectedValueProperty, value);
+ }
+
+ public static readonly BindableProperty CheckedColorProperty = BindableProperty.Create(nameof(CheckedColor), typeof(Color), typeof(ChampionClassSelector), Colors.DarkSalmon);
+
+ public Color CheckedColor
+ {
+ get => (Color)GetValue(CheckedColorProperty);
+ set => SetValue(CheckedColorProperty, value);
+ }
+
+ public static readonly BindableProperty UncheckedColorProperty = BindableProperty.Create(nameof(UncheckedColor), typeof(Color), typeof(ChampionClassSelector), Colors.DarkSalmon);
+
+ public Color UncheckedColor
+ {
+ get => (Color)GetValue(UncheckedColorProperty);
+ set => SetValue(UncheckedColorProperty, value);
+ }
+}
diff --git a/Sources/LolApp/ContentViews/SearchByStringView.cs b/Sources/LolApp/ContentViews/SearchByStringView.cs
new file mode 100644
index 0000000..308ce0d
--- /dev/null
+++ b/Sources/LolApp/ContentViews/SearchByStringView.cs
@@ -0,0 +1,38 @@
+using System.Windows.Input;
+
+namespace LolApp.ContentViews;
+
+public class SearchByStringView : ContentView
+{
+ public static readonly BindableProperty PlaceHolderProperty = BindableProperty.Create(nameof(PlaceHolder), typeof(string), typeof(SearchByStringView), string.Empty);
+
+ public string PlaceHolder
+ {
+ get => (string)GetValue(PlaceHolderProperty);
+ set => SetValue(PlaceHolderProperty, value);
+ }
+
+ public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(SearchByStringView), string.Empty);
+
+ public string Text
+ {
+ get => (string)GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(SearchByStringView), null);
+
+ public ICommand Command
+ {
+ get => (ICommand)GetValue(CommandProperty);
+ set => SetValue(CommandProperty, value);
+ }
+
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(SearchByStringView), null);
+
+ public object CommandParameter
+ {
+ get => GetValue(CommandParameterProperty);
+ set => SetValue(CommandParameterProperty, value);
+ }
+}
diff --git a/Sources/LolApp/LolApp.csproj b/Sources/LolApp/LolApp.csproj
new file mode 100644
index 0000000..ab4fa0c
--- /dev/null
+++ b/Sources/LolApp/LolApp.csproj
@@ -0,0 +1,99 @@
+
+
+
+ net7.0-android;net7.0-ios;net7.0-maccatalyst
+ Exe
+ LolApp
+ true
+ true
+ enable
+
+
+ LolApp
+
+
+ fr.uca.iut.lolapp
+ d3cd18a9-c614-4933-bd36-3008e72004d5
+
+
+ 1.0
+ 1
+
+ 14.2
+ 14.0
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+ {0C898A04-092A-49AA-BE65-8AE818A2AF50}
+
+
+
+ false
+ appleIUT_TP2022
+ iPhone Developer: Cedric BOUHOURS (M2E3ZQNZ3K)
+
+
+ false
+ Developer ID Application
+ 3rd Party Mac Developer Installer
+
+
+ false
+
+
+ $(TargetFrameworks);net7.0-windows10.0.22621.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/MainPage.xaml b/Sources/LolApp/MainPage.xaml
new file mode 100644
index 0000000..9db8163
--- /dev/null
+++ b/Sources/LolApp/MainPage.xaml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/MainPage.xaml.cs b/Sources/LolApp/MainPage.xaml.cs
new file mode 100644
index 0000000..0750b67
--- /dev/null
+++ b/Sources/LolApp/MainPage.xaml.cs
@@ -0,0 +1,11 @@
+namespace LolApp;
+
+public partial class MainPage : ContentPage
+{
+ public MainPage()
+ {
+ InitializeComponent();
+ }
+}
+
+
diff --git a/Sources/LolApp/MauiProgram.cs b/Sources/LolApp/MauiProgram.cs
new file mode 100644
index 0000000..c12deff
--- /dev/null
+++ b/Sources/LolApp/MauiProgram.cs
@@ -0,0 +1,39 @@
+using CommunityToolkit.Maui;
+using LolApp.ViewModels;
+using Microsoft.Extensions.Logging;
+using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Platform;
+using Model;
+using StubLib;
+using ViewModels;
+
+namespace LolApp;
+
+public static class MauiProgram
+{
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .UseMauiCommunityToolkit()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ fonts.AddFont("Font Awesome 6 Free-Solid-900.otf", "FASolid");
+ });
+ builder.Services.AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton();
+
+#if DEBUG
+ builder.Logging.AddDebug();
+#endif
+
+ return builder.Build();
+ }
+}
+
diff --git a/Sources/LolApp/Platforms/Android/AndroidManifest.xml b/Sources/LolApp/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..e6c8a36
--- /dev/null
+++ b/Sources/LolApp/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Sources/LolApp/Platforms/Android/MainActivity.cs b/Sources/LolApp/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..016887c
--- /dev/null
+++ b/Sources/LolApp/Platforms/Android/MainActivity.cs
@@ -0,0 +1,11 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+
+namespace LolApp;
+
+[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+public class MainActivity : MauiAppCompatActivity
+{
+}
+
diff --git a/Sources/LolApp/Platforms/Android/MainApplication.cs b/Sources/LolApp/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000..95e1d92
--- /dev/null
+++ b/Sources/LolApp/Platforms/Android/MainApplication.cs
@@ -0,0 +1,16 @@
+using Android.App;
+using Android.Runtime;
+
+namespace LolApp;
+
+[Application]
+public class MainApplication : MauiApplication
+{
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
+
diff --git a/Sources/LolApp/Platforms/Android/Resources/values/colors.xml b/Sources/LolApp/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000..c2794f7
--- /dev/null
+++ b/Sources/LolApp/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
+
diff --git a/Sources/LolApp/Platforms/MacCatalyst/AppDelegate.cs b/Sources/LolApp/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 0000000..d5d15a5
--- /dev/null
+++ b/Sources/LolApp/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,10 @@
+using Foundation;
+
+namespace LolApp;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
+
diff --git a/Sources/LolApp/Platforms/MacCatalyst/Info.plist b/Sources/LolApp/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 0000000..ec44095
--- /dev/null
+++ b/Sources/LolApp/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,36 @@
+
+
+
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+ NSCameraUsageDescription
+ New Entry
+ NSPhotoLibraryUsageDescription
+ New Entry
+ NSPhotoLibraryAddUsageDescription
+ New Entry
+
+
diff --git a/Sources/LolApp/Platforms/MacCatalyst/Program.cs b/Sources/LolApp/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 0000000..f1b8f29
--- /dev/null
+++ b/Sources/LolApp/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,16 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace LolApp;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
+
diff --git a/Sources/LolApp/Platforms/Tizen/Main.cs b/Sources/LolApp/Platforms/Tizen/Main.cs
new file mode 100644
index 0000000..d58b3d9
--- /dev/null
+++ b/Sources/LolApp/Platforms/Tizen/Main.cs
@@ -0,0 +1,17 @@
+using System;
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+
+namespace LolApp;
+
+class Program : MauiApplication
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ static void Main(string[] args)
+ {
+ var app = new Program();
+ app.Run(args);
+ }
+}
+
diff --git a/Sources/LolApp/Platforms/Tizen/tizen-manifest.xml b/Sources/LolApp/Platforms/Tizen/tizen-manifest.xml
new file mode 100644
index 0000000..109d29f
--- /dev/null
+++ b/Sources/LolApp/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ maui-appicon-placeholder
+
+
+
+
+ http://tizen.org/privilege/internet
+
+
+
+
diff --git a/Sources/LolApp/Platforms/Windows/App.xaml b/Sources/LolApp/Platforms/Windows/App.xaml
new file mode 100644
index 0000000..23c64b9
--- /dev/null
+++ b/Sources/LolApp/Platforms/Windows/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/Sources/LolApp/Platforms/Windows/App.xaml.cs b/Sources/LolApp/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000..c3b0669
--- /dev/null
+++ b/Sources/LolApp/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,25 @@
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace LolApp.WinUI;
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+public partial class App : MauiWinUIApplication
+{
+ ///
+ /// 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()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
+
+
diff --git a/Sources/LolApp/Platforms/Windows/Package.appxmanifest b/Sources/LolApp/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000..52b7801
--- /dev/null
+++ b/Sources/LolApp/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/Platforms/Windows/app.manifest b/Sources/LolApp/Platforms/Windows/app.manifest
new file mode 100644
index 0000000..91a1b32
--- /dev/null
+++ b/Sources/LolApp/Platforms/Windows/app.manifest
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
+
diff --git a/Sources/LolApp/Platforms/iOS/AppDelegate.cs b/Sources/LolApp/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000..d5d15a5
--- /dev/null
+++ b/Sources/LolApp/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,10 @@
+using Foundation;
+
+namespace LolApp;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
+
diff --git a/Sources/LolApp/Platforms/iOS/Info.plist b/Sources/LolApp/Platforms/iOS/Info.plist
new file mode 100644
index 0000000..352a326
--- /dev/null
+++ b/Sources/LolApp/Platforms/iOS/Info.plist
@@ -0,0 +1,38 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+ NSCameraUsageDescription
+ New Entry
+ NSPhotoLibraryUsageDescription
+ Pour accéder aux images...
+ NSPhotoLibraryAddUsageDescription
+ Pour accéder aux images...
+
+
diff --git a/Sources/LolApp/Platforms/iOS/Program.cs b/Sources/LolApp/Platforms/iOS/Program.cs
new file mode 100644
index 0000000..f1b8f29
--- /dev/null
+++ b/Sources/LolApp/Platforms/iOS/Program.cs
@@ -0,0 +1,16 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace LolApp;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
+
diff --git a/Sources/LolApp/Properties/launchSettings.json b/Sources/LolApp/Properties/launchSettings.json
new file mode 100644
index 0000000..90f92d9
--- /dev/null
+++ b/Sources/LolApp/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
diff --git a/Sources/LolApp/Resources/AppIcon/appicon.png b/Sources/LolApp/Resources/AppIcon/appicon.png
new file mode 100644
index 0000000..8263b46
Binary files /dev/null and b/Sources/LolApp/Resources/AppIcon/appicon.png differ
diff --git a/Sources/LolApp/Resources/AppIcon/appiconfg.svg b/Sources/LolApp/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000..e9b7139
--- /dev/null
+++ b/Sources/LolApp/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/Sources/LolApp/Resources/Converters/Base64ToImageSourceConverter.cs b/Sources/LolApp/Resources/Converters/Base64ToImageSourceConverter.cs
new file mode 100644
index 0000000..e258576
--- /dev/null
+++ b/Sources/LolApp/Resources/Converters/Base64ToImageSourceConverter.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using CommunityToolkit.Maui.Converters;
+
+namespace LolApp.Resources.Converters
+{
+ public class Base64ToImageSourceConverter : ByteArrayToImageSourceConverter, IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ string base64 = value as string;
+ if (string.IsNullOrWhiteSpace(base64)) return null;
+ try
+ {
+ byte[] bytes = System.Convert.FromBase64String(base64);
+ return base.ConvertFrom(bytes, culture);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ ImageSource source = value as ImageSource;
+ if (source == null) return null;
+ byte[] bytes = base.ConvertBackTo(source, culture) as byte[];
+ return System.Convert.ToBase64String(bytes);
+ }
+ }
+}
+
diff --git a/Sources/LolApp/Resources/Converters/ChampionClassToIconConverter.cs b/Sources/LolApp/Resources/Converters/ChampionClassToIconConverter.cs
new file mode 100644
index 0000000..946b9a6
--- /dev/null
+++ b/Sources/LolApp/Resources/Converters/ChampionClassToIconConverter.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Globalization;
+using Model;
+
+namespace LolApp.Resources.Converters
+{
+ public class ChampionClassToIconConverter : IValueConverter
+ {
+ private static Dictionary icons = new()
+ {
+ [ChampionClass.Assassin] = "assassin.png",
+ [ChampionClass.Fighter] = "fighter.png",
+ [ChampionClass.Mage] = "mage.png",
+ [ChampionClass.Marksman] = "marksman.png",
+ [ChampionClass.Support] = "support.png",
+ [ChampionClass.Tank] = "tank.png"
+ };
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ try
+ {
+ ChampionClass champClass = (ChampionClass)value;
+ if(!icons.TryGetValue(champClass, out string icon))
+ {
+ return "";
+ }
+ return ImageSource.FromFile($"{icon}");
+ }
+ catch
+ {
+ return "";
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
+
diff --git a/Sources/LolApp/Resources/Converters/ImageRatioConverter.cs b/Sources/LolApp/Resources/Converters/ImageRatioConverter.cs
new file mode 100644
index 0000000..a3e0b7e
--- /dev/null
+++ b/Sources/LolApp/Resources/Converters/ImageRatioConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Globalization;
+
+namespace LolApp.Resources.Converters
+{
+ public class ImageRatioConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ try
+ {
+ double parentWidth = (double)value;
+ double ratio = (double)parameter;
+ return parentWidth*ratio;
+ }
+ catch
+ {
+ return 0.0;
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
+
diff --git a/Sources/LolApp/Resources/Converters/PlusOneConverter.cs b/Sources/LolApp/Resources/Converters/PlusOneConverter.cs
new file mode 100644
index 0000000..2d9eaec
--- /dev/null
+++ b/Sources/LolApp/Resources/Converters/PlusOneConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Globalization;
+
+namespace LolApp.Resources.Converters
+{
+ public class PlusOneConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ int i = -1;
+ try
+ {
+ i = (int)value;
+ }
+ catch (InvalidCastException e)
+ {
+ throw new InvalidCastException("PlusOneConverter : the value must be an int");
+ }
+ return i + 1;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
+
diff --git a/Sources/LolApp/Resources/Fonts/Font Awesome 6 Free-Solid-900.otf b/Sources/LolApp/Resources/Fonts/Font Awesome 6 Free-Solid-900.otf
new file mode 100644
index 0000000..f1d88fc
Binary files /dev/null and b/Sources/LolApp/Resources/Fonts/Font Awesome 6 Free-Solid-900.otf differ
diff --git a/Sources/LolApp/Resources/Fonts/OpenSans-Regular.ttf b/Sources/LolApp/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000..a49f11d
Binary files /dev/null and b/Sources/LolApp/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/Sources/LolApp/Resources/Fonts/OpenSans-Semibold.ttf b/Sources/LolApp/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000..23911e4
Binary files /dev/null and b/Sources/LolApp/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/Sources/LolApp/Resources/Images/assassin.svg b/Sources/LolApp/Resources/Images/assassin.svg
new file mode 100644
index 0000000..fb86680
--- /dev/null
+++ b/Sources/LolApp/Resources/Images/assassin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Sources/LolApp/Resources/Images/fighter.svg b/Sources/LolApp/Resources/Images/fighter.svg
new file mode 100644
index 0000000..42cb7df
--- /dev/null
+++ b/Sources/LolApp/Resources/Images/fighter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Sources/LolApp/Resources/Images/lol.png b/Sources/LolApp/Resources/Images/lol.png
new file mode 100644
index 0000000..10a552c
Binary files /dev/null and b/Sources/LolApp/Resources/Images/lol.png differ
diff --git a/Sources/LolApp/Resources/Images/lollogo.jpg b/Sources/LolApp/Resources/Images/lollogo.jpg
new file mode 100644
index 0000000..a1b82b5
Binary files /dev/null and b/Sources/LolApp/Resources/Images/lollogo.jpg differ
diff --git a/Sources/LolApp/Resources/Images/mage.svg b/Sources/LolApp/Resources/Images/mage.svg
new file mode 100644
index 0000000..45d627b
--- /dev/null
+++ b/Sources/LolApp/Resources/Images/mage.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Sources/LolApp/Resources/Images/marksman.svg b/Sources/LolApp/Resources/Images/marksman.svg
new file mode 100644
index 0000000..f91066f
--- /dev/null
+++ b/Sources/LolApp/Resources/Images/marksman.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Sources/LolApp/Resources/Images/rp.png b/Sources/LolApp/Resources/Images/rp.png
new file mode 100644
index 0000000..e025310
Binary files /dev/null and b/Sources/LolApp/Resources/Images/rp.png differ
diff --git a/Sources/LolApp/Resources/Images/support.svg b/Sources/LolApp/Resources/Images/support.svg
new file mode 100644
index 0000000..2d1f053
--- /dev/null
+++ b/Sources/LolApp/Resources/Images/support.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Sources/LolApp/Resources/Images/sword.png b/Sources/LolApp/Resources/Images/sword.png
new file mode 100644
index 0000000..fb87c07
Binary files /dev/null and b/Sources/LolApp/Resources/Images/sword.png differ
diff --git a/Sources/LolApp/Resources/Images/tank.svg b/Sources/LolApp/Resources/Images/tank.svg
new file mode 100644
index 0000000..2ec01a3
--- /dev/null
+++ b/Sources/LolApp/Resources/Images/tank.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Sources/LolApp/Resources/Raw/AboutAssets.txt b/Sources/LolApp/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000..808d6d3
--- /dev/null
+++ b/Sources/LolApp/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,17 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
+
+
diff --git a/Sources/LolApp/Resources/Splash/splash.png b/Sources/LolApp/Resources/Splash/splash.png
new file mode 100644
index 0000000..e89e1c2
Binary files /dev/null and b/Sources/LolApp/Resources/Splash/splash.png differ
diff --git a/Sources/LolApp/Resources/Styles/Colors.xaml b/Sources/LolApp/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000..286775e
--- /dev/null
+++ b/Sources/LolApp/Resources/Styles/Colors.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+ #D2B977
+ #F0E7B7
+ #2B0B98
+ White
+ Black
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #F7B548
+ #FFD590
+ #FFE5B9
+ #28C2D1
+ #7BDDEF
+ #C3F2F4
+ #3E8EED
+ #72ACF1
+ #A7CBF6
+
+ Transparent
+
+
diff --git a/Sources/LolApp/Resources/Styles/FontAwesomeGlyphs.xaml b/Sources/LolApp/Resources/Styles/FontAwesomeGlyphs.xaml
new file mode 100644
index 0000000..a53b993
--- /dev/null
+++ b/Sources/LolApp/Resources/Styles/FontAwesomeGlyphs.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+ +
+
+
diff --git a/Sources/LolApp/Resources/Styles/MyStyles.xaml b/Sources/LolApp/Resources/Styles/MyStyles.xaml
new file mode 100644
index 0000000..b988753
--- /dev/null
+++ b/Sources/LolApp/Resources/Styles/MyStyles.xaml
@@ -0,0 +1,102 @@
+
+
+
+
+ 0.59
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/Resources/Styles/Styles.xaml b/Sources/LolApp/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000..d23a11d
--- /dev/null
+++ b/Sources/LolApp/Resources/Styles/Styles.xaml
@@ -0,0 +1,406 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/SkinPage.xaml b/Sources/LolApp/SkinPage.xaml
new file mode 100644
index 0000000..0b082ab
--- /dev/null
+++ b/Sources/LolApp/SkinPage.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sources/LolApp/SkinPage.xaml.cs b/Sources/LolApp/SkinPage.xaml.cs
new file mode 100644
index 0000000..f4ac283
--- /dev/null
+++ b/Sources/LolApp/SkinPage.xaml.cs
@@ -0,0 +1,18 @@
+using LolApp.ViewModels;
+using ViewModels;
+
+namespace LolApp;
+
+public partial class SkinPage : ContentPage
+{
+ public ApplicationVM AppVM { get; set; }
+ public SkinVM SkinVM { get; }
+
+ public SkinPage(SkinVM svm, ApplicationVM appVM)
+ {
+ BindingContext = SkinVM = svm;
+ AppVM = appVM;
+
+ InitializeComponent();
+ }
+}
diff --git a/Sources/LolApp/ViewModels/AddChampionPageVM.cs b/Sources/LolApp/ViewModels/AddChampionPageVM.cs
new file mode 100644
index 0000000..31fb1e3
--- /dev/null
+++ b/Sources/LolApp/ViewModels/AddChampionPageVM.cs
@@ -0,0 +1,80 @@
+using System;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+//using Microsoft.Maui.Graphics.Platform;
+using ViewModels;
+
+namespace LolApp.ViewModels
+{
+ [ObservableObject]
+ public partial class AddChampionPageVM
+ {
+ ChampionsMgrVM ChampionsMgrVM { get; }
+
+ public AddChampionPageVM(ChampionsMgrVM championsMgrVM, ChampionVM champion = null)
+ {
+ ChampionsMgrVM = championsMgrVM;
+ if(champion == null) return;
+
+ oldChampion = champion;
+ IsNew = false;
+ this.champion = new EditableChampionVM(oldChampion);
+ }
+
+ private ChampionVM oldChampion;
+
+ [ObservableProperty]
+ bool isNew = true;
+
+ [ObservableProperty]
+ EditableChampionVM champion = new ();
+
+ [RelayCommand]
+ public async void PickIcon() => Champion.IconBase64 = await PickIconsAndImagesUtils.PickPhoto(42);
+
+ [RelayCommand]
+ public async void PickLargeImage() => Champion.LargeImageBase64 = await PickIconsAndImagesUtils.PickPhoto(1000);
+
+ [RelayCommand]
+ async Task Cancel()
+ => await App.Current.MainPage.Navigation.PopAsync();
+
+ [RelayCommand]
+ async Task AddChampion()
+ {
+ ChampionVM champVM = Champion.ToChampionVM();
+ await ChampionsMgrVM.AddChampion(champVM);
+ await App.Current.MainPage.Navigation.PopAsync();
+ }
+
+ [RelayCommand]
+ async Task EditChampion()
+ {
+ ChampionVM newChampion = Champion.ToChampionVM();
+ await ChampionsMgrVM.EditChampion(oldChampion, newChampion);
+ await App.Current.MainPage.Navigation.PopAsync();
+ }
+
+ [ObservableProperty]
+ string newCharacteristicDescription;
+
+ [ObservableProperty]
+ int newCharacteristicValue;
+
+ [RelayCommand]
+ void AddCharacteristic()
+ {
+ Champion?.AddCharacteristic(newCharacteristicDescription, newCharacteristicValue);
+ }
+
+ [RelayCommand]
+ void RemoveCharacteristic(KeyValuePair characteristic)
+ => Champion?.RemoveCharacteristic(characteristic);
+
+ [RelayCommand]
+ async Task AddSkill()
+ => await App.Current.MainPage.Navigation.PushModalAsync(new AddSkill(Champion));
+
+ }
+}
+
diff --git a/Sources/LolApp/ViewModels/AddOrEditSkinPageVM.cs b/Sources/LolApp/ViewModels/AddOrEditSkinPageVM.cs
new file mode 100644
index 0000000..09a134c
--- /dev/null
+++ b/Sources/LolApp/ViewModels/AddOrEditSkinPageVM.cs
@@ -0,0 +1,64 @@
+using System;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Model;
+using ViewModels;
+
+namespace LolApp.ViewModels
+{
+ [ObservableObject]
+ public partial class AddOrEditSkinPageVM
+ {
+ SkinsMgrVM SkinsMgrVM { get; }
+
+ private SkinVM oldSkin;
+
+ [ObservableProperty]
+ bool isNew = true;
+
+ [ObservableProperty]
+ EditableSkinVM skin;
+
+ public AddOrEditSkinPageVM(SkinsMgrVM skinsMgrVM, SkinVM oldSkin)
+ {
+ SkinsMgrVM = skinsMgrVM;
+
+ this.oldSkin = oldSkin;
+ IsNew = false;
+ this.skin = new EditableSkinVM(oldSkin);
+ }
+
+ public AddOrEditSkinPageVM(SkinsMgrVM skinsMgrVM, ChampionVM champion)
+ {
+ SkinsMgrVM = skinsMgrVM;
+ skin = new EditableSkinVM(champion);
+ }
+
+ [RelayCommand]
+ public async void PickIcon() => Skin.IconBase64 = await PickIconsAndImagesUtils.PickPhoto(42);
+
+ [RelayCommand]
+ public async void PickLargeImage() => Skin.LargeImageBase64 = await PickIconsAndImagesUtils.PickPhoto(1000);
+
+ [RelayCommand]
+ async Task Cancel()
+ => await App.Current.MainPage.Navigation.PopAsync();
+
+ [RelayCommand]
+ async Task AddSkin()
+ {
+ SkinVM skinVM = Skin.ToSkinVM();
+ await SkinsMgrVM.AddSkin(skinVM);
+ await App.Current.MainPage.Navigation.PopAsync();
+ }
+
+ [RelayCommand]
+ async Task EditSkin()
+ {
+ SkinVM newSkin = Skin.ToSkinVM();
+ await SkinsMgrVM.EditSkin(oldSkin, newSkin);
+ await App.Current.MainPage.Navigation.PopAsync();
+ }
+ }
+}
+
diff --git a/Sources/LolApp/ViewModels/AddSkillVM.cs b/Sources/LolApp/ViewModels/AddSkillVM.cs
new file mode 100644
index 0000000..ef40e97
--- /dev/null
+++ b/Sources/LolApp/ViewModels/AddSkillVM.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Linq;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Model;
+using ViewModels;
+
+namespace LolApp.ViewModels
+{
+ public partial class AddSkillVM : ObservableObject
+ {
+ public AddSkillVM(EditableChampionVM champion)
+ {
+ Champion = champion;
+ }
+
+ [ObservableProperty]
+ SkillType skillType;
+
+ [ObservableProperty]
+ string name;
+
+ [ObservableProperty]
+ string description;
+
+ [ObservableProperty]
+ EditableChampionVM champion;
+
+ [RelayCommand]
+ async void AddSkillToChampion()
+ {
+ champion.Skills.Add(new SkillVM(new Skill(name, skillType, description)));
+ await App.Current.MainPage.Navigation.PopModalAsync();
+ }
+
+ [RelayCommand]
+ async void Cancel()
+ {
+ await App.Current.MainPage.Navigation.PopModalAsync();
+ }
+
+ public IEnumerable AllSkills { get; }
+ = Enum.GetValues(typeof(SkillType)).Cast().Except(new SkillType[] {SkillType.Unknown}).ToList();
+ }
+}
+
diff --git a/Sources/LolApp/ViewModels/ApplicationVM.cs b/Sources/LolApp/ViewModels/ApplicationVM.cs
new file mode 100644
index 0000000..9f029f0
--- /dev/null
+++ b/Sources/LolApp/ViewModels/ApplicationVM.cs
@@ -0,0 +1,58 @@
+using System;
+using CommunityToolkit.Mvvm.Input;
+using Model;
+using ViewModels;
+
+namespace LolApp.ViewModels
+{
+ public partial class ApplicationVM
+ {
+ public ChampionsMgrVM ChampionsMgrVM { get; set; }
+
+ public SkinsMgrVM SkinsMgrVM { get; set; }
+
+ public ApplicationVM(ChampionsMgrVM championsMgrVM, SkinsMgrVM skinsMgrVM)
+ {
+ ChampionsMgrVM = championsMgrVM;
+ SkinsMgrVM = skinsMgrVM;
+ }
+
+ [RelayCommand]
+ async Task NavigateToChampionDetailsPage(ChampionVM cvm)
+ {
+ SkinsMgrVM.Champion = cvm;
+ SkinsMgrVM.Index = 0;
+ SkinsMgrVM.Count = 5;
+ await SkinsMgrVM.LoadSkinsCommand.ExecuteAsync(cvm);
+ await App.Current.MainPage.Navigation.PushAsync(new ChampionPage(cvm, this));
+ }
+
+ [RelayCommand]
+ async Task NavigateToAddNewChampionPage()
+ => await App.Current.MainPage.Navigation.PushAsync(new AddChampionPage(ChampionsMgrVM));
+
+ [RelayCommand(CanExecute = nameof(CanNavigateToEditChampionPage))]
+ async Task NavigateToEditChampionPage(object champ)
+ => await App.Current.MainPage.Navigation.PushAsync(new AddChampionPage(ChampionsMgrVM, champ as ChampionVM));
+
+ bool CanNavigateToEditChampionPage(object champ) => champ != null && champ is ChampionVM;
+
+ [RelayCommand]
+ async Task NavigateToSkinDetailsPage(object svm)
+ {
+ if (svm == null || svm is not SkinVM) return;
+ await App.Current.MainPage.Navigation.PushAsync(new SkinPage(svm as SkinVM, this));
+ }
+
+ [RelayCommand]
+ async Task NavigateToAddNewSkinPage(ChampionVM champion)
+ => await App.Current.MainPage.Navigation.PushAsync(new AddOrEditSkinPage(SkinsMgrVM, champion));
+
+ [RelayCommand(CanExecute = nameof(CanNavigateToEditSkinPage))]
+ async Task NavigateToEditSkinPage(object skin)
+ => await App.Current.MainPage.Navigation.PushAsync(new AddOrEditSkinPage(SkinsMgrVM, skin as SkinVM));
+
+ bool CanNavigateToEditSkinPage(object skin) => skin != null && skin is SkinVM;
+ }
+}
+
diff --git a/Sources/LolApp/ViewModels/ChampionClassVM.cs b/Sources/LolApp/ViewModels/ChampionClassVM.cs
new file mode 100644
index 0000000..992f45f
--- /dev/null
+++ b/Sources/LolApp/ViewModels/ChampionClassVM.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Model;
+
+namespace LolApp.ViewModels
+{
+ [ObservableObject]
+ public partial class ChampionClassVM
+ {
+ [ObservableProperty]
+ private ChampionClass model;
+
+ [ObservableProperty]
+ private bool isSelected;
+
+ public ChampionClassVM(ChampionClass model)
+ {
+ Model = model;
+ }
+
+ public static IEnumerable Classes { get; }
+ = Enum.GetValues(typeof(ChampionClass)).Cast().Except(new ChampionClass[] {ChampionClass.Unknown})
+ .Select(cc => new ChampionClassVM(cc));
+ }
+}
+
diff --git a/Sources/LolApp/ViewModels/ChampionsPageVM.cs b/Sources/LolApp/ViewModels/ChampionsPageVM.cs
new file mode 100644
index 0000000..906875a
--- /dev/null
+++ b/Sources/LolApp/ViewModels/ChampionsPageVM.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Reflection;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Model;
+using ViewModels;
+
+namespace LolApp.ViewModels
+{
+ [ObservableObject]
+ public partial class ChampionsPageVM
+ {
+ public ChampionsMgrVM ChampionsMgrVM { get; set; }
+
+ public ChampionsPageVM(ChampionsMgrVM championsMgrVM)
+ {
+ ChampionsMgrVM = championsMgrVM;
+ PropertyChanged += ChampionsMgrVM_PropertyChanged;
+
+ }
+
+ [ObservableProperty]
+ private ChampionClassVM selectedClass;
+
+
+ [RelayCommand]
+ public async Task SelectedChampionClassChanged(ChampionClassVM champClass)
+ {
+ if(SelectedClass != null) SelectedClass.IsSelected = false;
+ if(champClass.Model == ChampionClass.Unknown
+ || champClass.Model == SelectedClass?.Model)
+ {
+ SelectedClass = null;
+ return;
+ }
+ SelectedClass = champClass;
+ SelectedClass.IsSelected = true;
+ await ChampionsMgrVM.LoadChampionsByClass(SelectedClass.Model);//ChampionsMgrVM.SelectedClass);
+
+ }
+
+
+ [ObservableProperty]
+ private ChampionVM selectedChampion;
+
+ [ObservableProperty]
+ private string searchedName;
+
+
+
+ [ObservableProperty]
+ private string searchedSkill;
+
+ [ObservableProperty]
+ private string searchedCharacteristic;
+
+ private static string[] searchedStrings = { nameof(SearchedName), nameof(SearchedSkill), nameof(SearchedCharacteristic), nameof(SelectedClass) };
+
+ private async void ChampionsMgrVM_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if(searchedStrings.Any(s => e.PropertyName == s))
+ {
+ if(GetProperty(e.PropertyName).GetValue(this) != GetProperty(e.PropertyName).GetDefaultValue())
+ {
+ foreach(string s in searchedStrings.Except(new string[]{e.PropertyName }))
+ {
+ var prop = GetProperty(s);
+ prop.ResetPropertyValue(this);
+ }
+ return;
+ }
+ ChampionsMgrVM.Index=0;
+ if(searchedStrings.All(s => GetProperty(s).GetValue(this) == GetProperty(s).GetDefaultValue()))
+ {
+ await ChampionsMgrVM.LoadChampions();
+ }
+ }
+ }
+
+ private PropertyInfo? GetProperty(string propName)
+ => typeof(ChampionsPageVM).GetProperty(propName);
+
+ }
+
+ public static class Extensions
+ {
+ public static void ResetPropertyValue(this PropertyInfo pi, ChampionsPageVM instance)
+ {
+ if(pi.PropertyType == typeof(ChampionClassVM))
+ {
+ var temp = pi.GetValue(instance);
+ if(temp != null)
+ (temp as ChampionClassVM).IsSelected = false;
+ return;
+ }
+ pi.SetValue(instance, pi.GetDefaultValue());
+ }
+
+ public static object GetDefaultValue(this Type t)
+ {
+ if (t.IsValueType)
+ return Activator.CreateInstance(t);
+
+ return null;
+ }
+
+ public static object GetDefaultValue(this PropertyInfo pi)
+ => pi.PropertyType.GetDefaultValue();
+ }
+}
+
diff --git a/Sources/LolApp/ViewModels/PickIconsAndImagesUtils.cs b/Sources/LolApp/ViewModels/PickIconsAndImagesUtils.cs
new file mode 100644
index 0000000..6cef1d1
--- /dev/null
+++ b/Sources/LolApp/ViewModels/PickIconsAndImagesUtils.cs
@@ -0,0 +1,32 @@
+using System;
+//using Microsoft.Maui.Graphics.Platform;
+using Microsoft.Maui.Graphics.Skia;
+
+namespace LolApp.ViewModels
+{
+ public static class PickIconsAndImagesUtils
+ {
+ public async static Task PickPhoto(float maxWidthAndHeight)
+ {
+ FileResult photo = await MediaPicker.Default.PickPhotoAsync();
+ return photo != null ? await ToBase64(photo, maxWidthAndHeight) : null;
+ }
+
+ public async static Task ToBase64(FileResult photo, float maxWidthAndHeight)
+ {
+ using (var stream = await photo.OpenReadAsync())
+ using (var memoryStream = new MemoryStream())
+ {
+ var image = SkiaImage.FromStream(memoryStream);
+ //var image = PlatformImage.FromStream(stream);
+ if(image != null)
+ {
+ var newImage = image.Downsize(maxWidthAndHeight, true);
+ return newImage.AsBase64();
+ }
+ }
+ return null;
+ }
+ }
+}
+
diff --git a/Sources/ViewModels/ChampionVM.cs b/Sources/ViewModels/ChampionVM.cs
new file mode 100644
index 0000000..bdd1e7f
--- /dev/null
+++ b/Sources/ViewModels/ChampionVM.cs
@@ -0,0 +1,101 @@
+using System;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Model;
+using Microsoft.Maui.Controls;
+using System.Collections.ObjectModel;
+
+
+namespace ViewModels
+{
+ public partial class ChampionVM : ObservableObject
+ {
+ public Champion Model
+ {
+ get => model;
+ set
+ {
+ model = value;
+ OnPropertyChanged(nameof(Name));
+ OnPropertyChanged(nameof(Bio));
+ OnPropertyChanged(nameof(Class));
+ OnPropertyChanged(nameof(Icon));
+ OnPropertyChanged(nameof(Image));
+ OnPropertyChanged(nameof(Skills));
+ OnPropertyChanged(nameof(Characteristics));
+ }
+ }
+ private Champion model;
+
+ public ChampionVM(Champion model)
+ {
+ Model = model;
+ foreach(var skill in Model.Skills)
+ {
+ Skills.Add(new SkillVM(skill));
+ }
+ Skills.CollectionChanged += Skills_CollectionChanged;
+ }
+
+ private void Skills_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ SkillVM vm = e.NewItems?[0] as SkillVM;
+ switch(e.Action)
+ {
+ case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
+ Model.Skills.Add(new Skill(vm.Name, vm.Type, vm.Description));
+ break;
+ case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
+ Model.Skills.Remove(vm.Model);
+ break;
+ }
+ }
+
+ public string Name => Model.Name;
+
+ public ChampionClass Class
+ {
+ get => Model.Class;
+ set => SetProperty(Model.Class, value, newValue => Model.Class = newValue);
+ }
+
+ public string Bio
+ {
+ get => Model.Bio;
+ set => SetProperty(Model.Bio, value, newBio => Model.Bio = newBio);
+ }
+
+ public string Icon
+ {
+ get => Model.Icon;
+ set
+ {
+ SetProperty(Model.Icon, value, newIcon =>
+ {
+ Model.Icon = newIcon;
+ });
+ }
+ }
+
+ public string Image
+ {
+ get => Model.Image.Base64;
+ set
+ {
+ SetProperty(Model.Image.Base64, value, newImage =>
+ {
+ Model.Image.Base64 = newImage;
+ });
+ }
+ }
+
+ [ObservableProperty]
+ private ObservableCollection skills = new ObservableCollection();
+
+ public ReadOnlyDictionary Characteristics
+ => Model.Characteristics;
+
+ [ObservableProperty]
+ private ObservableCollection skins = new ObservableCollection();
+ }
+}
+
diff --git a/Sources/ViewModels/ChampionsMgrVM.cs b/Sources/ViewModels/ChampionsMgrVM.cs
new file mode 100644
index 0000000..06e5d67
--- /dev/null
+++ b/Sources/ViewModels/ChampionsMgrVM.cs
@@ -0,0 +1,227 @@
+using System.Threading.Tasks;
+using Model;
+using System.Windows.Input;
+using CommunityToolkit.Mvvm.ComponentModel;
+using System.Collections.ObjectModel;
+using CommunityToolkit.Mvvm.Input;
+using System.Data.SqlTypes;
+using System.Reflection;
+
+namespace ViewModels;
+
+public partial class ChampionsMgrVM : ObservableObject
+{
+ internal IDataManager DataMgr { get; set; }
+
+ public ChampionsMgrVM(IDataManager dataManager)
+ {
+ DataMgr = dataManager;
+
+ loadingMethods = new Dictionary>()
+ {
+ [LoadingCriterium.None] = async (o) => await LoadChampions(),
+ [LoadingCriterium.ByName] = async (o) =>
+ {
+ string substring = o as string;
+ if(substring == null) return;
+ await LoadChampionsByName(substring);
+ },
+ [LoadingCriterium.BySkill] = async (o) =>
+ {
+ string skillString = o as string;
+ if(skillString == null) return;
+ await LoadChampionsBySkill(skillString);
+ },
+ [LoadingCriterium.ByCharacteristic] = async (o) =>
+ {
+ string characString = o as string;
+ if(characString == null) return;
+ await LoadChampionsByCharacteristic(characString);
+ },
+ [LoadingCriterium.ByClass] = async (o) =>
+ {
+ if(!Enum.IsDefined(typeof(ChampionClass), o)) return;
+ ChampionClass champClass = (ChampionClass)o;
+ await LoadChampionsByClass(champClass);
+ },
+ };
+ }
+
+ private async Task LoadChampionsFunc(Func>> loader,
+ Func> nbReader,
+ LoadingCriterium criterium,
+ object parameter = null)
+ {
+ Champions.Clear();
+ var someChampions = (await loader()).Select(c => new ChampionVM(c)).ToList();
+ foreach (var cvm in someChampions)
+ {
+ Champions.Add(cvm);
+ }
+ NbChampions = await nbReader();
+ currentLoadingCriterium = criterium;
+ currentLoadingParameter = parameter;
+ }
+
+ [RelayCommand]
+ public async Task LoadChampions()
+ {
+ await LoadChampionsFunc(async () => await DataMgr.ChampionsMgr.GetItems(index, count, "Name"),
+ async () => await DataMgr.ChampionsMgr.GetNbItems(),
+ LoadingCriterium.None);
+ }
+
+ [RelayCommand(CanExecute =nameof(CanLoadChampionsByName))]
+ public async Task LoadChampionsByName(string substring)
+ {
+ await LoadChampionsFunc(async () => await DataMgr.ChampionsMgr.GetItemsByName(substring, index, count, "Name"),
+ async () => await DataMgr.ChampionsMgr.GetNbItemsByName(substring),
+ LoadingCriterium.ByName,
+ substring);
+ }
+ private bool CanLoadChampionsByName(string substring)
+ => !string.IsNullOrWhiteSpace(substring);
+
+ [RelayCommand(CanExecute =nameof(CanLoadChampionsBySkill))]
+ public async Task LoadChampionsBySkill(string skill)
+ {
+ await LoadChampionsFunc(
+ async () => await DataMgr.ChampionsMgr.GetItemsBySkill(skill, index, count, "Name"),
+ async () => await DataMgr.ChampionsMgr.GetNbItemsBySkill(skill),
+ LoadingCriterium.BySkill,
+ skill);
+ }
+ private bool CanLoadChampionsBySkill(string substring) => !string.IsNullOrWhiteSpace(substring);
+
+ [RelayCommand(CanExecute = nameof(CanLoadChampionsByCharacteristic))]
+ public async Task LoadChampionsByCharacteristic(string characteristic)
+ {
+ await LoadChampionsFunc(
+ async () => await DataMgr.ChampionsMgr.GetItemsByCharacteristic(characteristic, index, count, "Name"),
+ async () => await DataMgr.ChampionsMgr.GetNbItemsByCharacteristic(characteristic),
+ LoadingCriterium.ByCharacteristic,
+ characteristic);
+ }
+
+ private bool CanLoadChampionsByCharacteristic(string characteristic)
+ => !string.IsNullOrWhiteSpace(characteristic);
+
+ [RelayCommand]
+ public async Task LoadChampionsByClass(ChampionClass champClass)
+ {
+ if(champClass == ChampionClass.Unknown)
+ {
+ return;
+ }
+ await LoadChampionsFunc(
+ async () => await DataMgr.ChampionsMgr.GetItemsByClass(champClass, index, count, "Name"),
+ async () => await DataMgr.ChampionsMgr.GetNbItemsByClass(champClass),
+ LoadingCriterium.ByClass,
+ champClass);
+ }
+
+ [RelayCommand(CanExecute =nameof(CanDeleteChampion))]
+ public async Task DeleteChampion(object champVM)
+ {
+ ChampionVM cvm = champVM as ChampionVM;
+ if(cvm == null || !Champions.Contains(cvm)) return false;
+ bool result = await DataMgr.ChampionsMgr.DeleteItem(cvm.Model);
+ if(result)
+ {
+ Champions.Remove(cvm);
+ await LoadChampions();
+ }
+ return result;
+ }
+ bool CanDeleteChampion(object cvm)
+ => cvm!= null && cvm is ChampionVM && Champions.Contains(cvm);
+
+ [ObservableProperty]
+ [NotifyCanExecuteChangedFor(nameof(NextPageCommand))]
+ [NotifyCanExecuteChangedFor(nameof(PreviousPageCommand))]
+ private int index = 0;
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(NbPages))]
+ [NotifyCanExecuteChangedFor(nameof(NextPageCommand))]
+ [NotifyCanExecuteChangedFor(nameof(PreviousPageCommand))]
+ private int count = 5;
+
+ public int NbPages
+ {
+ get
+ {
+ if(Count == 0 || NbChampions == 0)
+ {
+ return 0;
+ }
+ return (NbChampions-1) / Count + 1;
+ }
+ }
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(NbPages))]
+ [NotifyCanExecuteChangedFor(nameof(NextPageCommand))]
+ [NotifyCanExecuteChangedFor(nameof(PreviousPageCommand))]
+ private int nbChampions = 0;
+
+ [ObservableProperty]
+ private ObservableCollection champions = new ObservableCollection();
+
+ [RelayCommand(CanExecute =nameof(CanPreviousPage))]
+ async Task PreviousPage()
+ {
+ if(Index > 0)
+ {
+ Index--;
+ await loadingMethods[currentLoadingCriterium](currentLoadingParameter);
+ }
+ }
+ bool CanPreviousPage() => Index > 0;
+
+ [RelayCommand(CanExecute =nameof(CanNextPage))]
+ async Task NextPage()
+ {
+ if(Index < NbPages-1)
+ {
+ Index++;
+ await loadingMethods[currentLoadingCriterium](currentLoadingParameter);
+ }
+ }
+ bool CanNextPage() => Index < NbPages-1;
+
+
+ enum LoadingCriterium
+ {
+ None,
+ ByName,
+ BySkill,
+ ByCharacteristic,
+ ByClass
+ }
+
+ private LoadingCriterium currentLoadingCriterium = LoadingCriterium.None;
+ private object currentLoadingParameter = null;
+
+ private Dictionary> loadingMethods;
+
+ public async Task AddChampion(ChampionVM champVM)
+ {
+ var added = await DataMgr.ChampionsMgr.AddItem(champVM.Model);
+ if(added != null)
+ {
+ Champions.Add(champVM);
+ await LoadChampions();
+ }
+ }
+
+ public async Task EditChampion(ChampionVM oldChampion, ChampionVM newChampion)
+ {
+ var edited = await DataMgr.ChampionsMgr.UpdateItem(oldChampion.Model, newChampion.Model);
+ oldChampion.Model = newChampion.Model;
+ if(edited != null)
+ {
+ await LoadChampions();
+ }
+ }
+}
diff --git a/Sources/ViewModels/EditableChampionVM.cs b/Sources/ViewModels/EditableChampionVM.cs
new file mode 100644
index 0000000..2db038b
--- /dev/null
+++ b/Sources/ViewModels/EditableChampionVM.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.ObjectModel;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Model;
+
+namespace ViewModels
+{
+ [ObservableObject]
+ public partial class EditableChampionVM
+ {
+ public EditableChampionVM()
+ { }
+
+ public EditableChampionVM(ChampionVM championVM)
+ {
+ Name = championVM.Name;
+ IconBase64 = championVM.Icon;
+ LargeImageBase64 = championVM.Image;
+ Bio = championVM.Bio;
+ ChampionClass = championVM.Class;
+ foreach(var ch in championVM.Characteristics)
+ {
+ AddCharacteristic(ch.Key, ch.Value);
+ }
+ foreach(var skill in championVM.Skills)
+ {
+ Skills.Add(skill);
+ }
+ }
+
+ [ObservableProperty]
+ string name;
+
+ [ObservableProperty]
+ string iconBase64;
+
+ [ObservableProperty]
+ string largeImageBase64;
+
+ [ObservableProperty]
+ string bio;
+
+ [ObservableProperty]
+ ChampionClass championClass;
+
+ [ObservableProperty]
+ ObservableCollection> characteristics = new ();
+
+ public void AddCharacteristic(string description, int value)
+ => Characteristics.Add(new KeyValuePair(description, value));
+
+ public void RemoveCharacteristic(KeyValuePair characteristic)
+ => Characteristics.Remove(characteristic);
+
+ [ObservableProperty]
+ ObservableCollection skills = new ObservableCollection();
+
+
+
+ public ChampionVM ToChampionVM()
+ {
+ var champion = new Champion(name, championClass, iconBase64, largeImageBase64, bio);
+ champion.AddCharacteristics(characteristics.Select(kvp => Tuple.Create(kvp.Key, kvp.Value)).ToArray());
+ foreach(var skillVM in Skills)
+ {
+ champion.AddSkill(skillVM.Model);
+ }
+ return new ChampionVM(champion);
+ }
+ }
+}
+
diff --git a/Sources/ViewModels/EditableSkinVM.cs b/Sources/ViewModels/EditableSkinVM.cs
new file mode 100644
index 0000000..b35ca24
--- /dev/null
+++ b/Sources/ViewModels/EditableSkinVM.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Reflection.PortableExecutable;
+using System.Xml.Linq;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Model;
+
+namespace ViewModels
+{
+ [ObservableObject]
+ public partial class EditableSkinVM
+ {
+ public EditableSkinVM(ChampionVM championVM)
+ {
+ champion = championVM.Model;
+ }
+
+ public EditableSkinVM(SkinVM skinVM)
+ {
+ Name = skinVM.Name;
+ IconBase64 = skinVM.Icon;
+ LargeImageBase64 = skinVM.Image;
+ Description = skinVM.Description;
+ Price = skinVM.Price;
+ champion = skinVM.Champion;
+ }
+
+ [ObservableProperty]
+ private string name;
+
+ [ObservableProperty]
+ private string iconBase64;
+
+ [ObservableProperty]
+ private string largeImageBase64;
+
+ [ObservableProperty]
+ private string description;
+
+ [ObservableProperty]
+ private float price;
+
+ private Champion champion;
+
+ public SkinVM ToSkinVM()
+ {
+ var skin = new Skin(name, champion, price, iconBase64, largeImageBase64, description);
+ return new SkinVM(skin);
+ }
+ }
+}
+
diff --git a/Sources/ViewModels/SkillVM.cs b/Sources/ViewModels/SkillVM.cs
new file mode 100644
index 0000000..c9c2ca1
--- /dev/null
+++ b/Sources/ViewModels/SkillVM.cs
@@ -0,0 +1,32 @@
+using System;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Model;
+
+namespace ViewModels
+{
+ [ObservableObject]
+ public partial class SkillVM
+ {
+ [ObservableProperty]
+ private Skill model;
+
+ public SkillVM(Skill model)
+ {
+ Model = model;
+ }
+
+ public string Name => Model.Name;
+
+ public SkillType Type => Model.Type;
+
+ public string Description
+ {
+ get => Model.Description;
+ set
+ {
+ SetProperty(Model.Description, value, newValue => Model.Description = newValue);
+ }
+ }
+ }
+}
+
diff --git a/Sources/ViewModels/SkinVM.cs b/Sources/ViewModels/SkinVM.cs
new file mode 100644
index 0000000..358ec97
--- /dev/null
+++ b/Sources/ViewModels/SkinVM.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Reflection.PortableExecutable;
+using System.Security.Claims;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Model;
+
+namespace ViewModels
+{
+ [ObservableObject]
+ public partial class SkinVM
+ {
+ public Skin Model
+ {
+ get => model;
+ set
+ {
+ model = value;
+ OnPropertyChanged(nameof(Name));
+ OnPropertyChanged(nameof(Description));
+ OnPropertyChanged(nameof(Price));
+ OnPropertyChanged(nameof(Icon));
+ OnPropertyChanged(nameof(Image));
+ }
+ }
+ private Skin model;
+
+ public SkinVM(Skin model)
+ => Model = model;
+
+ public string Name => Model.Name;
+
+ public string Description
+ {
+ get => Model.Description;
+ set => SetProperty(Model.Description, value, newValue => Model.Description = newValue);
+ }
+
+ public float Price
+ {
+ get => Model.Price;
+ set => SetProperty(Model.Price, value, newValue => Model.Price = newValue);
+ }
+
+ public string Icon
+ {
+ get => Model.Icon;
+ set
+ {
+ SetProperty(Model.Icon, value, newIcon => Model.Icon = newIcon);
+ }
+ }
+
+ public string Image
+ {
+ get => Model.Image.Base64;
+ set
+ {
+ SetProperty(Model.Image.Base64, value, newImage => Model.Image.Base64 = newImage);
+ }
+ }
+
+ public Champion Champion => Model.Champion;
+ }
+}
+
diff --git a/Sources/ViewModels/SkinsMgrVM.cs b/Sources/ViewModels/SkinsMgrVM.cs
new file mode 100644
index 0000000..c51a6cf
--- /dev/null
+++ b/Sources/ViewModels/SkinsMgrVM.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.ObjectModel;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Model;
+
+namespace ViewModels
+{
+ [ObservableObject]
+ public partial class SkinsMgrVM
+ {
+ internal IDataManager DataMgr { get; set; }
+
+ [ObservableProperty]
+ private ChampionVM champion;
+
+ public SkinsMgrVM(IDataManager dataManager)
+ {
+ DataMgr = dataManager;
+ }
+
+ [ObservableProperty]
+ [NotifyCanExecuteChangedFor(nameof(NextPageCommand))]
+ [NotifyCanExecuteChangedFor(nameof(PreviousPageCommand))]
+ private int index = 0;
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(NbPages))]
+ [NotifyCanExecuteChangedFor(nameof(NextPageCommand))]
+ [NotifyCanExecuteChangedFor(nameof(PreviousPageCommand))]
+ private int count = 5;
+
+ public int NbPages
+ {
+ get
+ {
+ if(Count == 0 || NbSkins == 0)
+ {
+ return 0;
+ }
+ return (NbSkins-1) / Count + 1;
+ }
+ }
+
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(NbPages))]
+ [NotifyCanExecuteChangedFor(nameof(NextPageCommand))]
+ [NotifyCanExecuteChangedFor(nameof(PreviousPageCommand))]
+ private int nbSkins = 0;
+
+ [ObservableProperty]
+ private ObservableCollection skins = new ObservableCollection();
+
+ [RelayCommand]
+ async Task LoadSkins()
+ {
+ Skins.Clear();
+ IEnumerable skins;
+ if(Champion != null)
+ {
+ skins = await DataMgr.SkinsMgr.GetItemsByChampion(Champion.Model, Index, Count,"Name");
+
+ }
+ else
+ {
+ skins = await DataMgr.SkinsMgr.GetItems(Index, Count, "Name");
+ }
+
+ foreach(var skin in skins)
+ {
+ if(skin != null)
+ Skins.Add(new SkinVM(skin));
+ }
+ }
+
+ [RelayCommand(CanExecute =nameof(CanPreviousPage))]
+ async Task PreviousPage()
+ {
+ if(Index > 0)
+ {
+ Index--;
+ await LoadSkins();
+ }
+ }
+ bool CanPreviousPage() => Index > 0;
+
+ [RelayCommand(CanExecute =nameof(CanNextPage))]
+ async Task NextPage()
+ {
+ if(Index < NbPages-1)
+ {
+ Index++;
+ await LoadSkins();
+ }
+ }
+ bool CanNextPage() => Index < NbPages-1;
+
+ [RelayCommand(CanExecute =nameof(CanDeleteSkin))]
+ public async Task DeleteSkin(object skinVM)
+ {
+ SkinVM svm = skinVM as SkinVM;
+ if(svm == null || !Skins.Contains(svm)) return false;
+ bool result = await DataMgr.SkinsMgr.DeleteItem(svm.Model);
+ if(result)
+ {
+ Skins.Remove(svm);
+ await LoadSkins();
+ }
+ return result;
+ }
+ bool CanDeleteSkin(object svm)
+ => svm!= null && svm is SkinVM && Skins.Contains(svm);
+
+ public async Task AddSkin(SkinVM skinVM)
+ {
+ var added = await DataMgr.SkinsMgr.AddItem(skinVM.Model);
+ if(added != null)
+ {
+ Skins.Add(skinVM);
+ await LoadSkins();
+ }
+ }
+
+ public async Task EditSkin(SkinVM oldSkin, SkinVM newSkin)
+ {
+ var edited = await DataMgr.SkinsMgr.UpdateItem(oldSkin.Model, newSkin.Model);
+ oldSkin.Model = newSkin.Model;
+ if(edited != null)
+ {
+ await LoadSkins();
+ }
+ }
+ }
+}
+
diff --git a/Sources/ViewModels/ViewModels.csproj b/Sources/ViewModels/ViewModels.csproj
new file mode 100644
index 0000000..eea4ab7
--- /dev/null
+++ b/Sources/ViewModels/ViewModels.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net6.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+