Compare commits

..

13 Commits

Author SHA1 Message Date
Rémi LAVERGNE e8d2d0c267 Adding the IGDB client to the Manager constructor
continuous-integration/drone/push Build is passing Details
10 months ago
Rémi LAVERGNE a8ac2e3c92 IGDBClient and Settings initialization for the MAUI Project (binding)
continuous-integration/drone/push Build is passing Details
10 months ago
Rémi LAVERGNE ac68c30b8b Rename of the Main Page
10 months ago
Rémi LAVERGNE 9e32c0bb8e Registering & Binding of the HttpClient along with the others services (Auth, IGDB) for the MAUI Project
continuous-integration/drone/push Build is passing Details
10 months ago
Rémi LAVERGNE 7b6add2993 Modification on the Binding (binding on the Manager and not the page
continuous-integration/drone/push Build is passing Details
10 months ago
Rémi LAVERGNE 98383a66f1 Packages added or updated
continuous-integration/drone/push Build is passing Details
10 months ago
Rémi LAVERGNE 8fc639ed05 Creation of another Game Class to implement the API
continuous-integration/drone/push Build is failing Details
10 months ago
Rémi LAVERGNE 98502b0308 ♾️ Change for ignoring the appsettings.json file (API secrets)
continuous-integration/drone/push Build is failing Details
10 months ago
Rémi LAVERGNE c7a6ff5684 IGDB base client to query the API & return the response (async func)
continuous-integration/drone/push Build is failing Details
10 months ago
Rémi LAVERGNE 0356f5c9dd Class for requesting the access token for the API (read & return)
10 months ago
Rémi LAVERGNE 347ccb454c 🗃️ Class to store the response from the OAuthRequest (token)
10 months ago
Rémi LAVERGNE aec0db1257 🗃️ Class to store API application properties.
10 months ago
Rémi LAVERGNE dca28921fc Display the profile picture and pseudo on the Main Page only if connected
continuous-integration/drone/push Build is passing Details
10 months ago

3
.gitignore vendored

@ -7,6 +7,9 @@
## ##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# Ignore the appsettings file
appsettings.json
# User-specific files # User-specific files
*.rsuser *.rsuser
*.suo *.suo

@ -1,7 +1,11 @@
using Models; using System.Diagnostics;
using Models;
using Stub; using Stub;
using DataContractPersistance; using DataContractPersistance;
using GameAtlas.Views; using GameAtlas.Views;
using Microsoft.Extensions.Options;
using Microsoft.VisualBasic.FileIO;
using Models.API;
namespace GameAtlas; namespace GameAtlas;
@ -23,34 +27,42 @@ public partial class App : Application
/// <summary> /// <summary>
/// Gestionnaire principal de l'application. /// Gestionnaire principal de l'application.
/// </summary> /// </summary>
public Manager MyManager { get; private set; } = new Manager(new Stub.Stub());//new Manager(new DataContractPersistance.DataContractPers()); public Manager MyManager { get; private set; }
private readonly IGDBClient _igdbClient;
private readonly IGDBSettings _igdbSettings;
/// <summary> /// <summary>
/// Constructeur de l'application. /// Constructeur de l'application.
/// </summary> /// </summary>
public App() public App(IGDBClient igdbClient, IOptions<IGDBSettings> igdbSettings)
{ {
InitializeComponent(); MyManager = new Manager(new Stub.Stub(), _igdbClient);
if (File.Exists(Path.Combine(FilePath, FileName))) InitializeComponent();
{
//MyManager = new Manager(new DataContractPersistance.DataContractXML());
MyManager = new Manager(new DataContractPersistance.DataContractJSON());
}
MyManager.ChargerDonnees(); MyManager.ChargerDonnees();
MainPage = new AppShell(); MainPage = new AppShell();
_igdbClient = igdbClient;
_igdbSettings = igdbSettings.Value;
if (!File.Exists(Path.Combine(FilePath, FileName))) // For debug purposes, load games when the app starts
{ LoadGames();
//MyManager.Persistance = new DataContractPersistance.DataContractXML();
MyManager.Persistance = new DataContractPersistance.DataContractJSON();
} }
private async void LoadGames()
MyManager.SauvegardeDonnees(); {
try
{
var games = await _igdbClient.GetGamesAsync();
Debug.WriteLine($"Games: {games}");
}
catch (HttpRequestException ex)
{
Debug.WriteLine($"Request failed: {ex.Message}");
}
} }
} }

@ -25,7 +25,7 @@
<ShellContent <ShellContent
Title="Accueil" Title="Accueil"
Icon="home" Icon="home"
ContentTemplate="{DataTemplate views:PageAcceuil}" ContentTemplate="{DataTemplate views:PageAccueil}"
Route="PageAccueil" /> Route="PageAccueil" />
<ShellContent <ShellContent
Title="Parcourir" Title="Parcourir"

