Created
December 6, 2023 07:39
-
-
Save biohazard999/03ba58bbc580c0c416adc9d13dd1bc81 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@page "/" | |
@inherits FluxorComponent | |
@inject IState<MediatorState<TodoViewModel>> State | |
@inject IUnitOfWorkFactory UnitOfWorkFactory | |
<VDispatchOnce Dispatch="new FetchRequest(State.Value.Data)" /> | |
<VStackLayout Orientation="VOrientation.Vertical" | |
Gap="3" | |
Style="margin: 3rem auto; max-width: 30rem;"> | |
<VH4 TextAlign="VTextAlign.Center">Todo</VH4> | |
<VStackLayout Orientation="VOrientation.Vertical" Gap="2"> | |
<VEditor Model="@State.Value.Data" | |
For="m => m.NewTodo" /> | |
<VIf Value="@State.Value.HasErrors"> | |
<VDiv Style="color: orangered"> | |
@State.Value.Exception!.Message | |
</VDiv> | |
</VIf> | |
<VStackLayout Orientation="VOrientation.Horizontal" | |
Gap="2" | |
Justify="VJustify.End"> | |
<VButton Color="VColor.Primary" | |
StartIcon="MdiIcons.Refresh" | |
Dispatcher="() => new FetchRequest(State.Value.Data)" | |
Tooltip="Add a new Todo">Refresh</VButton> | |
<VButton Color="VColor.Success" | |
StartIcon="MdiIcons.Plus" | |
Dispatcher="() => new AddRequest(State.Value.Data.NewTodo)">Add</VButton> | |
</VStackLayout> | |
</VStackLayout> | |
<VIf Value="@(State.Value.Data.Items.Length > 0)"> | |
<VTable> | |
<VTBody> | |
<VForEach Items="@State.Value.Data.Items" Context="todo"> | |
<VTR Styles="@((s) => s.AddStyle("text-decoration", "line-through", todo.Done))"> | |
<VTD>@todo.Description</VTD> | |
<VTD> | |
<VStackLayout Justify="VJustify.End"> | |
<VEditor Model="todo" | |
For="m => m.Done" | |
Tooltip="Toggle Done" | |
DispatcherValueChanged="(_) => new ToggleDoneRequest(todo.Id)" /> | |
</VStackLayout> | |
</VTD> | |
<VTD> | |
<VStackLayout Justify="VJustify.End"> | |
<VIconButton Color="VColor.Error" | |
Icon="MdiIcons.Delete" | |
Text="Delete" | |
Tooltip="Delete Todo" | |
Dispatcher="() => new DeleteRequest(todo.Id)" /> | |
</VStackLayout> | |
</VTD> | |
</VTR> | |
</VForEach> | |
</VTBody> | |
</VTable> | |
</VIf> | |
</VStackLayout> | |
@code { | |
public sealed record TodoData(int Id, string Description, bool Done); | |
public sealed record TodoViewModel | |
{ | |
[VLabel("New Todo")] | |
public string NewTodo { get; set; } = string.Empty; | |
public TodoData[] Items { get; init; } = Array.Empty<TodoData>(); | |
} | |
public sealed record FetchRequest(TodoViewModel? ViewModel = null) : Request<TodoViewModel> | |
{ | |
public sealed record Handler(IUnitOfWorkFactory UnitOfWorkFactory) : RequestHandler<FetchRequest, TodoViewModel> | |
{ | |
public override async ValueTask<Response<TodoViewModel>> Handle(FetchRequest request, CancellationToken cancellationToken) | |
{ | |
using var uow = await UnitOfWorkFactory.CreateUnitOfWork<DbTodo>(); | |
var todos = await uow.Query<DbTodo>() | |
.OrderBy(x => x.Oid) | |
.Select(x => new TodoData(x.Oid, x.Task ?? "", x.Done)) | |
.ToArrayAsync(cancellationToken); | |
return (request.ViewModel ?? new TodoViewModel()) with | |
{ | |
Items = todos | |
}; | |
} | |
} | |
} | |
public sealed record AddRequest(string Description) : Request<TodoViewModel> | |
{ | |
public sealed record Handler(IUnitOfWorkFactory UnitOfWorkFactory) : RequestHandler<AddRequest, TodoViewModel> | |
{ | |
public override async ValueTask<Response<TodoViewModel>> Handle(AddRequest request, CancellationToken cancellationToken) | |
{ | |
if (string.IsNullOrEmpty(request.Description)) | |
{ | |
return new Response<TodoViewModel>.Error(new Exception("Description is required")); | |
} | |
using var uow = await UnitOfWorkFactory.CreateUnitOfWork<DbTodo>(); | |
var todo = new DbTodo(uow) | |
{ | |
Task = request.Description, | |
}; | |
await uow.CommitChangesAsync(cancellationToken); | |
var fetchHandler = new FetchRequest.Handler(UnitOfWorkFactory); | |
return await fetchHandler.Handle(new(), cancellationToken); | |
} | |
} | |
} | |
public sealed record ToggleDoneRequest(int TodoId) : Request<TodoViewModel> | |
{ | |
public sealed record Handler(IUnitOfWorkFactory UnitOfWorkFactory) : RequestHandler<ToggleDoneRequest, TodoViewModel> | |
{ | |
public override async ValueTask<Response<TodoViewModel>> Handle(ToggleDoneRequest request, CancellationToken cancellationToken) | |
{ | |
using var uow = await UnitOfWorkFactory.CreateUnitOfWork<DbTodo>(); | |
var todo = await uow.GetObjectByKeyAsync<DbTodo>(request.TodoId, cancellationToken); | |
if (todo is null) | |
{ | |
return new Response<TodoViewModel>.Error(new Exception("Todo not found")); | |
} | |
todo.Done = !todo.Done; | |
await uow.SaveAsync(todo, cancellationToken); | |
await uow.CommitChangesAsync(cancellationToken); | |
var fetchHandler = new FetchRequest.Handler(UnitOfWorkFactory); | |
return await fetchHandler.Handle(new(), cancellationToken); | |
} | |
} | |
} | |
public sealed record DeleteRequest(int TodoId) : Request<TodoViewModel> | |
{ | |
public sealed record Handler(IUnitOfWorkFactory UnitOfWorkFactory) : RequestHandler<DeleteRequest, TodoViewModel> | |
{ | |
public override async ValueTask<Response<TodoViewModel>> Handle(DeleteRequest request, CancellationToken cancellationToken) | |
{ | |
using var uow = await UnitOfWorkFactory.CreateUnitOfWork<DbTodo>(); | |
var todo = await uow.GetObjectByKeyAsync<DbTodo>(request.TodoId, cancellationToken); | |
if (todo is null) | |
{ | |
return new Response<TodoViewModel>.Error(new Exception("Task not found")); | |
} | |
await uow.DeleteAsync(todo, cancellationToken); | |
await uow.CommitChangesAsync(cancellationToken); | |
var fetchHandler = new FetchRequest.Handler(UnitOfWorkFactory); | |
return await fetchHandler.Handle(new(), cancellationToken); | |
} | |
} | |
} | |
[Persistent("Todo")] | |
public sealed class DbTodo : XPObject | |
{ | |
public DbTodo(Session session) : base(session) { } | |
private string? task; | |
[Size(SizeAttribute.Unlimited)] | |
public string? Task { get => task; set => SetPropertyValue(nameof(Task), ref task, value); } | |
private bool done; | |
public bool Done { get => done; set => SetPropertyValue(nameof(Done), ref done, value); } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment