diff --git a/Benchmarks/Benchmarks.csproj b/Benchmarks/Benchmarks.csproj
index a72013b..a174f1f 100644
--- a/Benchmarks/Benchmarks.csproj
+++ b/Benchmarks/Benchmarks.csproj
@@ -2,16 +2,16 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
-<!-- <PublishAot>true</PublishAot>-->
+ <!-- <PublishAot>true</PublishAot>-->
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="BenchmarkDotNet" Version="0.15.3" />
+ <PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
</ItemGroup>
</Project>
diff --git a/LibMatrix b/LibMatrix
-Subproject 91319ba62de889bde645b6f1df4dd6a960ee7de
+Subproject 1db452c75de1e25a9a2a8fd4fe2a04a2e1047f2
diff --git a/MatrixUtils.Abstractions/MatrixUtils.Abstractions.csproj b/MatrixUtils.Abstractions/MatrixUtils.Abstractions.csproj
index 96f9fcb..751aa5d 100644
--- a/MatrixUtils.Abstractions/MatrixUtils.Abstractions.csproj
+++ b/MatrixUtils.Abstractions/MatrixUtils.Abstractions.csproj
@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj" />
+ <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj"/>
</ItemGroup>
</Project>
diff --git a/MatrixUtils.Desktop/App.axaml.cs b/MatrixUtils.Desktop/App.axaml.cs
index 3a106ab..8a5d3e2 100644
--- a/MatrixUtils.Desktop/App.axaml.cs
+++ b/MatrixUtils.Desktop/App.axaml.cs
@@ -15,7 +15,6 @@ public partial class App : Application {
public override void OnFrameworkInitializationCompleted() {
host = Host.CreateDefaultBuilder().ConfigureServices((ctx, services) => {
services.AddSingleton<RMUDesktopConfiguration>();
- services.AddSingleton<SentryService>();
services.AddSingleton<TieredStorageService>(x =>
new TieredStorageService(
cacheStorageProvider: new FileStorageProvider(x.GetService<RMUDesktopConfiguration>()!.CacheStoragePath),
@@ -40,10 +39,10 @@ public partial class App : Application {
var scope = scopeFac.CreateScope();
desktop.MainWindow = scope.ServiceProvider.GetRequiredService<MainWindow>();
}
-
- if(Environment.GetEnvironmentVariable("AVALONIA_THEME")?.Equals("dark", StringComparison.OrdinalIgnoreCase) ?? false)
+
+ if (Environment.GetEnvironmentVariable("AVALONIA_THEME")?.Equals("dark", StringComparison.OrdinalIgnoreCase) ?? false)
RequestedThemeVariant = ThemeVariant.Dark;
-
+
base.OnFrameworkInitializationCompleted();
}
}
\ No newline at end of file
diff --git a/MatrixUtils.Desktop/MainWindow.axaml.cs b/MatrixUtils.Desktop/MainWindow.axaml.cs
index 9c783e4..a1eef56 100644
--- a/MatrixUtils.Desktop/MainWindow.axaml.cs
+++ b/MatrixUtils.Desktop/MainWindow.axaml.cs
@@ -14,7 +14,7 @@ public partial class MainWindow : Window {
private readonly RMUDesktopConfiguration _configuration;
public static MainWindow Instance { get; private set; } = null!;
- public MainWindow(ILogger<MainWindow> logger, IServiceScopeFactory scopeFactory, SentryService _) {
+ public MainWindow(ILogger<MainWindow> logger, IServiceScopeFactory scopeFactory) {
Instance = this;
_logger = logger;
_scopeFactory = scopeFactory;
diff --git a/MatrixUtils.Desktop/MatrixUtils.Desktop.csproj b/MatrixUtils.Desktop/MatrixUtils.Desktop.csproj
index a7ff4b9..419ae88 100644
--- a/MatrixUtils.Desktop/MatrixUtils.Desktop.csproj
+++ b/MatrixUtils.Desktop/MatrixUtils.Desktop.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
@@ -10,31 +10,28 @@
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
-<!-- <PublishTrimmed>true</PublishTrimmed>-->
-<!-- <PublishReadyToRun>true</PublishReadyToRun>-->
-<!-- <PublishSingleFile>true</PublishSingleFile>-->
-<!-- <PublishReadyToRunShowWarnings>true</PublishReadyToRunShowWarnings>-->
-<!-- <PublishTrimmedShowLinkerSizeComparison>true</PublishTrimmedShowLinkerSizeComparison>-->
-<!-- <PublishTrimmedShowLinkerSizeComparisonWarnings>true</PublishTrimmedShowLinkerSizeComparisonWarnings>-->
+ <!-- <PublishTrimmed>true</PublishTrimmed>-->
+ <!-- <PublishReadyToRun>true</PublishReadyToRun>-->
+ <!-- <PublishSingleFile>true</PublishSingleFile>-->
+ <!-- <PublishReadyToRunShowWarnings>true</PublishReadyToRunShowWarnings>-->
+ <!-- <PublishTrimmedShowLinkerSizeComparison>true</PublishTrimmedShowLinkerSizeComparison>-->
+ <!-- <PublishTrimmedShowLinkerSizeComparisonWarnings>true</PublishTrimmedShowLinkerSizeComparisonWarnings>-->
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Avalonia" Version="11.2.1" />
- <PackageReference Include="Avalonia.Desktop" Version="11.2.1" />
- <PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1" />
- <PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.1" />
+ <PackageReference Include="Avalonia" Version="11.3.8"/>
+ <PackageReference Include="Avalonia.Desktop" Version="11.3.8"/>
+ <PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.8"/>
+ <PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.8"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
- <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.1" />
- <PackageReference Include="Sentry" Version="4.13.0" />
+ <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.1"/>
</ItemGroup>
-
-
<ItemGroup>
- <PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.2.0" />
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
+ <PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.3.0.6"/>
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.2.25502.107"/>
</ItemGroup>
<ItemGroup>
<Content Include="appsettings*.json">
@@ -45,6 +42,6 @@
</Content>
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\MatrixUtils.Abstractions\MatrixUtils.Abstractions.csproj" />
+ <ProjectReference Include="..\MatrixUtils.Abstractions\MatrixUtils.Abstractions.csproj"/>
</ItemGroup>
</Project>
diff --git a/MatrixUtils.Desktop/RMUDesktopConfiguration.cs b/MatrixUtils.Desktop/RMUDesktopConfiguration.cs
index 62646ca..f9515f6 100644
--- a/MatrixUtils.Desktop/RMUDesktopConfiguration.cs
+++ b/MatrixUtils.Desktop/RMUDesktopConfiguration.cs
@@ -21,7 +21,6 @@ public class RMUDesktopConfiguration {
public string DataStoragePath { get; set; } = "";
public string CacheStoragePath { get; set; } = "";
- public string? SentryDsn { get; set; }
private static string ExpandPath(string path, bool retry = true) {
_logger.LogInformation("Expanding path `{}`", path);
@@ -44,4 +43,4 @@ public class RMUDesktopConfiguration {
return path;
}
-}
+}
\ No newline at end of file
diff --git a/MatrixUtils.Desktop/SentryService.cs b/MatrixUtils.Desktop/SentryService.cs
deleted file mode 100644
index c965632..0000000
--- a/MatrixUtils.Desktop/SentryService.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Sentry;
-
-namespace MatrixUtils.Desktop;
-
-public class SentryService : IDisposable {
- private IDisposable? _sentrySdkDisposable;
- public SentryService(IServiceScopeFactory scopeFactory, ILogger<SentryService> logger) {
- var config = scopeFactory.CreateScope().ServiceProvider.GetRequiredService<RMUDesktopConfiguration>();
- if (config.SentryDsn is null) {
- logger.LogWarning("Sentry DSN is not set, skipping Sentry initialisation");
- return;
- }
- _sentrySdkDisposable = SentrySdk.Init(o => {
- o.Dsn = config.SentryDsn;
- // When configuring for the first time, to see what the SDK is doing:
- o.Debug = true;
- // Set traces_sample_rate to 1.0 to capture 100% of transactions for performance monitoring.
- // We recommend adjusting this value in production.
- o.TracesSampleRate = 1.0;
- // Enable Global Mode if running in a client app
- o.IsGlobalModeEnabled = true;
- });
- }
-
- /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
- public void Dispose() => _sentrySdkDisposable?.Dispose();
-}
diff --git a/MatrixUtils.Desktop/appsettings.Development.json b/MatrixUtils.Desktop/appsettings.Development.json
index a1add03..baec0e2 100644
--- a/MatrixUtils.Desktop/appsettings.Development.json
+++ b/MatrixUtils.Desktop/appsettings.Development.json
@@ -1,14 +1,13 @@
{
- "Logging": {
- "LogLevel": {
- "Default": "Debug",
- "System": "Information",
- "Microsoft": "Information"
- }
- },
- "RMUDesktop": {
- "DataStoragePath": "rmu-desktop/data",
- "CacheStoragePath": "rmu-desktop/cache",
- "SentryDsn": "https://a41e99dd2fdd45f699c432b21ebce632@sentry.thearcanebrony.net/15"
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
}
+ },
+ "RMUDesktop": {
+ "DataStoragePath": "rmu-desktop/data",
+ "CacheStoragePath": "rmu-desktop/cache"
+ }
}
diff --git a/MatrixUtils.DmSpaced/MatrixUtils.DmSpaced.csproj b/MatrixUtils.DmSpaced/MatrixUtils.DmSpaced.csproj
index f8dd598..96c3bff 100644
--- a/MatrixUtils.DmSpaced/MatrixUtils.DmSpaced.csproj
+++ b/MatrixUtils.DmSpaced/MatrixUtils.DmSpaced.csproj
@@ -1,31 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>net9.0</TargetFramework>
- <LangVersion>preview</LangVersion>
- <ImplicitUsings>enable</ImplicitUsings>
- <Nullable>enable</Nullable>
- <PublishAot>false</PublishAot>
- <InvariantGlobalization>true</InvariantGlobalization>
- <!-- <PublishTrimmed>true</PublishTrimmed>-->
- <!-- <PublishReadyToRun>true</PublishReadyToRun>-->
- <!-- <PublishSingleFile>true</PublishSingleFile>-->
- <!-- <PublishReadyToRunShowWarnings>true</PublishReadyToRunShowWarnings>-->
- <!-- <PublishTrimmedShowLinkerSizeComparison>true</PublishTrimmedShowLinkerSizeComparison>-->
- <!-- <PublishTrimmedShowLinkerSizeComparisonWarnings>true</PublishTrimmedShowLinkerSizeComparisonWarnings>-->
- </PropertyGroup>
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net10.0</TargetFramework>
+ <LangVersion>preview</LangVersion>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ <PublishAot>false</PublishAot>
+ <InvariantGlobalization>true</InvariantGlobalization>
+ <!-- <PublishTrimmed>true</PublishTrimmed>-->
+ <!-- <PublishReadyToRun>true</PublishReadyToRun>-->
+ <!-- <PublishSingleFile>true</PublishSingleFile>-->
+ <!-- <PublishReadyToRunShowWarnings>true</PublishReadyToRunShowWarnings>-->
+ <!-- <PublishTrimmedShowLinkerSizeComparison>true</PublishTrimmedShowLinkerSizeComparison>-->
+ <!-- <PublishTrimmedShowLinkerSizeComparisonWarnings>true</PublishTrimmedShowLinkerSizeComparisonWarnings>-->
+ </PropertyGroup>
- <ItemGroup>
- <ProjectReference Include="..\MatrixUtils.LibDMSpace\MatrixUtils.LibDMSpace.csproj" />
- </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\MatrixUtils.LibDMSpace\MatrixUtils.LibDMSpace.csproj"/>
+ </ItemGroup>
- <ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
- </ItemGroup>
- <ItemGroup>
- <Content Include="appsettings*.json">
- <CopyToOutputDirectory>Always</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.2.25502.107"/>
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="appsettings*.json">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
</Project>
diff --git a/MatrixUtils.LibDMSpace/DMSpaceRoom.cs b/MatrixUtils.LibDMSpace/DMSpaceRoom.cs
index 89ab91b..1186b6c 100644
--- a/MatrixUtils.LibDMSpace/DMSpaceRoom.cs
+++ b/MatrixUtils.LibDMSpace/DMSpaceRoom.cs
@@ -58,7 +58,7 @@ public class DMSpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomI
var (userId, dmRooms) = entry;
DMSpaceChildLayer? layer = await GetStateOrNullAsync<DMSpaceChildLayer>(DMSpaceChildLayer.EventId, userId.UrlEncode()) ?? await CreateLayer(userId);
return (entry, layer);
- }).ToAsyncEnumerable();
+ }).ToAsyncResultEnumerable();
await foreach (var ((userId, dmRooms), layer) in layerTasks) {
var space = Homeserver.GetRoom(layer.SpaceId).AsSpace();
@@ -117,8 +117,7 @@ public class DMSpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomI
catch {
return (x, null);
}
-
- }).ToAsyncEnumerable();
+ }).ToAsyncResultEnumerable();
await foreach (var (layer, profile) in getProfileTasks) {
if (profile is null) continue;
var layerContent = layer.TypedContent as DMSpaceChildLayer;
diff --git a/MatrixUtils.LibDMSpace/MatrixUtils.LibDMSpace.csproj b/MatrixUtils.LibDMSpace/MatrixUtils.LibDMSpace.csproj
index e39440e..225b264 100644
--- a/MatrixUtils.LibDMSpace/MatrixUtils.LibDMSpace.csproj
+++ b/MatrixUtils.LibDMSpace/MatrixUtils.LibDMSpace.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LinkIncremental>true</LinkIncremental>
@@ -10,6 +10,6 @@
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj" />
+ <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj"/>
</ItemGroup>
</Project>
diff --git a/MatrixUtils.RoomUpgradeCLI/MatrixUtils.RoomUpgradeCLI.csproj b/MatrixUtils.RoomUpgradeCLI/MatrixUtils.RoomUpgradeCLI.csproj
index b0167e2..f349c81 100644
--- a/MatrixUtils.RoomUpgradeCLI/MatrixUtils.RoomUpgradeCLI.csproj
+++ b/MatrixUtils.RoomUpgradeCLI/MatrixUtils.RoomUpgradeCLI.csproj
@@ -1,22 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-MatrixUtils.RoomUpgradeCLI-19ffcbc3-eeaa-4cef-b398-0db2008ca04b</UserSecretsId>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.9" />
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.2.25502.107"/>
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj" />
- <ProjectReference Include="..\LibMatrix\Utilities\LibMatrix.Utilities.Bot\LibMatrix.Utilities.Bot.csproj" />
+ <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj"/>
+ <ProjectReference Include="..\LibMatrix\Utilities\LibMatrix.Utilities.Bot\LibMatrix.Utilities.Bot.csproj"/>
</ItemGroup>
<ItemGroup>
- <Folder Include="tmp\" />
+ <Folder Include="tmp\"/>
</ItemGroup>
</Project>
diff --git a/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj b/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj
index 11781a8..880401a 100644
--- a/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj
+++ b/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj
@@ -1,22 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.9" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.0-rc.2.25502.107"/>
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\MatrixUtils.Web\MatrixUtils.Web.csproj" />
+ <ProjectReference Include="..\MatrixUtils.Web\MatrixUtils.Web.csproj"/>
</ItemGroup>
<ItemGroup>
- <Folder Include="Controllers" />
+ <Folder Include="Controllers"/>
</ItemGroup>
diff --git a/MatrixUtils.Web/MatrixUtils.Web.csproj b/MatrixUtils.Web/MatrixUtils.Web.csproj
index 44fce2d..f7ebb62 100644
--- a/MatrixUtils.Web/MatrixUtils.Web.csproj
+++ b/MatrixUtils.Web/MatrixUtils.Web.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LinkIncremental>true</LinkIncremental>
@@ -13,38 +13,40 @@
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
<BlazorCacheBootResources>false</BlazorCacheBootResources>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
+ <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
+ <WasmEnableHotReload>false</WasmEnableHotReload>
</PropertyGroup>
<!-- Explicitly disable all the unused runtime things trimming would have removed anyways -->
<!-- https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trimming-options -->
-<!-- <PropertyGroup>-->
-<!-- <AutoreleasePoolSupport>false</AutoreleasePoolSupport> <!– Browser != MacOS –>-->
-<!-- <MetadataUpdaterSupport>false</MetadataUpdaterSupport> <!– Unreliable –>-->
-<!-- <DebuggerSupport>false</DebuggerSupport> <!– Unreliable –>-->
-<!-- <InvariantGlobalization>true</InvariantGlobalization> <!– invariant globalization is fine –>-->
-<!-- <!– unused features –>-->
-<!-- <EventSourceSupport>false</EventSourceSupport>-->
-<!-- <EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>-->
-<!-- <HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>-->
-<!-- <EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>-->
-<!-- <MetricsSupport>false</MetricsSupport>-->
-<!-- <UseNativeHttpHandler>false</UseNativeHttpHandler>-->
-<!-- <XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>-->
-<!-- <BuiltInComInteropSupport>false</BuiltInComInteropSupport>-->
-<!-- <CustomResourceTypesSupport>false</CustomResourceTypesSupport>-->
-<!-- <EnableCppCLIHostActivation>false</EnableCppCLIHostActivation>-->
-<!-- <StartupHookSupport>false</StartupHookSupport>-->
-<!-- </PropertyGroup>-->
+ <!-- <PropertyGroup>-->
+ <!-- <AutoreleasePoolSupport>false</AutoreleasePoolSupport> <!– Browser != MacOS –>-->
+ <!-- <MetadataUpdaterSupport>false</MetadataUpdaterSupport> <!– Unreliable –>-->
+ <!-- <DebuggerSupport>false</DebuggerSupport> <!– Unreliable –>-->
+ <!-- <InvariantGlobalization>true</InvariantGlobalization> <!– invariant globalization is fine –>-->
+ <!-- <!– unused features –>-->
+ <!-- <EventSourceSupport>false</EventSourceSupport>-->
+ <!-- <EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>-->
+ <!-- <HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>-->
+ <!-- <EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>-->
+ <!-- <MetricsSupport>false</MetricsSupport>-->
+ <!-- <UseNativeHttpHandler>false</UseNativeHttpHandler>-->
+ <!-- <XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>-->
+ <!-- <BuiltInComInteropSupport>false</BuiltInComInteropSupport>-->
+ <!-- <CustomResourceTypesSupport>false</CustomResourceTypesSupport>-->
+ <!-- <EnableCppCLIHostActivation>false</EnableCppCLIHostActivation>-->
+ <!-- <StartupHookSupport>false</StartupHookSupport>-->
+ <!-- </PropertyGroup>-->
<ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0"/>
<PackageReference Include="Blazored.SessionStorage" Version="2.4.0"/>
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.9" />
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.9" PrivateAssets="all" />
- <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.9" />
- <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="9.0.9" />
- <PackageReference Include="SpawnDev.BlazorJS" Version="2.29.0" />
- <PackageReference Include="SpawnDev.BlazorJS.WebWorkers" Version="2.19.0" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.0-rc.2.25502.107"/>
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.0-rc.2.25502.107" PrivateAssets="all"/>
+ <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="10.0.0-rc.2.25502.107"/>
+ <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="10.0.0-rc.2.25502.107"/>
+ <PackageReference Include="SpawnDev.BlazorJS" Version="2.38.0"/>
+ <PackageReference Include="SpawnDev.BlazorJS.WebWorkers" Version="2.21.0"/>
</ItemGroup>
<ItemGroup>
@@ -53,8 +55,8 @@
</ItemGroup>
<ItemGroup>
-<!-- <PackageReference Include="ArcaneLibs.Blazor.Components" Version="1.0.0-preview.20241210-161342" Condition="'$(Configuration)' == 'Release'"/>-->
-<!-- <ProjectReference Include="..\LibMatrix\ArcaneLibs\ArcaneLibs.Blazor.Components\ArcaneLibs.Blazor.Components.csproj" Condition="'$(Configuration)' == 'Debug'"/>-->
+ <!-- <PackageReference Include="ArcaneLibs.Blazor.Components" Version="1.0.0-preview.20241210-161342" Condition="'$(Configuration)' == 'Release'"/>-->
+ <!-- <ProjectReference Include="..\LibMatrix\ArcaneLibs\ArcaneLibs.Blazor.Components\ArcaneLibs.Blazor.Components.csproj" Condition="'$(Configuration)' == 'Debug'"/>-->
<ProjectReference Include="..\LibMatrix\ArcaneLibs\ArcaneLibs.Blazor.Components\ArcaneLibs.Blazor.Components.csproj"/>
</ItemGroup>
diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor
index eb168f4..f1c5907 100644
--- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor
+++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/RoomQuery/SynapseRoomQueryFilter.razor
@@ -1,46 +1,70 @@
@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters
+@using MatrixUtils.Web.Shared.FilterComponents
<div style="margin-left: 8px; margin-bottom: 8px;">
<u style="display: block;">String contains</u>
- <span class="tile tile280">Room ID: <FancyTextBox @bind-Value="@Filter.RoomIdContains"></FancyTextBox></span>
- <span class="tile tile280">Room name: <FancyTextBox @bind-Value="@Filter.NameContains"></FancyTextBox></span>
- <span class="tile tile280">Canonical alias: <FancyTextBox @bind-Value="@Filter.CanonicalAliasContains"></FancyTextBox></span>
- <span class="tile tile280">Creator: <FancyTextBox @bind-Value="@Filter.CreatorContains"></FancyTextBox></span>
- <span class="tile tile280">Room version: <FancyTextBox @bind-Value="@Filter.VersionContains"></FancyTextBox></span>
- <span class="tile tile280">Encryption algorithm: <FancyTextBox @bind-Value="@Filter.EncryptionContains"></FancyTextBox></span>
- <span class="tile tile280">Join rules: <FancyTextBox @bind-Value="@Filter.JoinRulesContains"></FancyTextBox></span>
- <span class="tile tile280">Guest access: <FancyTextBox @bind-Value="@Filter.GuestAccessContains"></FancyTextBox></span>
- <span class="tile tile280">History visibility: <FancyTextBox @bind-Value="@Filter.HistoryVisibilityContains"></FancyTextBox></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.RoomId" Label="Room ID"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.Name" Label="Room name"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.CanonicalAlias" Label="Canonical alias"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.Creator" Label="Creator"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.Version" Label="Room version"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.Encryption" Label="Encryption algorithm"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.JoinRules" Label="Join rules"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.GuestAccess" Label="Guest access"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.HistoryVisibility" Label="History visibility"/></span>
+ <span class="tile tile280"><StringFilterComponent Filter="@Filter.Topic" Label="Topic"/></span>
<u style="display: block;">Optional checks</u>
- <span class="tile tile150">
- <InputCheckbox @bind-Value="@Filter.CheckFederation"></InputCheckbox> Is federated:
- @if (Filter.CheckFederation) {
- <InputCheckbox @bind-Value="@Filter.Federatable"></InputCheckbox>
- }
- </span>
- <span class="tile tile150">
- <InputCheckbox @bind-Value="@Filter.CheckPublic"></InputCheckbox> Is public:
- @if (Filter.CheckPublic) {
- <InputCheckbox @bind-Value="@Filter.Public"></InputCheckbox>
- }
- </span>
+ <span class="tile tile150"><BooleanFilterComponent Filter="@Filter.Federation" Label="Is federated"/></span>
+ <span class="tile tile150"><BooleanFilterComponent Filter="@Filter.Public" Label="Is public"/></span>
+ <span class="tile tile150"><BooleanFilterComponent Filter="@Filter.Tombstone" Label="Is tombstoned"/></span>
<u style="display: block;">Ranges</u>
<span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEventsGreaterThan"/>
- <span class="range-sep">state events</span>
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEventsLessThan"/>
+ <InputCheckbox @bind-Value="@Filter.StateEvents.Enabled"/>
+ @if (!Filter.StateEvents.Enabled) {
+ <span>State events</span>
+ }
+ else {
+ <InputCheckbox @bind-Value="@Filter.StateEvents.CheckGreaterThan"/>
+ <span> </span>
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEvents.GreaterThan"/>
+ <span class="range-sep">state events</span>
+ <InputCheckbox @bind-Value="@Filter.StateEvents.CheckLessThan"/>
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.StateEvents.LessThan"/>
+ }
</span>
<span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembersGreaterThan"></InputNumber><span class="range-sep">members</span><InputNumber
- max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembersLessThan"></InputNumber>
+ <InputCheckbox @bind-Value="@Filter.JoinedMembers.Enabled"/>
+ @if (!Filter.JoinedMembers.Enabled) {
+ <span>Joined members</span>
+ }
+ else {
+ <InputCheckbox @bind-Value="@Filter.JoinedMembers.CheckGreaterThan"/>
+ <span> </span>
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembers.GreaterThan"/>
+ <span class="range-sep">members</span>
+ <InputCheckbox @bind-Value="@Filter.JoinedMembers.CheckLessThan"/>
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedMembers.LessThan"/>
+ }
</span>
<span class="tile center-children">
- <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedLocalMembersGreaterThan"></InputNumber><span
- class="range-sep">local members</span><InputNumber max="@int.MaxValue" class="int-input" TValue="int"
- @bind-Value="@Filter.JoinedLocalMembersLessThan"></InputNumber>
+ <InputCheckbox @bind-Value="@Filter.JoinedLocalMembers.Enabled"/>
+ <span> </span>
+ @if (!Filter.JoinedLocalMembers.Enabled) {
+ <span>Joined local members</span>
+ }
+ else {
+ <InputCheckbox @bind-Value="@Filter.JoinedLocalMembers.CheckGreaterThan"/>
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedLocalMembers.GreaterThan"/>
+ <span class="range-sep">local members</span>
+ <InputCheckbox @bind-Value="@Filter.JoinedLocalMembers.CheckLessThan"/>
+ <InputNumber max="@int.MaxValue" class="int-input" TValue="int" @bind-Value="@Filter.JoinedLocalMembers.LessThan"/>
+ }
</span>
</div>
+@* @{ *@
+@* Console.WriteLine($"Rendered SynapseRoomQueryFilter with filter: {Filter.ToJson()}"); *@
+@* } *@
@code {
diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor
index 48aea86..fc9f8e8 100644
--- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor
+++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/Components/SynapseRoomShutdownWindowContent.razor
@@ -1,8 +1,12 @@
+@using System.Text.Json.Serialization
+@using ArcaneLibs.Extensions
+@using LibMatrix
+@using LibMatrix.EventTypes.Spec.State.RoomInfo
@using LibMatrix.Homeservers.Extensions.NamedCaches
@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Requests
@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Responses
-@if (string.IsNullOrWhiteSpace(Context.DeleteId)) {
+@if (string.IsNullOrWhiteSpace(Context.DeleteId) || EditorOnly) {
<span>Block room: </span>
<InputCheckbox @bind-Value="@Context.DeleteRequest.Block"/>
<br/>
@@ -34,6 +38,12 @@
<br/>
<span>Delete <b>ALL</b> local user media: </span>
<InputCheckbox @bind-Value="@Context.ExtraOptions.DeleteLocalUserMedia"></InputCheckbox>
+ <br/>
+ <span>Follow tombstone (if any): </span>
+ <InputCheckbox @bind-Value="@Context.ExtraOptions.FollowTombstone"/>
+ @if (!EditorOnly) {
+ <LinkButton InlineText="true" OnClickAsync="@FollowTombstoneAsync">Exec</LinkButton>
+ }
</details>
<details>
@@ -50,7 +60,19 @@
<br/>
</details>
- <LinkButton OnClickAsync="@DeleteRoom">Execute</LinkButton>
+ @if (!EditorOnly) {
+ <LinkButton OnClickAsync="@DeleteRoom">Execute</LinkButton>
+ }
+}
+else {
+ <pre>
+ @(_status?.ToJson() ?? "Loading status...")
+ </pre>
+ <br/>
+ <LinkButton InlineText="true" OnClickAsync="@OnComplete">[Stop tracking]</LinkButton>
+ if (_status?.Status == SynapseAdminRoomDeleteStatus.Failed) {
+ <LinkButton InlineText="true" OnClickAsync="@ForceDelete">[Force delete]</LinkButton>
+ }
}
@code {
@@ -61,14 +83,52 @@
[Parameter]
public required AuthenticatedHomeserverSynapse Homeserver { get; set; }
+ [Parameter]
+ public bool EditorOnly { get; set; }
+
private NamedCache<RoomShutdownContext> TaskMap { get; set; } = null!;
+ private SynapseAdminRoomDeleteStatus? _status = null;
+ private bool _isTracking = false;
protected override async Task OnInitializedAsync() {
+ if (EditorOnly) return;
TaskMap = new NamedCache<RoomShutdownContext>(Homeserver, "gay.rory.matrixutils.synapse_room_shutdown_tasks");
+ var existing = await TaskMap.GetValueAsync(Context.RoomId);
+ if (existing is not null) {
+ Context = existing;
+ }
+
+ if (Context.ExecuteImmediately)
+ await DeleteRoom();
+ }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender) {
+ if (EditorOnly) return;
+ if (!_isTracking) {
+ if (!string.IsNullOrWhiteSpace(Context.DeleteId)) {
+ _isTracking = true;
+ _ = Task.Run(async () => {
+ do {
+ _status = await Homeserver.Admin.GetRoomDeleteStatus(Context.DeleteId);
+ StateHasChanged();
+ if (_status.Status == SynapseAdminRoomDeleteStatus.Complete) {
+ await OnComplete();
+ break;
+ }
+
+ await Task.Delay(1000);
+ } while (_status.Status != SynapseAdminRoomDeleteStatus.Failed && _status.Status != SynapseAdminRoomDeleteStatus.Complete);
+ });
+ }
+ }
}
public class RoomShutdownContext {
public required string RoomId { get; set; }
+
+ [JsonIgnore] // do NOT persist - this triggers immediate purging
+ public bool ExecuteImmediately { get; set; }
+
public string? DeleteId { get; set; }
public ExtraDeleteOptions ExtraOptions { get; set; } = new();
@@ -81,10 +141,11 @@
public SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom? RoomDetails { get; set; }
public class ExtraDeleteOptions {
- // room options
+ public bool FollowTombstone { get; set; }
+
+ // media options
public bool QuarantineLocalMedia { get; set; }
public bool QuarantineRemoteMedia { get; set; }
-
public bool DeleteRemoteMedia { get; set; }
// user options
@@ -95,9 +156,14 @@
}
public async Task OnComplete() {
+ if (EditorOnly) return;
+ Console.WriteLine($"Room shutdown task for {Context.RoomId} completed, removing from map.");
await OnCompleteLock.WaitAsync();
try {
- await TaskMap.RemoveValueAsync(Context.DeleteId!);
+ await TaskMap.RemoveValueAsync(Context.RoomId!);
+ }
+ catch (Exception e) {
+ Console.WriteLine("Failed to remove completed room shutdown task from map: " + e);
}
finally {
OnCompleteLock.Release();
@@ -105,6 +171,11 @@
}
public async Task DeleteRoom() {
+ if (EditorOnly) return;
+ if (Context.ExtraOptions.FollowTombstone) await FollowTombstoneAsync();
+
+ Console.WriteLine($"Deleting room {Context.RoomId} with options: " + Context.DeleteRequest.ToJson());
+
var resp = await Homeserver.Admin.DeleteRoom(Context.RoomId, Context.DeleteRequest, false);
Context.DeleteId = resp.DeleteId;
await TaskMap.SetValueAsync(Context.RoomId, Context);
@@ -112,4 +183,84 @@
private static readonly SemaphoreSlim OnCompleteLock = new(1, 1);
+ private async Task FollowTombstoneAsync() {
+ if (EditorOnly) return;
+ var tomb = await TryGetTombstoneAsync();
+ var content = tomb?.ContentAs<RoomTombstoneEventContent>();
+ if (content != null && !string.IsNullOrWhiteSpace(content.ReplacementRoom)) {
+ Console.WriteLine("Tombstone: " + tomb.ToJson());
+ if (!content.ReplacementRoom.StartsWith('!')) {
+ Console.WriteLine($"Invalid replacement room ID in tombstone: {content.ReplacementRoom}, ignoring!");
+ }
+ else {
+ var oldMembers = await Homeserver.Admin.GetRoomMembersAsync(Context.RoomId, localOnly: true);
+ var isKnownRoom = await Homeserver.Admin.CheckRoomKnownAsync(content.ReplacementRoom);
+ var targetMembers = isKnownRoom
+ ? await Homeserver.Admin.GetRoomMembersAsync(Context.RoomId, localOnly: true)
+ : new() { Members = [] };
+
+ var members = oldMembers.Members.Except(targetMembers.Members).ToList();
+ Console.WriteLine("To migrate: " + members.ToJson());
+ foreach (var member in members) {
+ var success = false;
+ do {
+ var sess = member == Homeserver.WhoAmI.UserId ? Homeserver : await Homeserver.Admin.GetHomeserverForUserAsync(member, TimeSpan.FromSeconds(15));
+ var oldRoom = sess.GetRoom(Context.RoomId);
+ var room = sess.GetRoom(content.ReplacementRoom);
+ try {
+ var servers = (await oldRoom.GetMembersByHomeserverAsync(joinedOnly: true))
+ .Select(x => new KeyValuePair<string, int>(x.Key, x.Value.Count))
+ .OrderByDescending(x => x.Key == "matrix.org" ? 0 : x.Value); // try anything else first, to reduce load on matrix.org
+
+ await room.JoinAsync(servers.Take(10).Select(x => x.Key).ToArray(), reason: "Automatically following tombstone as old room is being purged.", checkIfAlreadyMember: isKnownRoom);
+ Console.WriteLine($"Migrated {member} from {Context.RoomId} to {content.ReplacementRoom}");
+ success = true;
+ }
+ catch (Exception e) {
+ if (e is MatrixException { ErrorCode: "M_FORBIDDEN" }) {
+ Console.WriteLine($"Cannot migrate {member} to {content.ReplacementRoom}: {(e as MatrixException)!.GetAsJson()}");
+ success = true; // give up
+ continue;
+ }
+
+ Console.WriteLine($"Failed to invite {member} to {content.ReplacementRoom}: {e}");
+ success = false;
+ await Task.Delay(1000);
+ }
+ } while (!success);
+ }
+ }
+ }
+ }
+
+ private async Task<StateEventResponse?> TryGetTombstoneAsync() {
+ if (EditorOnly) return null;
+ try {
+ return (await Homeserver.Admin.GetRoomStateAsync(Context.RoomId, RoomTombstoneEventContent.EventId)).Events.FirstOrDefault(x => x.StateKey == "");
+ }
+ catch {
+ return null;
+ }
+ }
+
+ private async Task ForceDelete() {
+ if (EditorOnly) return;
+ Console.WriteLine($"Forcing purge for {Context.RoomId}!");
+ await OnCompleteLock.WaitAsync();
+ try {
+ var resp = await Homeserver.Admin.DeleteRoom(Context.RoomId, new() {
+ ForcePurge = true
+ }, waitForCompletion: false);
+ Context.DeleteId = resp.DeleteId;
+ await TaskMap.SetValueAsync(Context.RoomId, Context);
+ StateHasChanged();
+ }
+ catch (Exception e) {
+ Console.WriteLine("Failed to remove completed room shutdown task from map: " + e);
+ }
+ finally {
+ OnCompleteLock.Release();
+ }
+ }
+
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor
index 07a3dd2..05899c8 100644
--- a/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor
+++ b/MatrixUtils.Web/Pages/HSAdmin/Synapse/RoomQuery.razor
@@ -1,5 +1,7 @@
@page "/HSAdmin/Synapse/RoomQuery"
@using System.Diagnostics.CodeAnalysis
+@using System.Text.Json
+@using ArcaneLibs.Blazor.Components.Services
@using Microsoft.AspNetCore.WebUtilities
@using ArcaneLibs.Extensions
@using LibMatrix
@@ -10,6 +12,7 @@
@using MatrixUtils.Web.Pages.HSAdmin.Synapse.Components
@using MatrixUtils.Web.Pages.HSAdmin.Synapse.Components.RoomQuery
@inject ILogger<RoomQuery> Logger
+@inject BlazorSaveFileService BlazorSaveFileService
<h3>Homeserver Administration - Room Query</h3>
@@ -21,15 +24,42 @@
<option value="@item.Key">@item.Value</option>
}
</select><br/>
-<label>Ascending: </label>
-<InputCheckbox @bind-Value="Ascending"/><br/>
+<InputCheckbox @bind-Value="Ascending"/>
+<label> Ascending</label><br/>
+<InputCheckbox @bind-Value="FetchV12PlusCreatorServer"/>
+<label> Fetch v12+ room creation homeserver</label>
+<LinkButton InlineText="true" OnClickAsync="FetchV12PlusCreatorServersAsync"> (Execute manually)</LinkButton><br/>
+<InputCheckbox @bind-Value="FetchTombstones"/>
+<label> Check for tombstone events</label>
+<LinkButton InlineText="true" OnClickAsync="FetchTombstoneEventsAsync"> (Execute manually)</LinkButton><br/>
+<InputCheckbox @bind-Value="SummarizeLocalMembers"/>
+<label> Fetch local member list for small rooms</label>
+<LinkButton InlineText="true" OnClickAsync="FetchLocalMemberEventsAsync"> (Execute manually)</LinkButton><br/>
+<InputCheckbox @bind-Value="ShowFullResultData"/>
+<label> Show full result data (JSON)</label><br/>
+<InputCheckbox @bind-Value="EnableMultiPurge"/>
+<label> Enable multi-purge mode</label>
+@if (EnableMultiPurge) {
+ <span> </span>
+ <LinkButton InlineText="true" OnClick="@MultiPurgeInvertSelection">[Invert selection]</LinkButton>
+ <span> </span>
+ <details style="display: inline-block;">
+ <summary>Edit purge options</summary>
+ <SynapseRoomShutdownWindowContent Context="@DefaultShutdownContext" Homeserver="Homeserver" EditorOnly="true"/>
+ </details>
+}
+else {
+ <br/>
+}
<details>
- <summary>
- <span>Local filtering (slow)</span>
- </summary>
+ <summary>Local filtering (slow)</summary>
<SynapseRoomQueryFilter Filter="@Filter"/>
</details>
-<button class="btn btn-primary" @onclick="Search">Search</button>
+<LinkButton OnClickAsync="@Search">Search</LinkButton>
+
+@if (EnableMultiPurge) {
+ <LinkButton Color="#FF8800" OnClick="@PurgeSelection">Purge selected rooms</LinkButton>
+}
<br/>
@if (Results.Count > 0) {
@@ -40,6 +70,10 @@
<div class="room-list-item">
@* <RoomListItem RoomName="@res.Name" RoomId="@res.RoomId"></RoomListItem> *@
<p>
+ @if (EnableMultiPurge) {
+ <InputCheckbox @bind-Value="@room.MultiPurgeSelected"/>
+ <span> </span>
+ }
@if (!string.IsNullOrWhiteSpace(room.CanonicalAlias)) {
<span>@room.CanonicalAlias - </span>
}
@@ -57,6 +91,8 @@
<p>
<LinkButton OnClickAsync="@(() => DeleteRoom(room))">Delete room</LinkButton>
<LinkButton target="_blank" href="@($"/HSAdmin/Synapse/ResyncState?roomId={room.RoomId}&via={room.OriginHomeserver}")">Resync state</LinkButton>
+ <LinkButton OnClickAsync="@(() => ExportState(room))">@(room.JoinedLocalMembers == 0 ? "Try to export state" : "Export state")</LinkButton>
+ <LinkButton OnClickAsync="@(() => ForceJoin(room))">Force Join</LinkButton>
</p>
@{
@@ -119,11 +155,23 @@
memberSummary += $": {string.Join(", ", room.LocalMembers)}";
}
}
- <span>@memberSummary</span>
- <details>
- <summary>Full result data</summary>
- <pre>@room.ToJson(ignoreNull: true)</pre>
- </details>
+ <span>@memberSummary</span><br/>
+ @if (!string.IsNullOrWhiteSpace(room.TopicEvent?.ContentAs<RoomTopicEventContent>()?.Topic)) {
+ <details>
+ <summary>Room topic</summary>
+ <pre>@(room.TopicEvent?.ContentAs<RoomTopicEventContent>()?.Topic)</pre>
+ </details>
+ }
+ @foreach (var ex in room.Exceptions) {
+ <span style="color: red;">@ex</span>
+ <br/>
+ }
+ @if (ShowFullResultData) {
+ <details>
+ <summary>Full result data</summary>
+ <pre>@room.ToJson(ignoreNull: true)</pre>
+ </details>
+ }
</div>
}
@* *@
@@ -148,10 +196,6 @@
</ModalWindow>
}
-<style>
-
-</style>
-
@code {
[Parameter]
@@ -166,6 +210,18 @@
[SupplyParameterFromQuery(Name = "ascending")]
public bool Ascending { get; set; } = true;
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "FetchV12PlusCreatorServer")]
+ public bool FetchV12PlusCreatorServer { get; set; } = true;
+
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "SummarizeLocalMembers")]
+ public bool SummarizeLocalMembers { get; set; } = true;
+
+ [Parameter]
+ [SupplyParameterFromQuery(Name = "FetchTombstones")]
+ public bool FetchTombstones { get; set; } = true;
+
private List<RoomInfo> Results { get; set; } = new();
private AuthenticatedHomeserverSynapse Homeserver { get; set; } = null!;
@@ -178,6 +234,21 @@
private NamedCache<SynapseRoomShutdownWindowContent.RoomShutdownContext> TaskMap { get; set; } = null!;
+ private SynapseRoomShutdownWindowContent.RoomShutdownContext DefaultShutdownContext { get; set; } = new() {
+ RoomId = "",
+ DeleteRequest = new() { Block = true, Purge = true, ForcePurge = false }
+ };
+
+ public bool ShowFullResultData {
+ get;
+ set {
+ field = value;
+ StateHasChanged();
+ }
+ }
+
+ public bool EnableMultiPurge { get; set; }
+
protected override async Task OnInitializedAsync() {
var hs = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true);
if (hs is not AuthenticatedHomeserverSynapse synapse) {
@@ -199,67 +270,95 @@
foreach (var (key, value) in QueryHelpers.ParseQuery(new Uri(NavigationManager.Uri).Query)) {
switch (key) {
case "RoomIdContains":
- Filter.RoomIdContains = value[0]!;
+ Filter.RoomId.Enabled = Filter.RoomId.CheckValueContains = true;
+ Filter.RoomId.ValueContains = value[0]!;
break;
case "NameContains":
- Filter.NameContains = value[0]!;
+ Filter.Name.Enabled = Filter.Name.CheckValueContains = true;
+ Filter.Name.ValueContains = value[0]!;
break;
case "CanonicalAliasContains":
- Filter.CanonicalAliasContains = value[0]!;
+ Filter.CanonicalAlias.Enabled = Filter.CanonicalAlias.CheckValueContains = true;
+ Filter.CanonicalAlias.ValueContains = value[0]!;
break;
case "VersionContains":
- Filter.VersionContains = value[0]!;
+ Filter.Version.Enabled = Filter.Version.CheckValueContains = true;
+ Filter.Version.ValueContains = value[0]!;
break;
case "CreatorContains":
- Filter.CreatorContains = value[0]!;
+ Filter.Creator.Enabled = Filter.Creator.CheckValueContains = true;
+ Filter.Creator.ValueContains = value[0]!;
break;
case "EncryptionContains":
- Filter.EncryptionContains = value[0]!;
+ Filter.Encryption.Enabled = Filter.Encryption.CheckValueContains = true;
+ Filter.Encryption.ValueContains = value[0]!;
break;
case "JoinRulesContains":
- Filter.JoinRulesContains = value[0]!;
+ Filter.JoinRules.Enabled = Filter.JoinRules.CheckValueContains = true;
+ Filter.JoinRules.ValueContains = value[0]!;
break;
case "GuestAccessContains":
- Filter.GuestAccessContains = value[0]!;
+ Filter.GuestAccess.Enabled = Filter.GuestAccess.CheckValueContains = true;
+ Filter.GuestAccess.ValueContains = value[0]!;
break;
case "HistoryVisibilityContains":
- Filter.HistoryVisibilityContains = value[0]!;
+ Filter.HistoryVisibility.Enabled = Filter.HistoryVisibility.CheckValueContains = true;
+ Filter.HistoryVisibility.ValueContains = value[0]!;
break;
case "Federatable":
- Filter.Federatable = bool.Parse(value[0]!);
- Filter.CheckFederation = true;
+ Filter.Federation = new() {
+ Enabled = true,
+ Value = bool.Parse(value[0]!)
+ };
break;
case "Public":
- Filter.Public = value[0] == "true";
- Filter.CheckPublic = true;
+ Filter.Public = new() {
+ Enabled = true,
+ Value = bool.Parse(value[0]!)
+ };
break;
case "JoinedMembersGreaterThan":
- Filter.JoinedMembersGreaterThan = int.Parse(value[0]!);
+ Filter.JoinedMembers.Enabled = Filter.JoinedLocalMembers.CheckGreaterThan = true;
+ Filter.JoinedMembers.GreaterThan = int.Parse(value[0]!);
break;
case "JoinedMembersLessThan":
- Filter.JoinedMembersLessThan = int.Parse(value[0]!);
+ Filter.JoinedMembers.Enabled = Filter.JoinedLocalMembers.CheckLessThan = true;
+ Filter.JoinedMembers.LessThan = int.Parse(value[0]!);
break;
case "JoinedLocalMembersGreaterThan":
- Filter.JoinedLocalMembersGreaterThan = int.Parse(value[0]!);
+ Filter.JoinedLocalMembers.Enabled = Filter.JoinedLocalMembers.CheckGreaterThan = true;
+ Filter.JoinedLocalMembers.GreaterThan = int.Parse(value[0]!);
break;
case "JoinedLocalMembersLessThan":
- Filter.JoinedLocalMembersLessThan = int.Parse(value[0]!);
+ Filter.JoinedLocalMembers.Enabled = Filter.JoinedLocalMembers.CheckLessThan = true;
+ Filter.JoinedLocalMembers.LessThan = int.Parse(value[0]!);
break;
case "StateEventsGreaterThan":
- Filter.StateEventsGreaterThan = int.Parse(value[0]!);
+ Filter.StateEvents.Enabled = Filter.StateEvents.CheckGreaterThan = true;
+ Filter.StateEvents.GreaterThan = int.Parse(value[0]!);
break;
case "StateEventsLessThan":
- Filter.StateEventsLessThan = int.Parse(value[0]!);
+ Filter.StateEvents.Enabled = Filter.StateEvents.CheckLessThan = true;
+ Filter.StateEvents.LessThan = int.Parse(value[0]!);
break;
case "Execute":
execute = true;
break;
+ case "order_by":
+ case "name_search":
+ case "ascending":
+ case "FetchV12PlusCreatorServer":
+ case "SummarizeLocalMembers":
+ case "FetchTombstones":
+ break;
default:
Console.WriteLine($"Unknown query parameter: {key}");
break;
}
}
+ StateHasChanged();
+
if (execute)
_ = Search();
@@ -268,7 +367,26 @@
private async Task Search() {
Results.Clear();
- var searchRooms = Homeserver.Admin.SearchRoomsAsync(orderBy: OrderBy!, dir: Ascending ? "f" : "b", searchTerm: SearchTerm, localFilter: Filter).GetAsyncEnumerator();
+ Console.WriteLine("Starting search... Parameters: " + new {
+ orderBy = OrderBy!,
+ dir = Ascending ? "f" : "b",
+ searchTerm = SearchTerm,
+ localFilter = Filter,
+ chunkLimit = 1000,
+ fetchTombstones = FetchTombstones,
+ fetchTopics = true,
+ fetchCreateEvents = true
+ }.ToJson());
+ var searchRooms = Homeserver.Admin.SearchRoomsAsync(
+ orderBy: OrderBy!,
+ dir: Ascending ? "f" : "b",
+ searchTerm: SearchTerm,
+ localFilter: Filter,
+ chunkLimit: 1000,
+ fetchTombstones: FetchTombstones,
+ fetchTopics: true,
+ fetchCreateEvents: true
+ ).GetAsyncEnumerator();
var joinedRooms = await Homeserver.GetJoinedRooms();
while (await searchRooms.MoveNextAsync()) {
var room = searchRooms.Current;
@@ -288,15 +406,26 @@
StateEvents = room.StateEvents,
JoinedMembers = room.JoinedMembers,
JoinedLocalMembers = room.JoinedLocalMembers,
- OriginHomeserver = joinedRooms.Any(x => x.RoomId == room.RoomId)
- ? await Homeserver.GetRoom(room.RoomId).GetOriginHomeserverAsync()
- : (await Homeserver.Admin.GetRoomStateAsync(room.RoomId, RoomCreateEventContent.EventId)).Events.FirstOrDefault()?.Sender?.Split(':', 2)[1]
- ?? string.Empty
+ OriginHomeserver =
+ Homeserver.GetRoom(room.RoomId).IsV12PlusRoomId
+ ? room.RoomId.Split(':', 2).Skip(1).FirstOrDefault(string.Empty)
+ : string.Empty
};
+ if (string.IsNullOrWhiteSpace(roomInfo.OriginHomeserver) && FetchV12PlusCreatorServer) {
+ try {
+ if (joinedRooms.Any(x => x.RoomId == room.RoomId))
+ roomInfo.OriginHomeserver = await Homeserver.GetRoom(room.RoomId).GetOriginHomeserverAsync();
+ else roomInfo.OriginHomeserver = (await Homeserver.Admin.GetRoomStateAsync(room.RoomId, RoomCreateEventContent.EventId)).Events.FirstOrDefault()?.Sender?.Split(':', 2)[1];
+ }
+ catch (MatrixException e) {
+ roomInfo.Exceptions.Add($"While getting origin homeserver: {e.GetAsObject().ToJson(indent: false, ignoreNull: true)}");
+ }
+ }
+
Results.Add(roomInfo);
- if ((Results.Count <= 200 && Results.Count % 10 == 0) || Results.Count % 1000 == 0) {
+ if ((Results.Count <= 200 && Results.Count % 10 == 0 && FetchV12PlusCreatorServer) || Results.Count % 1000 == 0) {
StateHasChanged();
await Task.Yield();
await Task.Delay(1);
@@ -305,97 +434,29 @@
StateHasChanged();
- var getLocalMembersTasks = Results
- .Where(x => x.JoinedLocalMembers is > 0 and < 100)
- .Select(async r => {
- var members = (await Homeserver.Admin.GetRoomMembersAsync(r.RoomId)).Members.Where(x => x.EndsWith(":" + Homeserver.ServerName)).ToList();
- r.LocalMembers = members;
- }
- );
- await Task.WhenAll(getLocalMembersTasks);
-
- var getTombstoneTasks = Results
- .Select(async r => {
- var state = await Homeserver.Admin.GetRoomStateAsync(r.RoomId, type: "m.room.tombstone");
- var tombstone = state.Events.FirstOrDefault(x => x is { StateKey: "", Type: "m.room.tombstone" });
- if (tombstone is { } tombstoneEvent) {
- r.TombstoneEvent = tombstoneEvent;
- }
- });
- await Task.WhenAll(getTombstoneTasks);
+ if (FetchV12PlusCreatorServer) await FetchV12PlusCreatorServersAsync(false);
+ if (SummarizeLocalMembers) await FetchLocalMemberEventsAsync(false);
+ // if (CheckTombstone) await FetchTombstoneEventsAsync(false);
StateHasChanged();
}
- Task DeleteRoom(RoomInfo room) {
- DeleteRequests.TryAdd(room.RoomId, new() { RoomId = room.RoomId, RoomDetails = room, DeleteRequest = new() { Block = true, Purge = true, ForcePurge = false } });
+ private Task DeleteRoom(RoomInfo room, bool executeWithoutConfirmation = false) {
+ var dc = JsonSerializer.Deserialize<SynapseRoomShutdownWindowContent.RoomShutdownContext>(DefaultShutdownContext.ToJson())!;
+ dc.RoomId = room.RoomId;
+ dc.RoomDetails = room;
+ dc.ExecuteImmediately = executeWithoutConfirmation;
+ DeleteRequests.TryAdd(room.RoomId, dc);
StateHasChanged();
return Task.CompletedTask;
}
- //
- // private async Task DeleteRoom() {
- // if (DeleteRequest is { } deleteRequest) {
- // var media = await Homeserver.Admin.GetRoomMediaAsync(deleteRequest.RoomId);
- // if (deleteRequest.DeleteRequest.QuarantineRemoteMedia) {
- // foreach (var remoteMedia in media.Remote) {
- // await Homeserver.Admin.QuarantineMediaById(remoteMedia);
- // }
- // }
- //
- // if (deleteRequest.DeleteRequest.DeleteRemoteMedia) {
- // foreach (var remoteMedia in media.Remote) {
- // await Homeserver.Admin.DeleteMediaById(remoteMedia);
- // }
- // }
- // else if (deleteRequest.DeleteRequest.QuarantineLocalMedia) {
- // foreach (var localMedia in media.Local) {
- // await Homeserver.Admin.QuarantineMediaById(localMedia);
- // }
- // }
- //
- // var deleteId = await Homeserver.Admin.DeleteRoom(deleteRequest.RoomId, deleteRequest.DeleteRequest, waitForCompletion: false);
- // DeleteRequest = null;
- // List<string> alreadyCleanedUsers = [];
- // while (true) {
- // var status = await Homeserver.Admin.GetRoomDeleteStatus(deleteId.DeleteId);
- // DeleteStatuses[deleteRequest.RoomId] = status;
- // StateHasChanged();
- // await Task.Delay(5000);
- // if (status.Status == "complete") {
- // DeleteStatuses.Remove(deleteRequest.RoomId);
- // StateHasChanged();
- // break;
- // }
- //
- // if (status.Status == "failed") {
- // deleteId = await Homeserver.Admin.DeleteRoom(deleteRequest.RoomId, deleteRequest.DeleteRequest, waitForCompletion: false);
- // }
- //
- // var newCleanedUsers = status.ShutdownRoom?.KickedUsers?.Except(alreadyCleanedUsers).ToList();
- // if (newCleanedUsers is not null) {
- // alreadyCleanedUsers.AddRange(newCleanedUsers);
- // foreach (var user in newCleanedUsers) {
- // if (deleteRequest.DeleteRequest.SuspendLocalUsers) {
- // // await Homeserver.Admin.(user);
- // }
- //
- // if (deleteRequest.DeleteRequest.QuarantineLocalUserMedia) {
- // await Homeserver.Admin.QuarantineMediaByUserId(user);
- // }
- //
- // if (deleteRequest.DeleteRequest.DeleteLocalUserMedia) {
- // var userMedia = Homeserver.Admin.GetUserMediaEnumerableAsync(user);
- // await foreach (var mediaEntry in userMedia) {
- // await Homeserver.Admin.DeleteMediaById(mediaEntry.MediaId);
- // }
- // }
- // }
- // }
- // }
- // }
- // }
+ private void PurgeSelection() {
+ foreach (var room in Results.Where(x => x.MultiPurgeSelected)) {
+ DeleteRoom(room, true);
+ }
+ }
private readonly Dictionary<string, string> validOrderBy = new() {
{ "name", "Room name" },
@@ -415,11 +476,143 @@
private class RoomInfo : SynapseAdminRoomListResult.SynapseAdminRoomListResultRoom {
public List<string>? LocalMembers { get; set; }
- public StateEventResponse? TombstoneEvent { get; set; }
public required string OriginHomeserver { get; set; }
[field: AllowNull, MaybeNull]
public string MemberSummary => field ??= $"{JoinedMembers} members, of which {JoinedLocalMembers} are on this server";
+
+ public List<string> Exceptions { get; set; } = [];
+ public bool MultiPurgeSelected { get; set; }
+ }
+
+ private async Task ExportState(RoomInfo room) {
+ try {
+ var state = await Homeserver.Admin.GetRoomStateAsync(room.RoomId);
+ var json = state.ToJson();
+ await BlazorSaveFileService.SaveFileAsync($"{room.RoomId.Replace(":", "_")}_state.json", System.Text.Encoding.UTF8.GetBytes(json), "application/json");
+ }
+ catch (Exception e) {
+ Logger.LogError(e, "Failed to export room state for {RoomId}", room.RoomId);
+ }
+ }
+
+ private async Task ForceJoin(RoomInfo room) {
+ try {
+ await Homeserver.GetRoom(room.RoomId).JoinAsync([Homeserver.ServerName]);
+ }
+ catch (Exception e) {
+ Logger.LogError(e, "Failed to force-join room {RoomId}", room.RoomId);
+ // await Homeserver.Admin.room
+ }
+ }
+
+ private SemaphoreSlim _concurrencyLimiter = new SemaphoreSlim(16, 16);
+
+ private async Task FetchV12PlusCreatorServersAsync() => await FetchV12PlusCreatorServersAsync(true);
+
+ private async Task FetchV12PlusCreatorServersAsync(bool rerender) {
+ var joinedRooms = await Homeserver.GetJoinedRooms();
+ var tasks = Results
+ .Where(x => string.IsNullOrWhiteSpace(x.OriginHomeserver))
+ .Select(async r => {
+ if (!string.IsNullOrWhiteSpace(r.Creator) && r.Creator.Contains(':')) {
+ r.OriginHomeserver = r.Creator.Split(':', 2)[1];
+ return;
+ }
+
+ if (r.CreateEvent != null && !string.IsNullOrWhiteSpace(r.CreateEvent.Sender) && r.CreateEvent.Sender.Contains(':')) {
+ r.OriginHomeserver = r.CreateEvent.Sender.Split(':', 2)[1];
+ return;
+ }
+
+ await _concurrencyLimiter.WaitAsync();
+ try {
+ if (joinedRooms.Any(x => x.RoomId == r.RoomId))
+ r.OriginHomeserver = await Homeserver.GetRoom(r.RoomId).GetOriginHomeserverAsync();
+ else r.OriginHomeserver = (await Homeserver.Admin.GetRoomStateAsync(r.RoomId, RoomCreateEventContent.EventId)).Events.FirstOrDefault()?.Sender?.Split(':', 2)[1];
+ }
+ catch (MatrixException e) {
+ r.Exceptions.Add($"While getting origin homeserver: {e.GetAsObject().ToJson(indent: false, ignoreNull: true)}");
+ }
+ catch (Exception e) {
+ Console.WriteLine($"Failed to get origin homeserver for {r.RoomId}, unhandled exception: " + e);
+ }
+ finally {
+ _concurrencyLimiter.Release();
+ }
+ });
+
+ await Task.WhenAll(tasks);
+
+ if (rerender)
+ StateHasChanged();
+ }
+
+ private async Task FetchTombstoneEventsAsync() => await FetchTombstoneEventsAsync(true);
+
+ private async Task FetchTombstoneEventsAsync(bool rerender) {
+ var getTombstoneTasks = Results
+ .Where(x => x.TombstoneEvent is null)
+ .Select(async r => {
+ await _concurrencyLimiter.WaitAsync();
+ try {
+ var state = await Homeserver.Admin.GetRoomStateAsync(r.RoomId, type: "m.room.tombstone");
+ var tombstone = state.Events.FirstOrDefault(x => x is { StateKey: "", Type: "m.room.tombstone" });
+ if (tombstone is { } tombstoneEvent) {
+ r.TombstoneEvent = tombstoneEvent;
+ }
+ }
+ catch (MatrixException e) {
+ r.Exceptions.Add($"While checking for tombstone: {e.GetAsObject().ToJson(indent: false, ignoreNull: true)}");
+ }
+ catch (Exception e) {
+ Console.WriteLine($"Failed to check tombstone for {r.RoomId}, unhandled exception: " + e);
+ }
+ finally {
+ _concurrencyLimiter.Release();
+ }
+ });
+
+ await Task.WhenAll(getTombstoneTasks);
+
+ if (rerender)
+ StateHasChanged();
+ }
+
+ private async Task FetchLocalMemberEventsAsync() => await FetchLocalMemberEventsAsync(true);
+
+ private async Task FetchLocalMemberEventsAsync(bool rerender) {
+ var getLocalMembersTasks = Results
+ .Where(x => x.LocalMembers is null && x.JoinedLocalMembers is > 0 and < 100)
+ .Select(async r => {
+ await _concurrencyLimiter.WaitAsync();
+ try {
+ var members = (await Homeserver.Admin.GetRoomMembersAsync(r.RoomId)).Members.Where(x => x.EndsWith(":" + Homeserver.ServerName)).ToList();
+ r.LocalMembers = members;
+ }
+ catch (MatrixException e) {
+ r.Exceptions.Add($"While fetching local members: {e.GetAsObject().ToJson(ignoreNull: true, indent: false)}");
+ }
+ catch (Exception e) {
+ Console.WriteLine($"Failed to fetch local members for {r.RoomId}, unhandled exception: " + e);
+ }
+ finally {
+ _concurrencyLimiter.Release();
+ }
+ });
+
+ await Task.WhenAll(getLocalMembersTasks);
+
+ if (rerender)
+ StateHasChanged();
+ }
+
+ private void MultiPurgeInvertSelection() {
+ foreach (var room in Results) {
+ room.MultiPurgeSelected ^= true;
+ }
+
+ StateHasChanged();
}
}
diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor
index 360548d..7199934 100644
--- a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor
+++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor
@@ -26,7 +26,7 @@
<InputCheckbox @bind-Value="SetupData.DmSpaceInfo.LayerByUser"></InputCheckbox>
Create sub-spaces per user
</p>
-
+
<br/>
<LinkButton OnClickAsync="@Disband" Color="#FF0000">Disband</LinkButton>
<LinkButton OnClickAsync="@Execute">Next</LinkButton>
@@ -78,7 +78,7 @@ else {
userRooms.Add(room);
}
- var roomChecks = userRooms.Select(GetFeasibleSpaces).ToAsyncEnumerable();
+ var roomChecks = userRooms.Select(GetFeasibleSpaces).ToAsyncResultEnumerable();
await foreach (var room in roomChecks)
if (room.HasValue)
spaces.TryAdd(room.Value.id, room.Value.roomInfo);
@@ -109,8 +109,8 @@ else {
public async Task<(string id, RoomInfo roomInfo)?> GetFeasibleSpaces(GenericRoom room) {
try {
var ri = new RoomInfo(room);
-
- await foreach(var evt in room.GetFullStateAsync())
+
+ await foreach (var evt in room.GetFullStateAsync())
ri.StateEvents.Add(evt);
var powerLevels = (await ri.GetStateEvent(RoomPowerLevelEventContent.EventId)).TypedContent as RoomPowerLevelEventContent;
@@ -118,7 +118,7 @@ else {
Console.WriteLine($"No permission to send m.space.child in {room.RoomId}...");
return null;
}
-
+
Status = $"Found viable space: {ri.RoomName}";
if (!string.IsNullOrWhiteSpace(SetupData.DmSpaceConfiguration!.DMSpaceId)) {
if (await room.GetStateOrNullAsync<DMSpaceInfo>(DMSpaceInfo.EventId) is { } dsi) {
diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor
index 25d1629..ed65e94 100644
--- a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor
+++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor
@@ -38,7 +38,9 @@ else {
}
@foreach (var (room, usersList) in duplicateDmRooms) {
<ModalWindow Title="Duplicate room found" X="_offset += 30" Y="_offset">
- <p>Found room assigned to multiple users: <RoomListItem RoomInfo="@room"></RoomListItem></p>
+ <p>Found room assigned to multiple users:
+ <RoomListItem RoomInfo="@room"></RoomListItem>
+ </p>
<p>Users:</p>
@foreach (var userProfileResponse in usersList) {
<LinkButton OnClickAsync="@(() => SetRoomAssignment(room.Room.RoomId, userProfileResponse.Id))">
@@ -141,12 +143,12 @@ else {
}
var roomList = new List<RoomInfo>();
- var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncEnumerable();
+ var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncResultEnumerable();
await foreach (var result in tasks)
roomList.Add(result);
return (userProfile, roomList);
// StateHasChanged();
- }).ToAsyncEnumerable();
+ }).ToAsyncResultEnumerable();
await foreach (var res in results) {
SetupData.DMRooms.Add(res.userProfile, res.roomList);
// Status = $"Listed {dmRooms.Count} users";
@@ -181,8 +183,8 @@ else {
await roomInfo.FetchAllStateAsync();
roomMembers[roomInfo] = new();
// roomInfo.CreationEventContent = await room.GetCreateEventAsync();
-
- if(roomInfo.RoomName == room.RoomId)
+
+ if (roomInfo.RoomName == room.RoomId)
try {
roomInfo.RoomName = await room.GetNameOrFallbackAsync();
}
@@ -192,7 +194,7 @@ else {
await foreach (var member in membersEnum)
if (member.TypedContent is RoomMemberEventContent memberEvent)
roomMembers[roomInfo].Add(new() { DisplayName = memberEvent.DisplayName, AvatarUrl = memberEvent.AvatarUrl, Id = member.StateKey });
-
+
try {
string? roomIcon = (await room.GetAvatarUrlAsync())?.Url;
if (room is not null)
diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor
index 2bed22e..686894c 100644
--- a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor
+++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor
@@ -115,11 +115,11 @@ else {
// };
// }
// var roomList = new List<RoomInfo>();
- // var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncEnumerable();
+ // var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncResultEnumerable();
// await foreach (var result in tasks)
// roomList.Add(result);
// return (userProfile, roomList);
- // }).ToAsyncEnumerable();
+ // }).ToAsyncResultEnumerable();
// await foreach (var res in results) {
// dmRooms.Add(new RoomInfo() {
// Room = dmSpaceRoom,
diff --git a/MatrixUtils.Web/Pages/Moderation/UserRoomHistory.razor b/MatrixUtils.Web/Pages/Moderation/UserRoomHistory.razor
index e30adf6..17dd554 100644
--- a/MatrixUtils.Web/Pages/Moderation/UserRoomHistory.razor
+++ b/MatrixUtils.Web/Pages/Moderation/UserRoomHistory.razor
@@ -19,6 +19,7 @@ else {
else if (checkedRooms.Count > 1) {
<p>Done!</p>
}
+
@foreach (var (state, rooms) in matchingStates) {
<u>@state</u>
<br/>
@@ -71,13 +72,14 @@ else {
_semaphoreSlim.Release();
return; //abort if changed
}
+
matchingStates.Clear();
foreach (var homeserver in hss) {
currentHs = homeserver;
var rooms = await homeserver.GetJoinedRooms();
rooms.RemoveAll(x => checkedRooms.Contains(x.RoomId));
checkedRooms.AddRange(rooms.Select(x => x.RoomId));
- var tasks = rooms.Select(x => GetMembershipAsync(x, mxid)).ToAsyncEnumerable();
+ var tasks = rooms.Select(x => GetMembershipAsync(x, mxid)).ToAsyncResultEnumerable();
await foreach (var (room, state) in tasks) {
if (state is null) continue;
if (!matchingStates.ContainsKey(state.Membership))
@@ -97,8 +99,10 @@ else {
return; //abort if changed
}
}
+
StateHasChanged();
}
+
currentHs = null;
StateHasChanged();
_semaphoreSlim.Release();
diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
index 5876861..92c6ca5 100644
--- a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
+++ b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
@@ -381,7 +381,7 @@ else {
.SelectMany(x => x.ActivePolicies.Values)
.ToArray();
- await foreach (var modifiedPolicyInfos in tasks.ToAsyncEnumerable()) {
+ await foreach (var modifiedPolicyInfos in tasks.ToAsyncResultEnumerable()) {
if (modifiedPolicyInfos.Count == 0) continue;
var applySw = Stopwatch.StartNew();
// Console.WriteLine($"Main: got {modifiedPolicyInfos.Count} modified policies from worker, time: {scanSw.Elapsed}");
diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor
index 6df56ba..52c5f30 100644
--- a/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor
+++ b/MatrixUtils.Web/Pages/Rooms/PolicyLists.razor
@@ -12,9 +12,9 @@
<h3>
<span>Policy lists </span>
<LinkButton OnClickAsync="@(() => {
- ShowPolicyListCreationWindow = true;
- return Task.CompletedTask;
- })">
+ ShowPolicyListCreationWindow = true;
+ return Task.CompletedTask;
+ })">
<span class="oi oi-plus" aria-hidden="true"> Create</span>
</LinkButton>
</h3>
@@ -137,7 +137,7 @@
if (policies.Count == 0) return null;
Status2 = $"Found legacy list {room.RoomId}...";
return await RoomInfo.FromRoom(room, state, true);
- }).ToAsyncEnumerable();
+ }).ToAsyncResultEnumerable();
await foreach (var room in rooms) {
if (room is not null) {
@@ -145,7 +145,7 @@
StateHasChanged();
}
}
-
+
isLoading = false;
Status = "";
Status2 = "";
diff --git a/MatrixUtils.Web/Pages/StreamTest.razor b/MatrixUtils.Web/Pages/StreamTest.razor
index 8b9735e..7740596 100644
--- a/MatrixUtils.Web/Pages/StreamTest.razor
+++ b/MatrixUtils.Web/Pages/StreamTest.razor
@@ -89,7 +89,7 @@
// var res2 = Homeserver.ClientHttpClient.GetAsync(url);
// var tasks = Enumerable.Range(1, 128)
// .Select(x => Homeserver.ClientHttpClient.GetStreamAsync(url+$"?width={x*128}&height={x*128}"))
- // .ToAsyncEnumerable();
+ // .ToAsyncResultEnumerable();
await foreach (var result in GetStreamsDelayed(url)) {
Streams.Add(result);
// await Task.Delay(100);
@@ -107,7 +107,7 @@
for (int i = 0; i < 32; i++) {
var tasks = Enumerable.Range(1, 4)
.Select(x => Homeserver.ClientHttpClient.GetStreamAsync(url + $"?width={x * 128}&height={x * 128}&r={Random.Shared.Next(100000)}"))
- .ToAsyncEnumerable();
+ .ToAsyncResultEnumerable();
await foreach (var result in tasks) {
yield return result;
}
diff --git a/MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor b/MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor
index b0f7dbf..067036e 100644
--- a/MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor
+++ b/MatrixUtils.Web/Pages/Tools/Debug/MigrateRoom.razor
@@ -53,8 +53,8 @@
var oldRoom = hs.GetRoom(roomId);
var newRoom = hs.GetRoom(newRoomId);
var members = await oldRoom.GetMembersListAsync();
- var tasks = members.Select(x => ExecuteInvite(hs, newRoom, x.StateKey)).ToAsyncEnumerable();
- // var tasks = hss.Select(ExecuteInvite).ToAsyncEnumerable();
+ var tasks = members.Select(x => ExecuteInvite(hs, newRoom, x.StateKey)).ToAsyncResultEnumerable();
+ // var tasks = hss.Select(ExecuteInvite).ToAsyncResultEnumerable();
await foreach (var a in tasks) {
if (!string.IsNullOrWhiteSpace(a)) {
log.Add(a);
diff --git a/MatrixUtils.Web/Pages/Tools/Info/KnownHomeserverList.razor b/MatrixUtils.Web/Pages/Tools/Info/KnownHomeserverList.razor
index acad827..8ba160a 100644
--- a/MatrixUtils.Web/Pages/Tools/Info/KnownHomeserverList.razor
+++ b/MatrixUtils.Web/Pages/Tools/Info/KnownHomeserverList.razor
@@ -41,13 +41,13 @@
var ss = new SemaphoreSlim(32, 32);
var rooms = await hs.GetJoinedRooms();
RoomCount = rooms.Count;
- var fetchTasks = rooms.Select(roomId => workerService.TaskPool.Invoke(() => InternalGetMembersByHomeserver(hs.WellKnownUris.Client, hs.AccessToken, roomId.RoomId))).ToList().ToAsyncEnumerable();
+ var fetchTasks = rooms.Select(roomId => workerService.TaskPool.Invoke(() => InternalGetMembersByHomeserver(hs.WellKnownUris.Client, hs.AccessToken, roomId.RoomId))).ToList().ToAsyncResultEnumerable();
// var fetchTasks = rooms.Select(async x => {
- // await ss.WaitAsync();
- // var res = await x.GetMembersByHomeserverAsync();
- // ss.Release();
- // return res;
- // }).ToAsyncEnumerable();
+ // await ss.WaitAsync();
+ // var res = await x.GetMembersByHomeserverAsync();
+ // ss.Release();
+ // return res;
+ // }).ToAsyncResultEnumerable();
await foreach (var result in fetchTasks) {
foreach (var (resHomeserver, resMembers) in result) {
if (!homeservers.TryAdd(resHomeserver, resMembers)) {
diff --git a/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor b/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor
index f8d1d31..bfd5fd3 100644
--- a/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor
+++ b/MatrixUtils.Web/Pages/Tools/Info/PolicyListActivity.razor
@@ -83,7 +83,7 @@ else
foreach (var message in response.Chunk) {
if (!message.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue;
//OriginServerTs to datetime
- var dt = DateTimeOffset.FromUnixTimeMilliseconds((long)message.OriginServerTs!.Value).DateTime;
+ var dt = DateTimeOffset.FromUnixTimeMilliseconds(message.OriginServerTs!.Value).DateTime;
var date = new DateOnly(dt.Year, dt.Month, dt.Day);
if (!RoomData[roomName].ContainsKey(date.Year)) {
RoomData[roomName][date.Year] = new();
@@ -100,9 +100,8 @@ else
else rgb.B++;
RoomData[roomName][date.Year][date] = rgb;
}
-
-
}
+
var max = RoomData.SelectMany(x => x.Value.Values).Aggregate(new ActivityGraph.RGB(), (current, next) => new() {
R = Math.Max(current.R, next.Average(x => x.Value.R)),
G = Math.Max(current.G, next.Average(x => x.Value.G)),
diff --git a/MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor b/MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor
index fcdb3d0..dc5333b 100644
--- a/MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor
+++ b/MatrixUtils.Web/Pages/Tools/Info/SessionCount.razor
@@ -11,7 +11,8 @@
<p>Users: </p>
<InputTextArea @bind-Value="@UserIdString"></InputTextArea>
<br/>
-<InputText @bind-Value="@ImportFromRoomId"></InputText><LinkButton OnClickAsync="@DoImportFromRoomId">Import from room (ID)</LinkButton>
+<InputText @bind-Value="@ImportFromRoomId"></InputText>
+<LinkButton OnClickAsync="@DoImportFromRoomId">Import from room (ID)</LinkButton>
<details>
<summary>Rooms to be searched (@rooms.Count)</summary>
@@ -44,9 +45,7 @@
@foreach (var (userId, events) in matches) {
<p>
<span>@userId.PadRight(col1Width)</span>
- @foreach (var @event in events) {
-
-}
+ @foreach (var @event in events) { }
</p>
}
</pre>
@@ -97,7 +96,7 @@
rooms = new ObservableCollection<GenericRoom>(distinctRooms);
rooms.CollectionChanged += (sender, args) => StateHasChanged();
- var stateTasks = rooms.Select(async x => (x, await x.GetMembersListAsync())).ToAsyncEnumerable();
+ var stateTasks = rooms.Select(async x => (x, await x.GetMembersListAsync())).ToAsyncResultEnumerable();
await foreach (var (room, state) in stateTasks) {
roomMembers.Add(room, state);
diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor b/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor
index 1fd0ff6..5ad9de4 100644
--- a/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor
+++ b/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor
@@ -161,7 +161,7 @@
}
return null;
- }).ToAsyncEnumerable();
+ }).ToAsyncResultEnumerable();
await foreach (var result in results) {
if (result is not null) {
yield return result;
diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor b/MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor
index f39a2eb..2261cb8 100644
--- a/MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor
+++ b/MatrixUtils.Web/Pages/Tools/Moderation/UserTrace.razor
@@ -26,10 +26,10 @@
<details>
<summary>Results</summary>
- @foreach (var (userId, events) in matches.OrderBy(x=>x.Key)) {
+ @foreach (var (userId, events) in matches.OrderBy(x => x.Key)) {
<h4>@userId</h4>
<table>
- @foreach (var match in events.OrderBy(x=>x.RoomName)) {
+ @foreach (var match in events.OrderBy(x => x.RoomName)) {
<tr>
<td>@match.RoomName (<span>@match.Room.RoomId</span>)</td>
<td>
@@ -161,7 +161,7 @@
}
return null;
- }).ToAsyncEnumerable();
+ }).ToAsyncResultEnumerable();
await foreach (var result in results) {
if (result is not null) {
yield return result;
@@ -186,10 +186,9 @@
_ => $"Unknown membership {membership.Membership}, sent at {time} by {state.Sender} for {membership.Reason}"
};
}
-
+
private async Task ExportJson() {
var json = matches.ToJson();
-
}
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor b/MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor
index b893970..acc86a2 100644
--- a/MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor
+++ b/MatrixUtils.Web/Pages/Tools/User/CopyPowerlevel.razor
@@ -42,7 +42,7 @@
private async Task Execute() {
foreach (var hs in hss) {
var rooms = await hs.GetJoinedRooms();
- var tasks = rooms.Select(x=>ApplyPowerlevelsInRoom(hs, x)).ToAsyncEnumerable();
+ var tasks = rooms.Select(x => ApplyPowerlevelsInRoom(hs, x)).ToAsyncResultEnumerable();
await foreach (var a in tasks) {
if (!string.IsNullOrWhiteSpace(a)) {
log.Add(a);
@@ -62,12 +62,11 @@
log.Add("I am same PL in " + room.RoomId);
continue;
}
-
+
pls.SetUserPowerLevel(ahs.WhoAmI.UserId, pls.GetUserPowerLevel(hs.WhoAmI.UserId));
await room.SendStateEventAsync(RoomPowerLevelEventContent.EventId, pls);
log.Add($"Updated powerlevel of {room.RoomId} to {pls.GetUserPowerLevel(ahs.WhoAmI.UserId)}");
}
-
}
catch (MatrixException e) {
return $"Failed to update PLs in {room.RoomId}: {e.Message}";
@@ -75,6 +74,7 @@
catch (Exception e) {
return $"Failed to update PLs in {room.RoomId}: {e.Message}";
}
+
StateHasChanged();
return "";
}
diff --git a/MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor b/MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor
index 748f2fb..ee17f1d 100644
--- a/MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor
+++ b/MatrixUtils.Web/Pages/Tools/User/MassJoinRoom.razor
@@ -42,23 +42,24 @@
}
private async Task Execute() {
- // foreach (var hs in hss) {
- // var rooms = await hs.GetJoinedRooms();
- var tasks = hss.Select(ExecuteInvite).ToAsyncEnumerable();
+ // foreach (var hs in hss) {
+ // var rooms = await hs.GetJoinedRooms();
+ var tasks = hss.Select(ExecuteInvite).ToAsyncResultEnumerable();
await foreach (var a in tasks) {
if (!string.IsNullOrWhiteSpace(a)) {
log.Add(a);
StateHasChanged();
}
}
- tasks = hss.Select(ExecuteJoin).ToAsyncEnumerable();
+
+ tasks = hss.Select(ExecuteJoin).ToAsyncResultEnumerable();
await foreach (var a in tasks) {
if (!string.IsNullOrWhiteSpace(a)) {
log.Add(a);
StateHasChanged();
}
}
- // }
+ // }
}
private async Task<string> ExecuteInvite(AuthenticatedHomeserverGeneric hs) {
@@ -69,6 +70,7 @@
if (joinRule.JoinRule == RoomJoinRulesEventContent.JoinRules.Public) return "Room is public, no invite needed";
}
catch { }
+
var pls = await room.GetPowerLevelsAsync();
if (pls.GetUserPowerLevel(hs.WhoAmI.UserId) < pls.Invite) return "I do not have permission to send invite in " + room.RoomId;
await room.InviteUsersAsync(hss.Select(x => x.WhoAmI.UserId).ToList());
@@ -80,6 +82,7 @@
catch (Exception e) {
return $"Failed to invite in {room.RoomId}: {e.Message}";
}
+
StateHasChanged();
return "";
}
@@ -92,6 +95,7 @@
if (mse?.Membership == "join") return $"User {hs.WhoAmI.UserId} already in room";
}
catch { }
+
await room.JoinAsync();
}
catch (MatrixException e) {
@@ -100,6 +104,7 @@
catch (Exception e) {
return $"Failed to join {hs.WhoAmI.UserId} to {room.RoomId}: {e.Message}";
}
+
StateHasChanged();
return "";
}
diff --git a/MatrixUtils.Web/Pages/Tools/User/StickerManager.razor b/MatrixUtils.Web/Pages/Tools/User/StickerManager.razor
index 984130f..0e838c7 100644
--- a/MatrixUtils.Web/Pages/Tools/User/StickerManager.razor
+++ b/MatrixUtils.Web/Pages/Tools/User/StickerManager.razor
@@ -70,7 +70,7 @@
return room.RoomId;
})
.ToList();
- await foreach (var roomScanResult in roomScanTasks.ToAsyncEnumerable()) {
+ await foreach (var roomScanResult in roomScanTasks.ToAsyncResultEnumerable()) {
_observableProgressState.Label.Value = roomScanResult;
}
}
diff --git a/MatrixUtils.Web/Pages/User/Profile.razor b/MatrixUtils.Web/Pages/User/Profile.razor
index 290b92a..2b7b6cf 100644
--- a/MatrixUtils.Web/Pages/User/Profile.razor
+++ b/MatrixUtils.Web/Pages/User/Profile.razor
@@ -12,9 +12,13 @@
<div>
<MxcAvatar Homeserver="@Homeserver" MxcUri="@NewProfile.AvatarUrl" Circular="true" Size="96"/>
<div style="display: inline-block; vertical-align: middle;">
- <span>Display name: </span><FancyTextBox @bind-Value="@NewProfile.DisplayName"></FancyTextBox><br/>
- <span>Avatar URL: </span><FancyTextBox @bind-Value="@NewProfile.AvatarUrl"></FancyTextBox>
- <InputFile OnChange="@AvatarChanged"></InputFile><br/>
+ <span>Display name: </span>
+ <FancyTextBox @bind-Value="@NewProfile.DisplayName"></FancyTextBox>
+ <br/>
+ <span>Avatar URL: </span>
+ <FancyTextBox @bind-Value="@NewProfile.AvatarUrl"></FancyTextBox>
+ <InputFile OnChange="@AvatarChanged"></InputFile>
+ <br/>
<LinkButton OnClickAsync="@(() => UpdateProfile())">Update profile</LinkButton>
<LinkButton OnClickAsync="@(() => UpdateProfile(true))">Update profile (restore room overrides)</LinkButton>
</div>
@@ -26,7 +30,9 @@
<br/>
@* <details> *@
- <h4>Room profiles<hr></h4>
+ <h4>Room profiles
+ <hr>
+ </h4>
@foreach (var room in Rooms) {
<details class="details-compact">
@@ -41,9 +47,15 @@
@* <img src="@Homeserver.ResolveMediaUri(room.OwnMembership.AvatarUrl)" style="width: 96px; height: 96px; border-radius: 50%; object-fit: cover;"/> *@
<MxcAvatar Homeserver="@Homeserver" MxcUri="@room.OwnMembership.AvatarUrl" Circular="true" Size="96"/>
<div style="display: inline-block; vertical-align: middle;">
- <span>Display name: </span><FancyTextBox BackgroundColor="@(room.OwnMembership.DisplayName == OldProfile.DisplayName ? "" : "#ffff0033")" @bind-Value="@room.OwnMembership.DisplayName"></FancyTextBox><br/>
- <span>Avatar URL: </span><FancyTextBox BackgroundColor="@(room.OwnMembership.AvatarUrl == OldProfile.AvatarUrl ? "" : "#ffff0033")" @bind-Value="@room.OwnMembership.AvatarUrl"></FancyTextBox>
- <InputFile OnChange="@(ifcea => RoomAvatarChanged(ifcea, room.Room.RoomId))"></InputFile><br/>
+ <span>Display name: </span>
+ <FancyTextBox BackgroundColor="@(room.OwnMembership.DisplayName == OldProfile.DisplayName ? "" : "#ffff0033")"
+ @bind-Value="@room.OwnMembership.DisplayName"></FancyTextBox>
+ <br/>
+ <span>Avatar URL: </span>
+ <FancyTextBox BackgroundColor="@(room.OwnMembership.AvatarUrl == OldProfile.AvatarUrl ? "" : "#ffff0033")"
+ @bind-Value="@room.OwnMembership.AvatarUrl"></FancyTextBox>
+ <InputFile OnChange="@(ifcea => RoomAvatarChanged(ifcea, room.Room.RoomId))"></InputFile>
+ <br/>
<LinkButton OnClickAsync="@(() => UpdateRoomProfile(room.Room.RoomId))">Update profile</LinkButton>
</div>
<br/>
@@ -117,7 +129,7 @@
});
roomInfoTasks.Add(task);
}
-
+
await Task.WhenAll(roomInfoTasks);
StateHasChanged();
@@ -126,7 +138,7 @@
// var roomNameTasks = RoomProfiles.Keys.Select(x => Homeserver.GetRoom(x)).Select(async x => {
// var name = await x.GetNameOrFallbackAsync();
// return new KeyValuePair<string, string?>(x.RoomId, name);
- // }).ToAsyncEnumerable();
+ // }).ToAsyncResultEnumerable();
// await foreach (var (roomId, roomName) in roomNameTasks) {
// Status = $"Got room name for {roomId}: {roomName}";
diff --git a/MatrixUtils.Web/Shared/FilterComponents/BooleanFilterComponent.razor b/MatrixUtils.Web/Shared/FilterComponents/BooleanFilterComponent.razor
new file mode 100644
index 0000000..0730701
--- /dev/null
+++ b/MatrixUtils.Web/Shared/FilterComponents/BooleanFilterComponent.razor
@@ -0,0 +1,17 @@
+@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters
+<span>
+ <InputCheckbox @bind-Value="@Filter.Enabled"/> @Label:
+ @if (Filter.Enabled) {
+ <InputCheckbox @bind-Value="@Filter.Value"/>
+ }
+</span>
+
+@code {
+
+ [Parameter]
+ public required BoolFilter Filter { get; set; }
+
+ [Parameter]
+ public required string Label { get; set; }
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Shared/FilterComponents/StringFilterComponent.razor b/MatrixUtils.Web/Shared/FilterComponents/StringFilterComponent.razor
new file mode 100644
index 0000000..c5a6e15
--- /dev/null
+++ b/MatrixUtils.Web/Shared/FilterComponents/StringFilterComponent.razor
@@ -0,0 +1,31 @@
+@using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Filters
+<span style="vertical-align: top;">
+ <InputCheckbox @bind-Value="@Filter.Enabled"/> @Label:
+</span>
+@if (Filter.Enabled) {
+ <div style="display: inline-block;">
+ <InputCheckbox @bind-Value="@Filter.CheckValueContains"/>
+ Contains
+ <FancyTextBox @bind-Value="@Filter.ValueContains"></FancyTextBox>
+ <br/>
+ <InputCheckbox @bind-Value="@Filter.CheckValueEquals"/>
+ Equals
+ <FancyTextBox @bind-Value="@Filter.ValueEquals"></FancyTextBox>
+ <LinkButton OnClick="@SetEqualsNull" InlineText="true"> [Set null]</LinkButton>
+ </div>
+}
+
+@code {
+
+ [Parameter]
+ public required StringFilter Filter { get; set; }
+
+ [Parameter]
+ public required string Label { get; set; }
+
+ private void SetEqualsNull() {
+ Filter.ValueEquals = null;
+ StateHasChanged();
+ }
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/wwwroot/index.html b/MatrixUtils.Web/wwwroot/index.html
index f25d549..0a80cff 100644
--- a/MatrixUtils.Web/wwwroot/index.html
+++ b/MatrixUtils.Web/wwwroot/index.html
@@ -12,6 +12,7 @@
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png"/>
<link href="favicon.png" rel="icon" type="image/png"/>
<link href="MatrixUtils.Web.styles.css" rel="stylesheet"/>
+ <link rel="preload" id="webassembly"/>
</head>
<body>
@@ -49,11 +50,11 @@
}
setImageStream = async (element, imageStream) => {
- if(!(element instanceof HTMLElement)) {
+ if (!(element instanceof HTMLElement)) {
console.error("Element is not an HTMLElement", element);
return;
}
-
+
const arrayBuffer = await imageStream.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
@@ -65,7 +66,7 @@
}
</script>
<script src="_framework/blazor.webassembly.js"></script>
-<!-- <script>navigator.serviceWorker.register('service-worker.js');</script>-->
+ <!-- <script>navigator.serviceWorker.register('service-worker.js');</script>-->
<script src="sw-registrator.js"></script>
</body>
diff --git a/MatrixUtils.Web/wwwroot/sw-registrator.js b/MatrixUtils.Web/wwwroot/sw-registrator.js
index 94b96b2..67aa5cb 100644
--- a/MatrixUtils.Web/wwwroot/sw-registrator.js
+++ b/MatrixUtils.Web/wwwroot/sw-registrator.js
@@ -8,7 +8,7 @@ window.updateAvailable = new Promise((resolve, reject) => {
return;
}
- navigator.serviceWorker.register('/service-worker.js')
+ navigator.serviceWorker.register('/service-worker.js', {updateViaCache: 'none'})
.then(registration => {
console.info(`Service worker registration successful (scope: ${registration.scope})`);
diff --git a/MatrixUtils.sln b/MatrixUtils.sln
index 093e8e9..032c49b 100644
--- a/MatrixUtils.sln
+++ b/MatrixUtils.sln
@@ -66,10 +66,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MxApiExtensions", "MxApiExt
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{CEECE820-1BA9-4E29-8668-25967B3E712B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixUtils.Web.Ssr", "MatrixUtils.Web.Ssr\MatrixUtils.Web.Ssr\MatrixUtils.Web.Ssr.csproj", "{35F510FD-98FC-4760-A53B-9176A53A33A2}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixUtils.Web.Ssr.Client", "MatrixUtils.Web.Ssr\MatrixUtils.Web.Ssr.Client\MatrixUtils.Web.Ssr.Client.csproj", "{335DB9D5-FEEE-45E3-B76A-057D8BB48412}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.Federation", "LibMatrix\LibMatrix.Federation\LibMatrix.Federation.csproj", "{8F154875-96EE-4BE5-8456-F5EBB2516C1C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.FederationTest", "LibMatrix\Utilities\LibMatrix.FederationTest\LibMatrix.FederationTest.csproj", "{960CC2DF-BB1A-4164-A895-834F81B3A113}"
@@ -422,30 +418,6 @@ Global
{CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|x64.Build.0 = Release|Any CPU
{CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|x86.ActiveCfg = Release|Any CPU
{CEECE820-1BA9-4E29-8668-25967B3E712B}.Release|x86.Build.0 = Release|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|x64.ActiveCfg = Debug|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|x64.Build.0 = Debug|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|x86.ActiveCfg = Debug|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Debug|x86.Build.0 = Debug|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|Any CPU.Build.0 = Release|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|x64.ActiveCfg = Release|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|x64.Build.0 = Release|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|x86.ActiveCfg = Release|Any CPU
- {35F510FD-98FC-4760-A53B-9176A53A33A2}.Release|x86.Build.0 = Release|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|x64.ActiveCfg = Debug|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|x64.Build.0 = Debug|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|x86.ActiveCfg = Debug|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Debug|x86.Build.0 = Debug|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|Any CPU.Build.0 = Release|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|x64.ActiveCfg = Release|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|x64.Build.0 = Release|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|x86.ActiveCfg = Release|Any CPU
- {335DB9D5-FEEE-45E3-B76A-057D8BB48412}.Release|x86.Build.0 = Release|Any CPU
{8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F154875-96EE-4BE5-8456-F5EBB2516C1C}.Debug|x64.ActiveCfg = Debug|Any CPU
|