@ -18,6 +18,6 @@ public partial class AppShell : Shell
Routing.RegisterRoute(nameof(PageAdmin), typeof(PageAdmin)); Routing.RegisterRoute(nameof(PageAdmin), typeof(PageAdmin));
Routing.RegisterRoute(nameof(PageAcceuil), typeof(PageAcceuil)); Routing.RegisterRoute(nameof(PageAccueil), typeof(PageAccueil));
} }
} }

@ -15,6 +15,7 @@
<UseMaui>true</UseMaui> <UseMaui>true</UseMaui>
<SingleProject>true</SingleProject> <SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Display name --> <!-- Display name -->
<ApplicationTitle>GameAtlas</ApplicationTitle> <ApplicationTitle>GameAtlas</ApplicationTitle>
@ -94,7 +95,17 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="5.1.0" /> <PackageReference Include="CommunityToolkit.Maui" Version="5.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="appsettings.json" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -1,5 +1,12 @@
using CommunityToolkit.Maui; using System.Diagnostics;
using CommunityToolkit.Maui;
using GameAtlas.Views;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Models;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Models.API;
namespace GameAtlas; namespace GameAtlas;
@ -15,6 +22,7 @@ public static class MauiProgram
public static MauiApp CreateMauiApp() public static MauiApp CreateMauiApp()
{ {
var builder = MauiApp.CreateBuilder(); var builder = MauiApp.CreateBuilder();
builder.Configuration.AddJsonFile(Path.Combine(FileSystem.AppDataDirectory, "appsettings.json"), optional: false, reloadOnChange: true);
builder builder
.UseMauiApp<App>() .UseMauiApp<App>()
.UseMauiCommunityToolkit() .UseMauiCommunityToolkit()
@ -37,6 +45,16 @@ public static class MauiProgram
fonts.AddFont("Roboto-Black.ttf", "RobotoBlack"); fonts.AddFont("Roboto-Black.ttf", "RobotoBlack");
}); });
// Register the HttpClient, AuthService, IGDBClient as a service
builder.Services.AddSingleton<HttpClient>();
builder.Services.AddSingleton<AuthService>();
builder.Services.AddSingleton<IGDBClient>();
// Bind the configuration section to IGDBSettings (for IGDB API access)
var igdbSettings = new IGDBSettings();
builder.Configuration.GetSection("IGDB").Bind(igdbSettings);
builder.Services.AddSingleton(igdbSettings);
#if DEBUG #if DEBUG
builder.Logging.AddDebug(); builder.Logging.AddDebug();
#endif #endif

@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:views="clr-namespace:GameAtlas.Views.Composants" xmlns:views="clr-namespace:GameAtlas.Views.Composants"
x:Class="GameAtlas.Views.PageAcceuil" x:Class="GameAtlas.Views.PageAccueil"
Title="ACCUEIL"> Title="ACCUEIL">
<Grid> <Grid>
@ -23,7 +23,7 @@
RowDefinitions="auto, auto, auto, auto, auto, auto, *"> RowDefinitions="auto, auto, auto, auto, auto, auto, *">
<Grid HorizontalOptions="Start" Padding="25,20,0,20"> <Grid HorizontalOptions="Start" Padding="25,20,0,20" IsVisible="{Binding IsConnected}">
<HorizontalStackLayout Spacing="10"> <HorizontalStackLayout Spacing="10">
<Frame Grid.Row="0" WidthRequest="50" HeightRequest="50" CornerRadius="25" VerticalOptions="Start" > <Frame Grid.Row="0" WidthRequest="50" HeightRequest="50" CornerRadius="25" VerticalOptions="Start" >
<Frame.Background> <Frame.Background>
@ -35,11 +35,12 @@
</LinearGradientBrush> </LinearGradientBrush>
</Frame.Background> </Frame.Background>
<Frame HeightRequest="48" WidthRequest="48" CornerRadius="24" VerticalOptions="Center" HorizontalOptions="Center" IsClippedToBounds="True"> <Frame HeightRequest="48" WidthRequest="48" CornerRadius="24" VerticalOptions="Center" HorizontalOptions="Center" IsClippedToBounds="True">
<Image Source="{Binding AccueilManager.ConnectedUser.PhotoProfil}" Aspect="AspectFill"/> <Image Source="{Binding ConnectedUser.PhotoProfil}" Aspect="AspectFill"/>
</Frame> </Frame>
</Frame> </Frame>
<Label Text="{Binding AccueilManager.ConnectedUser.Pseudo}" FontSize="18" FontFamily="AladinRegular" TextColor="{StaticResource Black}" VerticalTextAlignment="Center"/> <Label Text="{Binding ConnectedUser.Pseudo}" FontSize="18" FontFamily="AladinRegular" TextColor="{StaticResource Black}" VerticalTextAlignment="Center"/>
</HorizontalStackLayout> </HorizontalStackLayout>
</Grid> </Grid>
@ -52,7 +53,7 @@
/> />
<ScrollView Orientation="Horizontal" Grid.Row="4" Padding="20,0,20,0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HorizontalScrollBarVisibility="Never"> <ScrollView Orientation="Horizontal" Grid.Row="4" Padding="20,0,20,0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" HorizontalScrollBarVisibility="Never">
<HorizontalStackLayout BindableLayout.ItemsSource="{Binding AccueilManager.ListJeuxAffiches}" Spacing="15"> <HorizontalStackLayout BindableLayout.ItemsSource="{Binding ListJeuxAffiches}" Spacing="15">
<BindableLayout.ItemTemplate> <BindableLayout.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid> <Grid>
@ -106,7 +107,7 @@
/> />
<Grid Grid.Row="6" Padding="0,0,0,20"> <Grid Grid.Row="6" Padding="0,0,0,20">
<VerticalStackLayout Spacing="30" BindableLayout.ItemsSource="{Binding AccueilManager.TopRatedGames}"> <VerticalStackLayout Spacing="30" BindableLayout.ItemsSource="{Binding TopRatedGames}">
<BindableLayout.ItemTemplate> <BindableLayout.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid ColumnDefinitions="auto,*,auto"> <Grid ColumnDefinitions="auto,*,auto">

@ -1,22 +1,20 @@
using Models; using Models;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
namespace GameAtlas.Views; namespace GameAtlas.Views;
/// <summary> /// <summary>
/// Code-Behind pour la page d'accueil. /// Code-Behind pour la page d'accueil.
/// </summary> /// </summary>
public partial class PageAcceuil : ContentPage public partial class PageAccueil : ContentPage
{ {
public Manager AccueilManager => (App.Current as App).MyManager; public Manager AccueilManager => (App.Current as App).MyManager;
public PageAcceuil() public PageAccueil()
{ {
InitializeComponent(); InitializeComponent();
BindingContext = AccueilManager;
BindingContext = this;
} }
private async void OnProfil_Tapped(object sender, EventArgs e) private async void OnProfil_Tapped(object sender, EventArgs e)

@ -110,6 +110,7 @@ public partial class PageConnexion : ContentPage
{ {
Debug.WriteLine("Mot de Passe verifié et valide"); Debug.WriteLine("Mot de Passe verifié et valide");
ConnexionManager.ConnectedUser = user; ConnexionManager.ConnectedUser = user;
ConnexionManager.IsConnected = true;
return true; return true;
} }
} }
@ -117,6 +118,7 @@ public partial class PageConnexion : ContentPage
Debug.WriteLine("La connexion a échoué (pseudo ou mot de passe invalide)"); Debug.WriteLine("La connexion a échoué (pseudo ou mot de passe invalide)");
DisplayAlert("Erreur", "Utilisateur ou Mot de Passe invalide.", "Ok"); DisplayAlert("Erreur", "Utilisateur ou Mot de Passe invalide.", "Ok");
ConnexionManager.ConnectedUser = null; ConnexionManager.ConnectedUser = null;
ConnexionManager.IsConnected = false;
return false; return false;
} }

@ -74,5 +74,6 @@ public partial class PageProfil : ContentPage
{ {
await Shell.Current.GoToAsync("//page/PageAccueil"); await Shell.Current.GoToAsync("//page/PageAccueil");
ProfilManager.ConnectedUser = null; ProfilManager.ConnectedUser = null;
ProfilManager.IsConnected = false;
} }
} }

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
namespace Models.API
{
public class AuthService
{
private readonly HttpClient _httpClient;
public AuthService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<OAuthResponse> GetAccessTokenAsync(string clientId, string clientSecret)
{
var request = new HttpRequestMessage(HttpMethod.Post, "https://id.twitch.tv/oauth2/token");
request.Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("client_secret", clientSecret),
new KeyValuePair<string, string>("grant_type", "client_credentials")
});
var response = await _httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Request error: {response.StatusCode}, Content: {errorContent}");
throw new HttpRequestException($"Response status code does not indicate success: {response.StatusCode} ({errorContent})");
}
var responseContent = await response.Content.ReadAsStringAsync();
var oauthResponse = JsonSerializer.Deserialize<OAuthResponse>(responseContent);
return oauthResponse;
}
}
}

