From bb8c2637af3b7982e7a4b2fd15e2fbec613d0848 Mon Sep 17 00:00:00 2001 From: TheArcaneBrony Date: Fri, 30 Jun 2023 03:36:58 +0200 Subject: Todays progress --- MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs | 10 +- MatrixRoomUtils.Core/Attributes/TraceAttribute.cs | 10 - MatrixRoomUtils.Core/AuthenticatedHomeServer.cs | 8 +- MatrixRoomUtils.Core/Authentication/MatrixAuth.cs | 35 -- .../Extensions/HttpClientExtensions.cs | 21 +- MatrixRoomUtils.Core/Helpers/MediaResolver.cs | 6 + MatrixRoomUtils.Core/Helpers/SyncHelper.cs | 4 +- MatrixRoomUtils.Core/Interfaces/IHomeServer.cs | 42 +- .../Interfaces/Services/IStorageProvider.cs | 28 +- MatrixRoomUtils.Core/RemoteHomeServer.cs | 1 + MatrixRoomUtils.Core/Responses/LoginResponse.cs | 2 +- MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs | 226 +++---- MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs | 14 +- .../Services/HomeserverProviderService.cs | 57 +- .../Services/HomeserverResolverService.cs | 14 +- MatrixRoomUtils.Core/Services/ServiceInstaller.cs | 4 +- MatrixRoomUtils.Core/StateEvent.cs | 4 +- .../Common/MjolnirShortcodeEventData.cs | 11 + .../StateEventTypes/Spec/RoomAliasEventData.cs | 11 + MatrixRoomUtils.Web/App.razor | 12 - .../Classes/LocalStorageProviderService.cs | 18 +- MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs | 84 --- MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs | 91 +++ .../DefaultRoomCreationTemplate.cs | 19 +- MatrixRoomUtils.Web/Pages/DataExportPage.razor | 67 -- MatrixRoomUtils.Web/Pages/DebugTools.razor | 13 +- MatrixRoomUtils.Web/Pages/DevOptions.razor | 62 +- MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor | 4 +- MatrixRoomUtils.Web/Pages/Index.razor | 67 +- .../Pages/KnownHomeserverList.razor | 112 ++-- MatrixRoomUtils.Web/Pages/LoginPage.razor | 52 +- MatrixRoomUtils.Web/Pages/MediaLocator.razor | 5 +- MatrixRoomUtils.Web/Pages/ModalTest.razor | 14 - .../Pages/PolicyList/PolicyListEditorPage.razor | 95 ++- .../Pages/PolicyList/PolicyListRoomList.razor | 39 +- .../Pages/RoomManager/RoomManager.razor | 21 +- .../Pages/RoomManager/RoomManagerCreateRoom.razor | 678 ++++++++++----------- .../Pages/RoomManager/RoomManagerSpace.razor | 71 ++- .../Pages/RoomManager/RoomManagerTimeline.razor | 7 +- .../Pages/RoomState/RoomStateEditorPage.razor | 42 +- .../Pages/RoomState/RoomStateRoomList.razor | 9 +- .../Pages/RoomState/RoomStateViewerPage.razor | 64 +- MatrixRoomUtils.Web/Pages/Rooms/Index.razor | 13 +- MatrixRoomUtils.Web/Program.cs | 2 +- .../Shared/IndexComponents/IndexUserItem.razor | 54 -- MatrixRoomUtils.Web/Shared/InlineUserItem.razor | 24 +- MatrixRoomUtils.Web/Shared/LogView.razor | 83 ++- MatrixRoomUtils.Web/Shared/MainLayout.razor | 16 +- MatrixRoomUtils.Web/Shared/NavMenu.razor | 5 - MatrixRoomUtils.Web/Shared/PortableDevTools.razor | 52 +- MatrixRoomUtils.Web/Shared/RoomList.razor | 17 +- .../RoomListComponents/RoomListCategory.razor | 4 +- .../Shared/RoomListComponents/RoomListSpace.razor | 4 +- MatrixRoomUtils.Web/Shared/RoomListItem.razor | 60 +- .../TimelineComponents/TimelineMemberItem.razor | 23 +- .../TimelineComponents/TimelineMessageItem.razor | 2 +- MatrixRoomUtils.Web/Shared/UserListItem.razor | 15 +- MatrixRoomUtils.Web/_Imports.razor | 19 +- .../wwwroot/css/bootstrap/bootstrap.min.css.map | 2 +- MatrixRoomUtils.sln.DotSettings.user | 1 + 60 files changed, 1198 insertions(+), 1352 deletions(-) delete mode 100644 MatrixRoomUtils.Core/Attributes/TraceAttribute.cs delete mode 100644 MatrixRoomUtils.Core/Authentication/MatrixAuth.cs create mode 100644 MatrixRoomUtils.Core/Helpers/MediaResolver.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Common/MjolnirShortcodeEventData.cs create mode 100644 MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAliasEventData.cs delete mode 100644 MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs create mode 100644 MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs delete mode 100644 MatrixRoomUtils.Web/Pages/DataExportPage.razor delete mode 100644 MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor diff --git a/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs b/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs index 0061c1f..8b9e726 100644 --- a/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs +++ b/MatrixRoomUtils.Bot/Bot/FileStorageProvider.cs @@ -23,13 +23,13 @@ public class FileStorageProvider : IStorageProvider { } } - public async Task SaveObject(string key, T value) => await File.WriteAllTextAsync(Path.Join(TargetPath, key), ObjectExtensions.ToJson(value)); + public async Task SaveObjectAsync(string key, T value) => await File.WriteAllTextAsync(Path.Join(TargetPath, key), ObjectExtensions.ToJson(value)); - public async Task LoadObject(string key) => JsonSerializer.Deserialize(await File.ReadAllTextAsync(Path.Join(TargetPath, key))); + public async Task LoadObjectAsync(string key) => JsonSerializer.Deserialize(await File.ReadAllTextAsync(Path.Join(TargetPath, key))); - public async Task ObjectExists(string key) => File.Exists(Path.Join(TargetPath, key)); + public async Task ObjectExistsAsync(string key) => File.Exists(Path.Join(TargetPath, key)); - public async Task> GetAllKeys() => Directory.GetFiles(TargetPath).Select(Path.GetFileName).ToList(); + public async Task> GetAllKeysAsync() => Directory.GetFiles(TargetPath).Select(Path.GetFileName).ToList(); - public async Task DeleteObject(string key) => File.Delete(Path.Join(TargetPath, key)); + public async Task DeleteObjectAsync(string key) => File.Delete(Path.Join(TargetPath, key)); } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Attributes/TraceAttribute.cs b/MatrixRoomUtils.Core/Attributes/TraceAttribute.cs deleted file mode 100644 index 34a0b67..0000000 --- a/MatrixRoomUtils.Core/Attributes/TraceAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace MatrixRoomUtils.Core.Attributes; - -[AttributeUsage(AttributeTargets.All)] -public class TraceAttribute : Attribute { - public TraceAttribute([CallerMemberName] string callerName = "") { - Console.WriteLine($"{callerName} called!"); - } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs index 10bbb36..09da766 100644 --- a/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs +++ b/MatrixRoomUtils.Core/AuthenticatedHomeServer.cs @@ -28,7 +28,7 @@ public class AuthenticatedHomeServer : IHomeServer { } public WhoAmIResponse WhoAmI { get; set; } = null!; - public string UserId { get; } + public string UserId => WhoAmI.UserId; public string AccessToken { get; set; } @@ -80,7 +80,7 @@ public class AuthenticatedHomeServer : IHomeServer { var url = $"/_synapse/admin/v1/rooms?limit={Math.Min(limit, 100)}&dir={dir}&order_by={orderBy}"; if (!string.IsNullOrEmpty(searchTerm)) url += $"&search_term={searchTerm}"; - if (res?.NextBatch != null) url += $"&from={res.NextBatch}"; + if (res?.NextBatch is not null) url += $"&from={res.NextBatch}"; Console.WriteLine($"--- ADMIN Querying Room List with URL: {url} - Already have {i} items... ---"); @@ -88,7 +88,7 @@ public class AuthenticatedHomeServer : IHomeServer { totalRooms ??= res?.TotalRooms; Console.WriteLine(res.ToJson(false)); foreach (var room in res.Rooms) { - if (localFilter != null) { + if (localFilter is not null) { if (!room.RoomId.Contains(localFilter.RoomIdContains)) { totalRooms--; continue; @@ -144,7 +144,7 @@ public class AuthenticatedHomeServer : IHomeServer { continue; } } - // if (contentSearch != null && !string.IsNullOrEmpty(contentSearch) && + // if (contentSearch is not null && !string.IsNullOrEmpty(contentSearch) && // !( // room.Name?.Contains(contentSearch, StringComparison.InvariantCultureIgnoreCase) == true || // room.CanonicalAlias?.Contains(contentSearch, StringComparison.InvariantCultureIgnoreCase) == true || diff --git a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs b/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs deleted file mode 100644 index 28e0c49..0000000 --- a/MatrixRoomUtils.Core/Authentication/MatrixAuth.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Net.Http.Json; -using System.Text.Json; -using MatrixRoomUtils.Core.Extensions; -using MatrixRoomUtils.Core.Responses; -using MatrixRoomUtils.Core.StateEventTypes; - -namespace MatrixRoomUtils.Core.Authentication; - -public class MatrixAuth { - [Obsolete("This is possibly broken and should not be used.", true)] - public static async Task Login(string homeserver, string username, string password) { - Console.WriteLine($"Logging in to {homeserver} as {username}..."); - homeserver = (new RemoteHomeServer(homeserver)).FullHomeServerDomain; - var hc = new MatrixHttpClient(); - var payload = new { - type = "m.login.password", - identifier = new { - type = "m.id.user", - user = username - }, - password, - initial_device_display_name = "Rory&::MatrixRoomUtils" - }; - Console.WriteLine($"Sending login request to {homeserver}..."); - var resp = await hc.PostAsJsonAsync($"{homeserver}/_matrix/client/v3/login", payload); - Console.WriteLine($"Login: {resp.StatusCode}"); - var data = await resp.Content.ReadFromJsonAsync(); - if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data); - - Console.WriteLine($"Login: {data.ToJson()}"); - return data.Deserialize(); - //var token = data.GetProperty("access_token").GetString(); - //return token; - } -} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs index 47b3121..852e1d8 100644 --- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs +++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs @@ -1,3 +1,4 @@ +using System.Reflection; using System.Text.Json; namespace MatrixRoomUtils.Core.Extensions; @@ -18,23 +19,35 @@ public static class HttpClientExtensions { public class MatrixHttpClient : HttpClient { public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + try + { + HttpRequestOptionsKey WebAssemblyEnableStreamingResponseKey = new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"); + request.Options.Set(WebAssemblyEnableStreamingResponseKey, true); + // var asm = Assembly.Load("Microsoft.AspNetCore.Components.WebAssembly"); + // var browserHttpHandlerType = asm.GetType("Microsoft.AspNetCore.Components.WebAssembly.Http.WebAssemblyHttpRequestMessageExtensions", true); + // var browserHttpHandlerMethod = browserHttpHandlerType.GetMethod("SetBrowserResponseStreamingEnabled", BindingFlags.Public | BindingFlags.Static); + // browserHttpHandlerMethod?.Invoke(null, new object[] {request, true}); + } + catch (Exception e) + { + Console.WriteLine("Failed to set browser response streaming:"); + Console.WriteLine(e); + } var a = await base.SendAsync(request, cancellationToken); if (!a.IsSuccessStatusCode) { Console.WriteLine($"Failed to send request: {a.StatusCode}"); var content = await a.Content.ReadAsStringAsync(cancellationToken); if (content.StartsWith('{')) { var ex = JsonSerializer.Deserialize(content); - if (ex?.RetryAfterMs != null) { + if (ex?.RetryAfterMs is not null) { await Task.Delay(ex.RetryAfterMs.Value, cancellationToken); + typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(request, 0); return await SendAsync(request, cancellationToken); } - throw ex!; } - throw new InvalidDataException("Encountered invalid data:\n" + content); } - return a; } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Helpers/MediaResolver.cs b/MatrixRoomUtils.Core/Helpers/MediaResolver.cs new file mode 100644 index 0000000..0869135 --- /dev/null +++ b/MatrixRoomUtils.Core/Helpers/MediaResolver.cs @@ -0,0 +1,6 @@ +namespace MatrixRoomUtils.Core.Helpers; + +public class MediaResolver { + public static string ResolveMediaUri(string homeserver, string mxc) => + mxc.Replace("mxc://", $"{homeserver}/_matrix/media/v3/download/"); +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Helpers/SyncHelper.cs b/MatrixRoomUtils.Core/Helpers/SyncHelper.cs index 1b9c598..eff412b 100644 --- a/MatrixRoomUtils.Core/Helpers/SyncHelper.cs +++ b/MatrixRoomUtils.Core/Helpers/SyncHelper.cs @@ -20,7 +20,7 @@ public class SyncHelper { public async Task Sync(string? since = null, CancellationToken? cancellationToken = null) { var outFileName = "sync-" + - (await _storageService.CacheStorageProvider.GetAllKeys()).Count(x => x.StartsWith("sync")) + + (await _storageService.CacheStorageProvider.GetAllKeysAsync()).Count(x => x.StartsWith("sync")) + ".json"; var url = "/_matrix/client/v3/sync?timeout=30000&set_presence=online"; if (!string.IsNullOrWhiteSpace(since)) url += $"&since={since}"; @@ -29,7 +29,7 @@ public class SyncHelper { try { var res = await _homeServer._httpClient.GetFromJsonAsync(url, cancellationToken: cancellationToken ?? CancellationToken.None); - await _storageService.CacheStorageProvider.SaveObject(outFileName, res); + await _storageService.CacheStorageProvider.SaveObjectAsync(outFileName, res); Console.WriteLine($"Wrote file: {outFileName}"); return res; } diff --git a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs index 4fa6c1b..4ee2a3e 100644 --- a/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs +++ b/MatrixRoomUtils.Core/Interfaces/IHomeServer.cs @@ -6,34 +6,36 @@ using MatrixRoomUtils.Core.StateEventTypes; namespace MatrixRoomUtils.Core.Interfaces; public class IHomeServer { - private readonly Dictionary _profileCache = new(); + private readonly Dictionary _profileCache = new(); public string HomeServerDomain { get; set; } public string FullHomeServerDomain { get; set; } protected internal MatrixHttpClient _httpClient { get; set; } = new(); - public async Task GetProfile(string mxid, bool debounce = false, bool cache = true) { - if (cache) { - if (debounce) await Task.Delay(Random.Shared.Next(100, 500)); - if (_profileCache.ContainsKey(mxid)) { - while (_profileCache[mxid] == null) { - Console.WriteLine($"Waiting for profile cache for {mxid}, currently {_profileCache[mxid]?.ToJson() ?? "null"} within {_profileCache.Count} profiles..."); - await Task.Delay(Random.Shared.Next(50, 500)); - } - - return _profileCache[mxid]; - } + // if (cache) { + // if (debounce) await Task.Delay(Random.Shared.Next(100, 500)); + // if (_profileCache.ContainsKey(mxid)) { + // while (_profileCache[mxid] == null) { + // Console.WriteLine($"Waiting for profile cache for {mxid}, currently {_profileCache[mxid]?.ToJson() ?? "null"} within {_profileCache.Count} profiles..."); + // await Task.Delay(Random.Shared.Next(50, 500)); + // } + // + // return _profileCache[mxid]; + // } + // } + if(mxid is null) throw new ArgumentNullException(nameof(mxid)); + if (_profileCache.ContainsKey(mxid)) { + if (_profileCache[mxid] is SemaphoreSlim s) await s.WaitAsync(); + if (_profileCache[mxid] is ProfileResponse p) return p; } - - _profileCache.Add(mxid, null); + _profileCache[mxid] = new SemaphoreSlim(1); + var resp = await _httpClient.GetAsync($"/_matrix/client/v3/profile/{mxid}"); - var data = await resp.Content.ReadFromJsonAsync(); + var data = await resp.Content.ReadFromJsonAsync(); if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data); - var profile = data.Deserialize(); - _profileCache[mxid] = profile; - return profile; + _profileCache[mxid] = data; + + return data; } - - public string? ResolveMediaUri(string mxc) => mxc.Replace("mxc://", $"{FullHomeServerDomain}/_matrix/media/v3/download/"); } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs b/MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs index 01314da..eefb79c 100644 --- a/MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs +++ b/MatrixRoomUtils.Core/Interfaces/Services/IStorageProvider.cs @@ -2,45 +2,45 @@ namespace MatrixRoomUtils.Core.Interfaces.Services; public interface IStorageProvider { // save all children of a type with reflection - public Task SaveAllChildren(string key, T value) { + public Task SaveAllChildrenAsync(string key, T value) { Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement SaveAllChildren(key, value)!"); - return Task.CompletedTask; + throw new NotImplementedException(); } // load all children of a type with reflection - public Task LoadAllChildren(string key) { + public Task LoadAllChildrenAsync(string key) { Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement LoadAllChildren(key)!"); - return Task.FromResult(default(T)); + throw new NotImplementedException(); } - public Task SaveObject(string key, T value) { + public Task SaveObjectAsync(string key, T value) { Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement SaveObject(key, value)!"); - return Task.CompletedTask; + throw new NotImplementedException(); } // load - public Task LoadObject(string key) { + public Task LoadObjectAsync(string key) { Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement LoadObject(key)!"); - return Task.FromResult(default(T)); + throw new NotImplementedException(); } // check if exists - public Task ObjectExists(string key) { + public Task ObjectExistsAsync(string key) { Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement ObjectExists(key)!"); - return Task.FromResult(false); + throw new NotImplementedException(); } // get all keys - public Task> GetAllKeys() { + public Task> GetAllKeysAsync() { Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement GetAllKeys()!"); - return Task.FromResult(new List()); + throw new NotImplementedException(); } // delete - public Task DeleteObject(string key) { + public Task DeleteObjectAsync(string key) { Console.WriteLine($"StorageProvider<{GetType().Name}> does not implement DeleteObject(key)!"); - return Task.CompletedTask; + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/RemoteHomeServer.cs b/MatrixRoomUtils.Core/RemoteHomeServer.cs index 9c5f2a3..e6c28c3 100644 --- a/MatrixRoomUtils.Core/RemoteHomeServer.cs +++ b/MatrixRoomUtils.Core/RemoteHomeServer.cs @@ -2,6 +2,7 @@ using System.Net.Http.Json; using System.Text.Json; using MatrixRoomUtils.Core.Extensions; using MatrixRoomUtils.Core.Interfaces; +using MatrixRoomUtils.Core.Services; namespace MatrixRoomUtils.Core; diff --git a/MatrixRoomUtils.Core/Responses/LoginResponse.cs b/MatrixRoomUtils.Core/Responses/LoginResponse.cs index b248739..239ea03 100644 --- a/MatrixRoomUtils.Core/Responses/LoginResponse.cs +++ b/MatrixRoomUtils.Core/Responses/LoginResponse.cs @@ -10,7 +10,7 @@ public class LoginResponse { public string DeviceId { get; set; } [JsonPropertyName("home_server")] - public string HomeServer { get; set; } + public string Homeserver { get; set; } [JsonPropertyName("user_id")] public string UserId { get; set; } diff --git a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs index 8dc30d1..f57c855 100644 --- a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs +++ b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs @@ -1,15 +1,17 @@ using System.Net.Http.Json; -using System.Text; using System.Text.Json; using System.Web; using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Responses; using MatrixRoomUtils.Core.RoomTypes; +using MatrixRoomUtils.Core.StateEventTypes; +using Microsoft.Extensions.Logging; namespace MatrixRoomUtils.Core; public class GenericRoom { internal readonly AuthenticatedHomeServer _homeServer; - internal readonly HttpClient _httpClient; + internal readonly MatrixHttpClient _httpClient; public GenericRoom(AuthenticatedHomeServer homeServer, string roomId) { _homeServer = homeServer; @@ -21,51 +23,41 @@ public class GenericRoom { public string RoomId { get; set; } - public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = true) { + [Obsolete("", true)] + public async Task GetStateAsync(string type, string stateKey = "") { var url = $"/_matrix/client/v3/rooms/{RoomId}/state"; if (!string.IsNullOrEmpty(type)) url += $"/{type}"; if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}"; + return await _httpClient.GetFromJsonAsync(url); + } - var res = await _httpClient.GetAsync(url); - if (!res.IsSuccessStatusCode) { - if (logOnFailure) Console.WriteLine($"{RoomId}/{stateKey}/{type} - got status: {res.StatusCode}"); - return null; + public async IAsyncEnumerable GetFullStateAsync() { + var res = await _httpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/state"); + var result = + JsonSerializer.DeserializeAsyncEnumerable(await res.Content.ReadAsStreamAsync()); + await foreach (var resp in result) { + yield return resp; } - - var result = await res.Content.ReadFromJsonAsync(); - return result; } - public async Task GetStateAsync(string type, string stateKey = "", bool logOnFailure = false) { - var res = await GetStateAsync(type, stateKey, logOnFailure); - if (res == null) return default; - return res.Value.Deserialize(); + public async Task GetStateAsync(string type, string stateKey = "") { + var url = $"/_matrix/client/v3/rooms/{RoomId}/state"; + if (!string.IsNullOrEmpty(type)) url += $"/{type}"; + if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}"; + return await _httpClient.GetFromJsonAsync(url); } public async Task GetMessagesAsync(string from = "", int limit = 10, string dir = "b", string filter = "") { var url = $"/_matrix/client/v3/rooms/{RoomId}/messages?from={from}&limit={limit}&dir={dir}"; if (!string.IsNullOrEmpty(filter)) url += $"&filter={filter}"; - var res = await _httpClient.GetAsync(url); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to get messages for {RoomId} - got status: {res.StatusCode}"); - } - - var result = await res.Content.ReadFromJsonAsync(); - return result ?? new MessagesResponse(); + var res = await _httpClient.GetFromJsonAsync(url); + return res ?? new MessagesResponse(); } public async Task GetNameAsync() { - var res = await GetStateAsync("m.room.name"); - if (!res.HasValue) { - Console.WriteLine($"Room {RoomId} has no name!"); - return RoomId; - } - - var resn = res?.TryGetProperty("name", out var name) ?? false ? name.GetString() ?? RoomId : RoomId; - //Console.WriteLine($"Got name: {resn}"); - return resn; + var res = await GetStateAsync("m.room.name"); + return res.Name ?? RoomId; } public async Task JoinAsync(string[]? homeservers = null, string? reason = null) { @@ -78,165 +70,89 @@ public class GenericRoom { }); } - public async Task> GetMembersAsync(bool joinedOnly = true) { - var res = await GetStateAsync(""); - if (!res.HasValue) return new List(); - var members = new List(); - foreach (var member in res.Value.EnumerateArray()) { - if (member.GetProperty("type").GetString() != "m.room.member") continue; - if (joinedOnly && member.GetProperty("content").GetProperty("membership").GetString() != "join") continue; - var memberId = member.GetProperty("state_key").GetString(); - members.Add( - memberId ?? throw new InvalidOperationException("Event type was member but state key was null!")); + public async IAsyncEnumerable GetMembersAsync(bool joinedOnly = true) { + var res = GetFullStateAsync(); + await foreach (var member in res) { + if (member.Type != "m.room.member") continue; + if (joinedOnly && (member.TypedContent as RoomMemberEventData).Membership is not "join") continue; + yield return member; } - - return members; } public async Task> GetAliasesAsync() { - var res = await GetStateAsync("m.room.aliases"); - if (!res.HasValue) return new List(); - var aliases = new List(); - foreach (var alias in res.Value.GetProperty("aliases").EnumerateArray()) aliases.Add(alias.GetString() ?? ""); - - return aliases; + var res = await GetStateAsync("m.room.aliases"); + return res.Aliases; } - public async Task GetCanonicalAliasAsync() { - var res = await GetStateAsync("m.room.canonical_alias"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("alias").GetString() ?? ""; - } - - public async Task GetTopicAsync() { - var res = await GetStateAsync("m.room.topic"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("topic").GetString() ?? ""; - } - - public async Task GetAvatarUrlAsync() { - var res = await GetStateAsync("m.room.avatar"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("url").GetString() ?? ""; - } + public async Task GetCanonicalAliasAsync() => + await GetStateAsync("m.room.canonical_alias"); - public async Task GetJoinRuleAsync() { - var res = await GetStateAsync("m.room.join_rules"); - if (!res.HasValue) return new JoinRulesEventData(); - return res.Value.Deserialize() ?? new JoinRulesEventData(); - } + public async Task GetTopicAsync() => + await GetStateAsync("m.room.topic"); - public async Task GetHistoryVisibilityAsync() { - var res = await GetStateAsync("m.room.history_visibility"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("history_visibility").GetString() ?? ""; - } + public async Task GetAvatarUrlAsync() => + await GetStateAsync("m.room.avatar"); - public async Task GetGuestAccessAsync() { - var res = await GetStateAsync("m.room.guest_access"); - if (!res.HasValue) return ""; - return res.Value.GetProperty("guest_access").GetString() ?? ""; - } + public async Task GetJoinRuleAsync() => + await GetStateAsync("m.room.join_rules"); - public async Task GetCreateEventAsync() { - var res = await GetStateAsync("m.room.create"); - if (!res.HasValue) return new CreateEvent(); + public async Task GetHistoryVisibilityAsync() => + await GetStateAsync("m.room.history_visibility"); - res.FindExtraJsonElementFields(typeof(CreateEvent)); + public async Task GetGuestAccessAsync() => + await GetStateAsync("m.room.guest_access"); - return res.Value.Deserialize() ?? new CreateEvent(); - } + public async Task GetCreateEventAsync() => + await GetStateAsync("m.room.create"); public async Task GetRoomType() { - var res = await GetStateAsync("m.room.create"); - if (!res.HasValue) return null; - if (res.Value.TryGetProperty("type", out var type)) return type.GetString(); - return null; + var res = await GetStateAsync("m.room.create"); + return res.Type; } - public async Task ForgetAsync() { - var res = await _httpClient.PostAsync($"/_matrix/client/v3/rooms/{RoomId}/forget", null); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to forget room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to forget room {RoomId} - got status: {res.StatusCode}"); - } - } + public async Task ForgetAsync() => + await _httpClient.PostAsync($"/_matrix/client/v3/rooms/{RoomId}/forget", null); - public async Task LeaveAsync(string? reason = null) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/leave", new { + public async Task LeaveAsync(string? reason = null) => + await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/leave", new { reason }); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to leave room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to leave room {RoomId} - got status: {res.StatusCode}"); - } - } - public async Task KickAsync(string userId, string? reason = null) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/kick", + public async Task KickAsync(string userId, string? reason = null) => + await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/kick", new UserIdAndReason() { UserId = userId, Reason = reason }); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to kick {userId} from room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to kick {userId} from room {RoomId} - got status: {res.StatusCode}"); - } - } - public async Task BanAsync(string userId, string? reason = null) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/ban", + public async Task BanAsync(string userId, string? reason = null) => + await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/ban", new UserIdAndReason() { UserId = userId, Reason = reason }); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to ban {userId} from room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to ban {userId} from room {RoomId} - got status: {res.StatusCode}"); - } - } - public async Task UnbanAsync(string userId) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/unban", + public async Task UnbanAsync(string userId) => + await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/unban", new UserIdAndReason() { UserId = userId }); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to unban {userId} from room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to unban {userId} from room {RoomId} - got status: {res.StatusCode}"); - } - } - - public async Task SendStateEventAsync(string eventType, object content) { - var res = await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state/{eventType}", content); - if (!res.IsSuccessStatusCode) { - Console.WriteLine( - $"Failed to send state event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - throw new Exception( - $"Failed to send state event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - } - return await res.Content.ReadFromJsonAsync(); - } + public async Task SendStateEventAsync(string eventType, object content) => + await (await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state/{eventType}", content)) + .Content.ReadFromJsonAsync(); public async Task SendMessageEventAsync(string eventType, MessageEventData content) { - var url = $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(); - var res = await _httpClient.PutAsJsonAsync(url, content); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - } - + var res = await _httpClient.PutAsJsonAsync( + $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(), content); var resu = await res.Content.ReadFromJsonAsync(); - return resu; } public async Task SendFileAsync(string eventType, string fileName, Stream fileStream) { - var url = $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(); var content = new MultipartFormDataContent(); content.Add(new StreamContent(fileStream), "file", fileName); - var res = await _httpClient.PutAsync(url, content); - if (!res.IsSuccessStatusCode) { - Console.WriteLine($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - throw new Exception($"Failed to send event {eventType} to room {RoomId} - got status: {res.StatusCode}"); - } - - var resu = await res.Content.ReadFromJsonAsync(); - - return resu; + var res = await + ( + await _httpClient.PutAsync( + $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(), + content + ) + ) + .Content.ReadFromJsonAsync(); + return res; } public readonly SpaceRoom AsSpace; diff --git a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs index 6b586c7..3be3130 100644 --- a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs +++ b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs @@ -8,20 +8,18 @@ namespace MatrixRoomUtils.Core.RoomTypes; public class SpaceRoom : GenericRoom { private readonly AuthenticatedHomeServer _homeServer; private readonly GenericRoom _room; + public SpaceRoom(AuthenticatedHomeServer homeServer, string roomId) : base(homeServer, roomId) { _homeServer = homeServer; } public async Task> GetRoomsAsync(bool includeRemoved = false) { var rooms = new List(); - var state = await GetStateAsync(""); - if (state != null) { - var states = state.Value.Deserialize()!; - foreach (var stateEvent in states.Where(x => x.Type == "m.space.child")) { - var roomId = stateEvent.StateKey; - if(stateEvent.TypedContent.ToJson() != "{}" || includeRemoved) - rooms.Add(await _homeServer.GetRoom(roomId)); - } + var state = GetFullStateAsync().ToBlockingEnumerable().ToList(); + var childStates = state.Where(x => x.Type == "m.space.child"); + foreach (var stateEvent in childStates) { + if (stateEvent.TypedContent.ToJson() != "{}" || includeRemoved) + rooms.Add(await _homeServer.GetRoom(stateEvent.StateKey)); } return rooms; diff --git a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs index 8a22d33..870e0d4 100644 --- a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs +++ b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs @@ -1,7 +1,11 @@ using System.Net.Http.Headers; using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Responses; using Microsoft.Extensions.Logging; + namespace MatrixRoomUtils.Core.Services; public class HomeserverProviderService { @@ -9,7 +13,8 @@ public class HomeserverProviderService { private readonly ILogger _logger; private readonly HomeserverResolverService _homeserverResolverService; - public HomeserverProviderService(TieredStorageService tieredStorageService, ILogger logger, HomeserverResolverService homeserverResolverService) { + public HomeserverProviderService(TieredStorageService tieredStorageService, + ILogger logger, HomeserverResolverService homeserverResolverService) { Console.WriteLine("Homeserver provider service instantiated!"); _tieredStorageService = tieredStorageService; _logger = logger; @@ -18,9 +23,11 @@ public class HomeserverProviderService { $"New HomeserverProviderService created with TieredStorageService<{string.Join(", ", tieredStorageService.GetType().GetProperties().Select(x => x.Name))}>!"); } - public async Task GetAuthenticatedWithToken(string homeserver, string accessToken) { + public async Task GetAuthenticatedWithToken(string homeserver, string accessToken, + string? overrideFullDomain = null) { var hs = new AuthenticatedHomeServer(_tieredStorageService, homeserver, accessToken); - hs.FullHomeServerDomain = await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver); + hs.FullHomeServerDomain = overrideFullDomain ?? + await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver); hs._httpClient.Dispose(); hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) }; hs._httpClient.Timeout = TimeSpan.FromSeconds(5); @@ -29,4 +36,48 @@ public class HomeserverProviderService { hs.WhoAmI = (await hs._httpClient.GetFromJsonAsync("/_matrix/client/v3/account/whoami"))!; return hs; } + + public async Task GetRemoteHomeserver(string homeserver, string? overrideFullDomain = null) { + var hs = new RemoteHomeServer(homeserver); + hs.FullHomeServerDomain = overrideFullDomain ?? + await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver); + hs._httpClient.Dispose(); + hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) }; + hs._httpClient.Timeout = TimeSpan.FromSeconds(5); + return hs; + } + + public async Task Login(string homeserver, string user, string password, + string? overrideFullDomain = null) { + var hs = await GetRemoteHomeserver(homeserver, overrideFullDomain); + var payload = new LoginRequest { + Identifier = new() { User = user }, + Password = password + }; + var resp = await hs._httpClient.PostAsJsonAsync("/_matrix/client/v3/login", payload); + var data = await resp.Content.ReadFromJsonAsync(); + return data!; + } + + private class LoginRequest { + [JsonPropertyName("type")] + public string Type { get; set; } = "m.login.password"; + + [JsonPropertyName("identifier")] + public LoginIdentifier Identifier { get; set; } = new(); + + [JsonPropertyName("password")] + public string Password { get; set; } = ""; + + [JsonPropertyName("initial_device_display_name")] + public string InitialDeviceDisplayName { get; set; } = "Rory&::LibMatrix"; + + public class LoginIdentifier { + [JsonPropertyName("type")] + public string Type { get; set; } = "m.id.user"; + + [JsonPropertyName("user")] + public string User { get; set; } = ""; + } + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Core/Services/HomeserverResolverService.cs b/MatrixRoomUtils.Core/Services/HomeserverResolverService.cs index f6363ab..526a261 100644 --- a/MatrixRoomUtils.Core/Services/HomeserverResolverService.cs +++ b/MatrixRoomUtils.Core/Services/HomeserverResolverService.cs @@ -6,11 +6,12 @@ using Microsoft.Extensions.Logging; namespace MatrixRoomUtils.Core.Services; public class HomeserverResolverService { - private readonly HttpClient _httpClient; + private readonly MatrixHttpClient _httpClient = new MatrixHttpClient(); private readonly ILogger _logger; - public HomeserverResolverService(HttpClient httpClient, ILogger logger) { - _httpClient = httpClient; + private static Dictionary _wellKnownCache = new(); + + public HomeserverResolverService(ILogger logger) { _logger = logger; } @@ -22,6 +23,12 @@ public class HomeserverResolverService { } private async Task _resolveHomeserverFromWellKnown(string homeserver) { + if(homeserver is null) throw new ArgumentNullException(nameof(homeserver)); + if (_wellKnownCache.ContainsKey(homeserver)) { + if (_wellKnownCache[homeserver] is SemaphoreSlim s) await s.WaitAsync(); + if (_wellKnownCache[homeserver] is string p) return p; + } + _wellKnownCache[homeserver] = new SemaphoreSlim(1); string? result = null; _logger.LogInformation($"Attempting to resolve homeserver: {homeserver}"); if (!homeserver.StartsWith("http")) homeserver = "https://" + homeserver; @@ -34,6 +41,7 @@ public class HomeserverResolverService { if(result is not null) { _logger.LogInformation($"Resolved homeserver: {homeserver} -> {result}"); + _wellKnownCache.TryAdd(homeserver, result); return result; } diff --git a/MatrixRoomUtils.Core/Services/ServiceInstaller.cs b/MatrixRoomUtils.Core/Services/ServiceInstaller.cs index 4a831c1..ef9238d 100644 --- a/MatrixRoomUtils.Core/Services/ServiceInstaller.cs +++ b/MatrixRoomUtils.Core/Services/ServiceInstaller.cs @@ -9,14 +9,14 @@ public static class ServiceInstaller { if (!services.Any(x => x.ServiceType == typeof(TieredStorageService))) throw new Exception("[MRUCore/DI] No TieredStorageService has been registered!"); //Add config - if(config != null) + if(config is not null) services.AddSingleton(config); else { services.AddSingleton(new RoryLibMatrixConfiguration()); } //Add services services.AddScoped(); - services.AddScoped(); + services.AddSingleton(); services.AddScoped(); return services; } diff --git a/MatrixRoomUtils.Core/StateEvent.cs b/MatrixRoomUtils.Core/StateEvent.cs index f2c8701..18b4632 100644 --- a/MatrixRoomUtils.Core/StateEvent.cs +++ b/MatrixRoomUtils.Core/StateEvent.cs @@ -29,7 +29,7 @@ public class StateEvent { get => _type; set { _type = value; - if (RawContent != null && this is StateEventResponse stateEventResponse) { + if (RawContent is not null && this is StateEventResponse stateEventResponse) { if (File.Exists($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json")) return; var x = GetType.Name; } @@ -46,7 +46,7 @@ public class StateEvent { get => _rawContent; set { _rawContent = value; - if (Type != null && this is StateEventResponse stateEventResponse) { + if (Type is not null && this is StateEventResponse stateEventResponse) { if (File.Exists($"unknown_state_events/{Type}/{stateEventResponse.EventId}.json")) return; var x = GetType.Name; } diff --git a/MatrixRoomUtils.Core/StateEventTypes/Common/MjolnirShortcodeEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Common/MjolnirShortcodeEventData.cs new file mode 100644 index 0000000..efc946d --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Common/MjolnirShortcodeEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core.StateEventTypes; + +[MatrixEvent(EventName = "org.matrix.mjolnir.shortcode")] +public class MjolnirShortcodeEventData : IStateEventType { + [JsonPropertyName("shortcode")] + public string? Shortcode { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAliasEventData.cs b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAliasEventData.cs new file mode 100644 index 0000000..5141ed2 --- /dev/null +++ b/MatrixRoomUtils.Core/StateEventTypes/Spec/RoomAliasEventData.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using MatrixRoomUtils.Core.Extensions; +using MatrixRoomUtils.Core.Interfaces; + +namespace MatrixRoomUtils.Core; + +[MatrixEvent(EventName = "m.room.alias")] +public class RoomAliasEventData : IStateEventType { + [JsonPropertyName("aliases")] + public List? Aliases { get; set; } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/App.razor b/MatrixRoomUtils.Web/App.razor index e58212b..a8cf817 100644 --- a/MatrixRoomUtils.Web/App.razor +++ b/MatrixRoomUtils.Web/App.razor @@ -10,15 +10,3 @@ - -@code { - - protected override async Task OnInitializedAsync() { - if (!RuntimeCache.WasLoaded) { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - Console.WriteLine("Loaded from local storage"); - StateHasChanged(); - } - } - -} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Classes/LocalStorageProviderService.cs b/MatrixRoomUtils.Web/Classes/LocalStorageProviderService.cs index 2a9082d..256c43d 100644 --- a/MatrixRoomUtils.Web/Classes/LocalStorageProviderService.cs +++ b/MatrixRoomUtils.Web/Classes/LocalStorageProviderService.cs @@ -4,7 +4,23 @@ using MatrixRoomUtils.Core.Interfaces.Services; namespace MatrixRoomUtils.Web.Classes; public class LocalStorageProviderService : IStorageProvider { + private readonly ILocalStorageService _localStorageService; + public LocalStorageProviderService(ILocalStorageService localStorageService) { - + _localStorageService = localStorageService; } + + async Task IStorageProvider.SaveAllChildrenAsync(string key, T value) => throw new NotImplementedException(); + + async Task IStorageProvider.LoadAllChildrenAsync(string key) where T : default => throw new NotImplementedException(); + + async Task IStorageProvider.SaveObjectAsync(string key, T value) => await _localStorageService.SetItemAsync(key, value); + + async Task IStorageProvider.LoadObjectAsync(string key) where T : default => await _localStorageService.GetItemAsync(key); + + async Task IStorageProvider.ObjectExistsAsync(string key) => await _localStorageService.ContainKeyAsync(key); + + async Task> IStorageProvider.GetAllKeysAsync() => (await _localStorageService.KeysAsync()).ToList(); + + async Task IStorageProvider.DeleteObjectAsync(string key) => await _localStorageService.RemoveItemAsync(key); } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs deleted file mode 100644 index 59c1ab6..0000000 --- a/MatrixRoomUtils.Web/Classes/LocalStorageWrapper.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Blazored.LocalStorage; -using MatrixRoomUtils.Core; - -namespace MatrixRoomUtils.Web.Classes; - -public class LocalStorageWrapper { - private readonly SemaphoreSlim _semaphoreSlim = new(1); - public Settings Settings { get; set; } = new(); - - //some basic logic - public async Task InitialiseRuntimeVariables(ILocalStorageService localStorage) { - //RuntimeCache stuff - // async Task Save() => await SaveToLocalStorage(localStorage); - // async Task SaveObject(string key, object obj) => await localStorage.SetItemAsync(key, obj); - // async Task RemoveObject(string key) => await localStorage.RemoveItemAsync(key); - // - // RuntimeCache.Save = Save; - // RuntimeCache.SaveObject = SaveObject; - // RuntimeCache.RemoveObject = RemoveObject; - if (RuntimeCache.LastUsedToken != null) { - Console.WriteLine("Access token is not null, creating authenticated home server"); - Console.WriteLine($"Homeserver cache: {RuntimeCache.HomeserverResolutionCache.Count} entries"); - // Console.WriteLine(RuntimeCache.HomeserverResolutionCache.ToJson()); - RuntimeCache.CurrentHomeServer = await new AuthenticatedHomeServer(RuntimeCache.LoginSessions[RuntimeCache.LastUsedToken].LoginResponse.HomeServer, RuntimeCache.LastUsedToken, null).Configure(); - Console.WriteLine("Created authenticated home server"); - } - } - - public async Task LoadFromLocalStorage(ILocalStorageService localStorage) { - await _semaphoreSlim.WaitAsync(); - if (RuntimeCache.WasLoaded) { - _semaphoreSlim.Release(); - return; - } - - Console.WriteLine("Loading from local storage..."); - Settings = await localStorage.GetItemAsync("rory.matrixroomutils.settings") ?? new Settings(); - - RuntimeCache.LastUsedToken = await localStorage.GetItemAsync("rory.matrixroomutils.last_used_token"); - RuntimeCache.LoginSessions = await localStorage.GetItemAsync>("rory.matrixroomutils.login_sessions") ?? new Dictionary(); - RuntimeCache.HomeserverResolutionCache = await localStorage.GetItemAsync>("rory.matrixroomutils.homeserver_resolution_cache") ?? new Dictionary(); - Console.WriteLine($"[LocalStorageWrapper] Loaded {RuntimeCache.LoginSessions.Count} login sessions, {RuntimeCache.HomeserverResolutionCache.Count} homeserver resolution cache entries"); - - //RuntimeCache.GenericResponseCache = await localStorage.GetItemAsync>>("rory.matrixroomutils.generic_cache") ?? new(); - - foreach (var s in (await localStorage.KeysAsync()).Where(x => x.StartsWith("rory.matrixroomutils.generic_cache:")).ToList()) { - Console.WriteLine($"Loading generic cache entry {s}"); - RuntimeCache.GenericResponseCache[s.Replace("rory.matrixroomutils.generic_cache:", "")] = await localStorage.GetItemAsync>(s); - } - - await InitialiseRuntimeVariables(localStorage); - RuntimeCache.WasLoaded = true; - _semaphoreSlim.Release(); - } - - public async Task SaveToLocalStorage(ILocalStorageService localStorage) { - Console.WriteLine("Saving to local storage..."); - await localStorage.SetItemAsync("rory.matrixroomutils.settings", Settings); - if (RuntimeCache.LoginSessions != null) await localStorage.SetItemAsync("rory.matrixroomutils.login_sessions", RuntimeCache.LoginSessions); - if (RuntimeCache.LastUsedToken != null) await localStorage.SetItemAsync("rory.matrixroomutils.last_used_token", RuntimeCache.LastUsedToken); - } - - public async Task SaveCacheToLocalStorage(ILocalStorageService localStorage, bool awaitSave = true, bool saveGenericCache = true) { - await localStorage.SetItemAsync("rory.matrixroomutils.homeserver_resolution_cache", - RuntimeCache.HomeserverResolutionCache.DistinctBy(x => x.Key) - .ToDictionary(x => x.Key, x => x.Value)); - //await localStorage.SetItemAsync("rory.matrixroomutils.generic_cache", RuntimeCache.GenericResponseCache); - if (saveGenericCache) - foreach (var s in RuntimeCache.GenericResponseCache.Keys) { - var t = localStorage.SetItemAsync($"rory.matrixroomutils.generic_cache:{s}", RuntimeCache.GenericResponseCache[s]); - if (awaitSave) await t; - } - } -} - -public class Settings { - public DeveloperSettings DeveloperSettings { get; set; } = new(); -} - -public class DeveloperSettings { - public bool EnableLogViewers { get; set; } = false; - public bool EnableConsoleLogging { get; set; } = true; - public bool EnablePortableDevtools { get; set; } = false; -} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs b/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs new file mode 100644 index 0000000..41f604d --- /dev/null +++ b/MatrixRoomUtils.Web/Classes/MRUStorageWrapper.cs @@ -0,0 +1,91 @@ +using MatrixRoomUtils.Core; +using MatrixRoomUtils.Core.Responses; +using MatrixRoomUtils.Core.Services; +using Microsoft.AspNetCore.Components; + +namespace MatrixRoomUtils.Web.Classes; + +public class MRUStorageWrapper { + private readonly TieredStorageService _storageService; + private readonly HomeserverProviderService _homeserverProviderService; + private readonly NavigationManager _navigationManager; + + public MRUStorageWrapper( + TieredStorageService storageService, + HomeserverProviderService homeserverProviderService, + NavigationManager navigationManager + ) { + _storageService = storageService; + _homeserverProviderService = homeserverProviderService; + _navigationManager = navigationManager; + } + + public async Task?> GetAllTokens() { + return await _storageService.DataStorageProvider.LoadObjectAsync>("mru.tokens") ?? new List(); + } + public async Task GetCurrentToken() { + var currentToken = await _storageService.DataStorageProvider.LoadObjectAsync("token"); + var allTokens = await GetAllTokens(); + if (allTokens is null or { Count: 0 }) { + await SetCurrentToken(null); + return null; + } + if (currentToken is null) { + await SetCurrentToken(currentToken = allTokens[0]); + } + if(!allTokens.Any(x=>x.AccessToken == currentToken.AccessToken)) { + await SetCurrentToken(currentToken = allTokens[0]); + } + return currentToken; + } + + public async Task AddToken(LoginResponse loginResponse) { + var tokens = await GetAllTokens(); + if (tokens == null) { + tokens = new List(); + } + + tokens.Add(loginResponse); + await _storageService.DataStorageProvider.SaveObjectAsync("mru.tokens", tokens); + } + + public async Task GetCurrentSession() { + var token = await GetCurrentToken(); + if (token == null) { + return null; + } + + return await _homeserverProviderService.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken); + } + + public async Task GetCurrentSessionOrNavigate() { + var session = await GetCurrentSession(); + if (session == null) { + _navigationManager.NavigateTo("/Login"); + } + return session; + } + public class Settings { + public DeveloperSettings DeveloperSettings { get; set; } = new(); + } + + public class DeveloperSettings { + public bool EnableLogViewers { get; set; } = false; + public bool EnableConsoleLogging { get; set; } = true; + public bool EnablePortableDevtools { get; set; } = false; + } + + public async Task RemoveToken(LoginResponse auth) { + var tokens = await GetAllTokens(); + if (tokens == null) { + return; + } + + tokens.RemoveAll(x=>x.AccessToken == auth.AccessToken); + await _storageService.DataStorageProvider.SaveObjectAsync("mru.tokens", tokens); + } + + public async Task SetCurrentToken(LoginResponse? auth) { + _storageService.DataStorageProvider.SaveObjectAsync("token", auth); + } +} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs index 4a31928..ca6345f 100644 --- a/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs +++ b/MatrixRoomUtils.Web/Classes/RoomCreationTemplates/DefaultRoomCreationTemplate.cs @@ -15,25 +15,25 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { InitialState = new List { new() { Type = "m.room.history_visibility", - Content = new { + TypedContent = new { history_visibility = "world_readable" } }, - new StateEvent { + new() { Type = "m.room.guest_access", - Content = new GuestAccessData { + TypedContent = new GuestAccessData { GuestAccess = "can_join" } }, new() { Type = "m.room.join_rules", - Content = new { - join_rule = "public" + TypedContent = new JoinRulesEventData() { + JoinRule = "public" } }, new() { Type = "m.room.server_acl", - Content = new { + TypedContent = new { allow = new[] { "*" }, deny = Array.Empty(), allow_ip_literals = false @@ -41,8 +41,8 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { }, new() { Type = "m.room.avatar", - Content = new { - url = "mxc://feline.support/UKNhEyrVsrAbYteVvZloZcFj" + TypedContent = new RoomAvatarEventData() { + Url = "mxc://feline.support/UKNhEyrVsrAbYteVvZloZcFj" } } }, @@ -78,7 +78,8 @@ public class DefaultRoomCreationTemplate : IRoomCreationTemplate { { "org.matrix.msc3401.call.member", 50 } }, Users = new Dictionary { - { RuntimeCache.CurrentHomeServer.UserId, 100 } + // { RuntimeCache.CurrentHomeServer.UserId, 100 } + //TODO: re-implement this } }, CreationContent = new JsonObject { diff --git a/MatrixRoomUtils.Web/Pages/DataExportPage.razor b/MatrixRoomUtils.Web/Pages/DataExportPage.razor deleted file mode 100644 index 732cd74..0000000 --- a/MatrixRoomUtils.Web/Pages/DataExportPage.razor +++ /dev/null @@ -1,67 +0,0 @@ -@page "/Export" -@using System.Text.Json -@inject NavigationManager NavigationManager -@inject ILocalStorageService LocalStorage - -Export - -

Data export

- -

-
Signed in accounts - Add new account or Import from TSV
-
-@if (_isLoaded) { - @foreach (var (token, user) in RuntimeCache.LoginSessions) { - @* *@ -
-@user.LoginResponse.UserId[1..].Split(":")[0]\auth\access_token=@token
-@user.LoginResponse.UserId[1..].Split(":")[0]\auth\device_id=@user.LoginResponse.DeviceId
-@user.LoginResponse.UserId[1..].Split(":")[0]\auth\home_server=@(RuntimeCache.HomeserverResolutionCache.ContainsKey(user.LoginResponse.HomeServer) ? RuntimeCache.HomeserverResolutionCache[user.LoginResponse.HomeServer].Result : "loading...")
-@user.LoginResponse.UserId[1..].Split(":")[0]\auth\user_id=@@@user.LoginResponse.UserId
-@user.LoginResponse.UserId[1..].Split(":")[0]\user\automatically_share_keys_with_trusted_users=true
-@user.LoginResponse.UserId[1..].Split(":")[0]\user\muted_tags=global
-@user.LoginResponse.UserId[1..].Split(":")[0]\user\online_key_backup=true
-@user.LoginResponse.UserId[1..].Split(":")[0]\user\only_share_keys_with_verified_users=false
-    
- } -} -else { -

Loading...

-

@resolvedHomeservers/@totalHomeservers homeservers resolved...

-} - -@code { - private bool _isLoaded; - private int resolvedHomeservers; - private int totalHomeservers; - - protected override async Task OnInitializedAsync() { - await base.OnInitializedAsync(); - if (!RuntimeCache.WasLoaded) { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - } - var homeservers = RuntimeCache.LoginSessions.Values.Select(x => x.LoginResponse.HomeServer).Distinct(); - totalHomeservers = homeservers.Count(); - StateHasChanged(); - foreach (var hs in homeservers) { - if (RuntimeCache.HomeserverResolutionCache.ContainsKey(hs)) { - resolvedHomeservers++; - continue; - } - var resolvedHomeserver = (await new RemoteHomeServer(hs).Configure()).FullHomeServerDomain; - - RuntimeCache.HomeserverResolutionCache.Add(hs, new HomeServerResolutionResult { Result = resolvedHomeserver, ResolutionTime = DateTime.Now }); - await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage); - - Console.WriteLine("Saved to local storage:"); - Console.WriteLine(JsonSerializer.Serialize(RuntimeCache.HomeserverResolutionCache, new JsonSerializerOptions { - WriteIndented = true - })); - resolvedHomeservers++; - StateHasChanged(); - } - StateHasChanged(); - _isLoaded = true; - } - -} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/DebugTools.razor b/MatrixRoomUtils.Web/Pages/DebugTools.razor index 4e4cec8..5116754 100644 --- a/MatrixRoomUtils.Web/Pages/DebugTools.razor +++ b/MatrixRoomUtils.Web/Pages/DebugTools.razor @@ -37,13 +37,10 @@ else { public List Rooms { get; set; } = new(); protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) { - NavigationManager.NavigateTo("/Login"); - return; - } - Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x => x.RoomId).ToList(); + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs == null) return; + Rooms = (await hs.GetJoinedRooms()).Select(x => x.RoomId).ToList(); Console.WriteLine("Fetched joined rooms!"); } @@ -53,7 +50,9 @@ else { private async Task SendGetRequest() { var field = typeof(IHomeServer).GetRuntimeFields().First(x => x.ToString().Contains("<_httpClient>k__BackingField")); - var httpClient = field.GetValue(RuntimeCache.CurrentHomeServer) as HttpClient; + 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) { diff --git a/MatrixRoomUtils.Web/Pages/DevOptions.razor b/MatrixRoomUtils.Web/Pages/DevOptions.razor index cdb5693..70dac31 100644 --- a/MatrixRoomUtils.Web/Pages/DevOptions.razor +++ b/MatrixRoomUtils.Web/Pages/DevOptions.razor @@ -7,69 +7,23 @@

Rory&::MatrixUtils - Developer options


-
-
-
- - +
+
+

-
- View caches -

Generic cache:

-
    - @foreach (var item in RuntimeCache.GenericResponseCache) { -
  • - @item.Key: @item.Value.Cache.Count entries
    - @if (item.Value.Cache.Count > 0) { -

    Earliest expiry: @(item.Value.Cache.Min(x => x.Value.ExpiryTime)) (@string.Format("{0:g}", item.Value.Cache.Min(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now)) from now)

    - @*

    Average expiry: @(item.Value.Cache.Average(x => x.Value.ExpiryTime.Value))(@item.Value.Cache.Average(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now) from now)

    *@ -

    Last expiry: @(item.Value.Cache.Max(x => x.Value.ExpiryTime)) (@string.Format("{0:g}", item.Value.Cache.Max(x => x.Value.ExpiryTime).Value.Subtract(DateTime.Now)) from now)

    - } -
  • - } -
-
- @code { + MRUStorageWrapper.Settings settings { get; set; } = new(); protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); + settings = await TieredStorage.DataStorageProvider.LoadObjectAsync("mru.settings"); await base.OnInitializedAsync(); - Task.Run(async () => { - while (true) { - await Task.Delay(1000); - StateHasChanged(); - } - }); } - protected async Task LogStuff() { + private async Task LogStuff() { await Task.Delay(100); - Console.WriteLine($"Settings: {LocalStorageWrapper.Settings.ToJson()}"); - - await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); - } - - protected async Task DropCaches() { - foreach (var (key, value) in RuntimeCache.GenericResponseCache) { - value.Cache.Clear(); - } - - //RuntimeCache.GenericResponseCache.Clear(); - RuntimeCache.HomeserverResolutionCache.Clear(); - await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage); - } - - protected async Task RandomiseCacheTimers() { - foreach (var keyValuePair in RuntimeCache.GenericResponseCache) { - Console.WriteLine($"Randomising cache timer for {keyValuePair.Key}"); - foreach (var cacheItem in keyValuePair.Value.Cache) { - cacheItem.Value.ExpiryTime = DateTime.Now.AddSeconds(Random.Shared.Next(15, 120)); - } - - await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage); - } + Console.WriteLine($"Settings: {settings.ToJson()}"); + await TieredStorage.DataStorageProvider.SaveObjectAsync("mru.settings", settings); } } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor index 858fad9..e3ebc72 100644 --- a/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor +++ b/MatrixRoomUtils.Web/Pages/HSAdmin/RoomQuery.razor @@ -165,7 +165,9 @@ private async Task Search() { Results.Clear(); - var searchRooms = RuntimeCache.CurrentHomeServer.Admin.SearchRoomsAsync(orderBy: OrderBy!, dir: Ascending ? "f" : "b", searchTerm: SearchTerm, localFilter: Filter).GetAsyncEnumerator(); + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + var searchRooms = hs.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)); diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor index 33cca61..16a6cee 100644 --- a/MatrixRoomUtils.Web/Pages/Index.razor +++ b/MatrixRoomUtils.Web/Pages/Index.razor @@ -1,7 +1,7 @@ @page "/" -@using MatrixRoomUtils.Web.Shared.IndexComponents -@inject NavigationManager NavigationManager -@inject ILocalStorageService LocalStorage +@using MatrixRoomUtils.Core.Helpers +@using MatrixRoomUtils.Core.Responses +@using MatrixRoomUtils.Web.Shared.SimpleComponents Index @@ -12,18 +12,67 @@ Small collection of tools to do not-so-everyday things.
Signed in accounts - Add new account

- @foreach (var (token, user) in RuntimeCache.LoginSessions) { - + @foreach (var (auth, user) in _users.OrderByDescending(x=>x.Value.RoomCount)) { + var _auth = auth; + var _user = user; +
+ +

+ + @_user.DisplayName on @_auth.Homeserver + Remove + +

+

Member of @_user.RoomCount rooms

+ +
} @code { + private Dictionary _users = new(); + protected override async Task OnInitializedAsync() { - if (!RuntimeCache.WasLoaded) { - Console.WriteLine("[INDEX] !!! LOCALSTORAGE WAS NOT LOADED !!!"); - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - } + _currentSession = await MRUStorage.GetCurrentToken(); + _users.Clear(); + var tokens = await MRUStorage.GetAllTokens(); + var profileTasks = tokens.Select(async token => { + UserInfo userInfo = new(); + var hs = await HomeserverProvider.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken); + var roomCountTask = hs.GetJoinedRooms(); + var profile = await hs.GetProfile(hs.WhoAmI.UserId); + userInfo.DisplayName = profile.DisplayName ?? hs.WhoAmI.UserId; + userInfo.AvatarUrl = MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, + profile.AvatarUrl + ?? "https://api.dicebear.com/6.x/identicon/svg?seed=" + hs.WhoAmI.UserId + ); + userInfo.RoomCount = (await roomCountTask).Count; + _users.Add(token, userInfo); + // StateHasChanged(); + }); + await Task.WhenAll(profileTasks); await base.OnInitializedAsync(); } + + private class UserInfo { + internal string AvatarUrl { get; set; } + internal string DisplayName { get; set; } + internal int RoomCount { get; set; } = 0; + } + + private async Task RemoveUser(LoginResponse auth) { + await MRUStorage.RemoveToken(auth); + if ((await MRUStorage.GetCurrentToken()).AccessToken == auth.AccessToken) + MRUStorage.SetCurrentToken((await MRUStorage.GetAllTokens()).FirstOrDefault()); + await OnInitializedAsync(); + } + + private LoginResponse _currentSession; + + private async Task SwitchSession(LoginResponse auth) { + Console.WriteLine($"Switching to {auth.Homeserver} {auth.AccessToken} {auth.UserId}"); + await MRUStorage.SetCurrentToken(auth); + await OnInitializedAsync(); + } } \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor index 80dbfd1..5ccecab 100644 --- a/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor +++ b/MatrixRoomUtils.Web/Pages/KnownHomeserverList.razor @@ -2,6 +2,7 @@ @using System.Text.Json @using System.Diagnostics @using MatrixRoomUtils.Core.Responses +@using MatrixRoomUtils.Core.StateEventTypes

Known Homeserver List


@@ -33,10 +34,10 @@ else { List HomeServers = new(); bool IsFinished { get; set; } HomeServerInfoQueryProgress QueryProgress { get; set; } = new(); - + AuthenticatedHomeServer hs { get; set; } protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - + hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; var sw = Stopwatch.StartNew(); HomeServers = await GetHomeservers(progressCallback: async progress => { if (sw.ElapsedMilliseconds > 1000) { @@ -61,7 +62,8 @@ else { private async Task> GetHomeservers(int memberLimit = 1000, Func>? progressCallback = null) { HomeServerInfoQueryProgress progress = new(); List homeServers = new(); - var rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); + + var rooms = await hs.GetJoinedRooms(); progress.TotalRooms = rooms.Count; var semaphore = new SemaphoreSlim(4); @@ -70,53 +72,69 @@ else { await semaphore.WaitAsync(); progress.ProcessedUsers.Add(room, new HomeServerInfoQueryProgress.State()); Console.WriteLine($"Fetching states for room ({rooms.IndexOf(room)}/{rooms.Count}) ({room.RoomId})"); - var states = (await room.GetStateAsync("")).Value.Deserialize>(); - states.RemoveAll(x => x.Type != "m.room.member" || x.Content.GetProperty("membership").GetString() != "join"); - Console.WriteLine($"Room {room.RoomId} has {states.Count} members"); - if (states.Count > memberLimit) { - Console.WriteLine("Skipping!"); - semaphore.Release(); - progress.ProcessedUsers.Remove(room); - progress.TotalRooms--; - return; - } - progress.ProcessedUsers[room].Total = states.Count; - var updateInterval = progress.ProcessedUsers[room].Total >= 1000 ? 1000 : 100; - while (progress.ProcessedUsers.Any(x => x.Value.Total == 0) && progress.ProcessedUsers[room].Total >= 1000) { - progress.ProcessedUsers[room].Blocked = true; - await Task.Delay(1000); - // if(progressCallback != null) - // await progressCallback.Invoke(progress); - } - progress.ProcessedUsers[room].Blocked = false; - var processedStates = 0; - foreach (var state in states) { - await semLock.WaitAsync(); - semLock.Release(); - if (progress.ProcessedUsers.Count(x => x.Value.Total == 0) > 5 && progress.ProcessedUsers[room].Total >= 200) { - progress.ProcessedUsers[room].Slowmode = true; - await Task.Delay(progress.ProcessedUsers[room].Total >= 500 ? 1000 : 100); - } - else { - progress.ProcessedUsers[room].Slowmode = false; - } + var states = room.GetFullStateAsync(); + await foreach (var state in states) { + if (state.Type is not "m.room.member") continue; + progress.ProcessedUsers[room].Total++; if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) { homeServers.Add(new HomeServerInfo { Server = state.StateKey.Split(':')[1] }); - } - var hs = homeServers.First(x => x.Server == state.StateKey.Split(':')[1]); - if (!hs.KnownUsers.Contains(state.StateKey.Split(':')[0])) - hs.KnownUsers.Add(state.StateKey.Split(':')[0]); - if (++progress.ProcessedUsers[room].Processed % updateInterval == 0 && progressCallback != null) { - await semLock.WaitAsync(); - var _ = await progressCallback.Invoke(progress); - semLock.Release(); + Console.WriteLine($"Added new homeserver {state.StateKey.Split(':')[1]}"); } } - Console.WriteLine("Collected states!"); - progress.ProcessedRooms++; - progress.ProcessedUsers[room].IsFinished = true; - progressCallback?.Invoke(progress); semaphore.Release(); + progress.ProcessedUsers[room].IsFinished = true; + progress.ProcessedRooms++; + if (progressCallback is not null) + await progressCallback.Invoke(progress); + + + + // states.RemoveAll(x => x.Type != "m.room.member" || (x.TypedContent as RoomMemberEventData).Membership != "join"); + // Console.WriteLine($"Room {room.RoomId} has {states.Count} members"); + // if (states.Count > memberLimit) { + // Console.WriteLine("Skipping!"); + // semaphore.Release(); + // progress.ProcessedUsers.Remove(room); + // progress.TotalRooms--; + // return; + // } + // progress.ProcessedUsers[room].Total = states.Count; + // var updateInterval = progress.ProcessedUsers[room].Total >= 1000 ? 1000 : 100; + // while (progress.ProcessedUsers.Any(x => x.Value.Total == 0) && progress.ProcessedUsers[room].Total >= 1000) { + // progress.ProcessedUsers[room].Blocked = true; + // await Task.Delay(1000); + // // if(progressCallback is not null) + // // await progressCallback.Invoke(progress); + // } + // progress.ProcessedUsers[room].Blocked = false; + // var processedStates = 0; + // foreach (var state in states) { + // await semLock.WaitAsync(); + // semLock.Release(); + // if (progress.ProcessedUsers.Count(x => x.Value.Total == 0) > 5 && progress.ProcessedUsers[room].Total >= 200) { + // progress.ProcessedUsers[room].Slowmode = true; + // await Task.Delay(progress.ProcessedUsers[room].Total >= 500 ? 1000 : 100); + // } + // else { + // progress.ProcessedUsers[room].Slowmode = false; + // } + // if (!homeServers.Any(x => x.Server == state.StateKey.Split(':')[1])) { + // homeServers.Add(new HomeServerInfo { Server = state.StateKey.Split(':')[1] }); + // } + // var hs = homeServers.First(x => x.Server == state.StateKey.Split(':')[1]); + // if (!hs.KnownUsers.Contains(state.StateKey.Split(':')[0])) + // hs.KnownUsers.Add(state.StateKey.Split(':')[0]); + // if (++progress.ProcessedUsers[room].Processed % updateInterval == 0 && progressCallback is not null) { + // await semLock.WaitAsync(); + // var _ = await progressCallback.Invoke(progress); + // semLock.Release(); + // } + // } + // Console.WriteLine("Collected states!"); + // progress.ProcessedRooms++; + // progress.ProcessedUsers[room].IsFinished = true; + // progressCallback?.Invoke(progress); + // semaphore.Release(); }); await Task.WhenAll(tasks); @@ -136,7 +154,7 @@ else { class HomeServerInfoQueryProgress { public int ProcessedRooms { get; set; } public int TotalRooms { get; set; } - public Dictionary ProcessedUsers { get; } = new(); + public Dictionary ProcessedUsers { get; } = new(); public List CurrentState { get; set; } = new(); public class State { diff --git a/MatrixRoomUtils.Web/Pages/LoginPage.razor b/MatrixRoomUtils.Web/Pages/LoginPage.razor index 9df7fa6..e19dd04 100644 --- a/MatrixRoomUtils.Web/Pages/LoginPage.razor +++ b/MatrixRoomUtils.Web/Pages/LoginPage.razor @@ -1,6 +1,6 @@ @page "/Login" -@using MatrixRoomUtils.Core.Authentication @using System.Text.Json +@using MatrixRoomUtils.Core.Responses @using MatrixRoomUtils.Web.Shared.SimpleComponents @inject ILocalStorageService LocalStorage @inject IJSRuntime JsRuntime @@ -22,20 +22,25 @@
-

Parsed records


+ + + + @foreach (var (homeserver, username, password) in records) { - + var record = (homeserver, username, password); + - + }
UsernameHomeserver
@username @homeserver@password.Length charsRemove

+
@@ -43,26 +48,34 @@ readonly List<(string homeserver, string username, string password)> records = new(); (string homeserver, string username, string password) newRecordInput = ("", "", ""); + List LoggedInSessions { get; set; } = new(); + async Task Login() { - foreach (var (homeserver, username, password) in records) { - if (RuntimeCache.LoginSessions.Any(x => x.Value.LoginResponse.UserId == $"@{username}:{homeserver}")) continue; - var result = await MatrixAuth.Login(homeserver, username, password); - Console.WriteLine($"Obtained access token for {result.UserId}!"); - - var userinfo = new UserInfo { - LoginResponse = result - }; - userinfo.Profile = await RuntimeCache.CurrentHomeServer.GetProfile(result.UserId); - RuntimeCache.LastUsedToken = result.AccessToken; + var loginTasks = records.Select(async record => { + var (homeserver, username, password) = record; + if (LoggedInSessions.Any(x => x.UserId == $"@{username}:{homeserver}")) return; + try { + var result = await HomeserverProvider.Login(homeserver, username, password); + if (result == null) { + Console.WriteLine($"Failed to login to {homeserver} as {username}!"); + return; + } + Console.WriteLine($"Obtained access token for {result.UserId}!"); - RuntimeCache.LoginSessions.Add(result.AccessToken, userinfo); + await MRUStorage.AddToken(result); + LoggedInSessions = await MRUStorage.GetAllTokens(); + } + catch (Exception e) { + Console.WriteLine($"Failed to login to {homeserver} as {username}!"); + Console.WriteLine(e); + } StateHasChanged(); - } - - await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); + }); + await Task.WhenAll(loginTasks); } private async Task FileChanged(InputFileChangeEventArgs obj) { + LoggedInSessions = await MRUStorage.GetAllTokens(); Console.WriteLine(JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true })); @@ -78,7 +91,8 @@ } } - private void AddRecord() { + private async Task AddRecord() { + LoggedInSessions = await MRUStorage.GetAllTokens(); records.Add(newRecordInput); newRecordInput = ("", "", ""); } diff --git a/MatrixRoomUtils.Web/Pages/MediaLocator.razor b/MatrixRoomUtils.Web/Pages/MediaLocator.razor index 38d1514..6221041 100644 --- a/MatrixRoomUtils.Web/Pages/MediaLocator.razor +++ b/MatrixRoomUtils.Web/Pages/MediaLocator.razor @@ -82,7 +82,6 @@ } async Task addMoreHomeservers() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); var res = await Http.GetAsync("/homeservers.txt"); var content = await res.Content.ReadAsStringAsync(); homeservers.Clear(); @@ -93,10 +92,8 @@ lines.ToList().ForEach(async line => { await sem.WaitAsync(); try { - homeservers.Add(await rhs.ResolveHomeserverFromWellKnown(line)); + homeservers.Add(await HomeserverResolver.ResolveHomeserverFromWellKnown(line)); StateHasChanged(); - if (Random.Shared.Next(0, 101) == 50) - await LocalStorageWrapper.SaveCacheToLocalStorage(LocalStorage); } catch (Exception e) { Console.WriteLine(e); diff --git a/MatrixRoomUtils.Web/Pages/ModalTest.razor b/MatrixRoomUtils.Web/Pages/ModalTest.razor index d031dc2..f32c672 100644 --- a/MatrixRoomUtils.Web/Pages/ModalTest.razor +++ b/MatrixRoomUtils.Web/Pages/ModalTest.razor @@ -31,20 +31,6 @@ double _y = 0; double multiplier = 1; - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - //var rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); - - //rooms.ForEach(async room => { - // var name = await room.GetNameAsync(); - // _windowInfos.Add(_windowInfos.Count, new WindowInfo() { X = _x, Y = _y, Title = name}); - // _x += 20; - // _y += 20; - // var dimension = await JsRuntime.InvokeAsync("getWindowDimensions"); - // if (_x > dimension.Width - 100) _x %= dimension.Width - 100; - // if (_y > dimension.Height - 50) _y %= dimension.Height - 50; - // StateHasChanged(); - //}); - for (int i = 0; i < 200; i++) { var i1 = i; _windowInfos.Add(_windowInfos.Count, new WindowInfo() { diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor index 0840ebf..8e2609f 100644 --- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor +++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListEditorPage.razor @@ -1,9 +1,8 @@ @page "/PolicyListEditor/{RoomId}" @using MatrixRoomUtils.Core.StateEventTypes @using System.Text.Json +@using MatrixRoomUtils.Core.Helpers @using MatrixRoomUtils.Core.Responses -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager

Policy list editor - Editing @RoomId


@@ -31,12 +30,13 @@ else { - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && x.Content.Entity != null)) { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && (x.TypedContent as PolicyRuleStateEventData).Entity is not null)) { + var policyData = policyEvent.TypedContent as PolicyRuleStateEventData; - Entity: @policyEvent.Content.Entity
State: @policyEvent.StateKey - @policyEvent.Content.Reason + Entity: @policyData.Entity
State: @policyEvent.StateKey + @policyData.Reason - @policyEvent.Content.ExpiryDateTime + @policyData.ExpiryDateTime @@ -47,7 +47,7 @@ else {
- Invalid events + Redacted events @@ -56,10 +56,11 @@ else { - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && x.Content.Entity == null)) { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && (x.TypedContent as PolicyRuleStateEventData).Entity == null)) { + var policyData = policyEvent.TypedContent as PolicyRuleStateEventData; - + } @@ -82,12 +83,13 @@ else { - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && x.Content.Entity != null)) { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && (x.TypedContent as PolicyRuleStateEventData).Entity is not null)) { + var policyData = policyEvent.TypedContent as PolicyRuleStateEventData; - - + +
@policyEvent.StateKey@policyEvent.Content.ToJson(false, true)@policyEvent.RawContent.ToJson(false, true)
Entity: @policyEvent.Content.Entity
State: @policyEvent.StateKey
@policyEvent.Content.ReasonEntity: @policyData.Entity
State: @policyEvent.StateKey
@policyData.Reason - @policyEvent.Content.ExpiryDateTime + @policyData.ExpiryDateTime @@ -97,7 +99,7 @@ else {
- Invalid events + Redacted events @@ -106,10 +108,10 @@ else { - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && x.Content.Entity == null)) { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && (x.TypedContent as PolicyRuleStateEventData).Entity == null)) { - + } @@ -135,17 +137,18 @@ else { - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity != null)) { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && (x.TypedContent as PolicyRuleStateEventData).Entity is not null)) { + var policyData = policyEvent.TypedContent as PolicyRuleStateEventData; @if (_enableAvatars) { } - - + +
@policyEvent.StateKey@policyEvent.Content.ToJson(false, true)@policyEvent.RawContent.ToJson(false, true)
- + Entity: @string.Join("", policyEvent.Content.Entity.Take(64))
State: @string.Join("", policyEvent.StateKey.Take(64))
@policyEvent.Content.ReasonEntity: @string.Join("", policyData.Entity.Take(64))
State: @string.Join("", policyEvent.StateKey.Take(64))
@policyData.Reason - @policyEvent.Content.ExpiryDateTime + @policyData.ExpiryDateTime @@ -155,7 +158,7 @@ else {
- Invalid events + Redacted events @@ -164,10 +167,10 @@ else { - @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity == null)) { + @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && (x.TypedContent as PolicyRuleStateEventData).Entity == null)) { - + } @@ -191,36 +194,32 @@ else { static readonly Dictionary avatars = new(); static readonly Dictionary servers = new(); - public static List> PolicyEvents { get; set; } = new(); + public static List PolicyEvents { get; set; } = new(); protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - // if(RuntimeCache.AccessToken == null || RuntimeCache.CurrentHomeserver == null) - if (RuntimeCache.CurrentHomeServer == null) { - NavigationManager.NavigateTo("/Login"); - return; - } + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; RoomId = RoomId.Replace('~', '.'); await LoadStatesAsync(); Console.WriteLine("Policy list editor initialized!"); } private async Task LoadStatesAsync() { - // using var client = new HttpClient(); - // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", LocalStorageWrapper.AccessToken); - // var response = await client.GetAsync($"{LocalStorageWrapper.CurrentHomeserver}/_matrix/client/v3/rooms/{RoomId}/state"); - // var Content = await response.Content.ReadAsStringAsync(); - // Console.WriteLine(JsonSerializer.Deserialize(Content).ToJson()); - // var stateEvents = JsonSerializer.Deserialize>(Content); - var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId); - var stateEventsQuery = await room.GetStateAsync(""); - if (stateEventsQuery == null) { - Console.WriteLine("state events query is null!!!"); + var hs = await MRUStorage.GetCurrentSession(); + var room = await hs.GetRoom(RoomId); + + var states = room.GetFullStateAsync(); + await foreach (var state in states) { + if (!state.Type.StartsWith("m.policy.rule")) continue; + PolicyEvents.Add(state); } - var stateEvents = stateEventsQuery.Value.Deserialize>(); - PolicyEvents = stateEvents.Where(x => x.Type.StartsWith("m.policy.rule")) - .Select(x => JsonSerializer.Deserialize>(JsonSerializer.Serialize(x))).ToList(); + + + // var stateEventsQuery = await room.GetStateAsync(""); + // var stateEvents = stateEventsQuery.Value.Deserialize>(); + // PolicyEvents = stateEvents.Where(x => x.Type.StartsWith("m.policy.rule")) + // .Select(x => JsonSerializer.Deserialize(JsonSerializer.Serialize(x))).ToList(); StateHasChanged(); } @@ -228,10 +227,10 @@ else { try { if (avatars.ContainsKey(userId)) return; var hs = userId.Split(':')[1]; - var server = servers.ContainsKey(hs) ? servers[hs] : await new RemoteHomeServer(userId.Split(':')[1]).Configure(); + var server = servers.ContainsKey(hs) ? servers[hs] : new RemoteHomeServer(userId.Split(':')[1]); if (!servers.ContainsKey(hs)) servers.Add(hs, server); var profile = await server.GetProfile(userId); - avatars.Add(userId, server.ResolveMediaUri(profile.AvatarUrl)); + avatars.Add(userId, MediaResolver.ResolveMediaUri(server.FullHomeServerDomain, profile.AvatarUrl)); servers.Add(userId, server); StateHasChanged(); } @@ -241,8 +240,8 @@ else { } private async Task GetAllAvatars() { - foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && x.Content.Entity != null)) { - await GetAvatar(policyEvent.Content.Entity); + foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && (x.TypedContent as PolicyRuleStateEventData).Entity is not null)) { + await GetAvatar((policyEvent.TypedContent as PolicyRuleStateEventData).Entity); } StateHasChanged(); } diff --git a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor index 8f711b5..4db2b5a 100644 --- a/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor +++ b/MatrixRoomUtils.Web/Pages/PolicyList/PolicyListRoomList.razor @@ -1,4 +1,7 @@ @page "/PolicyListEditor" +@using System.Text.Json.Serialization +@using MatrixRoomUtils.Core.Interfaces +@using MatrixRoomUtils.Core.StateEventTypes @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager

Policy list editor - Room list

@@ -39,28 +42,26 @@ else { private int totalRoomCount { get; set; } protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) { - NavigationManager.NavigateTo("/Login"); - return; - } + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; await EnumeratePolicyRooms(); Console.WriteLine("Policy list editor initialized!"); } private async Task EnumeratePolicyRooms() { - var xxxrooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); - totalRoomCount = xxxrooms.Count; + var hs = await MRUStorage.GetCurrentSession(); + var rooms = await hs.GetJoinedRooms(); + totalRoomCount = rooms.Count; StateHasChanged(); - var xxxsemaphore = new SemaphoreSlim(1000); - var xxxtasks = new List>(); - foreach (var room in xxxrooms) { - xxxtasks.Add(GetPolicyRoomInfo(room.RoomId, xxxsemaphore)); + var semaphore = new SemaphoreSlim(8); + var tasks = new List>(); + foreach (var room in rooms) { + tasks.Add(GetPolicyRoomInfo(room.RoomId, semaphore)); } - var xxxresults = await Task.WhenAll(xxxtasks); - PolicyRoomList.AddRange(xxxresults.Where(x => x != null).Select(x => x.Value)); + var results = await Task.WhenAll(tasks); + PolicyRoomList.AddRange(results.Where(x => x is not null).Select(x => x.Value)); Console.WriteLine($"Detected policy lists: {PolicyRoomList.ToJson()}"); } @@ -68,15 +69,15 @@ else { private async Task GetPolicyRoomInfo(string room, SemaphoreSlim semaphore) { try { await semaphore.WaitAsync(); + var hs = await MRUStorage.GetCurrentSession(); PolicyRoomInfo roomInfo = new() { RoomId = room }; - var r = await RuntimeCache.CurrentHomeServer.GetRoom(room); - var shortcodeState = await r.GetStateAsync("org.matrix.mjolnir.shortcode"); - if (!shortcodeState.HasValue) return null; - roomInfo.Shortcode = shortcodeState.Value.TryGetProperty("shortcode", out var shortcode) ? shortcode.GetString() : null; + var r = await hs.GetRoom(room); + var shortcodeState = await r.GetStateAsync("org.matrix.mjolnir.shortcode"); + roomInfo.Shortcode = shortcodeState.Shortcode; - if (roomInfo.Shortcode != null) { + if (roomInfo.Shortcode is not null) { roomInfo.Name = await r.GetNameAsync(); return roomInfo; } @@ -90,6 +91,8 @@ else { } } + + public struct PolicyRoomInfo { public string RoomId { get; set; } diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor index 9b0bb88..087adf8 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManager.razor @@ -34,25 +34,22 @@ else { @code { - public List Rooms { get; set; } = new(); - public List Spaces { get; set; } = new(); + public List Rooms { get; set; } = new(); + public List Spaces { get; set; } = new(); protected override async Task OnInitializedAsync() { Console.WriteLine("Initializing room manager"); - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); Console.WriteLine("Loaded from local storage"); await base.OnInitializedAsync(); Console.WriteLine("Initialized base"); - if (RuntimeCache.CurrentHomeServer == null) { - NavigationManager.NavigateTo("/Login"); - return; - } + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; Console.WriteLine("Fetching joined rooms"); - var _rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); + var _rooms = await hs.GetJoinedRooms(); StateHasChanged(); Console.WriteLine($"Got {_rooms.Count} rooms"); var semaphore = new SemaphoreSlim(10); - var tasks = new List>(); + var tasks = new List>(); foreach (var room in _rooms) { tasks.Add(CheckIfSpace(room, semaphore)); } @@ -61,14 +58,14 @@ else { Console.WriteLine("Fetched joined rooms!"); } - private async Task CheckIfSpace(Room room, SemaphoreSlim semaphore) { + private async Task CheckIfSpace(GenericRoom room, SemaphoreSlim semaphore) { await semaphore.WaitAsync(); // Console.WriteLine($"Checking if {room.RoomId} is a space"); try { var state = await room.GetStateAsync("m.room.create"); - if (state != null) { + if (state is not null) { //Console.WriteLine(state.Value.ToJson()); - if (state.Type != null) { + if (state.Type is not null) { if (state.Type == "m.space") { Console.WriteLine($"Room {room.RoomId} is a space!"); Spaces.Add(room); diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor index 80d852a..8368aa5 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerCreateRoom.razor @@ -1,339 +1,339 @@ -@page "/RoomManagerCreateRoom" -@using MatrixRoomUtils.Core.Responses -@using System.Text.Json -@using System.Reflection -@using MatrixRoomUtils.Core.StateEventTypes -@using MatrixRoomUtils.Web.Classes.RoomCreationTemplates -@* ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not *@ -@using MatrixRoomUtils.Web.Shared.SimpleComponents - -

Room Manager - Create Room

- -@*
@JsonString
*@ - -
@policyEvent.StateKey@policyEvent.Content.ToJson(false, true)@policyEvent.RawContent.ToJson(false, true)
- - - - - @if (creationEvent != null) { - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- @creationEvent.PowerLevelContentOverride.Users.Count members - @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) { - var _event = user; -
- - - - } - @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) { - var _user = user; - - - - - } - - - - - - - - - - - - - @* Initial states, should remain at bottom? *@ - - - - - - } -
Preset: - - @foreach (var createRoomRequest in Presets) { - - } - -
Room name: - -
Room alias (localpart): - -
Room type: - - - - - -
History visibility: - @* *@ - @* *@ - @* *@ - @* *@ - @* *@ - @* *@ -
Guest access: - @(guestAccessEvent.IsGuestAccessEnabled ? "Guests can join" : "Guests cannot join") (@guestAccessEvent.GuestAccess) - @* *@ - @* *@ - @* *@ - @* *@ -
Room icon: - -
-
- -
- -
Permissions:
: - -
: - -
Server ACLs: -
- @(creationEvent.ServerACLs.Allow.Count) allow rules - -
-
- @creationEvent.ServerACLs.Deny.Count deny rules - -
-
Invited members: -
- @creationEvent.InitialState.Count(x => x.Type == "m.room.member") members - - @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != RuntimeCache.CurrentHomeServer.UserId)) { - - } -
-
Initial states: -
- - @code{ - - private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" }; - - } - - @creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states - - @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) { - - - - - - } -
- @(initialState.Type): - @if (!string.IsNullOrEmpty(initialState.StateKey)) { -
- (@initialState.StateKey) - } -
-
@JsonSerializer.Serialize(initialState.Content, new JsonSerializerOptions { WriteIndented = true })
-
-
-
- @creationEvent.InitialState.Count initial states - - @foreach (var initialState in creationEvent.InitialState) { - var _state = initialState; - - - - - - } -
- @(_state.Type):
- -
-
@JsonSerializer.Serialize(_state.Content, new JsonSerializerOptions { WriteIndented = true })
-
-
-
- -
-
- Creation JSON -
-        @creationEvent.ToJson(ignoreNull: true)
-    
-
-
- Creation JSON (with null values) -
-    @creationEvent.ToJson()
-    
-
- - -@code { - - private string RoomPreset { - get { - if (Presets.ContainsValue(creationEvent)) { - return Presets.First(x => x.Value == creationEvent).Key; - } - return "Not a preset"; - } - set { - creationEvent = Presets[value]; - JsonChanged(); - OverwriteWrappedPropertiesFromEvent(); - creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - guestAccessEvent = creationEvent["m.room.guest_access"].As().Content; - - Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}"); - Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As().ToJson()}"); - creationEvent["m.room.guest_access"].As().Content.IsGuestAccessEnabled = true; - Console.WriteLine("-- Created new guest access content --"); - Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}"); - Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As().ToJson()}"); - Console.WriteLine($"Creation event casted back: {creationEvent["m.room.guest_access"].As().ToJson()}"); - StateHasChanged(); - } - } - - private Dictionary creationEventValidationErrors { get; set; } = new(); - - private CreateRoomRequest creationEvent { get; set; } - GuestAccessData guestAccessEvent { get; set; } - - private Dictionary Presets { get; set; } = new(); - - protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - - //creationEvent = Presets["Default room"] = - 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()); - - //wrappers - private List ServerACLAllowRules { get; set; } = new(); - private List ServerACLDenyRules { get; set; } = new(); - - private void OverwriteWrappedPropertiesFromEvent() { - Console.WriteLine("Overwriting wrapped properties from event"); - ServerACLAllowRules = creationEvent.ServerACLs.Allow; - ServerACLDenyRules = creationEvent.ServerACLs.Deny; - } - - private async Task OverwriteWrappedProperties() { - Console.WriteLine("Overwriting wrapped properties"); - Console.WriteLine($"Allow: {ServerACLAllowRules.Count}: {string.Join(", ", ServerACLAllowRules)}"); - Console.WriteLine($"Deny: {ServerACLDenyRules.Count}: {string.Join(", ", ServerACLDenyRules)}"); - creationEvent.ServerACLs = new ServerACLData { - Allow = ServerACLAllowRules, - Deny = ServerACLDenyRules, - AllowIpLiterals = creationEvent.ServerACLs.AllowIpLiterals - }; - - StateHasChanged(); - } - - private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) { - var res = await RuntimeCache.CurrentHomeServer.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType); - Console.WriteLine(res); - creationEvent.RoomIcon = 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)"); - //creationEvent.CreationContent.Add(); - var id = await RuntimeCache.CurrentHomeServer.CreateRoom(creationEvent); - // NavigationManager.NavigateTo($"/RoomManager/{id.RoomId.Replace('.','~')}"); - } - - private void InviteMember(string mxid) { - if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && RuntimeCache.CurrentHomeServer.UserId != mxid) - creationEvent.InitialState.Add(new StateEvent { - Type = "m.room.member", - StateKey = mxid, - Content = new { - 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 - }; - - } - +@* @page "/RoomManagerCreateRoom" *@ +@* @using MatrixRoomUtils.Core.Responses *@ +@* @using System.Text.Json *@ +@* @using System.Reflection *@ +@* @using MatrixRoomUtils.Core.Helpers *@ +@* @using MatrixRoomUtils.Core.StateEventTypes *@ +@* @using MatrixRoomUtils.Web.Classes.RoomCreationTemplates *@ +@* $1$ ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not #1# *@ +@* @using MatrixRoomUtils.Web.Shared.SimpleComponents *@ +@* *@ +@*

Room Manager - Create Room

*@ +@* *@ +@* $1$
@JsonString
#1# *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* @if (creationEvent is not null) { *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@*
*@ +@* @creationEvent.PowerLevelContentOverride.Users.Count members *@ +@* @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) { *@ +@* var _event = user; *@ +@*
*@ +@* *@ +@* *@ +@* *@ +@* } *@ +@* @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) { *@ +@* var _user = user; *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* } *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* $1$ Initial states, should remain at bottom? #1# *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* } *@ +@*
Preset: *@ +@* *@ +@* @foreach (var createRoomRequest in Presets) { *@ +@* *@ +@* } *@ +@* *@ +@*
Room name: *@ +@* *@ +@*
Room alias (localpart): *@ +@* *@ +@*
Room type: *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@*
History visibility: *@ +@* $1$ #1# *@ +@* $1$ #1# *@ +@* $1$ #1# *@ +@* $1$ #1# *@ +@* $1$ #1# *@ +@* $1$ #1# *@ +@*
Guest access: *@ +@* @(guestAccessEvent.IsGuestAccessEnabled ? "Guests can join" : "Guests cannot join") (@guestAccessEvent.GuestAccess) *@ +@* $1$ #1# *@ +@* $1$ #1# *@ +@* $1$ #1# *@ +@* $1$ #1# *@ +@*
Room icon: *@ +@* *@ +@*
*@ +@*
*@ +@* *@ +@*
*@ +@* *@ +@*
Permissions:
: *@ +@* *@ +@*
: *@ +@* *@ +@*
Server ACLs: *@ +@*
*@ +@* @(creationEvent["server"].ServerACLs.Allow.Count) allow rules *@ +@* *@ +@*
*@ +@*
*@ +@* @creationEvent.ServerACLs.Deny.Count deny rules *@ +@* *@ +@*
*@ +@*
Invited members: *@ +@*
*@ +@* @creationEvent.InitialState.Count(x => x.Type == "m.room.member") members *@ +@* *@ +@* @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != RuntimeCache.CurrentHomeServer.UserId)) { *@ +@* *@ +@* } *@ +@*
*@ +@*
Initial states: *@ +@*
*@ +@* *@ +@* @code{ *@ +@* *@ +@* private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" }; *@ +@* *@ +@* } *@ +@* *@ +@* @creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states *@ +@* *@ +@* @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) { *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* } *@ +@*
*@ +@* @(initialState.Type): *@ +@* @if (!string.IsNullOrEmpty(initialState.StateKey)) { *@ +@*
*@ +@* (@initialState.StateKey) *@ +@* } *@ +@*
*@ +@*
@JsonSerializer.Serialize(initialState.Content, new JsonSerializerOptions { WriteIndented = true })
*@ +@*
*@ +@*
*@ +@*
*@ +@* @creationEvent.InitialState.Count initial states *@ +@* *@ +@* @foreach (var initialState in creationEvent.InitialState) { *@ +@* var _state = initialState; *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* } *@ +@*
*@ +@* @(_state.Type):
*@ +@* *@ +@*
*@ +@*
@JsonSerializer.Serialize(_state.Content, new JsonSerializerOptions { WriteIndented = true })
*@ +@*
*@ +@*
*@ +@*
*@ +@* *@ +@*
*@ +@*
*@ +@* Creation JSON *@ +@*
 *@
+@*         @creationEvent.ToJson(ignoreNull: true) *@
+@*     
*@ +@*
*@ +@*
*@ +@* Creation JSON (with null values) *@ +@*
 *@
+@*     @creationEvent.ToJson() *@
+@*     
*@ +@*
*@ +@* *@ +@* *@ +@* @code { *@ +@* *@ +@* private string RoomPreset { *@ +@* get { *@ +@* if (Presets.ContainsValue(creationEvent)) { *@ +@* return Presets.First(x => x.Value == creationEvent).Key; *@ +@* } *@ +@* return "Not a preset"; *@ +@* } *@ +@* set { *@ +@* creationEvent = Presets[value]; *@ +@* JsonChanged(); *@ +@* OverwriteWrappedPropertiesFromEvent(); *@ +@* creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); *@ +@* creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); *@ +@* guestAccessEvent = creationEvent["m.room.guest_access"].As().Content; *@ +@* *@ +@* Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}"); *@ +@* Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As().ToJson()}"); *@ +@* creationEvent["m.room.guest_access"].As().Content.IsGuestAccessEnabled = true; *@ +@* Console.WriteLine("-- Created new guest access content --"); *@ +@* Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}"); *@ +@* Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As().ToJson()}"); *@ +@* Console.WriteLine($"Creation event casted back: {creationEvent["m.room.guest_access"].As().ToJson()}"); *@ +@* StateHasChanged(); *@ +@* } *@ +@* } *@ +@* *@ +@* private Dictionary creationEventValidationErrors { get; set; } = new(); *@ +@* *@ +@* private CreateRoomRequest creationEvent { get; set; } *@ +@* GuestAccessData guestAccessEvent { get; set; } *@ +@* *@ +@* private Dictionary Presets { get; set; } = new(); *@ +@* *@ +@* protected override async Task OnInitializedAsync() { *@ +@* *@ +@* //creationEvent = Presets["Default room"] = *@ +@* 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()); *@ +@* *@ +@* //wrappers *@ +@* private List ServerACLAllowRules { get; set; } = new(); *@ +@* private List ServerACLDenyRules { get; set; } = new(); *@ +@* *@ +@* private void OverwriteWrappedPropertiesFromEvent() { *@ +@* Console.WriteLine("Overwriting wrapped properties from event"); *@ +@* ServerACLAllowRules = creationEvent.ServerACLs.Allow; *@ +@* ServerACLDenyRules = creationEvent.ServerACLs.Deny; *@ +@* } *@ +@* *@ +@* private async Task OverwriteWrappedProperties() { *@ +@* Console.WriteLine("Overwriting wrapped properties"); *@ +@* Console.WriteLine($"Allow: {ServerACLAllowRules.Count}: {string.Join(", ", ServerACLAllowRules)}"); *@ +@* Console.WriteLine($"Deny: {ServerACLDenyRules.Count}: {string.Join(", ", ServerACLDenyRules)}"); *@ +@* creationEvent.ServerACLs = new ServerACLData { *@ +@* Allow = ServerACLAllowRules, *@ +@* Deny = ServerACLDenyRules, *@ +@* AllowIpLiterals = creationEvent.ServerACLs.AllowIpLiterals *@ +@* }; *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) { *@ +@* var res = await RuntimeCache.CurrentHomeServer.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType); *@ +@* Console.WriteLine(res); *@ +@* creationEvent.RoomIcon = 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)"); *@ +@* //creationEvent.CreationContent.Add(); *@ +@* var id = await RuntimeCache.CurrentHomeServer.CreateRoom(creationEvent); *@ +@* // NavigationManager.NavigateTo($"/RoomManager/{id.RoomId.Replace('.','~')}"); *@ +@* } *@ +@* *@ +@* private void InviteMember(string mxid) { *@ +@* if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && RuntimeCache.CurrentHomeServer.UserId != mxid) *@ +@* creationEvent.InitialState.Add(new StateEvent { *@ +@* Type = "m.room.member", *@ +@* StateKey = mxid, *@ +@* Content = new { *@ +@* 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/RoomManager/RoomManagerSpace.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor index a9c71c4..afa39b9 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerSpace.razor @@ -14,7 +14,7 @@ State list @foreach (var stateEvent in States.OrderBy(x => x.StateKey).ThenBy(x => x.Type)) {

@stateEvent.StateKey/@stateEvent.Type:

-
@stateEvent.Content.ToJson()
+
@stateEvent.RawContent.ToJson()
}
@@ -23,36 +23,57 @@ [Parameter] public string RoomId { get; set; } = "invalid!!!!!!"; - private Room? Room { get; set; } + private GenericRoom? Room { get; set; } - private StateEventResponse[] States { get; set; } = Array.Empty>(); - private List Rooms { get; } = new(); + private StateEventResponse[] States { get; set; } = Array.Empty(); + private List Rooms { get; } = new(); private List ServersInSpace { get; } = new(); protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - Room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId.Replace('~', '.')); - var state = await Room.GetStateAsync(""); - if (state != null) { - // Console.WriteLine(state.Value.ToJson()); - States = state.Value.Deserialize[]>()!; + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; - foreach (var stateEvent in States) { - if (stateEvent.Type == "m.space.child") { - // if (stateEvent.Content.ToJson().Length < 5) return; - var roomId = stateEvent.StateKey; - var room = await RuntimeCache.CurrentHomeServer.GetRoom(roomId); - if (room != null) { - Rooms.Add(room); - } + Room = await hs.GetRoom(RoomId.Replace('~', '.')); + + var state = Room.GetFullStateAsync(); + await foreach (var stateEvent in state) { + if (stateEvent.Type == "m.space.child") { + var roomId = stateEvent.StateKey; + var room = await 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); - } + } + else if (stateEvent.Type == "m.room.member") { + var serverName = stateEvent.StateKey.Split(':').Last(); + if (!ServersInSpace.Contains(serverName)) { + ServersInSpace.Add(serverName); } } + } + await base.OnInitializedAsync(); + + // var state = await Room.GetStateAsync(""); + // if (state is not null) { + // // Console.WriteLine(state.Value.ToJson()); + // States = state.Value.Deserialize()!; + // + // foreach (var stateEvent in States) { + // if (stateEvent.Type == "m.space.child") { + // // if (stateEvent.Content.ToJson().Length < 5) return; + // var roomId = stateEvent.StateKey; + // var room = await 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)) // { @@ -62,8 +83,8 @@ // //this is fine, apprently... // //Console.WriteLine($"Room {room.RoomId} has no Content.Type in m.room.create!"); // } - } - await base.OnInitializedAsync(); + + // await base.OnInitializedAsync(); } private async Task JoinAllRooms() { diff --git a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor index 9513a8a..e32b5cb 100644 --- a/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor +++ b/MatrixRoomUtils.Web/Pages/RoomManager/RoomManagerTimeline.razor @@ -20,10 +20,11 @@ private List Events { get; } = new(); protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); RoomId = RoomId.Replace('~', '.'); Console.WriteLine("RoomId: " + RoomId); - var room = await RuntimeCache.CurrentHomeServer.GetRoom(RoomId); + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + var room = await hs.GetRoom(RoomId); MessagesResponse? msgs = null; do { msgs = await room.GetMessagesAsync(limit: 250, from: msgs?.End, dir: "b"); @@ -32,7 +33,7 @@ msgs.Chunk.Reverse(); Events.InsertRange(0, msgs.Chunk); StateHasChanged(); - } while (msgs.End != null); + } while (msgs.End is not null); await base.OnInitializedAsync(); } diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor index 296514c..b2d28f6 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateEditorPage.razor @@ -45,12 +45,9 @@ public string status = ""; protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer != null) { - NavigationManager.NavigateTo("/Login"); - return; - } + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; RoomId = RoomId.Replace('~', '.'); await LoadStatesAsync(); Console.WriteLine("Policy list editor initialized!"); @@ -59,25 +56,20 @@ private DateTime _lastUpdate = DateTime.Now; private async Task LoadStatesAsync() { + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + var StateLoaded = 0; - using var client = new HttpClient(); - //TODO: can this be improved? - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); - var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/v3/rooms/{RoomId}/state"); - // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); - //var _events = await response.Content.ReadFromJsonAsync>(); - var _data = await response.Content.ReadAsStreamAsync(); - var __events = JsonSerializer.DeserializeAsyncEnumerable(_data); - await foreach (var _ev in __events) { - var e = new StateEventResponse { - Type = _ev.Type, - StateKey = _ev.StateKey, - OriginServerTs = _ev.OriginServerTs, - Content = _ev.Content - }; - Events.Add(e); - if (string.IsNullOrEmpty(e.StateKey)) { - FilteredEvents.Add(e); + var response = (await 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) { @@ -104,8 +96,8 @@ await Task.Delay(1); FilteredEvents = _FilteredEvents; - if (_shownType != null) - shownEventJson = _FilteredEvents.Where(x => x.Type == _shownType).First().Content.ToJson(indent: true, ignoreNull: true); + if (_shownType is not null) + shownEventJson = _FilteredEvents.Where(x => x.Type == _shownType).First().RawContent.ToJson(indent: true, ignoreNull: true); StateHasChanged(); } diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor index 82b5d75..55c44d9 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateRoomList.razor @@ -22,13 +22,10 @@ else { public List Rooms { get; set; } = new(); protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) { - NavigationManager.NavigateTo("/Login"); - return; - } - Rooms = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Select(x => x.RoomId).ToList(); + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + Rooms = (await hs.GetJoinedRooms()).Select(x => x.RoomId).ToList(); Console.WriteLine("Fetched joined rooms!"); } diff --git a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor index ff1d9ac..a0072ab 100644 --- a/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor +++ b/MatrixRoomUtils.Web/Pages/RoomState/RoomStateViewerPage.razor @@ -1,6 +1,7 @@ @page "/RoomStateViewer/{RoomId}" @using System.Net.Http.Headers @using System.Text.Json +@using MatrixRoomUtils.Core.Responses @inject ILocalStorageService LocalStorage @inject NavigationManager NavigationManager

Room state viewer - Viewing @RoomId

@@ -18,18 +19,18 @@ - @foreach (var stateEvent in FilteredEvents.Where(x => x.state_key == "").OrderBy(x => x.origin_server_ts)) { + @foreach (var stateEvent in FilteredEvents.Where(x => x.StateKey == "").OrderBy(x => x.OriginServerTs)) { - @stateEvent.type + @stateEvent.Type -
@stateEvent.content
+
@stateEvent.RawContent.ToJson()
} -@foreach (var group in FilteredEvents.GroupBy(x => x.state_key).OrderBy(x => x.Key).Where(x => x.Key != "")) { +@foreach (var group in FilteredEvents.GroupBy(x => x.StateKey).OrderBy(x => x.Key).Where(x => x.Key != "")) {
@group.Key @@ -40,11 +41,11 @@ - @foreach (var stateEvent in group.OrderBy(x => x.origin_server_ts)) { + @foreach (var stateEvent in group.OrderBy(x => x.OriginServerTs)) { - + } @@ -64,17 +65,14 @@ [Parameter] public string? RoomId { get; set; } - public List FilteredEvents { get; set; } = new(); - public List Events { get; set; } = new(); + public List FilteredEvents { get; set; } = new(); + public List Events { get; set; } = new(); public string status = ""; protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); await base.OnInitializedAsync(); - if (RuntimeCache.CurrentHomeServer == null) { - NavigationManager.NavigateTo("/Login"); - return; - } + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; RoomId = RoomId.Replace('~', '.'); await LoadStatesAsync(); Console.WriteLine("Policy list editor initialized!"); @@ -84,24 +82,13 @@ private async Task LoadStatesAsync() { var StateLoaded = 0; - //TODO: can we improve this? - using var client = new HttpClient(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", RuntimeCache.CurrentHomeServer.AccessToken); - var response = await client.GetAsync($"{RuntimeCache.CurrentHomeServer.FullHomeServerDomain}/_matrix/client/v3/rooms/{RoomId}/state"); - // var response = await client.GetAsync($"http://localhost:5117/matrix-hq-state.json"); - //var _events = await response.Content.ReadFromJsonAsync>(); - var _data = await response.Content.ReadAsStreamAsync(); - var __events = JsonSerializer.DeserializeAsyncEnumerable(_data); - await foreach (var _ev in __events) { - var e = new PreRenderedStateEvent { - type = _ev.type, - state_key = _ev.state_key, - origin_server_ts = _ev.origin_server_ts, - content = _ev.content.ToJson(true, true) - }; - Events.Add(e); - if (string.IsNullOrEmpty(e.state_key)) { - FilteredEvents.Add(e); + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + var response = (await 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) { @@ -121,7 +108,7 @@ await Task.Delay(1); var _FilteredEvents = Events; if (!ShowMembershipEvents) - _FilteredEvents = _FilteredEvents.Where(x => x.type != "m.room.member").ToList(); + _FilteredEvents = _FilteredEvents.Where(x => x.Type != "m.room.member").ToList(); status = "Done, rerendering!"; StateHasChanged(); @@ -130,17 +117,6 @@ 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 { diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor index 17551c9..20ddd0d 100644 --- a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor +++ b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor @@ -1,20 +1,19 @@ @page "/Rooms"

Room list

-@if (Rooms != null) { +@if (Rooms is not null) { } @code { - private List Rooms { get; set; } + private List Rooms { get; set; } - protected override async Task OnInitializedAsync() - { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - - Rooms = await RuntimeCache.CurrentHomeServer.GetJoinedRooms(); + protected override async Task OnInitializedAsync() { + var hs = await MRUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + Rooms = await hs.GetJoinedRooms(); await base.OnInitializedAsync(); } diff --git a/MatrixRoomUtils.Web/Program.cs b/MatrixRoomUtils.Web/Program.cs index 4ff10b4..54d4f0d 100644 --- a/MatrixRoomUtils.Web/Program.cs +++ b/MatrixRoomUtils.Web/Program.cs @@ -40,5 +40,5 @@ builder.Services.AddScoped(x => ); builder.Services.AddRoryLibMatrixServices(); - +builder.Services.AddScoped(); await builder.Build().RunAsync(); \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor b/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor deleted file mode 100644 index 975da43..0000000 --- a/MatrixRoomUtils.Web/Shared/IndexComponents/IndexUserItem.razor +++ /dev/null @@ -1,54 +0,0 @@ -@using MatrixRoomUtils.Core.Extensions -@inject ILocalStorageService LocalStorage -@inject NavigationManager NavigationManager - -
- -

- - @User.Profile.DisplayName on @User.LoginResponse.HomeServer - Remove -

-

Member of @_roomCount rooms

- -
- -@code { - - [Parameter] - public UserInfo User { get; set; } = null!; - - private string? _avatarUrl { get; set; } - private int _roomCount { get; set; } = 0; - - protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - - if (User.Profile.AvatarUrl != null && User.Profile.AvatarUrl != "") - _avatarUrl = RuntimeCache.CurrentHomeServer.ResolveMediaUri(User.Profile.AvatarUrl); - else _avatarUrl = "https://api.dicebear.com/6.x/identicon/svg?seed=" + User.LoginResponse.UserId; - try { - _roomCount = (await RuntimeCache.CurrentHomeServer.GetJoinedRooms()).Count; - } - catch { - _roomCount = -1; - } - await base.OnInitializedAsync(); - } - - private async Task RemoveUser() { - Console.WriteLine(User.ToJson()); - RuntimeCache.LoginSessions.Remove(User.AccessToken); - - StateHasChanged(); - } - - private async Task SetCurrent() { - RuntimeCache.LastUsedToken = User.AccessToken; - //RuntimeCache.CurrentHomeserver = await MatrixAuth.ResolveHomeserverFromWellKnown(LocalStorageWrapper.LoginSessions[Token].LoginResponse.HomeServer); - await LocalStorageWrapper.SaveToLocalStorage(LocalStorage); - await LocalStorageWrapper.InitialiseRuntimeVariables(LocalStorage); - StateHasChanged(); - } - -} \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor index 44f3b0a..a498c70 100644 --- a/MatrixRoomUtils.Web/Shared/InlineUserItem.razor +++ b/MatrixRoomUtils.Web/Shared/InlineUserItem.razor @@ -1,11 +1,12 @@ @using MatrixRoomUtils.Core.Responses @using MatrixRoomUtils.Core.StateEventTypes +@using MatrixRoomUtils.Core.Helpers
- + @ProfileName
- @if (ChildContent != null) { + @if (ChildContent is not null) { @ChildContent }
@@ -21,7 +22,7 @@ public ProfileResponse User { get; set; } [Parameter] - public string UserId { get; set; } + public string? UserId { get; set; } [Parameter] public string? ProfileAvatar { get; set; } = null; @@ -33,19 +34,16 @@ protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - + var hs = await MRUStorage.GetCurrentSession(); + await _semaphoreSlim.WaitAsync(); - if (User == null) { - if (UserId == null) { - throw new ArgumentNullException(nameof(UserId)); - } - User = await RuntimeCache.CurrentHomeServer.GetProfile(UserId); - } + if (User == null && UserId == null) + throw new ArgumentNullException(nameof(UserId)); + User ??= await hs.GetProfile(UserId); + - // UserId = User.; - ProfileAvatar ??= RuntimeCache.CurrentHomeServer.ResolveMediaUri(User.AvatarUrl); + ProfileAvatar ??= MediaResolver.ResolveMediaUri(hs.FullHomeServerDomain, User.AvatarUrl); ProfileName ??= User.DisplayName; _semaphoreSlim.Release(); diff --git a/MatrixRoomUtils.Web/Shared/LogView.razor b/MatrixRoomUtils.Web/Shared/LogView.razor index 2f83cb2..d541b82 100644 --- a/MatrixRoomUtils.Web/Shared/LogView.razor +++ b/MatrixRoomUtils.Web/Shared/LogView.razor @@ -1,42 +1,41 @@ -@using System.Text -@if (LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) { - Logs -
-
-        @_stringBuilder
-    
-} - -@code { - StringBuilder _stringBuilder = new(); - - protected override async Task OnInitializedAsync() { - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) { - Console.WriteLine("Console logging disabled!"); - var _sw = new StringWriter(); - Console.SetOut(_sw); - Console.SetError(_sw); - return; - } - if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) return; - //intecept stdout with textwriter to get logs - var sw = new StringWriter(_stringBuilder); - Console.SetOut(sw); - Console.SetError(sw); - //keep updated - var length = 0; - Task.Run(async () => { - while (true) { - await Task.Delay(100); - if (_stringBuilder.Length != length) { - StateHasChanged(); - length = _stringBuilder.Length; - } - } - // ReSharper disable once FunctionNeverReturns - This is intentional behavior - }); - await base.OnInitializedAsync(); - } - -} \ No newline at end of file +@* @using System.Text *@ +@* @if (LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) { *@ +@* Logs *@ +@*
*@ +@*
 *@
+@*         @_stringBuilder *@
+@*     
*@ +@* } *@ +@* *@ +@* @code { *@ +@* StringBuilder _stringBuilder = new(); *@ +@* *@ +@* protected override async Task OnInitializedAsync() { *@ +@* if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) { *@ +@* Console.WriteLine("Console logging disabled!"); *@ +@* var _sw = new StringWriter(); *@ +@* Console.SetOut(_sw); *@ +@* Console.SetError(_sw); *@ +@* return; *@ +@* } *@ +@* if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableLogViewers) return; *@ +@* //intecept stdout with textwriter to get logs *@ +@* var sw = new StringWriter(_stringBuilder); *@ +@* Console.SetOut(sw); *@ +@* Console.SetError(sw); *@ +@* //keep updated *@ +@* var length = 0; *@ +@* Task.Run(async () => { *@ +@* while (true) { *@ +@* await Task.Delay(100); *@ +@* if (_stringBuilder.Length != length) { *@ +@* StateHasChanged(); *@ +@* length = _stringBuilder.Length; *@ +@* } *@ +@* } *@ +@* // ReSharper disable once FunctionNeverReturns - This is intentional behavior *@ +@* }); *@ +@* await base.OnInitializedAsync(); *@ +@* } *@ +@* *@ +@* } *@ \ No newline at end of file diff --git a/MatrixRoomUtils.Web/Shared/MainLayout.razor b/MatrixRoomUtils.Web/Shared/MainLayout.razor index 317f9e6..691acbb 100644 --- a/MatrixRoomUtils.Web/Shared/MainLayout.razor +++ b/MatrixRoomUtils.Web/Shared/MainLayout.razor @@ -29,14 +29,14 @@ using var hc = new HttpClient(); var hr = await hc.SendAsync(new HttpRequestMessage(HttpMethod.Head, NavigationManager.ToAbsoluteUri("/MRU-BIN.tar.xz").AbsoluteUri)); showDownload = hr.StatusCode == HttpStatusCode.OK; - - await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage); - if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) { - Console.WriteLine("Console logging disabled!"); - var sw = new StringWriter(); - Console.SetOut(sw); - Console.SetError(sw); - } + + // TODO: fix console logging toggle + // if (!LocalStorageWrapper.Settings.DeveloperSettings.EnableConsoleLogging) { + // Console.WriteLine("Console logging disabled!"); + // var sw = new StringWriter(); + // Console.SetOut(sw); + // Console.SetError(sw); + // } await base.OnInitializedAsync(); } diff --git a/MatrixRoomUtils.Web/Shared/NavMenu.razor b/MatrixRoomUtils.Web/Shared/NavMenu.razor index 9ddbd84..5f9ad8a 100644 --- a/MatrixRoomUtils.Web/Shared/NavMenu.razor +++ b/MatrixRoomUtils.Web/Shared/NavMenu.razor @@ -23,11 +23,6 @@
Main tools

-
@stateEvent.type@stateEvent.Type -
@stateEvent.content
+
@stateEvent.RawContent.ToJson()
` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-`` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-`