diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor
index 5f9ad8a..48d3196 100644
--- a/MatrixRoomUtils.Web/Shared/NavMenu.razor
+++ b/MatrixRoomUtils.Web/Shared/NavMenu.razor
@@ -23,19 +23,10 @@
<h5 style="margin-left: 1em;">Main tools</h5>
<hr style="margin-bottom: 0em;"/>
</div>
+
<div class="nav-item px-3">
- <NavLink class="nav-link" href="RoomManager">
- <span class="oi oi-plus" aria-hidden="true"></span> Manage Rooms
- </NavLink>
- </div>
- <div class="nav-item px-3">
- <NavLink class="nav-link" href="PolicyListEditor">
- <span class="oi oi-plus" aria-hidden="true"></span> Policy list editor
- </NavLink>
- </div>
- <div class="nav-item px-3">
- <NavLink class="nav-link" href="RoomStateViewer">
- <span class="oi oi-plus" aria-hidden="true"></span> Room state viewer
+ <NavLink class="nav-link" href="Rooms">
+ <span class="oi oi-plus" aria-hidden="true"></span> Room list
</NavLink>
</div>
@* <div class="nav-item px-3"> *@
diff --git a/MatrixRoomUtils.Web/Shared/RoomList.razor b/MatrixRoomUtils.Web/Shared/RoomList.razor
index ac2cbb3..7e002ed 100644
--- a/MatrixRoomUtils.Web/Shared/RoomList.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomList.razor
@@ -1,18 +1,29 @@
@using MatrixRoomUtils.Web.Shared.RoomListComponents;
@using MatrixRoomUtils.Core.StateEventTypes
<p>@Rooms.Count rooms total, @RoomsWithTypes.Sum(x=>x.Value.Count) fetched so far...</p>
-@foreach (var category in RoomsWithTypes.OrderBy(x => x.Value.Count)) {
- <RoomListCategory Category="@category"></RoomListCategory>
+@if(Rooms.Count != RoomsWithTypes.Sum(x=>x.Value.Count)) {
+ <p>Fetching more rooms...</p>
+ @foreach (var category in RoomsWithTypes.OrderBy(x => x.Value.Count)) {
+ <p>@category.Key (@category.Value.Count)</p>
+ }
+}
+else {
+ @foreach (var category in RoomsWithTypes.OrderBy(x => x.Value.Count)) {
+ <RoomListCategory Category="@category" GlobalProfile="@GlobalProfile"></RoomListCategory>
+ }
}
@code {
[Parameter]
public List<GenericRoom> Rooms { get; set; }
+ [Parameter]
+ public ProfileResponse? GlobalProfile { get; set; }
Dictionary<string, List<GenericRoom>> RoomsWithTypes = new();
-
+
protected override async Task OnInitializedAsync() {
+ GlobalProfile ??= await (await MRUStorage.GetCurrentSession()!).GetProfile((await MRUStorage.GetCurrentSession()!).WhoAmI.UserId);
if (RoomsWithTypes.Any()) return;
var tasks = Rooms.Select(AddRoom);
@@ -29,27 +40,32 @@
};
- private static SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(8, 8);
+ private static SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(4, 4);
private async Task AddRoom(GenericRoom room) {
await _semaphoreSlim.WaitAsync();
- var roomType = GetRoomTypeName((await room.GetCreateEventAsync()).Type);
+ string roomType;
+ try {
+ var createEvent = await room.GetCreateEventAsync();
+ roomType = GetRoomTypeName(createEvent.Type);
- if (roomType == "Room") {
- var shortcodeState = await room.GetStateAsync<MjolnirShortcodeEventData>("org.matrix.mjolnir.shortcode");
- if (shortcodeState is not null) roomType = "Legacy policy room";
+ if (roomType == "Room") {
+ var shortcodeState = await room.GetStateAsync<MjolnirShortcodeEventData>("org.matrix.mjolnir.shortcode");
+ if (shortcodeState is not null) roomType = "Legacy policy room";
+ }
+ }
+ catch (MatrixException e) {
+ roomType = $"Error: {e.ErrorCode}";
}
if (!RoomsWithTypes.ContainsKey(roomType)) {
RoomsWithTypes.Add(roomType, new List<GenericRoom>());
}
RoomsWithTypes[roomType].Add(room);
-
+
// if (RoomsWithTypes.Count % 10 == 0)
StateHasChanged();
- await Task.Delay(100);
+ // await Task.Delay(100);
_semaphoreSlim.Release();
}
- private bool _isSpaceChildrenOpen = false;
-
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
index a7e9399..e860321 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
@@ -1,9 +1,12 @@
+@using MatrixRoomUtils.Core.StateEventTypes
<details>
<summary>@roomType (@rooms.Count)</summary>
@foreach (var room in rooms) {
<div class="room-list-item">
<RoomListItem Room="@room" ShowOwnProfile="@(roomType == "Room")"></RoomListItem>
<MatrixRoomUtils.Web.Shared.SimpleComponents.LinkButton href="@($"/Rooms/{room.RoomId}/Timeline")">View timeline</MatrixRoomUtils.Web.Shared.SimpleComponents.LinkButton>
+ <MatrixRoomUtils.Web.Shared.SimpleComponents.LinkButton href="@($"/Rooms/{room.RoomId}/State/View")">View state</MatrixRoomUtils.Web.Shared.SimpleComponents.LinkButton>
+ <MatrixRoomUtils.Web.Shared.SimpleComponents.LinkButton href="@($"/Rooms/{room.RoomId}/State/Edit")">Edit state</MatrixRoomUtils.Web.Shared.SimpleComponents.LinkButton>
@if (roomType == "Space") {
<RoomListSpace Space="@room"></RoomListSpace>
@@ -17,6 +20,9 @@
[Parameter]
public KeyValuePair<string, List<GenericRoom>> Category { get; set; }
+
+ [Parameter]
+ public ProfileResponse? GlobalProfile { get; set; }
private string roomType => Category.Key;
private List<GenericRoom> rooms => Category.Value;
diff --git a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListPolicyRoom.razor b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListPolicyRoom.razor
new file mode 100644
index 0000000..f05ac7b
--- /dev/null
+++ b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListPolicyRoom.razor
@@ -0,0 +1,12 @@
+<LinkButton href="@($"/Rooms/{Room.RoomId}/Policies")">Manage policies</LinkButton>
+
+@code {
+
+ [Parameter]
+ public GenericRoom Room { get; set; }
+
+ protected override async Task OnInitializedAsync() {
+ await base.OnInitializedAsync();
+ }
+
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
index 5d106c3..73dc334 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
@@ -26,7 +26,12 @@
protected override async Task OnInitializedAsync() {
if (Breadcrumbs == null) throw new ArgumentNullException(nameof(Breadcrumbs));
- Children = (await Space.AsSpace.GetRoomsAsync()).Where(x => !Breadcrumbs.Contains(x.RoomId)).ToList();
+ await Task.Delay(Random.Shared.Next(1000, 10000));
+ var rooms = Space.AsSpace.GetRoomsAsync();
+ await foreach (var room in rooms) {
+ if(Breadcrumbs.Contains(room.RoomId)) continue;
+ Children.Add(room);
+ }
await base.OnInitializedAsync();
}
diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor b/MatrixRoomUtils.Web/Shared/RoomListItem.razor
index 53219d6..13cc02d 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListItem.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListItem.razor
@@ -2,15 +2,20 @@
@using System.Text.Json
@using MatrixRoomUtils.Core.Helpers
@using MatrixRoomUtils.Core.StateEventTypes
-<div class="roomListItem" style="background-color: #ffffff11; border-radius: 25px; margin: 8px; width: fit-Content; @(hasDangerousRoomVersion ? "border: red 4px solid;" : hasOldRoomVersion ? "border: #FF0 1px solid;" : "")">
- @if (ShowOwnProfile) {
- <img class="imageUnloaded @(string.IsNullOrWhiteSpace(profileAvatar) ? "" : "imageLoaded")" style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height: 32px; border-radius: 50%; @(hasCustomProfileAvatar ? "border-color: red; border-width: 3px; border-style: dashed;" : "")" src="@(profileAvatar ?? "/icon-192.png")"/>
- <span style="vertical-align: middle; margin-right: 8px; border-radius: 75px; @(hasCustomProfileName ? "background-color: red;" : "")">@(profileName ?? "Loading...")</span>
+<div class="roomListItem" id="@RoomId" style="background-color: #ffffff11; border-radius: 25px; margin: 8px; width: fit-Content; @(hasDangerousRoomVersion ? "border: red 4px solid;" : hasOldRoomVersion ? "border: #FF0 1px solid;" : "")">
+ @if (OwnMemberState != null) {
+ <img class="imageUnloaded @(string.IsNullOrWhiteSpace(OwnMemberState?.AvatarUrl ?? GlobalProfile?.AvatarUrl) ? "" : "imageLoaded")"
+ style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height: 32px; border-radius: 50%; @(OwnMemberState?.AvatarUrl != GlobalProfile?.AvatarUrl ? "border-color: red; border-width: 3px; border-style: dashed;" : "")"
+ src="@MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, OwnMemberState.AvatarUrl ?? GlobalProfile.AvatarUrl ?? "/icon-192.png")"/>
+ <span style="vertical-align: middle; margin-right: 8px; border-radius: 75px; @(OwnMemberState?.AvatarUrl != GlobalProfile?.AvatarUrl ? "background-color: red;" : "")">
+ @(OwnMemberState?.Displayname ?? GlobalProfile?.DisplayName ?? "Loading...")
+ </span>
<span style="vertical-align: middle; padding-right: 8px; padding-left: 0px;">-></span>
}
- <img style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height: 32px; border-radius: 50%;" src="@roomIcon"/>
+ <img style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height: 32px; border-radius: 50%;"
+ src="@roomIcon"/>
<div style="display: inline-block;">
- <span style="vertical-align: middle; padding-right: 8px;">@RoomName</span>
+ <span style="vertical-align: middle; padding-right: 8px;">@roomName</span>
@if (ChildContent is not null) {
@ChildContent
}
@@ -24,91 +29,105 @@
public RenderFragment? ChildContent { get; set; }
[Parameter]
- public GenericRoom Room { get; set; }
+ public GenericRoom? Room { get; set; }
[Parameter]
- public string RoomId { get; set; }
+ public string? RoomId { get; set; }
[Parameter]
public bool ShowOwnProfile { get; set; } = false;
[Parameter]
- public string? RoomName { get; set; }
+ public RoomMemberEventData? OwnMemberState { get; set; }
- private string? roomIcon { get; set; } = "/icon-192.png";
+ [Parameter]
+ public ProfileResponse? GlobalProfile { get; set; }
- private string? profileAvatar { get; set; }
- private string? profileName { get; set; }
- private bool hasCustomProfileAvatar { get; set; } = false;
- private bool hasCustomProfileName { get; set; } = false;
+ private string? roomName { get; set; }
+
+ private string? roomIcon { get; set; } = "/icon-192.png";
private bool hasOldRoomVersion { get; set; } = false;
private bool hasDangerousRoomVersion { get; set; } = false;
- private static SemaphoreSlim _semaphoreSlim = new(128);
+ private static SemaphoreSlim _semaphoreSlim = new(8);
+ private static AuthenticatedHomeServer? hs { get; set; }
+ private static readonly string[] DangerousRoomVersions = { "1", "8" };
protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
await _semaphoreSlim.WaitAsync();
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
+ hs ??= await MRUStorage.GetCurrentSessionOrNavigate();
if (hs is null) return;
- if (Room == null) {
- if (RoomId == null) {
- throw new ArgumentNullException(nameof(RoomId));
- }
- Room = await hs.GetRoom(RoomId);
- }
- else {
- RoomId = Room.RoomId;
+ if (Room is null && RoomId is null) {
+ throw new ArgumentNullException(nameof(RoomId));
}
+ Room ??= await hs.GetRoom(RoomId);
+ RoomId = Room.RoomId;
- RoomName ??= await Room.GetNameAsync() ?? "Unnamed room: " + RoomId;
+ await CheckRoomVersion();
+ await GetRoomInfo();
+ await LoadOwnProfile();
+ _semaphoreSlim.Release();
+ }
- var ce = await Room.GetCreateEventAsync();
- if (ce is not null) {
- if (int.TryParse(ce.RoomVersion, out var rv) && rv < 10) {
- hasOldRoomVersion = true;
+ private async Task LoadOwnProfile() {
+ if (!ShowOwnProfile) return;
+ try {
+ OwnMemberState ??= await Room.GetStateAsync<RoomMemberEventData>("m.room.member", hs.UserId);
+ GlobalProfile ??= await hs.GetProfile(hs.UserId, true);
+ }
+ catch (MatrixException e) {
+ if (e is { ErrorCode: "M_FORBIDDEN" }) {
+ Console.WriteLine($"Failed to get profile for {hs.UserId}: {e.Message}");
+ ShowOwnProfile = false;
}
- if (new[] { "1", "8" }.Contains(ce.RoomVersion)) {
- hasDangerousRoomVersion = true;
- RoomName = "Dangerous room: " + RoomName;
+ else {
+ throw;
}
}
+ }
- var state = await Room.GetStateAsync<RoomAvatarEventData>("m.room.avatar");
- if (state is not null) {
- try {
- var url = state.Url;
- if (url is not null) {
- roomIcon = MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, url);
- Console.WriteLine($"Got avatar for room {RoomId}: {roomIcon} ({url})");
- }
+ private async Task CheckRoomVersion() {
+ try {
+ var ce = await Room.GetCreateEventAsync();
+ if (int.TryParse(ce.RoomVersion, out var rv)) {
+ if (rv < 10)
+ hasOldRoomVersion = true;
}
- catch (InvalidOperationException e) {
- Console.WriteLine($"Failed to get avatar for room {RoomId}: {e.Message}\n{state.ToJson()}");
+ else // treat unstable room versions as dangerous
+ hasDangerousRoomVersion = true;
+
+ if (DangerousRoomVersions.Contains(ce.RoomVersion)) {
+ hasDangerousRoomVersion = true;
+ roomName = "Dangerous room: " + roomName;
}
- catch (Exception e) {
- Console.WriteLine(e);
+ }
+ catch (MatrixException e) {
+ if (e is not { ErrorCode: "M_FORBIDDEN" }) {
+ throw;
}
}
+ }
+
+ private async Task GetRoomInfo() {
+ try {
+ roomName ??= await Room.GetNameAsync();
- if (ShowOwnProfile) {
- var profile = await hs.GetProfile(hs.UserId, true);
-
- var memberState = await Room.GetStateAsync<RoomMemberEventData>("m.room.member", hs.UserId);
- if (memberState is not null) {
-
- hasCustomProfileAvatar = memberState.AvatarUrl != profile.AvatarUrl;
- profileAvatar = MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, memberState.AvatarUrl ?? profile.AvatarUrl ?? "/icon-192.png");
-
- hasCustomProfileName = memberState.Displayname != profile.DisplayName;
- profileName = memberState.Displayname;
+ var state = await Room.GetStateAsync<RoomAvatarEventData>("m.room.avatar");
+ if (state?.Url is { } url) {
+ roomIcon = MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, url);
+ Console.WriteLine($"Got avatar for room {RoomId}: {roomIcon} ({url})");
+ }
+ }
+ catch (MatrixException e) {
+ if (e is not { ErrorCode: "M_FORBIDDEN" }) {
+ throw;
}
}
- _semaphoreSlim.Release();
}
}
\ No newline at end of file
|