Skip to content

Commit

Permalink
Merge pull request #260 from MartinZikmund/feature/blog-viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinZikmund authored Apr 14, 2024
2 parents 37d30c8 + a9b16aa commit 0ed5630
Show file tree
Hide file tree
Showing 17 changed files with 167 additions and 62 deletions.
5 changes: 3 additions & 2 deletions src/MZikmund.slnLaunch
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
"Projects": [
{
"Name": "web\\MZikmund.Web\\MZikmund.Web.csproj",
"Action": "Start"
"Action": "Start",
"DebugTarget": "https"
},
{
"Name": "app\\MZikmund.Windows\\MZikmund.Windows.csproj",
"Action": "Start",
"DebugTarget": ""
"DebugTarget": "Packaged"
}
]
}
Expand Down
2 changes: 2 additions & 0 deletions src/app/MZikmund/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ private static void ConfigureServices(IServiceCollection services)
services.AddScoped<AddOrUpdateTagDialogViewModel>();
services.AddScoped<PostsManagerViewModel>();
services.AddScoped<PostEditorViewModel>();
services.AddScoped<PostViewModel>();
services.AddScoped<WindowShellViewModel>();

services.AddSingleton<IThemeManager, ThemeManager>();
services.AddSingleton<IAppPreferences, AppPreferences>();
Expand Down
4 changes: 2 additions & 2 deletions src/app/MZikmund/Assets/PostPreviewTemplate.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="light">
<html lang="en" data-bs-theme="{ACTUALTHEME}">
<head>

<meta charset="utf-8" />
Expand All @@ -16,7 +16,7 @@
<article class="blog-post">

<div class="post-content">
{0}
{POSTCONTENT}
</div>
</article>
</main>
Expand Down
15 changes: 8 additions & 7 deletions src/app/MZikmund/Services/Loading/LoadingIndicator.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
using MZikmund.Services.Navigation;
using MZikmund.ViewModels;

namespace MZikmund.Services.Loading;

public class LoadingIndicator : ILoadingIndicator
{
private readonly IWindowShellProvider _windowShellProvider;
private readonly WindowShellViewModel _windowShellViewModel;

public LoadingIndicator(IWindowShellProvider windowShellProvider)
public LoadingIndicator(WindowShellViewModel windowShellProvider)
{
_windowShellProvider = windowShellProvider;
_windowShellViewModel = windowShellProvider ?? throw new ArgumentNullException(nameof(windowShellProvider));
}

public IDisposable BeginLoading() => _windowShellProvider.ViewModel.BeginLoading();
public IDisposable BeginLoading() => _windowShellViewModel.BeginLoading();

public bool IsLoading => _windowShellProvider.ViewModel.IsLoading;
public bool IsLoading => _windowShellViewModel.IsLoading;

public string StatusMessage
{
get => _windowShellProvider.ViewModel.LoadingStatusMessage;
set => _windowShellProvider.ViewModel.LoadingStatusMessage = value;
get => _windowShellViewModel.LoadingStatusMessage;
set => _windowShellViewModel.LoadingStatusMessage = value;
}
}
2 changes: 2 additions & 0 deletions src/app/MZikmund/Services/Navigation/INavigationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ namespace MZikmund.Services.Navigation;

public interface INavigationService
{
void ClearBackStack();

void Navigate<TViewModel>();

void Navigate<TViewModel>(object parameter);
Expand Down
5 changes: 4 additions & 1 deletion src/app/MZikmund/Services/Navigation/IWindowShellProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using MZikmund.ViewModels;
using Microsoft.UI.Dispatching;
using MZikmund.ViewModels;

namespace MZikmund.Services.Navigation;

Expand All @@ -11,4 +12,6 @@ public interface IWindowShellProvider
XamlRoot XamlRoot { get; }

IServiceProvider ServiceProvider { get; }

DispatcherQueue DispatcherQueue { get; }
}
1 change: 1 addition & 0 deletions src/app/MZikmund/Services/Navigation/NavigationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ public void Initialize() =>
SystemNavigationManager.GetForCurrentView().BackRequested += NavigationManagerBackRequested;

private void NavigationManagerBackRequested(object? sender, BackRequestedEventArgs? e) => GoBack();
public void ClearBackStack() => Frame.BackStack.Clear();
}
15 changes: 14 additions & 1 deletion src/app/MZikmund/Services/Navigation/WindowShellProvider.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@

using System.Diagnostics.CodeAnalysis;
using Microsoft.UI.Dispatching;
using MZikmund.ViewModels;

namespace MZikmund.Services.Navigation;

internal sealed class WindowShellProvider : IWindowShellProvider
{
private WindowShell? _shell;
private DispatcherQueue? _dispatcherQueue;

public WindowShellProvider()
{
Expand All @@ -20,6 +22,7 @@ public void SetShell(WindowShell shell)
}

_shell = shell;
_dispatcherQueue = shell.DispatcherQueue;
}

public WindowShellViewModel ViewModel
Expand Down Expand Up @@ -58,10 +61,20 @@ public IServiceProvider ServiceProvider
}
}

public DispatcherQueue DispatcherQueue
{
get
{
EnsureInitialized();
return _dispatcherQueue;
}
}

