about summary refs log tree commit diff
path: root/MatrixUtils.Web/Shared
diff options
context:
space:
mode:
Diffstat (limited to 'MatrixUtils.Web/Shared')
-rw-r--r--MatrixUtils.Web/Shared/ActivityGraph.razor148
-rw-r--r--MatrixUtils.Web/Shared/ActivityGraph.razor.css16
-rw-r--r--MatrixUtils.Web/Shared/MainLayout.razor4
-rw-r--r--MatrixUtils.Web/Shared/MxcImage.razor3
-rw-r--r--MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor4
-rw-r--r--MatrixUtils.Web/Shared/UserListItem.razor6
6 files changed, 174 insertions, 7 deletions
diff --git a/MatrixUtils.Web/Shared/ActivityGraph.razor b/MatrixUtils.Web/Shared/ActivityGraph.razor
new file mode 100644
index 0000000..51fb539
--- /dev/null
+++ b/MatrixUtils.Web/Shared/ActivityGraph.razor
@@ -0,0 +1,148 @@
+@using System.Drawing
+@using System.Runtime.InteropServices
+@using System.Diagnostics
+
+@if (Data is { Count: > 0 })
+{
+    @*                                                      12*5=60 *@
+    <div style="display: grid; grid-template-columns: 35px repeat(60, 1.5em); grid-template-rows: 1.5em repeat(7, 1.5em); gap: 0;">
+        @* row 0: month labels with colspan *@
+        @* @foreach (var month in Enumerable.Range(1, 12)) *@
+        @* { *@
+        @*     <div style="grid-row: 1; grid-column: @((int)(month * 4.3) + 1);"> *@
+        @*         <span aria-hidden="true">@(new DateTime(2021, month, 1).ToString("MMM")[..3])</span> *@
+        @*     </div> *@
+        @* } *@
+
+        @* column 0: day labels *@
+        @* @for (var i = 0; i < 7; i++) *@
+        @* { *@
+        @*     <div style="text-align: left; grid-column: 1; grid-row: @(i + 2)"> *@
+        @*         @(((DayOfWeek)i).ToString()[..3]) *@
+        @*     </div> *@
+        @* } *@
+
+
+        <div style="grid-row: 1; grid-column: 5;">Jan</div>
+        <div style="grid-row: 1; grid-column: 9;">Feb</div>
+        <div style="grid-row: 1; grid-column: 13;">Mar</div>
+        <div style="grid-row: 1; grid-column: 18;">Apr</div>
+        <div style="grid-row: 1; grid-column: 22;">May</div>
+        <div style="grid-row: 1; grid-column: 26;">Jun</div>
+        <div style="grid-row: 1; grid-column: 31;">Jul</div>
+        <div style="grid-row: 1; grid-column: 35;">Aug</div>
+        <div style="grid-row: 1; grid-column: 39;">Sep</div>
+        <div style="grid-row: 1; grid-column: 44;">Oct</div>
+        <div style="grid-row: 1; grid-column: 48;">Nov</div>
+        <div style="grid-row: 1; grid-column: 52;">Dec</div>
+        <div style="text-align: left; grid-column: 1; grid-row: 2">Sun</div>
+        <div style="text-align: left; grid-column: 1; grid-row: 3">Mon</div>
+        <div style="text-align: left; grid-column: 1; grid-row: 4">Tue</div>
+        <div style="text-align: left; grid-column: 1; grid-row: 5">Wed</div>
+        <div style="text-align: left; grid-column: 1; grid-row: 6">Thu</div>
+        <div style="text-align: left; grid-column: 1; grid-row: 7">Fri</div>
+        <div style="text-align: left; grid-column: 1; grid-row: 8">Sat</div>
+
+
+        @* pad activity cell dates... *@
+        <div style="grid-column: 2; grid-row: 2 / span @((int)(new DateOnly(Data.Keys.First().Year, 1, 1).DayOfWeek));"></div>
+
+        @* the actual activity cells *@
+
+        @code{
+            bool needsBorder = false;
+        }
+
+        @for (DateOnly date = new DateOnly(Data.Keys.First().Year, 1, 1); date <= new DateOnly(Data.Keys.First().Year, 1, 1).AddYears(1).AddDays(-1); date = date.AddDays(1))
+        {
+            var hasData = Data.TryGetValue(date, out var color);
+            var needsTopBorder = date.Day == 1 && date.Month != 1 && date.DayOfWeek != DayOfWeek.Sunday;
+            if (date.DayOfWeek == DayOfWeek.Sunday)
+                needsBorder = date.AddDays(7).Day <= 7 && date.Month != 12;
+            var needsLeftBorder = date.Day <= 7;
+
+            <div class="activity-cell-container"
+                 style="grid-row: @((int)date.DayOfWeek + 2); border-@(needsLeftBorder ? "left" : "right"): @(needsBorder ? "2px solid white" : "none"); border-top: @(needsTopBorder ? "2px solid white" : "none");">
+                @if (hasData)
+                {
+                    <div class="activity-cell"
+                         style="background-color: rgb(@(color.R / GlobalMax.R * 255), @(color.G / GlobalMax.G * 255), @(color.B / GlobalMax.B * 255));"
+                         title="@($"{color.R} {RLabel}, {color.G} {GLabel}, and {color.B} {BLabel} on {date.ToString("D")}")">
+                    </div>
+                }
+                else
+                {
+                    <div class="activity-cell"
+                         title="@($"No data on {date.ToString("D")}")">
+                    </div>
+                }
+            </div>
+        }
+    </div>
+}
+
+
+@code {
+    private Dictionary<DateOnly, RGB> _data = new();
+    private RGB? _globalMax = null;
+
+    [Parameter]
+    public Dictionary<DateOnly, RGB> Data
+    {
+        get => _data;
+        set
+        {
+            // var sw = Stopwatch.StartNew();
+            if (value is not { Count: > 0 }) return;
+            // Console.WriteLine($"Recalculating activity graph ({value.Count} datapoints)...");
+
+
+            // var year = (int)value.Keys.Average(x => x.Year);
+            // value = value
+            // .Where(x => x.Key.Year == year)
+            // .OrderBy(x => x.Key)
+            // .ToDictionary(x => x.Key, x => x.Value);
+
+            _data = value;
+            // Console.WriteLine($"Recalculated activity graph in {sw.Elapsed}");
+            // StateHasChanged();
+        }
+    }
+
+    [Parameter]
+    public RGB GlobalMax
+    {
+        get
+        {
+            if (_globalMax is not null) return _globalMax.Value;
+            if (Data is not { Count: > 0 }) return new RGB() { R = 255, G = 255, B = 255 };
+            return new RGB()
+            {
+                R = Data.Values.Max(x => x.R),
+                G = Data.Values.Max(x => x.G),
+                B = Data.Values.Max(x => x.B)
+            };
+        }
+        set => _globalMax = value;
+    }
+
+    [Parameter] public string RLabel { get; set; } = "R";
+    [Parameter] public string GLabel { get; set; } = "G";
+    [Parameter] public string BLabel { get; set; } = "B";
+
+    [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * 3, Pack = 1)]
+    public struct RGB()
+    {
+        public float R = 0;
+        public float G = 0;
+        public float B = 0;
+
+        public RGB(float r, float g, float b) : this()
+        {
+            R = r;
+            G = g;
+            B = b;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Shared/ActivityGraph.razor.css b/MatrixUtils.Web/Shared/ActivityGraph.razor.css
new file mode 100644
index 0000000..d8e543c
--- /dev/null
+++ b/MatrixUtils.Web/Shared/ActivityGraph.razor.css
@@ -0,0 +1,16 @@
+.activity-cell-container {
+    width: 100%;
+    height: 100%;
+    align-content: center;
+    justify-content: center;
+}
+
+.activity-cell {
+    width: 85%;
+    height: 85%;
+    border-radius: 5px;
+}
+
+.day-label {
+    grid-column: 1;
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Shared/MainLayout.razor b/MatrixUtils.Web/Shared/MainLayout.razor
index d8bf411..41c3d69 100644
--- a/MatrixUtils.Web/Shared/MainLayout.razor
+++ b/MatrixUtils.Web/Shared/MainLayout.razor
@@ -8,8 +8,8 @@
     <main>
         <div class="top-row px-4">
             <PortableDevTools></PortableDevTools>
-            <a href="https://cgit.rory.gay/matrix/MatrixRoomUtils.git/" target="_blank">Git</a>
-            <a href="https://matrix.to/#/%23mru%3Arory.gay?via=rory.gay&via=matrix.org&via=feline.support" target="_blank">Matrix</a>
+            <a style="color: #ccc; text-decoration: underline" href="https://cgit.rory.gay/matrix/MatrixRoomUtils.git/" target="_blank">Git</a>
+            <a style="color: #ccc; text-decoration: underline" href="https://matrix.to/#/%23mru%3Arory.gay?via=rory.gay&via=matrix.org&via=feline.support" target="_blank">Matrix</a>
         </div>
 
         <article class="Content px-4">
diff --git a/MatrixUtils.Web/Shared/MxcImage.razor b/MatrixUtils.Web/Shared/MxcImage.razor
index f31c19f..e651c3f 100644
--- a/MatrixUtils.Web/Shared/MxcImage.razor
+++ b/MatrixUtils.Web/Shared/MxcImage.razor
@@ -30,6 +30,7 @@
             StateHasChanged();
         }
     }
+    
     [Parameter]
     public RemoteHomeserver? Homeserver { get; set; }
 
@@ -41,7 +42,7 @@
         }
     }
 
