MatrixRoomUtils

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | LICENSE

commit dc3201d641a03e051c6f0db07612eb6b0bb506c3
parent 5132155714d3953a4a4fb0eba7fe2febe7e5e564
Author: TheArcaneBrony <myrainbowdash949@gmail.com>
Date:   Thu,  4 May 2023 15:26:17 +0200

Dark theme, fancier room list

Diffstat:
MMatrixRoomUtils.Core/AuthenticatedHomeServer.cs | 1-
MMatrixRoomUtils.Core/Extensions/StringExtensions.cs | 2--
MMatrixRoomUtils.Core/RemoteHomeServer.cs | 2--
MMatrixRoomUtils.Core/Responses/LoginResponse.cs | 1-
MMatrixRoomUtils.Core/Room.cs | 10+++-------
MMatrixRoomUtils.Web/App.razor | 3+--
MMatrixRoomUtils.Web/Pages/DataExportPage.razor | 2--
MMatrixRoomUtils.Web/Pages/Index.razor | 1-
MMatrixRoomUtils.Web/Pages/LoginPage.razor | 1-
AMatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AMatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DMatrixRoomUtils.Web/Pages/PolicyListEditorPage.razor | 230-------------------------------------------------------------------------------
DMatrixRoomUtils.Web/Pages/PolicyListRoomList.razor | 162-------------------------------------------------------------------------------
AMatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AMatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor | 37+++++++++++++++++++++++++++++++++++++
AMatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DMatrixRoomUtils.Web/Pages/RoomStateEditorPage.razor | 172-------------------------------------------------------------------------------
DMatrixRoomUtils.Web/Pages/RoomStateRoomList.razor | 97-------------------------------------------------------------------------------
DMatrixRoomUtils.Web/Pages/RoomStateViewerPage.razor | 171-------------------------------------------------------------------------------
MMatrixRoomUtils.Web/Pages/UserImportPage.razor | 1-
MMatrixRoomUtils.Web/Program.cs | 2+-
AMatrixRoomUtils.Web/Shared/RoomListItem.razor | 46++++++++++++++++++++++++++++++++++++++++++++++
MMatrixRoomUtils.Web/wwwroot/css/app.css | 11+++++++++++
23 files changed, 828 insertions(+), 853 deletions(-)

diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs @@ -1,7 +1,6 @@ using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; namespace MatrixRoomUtils.Core; diff --git a/MatrixRoomUtils.Core/Extensions/StringExtensions.cs b/MatrixRoomUtils.Core/Extensions/StringExtensions.cs @@ -1,5 +1,3 @@ -using MatrixRoomUtils.Core.Authentication; - namespace MatrixRoomUtils.Core.Extensions; public static class StringExtensions diff --git a/MatrixRoomUtils.Core/RemoteHomeServer.cs b/MatrixRoomUtils.Core/RemoteHomeServer.cs @@ -1,7 +1,5 @@ -using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; using MatrixRoomUtils.Core.Responses; diff --git a/MatrixRoomUtils.Core/Responses/LoginResponse.cs b/MatrixRoomUtils.Core/Responses/LoginResponse.cs @@ -1,7 +1,6 @@ using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; -using MatrixRoomUtils.Core.Authentication; namespace MatrixRoomUtils.Core.Responses; diff --git a/MatrixRoomUtils.Core/Room.cs b/MatrixRoomUtils.Core/Room.cs @@ -14,9 +14,8 @@ public class Room RoomId = roomId; } - public async Task<JsonElement?> GetStateAsync(string type, string state_key="") + public async Task<JsonElement?> GetStateAsync(string type, string state_key="", bool logOnFailure = false) { - Console.WriteLine($"{RoomId}::_qry[{type}::{state_key}]"); var url = $"/_matrix/client/r0/rooms/{RoomId}/state"; if (!string.IsNullOrEmpty(state_key)) url += $"/{type}/{state_key}"; else if (!string.IsNullOrEmpty(type)) url += $"/{type}"; @@ -24,21 +23,18 @@ public class Room var res = await _httpClient.GetAsync(url); if (!res.IsSuccessStatusCode) { - Console.WriteLine($"{RoomId}::_qry[{type}::{state_key}]->status=={res.StatusCode}"); + if(logOnFailure) Console.WriteLine($"{RoomId}/{state_key}/{type} - got status: {res.StatusCode}"); return null; } return await res.Content.ReadFromJsonAsync<JsonElement>(); } public async Task<string?> GetNameAsync() - { - Console.WriteLine($"{RoomId}::_qry_name"); + { var res = await GetStateAsync("m.room.name"); if (!res.HasValue) { - Console.WriteLine($"{RoomId}::_qry_name->null"); return null; } - Console.WriteLine($"{RoomId}::_qry_name->{res.Value.ToString()}"); var resn = res?.TryGetProperty("name", out var name) ?? false ? name.GetString() : null; Console.WriteLine($"Got name: {resn}"); return resn; diff --git a/MatrixRoomUtils.Web/App.razor b/MatrixRoomUtils.Web/App.razor @@ -1,5 +1,4 @@ -@using MatrixRoomUtils.Core -<Router AppAssembly="@typeof(App).Assembly"> +<Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/> <FocusOnNavigate RouteData="@routeData" Selector="h1"/> diff --git a/MatrixRoomUtils.Web/Pages/DataExportPage.razor b/MatrixRoomUtils.Web/Pages/DataExportPage.razor @@ -1,8 +1,6 @@ @page "/export" @using MatrixRoomUtils.Web.Shared.IndexComponents @using System.Text.Json -@using MatrixRoomUtils.Core -@using MatrixRoomUtils.Core.Authentication @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor @@ -1,6 +1,5 @@ @page "/" @using MatrixRoomUtils.Web.Shared.IndexComponents -@using MatrixRoomUtils.Core @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage diff --git a/MatrixRoomUtils.Web/Pages/LoginPage.razor b/MatrixRoomUtils.Web/Pages/LoginPage.razor @@ -1,5 +1,4 @@ @page "/Login" -@using MatrixRoomUtils.Core @using MatrixRoomUtils.Core.Authentication @inject ILocalStorageService LocalStorage <h3>Login</h3> diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor @@ -0,0 +1,228 @@ +@page "/PolicyListEditor/{RoomId}" +@using System.Text.Json +@using MatrixRoomUtils.Core.Extensions +@using MatrixRoomUtils.Core.StateEventTypes +@inject ILocalStorageService LocalStorage +@inject NavigationManager NavigationManager +<h3>Policy list editor - Editing @RoomId</h3> +<hr/> + +<p> + This policy list contains @PolicyEvents.Count(x => x.type == "m.policy.rule.server") server bans, + @PolicyEvents.Count(x => x.type == "m.policy.rule.room") room bans and + @PolicyEvents.Count(x => x.type == "m.policy.rule.user") user bans. +</p> + + +@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.server")) +{ + <p>No server policies</p> +} +else +{ + <h3>Server policies</h3> + <hr/> + <table class="table table-striped table-hover" style="width: fit-content;"> + <thead> + <tr> + <th scope="col" style="max-width: 50vw;">Server</th> + <th scope="col">Reason</th> + <th scope="col">Expires</th> + <th scope="col">Actions</th> + </tr> + </thead> + <tbody> + @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.server" && x.content.Entity != null)) + { + <tr> + <td>Entity: @policyEvent.content.Entity<br/>State: @policyEvent.state_key</td> + <td>@policyEvent.content.Reason</td> + <td> + @policyEvent.content.ExpiryDateTime + </td> + <td> + <button class="btn btn-danger" @* @onclick="async () => await RemovePolicyAsync(policyEvent)" *@>Remove</button> + </td> + </tr> + } + </tbody> + </table> + <details> + <summary>Invalid events</summary> + <table class="table table-striped table-hover" style="width: fit-content;"> + <thead> + <tr> + <th scope="col" style="max-width: 50vw;">State key</th> + <th scope="col">Serialised contents</th> + </tr> + </thead> + <tbody> + @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.server" && x.content.Entity == null)) + { + <tr> + <td>@policyEvent.state_key</td> + <td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td> + </tr> + } + </tbody> + </table> + </details> +} +@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.room")) +{ + <p>No room policies</p> +} +else +{ + <h3>Room policies</h3> + <hr/> + <table class="table table-striped table-hover" style="width: fit-content;"> + <thead> + <tr> + <th scope="col" style="max-width: 50vw;">Room</th> + <th scope="col">Reason</th> + <th scope="col">Expires</th> + <th scope="col">Actions</th> + </tr> + </thead> + <tbody> + @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.room" && x.content.Entity != null)) + { + <tr> + <td>Entity: @policyEvent.content.Entity<br/>State: @policyEvent.state_key</td> + <td>@policyEvent.content.Reason</td> + <td> + @policyEvent.content.ExpiryDateTime + </td> + <td> + <button class="btn btn-danger" @* @onclick="async () => await RemovePolicyAsync(policyEvent)" *@>Remove</button> + </td> + </tr> + } + </tbody> + </table> + <details> + <summary>Invalid events</summary> + <table class="table table-striped table-hover" style="width: fit-content;"> + <thead> + <tr> + <th scope="col" style="max-width: 50vw;">State key</th> + <th scope="col">Serialised contents</th> + </tr> + </thead> + <tbody> + @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.room" && x.content.Entity == null)) + { + <tr> + <td>@policyEvent.state_key</td> + <td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td> + </tr> + } + </tbody> + </table> + </details> +} +@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.user")) +{ + <p>No user policies</p> +} +else +{ + <h3>User policies</h3> + <hr/> + <table class="table table-striped table-hover" style="width: fit-content;"> + <thead> + <tr> + <th scope="col" style="max-width: 0.2vw; word-wrap: anywhere;">User</th> + <th scope="col">Reason</th> + <th scope="col">Expires</th> + <th scope="col">Actions</th> + </tr> + </thead> + <tbody> + @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.user" && x.content.Entity != null)) + { + <tr> + <td style="word-wrap: anywhere;">Entity: @string.Join("", policyEvent.content.Entity.Take(64))<br/>State: @string.Join("", policyEvent.state_key.Take(64))</td> + <td>@policyEvent.content.Reason</td> + <td> + @policyEvent.content.ExpiryDateTime + </td> + <td> + <button class="btn btn-danger" @* @onclick="async () => await RemovePolicyAsync(policyEvent)" *@>Remove</button> + </td> + </tr> + } + </tbody> + </table> + <details> + <summary>Invalid events</summary> + <table class="table table-striped table-hover" style="width: fit-content;"> + <thead> + <tr> + <th scope="col">State key</th> + <th scope="col">Serialised contents</th> + </tr> + </thead> + <tbody> + @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.user" && x.content.Entity == null)) + { + <tr> + <td>@policyEvent.state_key</td> + <td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td> + </tr> + } + </tbody> + </table> + </details> +} + +<LogView></LogView> + +@code { + //get room list + // - sync withroom list filter + // type = support.feline.msc3784 + //support.feline.policy.lists.msc.v1 + + [Parameter] + public string? RoomId { get; set; } + + public List<StateEvent<PolicyRuleStateEventData>> PolicyEvents { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + await base.OnInitializedAsync(); + // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null) + if (RuntimeCache.CurrentHomeServer == null) + { + NavigationManager.NavigateTo("/Login"); + return; + } + RoomId = RoomId.Replace('~', '.'); + await LoadStatesAsync(); + Console.WriteLine("Policy list editor initialized!"); + } + + private async Task LoadStatesAsync() + { + // using var client = new HttpClient(); + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); + // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/r0/rooms/{RoomId}/state"); + // var content = await response.Content.ReadAsStringAsync(); + // Console.WriteLine(JsonSerializer.Deserialize<object>(content).ToJson()); + // var stateEvents = JsonSerializer.Deserialize<List<StateEvent>>(content); + var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId); + var stateEventsQuery = await room.GetStateAsync(""); + if (stateEventsQuery == null) + { + Console.WriteLine("state events query is null!!!"); + } + var stateEvents = stateEventsQuery.Value.Deserialize<List<StateEvent>>(); + PolicyEvents = stateEvents.Where(x => x.type.StartsWith("m.policy.rule")) + .Select(x => JsonSerializer.Deserialize<StateEvent<PolicyRuleStateEventData>>(JsonSerializer.Serialize(x))).ToList(); + StateHasChanged(); + } + +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor @@ -0,0 +1,158 @@ +@page "/PolicyListEditor" +@using System.Text.Json +@using MatrixRoomUtils.Core.Extensions +@inject ILocalStorageService LocalStorage +@inject NavigationManager NavigationManager +<h3>Policy list editor - Room list</h3> +<hr/> + +@if (PolicyRoomList.Count == 0) +{ + <p>No policy rooms found.</p> + <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> +} +else +{ + @if (checkedRoomCount != totalRoomCount) + { + <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> + } + foreach (var s in PolicyRoomList) + { + <a href="@(NavigationManager.Uri + "/" + s.RoomId.Replace('.', '~'))">[@s.Shortcode] @s.Name (@s.RoomId)</a> + <br/> + } +} + +<div style="margin-bottom: 4em;"></div> +<LogView></LogView> + +@code { + //get room list + // - sync withroom list filter + // type = support.feline.msc3784 + //support.feline.policy.lists.msc.v1 + + public List<PolicyRoomInfo> PolicyRoomList { get; set; } = new(); + + private int checkedRoomCount { get; set; } = 0; + private int totalRoomCount { get; set; } = 0; + + protected override async Task OnInitializedAsync() + { + if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + await base.OnInitializedAsync(); + if (RuntimeCache.CurrentHomeServer == null) + { + NavigationManager.NavigateTo("/Login"); + return; + } + await EnumeratePolicyRooms(); + Console.WriteLine("Policy list editor initialized!"); + } + + private async Task EnumeratePolicyRooms() + { + var xxxrooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); + totalRoomCount = xxxrooms.Count; + StateHasChanged(); + + var xxxsemaphore = new SemaphoreSlim(256); + var xxxtasks = new List<Task<PolicyRoomInfo?>>(); + foreach (var room in xxxrooms) + { + xxxtasks.Add(GetPolicyRoomInfo(room.RoomId, xxxsemaphore)); + } + var xxxresults = await Task.WhenAll(xxxtasks); + PolicyRoomList.AddRange(xxxresults.Where(x => x != null).Select(x => x.Value)); + + Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}"); + return; + /* + using HttpClient wc = new(); + wc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); + + + + //get room list + //temporary hack until rooms get enumerated... + string[] rooms = { "!fTjMjIzNKEsFlUIiru:neko.dev" }; + var _rooms = await wc.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/v3/joined_rooms"); + Console.WriteLine($"Got {_rooms.StatusCode}..."); + if (!_rooms.IsSuccessStatusCode) + { + Console.WriteLine($"Failed to get rooms: {await _rooms.Content.ReadAsStringAsync()}"); + return; + } + var _rooms_o = await _rooms.Content.ReadFromJsonAsync<JsonElement>(); + if (_rooms_o.TryGetProperty("joined_rooms", out JsonElement _rooms_j)) + { + rooms = _rooms_j.EnumerateArray().Select(x => x.GetString()).ToArray(); + } + + totalRoomCount = rooms.Length; + StateHasChanged(); + + var semaphore = new SemaphoreSlim(256); + var tasks = new List<Task<PolicyRoomInfo?>>(); + foreach (string room in rooms) + { + tasks.Add(GetPolicyRoomInfo(room, semaphore)); + } + var results = await Task.WhenAll(tasks); + PolicyRoomList.AddRange(results.Where(x => x != null).Select(x => x.Value)); + + + //print to console + Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}"); + */ + } + + private async Task<PolicyRoomInfo?> GetPolicyRoomInfo(string room, SemaphoreSlim semaphore) + { + try + { + //TODO: refactor!!!!! + await semaphore.WaitAsync(); + PolicyRoomInfo roomInfo = new() + { + RoomId = room + }; + + + // --- // + var r = await RuntimeCache.CurrentHomeServer.GetRoom(room); + var shortcodeState = await r.GetStateAsync("org.matrix.mjolnir.shortcode"); + if(!shortcodeState.HasValue) return null; + roomInfo.Shortcode = shortcodeState.Value.TryGetProperty("shortcode", out JsonElement shortcode) ? shortcode.GetString() : null; + + if (roomInfo.Shortcode != null) + { + roomInfo.Name = await r.GetNameAsync(); + return roomInfo; + } + + return null; + } + finally + + { + checkedRoomCount++; + StateHasChanged(); + semaphore.Release(); + } + } + + public struct PolicyRoomInfo + + { + public + string RoomId { get; set; } + + public + string? Shortcode { get; set; } + + public + string? Name { get; set; } + } + } +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/PolicyListEditorPage.razor b/MatrixRoomUtils.Web/Pages/PolicyListEditorPage.razor @@ -1,229 +0,0 @@ -@page "/PolicyListEditor/{RoomId}" -@using System.Net.Http.Headers -@using System.Text.Json -@using MatrixRoomUtils.Core -@using MatrixRoomUtils.Core.Extensions -@using MatrixRoomUtils.Core.StateEventTypes -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager -<h3>Policy list editor</h3> - -<p> - This policy list contains @PolicyEvents.Count(x => x.type == "m.policy.rule.server") server bans, - @PolicyEvents.Count(x => x.type == "m.policy.rule.room") room bans and - @PolicyEvents.Count(x => x.type == "m.policy.rule.user") user bans. -</p> - - -@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.server")) -{ - <p>No server policies</p> -} -else -{ - <h3>Server policies</h3> - <hr/> - <table class="table table-striped table-hover" style="width: fit-content;"> - <thead> - <tr> - <th scope="col" style="max-width: 50vw;">Server</th> - <th scope="col">Reason</th> - <th scope="col">Expires</th> - <th scope="col">Actions</th> - </tr> - </thead> - <tbody> - @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.server" && x.content.Entity != null)) - { - <tr> - <td>Entity: @policyEvent.content.Entity<br/>State: @policyEvent.state_key</td> - <td>@policyEvent.content.Reason</td> - <td> - @policyEvent.content.ExpiryDateTime - </td> - <td> - <button class="btn btn-danger" @* @onclick="async () => await RemovePolicyAsync(policyEvent)" *@>Remove</button> - </td> - </tr> - } - </tbody> - </table> - <details> - <summary>Invalid events</summary> - <table class="table table-striped table-hover" style="width: fit-content;"> - <thead> - <tr> - <th scope="col" style="max-width: 50vw;">State key</th> - <th scope="col">Serialised contents</th> - </tr> - </thead> - <tbody> - @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.server" && x.content.Entity == null)) - { - <tr> - <td>@policyEvent.state_key</td> - <td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td> - </tr> - } - </tbody> - </table> - </details> -} -@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.room")) -{ - <p>No room policies</p> -} -else -{ - <h3>Room policies</h3> - <hr/> - <table class="table table-striped table-hover" style="width: fit-content;"> - <thead> - <tr> - <th scope="col" style="max-width: 50vw;">Room</th> - <th scope="col">Reason</th> - <th scope="col">Expires</th> - <th scope="col">Actions</th> - </tr> - </thead> - <tbody> - @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.room" && x.content.Entity != null)) - { - <tr> - <td>Entity: @policyEvent.content.Entity<br/>State: @policyEvent.state_key</td> - <td>@policyEvent.content.Reason</td> - <td> - @policyEvent.content.ExpiryDateTime - </td> - <td> - <button class="btn btn-danger" @* @onclick="async () => await RemovePolicyAsync(policyEvent)" *@>Remove</button> - </td> - </tr> - } - </tbody> - </table> - <details> - <summary>Invalid events</summary> - <table class="table table-striped table-hover" style="width: fit-content;"> - <thead> - <tr> - <th scope="col" style="max-width: 50vw;">State key</th> - <th scope="col">Serialised contents</th> - </tr> - </thead> - <tbody> - @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.room" && x.content.Entity == null)) - { - <tr> - <td>@policyEvent.state_key</td> - <td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td> - </tr> - } - </tbody> - </table> - </details> -} -@if (!PolicyEvents.Any(x => x.type == "m.policy.rule.user")) -{ - <p>No user policies</p> -} -else -{ - <h3>User policies</h3> - <hr/> - <table class="table table-striped table-hover" style="width: fit-content;"> - <thead> - <tr> - <th scope="col" style="max-width: 0.2vw; word-wrap: anywhere;">User</th> - <th scope="col">Reason</th> - <th scope="col">Expires</th> - <th scope="col">Actions</th> - </tr> - </thead> - <tbody> - @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.user" && x.content.Entity != null)) - { - <tr> - <td style="word-wrap: anywhere;">Entity: @string.Join("", policyEvent.content.Entity.Take(64))<br/>State: @string.Join("", policyEvent.state_key.Take(64))</td> - <td>@policyEvent.content.Reason</td> - <td> - @policyEvent.content.ExpiryDateTime - </td> - <td> - <button class="btn btn-danger" @* @onclick="async () => await RemovePolicyAsync(policyEvent)" *@>Remove</button> - </td> - </tr> - } - </tbody> - </table> - <details> - <summary>Invalid events</summary> - <table class="table table-striped table-hover" style="width: fit-content;"> - <thead> - <tr> - <th scope="col">State key</th> - <th scope="col">Serialised contents</th> - </tr> - </thead> - <tbody> - @foreach (var policyEvent in PolicyEvents.Where(x => x.type == "m.policy.rule.user" && x.content.Entity == null)) - { - <tr> - <td>@policyEvent.state_key</td> - <td>@policyEvent.content.ToJson(indent: false, ignoreNull: true)</td> - </tr> - } - </tbody> - </table> - </details> -} - -<LogView></LogView> - -@code { - //get room list - // - sync withroom list filter - // type = support.feline.msc3784 - //support.feline.policy.lists.msc.v1 - - [Parameter] - public string? RoomId { get; set; } - - public List<StateEvent<PolicyRuleStateEventData>> PolicyEvents { get; set; } = new(); - - protected override async Task OnInitializedAsync() - { - if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - await base.OnInitializedAsync(); - // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null) - if (RuntimeCache.CurrentHomeServer == null) - { - NavigationManager.NavigateTo("/Login"); - return; - } - RoomId = RoomId.Replace('~', '.'); - await LoadStatesAsync(); - Console.WriteLine("Policy list editor initialized!"); - } - - private async Task LoadStatesAsync() - { - // using var client = new HttpClient(); - // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); - // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/r0/rooms/{RoomId}/state"); - // var content = await response.Content.ReadAsStringAsync(); - // Console.WriteLine(JsonSerializer.Deserialize<object>(content).ToJson()); - // var stateEvents = JsonSerializer.Deserialize<List<StateEvent>>(content); - var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId); - var stateEventsQuery = await room.GetStateAsync(""); - if (stateEventsQuery == null) - { - Console.WriteLine("state events query is null!!!"); - } - var stateEvents = stateEventsQuery.Value.Deserialize<List<StateEvent>>(); - PolicyEvents = stateEvents.Where(x => x.type.StartsWith("m.policy.rule")) - .Select(x => JsonSerializer.Deserialize<StateEvent<PolicyRuleStateEventData>>(JsonSerializer.Serialize(x))).ToList(); - StateHasChanged(); - } - -} -\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyListRoomList.razor @@ -1,161 +0,0 @@ -@page "/PolicyListEditor" -@using System.Net.Http.Headers -@using System.Text.Json -@using MatrixRoomUtils.Core -@using MatrixRoomUtils.Core.Extensions -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager -<h3>Policy list editor</h3> - -<h5>Room list</h5> -<hr/> -@if (PolicyRoomList.Count == 0) -{ - <p>No policy rooms found.</p> - <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> -} -else -{ - @if (checkedRoomCount != totalRoomCount) - { - <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> - } - foreach (var s in PolicyRoomList) - { - <a href="@(NavigationManager.Uri + "/" + s.RoomId.Replace('.', '~'))">[@s.Shortcode] @s.Name (@s.RoomId)</a> - <br/> - } -} - -<div style="margin-bottom: 4em;"></div> -<LogView></LogView> - -@code { - //get room list - // - sync withroom list filter - // type = support.feline.msc3784 - //support.feline.policy.lists.msc.v1 - - public List<PolicyRoomInfo> PolicyRoomList { get; set; } = new(); - - private int checkedRoomCount { get; set; } = 0; - private int totalRoomCount { get; set; } = 0; - - protected override async Task OnInitializedAsync() - { - if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) - { - NavigationManager.NavigateTo("/Login"); - return; - } - await EnumeratePolicyRooms(); - Console.WriteLine("Policy list editor initialized!"); - } - - private async Task EnumeratePolicyRooms() - { - var xxxrooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); - totalRoomCount = xxxrooms.Count; - StateHasChanged(); - - var xxxsemaphore = new SemaphoreSlim(256); - var xxxtasks = new List<Task<PolicyRoomInfo?>>(); - foreach (var room in xxxrooms) - { - xxxtasks.Add(GetPolicyRoomInfo(room.RoomId, xxxsemaphore)); - } - var xxxresults = await Task.WhenAll(xxxtasks); - PolicyRoomList.AddRange(xxxresults.Where(x => x != null).Select(x => x.Value)); - - Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}"); - return; - /* - using HttpClient wc = new(); - wc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); - - - - //get room list - //temporary hack until rooms get enumerated... - string[] rooms = { "!fTjMjIzNKEsFlUIiru:neko.dev" }; - var _rooms = await wc.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/v3/joined_rooms"); - Console.WriteLine($"Got {_rooms.StatusCode}..."); - if (!_rooms.IsSuccessStatusCode) - { - Console.WriteLine($"Failed to get rooms: {await _rooms.Content.ReadAsStringAsync()}"); - return; - } - var _rooms_o = await _rooms.Content.ReadFromJsonAsync<JsonElement>(); - if (_rooms_o.TryGetProperty("joined_rooms", out JsonElement _rooms_j)) - { - rooms = _rooms_j.EnumerateArray().Select(x => x.GetString()).ToArray(); - } - - totalRoomCount = rooms.Length; - StateHasChanged(); - - var semaphore = new SemaphoreSlim(256); - var tasks = new List<Task<PolicyRoomInfo?>>(); - foreach (string room in rooms) - { - tasks.Add(GetPolicyRoomInfo(room, semaphore)); - } - var results = await Task.WhenAll(tasks); - PolicyRoomList.AddRange(results.Where(x => x != null).Select(x => x.Value)); - - - //print to console - Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}"); - */ - } - - private async Task<PolicyRoomInfo?> GetPolicyRoomInfo(string room, SemaphoreSlim semaphore) - { - try - { - //TODO: refactor!!!!! - await semaphore.WaitAsync(); - PolicyRoomInfo roomInfo = new() - { - RoomId = room - }; - - - // --- // - var r = await RuntimeCache.CurrentHomeServer.GetRoom(room); - var shortcodeState = await r.GetStateAsync("org.matrix.mjolnir.shortcode"); - if(!shortcodeState.HasValue) return null; - roomInfo.Shortcode = shortcodeState.Value.TryGetProperty("shortcode", out JsonElement shortcode) ? shortcode.GetString() : null; - - if (roomInfo.Shortcode != null) - { - roomInfo.Name = await r.GetNameAsync(); - return roomInfo; - } - - return null; - } - finally - - { - checkedRoomCount++; - StateHasChanged(); - semaphore.Release(); - } - } - - public struct PolicyRoomInfo - - { - public - string RoomId { get; set; } - - public - string? Shortcode { get; set; } - - public - string? Name { get; set; } - } - } -\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor @@ -0,0 +1,170 @@ +@page "/RoomStateViewer/{RoomId}/Edit" +@using System.Net.Http.Headers +@using System.Text.Json +@inject ILocalStorageService LocalStorage +@inject NavigationManager NavigationManager +<h3>Room state editor - Editing @RoomId</h3> +<hr/> + +<p>@status</p> + +<input type="checkbox" id="showAll" @bind="ShowMembershipEvents"/> Show member events +<br/> +<InputSelect @bind-Value="shownStateKey"> + <option value="">-- State key --</option> + @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key != "").Select(x => x.state_key).Distinct().OrderBy(x => x)) + { + <option value="@stateEvent">@stateEvent</option> + Console.WriteLine(stateEvent); + } +</InputSelect> +<br/> +<InputSelect @bind-Value="shownType"> + <option value="">-- Type --</option> + @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key != shownStateKey).Select(x => x.type).Distinct().OrderBy(x => x)) + { + <option value="@stateEvent">@stateEvent</option> + } +</InputSelect> +<br/> + +<textarea @bind="shownEventJson" style="width: 100%; height: fit-content;"></textarea> + +<LogView></LogView> + +@code { + //get room list + // - sync withroom list filter + // type = support.feline.msc3784 + //support.feline.policy.lists.msc.v1 + + [Parameter] + public string? RoomId { get; set; } + + public List<StateEvent> FilteredEvents { get; set; } = new(); + public List<StateEvent> Events { get; set; } = new(); + public string status = ""; + + protected override async Task OnInitializedAsync() + { + if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + await base.OnInitializedAsync(); + if (RuntimeCache.CurrentHomeServer != null) + { + NavigationManager.NavigateTo("/Login"); + return; + } + RoomId = RoomId.Replace('~', '.'); + await LoadStatesAsync(); + Console.WriteLine("Policy list editor initialized!"); + } + + private DateTime _lastUpdate = DateTime.Now; + + private async Task LoadStatesAsync() + { + int StateLoaded = 0; + using var client = new HttpClient(); + //TODO: can this be improved? + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); + var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); + // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); + //var _events = await response.Content.ReadFromJsonAsync<Queue<StateEventStruct>>(); + var _data = await response.Content.ReadAsStreamAsync(); + var __events = JsonSerializer.DeserializeAsyncEnumerable<StateEvent>(_data); + await foreach (var _ev in __events) + { + var e = new StateEvent() + { + type = _ev.type, + state_key = _ev.state_key, + origin_server_ts = _ev.origin_server_ts, + content = _ev.content + }; + Events.Add(e); + if (string.IsNullOrEmpty(e.state_key)) + { + FilteredEvents.Add(e); + } + StateLoaded++; + if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) + { + _lastUpdate = DateTime.Now; + status = $"Loaded {StateLoaded} state events"; + StateHasChanged(); + await Task.Delay(0); + } + } + + StateHasChanged(); + } + + private async Task RebuildFilteredData() + { + status = "Rebuilding filtered data..."; + StateHasChanged(); + await Task.Delay(1); + var _FilteredEvents = Events; + if (!ShowMembershipEvents) + _FilteredEvents = _FilteredEvents.Where(x => x.type != "m.room.member").ToList(); + + status = "Done, rerendering!"; + StateHasChanged(); + await Task.Delay(1); + FilteredEvents = _FilteredEvents; + + if(_shownType != null) + shownEventJson = _FilteredEvents.Where(x => x.type == _shownType).First().content.ToJson(indent: true, ignoreNull: true); + + StateHasChanged(); + } + + + public struct PreRenderedStateEvent + { + public string content { get; set; } + public long origin_server_ts { get; set; } + public string state_key { get; set; } + public string type { get; set; } + // public string sender { get; set; } + // public string event_id { get; set; } + // public string user_id { get; set; } + // public string replaces_state { get; set; } + } + + public bool ShowMembershipEvents + { + get => _showMembershipEvents; + set + { + _showMembershipEvents = value; + RebuildFilteredData(); + } + } + + private bool _showMembershipEvents; + private string _shownStateKey; + private string _shownType; + + private string shownStateKey + { + get => _shownStateKey; + set + { + _shownStateKey = value; + RebuildFilteredData(); + } + } + + private string shownType + { + get => _shownType; + set + { + _shownType = value; + RebuildFilteredData(); + } + } + + private string shownEventJson { get; set; } +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor @@ -0,0 +1,36 @@ +@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() + { + if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + await base.OnInitializedAsync(); + if (RuntimeCache.CurrentHomeServer == null) + { + NavigationManager.NavigateTo("/Login"); + return; + } + Rooms = (await RuntimeCache.CurrentHomeServer.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 @@ -0,0 +1,169 @@ +@page "/RoomStateViewer/{RoomId}" +@using System.Net.Http.Headers +@using System.Text.Json +@using MatrixRoomUtils.Core.Extensions +@inject ILocalStorageService LocalStorage +@inject NavigationManager NavigationManager +<h3>Room state viewer - Viewing @RoomId</h3> +<hr/> + +<p>@status</p> + +<input type="checkbox" id="showAll" @bind="ShowMembershipEvents"/> Show member events + +<table class="table table-striped table-hover" style="width: fit-content;"> + <thead> + <tr> + <th scope="col">Type</th> + <th scope="col">Content</th> + </tr> + </thead> + <tbody> + @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key == "").OrderBy(x => x.origin_server_ts)) + { + <tr> + <td>@stateEvent.type</td> + <td style="max-width: fit-content;"> + <pre>@stateEvent.content</pre> + </td> + </tr> + } + </tbody> +</table> + +@foreach (var group in FilteredEvents.GroupBy(x => x.state_key).OrderBy(x => x.Key).Where(x => x.Key != "")) +{ + <details> + <summary>@group.Key</summary> + <table class="table table-striped table-hover" style="width: fit-content;"> + <thead> + <tr> + <th scope="col">Type</th> + <th scope="col">Content</th> + </tr> + </thead> + <tbody> + @foreach (var stateEvent in group.OrderBy(x => x.origin_server_ts)) + { + <tr> + <td>@stateEvent.type</td> + <td style="max-width: fit-content;"> + <pre>@stateEvent.content</pre> + </td> + </tr> + } + </tbody> + </table> + </details> +} + +<LogView></LogView> + +@code { + //get room list + // - sync withroom list filter + // type = support.feline.msc3784 + //support.feline.policy.lists.msc.v1 + + [Parameter] + public string? RoomId { get; set; } + + public List<PreRenderedStateEvent> FilteredEvents { get; set; } = new(); + public List<PreRenderedStateEvent> Events { get; set; } = new(); + public string status = ""; + + protected override async Task OnInitializedAsync() + { + if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + await base.OnInitializedAsync(); + if (RuntimeCache.CurrentHomeServer == null) + { + NavigationManager.NavigateTo("/Login"); + return; + } + RoomId = RoomId.Replace('~', '.'); + await LoadStatesAsync(); + Console.WriteLine("Policy list editor initialized!"); + } + private DateTime _lastUpdate = DateTime.Now; + + private async Task LoadStatesAsync() + { + int StateLoaded = 0; + //TODO: can we improve this? + using var client = new HttpClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); + var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); + // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); + //var _events = await response.Content.ReadFromJsonAsync<Queue<StateEventStruct>>(); + var _data = await response.Content.ReadAsStreamAsync(); + var __events = JsonSerializer.DeserializeAsyncEnumerable<StateEventStruct>(_data); + await foreach (var _ev in __events) + { + var e = new PreRenderedStateEvent() + { + type = _ev.type, + state_key = _ev.state_key, + origin_server_ts = _ev.origin_server_ts, + content = _ev.content.ToJson(indent: true, ignoreNull: true), + }; + Events.Add(e); + if (string.IsNullOrEmpty(e.state_key)) + { + FilteredEvents.Add(e); + } + StateLoaded++; + if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) + { + _lastUpdate = DateTime.Now; + status = $"Loaded {StateLoaded} state events"; + StateHasChanged(); + await Task.Delay(0); + } + + } + + StateHasChanged(); + } + + private async Task RebuildFilteredData() + { + status = "Rebuilding filtered data..."; + StateHasChanged(); + await Task.Delay(1); + var _FilteredEvents = Events; + if (!ShowMembershipEvents) + _FilteredEvents = _FilteredEvents.Where(x => x.type != "m.room.member").ToList(); + + status = "Done, rerendering!"; + StateHasChanged(); + await Task.Delay(1); + FilteredEvents = _FilteredEvents; + StateHasChanged(); + } + + + public struct PreRenderedStateEvent + { + public string content { get; set; } + public long origin_server_ts { get; set; } + public string state_key { get; set; } + public string type { get; set; } + // public string sender { get; set; } + // public string event_id { get; set; } + // public string user_id { get; set; } + // public string replaces_state { get; set; } + } + + public bool ShowMembershipEvents + { + get => _showMembershipEvents; + set + { + _showMembershipEvents = value; + RebuildFilteredData(); + } + } + + private bool _showMembershipEvents; +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomStateEditorPage.razor @@ -1,171 +0,0 @@ -@page "/RoomStateViewer/{RoomId}/Edit" -@using System.Net.Http.Headers -@using System.Text.Json -@using MatrixRoomUtils.Core -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager -<h3>Room state editor</h3> -<p>Room ID: @RoomId</p> - -<p>@status</p> - -<input type="checkbox" id="showAll" @bind="ShowMembershipEvents"/> Show member events -<br/> -<InputSelect @bind-Value="shownStateKey"> - <option value="">-- State key --</option> - @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key != "").Select(x => x.state_key).Distinct().OrderBy(x => x)) - { - <option value="@stateEvent">@stateEvent</option> - Console.WriteLine(stateEvent); - } -</InputSelect> -<br/> -<InputSelect @bind-Value="shownType"> - <option value="">-- Type --</option> - @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key != shownStateKey).Select(x => x.type).Distinct().OrderBy(x => x)) - { - <option value="@stateEvent">@stateEvent</option> - } -</InputSelect> -<br/> - -<textarea @bind="shownEventJson" style="width: 100%; height: fit-content;"></textarea> - -<LogView></LogView> - -@code { - //get room list - // - sync withroom list filter - // type = support.feline.msc3784 - //support.feline.policy.lists.msc.v1 - - [Parameter] - public string? RoomId { get; set; } - - public List<StateEvent> FilteredEvents { get; set; } = new(); - public List<StateEvent> Events { get; set; } = new(); - public string status = ""; - - protected override async Task OnInitializedAsync() - { - if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer != null) - { - NavigationManager.NavigateTo("/Login"); - return; - } - RoomId = RoomId.Replace('~', '.'); - await LoadStatesAsync(); - Console.WriteLine("Policy list editor initialized!"); - } - - private DateTime _lastUpdate = DateTime.Now; - - private async Task LoadStatesAsync() - { - int StateLoaded = 0; - using var client = new HttpClient(); - //TODO: can this be improved? - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); - var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); - // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); - //var _events = await response.Content.ReadFromJsonAsync<Queue<StateEventStruct>>(); - var _data = await response.Content.ReadAsStreamAsync(); - var __events = JsonSerializer.DeserializeAsyncEnumerable<StateEvent>(_data); - await foreach (var _ev in __events) - { - var e = new StateEvent() - { - type = _ev.type, - state_key = _ev.state_key, - origin_server_ts = _ev.origin_server_ts, - content = _ev.content - }; - Events.Add(e); - if (string.IsNullOrEmpty(e.state_key)) - { - FilteredEvents.Add(e); - } - StateLoaded++; - if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) - { - _lastUpdate = DateTime.Now; - status = $"Loaded {StateLoaded} state events"; - StateHasChanged(); - await Task.Delay(0); - } - } - - StateHasChanged(); - } - - private async Task RebuildFilteredData() - { - status = "Rebuilding filtered data..."; - StateHasChanged(); - await Task.Delay(1); - var _FilteredEvents = Events; - if (!ShowMembershipEvents) - _FilteredEvents = _FilteredEvents.Where(x => x.type != "m.room.member").ToList(); - - status = "Done, rerendering!"; - StateHasChanged(); - await Task.Delay(1); - FilteredEvents = _FilteredEvents; - - if(_shownType != null) - shownEventJson = _FilteredEvents.Where(x => x.type == _shownType).First().content.ToJson(indent: true, ignoreNull: true); - - StateHasChanged(); - } - - - public struct PreRenderedStateEvent - { - public string content { get; set; } - public long origin_server_ts { get; set; } - public string state_key { get; set; } - public string type { get; set; } - // public string sender { get; set; } - // public string event_id { get; set; } - // public string user_id { get; set; } - // public string replaces_state { get; set; } - } - - public bool ShowMembershipEvents - { - get => _showMembershipEvents; - set - { - _showMembershipEvents = value; - RebuildFilteredData(); - } - } - - private bool _showMembershipEvents; - private string _shownStateKey; - private string _shownType; - - private string shownStateKey - { - get => _shownStateKey; - set - { - _shownStateKey = value; - RebuildFilteredData(); - } - } - - private string shownType - { - get => _shownType; - set - { - _shownType = value; - RebuildFilteredData(); - } - } - - private string shownEventJson { get; set; } -} -\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomStateRoomList.razor @@ -1,96 +0,0 @@ -@page "/RoomStateViewer" -@using System.Net.Http.Headers -@using System.Text.Json -@using MatrixRoomUtils.Core -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager -<h3>Room state viewer</h3> - -<h5>Room list</h5> -<hr/> -@if (PolicyRoomList.Count == 0) -{ - <p>No policy rooms found.</p> - <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> -} -else -{ - @if (checkedRoomCount != totalRoomCount) - { - <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> - } - foreach (var s in PolicyRoomList) - { - <a href="@(NavigationManager.Uri + "/" + s.RoomId.Replace('.', '~'))">@s.Name (@s.RoomId)</a> - <br/> - } - <div style="margin-bottom: 4em;"></div> -} - -<LogView></LogView> - -@code { - - public List<PolicyRoomInfo> PolicyRoomList { get; set; } = new(); - - private int checkedRoomCount { get; set; } = 0; - private int totalRoomCount { get; set; } = 0; - - protected override async Task OnInitializedAsync() - { - if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) - { - NavigationManager.NavigateTo("/Login"); - return; - } - await EnumeratePolicyRooms(); - Console.WriteLine("Policy list editor initialized!"); - } - - private async Task EnumeratePolicyRooms() - { - var rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x=>x.RoomId).ToList(); - - totalRoomCount = rooms.Count; - StateHasChanged(); - - var semaphore = new SemaphoreSlim(128); - var tasks = new List<Task<PolicyRoomInfo?>>(); - foreach (string room in rooms) - { - tasks.Add(GetPolicyRoomInfo(room, semaphore)); - } - var results = await Task.WhenAll(tasks); - PolicyRoomList.AddRange(results.Where(x => x != null).Select(x=>x.Value)); - - StateHasChanged(); - } - - private async Task<PolicyRoomInfo?> GetPolicyRoomInfo(string room, SemaphoreSlim semaphore) - { - try - { - await semaphore.WaitAsync(); - return new PolicyRoomInfo() - { - RoomId = room, - Name = await (await RuntimeCache.CurrentHomeServer.GetRoom(room)).GetNameAsync() - }; - } - finally - { - checkedRoomCount++; - StateHasChanged(); - semaphore.Release(); - } - } - - - public struct PolicyRoomInfo - { - public string RoomId { get; set; } - public string Name { get; set; } - } -} -\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomStateViewerPage.razor @@ -1,170 +0,0 @@ -@page "/RoomStateViewer/{RoomId}" -@using System.Net.Http.Headers -@using System.Text.Json -@using MatrixRoomUtils.Core -@using MatrixRoomUtils.Core.Extensions -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager -<h3>Room state viewer</h3> -<p>Room ID: @RoomId</p> - -<p>@status</p> - -<input type="checkbox" id="showAll" @bind="ShowMembershipEvents"/> Show member events - -<table class="table table-striped table-hover" style="width: fit-content;"> - <thead> - <tr> - <th scope="col">Type</th> - <th scope="col">Content</th> - </tr> - </thead> - <tbody> - @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key == "").OrderBy(x => x.origin_server_ts)) - { - <tr> - <td>@stateEvent.type</td> - <td style="max-width: fit-content;"> - <pre>@stateEvent.content</pre> - </td> - </tr> - } - </tbody> -</table> - -@foreach (var group in FilteredEvents.GroupBy(x => x.state_key).OrderBy(x => x.Key).Where(x => x.Key != "")) -{ - <details> - <summary>@group.Key</summary> - <table class="table table-striped table-hover" style="width: fit-content;"> - <thead> - <tr> - <th scope="col">Type</th> - <th scope="col">Content</th> - </tr> - </thead> - <tbody> - @foreach (var stateEvent in group.OrderBy(x => x.origin_server_ts)) - { - <tr> - <td>@stateEvent.type</td> - <td style="max-width: fit-content;"> - <pre>@stateEvent.content</pre> - </td> - </tr> - } - </tbody> - </table> - </details> -} - -<LogView></LogView> - -@code { - //get room list - // - sync withroom list filter - // type = support.feline.msc3784 - //support.feline.policy.lists.msc.v1 - - [Parameter] - public string? RoomId { get; set; } - - public List<PreRenderedStateEvent> FilteredEvents { get; set; } = new(); - public List<PreRenderedStateEvent> Events { get; set; } = new(); - public string status = ""; - - protected override async Task OnInitializedAsync() - { - if (!RuntimeCache.WasLoaded) await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) - { - NavigationManager.NavigateTo("/Login"); - return; - } - RoomId = RoomId.Replace('~', '.'); - await LoadStatesAsync(); - Console.WriteLine("Policy list editor initialized!"); - } - private DateTime _lastUpdate = DateTime.Now; - - private async Task LoadStatesAsync() - { - int StateLoaded = 0; - //TODO: can we improve this? - using var client = new HttpClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); - var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/r0/rooms/{RoomId}/state"); - // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); - //var _events = await response.Content.ReadFromJsonAsync<Queue<StateEventStruct>>(); - var _data = await response.Content.ReadAsStreamAsync(); - var __events = JsonSerializer.DeserializeAsyncEnumerable<StateEventStruct>(_data); - await foreach (var _ev in __events) - { - var e = new PreRenderedStateEvent() - { - type = _ev.type, - state_key = _ev.state_key, - origin_server_ts = _ev.origin_server_ts, - content = _ev.content.ToJson(indent: true, ignoreNull: true), - }; - Events.Add(e); - if (string.IsNullOrEmpty(e.state_key)) - { - FilteredEvents.Add(e); - } - StateLoaded++; - if ((DateTime.Now - _lastUpdate).TotalMilliseconds > 100) - { - _lastUpdate = DateTime.Now; - status = $"Loaded {StateLoaded} state events"; - StateHasChanged(); - await Task.Delay(0); - } - - } - - StateHasChanged(); - } - - private async Task RebuildFilteredData() - { - status = "Rebuilding filtered data..."; - StateHasChanged(); - await Task.Delay(1); - var _FilteredEvents = Events; - if (!ShowMembershipEvents) - _FilteredEvents = _FilteredEvents.Where(x => x.type != "m.room.member").ToList(); - - status = "Done, rerendering!"; - StateHasChanged(); - await Task.Delay(1); - FilteredEvents = _FilteredEvents; - StateHasChanged(); - } - - - public struct PreRenderedStateEvent - { - public string content { get; set; } - public long origin_server_ts { get; set; } - public string state_key { get; set; } - public string type { get; set; } - // public string sender { get; set; } - // public string event_id { get; set; } - // public string user_id { get; set; } - // public string replaces_state { get; set; } - } - - public bool ShowMembershipEvents - { - get => _showMembershipEvents; - set - { - _showMembershipEvents = value; - RebuildFilteredData(); - } - } - - private bool _showMembershipEvents; -} -\ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/UserImportPage.razor b/MatrixRoomUtils.Web/Pages/UserImportPage.razor @@ -1,6 +1,5 @@ @page "/ImportUsers" @using System.Text.Json -@using MatrixRoomUtils.Core @using MatrixRoomUtils.Core.Authentication @inject ILocalStorageService LocalStorage <h3>Login</h3> diff --git a/MatrixRoomUtils.Web/Program.cs b/MatrixRoomUtils.Web/Program.cs @@ -1,6 +1,6 @@ using System.Text.Json; using System.Text.Json.Serialization; -using Blazored.LocalStorage;using MatrixRoomUtils; +using Blazored.LocalStorage; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using MatrixRoomUtils.Web; diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor b/MatrixRoomUtils.Web/Shared/RoomListItem.razor @@ -0,0 +1,45 @@ +<div style="background-color: #ffffff11; border-radius: 25px; margin: 8px; width: fit-content;"> + <img style="width: 32px; height: 32px; border-radius: 50%;" src="@roomIcon"/> + <span style="vertical-align: middle; padding-right: 8px;">@roomName</span> +</div> + +@code { + [Parameter] + public Room Room { get; set; } + [Parameter] + public string RoomId { get; set; } + + private string roomName { get; set; } = "Loading..."; + private string roomIcon { get; set; } = "/icon-192.png"; + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + if (Room == null) + { + if (RoomId == null) + { + throw new ArgumentNullException(nameof(RoomId)); + } + Room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId); + } + + roomName = await Room.GetNameAsync(); + if (roomName == null) + { + roomName = "Unnamed room: " + RoomId; + } + + var state = await Room.GetStateAsync("m.room.avatar"); + if (state != null) + { + var url = state.Value.GetProperty("url").GetString(); + if (url != null) + { + roomIcon = await RuntimeCache.CurrentHomeServer.ResolveMediaUri(url); + } + } + + } + +} +\ No newline at end of file diff --git a/MatrixRoomUtils.Web/wwwroot/css/app.css b/MatrixRoomUtils.Web/wwwroot/css/app.css @@ -2,6 +2,17 @@ html, body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + background-color: #222; + color: #aaa; +} + +#app > div > main > div { + background-color: #333; + border-bottom: none; +} + +.table, .table-striped>tbody>tr:nth-of-type(odd), .table-hover>tbody>tr:hover { + color: unset; } h1:focus {