diff options
Diffstat (limited to 'MatrixRoomUtils.Web/Pages')
-rw-r--r-- | MatrixRoomUtils.Web/Pages/About.razor | 32 | ||||
-rw-r--r-- | MatrixRoomUtils.Web/Pages/Rooms/Create.razor | 450 |
2 files changed, 266 insertions, 216 deletions
diff --git a/MatrixRoomUtils.Web/Pages/About.razor b/MatrixRoomUtils.Web/Pages/About.razor index cf43c4f..b8d9c4a 100644 --- a/MatrixRoomUtils.Web/Pages/About.razor +++ b/MatrixRoomUtils.Web/Pages/About.razor @@ -1,7 +1,9 @@ @page "/About" @using System.Net +@using System.Net.Sockets @inject NavigationManager NavigationManager @inject ILocalStorageService LocalStorage +@using XtermBlazor <PageTitle>About</PageTitle> @@ -20,6 +22,9 @@ <p>This deployment also serves a copy of the compiled, hosting-ready binaries at <a href="MRU-SRC.tar.xz">/MRU-SRC.tar.xz</a>!</p> } +<Xterm @ref="_terminal" Options="_options" OnFirstRender="@OnFirstRender" style="max-width: fit-content; overflow-x: hidden;"/> + + @code { private bool showBinDownload { get; set; } @@ -34,4 +39,29 @@ await base.OnInitializedAsync(); } -} \ No newline at end of file + + private Xterm _terminal; + + private TerminalOptions _options = new TerminalOptions + { + CursorBlink = true, + CursorStyle = CursorStyle.Block, + Theme = + { + Background = "#17615e", + }, + }; + + private async Task OnFirstRender() { + var message = "Hello, World!\nThis is a terminal emulator!\n\nYou can type stuff here, and it will be sent to the server!\n\nThis is a test of the emergency broadcast system.\n\nThis is only a t"; + _terminal.Options.RendererType = RendererType.Dom; + _terminal.Options.ScreenReaderMode = true; + TcpClient. + for (var i = 0; i < message.Length; i++) { + await _terminal.Write(message[i].ToString()); + + await Task.Delay(50); + _terminal.Options.Theme.Background = $"#{(i * 2):X6}"; + } + } +} diff --git a/MatrixRoomUtils.Web/Pages/Rooms/Create.razor b/MatrixRoomUtils.Web/Pages/Rooms/Create.razor index 4255424..3a98801 100644 --- a/MatrixRoomUtils.Web/Pages/Rooms/Create.razor +++ b/MatrixRoomUtils.Web/Pages/Rooms/Create.razor @@ -12,213 +12,228 @@ <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.table-top-first-tr tr td:first-child { + vertical-align: top; + } </style> <table class="table-top-first-tr"> - <tr> - <td style="padding-bottom: 16px;">Preset:</td> - <td style="padding-bottom: 16px;"> - <InputSelect @bind-Value="@RoomPreset"> - @foreach (var createRoomRequest in Presets) { - <option value="@createRoomRequest.Key">@createRoomRequest.Key</option> - } - </InputSelect> - </td> - </tr> - <tr> - <td>Room name:</td> - <td> - <FancyTextBox @bind-Value="@creationEvent.Name"></FancyTextBox> - </td> - </tr> - <tr> - <td>Room alias (localpart):</td> - <td> - <FancyTextBox @bind-Value="@creationEvent.RoomAliasName"></FancyTextBox> - </td> - </tr> - <tr> - <td>Room type:</td> - <td> - <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;"> - @{ - var historyVisibility = creationEvent["m.room.history_visibility"].TypedContent as HistoryVisibilityEventData; - } - <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> + <tr style="padding-bottom: 16px;"> + <td>Preset:</td> <td> - @{ - var guestAccessEvent = creationEvent["m.room.guest_access"].TypedContent as GuestAccessEventData; + @if (Presets is null) { + <p style="color: red;">Presets is null!</p> } - <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> - @{ - var roomAvatarEvent = creationEvent["m.room.avatar"].TypedContent as RoomAvatarEventData; + else { + <InputSelect @bind-Value="@RoomPreset"> + @foreach (var createRoomRequest in Presets) { + <option value="@createRoomRequest.Key">@createRoomRequest.Key</option> + } + </InputSelect> } - <img src="@MediaResolver.ResolveMediaUri(HomeServer.HomeServerDomain, 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> - @{ - var serverAcl = creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData; - } - <details> - <summary>@((creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData).Allow.Count) allow rules</summary> - <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> - </details> - <details> - <summary>@(creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData).Deny.Count deny rules</summary> - <StringListEditor @bind-Items="@serverAcl.Deny"></StringListEditor> - </details> </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>Invited members:</td> - <td> + <tr> + <td>Room icon:</td> + <td> + <img src="@MediaResolver.ResolveMediaUri(HomeServer.HomeServerDomain, 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.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> + <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> - </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" }; + </tr> + <tr> + <td>Server ACLs:</td> + <td> + @if (serverAcl?.Allow is null) { + <p>No allow rules exist!</p> + <button @onclick="@(() => { serverAcl.Allow = new() { "*" }; })">Create sane defaults</button> } + else { + <details> + <summary>@((creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData).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(); })">Create sane defaults</button> + } + else { + <details> + <summary>@((creationEvent["m.room.server_acls"].TypedContent as ServerACLEventData).Deny.Count) deny rules</summary> + @* <StringListEditor @bind-Items="@serverAcl.Allow"></StringListEditor> *@ + </details> + } + </td> + </tr> - <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> + <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> } - </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> + </details> + </td> + </tr> + @* Initial states, should remain at bottom *@ + <tr> + <td style="vertical-align: top;">Initial states:</td> + <td> + <details> - <td> - <pre>@JsonSerializer.Serialize(_state.RawContent, new JsonSerializerOptions { WriteIndented = true })</pre> - </td> - </tr> + @code + { + private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" }; } - </table> - </details> - </td> - </tr> + + <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/> -<details> - <summary>Creation JSON</summary> +<ModalWindow Title="Creation JSON"> <pre> - @creationEvent.ToJson(ignoreNull: true) - </pre> -</details> -<details open> - <summary>Creation JSON (with null values)</summary> + @creationEvent.ToJson(ignoreNull: true) + </pre> +</ModalWindow> +<ModalWindow Title="Creation JSON (with null values)"> <pre> - @creationEvent.ToJson() - </pre> -</details> + @creationEvent.ToJson() + </pre> +</ModalWindow> +@if (_matrixException is not null) { + <ModalWindow Title="@("Matrix exception: " + _matrixException.ErrorCode)"> + <pre> + @_matrixException.Message + </pre> + </ModalWindow> +} @code { @@ -232,22 +247,22 @@ set { creationEvent = Presets[value]; JsonChanged(); - - creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); - guestAccessEvent = creationEvent["m.room.guest_access"].TypedContent as GuestAccessEventData; StateHasChanged(); } } - private Dictionary<string, string> creationEventValidationErrors { get; set; } = new(); + private CreateRoomRequest? creationEvent { get; set; } - private CreateRoomRequest creationEvent { get; set; } - GuestAccessEventData guestAccessEvent { get; set; } - - private Dictionary<string, CreateRoomRequest> Presets { get; set; } = new(); + private Dictionary<string, CreateRoomRequest>? Presets { get; set; } = new(); private AuthenticatedHomeServer? HomeServer { get; set; } + private MatrixException? _matrixException { get; set; } + + private HistoryVisibilityEventData? historyVisibility => creationEvent?["m.room.history_visibility"].TypedContent as HistoryVisibilityEventData; + private GuestAccessEventData? guestAccessEvent => creationEvent?["m.room.guest_access"].TypedContent as GuestAccessEventData; + private ServerACLEventData? serverAcl => creationEvent?["m.room.server_acls"].TypedContent as ServerACLEventData; + private RoomAvatarEventData? roomAvatarEvent => creationEvent?["m.room.avatar"].TypedContent as RoomAvatarEventData; + protected override async Task OnInitializedAsync() { HomeServer = await MRUStorage.GetCurrentSessionOrNavigate(); if (HomeServer is null) return; @@ -280,7 +295,12 @@ Console.WriteLine("Create room"); Console.WriteLine(creationEvent.ToJson()); creationEvent.CreationContent.Add("rory.gay.created_using", "Rory&::MatrixRoomUtils (https://mru.rory.gay)"); - var id = await HomeServer.CreateRoom(creationEvent); + try { + var id = await HomeServer.CreateRoom(creationEvent); + } + catch (MatrixException e) { + _matrixException = e; + } } private void InviteMember(string mxid) { @@ -295,28 +315,28 @@ }); } - 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 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 + 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 }; -} \ No newline at end of file +} |