feat(dashboard): add revision-safe interactive widget grid
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace JobTrackerApi.Models;
|
||||
|
||||
public sealed record DashboardWidget(string Id, bool Visible, string Size);
|
||||
public sealed record DashboardWidget(string Id, bool Visible, string Size,
|
||||
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] int? X = null,
|
||||
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] int? Y = null,
|
||||
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] int? W = null,
|
||||
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] int? H = null);
|
||||
public sealed record DashboardLayout(int Version, long Revision, IReadOnlyList<DashboardWidget> Widgets)
|
||||
{
|
||||
public const int MaxBytes = 16 * 1024;
|
||||
@@ -14,7 +19,7 @@ public sealed record DashboardLayout(int Version, long Revision, IReadOnlyList<D
|
||||
|
||||
public static DashboardLayout? Parse(JsonElement root, bool reading = false, long revision = 0)
|
||||
{
|
||||
if (root.ValueKind != JsonValueKind.Object || !root.TryGetProperty("version", out var version) || version.ValueKind != JsonValueKind.Number || !version.TryGetInt32(out var v) || v != 1
|
||||
if (root.ValueKind != JsonValueKind.Object || !root.TryGetProperty("version", out var version) || version.ValueKind != JsonValueKind.Number || !version.TryGetInt32(out var v) || v is not (1 or 2)
|
||||
|| !root.TryGetProperty("widgets", out var widgets) || widgets.ValueKind != JsonValueKind.Array || widgets.GetArrayLength() > 32) return null;
|
||||
if (!reading && (!root.TryGetProperty("revision", out var rev) || rev.ValueKind != JsonValueKind.Number || !rev.TryGetInt64(out revision) || revision < 0 || revision >= MaxRevision)) return null;
|
||||
var result = new List<DashboardWidget>();
|
||||
@@ -35,9 +40,35 @@ public sealed record DashboardLayout(int Version, long Revision, IReadOnlyList<D
|
||||
if (!reading) return null;
|
||||
size = id == "cards" ? "full" : "standard";
|
||||
}
|
||||
result.Add(new DashboardWidget(id, visible.GetBoolean(), size));
|
||||
if (v == 1) result.Add(new DashboardWidget(id, visible.GetBoolean(), size));
|
||||
else
|
||||
{
|
||||
static bool Integer(JsonElement item, string key, out int number)
|
||||
{
|
||||
number = 0;
|
||||
return item.TryGetProperty(key, out var field) && field.ValueKind == JsonValueKind.Number && field.TryGetInt32(out number);
|
||||
}
|
||||
if (!Integer(widget, "x", out var x) || !Integer(widget, "y", out var y) || !Integer(widget, "w", out var w) || !Integer(widget, "h", out var h)
|
||||
|| x is < 0 or > 12 || y is < 0 or > 256 || w is < 4 or > 12 || h is < 4 or > 24 || x + w > 12 || y + h > 256
|
||||
|| (id == "cards" && w != 12) || (!reading && size != (w == 12 ? "full" : "standard")))
|
||||
{
|
||||
if (reading) continue;
|
||||
return null;
|
||||
}
|
||||
result.Add(new DashboardWidget(id, visible.GetBoolean(), w == 12 ? "full" : "standard", x, y, w, h));
|
||||
}
|
||||
}
|
||||
return new DashboardLayout(1, revision, result);
|
||||
if (!reading && v == 2)
|
||||
{
|
||||
var visibleWidgets = result.Where(widget => widget.Visible).ToArray();
|
||||
for (var first = 0; first < visibleWidgets.Length; first++)
|
||||
for (var second = first + 1; second < visibleWidgets.Length; second++)
|
||||
{
|
||||
var a = visibleWidgets[first]; var b = visibleWidgets[second];
|
||||
if (a.X < b.X + b.W && a.X + a.W > b.X && a.Y < b.Y + b.H && a.Y + a.H > b.Y) return null;
|
||||
}
|
||||
}
|
||||
return new DashboardLayout(v, revision, result);
|
||||
}
|
||||
|
||||
public static DashboardLayout Read(string? json, long revision)
|
||||
@@ -49,7 +80,7 @@ public sealed record DashboardLayout(int Version, long Revision, IReadOnlyList<D
|
||||
using var document = JsonDocument.Parse(json);
|
||||
if (document.RootElement.ValueKind == JsonValueKind.Object
|
||||
&& document.RootElement.TryGetProperty("version", out var version) && version.ValueKind == JsonValueKind.Number
|
||||
&& version.TryGetInt32(out var futureVersion) && futureVersion > 1)
|
||||
&& version.TryGetInt32(out var futureVersion) && futureVersion > 2)
|
||||
return new(futureVersion, revision, []);
|
||||
return Parse(document.RootElement, reading: true, revision) ?? new(1, revision, []);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user