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/FilterComponents/BooleanFilterComponent.razor17
-rw-r--r--MatrixUtils.Web/Shared/FilterComponents/StringFilterComponent.razor31
-rw-r--r--MatrixUtils.Web/Shared/InlineUserItem.razor5
-rw-r--r--MatrixUtils.Web/Shared/InputLocalPart.razor50
-rw-r--r--MatrixUtils.Web/Shared/MainLayout.razor12
-rw-r--r--MatrixUtils.Web/Shared/MainLayout.razor.css1
-rw-r--r--MatrixUtils.Web/Shared/MxcAvatar.razor49
-rw-r--r--MatrixUtils.Web/Shared/MxcImage.razor78
-rw-r--r--MatrixUtils.Web/Shared/NavMenu.razor6
-rw-r--r--MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor221
-rw-r--r--MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor.css15
-rw-r--r--MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor106
-rw-r--r--MatrixUtils.Web/Shared/RoomList.razor5
-rw-r--r--MatrixUtils.Web/Shared/RoomListComponents/RoomListCategory.razor13
-rw-r--r--MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor13
-rw-r--r--MatrixUtils.Web/Shared/RoomListItem.razor53
-rw-r--r--MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor2
-rw-r--r--MatrixUtils.Web/Shared/TimelineComponents/TimelineCanonicalAliasItem.razor2
-rw-r--r--MatrixUtils.Web/Shared/TimelineComponents/TimelineHistoryVisibilityItem.razor2
-rw-r--r--MatrixUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor2
-rw-r--r--MatrixUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor2
-rw-r--r--MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomCreateItem.razor2
-rw-r--r--MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomNameItem.razor2
-rw-r--r--MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomTopicItem.razor2
-rw-r--r--MatrixUtils.Web/Shared/UserListItem.razor25
25 files changed, 600 insertions, 116 deletions
diff --git a/MatrixUtils.Web/Shared/FilterComponents/BooleanFilterComponent.razor b/MatrixUtils.Web/Shared/FilterComponents/BooleanFilterComponent.razor
new file mode 100644

