fix(postit): repair Circles bindings + align UserSearchApi route #40

Merged
notazof merged 1 commit from feat/postit-acl-members into release/1.0.8-rc1 2026-08-20 07:24:20 +01:00
7 changed files with 17 additions and 71 deletions
Showing only changes of commit bd6ca9d11f - Show all commits

fix(postit): repair Circles bindings + align UserSearchApi route
All checks were successful
Dotnet build and test / build (pull_request) Successful in 12m51s

- CirclesPage: drop VisualRoot/MainWindow hack, switch to App.PushPageAsync(vm)
- CirclesPageViewModel: make OpenAddMemberAsync public so Avalonia XAML trampoline can call it
- AddCircleMemberDialog: bind SearchAsync/Add (drop Command suffix)
- UserSearchApiController: route under Constants.APIPrefix (= api/v1/user-search), matching the rest of Yavsc.Blogs controllers and the PostIt client's BlogsApiUrl default
Paul Schneider 2026-08-20 07:22:07 +01:00
Signed by: notazof
GPG key ID: 1DD5D838E5343B06

View file

@ -183,7 +183,6 @@ public partial class App : Application
// here — the parametrised ctors stay for direct test wiring.
services.AddTransient<PostAclDialog>();
services.AddTransient<AddCircleMemberDialog>();
// ViewModels
services.AddSingleton(settings);
services.AddSingleton<YavscApiClient>(api);

View file

@ -1,9 +1,10 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using PostIt.Services;
using Yavsc.Api.Client;
using Yavsc.Api.Client.Dtos;
@ -63,12 +64,6 @@ public partial class CirclesPageViewModel : ViewModelBase
[ObservableProperty]
public partial string StatusMessage { get; set; } = string.Empty;
/// <summary>
/// Raised when the user wants to add a member to the
/// currently selected circle. The view listens to this
/// event and opens <c>AddCircleMemberDialog</c>.
/// </summary>
public event EventHandler? AddMemberRequested;
public CirclesPageViewModel(CircleApiClient client)
{
@ -116,6 +111,16 @@ public partial class CirclesPageViewModel : ViewModelBase
}
}
[RelayCommand]
internal async Task OpenAddMemberAsync()
{
var app = Application.Current as App;
var services = app?.ServiceProvider;
var directory = services.GetRequiredService<IUserDirectory>();
AddCircleMemberDialogViewModel model =
new AddCircleMemberDialogViewModel(directory);
await app.PushPageAsync(model);
}
/// <summary>
/// Load the members of one of the caller's circles. The
/// server scopes the endpoint with a 404 when the circle
@ -231,22 +236,6 @@ public partial class CirclesPageViewModel : ViewModelBase
}
}
/// <summary>
/// Fire the <see cref="AddMemberRequested"/> event so
/// the view opens <c>AddCircleMemberDialog</c>. The view
/// forwards the dialog's <c>Confirmed</c> event back to
/// <see cref="OnAddMemberConfirmedAsync"/>.
/// </summary>
[RelayCommand]
public void OpenAddMember()
{
if (SelectedCircle is null)
{
StatusMessage = "Sélectionnez d'abord un cercle";
return;
}
AddMemberRequested?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Called by the view when the dialog confirms a

View file

@ -16,7 +16,7 @@
PlaceholderText="Nom ou email d'un utilisateur Yavsc..."
HorizontalAlignment="Stretch"/>
<Button Grid.Column="1" Content="Rechercher"
Command="{Binding SearchCommand}"
Command="{Binding SearchAsync}"
Margin="8,0,0,0"/>
</Grid>
@ -47,7 +47,7 @@
<TextBlock Grid.Column="0" Text="{Binding StatusMessage}"
VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="Ajouter"
Command="{Binding AddCommand}"
Command="{Binding Add}"
IsEnabled="{Binding Selected, Converter={x:Static ObjectConverters.IsNotNull}}"
Margin="0,0,8,0"/>
<Button Grid.Column="2" Content="Fermer"

View file

@ -25,12 +25,6 @@ public partial class AddCircleMemberDialog : ContentPage
InitializeComponent();
}
public AddCircleMemberDialog(IUserDirectory directory)
{
InitializeComponent();
DataContext = new AddCircleMemberDialogViewModel(directory);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);

View file

@ -70,7 +70,7 @@
FontWeight="Bold"
VerticalAlignment="Center"/>
<Button Content="Ajouter un membre"
Command="{Binding OpenAddMemberCommand}"/>
Command="{Binding OpenAddMemberAsync}"/>
</StackPanel>
<ListBox Grid.Row="1"
ItemsSource="{Binding Members}">

View file

@ -10,46 +10,10 @@ namespace PostIt.Views;
public partial class CirclesPage : ContentPage
{
private CirclesPageViewModel? _vm;
public CirclesPage()
{
InitializeComponent();
DataContextChanged += OnDataContextChanged;
}
private void OnDataContextChanged(object? sender, EventArgs e)
{
// Unsubscribe from the previous VM to avoid leaking
// handlers across navigation pushes / DataContext resets.
if (_vm is not null)
_vm.AddMemberRequested -= OnAddMemberRequested;
_vm = DataContext as CirclesPageViewModel;
if (_vm is not null)
_vm.AddMemberRequested += OnAddMemberRequested;
}
private void OnAddMemberRequested(object? sender, EventArgs e)
{
var app = Application.Current as App;
var services = app?.ServiceProvider;
if (services is null || _vm is null) return;
// Resolve the directory via DI. The dialog raises its
// own Confirmed event; the VM subscribes via the method
// below — we pass the VM in so the closure can call
// back into it without the dialog needing to know the
// type of its caller. EventHandler<UserSummary> wants a
// void return, so wrap the async VM method in a fire-
// and-forget helper.
var directory = services.GetRequiredService<IUserDirectory>();
var dialog = new AddCircleMemberDialog(directory);
dialog.ViewModel!.Confirmed += async (sender, picked) =>
await _vm.OnAddMemberConfirmedAsync(sender, picked);
if (this.VisualRoot is MainWindow window)
_ = window.NavRoot.PushAsync(dialog);
}
private void InitializeComponent()

View file

@ -26,7 +26,7 @@ namespace Yavsc.Blogs.Controllers
/// exposing it.</para>
/// </summary>
[Produces("application/json")]
[Route("api/user-search")]
[Route( Constants.APIPrefix + "/user-search")]
[Authorize]
public class UserSearchApiController : Controller
{