diff options
author | Rory& <root@rory.gay> | 2024-01-24 02:31:56 +0100 |
---|---|---|
committer | Rory& <root@rory.gay> | 2024-01-24 17:05:25 +0100 |
commit | 03313562d21d5db9bf6a14ebbeab80e06c883d3a (patch) | |
tree | e000546a2ee8e6a886a7ed9fd01ad674178fb7cb /MatrixUtils.Web/Pages/Rooms/Create.razor | |
parent | Make RMU installable (diff) | |
download | MatrixUtils-03313562d21d5db9bf6a14ebbeab80e06c883d3a.tar.xz |
MRU->RMU, fixes, cleanup
Diffstat (limited to 'MatrixUtils.Web/Pages/Rooms/Create.razor')
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/Create.razor | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/MatrixUtils.Web/Pages/Rooms/Create.razor b/MatrixUtils.Web/Pages/Rooms/Create.razor new file mode 100644 index 0000000..35b2ffb --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/Create.razor @@ -0,0 +1,338 @@ +@page "/Rooms/Create" +@using System.Text.Json +@using System.Reflection +@using ArcaneLibs.Extensions +@using LibMatrix +@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.RoomInfo +@using LibMatrix.Homeservers +@using LibMatrix.Responses +@using MatrixUtils.Web.Classes.RoomCreationTemplates +@* @* ReSharper disable once RedundantUsingDirective - Must not remove this, Rider marks this as "unused" when it's not */ *@ + +<h3>Room Manager - Create Room</h3> + +@* <pre Contenteditable="true" @onkeypress="@JsonChanged" content="JsonString">@JsonString</pre> *@ +<style> + table.table-top-first-tr tr td:first-child { + vertical-align: top; + } + </style> +<table class="table-top-first-tr"> + <tr style="padding-bottom: 16px;"> + <td>Preset:</td> + <td> + @if (Presets is null) { + <p style="color: red;">Presets is null!</p> + } + else { + <InputSelect @bind-Value="@RoomPreset"> + @foreach (var createRoomRequest in Presets) { + <option value="@createRoomRequest.Key">@createRoomRequest.Key</option> + } + </InputSelect> + } + </td> + </tr> + @if (creationEvent is not null) { + <tr> + <td>Room name:</td> + <td> + @if (creationEvent.Name is null) { + <p style="color: red;">creationEvent.Name is null!</p> + } + else { + <FancyTextBox @bind-Value="@creationEvent.Name"></FancyTextBox> + <p>(#<FancyTextBox @bind-Value="@creationEvent.RoomAliasName"></FancyTextBox>:@Homeserver.WhoAmI.UserId.Split(':').Last())</p> + } + </td> + </tr> + <tr> + <td>Room type:</td> + <td> + @if (creationEvent.CreationContentBaseType is null) { + <p style="color: red;">creationEvent._creationContentBaseType is null!</p> + } + else { + <InputSelect @bind-Value="@creationEvent.CreationContentBaseType.Type"> + <option value="">Room</option> + <option value="m.space">Space</option> + </InputSelect> + <FancyTextBox @bind-Value="@creationEvent.CreationContentBaseType.Type"></FancyTextBox> + } + </td> + </tr> + <tr> + <td style="padding-top: 16px;">History visibility:</td> + <td style="padding-top: 16px;"> + <InputSelect @bind-Value="@historyVisibility.HistoryVisibility"> + <option value="invited">Invited</option> + <option value="joined">Joined</option> + <option value="shared">Shared</option> + <option value="world_readable">World readable</option> + </InputSelect> + </td> + </tr> + <tr> + <td>Guest access:</td> + <td> + <ToggleSlider @bind-Value="guestAccessEvent.IsGuestAccessEnabled"> + @(guestAccessEvent.IsGuestAccessEnabled ? "Guests can join" : "Guests cannot join") (@guestAccessEvent.GuestAccess) + </ToggleSlider> + <InputSelect @bind-Value="@guestAccessEvent.GuestAccess"> + <option value="can_join">Can join</option> + <option value="forbidden">Forbidden</option> + </InputSelect> + </td> + </tr> + + <tr> + <td>Room icon:</td> + <td> + <img src="@Homeserver.ResolveMediaUri(roomAvatarEvent.Url)" style="width: 128px; height: 128px; border-radius: 50%;"/> + <div style="display: inline-block; vertical-align: middle;"> + <FancyTextBox @bind-Value="@roomAvatarEvent.Url"></FancyTextBox><br/> + <InputFile OnChange="RoomIconFilePicked"></InputFile> + </div> + </td> + </tr> + <tr> + <td>Permissions:</td> + <details> + <summary>@creationEvent.PowerLevelContentOverride.Users.Count members</summary> + @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys) { + var _event = user; + <tr> + <td> + <FancyTextBox Formatter="@GetPermissionFriendlyName" + Value="@_event" + ValueChanged="val => { creationEvent.PowerLevelContentOverride.Events.ChangeKey(_event, val); }"> + </FancyTextBox>: + </td> + <td> + <input type="number" value="@creationEvent.PowerLevelContentOverride.Events[_event]" @oninput="val => { creationEvent.PowerLevelContentOverride.Events[_event] = int.Parse(val.Value.ToString()); }" @onfocusout="() => { creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"/> + </td> + </tr> + } + @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys) { + var _user = user; + <tr> + <td><FancyTextBox Value="@_user" ValueChanged="val => { creationEvent.PowerLevelContentOverride.Users.ChangeKey(_user, val); creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); }"></FancyTextBox>:</td> + <td> + <input type="number" value="@creationEvent.PowerLevelContentOverride.Users[_user]" @oninput="val => { creationEvent.PowerLevelContentOverride.Users[_user] = int.Parse(val.Value.ToString()); }"/> + </td> + </tr> + } + </details> + </tr> + <tr> + <td>Server ACLs:</td> + <td> + @if (serverAcl?.Allow is null) { + <p>No allow rules exist!</p> + <button @onclick="@(() => { serverAcl.Allow = new List<string> { "*" }; })">Create sane defaults</button> + } + else { + <details> + <summary>@((creationEvent["m.room.server_acls"].TypedContent as RoomServerACLEventContent).Allow.Count) allow rules</summary> + @* <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> *@ + </details> + } + @if (serverAcl?.Deny is null) { + <p>No deny rules exist!</p> + <button @onclick="@(() => { serverAcl.Allow = new List<string>(); })">Create sane defaults</button> + } + else { + <details> + <summary>@((creationEvent["m.room.server_acls"].TypedContent as RoomServerACLEventContent).Deny.Count) deny rules</summary> + @* <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> *@ + </details> + } + </td> + </tr> + + <tr> + <td>Invited members:</td> + <td> + <details> + <summary>@creationEvent.InitialState.Count(x => x.Type == "m.room.member") members</summary> + @* <button @onclick="() => { RuntimeCache.LoginSessions.Select(x => x.Value.LoginResponse.UserId).ToList().ForEach(InviteMember); }">Invite all logged in accounts</button> *@ + @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != Homeserver.UserId)) { + <UserListItem UserId="@member.StateKey"></UserListItem> + } + </details> + </td> + </tr> + @* Initial states, should remain at bottom *@ + <tr> + <td style="vertical-align: top;">Initial states:</td> + <td> + <details> + + @code + { + private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" }; + } + + <summary> @creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states</summary> + <table> + @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type))) { + <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> + <details> + <summary> @creationEvent.InitialState.Count initial states</summary> + <table> + @foreach (var initialState in creationEvent.InitialState) { + var _state = initialState; + <tr> + <td style="vertical-align: top;"> + <span>@(_state.Type):</span><br/> + <button @onclick="() => { creationEvent.InitialState.Remove(_state); StateHasChanged(); }">Remove</button> + </td> + + <td> + <pre>@JsonSerializer.Serialize(_state.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre> + </td> + </tr> + } + </table> + </details> + </td> + </tr> + } +</table> +<button @onclick="CreateRoom">Create room</button> +<br/> +<ModalWindow Title="Creation JSON"> + <pre> + @creationEvent.ToJson(ignoreNull: true) + </pre> +</ModalWindow> +<ModalWindow Title="Creation JSON (with null values)"> + <pre> + @creationEvent.ToJson() + </pre> +</ModalWindow> + +@if (_matrixException is not null) { + <ModalWindow Title="@("Matrix exception: " + _matrixException.ErrorCode)"> + <pre> + @_matrixException.Message + </pre> + </ModalWindow> +} + +@code { + + private string RoomPreset { + get => Presets.ContainsValue(creationEvent) ? Presets.First(x => x.Value == creationEvent).Key : "Not a preset"; + set { + creationEvent = Presets[value]; + JsonChanged(); + StateHasChanged(); + } + } + + private CreateRoomRequest? creationEvent { get; set; } + + private Dictionary<string, CreateRoomRequest>? Presets { get; set; } = new(); + private AuthenticatedHomeserverGeneric? Homeserver { get; set; } + + private MatrixException? _matrixException { get; set; } + + private RoomHistoryVisibilityEventContent? historyVisibility => creationEvent?["m.room.history_visibility"].TypedContent as RoomHistoryVisibilityEventContent; + private RoomGuestAccessEventContent? guestAccessEvent => creationEvent?["m.room.guest_access"].TypedContent as RoomGuestAccessEventContent; + private RoomServerACLEventContent? serverAcl => creationEvent?["m.room.server_acls"].TypedContent as RoomServerACLEventContent; + private RoomAvatarEventContent? roomAvatarEvent => creationEvent?["m.room.avatar"].TypedContent as RoomAvatarEventContent; + + protected override async Task OnInitializedAsync() { + Homeserver = await RMUStorage.GetCurrentSessionOrNavigate(); + if (Homeserver is null) return; + + foreach (var x in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Contains(typeof(IRoomCreationTemplate))).ToList()) { + Console.WriteLine($"Found room creation template in class: {x.FullName}"); + var instance = (IRoomCreationTemplate)Activator.CreateInstance(x); + Presets[instance.Name] = instance.CreateRoomRequest; + } + Presets = Presets.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); + + if (!Presets.ContainsKey("Default")) { + Console.WriteLine($"No default room found in {Presets.Count} presets: {string.Join(", ", Presets.Keys)}"); + } + else RoomPreset = "Default"; + + await base.OnInitializedAsync(); + } + + private void JsonChanged() => Console.WriteLine(creationEvent.ToJson()); + + private async Task RoomIconFilePicked(InputFileChangeEventArgs obj) { + var res = await Homeserver.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType); + Console.WriteLine(res); + (creationEvent["m.room.avatar"].TypedContent as RoomAvatarEventContent).Url = res; + StateHasChanged(); + } + + private async Task CreateRoom() { + Console.WriteLine("Create room"); + Console.WriteLine(creationEvent.ToJson()); + creationEvent.CreationContent.Add("rory.gay.created_using", "Rory&::MatrixUtils (https://rmu.rory.gay)"); + try { + var id = await Homeserver.CreateRoom(creationEvent); + } + catch (MatrixException e) { + _matrixException = e; + } + } + + private void InviteMember(string mxid) { + if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && Homeserver.UserId != mxid) + creationEvent.InitialState.Add(new StateEvent { + Type = "m.room.member", + StateKey = mxid, + TypedContent = new RoomMemberEventContent { + Membership = "invite", + Reason = "Automatically invited at room creation time." + } + }); + } + + private string GetStateFriendlyName(string key) => key switch { + "m.room.history_visibility" => "History visibility", + "m.room.guest_access" => "Guest access", + "m.room.join_rules" => "Join rules", + "m.room.server_acl" => "Server ACL", + "m.room.avatar" => "Avatar", + _ => key + }; + + private string GetPermissionFriendlyName(string key) => key switch { + "m.reaction" => "Send reaction", + "m.room.avatar" => "Change room icon", + "m.room.canonical_alias" => "Change room alias", + "m.room.encryption" => "Enable encryption", + "m.room.history_visibility" => "Change history visibility", + "m.room.name" => "Change room name", + "m.room.power_levels" => "Change power levels", + "m.room.tombstone" => "Upgrade room", + "m.room.topic" => "Change room topic", + "m.room.pinned_events" => "Pin events", + "m.room.server_acl" => "Change server ACLs", + _ => key + }; + +} |