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/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/PolicyEditorComponents/MassPolicyEditorModal.razor b/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor
index 11ba18a..b49358d 100644
--- a/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor
+++ b/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor
@@ -5,8 +5,9 @@
@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">
+<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>
@@ -14,25 +15,58 @@
<option value="@type">@mappedType.GetFriendlyName().ToLower()</option>
}
</select><br/>
-
+
<span>Reason:</span>
- <FancyTextBox @bind-Value="@Reason"></FancyTextBox><br/>
-
+ <FancyTextBox @bind-Value="@Reason"></FancyTextBox>
+ <br/>
+
<span>Recommendation:</span>
- <FancyTextBox @bind-Value="@Recommendation"></FancyTextBox><br/>
+ <FancyTextBox @bind-Value="@Recommendation"></FancyTextBox>
+ <br/>
<span>Entities:</span><br/>
- <InputTextArea @bind-Value="@Users" style="width: 500px;"></InputTextArea><br/>
-
-
+ <FancyTextBox Multiline="true" @bind-Value="@Entities"></FancyTextBox>
+ <br/>
+
+
@* <details> *@
@* <summary>JSON data</summary> *@
@* <pre> *@
@* $1$ @PolicyEvent.ToJson(true, true) #1# *@
@* </pre> *@
@* </details> *@
- <LinkButton OnClick="@(() => { OnClose.Invoke(); return Task.CompletedTask; })"> Cancel </LinkButton>
- <LinkButton OnClick="@(() => { _ = Save(); return Task.CompletedTask; })"> Save </LinkButton>
+ @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>
@@ -47,33 +81,118 @@
[Parameter]
public required GenericRoom Room { get; set; }
- public string Recommendation { get; set; } = "m.ban";
- public string Reason { get; set; } = "spam";
- public string Users { get; set; } = "";
+ private string Recommendation { get; set; } = "m.ban";
+ private string Reason { get; set; } = "spam";
- private static FrozenSet<Type> KnownPolicyTypes = StateEvent.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))).ToFrozenSet();
+ private string Entities { get; set; } = "";
+
+ private string? Response {
+ get;
+ set {
+ field = value;
+ StateHasChanged();
+ }
+ }
+
+ private bool VerifyIntent { get; set; }
+
+ private static FrozenSet<Type> KnownPolicyTypes = MatrixEvent.KnownEventTypes.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() {
+ 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 DoActualSave();
+ await SaveAll(entities);
}
catch (Exception e) {
- Console.WriteLine($"Failed to save: {e}");
+ Response = $"Failed to save: {e}";
}
}
- private async Task DoActualSave() {
- Console.WriteLine($"Saving ---");
- Console.WriteLine($"Users = {Users}");
- var users = Users.Split("\n").Select(x => x.Trim()).Where(x => x.StartsWith('@')).ToList();
- var tasks = users.Select(x => ExecuteBan(Room, x)).ToList();
- await Task.WhenAll(tasks);
-
+ 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 MatrixEvent() {
+ Type = MappedType,
+ TypedContent = content,
+ StateKey = content.GetDraupnir2StateKey()
+ };
+ });
+
+ foreach (var chunk in events.Chunk(50))
+ await Room.BulkSendEventsAsync(chunk);
+
OnSaved.Invoke();
}
@@ -81,7 +200,7 @@
bool success = false;
while (!success) {
try {
- var content = Activator.CreateInstance(PolicyTypes[MappedType!]) as PolicyRuleEventContent;
+ 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;
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 5819bee..501ca99 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">
@@ -52,7 +52,12 @@
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>
+ <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) {
@@ -61,13 +66,22 @@
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>
+ <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>
+ <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>
}
}
@@ -88,8 +102,8 @@
@PolicyEvent.ToJson(true, true)
</pre>
</details>
- <LinkButton OnClick="@(() => { OnClose.Invoke(); return Task.CompletedTask; })"> Cancel </LinkButton>
- <LinkButton OnClick="@(() => { OnSave.Invoke(PolicyEvent); return Task.CompletedTask; })"> Save </LinkButton>
+ <LinkButton OnClickAsync="@InvokeOnClose">Cancel</LinkButton>
+ <LinkButton OnClickAsync="@InvokeOnSave">Save</LinkButton>
}
else {
<p>Policy data is null</p>
@@ -99,7 +113,7 @@
@code {
[Parameter]
- public StateEventResponse? PolicyEvent {
+ public MatrixEventResponse? PolicyEvent {
get => _policyEvent;
set {
if (value is not null && value != _policyEvent)
@@ -111,19 +125,41 @@
}
[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<MatrixEventResponse>? OnSave { get; set; }
+
+ [Parameter]
+ public Func<MatrixEventResponse, 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; }
- private static FrozenSet<Type> KnownPolicyTypes = StateEvent.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))).ToFrozenSet();
+ private static FrozenSet<Type> KnownPolicyTypes = MatrixEvent.KnownEventTypes.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 StateEventResponse? _policyEvent;
+ private MatrixEventResponse? _policyEvent;
private string? MappedType {
get => _policyEvent?.Type;
diff --git a/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor b/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
index 27f0499..471f586 100644
--- a/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
+++ b/MatrixUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
@@ -45,7 +45,7 @@
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 rooms = Space.Room.AsSpace().GetChildrenAsync();
var joinedRooms = await Homeserver.GetJoinedRooms();
await foreach (var room in rooms) {
if (Breadcrumbs.Contains(room.RoomId)) continue;
diff --git a/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor
index f107eb3..80c69f2 100644
--- a/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor
+++ b/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor
@@ -6,19 +6,19 @@
@code {
[Parameter]
- public StateEventResponse Event { get; set; }
+ public MatrixEventResponse Event { get; set; }
[Parameter]
- public List<StateEventResponse> Events { get; set; }
+ public List<MatrixEventResponse> Events { get; set; }
[Parameter]
public AuthenticatedHomeserverGeneric Homeserver { get; set; }
- public IEnumerable<StateEventResponse> EventsBefore => Events.TakeWhile(e => e.EventId != Event.EventId);
+ public IEnumerable<MatrixEventResponse> EventsBefore => Events.TakeWhile(e => e.EventId != Event.EventId);
- public IEnumerable<StateEventResponse> MatchingEventsBefore => EventsBefore.Where(x => x.Type == Event.Type && x.StateKey == Event.StateKey);
+ public IEnumerable<MatrixEventResponse> MatchingEventsBefore => EventsBefore.Where(x => x.Type == Event.Type && x.StateKey == Event.StateKey);
- public StateEventResponse? PreviousState => MatchingEventsBefore.LastOrDefault();
+ public MatrixEventResponse? PreviousState => MatchingEventsBefore.LastOrDefault();
public RoomMemberEventContent? CurrentSenderMemberEventContent => EventsBefore.LastOrDefault(x => x.Type == "m.room.member" && x.StateKey == Event.Sender)?
.TypedContent as RoomMemberEventContent;
@@ -27,6 +27,4 @@
public bool HasPreviousMessage => EventsBefore.Last() is { Type: "m.room.message" } response && response.Sender == Event.Sender;
-
-
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Shared/UserListItem.razor b/MatrixUtils.Web/Shared/UserListItem.razor
index 5084807..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;">
@@ -26,7 +31,7 @@
[Parameter]
public AuthenticatedHomeserverGeneric _homeserver { get; set; }
- private SvgIdenticonGenerator _identiconGenerator = new();
+ private static SvgIdenticonGenerator _identiconGenerator = new();
protected override async Task OnInitializedAsync() {
// _homeserver = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true);
@@ -37,7 +42,14 @@
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();
|