@page "/Rooms/{RoomId}/Policies" @using LibMatrix @using ArcaneLibs.Extensions @using LibMatrix.EventTypes.Spec.State @using LibMatrix.EventTypes.Spec.State.Policy @using System.Diagnostics @using LibMatrix.RoomTypes @using System.Collections.Frozen @using System.Reflection @using ArcaneLibs.Attributes @using LibMatrix.EventTypes @using MatrixUtils.Web.Shared.PolicyEditorComponents

Policy list editor - Editing @RoomId


@* *@ Create new policy @if (Loading) {

Loading...

} else if (PolicyEventsByType is not { Count: > 0 }) {

No policies yet

} else { @foreach (var (type, value) in PolicyEventsByType) {

@(GetValidPolicyEventsByType(type).Count) active, @(GetInvalidPolicyEventsByType(type).Count) invalid (@value.Count total) @(GetPolicyTypeName(type).ToLower())

} @foreach (var type in KnownPolicyTypes.OrderByDescending(t => GetPolicyEventsByType(t).Count)) {
@($"{GetPolicyTypeName(type)}: {GetPolicyEventsByType(type).Count} policies")
@{ var policies = GetValidPolicyEventsByType(type); var invalidPolicies = GetInvalidPolicyEventsByType(type); // enumerate all properties with friendly name var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => (x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyNameOrNull()) is not null) .Where(x => x.GetCustomAttribute() is null) .ToFrozenSet(); var propNames = props.Select(x => x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyName()!).ToFrozenSet(); } @foreach (var name in propNames) { } @foreach (var policy in policies.OrderBy(x => x.RawContent?["entity"]?.GetValue())) { @{ var typedContent = policy.TypedContent!; var proxySafeProps = typedContent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(x => props.Any(y => y.Name == x.Name)) .ToFrozenSet(); Console.WriteLine($"{proxySafeProps?.Count} proxy safe props found in {policies.FirstOrDefault()?.TypedContent?.GetType()}"); } @foreach (var prop in proxySafeProps ?? Enumerable.Empty()) { } }
@nameActions
@prop.GetGetMethod()?.Invoke(typedContent, null)
@if (PowerLevels.UserHasStatePermission(Homeserver.WhoAmI.UserId, policy.Type)) { Edit Remove @if (policy.IsLegacyType) { Update policy type } }
@("Invalid " + GetPolicyTypeName(type).ToLower()) @foreach (var policy in invalidPolicies) { }
State key Json contents
@policy.StateKey
@policy.RawContent.ToJson(true, false)
} } @if (CurrentlyEditingEvent is not null) { } @code { #if DEBUG private const bool Debug = true; #else private const bool Debug = false; #endif private bool Loading { get; set; } = true; //get room list // - sync withroom list filter // Type = support.feline.msc3784 //support.feline.policy.lists.msc.v1 [Parameter] public string RoomId { get; set; } = null!; private bool _enableAvatars; private StateEventResponse? _currentlyEditingEvent; // static readonly Dictionary Avatars = new(); // static readonly Dictionary Servers = new(); // private static List PolicyEvents { get; set; } = new(); private Dictionary> PolicyEventsByType { get; set; } = new(); private StateEventResponse? CurrentlyEditingEvent { get => _currentlyEditingEvent; set { _currentlyEditingEvent = value; StateHasChanged(); } } // public bool EnableAvatars { // get => _enableAvatars; // set { // _enableAvatars = value; // if (value) GetAllAvatars(); // } // } private AuthenticatedHomeserverGeneric Homeserver { get; set; } private GenericRoom Room { get; set; } private RoomPowerLevelEventContent PowerLevels { get; set; } protected override async Task OnInitializedAsync() { var sw = Stopwatch.StartNew(); await base.OnInitializedAsync(); Homeserver = (await RMUStorage.GetCurrentSessionOrNavigate())!; if (Homeserver is null) return; Room = Homeserver.GetRoom(RoomId!); PowerLevels = (await Room.GetPowerLevelsAsync())!; await LoadStatesAsync(); Console.WriteLine($"Policy list editor initialized in {sw.Elapsed}!"); } private async Task LoadStatesAsync() { Loading = true; var states = Room.GetFullStateAsync(); PolicyEventsByType.Clear(); await foreach (var state in states) { if (state is null) continue; if (!state.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue; if (!PolicyEventsByType.ContainsKey(state.MappedType)) PolicyEventsByType.Add(state.MappedType, new()); PolicyEventsByType[state.MappedType].Add(state); } Loading = false; StateHasChanged(); } // private async Task GetAllAvatars() { // // if (!_enableAvatars) return; // Console.WriteLine("Getting avatars..."); // var users = GetValidPolicyEventsByType(typeof(UserPolicyRuleEventContent)).Select(x => x.RawContent!["entity"]!.GetValue()).Where(x => x.Contains(':') && !x.Contains("*")).ToList(); // Console.WriteLine($"Got {users.Count} users!"); // var usersByHomeServer = users.GroupBy(x => x!.Split(':')[1]).ToDictionary(x => x.Key!, x => x.ToList()); // Console.WriteLine($"Got {usersByHomeServer.Count} homeservers!"); // var homeserverTasks = usersByHomeServer.Keys.Select(x => RemoteHomeserver.TryCreate(x)).ToAsyncEnumerable(); // await foreach (var server in homeserverTasks) { // if (server is null) continue; // var profileTasks = usersByHomeServer[server.BaseUrl].Select(x => TryGetProfile(server, x)).ToList(); // await Task.WhenAll(profileTasks); // profileTasks.RemoveAll(x => x.Result is not { Value: { AvatarUrl: not null } }); // foreach (var profile in profileTasks.Select(x => x.Result!.Value)) { // // if (profile is null) continue; // if (!string.IsNullOrWhiteSpace(profile.Value.AvatarUrl)) { // var url = await hsResolver.ResolveMediaUri(server.BaseUrl, profile.Value.AvatarUrl); // Avatars.TryAdd(profile.Key, url); // } // else Avatars.TryAdd(profile.Key, null); // } // // StateHasChanged(); // } // } // // private async Task?> TryGetProfile(RemoteHomeserver server, string mxid) { // try { // return new KeyValuePair(mxid, await server.GetProfileAsync(mxid)); // } // catch { // return null; // } // } private List GetPolicyEventsByType(Type type) => PolicyEventsByType.ContainsKey(type) ? PolicyEventsByType[type] : []; private List GetValidPolicyEventsByType(Type type) => GetPolicyEventsByType(type) .Where(x => !string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList(); private List GetInvalidPolicyEventsByType(Type type) => GetPolicyEventsByType(type) .Where(x => string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList(); private string? GetPolicyTypeNameOrNull(Type type) => type.GetFriendlyNamePluralOrNull() ?? type.GetCustomAttributes() .FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.EventName))?.EventName; private string GetPolicyTypeName(Type type) => GetPolicyTypeNameOrNull(type) ?? type.Name; private async Task RemovePolicyAsync(StateEventResponse policyEvent) { await Room.SendStateEventAsync(policyEvent.Type, policyEvent.StateKey, new { }); PolicyEventsByType[policyEvent.MappedType].Remove(policyEvent); await LoadStatesAsync(); } private async Task UpdatePolicyAsync(StateEventResponse policyEvent) { await Room.SendStateEventAsync(policyEvent.Type, policyEvent.StateKey, policyEvent.RawContent); CurrentlyEditingEvent = null; await LoadStatesAsync(); } private async Task UpgradePolicyAsync(StateEventResponse policyEvent) { policyEvent.RawContent["upgraded_from_type"] = policyEvent.Type; await LoadStatesAsync(); } private static FrozenSet KnownPolicyTypes = StateEvent.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))).ToFrozenSet(); // event types, unnamed private static Dictionary PolicyTypes = KnownPolicyTypes .ToDictionary(x => x.GetCustomAttributes().First(y => !string.IsNullOrWhiteSpace(y.EventName)).EventName, x => x); }