[MemberNotNull(nameof(_shell))]
[MemberNotNull(nameof(_dispatcherQueue))]
private void EnsureInitialized()
{
if (_shell is null)
if (_shell is null || _dispatcherQueue is null)
{
throw new InvalidOperationException("WindowShellProvider was not initialized.");
}
Expand Down
1 change: 1 addition & 0 deletions src/app/MZikmund/ViewModels/Admin/PostEditorViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ private async Task SaveAsync()
Post.Categories = Categories;
Post.IsPublished = true;
Post.PublishedDate = DateTimeOffset.UtcNow; // TODO: Don't always publish!
Post.Content = PostContent;

if (Post.Id == Guid.Empty)
{
Expand Down
23 changes: 20 additions & 3 deletions src/app/MZikmund/ViewModels/BlogViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,34 @@
using MZikmund.DataContracts.Blog;
using MZikmund.Services.Loading;
using MZikmund.Services.Localization;
using MZikmund.Services.Navigation;
using MZikmund.ViewModels.Items;
using MZikmund.Web.Core.Services;

namespace MZikmund.ViewModels;

public class BlogViewModel : PageViewModel
{
private readonly IMZikmundApi _api;
private readonly ILoadingIndicator _loadingIndicator;
private readonly IMarkdownConverter _markdownConverter;
private readonly INavigationService _navigationService;

public BlogViewModel(IMZikmundApi api, ILoadingIndicator loadingIndicator)
public BlogViewModel(
IMZikmundApi api,
ILoadingIndicator loadingIndicator,
IMarkdownConverter markdownConverter,
INavigationService navigationService)
{
_api = api ?? throw new ArgumentNullException(nameof(api));
_loadingIndicator = loadingIndicator;
_markdownConverter = markdownConverter;
_navigationService = navigationService;
}

public override string Title => Localizer.Instance.GetString("Blog");

public ObservableCollection<PostListItem> Posts { get; } = new ObservableCollection<PostListItem>();
public ObservableCollection<PostListItemViewModel> Posts { get; } = new();

public override async void ViewNavigatedTo(object? parameter)
{
Expand All @@ -29,7 +40,13 @@ public override async void ViewNavigatedTo(object? parameter)
var posts = await _api.GetPostsAsync();
foreach (var post in posts.Content!.Data)
{
Posts.Add(post);
Posts.Add(new(post, _markdownConverter));
}
}

public void ItemClicked(object sender, ItemClickEventArgs args)
{
var item = (PostListItemViewModel)args.ClickedItem;
_navigationService.Navigate<PostViewModel>(item.Item.Id);
}
}
19 changes: 19 additions & 0 deletions src/app/MZikmund/ViewModels/Items/PostListItemViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MZikmund.DataContracts.Blog;
using MZikmund.Web.Core.Services;

namespace MZikmund.ViewModels.Items;

public sealed class PostListItemViewModel : ObservableObject
{
private readonly IMarkdownConverter _markdownConverter;

public PostListItemViewModel(PostListItem item, IMarkdownConverter markdownConverter)
{
Item = item;
_markdownConverter = markdownConverter;
}

public PostListItem Item { get; }

public string AbstractPlain => _markdownConverter.ToPlainText(Item.Abstract);
}
22 changes: 20 additions & 2 deletions src/app/MZikmund/ViewModels/PostViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
namespace MZikmund.ViewModels;
using MZikmund.Api.Client;
using MZikmund.DataContracts.Blog;
using MZikmund.Web.Core.Services;

namespace MZikmund.ViewModels;

public class PostViewModel : PageViewModel
{
public PostViewModel()
private readonly IMZikmundApi _api;
private readonly IPostContentProcessor _postContentProcessor;

public PostViewModel(IMZikmundApi api, IPostContentProcessor postContentProcessor)
{
_api = api;
_postContentProcessor = postContentProcessor;
}

public override async void ViewNavigatedTo(object? parameter)
{
var postId = (Guid)parameter!;
var postResponse = await _api.GetPostAsync(postId);
Post = postResponse.Content!;
HtmlPreview = await _postContentProcessor.ProcessAsync(Post.Content);
}

public Post? Post { get; private set; }

public string HtmlPreview { get; private set; } = "";
}
20 changes: 10 additions & 10 deletions src/app/MZikmund/ViewModels/WindowShellViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
using Microsoft.UI.Dispatching;
using MZikmund.Services.Navigation;
using MZikmund.ViewModels;
using Uno.Disposables;

namespace MZikmund.ViewModels;

public class WindowShellViewModel : ViewModelBase
{
private readonly DispatcherQueue _dispatcher;
private readonly IWindowShellProvider _provider;
private readonly INavigationService _navigationService;
private RefCountDisposable? _refCountDisposable;

public WindowShellViewModel(DispatcherQueue dispatcher)
public WindowShellViewModel(IWindowShellProvider provider, INavigationService navigationService)
{
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
_navigationService = navigationService ?? throw new ArgumentNullException(nameof(navigationService));
}

public string Title { get; set; } = "Martin Zikmund";
Expand All @@ -26,19 +29,19 @@ public IDisposable BeginLoading()

IsLoading = true;
_refCountDisposable = new RefCountDisposable(Disposable.Create(
() => // TODO: Await TryEneque
() => // TODO: Await TryEnequeAsync
{
#if __WASM__
IsLoading = false;
return;
#else
if (_dispatcher.HasThreadAccess)
if (_provider.DispatcherQueue.HasThreadAccess)
{
IsLoading = false;
}
else
{
_dispatcher.TryEnqueue(DispatcherQueuePriority.Normal, () =>
_provider.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
if (_refCountDisposable == null || _refCountDisposable.IsDisposed)
{
Expand All @@ -55,8 +58,5 @@ public IDisposable BeginLoading()

public string LoadingStatusMessage { get; set; } = "";

public void BackRequested()
{
//NavigationService.GoBack();
}
public void BackRequested() => _navigationService.GoBack();
}
Loading

0 comments on commit 0ed5630

Please sign in to comment.