refacto blogPost
Some checks failed
Dotnet build and test / log-the-inputs (push) Has been cancelled
Dotnet build and test / build (push) Has been cancelled

This commit is contained in:
Paul Schneider 2026-08-03 01:32:59 +01:00
commit b25e0e842e
No known key found for this signature in database
GPG key ID: 1E66C65EE2B46F1B
11 changed files with 85 additions and 52 deletions

View file

@ -1,6 +1,7 @@
using PostIt.Models; using PostIt.Models;
using PostIt.Services; using PostIt.Services;
using PostIt.ViewModels; using PostIt.ViewModels;
using Yavsc.Models;
namespace PostIt.Tests; namespace PostIt.Tests;

View file

@ -24,7 +24,7 @@ public partial class App : Application
/// binding sink with a cross-thread exception inside /// binding sink with a cross-thread exception inside
/// <c>DataValidationErrors.SetErrors</c>. /// <c>DataValidationErrors.SetErrors</c>.
/// </summary> /// </summary>
public IServiceProvider? Services { get; private set; } public IServiceProvider? ServiceProvider { get; private set; }
private MainWindow window; private MainWindow window;
public App() public App()
{ {
@ -91,19 +91,17 @@ public partial class App : Application
services.AddSingleton(sessionStatus); services.AddSingleton(sessionStatus);
services.AddTransient<SessionStatusBanner>(); services.AddTransient<SessionStatusBanner>();
var provider = services.BuildServiceProvider(); ServiceProvider = services.BuildServiceProvider();
// Bind the canonical Settings to the static accessor so any // Bind the canonical Settings to the static accessor so any
// code path that can't easily take a constructor parameter // code path that can't easily take a constructor parameter
// (designer surfaces, Avalonia data templates) still gets // (designer surfaces, Avalonia data templates) still gets
// the same instance the rest of the app is using. Idempotent: // the same instance the rest of the app is using. Idempotent:
// re-binding from a second App boot (tests) is a no-op. // re-binding from a second App boot (tests) is a no-op.
Settings.BindToServiceProvider(provider); Settings.BindToServiceProvider(ServiceProvider);
Services = provider;
DataTemplates.Clear(); DataTemplates.Clear();
DataTemplates.Add(new ViewLocator(provider)); DataTemplates.Add(new ViewLocator(ServiceProvider));
// Wire the Settings singleton onto the SettingsPage singleton // Wire the Settings singleton onto the SettingsPage singleton
// once, at composition time. The page is registered as a // once, at composition time. The page is registered as a
@ -113,7 +111,7 @@ public partial class App : Application
// DataContext, and the TwoWay bindings inside the page keep // DataContext, and the TwoWay bindings inside the page keep
// mutating the same in-memory Settings instance that the rest // mutating the same in-memory Settings instance that the rest
// of the app reads (OidcClientOptions construction, etc.). // of the app reads (OidcClientOptions construction, etc.).
provider.GetRequiredService<SettingsPage>().DataContext = settings; ServiceProvider.GetRequiredService<SettingsPage>().DataContext = settings;
// Settings.DarkMode was previously a dead field: it round- // Settings.DarkMode was previously a dead field: it round-
// tripped through the settings file and the SettingsPage // tripped through the settings file and the SettingsPage
@ -134,8 +132,8 @@ public partial class App : Application
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
var homePage = provider.GetRequiredService<HomePage>(); var homePage = ServiceProvider.GetRequiredService<HomePage>();
homePage.DataContext = provider.GetRequiredService<HomePageViewModel>(); homePage.DataContext = ServiceProvider.GetRequiredService<HomePageViewModel>();
window = new MainWindow(); window = new MainWindow();
window.SessionBanner.DataContext = sessionStatus; window.SessionBanner.DataContext = sessionStatus;
@ -155,8 +153,8 @@ public partial class App : Application
{ {
var w = (MainWindow)((IClassicDesktopStyleApplicationLifetime)ApplicationLifetime!).MainWindow!; var w = (MainWindow)((IClassicDesktopStyleApplicationLifetime)ApplicationLifetime!).MainWindow!;
var nav = w.NavRoot; var nav = w.NavRoot;
var hp = provider.GetRequiredService<HomePage>(); var hp = ServiceProvider.GetRequiredService<HomePage>();
hp.DataContext = provider.GetRequiredService<HomePageViewModel>(); hp.DataContext = ServiceProvider.GetRequiredService<HomePageViewModel>();
_ = nav.PopToRootAsync(); _ = nav.PopToRootAsync();
}; };
@ -187,7 +185,7 @@ public partial class App : Application
sessionStatus.OpenSettingsRequested += () => sessionStatus.OpenSettingsRequested += () =>
{ {
var w = (MainWindow)((IClassicDesktopStyleApplicationLifetime)ApplicationLifetime!).MainWindow!; var w = (MainWindow)((IClassicDesktopStyleApplicationLifetime)ApplicationLifetime!).MainWindow!;
var settingsPage = provider.GetRequiredService<SettingsPage>(); var settingsPage = ServiceProvider.GetRequiredService<SettingsPage>();
var stack = w.NavRoot.NavigationStack; var stack = w.NavRoot.NavigationStack;
if (stack.Count > 0 && ReferenceEquals(stack[stack.Count - 1], settingsPage)) if (stack.Count > 0 && ReferenceEquals(stack[stack.Count - 1], settingsPage))
{ {
@ -196,13 +194,13 @@ public partial class App : Application
_ = w.NavRoot.PushAsync(settingsPage); _ = w.NavRoot.PushAsync(settingsPage);
}; };
window.Opened += async (_, _) => await BootAsync(provider, api); window.Opened += async (_, _) => await BootAsync(ServiceProvider, api);
} }
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView) else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView)
{ {
singleView.MainView = new MainWindow singleView.MainView = new MainWindow
{ {
DataContext = provider.GetRequiredService<HomePageViewModel>() DataContext = ServiceProvider.GetRequiredService<HomePageViewModel>()
}; };
} }
} }
@ -243,8 +241,8 @@ public partial class App : Application
public static async Task PushMainPageAsync() public static async Task PushMainPageAsync()
{ {
var app = (App)Current; var app = (App)Current;
var mainVm = app.Services.GetRequiredService<MainPageViewModel>(); var mainVm = app.ServiceProvider.GetRequiredService<MainPageViewModel>();
var mainPage = app.Services.GetRequiredService<MainPage>(); var mainPage = app.ServiceProvider.GetRequiredService<MainPage>();
mainPage.DataContext = mainVm; mainPage.DataContext = mainVm;
await app.window.FindControl<NavigationPage>("NavRoot").PushAsync(mainPage).ConfigureAwait(true); await app.window.FindControl<NavigationPage>("NavRoot").PushAsync(mainPage).ConfigureAwait(true);
} }

