Skip to content

Commit

Permalink
Added feature: Merging props
Browse files Browse the repository at this point in the history
  • Loading branch information
mergehez committed Oct 10, 2024
1 parent 597f4cf commit 2f606cc
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 35 deletions.
16 changes: 14 additions & 2 deletions InertiaNetCore/Extensions/InertiaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,26 @@ namespace InertiaNetCore.Extensions;

internal static class InertiaExtensions
{
internal static List<string> GetPartialData(this ActionContext context)
private static List<string> GetInertiaHeaderData(this ActionContext context, string header)
{
return context.HttpContext.Request.Headers.TryGetValue("X-Inertia-Partial-Data", out var data)
return context.HttpContext.Request.Headers.TryGetValue(header, out var data)
? data.FirstOrDefault()?.Split(",")
.Where(s => !string.IsNullOrEmpty(s))
.ToList() ?? []
: [];
}
internal static List<string> GetPartialData(this ActionContext context)
{
return context.GetInertiaHeaderData("X-Inertia-Partial-Data");
}
internal static List<string> GetInertiaExcepts(this ActionContext context)
{
return context.GetInertiaHeaderData("X-Inertia-Partial-Except");
}
internal static List<string> GetInertiaResetData(this ActionContext context)
{
return context.GetInertiaHeaderData("X-Inertia-Reset");
}

internal static bool IsInertiaPartialComponent(this ActionContext context, string component)
{
Expand Down
3 changes: 3 additions & 0 deletions InertiaNetCore/Inertia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ public static class Inertia

public static AlwaysProp<T> Always<T>(Func<T?> callback) => _factory.Always(callback);
public static AlwaysProp<T> Always<T>(Func<Task<T?>> callback) => _factory.Always(callback);

public static MergeProp<T> Merge<T>(Func<T?> callback) => _factory.Merge(callback);
public static MergeProp<T> Merge<T>(Func<Task<T?>> callback) => _factory.Merge(callback);
}
1 change: 1 addition & 0 deletions InertiaNetCore/Models/InertiaPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public readonly record struct InertiaPage
{
public required InertiaProps Props { get; init; }
public required Dictionary<string, List<string>> DeferredProps { get; init; }
public required List<string> MergeProps { get; init; }
public required string Component { get; init; }
public required string? Version { get; init; }
public required string Url { get; init; }
Expand Down
12 changes: 6 additions & 6 deletions InertiaNetCore/Models/InertiaProps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ namespace InertiaNetCore.Models;

public class InertiaProps : Dictionary<string, object?>
{
internal async Task<InertiaProps> ToProcessedProps(List<string>? partials)
internal async Task<InertiaProps> ToProcessedProps(bool isPartial, List<string> partials, List<string> excepts)
{
var props = new InertiaProps();

if(partials is not null && partials.Count == 0)
partials = null;

foreach (var (key, value) in this)
{
if(partials is null && value is IIgnoreFirstProp)
if(isPartial && excepts.Contains(key, StringComparer.InvariantCultureIgnoreCase))
continue;

if(!isPartial && value is IIgnoreFirstProp)
continue;

if(partials is not null && value is not IAlwaysProp && !partials.Contains(key, StringComparer.InvariantCultureIgnoreCase))
if(isPartial && value is not IAlwaysProp && !partials.Contains(key, StringComparer.InvariantCultureIgnoreCase))
continue;

props.Add(key, value switch
Expand Down
65 changes: 44 additions & 21 deletions InertiaNetCore/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class Response(string component, InertiaProps props, string? version, Ine
: IActionResult
{
private IDictionary<string, object>? _viewData;

public async Task ExecuteResultAsync(ActionContext context)
{
var page = new InertiaPage
Expand All @@ -23,8 +23,9 @@ public async Task ExecuteResultAsync(ActionContext context)
Url = context.HttpContext.RequestedUri(),
Props = await GetFinalProps(context),
DeferredProps = GetDeferredProps(context),
MergeProps = GetMergeProps(context)
};

if (!context.HttpContext.IsInertiaRequest())
{
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), context.ModelState)
Expand All @@ -45,33 +46,36 @@ public async Task ExecuteResultAsync(ActionContext context)
context.HttpContext.Response.Headers.Append("X-Inertia", "true");
context.HttpContext.Response.Headers.Append("Vary", "Accept");
context.HttpContext.Response.StatusCode = 200;
var jsonResult = new JsonResult(page, jsonSerializerOptions);

var jsonResult = new JsonResult(page, options.JsonSerializerOptions);
await jsonResult.ExecuteResultAsync(context);
}
}

private async Task<InertiaProps> GetFinalProps(ActionContext context)
{
var partials = context.IsInertiaPartialComponent(component) ? context.GetPartialData() : null;
var isPartial = context.IsInertiaPartialComponent(component);
var partials = isPartial ? context.GetPartialData() : [];
var excepts = isPartial ? context.GetInertiaExcepts() : [];

var shared = context.HttpContext.Features.Get<InertiaSharedProps>();
var flash = context.HttpContext.Features.Get<InertiaFlashMessages>()
var flash = context.HttpContext.Features.Get<InertiaFlashMessages>()
?? InertiaFlashMessages.FromSession(context.HttpContext);
var errors = GetErrors(context);
var finalProps = await props.ToProcessedProps(partials);

var finalProps = await props.ToProcessedProps(isPartial, partials, excepts);

finalProps = finalProps
.Merge(shared?.GetData())
.AddTimeStamp()
.AddFlash(flash.GetData())
.AddErrors(errors);

flash.Clear(false);

return finalProps;
}

private Dictionary<string, List<string>> GetDeferredProps(ActionContext context)
{
if (context.IsInertiaPartialComponent(component))
Expand All @@ -95,31 +99,50 @@ private Dictionary<string, List<string>> GetDeferredProps(ActionContext context)
g => g.Select(x => x.Key).ToList()
);
}


private List<string> GetMergeProps(ActionContext context)
{
var resetData = context.GetInertiaResetData();

var tmp = new Dictionary<string, string>();

foreach (var (key, value) in props)
{
if (value is IMergeableProp { Merge: true } && !resetData.Contains(key))
tmp[key] = key;
}

// apply json serialization options to dictionary keys before grouping them
var jsonOptions = options.JsonSerializerOptions as JsonSerializerOptions;
tmp = JsonSerializer.Deserialize<Dictionary<string, string>>(JsonSerializer.Serialize(tmp, jsonOptions), jsonOptions);

return tmp!.Select(prop => prop.Key).ToList();
}

private static Dictionary<string, string> GetErrors(ActionContext context)
{
var sessionErrors = context.HttpContext.Session.GetString("errors");
if (sessionErrors is not null)
{
var errors = JsonSerializer.Deserialize<Dictionary<string, string>>(sessionErrors);
context.HttpContext.Session.Remove("errors");
if(errors is not null)

if (errors is not null)
return errors;
}
if (context.ModelState.IsValid)

if (context.ModelState.IsValid)
return [];

return context.ModelState.ToDictionary(
kv => kv.Key,
kv => kv.Value?.Errors.FirstOrDefault()?.ErrorMessage ?? ""
);
);
}

public Response WithViewData(IDictionary<string, object> viewData)
{
_viewData = viewData;
return this;
}
}
}
2 changes: 2 additions & 0 deletions InertiaNetCore/ResponseFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,6 @@ public void Flash(string key, string? value)
public DeferredProp<T> Defer<T>(Func<Task<T?>> callback, string? group) => new(callback, group);
public AlwaysProp<T> Always<T>(Func<T?> callback) => new(callback);
public AlwaysProp<T> Always<T>(Func<Task<T?>> callback) => new(callback);
public MergeProp<T> Merge<T>(Func<T?> callback) => new(callback);
public MergeProp<T> Merge<T>(Func<Task<T?>> callback) => new(callback);
}
1 change: 1 addition & 0 deletions InertiaNetCore/Utils/DeferProp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace InertiaNetCore.Utils;
/// </summary>
public class DeferredProp<T> : InvokableProp<T>, IDeferredProp
{
public bool Merge { get; set; }
public string? Group { get; }

public DeferredProp(Func<T?> callback, string? group) : base(callback)
Expand Down
11 changes: 7 additions & 4 deletions InertiaNetCore/Utils/Interfaces.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ internal interface IAlwaysProp;

internal interface IIgnoreFirstProp;

internal interface ILazyProp : IIgnoreFirstProp;

internal interface IDeferredProp : IIgnoreFirstProp
internal interface IDeferredProp : IIgnoreFirstProp, IMergeableProp
{
string? Group { get; }
}
}

