Navigation and DI
This commit is contained in:
parent
e526b050ed
commit
ee2c8452ac
10 changed files with 87 additions and 56 deletions
|
|
@ -1,7 +1,6 @@
|
|||
<Project>
|
||||
<!-- Pull in shared package versions from the repository root. -->
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Packages.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
|
||||
<!-- PostIt-product-specific versions -->
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia" Version="12.0.4" />
|
||||
|
|
@ -13,6 +12,7 @@
|
|||
<PackageVersion Include="Avalonia.Themes.Fluent" Version="12.0.4" />
|
||||
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.2" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.9" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Browser" Version="1.8.0" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PostIt.Services;
|
||||
using PostIt.ViewModels;
|
||||
using PostIt.Views;
|
||||
|
|
@ -22,49 +25,52 @@ public partial class App : Application
|
|||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
// Single-instance hand-off: if we were launched with a
|
||||
// custom-scheme URL on the command line, we are a 2nd
|
||||
// instance whose job is to forward the OAuth2 callback
|
||||
// URL to the running PostIt process and exit. The first
|
||||
// instance is parked inside CustomSchemeBrowser.InvokeAsync
|
||||
// waiting on the named pipe for exactly this message.
|
||||
if (TryHandOffCustomSchemeUrl())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (TryHandOffCustomSchemeUrl()) return;
|
||||
|
||||
var settings = new Settings();
|
||||
// Synchronous: Settings.Load is intentionally non-async so we
|
||||
// don't deadlock the Avalonia UI thread. .Wait() on an async
|
||||
// method would block here forever on the await inside the
|
||||
// file read.
|
||||
settings.Load();
|
||||
|
||||
var tokenStore = new TokenStore(System.IO.Path.Combine(
|
||||
System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData),
|
||||
"PostIt", "tokens.json"));
|
||||
var client = new BlogApiClient(new YavscApiClient(settings, tokenStore));
|
||||
|
||||
var api = new YavscApiClient(settings, tokenStore);
|
||||
var client = new BlogApiClient(api);
|
||||
|
||||
// Configure DI
|
||||
var services = new ServiceCollection();
|
||||
|
||||
// Vues
|
||||
services.AddTransient<MainPage>();
|
||||
services.AddTransient<LoginPage>();
|
||||
services.AddTransient<SettingsPage>();
|
||||
services.AddTransient<HomePage>();
|
||||
|
||||
// ViewModels
|
||||
services.AddSingleton(settings);
|
||||
services.AddSingleton(api);
|
||||
services.AddSingleton(client);
|
||||
services.AddTransient<MainPageViewModel>();
|
||||
services.AddTransient<SettingsPageViewModel>();
|
||||
services.AddTransient<LoginPageViewModel>();
|
||||
services.AddTransient<HomePageViewModel>();
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
// Injecter le ViewLocator avec le provider
|
||||
DataTemplates.Clear();
|
||||
DataTemplates.Add(new ViewLocator(provider));
|
||||
|
||||
// Page de départ
|
||||
var homeVm = provider.GetRequiredService<HomePageViewModel>();
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainPageViewModel(client, settings)
|
||||
};
|
||||
desktop.MainWindow = new MainWindow { DataContext = homeVm };
|
||||
}
|
||||
else if (ApplicationLifetime is IActivityApplicationLifetime singleViewFactoryApplicationLifetime)
|
||||
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView)
|
||||
{
|
||||
singleViewFactoryApplicationLifetime.MainViewFactory = () =>
|
||||
{
|
||||
return new MainPage { DataContext = new MainPageViewModel(client, settings) };
|
||||
};
|
||||
singleView.MainView = new MainWindow { DataContext = homeVm };
|
||||
}
|
||||
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
|
||||
{
|
||||
singleViewPlatform.MainView = new MainPage
|
||||
{
|
||||
DataContext = new MainPageViewModel(client, settings)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool TryHandOffCustomSchemeUrl()
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
</PackageReference>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||
<PackageReference Include="IdentityModel.OidcClient" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||
<ProjectReference Include="../../Yavsc.Abstract/Yavsc.Abstract.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public class ViewLocator : IDataTemplate
|
|||
MainPageViewModel => _services.GetRequiredService<MainPage>(),
|
||||
SettingsPageViewModel => _services.GetRequiredService<SettingsPage>(),
|
||||
LoginPageViewModel => _services.GetRequiredService<LoginPage>(),
|
||||
HomePageViewModel => _services.GetRequiredService<HomePage>(),
|
||||
_ => new TextBlock { Text = $"No view for {data.GetType().Name}" }
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,28 @@
|
|||
|
||||
namespace PostIt.ViewModels;
|
||||
using PostIt;
|
||||
using PostIt.Services;
|
||||
using PostIt.ViewModels;
|
||||
|
||||
public class HomePageViewModel : ViewModelBase
|
||||
{
|
||||
public YavscApiClient Api { get; }
|
||||
public Settings Settings { get; }
|
||||
|
||||
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(); }
|
||||
|
||||
public HomePageViewModel(YavscApiClient api, Settings settings)
|
||||
{
|
||||
Api = api;
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
// Constructeur sans arg pour le designer Avalonia
|
||||
public HomePageViewModel() : this(null!, null!) { }
|
||||
}
|
||||
|
|
@ -111,6 +111,10 @@ public partial class LoginPageViewModel : ViewModelBase
|
|||
private set => this.SetProperty(ref _isBusy, value);
|
||||
}
|
||||
|
||||
private bool _LoginSuccess;
|
||||
public bool LoginSuccess { get => _isBusy;
|
||||
private set => this.SetProperty(ref _LoginSuccess, value); }
|
||||
|
||||
/// <summary>
|
||||
/// Optional override used by tests. When set, this factory is called
|
||||
/// instead of <see cref="Platform.CreateBrowser"/> to obtain the
|
||||
|
|
@ -132,6 +136,7 @@ public partial class LoginPageViewModel : ViewModelBase
|
|||
/// constructing a fresh one.
|
||||
/// </summary>
|
||||
public YavscApiClient? ApiClientOverride { get; set; }
|
||||
public Action LoginSucceeded { get; internal set; }
|
||||
|
||||
private YavscApiClient? _api;
|
||||
|
||||
|
|
@ -171,7 +176,7 @@ public partial class LoginPageViewModel : ViewModelBase
|
|||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
|
||||
LoginSuccess = false;
|
||||
if (SettingsLoadOverride is not null)
|
||||
await SettingsLoadOverride().ConfigureAwait(false);
|
||||
else
|
||||
|
|
@ -222,6 +227,8 @@ public partial class LoginPageViewModel : ViewModelBase
|
|||
IsBusy = false;
|
||||
AccessToken = _api.CurrentAccessToken;
|
||||
StatusMessage = "Interactive token acquired.";
|
||||
LoginSuccess = true;
|
||||
LoginSucceeded?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using System.Threading.Tasks;
|
||||
using PostIt.Services;
|
||||
using PostIt.ViewModels;
|
||||
|
||||
namespace PostIt.Views;
|
||||
|
||||
|
|
@ -13,11 +13,18 @@ public partial class HomePage : ContentPage
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void OnLoginClick(object? sender, RoutedEventArgs e)
|
||||
private void OnLoginClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Navigation is not null)
|
||||
|
||||
await Navigation.PushModalAsync(new LoginPage());
|
||||
var vm = (HomePageViewModel)DataContext!;
|
||||
var loginVm = new LoginPageViewModel(vm.Settings, apiClient: vm.Api);
|
||||
loginVm.LoginSucceeded += () =>
|
||||
{
|
||||
var client = new BlogApiClient(vm.Api);
|
||||
Navigation?.PushAsync(new MainPage
|
||||
{
|
||||
DataContext = new MainPageViewModel(client, vm.Settings)
|
||||
});
|
||||
};
|
||||
Navigation?.PushAsync(new LoginPage { DataContext = loginVm });
|
||||
}
|
||||
|
||||
}
|
||||
0
src/PostIt/PostIt/Views/InitialPage.cs
Normal file
0
src/PostIt/PostIt/Views/InitialPage.cs
Normal file
|
|
@ -19,20 +19,15 @@ using Microsoft.AspNetCore.Localization;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
using Yavsc.Helpers;
|
||||
|
|
@ -44,7 +39,6 @@ using Yavsc.Services;
|
|||
using Yavsc.Services.Kyc;
|
||||
using Yavsc.Settings;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
using static IdentityServer8.IdentityServerConstants;
|
||||
using IdentityServer8.Models;
|
||||
using IdentityServer8.EntityFramework.Mappers;
|
||||
|
||||
|
|
@ -302,6 +296,7 @@ public static class HostingExtensions
|
|||
|
||||
// see https://IdentityServer8.readthedocs.io/en/latest/topics/resources.html
|
||||
options.EmitStaticAudienceClaim = true;
|
||||
options.UserInteraction.LoginUrl = "/signin";
|
||||
|
||||
})
|
||||
.AddAspNetIdentity<ApplicationUser>()
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ builder.Services
|
|||
options.ClaimActions.MapUniqueJsonKey("preferred_username", "preferred_username");
|
||||
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
|
||||
options.SaveTokens = true;
|
||||
|
||||
});
|
||||
|
||||
using (var app = builder.Build())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue