about summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.idea/.idea.MatrixUtils/.idea/vcs.xml3
-rw-r--r--Benchmarks/Benchmarks.csproj2
m---------LibMatrix0
-rw-r--r--MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj2
-rw-r--r--MatrixUtils.Web/Classes/RmuSessionStore.cs2
-rw-r--r--MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs8
-rw-r--r--MatrixUtils.Web/MatrixUtils.Web.csproj10
-rw-r--r--MatrixUtils.Web/Pages/Dev/DevUtilities.razor31
-rw-r--r--MatrixUtils.Web/Pages/Dev/WellKnownRes.razor2
-rw-r--r--MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor1
-rw-r--r--MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor2
-rw-r--r--MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor6
-rw-r--r--MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor2
-rw-r--r--MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor2
-rw-r--r--MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor2
-rw-r--r--MatrixUtils.Web/Pages/HSAdmin/Synapse/UserList.razor4
-rw-r--r--MatrixUtils.Web/Pages/Index.razor13
-rw-r--r--MatrixUtils.Web/Pages/InvalidSession.razor6
-rw-r--r--MatrixUtils.Web/Pages/Labs/Client/ClientComponents/ClientRoomList.razor2
-rw-r--r--MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor4
-rw-r--r--MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor6
-rw-r--r--MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor2
-rw-r--r--MatrixUtils.Web/Pages/LoginPage.razor8
-rw-r--r--MatrixUtils.Web/Pages/Rooms/Create2.razor159
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyList.razor696
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyList.razor.cs144
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyList.razor.css9
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyList2.razor12
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor66
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListEditorHeader.razor65
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor167
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyLists.razor160
-rw-r--r--MatrixUtils.Web/Pages/Rooms/PolicyLists.razor.css6
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateBasicRoomInfoOptions.razor52
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateCreateOptions.razor92
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor52
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor55
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePermissionsOptions.razor123
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePrivacyOptions.razor70
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor33
-rw-r--r--MatrixUtils.Web/Pages/Rooms/Timeline.razor1
-rw-r--r--MatrixUtils.Web/Pages/ServerInfo.razor1
-rw-r--r--MatrixUtils.Web/Pages/Tools/Debug/JoinRoom.razor33
-rw-r--r--MatrixUtils.Web/Pages/Tools/Debug/LeaveRoom.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor4
-rw-r--r--MatrixUtils.Web/Pages/Tools/InviteCounter.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/MassCMEBan.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectionsEditor.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirWatchedListsEditor.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor6
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/InviteCounter.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/MassCMEBan.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/MembershipHistory.razor14
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor6
-rw-r--r--MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor4
-rw-r--r--MatrixUtils.Web/Pages/Tools/Room/DropPowerlevel.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor2
-rw-r--r--MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor2
-rw-r--r--MatrixUtils.Web/Pages/User/Profile.razor6
-rw-r--r--MatrixUtils.Web/Shared/InputLocalPart.razor50
-rw-r--r--MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor23
-rw-r--r--MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor54
-rw-r--r--MatrixUtils.Web/Shared/UserListItem.razor18
-rw-r--r--MatrixUtils.sln278
67 files changed, 2040 insertions, 565 deletions
diff --git a/.idea/.idea.MatrixUtils/.idea/vcs.xml b/.idea/.idea.MatrixUtils/.idea/vcs.xml

index 94a25f7..df05d42 100644 --- a/.idea/.idea.MatrixUtils/.idea/vcs.xml +++ b/.idea/.idea.MatrixUtils/.idea/vcs.xml
@@ -2,5 +2,8 @@ <project version="4"> <component name="VcsDirectoryMappings"> <mapping directory="$PROJECT_DIR$" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/LibMatrix" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/LibMatrix/ArcaneLibs" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/MxApiExtensions" vcs="Git" /> </component> </project> \ No newline at end of file diff --git a/Benchmarks/Benchmarks.csproj b/Benchmarks/Benchmarks.csproj
index 5d584c9..e61ef31 100644 --- a/Benchmarks/Benchmarks.csproj +++ b/Benchmarks/Benchmarks.csproj
@@ -11,7 +11,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="BenchmarkDotNet" Version="0.14.0" /> + <PackageReference Include="BenchmarkDotNet" Version="0.15.2" /> </ItemGroup> </Project> diff --git a/LibMatrix b/LibMatrix -Subproject 61a69b505eb202fe32345b5af3a80ef601bc679 +Subproject fcad35734ffe635a85e27349ff09bc035f26806 diff --git a/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj b/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj
index 24401ab..e69774b 100644 --- a/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj +++ b/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj
@@ -8,7 +8,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.1" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.7" /> </ItemGroup> <ItemGroup> diff --git a/MatrixUtils.Web/Classes/RmuSessionStore.cs b/MatrixUtils.Web/Classes/RmuSessionStore.cs
index 746f68a..9df8837 100644 --- a/MatrixUtils.Web/Classes/RmuSessionStore.cs +++ b/MatrixUtils.Web/Classes/RmuSessionStore.cs
@@ -1,5 +1,3 @@ -using System.Text.Json; -using System.Text.Json.Nodes; using LibMatrix; using LibMatrix.Homeservers; using LibMatrix.Services; diff --git a/MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs b/MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
index 7078308..215ad14 100644 --- a/MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs +++ b/MatrixUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs
@@ -82,10 +82,8 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { //TODO: re-implement this } }, - CreationContent = new JsonObject { - { - "type", null - } + CreationContent = new() { + { "type", null } } }; -} +} \ No newline at end of file diff --git a/MatrixUtils.Web/MatrixUtils.Web.csproj b/MatrixUtils.Web/MatrixUtils.Web.csproj
index aa9f37f..f418340 100644 --- a/MatrixUtils.Web/MatrixUtils.Web.csproj +++ b/MatrixUtils.Web/MatrixUtils.Web.csproj
@@ -39,11 +39,11 @@ <ItemGroup> <PackageReference Include="Blazored.LocalStorage" Version="4.5.0"/> <PackageReference Include="Blazored.SessionStorage" Version="2.4.0"/> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.1" PrivateAssets="all" /> - <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.2" /> - <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="9.0.1" /> - <PackageReference Include="SpawnDev.BlazorJS.WebWorkers" Version="2.5.39" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.7" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.7" PrivateAssets="all" /> + <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.7" /> + <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="9.0.7" /> + <PackageReference Include="SpawnDev.BlazorJS.WebWorkers" Version="2.17.1" /> </ItemGroup> <ItemGroup> diff --git a/MatrixUtils.Web/Pages/Dev/DevUtilities.razor b/MatrixUtils.Web/Pages/Dev/DevUtilities.razor
index 3b2d533..f6392a4 100644 --- a/MatrixUtils.Web/Pages/Dev/DevUtilities.razor +++ b/MatrixUtils.Web/Pages/Dev/DevUtilities.razor
@@ -1,5 +1,8 @@ @page "/Dev/Utilities" @using ArcaneLibs.Extensions +@using LibMatrix.EventTypes.Spec.Ephemeral +@using LibMatrix.EventTypes.Spec.State.RoomInfo +@using LibMatrix.Helpers @using MatrixUtils.Abstractions <h3>Debug Tools</h3> @@ -14,7 +17,7 @@ else { <summary>Room List</summary> @foreach (var roomId in Rooms) { <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@roomId.Replace('.', '~')"> - <RoomListItem RoomInfo="@(new RoomInfo(hs.GetRoom(roomId)))" LoadData="true"></RoomListItem> + <RoomListItem Homeserver="hs" RoomInfo="@(new RoomInfo(hs.GetRoom(roomId)))" LoadData="true"></RoomListItem> </a> } </details> @@ -61,6 +64,7 @@ else { StateHasChanged(); return; } + if (res.Content.Headers.ContentType.MediaType == "application/json") GetRequestResult = $"Error: {res.StatusCode}\n" + (await res.Content.ReadFromJsonAsync<object>()).ToJson(); else @@ -69,7 +73,32 @@ else { catch (Exception e) { GetRequestResult = $"Error: {e}"; } + StateHasChanged(); } + private async Task TestRoomBuilder() { + var rb = new RoomBuilder() { + HistoryVisibility = new RoomHistoryVisibilityEventContent() { HistoryVisibility = RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Shared }, + ImportantState = [ + new() { + RawContent = new() { + ["type"] = "m.room.name", + ["name"] = "Test Room" + } + }, + new() { + Type = "test", + TypedContent = new PresenceEventContent() { + Presence = "online", + LastActiveAgo = 0, + } + }, + + ] + }; + + await rb.Create(hs); + } + } \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Dev/WellKnownRes.razor b/MatrixUtils.Web/Pages/Dev/WellKnownRes.razor
index 1906dd8..c636c56 100644 --- a/MatrixUtils.Web/Pages/Dev/WellKnownRes.razor +++ b/MatrixUtils.Web/Pages/Dev/WellKnownRes.razor
@@ -8,7 +8,7 @@ <h3>Known Homeserver List</h3> <hr/> -<span>Room ID: <FancyTextBox @bind-Value="@RoomId"/><LinkButton OnClick="@Execute">Execute</LinkButton></span> +<span>Room ID: <FancyTextBox @bind-Value="@RoomId"/><LinkButton OnClickAsync="@Execute">Execute</LinkButton></span> <span>Stats:</span><br/> <span>Server count: @entries.Count</span><br/> diff --git a/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor b/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor
index 6a301bf..21b0972 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/HSAdmin.razor
@@ -1,5 +1,6 @@ @page "/HSAdmin" @using ArcaneLibs.Extensions +@using LibMatrix.Responses.Federation <h3>Homeserver Admininistration</h3> <hr/> diff --git a/MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor b/MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor
index 87600c6..ec2ec54 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/HSE/ManageExternalProfiles.razor
@@ -3,7 +3,7 @@ @using LibMatrix.Responses <h3>Manage external profiles</h3> -<LinkButton OnClick="AddAllLocalProfiles">Add local sessions</LinkButton> +<LinkButton OnClickAsync="AddAllLocalProfiles">Add local sessions</LinkButton> @foreach(var p in ExternalProfiles) { diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor
index d07ff08..594ff35 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/BlockMedia.razor
@@ -24,13 +24,13 @@ <pre>@MxcUri?.ToJson(ignoreNull: true)</pre> @if (Event is not null) { - <LinkButton OnClick="@RedactAllEvents">Redact all messages</LinkButton> + <LinkButton OnClickAsync="@RedactAllEvents">Redact all messages</LinkButton> } @if (Event?.Sender?.Split(':', 2)[1] == Homeserver?.ServerName) { <p>User is a local user!</p> - <LinkButton OnClick="@DeactivateUser">Deactivate User</LinkButton> - <LinkButton OnClick="@QuarantineMediaByUser">Quarantine all media</LinkButton> + <LinkButton OnClickAsync="@DeactivateUser">Deactivate User</LinkButton> + <LinkButton OnClickAsync="@QuarantineMediaByUser">Quarantine all media</LinkButton> } } diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor
index 3b3acac..999e331 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor
@@ -50,7 +50,7 @@ <br/> </details> - <LinkButton OnClick="@DeleteRoom">Execute</LinkButton> + <LinkButton OnClickAsync="@DeleteRoom">Execute</LinkButton> } @code { diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor
index 3e38ee2..5e45e5b 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor
@@ -70,7 +70,7 @@ } </p> <p> - <LinkButton OnClick="@(() => DeleteRoom(room))">Delete room</LinkButton> + <LinkButton OnClickAsync="@(() => DeleteRoom(room))">Delete room</LinkButton> <LinkButton target="_blank" href="@($"/HSAdmin/Synapse/ResyncState?roomId={room.RoomId}&via={room.RoomId.Split(':', 2)[1]}")">Resync state</LinkButton> </p> diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor
index f3faafa..c2446a2 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/SubTools/SynapseRoomStateResync.razor
@@ -16,7 +16,7 @@ <span>Via: </span> <InputText @bind-Value="@Via"></InputText> <br/> - <LinkButton OnClick="@Execute">Execute</LinkButton> + <LinkButton OnClickAsync="@Execute">Execute</LinkButton> } @if (Executing) { diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/UserList.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/UserList.razor
index a2ada30..54ac800 100644 --- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/UserList.razor +++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/UserList.razor
@@ -59,8 +59,8 @@ <br/> </p> <p> - <LinkButton OnClick="@(() => Login(user))">Log in</LinkButton> - @* <LinkButton OnClick="@(() => DeleteRoom(user))">Delete room</LinkButton> *@ + <LinkButton OnClickAsync="@(() => Login(user))">Log in</LinkButton> + @* <LinkButton OnClickAsync="@(() => DeleteRoom(user))">Delete room</LinkButton> *@ @* <LinkButton target="_blank" href="@($"/HSAdmin/Synapse/ResyncState?roomId={user.RoomId}&via={user.RoomId.Split(':', 2)[1]}")">Resync state</LinkButton> *@ </p> diff --git a/MatrixUtils.Web/Pages/Index.razor b/MatrixUtils.Web/Pages/Index.razor
index 509c634..82ee0f2 100644 --- a/MatrixUtils.Web/Pages/Index.razor +++ b/MatrixUtils.Web/Pages/Index.razor
@@ -4,6 +4,7 @@ @using LibMatrix @using ArcaneLibs @using System.Diagnostics +@using LibMatrix.Responses.Federation <PageTitle>Index</PageTitle> @@ -58,9 +59,9 @@ Small collection of tools to do not-so-everyday things. </td> <td> <p> - <LinkButton OnClick="@(() => ManageUser(session.SessionId))">Manage</LinkButton> - <LinkButton OnClick="@(() => RemoveUser(session.SessionId))">Remove</LinkButton> - <LinkButton OnClick="@(() => RemoveUser(session.SessionId, true))">Log out</LinkButton> + <LinkButton OnClickAsync="@(() => ManageUser(session.SessionId))">Manage</LinkButton> + <LinkButton OnClickAsync="@(() => RemoveUser(session.SessionId))">Remove</LinkButton> + <LinkButton OnClickAsync="@(() => RemoveUser(session.SessionId, true))">Log out</LinkButton> </p> </td> </tr> @@ -89,7 +90,7 @@ Small collection of tools to do not-so-everyday things. </p> </td> <td> - <LinkButton OnClick="@(() => RemoveUser(session.SessionId))">Remove</LinkButton> + <LinkButton OnClickAsync="@(() => RemoveUser(session.SessionId))">Remove</LinkButton> </td> </tr> } @@ -118,10 +119,10 @@ Small collection of tools to do not-so-everyday things. </p> </td> <td> - <LinkButton OnClick="@(() => Task.Run(() => NavigationManager.NavigateTo($"/InvalidSession?ctx={session.SessionId}")))">Re-login</LinkButton> + <LinkButton OnClickAsync="@(() => Task.Run(() => NavigationManager.NavigateTo($"/InvalidSession?ctx={session.SessionId}")))">Re-login</LinkButton> </td> <td> - <LinkButton OnClick="@(() => RemoveUser(session.SessionId))">Remove</LinkButton> + <LinkButton OnClickAsync="@(() => RemoveUser(session.SessionId))">Remove</LinkButton> </td> </tr> } diff --git a/MatrixUtils.Web/Pages/InvalidSession.razor b/MatrixUtils.Web/Pages/InvalidSession.razor
index 1ec99f6..f86d112 100644 --- a/MatrixUtils.Web/Pages/InvalidSession.razor +++ b/MatrixUtils.Web/Pages/InvalidSession.razor
@@ -8,14 +8,14 @@ @if (_auth is not null) { <p>It appears that the affected user is @_auth.UserId (@_auth.DeviceId) on @_auth.Homeserver!</p> - <LinkButton OnClick="@(OpenRefreshDialog)">Refresh token</LinkButton> - <LinkButton OnClick="@(RemoveUser)">Remove</LinkButton> + <LinkButton OnClickAsync="@(OpenRefreshDialog)">Refresh token</LinkButton> + <LinkButton OnClickAsync="@(RemoveUser)">Remove</LinkButton> @if (_showRefreshDialog) { <ModalWindow MinWidth="300" X="275" Y="300" Title="@($"Password for {_auth.UserId}")"> <FancyTextBox IsPassword="true" @bind-Value="@_password"></FancyTextBox> <br/> - <LinkButton OnClick="TryLogin">Log in</LinkButton> + <LinkButton OnClickAsync="TryLogin">Log in</LinkButton> @if (_loginException is not null) { <pre style="color: red;">@_loginException.RawContent</pre> } diff --git a/MatrixUtils.Web/Pages/Labs/Client/ClientComponents/ClientRoomList.razor b/MatrixUtils.Web/Pages/Labs/Client/ClientComponents/ClientRoomList.razor
index 8831dd1..56c8cfe 100644 --- a/MatrixUtils.Web/Pages/Labs/Client/ClientComponents/ClientRoomList.razor +++ b/MatrixUtils.Web/Pages/Labs/Client/ClientComponents/ClientRoomList.razor
@@ -1,7 +1,7 @@ @using ClientContext = MatrixUtils.Web.Pages.Labs.Client.Index.ClientContext @* user header and room list *@ @foreach (var room in Data.SyncWrapper.Rooms) { - <LinkButton OnClick="@(async () => Data.SelectedRoom = room)" Color="@(Data.SelectedRoom == room ? "#FF00FF" : "")"> + <LinkButton OnClickAsync="@(async () => Data.SelectedRoom = room)" Color="@(Data.SelectedRoom == room ? "#FF00FF" : "")"> @room.RoomName </LinkButton> <br/> diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor
index a974a8f..360548d 100644 --- a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor +++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor
@@ -28,8 +28,8 @@ </p> <br/> - <LinkButton OnClick="@Disband" Color="#FF0000">Disband</LinkButton> - <LinkButton OnClick="@Execute">Next</LinkButton> + <LinkButton OnClickAsync="@Disband" Color="#FF0000">Disband</LinkButton> + <LinkButton OnClickAsync="@Execute">Next</LinkButton> } else { <p>Discovering spaces, please wait...</p> diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor
index 9f31647..25d1629 100644 --- a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor +++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor
@@ -31,7 +31,7 @@ else { } <br/> -<LinkButton OnClick="@Execute">Next</LinkButton> +<LinkButton OnClickAsync="@Execute">Next</LinkButton> @{ var _offset = 0; @@ -41,7 +41,7 @@ else { <p>Found room assigned to multiple users: <RoomListItem RoomInfo="@room"></RoomListItem></p> <p>Users:</p> @foreach (var userProfileResponse in usersList) { - <LinkButton OnClick="@(() => SetRoomAssignment(room.Room.RoomId, userProfileResponse.Id))"> + <LinkButton OnClickAsync="@(() => SetRoomAssignment(room.Room.RoomId, userProfileResponse.Id))"> <span>Assign to </span> <InlineUserItem User="userProfileResponse"></InlineUserItem> </LinkButton> @@ -54,7 +54,7 @@ else { <ModalWindow Title="Re-assign DM" OnCloseClicked="@(() => DmToReassign = null)"> <RoomListItem RoomInfo="@DmToReassign"></RoomListItem> @foreach (var userProfileResponse in roomMembers[DmToReassign]) { - <LinkButton OnClick="@(() => SetRoomAssignment(DmToReassign.Room.RoomId, userProfileResponse.Id))"> + <LinkButton OnClickAsync="@(() => SetRoomAssignment(DmToReassign.Room.RoomId, userProfileResponse.Id))"> <span>Assign to </span> <InlineUserItem User="userProfileResponse"></InlineUserItem> </LinkButton> diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor
index e5a8b22..2bed22e 100644 --- a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor +++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor
@@ -59,7 +59,7 @@ else { } <br/> -<LinkButton OnClick="@Execute">Next</LinkButton> +<LinkButton OnClickAsync="@Execute">Next</LinkButton> @code { diff --git a/MatrixUtils.Web/Pages/LoginPage.razor b/MatrixUtils.Web/Pages/LoginPage.razor
index 88577a2..38ede74 100644 --- a/MatrixUtils.Web/Pages/LoginPage.razor +++ b/MatrixUtils.Web/Pages/LoginPage.razor
@@ -22,8 +22,8 @@ <FancyTextBox @bind-Value="@newRecordInput.Proxy"></FancyTextBox> </span> <br/> -<LinkButton OnClick="@AddRecord">Add account to queue</LinkButton> -<LinkButton OnClick="@(() => Login(newRecordInput))">Log in</LinkButton> +<LinkButton OnClickAsync="@AddRecord">Add account to queue</LinkButton> +<LinkButton OnClickAsync="@(() => Login(newRecordInput))">Log in</LinkButton> <br/> <br/> @@ -44,7 +44,7 @@ <FancyTextBox @bind-Value="@newRecordInput.Proxy"></FancyTextBox> </span> <br/> -<LinkButton OnClick="@(() => AddWithAccessToken(newRecordInput))">Add session</LinkButton> +<LinkButton OnClickAsync="@(() => AddWithAccessToken(newRecordInput))">Add session</LinkButton> <br/> <br/> @@ -101,7 +101,7 @@ } </table> <br/> -<LinkButton OnClick="@LoginAll">Log in</LinkButton> +<LinkButton OnClickAsync="@LoginAll">Log in</LinkButton> @code { diff --git a/MatrixUtils.Web/Pages/Rooms/Create2.razor b/MatrixUtils.Web/Pages/Rooms/Create2.razor new file mode 100644
index 0000000..19f6fbc --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/Create2.razor
@@ -0,0 +1,159 @@ +@page "/Rooms/Create2" +@using System.Text.Json +@using System.Reflection +@using ArcaneLibs +@using ArcaneLibs.Extensions +@using Blazored.LocalStorage +@using LibMatrix +@using LibMatrix.EventTypes.Spec.State.RoomInfo +@using LibMatrix.Helpers +@using LibMatrix.Responses +@using MatrixUtils.Web.Classes.RoomCreationTemplates +@using MatrixUtils.Web.Pages.Rooms.RoomCreateComponents +@inject ILogger<Create2> logger +@* @* ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not */ *@ + +<h3>Room Manager - Create Room</h3> + +@if (Ready) { + <style> + table.table-top-first-tr tr td:first-child { + vertical-align: top; + } + </style> + <table class="table-top-first-tr"> + @if (roomBuilder is RoomUpgradeBuilder roomUpgrade) { + <RoomCreateUpgradeOptions roomUpgrade="@roomUpgrade" PageStateHasChanged="@StateHasChanged"/> + } + else { + @* <tr style="padding-bottom: 16px;"> *@ + @* <td>Preset:</td> *@ + @* <td> *@ + @* @if (Presets is null) { *@ + @* <p style="color: red;">Presets is null!</p> *@ + @* } *@ + @* else { *@ + @* <p style="color: red;">Support for presets is currently disabled!</p> *@ + @* $1$ <InputSelect @bind-Value="@RoomPreset"> #1# *@ + @* $1$ @foreach (var createRoomRequest in Presets) { #1# *@ + @* $1$ <option value="@createRoomRequest.Key">@createRoomRequest.Key</option> #1# *@ + @* $1$ } #1# *@ + @* $1$ </InputSelect> #1# *@ + @* } *@ + @* </td> *@ + @* </tr> *@ + } + <RoomCreateBasicRoomInfoOptions roomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged" Homeserver="@Homeserver"/> + <RoomCreateCreateOptions roomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged" Homeserver="@Homeserver"/> + <RoomCreatePrivacyOptions roomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged" Homeserver="@Homeserver"/> + <RoomCreatePermissionsOptions roomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged" Homeserver="@Homeserver"/> + <RoomCreateMembershipOptions roomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged" Homeserver="@Homeserver"/> + @* Initial states, should remain at bottom *@ + </table> + <LinkButton OnClickAsync="@CreateRoom">Create room</LinkButton> + <br/> + <div + style="position: fixed; top: 56px; right: 0; width: fit-content; max-width: 25%; height: calc(100vh - 56px); overflow: auto; background-color: #2c3054; padding-right: 32px; border-left: 1px solid #ccc;"> + <details open> + <summary>RoomBuilder state</summary> + <InputCheckbox @bind-Value="@ShowNullInState"/> + <span>Show null values</span><br/> + <pre> + @roomBuilder.ToJson(ignoreNull: !ShowNullInState) + </pre> + </details> + </div> +} +@if (_matrixException is not null) { + <ModalWindow Title="@("Matrix exception: " + _matrixException.ErrorCode)"> + <pre> + @_matrixException.Message + </pre> + </ModalWindow> +} + +@code { + +#region State + + [Parameter, SupplyParameterFromQuery(Name = "previousRoomId")] + public string? PreviousRoomId { get; set; } + + private bool ShowNullInState { get; set; } + + private bool Ready { get; set; } + + private RoomBuilder roomBuilder { get; set; } = new(); + + private AuthenticatedHomeserverGeneric? Homeserver { get; set; } + + private MatrixException? _matrixException { get; set; } + +#endregion + +#region Presets + + private Dictionary<string, CreateRoomRequest>? Presets { get; set; } = new(); + // private string RoomPreset { + // get => Presets.ContainsValue(roomBuilder) ? Presets.First(x => x.Value == roomBuilder).Key : "Not a preset"; + // set { + // roomBuilder = Presets[value]; + // JsonChanged(); + // StateHasChanged(); + // } + // } + +#endregion + + protected override async Task OnInitializedAsync() { + Homeserver = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true); + if (Homeserver is null) return; + if (!string.IsNullOrWhiteSpace(PreviousRoomId)) { + roomBuilder = new RoomUpgradeBuilder(Homeserver.GetRoom(PreviousRoomId)); + } + + roomBuilder.ServerAcls.Allow = ["*"]; + roomBuilder.ServerAcls.Deny = []; + + // foreach (var x in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Contains(typeof(IRoomCreationTemplate))).ToList()) { + // Console.WriteLine($"Found room creation template in class: {x.FullName}"); + // var instance = (IRoomCreationTemplate)Activator.CreateInstance(x); + // Presets[instance.Name] = instance.CreateRoomRequest; + // } + // + // Presets = Presets.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); + + // if (!Presets.ContainsKey("Default")) { + // Console.WriteLine($"No default room found in {Presets.Count} presets: {string.Join(", ", Presets.Keys)}"); + // } + // else RoomPreset = "Default"; + + Ready = true; + StateHasChanged(); + if (roomBuilder is RoomUpgradeBuilder roomUpgrade) { + await roomUpgrade.ImportAsync().ConfigureAwait(false); + StateHasChanged(); + } + } + + protected override bool ShouldRender() { + if (roomBuilder.Type == "") + roomBuilder.Type = null; // Reset to null if empty, so it doesn't get sent as an empty string + var result = base.ShouldRender(); + logger.LogInformation("ShouldRender: " + result); + return result; + } + + private async Task CreateRoom() { + Console.WriteLine("Create room"); + Console.WriteLine(roomBuilder.ToJson()); + roomBuilder.AdditionalCreationContent["gay.rory.created_using"] = "Rory&::MatrixUtils (https://mru.rory.gay)"; + try { + var newRoom = await roomBuilder.Create(Homeserver); + } + catch (MatrixException e) { + _matrixException = e; + } + } + +} diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
index 96879b8..cdb5894 100644 --- a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor +++ b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
@@ -8,190 +8,118 @@ @using System.Reflection @using ArcaneLibs.Attributes @using LibMatrix.EventTypes -@using LibMatrix.EventTypes.Common @using LibMatrix.EventTypes.Interop.Draupnir @using LibMatrix.EventTypes.Spec.State.RoomInfo - -@using MatrixUtils.Web.Shared.PolicyEditorComponents @using SpawnDev.BlazorJS.WebWorkers +@using MatrixUtils.Web.Pages.Rooms.PolicyListComponents @inject WebWorkerService WebWorkerService +@inject ILogger<PolicyList> logger -<h3>Policy list editor - Editing @(RoomName ?? RoomId)</h3> -@if (!string.IsNullOrWhiteSpace(DraupnirShortcode)) { - <span style="margin-right: 2em;">Shortcode: @DraupnirShortcode</span> -} -@if (!string.IsNullOrWhiteSpace(RoomAlias)) { - <span>Alias: @RoomAlias</span> -} -<hr/> -@* <InputCheckbox @bind-Value="EnableAvatars"></InputCheckbox><label>Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!)</label> *@ -<LinkButton OnClick="@(() => { - CurrentlyEditingEvent = new() { Type = "", RawContent = new() }; - return Task.CompletedTask; - })">Create new policy -</LinkButton> -<LinkButton OnClick="@(() => { - MassCreatePolicies = true; - return Task.CompletedTask; - })">Create many new policies -</LinkButton> -<LinkButton OnClick="@LoadStatesAsync">Refresh</LinkButton> - -@if (Loading) { - <p>Loading...</p> -} -else if (PolicyEventsByType is not { Count: > 0 }) { - <p>No policies yet</p> +@if (!IsInitialised) { + <p>Connecting to homeserver...</p> } else { - var renderSw = Stopwatch.StartNew(); - var renderTotalSw = Stopwatch.StartNew(); - @foreach (var (type, value) in PolicyEventsByType) { - <p> - @(GetValidPolicyEventsByType(type).Count) active, - @(GetInvalidPolicyEventsByType(type).Count) invalid, - @(GetRemovedPolicyEventsByType(type).Count) removed - (@value.Count total) - @(GetPolicyTypeName(type).ToLower()) - </p> - } - - Console.WriteLine($"Rendered header in {renderSw.GetElapsedAndRestart()}"); - - var renderSw2 = Stopwatch.StartNew(); - IOrderedEnumerable<Type> policiesByType = KnownPolicyTypes.Where(t => GetPolicyEventsByType(t).Count > 0).OrderByDescending(t => GetPolicyEventsByType(t).Count); - Console.WriteLine($"Ordered policy types by count in {renderSw2.GetElapsedAndRestart()}"); - - foreach (var type in policiesByType) { - <details> - <summary> - <span> - @($"{GetPolicyTypeName(type)}: {GetPolicyEventsByType(type).Count} policies") - </span> - <hr style="margin: revert;"/> - </summary> - <table class="table table-striped table-hover"> - @{ - var renderSw3 = Stopwatch.StartNew(); - var policies = GetValidPolicyEventsByType(type); - var invalidPolicies = GetInvalidPolicyEventsByType(type); - // enumerate all properties with friendly name - var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(x => (x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyNameOrNull()) is not null) - .Where(x => x.GetCustomAttribute<TableHideAttribute>() is null) - .ToFrozenSet(); - var propNames = props.Select(x => x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyName()!).ToFrozenSet(); - - var proxySafeProps = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(x => props.Any(y => y.Name == x.Name)) - .ToFrozenSet(); - Console.WriteLine($"{proxySafeProps?.Count} proxy safe props found in {policies.FirstOrDefault()?.TypedContent?.GetType()}"); - Console.WriteLine($"Filtered policies and got properties in {renderSw3.GetElapsedAndRestart()}"); - } - <thead> - <tr> - @foreach (var name in propNames) { - <th>@name</th> - } - <th>Actions</th> - </tr> - </thead> - <tbody style="border-width: 1px;"> - @foreach (var policy in policies.OrderBy(x => x.RawContent?["entity"]?.GetValue<string>())) { - <tr> - @{ - var typedContent = policy.TypedContent! as PolicyRuleEventContent; - } - @foreach (var prop in proxySafeProps ?? Enumerable.Empty<PropertyInfo>()) { - if (prop.Name == "Entity") { - <td>@TruncateMxid(typedContent!.Entity)</td> - } - else { - <td>@prop.GetGetMethod()?.Invoke(typedContent, null)</td> - } - } - <td> - <div style="display: ruby;"> - @if (PowerLevels.UserHasStatePermission(Homeserver.WhoAmI.UserId, policy.Type)) { - <LinkButton OnClick="@(() => { - CurrentlyEditingEvent = policy; - return Task.CompletedTask; - })">Edit - </LinkButton> - <LinkButton OnClick="@(() => RemovePolicyAsync(policy))">Remove</LinkButton> - @if (policy.IsLegacyType) { - <LinkButton OnClick="@(() => RemovePolicyAsync(policy))">Update policy type</LinkButton> - } - - @if (PolicyTypeIds[typeof(ServerPolicyRuleEventContent)].Contains(policy.Type)) { - <LinkButton OnClick="@(() => { - ServerPolicyToMakePermanent = policy; - return Task.CompletedTask; - })">Make permanent - </LinkButton> - @if (CurrentUserIsDraupnir) { - <LinkButton Color="@(ActiveKicks.ContainsKey(policy) ? "#FF0000" : null)" OnClick="@(() => DraupnirKickMatching(policy))">Kick - users @(ActiveKicks.TryGetValue(policy, out var kick) ? $"({kick})" : null) - </LinkButton> - } - } - } - else { - <p>No permission to modify</p> - } - </div> - </td> - </tr> - } - </tbody> - </table> - <details> - <summary> - <u> - @("Invalid " + GetPolicyTypeName(type).ToLower()) - </u> - </summary> - <table class="table table-striped table-hover"> - <thead> - <tr> - <th>State key</th> - <th>Json contents</th> - </tr> - </thead> - <tbody> - @foreach (var policy in invalidPolicies) { - <tr> - <td>@policy.StateKey</td> - <td> - <pre>@policy.RawContent.ToJson(true, false)</pre> - </td> - </tr> - } - </tbody> - </table> - </details> - </details> + <PolicyListEditorHeader Room="@Room" ReloadStateAsync="@(() => LoadStateAsync(true))"></PolicyListEditorHeader> + @if (Loading) { + <p>Loading...</p> } + // else if (PolicyEventsByType is not { Count: > 0 }) { + @* <p>No policies yet</p> *@ + // } + else { + var renderSw = Stopwatch.StartNew(); + var renderTotalSw = Stopwatch.StartNew(); + @foreach (var value in PolicyCollections.Values.OrderByDescending(x => x.TotalCount)) { + <p> + @value.ActivePolicies.Count active, + @value.RemovedPolicies.Count removed + (@value.TotalCount total) + @value.Name.ToLower() + </p> + } - Console.WriteLine($"Rendered policies in {renderSw.GetElapsedAndRestart()}"); - Console.WriteLine($"Rendered in {renderTotalSw.Elapsed}"); -} + // logger.LogInformation($"Rendered header in {renderSw.GetElapsedAndRestart()}"); -@if (CurrentlyEditingEvent is not null) { - <PolicyEditorModal PolicyEvent="@CurrentlyEditingEvent" OnClose="@(() => CurrentlyEditingEvent = null)" OnSave="@(e => UpdatePolicyAsync(e))"></PolicyEditorModal> -} + // var renderSw2 = Stopwatch.StartNew(); + // IOrderedEnumerable<Type> policiesByType = KnownPolicyTypes.Where(t => GetPolicyEventsByType(t).Count > 0).OrderByDescending(t => GetPolicyEventsByType(t).Count); + // logger.LogInformation($"Ordered policy types by count in {renderSw2.GetElapsedAndRestart()}"); -@if (ServerPolicyToMakePermanent is not null) { - <ModalWindow Title="Make policy permanent"> - - </ModalWindow> -} + foreach (var collection in PolicyCollections.Values.OrderByDescending(x => x.ActivePolicies.Count)) { + <PolicyListCategoryComponent PolicyCollection="@collection" Room="@Room"></PolicyListCategoryComponent> + } -@if (MassCreatePolicies) { - <MassPolicyEditorModal Room="@Room" OnClose="@(() => MassCreatePolicies = false)" OnSaved="@(() => { - MassCreatePolicies = false; - _ = LoadStatesAsync(); - })"></MassPolicyEditorModal> + // foreach (var type in policiesByType) { + @* foreach (var type in (List<Type>) []) { *@ + @* <details> *@ + @* <summary> *@ + @* <span> *@ + @* @($"{GetPolicyTypeName(type)}: {GetPolicyEventsByType(type).Count} policies") *@ + @* </span> *@ + @* <hr style="margin: revert;"/> *@ + @* </summary> *@ + @* <table class="table table-striped table-hover table-bordered align-middle"> *@ + @* @{ *@ + @* var renderSw3 = Stopwatch.StartNew(); *@ + @* var policies = GetValidPolicyEventsByType(type); *@ + @* var invalidPolicies = GetInvalidPolicyEventsByType(type); *@ + @* // enumerate all properties with friendly name *@ + @* var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) *@ + @* .Where(x => (x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyNameOrNull()) is not null) *@ + @* .Where(x => x.GetCustomAttribute<TableHideAttribute>() is null) *@ + @* .ToFrozenSet(); *@ + @* var propNames = props.Select(x => x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyName()!).ToFrozenSet(); *@ + @* *@ + @* var proxySafeProps = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) *@ + @* .Where(x => props.Any(y => y.Name == x.Name)) *@ + @* .ToFrozenSet(); *@ + @* logger.LogInformation($"{proxySafeProps?.Count} proxy safe props found in {policies.FirstOrDefault()?.TypedContent?.GetType()}"); *@ + @* logger.LogInformation($"Filtered policies and got properties in {renderSw3.GetElapsedAndRestart()}"); *@ + @* } *@ + @* <thead> *@ + @* <tr> *@ + @* @foreach (var name in propNames) { *@ + @* <th>@name</th> *@ + @* } *@ + @* <th>Actions</th> *@ + @* </tr> *@ + @* </thead> *@ + @* <tbody> *@ + @* @foreach (var policy in policies.OrderBy(x => x.RawContent?["entity"]?.GetValue<string>())) { *@ + @* <PolicyListRowComponent PolicyInfo="@policy" Room="@Room"></PolicyListRowComponent> *@ + @* } *@ + @* </tbody> *@ + @* </table> *@ + @* <details> *@ + @* <summary> *@ + @* <u> *@ + @* @("Invalid " + GetPolicyTypeName(type).ToLower()) *@ + @* </u> *@ + @* </summary> *@ + @* <table class="table table-striped table-hover"> *@ + @* <thead> *@ + @* <tr> *@ + @* <th>State key</th> *@ + @* <th>Json contents</th> *@ + @* </tr> *@ + @* </thead> *@ + @* <tbody> *@ + @* @foreach (var policy in invalidPolicies) { *@ + @* <tr> *@ + @* <td>@policy.StateKey</td> *@ + @* <td> *@ + @* <pre>@policy.RawContent.ToJson(true, false)</pre> *@ + @* </td> *@ + @* </tr> *@ + @* } *@ + @* </tbody> *@ + @* </table> *@ + @* </details> *@ + @* </details> *@ + // } + + // logger.LogInformation($"Rendered policies in {renderSw.GetElapsedAndRestart()}"); + logger.LogInformation($"Rendered in {renderTotalSw.Elapsed}"); + } } @code { @@ -202,6 +130,7 @@ else { private const bool Debug = false; #endif + private bool IsInitialised { get; set; } = false; private bool Loading { get; set; } = true; [Parameter] @@ -209,14 +138,6 @@ else { private Dictionary<Type, List<StateEventResponse>> PolicyEventsByType { get; set; } = new(); - private StateEventResponse? CurrentlyEditingEvent { - get; - set { - field = value; - StateHasChanged(); - } - } - public StateEventResponse? ServerPolicyToMakePermanent { get; set { @@ -225,22 +146,12 @@ else { } } - private AuthenticatedHomeserverGeneric Homeserver { get; set; } - private GenericRoom Room { get; set; } - private RoomPowerLevelEventContent PowerLevels { get; set; } + private AuthenticatedHomeserverGeneric Homeserver { get; set; } = null!; + private GenericRoom Room { get; set; } = null!; + private RoomPowerLevelEventContent PowerLevels { get; set; } = null!; public bool CurrentUserIsDraupnir { get; set; } - public string? RoomName { get; set; } - public string? RoomAlias { get; set; } - public string? DraupnirShortcode { get; set; } - public Dictionary<StateEventResponse, int> ActiveKicks { get; set; } = []; - public bool MassCreatePolicies { - get; - set { - field = value; - StateHasChanged(); - } - } + public Dictionary<StateEventResponse, int> ActiveKicks { get; set; } = []; protected override async Task OnInitializedAsync() { var sw = Stopwatch.StartNew(); @@ -248,64 +159,234 @@ else { Homeserver = (await sessionStore.GetCurrentHomeserver(navigateOnFailure: true))!; if (Homeserver is null) return; Room = Homeserver.GetRoom(RoomId!); + IsInitialised = true; + StateHasChanged(); await Task.WhenAll( Task.Run(async () => { PowerLevels = (await Room.GetPowerLevelsAsync())!; }), - Task.Run(async () => { DraupnirShortcode = (await Room.GetStateOrNullAsync<MjolnirShortcodeEventContent>(MjolnirShortcodeEventContent.EventId))?.Shortcode; }), - Task.Run(async () => { RoomAlias = (await Room.GetCanonicalAliasAsync())?.Alias; }), - Task.Run(async () => { RoomName = await Room.GetNameOrFallbackAsync(); }), Task.Run(async () => { CurrentUserIsDraupnir = (await Homeserver.GetAccountDataOrNullAsync<object>(DraupnirProtectedRoomsData.EventId)) is not null; }) ); StateHasChanged(); - await LoadStatesAsync(); - Console.WriteLine($"Policy list editor initialized in {sw.Elapsed}!"); + await LoadStateAsync(firstLoad: true); + Loading = false; + logger.LogInformation($"Policy list editor initialized in {sw.Elapsed}!"); } - private async Task LoadStatesAsync() { + private async Task LoadStateAsync(bool firstLoad = false) { + var sw = Stopwatch.StartNew(); + // Loading = true; + // var states = Room.GetFullStateAsync(); + var states = await Room.GetFullStateAsListAsync(); + // PolicyEventsByType.Clear(); + + logger.LogInformation($"LoadStatesAsync: Loaded state in {sw.Elapsed}"); + + foreach (var type in KnownPolicyTypes) { + if (!PolicyCollections.ContainsKey(type)) { + var filterPropSw = Stopwatch.StartNew(); + // enumerate all properties with friendly name + var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => (x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyNameOrNull()) is not null) + .Where(x => x.GetCustomAttribute<TableHideAttribute>() is null) + .ToFrozenSet(); + + var proxySafeProps = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => props.Any(y => y.Name == x.Name)) + .ToFrozenDictionary(x => x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyName(), x => x); + logger.LogInformation($"{proxySafeProps?.Count} proxy safe props found in {type.FullName} ({filterPropSw.Elapsed})"); + PolicyCollections.Add(type, new() { + Name = type.GetFriendlyNamePluralOrNull() ?? type.FullName ?? type.Name, + ActivePolicies = [], + RemovedPolicies = [], + PropertiesToDisplay = proxySafeProps + }); + } + } + + var count = 0; + var parseSw = Stopwatch.StartNew(); + foreach (var evt in states) { + var sw2 = Stopwatch.StartNew(); + var mappedType = evt.MappedType; + logger.LogInformation($"Processing state #{count++:000000} {evt.Type} @ {sw.Elapsed} (took {parseSw.Elapsed:c} so far to process)"); + if (!mappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue; + + var collection = PolicyCollections[mappedType]; + + var key = (evt.Type, evt.StateKey!); + var policyInfo = new PolicyCollection.PolicyInfo { + Policy = evt, + MadeRedundantBy = [] + }; + if (evt.RawContent is null or { Count: 0 } || string.IsNullOrWhiteSpace(evt.RawContent?["recommendation"]?.GetValue<string>())) { + collection.ActivePolicies.Remove(key); + if (!collection.RemovedPolicies.TryAdd(key, policyInfo)) { + if (StateEvent.Equals(collection.RemovedPolicies[key].Policy, evt)) continue; + collection.RemovedPolicies[key] = policyInfo; + } + } + else { + collection.RemovedPolicies.Remove(key); + if (!collection.ActivePolicies.TryAdd(key, policyInfo)) { + if (StateEvent.Equals(collection.ActivePolicies[key].Policy, evt)) continue; + collection.ActivePolicies[key] = policyInfo; + } + } + } + + logger.LogInformation($"LoadStatesAsync: Processed state in {sw.Elapsed}"); + foreach (var collection in PolicyCollections) { + logger.LogInformation($"Policy collection {collection.Key.FullName} has {collection.Value.ActivePolicies.Count} active and {collection.Value.RemovedPolicies.Count} removed policies."); + } + + logger.LogInformation("LoadStatesAsync: Scanning for redundant policies..."); + + Loading = false; + var allPolicies = PolicyCollections.Values + .SelectMany(x => x.ActivePolicies.Values) + .Select(x => (x, x.Policy.TypedContent as PolicyRuleEventContent)) + .ToList(); + var wildcardPolicies = allPolicies + .Where(x => x.Item2!.IsGlobRule() || x.Item2 is ServerPolicyRuleEventContent) + .ToList(); + Console.WriteLine($"Got {allPolicies.Count} total policies, {wildcardPolicies.Count} wildcard policies."); + int i = 0; + int hits = 0; + int redundant = 0; + int duplicates = 0; + foreach (var policy in allPolicies) { + if (policy.Item2 is null) continue; + var matchingPolicies = wildcardPolicies + .Where(x => + !StateEvent.TypeKeyPairMatches(policy.x.Policy, x.x.Policy) + && x.Item2!.EntityMatches(policy.Item2.Entity!) + ) + .ToList(); + + if (matchingPolicies.Count > 0) { + logger.LogInformation($"{i} Got {matchingPolicies.Count} hits for {policy.x.Policy.RawContent.ToJson()}: {matchingPolicies.Select(x => x.x.Policy.RawContent).ToJson()}"); + foreach (var match in matchingPolicies) { + policy.x.MadeRedundantBy.Add(match.x.Policy); + } + + hits++; + redundant += matchingPolicies.Count; + + if (hits % 5 == 0) + StateHasChanged(); + } + else logger.LogInformation("Sleeping..."); + await Task.Delay(1); + i++; + } + + i = 0; + foreach (var policy in allPolicies) { + if (policy.Item2 is null) continue; + var matchingPolicies = allPolicies + .Where(x => + !StateEvent.TypeKeyPairMatches(policy.x.Policy, x.x.Policy) + && x.Item2!.Entity == policy.Item2.Entity! + ) + .ToList(); + + if (matchingPolicies.Count > 0) { + logger.LogInformation($"{i} Got {matchingPolicies.Count} duplicates for {policy.x.Policy.RawContent.ToJson()}: {matchingPolicies.Select(x => x.x.Policy.RawContent).ToJson()}"); + foreach (var match in matchingPolicies) { + policy.x.MadeRedundantBy.Add(match.x.Policy); + } + + hits++; + redundant += matchingPolicies.Count; + + if (hits % 5 == 0) + StateHasChanged(); + } + else logger.LogInformation("Sleeping..."); + await Task.Delay(1); + i++; + } + + logger.LogInformation($"LoadStatesAsync: Found {hits} ({redundant} redundant, {duplicates} duplicates) redundant policies in {sw.Elapsed}"); + StateHasChanged(); + } + + // the old one: + private async Task LoadStatesAsync(bool firstLoad = false) { + await LoadStateAsync(firstLoad); + return; var sw = Stopwatch.StartNew(); Loading = true; // var states = Room.GetFullStateAsync(); var states = await Room.GetFullStateAsListAsync(); // PolicyEventsByType.Clear(); - Console.WriteLine($"LoadStatesAsync: Loaded state in {sw.Elapsed}"); + logger.LogInformation($"LoadStatesAsync: Loaded state in {sw.Elapsed}"); - foreach (var state in states) { - if (state is null) continue; - if (!state.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue; + foreach (var type in KnownPolicyTypes) { + if (!PolicyEventsByType.ContainsKey(type)) + PolicyEventsByType.Add(type, new List + <StateEventResponse>(16000)); + } - if (!PolicyEventsByType.ContainsKey(state.MappedType)) PolicyEventsByType.Add(state.MappedType, new()); + int count = 0; + foreach (var state in states) { + var _spsw = Stopwatch.StartNew(); + TimeSpan e1, e2, e3, e4, e5, e6, t; + if (!state.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue; + e1 = _spsw.Elapsed; var targetPolicies = PolicyEventsByType[state.MappedType]; + e2 = _spsw.Elapsed; + if (!firstLoad && targetPolicies.FirstOrDefault(x => StateEvent.TypeKeyPairMatches(x, state)) is { } evt) { + e3 = _spsw.Elapsed; + if (StateEvent.Equals(evt, state)) { + if (count % 100 == 0) { + await Task.Delay(10); + await Task.Yield(); + } - if (targetPolicies.FirstOrDefault(x => StateEvent.TypeKeyPairMatches(x, state)) is { } evt) { - if (StateEvent.Equals(evt, state)) continue; + e4 = _spsw.Elapsed; + logger.LogInformation($"[E] LoadStatesAsync: Processed state #{count++:000000} {state.Type} @ {sw.Elapsed} (e1={e1:c}, e2={e2:c}, e3={e3:c}, e4={e4:c}, e5={TimeSpan.Zero:c},t={_spsw.Elapsed:c})"); + continue; + } + + e4 = _spsw.Elapsed; targetPolicies.Remove(evt); + e5 = _spsw.Elapsed; + targetPolicies.Add(state); + e6 = _spsw.Elapsed; + t = _spsw.Elapsed; + logger.LogInformation($"[M] LoadStatesAsync: Processed state #{count++:000000} {state.Type} @ {sw.Elapsed} (e1={e1:c}, e2={e2:c}, e3={e3:c}, e4={e4:c}, e5={e5:c}, e6={e6:c},t={t:c})"); + } + else { targetPolicies.Add(state); + t = _spsw.Elapsed; + logger.LogInformation($"[N] LoadStatesAsync: Processed state #{count++:000000} {state.Type} @ {sw.Elapsed} (e1={e1:c}, e2={e2:c}, e3={TimeSpan.Zero:c}, e4={TimeSpan.Zero:c}, e5={TimeSpan.Zero:c}, e6={TimeSpan.Zero:c}, t={t:c})"); } - else targetPolicies.Add(state); + + // await Task.Delay(10); + // await Task.Yield(); } - Console.WriteLine($"LoadStatesAsync: Processed state in {sw.Elapsed}"); + logger.LogInformation($"LoadStatesAsync: Processed state in {sw.Elapsed}"); Loading = false; StateHasChanged(); await Task.Delay(10); await Task.Yield(); - Console.WriteLine($"LoadStatesAsync: yield finished in {sw.Elapsed}"); + logger.LogInformation($"LoadStatesAsync: yield finished in {sw.Elapsed}"); } - // private List<StateEventResponse> AllPolicies { get; set; } = []; - private List<StateEventResponse> GetPolicyEventsByType(Type type) => PolicyEventsByType.ContainsKey(type) ? PolicyEventsByType[type] : []; - private List<StateEventResponse> GetValidPolicyEventsByType(Type type) => GetPolicyEventsByType(type) - .Where(x => !string.IsNullOrWhiteSpace(x.RawContent?["recommendation"]?.GetValue<string>())).ToList(); - - private List<StateEventResponse> GetInvalidPolicyEventsByType(Type type) => GetPolicyEventsByType(type) - .Where(x => x.RawContent is { Count: > 0 } && string.IsNullOrWhiteSpace(x.RawContent?["recommendation"]?.GetValue<string>())).ToList(); - - private List<StateEventResponse> GetRemovedPolicyEventsByType(Type type) => GetPolicyEventsByType(type) - .Where(x => x.RawContent is null or { Count: 0 }).ToList(); + // private List<StateEventResponse> GetValidPolicyEventsByType(Type type) => GetPolicyEventsByType(type) + // .Where(x => !string.IsNullOrWhiteSpace(x.RawContent?["recommendation"]?.GetValue<string>())).ToList(); + // + // private List<StateEventResponse> GetInvalidPolicyEventsByType(Type type) => GetPolicyEventsByType(type) + // .Where(x => x.RawContent is { Count: > 0 } && string.IsNullOrWhiteSpace(x.RawContent?["recommendation"]?.GetValue<string>())).ToList(); + // + // private List<StateEventResponse> GetRemovedPolicyEventsByType(Type type) => GetPolicyEventsByType(type) + // .Where(x => x.RawContent is null or { Count: 0 }).ToList(); private string? GetPolicyTypeNameOrNull(Type type) => type.GetFriendlyNamePluralOrNull() ?? type.GetCustomAttributes<MatrixEventAttribute>() @@ -313,23 +394,6 @@ else { private string GetPolicyTypeName(Type type) => GetPolicyTypeNameOrNull(type) ?? type.Name; - private async Task RemovePolicyAsync(StateEventResponse policyEvent) { - await Room.SendStateEventAsync(policyEvent.Type, policyEvent.StateKey, new { }); - PolicyEventsByType[policyEvent.MappedType].Remove(policyEvent); - await LoadStatesAsync(); - } - - private async Task UpdatePolicyAsync(StateEventResponse policyEvent) { - await Room.SendStateEventAsync(policyEvent.Type, policyEvent.StateKey, policyEvent.RawContent); - CurrentlyEditingEvent = null; - await LoadStatesAsync(); - } - - private async Task UpgradePolicyAsync(StateEventResponse policyEvent) { - policyEvent.RawContent["gay.rory.matrixutils.upgraded_from_type"] = policyEvent.Type; - await LoadStatesAsync(); - } - private static FrozenSet<Type> KnownPolicyTypes = StateEvent.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))).ToFrozenSet(); // event types, unnamed @@ -339,154 +403,28 @@ else { private static Dictionary<Type, string[]> PolicyTypeIds = KnownPolicyTypes .ToDictionary(x => x, x => x.GetCustomAttributes<MatrixEventAttribute>().Select(y => y.EventName).ToArray()); - private static string TruncateMxid(string? mxid) { - if (string.IsNullOrWhiteSpace(mxid)) return mxid; - var parts = mxid.Split(':', 2); - if (parts[0].Length > 50) - parts[0] = parts[0][..50] + "[...]"; - - if (parts is [_, { Length: > 50 }]) - parts[1] = parts[1][..50] + "[...]"; - - return parts.Length == 1 ? parts[0] : $"{parts[0]}:{parts[1]}"; - } - - private struct PolicyStats { - public int Active { get; set; } - public int Invalid { get; set; } - public int Removed { get; set; } - } - -#region Draupnir interop - - private SemaphoreSlim ss = new(16, 16); - - private async Task DraupnirKickMatching(StateEventResponse policy) { - try { - var content = policy.TypedContent! as PolicyRuleEventContent; - if (content is null) return; - if (string.IsNullOrWhiteSpace(content.Entity)) return; - - var data = await Homeserver.GetAccountDataAsync<DraupnirProtectedRoomsData>(DraupnirProtectedRoomsData.EventId); - var rooms = data.Rooms.Select(Homeserver.GetRoom).ToList(); + Dictionary<Type, PolicyCollection> PolicyCollections { get; set; } = new(); - ActiveKicks.Add(policy, rooms.Count); - StateHasChanged(); - await Task.Delay(500); - - // for (int i = 0; i < 12; i++) { - // _ = WebWorkerService.TaskPool.Invoke(WasteCpu); - // } - - // static async Task runKicks(string roomId, PolicyRuleEventContent content) { - // Console.WriteLine($"Checking {roomId}..."); - // // Console.WriteLine($"Checking {room.RoomId}..."); - // // - // // try { - // // var members = await room.GetMembersListAsync(); - // // foreach (var member in members) { - // // var membership = member.ContentAs<RoomMemberEventContent>(); - // // if (member.StateKey == room.Homeserver.WhoAmI.UserId) continue; - // // if (membership?.Membership is "leave" or "ban") continue; - // // - // // if (content.EntityMatches(member.StateKey!)) - // // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given"); - // // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)"); - // // } - // // } - // // finally { - // // Console.WriteLine($"Finished checking {room.RoomId}..."); - // // } - // } - // - // try { - // var tasks = rooms.Select(room => WebWorkerService.TaskPool.Invoke(runKicks, room.RoomId, content)).ToList(); - // - // await Task.WhenAll(tasks); - // } - // catch (Exception e) { - // Console.WriteLine(e); - // } - - await NastyInternalsPleaseIgnore.ExecuteKickWithWasmWorkers(WebWorkerService, Homeserver, policy, data.Rooms); - // await Task.Run(async () => { - // foreach (var room in rooms) { - // try { - // Console.WriteLine($"Checking {room.RoomId}..."); - // var members = await room.GetMembersListAsync(); - // foreach (var member in members) { - // var membership = member.ContentAs<RoomMemberEventContent>(); - // if (member.StateKey == room.Homeserver.WhoAmI.UserId) continue; - // if (membership?.Membership is "leave" or "ban") continue; - // - // if (content.EntityMatches(member.StateKey!)) - // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given"); - // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)"); - // } - // ActiveKicks[policy]--; - // StateHasChanged(); - // } - // finally { - // Console.WriteLine($"Finished checking {room.RoomId}..."); - // } - // } - // }); - } - finally { - ActiveKicks.Remove(policy); - StateHasChanged(); - await Task.Delay(500); - } - } + public struct PolicyCollection { + public required string Name { get; init; } + public int TotalCount => ActivePolicies.Count + RemovedPolicies.Count; -#region Nasty, nasty internals, please ignore! + public required Dictionary<(string Type, string StateKey), PolicyInfo> ActivePolicies { get; set; } - private static class NastyInternalsPleaseIgnore { - public static async Task ExecuteKickWithWasmWorkers(WebWorkerService workerService, AuthenticatedHomeserverGeneric hs, StateEventResponse evt, List<string> roomIds) { - try { - // var tasks = roomIds.Select(roomId => workerService.TaskPool.Invoke(ExecuteKickInternal, hs.WellKnownUris.Client, hs.AccessToken, roomId, content.Entity)).ToList(); - var tasks = roomIds.Select(roomId => workerService.TaskPool.Invoke(ExecuteKickInternal2, hs.WellKnownUris, hs.AccessToken, roomId, evt)).ToList(); - // workerService.TaskPool.Invoke(ExecuteKickInternal, hs.BaseUrl, hs.AccessToken, roomIds, content.Entity); - await Task.WhenAll(tasks); - } - catch (Exception e) { - Console.WriteLine(e); - } - } - - private static async Task ExecuteKickInternal(string homeserverBaseUrl, string accessToken, string roomId, string entity) { - try { - Console.WriteLine("args: " + string.Join(", ", homeserverBaseUrl, accessToken, roomId, entity)); - Console.WriteLine($"Checking {roomId}..."); - var hs = new AuthenticatedHomeserverGeneric(homeserverBaseUrl, new() { Client = homeserverBaseUrl }, null, accessToken); - Console.WriteLine($"Got HS..."); - var room = hs.GetRoom(roomId); - Console.WriteLine($"Got room..."); - var members = await room.GetMembersListAsync(); - Console.WriteLine($"Got members..."); - // foreach (var member in members) { - // var membership = member.ContentAs<RoomMemberEventContent>(); - // if (member.StateKey == hs.WhoAmI.UserId) continue; - // if (membership?.Membership is "leave" or "ban") continue; - // - // if (entity == member.StateKey) - // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given"); - // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)"); - // } - } - catch (Exception e) { - Console.WriteLine(e); - } - } + // public Dictionary<(string Type, string StateKey), StateEventResponse> InvalidPolicies { get; set; } + public required Dictionary<(string Type, string StateKey), PolicyInfo> RemovedPolicies { get; set; } + public required FrozenDictionary<string, PropertyInfo> PropertiesToDisplay { get; set; } - private async static Task ExecuteKickInternal2(HomeserverResolverService.WellKnownUris wellKnownUris, string accessToken, string roomId, StateEventResponse policy) { - Console.WriteLine($"Checking {roomId}..."); - Console.WriteLine(policy.EventId); + public struct PolicyInfo { + public required StateEventResponse Policy { get; init; } + public required List<StateEventResponse> MadeRedundantBy { get; set; } } } -#endregion - -#endregion + // private struct PolicyStats { + // public int Active { get; set; } + // public int Invalid { get; set; } + // public int Removed { get; set; } + // } } \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor.cs b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor.cs new file mode 100644
index 0000000..0106c6e --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor.cs
@@ -0,0 +1,144 @@ +using LibMatrix; +using LibMatrix.EventTypes.Interop.Draupnir; +using LibMatrix.EventTypes.Spec.State.Policy; +using LibMatrix.Homeservers; +using LibMatrix.Services; +using SpawnDev.BlazorJS.WebWorkers; + +namespace MatrixUtils.Web.Pages.Rooms; + +public partial class PolicyList { + +#region Draupnir interop + + private SemaphoreSlim ss = new(16, 16); + + private async Task DraupnirKickMatching(StateEventResponse policy) { + try { + var content = policy.TypedContent! as PolicyRuleEventContent; + if (content is null) return; + if (string.IsNullOrWhiteSpace(content.Entity)) return; + + var data = await Homeserver.GetAccountDataAsync<DraupnirProtectedRoomsData>(DraupnirProtectedRoomsData.EventId); + var rooms = data.Rooms.Select(Homeserver.GetRoom).ToList(); + + ActiveKicks.Add(policy, rooms.Count); + StateHasChanged(); + await Task.Delay(500); + + // for (int i = 0; i < 12; i++) { + // _ = WebWorkerService.TaskPool.Invoke(WasteCpu); + // } + + // static async Task runKicks(string roomId, PolicyRuleEventContent content) { + // Console.WriteLine($"Checking {roomId}..."); + // // Console.WriteLine($"Checking {room.RoomId}..."); + // // + // // try { + // // var members = await room.GetMembersListAsync(); + // // foreach (var member in members) { + // // var membership = member.ContentAs<RoomMemberEventContent>(); + // // if (member.StateKey == room.Homeserver.WhoAmI.UserId) continue; + // // if (membership?.Membership is "leave" or "ban") continue; + // // + // // if (content.EntityMatches(member.StateKey!)) + // // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given"); + // // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)"); + // // } + // // } + // // finally { + // // Console.WriteLine($"Finished checking {room.RoomId}..."); + // // } + // } + // + // try { + // var tasks = rooms.Select(room => WebWorkerService.TaskPool.Invoke(runKicks, room.RoomId, content)).ToList(); + // + // await Task.WhenAll(tasks); + // } + // catch (Exception e) { + // Console.WriteLine(e); + // } + + await NastyInternalsPleaseIgnore.ExecuteKickWithWasmWorkers(WebWorkerService, Homeserver, policy, data.Rooms); + // await Task.Run(async () => { + // foreach (var room in rooms) { + // try { + // Console.WriteLine($"Checking {room.RoomId}..."); + // var members = await room.GetMembersListAsync(); + // foreach (var member in members) { + // var membership = member.ContentAs<RoomMemberEventContent>(); + // if (member.StateKey == room.Homeserver.WhoAmI.UserId) continue; + // if (membership?.Membership is "leave" or "ban") continue; + // + // if (content.EntityMatches(member.StateKey!)) + // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given"); + // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)"); + // } + // ActiveKicks[policy]--; + // StateHasChanged(); + // } + // finally { + // Console.WriteLine($"Finished checking {room.RoomId}..."); + // } + // } + // }); + } + finally { + ActiveKicks.Remove(policy); + StateHasChanged(); + await Task.Delay(500); + } + } + +#region Nasty, nasty internals, please ignore! + + private static class NastyInternalsPleaseIgnore { + public static async Task ExecuteKickWithWasmWorkers(WebWorkerService workerService, AuthenticatedHomeserverGeneric hs, StateEventResponse evt, List<string> roomIds) { + try { + // var tasks = roomIds.Select(roomId => workerService.TaskPool.Invoke(ExecuteKickInternal, hs.WellKnownUris.Client, hs.AccessToken, roomId, content.Entity)).ToList(); + var tasks = roomIds.Select(roomId => workerService.TaskPool.Invoke(ExecuteKickInternal2, hs.WellKnownUris, hs.AccessToken, roomId, evt)).ToList(); + // workerService.TaskPool.Invoke(ExecuteKickInternal, hs.BaseUrl, hs.AccessToken, roomIds, content.Entity); + await Task.WhenAll(tasks); + } + catch (Exception e) { + Console.WriteLine(e); + } + } + + private static async Task ExecuteKickInternal(string homeserverBaseUrl, string accessToken, string roomId, string entity) { + try { + Console.WriteLine("args: " + string.Join(", ", homeserverBaseUrl, accessToken, roomId, entity)); + Console.WriteLine($"Checking {roomId}..."); + var hs = new AuthenticatedHomeserverGeneric(homeserverBaseUrl, new() { Client = homeserverBaseUrl }, null, accessToken); + Console.WriteLine($"Got HS..."); + var room = hs.GetRoom(roomId); + Console.WriteLine($"Got room..."); + var members = await room.GetMembersListAsync(); + Console.WriteLine($"Got members..."); + // foreach (var member in members) { + // var membership = member.ContentAs<RoomMemberEventContent>(); + // if (member.StateKey == hs.WhoAmI.UserId) continue; + // if (membership?.Membership is "leave" or "ban") continue; + // + // if (entity == member.StateKey) + // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given"); + // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)"); + // } + } + catch (Exception e) { + Console.WriteLine(e); + } + } + + private async static Task ExecuteKickInternal2(HomeserverResolverService.WellKnownUris wellKnownUris, string accessToken, string roomId, StateEventResponse policy) { + Console.WriteLine($"Checking {roomId}..."); + Console.WriteLine(policy.EventId); + } + } + +#endregion + +#endregion + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor.css b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor.css deleted file mode 100644
index afe9fb0..0000000 --- a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor.css +++ /dev/null
@@ -1,9 +0,0 @@ -th { - border-width: 1px; -} - -table { - width: fit-content; - border-width: 1px; - vertical-align: middle; -} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyList2.razor b/MatrixUtils.Web/Pages/Rooms/PolicyList2.razor
index 982fc5a..5d5bb5d 100644 --- a/MatrixUtils.Web/Pages/Rooms/PolicyList2.razor +++ b/MatrixUtils.Web/Pages/Rooms/PolicyList2.razor
@@ -15,7 +15,7 @@ <h3>Policy list editor - Editing @RoomId</h3> <hr/> @* <InputCheckbox @bind-Value="EnableAvatars"></InputCheckbox><label>Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!)</label> *@ -<LinkButton OnClick="@(() => { CurrentlyEditingEvent = new() { Type = "", RawContent = new() }; return Task.CompletedTask; })">Create new policy</LinkButton> +<LinkButton OnClickAsync="@(() => { CurrentlyEditingEvent = new() { Type = "", RawContent = new() }; return Task.CompletedTask; })">Create new policy</LinkButton> @if (Loading) { <p>Loading...</p> @@ -71,16 +71,16 @@ else { } <div style="display: ruby;"> @if (PowerLevels.UserHasStatePermission(Homeserver.WhoAmI.UserId, policy.Type)) { - <LinkButton OnClick="@(() => { CurrentlyEditingEvent = policy; return Task.CompletedTask; })">Edit</LinkButton> - <LinkButton OnClick="@(() => RemovePolicyAsync(policy))">Remove</LinkButton> + <LinkButton OnClickAsync="@(() => { CurrentlyEditingEvent = policy; return Task.CompletedTask; })">Edit</LinkButton> + <LinkButton OnClickAsync="@(() => RemovePolicyAsync(policy))">Remove</LinkButton> @if (policy.IsLegacyType) { - <LinkButton OnClick="@(() => RemovePolicyAsync(policy))">Update policy type</LinkButton> + <LinkButton OnClickAsync="@(() => RemovePolicyAsync(policy))">Update policy type</LinkButton> } @if (PolicyTypeIds[typeof(ServerPolicyRuleEventContent)].Contains(policy.EventId)) { - <LinkButton OnClick="@(() => { ServerPolicyToMakePermanent = policy; return Task.CompletedTask; })">Make permanent (wildcard)</LinkButton> + <LinkButton OnClickAsync="@(() => { ServerPolicyToMakePermanent = policy; return Task.CompletedTask; })">Make permanent (wildcard)</LinkButton> @if (CurrentUserIsDraupnir) { - <LinkButton OnClick="@(() => UpgradePolicyAsync(policy))">Kick matching users</LinkButton> + <LinkButton OnClickAsync="@(() => UpgradePolicyAsync(policy))">Kick matching users</LinkButton> } } else { diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor new file mode 100644
index 0000000..f818b62 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListCategoryComponent.razor
@@ -0,0 +1,66 @@ +@using ArcaneLibs.Extensions +@using LibMatrix.RoomTypes +<details> + <summary> + <span> + @($"{PolicyCollection.Name}: {PolicyCollection.TotalCount} policies") + </span> + <hr style="margin: revert;"/> + </summary> + <table class="table table-striped table-hover table-bordered align-middle"> + <thead> + <tr> + @foreach (var name in PolicyCollection.PropertiesToDisplay!.Keys) { + <th>@name</th> + } + <th>Actions</th> + </tr> + </thead> + <tbody> + @foreach (var policy in PolicyCollection.ActivePolicies.Values.OrderBy(x => x.Policy.RawContent?["entity"]?.GetValue<string>())) { + <PolicyListRowComponent PolicyInfo="@policy" PolicyCollection="@PolicyCollection" Room="@Room"></PolicyListRowComponent> + } + </tbody> + </table> + <details> + <summary> + <u> + @("Invalid " + PolicyCollection.Name.ToLower()) + </u> + </summary> + <table class="table table-striped table-hover table-bordered align-middle"> + <thead> + <tr> + <th>State key</th> + <th>Json contents</th> + </tr> + </thead> + <tbody> + @foreach (var policy in PolicyCollection.RemovedPolicies.Values) { + <tr> + <td>@policy.Policy.StateKey</td> + <td> + <pre>@policy.Policy.RawContent.ToJson(true, false)</pre> + </td> + </tr> + } + </tbody> + </table> + </details> +</details> + +@code { + + [Parameter] + public required PolicyList.PolicyCollection PolicyCollection { get; set; } + + [Parameter] + public required GenericRoom Room { get; set; } + + protected override bool ShouldRender() { + // if (PolicyCollection is null) return false; + + return true; + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListEditorHeader.razor b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListEditorHeader.razor new file mode 100644
index 0000000..e82f17d --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListEditorHeader.razor
@@ -0,0 +1,65 @@ +@using LibMatrix +@using LibMatrix.EventTypes.Common +@using LibMatrix.RoomTypes +@using MatrixUtils.Web.Shared.PolicyEditorComponents +<h3>Policy list editor - Editing @(RoomName ?? Room.RoomId)</h3> +@if (!string.IsNullOrWhiteSpace(DraupnirShortcode)) { + <span style="margin-right: 2em;">Shortcode: @DraupnirShortcode</span> +} +@if (!string.IsNullOrWhiteSpace(RoomAlias)) { + <span>Alias: @RoomAlias</span> +} +<hr/> +@* <InputCheckbox @bind-Value="EnableAvatars"></InputCheckbox><label>Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!)</label> *@ +<LinkButton OnClickAsync="@(() => { + CurrentlyEditingEvent = new() { Type = "", RawContent = new() }; + return Task.CompletedTask; + })">Create new policy +</LinkButton> +<LinkButton OnClickAsync="@(() => { + MassCreatePolicies = true; + return Task.CompletedTask; + })">Create many new policies +</LinkButton> +<LinkButton OnClickAsync="@(() => ReloadStateAsync())">Refresh</LinkButton> + +@if (CurrentlyEditingEvent is not null) { + <PolicyEditorModal PolicyEvent="@CurrentlyEditingEvent" OnClose="@(() => CurrentlyEditingEvent = null)" OnSaveAsync="@UpdatePolicyAsync"></PolicyEditorModal> +} + +@if (MassCreatePolicies) { + <MassPolicyEditorModal Room="@Room" OnClose="@(() => MassCreatePolicies = false)" OnSaved="@(() => { + MassCreatePolicies = false; + // _ = LoadStatesAsync(); + })"></MassPolicyEditorModal> +} + +@code { + [Parameter] + public required GenericRoom Room { get; set; } + + [Parameter] + public required Func<Task> ReloadStateAsync { get; set; } + + private string? RoomName { get; set; } + private string? RoomAlias { get; set; } + private string? DraupnirShortcode { get; set; } + + private StateEventResponse? CurrentlyEditingEvent { get; set { field = value; StateHasChanged(); } } + private bool MassCreatePolicies { get; set { field = value; StateHasChanged(); } } + + protected override async Task OnInitializedAsync() { + await Task.WhenAll( + Task.Run(async () => { DraupnirShortcode = (await Room.GetStateOrNullAsync<MjolnirShortcodeEventContent>(MjolnirShortcodeEventContent.EventId))?.Shortcode; }), + Task.Run(async () => { RoomAlias = (await Room.GetCanonicalAliasAsync())?.Alias; }), + Task.Run(async () => { RoomName = await Room.GetNameOrFallbackAsync(); }) + ); + + StateHasChanged(); + } + + private async Task UpdatePolicyAsync(StateEventResponse evt) { + Console.WriteLine("UpdatePolicyAsync in PolicyListEditorHeader not yet implementeD!"); + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor new file mode 100644
index 0000000..11de82c --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/PolicyListComponents/PolicyListRowComponent.razor
@@ -0,0 +1,167 @@ +@using System.Reflection +@using LibMatrix +@using LibMatrix.EventTypes.Spec.State.Policy +@using LibMatrix.RoomTypes +@using MatrixUtils.Web.Shared.PolicyEditorComponents + +@if (_isInitialized && IsVisible) { + <tr id="@PolicyInfo.Policy.EventId"> + @foreach (var prop in PolicyCollection.PropertiesToDisplay.Values) { + if (prop.Name == "Entity") { + <td> + <span>@TruncateMxid(TypedContent.Entity)</span> + @foreach (var dup in PolicyInfo.MadeRedundantBy) { + <br/> + <span>Also matched by @dup.FriendlyTypeName.ToLower() <a href="@Anchor(dup.EventId)">@TruncateMxid(dup.RawContent["entity"].GetValue<string>())</a></span> + } + </td> + } + else { + <td>@prop.GetGetMethod()?.Invoke(TypedContent, null)</td> + } + } + <td> + <div style="display: flex; flex-direction: row; gap: 0.5em;"> + @* @if (PowerLevels.UserHasStatePermission(Homeserver.WhoAmI.UserId, Policy.Type)) { *@ + @if (true) { + <LinkButton OnClickAsync="@(() => { + IsEditing = true; + return Task.CompletedTask; + })">Edit + </LinkButton> + <LinkButton OnClickAsync="@RemovePolicyAsync">Remove</LinkButton> + @if (Policy.IsLegacyType) { + <LinkButton OnClickAsync="@RemovePolicyAsync">Update policy type</LinkButton> + } + + @if (TypedContent.Entity?.StartsWith("@*:", StringComparison.Ordinal) == true) { + <LinkButton OnClickAsync="@ConvertToAclAsync">Convert to ACL</LinkButton> + } + + @* @if (PolicyTypeIds[typeof(ServerPolicyRuleEventContent)].Contains(Policy.Type)) { *@ + @* <LinkButton OnClickAsync="@(() => { *@ + @* ServerPolicyToMakePermanent = Policy; *@ + @* return Task.CompletedTask; *@ + @* })">Make permanent *@ + @* </LinkButton> *@ + @* @if (CurrentUserIsDraupnir) { *@ + @* <LinkButton Color="@(ActiveKicks.ContainsKey(Policy) ? "#FF0000" : null)" OnClick="@(() => DraupnirKickMatching(Policy))">Kick *@ + @* users @(ActiveKicks.TryGetValue(Policy, out var kick) ? $"({kick})" : null) *@ + @* </LinkButton> *@ + @* } *@ + // } + } + else { + <p>No permission to modify</p> + } + </div> + </td> + </tr> + + @if (IsEditing) { + <PolicyEditorModal PolicyEvent="@Policy" OnClose="@(() => IsEditing = false)" OnSaveAsync="@UpdatePolicyAsync"></PolicyEditorModal> + } + @* TODO: Implement ability to turn ACLs into wildcards *@ + @*@if (ServerPolicyToMakePermanent is not null) { + <ModalWindow Title="Make policy permanent"> + + </ModalWindow> + }*@ +} + + + +@code { + + [Parameter] + public PolicyList.PolicyCollection.PolicyInfo PolicyInfo { get; set; } + + [Parameter] + public GenericRoom Room { get; set; } = null!; + + [Parameter] + public required PolicyList.PolicyCollection PolicyCollection { get; set; } + + private StateEventResponse Policy => PolicyInfo.Policy; + + private bool IsEditing { + get; + set { + field = value; + _isDirty = true; + StateHasChanged(); + } + } + + public bool IsVisible { + get; + set { + field = value; + _isDirty = true; + } + } = true; + + private PolicyRuleEventContent TypedContent { get; set; } + + private bool _isDirty = true; + private bool _isInitialized; + + protected override bool ShouldRender() => _isDirty; + + protected override void OnParametersSet() { + TypedContent = Policy.TypedContent as PolicyRuleEventContent ?? throw new InvalidOperationException("Policy must have a typed content of type PolicyRuleEventContent."); + _isDirty = true; + _isInitialized = true; + // Console.WriteLine($"ParametersSet {Policy.StateKey}"); + } + + private static string TruncateMxid(string? mxid) { + if (string.IsNullOrWhiteSpace(mxid)) return mxid; + var parts = mxid.Split(':', 2); + if (parts[0].Length > 50) + parts[0] = parts[0][..50] + "[...]"; + + if (parts is [_, { Length: > 50 }]) + parts[1] = parts[1][..50] + "[...]"; + + return parts.Length == 1 ? parts[0] : $"{parts[0]}:{parts[1]}"; + } + + private async Task RemovePolicyAsync() { + await Room.SendStateEventAsync(Policy.Type, Policy.StateKey, new { }); + IsVisible = false; + StateHasChanged(); + // PolicyEventsByType[policyEvent.MappedType].Remove(policyEvent); + // await LoadStatesAsync(); + } + + private async Task UpdatePolicyAsync(StateEventResponse evt) { + await Room.SendStateEventAsync(Policy.Type, Policy.StateKey, Policy.RawContent); + // CurrentlyEditingEvent = null; + // await LoadStatesAsync(); + } + + private async Task UpgradePolicyAsync() { + Policy.RawContent["gay.rory.matrixutils.upgraded_from_type"] = Policy.Type; + // await LoadStatesAsync(); + } + + private async Task ConvertToAclAsync() { + if (Policy.RawContent.ContainsKey("entity")) { + var newContent = Policy.ContentAs<ServerPolicyRuleEventContent>(); + newContent!.Entity = newContent.Entity!.Replace("@*:", ""); + await Room.SendStateEventAsync(ServerPolicyRuleEventContent.EventId, newContent.GetDraupnir2StateKey(), newContent); + await Room.SendStateEventAsync(Policy.Type, Policy.StateKey!, new { }); + IsVisible = false; + StateHasChanged(); + } + else { + throw new InvalidOperationException("Policy event must contain an 'entity' field to convert to ACL."); + } + } + + private string Anchor(string anchor) { + return $"{NavigationManager.Uri.Split('#')[0]}#{anchor}"; + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor
index 2903ab8..6df56ba 100644 --- a/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor +++ b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor
@@ -1,12 +1,24 @@ @page "/PolicyLists" +@using ArcaneLibs @using ArcaneLibs.Extensions @using LibMatrix @using LibMatrix.EventTypes @using LibMatrix.EventTypes.Common @using LibMatrix.EventTypes.Spec.State.Policy +@using LibMatrix.Helpers +@using LibMatrix.Responses @using LibMatrix.RoomTypes @inject ILogger<Index> logger -<h3>Policy lists </h3> @* <LinkButton href="/Rooms/Create">Create new policy list</LinkButton> *@ +<h3> + <span>Policy lists </span> + <LinkButton OnClickAsync="@(() => { + ShowPolicyListCreationWindow = true; + return Task.CompletedTask; + })"> + <span class="oi oi-plus" aria-hidden="true"> Create</span> + </LinkButton> +</h3> + @if (!string.IsNullOrWhiteSpace(Status)) { <p>@Status</p> @@ -16,22 +28,17 @@ } <hr/> -<table> +<table class="table table-striped table-hover table-bordered align-middle" aria-busy="@isLoading"> <thead> <tr> - <th/> <th>Room name</th> <th>Policies</th> + <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) { @@ -50,11 +57,44 @@ <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> + <td> + <LinkButton href="@($"/Rooms/{room.Room.RoomId}/Policies")"> + <span class="oi oi-pencil" aria-hidden="true"> View/edit policies</span> + </LinkButton> + </td> </tr> } </tbody> </table> +@if (ShowPolicyListCreationWindow && Homeserver != null) { + <ModalWindow Title="New policy list"> + @if (!string.IsNullOrWhiteSpace(_roomBuilder.Avatar.Url)) { + <MxcAvatar Homeserver="@Homeserver" MxcUri="@_roomBuilder.Avatar.Url" Circular="true" Size="4" SizeUnit="em"/> + } + else { + <img class="avatar" style="height: 4em; width: 4em; border-radius: 50%;" src="@IdenticonGenerator.GenerateAsDataUri(Homeserver.WhoAmI.UserId)"/> + } + <div style="display: inline-block; vertical-align: middle; padding-left: 1em;"> + <FancyTextBox @bind-Value="@_roomBuilder.Name.Name"></FancyTextBox> + <br/> + <span>#</span> + <FancyTextBox @bind-Value="@_roomBuilder.AliasLocalPart"></FancyTextBox> + <span>:@Homeserver!.ServerName</span> + <br/> + <FancyTextBox @bind-Value="@_roomBuilder.Avatar.Url"></FancyTextBox> + <InputFile OnChange="@RoomIconFilePicked"></InputFile> + </div> + <br/> + + <span>Bot shortcode: </span> + <FancyTextBox @bind-Value="@_shortcodeEvent.Shortcode"></FancyTextBox> + <br/> + <LinkButton OnClickAsync="@CreatePolicyList">Create</LinkButton> + + </ModalWindow> +} + @code { private List<RoomInfo> Rooms { get; } = []; @@ -65,44 +105,39 @@ Homeserver = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true); if (Homeserver is null) return; + isLoading = true; 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 = []; + List<Task> _tasks = []; await foreach (var room in Homeserver.GetJoinedRoomsByType("support.feline.policy.lists.msc.v1")) { - roomsByType.Add(room); + // roomsByType.Add(room); Status2 = $"Found {room.RoomId} (MSC3784)..."; + _tasks.Add(Task.Run(async () => { + Rooms.Add(await RoomInfo.FromRoom(room)); + StateHasChanged(); + })); } - List<Task<RoomInfo>> tasks = roomsByType.Select(async room => { - Status2 = $"Fetching room {room.RoomId}..."; - return await RoomInfo.FromRoom(room); - }).ToList(); + await Task.WhenAll(_tasks); - var results = tasks.ToAsyncEnumerable(); - await foreach (var result in results) { - Rooms.Add(result); - StateHasChanged(); - } + isLoading = false; + Status = ""; + Status2 = ""; + } + private async Task ScanLegacyLists() { + isLoading = true; 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)) + .Where(x => PolicyRoom.SpecPolicyEventTypes.Contains(x.Type)) .ToList(); if (policies.Count == 0) return null; Status2 = $"Found legacy list {room.RoomId}..."; return await RoomInfo.FromRoom(room, state, true); - }) - .ToAsyncEnumerable(); + }).ToAsyncEnumerable(); await foreach (var room in rooms) { if (room is not null) { @@ -110,32 +145,36 @@ StateHasChanged(); } } - + + isLoading = false; Status = ""; Status2 = ""; - await base.OnInitializedAsync(); } - private string _status; - - public string Status { - get => _status; + private string? Status { + get; set { - _status = value; + field = value; StateHasChanged(); } } - private string _status2; - - public string Status2 { - get => _status2; + private string? Status2 { + get; set { - _status2 = value; + field = value; StateHasChanged(); } } + private bool ShowPolicyListCreationWindow { + get; + set { + field = value; + StateHasChanged(); + } + } = true; + private class RoomInfo { public GenericRoom Room { get; set; } public string RoomName { get; set; } @@ -149,11 +188,6 @@ 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() { @@ -165,12 +199,40 @@ ?? 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)) } + { PolicyType.User, state.Count(x => PolicyRoom.UserPolicyEventTypes.Contains(x.Type)) }, + { PolicyType.Server, state.Count(x => PolicyRoom.ServerPolicyEventTypes.Contains(x.Type)) }, + { PolicyType.Room, state.Count(x => PolicyRoom.RoomPolicyEventTypes.Contains(x.Type)) } } }; } } + private readonly RoomBuilder _roomBuilder = new() { + Type = "support.feline.policy.lists.msc.v1", + Name = new() { Name = "New policy list" }, + AliasLocalPart = "policies" + }; + + private readonly MjolnirShortcodeEventContent _shortcodeEvent = new() { + Shortcode = "policy-list" + }; + + private bool isLoading = true; + + private static readonly SvgIdenticonGenerator IdenticonGenerator = new(); + + private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) { + var res = await Homeserver!.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType); + Console.WriteLine(res); + _roomBuilder.Avatar.Url = res; + StateHasChanged(); + } + + private async Task CreatePolicyList() { + var room = await _roomBuilder.Create(Homeserver!); + Status = $"Created policy list {room.RoomId} ({room.GetNameAsync()})"; + await room.SendStateEventAsync(MjolnirShortcodeEventContent.EventId, _shortcodeEvent); + NavigationManager.NavigateTo($"/Rooms/{room.RoomId}/Policies"); + } + } \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor.css b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor.css deleted file mode 100644
index f9b5b3f..0000000 --- a/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor.css +++ /dev/null
@@ -1,6 +0,0 @@ -table, th, td { - border-width: 1px; -} -td { - padding: 8px; -} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateBasicRoomInfoOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateBasicRoomInfoOptions.razor new file mode 100644
index 0000000..c1ee202 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateBasicRoomInfoOptions.razor
@@ -0,0 +1,52 @@ +@using ArcaneLibs +@using LibMatrix.Helpers +<tr> + <td>Room name:</td> + <td> + <FancyTextBox @bind-Value="@roomBuilder.Name.Name"></FancyTextBox> + </td> +</tr> +<tr> + <td>Room alias:</td> + <td> + <InputLocalPart Sigil="#" ServerName="@Homeserver.ServerName" @bind-LocalPart="@roomBuilder.AliasLocalPart"></InputLocalPart> + </td> +</tr> +<tr> + <td>Room icon:</td> + <td> + @if (!string.IsNullOrWhiteSpace(roomBuilder.Avatar.Url)) { + <MxcAvatar Homeserver="Homeserver" MxcUri="@roomBuilder.Avatar.Url" Size="3" SizeUnit="em" Circular="true"/> + } + else { + <img class="avatar" style="height: 3em; width: 3em; border-radius: 50%;" src="@IdenticonGenerator.GenerateAsDataUri(Homeserver.WhoAmI.UserId)"/> + } + <div style="display: inline-block; vertical-align: middle;"> + <FancyTextBox @bind-Value="@roomBuilder.Avatar.Url"></FancyTextBox> + <br/> + <SimpleFilePicker OnFilePicked="@RoomIconFilePicked"></SimpleFilePicker> + </div> + </td> +</tr> + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private static readonly SvgIdenticonGenerator IdenticonGenerator = new(); + + private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) { + var res = await Homeserver.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType); + Console.WriteLine(res); + roomBuilder.Avatar.Url = res; + PageStateHasChanged(); + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateCreateOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateCreateOptions.razor new file mode 100644
index 0000000..3f4a73d --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateCreateOptions.razor
@@ -0,0 +1,92 @@ +@using Blazored.LocalStorage +@using LibMatrix.Helpers +@inject ILocalStorageService LocalStorage +<tr> + <td>Room type:</td> + <td> + @if (RoomTypes.ContainsKey(roomBuilder.Type ?? "")) { + <InputSelect @bind-Value="@roomBuilder.Type"> + @foreach (var type in RoomTypes) { + <option value="@type.Key">@type.Value</option> + } + <option value="custom">Custom ...</option> + </InputSelect> + } + else { + <FancyTextBox @bind-Value="@roomBuilder.Type"></FancyTextBox> + } + + <span> version </span> + @if (Capabilities is null) { + <span style="color: #888;">Loading...</span> + } + else { + <InputSelect @bind-Value="@roomBuilder.Version"> + @foreach (var version in Capabilities.Capabilities.RoomVersions!.Available!) { + <option value="@version.Key">@version.Key (@version.Value)</option> + } + </InputSelect> + } + </td> +</tr> +<tr> + <td style="vertical-align: top;">Allow attribution:</td> + <td> + <InputCheckbox @bind-Value="@AllowAttribution"/> + <span>Allow attribution to Rory&::MatrixUtils</span> + <LinkButton InlineText="true" OnClick="@(() => ShowAttributionInfo = true)">?</LinkButton> + </td> +</tr> + +@if (ShowAttributionInfo) { + <ModalWindow Title="Allow attribution to Rory&::MatrixUtils" + OnCloseClicked="@(() => ShowAttributionInfo = false)"> + <span>This will add the following to the room creation content:</span> + <br/> + <pre>{ "gay.rory.created_using": "Rory&::MatrixUtils (https://mru.rory.gay)" }</pre> + <span>This is not visible to users unless they manually inspect the room's create event source.</span> + </ModalWindow> +} + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private AuthenticatedHomeserverGeneric.CapabilitiesResponse? Capabilities { get; set; } + + private bool ShowAttributionInfo { + get; + set { + field = value; + StateHasChanged(); + } + } + + private bool AllowAttribution { + get; + set { + field = value; + _ = LocalStorage.SetItemAsync("rmu.room_create.allow_attribution", value); + } + } = true; + + protected override async Task OnInitializedAsync() { + Capabilities = await Homeserver.GetCapabilitiesAsync(); + roomBuilder.Version = Capabilities.Capabilities.RoomVersions!.Default; + AllowAttribution = await LocalStorage.GetItemAsync<bool?>("rmu.room_create.allow_attribution") ?? true; + } + + private static Dictionary<string, string> RoomTypes { get; } = new() { + { "", "Room" }, + { "m.space", "Space" }, + { "support.feline.policy.lists.msc.v1", "Policy list" } + }; + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor new file mode 100644
index 0000000..272bd8b --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor
@@ -0,0 +1,52 @@ +@using System.Text.Json +@using LibMatrix +@using LibMatrix.Helpers +<tr> + <td style="vertical-align: top;">Initial room state:</td> + <td> + @foreach (var (displayName, events) in new Dictionary<string, List<StateEvent>>() { + { "Important room state (before final access rules)", roomBuilder.ImportantState }, + { "Additional room state (after final access rules)", roomBuilder.InitialState }, + }) { + <details> + + @code + { + // private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" }; + } + + @* <summary>@displayName: @events.Count(x => !ImplementedStates.Contains(x.Type)) events</summary> *@ + <summary>@displayName: @events.Count events</summary> + <table> + @* @foreach (var initialState in events.Where(x => !ImplementedStates.Contains(x.Type))) { *@ + @foreach (var initialState in events) { + <tr> + <td style="vertical-align: top;"> + @(initialState.Type): + @if (!string.IsNullOrEmpty(initialState.StateKey)) { + <br/> + <span>(@initialState.StateKey)</span> + } + + </td> + <td> + <pre>@JsonSerializer.Serialize(initialState.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre> + </td> + </tr> + } + </table> + </details> + } + </td> +</tr> + +@code { + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor new file mode 100644
index 0000000..5170d2d --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor
@@ -0,0 +1,55 @@ +@using ArcaneLibs.Extensions +@using LibMatrix.Helpers +<tr> + <td>Invited members:</td> + <td> + <details> + <summary>@roomBuilder.Invites.Count members</summary> + <LinkButton OnClickAsync="@InviteAllSessions" InlineText="true">Invite all logged in accounts</LinkButton> + @foreach (var member in roomBuilder.Invites) { + <UserListItem _homeserver="Homeserver" UserId="@member.Key"></UserListItem> + <span>: </span> + <FancyTextBox Value="@member.Value" ValueChanged="@(val => roomBuilder.Invites[member.Key] = val)"/> + } + </details> + </td> +</tr> +<tr> + <td>Banned members:</td> + <td> + <details> + <summary>@roomBuilder.Bans.Count members</summary> + @foreach (var member in roomBuilder.Bans) { + <UserListItem _homeserver="Homeserver" UserId="@member.Key"></UserListItem> + <span>: </span> + <FancyTextBox Value="@member.Value" ValueChanged="@(val => roomBuilder.Bans[member.Key] = val)"/> + } + </details> + </td> +</tr> + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private async Task InviteAllSessions() { + var sessions = await sessionStore.GetAllSessions(); + foreach (var session in sessions) { + if (roomBuilder.Invites.ContainsKey(session.Value.Auth.UserId) || session.Value.Auth.UserId == Homeserver!.WhoAmI.UserId) continue; + Console.WriteLine("Inviting " + session.Value.Auth.UserId); + roomBuilder.Invites.Add(session.Value.Auth.UserId, null); + Console.WriteLine("--"); + } + + Console.WriteLine("Got all sessions, invited: " + string.Join(", ", roomBuilder.Invites.Keys)); + StateHasChanged(); + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePermissionsOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePermissionsOptions.razor new file mode 100644
index 0000000..ba28b82 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePermissionsOptions.razor
@@ -0,0 +1,123 @@ +@using ArcaneLibs.Extensions +@using LibMatrix.Helpers +<tr> + <td>Permissions:</td> + <details> + <summary> + @if (roomBuilder.Version is "org.matrix.hydra.11" or "12") { + <span>@(roomBuilder.AdditionalCreators.Count + 1) creators, </span> + } + <span>@roomBuilder.PowerLevels.Users.Count members, @roomBuilder.PowerLevels.Events.Count events</span> + </summary> + + @if (roomBuilder.Version is "org.matrix.hydra.11" or "12") { + <span style="border-bottom: #444;">Creators:</span> + <br/> + <span>@Homeserver.WhoAmI.UserId (you - to change, visit <a href="/">the homepage</a>.)</span> + <br/> + + <StringListEditor @bind-Items="@roomBuilder.AdditionalCreators"></StringListEditor> + <br/> + } + + <span style="border-bottom: #444;">Events:</span><br/> + @foreach (var eventType in roomBuilder.PowerLevels.Events.Keys) { + var _event = eventType; + <tr> + <td> + <LinkButton InlineText="true" OnClick="@(() => { + roomBuilder.PowerLevels.Events.Remove(_event); + StateHasChanged(); + })">- + </LinkButton> + <div style="display: inline-flex;"> + <FancyTextBox Formatter="@GetPermissionFriendlyName" + Value="@_event" + ValueChanged="val => { roomBuilder.PowerLevels.Events.ChangeKey(_event, val); }"> + </FancyTextBox> + <span>:</span> + </div> + </td> + <td> + <input type="number" value="@roomBuilder.PowerLevels.Events[_event]" + @oninput="val => { roomBuilder.PowerLevels.Events[_event] = int.Parse(val.Value.ToString()); }" + @onfocusout="@(() => { roomBuilder.PowerLevels.Events = roomBuilder.PowerLevels.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); })"/> + </td> + </tr> + } + <tr> + <td> + <LinkButton InlineText="true" OnClick="@(() => { + roomBuilder.PowerLevels.Events[""] = 0; + StateHasChanged(); + })">+ + </LinkButton> + </td> + </tr> + + <span style="border-bottom: #444;">Users:</span><br/> + @foreach (var user in roomBuilder.PowerLevels.Users.Keys) { + var _user = user; + <tr> + <td> + <LinkButton InlineText="true" OnClick="@(() => { + roomBuilder.PowerLevels.Users.Remove(_user); + StateHasChanged(); + })">- + </LinkButton> + <div style="display: inline-flex;"> + <FancyTextBox Value="@_user" + ValueChanged="val => { roomBuilder.PowerLevels.Users.ChangeKey(_user, val); }"> + </FancyTextBox> + <span>:</span> + </div> + </td> + <td> + <input type="number" value="@roomBuilder.PowerLevels.Users[_user]" + @oninput="val => { roomBuilder.PowerLevels.Users[_user] = int.Parse(val.Value.ToString()); }" + @onfocusout="@(() => { roomBuilder.PowerLevels.Users = roomBuilder.PowerLevels.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); })"/> + </td> + </tr> + } + <tr> + <td> + <LinkButton InlineText="true" OnClick="@(() => { + roomBuilder.PowerLevels.Users[""] = 0; + StateHasChanged(); + })">+ + </LinkButton> + </td> + </tr> + </details> +</tr> + + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private string GetPermissionFriendlyName(string key) => key switch { + "m.reaction" => "Send reaction", + "m.room.avatar" => "Change room icon", + "m.room.canonical_alias" => "Change room alias", + "m.room.encryption" => "Enable encryption", + "m.room.history_visibility" => "Change history visibility", + "m.room.name" => "Change room name", + "m.room.power_levels" => "Change power levels", + "m.room.tombstone" => "Upgrade room", + "m.room.topic" => "Change room topic", + "m.room.pinned_events" => "Pin events", + "m.room.server_acl" => "Change server ACLs", + "org.matrix.msc4284.policy" => "Change policy server", + "m.room.guest_access" => "Change guest access", + _ => key + }; + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePrivacyOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePrivacyOptions.razor new file mode 100644
index 0000000..76f61c4 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePrivacyOptions.razor
@@ -0,0 +1,70 @@ +@using LibMatrix.Helpers +<tr> + <td style="padding-top: 16px;">Join rules:</td> + <td style="padding-top: 16px;"> + <InputSelect @bind-Value="@roomBuilder.JoinRules.JoinRuleValue"> + <option value="public">Anyone can join</option> + <option value="invite">Invite only</option> + <option value="knock">Ask to join</option> + <option value="restricted">Invite only (or mutual room)</option> + <option value="knock_restricted">Ask to join (or mutual room)</option> + </InputSelect> + </td> +</tr> +<tr> + <td>History visibility:</td> + <td> + <InputSelect @bind-Value="@roomBuilder.HistoryVisibility.HistoryVisibility"> + <option value="invited">Since invite</option> + <option value="joined">Since join</option> + <option value="shared">Since room creation (members only)</option> + <option value="world_readable">World readable (everyone)</option> + </InputSelect> + </td> +</tr> +<tr> + <td>Guest access:</td> + <td> + <InputCheckbox @bind-Value="roomBuilder.GuestAccess.IsGuestAccessEnabled"/> + <span>Allow guests to join</span> + <LinkButton InlineText="true" href="https://spec.matrix.org/v1.15/client-server-api/#guest-access" target="_blank">?</LinkButton> + </td> +</tr> +<tr> + <td>Server ACLs:</td> + <td> + @if (roomBuilder.ServerAcls?.Allow is null) { + <p>No allow rules exist!</p> + <LinkButton OnClick="@(() => { roomBuilder.ServerAcls!.Allow = ["*"]; })">Create sane defaults</LinkButton> + } + else { + <details> + <summary>@(roomBuilder.ServerAcls.Allow?.Count) allow rules</summary> + <StringListEditor @bind-Items="@roomBuilder.ServerAcls.Allow"></StringListEditor> + </details> + } + @if (roomBuilder.ServerAcls?.Deny is null) { + <p>No deny rules exist!</p> + <LinkButton OnClick="@(() => { roomBuilder.ServerAcls!.Deny = []; })">Create sane defaults</LinkButton> + } + else { + <details> + <summary>@(roomBuilder.ServerAcls.Deny?.Count) deny rules</summary> + <StringListEditor @bind-Items="@roomBuilder.ServerAcls.Deny"></StringListEditor> + </details> + } + </td> +</tr> + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor new file mode 100644
index 0000000..3e8c3dd --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor
@@ -0,0 +1,33 @@ +@using LibMatrix.Helpers +<tr> + <td>Room upgrade options</td> + <td> + <details> + <summary>Upgrading from @roomUpgrade.OldRoom.RoomId</summary> + <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.InviteMembers"></InputCheckbox> + <span>Invite members</span> + <br/> + <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.InvitePowerlevelUsers"></InputCheckbox> + <span>Invite users with powerlevels</span> + <br/> + <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.MigrateBans"></InputCheckbox> + <span>Copy bans (do not use with moderation bots!)</span> + <br/> + <LinkButton OnClickAsync="@(async () => { + await roomUpgrade.ImportAsync(); + PageStateHasChanged(); + })">Apply + </LinkButton> + </details> + </td> +</tr> + +@code { + + [Parameter] + public required RoomUpgradeBuilder roomUpgrade { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/Timeline.razor b/MatrixUtils.Web/Pages/Rooms/Timeline.razor
index 108581c..2af819b 100644 --- a/MatrixUtils.Web/Pages/Rooms/Timeline.razor +++ b/MatrixUtils.Web/Pages/Rooms/Timeline.razor
@@ -3,6 +3,7 @@ @using LibMatrix @using LibMatrix.EventTypes.Spec @using LibMatrix.EventTypes.Spec.State.RoomInfo +@using LibMatrix.Responses <h3>RoomManagerTimeline</h3> <hr/> <p>Loaded @Events.Count events...</p> diff --git a/MatrixUtils.Web/Pages/ServerInfo.razor b/MatrixUtils.Web/Pages/ServerInfo.razor
index 8dd7907..3da93f2 100644 --- a/MatrixUtils.Web/Pages/ServerInfo.razor +++ b/MatrixUtils.Web/Pages/ServerInfo.razor
@@ -1,6 +1,7 @@ @page "/ServerInfo/{Homeserver}" @using LibMatrix.Responses @using ArcaneLibs.Extensions +@using LibMatrix.Responses.Federation <h3>Server info for @Homeserver</h3> <hr/> @if (ServerVersionResponse is not null) { diff --git a/MatrixUtils.Web/Pages/Tools/Debug/JoinRoom.razor b/MatrixUtils.Web/Pages/Tools/Debug/JoinRoom.razor
index 319c9e7..cb56a40 100644 --- a/MatrixUtils.Web/Pages/Tools/Debug/JoinRoom.razor +++ b/MatrixUtils.Web/Pages/Tools/Debug/JoinRoom.razor
@@ -5,10 +5,13 @@ <span>Room ID: </span> <InputText @bind-Value="@RoomId"></InputText> <br/> -<span>Via server: </span> -<InputText @bind-Value="@Server"></InputText> +<span>Via server(s), comma separated: </span> +<InputText @bind-Value="@Servers"></InputText> <br/> -<LinkButton OnClick="@Join">Join</LinkButton> +<span>Unblock room (Synapse): </span> +<InputCheckbox @bind-Value="@Unblock"></InputCheckbox> +<br/> +<LinkButton OnClickAsync="@Join">Join</LinkButton> <br/><br/> @foreach (var line in Log) { <pre>@line</pre> @@ -21,9 +24,12 @@ [Parameter, SupplyParameterFromQuery(Name = "roomId")] public string? RoomId { get; set; } - + [Parameter, SupplyParameterFromQuery(Name = "via")] - public string? Server { get; set; } + public string? Servers { get; set; } + + [Parameter, SupplyParameterFromQuery(Name = "unblock")] + public bool Unblock { get; set; } = false; protected override async Task OnInitializedAsync() { hs = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true); @@ -39,14 +45,19 @@ if (string.IsNullOrWhiteSpace(RoomId)) return; var room = hs.GetRoom(RoomId); Log.Add("Got room object..."); - - if (hs is AuthenticatedHomeserverSynapse synapse) { - await synapse.Admin.BlockRoom(RoomId, false); - Log.Add($"Synapse: unblocked room"); + + if (Unblock && hs is AuthenticatedHomeserverSynapse synapse) { + try { + await synapse.Admin.BlockRoom(RoomId, false); + Log.Add($"Synapse: unblocked room"); + } + catch (Exception e) { + Log.Add($"Synapse: failed to unblock room: {e}"); + } } - + try { - await room.JoinAsync([Server], checkIfAlreadyMember: false); + await room.JoinAsync(Servers?.Split(','), checkIfAlreadyMember: false); Log.Add("Joined room!"); } catch (Exception e) { diff --git a/MatrixUtils.Web/Pages/Tools/Debug/LeaveRoom.razor b/MatrixUtils.Web/Pages/Tools/Debug/LeaveRoom.razor
index 7844331..c40fa0b 100644 --- a/MatrixUtils.Web/Pages/Tools/Debug/LeaveRoom.razor +++ b/MatrixUtils.Web/Pages/Tools/Debug/LeaveRoom.razor
@@ -5,7 +5,7 @@ <span>Room ID: </span> <InputText @bind-Value="@RoomId"></InputText> <br/> -<LinkButton OnClick="@Leave">Leave</LinkButton> +<LinkButton OnClickAsync="@Leave">Leave</LinkButton> <br/><br/> @foreach (var line in Log) { <p>@line</p> diff --git a/MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor b/MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor
index 0943216..b0f7dbf 100644 --- a/MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor +++ b/MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor
@@ -17,7 +17,7 @@ </details> <br/> -<LinkButton OnClick="Execute">Execute</LinkButton> +<LinkButton OnClickAsync="Execute">Execute</LinkButton> <br/> @foreach (var line in Enumerable.Reverse(log)) { <p>@line</p> diff --git a/MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor b/MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor
index 5c238b3..fcdb3d0 100644 --- a/MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor +++ b/MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor
@@ -11,7 +11,7 @@ <p>Users: </p> <InputTextArea @bind-Value="@UserIdString"></InputTextArea> <br/> -<InputText @bind-Value="@ImportFromRoomId"></InputText><LinkButton OnClick="@DoImportFromRoomId">Import from room (ID)</LinkButton> +<InputText @bind-Value="@ImportFromRoomId"></InputText><LinkButton OnClickAsync="@DoImportFromRoomId">Import from room (ID)</LinkButton> <details> <summary>Rooms to be searched (@rooms.Count)</summary> @@ -21,7 +21,7 @@ } </details> <br/> -<LinkButton OnClick="Execute">Execute</LinkButton> +<LinkButton OnClickAsync="Execute">Execute</LinkButton> <br/> <details> diff --git a/MatrixUtils.Web/Pages/Tools/InviteCounter.razor b/MatrixUtils.Web/Pages/Tools/InviteCounter.razor
index 2313884..16a3853 100644 --- a/MatrixUtils.Web/Pages/Tools/InviteCounter.razor +++ b/MatrixUtils.Web/Pages/Tools/InviteCounter.razor
@@ -9,7 +9,7 @@ <br/> <span>Room ID: </span> <InputText @bind-Value="@roomId"></InputText> -<LinkButton OnClick="@Execute">Execute</LinkButton> +<LinkButton OnClickAsync="@Execute">Execute</LinkButton> <br/> diff --git a/MatrixUtils.Web/Pages/Tools/MassCMEBan.razor b/MatrixUtils.Web/Pages/Tools/MassCMEBan.razor
index a252e6b..5b0f510 100644 --- a/MatrixUtils.Web/Pages/Tools/MassCMEBan.razor +++ b/MatrixUtils.Web/Pages/Tools/MassCMEBan.razor
@@ -7,7 +7,7 @@ <br/> <span>Users:</span> <InputTextArea @bind-Value="@roomId"></InputTextArea> -<LinkButton OnClick="@Execute">Execute</LinkButton> +<LinkButton OnClickAsync="@Execute">Execute</LinkButton> <br/> diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor b/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor
index b0d5a65..1ff97c8 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor
@@ -53,7 +53,7 @@ </div> } <br/> -<LinkButton OnClick="@Apply">Apply</LinkButton> +<LinkButton OnClickAsync="@Apply">Apply</LinkButton> @code { diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectionsEditor.razor b/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectionsEditor.razor
index ea39c9a..9b0266c 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectionsEditor.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectionsEditor.razor
@@ -49,7 +49,7 @@ </div> } <br/> -<LinkButton OnClick="@Apply">Apply</LinkButton> +<LinkButton OnClickAsync="@Apply">Apply</LinkButton> @code { diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirWatchedListsEditor.razor b/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirWatchedListsEditor.razor
index 9e70687..69a9048 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirWatchedListsEditor.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirWatchedListsEditor.razor
@@ -49,7 +49,7 @@ </div> } <br/> -<LinkButton OnClick="@Apply">Apply</LinkButton> +<LinkButton OnClickAsync="@Apply">Apply</LinkButton> @code { diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor b/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor
index b62cf57..1fd0ff6 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor
@@ -14,10 +14,10 @@ <p>Users (regex): </p> <InputTextArea @bind-Value="@UserIdString"></InputTextArea> -<LinkButton OnClick="Execute">Execute</LinkButton> +<LinkButton OnClickAsync="Execute">Execute</LinkButton> <br/> -<LinkButton OnClick="RemoveKicks">Remove kicks</LinkButton> -<LinkButton OnClick="RemoveBans">Remove bans</LinkButton> +<LinkButton OnClickAsync="RemoveKicks">Remove kicks</LinkButton> +<LinkButton OnClickAsync="RemoveBans">Remove bans</LinkButton> <br/> diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/InviteCounter.razor b/MatrixUtils.Web/Pages/Tools/Moderation/InviteCounter.razor
index 5c5946f..ac68e3d 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/InviteCounter.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/InviteCounter.razor
@@ -9,7 +9,7 @@ <br/> <span>Room ID: </span> <InputText @bind-Value="@roomId"></InputText> -<LinkButton OnClick="@Execute">Execute</LinkButton> +<LinkButton OnClickAsync="@Execute">Execute</LinkButton> <br/> diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/MassCMEBan.razor b/MatrixUtils.Web/Pages/Tools/Moderation/MassCMEBan.razor
index 8fdad84..605890d 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/MassCMEBan.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/MassCMEBan.razor
@@ -8,7 +8,7 @@ <br/> <span>Users:</span> <InputTextArea @bind-Value="@roomId"></InputTextArea> -<LinkButton OnClick="@Execute">Execute</LinkButton> +<LinkButton OnClickAsync="@Execute">Execute</LinkButton> <br/> diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/MembershipHistory.razor b/MatrixUtils.Web/Pages/Tools/Moderation/MembershipHistory.razor
index 1ec3cd0..11c4a80 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/MembershipHistory.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/MembershipHistory.razor
@@ -16,7 +16,7 @@ <br/> <span>Room ID: </span> <InputText @bind-Value="@RoomId"></InputText> -<LinkButton OnClick="@Execute">Execute</LinkButton> +<LinkButton OnClickAsync="@Execute">Execute</LinkButton> <p> <span><InputCheckbox @bind-Value="ChronologicalOrder"/>Chronological order</span> <span><InputCheckbox @bind-Value="DoDisambiguate"/>Enable extended filters</span> @@ -30,17 +30,17 @@ <span><InputCheckbox @bind-Value="ShowBans"/> bans</span> </p> <p> - <LinkButton OnClick="@(async () => { + <LinkButton OnClickAsync="@(async () => { ShowJoins = ShowLeaves = ShowKnocks = ShowInvites = ShowBans = false; StateHasChanged(); })">Hide all </LinkButton> - <LinkButton OnClick="@(async () => { + <LinkButton OnClickAsync="@(async () => { ShowJoins = ShowLeaves = ShowKnocks = ShowInvites = ShowBans = true; StateHasChanged(); })">Show all </LinkButton> - <LinkButton OnClick="@(async () => { + <LinkButton OnClickAsync="@(async () => { ShowJoins ^= true; ShowLeaves ^= true; ShowKnocks ^= true; @@ -129,17 +129,17 @@ </p> <p> - <LinkButton OnClick="@(async () => { + <LinkButton OnClickAsync="@(async () => { DoDisambiguate = DisambiguateProfileUpdates = DisambiguateKicks = DisambiguateUnbans = DisambiguateInviteAccepted = DisambiguateInviteRejected = DisambiguateInviteRetracted = DisambiguateKnockAccepted = DisambiguateKnockRejected = DisambiguateKnockRetracted = DisambiguateKnockActions = DisambiguateInviteActions = false; StateHasChanged(); })">Un-disambiguate all </LinkButton> - <LinkButton OnClick="@(async () => { + <LinkButton OnClickAsync="@(async () => { DoDisambiguate = DisambiguateProfileUpdates = DisambiguateKicks = DisambiguateUnbans = DisambiguateInviteAccepted = DisambiguateInviteRejected = DisambiguateInviteRetracted = DisambiguateKnockAccepted = DisambiguateKnockRejected = DisambiguateKnockRetracted = DisambiguateKnockActions = DisambiguateInviteActions = true; StateHasChanged(); })">Disambiguate all </LinkButton> - <LinkButton OnClick="@(async () => { + <LinkButton OnClickAsync="@(async () => { DisambiguateProfileUpdates ^= true; DisambiguateKicks ^= true; DisambiguateUnbans ^= true; diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor b/MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor
index 31fde0b..ee77532 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/RoomIntersections.razor
@@ -8,13 +8,13 @@ <p>Set A: </p> <InputText @bind-Value="@ImportSetASpaceId"></InputText> -<LinkButton OnClick="@(() => AppendSet(ImportSetASpaceId, RoomsA))">Append Set A</LinkButton> +<LinkButton OnClickAsync="@(() => AppendSet(ImportSetASpaceId, RoomsA))">Append Set A</LinkButton> <p>Set B: </p> <InputText @bind-Value="@ImportSetBSpaceId"></InputText> -<LinkButton OnClick="@(() => AppendSet(ImportSetBSpaceId, RoomsB))">Append Set B</LinkButton> +<LinkButton OnClickAsync="@(() => AppendSet(ImportSetBSpaceId, RoomsB))">Append Set B</LinkButton> <br/> -<LinkButton OnClick="@Execute">Execute</LinkButton> +<LinkButton OnClickAsync="@Execute">Execute</LinkButton> <br/> <details> diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor b/MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor
index c3cc09c..f39a2eb 100644 --- a/MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor +++ b/MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor
@@ -11,7 +11,7 @@ <InputTextArea @bind-Value="@UserIdString"></InputTextArea> <br/> <InputText @bind-Value="@ImportFromRoomId"></InputText> -<LinkButton OnClick="@DoImportFromRoomId">Import from room (ID)</LinkButton> +<LinkButton OnClickAsync="@DoImportFromRoomId">Import from room (ID)</LinkButton> <details> <summary>Rooms to be searched (@rooms.Count)</summary> @@ -21,7 +21,7 @@ } </details> <br/> -<LinkButton OnClick="Execute">Execute</LinkButton> +<LinkButton OnClickAsync="Execute">Execute</LinkButton> <br/> <details> diff --git a/MatrixUtils.Web/Pages/Tools/Room/DropPowerlevel.razor b/MatrixUtils.Web/Pages/Tools/Room/DropPowerlevel.razor
index 3f9c141..208cd19 100644 --- a/MatrixUtils.Web/Pages/Tools/Room/DropPowerlevel.razor +++ b/MatrixUtils.Web/Pages/Tools/Room/DropPowerlevel.razor
@@ -6,7 +6,7 @@ <span>User ID: </span><FancyTextBox @bind-Value="@UserId"/><br/> <span>Room ID: </span><FancyTextBox @bind-Value="@RoomId"/><br/> -<LinkButton OnClick="@Execute">Execute</LinkButton> +<LinkButton OnClickAsync="@Execute">Execute</LinkButton> <pre>@Result</pre> diff --git a/MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor b/MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor
index 5d5ca20..d6ae945 100644 --- a/MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor +++ b/MatrixUtils.Web/Pages/Tools/Room/SpaceRestrictedJoins.razor
@@ -10,7 +10,7 @@ <p><InputCheckbox @bind-Value="@ChangeKnocking"/> Change knock access: <InputCheckbox @bind-Value="@Knocking"/></p> <br/> -<LinkButton OnClick="Execute">Execute</LinkButton> +<LinkButton OnClickAsync="Execute">Execute</LinkButton> <br/> <br/> diff --git a/MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor b/MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor
index e5ffd5b..b893970 100644 --- a/MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor +++ b/MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor
@@ -12,7 +12,7 @@ } <br/> -<LinkButton OnClick="Execute">Execute</LinkButton> +<LinkButton OnClickAsync="Execute">Execute</LinkButton> <br/> @foreach (var line in Enumerable.Reverse(log)) { <p>@line</p> diff --git a/MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor b/MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor
index c373a37..748f2fb 100644 --- a/MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor +++ b/MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor
@@ -13,7 +13,7 @@ } <br/> -<LinkButton OnClick="Execute">Execute</LinkButton> +<LinkButton OnClickAsync="Execute">Execute</LinkButton> <br/> @foreach (var line in Enumerable.Reverse(log)) { <p>@line</p> diff --git a/MatrixUtils.Web/Pages/User/Profile.razor b/MatrixUtils.Web/Pages/User/Profile.razor
index ccd3e7b..290b92a 100644 --- a/MatrixUtils.Web/Pages/User/Profile.razor +++ b/MatrixUtils.Web/Pages/User/Profile.razor
@@ -15,8 +15,8 @@ <span>Display name: </span><FancyTextBox @bind-Value="@NewProfile.DisplayName"></FancyTextBox><br/> <span>Avatar URL: </span><FancyTextBox @bind-Value="@NewProfile.AvatarUrl"></FancyTextBox> <InputFile OnChange="@AvatarChanged"></InputFile><br/> - <LinkButton OnClick="@(() => UpdateProfile())">Update profile</LinkButton> - <LinkButton OnClick="@(() => UpdateProfile(true))">Update profile (restore room overrides)</LinkButton> + <LinkButton OnClickAsync="@(() => UpdateProfile())">Update profile</LinkButton> + <LinkButton OnClickAsync="@(() => UpdateProfile(true))">Update profile (restore room overrides)</LinkButton> </div> </div> @if (!string.IsNullOrWhiteSpace(Status)) { @@ -44,7 +44,7 @@ <span>Display name: </span><FancyTextBox BackgroundColor="@(room.OwnMembership.DisplayName == OldProfile.DisplayName ? "" : "#ffff0033")" @bind-Value="@room.OwnMembership.DisplayName"></FancyTextBox><br/> <span>Avatar URL: </span><FancyTextBox BackgroundColor="@(room.OwnMembership.AvatarUrl == OldProfile.AvatarUrl ? "" : "#ffff0033")" @bind-Value="@room.OwnMembership.AvatarUrl"></FancyTextBox> <InputFile OnChange="@(ifcea => RoomAvatarChanged(ifcea, room.Room.RoomId))"></InputFile><br/> - <LinkButton OnClick="@(() => UpdateRoomProfile(room.Room.RoomId))">Update profile</LinkButton> + <LinkButton OnClickAsync="@(() => UpdateRoomProfile(room.Room.RoomId))">Update profile</LinkButton> </div> <br/> @if (!string.IsNullOrWhiteSpace(Status)) { diff --git a/MatrixUtils.Web/Shared/InputLocalPart.razor b/MatrixUtils.Web/Shared/InputLocalPart.razor new file mode 100644
index 0000000..8f34377 --- /dev/null +++ b/MatrixUtils.Web/Shared/InputLocalPart.razor
@@ -0,0 +1,50 @@ +<div style="display: inline-flex;"> + @if (!string.IsNullOrWhiteSpace(Label)) { + <label>@Label</label> + } + <span>@Sigil</span> + <FancyTextBox @bind-Value="@LocalPart"></FancyTextBox> + <span>:</span> + @if (ServerNameChanged is not null) { + <FancyTextBox @bind-Value="@ServerName"></FancyTextBox> + } + else { + <span>@ServerName</span> + } +</div> + +@code { + + [Parameter] + public string? Label { get; set; } + + [Parameter] + public required string Sigil { get; set; } + + [Parameter] + public string? LocalPart { + get; + set { + if (field == value) return; + field = value; + LocalPartChanged.InvokeAsync(value); + } + } + + [Parameter] + public EventCallback<string> LocalPartChanged { get; set; } + + [Parameter] + public string? ServerName { + get; + set { + if (field == value) return; + field = value; + ServerNameChanged?.InvokeAsync(value); + } + } + + [Parameter] + public EventCallback<string>? ServerNameChanged { get; set; } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor b/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor
index f7bb200..99dbbc3 100644 --- a/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor +++ b/MatrixUtils.Web/Shared/PolicyEditorComponents/MassPolicyEditorModal.razor
@@ -36,15 +36,15 @@ @* </pre> *@ @* </details> *@ @if (!VerifyIntent) { - <LinkButton OnClick="@(() => { - OnClose.Invoke(); - return Task.CompletedTask; - })"> Cancel + <LinkButton OnClickAsync="@(() => { + OnClose.Invoke(); + return Task.CompletedTask; + })"> Cancel </LinkButton> - <LinkButton OnClick="@(() => { - _ = Save(); - return Task.CompletedTask; - })"> Save + <LinkButton OnClickAsync="@(() => { + _ = Save(); + return Task.CompletedTask; + })"> Save </LinkButton> @if (!string.IsNullOrWhiteSpace(Response)) { <pre style="color: red;">@Response</pre> @@ -63,14 +63,9 @@ VerifyIntent = false; Response = null; StateHasChanged(); - return Task.CompletedTask; })">No </LinkButton> - <LinkButton Color="#FF0000" OnClick="@(() => { - _ = Save(force: true); - return Task.CompletedTask; - })"> Yes - </LinkButton> + <LinkButton Color="#FF0000" OnClick="@(() => { _ = Save(force: true); })">Yes</LinkButton> } </ModalWindow> diff --git a/MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor b/MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor
index 5819bee..0205e16 100644 --- a/MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor +++ b/MatrixUtils.Web/Shared/PolicyEditorComponents/PolicyEditorModal.razor
@@ -6,7 +6,7 @@ @using System.Collections.Frozen @using LibMatrix.EventTypes <ModalWindow Title="@((string.IsNullOrWhiteSpace(PolicyEvent.EventId) ? "Creating new " : "Editing ") + (PolicyEvent.MappedType.GetFriendlyNameOrNull()?.ToLower() ?? "event"))" - OnCloseClicked="@OnClose" X="60" Y="60" MinWidth="300"> + OnCloseClickedAsync="@InvokeOnClose" X="60" Y="60" MinWidth="300"> @if (string.IsNullOrWhiteSpace(PolicyEvent.EventId)) { <span>Policy type:</span> <select @bind="@MappedType"> @@ -52,7 +52,12 @@ else { switch (Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType) { case Type t when t == typeof(string): - <FancyTextBox Value="@(getter?.Invoke(PolicyData, null) as string)" ValueChanged="@((string e) => { Console.WriteLine($"{prop.Name} ({setter is not null}) -> {e}"); setter?.Invoke(PolicyData, [e]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); })"></FancyTextBox> + <FancyTextBox Value="@(getter?.Invoke(PolicyData, null) as string)" ValueChanged="@((string e) => { + Console.WriteLine($"{prop.Name} ({setter is not null}) -> {e}"); + setter?.Invoke(PolicyData, [e]); + PolicyEvent.TypedContent = PolicyData; + StateHasChanged(); + })"></FancyTextBox> break; case Type t when t == typeof(DateTime): if (!isNullable) { @@ -61,13 +66,22 @@ else { var value = getter?.Invoke(PolicyData, null) as DateTime?; if (value is null) { - <button @onclick="() => { setter?.Invoke(PolicyData, [DateTime.Now]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); }">Add value</button> + <button @onclick="() => { setter?.Invoke(PolicyData, [DateTime.Now]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); }"> + Add value + </button> } else { var notNullValue = Nullable.GetValueRefOrDefaultRef(ref value); Console.WriteLine($"Value: {value?.ToString() ?? "null"}"); - <InputDate TValue="DateTime" ValueExpression="@(() => notNullValue)" ValueChanged="@(e => { Console.WriteLine($"{prop.Name} ({setter is not null}) -> {e}"); setter?.Invoke(PolicyData, [e]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); })"></InputDate> - <button @onclick="() => { setter?.Invoke(PolicyData, [null]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); }">Remove value</button> + <InputDate TValue="DateTime" ValueExpression="@(() => notNullValue)" ValueChanged="@(e => { + Console.WriteLine($"{prop.Name} ({setter is not null}) -> {e}"); + setter?.Invoke(PolicyData, [e]); + PolicyEvent.TypedContent = PolicyData; + StateHasChanged(); + })"></InputDate> + <button @onclick="() => { setter?.Invoke(PolicyData, [null]); PolicyEvent.TypedContent = PolicyData; StateHasChanged(); }">Remove + value + </button> } } @@ -88,8 +102,8 @@ @PolicyEvent.ToJson(true, true) </pre> </details> - <LinkButton OnClick="@(() => { OnClose.Invoke(); return Task.CompletedTask; })"> Cancel </LinkButton> - <LinkButton OnClick="@(() => { OnSave.Invoke(PolicyEvent); return Task.CompletedTask; })"> Save </LinkButton> + <LinkButton OnClickAsync="@InvokeOnClose">Cancel</LinkButton> + <LinkButton OnClickAsync="@InvokeOnSave">Save</LinkButton> } else { <p>Policy data is null</p> @@ -111,10 +125,32 @@ } [Parameter] - public required Action OnClose { get; set; } + public Action? OnClose { get; set; } [Parameter] - public required Action<StateEventResponse> OnSave { get; set; } + public Func<Task>? OnCloseAsync { get; set; } + + private async Task InvokeOnClose() { + if (OnClose is not null) + OnClose.Invoke(); + + if (OnCloseAsync is not null) + await OnCloseAsync.Invoke(); + } + + [Parameter] + public Action<StateEventResponse>? OnSave { get; set; } + + [Parameter] + public Func<StateEventResponse, Task>? OnSaveAsync { get; set; } + + private async Task InvokeOnSave() { + if (OnSave is not null) + OnSave.Invoke(PolicyEvent); + + if (OnSaveAsync is not null) + await OnSaveAsync.Invoke(PolicyEvent); + } public PolicyRuleEventContent? PolicyData { get; set; } diff --git a/MatrixUtils.Web/Shared/UserListItem.razor b/MatrixUtils.Web/Shared/UserListItem.razor
index 5084807..fd2fdec 100644 --- a/MatrixUtils.Web/Shared/UserListItem.razor +++ b/MatrixUtils.Web/Shared/UserListItem.razor
@@ -1,7 +1,12 @@ @using LibMatrix.Responses @using ArcaneLibs <div style="background-color: #ffffff11; border-radius: 25px; margin: 8px; width: fit-Content;"> - <img style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height: 32px; border-radius: 50%;" src="@(string.IsNullOrWhiteSpace(User?.AvatarUrl) ? _identiconGenerator.GenerateAsDataUri(UserId) : User.AvatarUrl)"/> + @if (!string.IsNullOrWhiteSpace(User?.AvatarUrl)) { + <MxcAvatar Homeserver="@_homeserver" Size="32" Circular="true" MxcUri="@User.AvatarUrl"/> + } + else { + <img style="@(ChildContent is not null ? "vertical-align: baseline;" : "") width: 32px; height: 32px; border-radius: 50%;" src="@_identiconGenerator.GenerateAsDataUri(UserId)"/> + } <span style="vertical-align: middle; margin-right: 8px; border-radius: 75px;">@User?.DisplayName</span> <div style="display: inline-block;"> @@ -26,7 +31,7 @@ [Parameter] public AuthenticatedHomeserverGeneric _homeserver { get; set; } - private SvgIdenticonGenerator _identiconGenerator = new(); + private static SvgIdenticonGenerator _identiconGenerator = new(); protected override async Task OnInitializedAsync() { // _homeserver = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true); @@ -37,7 +42,14 @@ throw new ArgumentNullException(nameof(UserId)); } - User = await _homeserver.GetProfileAsync(UserId); + try { + User = await _homeserver.GetProfileAsync(UserId); + } + catch (Exception) { + User = new() { + DisplayName = UserId + }; + } } await base.OnInitializedAsync(); diff --git a/MatrixUtils.sln b/MatrixUtils.sln
index 6b33671..889232b 100644 --- a/MatrixUtils.sln +++ b/MatrixUtils.sln
@@ -1,6 +1,5 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixUtils.Web", "MatrixUtils.Web\MatrixUtils.Web.csproj", "{D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixUtils.Web.Server", "MatrixUtils.Web.Server\MatrixUtils.Web.Server.csproj", "{F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}" @@ -71,132 +70,407 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixUtils.Web.Ssr", "Matr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixUtils.Web.Ssr.Client", "MatrixUtils.Web.Ssr\MatrixUtils.Web.Ssr.Client\MatrixUtils.Web.Ssr.Client.csproj", "{335DB9D5-FEEE-45E3-B76A-057D8BB48412}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.Federation", "LibMatrix\LibMatrix.Federation\LibMatrix.Federation.csproj", "{8F154875-96EE-4BE5-8456-F5EBB2516C1C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.FederationTest", "LibMatrix\Utilities\LibMatrix.FederationTest\LibMatrix.FederationTest.csproj", "{960CC2DF-BB1A-4164-A895-834F81B3A113}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Debug|x64.Build.0 = Debug|Any CPU + {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Debug|x86.ActiveCfg = Debug|Any CPU + {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Debug|x86.Build.0 = Debug|Any CPU {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Release|Any CPU.ActiveCfg = Release|Any CPU {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Release|Any CPU.Build.0 = Release|Any CPU + {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Release|x64.ActiveCfg = Release|Any CPU + {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Release|x64.Build.0 = Release|Any CPU + {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Release|x86.ActiveCfg = Release|Any CPU + {D38DA95D-DD83-4340-96A4-6F59FC6AE3D9}.Release|x86.Build.0 = Release|Any CPU {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Debug|x64.ActiveCfg = Debug|Any CPU + {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Debug|x64.Build.0 = Debug|Any CPU + {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Debug|x86.ActiveCfg = Debug|Any CPU + {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Debug|x86.Build.0 = Debug|Any CPU {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Release|Any CPU.Build.0 = Release|Any CPU + {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Release|x64.ActiveCfg = Release|Any CPU + {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Release|x64.Build.0 = Release|Any CPU + {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Release|x86.ActiveCfg = Release|Any CPU + {F997F26F-2EC1-4D18-B3DD-C46FB2AD65C0}.Release|x86.Build.0 = Release|Any CPU {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Debug|x64.ActiveCfg = Debug|Any CPU + {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Debug|x64.Build.0 = Debug|Any CPU + {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Debug|x86.ActiveCfg = Debug|Any CPU + {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Debug|x86.Build.0 = Debug|Any CPU {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Release|Any CPU.ActiveCfg = Release|Any CPU {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Release|Any CPU.Build.0 = Release|Any CPU + {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Release|x64.ActiveCfg = Release|Any CPU + {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Release|x64.Build.0 = Release|Any CPU + {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Release|x86.ActiveCfg = Release|Any CPU + {27C08A4F-5AF0-4C2C-AFCB-050E3388C116}.Release|x86.Build.0 = Release|Any CPU {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Debug|x64.ActiveCfg = Debug|Any CPU + {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Debug|x64.Build.0 = Debug|Any CPU + {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Debug|x86.ActiveCfg = Debug|Any CPU + {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Debug|x86.Build.0 = Debug|Any CPU {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Release|Any CPU.ActiveCfg = Release|Any CPU {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Release|Any CPU.Build.0 = Release|Any CPU + {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Release|x64.ActiveCfg = Release|Any CPU + {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Release|x64.Build.0 = Release|Any CPU + {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Release|x86.ActiveCfg = Release|Any CPU + {EDD2FBAB-2DEC-4527-AE9C-20E21D0D6B14}.Release|x86.Build.0 = Release|Any CPU {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Debug|x64.ActiveCfg = Debug|Any CPU + {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Debug|x64.Build.0 = Debug|Any CPU + {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Debug|x86.ActiveCfg = Debug|Any CPU + {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Debug|x86.Build.0 = Debug|Any CPU {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Release|Any CPU.Build.0 = Release|Any CPU + {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Release|x64.ActiveCfg = Release|Any CPU + {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Release|x64.Build.0 = Release|Any CPU + {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Release|x86.ActiveCfg = Release|Any CPU + {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Release|x86.Build.0 = Release|Any CPU {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Debug|x64.ActiveCfg = Debug|Any CPU + {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Debug|x64.Build.0 = Debug|Any CPU + {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Debug|x86.ActiveCfg = Debug|Any CPU + {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Debug|x86.Build.0 = Debug|Any CPU {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Release|Any CPU.Build.0 = Release|Any CPU + {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Release|x64.ActiveCfg = Release|Any CPU + {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Release|x64.Build.0 = Release|Any CPU + {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Release|x86.ActiveCfg = Release|Any CPU + {EC5536AB-0613-4CB5-B22B-822A3DBB112A}.Release|x86.Build.0 = Release|Any CPU {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Debug|x64.ActiveCfg = Debug|Any CPU + {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Debug|x64.Build.0 = Debug|Any CPU + {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Debug|x86.Build.0 = Debug|Any CPU {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Release|Any CPU.Build.0 = Release|Any CPU + {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Release|x64.ActiveCfg = Release|Any CPU + {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Release|x64.Build.0 = Release|Any CPU + {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Release|x86.ActiveCfg = Release|Any CPU + {CF252EDF-C5A1-4030-8666-C78AA0A3B7DE}.Release|x86.Build.0 = Release|Any CPU {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Debug|x64.ActiveCfg = Debug|Any CPU + {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Debug|x64.Build.0 = Debug|Any CPU + {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Debug|x86.ActiveCfg = Debug|Any CPU + {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Debug|x86.Build.0 = Debug|Any CPU {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Release|Any CPU.Build.0 = Release|Any CPU + {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Release|x64.ActiveCfg = Release|Any CPU + {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Release|x64.Build.0 = Release|Any CPU + {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Release|x86.ActiveCfg = Release|Any CPU + {0C542A8E-54B6-4A20-B7A9-8C7190A0C232}.Release|x86.Build.0 = Release|Any CPU {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Debug|x64.ActiveCfg = Debug|Any CPU + {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Debug|x64.Build.0 = Debug|Any CPU + {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Debug|x86.ActiveCfg = Debug|Any CPU + {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Debug|x86.Build.0 = Debug|Any CPU {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Release|Any CPU.ActiveCfg = Release|Any CPU {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Release|Any CPU.Build.0 = Release|Any CPU + {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Release|x64.ActiveCfg = Release|Any CPU + {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Release|x64.Build.0 = Release|Any CPU + {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Release|x86.ActiveCfg = Release|Any CPU + {48AF8AC7-5F59-4401-B173-523D37FDD7A8}.Release|x86.Build.0 = Release|Any CPU {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Debug|x64.ActiveCfg = Debug|Any CPU + {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Debug|x64.Build.0 = Debug|Any CPU + {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Debug|x86.ActiveCfg = Debug|Any CPU + {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Debug|x86.Build.0 = Debug|Any CPU {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Release|Any CPU.ActiveCfg = Release|Any CPU {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Release|Any CPU.Build.0 = Release|Any CPU + {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Release|x64.ActiveCfg = Release|Any CPU + {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Release|x64.Build.0 = Release|Any CPU + {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Release|x86.ActiveCfg = Release|Any CPU + {CFAFFBF1-8C85-4FB2-AB32-B5C17AC7BB5D}.Release|x86.Build.0 = Release|Any CPU {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Debug|x64.ActiveCfg = Debug|Any CPU + {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Debug|x64.Build.0 = Debug|Any CPU + {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Debug|x86.ActiveCfg = Debug|Any CPU + {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Debug|x86.Build.0 = Debug|Any CPU {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Release|Any CPU.Build.0 = Release|Any CPU + {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Release|x64.ActiveCfg = Release|Any CPU + {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Release|x64.Build.0 = Release|Any CPU + {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Release|x86.ActiveCfg = Release|Any CPU + {ADFBDF2D-0CEC-43C1-8896-75DCE439CF72}.Release|x86.Build.0 = Release|Any CPU {D6315791-949B-4501-AA95-50516DE899C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D6315791-949B-4501-AA95-50516DE899C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6315791-949B-4501-AA95-50516DE899C1}.Debug|x64.ActiveCfg = Debug|Any CPU + {D6315791-949B-4501-AA95-50516DE899C1}.Debug|x64.Build.0 = Debug|Any CPU + {D6315791-949B-4501-AA95-50516DE899C1}.Debug|x86.ActiveCfg = Debug|Any CPU + {D6315791-949B-4501-AA95-50516DE899C1}.Debug|x86.Build.0 = Debug|Any CPU {D6315791-949B-4501-AA95-50516DE899C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {D6315791-949B-4501-AA95-50516DE899C1}.Release|Any CPU.Build.0 = Release|Any CPU + {D6315791-949B-4501-AA95-50516DE899C1}.Release|x64.ActiveCfg = Release|Any CPU + {D6315791-949B-4501-AA95-50516DE899C1}.Release|x64.Build.0 = Release|Any CPU + {D6315791-949B-4501-AA95-50516DE899C1}.Release|x86.ActiveCfg = Release|Any CPU + {D6315791-949B-4501-AA95-50516DE899C1}.Release|x86.Build.0 = Release|Any CPU {03466515-77CC-49E4-90E5-9A21EDD0A644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {03466515-77CC-49E4-90E5-9A21EDD0A644}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03466515-77CC-49E4-90E5-9A21EDD0A644}.Debug|x64.ActiveCfg = Debug|Any CPU + {03466515-77CC-49E4-90E5-9A21EDD0A644}.Debug|x64.Build.0 = Debug|Any CPU + {03466515-77CC-49E4-90E5-9A21EDD0A644}.Debug|x86.ActiveCfg = Debug|Any CPU + {03466515-77CC-49E4-90E5-9A21EDD0A644}.Debug|x86.Build.0 = Debug|Any CPU {03466515-77CC-49E4-90E5-9A21EDD0A644}.Release|Any CPU.ActiveCfg = Release|Any CPU {03466515-77CC-49E4-90E5-9A21EDD0A644}.Release|Any CPU.Build.0 = Release|Any CPU + {03466515-77CC-49E4-90E5-9A21EDD0A644}.Release|x64.ActiveCfg = Release|Any CPU + {03466515-77CC-49E4-90E5-9A21EDD0A644}.Release|x64.Build.0 = Release|Any CPU + {03466515-77CC-49E4-90E5-9A21EDD0A644}.Release|x86.ActiveCfg = Release|Any CPU + {03466515-77CC-49E4-90E5-9A21EDD0A644}.Release|x86.Build.0 = Release|Any CPU {0336306C-285A-4810-9253-5C5F0373992E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0336306C-285A-4810-9253-5C5F0373992E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0336306C-285A-4810-9253-5C5F0373992E}.Debug|x64.ActiveCfg = Debug|Any CPU + {0336306C-285A-4810-9253-5C5F0373992E}.Debug|x64.Build.0 = Debug|Any CPU + {0336306C-285A-4810-9253-5C5F0373992E}.Debug|x86.ActiveCfg = Debug|Any CPU + {0336306C-285A-4810-9253-5C5F0373992E}.Debug|x86.Build.0 = Debug|Any CPU {0336306C-285A-4810-9253-5C5F0373992E}.Release|Any CPU.ActiveCfg = Release|Any CPU {0336306C-285A-4810-9253-5C5F0373992E}.Release|Any CPU.Build.0 = Release|Any CPU + {0336306C-285A-4810-9253-5C5F0373992E}.Release|x64.ActiveCfg = Release|Any CPU + {0336306C-285A-4810-9253-5C5F0373992E}.Release|x64.Build.0 = Release|Any CPU + {0336306C-285A-4810-9253-5C5F0373992E}.Release|x86.ActiveCfg = Release|Any CPU + {0336306C-285A-4810-9253-5C5F0373992E}.Release|x86.Build.0 = Release|Any CPU {D7E5B226-114C-4747-9277-A4D6341A16FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7E5B226-114C-4747-9277-A4D6341A16FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7E5B226-114C-4747-9277-A4D6341A16FE}.Debug|x64.ActiveCfg = Debug|Any CPU + {D7E5B226-114C-4747-9277-A4D6341A16FE}.Debug|x64.Build.0 = Debug|Any CPU + {D7E5B226-114C-4747-9277-A4D6341A16FE}.Debug|x86.ActiveCfg = Debug|Any CPU + {D7E5B226-114C-4747-9277-A4D6341A16FE}.Debug|x86.Build.0 = Debug|Any CPU {D7E5B226-114C-4747-9277-A4D6341A16FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7E5B226-114C-4747-9277-A4D6341A16FE}.Release|Any CPU.Build.0 = Release|Any CPU + {D7E5B226-114C-4747-9277-A4D6341A16FE}.Release|x64.ActiveCfg = Release|Any CPU + {D7E5B226-114C-4747-9277-A4D6341A16FE}.Release|x64.Build.0 = Release|Any CPU + {D7E5B226-114C-4747-9277-A4D6341A16FE}.Release|x86.ActiveCfg = Release|Any CPU + {D7E5B226-114C-4747-9277-A4D6341A16FE}.Release|x86.Build.0 = Release|Any CPU {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Debug|x64.ActiveCfg = Debug|Any CPU + {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Debug|x64.Build.0 = Debug|Any CPU + {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Debug|x86.ActiveCfg = Debug|Any CPU + {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Debug|x86.Build.0 = Debug|Any CPU {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Release|Any CPU.ActiveCfg = Release|Any CPU {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Release|Any CPU.Build.0 = Release|Any CPU + {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Release|x64.ActiveCfg = Release|Any CPU + {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Release|x64.Build.0 = Release|Any CPU + {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Release|x86.ActiveCfg = Release|Any CPU + {D293AFEC-8322-4FEC-8425-143B5FE10D0F}.Release|x86.Build.0 = Release|Any CPU {FA6A9923-419A-40E1-8A32-30DD906E5025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FA6A9923-419A-40E1-8A32-30DD906E5025}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA6A9923-419A-40E1-8A32-30DD906E5025}.Debug|x64.ActiveCfg = Debug|Any CPU + {FA6A9923-419A-40E1-8A32-30DD906E5025}.Debug|x64.Build.0 = Debug|Any CPU + {FA6A9923-419A-40E1-8A32-30DD906E5025}.Debug|x86.ActiveCfg = Debug|Any CPU + {FA6A9923-419A-40E1-8A32-30DD906E5025}.Debug|x86.Build.0 = Debug|Any CPU {FA6A9923-419A-40E1-8A32-30DD906E5025}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA6A9923-419A-40E1-8A32-30DD906E5025}.Release|Any CPU.Build.0 = Release|Any CPU + {FA6A9923-419A-40E1-8A32-30DD906E5025}.Release|x64.ActiveCfg = Release|Any CPU + {FA6A9923-419A-40E1-8A32-30DD906E5025}.Release|x64.Build.0 = Release|Any CPU + {FA6A9923-419A-40E1-8A32-30DD906E5025}.Release|x86.ActiveCfg = Release|Any CPU + {FA6A9923-419A-40E1-8A32-30DD906E5025}.Release|x86.Build.0 = Release|Any CPU {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Debug|x64.ActiveCfg = Debug|Any CPU + {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Debug|x64.Build.0 = Debug|Any CPU + {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Debug|x86.ActiveCfg = Debug|Any CPU + {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Debug|x86.Build.0 = Debug|Any CPU {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Release|Any CPU.ActiveCfg = Release|Any CPU {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Release|Any CPU.Build.0 = Release|Any CPU + {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Release|x64.ActiveCfg = Release|Any CPU + {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Release|x64.Build.0 = Release|Any CPU + {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Release|x86.ActiveCfg = Release|Any CPU + {43ECF2DB-CBA6-4A31-BD6A-B059CEA03CA0}.Release|x86.Build.0 = Release|Any CPU {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Debug|x64.Build.0 = Debug|Any CPU + {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Debug|x86.Build.0 = Debug|Any CPU {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Release|Any CPU.Build.0 = Release|Any CPU + {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Release|x64.ActiveCfg = Release|Any CPU + {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Release|x64.Build.0 = Release|Any CPU + {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Release|x86.ActiveCfg = Release|Any CPU + {CC87DFFB-EE19-4147-9212-4FAF16D79AD5}.Release|x86.Build.0 = Release|Any CPU {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Debug|x64.ActiveCfg = Debug|Any CPU + {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Debug|x64.Build.0 = Debug|Any CPU + {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Debug|x86.ActiveCfg = Debug|Any CPU + {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Debug|x86.Build.0 = Debug|Any CPU {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Release|Any CPU.ActiveCfg = Release|Any CPU {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Release|Any CPU.Build.0 = Release|Any CPU + {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Release|x64.ActiveCfg = Release|Any CPU + {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Release|x64.Build.0 = Release|Any CPU + {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Release|x86.ActiveCfg = Release|Any CPU + {DBCE6260-052E-46F9-ACCD-059AA51B8A48}.Release|x86.Build.0 = Release|Any CPU {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Debug|x64.ActiveCfg = Debug|Any CPU + {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Debug|x64.Build.0 = Debug|Any CPU + {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Debug|x86.ActiveCfg = Debug|Any CPU + {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Debug|x86.Build.0 = Debug|Any CPU {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Release|Any CPU.ActiveCfg = Release|Any CPU {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Release|Any CPU.Build.0 = Release|Any CPU + {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Release|x64.ActiveCfg = Release|Any CPU + {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Release|x64.Build.0 = Release|Any CPU + {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Release|x86.ActiveCfg = Release|Any CPU + {7AA3CDF9-D1F6-4A12-BA47-EB721F353701}.Release|x86.Build.0 = Release|Any CPU {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Debug|x64.ActiveCfg = Debug|Any CPU + {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Debug|x64.Build.0 = Debug|Any CPU + {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Debug|x86.ActiveCfg = Debug|Any CPU + {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Debug|x86.Build.0 = Debug|Any CPU {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Release|Any CPU.ActiveCfg = Release|Any CPU {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Release|Any CPU.Build.0 = Release|Any CPU + {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Release|x64.ActiveCfg = Release|Any CPU + {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Release|x64.Build.0 = Release|Any CPU + {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Release|x86.ActiveCfg = Release|Any CPU + {D7F9BDF7-35B7-4C84-A34E-B940C1763CC9}.Release|x86.Build.0 = Release|Any CPU {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Debug|x64.ActiveCfg = Debug|Any CPU + {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Debug|x64.Build.0 = Debug|Any CPU + {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Debug|x86.ActiveCfg = Debug|Any CPU + {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Debug|x86.Build.0 = Debug|Any CPU {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Release|Any CPU.ActiveCfg = Release|Any CPU {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Release|Any CPU.Build.0 = Release|Any CPU + {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Release|x64.ActiveCfg = Release|Any CPU + {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Release|x64.Build.0 = Release|Any CPU + {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Release|x86.ActiveCfg = Release|Any CPU + {72D44C6C-1BC7-4310-B1A9-1169C0812E33}.Release|x86.Build.0 = Release|Any CPU {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Debug|x64.ActiveCfg = Debug|Any CPU + {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Debug|x64.Build.0 = Debug|Any CPU + {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Debug|x86.ActiveCfg = Debug|Any CPU + {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Debug|x86.Build.0 = Debug|Any CPU {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Release|Any CPU.ActiveCfg = Release|Any CPU {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Release|Any CPU.Build.0 = Release|Any CPU + {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Release|x64.ActiveCfg = Release|Any CPU + {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Release|x64.Build.0 = Release|Any CPU + {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Release|x86.ActiveCfg = Release|Any CPU + {CDBE012E-B48B-4F9D-8CA4-99F6328E9630}.Release|x86.Build.0 = Release|Any CPU {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Debug|x64.ActiveCfg = Debug|Any CPU + {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Debug|x64.Build.0 = Debug|Any CPU + {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Debug|x86.ActiveCfg = Debug|Any CPU + {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Debug|x86.Build.0 = Debug|Any CPU {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Release|Any CPU.ActiveCfg = Release|Any CPU {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Release|Any CPU.Build.0 = Release|Any CPU + {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Release|x64.ActiveCfg = Release|Any CPU + {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Release|x64.Build.0 = Release|Any CPU + {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Release|x86.ActiveCfg = Release|Any CPU + {3BD05B05-86DE-4680-A7A0-5A326E41E776}.Release|x86.Build.0 = Release|Any CPU {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Debug|x64.ActiveCfg = Debug|Any CPU + {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Debug|x64.Build.0 = Debug|Any CPU + {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Debug|x86.ActiveCfg = Debug|Any CPU + {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Debug|x86.Build.0 = Debug|Any CPU {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Release|Any CPU.ActiveCfg = Release|Any CPU {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Release|Any CPU.Build.0 = Release|Any CPU + {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Release|x64.ActiveCfg = Release|Any CPU + {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Release|x64.Build.0 = Release|Any CPU + {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Release|x86.ActiveCfg = Release|Any CPU + {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53}.Release|x86.Build.0 = Release|Any CPU {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Debug|x64.ActiveCfg = Debug|Any CPU + {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Debug|x64.Build.0 = Debug|Any CPU + {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Debug|x86.ActiveCfg = Debug|Any CPU + {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Debug|x86.Build.0 = Debug|Any CPU {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Release|Any CPU.ActiveCfg = Release|Any CPU {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Release|Any CPU.Build.0 = Release|Any CPU + {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Release|x64.ActiveCfg = Release|Any CPU + {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Release|x64.Build.0 = Release|Any CPU + {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Release|x86.ActiveCfg = Release|Any CPU + {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684}.Release|x86.Build.0 = Release|Any CPU {CEECE820-1BA9-4E29-8668-25967B3E712B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CEECE820-1BA9-4E29-8668-25967B3E712B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CEECE820-1BA9-4E29-8668-25967B3E712B}.Debug|x64.ActiveCfg = Debug|Any CPU + {CEECE820-1BA9-4E29-8668-25967B3E712B}.Debug|x64.Build.0 = Debug|Any CPU + {CEECE820-1BA9-4E29-8668-25967B3E712B}.Debug|x86.ActiveCfg = Debug|Any CPU + {CEECE820-1BA9-4E29-8668-25967B3E712B}.Debug|x86.Build.0 = Debug|Any CPU {CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|Any CPU.ActiveCfg = Release|Any CPU {CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|Any CPU.Build.0 = Release|Any CPU + {CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|x64.ActiveCfg = Release|Any CPU + {CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|x64.Build.0 = Release|Any CPU + {CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|x86.ActiveCfg = Release|Any CPU + {CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|x86.Build.0 = Release|Any CPU {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|x64.ActiveCfg = Debug|Any CPU + {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|x64.Build.0 = Debug|Any CPU + {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|x86.ActiveCfg = Debug|Any CPU + {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|x86.Build.0 = Debug|Any CPU {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|Any CPU.Build.0 = Release|Any CPU + {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|x64.ActiveCfg = Release|Any CPU + {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|x64.Build.0 = Release|Any CPU + {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|x86.ActiveCfg = Release|Any CPU + {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|x86.Build.0 = Release|Any CPU {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|Any CPU.Build.0 = Debug|Any CPU + {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|x64.ActiveCfg = Debug|Any CPU + {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|x64.Build.0 = Debug|Any CPU + {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|x86.ActiveCfg = Debug|Any CPU + {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|x86.Build.0 = Debug|Any CPU {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|Any CPU.ActiveCfg = Release|Any CPU {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|Any CPU.Build.0 = Release|Any CPU + {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|x64.ActiveCfg = Release|Any CPU + {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|x64.Build.0 = Release|Any CPU + {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|x86.ActiveCfg = Release|Any CPU + {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|x86.Build.0 = Release|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|x64.ActiveCfg = Debug|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|x64.Build.0 = Debug|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|x86.ActiveCfg = Debug|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|x86.Build.0 = Debug|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Release|Any CPU.Build.0 = Release|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Release|x64.ActiveCfg = Release|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Release|x64.Build.0 = Release|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Release|x86.ActiveCfg = Release|Any CPU + {8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Release|x86.Build.0 = Release|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Debug|Any CPU.Build.0 = Debug|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Debug|x64.ActiveCfg = Debug|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Debug|x64.Build.0 = Debug|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Debug|x86.ActiveCfg = Debug|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Debug|x86.Build.0 = Debug|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|Any CPU.ActiveCfg = Release|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|Any CPU.Build.0 = Release|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|x64.ActiveCfg = Release|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|x64.Build.0 = Release|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|x86.ActiveCfg = Release|Any CPU + {960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {84BE90C4-2FDE-4A48-B154-58926EF24846} = {933DC8A6-8B1F-46BF-9046-4B636AA46469} @@ -223,5 +497,7 @@ Global {3BD05B05-86DE-4680-A7A0-5A326E41E776} = {0641F1C8-8518-4C67-B385-832745C063FD} {98BB2D9F-BFB9-4E70-93D5-7C4C1205BD53} = {0641F1C8-8518-4C67-B385-832745C063FD} {44BFB1AD-62FB-4B5B-A5A8-E7D04D731684} = {0641F1C8-8518-4C67-B385-832745C063FD} + {8F154875-96EE-4BE5-8456-F5EBB2516C1C} = {933DC8A6-8B1F-46BF-9046-4B636AA46469} + {960CC2DF-BB1A-4164-A895-834F81B3A113} = {80828C75-9C5B-442F-86A4-8CE9D85E811C} EndGlobalSection EndGlobal