public interface IMergeableProp
{
bool Merge { get; set; }
}
2 changes: 1 addition & 1 deletion InertiaNetCore/Utils/LazyProp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace InertiaNetCore.Utils;
/// OPTIONALLY included on partial reloads (you should call <c>router.reload({ only: ["propName"] })</c>) <br/>
/// ONLY evaluated when needed
/// </summary>
public class LazyProp<T> : InvokableProp<T>, ILazyProp
public class LazyProp<T> : InvokableProp<T>, IIgnoreFirstProp
{
public LazyProp(Func<T?> callback) : base(callback)
{
Expand Down
19 changes: 19 additions & 0 deletions InertiaNetCore/Utils/MergeProp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace InertiaNetCore.Utils;

/// <summary>
/// By default, Inertia overwrites props with the same name when reloading a page.
/// However, there are instances, such as pagination or infinite scrolling, where that is not the desired behavior.
/// In these cases, you can merge props instead of overwriting them.
/// </summary>
public class MergeProp<T> : InvokableProp<T>, IMergeableProp
{
public bool Merge { get; set; } = true;

public MergeProp(Func<T?> callback) : base(callback)
{
}

public MergeProp(Func<Task<T?>> callbackAsync) : base(callbackAsync)
{
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,5 +334,5 @@ export default defineConfig({
## Work in progress

- [x] Deferred props
- [ ] Merging props
- [x] Merging props
- [ ] History encryption

0 comments on commit 2f606cc

Please sign in to comment.