about summary refs log tree commit diff
path: root/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-08-06 03:15:16 +0200
committerRory& <root@rory.gay>2025-08-06 03:15:16 +0200
commit89a14526658e5d061b1aef34ab569e979c9c0cf8 (patch)
treef98077d6775e78a77fb142c7f877e44e8af24cf0 /MatrixUtils.Web/Pages/Rooms/RoomCreateComponents
parentSynapse admin fixes, variou schanges (diff)
downloadMatrixUtils-dev/synapse-resync-state.tar.xz
Various changes, room create/upgrade work dev/synapse-resync-state
Diffstat (limited to 'MatrixUtils.Web/Pages/Rooms/RoomCreateComponents')
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateBasicRoomInfoOptions.razor52
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateCreateOptions.razor92
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor52
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor55
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePermissionsOptions.razor123
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePrivacyOptions.razor70
-rw-r--r--MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor33
7 files changed, 477 insertions, 0 deletions
diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateBasicRoomInfoOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateBasicRoomInfoOptions.razor
new file mode 100644

index 0000000..c1ee202 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateBasicRoomInfoOptions.razor
@@ -0,0 +1,52 @@ +@using ArcaneLibs +@using LibMatrix.Helpers +<tr> + <td>Room name:</td> + <td> + <FancyTextBox @bind-Value="@roomBuilder.Name.Name"></FancyTextBox> + </td> +</tr> +<tr> + <td>Room alias:</td> + <td> + <InputLocalPart Sigil="#" ServerName="@Homeserver.ServerName" @bind-LocalPart="@roomBuilder.AliasLocalPart"></InputLocalPart> + </td> +</tr> +<tr> + <td>Room icon:</td> + <td> + @if (!string.IsNullOrWhiteSpace(roomBuilder.Avatar.Url)) { + <MxcAvatar Homeserver="Homeserver" MxcUri="@roomBuilder.Avatar.Url" Size="3" SizeUnit="em" Circular="true"/> + } + else { + <img class="avatar" style="height: 3em; width: 3em; border-radius: 50%;" src="@IdenticonGenerator.GenerateAsDataUri(Homeserver.WhoAmI.UserId)"/> + } + <div style="display: inline-block; vertical-align: middle;"> + <FancyTextBox @bind-Value="@roomBuilder.Avatar.Url"></FancyTextBox> + <br/> + <SimpleFilePicker OnFilePicked="@RoomIconFilePicked"></SimpleFilePicker> + </div> + </td> +</tr> + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private static readonly SvgIdenticonGenerator IdenticonGenerator = new(); + + private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) { + var res = await Homeserver.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType); + Console.WriteLine(res); + roomBuilder.Avatar.Url = res; + PageStateHasChanged(); + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateCreateOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateCreateOptions.razor new file mode 100644
index 0000000..3f4a73d --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateCreateOptions.razor
@@ -0,0 +1,92 @@ +@using Blazored.LocalStorage +@using LibMatrix.Helpers +@inject ILocalStorageService LocalStorage +<tr> + <td>Room type:</td> + <td> + @if (RoomTypes.ContainsKey(roomBuilder.Type ?? "")) { + <InputSelect @bind-Value="@roomBuilder.Type"> + @foreach (var type in RoomTypes) { + <option value="@type.Key">@type.Value</option> + } + <option value="custom">Custom ...</option> + </InputSelect> + } + else { + <FancyTextBox @bind-Value="@roomBuilder.Type"></FancyTextBox> + } + + <span> version </span> + @if (Capabilities is null) { + <span style="color: #888;">Loading...</span> + } + else { + <InputSelect @bind-Value="@roomBuilder.Version"> + @foreach (var version in Capabilities.Capabilities.RoomVersions!.Available!) { + <option value="@version.Key">@version.Key (@version.Value)</option> + } + </InputSelect> + } + </td> +</tr> +<tr> + <td style="vertical-align: top;">Allow attribution:</td> + <td> + <InputCheckbox @bind-Value="@AllowAttribution"/> + <span>Allow attribution to Rory&::MatrixUtils</span> + <LinkButton InlineText="true" OnClick="@(() => ShowAttributionInfo = true)">?</LinkButton> + </td> +</tr> + +@if (ShowAttributionInfo) { + <ModalWindow Title="Allow attribution to Rory&::MatrixUtils" + OnCloseClicked="@(() => ShowAttributionInfo = false)"> + <span>This will add the following to the room creation content:</span> + <br/> + <pre>{ "gay.rory.created_using": "Rory&::MatrixUtils (https://mru.rory.gay)" }</pre> + <span>This is not visible to users unless they manually inspect the room's create event source.</span> + </ModalWindow> +} + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private AuthenticatedHomeserverGeneric.CapabilitiesResponse? Capabilities { get; set; } + + private bool ShowAttributionInfo { + get; + set { + field = value; + StateHasChanged(); + } + } + + private bool AllowAttribution { + get; + set { + field = value; + _ = LocalStorage.SetItemAsync("rmu.room_create.allow_attribution", value); + } + } = true; + + protected override async Task OnInitializedAsync() { + Capabilities = await Homeserver.GetCapabilitiesAsync(); + roomBuilder.Version = Capabilities.Capabilities.RoomVersions!.Default; + AllowAttribution = await LocalStorage.GetItemAsync<bool?>("rmu.room_create.allow_attribution") ?? true; + } + + private static Dictionary<string, string> RoomTypes { get; } = new() { + { "", "Room" }, + { "m.space", "Space" }, + { "support.feline.policy.lists.msc.v1", "Policy list" } + }; + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor new file mode 100644
index 0000000..272bd8b --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateInitialStateOptions.razor
@@ -0,0 +1,52 @@ +@using System.Text.Json +@using LibMatrix +@using LibMatrix.Helpers +<tr> + <td style="vertical-align: top;">Initial room state:</td> + <td> + @foreach (var (displayName, events) in new Dictionary<string, List<StateEvent>>() { + { "Important room state (before final access rules)", roomBuilder.ImportantState }, + { "Additional room state (after final access rules)", roomBuilder.InitialState }, + }) { + <details> + + @code + { + // private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" }; + } + + @* <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))) { *@ + @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> + } + </table> + </details> + } + </td> +</tr> + +@code { + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor new file mode 100644
index 0000000..5170d2d --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateMembershipOptions.razor
@@ -0,0 +1,55 @@ +@using ArcaneLibs.Extensions +@using LibMatrix.Helpers +<tr> + <td>Invited members:</td> + <td> + <details> + <summary>@roomBuilder.Invites.Count members</summary> + <LinkButton OnClickAsync="@InviteAllSessions" InlineText="true">Invite all logged in accounts</LinkButton> + @foreach (var member in roomBuilder.Invites) { + <UserListItem _homeserver="Homeserver" UserId="@member.Key"></UserListItem> + <span>: </span> + <FancyTextBox Value="@member.Value" ValueChanged="@(val => roomBuilder.Invites[member.Key] = val)"/> + } + </details> + </td> +</tr> +<tr> + <td>Banned members:</td> + <td> + <details> + <summary>@roomBuilder.Bans.Count members</summary> + @foreach (var member in roomBuilder.Bans) { + <UserListItem _homeserver="Homeserver" UserId="@member.Key"></UserListItem> + <span>: </span> + <FancyTextBox Value="@member.Value" ValueChanged="@(val => roomBuilder.Bans[member.Key] = val)"/> + } + </details> + </td> +</tr> + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private async Task InviteAllSessions() { + var sessions = await sessionStore.GetAllSessions(); + foreach (var session in sessions) { + if (roomBuilder.Invites.ContainsKey(session.Value.Auth.UserId) || session.Value.Auth.UserId == Homeserver!.WhoAmI.UserId) continue; + Console.WriteLine("Inviting " + session.Value.Auth.UserId); + roomBuilder.Invites.Add(session.Value.Auth.UserId, null); + Console.WriteLine("--"); + } + + Console.WriteLine("Got all sessions, invited: " + string.Join(", ", roomBuilder.Invites.Keys)); + StateHasChanged(); + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePermissionsOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePermissionsOptions.razor new file mode 100644
index 0000000..ba28b82 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePermissionsOptions.razor
@@ -0,0 +1,123 @@ +@using ArcaneLibs.Extensions +@using LibMatrix.Helpers +<tr> + <td>Permissions:</td> + <details> + <summary> + @if (roomBuilder.Version is "org.matrix.hydra.11" or "12") { + <span>@(roomBuilder.AdditionalCreators.Count + 1) creators, </span> + } + <span>@roomBuilder.PowerLevels.Users.Count members, @roomBuilder.PowerLevels.Events.Count events</span> + </summary> + + @if (roomBuilder.Version is "org.matrix.hydra.11" or "12") { + <span style="border-bottom: #444;">Creators:</span> + <br/> + <span>@Homeserver.WhoAmI.UserId (you - to change, visit <a href="/">the homepage</a>.)</span> + <br/> + + <StringListEditor @bind-Items="@roomBuilder.AdditionalCreators"></StringListEditor> + <br/> + } + + <span style="border-bottom: #444;">Events:</span><br/> + @foreach (var eventType in roomBuilder.PowerLevels.Events.Keys) { + var _event = eventType; + <tr> + <td> + <LinkButton InlineText="true" OnClick="@(() => { + roomBuilder.PowerLevels.Events.Remove(_event); + StateHasChanged(); + })">- + </LinkButton> + <div style="display: inline-flex;"> + <FancyTextBox Formatter="@GetPermissionFriendlyName" + Value="@_event" + ValueChanged="val => { roomBuilder.PowerLevels.Events.ChangeKey(_event, val); }"> + </FancyTextBox> + <span>:</span> + </div> + </td> + <td> + <input type="number" value="@roomBuilder.PowerLevels.Events[_event]" + @oninput="val => { roomBuilder.PowerLevels.Events[_event] = int.Parse(val.Value.ToString()); }" + @onfocusout="@(() => { roomBuilder.PowerLevels.Events = roomBuilder.PowerLevels.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); })"/> + </td> + </tr> + } + <tr> + <td> + <LinkButton InlineText="true" OnClick="@(() => { + roomBuilder.PowerLevels.Events[""] = 0; + StateHasChanged(); + })">+ + </LinkButton> + </td> + </tr> + + <span style="border-bottom: #444;">Users:</span><br/> + @foreach (var user in roomBuilder.PowerLevels.Users.Keys) { + var _user = user; + <tr> + <td> + <LinkButton InlineText="true" OnClick="@(() => { + roomBuilder.PowerLevels.Users.Remove(_user); + StateHasChanged(); + })">- + </LinkButton> + <div style="display: inline-flex;"> + <FancyTextBox Value="@_user" + ValueChanged="val => { roomBuilder.PowerLevels.Users.ChangeKey(_user, val); }"> + </FancyTextBox> + <span>:</span> + </div> + </td> + <td> + <input type="number" value="@roomBuilder.PowerLevels.Users[_user]" + @oninput="val => { roomBuilder.PowerLevels.Users[_user] = int.Parse(val.Value.ToString()); }" + @onfocusout="@(() => { roomBuilder.PowerLevels.Users = roomBuilder.PowerLevels.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); })"/> + </td> + </tr> + } + <tr> + <td> + <LinkButton InlineText="true" OnClick="@(() => { + roomBuilder.PowerLevels.Users[""] = 0; + StateHasChanged(); + })">+ + </LinkButton> + </td> + </tr> + </details> +</tr> + + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + + private string GetPermissionFriendlyName(string key) => key switch { + "m.reaction" => "Send reaction", + "m.room.avatar" => "Change room icon", + "m.room.canonical_alias" => "Change room alias", + "m.room.encryption" => "Enable encryption", + "m.room.history_visibility" => "Change history visibility", + "m.room.name" => "Change room name", + "m.room.power_levels" => "Change power levels", + "m.room.tombstone" => "Upgrade room", + "m.room.topic" => "Change room topic", + "m.room.pinned_events" => "Pin events", + "m.room.server_acl" => "Change server ACLs", + "org.matrix.msc4284.policy" => "Change policy server", + "m.room.guest_access" => "Change guest access", + _ => key + }; + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePrivacyOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePrivacyOptions.razor new file mode 100644
index 0000000..76f61c4 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreatePrivacyOptions.razor
@@ -0,0 +1,70 @@ +@using LibMatrix.Helpers +<tr> + <td style="padding-top: 16px;">Join rules:</td> + <td style="padding-top: 16px;"> + <InputSelect @bind-Value="@roomBuilder.JoinRules.JoinRuleValue"> + <option value="public">Anyone can join</option> + <option value="invite">Invite only</option> + <option value="knock">Ask to join</option> + <option value="restricted">Invite only (or mutual room)</option> + <option value="knock_restricted">Ask to join (or mutual room)</option> + </InputSelect> + </td> +</tr> +<tr> + <td>History visibility:</td> + <td> + <InputSelect @bind-Value="@roomBuilder.HistoryVisibility.HistoryVisibility"> + <option value="invited">Since invite</option> + <option value="joined">Since join</option> + <option value="shared">Since room creation (members only)</option> + <option value="world_readable">World readable (everyone)</option> + </InputSelect> + </td> +</tr> +<tr> + <td>Guest access:</td> + <td> + <InputCheckbox @bind-Value="roomBuilder.GuestAccess.IsGuestAccessEnabled"/> + <span>Allow guests to join</span> + <LinkButton InlineText="true" href="https://spec.matrix.org/v1.15/client-server-api/#guest-access" target="_blank">?</LinkButton> + </td> +</tr> +<tr> + <td>Server ACLs:</td> + <td> + @if (roomBuilder.ServerAcls?.Allow is null) { + <p>No allow rules exist!</p> + <LinkButton OnClick="@(() => { roomBuilder.ServerAcls!.Allow = ["*"]; })">Create sane defaults</LinkButton> + } + else { + <details> + <summary>@(roomBuilder.ServerAcls.Allow?.Count) allow rules</summary> + <StringListEditor @bind-Items="@roomBuilder.ServerAcls.Allow"></StringListEditor> + </details> + } + @if (roomBuilder.ServerAcls?.Deny is null) { + <p>No deny rules exist!</p> + <LinkButton OnClick="@(() => { roomBuilder.ServerAcls!.Deny = []; })">Create sane defaults</LinkButton> + } + else { + <details> + <summary>@(roomBuilder.ServerAcls.Deny?.Count) deny rules</summary> + <StringListEditor @bind-Items="@roomBuilder.ServerAcls.Deny"></StringListEditor> + </details> + } + </td> +</tr> + +@code { + + [Parameter] + public required RoomBuilder roomBuilder { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + + [Parameter] + public AuthenticatedHomeserverGeneric Homeserver { get; set; } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor new file mode 100644
index 0000000..3e8c3dd --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/RoomCreateComponents/RoomCreateUpgradeOptions.razor
@@ -0,0 +1,33 @@ +@using LibMatrix.Helpers +<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> + <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> + </td> +</tr> + +@code { + + [Parameter] + public required RoomUpgradeBuilder roomUpgrade { get; set; } + + [Parameter] + public required Action PageStateHasChanged { get; set; } + +} \ No newline at end of file