Skip to content

Instantly share code, notes, and snippets.

@biohazard999
Created December 6, 2023 07:39
Show Gist options
  • Save biohazard999/03ba58bbc580c0c416adc9d13dd1bc81 to your computer and use it in GitHub Desktop.
Save biohazard999/03ba58bbc580c0c416adc9d13dd1bc81 to your computer and use it in GitHub Desktop.
@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