diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor
deleted file mode 100644
index 18d7c3f..0000000
--- a/MatrixRoomUtils.Web/Pages/About.razor
+++ /dev/null
@@ -1,12 +0,0 @@
-@page "/About"
-
-<PageTitle>About</PageTitle>
-
-<h3>Rory&::MatrixUtils - About</h3>
-<hr/>
-<p>Rory&::MatrixUtils is a "small" collection of tools to do not-so-everyday things.</p>
-<p>These range from joining rooms on dead homeservers, to managing your accounts and rooms, and creating rooms based on templates.</p>
-
-<br/><br/>
-<p>You can find the source code on <a href="https://cgit.rory.gay/matrix/MatrixRoomUtils.git/">cgit.rory.gay</a>.<br/></p>
-<p>You can also join the <a href="https://matrix.to/#/%23mru%3Arory.gay?via=rory.gay&via=matrix.org&via=feline.support">Matrix room</a> for this project.</p>
diff --git a/MatrixRoomUtils.Web/Pages/Dev/DevOptions.razor b/MatrixRoomUtils.Web/Pages/Dev/DevOptions.razor
deleted file mode 100644
index a1e928f..0000000
--- a/MatrixRoomUtils.Web/Pages/Dev/DevOptions.razor
+++ /dev/null
@@ -1,71 +0,0 @@
-@page "/Dev/Options"
-@using ArcaneLibs.Extensions
-@using System.Text.Unicode
-@using System.Text
-@using System.Text.Json
-@inject NavigationManager NavigationManager
-@inject ILocalStorageService LocalStorage
-
-<PageTitle>Developer options</PageTitle>
-
-<h3>Rory&::MatrixUtils - Developer options</h3>
-<hr/>
-
-<p>
- <span>Import local storage: </span>
- <InputFile OnChange="ImportLocalStorage"></InputFile>
-</p>
-<p>
- <span>Export local storage: </span>
- <button @onclick="@ExportLocalStorage">Export</button>
-</p>
-
-@if (userSettings is not null) {
- <InputCheckbox @bind-Value="@userSettings.DeveloperSettings.EnableLogViewers" @oninput="@LogStuff"></InputCheckbox>
- <label> Enable log views</label>
- <br/>
- <InputCheckbox @bind-Value="@userSettings.DeveloperSettings.EnableConsoleLogging" @oninput="@LogStuff"></InputCheckbox>
- <label> Enable console logging</label>
- <br/>
- <InputCheckbox @bind-Value="@userSettings.DeveloperSettings.EnablePortableDevtools" @oninput="@LogStuff"></InputCheckbox>
- <label> Enable portable devtools</label>
- <br/>
-}
-<br/>
-
-@code {
-
- private MRUStorageWrapper.Settings? userSettings { get; set; }
- protected override async Task OnInitializedAsync() {
- // userSettings = await TieredStorage.DataStorageProvider.LoadObjectAsync<MRUStorageWrapper.Settings>("mru.settings");
-
- await base.OnInitializedAsync();
- }
-
- private async Task LogStuff() {
- await Task.Delay(100);
- Console.WriteLine($"Settings: {userSettings.ToJson()}");
- await TieredStorage.DataStorageProvider.SaveObjectAsync("mru.settings", userSettings);
- }
-
- private async Task ExportLocalStorage() {
- var keys = await TieredStorage.DataStorageProvider.GetAllKeysAsync();
- var data = new Dictionary<string, object>();
- foreach (var key in keys) {
- data.Add(key, await TieredStorage.DataStorageProvider.LoadObjectAsync<object>(key));
- }
- var dataUri = "data:application/json;base64,";
- dataUri += Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(data)));
- await JSRuntime.InvokeVoidAsync("window.open", dataUri, "_blank");
- }
-
- private async Task ImportLocalStorage(InputFileChangeEventArgs obj) {
- if (obj.FileCount != 1) return;
- var data = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(obj.File.OpenReadStream());
- foreach (var (key, value) in data) {
- await TieredStorage.DataStorageProvider.SaveObjectAsync(key, value);
- }
- NavigationManager.NavigateTo(NavigationManager.Uri, true, true);
- }
-
-}
diff --git a/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor b/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor
deleted file mode 100644
index 27fe35e..0000000
--- a/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor
+++ /dev/null
@@ -1,81 +0,0 @@
-@page "/Dev/Utilities"
-@using System.Reflection
-@using ArcaneLibs.Extensions
-@using LibMatrix.Extensions
-@using LibMatrix.Homeservers
-@using MatrixRoomUtils.Abstractions
-@inject ILocalStorageService LocalStorage
-@inject NavigationManager NavigationManager
-<h3>Debug Tools</h3>
-<hr/>
-@if (Rooms.Count == 0) {
- <p>You are not in any rooms!</p>
- @* <p>Loading progress: @checkedRoomCount/@totalRoomCount</p> *@
-}
-else {
- <details>
- <summary>Room List</summary>
- @foreach (var room in Rooms) {
- <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.', '~')">
- <RoomListItem RoomInfo="@(new RoomInfo() { Room = hs.GetRoom(room) })" LoadData="true"></RoomListItem>
- </a>
- }
- </details>
-}
-
-<details open>
- <summary>Send GET request to URL</summary>
- <div class="input-group">
- <input type="text" class="form-control" @bind-value="get_request_url" placeholder="URL">
- <button class="btn btn-outline-secondary" type="button" @onclick="SendGetRequest">Send</button>
- </div>
- <br/>
- <pre>@get_request_result</pre>
-</details>
-
-<div style="margin-bottom: 4em;"></div>
-<LogView></LogView>
-
-@code {
- public List<string> Rooms { get; set; } = new();
- public AuthenticatedHomeserverGeneric? hs { get; set; }
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs == null) return;
- Rooms = (await hs.GetJoinedRooms()).Select(x => x.RoomId).ToList();
- Console.WriteLine("Fetched joined rooms!");
- }
-
- //send req
- string get_request_url { get; set; } = "";
- string get_request_result { get; set; } = "";
-
- private async Task SendGetRequest() {
- var field = typeof(RemoteHomeserver).GetRuntimeFields().First(x => x.ToString().Contains("<_httpClient>k__BackingField"));
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs == null) return;
- var httpClient = field.GetValue(hs) as MatrixHttpClient;
- try {
- var res = await httpClient.GetAsync(get_request_url);
- if (res.IsSuccessStatusCode) {
- if (res.Content.Headers.ContentType.MediaType == "application/json")
- get_request_result = (await res.Content.ReadFromJsonAsync<object>()).ToJson();
- else
- get_request_result = await res.Content.ReadAsStringAsync();
- StateHasChanged();
- return;
- }
- if (res.Content.Headers.ContentType.MediaType == "application/json")
- get_request_result = $"Error: {res.StatusCode}\n" + (await res.Content.ReadFromJsonAsync<object>()).ToJson();
- else
- get_request_result = $"Error: {res.StatusCode}\n" + await res.Content.ReadAsStringAsync();
- }
- catch (Exception e) {
- get_request_result = $"Error: {e}";
- }
- StateHasChanged();
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Dev/ModalTest.razor b/MatrixRoomUtils.Web/Pages/Dev/ModalTest.razor
deleted file mode 100644
index 4a0487f..0000000
--- a/MatrixRoomUtils.Web/Pages/Dev/ModalTest.razor
+++ /dev/null
@@ -1,88 +0,0 @@
-@page "/Dev/ModalTest"
-@inject IJSRuntime JsRuntime
-<h3>ModalTest</h3>
-
-@foreach (var (key, value) in _windowInfos) {
- @* <ModalWindow X="@value.X" Y="@value.Y" Title="@value.Title">@value.Content</ModalWindow> *@
-}
-@for (var i = 0; i < 5; i++) {
- var i1 = i;
- <ModalWindow X="@Random.Shared.Next(1400)" Y="@Random.Shared.Next(1000)" Title="@("Window " + i1)" OnCloseClicked="() => OnCloseClicked(i1)">
- @for (var j = 0; j < i1; j++) {
- <h1>@j</h1>
- }
- </ModalWindow>
-}
-
-@code {
-
- private Dictionary<int, WindowInfo> _windowInfos = new();
-
- private class WindowInfo {
- public double X;
- public double Y;
- public string Title;
- public RenderFragment Content;
- }
-
- protected override async Task OnInitializedAsync() {
- double _x = 2;
- double _xv = 20;
- double _y = 0;
- double multiplier = 1;
-
- for (var i = 0; i < 200; i++) {
- var i1 = i;
- _windowInfos.Add(_windowInfos.Count, new WindowInfo {
- X = _x,
- Y = _y,
- Title = "Win" + i1,
- Content = builder => {
- builder.OpenComponent<ModalWindow>(0);
- builder.AddAttribute(1, "X", _x);
- builder.AddAttribute(2, "Y", _y);
- builder.AddAttribute(3, "Title", "Win" + i1);
- builder.AddAttribute(4, "ChildContent", (RenderFragment)(builder2 => {
- builder2.OpenElement(0, "h1");
- builder2.AddContent(1, "Hello " + i1);
- builder2.CloseElement();
- }));
- builder.CloseComponent();
- }
- });
- //_x += _xv /= 1000/System.Math.Sqrt((double)_windowInfos.Count)*_windowInfos.Count.ToString().Length*multiplier;
- _y += 20;
- _x += 20;
- var dimension = await JsRuntime.InvokeAsync<WindowDimension>("getWindowDimensions");
- if (_x > dimension.Width - 100) _x %= dimension.Width - 100;
- if (_y > dimension.Height - 50) {
- _y %= dimension.Height - 50;
- _xv = 20;
- }
- if (
- (_windowInfos.Count < 10 && _windowInfos.Count % 2 == 0) ||
- (_windowInfos.Count < 100 && _windowInfos.Count % 10 == 0) ||
- (_windowInfos.Count < 1000 && _windowInfos.Count % 50 == 0) ||
- (_windowInfos.Count < 10000 && _windowInfos.Count % 100 == 0)
- ) {
- StateHasChanged();
- await Task.Delay(25);
- }
- if(_windowInfos.Count > 750) multiplier = 2;
- if(_windowInfos.Count > 1500) multiplier = 3;
-
- }
-
- await base.OnInitializedAsync();
- }
-
- private void OnCloseClicked(int i1) {
- Console.WriteLine("Close clicked on " + i1);
- }
-
- public class WindowDimension {
- public int Width { get; set; }
- public int Height { get; set; }
- }
-
-}
diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor
deleted file mode 100644
index d1a2df5..0000000
--- a/MatrixRoomUtils.Web/Pages/HSAdmin/HSAdmin.razor
+++ /dev/null
@@ -1,34 +0,0 @@
-@page "/HSAdmin"
-@using LibMatrix.Homeservers
-@using ArcaneLibs.Extensions
-<h3>Homeserver Admininistration</h3>
-<hr/>
-
-@if (Homeserver is null) {
- <p>Homeserver is null...</p>
-}
-else {
- @if (Homeserver is AuthenticatedHomeserverSynapse) {
- <h4>Synapse tools</h4>
- <hr/>
- <a href="/HSAdmin/RoomQuery">Query rooms</a>
- }
- else {
- <p>Homeserver type @Homeserver.GetType().Name does not have any administration tools in MRU.</p>
- <p>Server info:</p>
- <pre>@ServerVersionResponse?.ToJson(ignoreNull: true)</pre>
- }
-}
-
-@code {
- public AuthenticatedHomeserverGeneric? Homeserver { get; set; }
- public ServerVersionResponse? ServerVersionResponse { get; set; }
-
- protected override async Task OnInitializedAsync() {
- Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
- if (Homeserver is null) return;
- ServerVersionResponse = await (Homeserver.FederationClient?.GetServerVersionAsync() ?? Task.FromResult<ServerVersionResponse?>(null));
- await base.OnInitializedAsync();
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor
deleted file mode 100644
index 7e4058b..0000000
--- a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor
+++ /dev/null
@@ -1,200 +0,0 @@
-@page "/HSAdmin/RoomQuery"
-@using LibMatrix.Responses.Admin
-@using LibMatrix.Filters
-@using LibMatrix.Homeservers
-@using ArcaneLibs.Extensions
-
-<h3>Homeserver Administration - Room Query</h3>
-
-<label>Search name: </label>
-<InputText @bind-Value="SearchTerm"/><br/>
-<label>Order by: </label>
-<select @bind="OrderBy">
- @foreach (var item in validOrderBy) {
- <option value="@item.Key">@item.Value</option>
- }
-</select><br/>
-<label>Ascending: </label>
-<InputCheckbox @bind-Value="Ascending"/><br/>
-<details>
- <summary>
- <span>Local filtering (slow)</span>
-
- </summary>
- <div style="margin-left: 8px; margin-bottom: 8px;">
- <u style="display: block;">String contains</u>
- <span class="tile tile280">Room ID: <FancyTextBox @bind-Value="@Filter.RoomIdContains"></FancyTextBox></span>
- <span class="tile tile280">Room name: <FancyTextBox @bind-Value="@Filter.NameContains"></FancyTextBox></span>
- <span class="tile tile280">Canonical alias: <FancyTextBox @bind-Value="@Filter.CanonicalAliasContains"></FancyTextBox></span>
- <span class="tile tile280">Creator: <FancyTextBox @bind-Value="@Filter.CreatorContains"></FancyTextBox></span>
- <span class="tile tile280">Room version: <FancyTextBox @bind-Value="@Filter.VersionContains"></FancyTextBox></span>
- <span class="tile tile280">Encryption algorithm: <FancyTextBox @bind-Value="@Filter.EncryptionContains"></FancyTextBox></span>
- <span class="tile tile280">Join rules: <FancyTextBox @bind-Value="@Filter.JoinRulesContains"></FancyTextBox></span>
- <span class="tile tile280">Guest access: <FancyTextBox @bind-Value="@Filter.GuestAccessContains"></FancyTextBox></span>
- <span class="tile tile280">History visibility: <FancyTextBox @bind-Value="@Filter.HistoryVisibilityContains"></FancyTextBox></span>
-
- <u style="display: block;">Optional checks</u>
- <span class="tile tile150">
- <InputCheckbox @bind-Value="@Filter.CheckFederation"></InputCheckbox> Is federated:
- @if (Filter.CheckFederation) {
- <InputCheckbox @bind-Value="@Filter.Federatable"></InputCheckbox>
- }
- </span>
- <span class="tile tile150">
- <InputCheckbox @bind-Value="@Filter.CheckPublic"></InputCheckbox> Is public:
- @if (Filter.CheckPublic) {
- <InputCheckbox @bind-Value="@Filter.Public"></InputCheckbox>
- }
- </span>
-
- <u style="display: block;">Ranges</u>
- <span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEventsGreaterThan"></InputNumber><span class="range-sep">state events</span><InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEventsLessThan"></InputNumber>
- </span>
- <span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembersGreaterThan"></InputNumber><span class="range-sep">members</span><InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembersLessThan"></InputNumber>
- </span>
- <span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedLocalMembersGreaterThan"></InputNumber><span class="range-sep">local members</span><InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedLocalMembersLessThan"></InputNumber>
- </span>
- </div>
-</details>
-<button class="btn btn-primary" @onclick="Search">Search</button>
-<br/>
-
-@if (Results.Count > 0) {
- <p>Found @Results.Count rooms</p>
- <details>
- <summary>TSV data (copy/paste)</summary>
- <pre style="font-size: 0.6em;">
- <table>
- @foreach (var res in Results) {
- <tr>
- <td style="padding: 8px;">@res.RoomId@("\t")</td>
- <td style="padding: 8px;">@res.CanonicalAlias@("\t")</td>
- <td style="padding: 8px;">@res.Creator@("\t")</td>
- <td style="padding: 8px;">@res.Name</td>
- </tr>
- }
- </table>
- </pre>
- </details>
-}
-
-@foreach (var res in Results) {
- <div style="background-color: #ffffff11; border-radius: 0.5em; display: block; margin-top: 4px; padding: 4px;">
- <RoomListItem RoomName="@res.Name" RoomId="@res.RoomId"></RoomListItem>
- <p>
- @if (!string.IsNullOrWhiteSpace(res.CanonicalAlias)) {
- <span>@res.CanonicalAlias (@res.RoomId)</span>
- <br/>
- }
- else {
- <span>@res.RoomId</span>
- <br/>
- }
- @if (!string.IsNullOrWhiteSpace(res.Creator)) {
- <span>Created by <InlineUserItem UserId="@res.Creator"></InlineUserItem></span>
- <br/>
- }
- </p>
- <span>@res.StateEvents state events</span><br/>
- <span>@res.JoinedMembers members, of which @res.JoinedLocalMembers are on this server</span>
- <details>
- <summary>Full result data</summary>
- <pre>@res.ToJson(ignoreNull: true)</pre>
- </details>
- </div>
-}
-
-<style>
- .int-input {
- width: 128px;
- }
- .tile {
- display: inline-block;
- padding: 4px;
- border: 1px solid #ffffff22;
- }
- .tile280 {
- min-width: 280px;
- }
- .tile150 {
- min-width: 150px;
- }
- .range-sep {
- display: inline-block;
- padding: 4px;
- width: 150px;
- }
- .range-sep::before {
- content: "@("<") ";
- }
- .range-sep::after {
- content: " @("<")";
- }
- .center-children {
- text-align: center;
- }
-</style>
-
-@code {
-
- [Parameter]
- [SupplyParameterFromQuery(Name = "order_by")]
- public string? OrderBy { get; set; }
-
- [Parameter]
- [SupplyParameterFromQuery(Name = "name_search")]
- public string SearchTerm { get; set; }
-
- [Parameter]
- [SupplyParameterFromQuery(Name = "ascending")]
- public bool Ascending { get; set; }
-
- public List<AdminRoomListingResult.AdminRoomListingResultRoom> Results { get; set; } = new();
-
- private string Status { get; set; }
-
- public LocalRoomQueryFilter Filter { get; set; } = new();
-
- protected override Task OnParametersSetAsync() {
- if (Ascending == null)
- Ascending = true;
- OrderBy ??= "name";
- return Task.CompletedTask;
- }
-
- private async Task Search() {
- Results.Clear();
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is AuthenticatedHomeserverSynapse synapse) {
- var searchRooms = synapse.Admin.SearchRoomsAsync(orderBy: OrderBy!, dir: Ascending ? "f" : "b", searchTerm: SearchTerm, localFilter: Filter).GetAsyncEnumerator();
- while (await searchRooms.MoveNextAsync()) {
- var room = searchRooms.Current;
- Console.WriteLine("Hit: " + room.ToJson(false));
- Results.Add(room);
- if (Results.Count % 10 == 0)
- StateHasChanged();
- }
- }
-
- }
-
- private readonly Dictionary<string, string> validOrderBy = new() {
- { "name", "Room name" },
- { "canonical_alias", "Main alias address" },
- { "joined_members", "Number of members (reversed)" },
- { "joined_local_members", "Number of local members (reversed)" },
- { "version", "Room version" },
- { "creator", "Creator of the room" },
- { "encryption", "End-to-end encryption algorithm" },
- { "federatable", "Is room federated" },
- { "public", "Visibility in room list" },
- { "join_rules", "Join rules" },
- { "guest_access", "Guest access" },
- { "history_visibility", "Visibility of history" },
- { "state_events", "Number of state events" }
- };
-
-}
diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor.css b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor.css
deleted file mode 100644
index e69de29..0000000
--- a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor.css
+++ /dev/null
diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor
deleted file mode 100644
index 250dc2d..0000000
--- a/MatrixRoomUtils.Web/Pages/Index.razor
+++ /dev/null
@@ -1,191 +0,0 @@
-@page "/"
-@inject ILogger<Index> logger
-@using LibMatrix.Responses
-@using LibMatrix
-@using LibMatrix.Homeservers
-@using ArcaneLibs.Extensions
-@using MatrixRoomUtils.Web.Pages.Dev
-
-<PageTitle>Index</PageTitle>
-
-<h3>Rory&::MatrixUtils</h3>
-Small collection of tools to do not-so-everyday things.
-
-<br/><br/>
-<h5>Signed in accounts - <a href="/Login">Add new account</a></h5>
-<hr/>
-<form>
- <table>
- @foreach (var session in _sessions.OrderByDescending(x => x.UserInfo.RoomCount)) {
- var _auth = session.UserAuth;
- <tr class="user-entry">
- <td>
- <img class="avatar" src="@session.UserInfo.AvatarUrl"/>
- </td>
- <td class="user-info">
- <p>
- <input type="radio" name="csa" checked="@(_currentSession.AccessToken == _auth.AccessToken)" @onclick="@(() => SwitchSession(_auth))" style="text-decoration-line: unset;"/>
- <b>@session.UserInfo.DisplayName</b> on <b>@_auth.Homeserver</b><br/>
- </p>
- <span style="display: inline-block; width: 128px;">@session.UserInfo.RoomCount rooms</span>
- <a style="color: #888888" href="@("/ServerInfo/" + session.Homeserver.ServerName + "/")">@session.ServerVersion.Server.Name @session.ServerVersion.Server.Version</a>
- @if (_auth.Proxy != null) {
- <span class="badge badge-info"> (proxied via @_auth.Proxy)</span>
- }
- else {
- <p>Not proxied</p>
- }
- @if (_debug) {
- <p>T=@session.Homeserver.GetType().FullName</p>
- <p>D=@session.Homeserver.WhoAmI.DeviceId</p>
- <p>U=@session.Homeserver.WhoAmI.UserId</p>
- }
- </td>
- <td>
- <p>
- <LinkButton OnClick="@(() => ManageUser(_auth))">Manage</LinkButton>
- <LinkButton OnClick="@(() => RemoveUser(_auth))">Remove</LinkButton>
- <LinkButton OnClick="@(() => RemoveUser(_auth, true))">Log out</LinkButton>
- </p>
- </td>
- </tr>
- }
- </table>
-</form>
-
-@if (_offlineSessions.Count > 0) {
- <br/>
- <br/>
- <h5>Sessions on unreachable servers</h5>
- <hr/>
- <form>
- <table>
- @foreach (var session in _offlineSessions) {
- <tr class="user-entry">
- <td>
- <p>
- @{
- string[] parts = session.UserId.Split(':');
- }
- <span>@parts[0][1..]</span> on <span>@parts[1]</span>
- @if (!string.IsNullOrWhiteSpace(session.Proxy)) {
- <span class="badge badge-info"> (proxied via @session.Proxy)</span>
- }
- </p>
- </td>
- <td>
- <LinkButton OnClick="@(() => RemoveUser(session))">Remove</LinkButton>
- </td>
- </tr>
- }
- </table>
- </form>
-}
-
-@code
-{
-#if DEBUG
- private const bool _debug = true;
-#else
- private const bool _debug = false;
-#endif
-
- private class AuthInfo {
- public UserAuth UserAuth { get; set; }
- public UserInfo UserInfo { get; set; }
- public ServerVersionResponse ServerVersion { get; set; }
- public AuthenticatedHomeserverGeneric Homeserver { get; set; }
- }
-
- // private Dictionary<UserAuth, UserInfo> _users = new();
- private readonly List<AuthInfo> _sessions = [];
- private readonly List<UserAuth> _offlineSessions = [];
- private LoginResponse? _currentSession;
-
- protected override async Task OnInitializedAsync() {
- Console.WriteLine("Index.OnInitializedAsync");
- _currentSession = await MRUStorage.GetCurrentToken();
- _sessions.Clear();
- _offlineSessions.Clear();
- var tokens = await MRUStorage.GetAllTokens();
- var profileTasks = tokens.Select(async token => {
- UserInfo userInfo = new();
- AuthenticatedHomeserverGeneric hs;
- Console.WriteLine($"Getting hs for {token.ToJson()}");
- try {
- hs = await hsProvider.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken, token.Proxy);
- }
- catch (MatrixException e) {
- if (e.ErrorCode != "M_UNKNOWN_TOKEN") throw;
- NavigationManager.NavigateTo("/InvalidSession?ctx=" + token.AccessToken);
- return;
-
- }
- catch (Exception e) {
- logger.LogError(e, $"Failed to instantiate AuthenticatedHomeserver for {token.ToJson()}, homeserver may be offline?", token.UserId);
- _offlineSessions.Add(token);
- return;
- }
-
- Console.WriteLine($"Got hs for {token.ToJson()}");
-
- var roomCountTask = hs.GetJoinedRooms();
- var profile = await hs.GetProfileAsync(hs.WhoAmI.UserId);
- userInfo.DisplayName = profile.DisplayName ?? hs.WhoAmI.UserId;
- Console.WriteLine(profile.ToJson());
- _sessions.Add(new() {
- UserInfo = new() {
- AvatarUrl = string.IsNullOrWhiteSpace(profile.AvatarUrl) ? "https://api.dicebear.com/6.x/identicon/svg?seed=" + hs.WhoAmI.UserId : hs.ResolveMediaUri(profile.AvatarUrl),
- RoomCount = (await roomCountTask).Count,
- DisplayName = profile.DisplayName ?? hs.WhoAmI.UserId
- },
- UserAuth = token,
- ServerVersion = await (hs.FederationClient?.GetServerVersionAsync() ?? Task.FromResult<ServerVersionResponse?>(null)),
- Homeserver = hs
- });
- });
- Console.WriteLine("Waiting for profile tasks");
- await Task.WhenAll(profileTasks);
- Console.WriteLine("Done waiting for profile tasks");
- await base.OnInitializedAsync();
- }
-
- private class UserInfo {
- internal string AvatarUrl { get; set; }
- internal string DisplayName { get; set; }
- internal int RoomCount { get; set; }
- }
-
- private async Task RemoveUser(UserAuth auth, bool logout = false) {
- try {
- if (logout) {
- await (await hsProvider.GetAuthenticatedWithToken(auth.Homeserver, auth.AccessToken, auth.Proxy)).Logout();
- }
- }
- catch (Exception e) {
- if (e is MatrixException { ErrorCode: "M_UNKNOWN_TOKEN" }) {
- //todo: handle this
- return;
- }
-
- Console.WriteLine(e);
- }
-
- await MRUStorage.RemoveToken(auth);
- if ((await MRUStorage.GetCurrentToken())?.AccessToken == auth.AccessToken)
- await MRUStorage.SetCurrentToken((await MRUStorage.GetAllTokens() ?? throw new InvalidOperationException()).FirstOrDefault());
- await OnInitializedAsync();
- }
-
-
- private async Task SwitchSession(UserAuth auth) {
- Console.WriteLine($"Switching to {auth.Homeserver} {auth.UserId} via {auth.Proxy}");
- await MRUStorage.SetCurrentToken(auth);
- await OnInitializedAsync();
- }
-
- private async Task ManageUser(UserAuth auth) {
- await SwitchSession(auth);
- NavigationManager.NavigateTo("/User/Profile");
- }
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Index.razor.css b/MatrixRoomUtils.Web/Pages/Index.razor.css
deleted file mode 100644
index c6b7bd7..0000000
--- a/MatrixRoomUtils.Web/Pages/Index.razor.css
+++ /dev/null
@@ -1,25 +0,0 @@
-.user-entry {
- margin-bottom: 1em;
-}
-
-.avatar {
- width: 4em;
- height: 4em;
- border-radius: 50%;
- margin-right: 0.5em;
- vertical-align: middle;
-}
-
-.user-entry > td {
- margin-right: 0.5em;
- vertical-align: middle;
-}
-
-.user-info {
- margin-bottom: 0.5em;
- display: inline-block;
- vertical-align: middle;
-}
-.user-info > p {
- margin: 0;
-}
diff --git a/MatrixRoomUtils.Web/Pages/InvalidSession.razor b/MatrixRoomUtils.Web/Pages/InvalidSession.razor
deleted file mode 100644
index 7d4769c..0000000
--- a/MatrixRoomUtils.Web/Pages/InvalidSession.razor
+++ /dev/null
@@ -1,100 +0,0 @@
-@page "/InvalidSession"
-@using LibMatrix
-
-<PageTitle>Invalid session</PageTitle>
-
-<h3>Rory&::MatrixUtils - Invalid session encountered</h3>
-<p>A session was encountered that is no longer valid. This can happen if you have logged out of the account on another device, or if the access token has expired.</p>
-
-@if (_login is not null) {
- <p>It appears that the affected user is @_login.UserId (@_login.DeviceId) on @_login.Homeserver!</p>
- <LinkButton OnClick="@(OpenRefreshDialog)">Refresh token</LinkButton>
- <LinkButton OnClick="@(RemoveUser)">Remove</LinkButton>
-
- @if (_showRefreshDialog) {
- <ModalWindow MinWidth="300" X="275" Y="300" Title="@($"Password for {_login.UserId}")">
- <FancyTextBox IsPassword="true" @bind-Value="@_password"></FancyTextBox><br/>
- <LinkButton OnClick="TryLogin">Log in</LinkButton>
- @if (_loginException is not null) {
- <pre style="color: red;">@_loginException.RawContent</pre>
- }
- </ModalWindow>
- }
-}
-else {
- <b>Something has gone wrong and the login was not passed along!</b>
-}
-
-@code
-{
- [Parameter]
- [SupplyParameterFromQuery(Name = "ctx")]
- public string Context { get; set; }
-
- private UserAuth? _login { get; set; }
-
- private bool _showRefreshDialog { get; set; }
-
- private string _password { get; set; } = "";
-
- private MatrixException? _loginException { get; set; }
-
- protected override async Task OnInitializedAsync() {
- var tokens = await MRUStorage.GetAllTokens();
- if (tokens is null || tokens.Count == 0) {
- NavigationManager.NavigateTo("/Login");
- return;
- }
-
- _login = tokens.FirstOrDefault(x => x.AccessToken == Context);
-
- if (_login is null) {
- Console.WriteLine($"Could not find {_login} in stored tokens!");
- }
-
- await base.OnInitializedAsync();
- }
-
- private async Task RemoveUser() {
- await MRUStorage.RemoveToken(_login!);
- if ((await MRUStorage.GetCurrentToken())!.AccessToken == _login!.AccessToken)
- await MRUStorage.SetCurrentToken((await MRUStorage.GetAllTokens())?.FirstOrDefault());
- await OnInitializedAsync();
- }
-
- private async Task OpenRefreshDialog() {
- _showRefreshDialog = true;
- StateHasChanged();
- await Task.CompletedTask;
- }
-
- private async Task SwitchSession(UserAuth auth) {
- Console.WriteLine($"Switching to {auth.Homeserver} {auth.AccessToken} {auth.UserId}");
- await MRUStorage.SetCurrentToken(auth);
- await OnInitializedAsync();
- }
-
- private async Task TryLogin() {
- if(_login is null) throw new NullReferenceException("Login is null!");
- try {
- var result = new UserAuth(await hsProvider.Login(_login.Homeserver, _login.UserId, _password));
- if (result is null) {
- Console.WriteLine($"Failed to login to {_login.Homeserver} as {_login.UserId}!");
- return;
- }
- Console.WriteLine($"Obtained access token for {result.UserId}!");
-
- await RemoveUser();
- await MRUStorage.AddToken(result);
- if (result.UserId == (await MRUStorage.GetCurrentToken())?.UserId)
- await MRUStorage.SetCurrentToken(result);
- NavigationManager.NavigateTo("/");
- }
- catch (MatrixException e) {
- Console.WriteLine($"Failed to login to {_login.Homeserver} as {_login.UserId}!");
- Console.WriteLine(e);
- _loginException = e;
- StateHasChanged();
- }
- }
-}
diff --git a/MatrixRoomUtils.Web/Pages/LoginPage.razor b/MatrixRoomUtils.Web/Pages/LoginPage.razor
deleted file mode 100644
index b6f244d..0000000
--- a/MatrixRoomUtils.Web/Pages/LoginPage.razor
+++ /dev/null
@@ -1,160 +0,0 @@
-@page "/Login"
-@using System.Text.Json
-@using System.Text.Json.Serialization
-@using LibMatrix
-@inject ILocalStorageService LocalStorage
-@inject IJSRuntime JsRuntime
-<h3>Login</h3>
-<hr/>
-
-<span style="display: block;">
- <label>User ID:</label>
- <span>@@</span><!--
- --><FancyTextBox @bind-Value="@newRecordInput.Username"></FancyTextBox><!--
- --><span>:</span><!--
- --><FancyTextBox @bind-Value="@newRecordInput.Homeserver"></FancyTextBox>
-</span>
-<span style="display: block;">
- <label>Password:</label>
- <FancyTextBox @bind-Value="@newRecordInput.Password" IsPassword="true"></FancyTextBox>
-</span>
-<span style="display: block">
- <label>Proxy (<a href="https://cgit.rory.gay/matrix/MxApiExtensions.git">MxApiExtensions</a> or similar):</label>
- <FancyTextBox @bind-Value="@newRecordInput.Proxy"></FancyTextBox>
-</span>
-<br/>
-<LinkButton OnClick="@AddRecord">Add account to queue</LinkButton>
-<LinkButton OnClick="@(() => Login(newRecordInput))">Log in</LinkButton>
-<br/>
-<br/>
-
-<h4>Import from TSV</h4>
-<hr/>
-<span>Import credentials from a TSV (Tab Separated Values) file</span><br/>
-<span>Columns: username, homeserver, password, proxy</span><br/>
-<span>Keep in mind there is no column header!</span><br/>
-<br/>
-<InputFile OnChange="@FileChanged" accept=".tsv"></InputFile>
-<br/>
-<br/>
-
-<table border="1">
- <thead style="border-bottom: 1px solid white;">
- <th style="min-width: 150px; text-align: center; border-right: 1px solid white;">Username</th>
- <th style="min-width: 150px; text-align: center; border-right: 1px solid white;">Homeserver</th>
- <th style="min-width: 150px; text-align: center; border-right: 1px solid white;">Password</th>
- <th style="min-width: 150px; text-align: center; border-right: 1px solid white;">Proxy</th>
- <th style="min-width: 150px; text-align: center;">Actions</th>
- </thead>
- @foreach (var record in records) {
- var r = record;
- <tr style="background-color: @(LoggedInSessions.Any(x => x.UserId == $"@{r.Username}:{r.Homeserver}" && x.Proxy == r.Proxy) ? "green" : "unset")">
- <td style="border-width: 1px;">
- <FancyTextBox @bind-Value="@r.Username"></FancyTextBox>
- </td>
- <td style="border-width: 1px;">
- <FancyTextBox @bind-Value="@r.Homeserver"></FancyTextBox>
- </td>
- <td style="border-width: 1px;">
- <FancyTextBox @bind-Value="@r.Password" IsPassword="true"></FancyTextBox>
- </td>
- <td style="border-width: 1px;">
- <FancyTextBox @bind-Value="@r.Proxy"></FancyTextBox>
- </td>
- <td style="border-width: 1px;">
- <a role="button" @onclick="() => records.Remove(r)">Remove</a>
- </td>
- </tr>
- @if (r.Exception is MatrixException me) {
- <tr>
- <td style="border-width: 1px;">Exception:</td>
- <td style="border-width: 1px;">@me.ErrorCode</td>
- <td style="border-width: 1px;" colspan="3">@me.Error</td>
- </tr>
- }
- else if (r.Exception is { } e) {
- <tr>
- <td style="border-width: 1px;">Exception:</td>
- <td style="border-width: 1px;" colspan="4">@e.Message</td>
- </tr>
- }
- }
-</table>
-<br/>
-<LinkButton OnClick="@LoginAll">Log in</LinkButton>
-
-
-@code {
- readonly List<LoginStruct> records = new();
- private LoginStruct newRecordInput = new();
-
- List<UserAuth>? LoggedInSessions { get; set; } = new();
-
- async Task LoginAll() {
- var loginTasks = records.Select(Login);
- await Task.WhenAll(loginTasks);
- }
-
- async Task Login(LoginStruct record) {
- if (!records.Contains(record))
- records.Add(record);
- if (LoggedInSessions.Any(x => x.UserId == $"@{record.Username}:{record.Homeserver}" && x.Proxy == record.Proxy)) return;
- StateHasChanged();
- try {
- var result = new UserAuth(await hsProvider.Login(record.Homeserver, record.Username, record.Password, record.Proxy)) {
- Proxy = record.Proxy
- };
- if (result == null) {
- Console.WriteLine($"Failed to login to {record.Homeserver} as {record.Username}!");
- return;
- }
-
- Console.WriteLine($"Obtained access token for {result.UserId}!");
-
- await MRUStorage.AddToken(result);
- LoggedInSessions = await MRUStorage.GetAllTokens();
- }
- catch (Exception e) {
- Console.WriteLine($"Failed to login to {record.Homeserver} as {record.Username}!");
- Console.WriteLine(e);
- record.Exception = e;
- }
-
- StateHasChanged();
- }
-
- private async Task FileChanged(InputFileChangeEventArgs obj) {
- LoggedInSessions = await MRUStorage.GetAllTokens();
- Console.WriteLine(JsonSerializer.Serialize(obj, new JsonSerializerOptions {
- WriteIndented = true
- }));
- await using var rs = obj.File.OpenReadStream();
- using var sr = new StreamReader(rs);
- var tsvData = await sr.ReadToEndAsync();
- records.Clear();
- foreach (var line in tsvData.Split('\n')) {
- string?[] parts = line.Split('\t');
- if (parts.Length < 3)
- continue;
- string? via = parts.Length > 3 ? parts[3] : null;
- records.Add(new() { Homeserver = parts[0], Username = parts[1], Password = parts[2], Proxy = via });
- }
- }
-
- private async Task AddRecord() {
- LoggedInSessions = await MRUStorage.GetAllTokens();
- records.Add(newRecordInput);
- newRecordInput = new();
- }
-
- private class LoginStruct {
- public string? Homeserver { get; set; } = "";
- public string? Username { get; set; } = "";
- public string? Password { get; set; } = "";
- public string? Proxy { get; set; }
-
- [JsonIgnore]
- internal Exception? Exception { get; set; }
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/ModerationUtilities/UserRoomHistory.razor b/MatrixRoomUtils.Web/Pages/ModerationUtilities/UserRoomHistory.razor
deleted file mode 100644
index 506a8a1..0000000
--- a/MatrixRoomUtils.Web/Pages/ModerationUtilities/UserRoomHistory.razor
+++ /dev/null
@@ -1,115 +0,0 @@
-@page "/UserRoomHistory/{UserId}"
-@using LibMatrix.Homeservers
-@using LibMatrix
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.RoomTypes
-@using ArcaneLibs.Extensions
-@using MatrixRoomUtils.Abstractions
-<h3>UserRoomHistory</h3>
-
-<span>Enter mxid: </span>
-<FancyTextBox @bind-Value="@UserId"></FancyTextBox>
-
-@if (string.IsNullOrWhiteSpace(UserId)) {
- <p>UserId is null!</p>
-}
-else {
- <p>Checked @checkedRooms.Count so far...</p>
- @if (currentHs is not null) {
- <p>Checking rooms from @currentHs.UserId's perspective</p>
- }
- else if (checkedRooms.Count > 1) {
- <p>Done!</p>
- }
- @foreach (var (state, rooms) in matchingStates) {
- <u>@state</u>
- <br/>
- @foreach (var roomInfo in rooms) {
- <RoomListItem RoomInfo="roomInfo" LoadData="true"></RoomListItem>
- }
- }
-}
-
-@code {
- private string? _userId;
-
- [Parameter]
- public string? UserId {
- get => _userId;
- set {
- _userId = value;
- FindMember(value);
- }
- }
-
- private List<AuthenticatedHomeserverGeneric> hss = new();
- private AuthenticatedHomeserverGeneric? currentHs { get; set; }
-
- protected override async Task OnInitializedAsync() {
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- var sessions = await MRUStorage.GetAllTokens();
- foreach (var userAuth in sessions) {
- var session = await MRUStorage.GetSession(userAuth);
- if (session is not null) {
- hss.Add(session);
- StateHasChanged();
- }
- }
-
- StateHasChanged();
- Console.WriteLine("Rerendered!");
- await base.OnInitializedAsync();
- if (!string.IsNullOrWhiteSpace(UserId)) FindMember(UserId);
- }
-
- public Dictionary<string, List<RoomInfo>> matchingStates = new();
- public List<string> checkedRooms = new();
- private SemaphoreSlim _semaphoreSlim = new(1, 1);
-
- public async Task FindMember(string mxid) {
- await _semaphoreSlim.WaitAsync();
- if (mxid != UserId) {
- _semaphoreSlim.Release();
- return; //abort if changed
- }
- matchingStates.Clear();
- foreach (var homeserver in hss) {
- currentHs = homeserver;
- var rooms = await homeserver.GetJoinedRooms();
- rooms.RemoveAll(x => checkedRooms.Contains(x.RoomId));
- checkedRooms.AddRange(rooms.Select(x => x.RoomId));
- var tasks = rooms.Select(x => GetMembershipAsync(x, mxid)).ToAsyncEnumerable();
- await foreach (var (room, state) in tasks) {
- if (state is null) continue;
- if (!matchingStates.ContainsKey(state.Membership))
- matchingStates.Add(state.Membership, new());
- var roomInfo = new RoomInfo() {
- Room = room
- };
- matchingStates[state.Membership].Add(roomInfo);
- roomInfo.StateEvents.Add(new() {
- Type = RoomNameEventContent.EventId,
- TypedContent = new RoomNameEventContent() {
- Name = await room.GetNameOrFallbackAsync(4)
- },
- RoomId = null, Sender = null, EventId = null //TODO implement
- });
- StateHasChanged();
- if (mxid != UserId) {
- _semaphoreSlim.Release();
- return; //abort if changed
- }
- }
- StateHasChanged();
- }
- currentHs = null;
- StateHasChanged();
- _semaphoreSlim.Release();
- }
-
- public async Task<(GenericRoom roomId, RoomMemberEventContent? content)> GetMembershipAsync(GenericRoom room, string mxid) {
- return (room, await room.GetStateOrNullAsync<RoomMemberEventContent>(RoomMemberEventContent.EventId, mxid));
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Create.razor b/MatrixRoomUtils.Web/Pages/Rooms/Create.razor
deleted file mode 100644
index e477f4c..0000000
--- a/MatrixRoomUtils.Web/Pages/Rooms/Create.razor
+++ /dev/null
@@ -1,338 +0,0 @@
-@page "/Rooms/Create"
-@using System.Text.Json
-@using System.Reflection
-@using ArcaneLibs.Extensions
-@using LibMatrix
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.EventTypes.Spec.State.RoomInfo
-@using LibMatrix.Homeservers
-@using LibMatrix.Responses
-@using MatrixRoomUtils.Web.Classes.RoomCreationTemplates
-@* @* ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not */ *@
-
-<h3>Room Manager - Create Room</h3>
-
-@* <pre Contenteditable="true" @onkeypress="@JsonChanged" content="JsonString">@JsonString</pre> *@
-<style>
- table.table-top-first-tr tr td:first-child {
- vertical-align: top;
- }
- </style>
-<table class="table-top-first-tr">
- <tr style="padding-bottom: 16px;">
- <td>Preset:</td>
- <td>
- @if (Presets is null) {
- <p style="color: red;">Presets is null!</p>
- }
- else {
- <InputSelect @bind-Value="@RoomPreset">
- @foreach (var createRoomRequest in Presets) {
- <option value="@createRoomRequest.Key">@createRoomRequest.Key</option>
- }
- </InputSelect>
- }
- </td>
- </tr>
- @if (creationEvent is not null) {
- <tr>
- <td>Room name:</td>
- <td>
- @if (creationEvent.Name is null) {
- <p style="color: red;">creationEvent.Name is null!</p>
- }
- else {
- <FancyTextBox @bind-Value="@creationEvent.Name"></FancyTextBox>
- <p>(#<FancyTextBox @bind-Value="@creationEvent.RoomAliasName"></FancyTextBox>:@Homeserver.WhoAmI.UserId.Split(':').Last())</p>
- }
- </td>
- </tr>
- <tr>
- <td>Room type:</td>
- <td>
- @if (creationEvent.CreationContentBaseType is null) {
- <p style="color: red;">creationEvent._creationContentBaseType is null!</p>
- }
- else {
- <InputSelect @bind-Value="@creationEvent.CreationContentBaseType.Type">
- <option value="">Room</option>
- <option value="m.space">Space</option>
- </InputSelect>
- <FancyTextBox @bind-Value="@creationEvent.CreationContentBaseType.Type"></FancyTextBox>
- }
- </td>
- </tr>
- <tr>
- <td style="padding-top: 16px;">History visibility:</td>
- <td style="padding-top: 16px;">
- <InputSelect @bind-Value="@historyVisibility.HistoryVisibility">
- <option value="invited">Invited</option>
- <option value="joined">Joined</option>
- <option value="shared">Shared</option>
- <option value="world_readable">World readable</option>
- </InputSelect>
- </td>
- </tr>
- <tr>
- <td>Guest access:</td>
- <td>
- <ToggleSlider @bind-Value="guestAccessEvent.IsGuestAccessEnabled">
- @(guestAccessEvent.IsGuestAccessEnabled ? "Guests can join" : "Guests cannot join") (@guestAccessEvent.GuestAccess)
- </ToggleSlider>
- <InputSelect @bind-Value="@guestAccessEvent.GuestAccess">
- <option value="can_join">Can join</option>
- <option value="forbidden">Forbidden</option>
- </InputSelect>
- </td>
- </tr>
-
- <tr>
- <td>Room icon:</td>
- <td>
- <img src="@Homeserver.ResolveMediaUri(roomAvatarEvent.Url)" style="width: 128px; height: 128px; border-radius: 50%;"/>
- <div style="display: inline-block; vertical-align: middle;">
- <FancyTextBox @bind-Value="@roomAvatarEvent.Url"></FancyTextBox><br/>
- <InputFile OnChange="RoomIconFilePicked"></InputFile>
- </div>
- </td>
- </tr>
- <tr>
- <td>Permissions:</td>
- <details>
- <summary>@creationEvent.PowerLevelContentOverride.Users.Count members</summary>
- @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) {
- var _event = user;
- <tr>
- <td>
- <FancyTextBox Formatter="@GetPermissionFriendlyName"
- Value="@_event"
- ValueChanged="val => { creationEvent.PowerLevelContentOverride.Events.ChangeKey(_event, val); }">
- </FancyTextBox>:
- </td>
- <td>
- <input type="number" value="@creationEvent.PowerLevelContentOverride.Events[_event]" @oninput="val => { creationEvent.PowerLevelContentOverride.Events[_event] = int.Parse(val.Value.ToString()); }" @onfocusout="() => { creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"/>
- </td>
- </tr>
- }
- @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) {
- var _user = user;
- <tr>
- <td><FancyTextBox Value="@_user" ValueChanged="val => { creationEvent.PowerLevelContentOverride.Users.ChangeKey(_user, val); creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"></FancyTextBox>:</td>
- <td>
- <input type="number" value="@creationEvent.PowerLevelContentOverride.Users[_user]" @oninput="val => { creationEvent.PowerLevelContentOverride.Users[_user] = int.Parse(val.Value.ToString()); }"/>
- </td>
- </tr>
- }
- </details>
- </tr>
- <tr>
- <td>Server ACLs:</td>
- <td>
- @if (serverAcl?.Allow is null) {
- <p>No allow rules exist!</p>
- <button @onclick="@(() => { serverAcl.Allow = new List<string> { "*" }; })">Create sane defaults</button>
- }
- else {
- <details>
- <summary>@((creationEvent["m.room.server_acls"].TypedContent as RoomServerACLEventContent).Allow.Count) allow rules</summary>
- @* <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> *@
- </details>
- }
- @if (serverAcl?.Deny is null) {
- <p>No deny rules exist!</p>
- <button @onclick="@(() => { serverAcl.Allow = new List<string>(); })">Create sane defaults</button>
- }
- else {
- <details>
- <summary>@((creationEvent["m.room.server_acls"].TypedContent as RoomServerACLEventContent).Deny.Count) deny rules</summary>
- @* <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> *@
- </details>
- }
- </td>
- </tr>
-
- <tr>
- <td>Invited members:</td>
- <td>
- <details>
- <summary>@creationEvent.InitialState.Count(x => x.Type == "m.room.member") members</summary>
- @* <button @onclick="() => { RuntimeCache.LoginSessions.Select(x => x.Value.LoginResponse.UserId).ToList().ForEach(InviteMember); }">Invite all logged in accounts</button> *@
- @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != Homeserver.UserId)) {
- <UserListItem UserId="@member.StateKey"></UserListItem>
- }
- </details>
- </td>
- </tr>
- @* Initial states, should remain at bottom *@
- <tr>
- <td style="vertical-align: top;">Initial states:</td>
- <td>
- <details>
-
- @code
- {
- private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" };
- }
-
- <summary> @creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states</summary>
- <table>
- @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) {
- <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>
- <details>
- <summary> @creationEvent.InitialState.Count initial states</summary>
- <table>
- @foreach (var initialState in creationEvent.InitialState) {
- var _state = initialState;
- <tr>
- <td style="vertical-align: top;">
- <span>@(_state.Type):</span><br/>
- <button @onclick="() => { creationEvent.InitialState.Remove(_state); StateHasChanged(); }">Remove</button>
- </td>
-
- <td>
- <pre>@JsonSerializer.Serialize(_state.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre>
- </td>
- </tr>
- }
- </table>
- </details>
- </td>
- </tr>
- }
-</table>
-<button @onclick="CreateRoom">Create room</button>
-<br/>
-<ModalWindow Title="Creation JSON">
- <pre>
- @creationEvent.ToJson(ignoreNull: true)
- </pre>
-</ModalWindow>
-<ModalWindow Title="Creation JSON (with null values)">
- <pre>
- @creationEvent.ToJson()
- </pre>
-</ModalWindow>
-
-@if (_matrixException is not null) {
- <ModalWindow Title="@("Matrix exception: " + _matrixException.ErrorCode)">
- <pre>
- @_matrixException.Message
- </pre>
- </ModalWindow>
-}
-
-@code {
-
- private string RoomPreset {
- get => Presets.ContainsValue(creationEvent) ? Presets.First(x => x.Value == creationEvent).Key : "Not a preset";
- set {
- creationEvent = Presets[value];
- JsonChanged();
- StateHasChanged();
- }
- }
-
- private CreateRoomRequest? creationEvent { get; set; }
-
- private Dictionary<string, CreateRoomRequest>? Presets { get; set; } = new();
- private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
-
- private MatrixException? _matrixException { get; set; }
-
- private RoomHistoryVisibilityEventContent? historyVisibility => creationEvent?["m.room.history_visibility"].TypedContent as RoomHistoryVisibilityEventContent;
- private RoomGuestAccessEventContent? guestAccessEvent => creationEvent?["m.room.guest_access"].TypedContent as RoomGuestAccessEventContent;
- private RoomServerACLEventContent? serverAcl => creationEvent?["m.room.server_acls"].TypedContent as RoomServerACLEventContent;
- private RoomAvatarEventContent? roomAvatarEvent => creationEvent?["m.room.avatar"].TypedContent as RoomAvatarEventContent;
-
- protected override async Task OnInitializedAsync() {
- Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
- if (Homeserver is null) return;
-
- 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";
-
- await base.OnInitializedAsync();
- }
-
- private void JsonChanged() => Console.WriteLine(creationEvent.ToJson());
-
- private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) {
- var res = await Homeserver.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType);
- Console.WriteLine(res);
- (creationEvent["m.room.avatar"].TypedContent as RoomAvatarEventContent).Url = res;
- StateHasChanged();
- }
-
- private async Task CreateRoom() {
- Console.WriteLine("Create room");
- Console.WriteLine(creationEvent.ToJson());
- creationEvent.CreationContent.Add("rory.gay.created_using", "Rory&::MatrixRoomUtils (https://mru.rory.gay)");
- try {
- var id = await Homeserver.CreateRoom(creationEvent);
- }
- catch (MatrixException e) {
- _matrixException = e;
- }
- }
-
- private void InviteMember(string mxid) {
- if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && Homeserver.UserId != mxid)
- creationEvent.InitialState.Add(new StateEvent {
- Type = "m.room.member",
- StateKey = mxid,
- TypedContent = new RoomMemberEventContent {
- Membership = "invite",
- Reason = "Automatically invited at room creation time."
- }
- });
- }
-
- private string GetStateFriendlyName(string key) => key switch {
- "m.room.history_visibility" => "History visibility",
- "m.room.guest_access" => "Guest access",
- "m.room.join_rules" => "Join rules",
- "m.room.server_acl" => "Server ACL",
- "m.room.avatar" => "Avatar",
- _ => key
- };
-
- 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",
- _ => key
- };
-
-}
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
deleted file mode 100644
index 6cabe82..0000000
--- a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
+++ /dev/null
@@ -1,250 +0,0 @@
-@page "/Rooms"
-@using LibMatrix.Filters
-@using LibMatrix.Helpers
-@using LibMatrix.Extensions
-@using LibMatrix.Responses
-@using System.Collections.ObjectModel
-@using System.Diagnostics
-@using ArcaneLibs.Extensions
-@using MatrixRoomUtils.Abstractions
-@inject ILogger<Index> logger
-<h3>Room list</h3>
-
-<p>@Status</p>
-<p>@Status2</p>
-
-<LinkButton href="/Rooms/Create">Create new room</LinkButton>
-
-<RoomList Rooms="Rooms" GlobalProfile="@GlobalProfile" @bind-StillFetching="RenderContents"></RoomList>
-
-@code {
- private ObservableCollection<RoomInfo> Rooms { get; } = new();
- private UserProfileResponse GlobalProfile { get; set; }
-
- private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
-
- private static SyncFilter filter = new() {
- AccountData = new SyncFilter.EventFilter {
- NotTypes = new List<string> { "*" },
- Limit = 1
- },
- Presence = new SyncFilter.EventFilter {
- NotTypes = new List<string> { "*" },
- Limit = 1
- },
- Room = new SyncFilter.RoomFilter {
- AccountData = new SyncFilter.RoomFilter.StateFilter {
- NotTypes = new List<string> { "*" },
- Limit = 1
- },
- Ephemeral = new SyncFilter.RoomFilter.StateFilter {
- NotTypes = new List<string> { "*" },
- Limit = 1
- },
- State = new SyncFilter.RoomFilter.StateFilter {
- Types = new List<string> {
- "m.room.create",
- "m.room.name",
- "m.room.avatar",
- "org.matrix.mjolnir.shortcode",
- "m.room.power_levels",
- }
- },
- Timeline = new SyncFilter.RoomFilter.StateFilter {
- NotTypes = new List<string> { "*" },
- Limit = 1
- }
- }
- };
-
- // private static SyncFilter profileUpdateFilter = new() {
- // AccountData = new SyncFilter.EventFilter {
- // NotTypes = new List<string> { "*" },
- // Limit = 1
- // },
- // Presence = new SyncFilter.EventFilter {
- // NotTypes = new List<string> { "*" },
- // Limit = 1
- // },
- // Room = new SyncFilter.RoomFilter {
- // AccountData = new SyncFilter.RoomFilter.StateFilter {
- // NotTypes = new List<string> { "*" },
- // Limit = 1
- // },
- // Ephemeral = new SyncFilter.RoomFilter.StateFilter {
- // NotTypes = new List<string> { "*" },
- // Limit = 1
- // },
- // State = new SyncFilter.RoomFilter.StateFilter {
- // Types = new List<string> {
- // "m.room.member"
- // },
- // Senders = new()
- // },
- // Timeline = new SyncFilter.RoomFilter.StateFilter {
- // NotTypes = new List<string> { "*" },
- // Limit = 1
- // }
- // }
- // };
-
- private SyncHelper syncHelper;
-
- // SyncHelper profileSyncHelper;
-
- protected override async Task OnInitializedAsync() {
- Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
- if (Homeserver is null) return;
- var rooms = await Homeserver.GetJoinedRooms();
- // SemaphoreSlim _semaphore = new(160, 160);
-
- var roomTasks = rooms.Select(async room => {
- RoomInfo ri;
- // await _semaphore.WaitAsync();
- ri = new() { Room = room };
- await Task.WhenAll((filter.Room?.State?.Types ?? []).Select(x => ri.GetStateEvent(x)));
- return ri;
- }).ToAsyncEnumerable();
-
- await foreach (var room in roomTasks) {
- Rooms.Add(room);
- StateHasChanged();
- // await Task.Delay(50);
- // _semaphore.Release();
- }
-
- if (rooms.Count >= 150) RenderContents = true;
-
- GlobalProfile = await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId);
- syncHelper = new SyncHelper(Homeserver, logger) {
- Timeout = 30000,
- Filter = filter,
- MinimumDelay = TimeSpan.FromMilliseconds(5000)
- };
- // profileSyncHelper = new SyncHelper(Homeserver, logger) {
- // Timeout = 10000,
- // Filter = profileUpdateFilter,
- // MinimumDelay = TimeSpan.FromMilliseconds(5000)
- // };
- // profileUpdateFilter.Room.State.Senders.Add(Homeserver.WhoAmI.UserId);
-
- RunSyncLoop(syncHelper);
- // RunSyncLoop(profileSyncHelper);
- RunQueueProcessor();
-
- await base.OnInitializedAsync();
- }
-
- private async Task RunQueueProcessor() {
- var renderTimeSw = Stopwatch.StartNew();
- var isInitialSync = true;
- while (true) {
- try {
- while (queue.Count == 0) {
- Console.WriteLine("Queue is empty, waiting...");
- await Task.Delay(isInitialSync ? 100 : 2500);
- }
-
- Console.WriteLine($"Queue no longer empty after {renderTimeSw.Elapsed}!");
-
- int maxUpdates = 10;
- isInitialSync = false;
- while (maxUpdates-- > 0 && queue.TryDequeue(out var queueEntry)) {
- var (roomId, roomData) = queueEntry;
- Console.WriteLine($"Dequeued room {roomId}");
- RoomInfo room;
-
- if (Rooms.Any(x => x.Room.RoomId == roomId)) {
- room = Rooms.First(x => x.Room.RoomId == roomId);
- Console.WriteLine($"QueueWorker: {roomId} already known with {room.StateEvents?.Count ?? 0} state events");
- }
- else {
- Console.WriteLine($"QueueWorker: encountered new room {roomId}!");
- room = new RoomInfo() {
- Room = Homeserver.GetRoom(roomId)
- };
- Rooms.Add(room);
- }
-
- if (room.StateEvents is null) {
- Console.WriteLine($"QueueWorker: {roomId} does not have state events on record?");
- throw new InvalidDataException("Somehow this is null???");
- }
-
- if (roomData.State?.Events is { Count: > 0 })
- room.StateEvents.MergeStateEventLists(roomData.State.Events);
- else {
- Console.WriteLine($"QueueWorker: could not merge state for {room.Room.RoomId} as new data contains no state events!");
- }
- }
- Console.WriteLine($"QueueWorker: {queue.Count} entries left in queue, {maxUpdates} maxUpdates left, RenderContents: {RenderContents}");
- Status = $"Got {Rooms.Count} rooms so far! {queue.Count} entries in processing queue...";
-
- RenderContents |= queue.Count == 0;
- await Task.Delay(Rooms.Count);
- }
- catch (Exception e) {
- Console.WriteLine("QueueWorker exception: " + e);
- }
- }
- }
-
- private bool RenderContents { get; set; } = false;
-
- private string _status;
-
- public string Status {
- get => _status;
- set {
- _status = value;
- StateHasChanged();
- }
- }
-
- private string _status2;
-
- public string Status2 {
- get => _status2;
- set {
- _status2 = value;
- StateHasChanged();
- }
- }
-
- private Queue<KeyValuePair<string, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure>> queue = new();
-
- private async Task RunSyncLoop(SyncHelper syncHelper) {
- Status = "Initial syncing...";
- Console.WriteLine("starting sync");
-
- var syncs = syncHelper.EnumerateSyncAsync();
- await foreach (var sync in syncs) {
- Console.WriteLine("trying sync");
- if (sync is null) continue;
-
- Status = $"Got sync with {sync.Rooms?.Join?.Count ?? 0} room updates, next batch: {sync.NextBatch}!";
- if (sync?.Rooms?.Join != null)
- foreach (var joinedRoom in sync.Rooms.Join)
- if ( /*joinedRoom.Value.AccountData?.Events?.Count > 0 ||*/ joinedRoom.Value.State?.Events?.Count > 0) {
- joinedRoom.Value.State.Events.RemoveAll(x => x.Type == "m.room.member" && x.StateKey != Homeserver.WhoAmI?.UserId);
- // We can't trust servers to give us what we ask for, and this ruins performance
- // Thanks, Conduit.
- joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.Types?.Contains(x.Type) == false);
- if (filter.Room?.State?.NotSenders?.Any() ?? false)
- joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.NotSenders?.Contains(x.Sender) ?? false);
-
- queue.Enqueue(joinedRoom);
- }
- if (sync.Rooms.Leave is {Count: > 0})
- foreach (var leftRoom in sync.Rooms.Leave)
- if (Rooms.Any(x => x.Room.RoomId == leftRoom.Key))
- Rooms.Remove(Rooms.First(x => x.Room.RoomId == leftRoom.Key));
-
- Status = $"Got {Rooms.Count} rooms so far! {queue.Count} entries in processing queue... " +
- $"{sync?.Rooms?.Join?.Count ?? 0} new updates!";
-
- Status2 = $"Next batch: {sync.NextBatch}";
- }
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
deleted file mode 100644
index b89d59c..0000000
--- a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
+++ /dev/null
@@ -1,267 +0,0 @@
-@page "/Rooms/{RoomId}/Policies"
-@using LibMatrix
-@using ArcaneLibs.Extensions
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.EventTypes.Spec.State.Policy
-@using System.Diagnostics
-@using LibMatrix.RoomTypes
-@using System.Collections.Frozen
-@using System.Reflection
-@using ArcaneLibs.Attributes
-@using LibMatrix.EventTypes
-
-@using MatrixRoomUtils.Web.Shared.PolicyEditorComponents
-
-<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>
-
-@if (Loading) {
- <p>Loading...</p>
-}
-else if (PolicyEventsByType is not { Count: > 0 }) {
- <p>No policies yet</p>
-}
-else {
- @foreach (var (type, value) in PolicyEventsByType) {
- <p>
- @(GetValidPolicyEventsByType(type).Count) active,
- @(GetInvalidPolicyEventsByType(type).Count) invalid
- (@value.Count total)
- @(GetPolicyTypeName(type).ToLower())
- </p>
- }
-
- @foreach (var type in KnownPolicyTypes.OrderByDescending(t => GetPolicyEventsByType(t).Count)) {
- <details>
- <summary>
- <span>
- @($"{GetPolicyTypeName(type)}: {GetPolicyEventsByType(type).Count} policies")
- </span>
- <hr style="margin: revert;"/>
- </summary>
- <table class="table table-striped table-hover" style="width: fit-content; border-width: 1px; vertical-align: middle;">
- @{
- 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();
- }
- <thead>
- <tr>
- @foreach (var name in propNames) {
- <th style="border-width: 1px">@name</th>
- }
- <th style="border-width: 1px">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!;
- var proxySafeProps = typedContent.GetType().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()}");
- }
- @foreach (var prop in proxySafeProps ?? Enumerable.Empty<PropertyInfo>()) {
- <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>
- }
- }
- </div>
- </td>
- </tr>
- }
- </tbody>
- </table>
- <details>
- <summary>
- <u>
- @("Invalid " + GetPolicyTypeName(type).ToLower())
- </u>
- </summary>
- <table class="table table-striped table-hover" style="width: fit-content; border-width: 1px; vertical-align: middle;">
- <thead>
- <tr>
- <th style="border-width: 1px">State key</th>
- <th style="border-width: 1px">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>
- }
-}
-
-@if (CurrentlyEditingEvent is not null) {
- <PolicyEditorModal PolicyEvent="@CurrentlyEditingEvent" OnClose="@(() => CurrentlyEditingEvent = null)" OnSave="@(e => UpdatePolicyAsync(e))"></PolicyEditorModal>
-}
-
-@code {
-
-#if DEBUG
- private const bool Debug = true;
-#else
- private const bool Debug = false;
-#endif
-
- private bool Loading { get; set; } = true;
- //get room list
- // - sync withroom list filter
- // Type = support.feline.msc3784
- //support.feline.policy.lists.msc.v1
-
- [Parameter]
- public string RoomId { get; set; } = null!;
-
- private bool _enableAvatars;
- private StateEventResponse? _currentlyEditingEvent;
-
- // static readonly Dictionary<string, string?> Avatars = new();
- // static readonly Dictionary<string, RemoteHomeserver> Servers = new();
-
- // private static List<StateEventResponse> PolicyEvents { get; set; } = new();
- private Dictionary<Type, List<StateEventResponse>> PolicyEventsByType { get; set; } = new();
-
- private StateEventResponse? CurrentlyEditingEvent {
- get => _currentlyEditingEvent;
- set {
- _currentlyEditingEvent = value;
- StateHasChanged();
- }
- }
-
- // public bool EnableAvatars {
- // get => _enableAvatars;
- // set {
- // _enableAvatars = value;
- // if (value) GetAllAvatars();
- // }
- // }
-
- private AuthenticatedHomeserverGeneric Homeserver { get; set; }
- private GenericRoom Room { get; set; }
- private RoomPowerLevelEventContent PowerLevels { get; set; }
-
- protected override async Task OnInitializedAsync() {
- var sw = Stopwatch.StartNew();
- await base.OnInitializedAsync();
- Homeserver = (await MRUStorage.GetCurrentSessionOrNavigate())!;
- if (Homeserver is null) return;
- Room = Homeserver.GetRoom(RoomId!);
- PowerLevels = (await Room.GetPowerLevelsAsync())!;
- await LoadStatesAsync();
- Console.WriteLine($"Policy list editor initialized in {sw.Elapsed}!");
- }
-
- private async Task LoadStatesAsync() {
- Loading = true;
- var states = Room.GetFullStateAsync();
- PolicyEventsByType.Clear();
- await foreach (var state in states) {
- if (state is null) continue;
- if (!state.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue;
- if (!PolicyEventsByType.ContainsKey(state.MappedType)) PolicyEventsByType.Add(state.MappedType, new());
- PolicyEventsByType[state.MappedType].Add(state);
- }
-
- Loading = false;
- StateHasChanged();
- }
-
- // private async Task GetAllAvatars() {
- // // if (!_enableAvatars) return;
- // Console.WriteLine("Getting avatars...");
- // var users = GetValidPolicyEventsByType(typeof(UserPolicyRuleEventContent)).Select(x => x.RawContent!["entity"]!.GetValue<string>()).Where(x => x.Contains(':') && !x.Contains("*")).ToList();
- // Console.WriteLine($"Got {users.Count} users!");
- // var usersByHomeServer = users.GroupBy(x => x!.Split(':')[1]).ToDictionary(x => x.Key!, x => x.ToList());
- // Console.WriteLine($"Got {usersByHomeServer.Count} homeservers!");
- // var homeserverTasks = usersByHomeServer.Keys.Select(x => RemoteHomeserver.TryCreate(x)).ToAsyncEnumerable();
- // await foreach (var server in homeserverTasks) {
- // if (server is null) continue;
- // var profileTasks = usersByHomeServer[server.BaseUrl].Select(x => TryGetProfile(server, x)).ToList();
- // await Task.WhenAll(profileTasks);
- // profileTasks.RemoveAll(x => x.Result is not { Value: { AvatarUrl: not null } });
- // foreach (var profile in profileTasks.Select(x => x.Result!.Value)) {
- // // if (profile is null) continue;
- // if (!string.IsNullOrWhiteSpace(profile.Value.AvatarUrl)) {
- // var url = await hsResolver.ResolveMediaUri(server.BaseUrl, profile.Value.AvatarUrl);
- // Avatars.TryAdd(profile.Key, url);
- // }
- // else Avatars.TryAdd(profile.Key, null);
- // }
- //
- // StateHasChanged();
- // }
- // }
- //
- // private async Task<KeyValuePair<string, UserProfileResponse>?> TryGetProfile(RemoteHomeserver server, string mxid) {
- // try {
- // return new KeyValuePair<string, UserProfileResponse>(mxid, await server.GetProfileAsync(mxid));
- // }
- // catch {
- // return null;
- // }
- // }
-
- 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?["entity"]?.GetValue<string>())).ToList();
-
- private List<StateEventResponse> GetInvalidPolicyEventsByType(Type type) => GetPolicyEventsByType(type)
- .Where(x => string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue<string>())).ToList();
-
- private string? GetPolicyTypeNameOrNull(Type type) => type.GetFriendlyNamePluralOrNull()
- ?? type.GetCustomAttributes<MatrixEventAttribute>()
- .FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.EventName))?.EventName;
-
- 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);
- await LoadStatesAsync();
- }
-
- private async Task UpgradePolicyAsync(StateEventResponse policyEvent) {
- policyEvent.RawContent["upgraded_from_type"] = policyEvent.Type;
- await LoadStatesAsync();
- }
-
- private static FrozenSet<Type> KnownPolicyTypes = StateEvent.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))).ToFrozenSet();
-
- // event types, unnamed
- private static Dictionary<string, Type> PolicyTypes = KnownPolicyTypes
- .ToDictionary(x => x.GetCustomAttributes<MatrixEventAttribute>().First(y => !string.IsNullOrWhiteSpace(y.EventName)).EventName, x => x);
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Space.razor b/MatrixRoomUtils.Web/Pages/Rooms/Space.razor
deleted file mode 100644
index ce94d01..0000000
--- a/MatrixRoomUtils.Web/Pages/Rooms/Space.razor
+++ /dev/null
@@ -1,100 +0,0 @@
-@page "/Rooms/{RoomId}/Space"
-@using LibMatrix.RoomTypes
-@using ArcaneLibs.Extensions
-@using LibMatrix
-<h3>Room manager - Viewing Space</h3>
-
-<button onclick="@JoinAllRooms">Join all rooms</button>
-@foreach (var room in Rooms) {
- <RoomListItem Room="room" ShowOwnProfile="true"></RoomListItem>
-}
-
-
-<br/>
-<details style="background: #0002;">
- <summary style="background: #fff1;">State list</summary>
- @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type)) {
- <p>@stateEvent.StateKey/@stateEvent.Type:</p>
- <pre>@stateEvent.RawContent.ToJson()</pre>
- }
-</details>
-
-@code {
-
- [Parameter]
- public string RoomId { get; set; } = "invalid!!!!!!";
-
- private GenericRoom? Room { get; set; }
-
- private StateEventResponse[] States { get; set; } = Array.Empty<StateEventResponse>();
- private List<GenericRoom> Rooms { get; } = new();
- private List<string> ServersInSpace { get; } = new();
-
- protected override async Task OnInitializedAsync() {
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
-
- Room = hs.GetRoom(RoomId.Replace('~', '.'));
-
- var state = Room.GetFullStateAsync();
- await foreach (var stateEvent in state) {
- switch (stateEvent.Type) {
- case "m.space.child": {
- var roomId = stateEvent.StateKey;
- var room = hs.GetRoom(roomId);
- if (room is not null) {
- Rooms.Add(room);
- }
- break;
- }
- case "m.room.member": {
- var serverName = stateEvent.StateKey.Split(':').Last();
- if (!ServersInSpace.Contains(serverName)) {
- ServersInSpace.Add(serverName);
- }
- break;
- }
- }
- }
- await base.OnInitializedAsync();
-
- // var state = await Room.GetStateAsync("");
- // if (state is not null) {
- // // Console.WriteLine(state.Value.ToJson());
- // States = state.Value.Deserialize<StateEventResponse[]>()!;
- //
- // foreach (var stateEvent in States) {
- // if (stateEvent.Type == "m.space.child") {
- // // if (stateEvent.Content.ToJson().Length < 5) return;
- // var roomId = stateEvent.StateKey;
- // var room = hs.GetRoom(roomId);
- // if (room is not null) {
- // Rooms.Add(room);
- // }
- // }
- // else if (stateEvent.Type == "m.room.member") {
- // var serverName = stateEvent.StateKey.Split(':').Last();
- // if (!ServersInSpace.Contains(serverName)) {
- // ServersInSpace.Add(serverName);
- // }
- // }
- // }
-
- // if(state.Value.TryGetProperty("Type", out var Type))
- // {
- // }
- // else
- // {
- // //this is fine, apprently...
- // //Console.WriteLine($"Room {room.RoomId} has no Content.Type in m.room.create!");
- // }
-
- // await base.OnInitializedAsync();
- }
-
- private async Task JoinAllRooms() {
- List<Task<RoomIdResponse>> tasks = Rooms.Select(room => room.JoinAsync(ServersInSpace.ToArray())).ToList();
- await Task.WhenAll(tasks);
- }
-
-}
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/StateEditor.razor b/MatrixRoomUtils.Web/Pages/Rooms/StateEditor.razor
deleted file mode 100644
index 0e9d4f6..0000000
--- a/MatrixRoomUtils.Web/Pages/Rooms/StateEditor.razor
+++ /dev/null
@@ -1,144 +0,0 @@
-@page "/Rooms/{RoomId}/State/Edit"
-@using ArcaneLibs.Extensions
-@using LibMatrix
-@inject ILocalStorageService LocalStorage
-@inject NavigationManager NavigationManager
-<h3>Room state editor - Editing @RoomId</h3>
-<hr/>
-
-<p>@status</p>
-
-<input type="checkbox" id="showAll" @bind="ShowMembershipEvents"/> Show member events
-<br/>
-<InputSelect @bind-Value="shownStateKey">
- <option value="">-- State key --</option>
- @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != "").Select(x => x.StateKey).Distinct().OrderBy(x => x)) {
- <option value="@stateEvent">@stateEvent</option>
- Console.WriteLine(stateEvent);
- }
-</InputSelect>
-<br/>
-<InputSelect @bind-Value="shownType">
- <option value="">-- Type --</option>
- @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey != shownStateKey).Select(x => x.Type).Distinct().OrderBy(x => x)) {
- <option value="@stateEvent">@stateEvent</option>
- }
-</InputSelect>
-<br/>
-
-<textarea @bind="shownEventJson" style="width: 100%; height: fit-Content;"></textarea>
-
-<LogView></LogView>
-
-@code {
- //get room list
- // - sync withroom list filter
- // Type = support.feline.msc3784
- //support.feline.policy.lists.msc.v1
-
- [Parameter]
- public string? RoomId { get; set; }
-
- public List<StateEventResponse> FilteredEvents { get; set; } = new();
- public List<StateEventResponse> Events { get; set; } = new();
- public string status = "";
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- RoomId = RoomId.Replace('~', '.');
- await LoadStatesAsync();
- Console.WriteLine("Policy list editor initialized!");
- }
-
- private DateTime _lastUpdate = DateTime.Now;
-
- private async Task LoadStatesAsync() {
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
-
- var StateLoaded = 0;
- var response = (hs.GetRoom(RoomId)).GetFullStateAsync();
- await foreach (var _ev in response) {
- // var e = new StateEventResponse {
- // Type = _ev.Type,
- // StateKey = _ev.StateKey,
- // OriginServerTs = _ev.OriginServerTs,
- // Content = _ev.Content
- // };
- Events.Add(_ev);
- if (string.IsNullOrEmpty(_ev.StateKey)) {
- FilteredEvents.Add(_ev);
- }
- StateLoaded++;
-
- if (!((DateTime.Now - _lastUpdate).TotalMilliseconds > 100)) continue;
- _lastUpdate = DateTime.Now;
- status = $"Loaded {StateLoaded} state events";
- StateHasChanged();
- await Task.Delay(0);
- }
-
- StateHasChanged();
- }
-
- private async Task RebuildFilteredData() {
- status = "Rebuilding filtered data...";
- StateHasChanged();
- await Task.Delay(1);
- var _FilteredEvents = Events;
- if (!ShowMembershipEvents)
- _FilteredEvents = _FilteredEvents.Where(x => x.Type != "m.room.member").ToList();
-
- status = "Done, rerendering!";
- StateHasChanged();
- await Task.Delay(1);
- FilteredEvents = _FilteredEvents;
-
- if (_shownType is not null)
- shownEventJson = _FilteredEvents.First(x => x.Type == _shownType).RawContent.ToJson(indent: true, ignoreNull: true);
-
- StateHasChanged();
- }
-
- public struct PreRenderedStateEvent {
- public string content { get; set; }
- public long origin_server_ts { get; set; }
- public string state_key { get; set; }
- public string type { get; set; }
- // public string Sender { get; set; }
- // public string EventId { get; set; }
- // public string UserId { get; set; }
- // public string ReplacesState { get; set; }
- }
-
- public bool ShowMembershipEvents {
- get => _showMembershipEvents;
- set {
- _showMembershipEvents = value;
- RebuildFilteredData();
- }
- }
-
- private bool _showMembershipEvents;
- private string _shownStateKey;
- private string _shownType;
-
- private string shownStateKey {
- get => _shownStateKey;
- set {
- _shownStateKey = value;
- RebuildFilteredData();
- }
- }
-
- private string shownType {
- get => _shownType;
- set {
- _shownType = value;
- RebuildFilteredData();
- }
- }
-
- private string shownEventJson { get; set; }
-}
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/StateViewer.razor b/MatrixRoomUtils.Web/Pages/Rooms/StateViewer.razor
deleted file mode 100644
index 2d0e0b0..0000000
--- a/MatrixRoomUtils.Web/Pages/Rooms/StateViewer.razor
+++ /dev/null
@@ -1,127 +0,0 @@
-@page "/Rooms/{RoomId}/State/View"
-@using ArcaneLibs.Extensions
-@using LibMatrix
-@inject ILocalStorageService LocalStorage
-@inject NavigationManager NavigationManager
-<h3>Room state viewer - Viewing @RoomId</h3>
-<hr/>
-
-<p>@status</p>
-
-<input type="checkbox" id="showAll" @bind="ShowMembershipEvents"/> Show member events
-
-<table class="table table-striped table-hover" style="width: fit-Content;">
- <thead>
- <tr>
- <th scope="col">Type</th>
- <th scope="col">Content</th>
- </tr>
- </thead>
- <tbody>
- @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey == "").OrderBy(x => x.OriginServerTs)) {
- <tr>
- <td>@stateEvent.Type</td>
- <td style="max-width: fit-Content;">
- <pre>@stateEvent.RawContent.ToJson()</pre>
- </td>
- </tr>
- }
- </tbody>
-</table>
-
-@foreach (var group in FilteredEvents.GroupBy(x => x.StateKey).OrderBy(x => x.Key).Where(x => x.Key != "")) {
- <details>
- <summary>@group.Key</summary>
- <table class="table table-striped table-hover" style="width: fit-Content;">
- <thead>
- <tr>
- <th scope="col">Type</th>
- <th scope="col">Content</th>
- </tr>
- </thead>
- <tbody>
- @foreach (var stateEvent in group.OrderBy(x => x.OriginServerTs)) {
- <tr>
- <td>@stateEvent.Type</td>
- <td style="max-width: fit-Content;">
- <pre>@stateEvent.RawContent.ToJson()</pre>
- </td>
- </tr>
- }
- </tbody>
- </table>
- </details>
-}
-
-<LogView></LogView>
-
-@code {
- //get room list
- // - sync withroom list filter
- // Type = support.feline.msc3784
- //support.feline.policy.lists.msc.v1
-
- [Parameter]
- public string? RoomId { get; set; }
-
- public List<StateEventResponse> FilteredEvents { get; set; } = new();
- public List<StateEventResponse> Events { get; set; } = new();
- public string status = "";
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- await LoadStatesAsync();
- Console.WriteLine("Policy list editor initialized!");
- }
-
- private DateTime _lastUpdate = DateTime.Now;
-
- private async Task LoadStatesAsync() {
- var StateLoaded = 0;
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- var response = (hs.GetRoom(RoomId)).GetFullStateAsync();
- await foreach (var _ev in response) {
- Events.Add(_ev);
- if (string.IsNullOrEmpty(_ev.StateKey)) {
- FilteredEvents.Add(_ev);
- }
- StateLoaded++;
-
- if (!((DateTime.Now - _lastUpdate).TotalMilliseconds > 100)) continue;
- _lastUpdate = DateTime.Now;
- status = $"Loaded {StateLoaded} state events";
- StateHasChanged();
- await Task.Delay(0);
- }
-
- StateHasChanged();
- }
-
- private async Task RebuildFilteredData() {
- status = "Rebuilding filtered data...";
- StateHasChanged();
- await Task.Delay(1);
- var _FilteredEvents = Events;
- if (!ShowMembershipEvents)
- _FilteredEvents = _FilteredEvents.Where(x => x.Type != "m.room.member").ToList();
-
- status = "Done, rerendering!";
- StateHasChanged();
- await Task.Delay(1);
- FilteredEvents = _FilteredEvents;
- StateHasChanged();
- }
-
- public bool ShowMembershipEvents {
- get => _showMembershipEvents;
- set {
- _showMembershipEvents = value;
- RebuildFilteredData();
- }
- }
-
- private bool _showMembershipEvents;
-}
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Timeline.razor b/MatrixRoomUtils.Web/Pages/Rooms/Timeline.razor
deleted file mode 100644
index 01bf555..0000000
--- a/MatrixRoomUtils.Web/Pages/Rooms/Timeline.razor
+++ /dev/null
@@ -1,60 +0,0 @@
-@page "/Rooms/{RoomId}/Timeline"
-@using MatrixRoomUtils.Web.Shared.TimelineComponents
-@using LibMatrix
-@using LibMatrix.EventTypes.Spec
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.Homeservers
-<h3>RoomManagerTimeline</h3>
-<hr/>
-<p>Loaded @Events.Count events...</p>
-
-@foreach (var evt in Events) {
- <div type="@evt.Type" key="@evt.StateKey" itemid="@evt.EventId">
- <DynamicComponent Type="@ComponentType(evt)"
- Parameters="@(new Dictionary<string, object> { { "Event", evt }, { "Events", Events }, { "Homeserver", Homeserver!} })">
- </DynamicComponent>
- </div>
-}
-
-@code {
-
- [Parameter]
- public string RoomId { get; set; }
-
- private List<MessagesResponse> Messages { get; } = new();
- private List<StateEventResponse> Events { get; } = new();
-
- private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
-
- protected override async Task OnInitializedAsync() {
- Console.WriteLine("RoomId: " + RoomId);
- Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
- if (Homeserver is null) return;
- var room = Homeserver.GetRoom(RoomId);
- MessagesResponse? msgs = null;
- do {
- msgs = await room.GetMessagesAsync(limit: 1000, from: msgs?.End, dir: "b");
- Messages.Add(msgs);
- Console.WriteLine($"Got {msgs.Chunk.Count} messages");
- msgs.Chunk.Reverse();
- Events.InsertRange(0, msgs.Chunk);
- } while (msgs.End is not null);
-
-
- await base.OnInitializedAsync();
- }
-
- private StateEventResponse GetProfileEventBefore(StateEventResponse Event) => Events.TakeWhile(x => x != Event).Last(e => e.Type == "m.room.member" && e.StateKey == Event.Sender);
-
- private Type ComponentType(StateEvent Event) => Event.TypedContent switch {
- RoomCanonicalAliasEventContent => typeof(TimelineCanonicalAliasItem),
- RoomHistoryVisibilityEventContent => typeof(TimelineHistoryVisibilityItem),
- RoomTopicEventContent => typeof(TimelineRoomTopicItem),
- RoomMemberEventContent => typeof(TimelineMemberItem),
- RoomMessageEventContent => typeof(TimelineMessageItem),
- RoomCreateEventContent => typeof(TimelineRoomCreateItem),
- RoomNameEventContent => typeof(TimelineRoomNameItem),
- _ => typeof(TimelineUnknownItem)
- };
-
-}
diff --git a/MatrixRoomUtils.Web/Pages/ServerInfo.razor b/MatrixRoomUtils.Web/Pages/ServerInfo.razor
deleted file mode 100644
index 71a1980..0000000
--- a/MatrixRoomUtils.Web/Pages/ServerInfo.razor
+++ /dev/null
@@ -1,235 +0,0 @@
-@page "/ServerInfo/{Homeserver}"
-@using LibMatrix.Homeservers
-@using LibMatrix.Responses
-@using ArcaneLibs.Extensions
-<h3>ServerInfo</h3>
-<hr/>
-@if (ServerVersionResponse is not null) {
- <p>Server version: @ServerVersionResponse.Server.Name @ServerVersionResponse.Server.Version</p>
- <pre>@ServerVersionResponse?.ToJson(ignoreNull: true)</pre>
- <br/>
-}
-@if (ClientVersionsResponse is not null) {
- <p>Client versions:</p>
- <details>
- <summary>JSON data</summary>
- <pre>@ClientVersionsResponse?.ToJson(ignoreNull: true)</pre>
- </details>
- <u>Spec versions</u>
- <table>
- <thead>
- <td></td>
- <td>Version</td>
- <td>Release date</td>
- </thead>
- @foreach (var (version, info) in ClientVersions) {
- <tr>
- <td>@(ClientVersionsResponse.Versions.Contains(version) ? "\u2714" : "\u274c")</td>
- <td><a href="@info.SpecUrl">@info.Name</a></td>
- <td>@info.Released</td>
- </tr>
- }
-
- @foreach (var version in ClientVersionsResponse.Versions) {
- if (!ClientVersions.ContainsKey(version)) {
- <tr>
- <td>@("\u2714")</td>
- <td><a href="https://spec.matrix.org/@version">Unknown version: @version</a></td>
- <td></td>
- </tr>
- }
- }
- </table>
- <u>Unstable features</u>
- <table>
- <thead>
- <td style="padding-right: 8px;">Supported</td>
- <td style="padding-right: 8px;">Enabled</td>
- <td style="padding-right: 8px;">Name</td>
- </thead>
- @* @foreach (var (version, info) in ClientVersions) { *@
- @* <tr> *@
- @* *@
- @* <td>@("\u2714")</td> *@
- @* <td>@(ClientVersionsResponse.Versions.Contains(version) ? "\u2714" : "\u274c")</td> *@
- @* <td>@info.Released</td> *@
- @* </tr> *@
- @* } *@
-
- @foreach (var version in ClientVersionsResponse.UnstableFeatures) {
- if (!ClientVersions.ContainsKey(version.Key)) {
- <tr>
- <td>@("\u2714")</td>
- <td>@(version.Value ? "\u2714" : "\u274c")</td>
- <td>@version.Key</td>
- </tr>
- }
- }
- </table>
-}
-
-
-@code {
-
- [Parameter]
- public string? Homeserver { get; set; }
-
- public ServerVersionResponse? ServerVersionResponse { get; set; }
- public ClientVersionsResponse? ClientVersionsResponse { get; set; }
-
- protected override async Task OnParametersSetAsync() {
- if (Homeserver is not null) {
- var rhs = await hsProvider.GetRemoteHomeserver(Homeserver);
- ServerVersionResponse = await (rhs.FederationClient?.GetServerVersionAsync() ?? Task.FromResult<ServerVersionResponse?>(null));
- ClientVersionsResponse = await rhs.GetClientVersionsAsync();
- }
- base.OnParametersSetAsync();
- }
-
- private class ClientVersionInfo {
- public string Name { get; set; }
- public string SpecUrl { get; set; }
- public DateTime Released { get; set; }
- }
-
- private Dictionary<string, ClientVersionInfo> ClientVersions = new() {
- {
- "legacy",
- new() {
- Name = "Legacy: Last draft before formal release of r0.0.0",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/legacy/"
- }
- },
- {
- "r0.0.0",
- new() {
- Name = "r0.0.0: Initial release: media repo, sync v2",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/r0.0.0/"
- }
- },
- {
- "r0.0.1",
- new() {
- Name = "r0.0.1: User-interactive authentication, groups, read receipts, presence",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/r0.0.1/"
- }
- },
- {
- "r0.1.0",
- new() {
- Name = "r0.1.0: Device management, account data, push rules, VoIP",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/r0.1.0/"
- }
- },
- {
- "r0.2.0",
- new() {
- Name = "r0.2.0: Clarifications",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/client_server/r0.2.0.html"
- }
- },
- {
- "r0.3.0",
- new() {
- Name = "r0.3.0: Device management",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/client_server/r0.3.0.html"
- }
- },
- {
- "r0.4.0",
- new() {
- Name = "r0.4.0: Room directory",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/r0.4.0/"
- }
- },
- {
- "r0.5.0",
- new() {
- Name = "r0.5.0: Push rules, VoIP, groups, read receipts, presence",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/r0.5.0/"
- }
- },
- {
- "r0.6.0",
- new() {
- Name = "r0.6.0: Unbinding 3PIDs, clean up bindings from register",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/r0.6.0/"
- }
- },
- {
- "r0.6.1",
- new(){
- Name = "r0.6.1: Moderation policies, better alias handling",
- Released = DateTime.Parse("2014-07-01 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/legacy/r0.6.1/"
- }
- },
- {
- "v1.1",
- new() {
- Name = "v1.1: Key backup, knocking",
- Released = DateTime.Parse("2021-11-09 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/v1.1/"
- }
- }, {
- "v1.2",
- new() {
- Name = "v1.2: ",
- Released = DateTime.Parse("2022-02-02 00:00:00 +0000"),
- SpecUrl = "https://spec.matrix.org/v1.2/"
- }
- }, {
- "v1.3",
- new() {
- Name = "v1.3: ",
- Released = DateTime.Parse("2022-06-15 00:00:00 +0100"),
- SpecUrl = "https://spec.matrix.org/v1.3/"
- }
- }, {
- "v1.4",
- new() {
- Name = "v1.4: ",
- Released = DateTime.Parse("2022-09-29 00:00:00 +0100"),
- SpecUrl = "https://spec.matrix.org/v1.4/"
- }
- }, {
- "v1.5",
- new() {
- Name = "v1.5: ",
- Released = DateTime.Parse("2022-11-17 08:22:11 -0700"),
- SpecUrl = "https://spec.matrix.org/v1.5/"
- }
- }, {
- "v1.6",
- new () {
- Name = "v1.6: ",
- Released = DateTime.Parse("2023-02-14 08:25:40 -0700"),
- SpecUrl = "https://spec.matrix.org/v1.6"
- }
- }, {
- "v1.7",
- new () {
- Name = "v1.7: ",
- Released = DateTime.Parse("2023-05-25 09:47:21 -0600"),
- SpecUrl = "https://spec.matrix.org/v1.7"
- }
- }, {
- "v1.8",
- new () {
- Name = "v1.8: Room version 11",
- Released = DateTime.Parse("2023-08-23 09:23:53 -0600"),
- SpecUrl = "https://spec.matrix.org/v1.8"
- }
- }
- };
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Tools/CopyPowerlevel.razor b/MatrixRoomUtils.Web/Pages/Tools/CopyPowerlevel.razor
deleted file mode 100644
index bcbc974..0000000
--- a/MatrixRoomUtils.Web/Pages/Tools/CopyPowerlevel.razor
+++ /dev/null
@@ -1,84 +0,0 @@
-@page "/CopyPowerlevel"
-@using System.Diagnostics
-@using ArcaneLibs.Extensions
-@using LibMatrix
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.Homeservers
-@using LibMatrix.RoomTypes
-<h3>Copy powerlevel</h3>
-<hr/>
-
-<p>Users: </p>
-@foreach (var hs in hss) {
- <p>@hs.WhoAmI.UserId</p>
-}
-
-<br/>
-<LinkButton OnClick="Execute">Execute</LinkButton>
-<br/>
-@foreach (var line in Enumerable.Reverse(log)) {
- <p>@line</p>
-}
-
-@code {
- private List<string> log { get; set; } = new();
- List<AuthenticatedHomeserverGeneric> hss { get; set; } = new();
-
- protected override async Task OnInitializedAsync() {
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- var sessions = await MRUStorage.GetAllTokens();
- foreach (var userAuth in sessions) {
- var session = await MRUStorage.GetSession(userAuth);
- if (session is not null) {
- hss.Add(session);
- StateHasChanged();
- }
- }
-
- StateHasChanged();
- Console.WriteLine("Rerendered!");
- await base.OnInitializedAsync();
- }
-
- private async Task Execute() {
- foreach (var hs in hss) {
- var rooms = await hs.GetJoinedRooms();
- var tasks = rooms.Select(x=>Execute(hs, x)).ToAsyncEnumerable();
- await foreach (var a in tasks) {
- if (!string.IsNullOrWhiteSpace(a)) {
- log.Add(a);
- StateHasChanged();
- }
- }
- }
- }
-
- private async Task<string> Execute(AuthenticatedHomeserverGeneric hs, GenericRoom room) {
- try {
- var pls = await room.GetPowerLevelsAsync();
- // if (pls.GetUserPowerLevel(hs.WhoAmI.UserId) == pls.UsersDefault) return "I am default PL in " + room.RoomId;
- if (!pls.UserHasStatePermission(hs.WhoAmI.UserId, RoomPowerLevelEventContent.EventId)) return "I do not have permission to send PL in " + room.RoomId;
- foreach (var ahs in hss) {
- if (pls.GetUserPowerLevel(hs.WhoAmI.UserId) == pls.GetUserPowerLevel(ahs.WhoAmI.UserId)) {
- log.Add("I am same PL in " + room.RoomId);
- continue;
- }
-
- pls.SetUserPowerLevel(ahs.WhoAmI.UserId, pls.GetUserPowerLevel(hs.WhoAmI.UserId));
- await room.SendStateEventAsync(RoomPowerLevelEventContent.EventId, pls);
- log.Add($"Updated powerlevel of {room.RoomId} to {pls.GetUserPowerLevel(ahs.WhoAmI.UserId)}");
- }
-
- }
- catch (MatrixException e) {
- return $"Failed to update PLs in {room.RoomId}: {e.Message}";
- }
- catch (Exception e) {
- return $"Failed to update PLs in {room.RoomId}: {e.Message}";
- }
- StateHasChanged();
- return "";
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor
deleted file mode 100644
index dbf2f5f..0000000
--- a/MatrixRoomUtils.Web/Pages/Tools/KnownHomeserverList.razor
+++ /dev/null
@@ -1,54 +0,0 @@
-@page "/KnownHomeserverList"
-@using System.Diagnostics
-@using ArcaneLibs.Extensions
-@using LibMatrix.Homeservers
-@using LibMatrix.RoomTypes
-<h3>Known Homeserver List</h3>
-<hr/>
-
-@if (!IsFinished) {
- <p>
- <b>Loading...</b>
- </p>
-}
-
-@foreach (var (homeserver, members) in counts.OrderByDescending(x => x.Value)) {
- <p>@homeserver - @members</p>
-}
-<hr/>
-
-@code {
- Dictionary<string, List<string>> homeservers { get; set; } = new();
- Dictionary<string, int> counts { get; set; } = new();
- // List<HomeserverInfo> Homeservers = new();
- bool IsFinished { get; set; }
- // HomeserverInfoQueryProgress QueryProgress { get; set; } = new();
- AuthenticatedHomeserverGeneric? hs { get; set; }
-
- protected override async Task OnInitializedAsync() {
- hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- var fetchTasks = (await hs.GetJoinedRooms()).Select(x=>x.GetMembersByHomeserverAsync()).ToAsyncEnumerable();
- await foreach (var result in fetchTasks) {
- foreach (var (resHomeserver, resMembers) in result) {
- if (!homeservers.TryAdd(resHomeserver, resMembers)) {
- homeservers[resHomeserver].AddRange(resMembers);
- }
- counts[resHomeserver] = homeservers[resHomeserver].Count;
- }
- // StateHasChanged();
- // await Task.Delay(250);
- }
-
- foreach (var resHomeserver in homeservers.Keys) {
- homeservers[resHomeserver] = homeservers[resHomeserver].Distinct().ToList();
- counts[resHomeserver] = homeservers[resHomeserver].Count;
- }
-
- IsFinished = true;
- StateHasChanged();
- Console.WriteLine("Rerendered!");
- await base.OnInitializedAsync();
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Tools/MassJoinRoom.razor b/MatrixRoomUtils.Web/Pages/Tools/MassJoinRoom.razor
deleted file mode 100644
index 816b7db..0000000
--- a/MatrixRoomUtils.Web/Pages/Tools/MassJoinRoom.razor
+++ /dev/null
@@ -1,110 +0,0 @@
-@page "/MassRoomJoin"
-@using System.Diagnostics
-@using ArcaneLibs.Extensions
-@using LibMatrix
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.Homeservers
-@using LibMatrix.RoomTypes
-<h3>Mass join room</h3>
-<hr/>
-<p>Room: </p>
-<FancyTextBox @bind-Value="@roomId"></FancyTextBox>
-
-<p>Users: </p>
-@foreach (var hs in hss) {
- <p>@hs.WhoAmI.UserId</p>
-}
-
-<br/>
-<LinkButton OnClick="Execute">Execute</LinkButton>
-<br/>
-@foreach (var line in Enumerable.Reverse(log)) {
- <p>@line</p>
-}
-
-@code {
- private List<string> log { get; set; } = new();
- List<AuthenticatedHomeserverGeneric> hss { get; set; } = new();
- string roomId { get; set; }
-
- protected override async Task OnInitializedAsync() {
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
- var sessions = await MRUStorage.GetAllTokens();
- foreach (var userAuth in sessions) {
- var session = await MRUStorage.GetSession(userAuth);
- if (session is not null) {
- hss.Add(session);
- StateHasChanged();
- }
- }
-
- StateHasChanged();
- Console.WriteLine("Rerendered!");
- await base.OnInitializedAsync();
- }
-
- private async Task Execute() {
- // foreach (var hs in hss) {
- // var rooms = await hs.GetJoinedRooms();
- var tasks = hss.Select(ExecuteInvite).ToAsyncEnumerable();
- await foreach (var a in tasks) {
- if (!string.IsNullOrWhiteSpace(a)) {
- log.Add(a);
- StateHasChanged();
- }
- }
- tasks = hss.Select(ExecuteJoin).ToAsyncEnumerable();
- await foreach (var a in tasks) {
- if (!string.IsNullOrWhiteSpace(a)) {
- log.Add(a);
- StateHasChanged();
- }
- }
- // }
- }
-
- private async Task<string> ExecuteInvite(AuthenticatedHomeserverGeneric hs) {
- var room = hs.GetRoom(roomId);
- try {
- try {
- var joinRule = await room.GetJoinRuleAsync();
- if (joinRule.JoinRule == RoomJoinRulesEventContent.JoinRules.Public) return "Room is public, no invite needed";
- }
- catch { }
- var pls = await room.GetPowerLevelsAsync();
- if (pls.GetUserPowerLevel(hs.WhoAmI.UserId) < pls.Invite) return "I do not have permission to send invite in " + room.RoomId;
- await room.InviteUsersAsync(hss.Select(x => x.WhoAmI.UserId).ToList());
- log.Add($"Invited to {room.RoomId} to {pls.GetUserPowerLevel(hs.WhoAmI.UserId)}");
- }
- catch (MatrixException e) {
- return $"Failed to invite in {room.RoomId}: {e.Message}";
- }
- catch (Exception e) {
- return $"Failed to invite in {room.RoomId}: {e.Message}";
- }
- StateHasChanged();
- return "";
- }
-
- private async Task<string> ExecuteJoin(AuthenticatedHomeserverGeneric hs) {
- var room = hs.GetRoom(roomId);
- try {
- try {
- var mse = await room.GetStateOrNullAsync<RoomMemberEventContent>(RoomMemberEventContent.EventId, hs.WhoAmI.UserId);
- if (mse?.Membership == "join") return $"User {hs.WhoAmI.UserId} already in room";
- }
- catch { }
- await room.JoinAsync();
- }
- catch (MatrixException e) {
- return $"Failed to join {hs.WhoAmI.UserId} to {room.RoomId}: {e.Message}";
- }
- catch (Exception e) {
- return $"Failed to join {hs.WhoAmI.UserId} to {room.RoomId}: {e.Message}";
- }
- StateHasChanged();
- return "";
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor b/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor
deleted file mode 100644
index 20aa639..0000000
--- a/MatrixRoomUtils.Web/Pages/Tools/MediaLocator.razor
+++ /dev/null
@@ -1,111 +0,0 @@
-@page "/MediaLocator"
-@using LibMatrix.Homeservers
-@inject HttpClient Http
-<h3>Media locator</h3>
-<hr/>
-
-<b>This is going to expose your IP address to all these homeservers!</b>
-<details>
- <summary>Checked homeserver list (@homeservers.Count entries)</summary>
- <ul>
- @foreach (var hs in homeservers) {
- <li>@hs</li>
- }
- </ul>
-</details>
-<button @onclick="addMoreHomeservers">Add more homeservers</button>
-<br/>
-<span>MXC URL: </span>
-<input type="text" @bind="mxcUrl"/>
-<button @onclick="executeSearch">Search</button>
-
-@if (successResults.Count > 0) {
- <h4>Successes</h4>
- <ul>
- @foreach (var result in successResults) {
- <li>@result</li>
- }
- </ul>
-}
-
-@if (errorResults.Count > 0) {
- <h4>Errors</h4>
- <ul>
- @foreach (var result in errorResults) {
- <li>@result</li>
- }
- </ul>
-}
-
-
-@code {
- string mxcUrl { get; set; }
- readonly List<string> successResults = new();
- readonly List<string> errorResults = new();
- readonly List<string> homeservers = new();
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- homeservers.AddRange(new[] {
- "matrix.org",
- "feline.support",
- "rory.gay",
- "the-apothecary.club",
- "envs.net",
- "projectsegfau.lt"
- });
- }
-
- Task executeSearch() {
- var sem = new SemaphoreSlim(128, 128);
- homeservers.ForEach(async hs => {
- await sem.WaitAsync();
- var httpClient = new HttpClient { BaseAddress = new Uri(hs) };
- httpClient.Timeout = TimeSpan.FromSeconds(5);
- var rmu = mxcUrl.Replace("mxc://", $"{hs}/_matrix/media/v3/download/");
- try {
- var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, rmu));
- if (res.IsSuccessStatusCode) {
- successResults.Add($"{hs}: found - {res.Content.Headers.ContentLength} bytes");
- StateHasChanged();
- return;
- }
- errorResults.Add($"Error: {hs} - {res.StatusCode}\n" + await res.Content.ReadAsStringAsync());
- }
- catch (Exception e) {
- errorResults.Add($"Error: {e}");
- }
- finally {
- sem.Release();
- }
- StateHasChanged();
- });
- return Task.CompletedTask;
- }
-
- async Task addMoreHomeservers() {
- var res = await Http.GetAsync("/homeservers.txt");
- var content = await res.Content.ReadAsStringAsync();
- homeservers.Clear();
- var lines = content.Split("\n");
-
- var rhs = new RemoteHomeserver("rory.gay");
- var sem = new SemaphoreSlim(128, 128);
- lines.ToList().ForEach(async line => {
- await sem.WaitAsync();
- try {
- homeservers.Add((await hsResolver.ResolveHomeserverFromWellKnown(line)).Client);
- StateHasChanged();
- }
- catch (Exception e) {
- Console.WriteLine(e);
- }
- finally {
- sem.Release();
- }
- });
-
- StateHasChanged();
- }
-
-}
diff --git a/MatrixRoomUtils.Web/Pages/Tools/SpaceDebug.razor b/MatrixRoomUtils.Web/Pages/Tools/SpaceDebug.razor
deleted file mode 100644
index 5b4815d..0000000
--- a/MatrixRoomUtils.Web/Pages/Tools/SpaceDebug.razor
+++ /dev/null
@@ -1,113 +0,0 @@
-@page "/SpaceDebug"
-@using LibMatrix.Filters
-@using LibMatrix.Helpers
-<h3>SpaceDebug</h3>
-<hr/>
-
-<p>@Status</p>
-
-<b>Has parent:</b>
-<br/>
-
-@foreach (var (roomId, parents) in SpaceParents) {
- <p>@roomId's parents</p>
- <ul>
- @foreach (var parent in parents) {
- <li>@parent</li>
- }
- </ul>
-}
-
-<b>Space children:</b>
-
-@foreach (var (roomId, children) in SpaceChildren) {
- <p>@roomId's children</p>
- <ul>
- @foreach (var child in children) {
- <li>@child</li>
- }
- </ul>
-}
-
-@code {
- private string _status = "Loading...";
-
- public string Status {
- get => _status;
- set {
- _status = value;
- StateHasChanged();
- }
- }
-
- public Dictionary<string, List<string>> SpaceChildren { get; set; } = new();
- public Dictionary<string, List<string>> SpaceParents { get; set; } = new();
-
- protected override async Task OnInitializedAsync() {
- Status = "Getting homeserver...";
- var hs = await MRUStorage.GetCurrentSessionOrNavigate();
- if (hs is null) return;
-
- var syncHelper = new SyncHelper(hs) {
- Filter = new SyncFilter() {
- Presence = new(0),
- Room = new() {
- AccountData = new(limit: 0),
- Ephemeral = new(limit: 0),
- State = new(limit: 1000, types: new() { "m.space.child", "m.space.parent" }),
- Timeline = new(limit: 0)
- },
- AccountData = new(limit: 0)
- }
- };
-
- Status = "Syncing...";
-
- var syncs = syncHelper.EnumerateSyncAsync();
- await foreach (var sync in syncs) {
- if (sync is null) {
- Status = "Sync failed";
- continue;
- }
-
- if (sync.Rooms is null) {
- Status = "No rooms in sync...";
- break;
- }
-
- if (sync.Rooms.Join is null) {
- Status = "No joined rooms in sync...";
- break;
- }
-
- if (sync.Rooms.Join.Count == 0) {
- Status = "Joined rooms list was empty...";
- break;
- }
-
- // nextBatch = sync.NextBatch;
- foreach (var (roomId, data) in sync.Rooms!.Join!) {
- data.State?.Events?.ForEach(e => {
- if (e.Type == "m.space.child") {
- if (!SpaceChildren.ContainsKey(roomId)) SpaceChildren[roomId] = new();
- if (e.RawContent is null) e.StateKey += " (null)";
- else if (e.RawContent.Count == 0) e.StateKey += " (empty)";
- SpaceChildren[roomId].Add(e.StateKey);
- }
- if (e.Type == "m.space.parent") {
- if (!SpaceParents.ContainsKey(roomId)) SpaceParents[roomId] = new();
- if (e.RawContent is null) e.StateKey += " (null)";
- else if (e.RawContent.Count == 0) e.StateKey += " (empty)";
- SpaceParents[roomId].Add(e.StateKey);
- }
- });
- }
- Status = $"Synced {sync.Rooms.Join.Count} rooms, found {SpaceChildren.Count} spaces, {SpaceParents.Count} parents";
- }
- Status = $"Synced: found {SpaceChildren.Count}->{SpaceChildren.Sum(x => x.Value.Count)} spaces, {SpaceParents.Count}->{SpaceParents.Sum(x => x.Value.Count)} parents!";
-
- await base.OnInitializedAsync();
- }
-
-
-}
diff --git a/MatrixRoomUtils.Web/Pages/User/DMManager.razor b/MatrixRoomUtils.Web/Pages/User/DMManager.razor
deleted file mode 100644
index a327793..0000000
--- a/MatrixRoomUtils.Web/Pages/User/DMManager.razor
+++ /dev/null
@@ -1,62 +0,0 @@
-@page "/User/DirectMessages"
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.Responses
-@using MatrixRoomUtils.Abstractions
-<h3>Direct Messages</h3>
-<hr/>
-
-@foreach (var (targetUser, rooms) in DMRooms) {
- <div>
- <InlineUserItem User="targetUser"></InlineUserItem>
- @foreach (var room in rooms) {
- <RoomListItem RoomInfo="room" LoadData="true"></RoomListItem>
- }
- </div>
-}
-
-@code {
- private string? _status;
- private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
- private Dictionary<UserProfileResponse, List<RoomInfo>> DMRooms { get; set; } = new();
-
- public string? Status {
- get => _status;
- set {
- _status = value;
- StateHasChanged();
- }
- }
-
- protected override async Task OnInitializedAsync() {
- Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
- if (Homeserver is null) return;
- Status = "Loading global profile...";
- if (Homeserver.WhoAmI?.UserId is null) return;
-
- Status = "Loading DM list from account data...";
- var dms = await Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
- DMRooms.Clear();
- foreach (var (userId, rooms) in dms) {
- var roomList = new List<RoomInfo>();
- DMRooms.Add(await Homeserver.GetProfileAsync(userId), roomList);
- foreach (var room in rooms) {
- var roomInfo = new RoomInfo() { Room = Homeserver.GetRoom(room) };
- roomList.Add(roomInfo);
- roomInfo.StateEvents.Add(new() {
- Type = RoomNameEventContent.EventId,
- TypedContent = new RoomNameEventContent() {
- Name = await Homeserver.GetRoom(room).GetNameOrFallbackAsync(4)
- },
- RoomId = room, Sender = null, EventId = null
- });
- }
- StateHasChanged();
- }
-
- StateHasChanged();
- Status = null;
-
- await base.OnInitializedAsync();
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/User/DMSpace.razor b/MatrixRoomUtils.Web/Pages/User/DMSpace.razor
deleted file mode 100644
index 21cc264..0000000
--- a/MatrixRoomUtils.Web/Pages/User/DMSpace.razor
+++ /dev/null
@@ -1,86 +0,0 @@
-@page "/User/DMSpace/Setup"
-@using LibMatrix.Homeservers
-@using LibMatrix
-@using MatrixRoomUtils.LibDMSpace
-@using MatrixRoomUtils.LibDMSpace.StateEvents
-@using MatrixRoomUtils.Web.Pages.User.DMSpaceStages
-<h3>DM Space Management</h3>
-<hr/>
-<CascadingValue Value="@DmSpace">
- @switch (Stage) {
- case -1:
- <p>Initialising...</p>
- break;
- case 0:
- <DMSpaceStage0/>
- break;
- case 1:
- <DMSpaceStage1/>
- break;
- case 2:
- <DMSpaceStage2/>
- break;
- case 3:
- <DMSpaceStage3/>
- break;
- default:
- <p>Stage is unknown value: @Stage!</p>
- break;
- }
-</CascadingValue>
-
-@code {
- private int _stage = -1;
-
- [Parameter, SupplyParameterFromQuery(Name = "stage")]
- public int Stage {
- get => _stage;
- set {
- _stage = value;
- Console.WriteLine($"Stage is now {value}");
- StateHasChanged();
- }
- }
-
- public AuthenticatedHomeserverGeneric? Homeserver { get; set; }
-
- public DMSpaceConfiguration? DmSpaceConfiguration { get; set; }
-
- [Parameter]
- public DMSpace? DmSpace { get; set; }
-
- protected override async Task OnInitializedAsync() {
- if (NavigationManager.Uri.Contains("?stage=")) {
- NavigationManager.NavigateTo("/User/DMSpace", true);
- }
- DmSpace = this;
- Homeserver ??= await MRUStorage.GetCurrentSessionOrNavigate();
- if (Homeserver is null) return;
- try {
- DmSpaceConfiguration = await Homeserver.GetAccountDataAsync<DMSpaceConfiguration>("gay.rory.dm_space");
- var room = Homeserver.GetRoom(DmSpaceConfiguration.DMSpaceId);
- await room.GetStateAsync<object>(DMSpaceInfo.EventId);
- Stage = 1;
- }
- catch (MatrixException e) {
- if (e.ErrorCode == "M_NOT_FOUND") {
- Stage = 0;
- DmSpaceConfiguration = new();
- }
- else throw;
- }
- catch (Exception e) {
- throw;
- }
- finally {
- StateHasChanged();
- }
- await base.OnInitializedAsync();
- }
-
- protected override async Task OnParametersSetAsync() {
- StateHasChanged();
- await base.OnParametersSetAsync();
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage0.razor b/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage0.razor
deleted file mode 100644
index 49fd5b4..0000000
--- a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage0.razor
+++ /dev/null
@@ -1,11 +0,0 @@
-<b>
- <u>Welcome to the DM Space tool!</u>
-</b>
-<p>This wizard will help you set up a DM space.</p>
-<p>This is useful for eg. sharing DM rooms across multiple accounts.</p>
-<br/>
-<LinkButton href="/User/DMSpace?stage=1">Get started</LinkButton>
-
-@code {
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage1.razor b/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage1.razor
deleted file mode 100644
index 5958fc5..0000000
--- a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage1.razor
+++ /dev/null
@@ -1,128 +0,0 @@
-@using LibMatrix.Homeservers
-@using LibMatrix.RoomTypes
-@using LibMatrix
-@using LibMatrix.Responses
-@using MatrixRoomUtils.LibDMSpace
-@using MatrixRoomUtils.LibDMSpace.StateEvents
-@using Microsoft.Extensions.Primitives
-@using ArcaneLibs.Extensions
-<b>
- <u>DM Space setup tool - stage 1: Configure space</u>
-</b>
-<p>You will need a space to use for DM rooms.</p>
-@if (DmSpace is not null) {
- <p>
- Selected space:
- <InputSelect @bind-Value="DmSpace.DmSpaceConfiguration.DMSpaceId">
- @foreach (var (id, name) in spaces) {
- <option value="@id">@name</option>
- }
- </InputSelect>
- </p>
- <p>
- <InputCheckbox @bind-Value="DmSpaceInfo.LayerByUser"></InputCheckbox>
- Create sub-spaces per user
- </p>
-}
-else {
- <b>Error: DmSpaceConfiguration is null!</b>
-}
-
-<br/>
-<LinkButton OnClick="@Execute">Next</LinkButton>
-
-@if (!string.IsNullOrWhiteSpace(Status)) {
- <p>@Status</p>
-}
-
-@code {
-
- private string? Status {
- get => _status;
- set {
- _status = value;
- StateHasChanged();
- }
- }
-
- private Dictionary<string, string> spaces = new() { { "", "New space" } };
- private string? _status;
-
- [CascadingParameter]
- public DMSpace? DmSpace { get; set; }
-
- public DMSpaceInfo? DmSpaceInfo { get; set; } = new();
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- }
-
- SemaphoreSlim _semaphoreSlim = new(1, 1);
- protected override async Task OnParametersSetAsync() {
- if (DmSpace is null)
- return;
- await _semaphoreSlim.WaitAsync();
- DmSpace.DmSpaceConfiguration ??= new();
- if (spaces.Count == 1) {
- Status = "Looking for spaces...";
- var userRoomsEnum = DmSpace.Homeserver.GetJoinedRoomsByType("m.space");
- List<GenericRoom> userRooms = new();
- await foreach (var room in userRoomsEnum) {
- userRooms.Add(room);
- }
- var roomChecks = userRooms.Select(GetFeasibleSpaces).ToAsyncEnumerable();
- await foreach(var room in roomChecks)
- if(room.HasValue)
- spaces.TryAdd(room.Value.id, room.Value.name);
-
- Status = "Done!";
- }
- _semaphoreSlim.Release();
- await base.OnParametersSetAsync();
- }
-
- private async Task Execute() {
- if (string.IsNullOrWhiteSpace(DmSpace.DmSpaceConfiguration.DMSpaceId)) {
- var crr = CreateRoomRequest.CreatePrivate(DmSpace.Homeserver, "Direct Messages");
- crr.CreationContentBaseType.Type = "m.space";
- DmSpace.DmSpaceConfiguration.DMSpaceId = (await DmSpace.Homeserver.CreateRoom(crr)).RoomId;
- }
- await DmSpace.Homeserver!.SetAccountDataAsync(DMSpaceConfiguration.EventId, DmSpace.DmSpaceConfiguration);
- var space = DmSpace.Homeserver.GetRoom(DmSpace.DmSpaceConfiguration.DMSpaceId);
- await space.SendStateEventAsync(DMSpaceInfo.EventId, DmSpaceInfo);
-
- NavigationManager.NavigateTo("/User/DMSpace/Setup?stage=2");
- }
-
- public async Task<(string id, string name)?> GetFeasibleSpaces(GenericRoom room) {
- try {
- var pls = await room.GetPowerLevelsAsync();
- if (!pls.UserHasStatePermission(DmSpace.Homeserver.WhoAmI.UserId, "m.space.child")) {
- Console.WriteLine($"No permission to send m.space.child in {room.RoomId}...");
- return null;
- }
- var roomName = await room.GetNameAsync();
- Status = $"Found viable space: {roomName}";
- if (string.IsNullOrWhiteSpace(DmSpace.DmSpaceConfiguration.DMSpaceId)) {
- try {
- var dsi = await DmSpace.Homeserver.GetRoom(room.RoomId).GetStateOrNullAsync<DMSpaceInfo>(DMSpaceInfo.EventId) ?? new DMSpaceInfo();
- if (await room.GetStateOrNullAsync<DMSpaceInfo>(DMSpaceInfo.EventId) is not null && dsi is not null) {
- DmSpace.DmSpaceConfiguration.DMSpaceId = room.RoomId;
- DmSpaceInfo = dsi;
- }
- }
- catch (MatrixException e) {
- if (e.ErrorCode == "M_NOT_FOUND") Console.WriteLine($"{room.RoomId} is not a DM space.");
- else throw;
- }
- }
- return (room.RoomId, roomName);
- }
- catch (MatrixException e) {
- if (e.ErrorCode == "M_NOT_FOUND") Console.WriteLine($"m.room.power_levels does not exist in {room.RoomId}!!!");
- else throw;
- }
- return null;
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor b/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor
deleted file mode 100644
index 60c68ac..0000000
--- a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor
+++ /dev/null
@@ -1,242 +0,0 @@
-@using LibMatrix.Homeservers
-@using LibMatrix.RoomTypes
-@using LibMatrix
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.Responses
-@using MatrixRoomUtils.LibDMSpace
-@using MatrixRoomUtils.LibDMSpace.StateEvents
-@using ArcaneLibs.Extensions
-@using System.Text.Json.Serialization
-@using MatrixRoomUtils.Abstractions
-<b>
- <u>DM Space setup tool - stage 2: Fix DM room attribution</u>
-</b>
-<p>This is just to make sure that your DMs are attributed to the right person!</p>
-
-@if (!string.IsNullOrWhiteSpace(Status)) {
- <p>@Status</p>
-}
-
-@if (DmSpace is not null) {
- @foreach (var (userId, room) in dmRooms.OrderBy(x => x.Key.Id)) {
- <InlineUserItem User="@userId"></InlineUserItem>
- @foreach (var roomInfo in room) {
- <RoomListItem RoomInfo="@roomInfo">
- <LinkButton Round="true" OnClick="@(async () => DmToReassign = roomInfo)">Reassign</LinkButton>
- </RoomListItem>
- }
- }
-}
-else {
- <b>Error: DmSpaceConfiguration is null!</b>
-}
-
-<br/>
-<LinkButton OnClick="@Execute">Next</LinkButton>
-
-@{
- var _offset = 0;
-}
-@foreach (var (room, usersList) in duplicateDmRooms) {
- <ModalWindow Title="Duplicate room found" X="_offset += 30" Y="_offset">
- <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))">
- <span>Assign to </span>
- <InlineUserItem User="userProfileResponse"></InlineUserItem>
- </LinkButton>
- <br/>
- }
- </ModalWindow>
-}
-
-@if (DmToReassign is not null) {
- <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))">
- <span>Assign to </span>
- <InlineUserItem User="userProfileResponse"></InlineUserItem>
- </LinkButton>
- <br/>
- }
- </ModalWindow>
-}
-
-@code {
-
- private string newMxid { get; set; } = "";
-
- private RoomInfo? DmToReassign {
- get => _dmToReassign;
- set {
- _dmToReassign = value;
- StateHasChanged();
- }
- }
-
- private string? Status {
- get => _status;
- set {
- _status = value;
- StateHasChanged();
- }
- }
-
- private string? _status;
- private RoomInfo? _dmToReassign;
-
- [CascadingParameter]
- public DMSpace? DmSpace { get; set; }
-
- private Dictionary<UserProfileWithId, List<RoomInfo>> dmRooms { get; set; } = new();
- private Dictionary<RoomInfo, List<UserProfileWithId>> duplicateDmRooms { get; set; } = new();
- private Dictionary<RoomInfo, List<UserProfileWithId>> roomMembers { get; set; } = new();
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- }
-
- SemaphoreSlim _semaphore = new(1, 1);
-
- protected override async Task OnParametersSetAsync() {
- if (DmSpace is null)
- return;
- await _semaphore.WaitAsync();
- DmToReassign = null;
- var hs = DmSpace.Homeserver;
- Status = "Loading DM list from account data...";
- var dms = await DmSpace.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
- Status = "Optimising DM list from account data...";
- var joinedRooms = (await hs.GetJoinedRooms()).Select(x => x.RoomId).ToList();
- foreach (var (user, rooms) in dms) {
- for (var i = rooms.Count - 1; i >= 0; i--) {
- var roomId = rooms[i];
- if (!joinedRooms.Contains(roomId))
- rooms.RemoveAt(i);
- }
- dms[user] = rooms.Distinct().ToList();
- }
- dms.RemoveAll((x, y) => y is {Count: 0});
- await DmSpace.Homeserver.SetAccountDataAsync("m.direct", dms);
- dmRooms.Clear();
-
- Status = "DM list optimised, fetching info...";
- var results = dms.Select(async x => {
- var (userId, rooms) = x;
- UserProfileWithId userProfile;
- try {
- var profile = await DmSpace.Homeserver.GetProfileAsync(userId);
- userProfile = new() {
- AvatarUrl = profile.AvatarUrl,
- Id = userId,
- DisplayName = profile.DisplayName
- };
- }
- catch {
- userProfile = new() {
- AvatarUrl = "mxc://feline.support/uUxBwaboPkMGtbZcAGZaIzpK",
- DisplayName = userId,
- Id = userId
- };
- }
- var roomList = new List<RoomInfo>();
- var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncEnumerable();
- await foreach (var result in tasks)
- roomList.Add(result);
- return (userProfile, roomList);
- // StateHasChanged();
- }).ToAsyncEnumerable();
- await foreach (var res in results) {
- dmRooms.Add(res.userProfile, res.roomList);
- // Status = $"Listed {dmRooms.Count} users";
- }
- _semaphore.Release();
- var duplicateDmRoomIds = new Dictionary<string, List<UserProfileWithId>>();
- foreach (var (user, rooms) in dmRooms) {
- foreach (var roomInfo in rooms) {
- if (!duplicateDmRoomIds.ContainsKey(roomInfo.Room.RoomId))
- duplicateDmRoomIds.Add(roomInfo.Room.RoomId, new());
- duplicateDmRoomIds[roomInfo.Room.RoomId].Add(user);
- }
- }
- duplicateDmRoomIds.RemoveAll((x, y) => y.Count == 1);
- foreach (var (roomId, users) in duplicateDmRoomIds) {
- duplicateDmRooms.Add(dmRooms.First(x => x.Value.Any(x => x.Room.RoomId == roomId)).Value.First(x => x.Room.RoomId == roomId), users);
- }
-
- // StateHasChanged();
- Status = null;
- await base.OnParametersSetAsync();
- }
-
- private async Task Execute() {
- NavigationManager.NavigateTo("/User/DMSpace/Setup?stage=3");
- }
-
- private async Task<RoomInfo> GetRoomInfo(GenericRoom room) {
- var roomInfo = new RoomInfo() {
- Room = room
- };
- roomMembers[roomInfo] = new();
- roomInfo.CreationEventContent = await room.GetCreateEventAsync();
- try {
- roomInfo.RoomName = await room.GetNameAsync();
- }
- catch { }
-
- var membersEnum = room.GetMembersEnumerableAsync(true);
- await foreach (var member in membersEnum)
- if (member.TypedContent is RoomMemberEventContent memberEvent)
- roomMembers[roomInfo].Add(new() { DisplayName = memberEvent.DisplayName, AvatarUrl = memberEvent.AvatarUrl, Id = member.StateKey });
-
- if (string.IsNullOrWhiteSpace(roomInfo.RoomName) || roomInfo.RoomName == room.RoomId) {
- List<string> displayNames = new List<string>();
- foreach (var member in roomMembers[roomInfo])
- if (!string.IsNullOrWhiteSpace(member.DisplayName))
- displayNames.Add(member.DisplayName);
- roomInfo.RoomName = string.Join(", ", displayNames);
- }
- try {
- string? roomIcon = (await room.GetAvatarUrlAsync())?.Url;
- if (room is not null)
- roomInfo.RoomIcon = roomIcon;
- }
- catch { }
- return roomInfo;
- }
-
- private async Task<List<RoomInfo>> GetRoomInfoForRooms(List<GenericRoom> rooms) {
- var tasks = rooms.Select(GetRoomInfo).ToList();
- await Task.WhenAll(tasks);
- return tasks.Select(x => x.Result).ToList();
- }
-
- private async Task SetRoomAssignment(string roomId, string userId) {
- var hs = DmSpace.Homeserver;
- Status = "Loading DM list from account data...";
- var dms = await DmSpace.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
- Status = "Updating DM list from account data...";
-
- foreach (var (user, rooms) in dms) {
- rooms.RemoveAll(x => x == roomId);
- dms[user] = rooms.Distinct().ToList();
- }
- if(!dms.ContainsKey(userId))
- dms.Add(userId, new());
- dms[userId].Add(roomId);
- dms.RemoveAll((x, y) => y is {Count: 0});
- await DmSpace.Homeserver.SetAccountDataAsync("m.direct", dms);
-
- duplicateDmRooms.RemoveAll((x, y) => x.Room.RoomId == roomId);
- StateHasChanged();
- if (duplicateDmRooms.Count == 0) await OnParametersSetAsync();
- }
-
- private class UserProfileWithId : UserProfileResponse {
- [JsonIgnore]
- public string Id { get; set; }
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor b/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor
deleted file mode 100644
index 42573e6..0000000
--- a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor
+++ /dev/null
@@ -1,191 +0,0 @@
-@using LibMatrix.Homeservers
-@using LibMatrix.RoomTypes
-@using LibMatrix
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix.Responses
-@using MatrixRoomUtils.LibDMSpace
-@using MatrixRoomUtils.LibDMSpace.StateEvents
-@using ArcaneLibs.Extensions
-@using System.Text.Json.Serialization
-@using MatrixRoomUtils.Abstractions
-
-<b>
- <u>DM Space setup tool - stage 3: Preview space layout</u>
-</b>
-<p>This gives you a preview of how your settings would impact layout!</p>
-
-@if (!string.IsNullOrWhiteSpace(Status)) {
- <p>@Status</p>
-}
-
-@if (DmSpace is not null) {
- @if (dmSpaceInfo is not null && dmSpaceRoomInfo is not null) {
- <p>
- <InputCheckbox @bind-Value="dmSpaceInfo.LayerByUser"></InputCheckbox>
- Create sub-spaces per user
- </p>
- @if (!dmSpaceInfo.LayerByUser) {
- <RoomListItem RoomInfo="@dmSpaceRoomInfo"></RoomListItem>
- @foreach (var (userId, room) in dmRooms.OrderBy(x => x.Key.RoomName)) {
- @foreach (var roomInfo in room) {
- <div style="margin-left: 32px;">
- <RoomListItem RoomInfo="@roomInfo"></RoomListItem>
- </div>
- }
- }
- }
- else {
- <RoomListItem RoomInfo="@dmSpaceRoomInfo"></RoomListItem>
- @foreach (var (userId, room) in dmRooms.OrderBy(x => x.Key.RoomName)) {
- <div style="margin-left: 32px;">
- <RoomListItem RoomInfo="@userId"></RoomListItem>
- </div>
- @foreach (var roomInfo in room) {
- <div style="margin-left: 64px;">
- <RoomListItem RoomInfo="@roomInfo"></RoomListItem>
- </div>
- }
- }
- }
- }
- else {
- <b>Error: dmSpaceInfo is null!</b>
- }
-}
-else {
- <b>Error: DmSpaceConfiguration is null!</b>
-}
-
-<br/>
-<LinkButton OnClick="@Execute">Next</LinkButton>
-
-@code {
-
- private string? Status {
- get => _status;
- set {
- _status = value;
- StateHasChanged();
- }
- }
-
- private string? _status;
-
- [CascadingParameter]
- public DMSpace? DmSpace { get; set; }
-
- private Dictionary<RoomInfo, List<RoomInfo>> dmRooms { get; set; } = new();
- private DMSpaceInfo? dmSpaceInfo { get; set; }
- private RoomInfo? dmSpaceRoomInfo { get; set; }
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- }
-
- SemaphoreSlim _semaphore = new(1, 1);
-
- protected override async Task OnParametersSetAsync() {
- if (DmSpace is null)
- return;
- await _semaphore.WaitAsync();
- var hs = DmSpace.Homeserver;
- var dmSpaceRoom = new DMSpaceRoom(hs, DmSpace.DmSpaceConfiguration.DMSpaceId);
- dmSpaceRoomInfo = new() {
- RoomName = await dmSpaceRoom.GetNameAsync(),
- CreationEventContent = await dmSpaceRoom.GetCreateEventAsync(),
- RoomIcon = "mxc://feline.support/uUxBwaboPkMGtbZcAGZaIzpK",
- Room = dmSpaceRoom
- };
- dmSpaceInfo = await dmSpaceRoom.GetDmSpaceInfo();
- Status = "Loading DM list from account data...";
- var dms = await DmSpace.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
- dmRooms.Clear();
-
- Status = "DM list optimised, fetching info...";
- var results = dms.Select(async x => {
- var (userId, rooms) = x;
- UserProfileWithId userProfile;
- try {
- var profile = await DmSpace.Homeserver.GetProfileAsync(userId);
- userProfile = new() {
- AvatarUrl = profile.AvatarUrl,
- Id = userId,
- DisplayName = profile.DisplayName
- };
- }
- catch {
- userProfile = new() {
- AvatarUrl = "mxc://feline.support/uUxBwaboPkMGtbZcAGZaIzpK",
- DisplayName = userId,
- Id = userId
- };
- }
- var roomList = new List<RoomInfo>();
- var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncEnumerable();
- await foreach (var result in tasks)
- roomList.Add(result);
- return (userProfile, roomList);
- }).ToAsyncEnumerable();
- await foreach (var res in results) {
- dmRooms.Add(new RoomInfo() {
- Room = dmSpaceRoom,
- RoomIcon = res.userProfile.AvatarUrl,
- RoomName = res.userProfile.DisplayName,
- CreationEventContent = await dmSpaceRoom.GetCreateEventAsync()
- }, res.roomList);
- }
- _semaphore.Release();
- Status = null;
- await base.OnParametersSetAsync();
- }
-
- private async Task Execute() {
- var hs = DmSpace.Homeserver;
- var dmSpaceRoom = new DMSpaceRoom(hs, DmSpace.DmSpaceConfiguration.DMSpaceId);
- NavigationManager.NavigateTo("/User/DMSpace/Setup?stage=3");
- }
-
- private async Task<RoomInfo> GetRoomInfo(GenericRoom room) {
- var roomInfo = new RoomInfo() {
- Room = room
- };
- var roomMembers = new List<UserProfileWithId>();
- roomInfo.CreationEventContent = await room.GetCreateEventAsync();
- try {
- roomInfo.RoomName = await room.GetNameAsync();
- }
- catch { }
-
- var membersEnum = room.GetMembersEnumerableAsync(true);
- await foreach (var member in membersEnum)
- if (member.TypedContent is RoomMemberEventContent memberEvent)
- roomMembers.Add(new() { DisplayName = memberEvent.DisplayName, AvatarUrl = memberEvent.AvatarUrl, Id = member.StateKey });
-
- if (string.IsNullOrWhiteSpace(roomInfo.RoomName) || roomInfo.RoomName == room.RoomId) {
- List<string> displayNames = new List<string>();
- foreach (var member in roomMembers)
- if (!string.IsNullOrWhiteSpace(member.DisplayName))
- displayNames.Add(member.DisplayName);
- roomInfo.RoomName = string.Join(", ", displayNames);
- }
- try {
- string? roomIcon = (await room.GetAvatarUrlAsync())?.Url;
- if (room is not null)
- roomInfo.RoomIcon = roomIcon;
- }
- catch { }
- return roomInfo;
- }
-
- private async Task<List<RoomInfo>> GetRoomInfoForRooms(List<GenericRoom> rooms) {
- var tasks = rooms.Select(GetRoomInfo).ToList();
- await Task.WhenAll(tasks);
- return tasks.Select(x => x.Result).ToList();
- }
-
- private class UserProfileWithId : UserProfileResponse {
- [JsonIgnore]
- public string Id { get; set; }
- }
-
-}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/User/Profile.razor b/MatrixRoomUtils.Web/Pages/User/Profile.razor
deleted file mode 100644
index 73d7c6e..0000000
--- a/MatrixRoomUtils.Web/Pages/User/Profile.razor
+++ /dev/null
@@ -1,134 +0,0 @@
-@page "/User/Profile"
-@using LibMatrix.Homeservers
-@using LibMatrix.EventTypes.Spec.State
-@using ArcaneLibs.Extensions
-@using LibMatrix.Responses
-<h3>Manage Profile - @Homeserver?.WhoAmI?.UserId</h3>
-<hr/>
-
-@if (NewProfile is not null) {
- <h4>Profile</h4>
- <hr/>
- <div>
- <img src="@Homeserver.ResolveMediaUri(NewProfile.AvatarUrl)" style="width: 96px; height: 96px; border-radius: 50%; object-fit: cover;"/>
- <div style="display: inline-block; vertical-align: middle;">
- <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>
- </div>
- </div>
- @if (!string.IsNullOrWhiteSpace(Status)) {
- <p>@Status</p>
- }
-
- <br/>
-
- @* <details> *@
- <h4>Room profiles<hr></h4>
-
- @foreach (var (roomId, roomProfile) in RoomProfiles.OrderBy(x => RoomNames.TryGetValue(x.Key, out var _name) ? _name : x.Key)) {
- <details class="details-compact">
- <summary style="@(roomProfile.DisplayName == OldProfile.DisplayName && roomProfile.AvatarUrl == OldProfile.AvatarUrl ? "" : "#ffff0033")">@(RoomNames.TryGetValue(roomId, out var name) ? name : roomId)</summary>
- <img src="@Homeserver.ResolveMediaUri(roomProfile.AvatarUrl)" style="width: 96px; height: 96px; border-radius: 50%; object-fit: cover;"/>
- <div style="display: inline-block; vertical-align: middle;">
- <span>Display name: </span><FancyTextBox BackgroundColor="@(roomProfile.DisplayName == OldProfile.DisplayName ? "" : "#ffff0033")" @bind-Value="@roomProfile.DisplayName"></FancyTextBox><br/>
- <span>Avatar URL: </span><FancyTextBox BackgroundColor="@(roomProfile.AvatarUrl == OldProfile.AvatarUrl ? "" : "#ffff0033")" @bind-Value="@roomProfile.AvatarUrl"></FancyTextBox>
- <InputFile OnChange="@(ifcea => RoomAvatarChanged(ifcea, roomId))"></InputFile><br/>
- <LinkButton OnClick="@(() => UpdateRoomProfile(roomId))">Update profile</LinkButton>
- </div>
- <br/>
- @if (!string.IsNullOrWhiteSpace(Status)) {
- <p>@Status</p>
- }
- </details>
- <br/>
- }
- // </details>
-}
-
-@code {
- private string? _status = null;
-
- private AuthenticatedHomeserverGeneric? Homeserver { get; set; }
- private UserProfileResponse? NewProfile { get; set; }
- private UserProfileResponse? OldProfile { get; set; }
-
- private string? Status {
- get => _status;
- set {
- _status = value;
- StateHasChanged();
- }
- }
-
- private Dictionary<string, RoomMemberEventContent> RoomProfiles { get; set; } = new();
- private Dictionary<string, string> RoomNames { get; set; } = new();
-
- protected override async Task OnInitializedAsync() {
- Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
- if (Homeserver is null) return;
- Status = "Loading global profile...";
- if (Homeserver.WhoAmI?.UserId is null) return;
- NewProfile = (await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId)); //.DeepClone();
- OldProfile = (await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId)); //.DeepClone();
- Status = "Loading room profiles...";
- var roomProfiles = Homeserver.GetRoomProfilesAsync();
- await foreach (var (roomId, roomProfile) in roomProfiles) {
- // Status = $"Got profile for {roomId}...";
- RoomProfiles[roomId] = roomProfile; //.DeepClone();
- }
-
- StateHasChanged();
- Status = "Room profiles loaded, loading room names...";
-
- var roomNameTasks = RoomProfiles.Keys.Select(x => Homeserver.GetRoom(x)).Select(async x => {
- var name = await x.GetNameOrFallbackAsync();
- return new KeyValuePair<string, string?>(x.RoomId, name);
- }).ToAsyncEnumerable();
-
- await foreach (var (roomId, roomName) in roomNameTasks) {
- // Status = $"Got room name for {roomId}: {roomName}";
- RoomNames[roomId] = roomName;
- }
-
- StateHasChanged();
- Status = null;
-
- await base.OnInitializedAsync();
- }
-
- private async Task AvatarChanged(InputFileChangeEventArgs arg) {
- var res = await Homeserver.UploadFile(arg.File.Name, arg.File.OpenReadStream(Int64.MaxValue), arg.File.ContentType);
- Console.WriteLine(res);
- NewProfile.AvatarUrl = res;
- StateHasChanged();
- }
-
- private async Task UpdateProfile(bool restoreRoomProfiles = false) {
- Status = "Busy processing global profile update, please do not leave this page...";
- StateHasChanged();
- await Homeserver.UpdateProfileAsync(NewProfile, restoreRoomProfiles);
- Status = null;
- StateHasChanged();
- await OnInitializedAsync();
- }
-
- private async Task RoomAvatarChanged(InputFileChangeEventArgs arg, string roomId) {
- var res = await Homeserver.UploadFile(arg.File.Name, arg.File.OpenReadStream(Int64.MaxValue), arg.File.ContentType);
- Console.WriteLine(res);
- RoomProfiles[roomId].AvatarUrl = res;
- StateHasChanged();
- }
-
- private async Task UpdateRoomProfile(string roomId) {
- Status = "Busy processing room profile update, please do not leave this page...";
- StateHasChanged();
- var room = Homeserver.GetRoom(roomId);
- await room.SendStateEventAsync("m.room.member", Homeserver.WhoAmI.UserId, RoomProfiles[roomId]);
- Status = null;
- StateHasChanged();
- }
-
-}
\ No newline at end of file
|