-    private string StyleString => $"{Style} {(Circular ? "border-radius: 50%;" : "")} {(Width.HasValue ? $"width: {Width}px;" : "")} {(Height.HasValue ? $"height: {Height}px;" : "")}";
+    private string StyleString => $"{Style} {(Circular ? "border-radius: 50%;" : "")} {(Width.HasValue ? $"width: {Width}px;" : "")} {(Height.HasValue ? $"height: {Height}px;" : "")} object-fit: cover;";
     
     private static readonly string Prefix = "mxc://";
     private static readonly int PrefixLength = Prefix.Length;
diff --git a/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor b/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
index 9c481e3..6954990 100644
--- a/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
+++ b/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
@@ -48,9 +48,7 @@
             if (Breadcrumbs.Contains(room.RoomId)) continue;
             var roomInfo = KnownRooms.FirstOrDefault(x => x.Room.RoomId == room.RoomId);
             if (roomInfo is null) {
-                roomInfo = new RoomInfo() {
-                    Room = room
-                };
+                roomInfo = new RoomInfo(room);
                 KnownRooms.Add(roomInfo);
             }
             if(joinedRooms.Any(x=>x.RoomId == room.RoomId))
diff --git a/MatrixUtils.Web/Shared/UserListItem.razor b/MatrixUtils.Web/Shared/UserListItem.razor
index 525296e..daa9c9e 100644
--- a/MatrixUtils.Web/Shared/UserListItem.razor
+++ b/MatrixUtils.Web/Shared/UserListItem.razor
@@ -2,8 +2,9 @@
 @using LibMatrix.EventTypes.Spec.State
 @using LibMatrix.Homeservers
 @using LibMatrix.Responses
