diff --git a/LibMatrix b/LibMatrix
-Subproject fcad35734ffe635a85e27349ff09bc035f26806
+Subproject 1feb7fb87444807c3fb5d266fa3cb76069c061a
diff --git a/MatrixUtils.RoomUpgradeCLI/Commands/NewFileCommand.cs b/MatrixUtils.RoomUpgradeCLI/Commands/NewFileCommand.cs
new file mode 100644
index 0000000..da2ad2f
--- /dev/null
+++ b/MatrixUtils.RoomUpgradeCLI/Commands/NewFileCommand.cs
@@ -0,0 +1,230 @@
+using ArcaneLibs.Extensions;
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Helpers;
+using LibMatrix.Homeservers;
+using LibMatrix.RoomTypes;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace MatrixUtils.RoomUpgradeCLI.Commands;
+
+public class NewFileCommand(ILogger<NewFileCommand> logger, IHost host, RuntimeContext ctx, AuthenticatedHomeserverGeneric hs) : IHostedService {
+ public async Task StartAsync(CancellationToken cancellationToken) {
+ var rb = ctx.Args.Contains("--upgrade") ? new RoomUpgradeBuilder() : new RoomBuilder();
+ if (ctx.Args.Length <= 1) {
+ await PrintHelp();
+ return;
+ }
+ var filename = ctx.Args[1];
+ if (filename.StartsWith("--")) {
+ Console.WriteLine("Filename cannot start with --, please provide a valid filename.");
+ await PrintHelp();
+ }
+ for (int i = 2; i < ctx.Args.Length; i++) {
+ switch (ctx.Args[i]) {
+ case "--alias":
+ rb.AliasLocalPart = ctx.Args[++i];
+ break;
+ case "--avatar-url":
+ rb.Avatar!.Url = ctx.Args[++i];
+ break;
+ case "--copy-avatar": {
+ var room = hs.GetRoom(ctx.Args[++i]);
+ rb.Avatar = await room.GetAvatarUrlAsync() ?? throw new ArgumentException($"Room {room.RoomId} does not have an avatar");
+ break;
+ }
+ case "--copy-powerlevels": {
+ var room = hs.GetRoom(ctx.Args[++i]);
+ rb.PowerLevels = await room.GetPowerLevelsAsync() ?? throw new ArgumentException($"Room {room.RoomId} does not have power levels???");
+ break;
+ }
+ case "--invite-admin":
+ var inviteAdmin = ctx.Args[++i];
+ if (!inviteAdmin.StartsWith('@')) {
+ throw new ArgumentException("Invalid user reference: " + inviteAdmin);
+ }
+
+ rb.Invites.Add(inviteAdmin, "Marked explicitly as admin to be invited");
+ break;
+ case "--invite":
+ var inviteUser = ctx.Args[++i];
+ if (!inviteUser.StartsWith('@')) {
+ throw new ArgumentException("Invalid user reference: " + inviteUser);
+ }
+
+ rb.Invites.Add(inviteUser, "Marked explicitly to be invited");
+ break;
+ case "--name":
+ var nameEvt = rb.Name = new() { Name = "" };
+ while (i + 1 < ctx.Args.Length && !ctx.Args[i + 1].StartsWith("--")) {
+ nameEvt.Name += (nameEvt.Name.Length > 0 ? " " : "") + ctx.Args[++i];
+ }
+
+ break;
+ case "--topic":
+ var topicEvt = rb.Topic = new() { Topic = "" };
+ while (i + 1 < ctx.Args.Length && !ctx.Args[i + 1].StartsWith("--")) {
+ topicEvt.Topic += (topicEvt.Topic.Length > 0 ? " " : "") + ctx.Args[++i];
+ }
+
+ break;
+ case "--federate":
+ rb.IsFederatable = bool.Parse(ctx.Args[++i]);
+ break;
+ case "--public":
+ case "--invite-only":
+ case "--knock":
+ case "--restricted":
+ case "--knock_restricted":
+ case "--private":
+ rb.JoinRules.JoinRule = ctx.Args[i].Replace("--", "").ToLowerInvariant() switch {
+ "public" => RoomJoinRulesEventContent.JoinRules.Public,
+ "invite-only" => RoomJoinRulesEventContent.JoinRules.Invite,
+ "knock" => RoomJoinRulesEventContent.JoinRules.Knock,
+ "restricted" => RoomJoinRulesEventContent.JoinRules.Restricted,
+ "knock_restricted" => RoomJoinRulesEventContent.JoinRules.KnockRestricted,
+ "private" => RoomJoinRulesEventContent.JoinRules.Private,
+ _ => throw new ArgumentException("Unknown join rule: " + ctx.Args[i])
+ };
+ break;
+ case "--join-rule":
+ if (i + 1 >= ctx.Args.Length || !ctx.Args[i + 1].StartsWith("--")) {
+ throw new ArgumentException("Expected join rule after --join-rule");
+ }
+
+ rb.JoinRules.JoinRule = ctx.Args[++i].ToLowerInvariant() switch {
+ "public" => RoomJoinRulesEventContent.JoinRules.Public,
+ "invite" => RoomJoinRulesEventContent.JoinRules.Invite,
+ "knock" => RoomJoinRulesEventContent.JoinRules.Knock,
+ "restricted" => RoomJoinRulesEventContent.JoinRules.Restricted,
+ "knock_restricted" => RoomJoinRulesEventContent.JoinRules.KnockRestricted,
+ "private" => RoomJoinRulesEventContent.JoinRules.Private,
+ _ => throw new ArgumentException("Unknown join rule: " + ctx.Args[i])
+ };
+ break;
+ case "--history-visibility":
+ rb.HistoryVisibility = new RoomHistoryVisibilityEventContent {
+ HistoryVisibility = ctx.Args[++i].ToLowerInvariant() switch {
+ "shared" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Shared,
+ "invited" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Invited,
+ "joined" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Joined,
+ "world_readable" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.WorldReadable,
+ _ => throw new ArgumentException("Unknown history visibility: " + ctx.Args[i])
+ }
+ };
+ break;
+ case "--type":
+ rb.Type = ctx.Args[++i];
+ break;
+ case "--version":
+ rb.Version = ctx.Args[++i];
+ // if (!RoomBuilder.V12PlusRoomVersions.Contains(rb.Version)) {
+ // logger.LogWarning("Using room version {Version} which is not v12 or higher, this may cause issues with some features.", rb.Version);
+ // }
+ break;
+ // upgrade options
+ case "--invite-members":
+ if (rb is not RoomUpgradeBuilder upgradeBuilder) {
+ throw new InvalidOperationException("Invite members can only be used with room upgrades");
+ }
+
+ upgradeBuilder.UpgradeOptions.InviteMembers = true;
+ break;
+ case "--invite-powerlevel-users":
+ if (rb is not RoomUpgradeBuilder upgradeBuilderInvite) {
+ throw new InvalidOperationException("Invite powerlevel users can only be used with room upgrades");
+ }
+
+ upgradeBuilderInvite.UpgradeOptions.InvitePowerlevelUsers = true;
+ break;
+ case "--migrate-bans":
+ if (rb is not RoomUpgradeBuilder upgradeBuilderBan) {
+ throw new InvalidOperationException("Migrate bans can only be used with room upgrades");
+ }
+
+ upgradeBuilderBan.UpgradeOptions.MigrateBans = true;
+ break;
+ case "--migrate-empty-state-events":
+ if (rb is not RoomUpgradeBuilder upgradeBuilderEmpty) {
+ throw new InvalidOperationException("Migrate empty state events can only be used with room upgrades");
+ }
+
+ upgradeBuilderEmpty.UpgradeOptions.MigrateEmptyStateEvents = true;
+ break;
+ case "--upgrade-unstable-values":
+ if (rb is not RoomUpgradeBuilder upgradeBuilderUnstable) {
+ throw new InvalidOperationException("Update unstable values can only be used with room upgrades");
+ }
+
+ upgradeBuilderUnstable.UpgradeOptions.UpgradeUnstableValues = true;
+ break;
+ case "--msc4321-policy-list-upgrade":
+ if (rb is not RoomUpgradeBuilder upgradeBuilderPolicy) {
+ throw new InvalidOperationException("MSC4321 policy list upgrade can only be used with room upgrades");
+ }
+
+ upgradeBuilderPolicy.UpgradeOptions.Msc4321PolicyListUpgradeOptions.Enable = true;
+ upgradeBuilderPolicy.UpgradeOptions.Msc4321PolicyListUpgradeOptions.UpgradeType = ctx.Args[++i].ToLowerInvariant() switch {
+ "move" => RoomUpgradeBuilder.Msc4321PolicyListUpgradeOptions.Msc4321PolicyListUpgradeType.Move,
+ "transition" => RoomUpgradeBuilder.Msc4321PolicyListUpgradeOptions.Msc4321PolicyListUpgradeType.Transition,
+ _ => throw new ArgumentException("Unknown MSC4321 policy list upgrade type: " + ctx.Args[i])
+ };
+ break;
+
+ case "--upgrade":
+ if (rb is not RoomUpgradeBuilder upgradeBuilderUpgrade) {
+ throw new InvalidOperationException("Upgrade can only be used with room upgrades");
+ }
+ upgradeBuilderUpgrade.OldRoomId = ctx.Args[++i];
+ break;
+ case "--help":
+ await PrintHelp();
+ return;
+ default:
+ throw new ArgumentException("Unknown argument: " + ctx.Args[i]);
+ }
+ }
+
+ await File.WriteAllTextAsync(filename, rb.ToJson(), cancellationToken);
+
+ await host.StopAsync(cancellationToken);
+ }
+
+ public async Task StopAsync(CancellationToken cancellationToken) { }
+
+ private async Task PrintHelp() {
+ Console.WriteLine("Usage: new [filename] [options]");
+ Console.WriteLine("Options:");
+ Console.WriteLine(" --help Show this help message");
+ Console.WriteLine(" --version <version> Set the room version (e.g. 9, 10, 11, 12)");
+ Console.WriteLine("-- New room options --");
+ Console.WriteLine(" --alias <alias> Set the room alias (local part)");
+ Console.WriteLine(" --avatar-url <url> Set the room avatar URL");
+ Console.WriteLine(" --copy-avatar <roomId> Copy the avatar from an existing room");
+ Console.WriteLine(" --copy-powerlevels <roomId> Copy power levels from an existing room");
+ Console.WriteLine(" --invite-admin <userId> Invite a user as an admin (userId must start with '@')");
+ Console.WriteLine(" --invite <userId> Invite a user (userId must start with '@')");
+ Console.WriteLine(" --name <name> Set the room name (can be multiple words)");
+ Console.WriteLine(" --topic <topic> Set the room topic (can be multiple words)");
+ Console.WriteLine(" --federate <true|false> Set whether the room is federatable");
+ Console.WriteLine(" --public Set the room join rule to public");
+ Console.WriteLine(" --invite-only Set the room join rule to invite-only");
+ Console.WriteLine(" --knock Set the room join rule to knock");
+ Console.WriteLine(" --restricted Set the room join rule to restricted");
+ Console.WriteLine(" --knock_restricted Set the room join rule to knock_restricted");
+ Console.WriteLine(" --private Set the room join rule to private");
+ Console.WriteLine(" --join-rule <rule> Set the room join rule (public, invite, knock, restricted, knock_restricted, private)");
+ Console.WriteLine(" --history-visibility <visibility> Set the room history visibility (shared, invited, joined, world_readable)");
+ Console.WriteLine(" --type <type> Set the room type (e.g. m.space, m.room, support.feline.policy.list.msc.v1 etc.)");
+ // upgrade opts
+ Console.WriteLine("-- Upgrade options --");
+ Console.WriteLine(" --upgrade <roomId> Create a room upgrade file instead of a new room file - WARNING: incompatible with non-upgrade options");
+ Console.WriteLine(" --invite-members Invite members during room upgrade");
+ Console.WriteLine(" --invite-powerlevel-users Invite users with power levels during room upgrade");
+ Console.WriteLine(" --migrate-bans Migrate bans during room upgrade");
+ Console.WriteLine(" --migrate-empty-state-events Migrate empty state events during room upgrade");
+ Console.WriteLine(" --upgrade-unstable-values Upgrade unstable values during room upgrade");
+ Console.WriteLine(" --msc4321-policy-list-upgrade <move|transition> Upgrade MSC4321 policy list");
+ Console.WriteLine("WARNING: The --upgrade option is incompatible with options listed under \"New room\", please use the equivalent options in the `apply-upgrade` command instead.");
+ await host.StopAsync();
+ }
+}
\ No newline at end of file
diff --git a/MatrixUtils.RoomUpgradeCLI/MatrixUtils.RoomUpgradeCLI.csproj b/MatrixUtils.RoomUpgradeCLI/MatrixUtils.RoomUpgradeCLI.csproj
new file mode 100644
index 0000000..bf76cfe
--- /dev/null
+++ b/MatrixUtils.RoomUpgradeCLI/MatrixUtils.RoomUpgradeCLI.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk.Worker">
+
+ <PropertyGroup>
+ <TargetFramework>net9.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.8"/>
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj" />
+ <ProjectReference Include="..\LibMatrix\Utilities\LibMatrix.Utilities.Bot\LibMatrix.Utilities.Bot.csproj" />
+ </ItemGroup>
+</Project>
diff --git a/MatrixUtils.RoomUpgradeCLI/Program.cs b/MatrixUtils.RoomUpgradeCLI/Program.cs
new file mode 100644
index 0000000..64de553
--- /dev/null
+++ b/MatrixUtils.RoomUpgradeCLI/Program.cs
@@ -0,0 +1,28 @@
+using LibMatrix.Services;
+using LibMatrix.Utilities.Bot;
+using MatrixUtils.RoomUpgradeCLI;
+using MatrixUtils.RoomUpgradeCLI.Commands;
+
+var builder = Host.CreateApplicationBuilder(args);
+builder.Services.AddRoryLibMatrixServices();
+builder.Services.AddMatrixBot();
+
+if (args.Length == 0) {
+ Console.WriteLine("No command provided. Use 'new', 'new-upgrade', or 'import-upgrade'.");
+ return;
+}
+
+builder.Services.AddSingleton<RuntimeContext>(new RuntimeContext() {
+ Args = args
+});
+
+if (args[0] == "new")
+ builder.Services.AddHostedService<NewFileCommand>();
+else if (args[0] == "import-upgrade") { }
+else {
+ Console.WriteLine("Unknown command. Use 'new', 'new-upgrade', or 'import-upgrade'.");
+ return;
+}
+
+var host = builder.Build();
+host.Run();
\ No newline at end of file
diff --git a/MatrixUtils.RoomUpgradeCLI/Properties/launchSettings.json b/MatrixUtils.RoomUpgradeCLI/Properties/launchSettings.json
new file mode 100644
index 0000000..76f122f
--- /dev/null
+++ b/MatrixUtils.RoomUpgradeCLI/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "MatrixUtils.RoomUpgradeCLI": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "environmentVariables": {
+ "DOTNET_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/MatrixUtils.RoomUpgradeCLI/RuntimeContext.cs b/MatrixUtils.RoomUpgradeCLI/RuntimeContext.cs
new file mode 100644
index 0000000..50e6781
--- /dev/null
+++ b/MatrixUtils.RoomUpgradeCLI/RuntimeContext.cs
@@ -0,0 +1,5 @@
+namespace MatrixUtils.RoomUpgradeCLI;
+
+public class RuntimeContext {
+ public string[] Args { get; set; }
+}
\ No newline at end of file
diff --git a/MatrixUtils.RoomUpgradeCLI/appsettings.Development.json b/MatrixUtils.RoomUpgradeCLI/appsettings.Development.json
new file mode 100644
index 0000000..b2dcdb6
--- /dev/null
+++ b/MatrixUtils.RoomUpgradeCLI/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/MatrixUtils.RoomUpgradeCLI/appsettings.json b/MatrixUtils.RoomUpgradeCLI/appsettings.json
new file mode 100644
index 0000000..b2dcdb6
--- /dev/null
+++ b/MatrixUtils.RoomUpgradeCLI/appsettings.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj b/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj
index e69774b..f446bf3 100644
--- a/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj
+++ b/MatrixUtils.Web.Server/MatrixUtils.Web.Server.csproj
@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.7" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.8" />
</ItemGroup>
<ItemGroup>
diff --git a/MatrixUtils.Web/MatrixUtils.Web.csproj b/MatrixUtils.Web/MatrixUtils.Web.csproj
index f418340..18204d0 100644
--- a/MatrixUtils.Web/MatrixUtils.Web.csproj
+++ b/MatrixUtils.Web/MatrixUtils.Web.csproj
@@ -39,10 +39,10 @@
<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.7" />
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.7" PrivateAssets="all" />
- <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.7" />
- <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="9.0.7" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.8" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.8" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.8" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="9.0.8" />
<PackageReference Include="SpawnDev.BlazorJS.WebWorkers" Version="2.17.1" />
</ItemGroup>
diff --git a/MatrixUtils.Web/Pages/Rooms/Create2.razor b/MatrixUtils.Web/Pages/Rooms/Create2.razor
index 19f6fbc..4a29847 100644
--- a/MatrixUtils.Web/Pages/Rooms/Create2.razor
+++ b/MatrixUtils.Web/Pages/Rooms/Create2.razor
@@ -1,14 +1,9 @@
@page "/Rooms/Create2"
-@using System.Text.Json
-@using System.Reflection
-@using ArcaneLibs
@using ArcaneLibs.Extensions
-@using Blazored.LocalStorage
@using LibMatrix
-@using LibMatrix.EventTypes.Spec.State.RoomInfo
@using LibMatrix.Helpers
@using LibMatrix.Responses
-@using MatrixUtils.Web.Classes.RoomCreationTemplates
+@using LibMatrix.RoomTypes
@using MatrixUtils.Web.Pages.Rooms.RoomCreateComponents
@inject ILogger<Create2> logger
@* @* ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not */ *@
@@ -23,7 +18,7 @@
</style>
<table class="table-top-first-tr">
@if (roomBuilder is RoomUpgradeBuilder roomUpgrade) {
- <RoomCreateUpgradeOptions roomUpgrade="@roomUpgrade" PageStateHasChanged="@StateHasChanged"/>
+ <RoomCreateUpgradeOptions roomUpgrade="@roomUpgrade" PageStateHasChanged="@StateHasChanged" OldRoom="@PreviousRoom" />
}
else {
@* <tr style="padding-bottom: 16px;"> *@
@@ -49,21 +44,13 @@
<RoomCreatePermissionsOptions roomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged" Homeserver="@Homeserver"/>
<RoomCreateMembershipOptions roomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged" Homeserver="@Homeserver"/>
@* Initial states, should remain at bottom *@
- </table>
+ <RoomCreateInitialStateOptions roomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged" Homeserver="@Homeserver"/>
+ </table>
<LinkButton OnClickAsync="@CreateRoom">Create room</LinkButton>
- <br/>
- <div
- style="position: fixed; top: 56px; right: 0; width: fit-content; max-width: 25%; height: calc(100vh - 56px); overflow: auto; background-color: #2c3054; padding-right: 32px; border-left: 1px solid #ccc;">
- <details open>
- <summary>RoomBuilder state</summary>
- <InputCheckbox @bind-Value="@ShowNullInState"/>
- <span>Show null values</span><br/>
- <pre>
- @roomBuilder.ToJson(ignoreNull: !ShowNullInState)
- </pre>
- </details>
- </div>
}
+
+<RoomCreateStateDisplay @bind-RoomBuilder="@roomBuilder" PageStateHasChanged="@StateHasChanged"/>
+
@if (_matrixException is not null) {
<ModalWindow Title="@("Matrix exception: " + _matrixException.ErrorCode)">
<pre>
@@ -73,13 +60,13 @@
}
@code {
-
+
#region State
[Parameter, SupplyParameterFromQuery(Name = "previousRoomId")]
public string? PreviousRoomId { get; set; }
-
- private bool ShowNullInState { get; set; }
+
+ public GenericRoom? PreviousRoom { get; set; }
private bool Ready { get; set; }
@@ -109,7 +96,8 @@
Homeserver = await sessionStore.GetCurrentHomeserver(navigateOnFailure: true);
if (Homeserver is null) return;
if (!string.IsNullOrWhiteSpace(PreviousRoomId)) {
- roomBuilder = new RoomUpgradeBuilder(Homeserver.GetRoom(PreviousRoomId));
+ roomBuilder = new RoomUpgradeBuilder();
+ PreviousRoom = Homeserver.GetRoom(PreviousRoomId);
}
roomBuilder.ServerAcls.Allow = ["*"];
@@ -124,14 +112,14 @@
// Presets = Presets.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
// if (!Presets.ContainsKey("Default")) {
- // Console.WriteLine($"No default room found in {Presets.Count} presets: {string.Join(", ", Presets.Keys)}");
+ // Console.WriteLine($"No default room found in {Presets.Count} presets: {string.Join(", ", Presets.Keys)}");
// }
// else RoomPreset = "Default";
Ready = true;
StateHasChanged();
if (roomBuilder is RoomUpgradeBuilder roomUpgrade) {
- await roomUpgrade.ImportAsync().ConfigureAwait(false);
+ // await roomUpgrade.ImportAsync().ConfigureAwait(false);
StateHasChanged();
}
}
diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor
index 272bd8b..99facbf 100644
--- a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor
+++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor
@@ -1,4 +1,5 @@
@using System.Text.Json
+@using ArcaneLibs.Extensions
@using LibMatrix
@using LibMatrix.Helpers
<tr>
@@ -8,7 +9,7 @@
{ "Important room state (before final access rules)", roomBuilder.ImportantState },
{ "Additional room state (after final access rules)", roomBuilder.InitialState },
}) {
- <details>
+ <details open>
@code
{
@@ -17,30 +18,50 @@
@* <summary>@displayName: @events.Count(x => !ImplementedStates.Contains(x.Type)) events</summary> *@
<summary>@displayName: @events.Count events</summary>
- <table>
- @* @foreach (var initialState in events.Where(x => !ImplementedStates.Contains(x.Type))) { *@
+ <LinkButton OnClick="@(() => {
+ events.Clear();
+ StateHasChanged();
+ })">Remove all
+ </LinkButton>
+ <LinkButton OnClick="@(() => {
+ events.Insert(0, new() {
+ Type = "",
+ StateKey = "",
+ RawContent = new(),
+ });
+ StateHasChanged();
+ })">Add new event
+ </LinkButton>
+ <br/>
+ @if (events.Count > 1000) {
+ <span style="color: red;">Warning: Too many initial state events! (more than 1000) - Please use the save/load feature in the state panel instead.</span>
+ }
+ else {
+ int i = 0;
@foreach (var initialState in events) {
- <tr>
- <td style="vertical-align: top;">
- @(initialState.Type):
- @if (!string.IsNullOrEmpty(initialState.StateKey)) {
- <br/>
- <span>(@initialState.StateKey)</span>
- }
-
- </td>
- <td>
- <pre>@JsonSerializer.Serialize(initialState.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre>
- </td>
- </tr>
+ <div id="@(initialState.Type + "/" + initialState.StateKey)">
+ <span>Event @(++i) (@GetRemoveButton(events, initialState))</span>
+ <br/>
+ @* <FancyTextBox Multiline="true" Value="@initialState.ToJson(ignoreNull: true)" *@
+ @* ValueChanged="@(json => { *@
+ @* if (string.IsNullOrWhiteSpace(json)) *@
+ @* events.Remove(initialState); *@
+ @* else *@
+ @* events.Replace(initialState, JsonSerializer.Deserialize<StateEvent>(json)); *@
+ @* StateHasChanged(); *@
+ @* })"></FancyTextBox> *@
+ <FancyTextBoxLazyJson T="StateEvent" Value="@initialState" ValueChanged="@(evt => { events.Replace(initialState, evt); })"></FancyTextBoxLazyJson>
+ <br/>
+ </div>
}
- </table>
+ }
</details>
}
</td>
</tr>
@code {
+
[Parameter]
public required RoomBuilder roomBuilder { get; set; }
@@ -49,4 +70,14 @@
[Parameter]
public AuthenticatedHomeserverGeneric Homeserver { get; set; }
+
+ private RenderFragment GetRemoveButton(List<StateEvent> events, StateEvent initialState) {
+ return @<span>
+ <LinkButton InlineText="true" OnClick="@(() => {
+ events.Remove(initialState);
+ PageStateHasChanged();
+ })">Remove</LinkButton>
+ </span>;
+ }
+
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor
index 5170d2d..6e300d4 100644
--- a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor
+++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor
@@ -6,10 +6,13 @@
<details>
<summary>@roomBuilder.Invites.Count members</summary>
<LinkButton OnClickAsync="@InviteAllSessions" InlineText="true">Invite all logged in accounts</LinkButton>
+ <br/>
@foreach (var member in roomBuilder.Invites) {
- <UserListItem _homeserver="Homeserver" UserId="@member.Key"></UserListItem>
+ <FancyTextBox Value="@member.Key" ValueChanged="@(val => roomBuilder.Invites.ChangeKey(member.Key, val))"/>
+ @* <UserListItem _homeserver="Homeserver" UserId="@member.Key"></UserListItem> *@
<span>: </span>
<FancyTextBox Value="@member.Value" ValueChanged="@(val => roomBuilder.Invites[member.Key] = val)"/>
+ <br/>
}
</details>
</td>
@@ -19,8 +22,10 @@
<td>
<details>
<summary>@roomBuilder.Bans.Count members</summary>
+ <br/>
@foreach (var member in roomBuilder.Bans) {
- <UserListItem _homeserver="Homeserver" UserId="@member.Key"></UserListItem>
+ @* <UserListItem _homeserver="Homeserver" UserId="@member.Key"></UserListItem> *@
+ <FancyTextBox Value="@member.Value" ValueChanged="@(val => roomBuilder.Bans.ChangeKey(member.Key, val))"/>
<span>: </span>
<FancyTextBox Value="@member.Value" ValueChanged="@(val => roomBuilder.Bans[member.Key] = val)"/>
}
diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMsc4321UpgradeOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMsc4321UpgradeOptions.razor
new file mode 100644
index 0000000..94e9638
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMsc4321UpgradeOptions.razor
@@ -0,0 +1,19 @@
+@using LibMatrix.Helpers
+<div style="border-left: solid 1px white; padding-left: 8px; margin-left: 8px;">
+ <span>Policy list upgrade type:</span>
+ <InputSelect @bind-Value="@roomUpgrade.UpgradeOptions.Msc4321PolicyListUpgradeOptions.UpgradeType">
+ <option value="@RoomUpgradeBuilder.Msc4321PolicyListUpgradeOptions.Msc4321PolicyListUpgradeType.Move">Move policy list (copy policies)</option>
+ <option value="@RoomUpgradeBuilder.Msc4321PolicyListUpgradeOptions.Msc4321PolicyListUpgradeType.Transition">Transition policy list (new list)</option>
+ </InputSelect>
+ <br/>
+</div>
+
+@code {
+
+ [Parameter]
+ public required RoomUpgradeBuilder roomUpgrade { get; set; }
+
+ [Parameter]
+ public required Action PageStateHasChanged { get; set; }
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateStateDisplay.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateStateDisplay.razor
new file mode 100644
index 0000000..eb373ba
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateStateDisplay.razor
@@ -0,0 +1,65 @@
+@using System.Text.Json
+@using System.Text.Json.Nodes
+@using ArcaneLibs.Blazor.Components.Services
+@using ArcaneLibs.Extensions
+@using LibMatrix.Helpers
+@inject BlazorSaveFileService SaveFileService
+<div
+ style="position: fixed; top: 56px; right: 0; width: fit-content; max-width: 25%; height: calc(100vh - 56px); overflow: auto; background-color: #2c3054; padding-right: 32px; border-left: 1px solid #ccc;">
+ <details open>
+ <summary>RoomBuilder state</summary>
+ <InputCheckbox @bind-Value="@ShowNullInState"/>
+ <span>Show null values</span><br/>
+ <LinkButton OnClickAsync="@SaveFile">Save</LinkButton>
+ <SimpleFilePicker OnFilePicked="@LoadFile"/>
+ <br/>
+ <pre>
+ @RoomBuilder.ToJson(ignoreNull: !ShowNullInState)
+ </pre>
+ </details>
+</div>
+
+@code {
+
+ [Parameter]
+ public required RoomBuilder RoomBuilder { get; set; }
+
+ [Parameter]
+ public required EventCallback<RoomBuilder> RoomBuilderChanged { get; set; }
+
+ [Parameter]
+ public required Action PageStateHasChanged { get; set; }
+
+ private bool ShowNullInState { get; set; }
+
+ private async Task SaveFile() {
+ Console.WriteLine("Saving room builder state to file...");
+ await SaveFileService.SaveFileAsync("room-builder.json", RoomBuilder.ToJson(), "application/json");
+ }
+
+ private async Task LoadFile(InputFileChangeEventArgs e) {
+ if (!RoomBuilderChanged.HasDelegate) throw new InvalidOperationException("RoomBuilderChanged must have a delegate.");
+ if (e.FileCount == 0) return;
+ Console.WriteLine("Loading room builder state from file...");
+ var stream = e.File.OpenReadStream(4 * 1024 * 1024 * 1024L);
+ var json = await JsonSerializer.DeserializeAsync<JsonObject>(stream);
+ if (json is null) {
+ Console.WriteLine("Failed to deserialize JSON from file.");
+ return;
+ }
+
+ if (json.ContainsKey(nameof(RoomUpgradeBuilder.UpgradeOptions))) {
+ Console.WriteLine("Got room upgrade builder state.");
+ RoomBuilder = json.Deserialize<RoomUpgradeBuilder>();
+ }
+ else {
+ Console.WriteLine("Got room builder state.");
+ RoomBuilder = json.Deserialize<RoomBuilder>();
+ }
+
+ await RoomBuilderChanged.InvokeAsync(RoomBuilder);
+ PageStateHasChanged();
+ Console.WriteLine("Room builder state loaded from file.");
+ }
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor
index 3e8c3dd..d4c4bfe 100644
--- a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor
+++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor
@@ -1,33 +1,51 @@
@using LibMatrix.Helpers
+@using LibMatrix.RoomTypes
<tr>
<td>Room upgrade options</td>
<td>
- <details>
- <summary>Upgrading from @roomUpgrade.OldRoom.RoomId</summary>
- <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.InviteMembers"></InputCheckbox>
- <span>Invite members</span>
+ @* <details> *@
+ @* <summary>Upgrading from @roomUpgrade.OldRoom.RoomId</summary> *@
+ <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.InviteMembers"></InputCheckbox>
+ <span>Invite members</span>
+ <br/>
+ <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.InvitePowerlevelUsers"></InputCheckbox>
+ <span>Invite users with powerlevels</span>
+ <br/>
+ <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.MigrateBans"></InputCheckbox>
+ <span>Copy bans (do not use with moderation bots!)</span>
+ <br/>
+ <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.MigrateEmptyStateEvents"></InputCheckbox>
+ <span>Include empty state events</span>
+ <br/>
+ <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.UpgradeUnstableValues"></InputCheckbox>
+ <span>Update unstable namespaced values to spec versions (experimental)</span>
+ <br/>
+ @if (roomUpgrade.Type == "support.feline.policy.lists.msc.v1") {
+ <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.Msc4321PolicyListUpgradeOptions.Enable"></InputCheckbox>
+ <span>Enable MSC4321 support</span>
<br/>
- <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.InvitePowerlevelUsers"></InputCheckbox>
- <span>Invite users with powerlevels</span>
- <br/>
- <InputCheckbox @bind-Value="@roomUpgrade.UpgradeOptions.MigrateBans"></InputCheckbox>
- <span>Copy bans (do not use with moderation bots!)</span>
- <br/>
- <LinkButton OnClickAsync="@(async () => {
- await roomUpgrade.ImportAsync();
- PageStateHasChanged();
- })">Apply
- </LinkButton>
- </details>
+ @if (roomUpgrade.UpgradeOptions.Msc4321PolicyListUpgradeOptions.Enable) {
+ <RoomCreateMsc4321UpgradeOptions roomUpgrade="@roomUpgrade" PageStateHasChanged="@PageStateHasChanged"/>
+ }
+ }
+ <LinkButton OnClickAsync="@(async () => {
+ await roomUpgrade.ImportAsync(OldRoom);
+ PageStateHasChanged();
+ })">Apply
+ </LinkButton>
+ @* </details> *@
</td>
</tr>
@code {
[Parameter]
- public required RoomUpgradeBuilder roomUpgrade { get; set; }
+ public required GenericRoom OldRoom { get; set; }
[Parameter]
+ public required RoomUpgradeBuilder roomUpgrade { get; set; }
+
+ [Parameter]
public required Action PageStateHasChanged { get; set; }
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Program.cs b/MatrixUtils.Web/Program.cs
index 57fe03d..cad5393 100644
--- a/MatrixUtils.Web/Program.cs
+++ b/MatrixUtils.Web/Program.cs
@@ -1,6 +1,7 @@
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;
+using ArcaneLibs.Blazor.Components.Services;
using Blazored.LocalStorage;
using Blazored.SessionStorage;
using LibMatrix.Extensions;
@@ -20,8 +21,7 @@ builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddBlazorJSRuntime();
-builder.Services.AddWebWorkerService(webWorkerService =>
-{
+builder.Services.AddWebWorkerService(webWorkerService => {
// Optionally configure the WebWorkerService service before it is used
// Default WebWorkerService.TaskPool settings: PoolSize = 0, MaxPoolSize = 1, AutoGrow = true
// Below sets TaskPool max size to 2. By default the TaskPool size will grow as needed up to the max pool size.
@@ -48,8 +48,7 @@ catch (Exception e) {
Console.WriteLine("Could not load appsettings: " + e);
}
-builder.Logging.AddConfiguration(
- builder.Configuration.GetSection("Logging"));
+builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
builder.Services.AddBlazoredLocalStorage(config => {
config.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
@@ -82,5 +81,6 @@ MatrixHttpClient.LogRequests = false;
builder.Services.AddRoryLibMatrixServices();
builder.Services.AddScoped<RmuSessionStore>();
+builder.Services.AddSingleton<BlazorSaveFileService>();
// await builder.Build().RunAsync();
await builder.Build().BlazorJSRunAsync();
\ No newline at end of file
diff --git a/MatrixUtils.sln b/MatrixUtils.sln
index 889232b..093e8e9 100644
--- a/MatrixUtils.sln
+++ b/MatrixUtils.sln
@@ -74,6 +74,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.Federation", "Lib
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.FederationTest", "LibMatrix\Utilities\LibMatrix.FederationTest\LibMatrix.FederationTest.csproj", "{960CC2DF-BB1A-4164-A895-834F81B3A113}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixUtils.RoomUpgradeCLI", "MatrixUtils.RoomUpgradeCLI\MatrixUtils.RoomUpgradeCLI.csproj", "{F0F10F51-4883-4C70-80D2-24D3AA8C0096}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -468,6 +470,18 @@ Global
{960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|x64.Build.0 = Release|Any CPU
{960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|x86.ActiveCfg = Release|Any CPU
{960CC2DF-BB1A-4164-A895-834F81B3A113}.Release|x86.Build.0 = Release|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Debug|x64.Build.0 = Debug|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Debug|x86.Build.0 = Debug|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Release|x64.ActiveCfg = Release|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Release|x64.Build.0 = Release|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Release|x86.ActiveCfg = Release|Any CPU
+ {F0F10F51-4883-4C70-80D2-24D3AA8C0096}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
|