diff --git a/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs b/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs
index c267298..66f3c4d 100644
--- a/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs
+++ b/MatrixRoomUtils.Bot/Bot/Commands/CmdCommand.cs
@@ -39,7 +39,7 @@ public class CmdCommand : ICommand {
if ((output.Count > 0 && (msg + output[0]).Length > 64000) || output.Count == 0) {
await ctx.Room.SendMessageEventAsync("m.room.message", new() {
FormattedBody = $"```ansi\n{msg}\n```",
- Body = Markdig.Markdown.ToHtml(msg),
+ // Body = Markdig.Markdown.ToHtml(msg),
Format = "org.matrix.custom.html"
});
msg = "";
diff --git a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj
index 7012647..5eba4fd 100644
--- a/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj
+++ b/MatrixRoomUtils.Bot/MatrixRoomUtils.Bot.csproj
@@ -22,7 +22,6 @@
<ItemGroup>
<PackageReference Include="ArcaneLibs" Version="1.0.0-preview3020494760.012ed3f" />
- <PackageReference Include="Markdig" Version="0.31.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0-preview.5.23280.8" />
</ItemGroup>
<ItemGroup>
diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
index 852e1d8..060867d 100644
--- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
@@ -1,3 +1,4 @@
+using System.Net.Http.Headers;
using System.Reflection;
using System.Text.Json;
@@ -19,6 +20,7 @@ public static class HttpClientExtensions {
public class MatrixHttpClient : HttpClient {
public override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
+ Console.WriteLine($"Sending request to {request.RequestUri}");
try
{
HttpRequestOptionsKey<bool> WebAssemblyEnableStreamingResponseKey = new HttpRequestOptionsKey<bool>("WebAssemblyEnableStreamingResponse");
@@ -35,10 +37,10 @@ public class MatrixHttpClient : HttpClient {
}
var a = await base.SendAsync(request, cancellationToken);
if (!a.IsSuccessStatusCode) {
- Console.WriteLine($"Failed to send request: {a.StatusCode}");
var content = await a.Content.ReadAsStringAsync(cancellationToken);
if (content.StartsWith('{')) {
var ex = JsonSerializer.Deserialize<MatrixException>(content);
+ Console.WriteLine($"Failed to send request: {ex}");
if (ex?.RetryAfterMs is not null) {
await Task.Delay(ex.RetryAfterMs.Value, cancellationToken);
typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(request, 0);
@@ -50,4 +52,13 @@ public class MatrixHttpClient : HttpClient {
}
return a;
}
+ // GetFromJsonAsync
+ public async Task<T> GetFromJsonAsync<T>(string requestUri, CancellationToken cancellationToken = default) {
+ var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
+ request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+ var response = await SendAsync(request, cancellationToken);
+ response.EnsureSuccessStatusCode();
+ await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
+ return await JsonSerializer.DeserializeAsync<T>(responseStream, cancellationToken: cancellationToken);
+ }
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/MatrixException.cs b/MatrixRoomUtils.Core/MatrixException.cs
index 50fae20..4795d6d 100644
--- a/MatrixRoomUtils.Core/MatrixException.cs
+++ b/MatrixRoomUtils.Core/MatrixException.cs
@@ -17,41 +17,41 @@ public class MatrixException : Exception {
public int? RetryAfterMs { get; set; }
public override string Message =>
- ErrorCode switch {
+ $"{ErrorCode}: {ErrorCode switch {
// common
- "M_FORBIDDEN" => "You do not have permission to perform this action: " + Error,
- "M_UNKNOWN_TOKEN" => "The access token specified was not recognised: " + Error + (SoftLogout == true ? " (soft logout)" : ""),
- "M_MISSING_TOKEN" => "No access token was specified: " + Error,
- "M_BAD_JSON" => "Request contained valid JSON, but it was malformed in some way: " + Error,
- "M_NOT_JSON" => "Request did not contain valid JSON: " + Error,
- "M_NOT_FOUND" => "The requested resource was not found: " + Error,
- "M_LIMIT_EXCEEDED" => "Too many requests have been sent in a short period of time. Wait a while then try again: " + Error,
- "M_UNRECOGNISED" => "The server did not recognise the request: " + Error,
- "M_UNKOWN" => "The server encountered an unexpected error: " + Error,
+ "M_FORBIDDEN" => $"You do not have permission to perform this action: {Error}",
+ "M_UNKNOWN_TOKEN" => $"The access token specified was not recognised: {Error}{(SoftLogout == true ? " (soft logout)" : "")}",
+ "M_MISSING_TOKEN" => $"No access token was specified: {Error}",
+ "M_BAD_JSON" => $"Request contained valid JSON, but it was malformed in some way: {Error}",
+ "M_NOT_JSON" => $"Request did not contain valid JSON: {Error}",
+ "M_NOT_FOUND" => $"The requested resource was not found: {Error}",
+ "M_LIMIT_EXCEEDED" => $"Too many requests have been sent in a short period of time. Wait a while then try again: {Error}",
+ "M_UNRECOGNISED" => $"The server did not recognise the request: {Error}",
+ "M_UNKOWN" => $"The server encountered an unexpected error: {Error}",
// endpoint specific
- "M_UNAUTHORIZED" => "The request did not contain valid authentication information for the target of the request: " + Error,
- "M_USER_DEACTIVATED" => "The user ID associated with the request has been deactivated: " + Error,
- "M_USER_IN_USE" => "The user ID associated with the request is already in use: " + Error,
- "M_INVALID_USERNAME" => "The requested user ID is not valid: " + Error,
- "M_ROOM_IN_USE" => "The room alias requested is already taken: " + Error,
- "M_INVALID_ROOM_STATE" => "The room associated with the request is not in a valid state to perform the request: " + Error,
- "M_THREEPID_IN_USE" => "The threepid requested is already associated with a user ID on this server: " + Error,
- "M_THREEPID_NOT_FOUND" => "The threepid requested is not associated with any user ID: " + Error,
- "M_THREEPID_AUTH_FAILED" => "The provided threepid and/or token was invalid: " + Error,
- "M_THREEPID_DENIED" => "The homeserver does not permit the third party identifier in question: " + Error,
- "M_SERVER_NOT_TRUSTED" => "The homeserver does not trust the identity server: " + Error,
- "M_UNSUPPORTED_ROOM_VERSION" => "The room version is not supported: " + Error,
- "M_INCOMPATIBLE_ROOM_VERSION" => "The room version is incompatible: " + Error,
- "M_BAD_STATE" => "The request was invalid because the state was invalid: " + Error,
- "M_GUEST_ACCESS_FORBIDDEN" => "Guest access is forbidden: " + Error,
- "M_CAPTCHA_NEEDED" => "Captcha needed: " + Error,
- "M_CAPTCHA_INVALID" => "Captcha invalid: " + Error,
- "M_MISSING_PARAM" => "Missing parameter: " + Error,
- "M_INVALID_PARAM" => "Invalid parameter: " + Error,
- "M_TOO_LARGE" => "The request or entity was too large: " + Error,
- "M_EXCLUSIVE" => "The resource being requested is reserved by an application service, or the application service making the request has not created the resource: " + Error,
- "M_RESOURCE_LIMIT_EXCEEDED" => "Exceeded resource limit: " + Error,
- "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => "Cannot leave server notice room: " + Error,
- _ => "Unknown error: " + new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)
- };
+ "M_UNAUTHORIZED" => $"The request did not contain valid authentication information for the target of the request: {Error}",
+ "M_USER_DEACTIVATED" => $"The user ID associated with the request has been deactivated: {Error}",
+ "M_USER_IN_USE" => $"The user ID associated with the request is already in use: {Error}",
+ "M_INVALID_USERNAME" => $"The requested user ID is not valid: {Error}",
+ "M_ROOM_IN_USE" => $"The room alias requested is already taken: {Error}",
+ "M_INVALID_ROOM_STATE" => $"The room associated with the request is not in a valid state to perform the request: {Error}",
+ "M_THREEPID_IN_USE" => $"The threepid requested is already associated with a user ID on this server: {Error}",
+ "M_THREEPID_NOT_FOUND" => $"The threepid requested is not associated with any user ID: {Error}",
+ "M_THREEPID_AUTH_FAILED" => $"The provided threepid and/or token was invalid: {Error}",
+ "M_THREEPID_DENIED" => $"The homeserver does not permit the third party identifier in question: {Error}",
+ "M_SERVER_NOT_TRUSTED" => $"The homeserver does not trust the identity server: {Error}",
+ "M_UNSUPPORTED_ROOM_VERSION" => $"The room version is not supported: {Error}",
+ "M_INCOMPATIBLE_ROOM_VERSION" => $"The room version is incompatible: {Error}",
+ "M_BAD_STATE" => $"The request was invalid because the state was invalid: {Error}",
+ "M_GUEST_ACCESS_FORBIDDEN" => $"Guest access is forbidden: {Error}",
+ "M_CAPTCHA_NEEDED" => $"Captcha needed: {Error}",
+ "M_CAPTCHA_INVALID" => $"Captcha invalid: {Error}",
+ "M_MISSING_PARAM" => $"Missing parameter: {Error}",
+ "M_INVALID_PARAM" => $"Invalid parameter: {Error}",
+ "M_TOO_LARGE" => $"The request or entity was too large: {Error}",
+ "M_EXCLUSIVE" => $"The resource being requested is reserved by an application service, or the application service making the request has not created the resource: {Error}",
+ "M_RESOURCE_LIMIT_EXCEEDED" => $"Exceeded resource limit: {Error}",
+ "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => $"Cannot leave server notice room: {Error}",
+ _ => $"Unknown error: {new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)}"
+ }}";
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs
index f57c855..879ae6b 100644
--- a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs
+++ b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs
@@ -44,7 +44,17 @@ public class GenericRoom {
var url = $"/_matrix/client/v3/rooms/{RoomId}/state";
if (!string.IsNullOrEmpty(type)) url += $"/{type}";
if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}";
- return await _httpClient.GetFromJsonAsync<T>(url);
+ try {
+ var resp = await _httpClient.GetFromJsonAsync<T>(url);
+ return resp;
+ }
+ catch (MatrixException e) {
+ if (e is not { ErrorCode: "M_NOT_FOUND" }) {
+ throw;
+ }
+ Console.WriteLine(e);
+ return default;
+ }
}
public async Task<MessagesResponse> GetMessagesAsync(string from = "", int limit = 10, string dir = "b",
@@ -56,8 +66,13 @@ public class GenericRoom {
}
public async Task<string> GetNameAsync() {
- var res = await GetStateAsync<RoomNameEventData>("m.room.name");
- return res.Name ?? RoomId;
+ try {
+ var res = await GetStateAsync<RoomNameEventData>("m.room.name");
+ return res?.Name ?? RoomId;
+ }
+ catch (MatrixException e) {
+ return $"{RoomId} ({e.ErrorCode})";
+ }
}
public async Task JoinAsync(string[]? homeservers = null, string? reason = null) {
diff --git a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs
index 3be3130..1b93064 100644
--- a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs
+++ b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs
@@ -13,15 +13,16 @@ public class SpaceRoom : GenericRoom {
_homeServer = homeServer;
}
- public async Task<List<GenericRoom>> GetRoomsAsync(bool includeRemoved = false) {
+ private static SemaphoreSlim _semaphore = new(1, 1);
+ public async IAsyncEnumerable<GenericRoom> GetRoomsAsync(bool includeRemoved = false) {
+ await _semaphore.WaitAsync();
var rooms = new List<GenericRoom>();
- var state = GetFullStateAsync().ToBlockingEnumerable().ToList();
- var childStates = state.Where(x => x.Type == "m.space.child");
- foreach (var stateEvent in childStates) {
- if (stateEvent.TypedContent.ToJson() != "{}" || includeRemoved)
- rooms.Add(await _homeServer.GetRoom(stateEvent.StateKey));
+ var state = GetFullStateAsync();
+ await foreach (var stateEvent in state) {
+ if (stateEvent.Type != "m.space.child") continue;
+ if (stateEvent.RawContent.ToJson() != "{}" || includeRemoved)
+ yield return await _homeServer.GetRoom(stateEvent.StateKey);
}
-
- return rooms;
+ _semaphore.Release();
}
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs
index 870e0d4..b2ea987 100644
--- a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs
+++ b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs
@@ -30,9 +30,9 @@ public class HomeserverProviderService {
await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver);
hs._httpClient.Dispose();
hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) };
- hs._httpClient.Timeout = TimeSpan.FromSeconds(5);
+ hs._httpClient.Timeout = TimeSpan.FromSeconds(120);
hs._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
-
+
hs.WhoAmI = (await hs._httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"))!;
return hs;
}
@@ -43,7 +43,7 @@ public class HomeserverProviderService {
await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver);
hs._httpClient.Dispose();
hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) };
- hs._httpClient.Timeout = TimeSpan.FromSeconds(5);
+ hs._httpClient.Timeout = TimeSpan.FromSeconds(120);
return hs;
}
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
index b2d28f6..8b2ff0c 100644
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor
@@ -1,4 +1,4 @@
-@page "/RoomStateViewer/{RoomId}/Edit"
+@page "/Rooms/{RoomId}/State/Edit"
@using System.Net.Http.Headers
@using System.Text.Json
@using MatrixRoomUtils.Core.Responses
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor
deleted file mode 100644
index 55c44d9..0000000
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor
+++ /dev/null
@@ -1,32 +0,0 @@
-@page "/RoomStateViewer"
-@inject ILocalStorageService LocalStorage
-@inject NavigationManager NavigationManager
-<h3>Room state viewer - Room list</h3>
-<hr/>
-@if (Rooms.Count == 0) {
- <p>You are not in any rooms!</p>
- @* <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> *@
-}
-else {
- @foreach (var room in Rooms) {
- <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.', '~')">
- <RoomListItem RoomId="@room"></RoomListItem>
- </a>
- }
- <div style="margin-bottom: 4em;"></div>
-}
-
-<LogView></LogView>
-
-@code {
- public List<string> Rooms { get; set; } = new();
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- Rooms = (await hs.GetJoinedRooms()).Select(x => x.RoomId).ToList();
- Console.WriteLine("Fetched joined rooms!");
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
index a0072ab..09b38f0 100644
--- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
+++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor
@@ -1,4 +1,4 @@
-@page "/RoomStateViewer/{RoomId}"
+@page "/Rooms/{RoomId}/State/View"
@using System.Net.Http.Headers
@using System.Text.Json
@using MatrixRoomUtils.Core.Responses
@@ -73,7 +73,6 @@
await base.OnInitializedAsync();
var hs = await MRUStorage.GetCurrentSessionOrNavigate();
if (hs is null) return;
- RoomId = RoomId.Replace('~', '.');
await LoadStatesAsync();
Console.WriteLine("Policy list editor initialized!");
}
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
index 20ddd0d..932748d 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
@@ -1,18 +1,21 @@
@page "/Rooms"
+@using MatrixRoomUtils.Core.StateEventTypes
<h3>Room list</h3>
@if (Rooms is not null) {
- <RoomList Rooms="Rooms"></RoomList>
+ <RoomList Rooms="Rooms" GlobalProfile="@GlobalProfile"></RoomList>
}
@code {
private List<GenericRoom> Rooms { get; set; }
+ private ProfileResponse GlobalProfile { get; set; }
protected override async Task OnInitializedAsync() {
var hs = await MRUStorage.GetCurrentSessionOrNavigate();
if (hs is null) return;
+ GlobalProfile = await hs.GetProfile(hs.WhoAmI.UserId);
Rooms = await hs.GetJoinedRooms();
await base.OnInitializedAsync();
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
|