+@using ArcaneLibs
 <div style="background-color: #ffffff11; border-radius: 25px; margin: 8px; width: fit-Content;">
-    <img style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height:  32px; border-radius: 50%;" src="@(string.IsNullOrWhiteSpace(User?.AvatarUrl) ? "https://api.dicebear.com/6.x/identicon/svg?seed=" + UserId : User.AvatarUrl)"/>
+    <img style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height:  32px; border-radius: 50%;" src="@(string.IsNullOrWhiteSpace(User?.AvatarUrl) ? _identiconGenerator.GenerateAsDataUri(UserId) : User.AvatarUrl)"/>
     <span style="vertical-align: middle; margin-right: 8px; border-radius: 75px;">@User?.DisplayName</span>
 
     <div style="display: inline-block;">
@@ -27,6 +28,8 @@
 
     private AuthenticatedHomeserverGeneric _homeserver = null!;
 
+    private SvgIdenticonGenerator _identiconGenerator = new();
+
     protected override async Task OnInitializedAsync() {
         _homeserver = await RMUStorage.GetCurrentSessionOrNavigate();
         if (_homeserver is null) return;
@@ -35,6 +38,7 @@
             if (UserId == null) {
                 throw new ArgumentNullException(nameof(UserId));
             }
+
             User = await _homeserver.GetProfileAsync(UserId);
         }