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