View file

@ -1,16 +1,37 @@
using System; using System;
using Yavsc.Abstract.Identity;
using Yavsc.Abstract.Identity.Security;
using Yavsc.Blogspot;
namespace PostIt.Models; namespace PostIt.Models;
public class BlogPost public class BlogPost : IBlogPost
{ {
public string AuthorId { get; set; }
public IApplicationUser Author { get; set; }
public string Article { get; set ; }
public string Photo { get; set ; }
public long Id { get; set ; } public long Id { get; set ; }
public string Title { get; set; } = string.Empty;
public string? Article { get; set; }
public string? Photo { get; set; }
public string? AuthorId { get; set; }
public DateTime DateCreated { get; set ; } public DateTime DateCreated { get; set ; }
public string? UserCreated { get; set; } public string UserCreated { get; set ; }
public DateTime DateModified { get; set ; } public DateTime DateModified { get; set ; }
public string? UserModified { get; set; } public string UserModified { get; set ; }
public string Title { get; set ; }
public bool AuthorizeCircle(long circleId)
{
throw new NotImplementedException();
}
public ICircleAuthorization[] GetACL()
{
throw new NotImplementedException();
}
public string[] GetTags()
{
throw new NotImplementedException();
}
} }

View file

@ -1,4 +1,5 @@
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using PostIt; using PostIt;
using PostIt.Services; using PostIt.Services;
namespace PostIt.ViewModels; namespace PostIt.ViewModels;
@ -7,6 +8,7 @@ public class HomePageViewModel : ViewModelBase
{ {
public YavscApiClient Api { get; } public YavscApiClient Api { get; }
public Settings Settings { get; } public Settings Settings { get; }
public SessionStatusViewModel SessionStatus { get; }
private string _welcomeText = "Welcome to PostIt!"; private string _welcomeText = "Welcome to PostIt!";
public string WelcomeText public string WelcomeText
@ -18,10 +20,12 @@ public class HomePageViewModel : ViewModelBase
public override bool CanNavigateNext { get => true; protected set => throw new System.NotImplementedException(); } public override bool CanNavigateNext { get => true; protected set => throw new System.NotImplementedException(); }
public override bool CanNavigatePrevious { get => false; protected set => throw new System.NotImplementedException(); } public override bool CanNavigatePrevious { get => false; protected set => throw new System.NotImplementedException(); }
public HomePageViewModel(YavscApiClient api, Settings settings) public HomePageViewModel(YavscApiClient api, Settings settings, SessionStatusViewModel sessionStatus)
{ {
Api = api; Api = api;
Settings = settings; Settings = settings;
SessionStatus = sessionStatus;
} }
public RelayCommand OpenBlogs { get; set; } = new RelayCommand(() => App.PushMainPageAsync()); public RelayCommand OpenBlogs { get; set; } = new RelayCommand(() => App.PushMainPageAsync());
/// <summary> /// <summary>
@ -33,5 +37,8 @@ public class HomePageViewModel : ViewModelBase
/// (thread-safe dispatcher marshalling on PropertyChanged) — a /// (thread-safe dispatcher marshalling on PropertyChanged) — a
/// designer-only duplicate instance is therefore harmless. /// designer-only duplicate instance is therefore harmless.
/// </summary> /// </summary>
public HomePageViewModel() : this(null!, new Settings()) { } public HomePageViewModel() : this(null!, new Settings(), new SessionStatusViewModel())
{
}
} }

