Skip to content

Commit

Permalink
Now with images.
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferStrube committed Oct 17, 2024
1 parent e35d225 commit 1230ec1
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="KristofferStrube.ActivityStreams" Version="0.2.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0" PrivateAssets="all" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
@page "/FollowingGraph/"
@implements IDisposable
@inject HttpClient httpClient
@using KristofferStrube.ActivityStreams
@using KristofferStrube.Blazor.GraphEditor

<PageTitle>Blazor.GraphEditor - Following Graph</PageTitle>

<h1>Following Graph</h1>

<p>The following shows a following graph.</p>
<p>The following shows a following graph for @@kristofferstrube@@hachyderm.io</p>

@if (selectedUser is null)
{
<p>Select a node to show what user that is.</p>
}
else
{
<p>Selected user: <b>@selectedUser.id</b></p>
<p>Selected user: <b>@selectedUser.Id</b></p>
}

<div style="height:80vh;">
<GraphEditor @ref=GraphEditor
TNode="User"
TEdge="Follow"
NodeIdMapper="n => n.id"
NodeRadiusMapper="n => n.size"
NodeColorMapper="n => n.color"
NodeIdMapper="n => n.Id"
NodeRadiusMapper="n => n.Size"
NodeColorMapper="n => n.Color"
NodeImageMapper="n => n.Image"
NodeRepulsionMapper="_ => 1200"
EdgeSpringConstantMapper="_ => 0.8"
EdgeFromMapper="e => e.from"
EdgeToMapper="e => e.to"
EdgeWidthMapper="_ => 5"
Expand Down Expand Up @@ -62,6 +66,18 @@ else

List<User> users = [primaryUser, .. following];

Task.WhenAll(users.Select(async user =>

Check warning on line 69 in samples/KristofferStrube.Blazor.GraphEditor.WasmExample/Pages/FollowingGraph.razor

View workflow job for this annotation

GitHub Actions / build

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
{
var response = await httpClient.GetAsync($"https://kristoffer-strube.dk/API/mastodon/Profile/{user.Id}");

if (!response.IsSuccessStatusCode)
return;

Person? person = await response.Content.ReadFromJsonAsync<Person>();

user.Image = person?.Icon?.FirstOrDefault() is Image { } image ? image.Url.FirstOrDefault()?.Href?.ToString() : null;

Check warning on line 78 in samples/KristofferStrube.Blazor.GraphEditor.WasmExample/Pages/FollowingGraph.razor

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'source' in 'ILink? Enumerable.FirstOrDefault<ILink>(IEnumerable<ILink> source)'.
}));

List<Follow> edges = following.Select(f => new Follow(primaryUser, f)).ToList();

await GraphEditor.LoadGraph(users, edges);
Expand All @@ -81,7 +97,20 @@ else
}
}

public record User(string id, string color, float size = 30);
public class User(string id, string color, float size = 30) : IEquatable<User>
{
public string Id = id;
public string Color = color;
public float Size = size;

public string? Image { get; set; }

public override bool Equals(object? obj) => obj is User user && Equals(user);

public bool Equals(User? other) => other?.Id == Id;

public override int GetHashCode() => Id.GetHashCode();
}
public record Follow(User from, User to);

public void Dispose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="FollowingGraph">
<span class="oi oi-flash" aria-hidden="true"></span> Following Graph
<svg class="bi" viewBox="0 0 16 16">
<circle stroke="black" stroke-width="0" fill="#FFFFFF" cx="4" cy="12" r="4"></circle>
<circle stroke="black" stroke-width="0" fill="#FFFFFF" cx="13" cy="12" r="3"></circle>
<circle stroke="black" stroke-width="0" fill="#FFFFFF" cx="13" cy="3" r="2.236067977"></circle>
<circle stroke="black" stroke-width="0" fill="#FFFFFF" cx="4" cy="2" r="2"></circle>
<line stroke="#FFFFFF" stroke-width="1" x1="11" y1="5" x2="8" y2="8" stroke-linecap="round"></line>
<line stroke="#FFFFFF" stroke-width="1" x1="4" y1="5" x2="4" y2="7" stroke-linecap="round"></line>
<line stroke="#FFFFFF" stroke-width="1" x1="9" y1="12" x2="9" y2="12" stroke-linecap="round"></line>
</svg> Following Graph
</NavLink>
</div>
<div class="nav-item px-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
top: -2px;
}

.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}

.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
Expand Down
24 changes: 24 additions & 0 deletions src/KristofferStrube.Blazor.GraphEditor/GraphEditor.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,51 @@ private string EdgeId(TEdge e)
[Parameter, EditorRequired]
public required Func<TNode, string> NodeIdMapper { get; set; }

