diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 82721d8a2..a30c9c065 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -47,10 +47,7 @@ "group": "build", "isBuildCommand": true, "isTestCommand": false, - "isBackground": true, - "options": { - "cwd": "${workspaceFolder}" - } + "isBackground": true }, { "label": "test blogs backend", diff --git a/src/PostIt/PostIt.Tests/ActivitiesPageViewModelTests.cs b/src/PostIt/PostIt.Tests/ActivitiesPageViewModelTests.cs index 6cac39603..04cc0f314 100644 --- a/src/PostIt/PostIt.Tests/ActivitiesPageViewModelTests.cs +++ b/src/PostIt/PostIt.Tests/ActivitiesPageViewModelTests.cs @@ -162,12 +162,6 @@ public class ActivitiesPageViewModelTests return Task.CompletedTask; } - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - public ValueTask DisposeAsync() => ValueTask.CompletedTask; } } diff --git a/src/PostIt/PostIt.Tests/BillingCommandPageViewModelTests.cs b/src/PostIt/PostIt.Tests/BillingCommandPageViewModelTests.cs index 34804b4cd..17fcc968c 100644 --- a/src/PostIt/PostIt.Tests/BillingCommandPageViewModelTests.cs +++ b/src/PostIt/PostIt.Tests/BillingCommandPageViewModelTests.cs @@ -352,12 +352,6 @@ public class BillingCommandPageViewModelTests return Task.CompletedTask; } - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - public ValueTask DisposeAsync() => ValueTask.CompletedTask; } } diff --git a/src/PostIt/PostIt.Tests/BillingQueriesPageViewModelTests.cs b/src/PostIt/PostIt.Tests/BillingQueriesPageViewModelTests.cs index 0740963b6..a7f2b0844 100644 --- a/src/PostIt/PostIt.Tests/BillingQueriesPageViewModelTests.cs +++ b/src/PostIt/PostIt.Tests/BillingQueriesPageViewModelTests.cs @@ -129,12 +129,6 @@ public class BillingQueriesPageViewModelTests return Task.CompletedTask; } - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - public ValueTask DisposeAsync() => ValueTask.CompletedTask; } } diff --git a/src/PostIt/PostIt.Tests/MainPageButtonsTests.cs b/src/PostIt/PostIt.Tests/MainPageButtonsTests.cs index 8abf89139..d1d00532d 100644 --- a/src/PostIt/PostIt.Tests/MainPageButtonsTests.cs +++ b/src/PostIt/PostIt.Tests/MainPageButtonsTests.cs @@ -8,7 +8,6 @@ using Yavsc.Blogspot; using PostIt.Services; using PostIt.ViewModels; using PostIt.Views; -using PostIt.Views.Blogs; namespace PostIt.Tests; @@ -73,13 +72,13 @@ public class MainPageButtonsTests { } } - private static BlogsViewModel MakeViewModel(BlogPostDto? selectedPost = null) + private static MainViewModel MakeViewModel(BlogPostDto? selectedPost = null) { var api = new ThrowingApi(); var blog = new BlogApiClient(api, "http://localhost/"); var circle = new CircleApiClient(api, "http://localhost/"); var acl = new BlogAclApiClient(api, "http://localhost/"); - // Minimal DI graph: only what BlogsViewModel resolves + // Minimal DI graph: only what MainPageViewModel resolves // when the user clicks a navigation button. Today that's // SignaturePageViewModel / CirclesPageViewModel / ACL // dependencies. The graph intentionally stays local to this @@ -94,7 +93,7 @@ public class MainPageButtonsTests services.AddTransient(); services.AddTransient(); services.AddTransient(); - var vm = new BlogsViewModel(blog, services: services.BuildServiceProvider()); + var vm = new MainViewModel(blog, services: services.BuildServiceProvider()); if (selectedPost is not null) vm.SelectedPost = selectedPost; return vm; } @@ -102,7 +101,7 @@ public class MainPageButtonsTests /// /// Mount a real (as /// SessionStatusBannerTests does), push a - /// with the given VM onto + /// with the given VM onto /// NavRoot. PushAsync is awaited (via /// GetAwaiter().GetResult()) so the page is on the /// nav stack before the test tries to interact with its @@ -110,10 +109,10 @@ public class MainPageButtonsTests /// realised and KeyPressQwerty has a real /// to dispatch against. /// - private static (MainView window, BlogsPage page) MountMainPage(BlogsViewModel vm) + private static (MainView window, MainPage page) MountMainPage(MainViewModel vm) { var window = new MainView(); - var page = new BlogsPage { DataContext = vm }; + var page = new MainPage { DataContext = vm }; var app = (PostIt.App)Application.Current!; app.AttachMainWindow(window); window.NavRoot.PushAsync(page).GetAwaiter().GetResult(); @@ -204,7 +203,7 @@ public class MainPageButtonsTests public void Signature_dev_button_click_pushes_a_page_onto_nav_stack() { // Arrange: the "[DEV] Signature" button is bound to the - // BlogsViewModel.OpenSignatureDevCommand [RelayCommand]. + // MainPageViewModel.OpenSignatureDevCommand [RelayCommand]. // The click must push SignaturePage on top of NavRoot. // The ServiceCollection registered in MakeViewModel provides // SignaturePageViewModel so the command can resolve it via diff --git a/src/PostIt/PostIt.Tests/MainPageSaveTests.cs b/src/PostIt/PostIt.Tests/MainPageSaveTests.cs index cac2f5aaf..0fd5627c8 100644 --- a/src/PostIt/PostIt.Tests/MainPageSaveTests.cs +++ b/src/PostIt/PostIt.Tests/MainPageSaveTests.cs @@ -5,11 +5,10 @@ using Yavsc.Blogspot; using Yavsc.Api.Client; using PostIt.ViewModels; using PostIt.Views; -using PostIt.Views.Blogs; namespace PostIt.Tests; /// -/// Headless UI tests for the "Save" flow in . +/// Headless UI tests for the "Save" flow in . /// The pattern is the one SessionStatusBannerTests /// established: [AvaloniaFact], a /// hosting the page (via a because @@ -41,9 +40,9 @@ public class MainPageSaveTests var recorder = new CallRecorder(); var api = new RecordingYavscApiClient(recorder); var blog = new BlogApiClient(api, "http://localhost/"); - var viewModel = new BlogsViewModel(blog); + var viewModel = new MainViewModel(blog); - var page = new BlogsPage { DataContext = viewModel }; + var page = new MainPage { DataContext = viewModel }; // MainPage is a ContentPage (a Page, not a Control), so it // must be hosted in a navigation surface. The production // MainWindow.axaml uses NavigationPage, and the API is the diff --git a/src/PostIt/PostIt.Tests/PostAclDialogTests.cs b/src/PostIt/PostIt.Tests/PostAclDialogTests.cs index ed8282173..e7175203c 100644 --- a/src/PostIt/PostIt.Tests/PostAclDialogTests.cs +++ b/src/PostIt/PostIt.Tests/PostAclDialogTests.cs @@ -270,12 +270,6 @@ public class PostAclDialogTests return Task.CompletedTask; } - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - public ValueTask DisposeAsync() => ValueTask.CompletedTask; } } diff --git a/src/PostIt/PostIt.Tests/PostItViewModelTests.cs b/src/PostIt/PostIt.Tests/PostItViewModelTests.cs index 821a778dd..1a867bd61 100644 --- a/src/PostIt/PostIt.Tests/PostItViewModelTests.cs +++ b/src/PostIt/PostIt.Tests/PostItViewModelTests.cs @@ -11,12 +11,12 @@ public class PostItViewModelTests [Fact] public void SearchCommand_filters_posts_by_title_article_or_author() { - // BlogsViewModel no longer owns a BlogApiClient instance by + // MainPageViewModel no longer owns a BlogApiClient instance by // default; tests construct one with a fake YavscApiClient that // throws on any call (we never call the API in this test). var fakeApi = new ThrowingYavscApiClient(); var blog = new BlogApiClient(fakeApi, "http://localhost/"); - var viewModel = new BlogsViewModel(blog); + var viewModel = new MainViewModel(blog); viewModel.Posts.Add(new BlogPostDto { Id = 1, Title = "First post", Article = "Hello world", AuthorId = "alice" }); viewModel.Posts.Add(new BlogPostDto { Id = 2, Title = "Second post", Article = "Nothing here", AuthorId = "bob" }); @@ -60,7 +60,7 @@ public class PostItViewModelTests { var api = new RecordingPublishApi(); var blog = new BlogApiClient(api, "http://localhost/"); - var viewModel = new BlogsViewModel(blog); + var viewModel = new MainViewModel(blog); viewModel.SelectedPost = new BlogPostDto { Id = 42, IsPublished = false }; @@ -146,12 +146,6 @@ public class PostItViewModelTests return Task.CompletedTask; } - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - public ValueTask DisposeAsync() => ValueTask.CompletedTask; } } diff --git a/src/PostIt/PostIt.Tests/RdvPageHeadlessTests.cs b/src/PostIt/PostIt.Tests/RdvPageHeadlessTests.cs index 1bcb8a0d8..fdf8689ab 100644 --- a/src/PostIt/PostIt.Tests/RdvPageHeadlessTests.cs +++ b/src/PostIt/PostIt.Tests/RdvPageHeadlessTests.cs @@ -74,12 +74,6 @@ public class RdvPageHeadlessTests public Task CallAsync(HttpMethod method, string path, object? body = null, CancellationToken ct = default) => Task.CompletedTask; - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - public ValueTask DisposeAsync() => ValueTask.CompletedTask; } -} +} \ No newline at end of file diff --git a/src/PostIt/PostIt/App.axaml.cs b/src/PostIt/PostIt/App.axaml.cs index 9f0e8f76b..59d5e412c 100644 --- a/src/PostIt/PostIt/App.axaml.cs +++ b/src/PostIt/PostIt/App.axaml.cs @@ -148,7 +148,7 @@ private void ConfigureRootView(MainView rootView) public static async Task PushBlogsPageAsync() { var app = (App)Current!; - var mainVm = app.ServiceProvider!.GetRequiredService(); + var mainVm = app.ServiceProvider!.GetRequiredService(); await mainVm.InitializeAsync(); await app.PushPageAsync(mainVm); } diff --git a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs index c87eb9286..c0831b290 100644 --- a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs +++ b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection; using PostIt.Services; using PostIt.ViewModels; using PostIt.Views; -using PostIt.Views.Blogs; using PostIt.Views.Commands; using Yavsc.Api.Client; @@ -35,7 +34,7 @@ public static class ServiceCollectionHelpers // Vues services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); // SettingsPage is a singleton: there must be one and only one @@ -90,7 +89,7 @@ public static class ServiceCollectionHelpers sessionStatus.Refresh(); services.AddSingleton(sessionStatus); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); return services.BuildServiceProvider(); } } diff --git a/src/PostIt/PostIt/Services/YavscApiClient.cs b/src/PostIt/PostIt/Services/YavscApiClient.cs index 7e3187d8b..4d748f87d 100644 --- a/src/PostIt/PostIt/Services/YavscApiClient.cs +++ b/src/PostIt/PostIt/Services/YavscApiClient.cs @@ -191,28 +191,7 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable object? body = null, CancellationToken ct = default) { - using var response = await SendAsync(method, path, body is null ? null : () => JsonContent.Create(body), ct).ConfigureAwait(false); - await EnsureSuccessOrThrowAsync(response, ct).ConfigureAwait(false); - - var stream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false); - var dto = await JsonSerializer.DeserializeAsync(stream, - new JsonSerializerOptions { PropertyNameCaseInsensitive = true }, ct).ConfigureAwait(false); - return dto!; - } - - /// - /// Call a multipart endpoint, transparently refreshing the token if needed. - /// - public virtual async Task CallAsync( - HttpMethod method, - string path, - Func contentFactory, - CancellationToken ct = default) - { - if (contentFactory is null) - throw new ArgumentNullException(nameof(contentFactory)); - - using var response = await SendAsync(method, path, contentFactory, ct).ConfigureAwait(false); + using var response = await SendAsync(method, path, body, ct).ConfigureAwait(false); await EnsureSuccessOrThrowAsync(response, ct).ConfigureAwait(false); var stream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false); @@ -238,23 +217,7 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable object? body = null, CancellationToken ct = default) { - using var response = await SendAsync(method, path, body is null ? null : () => JsonContent.Create(body), ct).ConfigureAwait(false); - await EnsureSuccessOrThrowAsync(response, ct).ConfigureAwait(false); - } - - /// - /// Call a multipart endpoint that returns no useful body (DELETE, etc.). - /// - public async Task CallAsync( - HttpMethod method, - string path, - Func contentFactory, - CancellationToken ct = default) - { - if (contentFactory is null) - throw new ArgumentNullException(nameof(contentFactory)); - - using var response = await SendAsync(method, path, contentFactory, ct).ConfigureAwait(false); + using var response = await SendAsync(method, path, body, ct).ConfigureAwait(false); await EnsureSuccessOrThrowAsync(response, ct).ConfigureAwait(false); } @@ -269,7 +232,7 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable => CallAsync(method, path, body: null, ct); private async Task SendAsync( - HttpMethod method, string path, Func? contentFactory, CancellationToken ct) + HttpMethod method, string path, object? body, CancellationToken ct) { if (_tokens is null) throw new InvalidOperationException("Not logged in. Call LoginInteractiveAsync first."); @@ -277,8 +240,8 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable await EnsureFreshTokenAsync(ct).ConfigureAwait(false); using var req = new HttpRequestMessage(method, path); - if (contentFactory is not null) - req.Content = contentFactory(); + if (body is not null) + req.Content = JsonContent.Create(body); var response = await Http.SendAsync(req, ct).ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.Unauthorized) @@ -289,8 +252,8 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable await ForceRefreshAsync(ct).ConfigureAwait(false); using var retry = new HttpRequestMessage(method, path); - if (contentFactory is not null) - retry.Content = contentFactory(); + if (body is not null) + retry.Content = JsonContent.Create(body); response = await Http.SendAsync(retry, ct).ConfigureAwait(false); } diff --git a/src/PostIt/PostIt/ViewLocator.cs b/src/PostIt/PostIt/ViewLocator.cs index 97538abbb..f866e1bac 100644 --- a/src/PostIt/PostIt/ViewLocator.cs +++ b/src/PostIt/PostIt/ViewLocator.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.DependencyInjection; using PostIt.ViewModels; using PostIt.ViewModels.Commands; using PostIt.Views; -using PostIt.Views.Blogs; using PostIt.Views.Commands; namespace PostIt; @@ -39,7 +38,7 @@ public class ViewLocator : IDataTemplate var services = app!.ServiceProvider!; return data switch { - BlogsViewModel => services.GetRequiredService(), + MainViewModel => services.GetRequiredService(), Settings => services.GetRequiredService(), HomePageViewModel => services.GetRequiredService(), ActivitiesPageViewModel => services.GetRequiredService(), diff --git a/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs index aab923d80..bccf97167 100644 --- a/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs @@ -43,7 +43,7 @@ public partial class BillingQueriesPageViewModel : ViewModelBase, IActionStatusV ? $"Demandes en cours ({Form.Title})" : $"Commandes {Form.Title}"; public string ContextLabel => $"{Performer.UserName} · {Activity.Name}"; - public bool CanOpenDetails => !IsReadOnly; + public bool CanOpenDetails => true; public override bool CanNavigateNext { @@ -75,7 +75,7 @@ public partial class BillingQueriesPageViewModel : ViewModelBase, IActionStatusV public Task InitializeAsync() => RefreshAsync(); - private bool CanOpenSelectedQuery() => CanOpenDetails && SelectedQuery is not null; + private bool CanOpenSelectedQuery() => SelectedQuery is not null; [RelayCommand] public async Task RefreshAsync() diff --git a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs b/src/PostIt/PostIt/ViewModels/Layout/MainViewModel.cs similarity index 83% rename from src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs rename to src/PostIt/PostIt/ViewModels/Layout/MainViewModel.cs index 8ac05c85e..d2667c012 100644 --- a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Layout/MainViewModel.cs @@ -1,6 +1,5 @@ using System; using System.Collections.ObjectModel; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Avalonia; @@ -9,12 +8,11 @@ using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.DependencyInjection; using Yavsc.Blogspot; using Yavsc.Api.Client; -using Yavsc.Abstract.Files; using PostIt.Helpers; namespace PostIt.ViewModels; -public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel +public partial class MainViewModel : ViewModelBase, IActionStatusViewModel { /// Window/tab title. Cosmetic — bound by /// MainPage.axaml if at all. Not the post title. @@ -68,9 +66,6 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel [ObservableProperty] public partial ObservableCollection FilteredPosts { get; set; } - [ObservableProperty] - public partial ObservableCollection DraftAttachments { get; set; } - [ObservableProperty] public partial BlogPostDto? SelectedPost { get; set; } @@ -118,8 +113,6 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel await ExecuteAsync(async () => { - var attachments = DraftAttachments.ToArray(); - // Build a fresh BlogPostDto from the editor buffer on // every Save — we no longer mutate SelectedPost in // place. The previous behaviour copied the buffer @@ -140,28 +133,11 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel DateModified = DateTime.UtcNow, IsPublished = DraftIsPublished }; - var created = await BlogClient!.CreatePostAsync(draft, attachments); + var created = await BlogClient!.CreatePostAsync(draft); if (created is not null) { SelectedPost = created; - - if (TryAppendAttachmentLinks(created, attachments)) - { - var linkUpdate = new BlogPostDto - { - Id = created.Id, - AuthorId = created.AuthorId, - Photo = created.Photo, - Title = DraftTitle, - Article = DraftArticle ?? string.Empty, - DateCreated = created.DateCreated, - DateModified = DateTime.UtcNow, - }; - await BlogClient.UpdatePostAsync(created.Id, linkUpdate); - } - this.SetInfoStatus($"Billet {created.Id} créé."); - DraftAttachments.Clear(); } } else @@ -176,26 +152,8 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel DateCreated = SelectedPost.DateCreated, DateModified = DateTime.UtcNow, }; - - await BlogClient!.UpdatePostAsync(SelectedPost.Id, update, attachments); - - if (TryAppendAttachmentLinks(SelectedPost, attachments)) - { - var linkUpdate = new BlogPostDto - { - Id = SelectedPost.Id, - AuthorId = SelectedPost.AuthorId, - Photo = SelectedPost.Photo, - Title = DraftTitle, - Article = DraftArticle ?? string.Empty, - DateCreated = SelectedPost.DateCreated, - DateModified = DateTime.UtcNow, - }; - await BlogClient.UpdatePostAsync(SelectedPost.Id, linkUpdate); - } - + await BlogClient!.UpdatePostAsync(SelectedPost.Id, update); this.SetInfoStatus($"Billet {SelectedPost.Id} enregistré."); - DraftAttachments.Clear(); } await RefreshPostsAsync(); @@ -378,7 +336,7 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel } - public BlogsViewModel() + public MainViewModel() { SettingsModel = new Settings(); Init(SettingsModel); @@ -389,7 +347,6 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel { Posts = new ObservableCollection(); FilteredPosts = new ObservableCollection(); - DraftAttachments = new ObservableCollection(); SelectedPost = null; IsBusy = false; this.SetInfoStatus("Prêt."); @@ -439,7 +396,7 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel /// . Production code uses the /// (Settings, BlogApiClient) overload below. /// - public BlogsViewModel(BlogApiClient blogClient, Settings? settings = null, IServiceProvider? services = null) + public MainViewModel(BlogApiClient blogClient, Settings? settings = null, IServiceProvider? services = null) { SettingsModel = new Settings(); BlogClient = blogClient ?? throw new ArgumentNullException(nameof(blogClient)); ; @@ -470,7 +427,6 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel // Mirror publication state too. Defaults to false on // null selection so a fresh draft starts unpublished. DraftIsPublished = value?.IsPublished ?? false; - DraftAttachments.Clear(); UpdateCommandStates(); } @@ -552,55 +508,4 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel IsLoaded = true; } } - - private bool TryAppendAttachmentLinks(BlogPostDto post, IReadOnlyCollection attachments) - { - if (attachments.Count == 0) - return false; - - var ownerSegment = post.Author?.UserName; - if (string.IsNullOrWhiteSpace(ownerSegment)) - ownerSegment = post.AuthorId; - - if (string.IsNullOrWhiteSpace(ownerSegment)) - return false; - - var article = DraftArticle ?? string.Empty; - var links = new List(); - - foreach (var attachment in attachments) - { - var relativePath = $"{EscapePathSegment(ownerSegment)}/blogs/{post.Id}/{EscapePathSegment(attachment.FileName)}"; - var fileUrl = ResolveUserFileUrl(relativePath); - var markdownLine = $"- [{attachment.FileName}]({fileUrl})"; - - if (!article.Contains(markdownLine, StringComparison.Ordinal)) - links.Add(markdownLine); - } - - if (links.Count == 0) - return false; - - var prefix = article.Length == 0 - ? "" - : (article.EndsWith("\n", StringComparison.Ordinal) ? "\n" : "\n\n"); - - DraftArticle = article + prefix + string.Join("\n", links); - return true; - } - - private string ResolveUserFileUrl(string relativePath) - { - var authority = Settings?.Authentication?.Authority; - if (!string.IsNullOrWhiteSpace(authority) - && Uri.TryCreate(authority, UriKind.Absolute, out var baseUri)) - { - return FileServerUrlHelpers.GetUserFilesUri(baseUri, relativePath).ToString(); - } - - return $"{Yavsc.Constants.UserFilesPath}/{relativePath}"; - } - - private static string EscapePathSegment(string segment) - => Uri.EscapeDataString(segment); } diff --git a/src/PostIt/PostIt/Views/Blogs/MainPage.axaml b/src/PostIt/PostIt/Views/Blogs/MainPage.axaml index 2ded71b94..871a2f541 100644 --- a/src/PostIt/PostIt/Views/Blogs/MainPage.axaml +++ b/src/PostIt/PostIt/Views/Blogs/MainPage.axaml @@ -5,15 +5,15 @@ xmlns:vm="using:PostIt.ViewModels" xmlns:postitControls="using:PostIt.Controls" xmlns:models="using:Yavsc.Blogspot" - xmlns:views="using:PostIt.Views.Blogs" + xmlns:views="using:PostIt.Views" xmlns:AvaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit" mc:Ignorable="d" - x:Class="PostIt.Views.Blogs.BlogsPage" - x:DataType="vm:BlogsViewModel" + x:Class="PostIt.Views.MainPage" + x:DataType="vm:MainViewModel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - +