forked from Roflin/gamenight
		
	Added a client (From generator) and a basic login page to test it.
This commit is contained in:
		
							parent
							
								
									b2aba31264
								
							
						
					
					
						commit
						22f05c00c1
					
				@ -27,10 +27,8 @@
 | 
			
		||||
		<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Configuration" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Logging.WinUI" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Material.WinUI" Version="2.5.3" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI.Material" Version="2.5.5" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI" Version="2.5.5" />
 | 
			
		||||
 | 
			
		||||
@ -14,10 +14,8 @@
 | 
			
		||||
		<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Configuration" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Logging.WinUI" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Material.WinUI" Version="2.5.3" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI.Material" Version="2.5.5" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI" Version="2.5.5" />
 | 
			
		||||
 | 
			
		||||
@ -64,11 +64,9 @@
 | 
			
		||||
		<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Configuration" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Logging.WebAssembly.Console" Version="1.4.0" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Logging.WinUI" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Material.WinUI" Version="2.5.3" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI.Material" Version="2.5.5" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI" Version="2.5.5" />
 | 
			
		||||
 | 
			
		||||
@ -30,10 +30,8 @@
 | 
			
		||||
		<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Configuration" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Logging.WinUI" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Material.WinUI" Version="2.5.3" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI.Material" Version="2.5.5" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI" Version="2.5.5" />
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
using FrontendPlatformUno.Views;
 | 
			
		||||
using FrontendPlatformUno.ViewModels;
 | 
			
		||||
using FrontendPlatformUno.Services.Api;
 | 
			
		||||
 | 
			
		||||
namespace FrontendPlatformUno
 | 
			
		||||
{
 | 
			
		||||
@ -34,23 +35,11 @@ namespace FrontendPlatformUno
 | 
			
		||||
                    )
 | 
			
		||||
                    // Enable localization (see appsettings.json for supported languages)
 | 
			
		||||
                    .UseLocalization()
 | 
			
		||||
                    // Register Json serializers (ISerializer and ISerializer)
 | 
			
		||||
                    .UseSerialization((context, services) => services
 | 
			
		||||
                        .AddContentSerializer(context)
 | 
			
		||||
                        .AddJsonTypeInfo(WeatherForecastContext.Default.IImmutableListWeatherForecast))
 | 
			
		||||
                    .UseHttp((context, services) => services
 | 
			
		||||
                            // Register HttpClient
 | 
			
		||||
#if DEBUG
 | 
			
		||||
						// DelegatingHandler will be automatically injected into Refit Client
 | 
			
		||||
						.AddTransient<DelegatingHandler, DebugHttpHandler>()
 | 
			
		||||
#endif
 | 
			
		||||
                            .AddSingleton<IWeatherCache, WeatherCache>()
 | 
			
		||||
                            .AddRefitClient<IApiClient>(context))
 | 
			
		||||
                    .ConfigureServices((context, services) =>
 | 
			
		||||
                    {
 | 
			
		||||
                        // TODO: Register your services
 | 
			
		||||
                        //services.AddSingleton<IMyService, MyService>();
 | 
			
		||||
                        //services.AddSingleton<NavBarViewModel>();
 | 
			
		||||
                        services.AddSingleton<IDefaultApi>(_ => new DefaultApi("http://localhost:8080"));
 | 
			
		||||
                    })
 | 
			
		||||
                    .UseNavigation(RegisterRoutes)
 | 
			
		||||
                );
 | 
			
		||||
@ -68,8 +57,6 @@ namespace FrontendPlatformUno
 | 
			
		||||
        {
 | 
			
		||||
            views.Register(
 | 
			
		||||
                new ViewMap(ViewModel: typeof(ShellViewModel)),
 | 
			
		||||
                //new ViewMap<MainPage, MainViewModel>(),
 | 
			
		||||
                //new DataViewMap<SecondPage, SecondViewModel, Entity>(),
 | 
			
		||||
                new ViewMap<GamenightsPage, GamenightsViewModel>(),
 | 
			
		||||
                new ViewMap<LoginPage, LoginViewModel>()
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@
 | 
			
		||||
			ColorOverrideSource="ms-appx:///FrontendPlatformUno/Styles/ColorPaletteOverride.xaml"
 | 
			
		||||
			FontOverrideSource="ms-appx:///FrontendPlatformUno/Styles/MaterialFontsOverride.xaml" />
 | 
			
		||||
 | 
			
		||||
        <ResourceDictionary Source="ms-appx:///FrontendPlatformUno/Styles/GamenightStyles.xaml"/>
 | 
			
		||||
    </ResourceDictionary.MergedDictionaries>
 | 
			
		||||
    <!-- Add resources here -->
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows')) or '$(EnableWindowsTargeting)' == 'true'">$(TargetFrameworks);net7.0-windows10.0.19041</TargetFrameworks>
 | 
			
		||||
		<TargetFrameworks>$(TargetFrameworks);net7.0;net7.0-ios;net7.0-android;net7.0-maccatalyst</TargetFrameworks>
 | 
			
		||||
@ -8,16 +8,18 @@
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<PackageReference Include="JsonSubTypes" Version="2.0.1" />
 | 
			
		||||
		<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
 | 
			
		||||
		<PackageReference Include="Polly" Version="7.2.3" />
 | 
			
		||||
		<PackageReference Include="RestSharp" Version="108.0.2" />
 | 
			
		||||
		<PackageReference Include="Uno.Dsp.Tasks" Version="1.0.1" />
 | 
			
		||||
		<PackageReference Include="Uno.WinUI" Version="4.8.24" />
 | 
			
		||||
		<PackageReference Include="Uno.Resizetizer" Version="1.0.2" />
 | 
			
		||||
		<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Configuration" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Http.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Logging.WinUI" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Http" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Extensions.Serialization.Refit" Version="2.3.6" />
 | 
			
		||||
		<PackageReference Include="Uno.Material.WinUI" Version="2.5.3" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI.Material" Version="2.5.5" />
 | 
			
		||||
		<PackageReference Include="Uno.Toolkit.WinUI" Version="2.5.5" />
 | 
			
		||||
@ -77,8 +79,10 @@
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <None Remove="Styles\ColorPaletteOverride.zip" />
 | 
			
		||||
	  <None Remove="Styles\GamenightStyles.xaml" />
 | 
			
		||||
	  <None Remove="Views\GamenightsPage.xaml" />
 | 
			
		||||
	  <None Remove="Views\LoginPage.xaml" />
 | 
			
		||||
	  <None Remove="Views\NavBarUserControl.xaml" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
@ -86,7 +90,24 @@
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <UpToDateCheckInput Remove="Styles\GamenightStyles.xaml" />
 | 
			
		||||
	  <UpToDateCheckInput Remove="Views\GamenightsPage.xaml" />
 | 
			
		||||
	  <UpToDateCheckInput Remove="Views\LoginPage.xaml" />
 | 
			
		||||
	  <UpToDateCheckInput Remove="Views\NavBarUserControl.xaml" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <Compile Update="Services\Gamenight.cs">
 | 
			
		||||
	    <DesignTime>True</DesignTime>
 | 
			
		||||
	    <AutoGen>True</AutoGen>
 | 
			
		||||
	    <DependentUpon>Gamenight.yaml</DependentUpon>
 | 
			
		||||
	  </Compile>
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <None Update="Services\Gamenight.yaml">
 | 
			
		||||
	    <Generator>OpenApiCodeGenerator</Generator>
 | 
			
		||||
	    <LastGenOutput>Gamenight.cs</LastGenOutput>
 | 
			
		||||
	  </None>
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,17 @@
 | 
			
		||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 | 
			
		||||
  <PropertyGroup />
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Page Update="Styles\GamenightStyles.xaml">
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
    </Page>
 | 
			
		||||
    <Page Update="Views\GamenightsPage.xaml">
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
    </Page>
 | 
			
		||||
    <Page Update="Views\LoginPage.xaml">
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
    </Page>
 | 
			
		||||
    <Page Update="Views\NavBarUserControl.xaml">
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
    </Page>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
@ -2,8 +2,6 @@
 | 
			
		||||
global using FrontendPlatformUno.DataContracts;
 | 
			
		||||
global using FrontendPlatformUno.DataContracts.Serialization;
 | 
			
		||||
global using FrontendPlatformUno.Infrastructure;
 | 
			
		||||
global using FrontendPlatformUno.Services.Caching;
 | 
			
		||||
global using FrontendPlatformUno.Services.Endpoints;
 | 
			
		||||
global using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
global using Microsoft.Extensions.Hosting;
 | 
			
		||||
global using Microsoft.Extensions.Localization;
 | 
			
		||||
@ -13,7 +11,6 @@ global using Microsoft.UI.Xaml;
 | 
			
		||||
global using Microsoft.UI.Xaml.Controls;
 | 
			
		||||
global using Microsoft.UI.Xaml.Media;
 | 
			
		||||
global using Microsoft.UI.Xaml.Navigation;
 | 
			
		||||
global using Refit;
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Collections.Generic;
 | 
			
		||||
global using System.Collections.Immutable;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								FrontendPlatformUno/FrontendPlatformUno/Services/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								FrontendPlatformUno/FrontendPlatformUno/Services/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Gamenight.cs
 | 
			
		||||
@ -1,10 +0,0 @@
 | 
			
		||||
using FrontendPlatformUno.DataContracts;
 | 
			
		||||
using System.Collections.Immutable;
 | 
			
		||||
 | 
			
		||||
namespace FrontendPlatformUno.Services.Caching
 | 
			
		||||
{
 | 
			
		||||
    public interface IWeatherCache
 | 
			
		||||
    {
 | 
			
		||||
        ValueTask<IImmutableList<WeatherForecast>> GetForecast(CancellationToken token);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,80 +0,0 @@
 | 
			
		||||
using Uno.Extensions.Serialization;
 | 
			
		||||
using Windows.Networking.Connectivity;
 | 
			
		||||
using Windows.Storage;
 | 
			
		||||
 | 
			
		||||
namespace FrontendPlatformUno.Services.Caching
 | 
			
		||||
{
 | 
			
		||||
    public sealed class WeatherCache : IWeatherCache
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IApiClient _api;
 | 
			
		||||
        private readonly ISerializer _serializer;
 | 
			
		||||
        private readonly ILogger _logger;
 | 
			
		||||
 | 
			
		||||
        public WeatherCache(IApiClient api, ISerializer serializer, ILogger<WeatherCache> logger)
 | 
			
		||||
        {
 | 
			
		||||
            _api = api;
 | 
			
		||||
            _serializer = serializer;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool IsConnected => NetworkInformation.GetInternetConnectionProfile().GetNetworkConnectivityLevel() == NetworkConnectivityLevel.InternetAccess;
 | 
			
		||||
 | 
			
		||||
        public async ValueTask<IImmutableList<WeatherForecast>> GetForecast(CancellationToken token)
 | 
			
		||||
        {
 | 
			
		||||
            var weatherText = await GetCachedWeather();
 | 
			
		||||
            if (!string.IsNullOrWhiteSpace(weatherText))
 | 
			
		||||
            {
 | 
			
		||||
                return _serializer.FromString<ImmutableArray<WeatherForecast>>(weatherText);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!IsConnected)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogWarning("App is offline and cannot connect to the API.");
 | 
			
		||||
                throw new Exception("No internet connection");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var response = await _api.GetWeather(token);
 | 
			
		||||
 | 
			
		||||
            if (response.IsSuccessStatusCode && response.Content is not null)
 | 
			
		||||
            {
 | 
			
		||||
                var weather = response.Content;
 | 
			
		||||
                await Save(weather, token);
 | 
			
		||||
                return weather;
 | 
			
		||||
            }
 | 
			
		||||
            else if (response.Error is not null)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogError(response.Error, "An error occurred while retrieving the latest Forecast.");
 | 
			
		||||
                throw response.Error;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return ImmutableArray<WeatherForecast>.Empty;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async ValueTask<StorageFile> GetFile(CreationCollisionOption option) =>
 | 
			
		||||
            await ApplicationData.Current.TemporaryFolder.CreateFileAsync("weather.json", option);
 | 
			
		||||
 | 
			
		||||
        private async ValueTask<string?> GetCachedWeather()
 | 
			
		||||
        {
 | 
			
		||||
            var file = await GetFile(CreationCollisionOption.OpenIfExists);
 | 
			
		||||
            var properties = await file.GetBasicPropertiesAsync();
 | 
			
		||||
 | 
			
		||||
            // Reuse latest cache file if offline
 | 
			
		||||
            // or if the file is less than 5 minutes old
 | 
			
		||||
            if (IsConnected || DateTimeOffset.Now.AddMinutes(-5) > properties.DateModified)
 | 
			
		||||
            {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return await File.ReadAllTextAsync(file.Path);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async ValueTask Save(IImmutableList<WeatherForecast> weather, CancellationToken token)
 | 
			
		||||
        {
 | 
			
		||||
            var weatherText = _serializer.ToString(weather);
 | 
			
		||||
            var file = await GetFile(CreationCollisionOption.ReplaceExisting);
 | 
			
		||||
            await File.WriteAllTextAsync(file.Path, weatherText);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
using FrontendPlatformUno.DataContracts;
 | 
			
		||||
using System.Collections.Immutable;
 | 
			
		||||
 | 
			
		||||
namespace FrontendPlatformUno.Services.Endpoints
 | 
			
		||||
{
 | 
			
		||||
    [Headers("Content-Type: application/json")]
 | 
			
		||||
    public interface IApiClient
 | 
			
		||||
    {
 | 
			
		||||
        [Get("/api/weatherforecast")]
 | 
			
		||||
        Task<ApiResponse<IImmutableList<WeatherForecast>>> GetWeather(CancellationToken cancellationToken = default);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										249
									
								
								FrontendPlatformUno/FrontendPlatformUno/Services/Gamenight.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								FrontendPlatformUno/FrontendPlatformUno/Services/Gamenight.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,249 @@
 | 
			
		||||
openapi: 3.0.0
 | 
			
		||||
x-stoplight:
 | 
			
		||||
  id: w776sltk0h1bo
 | 
			
		||||
info:
 | 
			
		||||
  title: Gamenight
 | 
			
		||||
  version: '1.0'
 | 
			
		||||
  contact:
 | 
			
		||||
    name: Dennis Brentjes
 | 
			
		||||
    email: dennis@brentj.es
 | 
			
		||||
    url: 'https://brentj.es'
 | 
			
		||||
  description: Api specifaction for a Gamenight server
 | 
			
		||||
  license:
 | 
			
		||||
    name: MIT
 | 
			
		||||
servers:
 | 
			
		||||
  - url: 'http://localhost:8080'
 | 
			
		||||
    description: Gamenight
 | 
			
		||||
paths:
 | 
			
		||||
  /token:
 | 
			
		||||
    get:
 | 
			
		||||
      summary: ''
 | 
			
		||||
      operationId: get-token
 | 
			
		||||
      responses:
 | 
			
		||||
        '200':
 | 
			
		||||
          $ref: '#/components/responses/TokenResponse'
 | 
			
		||||
        '401':
 | 
			
		||||
          $ref: '#/components/responses/FailureResponse'
 | 
			
		||||
      requestBody:
 | 
			
		||||
        $ref: '#/components/requestBodies/Login'
 | 
			
		||||
      description: Submit your credentials to get a JWT-token to use with the rest of the api.
 | 
			
		||||
  /user:
 | 
			
		||||
    post:
 | 
			
		||||
      summary: ''
 | 
			
		||||
      operationId: post-register
 | 
			
		||||
      requestBody:
 | 
			
		||||
        $ref: '#/components/requestBodies/Register'
 | 
			
		||||
      responses:
 | 
			
		||||
        '200':
 | 
			
		||||
          description: ''
 | 
			
		||||
        '422':
 | 
			
		||||
          $ref: '#/components/responses/FailureResponse'
 | 
			
		||||
      description: 'Create a new user given a registration token and user information, username and email must be unique, and password and password_repeat must match.'
 | 
			
		||||
    parameters: []
 | 
			
		||||
  /gamenights:
 | 
			
		||||
    get:
 | 
			
		||||
      summary: Your GET endpoint
 | 
			
		||||
      responses:
 | 
			
		||||
        '200':
 | 
			
		||||
          $ref: '#/components/responses/GamenightsResponse'
 | 
			
		||||
        '400':
 | 
			
		||||
          $ref: '#/components/responses/FailureResponse'
 | 
			
		||||
        '401':
 | 
			
		||||
          $ref: '#/components/responses/FailureResponse'
 | 
			
		||||
      operationId: get-gamenights
 | 
			
		||||
      security:
 | 
			
		||||
        - JWT-Auth: []
 | 
			
		||||
      description: Retrieve the list of gamenights on this gamenight server. Requires authorization.
 | 
			
		||||
  /gamenight:
 | 
			
		||||
    post:
 | 
			
		||||
      summary: ''
 | 
			
		||||
      operationId: post-gamenight
 | 
			
		||||
      responses:
 | 
			
		||||
        '200':
 | 
			
		||||
          description: OK
 | 
			
		||||
        '401':
 | 
			
		||||
          $ref: '#/components/responses/FailureResponse'
 | 
			
		||||
        '422':
 | 
			
		||||
          $ref: '#/components/responses/FailureResponse'
 | 
			
		||||
      security:
 | 
			
		||||
        - JWT-Auth: []
 | 
			
		||||
      requestBody:
 | 
			
		||||
        $ref: '#/components/requestBodies/AddGamenight'
 | 
			
		||||
      description: 'Add a gamenight by providing a name and a date, only available when providing an JWT token.'
 | 
			
		||||
    get:
 | 
			
		||||
      summary: ''
 | 
			
		||||
      operationId: get-gamenight
 | 
			
		||||
      responses:
 | 
			
		||||
        '200':
 | 
			
		||||
          $ref: '#/components/responses/GamenightResponse'
 | 
			
		||||
        '401':
 | 
			
		||||
          $ref: '#/components/responses/FailureResponse'
 | 
			
		||||
        '422':
 | 
			
		||||
          $ref: '#/components/responses/FailureResponse'
 | 
			
		||||
      requestBody:
 | 
			
		||||
        $ref: '#/components/requestBodies/GetGamenight'
 | 
			
		||||
      security:
 | 
			
		||||
        - JWT-Auth: []
 | 
			
		||||
components:
 | 
			
		||||
  schemas:
 | 
			
		||||
    Gamenight:
 | 
			
		||||
      title: Gamenight
 | 
			
		||||
      x-stoplight:
 | 
			
		||||
        id: 0nmru75ph5wh3
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        id:
 | 
			
		||||
          type: string
 | 
			
		||||
          x-stoplight:
 | 
			
		||||
            id: 3bd6bux9juutk
 | 
			
		||||
        name:
 | 
			
		||||
          type: string
 | 
			
		||||
          x-stoplight:
 | 
			
		||||
            id: hp3ulebe5l5k2
 | 
			
		||||
        datetime:
 | 
			
		||||
          type: string
 | 
			
		||||
          x-stoplight:
 | 
			
		||||
            id: qrae8k13mit85
 | 
			
		||||
        owner_id:
 | 
			
		||||
          type: string
 | 
			
		||||
          x-stoplight:
 | 
			
		||||
            id: q8g2pnzqx4byh
 | 
			
		||||
      required:
 | 
			
		||||
        - id
 | 
			
		||||
        - name
 | 
			
		||||
        - datetime
 | 
			
		||||
        - owner_id
 | 
			
		||||
  requestBodies:
 | 
			
		||||
    Login:
 | 
			
		||||
      content:
 | 
			
		||||
        application/json:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              username:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: hvemn3s02e6h2
 | 
			
		||||
              password:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: yx02fdolrp9rj
 | 
			
		||||
            required:
 | 
			
		||||
              - username
 | 
			
		||||
              - password
 | 
			
		||||
    Register:
 | 
			
		||||
      content:
 | 
			
		||||
        application/json:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              username:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: el8e6kdc6xgmw
 | 
			
		||||
              email:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: ipexmxasalhym
 | 
			
		||||
              password:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: 5fztmf9jhd7dt
 | 
			
		||||
              password_repeat:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: xw1skr0mjujuq
 | 
			
		||||
              registration_token:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: kvq6rzg1fcn7s
 | 
			
		||||
            required:
 | 
			
		||||
              - username
 | 
			
		||||
              - email
 | 
			
		||||
              - password
 | 
			
		||||
              - password_repeat
 | 
			
		||||
              - registration_token
 | 
			
		||||
    AddGamenight:
 | 
			
		||||
      content:
 | 
			
		||||
        application/json:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              name:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: ak4fkyj5haj8k
 | 
			
		||||
              datetime:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: 3dylqwf2yuyku
 | 
			
		||||
    GetGamenight:
 | 
			
		||||
      content:
 | 
			
		||||
        application/json:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              id:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: qykfde4zkqfgx
 | 
			
		||||
  responses:
 | 
			
		||||
    TokenResponse:
 | 
			
		||||
      description: Example response
 | 
			
		||||
      content:
 | 
			
		||||
        application/json:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              jwt_token:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: qyp2q6rqsk1ql
 | 
			
		||||
            required:
 | 
			
		||||
              - jwt_token
 | 
			
		||||
    FailureResponse:
 | 
			
		||||
      description: Example response
 | 
			
		||||
      content:
 | 
			
		||||
        application/json:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              message:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: g1la0pt0erd0b
 | 
			
		||||
        application/xml:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              message:
 | 
			
		||||
                type: string
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: ir85ew0w88sv7
 | 
			
		||||
            required:
 | 
			
		||||
              - message
 | 
			
		||||
    GamenightsResponse:
 | 
			
		||||
      description: Example response
 | 
			
		||||
      content:
 | 
			
		||||
        application/json:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              gamenights:
 | 
			
		||||
                x-stoplight:
 | 
			
		||||
                  id: t1i2zjid122y2
 | 
			
		||||
                type: array
 | 
			
		||||
                items:
 | 
			
		||||
                  $ref: '#/components/schemas/Gamenight'
 | 
			
		||||
    GamenightResponse:
 | 
			
		||||
      description: Example response
 | 
			
		||||
      content:
 | 
			
		||||
        application/json:
 | 
			
		||||
          schema:
 | 
			
		||||
            $ref: '#/components/schemas/Gamenight'
 | 
			
		||||
  securitySchemes:
 | 
			
		||||
    JWT-Auth:
 | 
			
		||||
      type: http
 | 
			
		||||
      scheme: bearer
 | 
			
		||||
      bearerFormat: JWT
 | 
			
		||||
      description: ''
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
<ResourceDictionary
 | 
			
		||||
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 | 
			
		||||
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 | 
			
		||||
 | 
			
		||||
    <Style TargetType="TextBox" BasedOn="{StaticResource FilledTextBoxStyle}">
 | 
			
		||||
        <Setter Property="Margin" Value="5,10"/>
 | 
			
		||||
    </Style>
 | 
			
		||||
 | 
			
		||||
    <Style TargetType="PasswordBox" BasedOn="{StaticResource FilledPasswordBoxStyle}">
 | 
			
		||||
        <Setter Property="Margin" Value="5,10"/>
 | 
			
		||||
    </Style>
 | 
			
		||||
 | 
			
		||||
</ResourceDictionary>
 | 
			
		||||
@ -1,5 +1,8 @@
 | 
			
		||||
using CommunityToolkit.Mvvm.ComponentModel;
 | 
			
		||||
using CommunityToolkit.Mvvm.Input;
 | 
			
		||||
using FrontendPlatformUno.Services.Api;
 | 
			
		||||
using FrontendPlatformUno.Services.Client;
 | 
			
		||||
using FrontendPlatformUno.Services.Model;
 | 
			
		||||
using FrontendPlatformUno.Views;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
@ -16,16 +19,41 @@ namespace FrontendPlatformUno.ViewModels
 | 
			
		||||
        public string? Title { get; }
 | 
			
		||||
        public ICommand GoToLogin { get; }
 | 
			
		||||
 | 
			
		||||
        public LoginViewModel(INavigator navigator, IStringLocalizer stringLocalizer)
 | 
			
		||||
        public string? Username { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string? Password { get; set; }
 | 
			
		||||
        public ICommand LoginCommand { get; internal set; }
 | 
			
		||||
 | 
			
		||||
        private IDefaultApi GamenightApi { get; }
 | 
			
		||||
 | 
			
		||||
        public LoginViewModel(INavigator navigator, IStringLocalizer stringLocalizer, IDefaultApi gamenightApi)
 | 
			
		||||
        {
 | 
			
		||||
            Navigator = navigator;
 | 
			
		||||
            GamenightApi = gamenightApi;
 | 
			
		||||
            Title = $"{stringLocalizer["ApplicationName"]} - Login";
 | 
			
		||||
            GoToLogin = new AsyncRelayCommand(GoToLoginView);
 | 
			
		||||
            LoginCommand = new AsyncRelayCommand(Login);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task GoToLoginView()
 | 
			
		||||
        {
 | 
			
		||||
            await Navigator.NavigateViewModelAsync<LoginViewModel>(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task Login()
 | 
			
		||||
        {
 | 
			
		||||
            var req = new GetTokenRequest(Username, Password);
 | 
			
		||||
            GetToken200Response tokenResponse;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                tokenResponse = await GamenightApi.GetTokenAsync(req);
 | 
			
		||||
            } 
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Console.WriteLine(ex.ToString());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            ((Configuration)GamenightApi.Configuration).AccessToken = tokenResponse.JwtToken;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,18 +17,7 @@
 | 
			
		||||
                <RowDefinition Height="Auto" />
 | 
			
		||||
                <RowDefinition />
 | 
			
		||||
            </Grid.RowDefinitions>
 | 
			
		||||
            <utu:NavigationBar Content="{Binding Title}">
 | 
			
		||||
                <utu:NavigationBar.MainCommand>
 | 
			
		||||
                    <AppBarButton>
 | 
			
		||||
                        <AppBarButton.Icon>
 | 
			
		||||
                            <BitmapIcon UriSource="ms-appx:///FrontendPlatformUno/Assets/Icons/back.png" />
 | 
			
		||||
                        </AppBarButton.Icon>
 | 
			
		||||
                    </AppBarButton>
 | 
			
		||||
                </utu:NavigationBar.MainCommand>
 | 
			
		||||
                <utu:NavigationBar.PrimaryCommands>
 | 
			
		||||
                    <AppBarButton Label="Login" Tag="Login" Icon="OtherUser" not_skia:AutomationProperties.AutomationId="LoginPageButton" Command="{Binding GoToLogin}"/>
 | 
			
		||||
                </utu:NavigationBar.PrimaryCommands>
 | 
			
		||||
            </utu:NavigationBar>
 | 
			
		||||
            <local:NavBarUserControl/>
 | 
			
		||||
        </Grid>
 | 
			
		||||
    </Grid>
 | 
			
		||||
</Page>
 | 
			
		||||
 | 
			
		||||
@ -17,18 +17,21 @@
 | 
			
		||||
                <RowDefinition Height="Auto" />
 | 
			
		||||
                <RowDefinition />
 | 
			
		||||
            </Grid.RowDefinitions>
 | 
			
		||||
            <utu:NavigationBar Content="{Binding Title}">
 | 
			
		||||
                <utu:NavigationBar.MainCommand>
 | 
			
		||||
                    <AppBarButton>
 | 
			
		||||
                        <AppBarButton.Icon>
 | 
			
		||||
                            <BitmapIcon UriSource="ms-appx:///FrontendPlatformUno/Assets/Icons/back.png" />
 | 
			
		||||
                        </AppBarButton.Icon>
 | 
			
		||||
                    </AppBarButton>
 | 
			
		||||
                </utu:NavigationBar.MainCommand>
 | 
			
		||||
                <utu:NavigationBar.PrimaryCommands>
 | 
			
		||||
                    <AppBarButton Label="Login" Tag="Login" Icon="OtherUser" not_skia:AutomationProperties.AutomationId="LoginPageButton" Command="{Binding GoToLogin}"/>
 | 
			
		||||
                </utu:NavigationBar.PrimaryCommands>
 | 
			
		||||
            </utu:NavigationBar>
 | 
			
		||||
            <local:NavBarUserControl/>
 | 
			
		||||
 | 
			
		||||
            <Grid Grid.Row="1">
 | 
			
		||||
                <Grid.ColumnDefinitions>
 | 
			
		||||
                    <ColumnDefinition Width="*"/>
 | 
			
		||||
                    <ColumnDefinition MinWidth="600" Width="8*"/>
 | 
			
		||||
                    <ColumnDefinition Width="*"/>
 | 
			
		||||
                </Grid.ColumnDefinitions>
 | 
			
		||||
 | 
			
		||||
                <StackPanel Grid.Column="1" Orientation="Vertical" HorizontalAlignment="Stretch" VerticalAlignment="Center">
 | 
			
		||||
                    <TextBox HorizontalAlignment="Stretch" Text="{Binding Username, Mode=TwoWay}" Header="Username" PlaceholderText="Username" KeyUp="OnButton"/>
 | 
			
		||||
                    <PasswordBox HorizontalAlignment="Stretch" Password="{Binding Password, Mode=TwoWay}" Header="Password" PlaceholderText="Password" KeyUp="OnButton"/>
 | 
			
		||||
                    <Button Content="Login" Command="{Binding LoginCommand}"/>
 | 
			
		||||
                </StackPanel>
 | 
			
		||||
            </Grid>
 | 
			
		||||
        </Grid>
 | 
			
		||||
    </Grid>
 | 
			
		||||
</Page>
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,10 @@ using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Runtime.InteropServices.WindowsRuntime;
 | 
			
		||||
using Uno;
 | 
			
		||||
using Windows.Foundation;
 | 
			
		||||
using Windows.Foundation.Collections;
 | 
			
		||||
using Windows.UI.Core;
 | 
			
		||||
 | 
			
		||||
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
 | 
			
		||||
 | 
			
		||||
@ -27,5 +29,14 @@ namespace FrontendPlatformUno.Views
 | 
			
		||||
        {
 | 
			
		||||
            this.InitializeComponent();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void OnButton(object sender, KeyRoutedEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            if(e.Key == Windows.System.VirtualKey.Enter)
 | 
			
		||||
            {
 | 
			
		||||
                ((LoginViewModel)DataContext).LoginCommand?.Execute(null);
 | 
			
		||||
                e.Handled = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
<UserControl
 | 
			
		||||
    x:Class="FrontendPlatformUno.Views.NavBarUserControl"
 | 
			
		||||
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 | 
			
		||||
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
			
		||||
    xmlns:local="using:FrontendPlatformUno.Views"
 | 
			
		||||
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 | 
			
		||||
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 | 
			
		||||
    mc:Ignorable="d"
 | 
			
		||||
    xmlns:uen="using:Uno.Extensions.Navigation.UI"
 | 
			
		||||
	xmlns:utu="using:Uno.Toolkit.UI"
 | 
			
		||||
    xmlns:not_skia="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 | 
			
		||||
    d:DesignHeight="300"
 | 
			
		||||
    d:DesignWidth="400">
 | 
			
		||||
 | 
			
		||||
    <utu:NavigationBar Content="{Binding Title}">
 | 
			
		||||
        <utu:NavigationBar.MainCommand>
 | 
			
		||||
            <AppBarButton>
 | 
			
		||||
                <AppBarButton.Icon>
 | 
			
		||||
                    <BitmapIcon UriSource="ms-appx:///FrontendPlatformUno/Assets/Icons/back.png" />
 | 
			
		||||
                </AppBarButton.Icon>
 | 
			
		||||
            </AppBarButton>
 | 
			
		||||
        </utu:NavigationBar.MainCommand>
 | 
			
		||||
        <utu:NavigationBar.PrimaryCommands>
 | 
			
		||||
            <AppBarButton Label="Login" Tag="Login" Icon="OtherUser" not_skia:AutomationProperties.AutomationId="LoginPageButton" Command="{Binding GoToLogin}"/>
 | 
			
		||||
        </utu:NavigationBar.PrimaryCommands>
 | 
			
		||||
    </utu:NavigationBar>
 | 
			
		||||
</UserControl>
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
using Microsoft.UI.Xaml;
 | 
			
		||||
using Microsoft.UI.Xaml.Controls;
 | 
			
		||||
using Microsoft.UI.Xaml.Controls.Primitives;
 | 
			
		||||
using Microsoft.UI.Xaml.Data;
 | 
			
		||||
using Microsoft.UI.Xaml.Input;
 | 
			
		||||
using Microsoft.UI.Xaml.Media;
 | 
			
		||||
using Microsoft.UI.Xaml.Navigation;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Runtime.InteropServices.WindowsRuntime;
 | 
			
		||||
using Windows.Foundation;
 | 
			
		||||
using Windows.Foundation.Collections;
 | 
			
		||||
 | 
			
		||||
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
 | 
			
		||||
 | 
			
		||||
namespace FrontendPlatformUno.Views
 | 
			
		||||
{
 | 
			
		||||
    public sealed partial class NavBarUserControl : UserControl
 | 
			
		||||
    {
 | 
			
		||||
        public NavBarUserControl()
 | 
			
		||||
        {
 | 
			
		||||
            this.InitializeComponent();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -20,15 +20,6 @@ impl LoginResponse {
 | 
			
		||||
            jwt_token: Some(token)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn failure(message: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            login_result: false,
 | 
			
		||||
            message: Some(message),
 | 
			
		||||
            user_id: None,
 | 
			
		||||
            jwt_token: None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
 | 
			
		||||
@ -34,23 +34,22 @@ impl Into<schema::user::Register> for Register {
 | 
			
		||||
pub async fn login(pool: web::Data<DbPool>, login_data: web::Json<Login>) -> Result<impl Responder, ApiError> {
 | 
			
		||||
    let data = login_data.into_inner();
 | 
			
		||||
 | 
			
		||||
    let response = if let Some(user) = web::block(move || {
 | 
			
		||||
    if let Ok(Some(user)) = web::block(move || {
 | 
			
		||||
        let mut conn = pool.get_conn();
 | 
			
		||||
        schema::login(&mut conn, data.into())
 | 
			
		||||
    })
 | 
			
		||||
    .await?? 
 | 
			
		||||
    .await?
 | 
			
		||||
    {
 | 
			
		||||
        let token = get_token(&user)?;
 | 
			
		||||
        LoginResponse::success(user.id, token)
 | 
			
		||||
        let response = LoginResponse::success(user.id, token);
 | 
			
		||||
        Ok(HttpResponse::Ok()
 | 
			
		||||
            .content_type(ContentType::json())
 | 
			
		||||
            .body(serde_json::to_string(&response)?)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        LoginResponse::failure("User doesn't exist or password doesn't match".to_string())
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok()
 | 
			
		||||
        .content_type(ContentType::json())
 | 
			
		||||
        .body(serde_json::to_string(&response)?)
 | 
			
		||||
    )
 | 
			
		||||
        Err(ApiError{status: 401, message: "User doesn't exist or password doesn't match".to_string()})
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[post("/user")]
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user