View file

@ -28,7 +28,7 @@ public partial class MainPage : ContentPage
// Resolve via the App's DI container so the page gets // Resolve via the App's DI container so the page gets
// the canonical services (Api client, settings, ...). // the canonical services (Api client, settings, ...).
var app = Application.Current as App; var app = Application.Current as App;
var services = app?.Services; var services = app?.ServiceProvider;
if (services is null) return; if (services is null) return;
var page = services.GetRequiredService<SignaturePage>(); var page = services.GetRequiredService<SignaturePage>();

View file

@ -1,19 +0,0 @@

using Yavsc.Abstract.Identity;
namespace Yavsc
{
public interface IBlogPostPayLoad
{
string Article { get; set; }
string Photo { get; set; }
}
public interface IBlogPost : IBlogPostPayLoad, ITrackedEntity, IIdentified<long>, ITitle
{
string AuthorId { get; set; }
IApplicationUser Author { get; }
}
}

View file

@ -0,0 +1,15 @@
using Yavsc.Abstract.Identity;
using Yavsc.Abstract.Identity.Security;
using Yavsc.Interfaces;
namespace Yavsc.Blogspot
{
public interface IBlogPost : IBlogPostPayLoad, ICircleAuthorized, ITaggable<long>, ITrackedEntity, IIdentified<long>, ITitle
{
string AuthorId { get; set; }
IApplicationUser Author { get; }
}
}

View file

@ -0,0 +1,9 @@
namespace Yavsc.Blogspot
{
public interface IBlogPostPayLoad
{
string Article { get; set; }
string Photo { get; set; }
}
}

View file

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Yavsc.Blogspot;
using Yavsc.Models.Blog; using Yavsc.Models.Blog;
using Yavsc.Server.Exceptions; using Yavsc.Server.Exceptions;
using Yavsc.Server.Helpers; using Yavsc.Server.Helpers;

View file

@ -3,15 +3,14 @@ using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json; using Newtonsoft.Json;
using Yavsc.Abstract.Identity; using Yavsc.Abstract.Identity;
using Yavsc.Abstract.Identity.Security; using Yavsc.Abstract.Identity.Security;
using Yavsc.Interfaces;
using Yavsc.Models.Access; using Yavsc.Models.Access;
using Yavsc.Models.Relationship; using Yavsc.Models.Relationship;
using Yavsc.Blogspot;
namespace Yavsc.Models.Blog namespace Yavsc.Models.Blog
{ {
public class BlogPost : public class BlogPost : IBlogPost
IBlogPost, ICircleAuthorized, ITaggable<long>
{ {
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Display(Name = "Identifiant du post")] [Display(Name = "Identifiant du post")]

View file

@ -10,6 +10,7 @@ using Yavsc.Server.Helpers;
using Yavsc.Services; using Yavsc.Services;
using Yavsc.ViewModels.Auth; using Yavsc.ViewModels.Auth;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Yavsc.Blogspot;
public class BlogSpotService public class BlogSpotService
{ {