index 0000000..0730701 --- /dev/null +++ b/MatrixUtils.Web/Shared/FilterComponents/BooleanFilterComponent.razor
@@ -0,0 +1,17 @@ +@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters +<span> + <InputCheckbox @bind-Value="@Filter.Enabled"/> @Label: + @if (Filter.Enabled) { + <InputCheckbox @bind-Value="@Filter.Value"/> + } +</span> + +@code { + + [Parameter] + public required BoolFilter Filter { get; set; } + + [Parameter] + public required string Label { get; set; } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/FilterComponents/StringFilterComponent.razor b/MatrixUtils.Web/Shared/FilterComponents/StringFilterComponent.razor new file mode 100644
index 0000000..c5a6e15 --- /dev/null +++ b/MatrixUtils.Web/Shared/FilterComponents/StringFilterComponent.razor
@@ -0,0 +1,31 @@ +@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters +<span style="vertical-align: top;"> + <InputCheckbox @bind-Value="@Filter.Enabled"/> @Label: +</span> +@if (Filter.Enabled) { + <div style="display: inline-block;"> + <InputCheckbox @bind-Value="@Filter.CheckValueContains"/> + Contains + <FancyTextBox @bind-Value="@Filter.ValueContains"></FancyTextBox> + <br/> + <InputCheckbox @bind-Value="@Filter.CheckValueEquals"/> + Equals + <FancyTextBox @bind-Value="@Filter.ValueEquals"></FancyTextBox> + <LinkButton OnClick="@SetEqualsNull" InlineText="true"> [Set null]</LinkButton> + </div> +} + +@code { + + [Parameter] + public required StringFilter Filter { get; set; } + + [Parameter] + public required string Label { get; set; } + + private void SetEqualsNull() { + Filter.ValueEquals = null; + StateHasChanged(); + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/InlineUserItem.razor b/MatrixUtils.Web/Shared/InlineUserItem.razor
index 9c6608a..eaf7a92 100644 --- a/MatrixUtils.Web/Shared/InlineUserItem.razor +++ b/MatrixUtils.Web/Shared/InlineUserItem.razor
@@ -1,4 +1,4 @@ -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @using LibMatrix.Responses <div style="background-color: #ffffff11; border-radius: 0.5em; height: 1em; display: inline-block; vertical-align: middle;" alt="@UserId"> <img style="@(ChildContent is not null ? "vertical-align: baseline;" : "vertical-align: top;") width: 1em; height: 1em; border-radius: 50%;" src="@ProfileAvatar"/> @@ -39,7 +39,6 @@ protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - Homeserver ??= await RMUStorage.GetCurrentSessionOrNavigate(); if(Homeserver is null) return; await _semaphoreSlim.WaitAsync(); @@ -59,7 +58,7 @@ } - ProfileAvatar ??= Homeserver.ResolveMediaUri(User.AvatarUrl); + // ProfileAvatar ??= Homeserver.ResolveMediaUri(User.AvatarUrl); ProfileName ??= User.DisplayName; _semaphoreSlim.Release(); diff --git a/MatrixUtils.Web/Shared/InputLocalPart.razor b/MatrixUtils.Web/Shared/InputLocalPart.razor new file mode 100644
index 0000000..8f34377 --- /dev/null +++ b/MatrixUtils.Web/Shared/InputLocalPart.razor
@@ -0,0 +1,50 @@ +<div style="display: inline-flex;"> + @if (!string.IsNullOrWhiteSpace(Label)) { + <label>@Label</label> + } + <span>@Sigil</span> + <FancyTextBox @bind-Value="@LocalPart"></FancyTextBox> + <span>:</span> + @if (ServerNameChanged is not null) { + <FancyTextBox @bind-Value="@ServerName"></FancyTextBox> + } + else { + <span>@ServerName</span> + } +</div> + +@code { + + [Parameter] + public string? Label { get; set; } + + [Parameter] + public required string Sigil { get; set; } + + [Parameter] + public string? LocalPart { + get; + set { + if (field == value) return; + field = value; + LocalPartChanged.InvokeAsync(value); + } + } + + [Parameter] + public EventCallback<string> LocalPartChanged { get; set; } + + [Parameter] + public string? ServerName { + get; + set { + if (field == value) return; + field = value; + ServerNameChanged?.InvokeAsync(value); + } + } + + [Parameter] + public EventCallback<string>? ServerNameChanged { get; set; } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/MainLayout.razor b/MatrixUtils.Web/Shared/MainLayout.razor
index c67f73c..b32735f 100644 --- a/MatrixUtils.Web/Shared/MainLayout.razor +++ b/MatrixUtils.Web/Shared/MainLayout.razor
@@ -1,5 +1,4 @@ -@using ArcaneLibs -@inherits LayoutComponentBase +@inherits LayoutComponentBase <div class="page"> <div class="sidebar"> @@ -10,16 +9,15 @@ <div class="top-row px-4"> @* <PortableDevTools/> *@ @* <ResourceUsage/> *@ - <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> + <a style="color: #ccc; text-decoration: underline" href="https://cgit.rory.gay/matrix/tools/MatrixUtils.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"> @Body </article> - - </main> </div> -<UpdateAvailableDetector/> \ No newline at end of file +<UpdateAvailableDetector/> diff --git a/MatrixUtils.Web/Shared/MainLayout.razor.css b/MatrixUtils.Web/Shared/MainLayout.razor.css
index 01a5066..924393b 100644 --- a/MatrixUtils.Web/Shared/MainLayout.razor.css +++ b/MatrixUtils.Web/Shared/MainLayout.razor.css
@@ -57,6 +57,7 @@ main { .sidebar { width: 250px; + min-width: 250px; height: 100vh; position: sticky; top: 0; diff --git a/MatrixUtils.Web/Shared/MxcAvatar.razor b/MatrixUtils.Web/Shared/MxcAvatar.razor new file mode 100644
index 0000000..822894a --- /dev/null +++ b/MatrixUtils.Web/Shared/MxcAvatar.razor
@@ -0,0 +1,49 @@ +<MxcImage Homeserver="@Homeserver" Uri="@MxcUri" style="@StyleString"/> + +@code { + private string _style; + + [Parameter] + public string? MxcUri { + get; + set { + if(field == value) return; + field = value; + // UriHasChanged(value); + StateHasChanged(); + } + } + + [Parameter] + public bool Circular { get; set; } + + [Parameter] + public int Size { get; set; } = 48; + + [Parameter] + public string SizeUnit { get; set; } = "px"; + + [Parameter] + public required AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private string StyleString => $"{(Circular ? "border-radius: 50%;" : "")} width: {Size}{SizeUnit}; height: {Size}{SizeUnit}; object-fit: cover;"; + + private static readonly string Prefix = "mxc://"; + private static readonly int PrefixLength = Prefix.Length; + + // private async Task UriHasChanged(string? value) { + // if (string.IsNullOrWhiteSpace(value) || !value.StartsWith(Prefix)) { + // Console.WriteLine($"[MxcAvatar] UriHasChanged: {value} does not start with {Prefix}!"); + // return; + // } + // + // if (Homeserver is null) { + // Console.WriteLine($"[MxcAvatar] Homeserver is required for MxcAvatar! URI: {MxcUri}, Homeserver: {Homeserver?.ToString() ?? "null"}"); + // return; + // } + // + // Console.WriteLine($"[MxcAvatar] Homeserver: {Homeserver}"); + // StateHasChanged(); + // } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/MxcImage.razor b/MatrixUtils.Web/Shared/MxcImage.razor
index e651c3f..26609ee 100644 --- a/MatrixUtils.Web/Shared/MxcImage.razor +++ b/MatrixUtils.Web/Shared/MxcImage.razor
@@ -1,69 +1,69 @@ -<img src="@ResolvedUri" style="@StyleString"/> -@code { - private string _mxcUri; - private string _style; - private string _resolvedUri; +<AuthorizedImage src="@ResolvedUrl" AccessToken="@Homeserver?.AccessToken" style="@StyleString"/> +@code { [Parameter] - public string MxcUri { - get => _mxcUri ?? ""; + public string? Uri { + get; set { - Console.WriteLine($"New MXC uri: {value}"); - _mxcUri = value; + // Console.WriteLine($"New MXC uri: {value}"); + if (field == value) return; + field = value; UriHasChanged(value); } } + [Parameter] public bool Circular { get; set; } - + [Parameter] public int? Width { get; set; } - + [Parameter] public int? Height { get; set; } - + [Parameter] - public string Style { - get => _style; + public string? Style { + get; set { - _style = value; + field = value; StateHasChanged(); } } - + [Parameter] - public RemoteHomeserver? Homeserver { get; set; } + public required AuthenticatedHomeserverGeneric Homeserver { get; set; } - private string ResolvedUri { - get => _resolvedUri; + private string? ResolvedUrl { + get; set { - _resolvedUri = value; + field = value; StateHasChanged(); } } 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; - private async Task UriHasChanged(string value) { - if (!value.StartsWith(Prefix)) { - Console.WriteLine($"UriHasChanged: {value} does not start with {Prefix}, passing as resolved URI!!!"); - ResolvedUri = value; - return; - } - var uri = value[PrefixLength..].Split('/'); - Console.WriteLine($"UriHasChanged: {value} {uri[0]}"); - if (Homeserver is null) { - Console.WriteLine($"Homeserver is null, creating new remotehomeserver for {uri[0]}"); - Homeserver = await hsProvider.GetRemoteHomeserver(uri[0]); + // private static readonly string Prefix = "mxc://"; + // private static readonly int PrefixLength = Prefix.Length; + + private async Task UriHasChanged(string? value) { + try { + if (string.IsNullOrWhiteSpace(value)) { + ResolvedUrl = null; + return; + } + + if (Homeserver is null) { + Console.WriteLine($"Homeserver is required for MxcImage! Uri: {value}, Homeserver: {Homeserver?.ToString() ?? "null"}"); + return; + } + + ResolvedUrl = await Homeserver.GetMediaUrlAsync(value); + // Console.WriteLine($"[MxcImage] Resolved URL: {ResolvedUrl}"); + StateHasChanged(); + } catch (Exception e) { + await Console.Error.WriteLineAsync($"Error resolving media URL: {e}"); } - ResolvedUri = Homeserver.ResolveMediaUri(value); - Console.WriteLine($"ResolvedUri: {ResolvedUri}"); } - // [Parameter] - // public string Class { get; set; } - } \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/NavMenu.razor b/MatrixUtils.Web/Shared/NavMenu.razor
index 770a246..7371e66 100644 --- a/MatrixUtils.Web/Shared/NavMenu.razor +++ b/MatrixUtils.Web/Shared/NavMenu.razor
@@ -37,6 +37,12 @@ </div> <div class="nav-item px-3"> + <NavLink class="nav-link" href="PolicyLists"> + <span class="oi oi-ban" aria-hidden="true"></span> Manage policy lists + </NavLink> + </div> + + <div class="nav-item px-3"> <NavLink class="nav-link" href="User/Profile"> <span class="oi oi-person" aria-hidden="true"></span> Manage profile </NavLink> diff --git a/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor b/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor new file mode 100644
index 0000000..bb4b672 --- /dev/null +++ b/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor
@@ -0,0 +1,221 @@ +@using LibMatrix.EventTypes.Spec.State.Policy +@using System.Reflection +@using ArcaneLibs.Attributes +@using LibMatrix +@using System.Collections.Frozen +@using LibMatrix.EventTypes +@using LibMatrix.RoomTypes +<ModalWindow + Title="@("Creating many new " + (PolicyTypes.ContainsKey(MappedType ?? "") ? PolicyTypes[MappedType!].GetFriendlyNamePluralOrNull()?.ToLower() ?? PolicyTypes[MappedType!].Name : "event"))" + OnCloseClicked="@OnClose" X="60" Y="60" MinWidth="600"> + <span>Policy type:</span> + <select @bind="@MappedType"> + <option>Select a value</option> + @foreach (var (type, mappedType) in PolicyTypes) { + <option value="@type">@mappedType.GetFriendlyName().ToLower()</option> + } + </select><br/> + + <span>Reason:</span> + <FancyTextBox @bind-Value="@Reason"></FancyTextBox> + <br/> + + <span>Recommendation:</span> + <FancyTextBox @bind-Value="@Recommendation"></FancyTextBox> + <br/> + + <span>Entities:</span><br/> + <FancyTextBox Multiline="true" @bind-Value="@Entities"></FancyTextBox> + <br/> + + + @* <details> *@ + @* <summary>JSON data</summary> *@ + @* <pre> *@ + @* $1$ @PolicyEvent.ToJson(true, true) #1# *@ + @* </pre> *@ + @* </details> *@ + @if (!VerifyIntent) { + <LinkButton OnClickAsync="@(() => { + OnClose.Invoke(); + return Task.CompletedTask; + })"> Cancel + </LinkButton> + <LinkButton OnClickAsync="@(() => { + _ = Save(); + return Task.CompletedTask; + })"> Save + </LinkButton> + @if (!string.IsNullOrWhiteSpace(Response)) { + <pre style="color: red;">@Response</pre> + } + } + else { + <b class="blink">WARNING!!!</b> + <br/> + + @if (!string.IsNullOrWhiteSpace(Response)) { + <pre style="color: red;">@Response</pre> + } + + <span>Are you sure you want to do this?</span> + <LinkButton Color="#00FF00" OnClick="@(() => { + VerifyIntent = false; + Response = null; + StateHasChanged(); + })">No + </LinkButton> + <LinkButton Color="#FF0000" OnClick="@(() => { _ = Save(force: true); })">Yes</LinkButton> + } + +</ModalWindow> + +@code { + + [Parameter] + public required Action OnClose { get; set; } + + [Parameter] + public required Action OnSaved { get; set; } + + [Parameter] + public required GenericRoom Room { get; set; } + + private string Recommendation { get; set; } = "m.ban"; + private string Reason { get; set; } = "spam"; + + private string Entities { get; set; } = ""; + + private string? Response { + get; + set { + field = value; + StateHasChanged(); + } + } + + private bool VerifyIntent { get; set; } + + private static FrozenSet<Type> KnownPolicyTypes = StateEvent.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))).ToFrozenSet(); + + private static Dictionary<string, Type> PolicyTypes = KnownPolicyTypes + .ToDictionary(x => x.GetCustomAttributes<MatrixEventAttribute>().First(y => !string.IsNullOrWhiteSpace(y.EventName)).EventName, x => x); + + private static FrozenSet<string> AllKnownPolicyTypes = KnownPolicyTypes + .SelectMany(x => x.GetCustomAttributes<MatrixEventAttribute>().Select(y => y.EventName)) + .ToFrozenSet(); + + private string? MappedType { get; set; } + + private async Task Save(bool force = false) { + if (string.IsNullOrWhiteSpace(MappedType)) { + Response = "No type selected"; + return; + } + + if (string.IsNullOrWhiteSpace(Entities)) { + Response = "No users selected"; + return; + } + + Console.WriteLine("Saving ---"); + + var entities = Entities.Split("\n", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Select(x => x.Trim()) + .Distinct() + .ToList(); + + if (!force && !Validate(entities, PolicyTypes[MappedType])) { + List<string> distinctTypes = entities + .Select(GuessType) + .Where(x => x != null) + .Distinct() + .Select(x => x!.Name) + .ToList(); + + VerifyIntent = true; + Response = $"Invalid entities. Expected {PolicyTypes[MappedType].Name}, got:\n - " + + string.Join("\n - ", distinctTypes); + return; + } + + try { + await SaveAll(entities); + } + catch (Exception e) { + Response = $"Failed to save: {e}"; + } + } + + private bool Validate(List<string> entities, Type expectedType) { + return entities.All(x => GuessType(x) == expectedType); + } + + private Type? GuessType(string entity) { + var sigil = entity[0]; + return TypesBySigil.GetValueOrDefault(sigil.ToString(), typeof(ServerPolicyRuleEventContent)); + } + + private Dictionary<string, Type> TypesBySigil = new() { + { "@", typeof(UserPolicyRuleEventContent) }, + { "!", typeof(RoomPolicyRuleEventContent) }, + { "#", typeof(RoomPolicyRuleEventContent) } + }; + + private async Task SaveAll(List<string> entities) { + await foreach (var evt in Room.GetFullStateAsync()) { + if (evt is null + || !AllKnownPolicyTypes.Contains(evt.Type) + || !evt.TypedContent!.GetType().IsAssignableTo(PolicyTypes[MappedType!]) + ) continue; + + if (evt.TypedContent is PolicyRuleEventContent content && content.Recommendation == Recommendation && content.Reason == Reason) { + if (content.Entity != null && entities.Contains(content.Entity)) + entities.Remove(content.Entity); + } + } + + // var tasks = entities.Select(x => ExecuteBan(Room, x)).ToList(); + // await Task.WhenAll(tasks); + + var events = entities.Select(entity => { + var content = Activator.CreateInstance(PolicyTypes[MappedType!]) as PolicyRuleEventContent ?? throw new InvalidOperationException("Failed to create event content"); + content.Recommendation = Recommendation; + content.Reason = Reason; + content.Entity = entity; + return new StateEvent() { + Type = MappedType, + TypedContent = content, + StateKey = content.GetDraupnir2StateKey() + }; + }); + + foreach(var chunk in events.Chunk(50)) + await Room.BulkSendEventsAsync(chunk); + + OnSaved.Invoke(); + } + + private async Task ExecuteBan(GenericRoom room, string entity) { + bool success = false; + while (!success) { + try { + var content = Activator.CreateInstance(PolicyTypes[MappedType!]) as PolicyRuleEventContent ?? throw new InvalidOperationException("Failed to create event content"); + content.Recommendation = Recommendation; + content.Reason = Reason; + content.Entity = entity; + await room.SendStateEventAsync(MappedType!, content.GetDraupnir2StateKey(), content); + success = true; + } + catch (MatrixException e) { + if (e is not { ErrorCode: MatrixException.ErrorCodes.M_FORBIDDEN }) throw; + Console.WriteLine(e); + } + catch (Exception e) { + //ignored + Console.WriteLine(e); + } + } + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor.css b/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor.css new file mode 100644
index 0000000..49ab31b --- /dev/null +++ b/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor.css
@@ -0,0 +1,15 @@ +.blink { + animation: blinker 2s linear infinite; +} + +@keyframes blinker { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } +} \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor b/MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor
index 1bd00d1..0205e16 100644 --- a/MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor +++ b/MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor
@@ -6,7 +6,7 @@ @using System.Collections.Frozen @using LibMatrix.EventTypes <ModalWindow Title="@((string.IsNullOrWhiteSpace(PolicyEvent.EventId) ? "Creating new " : "Editing ") + (PolicyEvent.MappedType.GetFriendlyNameOrNull()?.ToLower() ?? "event"))" - OnCloseClicked="@OnClose" X="60" Y="60" MinWidth="300"> + OnCloseClickedAsync="@InvokeOnClose" X="60" Y="60" MinWidth="300"> @if (string.IsNullOrWhiteSpace(PolicyEvent.EventId)) { <span>Policy type:</span> <select @bind="@MappedType"> @@ -35,39 +35,75 @@ </thead> <tbody> @foreach (var prop in props) { + var isNullable = Nullable.GetUnderlyingType(prop.PropertyType) is not null; <tr> <td style="padding-right: 8px;"> <span>@prop.GetFriendlyName()</span> - @if (Nullable.GetUnderlyingType(prop.PropertyType) is not null) { + @if (Nullable.GetUnderlyingType(prop.PropertyType) is null) { <span style="color: red;">*</span> } </td> @{ var getter = prop.GetGetMethod(); var setter = prop.GetSetMethod(); - } - @switch (Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType) { - case Type t when t == typeof(string): - <FancyTextBox Value="@(getter?.Invoke(PolicyData, null) as string)" ValueChanged="@(e => { Console.WriteLine($"{prop.Name} ({setter is not null}) -> {e}"); setter?.Invoke(PolicyData, [e]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); })"></FancyTextBox> - break; - default: - <p style="color: red;">Unsupported type: @prop.PropertyType</p> - break; + if (getter is null) { + <p style="color: red;">Missing property getter: @prop.Name</p> + } + else { + switch (Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType) { + case Type t when t == typeof(string): + <FancyTextBox Value="@(getter?.Invoke(PolicyData, null) as string)" ValueChanged="@((string e) => { + Console.WriteLine($"{prop.Name} ({setter is not null}) -> {e}"); + setter?.Invoke(PolicyData, [e]); + PolicyEvent.TypedContent = PolicyData; + StateHasChanged(); + })"></FancyTextBox> + break; + case Type t when t == typeof(DateTime): + if (!isNullable) { + @* <InputDate TValue="DateTime" Value="@(getter?.Invoke(PolicyData, null) as DateTime? ?? new DateTime())" ValueChanged="@(e => { Console.WriteLine($"{prop.Name} ({setter is not null}) -> {e}"); setter?.Invoke(PolicyData, [e]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); })"></InputDate> *@ + } + else { + var value = getter?.Invoke(PolicyData, null) as DateTime?; + if (value is null) { + <button @onclick="() => { setter?.Invoke(PolicyData, [DateTime.Now]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); }"> + Add value + </button> + } + else { + var notNullValue = Nullable.GetValueRefOrDefaultRef(ref value); + Console.WriteLine($"Value: {value?.ToString() ?? "null"}"); + <InputDate TValue="DateTime" ValueExpression="@(() => notNullValue)" ValueChanged="@(e => { + Console.WriteLine($"{prop.Name} ({setter is not null}) -> {e}"); + setter?.Invoke(PolicyData, [e]); + PolicyEvent.TypedContent = PolicyData; + StateHasChanged(); + })"></InputDate> + <button @onclick="() => { setter?.Invoke(PolicyData, [null]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); }">Remove + value + </button> + } + } + + break; + default: + <p style="color: red;">Unsupported type: @prop.PropertyType</p> + break; + } + } } </tr> } </tbody> </table> - <br/> - <pre> - @PolicyEvent.ToJson(true, false) - </pre> - <LinkButton OnClick="@(() => { OnClose.Invoke(); return Task.CompletedTask; })"> Cancel </LinkButton> - <LinkButton OnClick="@(() => { OnSave.Invoke(PolicyEvent); return Task.CompletedTask; })"> Save </LinkButton> - @* <span>Target entity: </span> *@ - @* <FancyTextBox @bind-Value="@policyData.Entity"></FancyTextBox><br/> *@ - @* <span>Reason: </span> *@ - @* <FancyTextBox @bind-Value="@policyData.Reason"></FancyTextBox> *@ + <details> + <summary>JSON data</summary> + <pre> + @PolicyEvent.ToJson(true, true) + </pre> + </details> + <LinkButton OnClickAsync="@InvokeOnClose">Cancel</LinkButton> + <LinkButton OnClickAsync="@InvokeOnSave">Save</LinkButton> } else { <p>Policy data is null</p> @@ -89,10 +125,32 @@ } [Parameter] - public required Action OnClose { get; set; } + public Action? OnClose { get; set; } [Parameter] - public required Action<StateEventResponse> OnSave { get; set; } + public Func<Task>? OnCloseAsync { get; set; } + + private async Task InvokeOnClose() { + if (OnClose is not null) + OnClose.Invoke(); + + if (OnCloseAsync is not null) + await OnCloseAsync.Invoke(); + } + + [Parameter] + public Action<StateEventResponse>? OnSave { get; set; } + + [Parameter] + public Func<StateEventResponse, Task>? OnSaveAsync { get; set; } + + private async Task InvokeOnSave() { + if (OnSave is not null) + OnSave.Invoke(PolicyEvent); + + if (OnSaveAsync is not null) + await OnSaveAsync.Invoke(PolicyEvent); + } public PolicyRuleEventContent? PolicyData { get; set; } @@ -102,7 +160,7 @@ .ToDictionary(x => x.GetCustomAttributes<MatrixEventAttribute>().First(y => !string.IsNullOrWhiteSpace(y.EventName)).EventName, x => x); private StateEventResponse? _policyEvent; - + private string? MappedType { get => _policyEvent?.Type; set { @@ -110,9 +168,9 @@ PolicyEvent.Type = value; PolicyEvent.TypedContent ??= Activator.CreateInstance(PolicyTypes[value]) as PolicyRuleEventContent; PolicyData = PolicyEvent.TypedContent as PolicyRuleEventContent; + PolicyData.Recommendation ??= "m.ban"; } } } - } \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/RoomList.razor b/MatrixUtils.Web/Shared/RoomList.razor
index 42c5a9f..ba9cd69 100644 --- a/MatrixUtils.Web/Shared/RoomList.razor +++ b/MatrixUtils.Web/Shared/RoomList.razor
@@ -10,7 +10,7 @@ } else { @foreach (var category in RoomsWithTypes.OrderBy(x => x.Value.Count)) { - <RoomListCategory Category="@category" GlobalProfile="@GlobalProfile"></RoomListCategory> + <RoomListCategory Category="@category" GlobalProfile="@GlobalProfile" Homeserver="@Homeserver"></RoomListCategory> } } @@ -35,6 +35,9 @@ else { } [Parameter] + public AuthenticatedHomeserverGeneric? Homeserver { get; set; } + + [Parameter] public UserProfileResponse? GlobalProfile { get; set; } [Parameter] diff --git a/MatrixUtils.Web/Shared/RoomListComponents/RoomListCategory.razor b/MatrixUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
index 1f5ce89..1ab0a1a 100644 --- a/MatrixUtils.Web/Shared/RoomListComponents/RoomListCategory.razor +++ b/MatrixUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
@@ -1,12 +1,12 @@ +@using LibMatrix.EventTypes.Spec.State.RoomInfo @using MatrixUtils.Web.Classes.Constants -@using LibMatrix.EventTypes.Spec.State @using LibMatrix.Responses @using MatrixUtils.Abstractions <details open> <summary>@RoomType (@Rooms.Count)</summary> @foreach (var room in Rooms) { <div class="room-list-item"> - <RoomListItem RoomInfo="@room" ShowOwnProfile="@(RoomType == "Room")"></RoomListItem> + <RoomListItem RoomInfo="@room" ShowOwnProfile="@(RoomType == "Room")" Homeserver="@Homeserver"/> @* @if (RoomVersionDangerLevel(room) != 0 && *@ @* (room.StateEvents.FirstOrDefault(x=>x.Type == "m.room.power_levels")?.TypedContent is RoomPowerLevelEventContent powerLevels && powerLevels.UserHasPermission(Homeserver.UserId, "m.room.tombstone"))) { *@ @* <MatrixUtils.Web.Shared.SimpleComponents.LinkButton Color="@(RoomVersionDangerLevel(room) == 2 ? "#ff0000" : "#ff8800")" href="@($"/Rooms/Create?Import={room.Room.RoomId}")">Upgrade room</MatrixUtils.Web.Shared.SimpleComponents.LinkButton> *@ @@ -14,10 +14,11 @@ <LinkButton href="@($"/Rooms/{room.Room.RoomId}/Timeline")">View timeline</LinkButton> <LinkButton href="@($"/Rooms/{room.Room.RoomId}/State/View")">View state</LinkButton> <LinkButton href="@($"/Rooms/{room.Room.RoomId}/State/Edit")">Edit state</LinkButton> + <LinkButton href="@($"/Rooms/{room.Room.RoomId}/Upgrade")" Color="#888800">Upgrade/replace room</LinkButton> <LinkButton href="@($"/Tools/LeaveRoom?roomId={room.Room.RoomId}")" Color="#FF0000">Leave room</LinkButton> @if (room.CreationEventContent?.Type == "m.space") { - <RoomListSpace Space="@room"></RoomListSpace> + <RoomListSpace Space="@room" Homeserver="@Homeserver"/> } else if (room.CreationEventContent?.Type == "support.feline.policy.lists.msc.v1" || RoomType == "org.matrix.mjolnir.policy") { <LinkButton href="@($"/Rooms/{room.Room.RoomId}/Policies")">Manage policies</LinkButton> @@ -35,9 +36,9 @@ [Parameter] public UserProfileResponse? GlobalProfile { get; set; } - [CascadingParameter] - public AuthenticatedHomeserverGeneric Homeserver { get; set; } = null!; - + [Parameter] + public AuthenticatedHomeserverGeneric? Homeserver { get; set; } + private string RoomType => Category.Key; private List<RoomInfo> Rooms => Category.Value; diff --git a/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor b/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
index 6954990..471f586 100644 --- a/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor +++ b/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
@@ -35,15 +35,18 @@ set => _breadcrumbs = value; } + [Parameter] + public required AuthenticatedHomeserverGeneric Homeserver { get; set; } + private ObservableCollection<RoomInfo> Children { get; set; } = new(); private Collection<RoomInfo> Unjoined { get; set; } = new(); protected override async Task OnInitializedAsync() { if (Breadcrumbs == null) throw new ArgumentNullException(nameof(Breadcrumbs)); + if (Homeserver is null) throw new ArgumentNullException(nameof(Homeserver)); await Task.Delay(Random.Shared.Next(1000, 10000)); - var rooms = Space.Room.AsSpace.GetChildrenAsync(); - var hs = await RMUStorage.GetCurrentSessionOrNavigate(); - var joinedRooms = await hs.GetJoinedRooms(); + var rooms = Space.Room.AsSpace().GetChildrenAsync(); + var joinedRooms = await Homeserver.GetJoinedRooms(); await foreach (var room in rooms) { if (Breadcrumbs.Contains(room.RoomId)) continue; var roomInfo = KnownRooms.FirstOrDefault(x => x.Room.RoomId == room.RoomId); @@ -51,10 +54,12 @@ roomInfo = new RoomInfo(room); KnownRooms.Add(roomInfo); } - if(joinedRooms.Any(x=>x.RoomId == room.RoomId)) + + if (joinedRooms.Any(x => x.RoomId == room.RoomId)) Children.Add(roomInfo); else Unjoined.Add(roomInfo); } + await base.OnInitializedAsync(); } diff --git a/MatrixUtils.Web/Shared/RoomListItem.razor b/MatrixUtils.Web/Shared/RoomListItem.razor
index bfaa900..2d85f64 100644 --- a/MatrixUtils.Web/Shared/RoomListItem.razor +++ b/MatrixUtils.Web/Shared/RoomListItem.razor
@@ -1,19 +1,26 @@ +@using ArcaneLibs @using LibMatrix -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @using LibMatrix.Responses @using MatrixUtils.Abstractions @using MatrixUtils.Web.Classes.Constants @if (RoomInfo is not null) { <div class="roomListItem @(HasDangerousRoomVersion ? "dangerousRoomVersion" : HasOldRoomVersion ? "oldRoomVersion" : "")" id="@RoomInfo.Room.RoomId"> @if (OwnMemberState != null) { - @* Class="@("avatar32" + (OwnMemberState?.AvatarUrl != GlobalProfile?.AvatarUrl ? " highlightChange" : "") + (ChildContent is not null ? " vcenter" : ""))" *@ - <MxcImage Homeserver="hs" Circular="true" Height="32" Width="32" MxcUri="@(OwnMemberState.AvatarUrl ?? GlobalProfile.AvatarUrl)"/> + <MxcAvatar Homeserver="@Homeserver" Circular="true" Size="32" MxcUri="@(OwnMemberState.AvatarUrl ?? GlobalProfile.AvatarUrl)"/> <span class="centerVertical border75 @(OwnMemberState?.AvatarUrl != GlobalProfile?.AvatarUrl ? "highlightChange" : "")"> @(OwnMemberState?.DisplayName ?? GlobalProfile?.DisplayName ?? "Loading...") </span> <span class="centerVertical noLeftPadding">-></span> } - <MxcImage Circular="true" Height="32" Width="32" MxcUri="@RoomInfo.RoomIcon" Style="@(ChildContent is not null ? "vertical-align: middle;" : "")"/> + @* <MxcImage Circular="true" Height="32" Width="32" MxcUri="@RoomInfo.RoomIcon" Style="@(ChildContent is not null ? "vertical-align: middle;" : "")"/> *@ + + @if (!string.IsNullOrWhiteSpace(RoomInfo.RoomIcon)) { + <MxcAvatar Homeserver="@Homeserver" Circular="true" Size="32" MxcUri="@RoomInfo.RoomIcon"/> + } + else { + <img src="@Identicon" width="32" height="32" style="border-radius: 50%;"/> + } <div class="inlineBlock"> <span class="centerVertical">@RoomInfo.RoomName</span> @if (ChildContent is not null) { @@ -42,8 +49,6 @@ else { } } - - [Parameter] public bool ShowOwnProfile { get; set; } = false; @@ -61,27 +66,36 @@ else { OnParametersSetAsync(); } } + + [Parameter] + public AuthenticatedHomeserverGeneric? Homeserver { get; set; } private bool HasOldRoomVersion { get; set; } = false; private bool HasDangerousRoomVersion { get; set; } = false; + private string Identicon { get; set; } + + private static SvgIdenticonGenerator _identiconGenerator = new SvgIdenticonGenerator(); + private static SemaphoreSlim _semaphoreSlim = new(8); private RoomInfo? _roomInfo; private bool _loadData = false; - private static AuthenticatedHomeserverGeneric? hs { get; set; } private bool _hooked; - + private async Task RoomInfoChanged() { + if (RoomInfo is null) return; + Identicon = _identiconGenerator.GenerateAsDataUri(RoomInfo.Room.RoomId); + RoomInfo.PropertyChanged += async (_, a) => { if (a.PropertyName == nameof(RoomInfo.CreationEventContent)) { await CheckRoomVersion(); } - + StateHasChanged(); }; } - + // protected override async Task OnParametersSetAsync() { // if (RoomInfo != null) { // if (!_hooked) { @@ -127,21 +141,24 @@ else { protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - hs ??= await RMUStorage.GetCurrentSessionOrNavigate(); - if (hs is null) return; + // hs ??= await sessionStore.GetCurrentHomeserver(navigateOnFailure: true); + // if (hs is null) return; + if (Homeserver is null) { + Console.WriteLine($"RoomListItem called without homeserver"); + } await CheckRoomVersion(); } private async Task LoadOwnProfile() { if (!ShowOwnProfile) return; try { - // OwnMemberState ??= (await RoomInfo.GetStateEvent("m.room.member", hs.UserId)).TypedContent as RoomMemberEventContent; - GlobalProfile ??= await hs.GetProfileAsync(hs.UserId); + // OwnMemberState ??= (await RoomInfo.GetStateEvent("m.room.member", hs.UserId)).TypedContent as RoomMemberEventContent; + GlobalProfile ??= await Homeserver.GetProfileAsync(Homeserver.UserId); } catch (MatrixException e) { if (e is { ErrorCode: "M_FORBIDDEN" }) { - Console.WriteLine($"Failed to get profile for {hs.UserId}: {e.Message}"); + Console.WriteLine($"Failed to get profile for {Homeserver.UserId}: {e.Message}"); ShowOwnProfile = false; } else { @@ -151,8 +168,8 @@ else { } private async Task CheckRoomVersion() { - if (RoomInfo?.CreationEventContent is null) return; - + if (RoomInfo?.CreationEventContent is null) return; + var ce = RoomInfo.CreationEventContent; if (int.TryParse(ce.RoomVersion, out var rv)) { if (rv < 10) @@ -163,7 +180,7 @@ else { if (RoomConstants.DangerousRoomVersions.Contains(ce.RoomVersion)) { HasDangerousRoomVersion = true; - // RoomName = "Dangerous room: " + RoomName; + // RoomName = "Dangerous room: " + RoomName; } } diff --git a/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor
index 08aeffe..f107eb3 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor
@@ -1,5 +1,5 @@ @using LibMatrix -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @using LibMatrix.Responses <h3>BaseTimelineItem</h3> diff --git a/MatrixUtils.Web/Shared/TimelineComponents/TimelineCanonicalAliasItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/TimelineCanonicalAliasItem.razor
index 0488e36..d1984dd 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/TimelineCanonicalAliasItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/TimelineCanonicalAliasItem.razor
@@ -1,5 +1,5 @@ @using ArcaneLibs.Extensions -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @inherits BaseTimelineItem @if (currentEventContent is not null) { diff --git a/MatrixUtils.Web/Shared/TimelineComponents/TimelineHistoryVisibilityItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/TimelineHistoryVisibilityItem.razor
index bdd6104..5d09603 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/TimelineHistoryVisibilityItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/TimelineHistoryVisibilityItem.razor
@@ -1,5 +1,5 @@ @using ArcaneLibs.Extensions -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @inherits BaseTimelineItem @if (currentEventContent is not null) { diff --git a/MatrixUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor
index 3b18b95..e5a5650 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/TimelineMemberItem.razor
@@ -1,5 +1,5 @@ @using ArcaneLibs.Extensions -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @using LibMatrix.Responses @inherits BaseTimelineItem diff --git a/MatrixUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor
index 81956b0..98b5a6d 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/TimelineMessageItem.razor
@@ -15,7 +15,7 @@ } case "m.image": { <i>@currentEventContent.Body</i><br/> - <img src="@Homeserver.ResolveMediaUri(currentEventContent.Url)"> + @* <img src="@Homeserver.ResolveMediaUri(currentEventContent.Url)"> *@ break; } default: { diff --git a/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomCreateItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomCreateItem.razor
index f3e6c7e..aeb987a 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomCreateItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomCreateItem.razor
@@ -1,5 +1,5 @@ @using ArcaneLibs.Extensions -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @inherits BaseTimelineItem <i> diff --git a/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomNameItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomNameItem.razor
index 63594a9..c342c83 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomNameItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomNameItem.razor
@@ -1,5 +1,5 @@ @using ArcaneLibs.Extensions -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @inherits BaseTimelineItem @if (currentEventContent is not null) { diff --git a/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomTopicItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomTopicItem.razor
index f70d563..467c644 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomTopicItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/TimelineRoomTopicItem.razor
@@ -1,5 +1,5 @@ @using ArcaneLibs.Extensions -@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo @inherits BaseTimelineItem @if (currentEventContent is not null) { diff --git a/MatrixUtils.Web/Shared/UserListItem.razor b/MatrixUtils.Web/Shared/UserListItem.razor
index d4652b2..fd2fdec 100644 --- a/MatrixUtils.Web/Shared/UserListItem.razor +++ b/MatrixUtils.Web/Shared/UserListItem.razor
@@ -1,7 +1,12 @@ @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) ? _identiconGenerator.GenerateAsDataUri(UserId) : User.AvatarUrl)"/> + @if (!string.IsNullOrWhiteSpace(User?.AvatarUrl)) { + <MxcAvatar Homeserver="@_homeserver" Size="32" Circular="true" MxcUri="@User.AvatarUrl"/> + } + else { + <img style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height: 32px; border-radius: 50%;" src="@_identiconGenerator.GenerateAsDataUri(UserId)"/> + } <span style="vertical-align: middle; margin-right: 8px; border-radius: 75px;">@User?.DisplayName</span> <div style="display: inline-block;"> @@ -23,20 +28,28 @@ [Parameter] public string UserId { get; set; } - private AuthenticatedHomeserverGeneric _homeserver = null!; + [Parameter] + public AuthenticatedHomeserverGeneric _homeserver { get; set; } - private SvgIdenticonGenerator _identiconGenerator = new(); + private static SvgIdenticonGenerator _identiconGenerator = new(); protected override async Task OnInitializedAsync() { - _homeserver = await RMUStorage.GetCurrentSessionOrNavigate(); - if (_homeserver is null) return; + // _homeserver = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true); + // if (_homeserver is null) return; if (User == null) { if (UserId == null) { throw new ArgumentNullException(nameof(UserId)); } - User = await _homeserver.GetProfileAsync(UserId); + try { + User = await _homeserver.GetProfileAsync(UserId); + } + catch (Exception) { + User = new() { + DisplayName = UserId + }; + } } await base.OnInitializedAsync();