@ -0,0 +1,47 @@
using Microsoft.Extensions.Options;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Runtime;
using System.Text.Json;
using System.Threading.Tasks;
namespace Models.API
{
public class IGDBClient
{
private readonly HttpClient _httpClient;
private readonly AuthService _authService;
private readonly IGDBSettings _settings;
public IGDBClient(HttpClient httpClient, AuthService authService, IGDBSettings settings)
{
_httpClient = httpClient;
_authService = authService;
_settings = settings;
}
public async Task<string> GetGamesAsync()
{
var authResponse = await _authService.GetAccessTokenAsync(_settings.ClientId, _settings.ClientSecret);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResponse.AccessToken);
_httpClient.DefaultRequestHeaders.Add("Client-ID", _settings.ClientId);
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.igdb.com/v4/games");
request.Content = new StringContent("fields name,genres.name; limit 10;", System.Text.Encoding.UTF8, "application/json");
var response = await _httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Request error: {response.StatusCode}, Content: {errorContent}");
throw new HttpRequestException($"Response status code: {response.StatusCode} ({errorContent})");
}
var responseContent = await response.Content.ReadAsStringAsync();
return responseContent;
}
}
}

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Models.API
{
public class IGDBSettings
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Models.API
{
public class OAuthResponse
{
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
[JsonPropertyName("expires_in")]
public int ExpiresIn { get; set; }
[JsonPropertyName("token_type")]
public string TokenType { get; set; }
}
}

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Models
{
public class Game
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("summary")]
public string Summary { get; set; }
[JsonPropertyName("url")]
public string Url { get; set; }
[JsonPropertyName("cover.url")]
public string CoverUrl { get; set; }
public Game() { }
}
}

@ -6,6 +6,7 @@ using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Models.API;
namespace Models namespace Models
{ {
@ -14,6 +15,20 @@ namespace Models
/// </summary> /// </summary>
public class Manager : INotifyPropertyChanged public class Manager : INotifyPropertyChanged
{ {
private IGDBClient _igdbClient;
/// <summary>
/// Client IGDB utilisé pour récupérer les données des jeux.
/// </summary>
public IGDBClient IGDBClient
{
get => _igdbClient;
set
{
_igdbClient = value;
OnPropertyChanged();
}
}
/// <summary> /// <summary>
/// Liste des administrateurs de l'application. /// Liste des administrateurs de l'application.
/// </summary> /// </summary>
@ -78,6 +93,20 @@ namespace Models
} }
} }
private bool _isConnected;
/// <summary>
/// Indique si un utilisateur est connecté sur l'application.
/// </summary>
public bool IsConnected
{
get => _isConnected;
set
{
_isConnected = value;
OnPropertyChanged();
}
}
/// <summary> /// <summary>
/// Gestionnaire de persistance utilisé pour charger et sauvegarder les donnees. /// Gestionnaire de persistance utilisé pour charger et sauvegarder les donnees.
/// </summary> /// </summary>
@ -110,13 +139,16 @@ namespace Models
/// Constructeur de la classe Manager avec un gestionnaire de persistance spécifique. /// Constructeur de la classe Manager avec un gestionnaire de persistance spécifique.
/// </summary> /// </summary>
/// <param name="persistance">Le gestionnaire de persistance utilisé pour charger et sauvegarder les données.</param> /// <param name="persistance">Le gestionnaire de persistance utilisé pour charger et sauvegarder les données.</param>
public Manager(IPersistanceManager persistance) /// <param name="igdbClient">Le client IGDB utilisé pour récupérer les données des jeux.</param>
public Manager(IPersistanceManager persistance, IGDBClient igdbClient)
{ {
Utilisateurs = new List<Utilisateur>(); Utilisateurs = new List<Utilisateur>();
Admins = new List<Admin>(); Admins = new List<Admin>();
ListJeux = new ObservableCollection<Jeu>(); ListJeux = new ObservableCollection<Jeu>();
Persistance = persistance; Persistance = persistance;
IsConnected = false;
ConnectedUser = null; ConnectedUser = null;
IGDBClient = igdbClient;
} }
/// <summary> /// <summary>
@ -127,6 +159,7 @@ namespace Models
ListJeux = new ObservableCollection<Jeu>(); ListJeux = new ObservableCollection<Jeu>();
Admins = new List<Admin>(); Admins = new List<Admin>();
Utilisateurs = new List<Utilisateur>(); Utilisateurs = new List<Utilisateur>();
IsConnected = false;
ConnectedUser = null; ConnectedUser = null;
} }

@ -1,9 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UserSecretsId>4336014e-74be-4275-8110-a7ca055416d8</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
</ItemGroup>
</Project> </Project>

Loading…
Cancel
Save