diff options
m--------- | LibMatrix | 0 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/About.razor | 2 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/PolicyLists.razor | 177 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/PolicyLists.razor.css | 6 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/Space.razor | 11 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor | 2 | ||||
-rw-r--r-- | MatrixUtils.Web/Shared/NavMenu.razor | 6 |
7 files changed, 201 insertions, 3 deletions
diff --git a/LibMatrix b/LibMatrix -Subproject 77650d16a9cc66fdfe393320164cd8248cdff38 +Subproject bf1664b254bfc224f0087eb82fdba5235fbd162 diff --git a/MatrixUtils.Web/Pages/About.razor b/MatrixUtils.Web/Pages/About.razor index 330d1c2..9f83991 100644 --- a/MatrixUtils.Web/Pages/About.razor +++ b/MatrixUtils.Web/Pages/About.razor @@ -8,5 +8,5 @@ <p>These range from joining rooms on dead homeservers, to managing your accounts and rooms, and creating rooms based on templates.</p> <br/> -<p>You can find the source code on <a href="https://cgit.rory.gay/matrix/MatrixRoomUtils.git/">cgit.rory.gay</a>.<br/></p> +<p>You can find the source code on <a href="https://cgit.rory.gay/matrix/tools/MatrixUtils.git/about/">cgit.rory.gay</a>.<br/></p> <p>You can also join the <a href="https://matrix.to/#/%23mru%3Arory.gay?via=rory.gay&via=matrix.org&via=feline.support">Matrix room</a> for this project.</p> diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor new file mode 100644 index 0000000..63dc206 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor @@ -0,0 +1,177 @@ +@page "/PolicyLists" +@using System.Collections.ObjectModel +@using ArcaneLibs.Extensions +@using LibMatrix +@using LibMatrix.EventTypes +@using LibMatrix.EventTypes.Common +@using LibMatrix.EventTypes.Spec.State.Policy +@using LibMatrix.RoomTypes +@inject ILogger<Index> logger +<h3>Policy lists </h3> @* <LinkButton href="/Rooms/Create">Create new policy list</LinkButton> *@ + +@if (!string.IsNullOrWhiteSpace(Status)) { + <p>@Status</p> +} +@if (!string.IsNullOrWhiteSpace(Status2)) { + <p>@Status2</p> +} +<hr/> + +<table> + <thead> + <tr> + <th/> + <th>Room name</th> + <th>Policies</th> + </tr> + </thead> + <tbody> + @foreach (var room in Rooms.OrderByDescending(x => x.PolicyCounts.Sum(y => y.Value))) { + <tr> + <td> + <LinkButton href="@($"/Rooms/{room.Room.RoomId}/Policies")"> + <span class="oi oi-pencil" aria-hidden="true"></span> + </LinkButton> + </td> + <td style="padding-right: 24px;"> + <span>@room.RoomName</span> + @if (room.IsLegacy) { + <span style="color: red;"> (legacy)</span> + } + <br/> + @if (!string.IsNullOrWhiteSpace(room.Shortcode)) { + <span style="font-size: 0.8em;">@room.Shortcode</span> + } + else { + <span style="color: red;">(no shortcode)</span> + } + </td> + <td> + <span>@(room.PolicyCounts.GetValueOrDefault(RoomInfo.PolicyType.User) ?? 0) user policies</span><br/> + <span>@(room.PolicyCounts.GetValueOrDefault(RoomInfo.PolicyType.Server) ?? 0) server policies</span><br/> + <span>@(room.PolicyCounts.GetValueOrDefault(RoomInfo.PolicyType.Room) ?? 0) room policies</span><br/> + </td> + </tr> + } + </tbody> +</table> + +@code { + + private List<RoomInfo> Rooms { get; } = []; + + private AuthenticatedHomeserverGeneric? Homeserver { get; set; } + + protected override async Task OnInitializedAsync() { + Homeserver = await RMUStorage.GetCurrentSessionOrNavigate(); + if (Homeserver is null) return; + + Status = "Fetching rooms..."; + + var userEventTypes = EventContent.GetMatchingEventTypes<UserPolicyRuleEventContent>(); + var serverEventTypes = EventContent.GetMatchingEventTypes<ServerPolicyRuleEventContent>(); + var roomEventTypes = EventContent.GetMatchingEventTypes<RoomPolicyRuleEventContent>(); + var knownPolicyTypes = (List<string>) [..userEventTypes, ..serverEventTypes, ..roomEventTypes]; + + List<GenericRoom> roomsByType = []; + await foreach (var room in Homeserver.GetJoinedRoomsByType("support.feline.policy.lists.msc.v1")) { + roomsByType.Add(room); + Status2 = $"Found {room.RoomId} (MSC3784)..."; + } + + List<Task<RoomInfo>> tasks = roomsByType.Select(async room => { + Status2 = $"Fetching room {room.RoomId}..."; + return await RoomInfo.FromRoom(room); + }).ToList(); + + var results = tasks.ToAsyncEnumerable(); + await foreach (var result in results) { + Rooms.Add(result); + StateHasChanged(); + } + + Status = "Searching for legacy lists..."; + + var rooms = (await Homeserver.GetJoinedRooms()) + .Where(x => !Rooms.Any(y => y.Room.RoomId == x.RoomId)) + .Select(async room => { + var state = await room.GetFullStateAsListAsync(); + var policies = state + .Where(x => knownPolicyTypes.Contains(x.Type)) + .ToList(); + if (policies.Count == 0) return null; + Status2 = $"Found legacy list {room.RoomId}..."; + return await RoomInfo.FromRoom(room, state, true); + }) + .ToAsyncEnumerable(); + + await foreach (var room in rooms) { + if (room is not null) { + Rooms.Add(room); + StateHasChanged(); + } + } + + Status = ""; + Status2 = ""; + await base.OnInitializedAsync(); + } + + private string _status; + + public string Status { + get => _status; + set { + _status = value; + StateHasChanged(); + } + } + + private string _status2; + + public string Status2 { + get => _status2; + set { + _status2 = value; + StateHasChanged(); + } + } + + private class RoomInfo { + public GenericRoom Room { get; set; } + public string RoomName { get; set; } + public string? Shortcode { get; set; } + public Dictionary<PolicyType, int?> PolicyCounts { get; set; } + public bool IsLegacy { get; set; } + + public enum PolicyType { + User, + Room, + Server + } + + private static readonly List<string> userEventTypes = EventContent.GetMatchingEventTypes<UserPolicyRuleEventContent>(); + private static readonly List<string> serverEventTypes = EventContent.GetMatchingEventTypes<ServerPolicyRuleEventContent>(); + private static readonly List<string> roomEventTypes = EventContent.GetMatchingEventTypes<RoomPolicyRuleEventContent>(); + private static readonly List<string> allKnownPolicyTypes = [..userEventTypes, ..serverEventTypes, ..roomEventTypes]; + + public static async Task<RoomInfo> FromRoom(GenericRoom room, List<StateEventResponse>? state = null, bool legacy = false) { + state ??= await room.GetFullStateAsListAsync(); + return new RoomInfo() { + Room = room, + IsLegacy = legacy, + RoomName = await room.GetNameAsync() + ?? (await room.GetCanonicalAliasAsync())?.Alias + ?? (await room.GetStateOrNullAsync<MjolnirShortcodeEventContent>(MjolnirShortcodeEventContent.EventId))?.Shortcode + ?? room.RoomId, + Shortcode = (await room.GetStateOrNullAsync<MjolnirShortcodeEventContent>(MjolnirShortcodeEventContent.EventId))?.Shortcode, + PolicyCounts = new() { + { PolicyType.User, state.Count(x => userEventTypes.Contains(x.Type)) }, + { PolicyType.Server, state.Count(x => serverEventTypes.Contains(x.Type)) }, + { PolicyType.Room, state.Count(x => roomEventTypes.Contains(x.Type)) } + } + }; + } + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor.css b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor.css new file mode 100644 index 0000000..f9b5b3f --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor.css @@ -0,0 +1,6 @@ +table, th, td { + border-width: 1px; +} +td { + padding: 8px; +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/Space.razor b/MatrixUtils.Web/Pages/Rooms/Space.razor index 8153224..088fdcd 100644 --- a/MatrixUtils.Web/Pages/Rooms/Space.razor +++ b/MatrixUtils.Web/Pages/Rooms/Space.razor @@ -6,6 +6,9 @@ @using MatrixUtils.Abstractions <h3>Room manager - Viewing Space</h3> +<span>Add new room to space: </span> +<FancyTextBox @bind-Value="@NewRoomId"></FancyTextBox> +<button onclick="@AddNewRoom">Add</button> <button onclick="@JoinAllRooms">Join all rooms</button> @foreach (var room in Rooms) { <RoomListItem RoomInfo="room" ShowOwnProfile="true"></RoomListItem> @@ -31,6 +34,7 @@ private StateEventResponse[] States { get; set; } = Array.Empty<StateEventResponse>(); private List<RoomInfo> Rooms { get; } = new(); private List<string> ServersInSpace { get; } = new(); + private string? NewRoomId { get; set; } protected override async Task OnInitializedAsync() { var hs = await RMUStorage.GetCurrentSessionOrNavigate(); @@ -123,7 +127,7 @@ var ce = await room.GetCreateEventAsync(); if(ce is null) continue; if (ce.Type == "m.space") { - var children = room.AsSpace.GetChildrenAsync(true); + var children = room.AsSpace.GetChildrenAsync(false); await foreach (var child in children) { JoinRecursive(child.RoomId); } @@ -137,4 +141,9 @@ } + private async Task AddNewRoom() { + if (string.IsNullOrWhiteSpace(NewRoomId)) return; + await Room.AsSpace.AddChildByIdAsync(NewRoomId); + } + } diff --git a/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor b/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor index f2af9b5..e093db2 100644 --- a/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor +++ b/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor @@ -140,7 +140,7 @@ else var rgb = RoomData[roomName][date.Year][date]; if (message.RawContent?.Count == 0) rgb.R++; - else if (string.IsNullOrWhiteSpace(message.Unsigned?.ReplacesState)) rgb.G++; + else if (message.Unsigned?.ContainsKey("replaces_state") ?? false) rgb.G++; else rgb.B++; RoomData[roomName][date.Year][date] = rgb; } diff --git a/MatrixUtils.Web/Shared/NavMenu.razor b/MatrixUtils.Web/Shared/NavMenu.razor index 770a246..7371e66 100644 --- a/MatrixUtils.Web/Shared/NavMenu.razor +++ b/MatrixUtils.Web/Shared/NavMenu.razor @@ -37,6 +37,12 @@ </div> <div class="nav-item px-3"> + <NavLink class="nav-link" href="PolicyLists"> + <span class="oi oi-ban" aria-hidden="true"></span> Manage policy lists + </NavLink> + </div> + + <div class="nav-item px-3"> <NavLink class="nav-link" href="User/Profile"> <span class="oi oi-person" aria-hidden="true"></span> Manage profile </NavLink> |