From ede3857084bc7c6e65b7d36cbf913b09596e2787 Mon Sep 17 00:00:00 2001
From: Rory&
Date: Mon, 8 Jan 2024 13:55:15 +0100
Subject: Internal changes to policy list viewer (extensibility), fix
duplicating change handler for room list page (performance), use /state in
room list page before sync
---
.editorconfig | 15 +-
.idea/.idea.MatrixRoomUtils/.idea/avalonia.xml | 2 +
LibMatrix | 2 +-
.../FileStorageProvider.cs | 49 +++
.../MatrixRoomUtils.Abstractions.csproj | 12 +
MatrixRoomUtils.Abstractions/RoomInfo.cs | 100 ++++++
MatrixRoomUtils.Desktop/App.axaml.cs | 12 +-
.../Components/NavigationStack.axaml | 12 +-
.../Components/NavigationStack.axaml.cs | 24 +-
.../Components/Pages/RoomList.axaml | 21 ++
.../Components/Pages/RoomList.axaml.cs | 15 +
.../Components/RoomListEntry.axaml | 9 +-
.../Components/RoomListEntry.axaml.cs | 31 +-
MatrixRoomUtils.Desktop/FileStorageProvider.cs | 49 ---
MatrixRoomUtils.Desktop/LoginWindow.axaml | 18 +-
MatrixRoomUtils.Desktop/LoginWindow.axaml.cs | 1 +
MatrixRoomUtils.Desktop/MRUStorageWrapper.cs | 6 +-
MatrixRoomUtils.Desktop/MainWindow.axaml.cs | 13 +-
.../MatrixRoomUtils.Desktop.csproj | 29 +-
.../Properties/launchSettings.json | 3 +-
MatrixRoomUtils.Desktop/RoomInfo.cs | 40 ---
MatrixRoomUtils.Desktop/SentryService.cs | 2 +-
MatrixRoomUtils.LibDMSpace/DMSpaceRoom.cs | 6 +-
MatrixRoomUtils.Web/Classes/RoomInfo.cs | 99 ------
MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj | 36 +--
MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor | 1 +
MatrixRoomUtils.Web/Pages/Index.razor | 89 ++++--
.../ModerationUtilities/UserRoomHistory.razor | 1 +
MatrixRoomUtils.Web/Pages/Rooms/Index.razor | 102 ++++---
MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor | 336 ++++++++++++---------
MatrixRoomUtils.Web/Pages/User/DMManager.razor | 2 +-
.../Pages/User/DMSpaceStages/DMSpaceStage2.razor | 5 +-
.../Pages/User/DMSpaceStages/DMSpaceStage3.razor | 6 +-
MatrixRoomUtils.Web/Pages/User/Profile.razor | 78 ++---
MatrixRoomUtils.Web/Shared/RoomList.razor | 29 +-
.../RoomListComponents/RoomListCategory.razor | 13 +-
.../Shared/RoomListComponents/RoomListSpace.razor | 1 +
MatrixRoomUtils.Web/Shared/RoomListItem.razor | 13 +-
MatrixRoomUtils.Web/wwwroot/css/app.css | 10 +-
MatrixRoomUtils.sln | 6 +
MatrixRoomUtils.sln.DotSettings.user | 9 +-
MxApiExtensions | 2 +-
nuget.config | 12 +
43 files changed, 752 insertions(+), 569 deletions(-)
create mode 100644 MatrixRoomUtils.Abstractions/FileStorageProvider.cs
create mode 100644 MatrixRoomUtils.Abstractions/MatrixRoomUtils.Abstractions.csproj
create mode 100644 MatrixRoomUtils.Abstractions/RoomInfo.cs
create mode 100644 MatrixRoomUtils.Desktop/Components/Pages/RoomList.axaml
create mode 100644 MatrixRoomUtils.Desktop/Components/Pages/RoomList.axaml.cs
delete mode 100644 MatrixRoomUtils.Desktop/FileStorageProvider.cs
delete mode 100644 MatrixRoomUtils.Desktop/RoomInfo.cs
delete mode 100644 MatrixRoomUtils.Web/Classes/RoomInfo.cs
create mode 100644 nuget.config
diff --git a/.editorconfig b/.editorconfig
index e8dd2db..3bc1adf 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -371,7 +371,7 @@ dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
-file_header_template = # ReSharper properties
+file_header_template = # ReSharper properties
resharper_alignment_tab_fill_style = use_spaces
resharper_align_first_arg_by_paren = false
@@ -606,8 +606,8 @@ resharper_line_break_before_requires_clause = do_not_change
resharper_linkage_specification_braces = end_of_line
resharper_linkage_specification_indentation = none
resharper_local_function_body = expression_body
-resharper_macro_block_begin =
-resharper_macro_block_end =
+resharper_macro_block_begin =
+resharper_macro_block_end =
resharper_max_array_initializer_elements_on_line = 10000
resharper_max_attribute_length_for_same_line = 38
resharper_max_enum_members_on_line = 3
@@ -622,7 +622,7 @@ resharper_new_line_before_catch = true
resharper_new_line_before_else = true
resharper_new_line_before_enumerators = true
resharper_normalize_tag_names = false
-resharper_no_indent_inside_elements =
+resharper_no_indent_inside_elements =
resharper_no_indent_inside_if_element_longer_than = 2000000
resharper_null_checking_pattern_style = not_null_pattern
resharper_object_creation_when_type_evident = target_typed
@@ -675,7 +675,7 @@ resharper_requires_expression_braces = next_line
resharper_resx_allow_far_alignment = false
resharper_resx_attribute_indent = single_indent
resharper_resx_insert_final_newline = false
-resharper_resx_linebreak_before_elements =
+resharper_resx_linebreak_before_elements =
resharper_resx_max_blank_lines_between_tags = 0
resharper_resx_max_line_length = 2147483647
resharper_resx_pi_attribute_style = do_not_touch
@@ -902,7 +902,7 @@ resharper_xmldoc_wrap_text = true
resharper_xml_allow_far_alignment = false
resharper_xml_attribute_indent = align_by_first_attribute
resharper_xml_insert_final_newline = false
-resharper_xml_linebreak_before_elements =
+resharper_xml_linebreak_before_elements =
resharper_xml_max_blank_lines_between_tags = 2
resharper_xml_max_line_length = 180
resharper_xml_pi_attribute_style = do_not_touch
@@ -1792,3 +1792,6 @@ resharper_xaml_xaml_xamarin_forms_data_type_and_binding_context_type_mismatched_
resharper_xaml_x_key_attribute_disallowed_highlighting = error
resharper_xunit_xunit_test_with_console_output_highlighting = warning
resharper_zero_index_from_end_highlighting = warning
+
+# ReSharper properties
+resharper_csharp_int_align_comments = true
diff --git a/.idea/.idea.MatrixRoomUtils/.idea/avalonia.xml b/.idea/.idea.MatrixRoomUtils/.idea/avalonia.xml
index c468b91..f74ab1c 100644
--- a/.idea/.idea.MatrixRoomUtils/.idea/avalonia.xml
+++ b/.idea/.idea.MatrixRoomUtils/.idea/avalonia.xml
@@ -5,6 +5,8 @@
+
+
diff --git a/LibMatrix b/LibMatrix
index 5affd9f..0f9f9e9 160000
--- a/LibMatrix
+++ b/LibMatrix
@@ -1 +1 @@
-Subproject commit 5affd9f061e75f6575a2fe6715f9e8757cfe87e8
+Subproject commit 0f9f9e9201bbbed5981135d67e1265fd0f31aeff
diff --git a/MatrixRoomUtils.Abstractions/FileStorageProvider.cs b/MatrixRoomUtils.Abstractions/FileStorageProvider.cs
new file mode 100644
index 0000000..73d5604
--- /dev/null
+++ b/MatrixRoomUtils.Abstractions/FileStorageProvider.cs
@@ -0,0 +1,49 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Text.Json;
+using ArcaneLibs.Extensions;
+using LibMatrix.Extensions;
+using LibMatrix.Interfaces.Services;
+using Microsoft.Extensions.Logging;
+
+namespace MatrixRoomUtils.Abstractions;
+
+public class FileStorageProvider : IStorageProvider {
+ private readonly ILogger _logger;
+
+ public string TargetPath { get; }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ ///
+ public FileStorageProvider(string targetPath) {
+ // new Logger(new LoggerFactory()).LogInformation("test");
+ Console.WriteLine($"Initialised FileStorageProvider with path {targetPath}");
+ TargetPath = targetPath;
+ if (!Directory.Exists(targetPath)) {
+ Directory.CreateDirectory(targetPath);
+ }
+ }
+
+ public async Task SaveObjectAsync(string key, T value) => await File.WriteAllTextAsync(Path.Join(TargetPath, key), value?.ToJson());
+
+ [RequiresUnreferencedCode("This API uses reflection to deserialize JSON")]
+ public async Task LoadObjectAsync(string key) => JsonSerializer.Deserialize(await File.ReadAllTextAsync(Path.Join(TargetPath, key)));
+
+ public Task ObjectExistsAsync(string key) => Task.FromResult(File.Exists(Path.Join(TargetPath, key)));
+
+ public Task> GetAllKeysAsync() => Task.FromResult(Directory.GetFiles(TargetPath).Select(Path.GetFileName).ToList());
+
+ public Task DeleteObjectAsync(string key) {
+ File.Delete(Path.Join(TargetPath, key));
+ return Task.CompletedTask;
+ }
+
+ public async Task SaveStreamAsync(string key, Stream stream) {
+ Directory.CreateDirectory(Path.GetDirectoryName(Path.Join(TargetPath, key)) ?? throw new InvalidOperationException());
+ await using var fileStream = File.Create(Path.Join(TargetPath, key));
+ await stream.CopyToAsync(fileStream);
+ }
+
+ public Task LoadStreamAsync(string key) => Task.FromResult(File.Exists(Path.Join(TargetPath, key)) ? File.OpenRead(Path.Join(TargetPath, key)) : null);
+}
diff --git a/MatrixRoomUtils.Abstractions/MatrixRoomUtils.Abstractions.csproj b/MatrixRoomUtils.Abstractions/MatrixRoomUtils.Abstractions.csproj
new file mode 100644
index 0000000..1665ff0
--- /dev/null
+++ b/MatrixRoomUtils.Abstractions/MatrixRoomUtils.Abstractions.csproj
@@ -0,0 +1,12 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
diff --git a/MatrixRoomUtils.Abstractions/RoomInfo.cs b/MatrixRoomUtils.Abstractions/RoomInfo.cs
new file mode 100644
index 0000000..6db3447
--- /dev/null
+++ b/MatrixRoomUtils.Abstractions/RoomInfo.cs
@@ -0,0 +1,100 @@
+using System.Collections.ObjectModel;
+using System.Text.Json.Nodes;
+using ArcaneLibs;
+using LibMatrix;
+using LibMatrix.EventTypes.Spec.State;
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.RoomTypes;
+
+namespace MatrixRoomUtils.Abstractions;
+
+public class RoomInfo : NotifyPropertyChanged {
+ public required GenericRoom Room { get; set; }
+ public ObservableCollection StateEvents { get; } = new();
+
+ public async Task GetStateEvent(string type, string stateKey = "") {
+ var @event = StateEvents.FirstOrDefault(x => x.Type == type && x.StateKey == stateKey);
+ if (@event is not null) return @event;
+ @event = new StateEventResponse {
+ RoomId = Room.RoomId,
+ Type = type,
+ StateKey = stateKey,
+ Sender = null, //TODO implement
+ EventId = null
+ };
+ // if (Room is null) return null;
+ try {
+ @event.RawContent = await Room.GetStateAsync(type, stateKey);
+ }
+ catch (MatrixException e) {
+ if (e is { ErrorCode: "M_NOT_FOUND" }) {
+ if (type == "m.room.name")
+ @event = new() {
+ Type = type,
+ StateKey = stateKey,
+ TypedContent = new RoomNameEventContent() {
+ Name = await Room.GetNameOrFallbackAsync()
+ },
+ //TODO implement
+ RoomId = null,
+ Sender = null,
+ EventId = null
+ };
+ else
+ @event.RawContent = default!;
+ }
+ else {
+ throw;
+ }
+ }
+
+ StateEvents.Add(@event);
+ return @event;
+ }
+
+ public string? RoomIcon {
+ get => _roomIcon ?? "https://api.dicebear.com/6.x/identicon/svg?seed=" + Room.RoomId;
+ set => SetField(ref _roomIcon, value);
+ }
+
+ public string? RoomName {
+ get => _roomName ?? DefaultRoomName ?? Room.RoomId;
+ set => SetField(ref _roomName, value);
+ }
+
+ public RoomCreateEventContent? CreationEventContent {
+ get => _creationEventContent;
+ set => SetField(ref _creationEventContent, value);
+ }
+
+ public string? RoomCreator {
+ get => _roomCreator;
+ set => SetField(ref _roomCreator, value);
+ }
+
+ // public string? GetRoomIcon() => (StateEvents.FirstOrDefault(x => x?.Type == RoomAvatarEventContent.EventId)?.TypedContent as RoomAvatarEventContent)?.Url ??
+ // "mxc://rory.gay/dgP0YPjJEWaBwzhnbyLLwGGv";
+
+ private string? _roomIcon;
+ private string? _roomName;
+ private RoomCreateEventContent? _creationEventContent;
+ private string? _roomCreator;
+
+ public string? DefaultRoomName { get; set; }
+
+ public RoomInfo() {
+ StateEvents.CollectionChanged += (_, args) => {
+ if (args.NewItems is { Count: > 0 })
+ foreach (StateEventResponse newState in args.NewItems) { // TODO: switch statement benchmark?
+ if (newState.Type == RoomNameEventContent.EventId && newState.TypedContent is RoomNameEventContent roomNameContent)
+ RoomName = roomNameContent.Name;
+ else if (newState is { Type: RoomAvatarEventContent.EventId, TypedContent: RoomAvatarEventContent roomAvatarContent })
+ RoomIcon = roomAvatarContent.Url;
+ else if (newState is { Type: RoomCreateEventContent.EventId, TypedContent: RoomCreateEventContent roomCreateContent }) {
+ CreationEventContent = roomCreateContent;
+ RoomCreator = newState.Sender;
+ }
+ }
+ };
+ }
+}
diff --git a/MatrixRoomUtils.Desktop/App.axaml.cs b/MatrixRoomUtils.Desktop/App.axaml.cs
index 3963be6..aeb154c 100644
--- a/MatrixRoomUtils.Desktop/App.axaml.cs
+++ b/MatrixRoomUtils.Desktop/App.axaml.cs
@@ -1,7 +1,9 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
+using Avalonia.Styling;
using LibMatrix.Services;
+using MatrixRoomUtils.Abstractions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -10,10 +12,6 @@ namespace MatrixRoomUtils.Desktop;
public partial class App : Application {
public IHost host { get; set; }
- public override void Initialize() {
- AvaloniaXamlLoader.Load(this);
- }
-
public override void OnFrameworkInitializationCompleted() {
host = Host.CreateDefaultBuilder().ConfigureServices((ctx, services) => {
services.AddSingleton();
@@ -42,6 +40,10 @@ public partial class App : Application {
var scope = scopeFac.CreateScope();
desktop.MainWindow = scope.ServiceProvider.GetRequiredService();
}
+
+ if(Environment.GetEnvironmentVariable("AVALONIA_THEME")?.Equals("dark", StringComparison.OrdinalIgnoreCase) ?? false)
+ RequestedThemeVariant = ThemeVariant.Dark;
+
base.OnFrameworkInitializationCompleted();
}
-}
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Desktop/Components/NavigationStack.axaml b/MatrixRoomUtils.Desktop/Components/NavigationStack.axaml
index c773b8d..bc6b75d 100644
--- a/MatrixRoomUtils.Desktop/Components/NavigationStack.axaml
+++ b/MatrixRoomUtils.Desktop/Components/NavigationStack.axaml
@@ -4,9 +4,9 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MatrixRoomUtils.Desktop.Components.NavigationStack">
-
-
-
-
-
-
+
+ NagivationStack
+
+
+
+
\ No newline at end of file
diff --git a/MatrixRoomUtils.Desktop/Components/NavigationStack.axaml.cs b/MatrixRoomUtils.Desktop/Components/NavigationStack.axaml.cs
index d6343e2..92c617b 100644
--- a/MatrixRoomUtils.Desktop/Components/NavigationStack.axaml.cs
+++ b/MatrixRoomUtils.Desktop/Components/NavigationStack.axaml.cs
@@ -1,4 +1,5 @@
using Avalonia.Controls;
+using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace MatrixRoomUtils.Desktop.Components;
@@ -8,12 +9,24 @@ public partial class NavigationStack : UserControl {
InitializeComponent();
}
- private void InitializeComponent() {
- AvaloniaXamlLoader.Load(this);
+ // private void InitializeComponent() {
+ // AvaloniaXamlLoader.Load(this);
+ // buildView();
+ // }
+
+ protected override void OnLoaded(RoutedEventArgs e) {
+ base.OnLoaded(e);
buildView();
}
-
+
private void buildView() {
+ if (navPanel is null) {
+ Console.WriteLine("NavigationStack buildView called while navpanel is null!");
+ // await Task.Delay(100);
+ // if (navPanel is null)
+ // await buildView();
+ // else Console.WriteLine("navpanel is not null!");
+ }
navPanel.Children.Clear();
foreach (var item in _stack) {
Button btn = new() {
@@ -25,7 +38,7 @@ public partial class NavigationStack : UserControl {
};
navPanel.Children.Add(btn);
}
- content = Current?.View ?? new UserControl();
+ content.Content = Current?.View ?? new UserControl();
}
@@ -44,13 +57,16 @@ public partial class NavigationStack : UserControl {
Name = name,
View = view
});
+ buildView();
}
public void Pop() {
_stack.RemoveAt(_stack.Count - 1);
+ buildView();
}
public void PopTo(int index) {
_stack.RemoveRange(index, _stack.Count - index);
+ buildView();
}
}
diff --git a/MatrixRoomUtils.Desktop/Components/Pages/RoomList.axaml b/MatrixRoomUtils.Desktop/Components/Pages/RoomList.axaml
new file mode 100644
index 0000000..0e43d99
--- /dev/null
+++ b/MatrixRoomUtils.Desktop/Components/Pages/RoomList.axaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
diff --git a/MatrixRoomUtils.Desktop/Components/Pages/RoomList.axaml.cs b/MatrixRoomUtils.Desktop/Components/Pages/RoomList.axaml.cs
new file mode 100644
index 0000000..53c3063
--- /dev/null
+++ b/MatrixRoomUtils.Desktop/Components/Pages/RoomList.axaml.cs
@@ -0,0 +1,15 @@
+using System.Collections.ObjectModel;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using MatrixRoomUtils.Abstractions;
+
+namespace MatrixRoomUtils.Desktop.Components.Pages;
+
+public partial class RoomList : UserControl {
+ private ObservableCollection Rooms { get; set; } = new();
+
+ public RoomList() {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml b/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml
index 09fe52b..db22ccc 100644
--- a/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml
+++ b/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml
@@ -2,10 +2,15 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:components="clr-namespace:MatrixRoomUtils.Desktop.Components"
mc:Ignorable="d" d:DesignWidth="250" d:DesignHeight="32"
- x:Class="MatrixRoomUtils.Desktop.Components.RoomListEntry">
+ x:Class="MatrixRoomUtils.Desktop.Components.RoomListEntry"
+
+ x:DataType="components:RoomListEntry"
+ DataContext="{Binding $self}"
+ >
-
+
diff --git a/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs b/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs
index 69458aa..73115a2 100644
--- a/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs
+++ b/MatrixRoomUtils.Desktop/Components/RoomListEntry.axaml.cs
@@ -7,29 +7,28 @@ using LibMatrix.EventTypes.Spec.State.RoomInfo;
using LibMatrix.Helpers;
using LibMatrix.Interfaces.Services;
using LibMatrix.Services;
+using MatrixRoomUtils.Abstractions;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
namespace MatrixRoomUtils.Desktop.Components;
public partial class RoomListEntry : UserControl {
- private readonly IServiceScopeFactory _serviceScopeFactory;
- private readonly RoomInfo _roomInfo;
+ public RoomInfo Room { get; set; }
- public RoomListEntry(IServiceScopeFactory serviceScopeFactory, RoomInfo roomInfo) {
- _serviceScopeFactory = serviceScopeFactory;
- _roomInfo = roomInfo;
+ public RoomListEntry() {
InitializeComponent();
}
protected override void OnLoaded(RoutedEventArgs e) {
base.OnLoaded(e);
- RoomName.Content = _roomInfo.Room.RoomId;
+ RoomName.Content = Room.Room.RoomId;
Task.WhenAll(GetRoomName(), GetRoomIcon());
}
private async Task GetRoomName() {
try {
- var nameEvent = await _roomInfo.GetStateEvent("m.room.name");
+ var nameEvent = await Room.GetStateEvent("m.room.name");
if (nameEvent?.TypedContent is RoomNameEventContent nameData)
RoomName.Content = nameData.Name;
}
@@ -41,18 +40,22 @@ public partial class RoomListEntry : UserControl {
private async Task GetRoomIcon() {
try {
- var avatarEvent = await _roomInfo.GetStateEvent("m.room.avatar");
+ using var hc = new HttpClient();
+ var avatarEvent = await Room.GetStateEvent("m.room.avatar");
if (avatarEvent?.TypedContent is RoomAvatarEventContent avatarData) {
var mxcUrl = avatarData.Url;
- await using var svc = _serviceScopeFactory.CreateAsyncScope();
- var hs = await svc.ServiceProvider.GetService()?.GetCurrentSessionOrPrompt()!;
- var hsResolver = svc.ServiceProvider.GetService();
- var storage = svc.ServiceProvider.GetService()?.CacheStorageProvider;
- var resolvedUrl = await hsResolver.ResolveMediaUri(hs.ServerName, mxcUrl);
+ var resolvedUrl = await Room.Room.GetResolvedRoomAvatarUrlAsync();
+
+ // await using var svc = _serviceScopeFactory.CreateAsyncScope();
+ // var hs = await svc.ServiceProvider.GetService()?.GetCurrentSessionOrPrompt()!;
+ // var hsResolver = svc.ServiceProvider.GetService();
+ // var storage = svc.ServiceProvider.GetService()?.CacheStorageProvider;
+ // var resolvedUrl = await hsResolver.ResolveMediaUri(hs.ServerName, mxcUrl);
+ var storage = new FileStorageProvider("cache");
var storageKey = $"media/{mxcUrl.Replace("mxc://", "").Replace("/", ".")}";
try {
if (!await storage.ObjectExistsAsync(storageKey))
- await storage.SaveStreamAsync(storageKey, await hs.ClientHttpClient.GetStreamAsync(resolvedUrl));
+ await storage.SaveStreamAsync(storageKey, await hc.GetStreamAsync(resolvedUrl));
RoomIcon.Source = new Bitmap(await storage.LoadStreamAsync(storageKey) ?? throw new NullReferenceException());
}
diff --git a/MatrixRoomUtils.Desktop/FileStorageProvider.cs b/MatrixRoomUtils.Desktop/FileStorageProvider.cs
deleted file mode 100644
index 0429d1a..0000000
--- a/MatrixRoomUtils.Desktop/FileStorageProvider.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Text.Json;
-using ArcaneLibs.Extensions;
-using LibMatrix.Extensions;
-using LibMatrix.Interfaces.Services;
-using Microsoft.Extensions.Logging;
-
-namespace MatrixRoomUtils.Desktop;
-
-public class FileStorageProvider : IStorageProvider {
- private readonly ILogger _logger;
-
- public string TargetPath { get; }
-
- ///
- /// Creates a new instance of .
- ///
- ///
- public FileStorageProvider(string targetPath) {
- new Logger(new LoggerFactory()).LogInformation("test");
- Console.WriteLine($"Initialised FileStorageProvider with path {targetPath}");
- TargetPath = targetPath;
- if (!Directory.Exists(targetPath)) {
- Directory.CreateDirectory(targetPath);
- }
- }
-
- public async Task SaveObjectAsync(string key, T value) => await File.WriteAllTextAsync(Path.Join(TargetPath, key), value?.ToJson());
-
- [RequiresUnreferencedCode("This API uses reflection to deserialize JSON")]
- public async Task LoadObjectAsync(string key) => JsonSerializer.Deserialize(await File.ReadAllTextAsync(Path.Join(TargetPath, key)));
-
- public Task ObjectExistsAsync(string key) => Task.FromResult(File.Exists(Path.Join(TargetPath, key)));
-
- public Task> GetAllKeysAsync() => Task.FromResult(Directory.GetFiles(TargetPath).Select(Path.GetFileName).ToList());
-
- public Task DeleteObjectAsync(string key) {
- File.Delete(Path.Join(TargetPath, key));
- return Task.CompletedTask;
- }
-
- public async Task SaveStreamAsync(string key, Stream stream) {
- Directory.CreateDirectory(Path.GetDirectoryName(Path.Join(TargetPath, key)) ?? throw new InvalidOperationException());
- await using var fileStream = File.Create(Path.Join(TargetPath, key));
- await stream.CopyToAsync(fileStream);
- }
-
- public Task LoadStreamAsync(string key) => Task.FromResult(File.Exists(Path.Join(TargetPath, key)) ? File.OpenRead(Path.Join(TargetPath, key)) : null);
-}
diff --git a/MatrixRoomUtils.Desktop/LoginWindow.axaml b/MatrixRoomUtils.Desktop/LoginWindow.axaml
index a4600d5..fc0ee6f 100644
--- a/MatrixRoomUtils.Desktop/LoginWindow.axaml
+++ b/MatrixRoomUtils.Desktop/LoginWindow.axaml
@@ -4,14 +4,22 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:desktop="clr-namespace:MatrixRoomUtils.Desktop"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="MatrixRoomUtils.Desktop.LoginWindow"
Title="LoginWindow"
+ x:Class="MatrixRoomUtils.Desktop.LoginWindow"
x:DataType="desktop:LoginWindow"
DataContext="{Binding $self}"
- >
+ SizeToContent="WidthAndHeight" CanResize="False"
+ MinWidth="250">
-
-
+ Log in
+
+ User ID
+
+
+
+ Password
+
+
Login
-
+
\ No newline at end of file
diff --git a/MatrixRoomUtils.Desktop/LoginWindow.axaml.cs b/MatrixRoomUtils.Desktop/LoginWindow.axaml.cs
index 1f31b05..183c46b 100644
--- a/MatrixRoomUtils.Desktop/LoginWindow.axaml.cs
+++ b/MatrixRoomUtils.Desktop/LoginWindow.axaml.cs
@@ -2,6 +2,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
+using Avalonia.VisualTree;
namespace MatrixRoomUtils.Desktop;
diff --git a/MatrixRoomUtils.Desktop/MRUStorageWrapper.cs b/MatrixRoomUtils.Desktop/MRUStorageWrapper.cs
index 8a44518..b69c50d 100644
--- a/MatrixRoomUtils.Desktop/MRUStorageWrapper.cs
+++ b/MatrixRoomUtils.Desktop/MRUStorageWrapper.cs
@@ -1,3 +1,4 @@
+using Avalonia;
using LibMatrix;
using LibMatrix.Homeservers;
using LibMatrix.Responses;
@@ -74,8 +75,11 @@ public class MRUStorageWrapper(TieredStorageService storageService, HomeserverPr
if (session is null) {
// _navigationManager.NavigateTo("/Login");
var wnd = new LoginWindow(this);
+ wnd.Position = MainWindow.Instance.Position + new PixelPoint(50, 50);
await wnd.ShowDialog(MainWindow.Instance);
- while (wnd.IsVisible) await Task.Delay(100);
+ while (wnd.IsVisible) {
+ await Task.Delay(100);
+ }
session = await GetCurrentSession();
}
diff --git a/MatrixRoomUtils.Desktop/MainWindow.axaml.cs b/MatrixRoomUtils.Desktop/MainWindow.axaml.cs
index 9db59c5..6ef573e 100644
--- a/MatrixRoomUtils.Desktop/MainWindow.axaml.cs
+++ b/MatrixRoomUtils.Desktop/MainWindow.axaml.cs
@@ -1,5 +1,7 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
+using MatrixRoomUtils.Abstractions;
+using MatrixRoomUtils.Desktop.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -26,7 +28,6 @@ public partial class MainWindow : Window {
_logger.LogInformation("Cache location: {}", _configuration.CacheStoragePath);
_logger.LogInformation("Data location: {}", _configuration.DataStoragePath);
-
// for (int i = 0; i < 100; i++) {
// roomList.Children.Add(new RoomListEntry());
// }
@@ -39,12 +40,18 @@ public partial class MainWindow : Window {
var rooms = await hs.GetJoinedRooms();
foreach (var room in rooms) {
// roomList.Children.Add(new RoomListEntry(_scopeFactory, new RoomInfo(room)));
+
+ windowContent.Push("home", new RoomListEntry() {
+ Room = new RoomInfo() {
+ Room = room
+ }
+ });
+ base.OnLoaded(e);
}
- base.OnLoaded(e);
}
// public Command
// protected void LoadedCommand() {
// _logger.LogInformation("async command");
// }
-}
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Desktop/MatrixRoomUtils.Desktop.csproj b/MatrixRoomUtils.Desktop/MatrixRoomUtils.Desktop.csproj
index 6d9fc0e..94bf245 100644
--- a/MatrixRoomUtils.Desktop/MatrixRoomUtils.Desktop.csproj
+++ b/MatrixRoomUtils.Desktop/MatrixRoomUtils.Desktop.csproj
@@ -10,29 +10,27 @@
preview
enable
true
- true
- true
- true
- true
- true
- true
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
@@ -46,4 +44,7 @@
Always
+
+
+
diff --git a/MatrixRoomUtils.Desktop/Properties/launchSettings.json b/MatrixRoomUtils.Desktop/Properties/launchSettings.json
index 997e294..36405e8 100644
--- a/MatrixRoomUtils.Desktop/Properties/launchSettings.json
+++ b/MatrixRoomUtils.Desktop/Properties/launchSettings.json
@@ -12,7 +12,8 @@
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
- "DOTNET_ENVIRONMENT": "Development"
+ "DOTNET_ENVIRONMENT": "Development",
+ "AVALONIA_THEME": "Dark"
}
},
"Local config": {
diff --git a/MatrixRoomUtils.Desktop/RoomInfo.cs b/MatrixRoomUtils.Desktop/RoomInfo.cs
deleted file mode 100644
index a562086..0000000
--- a/MatrixRoomUtils.Desktop/RoomInfo.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using LibMatrix;
-using LibMatrix.EventTypes;
-using LibMatrix.Interfaces;
-using LibMatrix.Responses;
-using LibMatrix.RoomTypes;
-
-namespace MatrixRoomUtils.Desktop;
-
-public class RoomInfo {
- public RoomInfo() { }
-
- public RoomInfo(GenericRoom room) {
- Room = room;
- }
-
- public GenericRoom Room { get; set; }
- public List StateEvents { get; init; } = new();
-
- public async Task GetStateEvent(string type, string stateKey = "") {
- var @event = StateEvents.FirstOrDefault(x => x.Type == type && x.StateKey == stateKey);
- if (@event is not null) return @event;
- @event = new StateEventResponse {
- RoomId = Room.RoomId,
- Type = type,
- StateKey = stateKey,
- Sender = null, //TODO: implement
- EventId = null
- };
- try {
- @event.TypedContent = await Room.GetStateAsync(type, stateKey);
- }
- catch (MatrixException e) {
- if (e is { ErrorCode: "M_NOT_FOUND" }) @event.TypedContent = default!;
- else throw;
- }
-
- StateEvents.Add(@event);
- return @event;
- }
-}
diff --git a/MatrixRoomUtils.Desktop/SentryService.cs b/MatrixRoomUtils.Desktop/SentryService.cs
index 648946c..26212fa 100644
--- a/MatrixRoomUtils.Desktop/SentryService.cs
+++ b/MatrixRoomUtils.Desktop/SentryService.cs
@@ -6,7 +6,7 @@ namespace MatrixRoomUtils.Desktop;
public class SentryService : IDisposable {
private IDisposable? _sentrySdkDisposable;
- public SentryService(IServiceScopeFactory scopeFactory, ILogger logger) {
+ public SentryService(IServiceScopeFactory scopeFactory, ILogger logger) {
var config = scopeFactory.CreateScope().ServiceProvider.GetRequiredService();
if (config.SentryDsn is null) {
logger.LogWarning("Sentry DSN is not set, skipping Sentry initialisation");
diff --git a/MatrixRoomUtils.LibDMSpace/DMSpaceRoom.cs b/MatrixRoomUtils.LibDMSpace/DMSpaceRoom.cs
index cbe2303..1cf7064 100644
--- a/MatrixRoomUtils.LibDMSpace/DMSpaceRoom.cs
+++ b/MatrixRoomUtils.LibDMSpace/DMSpaceRoom.cs
@@ -24,7 +24,7 @@ public class DMSpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomI
}
public async Task AddChildAsync(GenericRoom room) {
- var members = room.GetMembersAsync(true);
+ var members = room.GetMembersEnumerableAsync(true);
Dictionary memberCountByHs = new();
await foreach (var member in members) {
var server = member.StateKey.Split(':')[1];
@@ -61,12 +61,12 @@ public class DMSpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomI
}
};
// Add all DM room members
- var members = homeserver.GetRoom(roomid).GetMembersAsync();
+ var members = homeserver.GetRoom(roomid).GetMembersEnumerableAsync();
await foreach (var member in members)
if (member.StateKey != userId)
dri.RemoteUsers.Add(member.StateKey);
// Remove members of DM space
- members = GetMembersAsync();
+ members = GetMembersEnumerableAsync();
await foreach (var member in members)
if (dri.RemoteUsers.Contains(member.StateKey))
dri.RemoteUsers.Remove(member.StateKey);
diff --git a/MatrixRoomUtils.Web/Classes/RoomInfo.cs b/MatrixRoomUtils.Web/Classes/RoomInfo.cs
deleted file mode 100644
index 9d0cd59..0000000
--- a/MatrixRoomUtils.Web/Classes/RoomInfo.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using System.Collections.ObjectModel;
-using System.Text.Json.Nodes;
-using ArcaneLibs;
-using LibMatrix;
-using LibMatrix.EventTypes.Spec.State;
-using LibMatrix.EventTypes.Spec.State.RoomInfo;
-using LibMatrix.Interfaces;
-using LibMatrix.RoomTypes;
-
-namespace MatrixRoomUtils.Web.Classes;
-
-public class RoomInfo : NotifyPropertyChanged {
- public required GenericRoom Room { get; set; }
- public ObservableCollection StateEvents { get; } = new();
-
- public async Task GetStateEvent(string type, string stateKey = "") {
- var @event = StateEvents.FirstOrDefault(x => x.Type == type && x.StateKey == stateKey);
- if (@event is not null) return @event;
- @event = new StateEventResponse {
- RoomId = Room.RoomId,
- Type = type,
- StateKey = stateKey,
- Sender = null, //TODO implement
- EventId = null
- };
- // if (Room is null) return null;
- try {
- @event.RawContent = await Room.GetStateAsync(type, stateKey);
- }
- catch (MatrixException e) {
- if (e is { ErrorCode: "M_NOT_FOUND" }) {
- if (type == "m.room.name")
- @event = new() {
- Type = type,
- StateKey = stateKey,
- TypedContent = new RoomNameEventContent() {
- Name = await Room.GetNameOrFallbackAsync()
- },
- //TODO implement
- RoomId = null,
- Sender = null,
- EventId = null
- };
- else
- @event.RawContent = default!;
- }
- else throw;
- }
-
- StateEvents.Add(@event);
- return @event;
- }
-
- public string? RoomIcon {
- get => _roomIcon ?? "https://api.dicebear.com/6.x/identicon/svg?seed=" + Room.RoomId;
- set => SetField(ref _roomIcon, value);
- }
-
- public string? RoomName {
- get => _roomName ?? DefaultRoomName ?? Room.RoomId;
- set => SetField(ref _roomName, value);
- }
-
- public RoomCreateEventContent? CreationEventContent {
- get => _creationEventContent;
- set => SetField(ref _creationEventContent, value);
- }
-
- public string? RoomCreator {
- get => _roomCreator;
- set => SetField(ref _roomCreator, value);
- }
-
- // public string? GetRoomIcon() => (StateEvents.FirstOrDefault(x => x?.Type == RoomAvatarEventContent.EventId)?.TypedContent as RoomAvatarEventContent)?.Url ??
- // "mxc://rory.gay/dgP0YPjJEWaBwzhnbyLLwGGv";
-
- private string? _roomIcon;
- private string? _roomName;
- private RoomCreateEventContent? _creationEventContent;
- private string? _roomCreator;
-
- public string? DefaultRoomName { get; set; }
-
- public RoomInfo() {
- StateEvents.CollectionChanged += (_, args) => {
- if (args.NewItems is { Count: > 0 })
- foreach (StateEventResponse newState in args.NewItems) {
- if (newState.GetType == typeof(RoomNameEventContent) && newState.TypedContent is RoomNameEventContent roomNameContent)
- RoomName = roomNameContent.Name;
- else if (newState.GetType == typeof(RoomAvatarEventContent) && newState.TypedContent is RoomAvatarEventContent roomAvatarContent)
- RoomIcon = roomAvatarContent.Url;
- else if (newState.GetType == typeof(RoomCreateEventContent) && newState.TypedContent is RoomCreateEventContent roomCreateContent) {
- CreationEventContent = roomCreateContent;
- RoomCreator = newState.Sender;
- }
- }
- };
- }
-}
diff --git a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
index c6678ce..ed62751 100644
--- a/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
+++ b/MatrixRoomUtils.Web/MatrixRoomUtils.Web.csproj
@@ -23,6 +23,7 @@
+
@@ -35,39 +36,4 @@
-
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-Bold.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-BoldItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-ExtraBold.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-ExtraBoldItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-ExtraLight.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-ExtraLightItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-Italic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-Light.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-LightItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-Medium.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-MediumItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-Regular.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-SemiBold.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-SemiBoldItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-Thin.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMono-ThinItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-Bold.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-BoldItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-ExtraBold.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-ExtraBoldItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-ExtraLight.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-ExtraLightItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-Italic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-Light.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-LightItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-Medium.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-MediumItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-Regular.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-SemiBold.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-SemiBoldItalic.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-Thin.ttf" />
- <_ContentIncludedByDefault Remove="wwwroot\css\jetbrains-mono\ttf\JetBrainsMonoNL-ThinItalic.ttf" />
-
-
diff --git a/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor b/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor
index 94c51b2..27fe35e 100644
--- a/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor
+++ b/MatrixRoomUtils.Web/Pages/Dev/DevUtilities.razor
@@ -3,6 +3,7 @@
@using ArcaneLibs.Extensions
@using LibMatrix.Extensions
@using LibMatrix.Homeservers
+@using MatrixRoomUtils.Abstractions
@inject ILocalStorageService LocalStorage
@inject NavigationManager NavigationManager
Debug Tools
diff --git a/MatrixRoomUtils.Web/Pages/Index.razor b/MatrixRoomUtils.Web/Pages/Index.razor
index 2d1d6c0..ebb0ebb 100644
--- a/MatrixRoomUtils.Web/Pages/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Index.razor
@@ -16,30 +16,29 @@ Small collection of tools to do not-so-everyday things.
+@if (_offlineSessions.Count > 0) {
+
+
+ Sessions on unreachable servers
+
+
+}
+
@code
{
#if DEBUG
- bool DEBUG = true;
+ private const bool _debug = true;
#else
- bool DEBUG = false;
+ private const bool _debug = false;
#endif
private class AuthInfo {
@@ -71,35 +98,42 @@ Small collection of tools to do not-so-everyday things.
}
// private Dictionary _users = new();
- private List _auth = new();
+ private readonly List _sessions = [];
+ private readonly List _offlineSessions = [];
+ private LoginResponse? _currentSession;
protected override async Task OnInitializedAsync() {
+ Console.WriteLine("Index.OnInitializedAsync");
_currentSession = await MRUStorage.GetCurrentToken();
- // _users.Clear();
- _auth.Clear();
+ _sessions.Clear();
+ _offlineSessions.Clear();
var tokens = await MRUStorage.GetAllTokens();
var profileTasks = tokens.Select(async token => {
UserInfo userInfo = new();
AuthenticatedHomeserverGeneric hs;
+ Console.WriteLine($"Getting hs for {token.ToJson()}");
try {
hs = await hsProvider.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken, token.Proxy);
}
catch (MatrixException e) {
- if (e.ErrorCode == "M_UNKNOWN_TOKEN") {
- NavigationManager.NavigateTo("/InvalidSession?ctx=" + token.AccessToken);
- return;
- }
- throw;
+ if (e.ErrorCode != "M_UNKNOWN_TOKEN") throw;
+ NavigationManager.NavigateTo("/InvalidSession?ctx=" + token.AccessToken);
+ return;
+
}
catch (HttpRequestException e) {
logger.LogError(e, $"Failed to instantiate AuthenticatedHomeserver for {token.ToJson()}, homeserver may be offline?", token.UserId);
+ _offlineSessions.Add(token);
return;
}
+
+ Console.WriteLine($"Got hs for {token.ToJson()}");
+
var roomCountTask = hs.GetJoinedRooms();
var profile = await hs.GetProfileAsync(hs.WhoAmI.UserId);
userInfo.DisplayName = profile.DisplayName ?? hs.WhoAmI.UserId;
Console.WriteLine(profile.ToJson());
- _auth.Add(new() {
+ _sessions.Add(new() {
UserInfo = new() {
AvatarUrl = string.IsNullOrWhiteSpace(profile.AvatarUrl) ? "https://api.dicebear.com/6.x/identicon/svg?seed=" + hs.WhoAmI.UserId : hs.ResolveMediaUri(profile.AvatarUrl),
RoomCount = (await roomCountTask).Count,
@@ -110,7 +144,9 @@ Small collection of tools to do not-so-everyday things.
Homeserver = hs
});
});
+ Console.WriteLine("Waiting for profile tasks");
await Task.WhenAll(profileTasks);
+ Console.WriteLine("Done waiting for profile tasks");
await base.OnInitializedAsync();
}
@@ -127,19 +163,20 @@ Small collection of tools to do not-so-everyday things.
}
}
catch (Exception e) {
- if (e is MatrixException {ErrorCode: "M_UNKNOWN_TOKEN" }) {
- //todo: handle this
+ if (e is MatrixException { ErrorCode: "M_UNKNOWN_TOKEN" }) {
+ //todo: handle this
return;
}
+
Console.WriteLine(e);
}
+
await MRUStorage.RemoveToken(auth);
if ((await MRUStorage.GetCurrentToken())?.AccessToken == auth.AccessToken)
await MRUStorage.SetCurrentToken((await MRUStorage.GetAllTokens() ?? throw new InvalidOperationException()).FirstOrDefault());
await OnInitializedAsync();
}
- private LoginResponse _currentSession;
private async Task SwitchSession(UserAuth auth) {
Console.WriteLine($"Switching to {auth.Homeserver} {auth.UserId} via {auth.Proxy}");
diff --git a/MatrixRoomUtils.Web/Pages/ModerationUtilities/UserRoomHistory.razor b/MatrixRoomUtils.Web/Pages/ModerationUtilities/UserRoomHistory.razor
index d33756b..506a8a1 100644
--- a/MatrixRoomUtils.Web/Pages/ModerationUtilities/UserRoomHistory.razor
+++ b/MatrixRoomUtils.Web/Pages/ModerationUtilities/UserRoomHistory.razor
@@ -4,6 +4,7 @@
@using LibMatrix.EventTypes.Spec.State
@using LibMatrix.RoomTypes
@using ArcaneLibs.Extensions
+@using MatrixRoomUtils.Abstractions
UserRoomHistory
Enter mxid:
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
index 2ac4bcb..6cabe82 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/Index.razor
@@ -1,25 +1,21 @@
@page "/Rooms"
@using LibMatrix.Filters
@using LibMatrix.Helpers
-@using LibMatrix.EventTypes.Spec.State
-@using LibMatrix
-@using LibMatrix.Homeservers
-@using ArcaneLibs.Extensions
@using LibMatrix.Extensions
@using LibMatrix.Responses
@using System.Collections.ObjectModel
@using System.Diagnostics
+@using ArcaneLibs.Extensions
+@using MatrixRoomUtils.Abstractions
@inject ILogger logger
Room list
@Status
@Status2
-@* @if (RenderContents) { *@
+
+Create new room
+
-@* } *@
-@* else { *@
-@* *@
-@* } *@
@code {
private ObservableCollection Rooms { get; } = new();
@@ -47,9 +43,9 @@
},
State = new SyncFilter.RoomFilter.StateFilter {
Types = new List {
+ "m.room.create",
"m.room.name",
"m.room.avatar",
- "m.room.create",
"org.matrix.mjolnir.shortcode",
"m.room.power_levels",
}
@@ -92,49 +88,72 @@
// }
// };
+ private SyncHelper syncHelper;
+
+ // SyncHelper profileSyncHelper;
+
protected override async Task OnInitializedAsync() {
Homeserver = await MRUStorage.GetCurrentSessionOrNavigate();
if (Homeserver is null) return;
var rooms = await Homeserver.GetJoinedRooms();
- foreach (var room in rooms) {
- Rooms.Add(new(){Room = room});
+ // SemaphoreSlim _semaphore = new(160, 160);
+
+ var roomTasks = rooms.Select(async room => {
+ RoomInfo ri;
+ // await _semaphore.WaitAsync();
+ ri = new() { Room = room };
+ await Task.WhenAll((filter.Room?.State?.Types ?? []).Select(x => ri.GetStateEvent(x)));
+ return ri;
+ }).ToAsyncEnumerable();
+
+ await foreach (var room in roomTasks) {
+ Rooms.Add(room);
+ StateHasChanged();
+ // await Task.Delay(50);
+ // _semaphore.Release();
}
-
- GlobalProfile = await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId);
- var syncHelper = new SyncHelper(Homeserver, logger) {
- Timeout = 10000,
+ if (rooms.Count >= 150) RenderContents = true;
+
+ GlobalProfile = await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId);
+ syncHelper = new SyncHelper(Homeserver, logger) {
+ Timeout = 30000,
Filter = filter,
MinimumDelay = TimeSpan.FromMilliseconds(5000)
};
- // profileUpdateFilter.Room.State.Senders.Add(Homeserver.WhoAmI.UserId);
- // var profileSyncHelper = new SyncHelper(Homeserver, logger) {
+ // profileSyncHelper = new SyncHelper(Homeserver, logger) {
// Timeout = 10000,
// Filter = profileUpdateFilter,
// MinimumDelay = TimeSpan.FromMilliseconds(5000)
- // };
+ // };
+ // profileUpdateFilter.Room.State.Senders.Add(Homeserver.WhoAmI.UserId);
+
RunSyncLoop(syncHelper);
// RunSyncLoop(profileSyncHelper);
RunQueueProcessor();
+
await base.OnInitializedAsync();
}
-
+
private async Task RunQueueProcessor() {
var renderTimeSw = Stopwatch.StartNew();
+ var isInitialSync = true;
while (true) {
try {
- if (queue.Count == 0) {
- while (queue.Count == 0) {
- Console.WriteLine("Queue is empty, waiting...");
- await Task.Delay(2500);
- }
- Console.WriteLine("Queue no longer empty!");
+ while (queue.Count == 0) {
+ Console.WriteLine("Queue is empty, waiting...");
+ await Task.Delay(isInitialSync ? 100 : 2500);
}
- while (queue.TryDequeue(out var queueEntry)) {
+
+ Console.WriteLine($"Queue no longer empty after {renderTimeSw.Elapsed}!");
+
+ int maxUpdates = 10;
+ isInitialSync = false;
+ while (maxUpdates-- > 0 && queue.TryDequeue(out var queueEntry)) {
var (roomId, roomData) = queueEntry;
Console.WriteLine($"Dequeued room {roomId}");
RoomInfo room;
-
+
if (Rooms.Any(x => x.Room.RoomId == roomId)) {
room = Rooms.First(x => x.Room.RoomId == roomId);
Console.WriteLine($"QueueWorker: {roomId} already known with {room.StateEvents?.Count ?? 0} state events");
@@ -146,26 +165,23 @@
};
Rooms.Add(room);
}
-
+
if (room.StateEvents is null) {
Console.WriteLine($"QueueWorker: {roomId} does not have state events on record?");
throw new InvalidDataException("Somehow this is null???");
}
- if (roomData.State?.Events is {Count: >0 })
+
+ if (roomData.State?.Events is { Count: > 0 })
room.StateEvents.MergeStateEventLists(roomData.State.Events);
else {
Console.WriteLine($"QueueWorker: could not merge state for {room.Room.RoomId} as new data contains no state events!");
}
- if (Random.Shared.Next(101) < 20 || true) {
- Status = $"Got {Rooms.Count} rooms so far! {queue.Count} entries in processing queue...";
- }
- RenderContents |= queue.Count == 0;
- if (queue.Count > 10) RenderContents = false;
- await Task.Delay(RenderContents ? 25 : 6);
}
- // else {
- // Console.WriteLine("Failed to dequeue item");
- // }
+ Console.WriteLine($"QueueWorker: {queue.Count} entries left in queue, {maxUpdates} maxUpdates left, RenderContents: {RenderContents}");
+ Status = $"Got {Rooms.Count} rooms so far! {queue.Count} entries in processing queue...";
+
+ RenderContents |= queue.Count == 0;
+ await Task.Delay(Rooms.Count);
}
catch (Exception e) {
Console.WriteLine("QueueWorker exception: " + e);
@@ -214,11 +230,15 @@
// We can't trust servers to give us what we ask for, and this ruins performance
// Thanks, Conduit.
joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.Types?.Contains(x.Type) == false);
- if(filter.Room?.State?.NotSenders?.Any() ?? false)
+ if (filter.Room?.State?.NotSenders?.Any() ?? false)
joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.NotSenders?.Contains(x.Sender) ?? false);
-
+
queue.Enqueue(joinedRoom);
}
+ if (sync.Rooms.Leave is {Count: > 0})
+ foreach (var leftRoom in sync.Rooms.Leave)
+ if (Rooms.Any(x => x.Room.RoomId == leftRoom.Key))
+ Rooms.Remove(Rooms.First(x => x.Room.RoomId == leftRoom.Key));
Status = $"Got {Rooms.Count} rooms so far! {queue.Count} entries in processing queue... " +
$"{sync?.Rooms?.Join?.Count ?? 0} new updates!";
diff --git a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
index 846d1cb..dbe0648 100644
--- a/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
+++ b/MatrixRoomUtils.Web/Pages/Rooms/PolicyList.razor
@@ -4,184 +4,199 @@
@using ArcaneLibs.Extensions
@using LibMatrix.EventTypes.Spec.State
@using LibMatrix.EventTypes.Spec.State.Policy
+@using System.Diagnostics
+@using System.Diagnostics.CodeAnalysis
+@using LibMatrix.Extensions
+@using LibMatrix.Responses
Policy list editor - Editing @RoomId
- This policy list contains @PolicyEvents.Count(x => x.Type == "m.policy.rule.server") server bans,
- @PolicyEvents.Count(x => x.Type == "m.policy.rule.room") room bans and
- @PolicyEvents.Count(x => x.Type == "m.policy.rule.user") user bans.
+ This policy list contains @GetPolicyCount(typeof(ServerPolicyRuleEventContent)) server bans,
+ @GetPolicyCount(typeof(RoomPolicyRuleEventContent)) room bans and
+ @GetPolicyCount(typeof(UserPolicyRuleEventContent)) user bans.
+ @foreach (var (key, value) in PolicyEventsByType) {
+
@key.Name: @value.Count
+ }
-Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!)
-
+Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!)
-@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.server")) {
+Server policies
+
+@if (!GetPolicyEventsByType(typeof(ServerPolicyRuleEventContent)).Any()) {
No server policies
}
else {
- Server policies
-
-
+
-
- Server
- Reason
- Expires
- Actions
-
-
-
- @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && (x.TypedContent as PolicyRuleEventContent).Entity is not null)) {
- var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
- Entity: @policyData.Entity State: @policyEvent.StateKey
- @policyData.Reason
-
- @policyData.ExpiryDateTime
-
-
- await RemovePolicyAsync(policyEvent)" *@>Edit
- @* await RemovePolicyAsync(policyEvent)" #1#>Remove *@
-
+ Server
+ Reason
+ Expires
+ Actions
- }
+
+
+ @foreach (var policyEvent in GetValidPolicyEventsByType(typeof(ServerPolicyRuleEventContent))) {
+ var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
+
+
+ Entity: @policyData.Entity
+ State: @policyEvent.StateKey
+
+ @policyData.Reason
+
+ @policyData.ExpiryDateTime
+
+
+ await RemovePolicyAsync(policyEvent)" *@>Edit
+ @* await RemovePolicyAsync(policyEvent)" #1#>Remove *@
+
+
+ }
- Redacted events
-
+ Redacted or invalid events
+
-
- State key
- Serialised Contents
-
-
-
- @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.server" && (x.TypedContent as PolicyRuleEventContent).Entity == null)) {
- var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
- @policyEvent.StateKey
- @policyEvent.RawContent.ToJson(false, true)
+ State key
+ Serialised Contents
- }
+
+
+ @foreach (var policyEvent in GetInvalidPolicyEventsByType(typeof(ServerPolicyRuleEventContent))) {
+
+ @policyEvent.StateKey
+ @policyEvent.RawContent.ToJson(false, true)
+
+ }
}
-@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.room")) {
+Room policies
+
+@if (!GetPolicyEventsByType(typeof(RoomPolicyRuleEventContent)).Any()) {
No room policies
}
else {
- Room policies
-
-
+
-
- Room
- Reason
- Expires
- Actions
-
-
-
- @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && (x.TypedContent as PolicyRuleEventContent).Entity is not null)) {
- var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
- Entity: @policyData.Entity State: @policyEvent.StateKey
- @policyData.Reason
-
- @policyData.ExpiryDateTime
-
-
- await RemovePolicyAsync(policyEvent)" *@>Remove
-
+ Room
+ Reason
+ Expires
+ Actions
- }
+
+
+ @foreach (var policyEvent in GetValidPolicyEventsByType(typeof(RoomPolicyRuleEventContent))) {
+ var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
+
+ Entity: @policyData.Entity State: @policyEvent.StateKey
+ @policyData.Reason
+
+ @policyData.ExpiryDateTime
+
+
+ await RemovePolicyAsync(policyEvent)" *@>Remove
+
+
+ }
- Redacted events
-
+ Redacted or invalid events
+
-
- State key
- Serialised Contents
-
-
-
- @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.room" && (x.TypedContent as PolicyRuleEventContent).Entity == null)) {
- @policyEvent.StateKey
- @policyEvent.RawContent!.ToJson(false, true)
+ State key
+ Serialised Contents
- }
+
+
+ @foreach (var policyEvent in GetInvalidPolicyEventsByType(typeof(RoomPolicyRuleEventContent))) {
+
+ @policyEvent.StateKey
+ @policyEvent.RawContent!.ToJson(false, true)
+
+ }
}
-@if (!PolicyEvents.Any(x => x.Type == "m.policy.rule.user")) {
+User policies
+
+@if (!GetPolicyEventsByType(typeof(UserPolicyRuleEventContent)).Any()) {
No user policies
}
else {
- User policies
-
-
+
-
- @if (_enableAvatars) {
-
- }
- User
- Reason
- Expires
- Actions
-
-
-
- @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && (x.TypedContent as PolicyRuleEventContent).Entity is not null)) {
- var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
- @if (_enableAvatars) {
-
-
-
+ @if (EnableAvatars) {
+
}
- Entity: @string.Join("", policyData.Entity.Take(64)) State: @string.Join("", policyEvent.StateKey.Take(64))
- @policyData.Reason
-
- @policyData.ExpiryDateTime
-
-
- await RemovePolicyAsync(policyEvent)" *@>Remove
-
+ User
+ Reason
+ Expires
+ Actions
- }
+
+
+ @foreach (var policyEvent in GetValidPolicyEventsByType(typeof(UserPolicyRuleEventContent))) {
+ var policyData = policyEvent.TypedContent as PolicyRuleEventContent;
+
+ @if (EnableAvatars) {
+
+ @if (Avatars.ContainsKey(policyData.Entity)) {
+
+ }
+
+ }
+ Entity: @string.Join("", policyData.Entity.Take(64)) State: @string.Join("", policyEvent.StateKey.Take(64))
+ @policyData.Reason
+
+ @policyData.ExpiryDateTime
+
+
+ await RemovePolicyAsync(policyEvent)" *@>Remove
+
+
+ }
- Redacted events
-
+ Redacted or invalid events
+
-
- State key
- Serialised Contents
-
-
-
- @foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && (x.TypedContent as PolicyRuleEventContent).Entity == null)) {
- @policyEvent.StateKey
- @policyEvent.RawContent.ToJson(false, true)
+ State key
+ Serialised Contents
- }
+
+
+ @foreach (var policyEvent in GetInvalidPolicyEventsByType(typeof(UserPolicyRuleEventContent))) {
+
+ @policyEvent.StateKey
+ @policyEvent.RawContent.ToJson(false, true)
+
+ }
}
-
-
@code {
+
+#if DEBUG
+ private const bool Debug = true;
+#else
+ private const bool Debug = false;
+#endif
+
//get room list
// - sync withroom list filter
// Type = support.feline.msc3784
@@ -192,18 +207,28 @@ else {
private bool _enableAvatars;
- static readonly Dictionary avatars = new();
- static readonly Dictionary servers = new();
+ static readonly Dictionary Avatars = new();
+ // static readonly Dictionary Servers = new();
- public static List PolicyEvents { get; set; } = new();
+ // private static List PolicyEvents { get; set; } = new();
+ private Dictionary> PolicyEventsByType { get; set; } = new();
+
+ public bool EnableAvatars {
+ get => _enableAvatars;
+ set {
+ _enableAvatars = value;
+ if (value) GetAllAvatars();
+ }
+ }
protected override async Task OnInitializedAsync() {
+ var sw = Stopwatch.StartNew();
await base.OnInitializedAsync();
var hs = await MRUStorage.GetCurrentSessionOrNavigate();
if (hs is null) return;
RoomId = RoomId.Replace('~', '.');
await LoadStatesAsync();
- Console.WriteLine("Policy list editor initialized!");
+ Console.WriteLine($"Policy list editor initialized in {sw.Elapsed}!");
}
private async Task LoadStatesAsync() {
@@ -214,39 +239,52 @@ else {
var states = room.GetFullStateAsync();
await foreach (var state in states) {
- if (!state.Type.StartsWith("m.policy.rule")) continue;
- PolicyEvents.Add(state);
+ if (state is null) continue;
+ if (!state.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue;
+ if (!PolicyEventsByType.ContainsKey(state.MappedType)) PolicyEventsByType.Add(state.MappedType, new());
+ PolicyEventsByType[state.MappedType].Add(state);
}
-
- // 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();
}
- private async Task GetAvatar(string userId) {
- try {
- if (avatars.ContainsKey(userId)) return;
- var hs = userId.Split(':')[1];
- var server = servers.ContainsKey(hs) ? servers[hs] : new RemoteHomeserver(userId.Split(':')[1]);
- if (!servers.ContainsKey(hs)) servers.Add(hs, server);
- var profile = await server.GetProfileAsync(userId);
- avatars.Add(userId, await hsResolver.ResolveMediaUri(server.BaseUrl, profile.AvatarUrl));
- servers.Add(userId, server);
+ private async Task GetAllAvatars() {
+ // if (!_enableAvatars) return;
+ Console.WriteLine("Getting avatars...");
+ var users = GetValidPolicyEventsByType(typeof(UserPolicyRuleEventContent)).Select(x => x.RawContent!["entity"]!.GetValue()).Where(x => x.Contains(':') && !x.Contains("*")).ToList();
+ Console.WriteLine($"Got {users.Count} users!");
+ var usersByHomeServer = users.GroupBy(x => x!.Split(':')[1]).ToDictionary(x => x.Key!, x => x.ToList());
+ Console.WriteLine($"Got {usersByHomeServer.Count} homeservers!");
+ var homeserverTasks = usersByHomeServer.Keys.Select(x => RemoteHomeserver.TryCreate(x)).ToAsyncEnumerable();
+ await foreach (var server in homeserverTasks) {
+ if (server is null) continue;
+ var profileTasks = usersByHomeServer[server.BaseUrl].Select(x => TryGetProfile(server, x)).ToList();
+ await Task.WhenAll(profileTasks);
+ profileTasks.RemoveAll(x => x.Result is not { Value: { AvatarUrl: not null } });
+ foreach (var profile in profileTasks.Select(x => x.Result!.Value)) {
+ // if (profile is null) continue;
+ if (!string.IsNullOrWhiteSpace(profile.Value.AvatarUrl)) {
+ var url = await hsResolver.ResolveMediaUri(server.BaseUrl, profile.Value.AvatarUrl);
+ Avatars.TryAdd(profile.Key, url);
+ }
+ else Avatars.TryAdd(profile.Key, null);
+ }
StateHasChanged();
}
- catch {
- // ignored
- }
}
- private async Task GetAllAvatars() {
- foreach (var policyEvent in PolicyEvents.Where(x => x.Type == "m.policy.rule.user" && (x.TypedContent as PolicyRuleEventContent).Entity is not null)) {
- await GetAvatar((policyEvent.TypedContent as PolicyRuleEventContent).Entity);
+ private async Task?> TryGetProfile(RemoteHomeserver server, string mxid) {
+ try {
+ return new KeyValuePair(mxid, await server.GetProfileAsync(mxid));
+ }
+ catch {
+ return null;
}
- StateHasChanged();
}
-}
+ private List GetPolicyEventsByType(Type type) => PolicyEventsByType.ContainsKey(type) ? PolicyEventsByType[type] : [];
+ private List GetValidPolicyEventsByType(Type type) => GetPolicyEventsByType(type).Where(x => !string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList();
+ private List GetInvalidPolicyEventsByType(Type type) => GetPolicyEventsByType(type).Where(x => string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList();
+ private int GetPolicyCount(Type type) => PolicyEventsByType.ContainsKey(type) ? PolicyEventsByType[type].Count : 0;
+
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Pages/User/DMManager.razor b/MatrixRoomUtils.Web/Pages/User/DMManager.razor
index 1b28516..a327793 100644
--- a/MatrixRoomUtils.Web/Pages/User/DMManager.razor
+++ b/MatrixRoomUtils.Web/Pages/User/DMManager.razor
@@ -1,7 +1,7 @@
@page "/User/DirectMessages"
-@using LibMatrix.Homeservers
@using LibMatrix.EventTypes.Spec.State
@using LibMatrix.Responses
+@using MatrixRoomUtils.Abstractions
Direct Messages
diff --git a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor b/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor
index 553f46d..60c68ac 100644
--- a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor
+++ b/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor
@@ -7,6 +7,7 @@
@using MatrixRoomUtils.LibDMSpace.StateEvents
@using ArcaneLibs.Extensions
@using System.Text.Json.Serialization
+@using MatrixRoomUtils.Abstractions
DM Space setup tool - stage 2: Fix DM room attribution
@@ -185,9 +186,9 @@ else {
}
catch { }
- var membersEnum = room.GetMembersAsync();
+ var membersEnum = room.GetMembersEnumerableAsync(true);
await foreach (var member in membersEnum)
- if (member.TypedContent is RoomMemberEventContent memberEvent && !string.IsNullOrWhiteSpace(memberEvent.Membership) && memberEvent.Membership == "join")
+ if (member.TypedContent is RoomMemberEventContent memberEvent)
roomMembers[roomInfo].Add(new() { DisplayName = memberEvent.DisplayName, AvatarUrl = memberEvent.AvatarUrl, Id = member.StateKey });
if (string.IsNullOrWhiteSpace(roomInfo.RoomName) || roomInfo.RoomName == room.RoomId) {
diff --git a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor b/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor
index 854b09c..42573e6 100644
--- a/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor
+++ b/MatrixRoomUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor
@@ -7,6 +7,8 @@
@using MatrixRoomUtils.LibDMSpace.StateEvents
@using ArcaneLibs.Extensions
@using System.Text.Json.Serialization
+@using MatrixRoomUtils.Abstractions
+
DM Space setup tool - stage 3: Preview space layout
@@ -154,9 +156,9 @@ else {
}
catch { }
- var membersEnum = room.GetMembersAsync();
+ var membersEnum = room.GetMembersEnumerableAsync(true);
await foreach (var member in membersEnum)
- if (member.TypedContent is RoomMemberEventContent memberEvent && !string.IsNullOrWhiteSpace(memberEvent.Membership) && memberEvent.Membership == "join")
+ if (member.TypedContent is RoomMemberEventContent memberEvent)
roomMembers.Add(new() { DisplayName = memberEvent.DisplayName, AvatarUrl = memberEvent.AvatarUrl, Id = member.StateKey });
if (string.IsNullOrWhiteSpace(roomInfo.RoomName) || roomInfo.RoomName == room.RoomId) {
diff --git a/MatrixRoomUtils.Web/Pages/User/Profile.razor b/MatrixRoomUtils.Web/Pages/User/Profile.razor
index ae3fb76..73d7c6e 100644
--- a/MatrixRoomUtils.Web/Pages/User/Profile.razor
+++ b/MatrixRoomUtils.Web/Pages/User/Profile.razor
@@ -9,40 +9,43 @@
@if (NewProfile is not null) {
Profile
-
-
-
-
Display name:
-
Avatar URL:
-
-
Update profile
-
Update profile (restore room overrides)
+
+
+
+ Display name:
+ Avatar URL:
+
+ Update profile
+ Update profile (restore room overrides)
+
@if (!string.IsNullOrWhiteSpace(Status)) {
@Status
}
-
- Room profiles
-
- @foreach (var (roomId, roomProfile) in RoomProfiles.OrderBy(x=>RoomNames.TryGetValue(x.Key, out var _name) ? _name : x.Key)) {
-
- @(RoomNames.TryGetValue(roomId, out var name) ? name : roomId)
-
-
- Display name:
- Avatar URL:
-
- Update profile
-
-
- @if (!string.IsNullOrWhiteSpace(Status)) {
- @Status
- }
-
+
+
+ @* *@
+ Room profiles
+
+ @foreach (var (roomId, roomProfile) in RoomProfiles.OrderBy(x => RoomNames.TryGetValue(x.Key, out var _name) ? _name : x.Key)) {
+
+ @(RoomNames.TryGetValue(roomId, out var name) ? name : roomId)
+
+
+ Display name:
+ Avatar URL:
+
+ Update profile
+
- }
-
+ @if (!string.IsNullOrWhiteSpace(Status)) {
+ @Status
+ }
+
+
+ }
+ //
}
@code {
@@ -54,7 +57,10 @@
private string? Status {
get => _status;
- set { _status = value; StateHasChanged(); }
+ set {
+ _status = value;
+ StateHasChanged();
+ }
}
private Dictionary
RoomProfiles { get; set; } = new();
@@ -65,14 +71,15 @@
if (Homeserver is null) return;
Status = "Loading global profile...";
if (Homeserver.WhoAmI?.UserId is null) return;
- NewProfile = (await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId)).DeepClone();
- OldProfile = (await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId)).DeepClone();
+ NewProfile = (await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId)); //.DeepClone();
+ OldProfile = (await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId)); //.DeepClone();
Status = "Loading room profiles...";
var roomProfiles = Homeserver.GetRoomProfilesAsync();
await foreach (var (roomId, roomProfile) in roomProfiles) {
// Status = $"Got profile for {roomId}...";
- RoomProfiles[roomId] = roomProfile.DeepClone();
+ RoomProfiles[roomId] = roomProfile; //.DeepClone();
}
+
StateHasChanged();
Status = "Room profiles loaded, loading room names...";
@@ -80,6 +87,7 @@
var name = await x.GetNameOrFallbackAsync();
return new KeyValuePair(x.RoomId, name);
}).ToAsyncEnumerable();
+
await foreach (var (roomId, roomName) in roomNameTasks) {
// Status = $"Got room name for {roomId}: {roomName}";
RoomNames[roomId] = roomName;
@@ -106,13 +114,14 @@
StateHasChanged();
await OnInitializedAsync();
}
+
private async Task RoomAvatarChanged(InputFileChangeEventArgs arg, string roomId) {
var res = await Homeserver.UploadFile(arg.File.Name, arg.File.OpenReadStream(Int64.MaxValue), arg.File.ContentType);
Console.WriteLine(res);
RoomProfiles[roomId].AvatarUrl = res;
StateHasChanged();
}
-
+
private async Task UpdateRoomProfile(string roomId) {
Status = "Busy processing room profile update, please do not leave this page...";
StateHasChanged();
@@ -122,5 +131,4 @@
StateHasChanged();
}
-}
-
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/RoomList.razor b/MatrixRoomUtils.Web/Shared/RoomList.razor
index f78c7f7..31f0430 100644
--- a/MatrixRoomUtils.Web/Shared/RoomList.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomList.razor
@@ -5,6 +5,7 @@
@using LibMatrix.EventTypes.Spec.State
@using System.Collections.ObjectModel
@using LibMatrix.Responses
+@using MatrixRoomUtils.Abstractions
@using _Imports = MatrixRoomUtils.Web._Imports
@if (!StillFetching) {
Fetching room details... @RoomsWithTypes.Sum(x => x.Value.Count) out of @Rooms.Count done!
@@ -34,18 +35,23 @@ else {
private Dictionary> RoomsWithTypes => Rooms is null ? new() : Rooms.GroupBy(x => GetRoomTypeName(x.CreationEventContent?.Type)).ToDictionary(x => x.Key, x => x.ToList());
+ private bool hooked;
protected override async Task OnParametersSetAsync() {
var hs = await MRUStorage.GetCurrentSessionOrNavigate();
if (hs is null) return;
- Rooms.CollectionChanged += (_, args) => {
- foreach (RoomInfo item in args.NewItems) {
- item.PropertyChanged += (_, args2) => {
- Console.WriteLine(args2);
- if(args2.PropertyName == nameof(item.CreationEventContent))
- StateHasChanged();
- };
- }
- };
+ if (!hooked) {
+ Rooms.CollectionChanged += (_, args) => {
+ foreach (RoomInfo item in args.NewItems) {
+ item.PropertyChanged += (_, args2) => {
+ // Console.WriteLine(args2);
+
+ if (args2.PropertyName == nameof(item.CreationEventContent))
+ StateHasChanged();
+ };
+ }
+ };
+ hooked = true;
+ }
// GlobalProfile ??= await hs.GetProfileAsync(hs.WhoAmI.UserId);
@@ -53,10 +59,10 @@ else {
}
private string GetRoomTypeName(string? roomType) => roomType switch {
+ null => "Room",
"m.space" => "Space",
"msc3588.stories.stories-room" => "Story room",
"support.feline.policy.lists.msc.v1" => "MSC3784 Policy list (v1)",
- null => "Room",
_ => roomType
};
@@ -88,5 +94,4 @@ else {
// _semaphoreSlim.Release();
// }
-}
-
+}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
index 55ffc1e..4db25e1 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListCategory.razor
@@ -3,6 +3,7 @@
@using LibMatrix.EventTypes.Spec.State
@using LibMatrix.Homeservers
@using LibMatrix.Responses
+@using MatrixRoomUtils.Abstractions
@RoomType (@Rooms.Count)
@foreach (var room in Rooms) {
@@ -42,17 +43,19 @@
private List Rooms => Category.Value;
private int RoomVersionDangerLevel(RoomInfo room) {
- var roomVersion = room.StateEvents.FirstOrDefault(x => x.Type == "m.room.create");
- if (roomVersion is null) return 0;
- return roomVersion.TypedContent is not RoomCreateEventContent roomVersionContent ? 0
+ var creationEvent = room.StateEvents.FirstOrDefault(x => x?.Type == "m.room.create");
+ if (creationEvent is null) return 0;
+ return creationEvent.TypedContent is not RoomCreateEventContent roomVersionContent ? 0
: RoomConstants.DangerousRoomVersions.Contains(roomVersionContent.RoomVersion) ? 2
: roomVersionContent.RoomVersion != RoomConstants.RecommendedRoomVersion ? 1 : 0;
}
public static string GetRoomTypeName(string roomType) {
return roomType switch {
- "Room" => "Rooms",
- "org.matrix.mjolnir.policy" => "Policies",
+ null => "Room",
+ "m.space" => "Space",
+ "org.matrix.mjolnir.policy" => "Policy room",
+
_ => roomType
};
}
diff --git a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
index e08f98d..a6c006b 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListComponents/RoomListSpace.razor
@@ -1,4 +1,5 @@
@using System.Collections.ObjectModel
+@using MatrixRoomUtils.Abstractions
Manage space
diff --git a/MatrixRoomUtils.Web/Shared/RoomListItem.razor b/MatrixRoomUtils.Web/Shared/RoomListItem.razor
index 3aa28e6..07f0756 100644
--- a/MatrixRoomUtils.Web/Shared/RoomListItem.razor
+++ b/MatrixRoomUtils.Web/Shared/RoomListItem.razor
@@ -5,6 +5,7 @@
@using LibMatrix.Homeservers
@using LibMatrix.Responses
@using LibMatrix.RoomTypes
+@using MatrixRoomUtils.Abstractions
@using MatrixRoomUtils.Web.Classes.Constants
@if (RoomInfo is not null) {
@@ -70,12 +71,16 @@ else {
private bool _loadData = false;
private static AuthenticatedHomeserverGeneric? hs { get; set; }
+ private bool _hooked;
protected override async Task OnParametersSetAsync() {
if (RoomInfo != null) {
- RoomInfo.PropertyChanged += (_, a) => {
- Console.WriteLine(a.PropertyName);
- StateHasChanged();
- };
+ if (!_hooked) {
+ _hooked = true;
+ RoomInfo.PropertyChanged += (_, a) => {
+ Console.WriteLine(a.PropertyName);
+ StateHasChanged();
+ };
+ }
if (LoadData) {
try {
diff --git a/MatrixRoomUtils.Web/wwwroot/css/app.css b/MatrixRoomUtils.Web/wwwroot/css/app.css
index 3baddbb..3fac9ca 100644
--- a/MatrixRoomUtils.Web/wwwroot/css/app.css
+++ b/MatrixRoomUtils.Web/wwwroot/css/app.css
@@ -1,8 +1,16 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
@import url('jetbrains-mono/jetbrains-mono.css');
+.avatar48 {
+ width: 48px;
+ height: 48px;
+ aspect-ratio: unset;
+ border-radius: 50%;
+}
+
.details-compact > summary {
- line-height: 0px;
+ line-height: 0;
+ white-space: nowrap;
}
.details-compact[open] > summary {
diff --git a/MatrixRoomUtils.sln b/MatrixRoomUtils.sln
index 58379b3..a26bbaa 100644
--- a/MatrixRoomUtils.sln
+++ b/MatrixRoomUtils.sln
@@ -48,6 +48,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixRoomUtils.LibDMSpace"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.EventTypes", "LibMatrix\LibMatrix.EventTypes\LibMatrix.EventTypes.csproj", "{1CAA2B6D-0365-4C8B-96EE-26026514FEE2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixRoomUtils.Abstractions", "MatrixRoomUtils.Abstractions\MatrixRoomUtils.Abstractions.csproj", "{FE20ED20-0D55-4D74-822B-E2AC7A54C487}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -126,6 +128,10 @@ Global
{1CAA2B6D-0365-4C8B-96EE-26026514FEE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1CAA2B6D-0365-4C8B-96EE-26026514FEE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1CAA2B6D-0365-4C8B-96EE-26026514FEE2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FE20ED20-0D55-4D74-822B-E2AC7A54C487}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F4E241C3-0300-4B87-8707-BCBDEF1F0185} = {8F4F6BEC-0C66-486B-A21A-1C35B2EDAD33}
diff --git a/MatrixRoomUtils.sln.DotSettings.user b/MatrixRoomUtils.sln.DotSettings.user
index 6daed5b..7142e45 100644
--- a/MatrixRoomUtils.sln.DotSettings.user
+++ b/MatrixRoomUtils.sln.DotSettings.user
@@ -1,2 +1,9 @@
- DoNotShowAndRun
\ No newline at end of file
+
ExplicitlyExcluded
+
ExplicitlyExcluded
+
DoNotShowAndRun
+
<SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
+ <Solution />
+</SessionState>
+
True
+
True
\ No newline at end of file
diff --git a/MxApiExtensions b/MxApiExtensions
index 0b662d3..3c0c7b2 160000
--- a/MxApiExtensions
+++ b/MxApiExtensions
@@ -1 +1 @@
-Subproject commit 0b662d36de30c4bdd3d9be97d08ace8d4d7be588
+Subproject commit 3c0c7b2e56a24bda06b8c567e5608546898c99d7
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 0000000..d9402a4
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
--
cgit 1.4.1