/// <summary>
/// Defaults to <c>"#66BB6A"</c>.
/// </summary>
[Parameter]
public Func<TNode, string> NodeColorMapper { get; set; } = _ => "#66BB6A";

/// <summary>
/// Defaults to <c>50</c>.
/// </summary>
[Parameter]
public Func<TNode, double> NodeRadiusMapper { get; set; } = _ => 50;

/// <summary>
/// Defaults to <c>800</c>.
/// </summary>
[Parameter]
public Func<TNode, double> NodeRepulsionMapper { get; set; } = _ => 800;

/// <summary>
/// Defaults to <see langword="null"/>.
/// </summary>
[Parameter]
public Func<TNode, string?> NodeImageMapper { get; set; } = _ => null;

[Parameter, EditorRequired]
public required Func<TEdge, TNode> EdgeFromMapper { get; set; }

[Parameter, EditorRequired]
public required Func<TEdge, TNode> EdgeToMapper { get; set; }

/// <summary>
/// Defaults to <c>1</c>.
/// </summary>
[Parameter]
public Func<TEdge, double> EdgeWidthMapper { get; set; } = _ => 1;

/// <summary>
/// Defaults to <c>1</c>.
/// </summary>
[Parameter]
public Func<TEdge, double> EdgeSpringConstantMapper { get; set; } = _ => 1;

/// <summary>
/// Defaults to <c>200</c>.
/// </summary>
[Parameter]
public Func<TEdge, double> EdgeSpringLengthMapper { get; set; } = _ => 200;

Expand Down
2 changes: 2 additions & 0 deletions src/KristofferStrube.Blazor.GraphEditor/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public override string Fill
}
}

public string? Image => GraphEditor.NodeImageMapper(Data);

public HashSet<Edge<TNodeData, TEdgeData>> Edges { get; } = new();

public Dictionary<string, Edge<TNodeData, TEdgeData>> NeighborNodes { get; } = new();
Expand Down
30 changes: 25 additions & 5 deletions src/KristofferStrube.Blazor.GraphEditor/NodeEditor.razor
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,44 @@

<ContextMenuTrigger @key=@SVGElement.Id MenuId="SVGMenu" WrapperTag="g" Data=@SVGElement MouseButtonTrigger="SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4">
<g transform="translate(@SVGElement.SVG.Translate.x.AsString() @SVGElement.SVG.Translate.y.AsString()) scale(@SVGElement.SVG.Scale.AsString())">

<circle @ref=ElementReference
@onfocusin="FocusElement"
@onfocusout="UnfocusElement"
@onpointerdown="SelectAsync"
@onkeyup="KeyUp"
@onfocusin="FocusElement"
@onfocusout="UnfocusElement"
@onpointerdown="SelectAsync"
@onkeyup="KeyUp"
tabindex="@(SVGElement.IsChildElement ? -1 : 0)"
cx=@SVGElement.Cx.AsString()
cy=@SVGElement.Cy.AsString()
r=@SVGElement.R.AsString()
stroke="@SVGElement.Stroke"
stroke-width="@SVGElement.StrokeWidth"
stroke-width="@((SVGElement.Selected ? 2 : 1))"
stroke-linecap="@SVGElement.StrokeLinecap.AsString()"
stroke-linejoin="@SVGElement.StrokeLinejoin.AsString()"
stroke-dasharray="@SVGElement.StrokeDasharray"
stroke-dashoffset="@SVGElement.StrokeDashoffset.AsString()"
fill="@SVGElement.Fill"
style="filter:brightness(@(SVGElement.Selected ? "0.8" : "1"))">
</circle>
@if (SVGElement.Image is not null)
{
<defs>
<clipPath id="[email protected](SVGElement.Data)">
<circle cx=@SVGElement.Cx.AsString()
cy=@SVGElement.Cy.AsString()
r=@((SVGElement.R - 1).AsString())>
</circle>
</clipPath>
</defs>
<image x=@((SVGElement.Cx - SVGElement.R).AsString())
y=@((SVGElement.Cy - SVGElement.R).AsString())
width="@((SVGElement.R * 2 - 1).AsString())"
height="@((SVGElement.R * 2 - 1).AsString())"
clip-path="url(#[email protected](SVGElement.Data))"
href="@SVGElement.Image"
style="pointer-events: none;">
</image>
}
</g>
</ContextMenuTrigger>

Expand Down

0 comments on commit 1230ec1

Please sign in to comment.