Compare commits

..

No commits in common. "20a6f22ec39aefbb8bf9b82b0daa72fca537130e" and "f2ae01729a821bef023627b9439f8e5447a499b8" have entirely different histories.

5 changed files with 18 additions and 32 deletions

View file

@ -56,7 +56,6 @@ pas vers un serveur HTTP.
|---------------------------------|-------------------------------------------------------------------|
| `Services/OidcLoginPhase` | Enum des étapes du flow : `Idle / Discovering / OpeningBrowser / AwaitingCallback / ExchangingCode / Success / Error` |
| `Services/YavscApiClient` | Client HTTP de l'API Yavsc. Porte `LoginInteractiveAsync(IProgress<OidcLoginPhase>)` et `TrySilentLoginAsync`. Refresh silencieux sur 401 et sur access-token bientôt expiré. |
| `Services/BlogApiClient` | Mapper DTO↔path pour la sous-API blog. **Note** : `pathPrefix` est *relatif* à `/api/v1/` (que porte déjà `BaseAddress`) — ex. `"blog"` pour matcher `[Route(APIPrefix + "/blog")]`. Ne pas ré-inclure `api/`. |
| `Services/SingleInstance` | Named-pipe helper. `TryHandOffAsync` côté 2ᵉ instance, `StartServerAsync` côté instance vivante. |
| `Services/CustomSchemeBrowser` | `IBrowser` OidcClient qui ouvre le système + attend le pipe. |
| `Services/SchemeUrlDetector` | Détection pure, testable, du `postit://callback` dans argv. |

View file

@ -15,16 +15,6 @@ namespace PostIt.Services;
/// <see cref="YavscApiClient"/>. This class is a thin DTO↔path
/// mapper, nothing more.
///
/// <para><b>URL convention.</b> <see cref="YavscApiClient"/>'s
/// <c>BaseAddress</c> already terminates with <c>/api/v1/</c>
/// (see <c>Settings.ApiUrl</c>). The path prefix below is
/// therefore <i>relative</i> to that version segment: a prefix of
/// <c>"blog"</c> resolves to <c>…/api/v1/blog</c>, which matches
/// the <c>[Route(APIPrefix + "/blog")]</c> attribute on
/// <c>Yavsc.Blogs.Controllers.BlogApiController</c>. Do not
/// re-include the <c>api/</c> segment here — that produced 404s
/// in the past (see commit "PostIt: fix blog API double-prefix").</para>
///
/// The class is intentionally non-IDisposable: it does not own the
/// <see cref="YavscApiClient"/> it depends on. Lifetimes are managed
/// by the consumer (typically a singleton service registered with
@ -32,7 +22,7 @@ namespace PostIt.Services;
/// </summary>
public sealed class BlogApiClient
{
private const string DefaultPathPrefix = "blog";
private const string DefaultPathPrefix = "api/blog";
private readonly YavscApiClient _api;
private readonly string _pathPrefix;

View file

@ -128,28 +128,9 @@ public partial class MainPageViewModel : ViewModelBase
[RelayCommand]
internal async Task Save()
{
// No selection means "create a new post from the editor".
// The server is the source of truth, so we POST without an id
// and let BlogApiController assign one. The local view-model
// is then rebound to the server-issued record.
if (SelectedPost is null)
{
var draft = new BlogPost
{
Title = string.Empty,
Article = string.Empty,
DateCreated = DateTime.UtcNow,
DateModified = DateTime.UtcNow
};
await ExecuteAsync(async () =>
{
var created = await BlogClient.CreatePostAsync(draft);
if (created is not null)
{
SelectedPost = created;
StatusMessage = $"Created post {created.Id}.";
}
});
StatusMessage = "A post must be selected before saving.";
return;
}
@ -195,6 +176,19 @@ public partial class MainPageViewModel : ViewModelBase
});
}
[RelayCommand]
internal void New()
{
SelectedPost = new BlogPost
{
Title = string.Empty,
Article = string.Empty,
DateCreated = DateTime.UtcNow,
DateModified = DateTime.UtcNow
};
StatusMessage = "New blog post ready.";
}
[RelayCommand]
internal void OpenSettings()
{
@ -259,6 +253,7 @@ public partial class MainPageViewModel : ViewModelBase
LoadPostsCommand.NotifyCanExecuteChanged();
SaveCommand.NotifyCanExecuteChanged();
DeleteCommand.NotifyCanExecuteChanged();
NewCommand.NotifyCanExecuteChanged();
}
private bool CanSave() => SelectedPost is not null && !IsBusy;

View file

@ -32,6 +32,7 @@
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Command="{Binding LoadPosts}" Content="Load posts" />
<Button Command="{Binding Search}" Content="Filter" />
<Button Command="{Binding New}" Content="New post" />
<Button Command="{Binding Save}" Content="Save" />
<Button Command="{Binding Delete}" Content="Delete" />
<!--

View file

@ -10,6 +10,7 @@ namespace Yavsc.Blogs.Controllers
[Authorize("BlogScope")]
[Produces("application/json")]
[Route(APIPrefix + "/blog")]
public class BlogApiController : Controller
{
private readonly BlogSpotService blogSpotService;