MatrixRoomUtils

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | LICENSE

RoomManagerCreateRoom.razor (16598B)


      1 @page "/RoomManagerCreateRoom"
      2 @using System.Text.Json
      3 @using MatrixRoomUtils.Core.Extensions
      4 @using MatrixRoomUtils.Core.Responses
      5 @using MatrixRoomUtils.Web.Shared.SimpleComponents
      6 @using System.Reflection
      7 @using System.Runtime.Intrinsics.X86
      8 @using System.Text.Json.Serialization
      9 @using MatrixRoomUtils.Web.Classes.RoomCreationTemplates
     10 <h3>Room Manager - Create Room</h3>
     11 
     12 @* <pre Contenteditable="true" @onkeypress="@JsonChanged" ="JsonString">@JsonString</pre> *@
     13 <style>
     14     table.table-top-first-tr tr td:first-child {
     15         vertical-align: top;
     16     }
     17 </style>
     18 <table class="table-top-first-tr">
     19     <tr>
     20         <td style="padding-bottom: 16px;">Preset:</td>
     21         <td style="padding-bottom: 16px;">
     22             <InputSelect @bind-Value="@RoomPreset">
     23                 @foreach (var createRoomRequest in Presets)
     24                 {
     25                     <option value="@createRoomRequest.Key">@createRoomRequest.Key</option>
     26                 }
     27             </InputSelect>
     28         </td>
     29     </tr>
     30     @if (creationEvent != null)
     31     {
     32         <tr>
     33             <td>Room name:</td>
     34             <td>
     35                 <FancyTextBox @bind-Value="@creationEvent.Name"></FancyTextBox>
     36             </td>
     37         </tr>
     38         <tr>
     39             <td>Room alias (localpart):</td>
     40             <td>
     41                 <FancyTextBox @bind-Value="@creationEvent.RoomAliasName"></FancyTextBox>
     42             </td>
     43         </tr>
     44         <tr>
     45             <td>Room type:</td>
     46             <td>
     47                 <InputSelect @bind-Value="@creationEvent._creationContentBaseType.Type">
     48                     <option value="">Room</option>
     49                     <option value="m.space">Space</option>
     50                 </InputSelect>
     51                 <FancyTextBox @bind-Value="@creationEvent._creationContentBaseType.Type"></FancyTextBox>
     52             </td>
     53         </tr>
     54         <tr>
     55             <td style="padding-top: 16px;">History visibility:</td>
     56             <td style="padding-top: 16px;">
     57                 <InputSelect @bind-Value="@creationEvent.HistoryVisibility">
     58                     <option value="invited">Invited</option>
     59                     <option value="joined">Joined</option>
     60                     <option value="shared">Shared</option>
     61                     <option value="world_readable">World readable</option>
     62                 </InputSelect>
     63             </td>
     64         </tr>
     65         <tr>
     66             <td>Guest access:</td>
     67             <td>
     68                 <ToggleSlider Value="guestAccessEvent.IsGuestAccessEnabled" ValueChanged="@((bool v) => { guestAccessEvent.IsGuestAccessEnabled = v; creationEvent["m.room.guest_access"].Content = guestAccessEvent; })">@(guestAccessEvent.IsGuestAccessEnabled ? "Guests can join" : "Guests cannot join")</ToggleSlider>
     69                 @* <InputSelect @bind-Value="@creationEvent.GuestAccess"> *@
     70                 @* <option value="can_join">Can join</option> *@
     71                 @* <option value="forbidden">Forbidden</option> *@
     72                 @* </InputSelect> *@
     73             </td>
     74         </tr>
     75 
     76         <tr>
     77             <td>Room icon:</td>
     78             <td>
     79                 <img src="@RuntimeCache.CurrentHomeServer?.ResolveMediaUri(creationEvent.RoomIcon ?? "")" style="width: 128px; height: 128px; border-radius: 50%;"/>
     80                 <div style="    display: inline-block;
     81                             vertical-align: middle;">
     82                     <FancyTextBox @bind-Value="@creationEvent.RoomIcon"></FancyTextBox><br/>
     83                     <InputFile OnChange="RoomIconFilePicked"></InputFile>
     84                 </div>
     85 
     86             </td>
     87         </tr>
     88         <tr>
     89             <td>Permissions:</td>
     90             <details>
     91                 <summary>@creationEvent.PowerLevelContentOverride.Users.Count members</summary>
     92                 @foreach (var user in creationEvent.PowerLevelContentOverride.Events.Keys)
     93                 {
     94                     var _event = user;
     95                     <tr>
     96                         <td><FancyTextBox Formatter="@GetPermissionFriendlyName" Value="@_event" ValueChanged="val => { creationEvent.PowerLevelContentOverride.Events.ChangeKey(_event, val); }"></FancyTextBox>:</td>
     97                         <td>
     98                             <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); }"/>
     99                         </td>
    100                     </tr>
    101                 }
    102                 @foreach (var user in creationEvent.PowerLevelContentOverride.Users.Keys)
    103                 {
    104                     var _user = user;
    105                     <tr>
    106                         <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>
    107                         <td>
    108                             <input type="number" value="@creationEvent.PowerLevelContentOverride.Users[_user]" @oninput="val => { creationEvent.PowerLevelContentOverride.Users[_user] = int.Parse(val.Value.ToString()); }"/>
    109                         </td>
    110                     </tr>
    111                 }
    112             </details>
    113         </tr>
    114         <tr>
    115             <td>Server ACLs:</td>
    116             <td>
    117                 <details>
    118                     <summary>@(creationEvent.ServerACLs.Allow.Count) allow rules</summary>
    119                     <StringListEditor ItemsChanged="OverwriteWrappedProperties" Items="@ServerACLAllowRules"></StringListEditor>
    120                 </details>
    121                 <details>
    122                     <summary>@creationEvent.ServerACLs.Deny.Count deny rules</summary>
    123                     <StringListEditor ItemsChanged="OverwriteWrappedProperties" Items="@ServerACLDenyRules"></StringListEditor>
    124                 </details>
    125             </td>
    126         </tr>
    127 
    128         <tr>
    129             <td>Invited members:</td>
    130             <td>
    131                 <details>
    132                     <summary>@creationEvent.InitialState.Count(x => x.Type == "m.room.member") members</summary>
    133                     <button @onclick="() => { RuntimeCache.LoginSessions.Select(x => x.Value.LoginResponse.UserId).ToList().ForEach(InviteMember); }">Invite all logged in accounts</button>
    134                     @foreach (var member in creationEvent.InitialState.Where(x => x.Type == "m.room.member" && x.StateKey != RuntimeCache.CurrentHomeServer.UserId))
    135                     {
    136                         <UserListItem UserId="@member.StateKey"></UserListItem>
    137                     }
    138                 </details>
    139             </td>
    140         </tr>
    141 
    142         @* Initial states, should remain at bottom? *@
    143 
    144         <tr>
    145             <td style="vertical-align: top;">Initial states:</td>
    146             <td>
    147                 <details>
    148 
    149                     @code{
    150 
    151                         private static readonly string[] ImplementedStates = { "m.room.avatar", "m.room.history_visibility", "m.room.guest_access", "m.room.server_acl" };
    152 
    153                     }
    154 
    155                     <summary>@creationEvent.InitialState.Count(x => !ImplementedStates.Contains(x.Type)) custom states</summary>
    156                     <table>
    157                         @foreach (var initialState in creationEvent.InitialState.Where(x => !ImplementedStates.Contains(x.Type)))
    158                         {
    159                             <tr>
    160                                 <td style="vertical-align: top;">
    161                                     @(initialState.Type):
    162                                     @if (!string.IsNullOrEmpty(initialState.StateKey))
    163                                     {
    164                                         <br/>
    165                                         <span>(@initialState.StateKey)</span>
    166                                     }
    167                                 </td>
    168 
    169                                 <td>
    170                                     <pre>@JsonSerializer.Serialize(initialState.Content, new JsonSerializerOptions { WriteIndented = true })</pre>
    171                                 </td>
    172                             </tr>
    173                         }
    174                     </table>
    175                 </details>
    176                 <details>
    177                     <summary>@creationEvent.InitialState.Count initial states</summary>
    178                     <table>
    179                         @foreach (var initialState in creationEvent.InitialState)
    180                         {
    181                             var _state = initialState;
    182                             <tr>
    183                                 <td style="vertical-align: top;">
    184                                     <span>@(_state.Type):</span><br/>
    185                                     <button @onclick="() => { creationEvent.InitialState.Remove(_state); StateHasChanged(); }">Remove</button>
    186                                 </td>
    187 
    188                                 <td>
    189                                     <pre>@JsonSerializer.Serialize(_state.Content, new JsonSerializerOptions { WriteIndented = true })</pre>
    190                                 </td>
    191                             </tr>
    192                         }
    193                     </table>
    194                 </details>
    195             </td>
    196         </tr>
    197     }
    198 </table>
    199 <button @onclick="CreateRoom">Create room</button>
    200 <br/>
    201 <details>
    202     <summary>Creation JSON</summary>
    203     <pre>
    204         @creationEvent.ToJson(ignoreNull: true)
    205     </pre>
    206 </details>
    207 <details open>
    208     <summary>Creation JSON (with null values)</summary>
    209     <pre>
    210     @creationEvent.ToJson()
    211     </pre>
    212 </details>
    213 
    214 
    215 @code {
    216 
    217     private string RoomPreset
    218     {
    219         get
    220         {
    221             if (Presets.ContainsValue(creationEvent))
    222             {
    223                 return Presets.First(x => x.Value == creationEvent).Key;
    224             }
    225             return "Not a preset";
    226         }
    227         set
    228         {
    229             creationEvent = Presets[value];
    230             JsonChanged();
    231             OverwriteWrappedPropertiesFromEvent();
    232             creationEvent.PowerLevelContentOverride.Events = creationEvent.PowerLevelContentOverride.Events.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
    233             creationEvent.PowerLevelContentOverride.Users = creationEvent.PowerLevelContentOverride.Users.OrderByDescending(x => x.Value).ThenBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
    234             guestAccessEvent = creationEvent["m.room.guest_access"].As<GuestAccessContent>().Content;
    235             
    236             Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}");
    237             Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As<GuestAccessContent>().ToJson()}");
    238             creationEvent["m.room.guest_access"].As<GuestAccessContent>().Content.IsGuestAccessEnabled = false;
    239             Console.WriteLine("-- Created new guest access content --");
    240             Console.WriteLine($"Creation event uncasted: {creationEvent["m.room.guest_access"].ToJson()}");
    241             Console.WriteLine($"Creation event casted: {creationEvent["m.room.guest_access"].As<GuestAccessContent>().ToJson()}");
    242             Console.WriteLine($"Creation event casted back: {creationEvent["m.room.guest_access"].As<GuestAccessContent>().ToJson()}");
    243             StateHasChanged();
    244         }
    245     }
    246 
    247     private Dictionary<string, string> creationEventValidationErrors { get; set; } = new();
    248 
    249     private CreateRoomRequest creationEvent { get; set; }
    250     GuestAccessContent guestAccessEvent { get; set; }
    251 
    252     private Dictionary<string, CreateRoomRequest> Presets { get; set; } = new();
    253 
    254     protected override async Task OnInitializedAsync()
    255     {
    256         await LocalStorageWrapper.LoadFromLocalStorage(LocalStorage);
    257 
    258     //creationEvent = Presets["Default room"] = 
    259         foreach (var x in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Contains(typeof(IRoomCreationTemplate))).ToList())
    260         {
    261             Console.WriteLine($"Found room creation template in class: {x.FullName}");
    262             var instance = (IRoomCreationTemplate)Activator.CreateInstance(x);
    263             Presets[instance.Name] = instance.CreateRoomRequest;
    264         }
    265         Presets = Presets.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
    266 
    267         if (!Presets.ContainsKey("Default"))
    268         {
    269             Console.WriteLine($"No default room found in {Presets.Count} presets: {string.Join(", ", Presets.Keys)}");
    270         }
    271         else RoomPreset = "Default";
    272 
    273         await base.OnInitializedAsync();
    274     }
    275 
    276     private void JsonChanged()
    277     {
    278         Console.WriteLine(creationEvent.ToJson());
    279     }
    280 
    281 
    282     //wrappers
    283     private List<string> ServerACLAllowRules { get; set; } = new();
    284     private List<string> ServerACLDenyRules { get; set; } = new();
    285 
    286     private void OverwriteWrappedPropertiesFromEvent()
    287     {
    288         Console.WriteLine("Overwriting wrapped properties from event");
    289         ServerACLAllowRules = creationEvent.ServerACLs.Allow;
    290         ServerACLDenyRules = creationEvent.ServerACLs.Deny;
    291     }
    292 
    293     private async Task OverwriteWrappedProperties()
    294     {
    295         Console.WriteLine("Overwriting wrapped properties");
    296         Console.WriteLine($"Allow: {ServerACLAllowRules.Count}: {string.Join(", ", ServerACLAllowRules)}");
    297         Console.WriteLine($"Deny: {ServerACLDenyRules.Count}: {string.Join(", ", ServerACLDenyRules)}");
    298         creationEvent.ServerACLs = new()
    299         {
    300             Allow = ServerACLAllowRules,
    301             Deny = ServerACLDenyRules,
    302             AllowIpLiterals = creationEvent.ServerACLs.AllowIpLiterals
    303         };
    304 
    305         StateHasChanged();
    306     }
    307 
    308     private async Task RoomIconFilePicked(InputFileChangeEventArgs obj)
    309     {
    310         var res = await RuntimeCache.CurrentHomeServer.UploadFile(obj.File.Name, obj.File.OpenReadStream(), obj.File.ContentType);
    311         Console.WriteLine(res);
    312         creationEvent.RoomIcon = res;
    313         StateHasChanged();
    314     }
    315 
    316     private async Task CreateRoom()
    317     {
    318         Console.WriteLine("Create room");
    319         Console.WriteLine(creationEvent.ToJson());
    320         creationEvent.CreationContent.Add("rory.gay.created_using", "Rory&::MatrixRoomUtils (https://mru.rory.gay)");
    321     //creationEvent.CreationContent.Add();
    322         var id = await RuntimeCache.CurrentHomeServer.CreateRoom(creationEvent);
    323     // NavigationManager.NavigateTo($"/RoomManager/{id.RoomId.Replace('.','~')}");
    324     }
    325 
    326     private void InviteMember(string mxid)
    327     {
    328         if (!creationEvent.InitialState.Any(x => x.Type == "m.room.member" && x.StateKey == mxid) && RuntimeCache.CurrentHomeServer.UserId != mxid)
    329             creationEvent.InitialState.Add(new()
    330             {
    331                 Type = "m.room.member",
    332                 StateKey = mxid,
    333                 Content = new
    334                 {
    335                     membership = "invite",
    336                     reason = "Automatically invited at room creation time."
    337                 }
    338             });
    339     }
    340 
    341 
    342     private string GetStateFriendlyName(string key) => key switch {
    343         "m.room.history_visibility" => "History visibility",
    344         "m.room.guest_access" => "Guest access",
    345         "m.room.join_rules" => "Join rules",
    346         "m.room.server_acl" => "Server ACL",
    347         "m.room.avatar" => "Avatar",
    348         _ => key
    349         };
    350 
    351     private string GetPermissionFriendlyName(string key) => key switch {
    352         "m.reaction" => "Send reaction",
    353         "m.room.avatar" => "Change room icon",
    354         "m.room.canonical_alias" => "Change room alias",
    355         "m.room.encryption" => "Enable encryption",
    356         "m.room.history_visibility" => "Change history visibility",
    357         "m.room.name" => "Change room name",
    358         "m.room.power_levels" => "Change power levels",
    359         "m.room.tombstone" => "Upgrade room",
    360         "m.room.topic" => "Change room topic",
    361         "m.room.pinned_events" => "Pin events",
    362         "m.room.server_acl" => "Change server ACLs",
    363         _ => key
    364         };
    365 
    366     public class GuestAccessContent
    367     {
    368         [JsonPropertyName("guest_access")]
    369         public string GuestAccess { get; set; }
    370 
    371         public bool IsGuestAccessEnabled
    372         {
    373             get => GuestAccess == "can_join";
    374             set => GuestAccess = value ? "can_join" : "forbidden";
    375         }
    376     }
    377 
    378     
    379     }
    380 
    381