feat/estimate #50
3 changed files with 82 additions and 4 deletions
commit
2c6ab134f5
|
|
@ -357,7 +357,7 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable
|
|||
/// single multipart file named <c>file</c> and validates the image
|
||||
/// content type before persisting it.
|
||||
/// </summary>
|
||||
public async Task SetAvatarAsync(
|
||||
public async Task<string> SetAvatarAsync(
|
||||
Stream imageStream,
|
||||
string fileName,
|
||||
string? contentType = null,
|
||||
|
|
@ -400,6 +400,27 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable
|
|||
}
|
||||
|
||||
await EnsureSuccessOrThrowAsync(response, ct).ConfigureAwait(false);
|
||||
|
||||
var payload = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
||||
if (string.IsNullOrWhiteSpace(payload))
|
||||
return "Avatar mis à jour.";
|
||||
|
||||
try
|
||||
{
|
||||
using var json = JsonDocument.Parse(payload);
|
||||
if (json.RootElement.TryGetProperty("message", out var msgEl))
|
||||
{
|
||||
var message = msgEl.GetString();
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
return message;
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Keep a user-friendly fallback when the API payload is not JSON.
|
||||
}
|
||||
|
||||
return "Avatar mis à jour.";
|
||||
}
|
||||
|
||||
public async Task LogoutAsync()
|
||||
|
|
|
|||
|
|
@ -46,8 +46,18 @@
|
|||
<TextBlock Grid.Row="10" Text="Dark mode"/>
|
||||
<CheckBox Grid.Row="11" x:Name="DarkModeCheckBox" IsChecked="{Binding DarkMode, Mode=TwoWay}"/>
|
||||
|
||||
<StackPanel Grid.Row="12" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Content="Choisir l'avatar"
|
||||
<StackPanel Grid.Row="12" Spacing="8">
|
||||
<TextBlock Text="Avatar: PNG, JPG/JPEG, WEBP, GIF. Taille max 2 MB."
|
||||
Opacity="0.8"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock x:Name="AvatarUploadStatusText"
|
||||
Text=""
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button x:Name="ChooseAvatarButton"
|
||||
Content="Choisir l'avatar"
|
||||
Click="ChooseAvatar_Click"
|
||||
Margin="0,0,8,0"/>
|
||||
|
||||
|
|
@ -62,6 +72,7 @@
|
|||
<Button Content="Sauver"
|
||||
Command="{Binding Save}"
|
||||
IsEnabled="{Binding IsDirty}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PostIt.Services;
|
||||
|
|
@ -11,6 +14,11 @@ using PostIt.Services;
|
|||
namespace PostIt.Views;
|
||||
public partial class SettingsPage: ContentPage
|
||||
{
|
||||
private const int MaxAvatarSizeMegabytes = 2;
|
||||
private const string AcceptedAvatarFormats = "PNG, JPG/JPEG, WEBP, GIF";
|
||||
|
||||
private int _avatarStatusVersion;
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
|
@ -18,9 +26,15 @@ public partial class SettingsPage: ContentPage
|
|||
|
||||
private async void ChooseAvatar_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var statusVersion = Interlocked.Increment(ref _avatarStatusVersion);
|
||||
ChooseAvatarButton.IsEnabled = false;
|
||||
SetAvatarStatus("Sélection d'un fichier avatar...", Brushes.Gray);
|
||||
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel is null)
|
||||
{
|
||||
SetAvatarStatus("Impossible d'accéder à la fenêtre active.", Brushes.IndianRed);
|
||||
ChooseAvatarButton.IsEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -40,19 +54,51 @@ public partial class SettingsPage: ContentPage
|
|||
var file = files.FirstOrDefault();
|
||||
if (file is null)
|
||||
{
|
||||
SetAvatarStatus("Upload annulé.", Brushes.Gray);
|
||||
ChooseAvatarButton.IsEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SetAvatarStatus("Upload avatar en cours...", Brushes.Gray);
|
||||
|
||||
await using var stream = await file.OpenReadAsync();
|
||||
var api = ((App)App.Current!).ServiceProvider!.GetRequiredService<YavscApiClient>();
|
||||
await api.SetAvatarAsync(stream, file.Name, GetMimeType(file.Name));
|
||||
var message = await api.SetAvatarAsync(stream, file.Name, GetMimeType(file.Name));
|
||||
SetAvatarStatus(string.IsNullOrWhiteSpace(message)
|
||||
? "Avatar mis à jour."
|
||||
: message, Brushes.ForestGreen);
|
||||
|
||||
_ = ClearSuccessStatusLaterAsync(statusVersion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetAvatarStatus($"Échec upload avatar: {ex.Message}", Brushes.IndianRed);
|
||||
Console.Error.WriteLine($"🩎 Avatar upload failed: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
ChooseAvatarButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ClearSuccessStatusLaterAsync(int statusVersion)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(true);
|
||||
if (statusVersion != _avatarStatusVersion)
|
||||
return;
|
||||
|
||||
if (AvatarUploadStatusText.Foreground == Brushes.ForestGreen)
|
||||
{
|
||||
SetAvatarStatus($"Avatar prêt. Formats supportés: {AcceptedAvatarFormats}. Taille max: {MaxAvatarSizeMegabytes} MB.", Brushes.Gray);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetAvatarStatus(string message, IBrush color)
|
||||
{
|
||||
AvatarUploadStatusText.Foreground = color;
|
||||
AvatarUploadStatusText.Text = message;
|
||||
}
|
||||
|
||||
private static string GetMimeType(string fileName)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue