diff --git a/.forgejo/workflows/buildAndTest.yml b/.forgejo/workflows/buildAndTest.yml
index 408212098..e1e936204 100644
--- a/.forgejo/workflows/buildAndTest.yml
+++ b/.forgejo/workflows/buildAndTest.yml
@@ -27,7 +27,7 @@ jobs:
build:
runs-on: docker
container:
- image: pazof/yavsc-build-env:debian13-dotnet10-android36-jdk21-v4
+ image: pazof/yavsc-build-env:debian13-dotnet10-android36-jdk21-v2
steps:
- name: Clone yavsc
run: |
diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml
index de61bd43d..7d0670a17 100644
--- a/.forgejo/workflows/release.yml
+++ b/.forgejo/workflows/release.yml
@@ -52,7 +52,7 @@ jobs:
release:
runs-on: docker
container:
- image: pazof/yavsc-build-env:debian13-dotnet10-android36-jdk21-v4
+ image: pazof/yavsc-build-env:debian13-dotnet10-android36-jdk21-v2
steps:
- name: Clone du repo au tag demandé
env:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f77b68af..e36d75dd3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,88 +1,7 @@
# Changelog
-## [1.0.8-rc22] - unstable
-### Fixed
-
-User experience login into PostIt
-
-## [1.0.8-rc21] - unstable
-
-### Fixed
-
-User experience editing the blog article.
-
-## [1.0.8-rc20] - unstable
-
-Chat SignalR fonctionnel de bout en bout (connexion, salons multiples,
-envoi/réception de messages), enregistreur audio et localisation dans
-PostIt, intégration Live.Avalonia démarrée, et reverse-proxy nginx
-apprêté pour WebSocket.
-
-### Added
-
-* [PostIt] Interface de chat : page `ChatPage` (connexion au hub,
- saisie du nom de salon, liste des messages et des membres présents),
- entrée depuis la page d'accueil.
-* [PostIt] Envoi de messages dans un salon joint : DTO `ChatMessage`
- (Name/Room/Message) pour l'événement `ReceiveMessage`, saisie avec
- envoi sur `Enter`, commande inactive tant qu'aucune salle n'est en
- focus (le hub refuse l'envoi hors salon, `LabNoJoinNoSend`).
-* [PostIt] Modèle multi-salles côté client : `JoinedRooms`,
- `SelectedRoom` (salle en focus), messages et utilisateurs indexés par
- salle (`FocusedMessages`/`FocusedUsers`), ComboBox de choix de la
- salle active ; un `Join` sur une salle déjà jointe ne fait que
- changer le focus.
-* [PostIt] Enregistreur audio (`AudioRecorder`, interface
- `IPCMProcessor`) et ressources de localisation
- (`Resources.{resx,en-EN,pt-PT}`).
-* [PostIt][WIP] Début d'intégration Live.Avalonia (preview XAML à
- chaud ; encore KO).
-* [contrib] Bloc `location /chathub` dans `template.vhost` : upgrade
- WebSocket (`Upgrade`/`Connection`), `proxy_read_timeout 3600s` et
- `proxy_buffering off` — sans quoi la connexion SignalR tombait au
- premier silence ou l'upgrade échouait derrière nginx.
-
-### Changed
-
-* [PostIt] `StatusNotice` déplacé de `Models` vers `ViewModels` et
- enrichi d'une commande d'action optionnelle (`CheckCommand` +
- `CheckCommandLabel`, interface `IWithLabel`) : le bandeau de statut
- propose désormais l'action contextuelle (Connecter / Déconnecter /
- Reconnecter / Vérifier la connexion).
-* [PostIt] `ChatViewModel` suit le cycle de vie complet de la
- connexion : handlers `Reconnecting` / `Reconnected` / `Closed` de
- `WithAutomaticReconnect`, pour refléter l'état réel au lieu d'un
- `IsConnected` optimiste.
-* [Yavsc.Api] `AddSignalR().AddJsonProtocol(IncludeFields = true)` :
- `ChatRoomInfo` n'expose que des champs publics, que System.Text.Json
- ne sérialise pas par défaut — le client recevait des objets vides
- (`Name` null, `Users` vide) et la liste des salons joints n'était
- jamais alimentée.
-* [Yavsc.Server] `IConnexionManager.OnConnected` prend le `userName`
- en paramètre (résolu depuis les claims dans `ChatHub`) au lieu de le
- relire depuis un dictionnaire jamais alimenté.
-* [PostIt.Android] `network_security_config.xml` déplacé sous
- `Resources/xml/` (chemin canonique Android).
-
-### Fixed
-
-* [Yavsc.Server] `KeyNotFoundException` dans
- `HubConnectionManager.OnConnected` à chaque connexion SignalR :
- `ChatUserNames` (et `ChatCxIds`) n'étaient jamais alimentés — relique
- d'un `SetUserName` disparu lors d'une refactorisation. L'exception
- dans `OnConnectedAsync` faisait fermer la connexion par le hub, et
- PostIt affichait « Déconnecté (The server closed the connection…) »
- immédiatement après la connexion.
-* [PostIt] URL du hub SignalR : retrait du préfixe `/api/v1` de
- `ApiUrl` avant d'apposer `Constants.ChatHubPath` (le hub est mappé à
- la racine du service, pas sous le préfixe versionné).
-* [PostIt] La `StatusBar` de la page chat référençait `Grid.Row="4"`
- sur une grille à 3 rangées (rangée hors limites, bandeau invisible).
-
-## [1.0.8-rc19] - unstable
-
-[PostIt] Blogs UX
+## [1.0.8-rc19] - Blogs UX
### Fixed
diff --git a/README.md b/README.md
index 1cee99bbb..fefbdd48d 100644
--- a/README.md
+++ b/README.md
@@ -232,12 +232,6 @@ L'impact de l'usage d'un nouveau nom d’environnement d'execution, à l'heure d
* ~/Views/Home/Index.cshtml
* ~/Views/Home/About.cshtml
-# 👥 Crédits & Co-autoraClaudt
-
-Ce projet a été développé de manière hybride :
-- **Conception, architecture et revue de code :** Paul Schneider
-- **Assistance au code et optimisation :** IA générative (Gemini, Google, Claude (Anthropic), https://qoder.com/ (ex-Windsurf))
-
# Remerciements
Photo de Dewang Guptasur Unsplash
diff --git a/contrib/Makefile b/contrib/Makefile
index 5ccf773a1..deb55bcc6 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -14,7 +14,7 @@ reinstall: copy-binaries
@echo "💯 backend started"
reinstall-prod:
- make reinstall APP_INSTALL_PREFIX= APP_INSTALL_ENV=production
+ make reinstall APP_INSTALL_ENV= APP_INSTALL_PREFIX= APP_INSTALL_ENV=production
generated/$(APP_INSTALL_PREFIX)yavscOrg.service:
generated/$(APP_INSTALL_PREFIX)yavscBlogs.service:
diff --git a/contrib/template.vhost b/contrib/template.vhost
index b4c9c139e..ea2c2cb6d 100644
--- a/contrib/template.vhost
+++ b/contrib/template.vhost
@@ -57,25 +57,5 @@ server {
proxy_read_timeout 100s;
proxy_buffering off;
}
-
- # SignalR (ChatHub) : WebSocket exige Upgrade/Connection et un timeout
- # long, sinon la connexion tombe au premier silence ou l'upgrade échoue.
- # Connection est fixé à "upgrade" (pas de map $connection_upgrade, qui
- # exigerait une directive map {} globale hors de ce vhost) : SignalR
- # négocie toujours un upgrade sur ce chemin, et la négociation initiale
- # (POST /negotiate, sans en-tête Upgrade) n'est pas affectée par cet
- # en-tête côté Kestrel.
- location /chathub {
- proxy_pass $BACKEND_SCHEME://$HTTP_HOST:$BACKEND_PORT;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_read_timeout 3600s;
- proxy_buffering off;
- }
include server-status.conf;
-}
+}
\ No newline at end of file
diff --git a/src/PostIt/Directory.Packages.props b/src/PostIt/Directory.Packages.props
index a353e4b3a..42d09cd55 100644
--- a/src/PostIt/Directory.Packages.props
+++ b/src/PostIt/Directory.Packages.props
@@ -4,7 +4,7 @@
true
- 12.1.3
+ 12.1.2
@@ -13,21 +13,16 @@
-
+
-
-
-
-
+
+
-
-
-
@@ -36,4 +31,4 @@
-
+
\ No newline at end of file
diff --git a/src/PostIt/PostIt.Android/Properties/AndroidManifest.xml b/src/PostIt/PostIt.Android/Properties/AndroidManifest.xml
index 256555df1..8793aae8a 100644
--- a/src/PostIt/PostIt.Android/Properties/AndroidManifest.xml
+++ b/src/PostIt/PostIt.Android/Properties/AndroidManifest.xml
@@ -1,10 +1,6 @@
-
+
diff --git a/src/PostIt/PostIt.Android/Resources/xml/network_security_config.xml b/src/PostIt/PostIt.Android/Resources/xml/network_security_config.xml
deleted file mode 100644
index 6d2c97559..000000000
--- a/src/PostIt/PostIt.Android/Resources/xml/network_security_config.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- 10.0.2.2
-
-
diff --git a/src/PostIt/PostIt.Desktop/Program.cs b/src/PostIt/PostIt.Desktop/Program.cs
index 1c5aa0c27..a2b8cd66f 100644
--- a/src/PostIt/PostIt.Desktop/Program.cs
+++ b/src/PostIt/PostIt.Desktop/Program.cs
@@ -1,7 +1,6 @@
using System;
using Avalonia;
using PostIt.Services;
-using PostIt.CLIOptions;
namespace PostIt.Desktop;
diff --git a/src/PostIt/PostIt.Tests/EstimateEditionPageViewModelTests.cs b/src/PostIt/PostIt.Tests/EstimateEditionPageViewModelTests.cs
index 31c82387d..17efd391b 100644
--- a/src/PostIt/PostIt.Tests/EstimateEditionPageViewModelTests.cs
+++ b/src/PostIt/PostIt.Tests/EstimateEditionPageViewModelTests.cs
@@ -1,5 +1,4 @@
using System.Net.Http;
-using PostIt.Models;
using PostIt.ViewModels;
using Yavsc;
using Yavsc.Api.Client;
diff --git a/src/PostIt/PostIt.Tests/MD/le-chant-1.md b/src/PostIt/PostIt.Tests/MD/le-chant-1.md
deleted file mode 100644
index f4a7506fe..000000000
--- a/src/PostIt/PostIt.Tests/MD/le-chant-1.md
+++ /dev/null
@@ -1,34 +0,0 @@
-
- Loin vers l'infini s'étendent
- Les grands prés marécageux .
- Pas un seul oiseau ne chante
- Sur les arbres secs et creux !
-
-*(Refrain)*
-
- Ô terre de détresse,
- Où nous devons sans cesse,
- Piocher, piocher .
-
- Dans ce camp morne et sauvage
- Entouré de murs de fer,
- Il nous semble vivre en cage
- Au milieu d'un grand désert .
-
- Bruit des pas et bruit des armes,
- Sentinelles jours et nuits,
- Et du sang, et des cris, des larmes,
- La mort pour celui qui fuit .
-
- Mais un jour dans notre vie,
- Le printemps refleurira .
- Liberté, liberté chérie
- Je dirai : « Tu es à moi ! »
-
-*(Dernier refrain)*
-
- Ô terre d'allégresse,
- Où nous pourrons sans cesse,
- Aimer, aimer !
-
-[audio : Mario Hacquard - Youtube](https://www.youtube.com/watch?v=ogw_mgk1pFs)
diff --git a/src/PostIt/PostIt.Tests/SessionStatusBannerTests.cs b/src/PostIt/PostIt.Tests/SessionStatusBannerTests.cs
index 3516e93d6..cc08304d5 100644
--- a/src/PostIt/PostIt.Tests/SessionStatusBannerTests.cs
+++ b/src/PostIt/PostIt.Tests/SessionStatusBannerTests.cs
@@ -101,7 +101,7 @@ public class SessionStatusBannerTests
{
MainWindow window = new MainWindow();
window.Show();
- var banner = new SessionStatusBanner();
+ var banner = window.MainView.SessionBanner;
var status = new SessionStatusViewModel();
banner.DataContext = status;
return banner;
diff --git a/src/PostIt/PostIt.Tests/StatusBarTests.cs b/src/PostIt/PostIt.Tests/StatusBarTests.cs
index 15889e0b0..4053dbaf3 100644
--- a/src/PostIt/PostIt.Tests/StatusBarTests.cs
+++ b/src/PostIt/PostIt.Tests/StatusBarTests.cs
@@ -2,7 +2,6 @@ using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.VisualTree;
using PostIt.Controls;
-using PostIt.Models;
using PostIt.ViewModels;
namespace PostIt.Tests;
@@ -22,9 +21,7 @@ public class StatusBarTests
[AvaloniaFact]
public void Status_message_is_rendered_by_a_SelectableTextBlock()
{
- var bar = new StatusBar {
- DataContext =
- StatusNotice.Error("Échec de la suppression : boom") };
+ var bar = new StatusBar { DataContext = StatusNotice.Error("Échec de la suppression : boom") };
var window = new Window { Content = bar };
window.Show();
@@ -35,4 +32,4 @@ public class StatusBarTests
Assert.NotNull(message);
Assert.Equal("Échec de la suppression : boom", message!.Text);
}
-}
+}
\ No newline at end of file
diff --git a/src/PostIt/PostIt/App.axaml.cs b/src/PostIt/PostIt/App.axaml.cs
index fca8e0313..9a04ffd03 100644
--- a/src/PostIt/PostIt/App.axaml.cs
+++ b/src/PostIt/PostIt/App.axaml.cs
@@ -6,26 +6,18 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Styling;
-using Avalonia.Threading;
using Microsoft.Extensions.DependencyInjection;
using PostIt.Services;
using PostIt.ViewModels;
using PostIt.Views;
using PostIt.Helpers;
-using System.Globalization;
-using Live.Avalonia;
-using System.Diagnostics;
-using System.Linq;
-using ReactiveUI;
-using System.Reactive;
namespace PostIt;
-public partial class App : Application, ILiveView, IVMPusherApp
+public partial class App : Application
{
- public static bool LiveMode { get; private set; }
private int _bootStarted;
- public static string? CliConfigFileSpecification { get; private set; }
+ public string? CliConfigFileSpecification { get; private set; }
///
/// DI container the platform entry points hand to ViewModels so
@@ -44,132 +36,36 @@ public partial class App : Application, ILiveView, IVMPusherApp
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
-
-#if DEBUG
- this.AttachDeveloperTools();
-#endif
}
public override void OnFrameworkInitializationCompleted()
{
if (TryHandOffCustomSchemeUrl()) return;
- this.ServiceProvider = new ServiceCollection().BuildPostItServices();
+ this.ServiceProvider = new ServiceCollection().BuildServices();
var settings = ServiceProvider.GetRequiredService();
- if (!LiveMode)
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
- // Debugging requires pdb loading etc, so we disable live reloading
- // during a test run with an attached debugger.
-
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- InitializeClassicDesktopLifetime(settings, desktop);
- }
- else if (ApplicationLifetime is IActivityApplicationLifetime singleViewFactoryApplicationLifetime)
- {
- InitializeActivityLifetime(settings, singleViewFactoryApplicationLifetime);
- }
- else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
- {
- InitializeSingleViewLifetime(settings, singleViewPlatform);
- }
+ InitializeClassicDesktopLifetime(settings, desktop);
}
- else
+ else if (ApplicationLifetime is IActivityApplicationLifetime singleViewFactoryApplicationLifetime)
{
- // Here, we create a new LiveViewHost, located in the 'Live.Avalonia'
- // namespace, and pass an ILiveView implementation to it. The ILiveView
- // implementation should have a parameterless constructor! Next, we
- // start listening for any changes in the source files. And then, we
- // show the LiveViewHost window. Simple enough, huh?
- var window = new LiveViewHost(this, Console.WriteLine);
- var view = ServiceProvider!.GetRequiredService();
-
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- InitializeClassicDesktopLifetime(window, view, settings, desktop);
- }
- else if (ApplicationLifetime is IActivityApplicationLifetime singleViewFactoryApplicationLifetime)
- {
- InitializeActivityLifetime(settings, singleViewFactoryApplicationLifetime);
- }
- else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
- {
- InitializeSingleViewLifetime(settings, singleViewPlatform);
- }
-
- window.StartWatchingSourceFilesForHotReloading();
- Console.WriteLine("LiveViewHost started for hot reloading.");
- window.Show();
+ InitializeActivityLifetime(settings, singleViewFactoryApplicationLifetime);
+ }
+ else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
+ {
+ InitializeSingleViewLifetime(settings, singleViewPlatform);
}
-
-
-#if DEBUG
- // Here we subscribe to ReactiveUI default exception handler to avoid app
- // termination in case if we do something wrong in our view models. See:
- // https://www.reactiveui.net/docs/handbook/default-exception-handler/
- //
- // In case if you are using another MV* framework, please refer to its
- // documentation explaining global exception handling.
- Console.WriteLine("Debug mode: ReactiveUI default exception handler is set.");
- RxApp.DefaultExceptionHandler = Observer.Create(Console.WriteLine);
-#endif
base.OnFrameworkInitializationCompleted();
}
- private bool IsProduction()
- {
-#if DEBUG
- return false;
-#else
- return true;
-#endif
- }
-
- public async Task PushPageAsync(ViewModelBase viewModel)
- {
- var template = DataTemplates.FirstOrDefault(t => t.Match(viewModel));
- if (template is null)
- {
- throw new InvalidOperationException($"No IDataTemplate found for {viewModel.GetType().Name}.");
- }
-
- Control? view = template.Build(viewModel) ;
- if (view is null)
- {
- throw new InvalidOperationException(
- $"Template for {viewModel.GetType().Name} returned .");
- }
-
- var page = view as Page;
- if (page is null)
- {
- // NavigationPage expects Page instances. Wrap any fallback control
- // (e.g. ViewLocator error TextBlock) into a ContentPage so it can render.
- page = new ContentPage { Content = view };
- }
-
- page.DataContext = viewModel;
-
- MainView? mainView = (Current as App).View;
-
- // Avoid stacking the same singleton page twice (e.g. SettingsPage).
- var stack = mainView.NavRoot.NavigationStack;
- if (stack.Count > 0 && ReferenceEquals(stack[stack.Count - 1], page))
- {
- return page;
- }
-
- await mainView.NavRoot.PushAsync(page);
- return page;
- }
-
public void InitializeSingleViewLifetime(Settings settings, ISingleViewApplicationLifetime singleViewPlatform)
{
singleViewPlatform.MainView = View = ServiceProvider!.GetRequiredService();
ConfigureRootView(View);
- ApplyStettings(settings);
+ ApplyDarkMode(settings);
}
public void InitializeActivityLifetime(Settings settings, IActivityApplicationLifetime singleViewFactoryApplicationLifetime)
@@ -178,7 +74,7 @@ public partial class App : Application, ILiveView, IVMPusherApp
() =>
{
this.ConfigureRootView(ServiceProvider!.GetRequiredService());
- ApplyStettings(settings);
+ ApplyDarkMode(settings);
return View!;
};
}
@@ -186,35 +82,37 @@ public partial class App : Application, ILiveView, IVMPusherApp
public void InitializeClassicDesktopLifetime(Settings settings, IClassicDesktopStyleApplicationLifetime desktop)
{
var window = ServiceProvider!.GetRequiredService();
- var mainView = (MainView)CreateView(window);
- InitializeClassicDesktopLifetime(window, mainView, settings, desktop);
- }
-
- public void InitializeClassicDesktopLifetime(Window window, MainView mainView, Settings settings, IClassicDesktopStyleApplicationLifetime desktop)
- {
- this.ConfigureRootView(mainView);
- ApplyStettings(settings);
desktop.MainWindow = window;
- window.Content = mainView;
- window.Show();
+ this.ConfigureRootView(window.MainView);
+
+ ApplyDarkMode(settings);
}
public void ConfigureRootView(MainView rootView)
+{
+ // Déclencher le Boot une seule fois lors du chargement du contrôle à l'écran.
+ rootView.AttachedToVisualTree += async (_, _) => await BootOnceAsync();
+
+ var sessionStatus = ServiceProvider!.GetRequiredService();
+ sessionStatus.LogoutCompleted += () =>
{
- rootView.AttachedToVisualTree += (_, _) =>
- Dispatcher.UIThread.Post(() => _ = BootOnceAsync());
+ // Remplacer Window.NavRoot par rootView.NavRoot
+ rootView.NavRoot.PopToRootAsync();
+ };
- var sessionStatus = ServiceProvider!.GetRequiredService();
- sessionStatus.LogoutCompleted += () =>
+ rootView.SessionBanner.DataContext = sessionStatus;
+ this.View = rootView;
+}
+
+ private async Task BootOnceAsync()
+ {
+ if (Interlocked.Exchange(ref _bootStarted, 1) == 1)
{
- // Remplacer Window.NavRoot par rootView.NavRoot
- rootView.NavRoot.PopToRootAsync();
- };
- if (rootView.DataContext==null)
- rootView.DataContext = ServiceProvider!.GetRequiredService();
+ return;
+ }
- rootView.SessionBanner.DataContext = sessionStatus;
- this.View = rootView;
+ var api = ServiceProvider!.GetRequiredService();
+ await BootAsync(this.ServiceProvider!, api);
}
///
@@ -228,24 +126,10 @@ public partial class App : Application, ILiveView, IVMPusherApp
View = mainView ?? throw new ArgumentNullException(nameof(mainView));
}
- private static void ApplyStettings(Settings settings)
+ private static void ApplyDarkMode(Settings settings)
{
- if (!string.IsNullOrWhiteSpace(settings.SelectedLanguage))
- Yavsc.Lang.Resources.Culture = new CultureInfo(settings.SelectedLanguage);
- Current!.RequestedThemeVariant =
- settings.DarkMode ? ThemeVariant.Dark : ThemeVariant.Light;
- }
-
-
- private async Task BootOnceAsync()
- {
- if (Interlocked.Exchange(ref _bootStarted, 1) == 1)
- {
- return;
- }
-
- var api = ServiceProvider!.GetRequiredService();
- await BootAsync(this.ServiceProvider!, api);
+ Application.Current!.RequestedThemeVariant =
+ settings.DarkMode ? ThemeVariant.Dark : ThemeVariant.Light;
}
///
@@ -263,9 +147,9 @@ public partial class App : Application, ILiveView, IVMPusherApp
var refreshed = await api.TrySilentLoginAsync().ConfigureAwait(true);
var sessionStatus = provider.GetRequiredService();
sessionStatus.Refresh();
- var homePageVm = provider.GetRequiredService();
+ var homePage = provider.GetRequiredService();
var app = (App)Current!;
- await app.PushPageAsync(homePageVm);
+ await app.PushPageAsync(homePage);
}
///
@@ -321,31 +205,8 @@ public partial class App : Application, ILiveView, IVMPusherApp
await View!.NavRoot.PopAsync();
}
- internal static void UseConfigFileWhenLoading(string configFile)
+ internal void UseConfigFileWhenLoading(string configFile)
{
- CliConfigFileSpecification = configFile;
- }
-
- internal static void EnableLiveMode()
- {
- LiveMode = true;
- }
-
- public object CreateView(Window window)
- {
- if (this.ServiceProvider == null)
- {
- var serviceCollection = new ServiceCollection();
- this.ServiceProvider = serviceCollection.BuildPostItServices();
- }
- if (window.DataContext == null)
- window.DataContext = ServiceProvider!.GetRequiredService();
-
- // The AppView class will inherit the DataContext
- // of the window. The AppView class can be a
- // UserControl, a Grid, a TextBlock, whatever.
- this.View = ServiceProvider!.GetRequiredService();
- this.ConfigureRootView(this.View);
- return this.View;
+ this.CliConfigFileSpecification = configFile;
}
}
diff --git a/src/PostIt/PostIt/CLIOptions/CLIOptions.cs b/src/PostIt/PostIt/CLIOptions/CLIOptions.cs
index 643102b38..c0bea85ff 100644
--- a/src/PostIt/PostIt/CLIOptions/CLIOptions.cs
+++ b/src/PostIt/PostIt/CLIOptions/CLIOptions.cs
@@ -4,21 +4,15 @@ using Mono.Options;
using PostIt;
using PostIt.ViewModels;
-namespace PostIt.CLIOptions;
-
public static class CLIOptionHandler
{
public static void HandleArgs(string[] args)
{
bool showHelp = false;
string configFile = null;
- bool liveMode = false;
var options = new OptionSet {
{ "c|configuration=", "Configuration file to load", (string v) => configFile = v },
- { "l|live", "Enable live mode", v => liveMode = true },
- { "v|version", "Show the application version", v => Console.WriteLine("PostIt version "
- + typeof(App).Assembly.GetName().Version?.ToString()) },
{ "h|help", "Show this message and exit", v => showHelp = true }
};
@@ -32,11 +26,8 @@ public static class CLIOptionHandler
}
if (configFile != null)
{
- App.UseConfigFileWhenLoading(configFile);
- }
- if (liveMode)
- {
- App.EnableLiveMode();
+ var app = App.Current as App;
+ app.UseConfigFileWhenLoading(configFile);
}
}
}
diff --git a/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml b/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml
deleted file mode 100644
index 9e8df6dd1..000000000
--- a/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs b/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs
deleted file mode 100644
index 0431b726e..000000000
--- a/src/PostIt/PostIt/Controls/MarkdownEditorControl.axaml.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using System;
-using Avalonia;
-using Avalonia.Controls;
-using AvaloniaEdit.Document;
-
-namespace PostIt.Controls;
-
-public partial class MarkdownEditorControl : UserControl
-{
- public static readonly StyledProperty MarkdownTextProperty =
- AvaloniaProperty.Register(nameof(MarkdownText));
-
- public static readonly StyledProperty DocumentProperty =
- AvaloniaProperty.Register(nameof(Document));
-
- public static readonly StyledProperty IsPreviewVisibleProperty =
- AvaloniaProperty.Register(nameof(IsPreviewVisible), false);
-
- public string? MarkdownText
- {
- get => GetValue(MarkdownTextProperty);
- set => SetValue(MarkdownTextProperty, value);
- }
-
- public TextDocument? Document
- {
- get => GetValue(DocumentProperty);
- set => SetValue(DocumentProperty, value);
- }
-
- public bool IsPreviewVisible
- {
- get => GetValue(IsPreviewVisibleProperty);
- set => SetValue(IsPreviewVisibleProperty, value);
- }
-
- public MarkdownEditorControl()
- {
- InitializeComponent();
- PropertyChanged += OnPropertyChanged;
- SizeChanged += OnSizeChanged;
- UpdateEditorLimits();
- }
-
- private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
- {
- if (e.Property == MarkdownTextProperty)
- {
- var text = e.NewValue as string ?? string.Empty;
- if (Document is null || !string.Equals(Document.Text, text, StringComparison.Ordinal))
- {
- Document = new TextDocument(text);
- }
- }
- else if (e.Property == DocumentProperty)
- {
- var newDocument = e.NewValue as TextDocument;
- var text = newDocument?.Text ?? string.Empty;
- if (!string.Equals(MarkdownText, text, StringComparison.Ordinal))
- {
- MarkdownText = text;
- }
- }
- }
-
- private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
- {
- UpdateEditorLimits();
- }
-
- private void UpdateEditorLimits()
- {
- var visibleHeight = Bounds.Height > 0 ? Bounds.Height - 24 : 260;
- visibleHeight = Math.Max(visibleHeight, 80);
-
- if (SourceEditor is not null)
- {
- SourceEditor.MaxHeight = visibleHeight;
- SourceEditor.Height = double.NaN;
- }
-
- if (PreviewViewer is not null)
- {
- PreviewViewer.MaxHeight = visibleHeight;
- PreviewViewer.Height = double.NaN;
- }
- }
-}
diff --git a/src/PostIt/PostIt/Controls/StatusBar.axaml b/src/PostIt/PostIt/Controls/StatusBar.axaml
index 755ff7d3e..2f46fee33 100644
--- a/src/PostIt/PostIt/Controls/StatusBar.axaml
+++ b/src/PostIt/PostIt/Controls/StatusBar.axaml
@@ -10,19 +10,16 @@
Padding="8">
+ FontWeight="Bold"
+ Foreground="{Binding Foreground}"
+ VerticalAlignment="Top"/>
+
-
+ Foreground="{Binding Foreground}"
+ TextWrapping="Wrap"
+ VerticalAlignment="Center"/>
diff --git a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs
index 778f2c4ed..f6fcc3e3b 100644
--- a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs
+++ b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs
@@ -2,10 +2,8 @@ using System;
using Microsoft.Extensions.DependencyInjection;
using PostIt.Services;
using PostIt.ViewModels;
-using PostIt.ViewModels.Chat;
using PostIt.Views;
using PostIt.Views.Blogs;
-using PostIt.Views.Chat;
using PostIt.Views.Commands;
using Yavsc.Api.Client;
@@ -13,7 +11,7 @@ namespace PostIt.Helpers;
public static class ServiceCollectionHelpers
{
- public static IServiceProvider BuildPostItServices(this ServiceCollection services)
+ public static IServiceProvider BuildServices(this ServiceCollection services)
{
var settings = new Settings();
settings.Load();
@@ -39,13 +37,9 @@ public static class ServiceCollectionHelpers
var reverseGeocoding = new NominatimReverseGeocodingService();
// Vues
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
-
-
- // Models
- services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
// SettingsPage is a singleton: there must be one and only one
// instance of the settings UI for the lifetime of the app.
@@ -65,7 +59,6 @@ public static class ServiceCollectionHelpers
services.AddSingleton();
services.AddSingleton();
services.AddTransient();
- services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
diff --git a/src/PostIt/PostIt/Helpers/ViewModelBaseHelpers.cs b/src/PostIt/PostIt/Helpers/ViewModelBaseHelpers.cs
new file mode 100644
index 000000000..f30e021d9
--- /dev/null
+++ b/src/PostIt/PostIt/Helpers/ViewModelBaseHelpers.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using PostIt.ViewModels;
+
+namespace PostIt.Helpers;
+
+public static class ViewModelBaseHelpers
+{
+ public static async Task PushPageAsync(this App app, ViewModelBase vm)
+ {
+ var window = app.View;
+ if (window is null)
+ {
+ throw new InvalidOperationException("MainWindow is not initialized yet.");
+ }
+
+ var template = app.DataTemplates.FirstOrDefault(t => t.Match(vm));
+ if (template is null)
+ {
+ throw new InvalidOperationException($"No IDataTemplate found for {vm.GetType().Name}.");
+ }
+
+ var view = template.Build(vm);
+ if (view is null)
+ {
+ throw new InvalidOperationException(
+ $"Template for {vm.GetType().Name} returned .");
+ }
+
+ var page = view as Page;
+ if (page is null)
+ {
+ // NavigationPage expects Page instances. Wrap any fallback control
+ // (e.g. ViewLocator error TextBlock) into a ContentPage so it can render.
+ page = new ContentPage { Content = view };
+ }
+
+ page.DataContext = vm;
+
+ // Avoid stacking the same singleton page twice (e.g. SettingsPage).
+ var stack = window.NavRoot.NavigationStack;
+ if (stack.Count > 0 && ReferenceEquals(stack[stack.Count - 1], page))
+ {
+ return page;
+ }
+
+ await window.NavRoot.PushAsync(page);
+
+ return page;
+ }
+}
diff --git a/src/PostIt/PostIt/IVMPusherApp.cs b/src/PostIt/PostIt/IVMPusherApp.cs
deleted file mode 100644
index 3fc0f474f..000000000
--- a/src/PostIt/PostIt/IVMPusherApp.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Threading.Tasks;
-using PostIt.ViewModels;
-using Avalonia.Controls;
-
-namespace PostIt;
-
-internal interface IVMPusherApp
-{
- Task PushPageAsync(ViewModelBase viewModel);
-}
diff --git a/src/PostIt/PostIt/Interfaces/IPCMProcessor.cs b/src/PostIt/PostIt/Interfaces/IPCMProcessor.cs
deleted file mode 100644
index e8e61070e..000000000
--- a/src/PostIt/PostIt/Interfaces/IPCMProcessor.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-using System;
-
-public interface IPCMProcessor : IDisposable
-{
- void Process(short[] audioFrame);
-}
diff --git a/src/PostIt/PostIt/Lang/Resources.Designer.cs b/src/PostIt/PostIt/Lang/Resources.Designer.cs
deleted file mode 100644
index 7d600fd2a..000000000
--- a/src/PostIt/PostIt/Lang/Resources.Designer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Yavsc.Lang {
- using System;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- public class Resources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- public Resources() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- public static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Yavsc.Lang.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- public static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Looks up a localized string similar to Bienvenue dans PostIt !!!.
- ///
- public static string GreetingText {
- get {
- return ResourceManager.GetString("GreetingText", resourceCulture);
- }
- }
- }
-}
diff --git a/src/PostIt/PostIt/Lang/Resources.en-EN.resx b/src/PostIt/PostIt/Lang/Resources.en-EN.resx
deleted file mode 100644
index 6128bca8d..000000000
--- a/src/PostIt/PostIt/Lang/Resources.en-EN.resx
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- Welcome to PostIt!
-
-
diff --git a/src/PostIt/PostIt/Lang/Resources.pt-PT.resx b/src/PostIt/PostIt/Lang/Resources.pt-PT.resx
deleted file mode 100644
index 049c25316..000000000
--- a/src/PostIt/PostIt/Lang/Resources.pt-PT.resx
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- Bemvindo em PostIt!
-
-
diff --git a/src/PostIt/PostIt/Lang/Resources.resx b/src/PostIt/PostIt/Lang/Resources.resx
deleted file mode 100644
index 574138ef6..000000000
--- a/src/PostIt/PostIt/Lang/Resources.resx
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- Bienvenue dans PostIt !!!
-
-
diff --git a/src/PostIt/PostIt/Models/ChatMessage.cs b/src/PostIt/PostIt/Models/ChatMessage.cs
deleted file mode 100644
index 52733311a..000000000
--- a/src/PostIt/PostIt/Models/ChatMessage.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace PostIt.ViewModels.Chat
-{
- ///
- /// DTO de message de salon, calqué sur la charge envoyée par
- /// ChatHub.Send côté serveur ({ Name, Room, Message }).
- /// Le protocole JSON de SignalR sérialise en camelCase ; la
- /// désérialisation est insensible à la casse par défaut.
- ///
- public class ChatMessage
- {
- public string? Name { get; set; }
- public string? Room { get; set; }
- public string? Message { get; set; }
- }
-}
diff --git a/src/PostIt/PostIt/Models/ChatMessageDto.cs b/src/PostIt/PostIt/Models/ChatMessageDto.cs
deleted file mode 100644
index b93ccbe29..000000000
--- a/src/PostIt/PostIt/Models/ChatMessageDto.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace PostIt.ViewModels.Chat
-{
- ///
- /// DTO minimal calqué sur l'objet anonyme émis par le hub
- /// ({ Name, Room, Message }) via l'événement
- /// ReceiveMessage.
- ///
- public class ChatMessageDto
- {
- public string? Name { get; set; }
- public string? Room { get; set; }
- public string? Message { get; set; }
- }
-}
diff --git a/src/PostIt/PostIt/Models/ChatRoomInfo.cs b/src/PostIt/PostIt/Models/ChatRoomInfo.cs
deleted file mode 100644
index 548371157..000000000
--- a/src/PostIt/PostIt/Models/ChatRoomInfo.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Collections.Generic;
-
-namespace PostIt.ViewModels.Chat
-{
- // DTO minimal calqué sur votre logique serveur ChatRoomInfo
- public class ChatRoomInfo
- {
- public string? Name { get; set; }
- public List Users { get; set; } = new();
- }
-}
diff --git a/src/PostIt/PostIt/Models/IWithLabel.cs b/src/PostIt/PostIt/Models/IWithLabel.cs
deleted file mode 100644
index b76f912db..000000000
--- a/src/PostIt/PostIt/Models/IWithLabel.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace PostIt.ViewModels;
-
-internal interface IWithLabel
-{
- string Label { get; }
-}
diff --git a/src/PostIt/PostIt/Models/StatusSeverity.cs b/src/PostIt/PostIt/Models/StatusSeverity.cs
deleted file mode 100644
index eb6ef4502..000000000
--- a/src/PostIt/PostIt/Models/StatusSeverity.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace PostIt.Models;
-
-public enum StatusSeverity
-{
- Info,
- Warning,
- Error
-}
diff --git a/src/PostIt/PostIt/PostIt.csproj b/src/PostIt/PostIt/PostIt.csproj
index 89525f190..9cdf71299 100644
--- a/src/PostIt/PostIt/PostIt.csproj
+++ b/src/PostIt/PostIt/PostIt.csproj
@@ -7,8 +7,6 @@
1.1.0.0
1.1.0-estimate.1+364.Branch.feature-estimate.Sha.3d6ab6e72f9ba4ccfa250a1cce154b550281405e
1.1.0-estimate.1
- true
-
@@ -32,36 +30,16 @@
None
All
-
-
-
-
-
-
-
- PublicResXFileCodeGenerator
- Resources.Designer.cs
-
-
-
-
-
- True
- True
- Lang/Resources.resx
-
-
-
-
+
\ No newline at end of file
diff --git a/src/PostIt/PostIt/Services/AudioRecorder.cs b/src/PostIt/PostIt/Services/AudioRecorder.cs
deleted file mode 100644
index 43e7f5c1b..000000000
--- a/src/PostIt/PostIt/Services/AudioRecorder.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.Threading;
-using Pv;
-
-public class AudioRecorder : IDisposable
-{
- private int frameLength;
- PvRecorder recorder;
-
- Thread thread;
- short[] audioFrame;
- IPCMProcessor processor;
-
- public bool processing { get; private set; }
-
- public AudioRecorder(IPCMProcessor processor, int frameLength=512, int deviceIndex = -1)
- {
- this.frameLength = frameLength;
- this.recorder = PvRecorder.Create(frameLength: frameLength, deviceIndex: deviceIndex);
- this.audioFrame = new short[frameLength];
- this.processor=processor;
- }
-
- public static string [] GetAvailableDevices()
- {
- return PvRecorder.GetAvailableDevices();
- }
-
- public void Start()
- {
- recorder.Start();
- thread = new Thread(ThreadLoop);
- thread.Start();
- }
-
- public void ThreadLoop()
- {
- while (recorder.IsRecording)
- {
- if (thread.ThreadState != ThreadState.Running)
- {
- break;
- }
- if (processing)
- {
- Thread.Sleep(10);
- continue;
- }
- processing = true;
- audioFrame = recorder.Read();
- processor.Process(audioFrame);
- processing = false;
- }
- }
-
- public void Stop()
- {
- thread.Interrupt();
- thread.Join();
- recorder.Stop();
- thread = null;
- }
- public void Dispose()
- {
- recorder.Dispose();
- }
-}
diff --git a/src/PostIt/PostIt/ViewLocator.cs b/src/PostIt/PostIt/ViewLocator.cs
index ffaaadc66..ae4662072 100644
--- a/src/PostIt/PostIt/ViewLocator.cs
+++ b/src/PostIt/PostIt/ViewLocator.cs
@@ -5,10 +5,8 @@ using Avalonia.Controls.Templates;
using Microsoft.Extensions.DependencyInjection;
using PostIt.ViewModels;
using PostIt.ViewModels.Commands;
-using PostIt.ViewModels.Chat;
using PostIt.Views;
using PostIt.Views.Blogs;
-using PostIt.Views.Chat;
using PostIt.Views.Commands;
namespace PostIt;
@@ -60,7 +58,6 @@ public class ViewLocator : IDataTemplate
EstimateListPageViewModel => services.GetRequiredService(),
EstimateValidationPageViewModel => services.GetRequiredService(),
MyFilesViewModel => services.GetRequiredService(),
- ChatViewModel => services.GetRequiredService(),
null => new TextBlock { Text = "No view for " },
_ => new TextBlock { Text = $"No view for {data.GetType().Name}" }
};
diff --git a/src/PostIt/PostIt/ViewModels/ACL/CirclesPageViewModel.cs b/src/PostIt/PostIt/ViewModels/ACL/CirclesPageViewModel.cs
index 6201c061b..cd068b4f4 100644
--- a/src/PostIt/PostIt/ViewModels/ACL/CirclesPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/ACL/CirclesPageViewModel.cs
@@ -5,7 +5,7 @@ using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
-using PostIt.Models;
+using PostIt.Helpers;
using PostIt.Services;
using Yavsc.Api.Client;
using Yavsc.Api.Client.Dtos;
diff --git a/src/PostIt/PostIt/ViewModels/ACL/PostAclDialogViewModel.cs b/src/PostIt/PostIt/ViewModels/ACL/PostAclDialogViewModel.cs
index 43ed60823..be1b220fa 100644
--- a/src/PostIt/PostIt/ViewModels/ACL/PostAclDialogViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/ACL/PostAclDialogViewModel.cs
@@ -12,7 +12,6 @@ using Yavsc.Api.Client.Dtos;
using Yavsc.Abstract.BlogSpot;
using Yavsc.Abstract.Identity.Security;
using System.Net.Http;
-using PostIt.Models;
namespace PostIt.ViewModels;
diff --git a/src/PostIt/PostIt/ViewModels/Activity/ActivitiesPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/ActivitiesPageViewModel.cs
index 1890309ec..bd13faf9b 100644
--- a/src/PostIt/PostIt/ViewModels/Activity/ActivitiesPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Activity/ActivitiesPageViewModel.cs
@@ -8,7 +8,6 @@ using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PostIt.Helpers;
-using PostIt.Models;
using Yavsc.Abstract.Workflow;
using Yavsc.Api.Client;
diff --git a/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs
index 5cd373708..f1b05b749 100644
--- a/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs
@@ -12,7 +12,6 @@ using PostIt.Helpers;
using Yavsc;
using Yavsc.Api.Client;
using Yavsc.Abstract.Workflow;
-using PostIt.Models;
namespace PostIt.ViewModels;
diff --git a/src/PostIt/PostIt/ViewModels/Activity/BillingQueryDetailsPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/BillingQueryDetailsPageViewModel.cs
index 0df05b21a..c9f122dce 100644
--- a/src/PostIt/PostIt/ViewModels/Activity/BillingQueryDetailsPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Activity/BillingQueryDetailsPageViewModel.cs
@@ -388,4 +388,4 @@ public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionSt
QueryStatus.Success => "V",
_ => "i",
};
-}
+}
\ No newline at end of file
diff --git a/src/PostIt/PostIt/ViewModels/Activity/CommandFormsPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/CommandFormsPageViewModel.cs
index 14e7e74b9..533fbc380 100644
--- a/src/PostIt/PostIt/ViewModels/Activity/CommandFormsPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Activity/CommandFormsPageViewModel.cs
@@ -6,7 +6,6 @@ using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PostIt.Helpers;
-using PostIt.Models;
using Yavsc.Abstract.Workflow;
using Yavsc.Api.Client;
diff --git a/src/PostIt/PostIt/ViewModels/Activity/EstimateEditionPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/EstimateEditionPageViewModel.cs
index f8f94436a..1f1e4b4be 100644
--- a/src/PostIt/PostIt/ViewModels/Activity/EstimateEditionPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Activity/EstimateEditionPageViewModel.cs
@@ -10,7 +10,6 @@ using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PostIt.Helpers;
-using PostIt.Models;
using Yavsc.Api.Client;
namespace PostIt.ViewModels;
diff --git a/src/PostIt/PostIt/ViewModels/Activity/EstimateListPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/EstimateListPageViewModel.cs
index 766dda903..c90a6d884 100644
--- a/src/PostIt/PostIt/ViewModels/Activity/EstimateListPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Activity/EstimateListPageViewModel.cs
@@ -9,7 +9,6 @@ using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PostIt.Helpers;
-using PostIt.Models;
using Yavsc.Api.Client;
namespace PostIt.ViewModels;
diff --git a/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs
index 360bf54ad..7fd84fd88 100644
--- a/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs
@@ -13,7 +13,6 @@ using PostIt.Helpers;
using Yavsc;
using Yavsc.Abstract.Workflow;
using Yavsc.Api.Client;
-using PostIt.Models;
namespace PostIt.ViewModels;
diff --git a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs b/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs
index b827bff26..a1e016e7f 100644
--- a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs
@@ -12,7 +12,6 @@ using Yavsc.Api.Client;
using Yavsc.Abstract.Files;
using PostIt.Helpers;
using AvaloniaEdit.Document;
-using PostIt.Models;
namespace PostIt.ViewModels;
@@ -39,64 +38,18 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel
/// Editor buffer for the post body. Same pattern as
/// .
- public string? DraftArticle
+ public string DraftArticle
{
- get => DraftArticleDocument?.Text ?? null;
- set
- {
- var currentText = DraftArticleDocument?.Text;
- if (string.Equals(currentText, value, StringComparison.Ordinal))
- {
- OnPropertyChanged(nameof(DraftArticle));
- return;
- }
-
+ get => DraftArticleDocument.Text;
+ set {
OnPropertyChanging(nameof(DraftArticle));
- try
- {
- DraftArticleDocument = value is not null ? new TextDocument(value) : null;
- }
- catch (Exception ex)
- {
- StatusMessage = $"Error setting DraftArticleDocument: {ex.Message}";
- // Handle any exceptions that might occur when setting the DraftArticleDocument
- }
- OnPropertyChanged(nameof(DraftArticle));
+ DraftArticleDocument = new TextDocument(value);
+ this.OnPropertyChanged(nameof(DraftArticle));
}
}
- private TextDocument? _draftArticleDocument;
-
- public TextDocument? DraftArticleDocument
- {
- get => _draftArticleDocument;
- set
- {
- if (ReferenceEquals(_draftArticleDocument, value))
- {
- return;
- }
-
- if (_draftArticleDocument is not null)
- {
- _draftArticleDocument.TextChanged -= DraftArticleDocument_TextChanged;
- }
-
- _draftArticleDocument = value;
-
- if (_draftArticleDocument is not null)
- {
- _draftArticleDocument.TextChanged += DraftArticleDocument_TextChanged;
- }
-
- OnPropertyChanged(nameof(DraftArticleDocument));
- }
- }
-
- private void DraftArticleDocument_TextChanged(object? sender, EventArgs e)
- {
- OnPropertyChanged(nameof(DraftArticle));
- }
+ [ObservableProperty]
+ public partial TextDocument DraftArticleDocument {get; set;}
/// Editor buffer for the post's publication state.
/// Reflects the server-side IsPublished flag (the
diff --git a/src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs b/src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs
deleted file mode 100644
index ba452cb4a..000000000
--- a/src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs
+++ /dev/null
@@ -1,353 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Threading.Tasks;
-using CommunityToolkit.Mvvm.ComponentModel;
-using CommunityToolkit.Mvvm.Input;
-using Microsoft.AspNetCore.SignalR.Client;
-using Microsoft.Extensions.DependencyInjection;
-using PostIt.Models;
-using PostIt.Services;
-using Yavsc;
-
-namespace PostIt.ViewModels.Chat
-{
- ///
- /// ViewModel pour la gestion du chat,
- /// incluant la connexion au Hub SignalR,
- /// la gestion des salons et des utilisateurs connectés.
- ///
-
- public partial class ChatViewModel : ViewModelBase
- {
- private readonly Settings _settings;
- private readonly YavscApiClient? _api;
-
- // Le paramètre optionnel permet l'instanciation design-time
- // (Design.DataContext dans ChatPage.axaml) sans conteneur DI.
- public ChatViewModel(YavscApiClient? api = null)
- {
- var app = App.Current as App;
- _settings = app!.ServiceProvider!.GetRequiredService(); // Assurez-vous que _settings est initialisé avant utilisation.
- _api = api;
- Status = new StatusNotice("Déconnecté", StatusSeverity.Info,
- new RelayCheckCommand("Connecter", async () => await ConnectAsync()));
- FocusedMessages = _systemMessages;
- }
-
- private HubConnection? _connection;
-
- [ObservableProperty]
- public partial string CurrentRoom { get; set; } = "General";
-
- [ObservableProperty]
- public partial string NickName { get; set; } = "";
-
- [ObservableProperty]
- public partial StatusNotice Status { get; set; }
-
- [ObservableProperty]
- [NotifyCanExecuteChangedFor(nameof(SendMessageCommand))]
- public partial bool IsConnected { get; set; } = false;
-
- [ObservableProperty]
- [NotifyCanExecuteChangedFor(nameof(SendMessageCommand))]
- public partial string MessageToSend { get; set; } = "";
-
- // Salles jointes (choix de la salle en focus dans la vue).
- public ObservableCollection JoinedRooms { get; } = new();
-
- // Salle en focus ; null = aucune salle, le journal affiche
- // alors les notifications système.
- [ObservableProperty]
- [NotifyCanExecuteChangedFor(nameof(SendMessageCommand))]
- public partial string? SelectedRoom { get; set; }
-
- // Notifications globales (connexion, erreurs de join, ...)
- private readonly ObservableCollection _systemMessages = new();
-
- // Messages et utilisateurs présents, indexés par salle.
- private readonly Dictionary> _messagesByRoom = new();
- private readonly Dictionary> _usersByRoom = new();
- private static readonly ObservableCollection _noUsers = new();
-
- // Vues "focus" exposées à la page : pointent vers la salle
- // sélectionnée, ou vers le journal système / une liste vide.
- [ObservableProperty]
- public partial ObservableCollection FocusedMessages { get; set; }
-
- [ObservableProperty]
- public partial ObservableCollection FocusedUsers { get; set; } = _noUsers;
-
- partial void OnSelectedRoomChanged(string? value)
- {
- FocusedMessages = value != null && _messagesByRoom.TryGetValue(value, out var msgs)
- ? msgs : _systemMessages;
- FocusedUsers = value != null && _usersByRoom.TryGetValue(value, out var users)
- ? users : _noUsers;
- }
-
- // Trace une notification système dans la vue courante
- // (et dans le journal global pour consultation ultérieure).
- private void Log(string line)
- {
- _systemMessages.Add(line);
- if (!ReferenceEquals(FocusedMessages, _systemMessages))
- FocusedMessages.Add(line);
- }
-
- // Commandes pour les boutons
- [RelayCommand]
- public async Task ConnectCommand() => await ConnectAsync();
-
- [RelayCommand]
- public async Task JoinRoomCommand() => await JoinRoomAsync();
-
- // Le serveur (ChatHub.Send) refuse tout message d'un utilisateur
- // qui n'a pas joint le salon ("NoJoinNoSend") : on garde la
- // commande inactive tant qu'aucune salle n'est en focus.
- private bool CanSendMessage() =>
- IsConnected && SelectedRoom != null && !string.IsNullOrWhiteSpace(MessageToSend);
-
- [RelayCommand(CanExecute = nameof(CanSendMessage))]
- private async Task SendMessageAsync()
- {
- if (_connection == null || SelectedRoom == null) return;
- var text = MessageToSend.Trim();
- try
- {
- await _connection.InvokeAsync("Send", SelectedRoom, text);
- MessageToSend = string.Empty;
- }
- catch (Exception ex)
- {
- Log($"Erreur d'envoi : {ex.Message}");
- }
- }
-
- public override bool CanNavigateNext { get; protected set; } = false;
- public override bool CanNavigatePrevious { get; protected set; } = true;
-
- private async Task DisconnectAsync()
- {
- if (_connection != null)
- {
- await _connection.StopAsync();
- await _connection.DisposeAsync();
- _connection = null;
- }
- IsConnected = false;
- SelectedRoom = null;
- JoinedRooms.Clear();
- _messagesByRoom.Clear();
- _usersByRoom.Clear();
- _systemMessages.Clear();
- Status = new StatusNotice("Déconnecté", StatusSeverity.Info,
- new RelayCheckCommand("Connecter", async () => await ConnectAsync()));
- }
-
- private async Task ConnectAsync()
- {
- try
- {
- Status = new StatusNotice("Connexion en cours...", StatusSeverity.Info,
- new RelayCheckCommand("Vérifier la connexion", async () => await CheckConnectionAsync()));
-
- if (_api == null || !_api.HasValidSession)
- {
- Status = new StatusNotice("Session expirée : reconnectez-vous avant d'ouvrir le chat.", StatusSeverity.Error,
- new RelayCheckCommand("Reconnecter", async () => await ConnectAsync()));
- return;
- }
-
- // Le hub est mappé à la racine du service ("/chathub"),
- // pas sous le préfixe versionné "/api/v1" : on le retire.
- var hubBase = _settings.ApiUrl.TrimEnd('/');
- if (hubBase.EndsWith("/api/v1", StringComparison.OrdinalIgnoreCase))
- hubBase = hubBase[..^"/api/v1".Length];
- var hubUrl = hubBase + "/" + Constants.ChatHubPath;
-
- _connection = new HubConnectionBuilder()
- .WithUrl(hubUrl,
-
- options =>
- {
- // Le hub est [Authorize] : on attache le Bearer OIDC.
- // AccessTokenProvider est rappelé à chaque reconnexion,
- // donc on tente un refresh silencieux à chaque appel.
- // Un refresh mort (invalid_grant) ne doit pas faire
- // échouer StartAsync avec une exception interne : on
- // renvoie le token courant (éventuellement expiré) et
- // le serveur répondra un 401 propre, surfacé à l'UI.
- options.AccessTokenProvider = async () =>
- {
- await _api.TrySilentLoginAsync();
- return _api.CurrentAccessToken;
- };
- })
- .WithAutomaticReconnect()
- .Build();
-
- // 1. Ecoute des notifications globales (notifyUser)
- _connection.On("notifyUser", (type, user, msg) =>
- {
- Avalonia.Threading.Dispatcher.UIThread.Post(() =>
- Log($"[{type}] {user}: {msg}"));
- });
-
- // 2. Ecoute des notifications de salon (notifyRoom)
- _connection.On("notifyRoom", (type, room, user) =>
- {
- Avalonia.Threading.Dispatcher.UIThread.Post(() =>
- {
- if (_messagesByRoom.TryGetValue(room, out var roomLog))
- roomLog.Add($"*{user} a interagi avec le salon {room} (Action: {type})");
- else
- Log($"*{user} a interagi avec le salon {room} (Action: {type})");
- if (type == "UserJoin" && _usersByRoom.TryGetValue(room, out var users)
- && !users.Contains(user))
- users.Add(user);
- });
- });
-
- // 3. Réception de l'état complet suite au Join réussi (joint)
- _connection.On("joint", (roomInfo) =>
- {
- Avalonia.Threading.Dispatcher.UIThread.Post(() => OnRoomJoined(roomInfo));
- });
-
- // 4. Réception des messages de salon (diffusés par ChatHub.Send)
- _connection.On("ReceiveMessage", (msg) =>
- {
- Avalonia.Threading.Dispatcher.UIThread.Post(() =>
- {
- var room = msg.Room ?? "?";
- if (!_messagesByRoom.TryGetValue(room, out var roomLog))
- roomLog = _messagesByRoom[room] = new();
- roomLog.Add($"{msg.Name} : {msg.Message}");
- });
- });
-
- await _connection.StartAsync();
- IsConnected = true;
- Status = new StatusNotice("Connecté au Hub SignalR", StatusSeverity.Info, new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
-
- // WithAutomaticReconnect ne lève pas d'exception en cas de
- // perte réseau : l'état passe par Reconnecting/Closed. Sans
- // ces handlers, IsConnected resterait optimiste et Join()
- // échouerait avec "connection is not active".
- _connection.Reconnecting += error =>
- {
- Avalonia.Threading.Dispatcher.UIThread.Post(() =>
- {
- IsConnected = false;
- Status = new StatusNotice($"Connexion perdue ({error?.Message ?? "réseau"}), reconnexion...", StatusSeverity.Error,
- new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
- });
- return Task.CompletedTask;
- };
- _connection.Reconnected += _ =>
- {
- Avalonia.Threading.Dispatcher.UIThread.Post(() =>
- {
- IsConnected = true;
- Status = new StatusNotice("Reconnecté au Hub SignalR", StatusSeverity.Info,
- new RelayCheckCommand("Déconnecter", async () => await DisconnectAsync()));
- });
- return Task.CompletedTask;
- };
- _connection.Closed += error =>
- {
- Avalonia.Threading.Dispatcher.UIThread.Post(() =>
- {
- IsConnected = false;
- Status = new StatusNotice($"Déconnecté ({error?.Message ?? "fin de connexion"})", StatusSeverity.Info,
- new RelayCheckCommand("Connecter", async () => await ConnectAsync()));
- });
- return Task.CompletedTask;
- };
- }
- catch (Exception ex)
- {
- Status = new StatusNotice($"Erreur : {ex.Message}", StatusSeverity.Error, new RelayCheckCommand("Reconnecter", async () => await ConnectAsync()));
- IsConnected = false;
- }
- }
-
- // Alimente JoinedRooms et les collections par salle à partir
- // du ChatRoomInfo renvoyé par le hub (valeur de retour du Join
- // et/ou événement "joint"). Idempotent.
- private void OnRoomJoined(ChatRoomInfo roomInfo)
- {
- // ChatHub.Join renseigne toujours Name (salle existante ou
- // créée) ; un Name nul signalerait un payload inattendu.
- var roomName = roomInfo.Name ?? CurrentRoom;
- if (roomInfo.Name == null)
- Log("OnRoomJoined : nom de salle absent du payload, repli sur la salle demandée.");
- if (!_messagesByRoom.TryGetValue(roomName, out var roomLog))
- roomLog = _messagesByRoom[roomName] = new();
- roomLog.Add($"Salon rejoint avec succès : {roomName}");
- var users = _usersByRoom[roomName] = new();
- foreach (var u in roomInfo.Users) users.Add(u);
- if (!JoinedRooms.Contains(roomName))
- JoinedRooms.Add(roomName);
- SelectedRoom = roomName;
- }
-
- private Task CheckConnectionAsync()
- {
- var state = _connection?.State.ToString() ?? "aucune connexion";
- Log($"État de la connexion : {state}");
- return Task.CompletedTask;
- }
-
- private async Task JoinRoomAsync()
- {
- if (_connection == null || string.IsNullOrWhiteSpace(CurrentRoom)) return;
-
- // Salle déjà jointe : on se contente de la mettre en focus.
- if (JoinedRooms.Contains(CurrentRoom))
- {
- SelectedRoom = CurrentRoom;
- return;
- }
-
- // InvokeCoreAsync exige une connexion active : avec la
- // reconnexion automatique, l'état peut être Reconnecting au
- // moment de l'appel. On tente une (re)connexion si nécessaire.
- if (_connection.State != HubConnectionState.Connected)
- {
- Log($"Connexion non active ({_connection.State}), tentative de reconnexion...");
- try
- {
- await _connection.StartAsync();
- }
- catch (Exception ex)
- {
- Log($"Reconnexion impossible : {ex.Message}");
- return;
- }
- }
-
- try
- {
- // Appel de la méthode publique 'Join' du Hub (renvoie un ChatRoomInfo)
- var info = await _connection.InvokeAsync("Join", CurrentRoom);
- if (info == null)
- {
- Log("Erreur lors de la validation du salon par le serveur.");
- }
- else
- {
- // On alimente le modèle depuis la valeur de retour,
- // sans attendre l'événement "joint".
- OnRoomJoined(info);
- }
- }
- catch (Exception ex)
- {
- Log($"Erreur Join: {ex.Message}");
- }
- }
- }
-}
diff --git a/src/PostIt/PostIt/ViewModels/Chat/RelayCheckCommand.cs b/src/PostIt/PostIt/ViewModels/Chat/RelayCheckCommand.cs
deleted file mode 100644
index 909d7a859..000000000
--- a/src/PostIt/PostIt/ViewModels/Chat/RelayCheckCommand.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using System.Windows.Input;
-
-namespace PostIt.ViewModels.Chat
-{
- internal class RelayCheckCommand : ICommand, IWithLabel
- {
- public string Label { get; }
- private Func value;
-
- public RelayCheckCommand(string v, Func value)
- {
- this.Label = v;
- this.value = value;
- }
-
- public event EventHandler? CanExecuteChanged;
-
- public bool CanExecute(object? parameter)
- {
- return value != null;
- }
-
- public void Execute(object? parameter)
- {
- if (value != null)
- {
- _ = value();
- }
- }
- }
-}
diff --git a/src/PostIt/PostIt/ViewModels/Commands/BillingCommandPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Commands/BillingCommandPageViewModel.cs
index 4898c9560..5ad2b08f4 100644
--- a/src/PostIt/PostIt/ViewModels/Commands/BillingCommandPageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Commands/BillingCommandPageViewModel.cs
@@ -2,7 +2,6 @@ using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
-using PostIt.Models;
using Yavsc;
using Yavsc.Abstract.Workflow;
using Yavsc.Api.Client;
diff --git a/src/PostIt/PostIt/ViewModels/Commands/BrushViewModel.cs b/src/PostIt/PostIt/ViewModels/Commands/BrushViewModel.cs
index 1b1847e96..72d9ce8ea 100644
--- a/src/PostIt/PostIt/ViewModels/Commands/BrushViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Commands/BrushViewModel.cs
@@ -6,7 +6,6 @@ using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
-using PostIt.Models;
using Yavsc.Abstract.Workflow;
using Yavsc.Api.Client;
using Yavsc.Models.Billing;
diff --git a/src/PostIt/PostIt/ViewModels/Layout/ActionStatusViewModelExtensions.cs b/src/PostIt/PostIt/ViewModels/Layout/ActionStatusViewModelExtensions.cs
index 9fe286ac0..614d477c8 100644
--- a/src/PostIt/PostIt/ViewModels/Layout/ActionStatusViewModelExtensions.cs
+++ b/src/PostIt/PostIt/ViewModels/Layout/ActionStatusViewModelExtensions.cs
@@ -1,5 +1,3 @@
-using PostIt.Models;
-
namespace PostIt.ViewModels;
public interface IActionStatusViewModel
@@ -31,4 +29,4 @@ public static class ActionStatusViewModelExtensions
_ => StatusNotice.Info(normalizedMessage),
};
}
-}
+}
\ No newline at end of file
diff --git a/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs b/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs
index b76f31f27..2c138a27d 100644
--- a/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs
@@ -3,8 +3,8 @@ using System.Threading.Tasks;
using Avalonia;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
+using PostIt.Helpers;
using PostIt.Services;
-using PostIt.ViewModels.Chat;
using Yavsc.Api.Client;
namespace PostIt.ViewModels;
@@ -36,11 +36,7 @@ public class HomePageViewModel : ViewModelBase
OpenProviderEstimates = new AsyncRelayCommand(() => OpenEstimateListAsync(EstimateListPerspective.Provider));
OpenBlogs = new AsyncRelayCommand(App.PushBlogsPageAsync);
OpenMyFiles = new AsyncRelayCommand(OpenMyFilesAsync);
- OpenChat = new AsyncRelayCommand(OpenChatAsync);
}
-
-
- public IAsyncRelayCommand OpenChat { get; }
public IAsyncRelayCommand OpenBlogs { get; }
public IAsyncRelayCommand OpenActivities { get; }
public IAsyncRelayCommand OpenProviderRequests { get; }
@@ -107,14 +103,6 @@ public class HomePageViewModel : ViewModelBase
await app.PushPageAsync(vm);
}
- private async Task OpenChatAsync()
- {
- var app = (App?)Application.Current;
- var vm = app!.ServiceProvider?.GetRequiredService();
- await app.PushPageAsync(vm!);
-
- }
-
///
/// Ouvre la page « Mes fichiers » (espace de stockage personnel),
/// servie par le host Blogs via api/v1/fs.
diff --git a/src/PostIt/PostIt/ViewModels/Layout/StatusNotice.cs b/src/PostIt/PostIt/ViewModels/Layout/StatusNotice.cs
new file mode 100644
index 000000000..c87af390c
--- /dev/null
+++ b/src/PostIt/PostIt/ViewModels/Layout/StatusNotice.cs
@@ -0,0 +1,35 @@
+namespace PostIt.ViewModels;
+
+public enum StatusSeverity
+{
+ Info,
+ Warning,
+ Error
+}
+
+public sealed class StatusNotice
+{
+ public string Message { get; }
+ public StatusSeverity Severity { get; }
+ public string Glyph { get; }
+ public string Background { get; }
+ public string BorderBrush { get; }
+ public string Foreground { get; }
+
+ private StatusNotice(string message, StatusSeverity severity)
+ {
+ Message = string.IsNullOrWhiteSpace(message) ? "Pret." : message;
+ Severity = severity;
+
+ (Glyph, Background, BorderBrush, Foreground) = severity switch
+ {
+ StatusSeverity.Error => ("!", "#7F1D1D", "#C62828", "#e1f0f6"),
+ StatusSeverity.Warning => ("~", "#7C4A03", "#E6A700", "#eaeaea"),
+ _ => ("i", "#E8F0FE", "#5B8DEF", "#1E3A8A"),
+ };
+ }
+
+ public static StatusNotice Info(string message) => new(message, StatusSeverity.Info);
+ public static StatusNotice Warning(string message) => new(message, StatusSeverity.Warning);
+ public static StatusNotice Error(string message) => new(message, StatusSeverity.Error);
+}
diff --git a/src/PostIt/PostIt/ViewModels/MyFiles/MyFilesViewModel.cs b/src/PostIt/PostIt/ViewModels/MyFiles/MyFilesViewModel.cs
index 169b29b16..cc4b46828 100644
--- a/src/PostIt/PostIt/ViewModels/MyFiles/MyFilesViewModel.cs
+++ b/src/PostIt/PostIt/ViewModels/MyFiles/MyFilesViewModel.cs
@@ -7,7 +7,6 @@ using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using PostIt.Helpers;
-using PostIt.Models;
using Yavsc.Api.Client;
namespace PostIt.ViewModels;
@@ -227,4 +226,4 @@ public partial class MyFilesViewModel : ViewModelBase, IActionStatusViewModel
var idx = current.LastIndexOf('/');
return idx < 0 ? string.Empty : current[..idx];
}
-}
+}
\ No newline at end of file
diff --git a/src/PostIt/PostIt/ViewModels/Settings/Settings.cs b/src/PostIt/PostIt/ViewModels/Settings/Settings.cs
index 72eea5d35..5576f01cd 100644
--- a/src/PostIt/PostIt/ViewModels/Settings/Settings.cs
+++ b/src/PostIt/PostIt/ViewModels/Settings/Settings.cs
@@ -8,8 +8,6 @@ using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
-using System.Globalization;
-using PostIt.Models;
[assembly: InternalsVisibleTo("PostIt.Tests")]
@@ -46,7 +44,6 @@ public partial class Settings : ViewModelBase
public partial StatusNotice ActionStatus { get; set; } = StatusNotice.Info("Pret.");
- [JsonIgnore]
public bool Loaded { get; private set; } = false;
@@ -62,16 +59,8 @@ public partial class Settings : ViewModelBase
/// .
///
[ObservableProperty]
- [JsonIgnore]
public partial bool IsDirty { get; private set; } = false;
- [JsonIgnore]
- public override bool CanNavigateNext { get => false; protected set => throw new System.NotImplementedException(); }
-
- [JsonIgnore]
- public override bool CanNavigatePrevious { get => true; protected set => throw new System.NotImplementedException(); }
-
- public string SelectedLanguage { get; internal set; } = CultureInfo.CurrentUICulture.Name;
///
/// Guards every mutation of the observable state. [ObservableProperty]
@@ -272,9 +261,10 @@ public partial class Settings : ViewModelBase
}
var app = App.Current as App;
- if (App.CliConfigFileSpecification!=null)
+ if (app!=null)
+ if (app.CliConfigFileSpecification!=null)
{
- SettingsFileFullName = App.CliConfigFileSpecification;
+ SettingsFileFullName = app.CliConfigFileSpecification;
}
if (SettingsFileFullName is not null)
@@ -542,4 +532,7 @@ public partial class Settings : ViewModelBase
///
partial void OnIsDirtyChanged(bool value) => SaveCommand.NotifyCanExecuteChanged();
+
+ public override bool CanNavigateNext { get => false; protected set => throw new System.NotImplementedException(); }
+ public override bool CanNavigatePrevious { get => true; protected set => throw new System.NotImplementedException(); }
}
diff --git a/src/PostIt/PostIt/ViewModels/StatusNotice.cs b/src/PostIt/PostIt/ViewModels/StatusNotice.cs
deleted file mode 100644
index 404287dbe..000000000
--- a/src/PostIt/PostIt/ViewModels/StatusNotice.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Windows.Input;
-using PostIt.Models;
-
-namespace PostIt.ViewModels;
-
-public sealed class StatusNotice
-{
- public string Message { get; }
- public StatusSeverity Severity { get; }
- public string Glyph { get; }
- public string Background { get; }
- public string BorderBrush { get; }
- public string Foreground { get; }
-
- public bool HasCheckCommand { get => CheckCommand != null; }
-
- public ICommand? CheckCommand { get; }
-
- public string CheckCommandLabel { get; }
-
- public StatusNotice(string message, StatusSeverity severity, ICommand? checkCommand)
- {
- Message = string.IsNullOrWhiteSpace(message) ? "Pret." : message;
- Severity = severity;
-
- (Glyph, Background, BorderBrush, Foreground) = severity switch
- {
- StatusSeverity.Error => ("!", "#7F1D1D", "#C62828", "#e1f0f6"),
- StatusSeverity.Warning => ("~", "#7C4A03", "#E6A700", "#eaeaea"),
- _ => ("i", "#E8F0FE", "#5B8DEF", "#1E3A8A"),
- };
- CheckCommand = checkCommand;
- CheckCommandLabel = HasCheckCommand ?
- (checkCommand is IWithLabel labeled ? labeled.Label : "Check") : string.Empty;
- }
-
- public static StatusNotice Info(string message, ICommand? checkCommand = null) => new(message, StatusSeverity.Info, checkCommand);
- public static StatusNotice Warning(string message, ICommand? checkCommand = null) => new(message, StatusSeverity.Warning, checkCommand);
- public static StatusNotice Error(string message, ICommand? checkCommand = null) => new(message, StatusSeverity.Error, checkCommand);
-}
diff --git a/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml b/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml
index 5c926bdee..90397f09d 100644
--- a/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml
+++ b/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml
@@ -17,14 +17,10 @@
-
-
+
+
-
+
@@ -107,7 +103,7 @@
-
+
@@ -141,61 +137,90 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ BorderBrush="Gray"
+ BorderThickness="1"
+ IsVisible="{Binding IsMarkdownViewerVisible}">
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+ VerticalAlignment="Stretch"
+ IsVisible="{Binding !IsMarkdownViewerVisible}">
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml.cs b/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml.cs
index b40021276..89743d0ad 100644
--- a/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml.cs
+++ b/src/PostIt/PostIt/Views/Blogs/BlogsPage.axaml.cs
@@ -4,16 +4,72 @@ using Avalonia.Controls;
using Avalonia.Platform.Storage;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
+using Avalonia.Controls.Platform;
+using Avalonia.Input;
+using System;
+using Avalonia;
namespace PostIt.Views.Blogs;
public partial class BlogsPage : ContentPage
{
+ private IInputPane _inputPane;
+
public BlogsPage()
{
InitializeComponent();
}
+ protected override void OnLoaded(RoutedEventArgs e)
+ {
+ base.OnLoaded(e);
+
+ // Récupère l'accès au TopLevel de la fenêtre/application
+ var topLevel = TopLevel.GetTopLevel(this);
+ if (topLevel != null)
+ {
+ _inputPane = topLevel.InputPane;
+ if (_inputPane != null)
+ {
+ // S'abonne aux changements d'état du clavier virtuel
+ _inputPane.StateChanged += OnInputPaneStateChanged;
+
+ // Applique la configuration initiale si le clavier est déjà présent
+ UpdateLayoutForKeyboard(_inputPane.OccludedRect.Height);
+ }
+ }
+ }
+
+ protected override void OnUnloaded(RoutedEventArgs e)
+ {
+ if (_inputPane != null)
+ {
+ _inputPane.StateChanged -= OnInputPaneStateChanged;
+ }
+ base.OnUnloaded(e);
+ }
+
+
+ private void UpdateLayoutForKeyboard(double keyboardHeight)
+ {
+ double visibleHeight = this.Bounds.Height - keyboardHeight;
+ if (keyboardHeight > 0)
+ {
+ DraftArticleTextEditor.MaxHeight = visibleHeight;
+ }
+ else
+ {
+ DraftArticleTextEditor.MaxHeight = double.PositiveInfinity;
+ }
+ }
+
+ private void OnInputPaneStateChanged(object? sender, InputPaneStateEventArgs e)
+ {
+ // e.EndRect contient le rectangle final du clavier virtuel
+ // e.EndRect.Height vous donne sa hauteur exacte en pixels indépendants du périphérique (pixels logiques)
+ UpdateLayoutForKeyboard(e.EndRect.Height);
+ }
+
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
diff --git a/src/PostIt/PostIt/Views/Chat/ChatPage.axaml b/src/PostIt/PostIt/Views/Chat/ChatPage.axaml
deleted file mode 100644
index 4749736a7..000000000
--- a/src/PostIt/PostIt/Views/Chat/ChatPage.axaml
+++ /dev/null
@@ -1,111 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/PostIt/PostIt/Views/Chat/ChatPage.axaml.cs b/src/PostIt/PostIt/Views/Chat/ChatPage.axaml.cs
deleted file mode 100644
index f73e1f827..000000000
--- a/src/PostIt/PostIt/Views/Chat/ChatPage.axaml.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Avalonia.Controls;
-namespace PostIt.Views.Chat;
-public partial class ChatPage : ContentPage
-{
-
- public ChatPage()
- {
- InitializeComponent();
- }
-
-}
diff --git a/src/PostIt/PostIt/Views/Layout/HomePage.axaml b/src/PostIt/PostIt/Views/Layout/HomePage.axaml
index 1de3ece68..31dd5851b 100644
--- a/src/PostIt/PostIt/Views/Layout/HomePage.axaml
+++ b/src/PostIt/PostIt/Views/Layout/HomePage.axaml
@@ -1,7 +1,6 @@
@@ -11,15 +10,10 @@
-
-
-