refacts and PostIt Nav

This commit is contained in:
Paul Schneider 2026-06-11 00:14:51 +01:00
commit e98bba4247
34 changed files with 315 additions and 200 deletions

View file

@ -10,6 +10,7 @@
<PackageVersion Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.9" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.9" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.DataAnnotations" Version="2.3.11" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.9" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.9" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9" />
@ -24,7 +25,6 @@
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.9" />
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="10.0.9" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication" Version="10.0.8" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.19.1" />
<PackageVersion Include="Microsoft.AspNetCore.Antiforgery" Version="2.3.11" />
<PackageVersion Include="Microsoft.AspNetCore.Razor" Version="2.3.0" />
@ -34,10 +34,8 @@
<PackageVersion Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="10.0.2" />
<PackageVersion Include="Google.Apis.Calendar.v3" Version="1.74.0.4154" />
<PackageVersion Include="Google.Apis.Compute.v1" Version="1.75.0.4157" />
<PackageVersion Include="IdentityModel.AspNetCore" Version="4.3.0" />
<PackageVersion Include="IdentityModel.OidcClient" Version="6.0.0" />
<PackageVersion Include="HigginsSoft.IdentityServer8" Version="8.0.5-preview-net9" />
@ -46,12 +44,9 @@
<PackageVersion Include="HigginsSoft.IdentityServer8.EntityFramework.Storage" Version="8.0.5-preview-net9" />
<PackageVersion Include="HigginsSoft.IdentityServer8.Security" Version="8.0.5-preview-net9" />
<PackageVersion Include="HigginsSoft.IdentityServer8.Storage" Version="8.0.5-preview-net9" />
<PackageVersion Include="Anthropic.SDK" Version="5.10.0" />
<PackageVersion Include="AsciiDocSharp" Version="0.2.0" />
<PackageVersion Include="AsciiDocSharp.Converters.Html" Version="0.2.0" />
<PackageVersion Include="Avalonia" Version="12.0.4" />
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
<PackageVersion Include="Avalonia.Desktop" Version="12.0.4" />
@ -60,16 +55,14 @@
<PackageVersion Include="Avalonia.Browser" Version="12.0.4" />
<PackageVersion Include="Avalonia.Android" Version="12.0.4" />
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.2" />
<PackageVersion Include="bootstrap" Version="5.3.8" />
<PackageVersion Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.2.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="Magick.NET-Q8-AnyCPU" Version="14.14.0" />
<PackageVersion Include="MailKit" Version="4.17.0" />
<PackageVersion Include="MimeKit" Version="4.17.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Json.NET" Version="1.0.33" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
<PackageVersion Include="PayPalMerchantSDK" Version="2.16.250" />
<PackageVersion Include="pazof.rules" Version="1.1.3" />
@ -81,6 +74,5 @@
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.2.1" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
</Project>

View file

@ -27,18 +27,18 @@ public partial class App : Application
{
desktop.MainWindow = new MainWindow
{
DataContext = new MainViewModel()
DataContext = new MainPageViewModel()
};
}
else if (ApplicationLifetime is IActivityApplicationLifetime singleViewFactoryApplicationLifetime)
{
singleViewFactoryApplicationLifetime.MainViewFactory = () => new MainView { DataContext = new MainViewModel() };
singleViewFactoryApplicationLifetime.MainViewFactory = () => new MainPage { DataContext = new MainPageViewModel() };
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
{
singleViewPlatform.MainView = new MainView
singleViewPlatform.MainView = new MainPage
{
DataContext = new MainViewModel()
DataContext = new MainPageViewModel()
};
}

View file

@ -26,7 +26,7 @@ public partial class Settings : ObservableObject
[ObservableProperty]
public partial string[] Scopes { get; set; }
public partial string[] Scopes { get; set; }
internal OidcClientOptions GetOidcClientOptions()
{
@ -40,26 +40,30 @@ public partial class Settings : ObservableObject
}
internal async Task Load(IStorageProvider storageProvider)
internal async Task Load()
{
var configFile = await storageProvider.TryGetFileFromPathAsync(
Path.Combine(AppContext.BaseDirectory, SettingsFileName));
String configPath =
Path.Combine(AppContext.BaseDirectory, SettingsFileName);
if (configFile is null)
FileInfo configFileInfo = new FileInfo(configPath);
if (!configFileInfo.Exists)
{
Console.Error.WriteLine("🩎 No settings file found.");
Console.Error.WriteLine($"🩎 Settings file not found at {configFileInfo.FullName}");
return; // no settings file
}
try {
using var stream = await configFile.OpenReadAsync();
Console.WriteLine($"🔎 Loading settings from {configFileInfo.FullName}");
using var reader = new StreamReader( stream);
try
{
using var stream = configFileInfo.OpenRead();
using var reader = new StreamReader(stream);
var json = await reader.ReadToEndAsync();
if (string.IsNullOrWhiteSpace(json))
{
Console.Error.WriteLine("🩎 Settings file is empty.");
return ;
return;
}
var settings = JsonSerializer.Deserialize<Settings>(json);
@ -67,14 +71,13 @@ public partial class Settings : ObservableObject
if (settings is null)
{
Console.Error.WriteLine("🩎 Settings file is invalid.");
return ;
return;
}
this.Authentication = settings.Authentication;
this.DarkMode = settings.DarkMode;
this.ApiUrl = settings.ApiUrl;
this.Scopes = settings.Scopes;
}
catch (Exception ex)
{

View file

@ -2,36 +2,34 @@ using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Microsoft.Extensions.DependencyInjection;
using PostIt.ViewModels;
using PostIt.Views;
namespace PostIt;
/// <summary>
/// Given a view model, returns the corresponding view if possible.
/// </summary>
[RequiresUnreferencedCode(
"Default implementation of ViewLocator involves reflection which may be trimmed away.",
Url = "https://docs.avaloniaui.net/docs/concepts/view-locator")]
public class ViewLocator : IDataTemplate
{
public Control? Build(object? param)
{
if (param is null)
return null;
private readonly IServiceProvider _services;
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
var type = Type.GetType(name);
if (type != null)
public ViewLocator(IServiceProvider services)
{
return (Control)Activator.CreateInstance(type)!;
_services = services;
}
return new TextBlock { Text = "Not Found: " + name };
public Control Build(object data)
{
return data switch
{
MainPageViewModel => _services.GetRequiredService<MainPage>(),
SettingsPageViewModel => _services.GetRequiredService<SettingsPage>(),
_ => new TextBlock { Text = $"No view for {data.GetType().Name}" }
};
}
public bool Match(object? data)
{
return data is ViewModelBase;
}
public bool Match(object data) => data is ViewModelBase;
}

View file

@ -0,0 +1,15 @@
namespace PostIt.ViewModels;
public class HomePageViewModel : ViewModelBase
{
private string _welcomeText = "Welcome to PostIt!";
public string WelcomeText
{
get => _welcomeText;
set => SetProperty(ref _welcomeText, value);
}
public override bool CanNavigateNext { get => true; protected set => throw new System.NotImplementedException(); }
public override bool CanNavigatePrevious { get => false; protected set => throw new System.NotImplementedException(); }
}

View file

@ -15,9 +15,15 @@ using Avalonia.Styling;
namespace PostIt.ViewModels;
public partial class MainViewModel : ViewModelBase
public partial class MainPageViewModel : ViewModelBase
{
[ObservableProperty]
public partial string Title { get; set; }
[ObservableProperty]
public partial ViewModelBase? CurrentViewModel { get; set; }
public SettingsPageViewModel SettingsModel { get; }
[ObservableProperty]
public partial string StatusMessage { get; set; }
@ -34,18 +40,20 @@ public partial class MainViewModel : ViewModelBase
public partial ObservableCollection<BlogPost> FilteredPosts { get; set; }
[ObservableProperty]
public partial BlogPost? SelectedPost{ get; set; }
public partial BlogPost? SelectedPost { get; set; }
[ObservableProperty]
public partial bool IsBusy{ get; set; }
public partial bool IsBusy { get; set; }
[ObservableProperty]
ThemeVariant themeVariant = ThemeVariant.Default;
[ObservableProperty]
public partial Settings Settings { get; private set; }
public override bool CanNavigateNext { get => throw new NotImplementedException(); protected set => throw new NotImplementedException(); }
public override bool CanNavigatePrevious { get => throw new NotImplementedException(); protected set => throw new NotImplementedException(); }
public MainViewModel()
public MainPageViewModel()
{
SearchText = string.Empty;
Posts = new ObservableCollection<BlogPost>();
@ -55,6 +63,9 @@ public partial class MainViewModel : ViewModelBase
IsBusy = false;
StatusMessage = "Ready";
Settings = new Settings();
Title = "PostIt";
CurrentViewModel = this;
SettingsModel = new SettingsPageViewModel();
}
partial void OnSearchTextChanged(string value)
@ -105,7 +116,7 @@ public partial class MainViewModel : ViewModelBase
try
{
var loginWin = new PostIt.Views.LoginWindow();
var loginWin = new PostIt.Views.LoginPage();
var result = await loginWin.StartLoginAsync(Settings.GetOidcClientOptions());
if (result is not null && !result.IsError && !string.IsNullOrWhiteSpace(result.AccessToken))
@ -202,6 +213,12 @@ public partial class MainViewModel : ViewModelBase
StatusMessage = "New blog post ready.";
}
[RelayCommand]
internal void OpenSettings()
{
// Appeler la méthode OpenSettings de la vue MainWindow
CurrentViewModel = SettingsModel;
}
private async Task RefreshPostsAsync()
{
@ -299,6 +316,7 @@ public partial class MainViewModel : ViewModelBase
return payload;
}
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true

View file

@ -2,7 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace PostIt.ViewModels;
public partial class SettingsViewModel : ViewModelBase
public partial class SettingsPageViewModel : ViewModelBase
{
[ObservableProperty]
public partial bool DarkMode { get; set; }
@ -15,5 +15,6 @@ public partial class SettingsViewModel : ViewModelBase
[ObservableProperty]
public partial string ClientSecret { get; set; }
public override bool CanNavigateNext { get => false; protected set => throw new System.NotImplementedException(); }
public override bool CanNavigatePrevious { get => true; protected set => throw new System.NotImplementedException(); }
}

View file

@ -5,4 +5,15 @@ namespace PostIt.ViewModels;
public abstract partial class ViewModelBase : ObservableObject
{
/// <summary>
/// Gets if the user can navigate to the next page
/// </summary>
public abstract bool CanNavigateNext { get; protected set; }
/// <summary>
/// Gets if the user can navigate to the previous page
/// </summary>
public abstract bool CanNavigatePrevious { get; protected set; }
}

View file

@ -0,0 +1,16 @@
<ContentPage xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PostIt.Views.HomePage"
Header="Home">
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="12">
<TextBlock Text="Welcome to the Home Page"
FontSize="22"
FontWeight="SemiBold"
HorizontalAlignment="Center"/>
<Button Content="Login"
Click="OnLoginClick"
HorizontalAlignment="Center"/>
</StackPanel>
</ContentPage>

View file

@ -0,0 +1,23 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using System.Threading.Tasks;
namespace PostIt.Views;
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
}
private async void OnLoginClick(object? sender, RoutedEventArgs e)
{
if (Navigation is not null)
await Navigation.PushModalAsync(new LoginPage());
}
}

View file

@ -0,0 +1,37 @@
<ContentPage xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PostIt.Views.LoginPage"
Header="Login">
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="20">
<TextBlock Text="Sign In"
FontSize="24"
HorizontalAlignment="Center"/>
<StackPanel Spacing="4">
<TextBlock Text="Email"/>
<TextBox Name="EmailBox"
PlaceholderText="Enter your email"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Password"/>
<TextBox Name="PasswordBox"
PlaceholderText="Enter your password"
PasswordChar="•"/>
</StackPanel>
<Button Content="Login"
Click="OnLoginClick"/>
<Button Content="Cancel"
Click="OnCancelClick"/>
<TextBlock Name="StatusText" Text="Starting authentication..." />
<ProgressBar IsIndeterminate="True" />
</StackPanel>
</ContentPage>

View file

@ -0,0 +1,51 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using IdentityModel.OidcClient;
using PostIt.Services;
namespace PostIt.Views;
public partial class LoginPage : ContentPage
{
private TaskCompletionSource<LoginResult?> _tcs = new();
public Settings Settings { get; }
public LoginPage()
{
this.Settings = new Settings();
InitializeComponent();
}
public async Task<LoginResult?> StartLoginAsync(OidcClientOptions options)
{
try
{
Settings.Load().Wait();
var client = new OidcClient(options);
var loginResult = await client.LoginAsync(new LoginRequest());
return loginResult;
}
catch (Exception)
{
return null;
}
}
private async void OnLoginClick(object? sender, RoutedEventArgs e)
{
// This is where you specify your actual login auth logic
if (Navigation is not null)
await Navigation.PopModalAsync();
}
private async void OnCancelClick(object? sender, RoutedEventArgs e)
{
// Cancel button dismisses all open modals
if (Navigation is not null)
await Navigation.PopAllModalsAsync();
}
}

View file

@ -1,13 +0,0 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PostIt.Views.LoginWindow"
Width="480" Height="160" WindowStartupLocation="CenterOwner"
Title="Login">
<StackPanel Margin="12" Spacing="12">
<TextBlock Name="StatusText" Text="Starting authentication..." />
<ProgressBar IsIndeterminate="True" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Name="CancelButton" Content="Cancel" />
</StackPanel>
</StackPanel>
</Window>

View file

@ -1,33 +0,0 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using IdentityModel.OidcClient;
using PostIt.Services;
namespace PostIt.Views;
public partial class LoginWindow : Window
{
private TaskCompletionSource<LoginResult?> _tcs = new();
public LoginWindow()
{
InitializeComponent();
CancelButton.Click += (_, __) => Close(null);
}
public async Task<LoginResult?> StartLoginAsync(OidcClientOptions options)
{
try
{
var client = new OidcClient(options);
var loginResult = await client.LoginAsync(new LoginRequest());
return loginResult;
}
catch (Exception)
{
return null;
}
}
}

View file

@ -1,4 +1,4 @@
<UserControl xmlns="https://github.com/avaloniaui"
<NavigationPage xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -6,18 +6,17 @@
xmlns:models="using:PostIt.Models"
xmlns:views="using:PostIt.Views"
xmlns:AvaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="600"
x:Class="PostIt.Views.MainView"
x:DataType="vm:MainViewModel">
mc:Ignorable="d"
x:Class="PostIt.Views.MainPage"
x:DataType="vm:MainPageViewModel" HorizontalAlignment="Center" VerticalAlignment="Center" >
<Design.DataContext>
<vm:MainViewModel />
<vm:MainPageViewModel />
</Design.DataContext>
<StackPanel Margin="12" Spacing="12">
<StackPanel Margin="12" Spacing="12" HorizontalAlignment="Center" >
<TextBlock Text="PostIt Blog API Interface" FontSize="20" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Spacing="8">
<StackPanel Orientation="Horizontal" Spacing="8" >
<Button Command="{Binding Login}" Content="Acquire token" />
<Button Command="{Binding LoadPosts}" Content="Load posts" />
<Button Command="{Binding Search}" Content="Filter" />
@ -26,7 +25,6 @@
<Button Command="{Binding Delete}" Content="Delete" />
</StackPanel>
<Grid ColumnDefinitions="2*,3*" RowDefinitions="*" ColumnSpacing="12">
<Border BorderBrush="Gray" BorderThickness="1" Padding="8">
<ListBox ItemsSource="{Binding FilteredPosts}" SelectedItem="{Binding SelectedPost, Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
@ -56,6 +54,6 @@
<TextBlock Text="{Binding StatusMessage}" Foreground="Gray" />
</StackPanel>
</Border>
</Grid>
</StackPanel>
</UserControl>
</NavigationPage>

View file

@ -4,9 +4,9 @@ using Avalonia.Controls;
namespace PostIt.Views;
public partial class MainView : UserControl
public partial class MainPage : NavigationPage
{
public MainView()
public MainPage()
{
InitializeComponent();
}

View file

@ -0,0 +1,26 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PostIt.ViewModels;
public partial class MainWindowViewModel : ObservableObject
{
// Stocke le ViewModel de la page actuellement affichée
[ObservableProperty]
private object? _currentPage;
// Instances des pages pour éviter de les recréer à chaque fois (Optionnel)
private readonly HomePageViewModel _homePage = new();
private readonly SettingsPageViewModel _settingsPage = new();
public MainWindowViewModel()
{
// Définir la page de démarrage par défaut
CurrentPage = _homePage;
}
[RelayCommand]
private void NavigateToHome() => CurrentPage = _homePage;
[RelayCommand]
private void NavigateToSettings() => CurrentPage = _settingsPage;
}

View file

@ -1,13 +1,14 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:PostIt.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="using:PostIt.Views"
x:DataType="vm:MainPageViewModel"
x:Class="PostIt.Views.MainWindow"
x:DataType="vm:MainViewModel"
Icon="/Assets/yavsc-logo.ico"
Title="PostIt"
>
<views:MainView x:Name="MainView"/>
<!-- Association des ViewModels aux Vues graphiques (UserControls) -->
<NavigationPage>
<views:HomePage/>
</NavigationPage>
</Window>

View file

@ -1,35 +1,11 @@
using Avalonia.Controls;
using Avalonia.Styling;
using PostIt.ViewModels;
using System;
using System.Threading.Tasks;
namespace PostIt.Views;
public partial class MainWindow : Window
{
internal Settings Settings { get; private set; }
public MainWindow()
{
InitializeComponent();
this.Settings = new Settings();
}
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
if (DataContext is MainViewModel vm)
{
Task.Run(async () => await this.Settings.Load(this.StorageProvider)).Wait();
this.RequestedThemeVariant = Settings.DarkMode ? ThemeVariant.Dark : ThemeVariant.Light;
MainView.DataContext = new MainViewModel();
vm.Settings.Load(this.StorageProvider).Wait();
}
}
}

View file

@ -1,11 +1,10 @@
<Window
<ContentPage
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="cl:avalonia.Controls"
x:Class="PostIt.Views.SettingsView"
x:Class="PostIt.Views.SettingsPage"
xmlns:vm="using:PostIt.ViewModels"
x:DataType="vm:SettingsViewModel"
Title="Settings"
x:DataType="vm:SettingsPageViewModel"
Width="400"
Height="300">
<Grid>
@ -26,4 +25,4 @@
<TextBlock Grid.Row="4" Text="ClientSecret"/>
<TextBox Grid.Row="5" x:Name="ClientSecretTextBox" Text="{Binding ClientSecret, Mode=TwoWay}"/>
</Grid>
</Window>
</ContentPage>

View file

@ -3,9 +3,9 @@
using Avalonia.Controls;
namespace PostIt.Views;
public partial class SettingsView: Window
public partial class SettingsPage: ContentPage
{
public SettingsView()
public SettingsPage()
{
InitializeComponent();
}

View file

@ -7,8 +7,8 @@ namespace Yavsc
{
public interface IBlogPostPayLoad
{
string? Article { get; set; }
string? Photo { get; set; }
string Article { get; set; }
string Photo { get; set; }
}
public interface IBlogPost : IBlogPostPayLoad, ITrackedEntity, IIdentified<long>, ITitle

View file

@ -7,10 +7,12 @@ namespace Yavsc.Models.FileSystem
{
[ValidRemoteUserFilePath]
[StringLength(512)]
[Required]
public required string Id { get; set; }
[StringLength(512)]
[ValidRemoteUserFilePath]
[Required]
public required string To { get; set; }
}

View file

@ -1,5 +1,5 @@
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace Yavsc.Abstract.IT {
@ -10,7 +10,7 @@ public class CiBuildSettings
/// The global process environment variables
/// </summary>
/// <value></value>
[JsonProperty("env")]
[JsonPropertyName("env")]
public string[] Environment { get; set; }
/// <summary>
@ -18,7 +18,7 @@ public class CiBuildSettings
/// </summary>
/// <value></value>
[Required]
[JsonPropertyAttribute("build")]
[JsonPropertyName("build")]
public CommandPipe Build { get; set; }
/// <summary>
@ -27,7 +27,7 @@ public class CiBuildSettings
/// must end ok in order to launch the build.
/// </summary>
/// <value></value>
[JsonPropertyAttribute("prepare")]
[JsonPropertyName("prepare")]
public CommandPipe Prepare { get; set; }
/// <summary>
@ -37,14 +37,14 @@ public class CiBuildSettings
/// only fired on successful build.
/// </summary>
/// <value></value>
[JsonPropertyAttribute("post_build")]
[JsonPropertyName("post_build")]
public CommandPipe PostBuild { get; set; }
/// <summary>
/// Additional emails, as dest of notifications
/// </summary>
/// <value></value>
[JsonPropertyAttribute("emails")]
[JsonPropertyName("emails")]
public string[] Emails { get; set; }
}

View file

@ -1,7 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace Yavsc.Abstract.IT
{
@ -13,17 +13,17 @@ namespace Yavsc.Abstract.IT
public class Command
{
[Required]
[JsonPropertyAttribute("path")]
[JsonPropertyName("path")]
public string Path { get; set; }
[JsonPropertyAttribute("args")]
[JsonPropertyName("args")]
public string[] Args { get; set; }
/// <summary>
/// Specific variables for this process
/// </summary>
/// <value></value>
[JsonPropertyAttribute("env")]
[JsonPropertyName("env")]
public string[] Environment { get; set; }
public virtual Process Start(string workingDir=null, bool redirectInput=false, bool redirectOutput=false)

View file

@ -1,8 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace Yavsc.Abstract.IT
{
@ -10,11 +8,11 @@ namespace Yavsc.Abstract.IT
{
[JsonPropertyAttribute("pipe")]
[JsonPropertyName("pipe")]
public Command[] Pipe { get; set; }
[JsonPropertyAttribute("working_dir")]
[JsonPropertyName("working_dir")]
public string WorkingDir { get; set; }
public virtual int Run()

View file

@ -20,7 +20,7 @@ namespace Yavsc.Models.IT.Fixing
ErrorMessageResourceName="TitleSizeError")]
public string Title { get; set; }
[YaStringLength(10240,
[StringLength(10240,
ErrorMessageResourceType=typeof(Yavsc.Models.IT.Fixing.Bug),
ErrorMessageResourceName="DescSizeError")]
public string Description { get; set; }

View file

@ -5,8 +5,8 @@ namespace Yavsc
public interface ITrackedEntity
{
DateTime DateCreated { get; set; }
string? UserCreated { get; set; }
string UserCreated { get; set; }
DateTime DateModified { get; set; }
string? UserModified { get; set; }
string UserModified { get; set; }
}
}

View file

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net9.0</TargetFrameworks>
<TargetFrameworks>net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Description> A shared model for a little client/server app, dealing about establishing some contract, between some human client and provider.
</Description>
@ -8,8 +8,6 @@
<RepositoryUrl>https://github.com/pazof/yavsc</RepositoryUrl>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<Library>true</Library>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
</Project>

View file

@ -40,7 +40,7 @@ namespace cli.Commands
});
loginCommand.OnExecute(async () =>
{
string? authHostName = Program.AppConfiguration.GetRequiredSection("ConnectionSettings:ServerApi")["Authority"];
string authHostName = Program.AppConfiguration.GetRequiredSection("ConnectionSettings:ServerApi")["Authority"];
throw new NotImplementedException();
/*

View file

@ -5,7 +5,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
internal class Program
{
public static IHost? AppHost { get; private set; }
public static IHost AppHost { get; private set; }
public static IConfigurationRoot AppConfiguration { get; private set; }
public static IHostEnvironment AppEnvironment { get; private set; }

View file

@ -13,7 +13,6 @@
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Yavsc.Abstract\Yavsc.Abstract.csproj" />

View file

@ -18,7 +18,7 @@ public class PostItViewModelTests
[Fact]
public void SearchCommand_filters_posts_by_title_article_or_author()
{
var viewModel = new MainViewModel();
var viewModel = new MainPageViewModel();
viewModel.Posts.Add(new BlogPost { Id = 1, Title = "First post", Article = "Hello world", AuthorId = "alice" });
viewModel.Posts.Add(new BlogPost { Id = 2, Title = "Second post", Article = "Nothing here", AuthorId = "bob" });

View file

@ -21,7 +21,6 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>