Skip to content

Commit

Permalink
Small improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix-CodingClimber committed Mar 12, 2024
1 parent 01a8daa commit 0b83c2f
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/DotNetElements.Core/Core/Contracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ public interface IEditImage : IModel<Guid>

public string? GetBase64Preview() => Data is not null ? $"data:Image/*;base64,{Convert.ToBase64String(Data)}" : null;

public static abstract IEditImage CreateFromData(ImageData data, string AlternateText);
public static abstract object CreateFromData(ImageData data, string AlternateText);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
public class GuidNotEmptyAttribute : ValidationAttribute
{
public const string DefaultErrorMessage = "The {0} field must not be empty";
public GuidNotEmptyAttribute() : base(DefaultErrorMessage) { }

private readonly bool allowNull;

public GuidNotEmptyAttribute(bool allowNull = false) : base(DefaultErrorMessage)
{
this.allowNull = allowNull;
}

public override bool IsValid(object? value)
{
// NotEmpty doesn't necessarily mean required
if (value is null)
return true;
return allowNull;

return value switch
{
Expand Down
35 changes: 20 additions & 15 deletions src/DotNetElements.Core/Core/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,13 @@ public virtual async Task<CrudResult<TEntity>> CreateAsync(TEntity entity, Expre
return CrudResult.DuplicateEntry();
}

// Set audit properties if needed
if (entity is ICreationAuditedEntity<TKey> auditedEntity)
auditedEntity.SetCreationAudited(CurrentUserProvider.GetCurrentUserId(), TimeProvider.GetUtcNow());
SetCreationAudited(entity);

EntityEntry<TEntity> createdEntity = Entities.Attach(entity);

await DbContext.SaveChangesAsync();

await LoadRelatedEntities(createdEntity);
await LoadRelatedEntities(createdEntity.Entity);

return createdEntity.Entity;
}
Expand All @@ -62,9 +60,7 @@ public virtual async Task<CrudResult<TSelf>> CreateOrUpdateAsync<TSelf>(TKey id,
return CrudResult.DuplicateEntry();
}

// Set audit properties if needed
if (entity is ICreationAuditedEntity<TKey> auditedEntity)
auditedEntity.SetCreationAudited(CurrentUserProvider.GetCurrentUserId(), TimeProvider.GetUtcNow());
SetCreationAudited(entity);

EntityEntry<TSelf> createdEntity = DbContext.Set<TSelf>().Attach(entity);

Expand All @@ -85,8 +81,7 @@ public virtual async Task<CrudResult<TSelf>> CreateOrUpdateAsync<TSelf>(TKey id,
// Check if entity has changed and set audit properties if needed
if (DbContext.ChangeTracker.HasChanges())
{
if (existingEntity is IAuditedEntity<TKey> auditedEntity)
auditedEntity.SetModificationAudited(CurrentUserProvider.GetCurrentUserId(), TimeProvider.GetUtcNow());
SetModificationAudited(existingEntity);

UpdateEntityVersion(existingEntity, entity);

Expand Down Expand Up @@ -116,8 +111,7 @@ public virtual async Task<CrudResult<TEntity>> UpdateAsync<TFrom>(TKey id, TFrom
// Check if entity has changed and set audit properties if needed
if (DbContext.ChangeTracker.HasChanges())
{
if (existingEntity is IAuditedEntity<TKey> auditedEntity)
auditedEntity.SetModificationAudited(CurrentUserProvider.GetCurrentUserId(), TimeProvider.GetUtcNow());
SetModificationAudited(existingEntity);

UpdateEntityVersion(existingEntity, from);

Expand Down Expand Up @@ -182,10 +176,9 @@ public TRelatedEntity AttachById<TRelatedEntity, TRelatedEntityKey>(TRelatedEnti
return DbContext.Set<TRelatedEntity>().Attach(TRelatedEntity.CreateRefById(id)).Entity;
}

protected async Task<CrudResult> UpdateAsync(TEntity entity)
protected async Task<CrudResult> UpdateAndSaveAsync(TEntity entity)
{
if (entity is IAuditedEntity<TKey> auditedEntity)
auditedEntity.SetModificationAudited(CurrentUserProvider.GetCurrentUserId(), TimeProvider.GetUtcNow());
SetModificationAudited(entity);

UpdateEntityVersion(entity, entity);

Expand Down Expand Up @@ -219,6 +212,18 @@ protected void UpdateEntityVersion<TTargetEntity, TSourceEntity>(TTargetEntity e
}
}

protected void SetCreationAudited<TCreationAuditedEntity>(TCreationAuditedEntity entity)
{
if (entity is ICreationAuditedEntity<Guid> auditedEntity)
auditedEntity.SetCreationAudited(CurrentUserProvider.GetCurrentUserId(), TimeProvider.GetUtcNow());
}

protected void SetModificationAudited<TModificationAuditedEntity>(TModificationAuditedEntity entity)
{
if (entity is IAuditedEntity<TKey> auditedEntity)
auditedEntity.SetModificationAudited(CurrentUserProvider.GetCurrentUserId(), TimeProvider.GetUtcNow());
}

// Set queried entities version to the version of the updating entity to detect weather or not the data has changed
// between getting the data in the first place and updating it now
protected void SetOriginalVersionQueried<TTargetEntity>(TTargetEntity entityFromDb, Guid? originalVersion)
Expand Down Expand Up @@ -260,7 +265,7 @@ protected async Task<bool> SaveChangesWithVersionCheckAsync()
return true;
}

private Task LoadRelatedEntities(TEntity entity)
protected Task LoadRelatedEntities(TEntity entity)
{
return LoadRelatedEntities(DbContext.Entry(entity));
}
Expand Down
11 changes: 9 additions & 2 deletions src/DotNetElements.Web.Blazor/CrudEditDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,16 @@ public CrudEditDialog()

private async Task OnSubmit()
{
bool isValid = EditContext?.Validate() is true;
OnBeforeValidate();

bool isValid = EditContext?.Validate() is true;

if (!isValid)
return;

Result<TModel> result;
OnAfterValidate();

Result<TModel> result;

if (IsEditMode)
result = await HttpClient.PostAsJsonWithResultAsync<TEditModel, TModel>(ApiEndpoint, Model!);
Expand All @@ -79,4 +83,7 @@ private void OnCancel()
{
Dialog?.Cancel();
}

protected virtual void OnBeforeValidate() { }
protected virtual void OnAfterValidate() { }
}
6 changes: 3 additions & 3 deletions src/DotNetElements.Web.Blazor/CrudTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public static class CrudTable
{
public static readonly DialogOptions DefaultEditDialogOptions = new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true };
public static DialogOptions DefaultEditDialogOptions => new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, DisableBackdropClick = true };
}

// todo use CrudService
Expand All @@ -25,7 +25,7 @@ public override async Task OnCreateEntry()
{ x => x.ApiEndpoint, Options.BaseEndpointUri }
};

var dialog = await DialogService.ShowAsync<TEditDialog>("New entry", parameters);
var dialog = await DialogService.ShowAsync<TEditDialog>("New entry", parameters, Options.EditDialogOptions);
var result = await dialog.Result;

if (result.Canceled)
Expand All @@ -52,7 +52,7 @@ protected override async Task OnEditEntry(ModelWithDetails<TModel, TDetails> con
{
{ x => x.IsEditMode, true },
{ x => x.Model, editModel },
{ x => x.EditContext, new EditContext(context.Value) },
{ x => x.EditContext, new EditContext(editModel) },
{ x => x.ApiEndpoint, Options.BaseEndpointUri }
};

Expand Down
58 changes: 38 additions & 20 deletions src/DotNetElements.Web.Blazor/DneImageBrowser.razor
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,21 @@
<div style="@Style" class="@(Class + " image-browser-container")">
<MudText Style="grid-area: image-list-header">Uploaded Images</MudText>
<MudPaper Outlined="true" Elevation="0" style="overflow-y: auto; grid-area: image-list">
<MudList SelectedValueChanged="OnSelectedImageChanged" Clickable="true">
@foreach (TImage image in Images)
{
<MudListItem Value="image">
<MudPaper Class="d-flex align-center flex-column">
<MudIconButton OnClick="() => OnDeleteImage_Internal(image)" Icon="@Icons.Material.Rounded.Delete" Color="Color.Error" Size="Size.Small" Style="position: absolute; right: 20px; top: 16px;" />
<MudImage Src="@($"data/images/{image.StoredFileName}")" Height="ThumbnailSize" Width="ThumbnailSize" ObjectFit="ObjectFit.Contain" />
<MudText>@image.FileName</MudText>
</MudPaper>
</MudListItem>
}
</MudList>
@if (Images is not null)
{
<MudList SelectedValueChanged="OnSelectedImageChanged" Clickable="true">
@foreach (TImage image in Images)
{
<MudListItem Value="image">
<MudPaper Class="d-flex align-center flex-column">
<MudIconButton OnClick="() => OnDeleteImage_Internal(image)" Icon="@Icons.Material.Rounded.Delete" Color="Color.Error" Size="Size.Small" Style="position: absolute; right: 20px; top: 16px;" />
<MudImage Src="@($"data/images/{image.StoredFileName}")" Height="ThumbnailSize" Width="ThumbnailSize" ObjectFit="ObjectFit.Contain" />
<MudText>@image.FileName</MudText>
</MudPaper>
</MudListItem>
}
</MudList>
}
</MudPaper>
<MudText Style="grid-area: preview-header">Image Preview</MudText>
<MudPaper Outlined="true" Elevation="0" Class="pa-4 d-flex" Style="grid-area: preview; max-height: calc(100vh - 460px);">
Expand All @@ -57,10 +60,13 @@
<div class="d-flex flex-column gap-2">
<p>Image info</p>
<MudTextField Value="selectedImage.FileName" Variant="Variant.Outlined" Label="File Name" Margin="Margin.Dense" ReadOnly="true" />
<EditForm EditContext="@selectedImageEditContext">
<DneDataAnnotationsValidator />
<MudTextField @bind-Value="selectedImage.AlternateText" For="() => selectedImage.AlternateText" Variant="Variant.Outlined" Label="Alternate Text" Margin="Margin.Dense" ReadOnly="@(!isImageInfoEditModeActive)" Required="true" />
</EditForm>
@if (selectedImageEditContext is not null)
{
<EditForm EditContext="@selectedImageEditContext">
<DneDataAnnotationsValidator />
<MudTextField @bind-Value="selectedImage.AlternateText" For="() => selectedImage.AlternateText" Variant="Variant.Outlined" Label="Alternate Text" Margin="Margin.Dense" ReadOnly="@(!isImageInfoEditModeActive)" Required="true" />
</EditForm>
}
</div>
<div>
@if (isImageInfoEditModeActive)
Expand All @@ -79,7 +85,7 @@
@code
{
[Parameter, EditorRequired]
public List<TImage> Images { get; set; } = default!;
public List<TImage>? Images { get; set; }

[Parameter]
public int ThumbnailSize { get; set; } = 128;
Expand All @@ -93,23 +99,33 @@
[Parameter, EditorRequired]
public EventCallback<ImageAction<TEditImage, TImage>> OnImageSavedAsync { get; set; }

[Parameter]
public TImage? SelectedImage { get; set; }
[Parameter]
public EventCallback<TImage?> SelectedImageChanged { get; set; }

private TEditImage? selectedImage;
private EditContext? selectedImageEditContext;

private bool isImageInfoEditModeActive;
private bool isNewImage;

private void OnSelectedImageChanged(object selectedValue)
private async Task OnSelectedImageChanged(object selectedValue)
{
isNewImage = false;
isImageInfoEditModeActive = false;

selectedImage = TEditImage.MapFromModel((TImage)selectedValue);
selectedImageEditContext = new EditContext(selectedImage);

SelectedImage = (TImage)selectedValue;
await SelectedImageChanged.InvokeAsync(SelectedImage);
}

private async Task OnDeleteImage_Internal(TImage image)
{
ArgumentNullException.ThrowIfNull(Images);

if (!OnDeleteImageAsync.HasDelegate)
return;

Expand All @@ -121,7 +137,6 @@

Images.Remove(image);

// todo not working
if (selectedImage?.Id.Equals(image.Id) is true)
ResetSelectedImage();
}
Expand All @@ -133,14 +148,17 @@
ArgumentNullException.ThrowIfNull(selectedImage);

selectedImageEditContext = new EditContext(selectedImage);
isNewImage = true;
isImageInfoEditModeActive = true;
isNewImage = true;
}

private async Task OnImageSaved_Internal()
{
ArgumentNullException.ThrowIfNull(selectedImage);

if (Images is null)
Images = [];

if (selectedImageEditContext?.Validate() is not true || !OnImageSavedAsync.HasDelegate)
return;

Expand Down
2 changes: 1 addition & 1 deletion src/DotNetElements.Web.Blazor/DneImageUpload.razor
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
public EventCallback<ImageData> OnImageUploaded { get; set; }

[Parameter]
public string Label { get; set; }
public string? Label { get; set; }

[Parameter]
public bool ShowImagePreview { get; set; } = true;
Expand Down
21 changes: 21 additions & 0 deletions src/DotNetElements.Web.Blazor/DneInputField.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@using System.Linq.Expressions
@inherits MudField
@typeparam TValue

<div>
@{
base.BuildRenderTree(__builder);
}
<div class="mud-input-control-helper-container px-2 ">
<ValidationMessage class="mud-input-helper-text mud-input-error" For="@For" />
</div>
</div>

@code
{
//
// Summary:
// Specifies the field for which validation messages should be displayed.
[Parameter]
public Expression<Func<TValue>>? For { get; set; }
}

0 comments on commit 0b83c2f

Please sign in to comment.