Umfassende Umstrukturierung der SlipItIn-App für eine moderne, zustandsbasierte und SignalR-gestützte Multiplayer-Architektur. Einführung neuer Services für Auth, API, SignalR, lokalen Speicher und Game-State-Management via Dependency Injection. Komplette Neugestaltung der Views (Login, Registrierung, Lobby, Spielbrett) mit MVVM und CommunityToolkit.Mvvm. Navigation jetzt über MAUI Shell mit expliziten Routen. Game-Logik auf Events/Messaging umgestellt, UI und Logik entkoppelt, Echtzeit-Updates integriert. SignalR für alle Spielaktionen inkl. Wiederverbindung und Offline-Queueing. Lokaler Speicher für Auth, Userdaten, GameState und Aktionen. MainPage entfernt, neue Views implementiert. Projektdatei um NuGet-Pakete und Shared-Projekt erweitert. Backend-Änderungen für direkte Spieler-Zuordnung und korrekte EF Core-Relationen. App ist nun robust, testbar und unterstützt Multiplayer- sowie Offline-Szenarien.
85 lines
2.9 KiB
C#
85 lines
2.9 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using SlipItIn.Services.Interfaces;
|
|
using SlipItIn.Shared.DTOs;
|
|
|
|
namespace SlipItIn.Services;
|
|
|
|
public class ApiService : IApiService
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private readonly IAppConfigurationService _configuration;
|
|
private readonly IAuthSessionService _authSession;
|
|
|
|
public ApiService(IAppConfigurationService configuration, IAuthSessionService authSession)
|
|
{
|
|
_configuration = configuration;
|
|
_authSession = authSession;
|
|
_httpClient = new HttpClient
|
|
{
|
|
BaseAddress = new Uri(_configuration.ApiBaseUrl)
|
|
};
|
|
}
|
|
|
|
public Task<AuthResponseDto> RegisterAsync(RegisterRequestDto request, CancellationToken cancellationToken = default)
|
|
=> PostAuthAsync("api/auth/register", request, cancellationToken);
|
|
|
|
public Task<AuthResponseDto> LoginAsync(LoginRequestDto request, CancellationToken cancellationToken = default)
|
|
=> PostAuthAsync("api/auth/login", request, cancellationToken);
|
|
|
|
public Task LogoutAsync() => _authSession.ClearSessionAsync();
|
|
|
|
private async Task<AuthResponseDto> PostAuthAsync<TRequest>(string endpoint, TRequest request, CancellationToken cancellationToken)
|
|
{
|
|
AttachAuthHeader();
|
|
|
|
var response = await _httpClient.PostAsJsonAsync(endpoint, request, cancellationToken);
|
|
|
|
if (response.IsSuccessStatusCode)
|
|
{
|
|
var payload = await response.Content.ReadFromJsonAsync<AuthResponseDto>(cancellationToken: cancellationToken);
|
|
if (payload is null)
|
|
throw new InvalidOperationException("Leere Auth-Antwort vom Server.");
|
|
|
|
return payload;
|
|
}
|
|
|
|
var message = await TryReadErrorMessageAsync(response, cancellationToken);
|
|
throw new InvalidOperationException(message);
|
|
}
|
|
|
|
private void AttachAuthHeader()
|
|
{
|
|
var token = _authSession.AccessToken;
|
|
_httpClient.DefaultRequestHeaders.Authorization = string.IsNullOrWhiteSpace(token)
|
|
? null
|
|
: new AuthenticationHeaderValue("Bearer", token);
|
|
}
|
|
|
|
private static async Task<string> TryReadErrorMessageAsync(HttpResponseMessage response, CancellationToken cancellationToken)
|
|
{
|
|
var raw = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
if (string.IsNullOrWhiteSpace(raw))
|
|
return $"Serverfehler ({(int)response.StatusCode}).";
|
|
|
|
try
|
|
{
|
|
using var doc = JsonDocument.Parse(raw);
|
|
if (doc.RootElement.ValueKind == JsonValueKind.Object &&
|
|
doc.RootElement.TryGetProperty("message", out var msgElement))
|
|
{
|
|
var msg = msgElement.GetString();
|
|
if (!string.IsNullOrWhiteSpace(msg))
|
|
return msg;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return raw;
|
|
}
|
|
}
|