diff options
Diffstat (limited to 'MatrixUtils.Web')
-rw-r--r-- | MatrixUtils.Web/Pages/Index.razor | 32 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/Index.razor | 10 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/Index2.razor | 2 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2ByRoomTypeTab.razor | 53 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2SyncContainer.razor | 1 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Rooms/Timeline.razor | 44 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Tools/InviteCounter.razor | 73 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Tools/MassCMEBan.razor | 75 | ||||
-rw-r--r-- | MatrixUtils.Web/Pages/Tools/UserTrace.razor | 97 | ||||
-rw-r--r-- | MatrixUtils.Web/Shared/MainLayout.razor | 8 | ||||
-rw-r--r-- | MatrixUtils.Web/Shared/ResourceUsage.razor | 64 | ||||
-rw-r--r-- | MatrixUtils.Web/Shared/RoomListItem.razor | 2 | ||||
-rw-r--r-- | MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor | 4 | ||||
-rw-r--r-- | MatrixUtils.Web/Shared/TimelineComponents/TimelineUnknownStateItem.razor | 16 |
14 files changed, 402 insertions, 79 deletions
diff --git a/MatrixUtils.Web/Pages/Index.razor b/MatrixUtils.Web/Pages/Index.razor index 19c74c3..7c0a9f2 100644 --- a/MatrixUtils.Web/Pages/Index.razor +++ b/MatrixUtils.Web/Pages/Index.razor @@ -88,6 +88,35 @@ Small collection of tools to do not-so-everyday things. </form> } +@if (_invalidSessions.Count > 0) { + <br/> + <br/> + <h5>Invalid sessions</h5> + <hr/> + <form> + <table> + @foreach (var session in _invalidSessions) { + <tr class="user-entry"> + <td> + <p> + @{ + string[] parts = session.UserId.Split(':'); + } + <span>@parts[0][1..]</span> on <span>@parts[1]</span> + @if (!string.IsNullOrWhiteSpace(session.Proxy)) { + <span class="badge badge-info"> (proxied via @session.Proxy)</span> + } + </p> + </td> + <td> + <LinkButton OnClick="@(() => RemoveUser(session))">Remove</LinkButton> + </td> + </tr> + } + </table> + </form> +} + @code { #if DEBUG @@ -106,6 +135,7 @@ Small collection of tools to do not-so-everyday things. // private Dictionary<UserAuth, UserInfo> _users = new(); private readonly List<AuthInfo> _sessions = []; private readonly List<UserAuth> _offlineSessions = []; + private readonly List<UserAuth> _invalidSessions = []; private LoginResponse? _currentSession; int scannedSessions = 0, totalSessions = 1; private SvgIdenticonGenerator _identiconGenerator = new(); @@ -162,7 +192,7 @@ Small collection of tools to do not-so-everyday things. } } catch (MatrixException e) { - if (e is { ErrorCode: "M_UNKNOWN_TOKEN" }) _offlineSessions.Add(token); + if (e is { ErrorCode: "M_UNKNOWN_TOKEN" }) _invalidSessions.Add(token); else throw; } catch { diff --git a/MatrixUtils.Web/Pages/Rooms/Index.razor b/MatrixUtils.Web/Pages/Rooms/Index.razor index c44e23f..28c4de2 100644 --- a/MatrixUtils.Web/Pages/Rooms/Index.razor +++ b/MatrixUtils.Web/Pages/Rooms/Index.razor @@ -13,8 +13,9 @@ <p>@Status2</p> <LinkButton href="/Rooms/Create">Create new room</LinkButton> - -<RoomList Rooms="Rooms" GlobalProfile="@GlobalProfile" @bind-StillFetching="RenderContents"></RoomList> +<CascadingValue TValue="AuthenticatedHomeserverGeneric" Value="Homeserver"> + <RoomList Rooms="Rooms" GlobalProfile="@GlobalProfile" @bind-StillFetching="RenderContents"></RoomList> +</CascadingValue> @code { @@ -73,10 +74,9 @@ // SemaphoreSlim _semaphore = new(160, 160); GlobalProfile = await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId); - // var filter = await Homeserver.NamedCaches.FilterCache.GetOrSetValueAsync(CommonSyncFilters.GetBasicRoomInfo); var filter = await Homeserver.NamedCaches.FilterCache.GetOrSetValueAsync(CommonSyncFilters.GetBasicRoomInfo); var filterData = await Homeserver.GetFilterAsync(filter); - + // Rooms = new ObservableCollection<RoomInfo>(rooms.Select(room => new RoomInfo(room))); // foreach (var stateType in filterData.Room?.State?.Types ?? []) { // var tasks = Rooms.Select(async room => { @@ -204,7 +204,7 @@ if (sync is null) continue; var filter = await Homeserver.GetFilterAsync(syncHelper.FilterId); - + Status = $"Got sync with {sync.Rooms?.Join?.Count ?? 0} room updates, next batch: {sync.NextBatch}!"; if (sync?.Rooms?.Join != null) foreach (var joinedRoom in sync.Rooms.Join) diff --git a/MatrixUtils.Web/Pages/Rooms/Index2.razor b/MatrixUtils.Web/Pages/Rooms/Index2.razor index ae31126..98b8a1d 100644 --- a/MatrixUtils.Web/Pages/Rooms/Index2.razor +++ b/MatrixUtils.Web/Pages/Rooms/Index2.razor @@ -27,9 +27,11 @@ break; case Tab.DMs: <h3>DMs tab</h3> + <RoomsIndex2DMsTab></RoomsIndex2DMsTab> break; case Tab.ByRoomType: <h3>By room type tab</h3> + <RoomsIndex2ByRoomTypeTab></RoomsIndex2ByRoomTypeTab> break; default: throw new InvalidEnumArgumentException(); diff --git a/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2ByRoomTypeTab.razor b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2ByRoomTypeTab.razor new file mode 100644 index 0000000..f4cf849 --- /dev/null +++ b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2ByRoomTypeTab.razor @@ -0,0 +1,53 @@ +@using MatrixUtils.Abstractions +@using System.Security.Cryptography +@using ArcaneLibs.Extensions +<h3>RoomsIndex2MainTab</h3> + +<div> + <div class="row"> + <div class="col-3" style="background-color: #ffffff66;"> + <LinkButton>Uncategorised rooms</LinkButton> + @foreach (var space in Data.Rooms.Where(x => x.RoomType == "m.space")) { + <div style="@("width: 100%; height: 50px; background-color: #" + RandomNumberGenerator.GetBytes(3).Append((byte)0x11).ToArray().AsHexString().Replace(" ",""))"> + <p>@space.RoomName</p> + </div> + } + </div> + <div class="col-9" style="background-color: #ff00ff66;"> + <p>omae wa mou shindeiru</p> + </div> + </div> +</div> + +@code { + + [CascadingParameter] + public Index2.RoomListViewData Data { get; set; } = null!; + + protected override async Task OnInitializedAsync() { + Data.Rooms.CollectionChanged += (sender, args) => { + DebouncedStateHasChanged(); + if (args.NewItems is { Count: > 0 }) + foreach (var newItem in args.NewItems) { + (newItem as RoomInfo).PropertyChanged += (sender, args) => { DebouncedStateHasChanged(); }; + } + }; + await base.OnInitializedAsync(); + } + + //debounce StateHasChanged, we dont want to reredner on every key stroke + + private CancellationTokenSource _debounceCts = new CancellationTokenSource(); + + private async Task DebouncedStateHasChanged() { + _debounceCts.Cancel(); + _debounceCts = new CancellationTokenSource(); + try { + await Task.Delay(100, _debounceCts.Token); + Console.WriteLine("DebouncedStateHasChanged - Calling StateHasChanged!"); + StateHasChanged(); + } + catch (TaskCanceledException) { } + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2SyncContainer.razor b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2SyncContainer.razor index 1fb3f89..418ee02 100644 --- a/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2SyncContainer.razor +++ b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2SyncContainer.razor @@ -50,7 +50,6 @@ ["Main"] = new SyncHelper(Data.Homeserver, logger) { Timeout = 30000, FilterId = await Data.Homeserver.NamedCaches.FilterCache.GetOrSetValueAsync(CommonSyncFilters.GetBasicRoomInfo), - // FilterId = await Data.Homeserver.NamedCaches.FilterCache.GetOrSetValueAsync(CommonSyncFilters.GetBasicRoomInfo), // MinimumDelay = TimeSpan.FromMilliseconds(5000) } }; diff --git a/MatrixUtils.Web/Pages/Rooms/Timeline.razor b/MatrixUtils.Web/Pages/Rooms/Timeline.razor index 3886c5b..e6b1248 100644 --- a/MatrixUtils.Web/Pages/Rooms/Timeline.razor +++ b/MatrixUtils.Web/Pages/Rooms/Timeline.razor @@ -8,9 +8,9 @@ <p>Loaded @Events.Count events...</p> @foreach (var evt in Events) { - <div type="@evt.Type" key="@evt.StateKey" itemid="@evt.EventId"> - <DynamicComponent Type="@ComponentType(evt)" - Parameters="@(new Dictionary<string, object> { { "Event", evt }, { "Events", Events }, { "Homeserver", Homeserver!} })"> + <div type="@evt.Event.Type" key="@evt.Event.StateKey" itemid="@evt.Event.EventId"> + <DynamicComponent Type="@evt.Type" + Parameters="@(new Dictionary<string, object> { { "Event", evt.Event }, { "Events", RawEvents }, { "Homeserver", Homeserver! } })"> </DynamicComponent> </div> } @@ -20,8 +20,8 @@ [Parameter] public string RoomId { get; set; } - private List<MessagesResponse> Messages { get; } = new(); - private List<StateEventResponse> Events { get; } = new(); + private List<TimelineEventItem> Events { get; } = new(); + private List<StateEventResponse> RawEvents { get; } = new(); private AuthenticatedHomeserverGeneric? Homeserver { get; set; } @@ -32,28 +32,34 @@ var room = Homeserver.GetRoom(RoomId); MessagesResponse? msgs = null; do { - msgs = await room.GetMessagesAsync(limit: 1000, from: msgs?.End, dir: "b"); - Messages.Add(msgs); + msgs = await room.GetMessagesAsync(limit: 10000, from: msgs?.End, dir: "b"); Console.WriteLine($"Got {msgs.Chunk.Count} messages"); + StateHasChanged(); msgs.Chunk.Reverse(); - Events.InsertRange(0, msgs.Chunk); + Events.InsertRange(0, msgs.Chunk.Select(x => new TimelineEventItem { Event = x, Type = ComponentType(x) })); + RawEvents.InsertRange(0, msgs.Chunk); } while (msgs.End is not null); - await base.OnInitializedAsync(); } - private StateEventResponse GetProfileEventBefore(StateEventResponse Event) => Events.TakeWhile(x => x != Event).Last(e => e.Type == "m.room.member" && e.StateKey == Event.Sender); + // private StateEventResponse GetProfileEventBefore(StateEventResponse Event) => Events.TakeWhile(x => x != Event).Last(e => e.Type == RoomMemberEventContent.EventId && e.StateKey == Event.Sender); - private Type ComponentType(StateEvent Event) => Event.TypedContent switch { - RoomCanonicalAliasEventContent => typeof(TimelineCanonicalAliasItem), - RoomHistoryVisibilityEventContent => typeof(TimelineHistoryVisibilityItem), - RoomTopicEventContent => typeof(TimelineRoomTopicItem), - RoomMemberEventContent => typeof(TimelineMemberItem), - RoomMessageEventContent => typeof(TimelineMessageItem), - RoomCreateEventContent => typeof(TimelineRoomCreateItem), - RoomNameEventContent => typeof(TimelineRoomNameItem), + private Type ComponentType(StateEvent Event) => Event.Type switch { + RoomCanonicalAliasEventContent.EventId => typeof(TimelineCanonicalAliasItem), + RoomHistoryVisibilityEventContent.EventId => typeof(TimelineHistoryVisibilityItem), + RoomTopicEventContent.EventId => typeof(TimelineRoomTopicItem), + RoomMemberEventContent.EventId => typeof(TimelineMemberItem), + RoomMessageEventContent.EventId => typeof(TimelineMessageItem), + RoomCreateEventContent.EventId => typeof(TimelineRoomCreateItem), + RoomNameEventContent.EventId => typeof(TimelineRoomNameItem), + // RoomMessageReactionEventContent.EventId => typeof(ComponentBase), _ => typeof(TimelineUnknownItem) }; + + private class TimelineEventItem : ComponentBase { + public StateEventResponse Event { get; set; } + public Type Type { get; set; } + } -} +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Tools/InviteCounter.razor b/MatrixUtils.Web/Pages/Tools/InviteCounter.razor new file mode 100644 index 0000000..8f4b4dd --- /dev/null +++ b/MatrixUtils.Web/Pages/Tools/InviteCounter.razor @@ -0,0 +1,73 @@ +@page "/Tools/InviteCounter" +@using ArcaneLibs.Extensions +@using LibMatrix.RoomTypes +@using System.Collections.ObjectModel +@using LibMatrix +@using System.Collections.Frozen +@using LibMatrix.EventTypes.Spec.State +@using MatrixUtils.Abstractions +<h3>User Trace</h3> +<hr/> + +<br/> +<span>Room ID: </span> +<InputText @bind-Value="@roomId"></InputText> +<LinkButton OnClick="@Execute">Execute</LinkButton> + +<br/> + +<details> + <summary>Results</summary> + @foreach (var (userId, events) in invites.OrderByDescending(x=>x.Value).ToList()) { + <p>@userId: @events</p> + } +</details> + +<br/> +@foreach (var line in log.Reverse()) { + <pre>@line</pre> +} + +@code { + private ObservableCollection<string> log { get; set; } = new(); + private Dictionary<string, int> invites { get; set; } = new(); + private AuthenticatedHomeserverGeneric hs { get; set; } + + [Parameter, SupplyParameterFromQuery(Name = "room")] + public string roomId { get; set; } + + + protected override async Task OnInitializedAsync() { + log.CollectionChanged += (sender, args) => StateHasChanged(); + hs = await RMUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + + StateHasChanged(); + Console.WriteLine("Rerendered!"); + await base.OnInitializedAsync(); + } + + private async Task<string> Execute() { + var room = hs.GetRoom(roomId); + var events = room.GetManyMessagesAsync(limit: int.MaxValue); + await foreach (var resp in events) { + var all = resp.State.Concat(resp.Chunk); + foreach (var evt in all) { + if(evt.Type != RoomMemberEventContent.EventId) continue; + var content = evt.TypedContent as RoomMemberEventContent; + if(content.Membership != "invite") continue; + if(!invites.ContainsKey(evt.Sender)) invites[evt.Sender] = 0; + invites[evt.Sender]++; + } + + log.Add($"{resp.State.Count} state, {resp.Chunk.Count} timeline"); + } + + + + StateHasChanged(); + + return ""; + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Tools/MassCMEBan.razor b/MatrixUtils.Web/Pages/Tools/MassCMEBan.razor new file mode 100644 index 0000000..cbbca9e --- /dev/null +++ b/MatrixUtils.Web/Pages/Tools/MassCMEBan.razor @@ -0,0 +1,75 @@ +@page "/Tools/MassCMEBan" +@using ArcaneLibs.Extensions +@using LibMatrix.RoomTypes +@using System.Collections.ObjectModel +@using LibMatrix +@using System.Collections.Frozen +@using LibMatrix.EventTypes.Spec.State +@using LibMatrix.EventTypes.Spec.State.Policy +@using MatrixUtils.Abstractions +<h3>User Trace</h3> +<hr/> + +<br/> +<span>Users:</span> +<InputTextArea @bind-Value="@roomId"></InputTextArea> +<LinkButton OnClick="@Execute">Execute</LinkButton> + +<br/> + +<br/> +@foreach (var line in log.Reverse()) { + <pre>@line</pre> +} + +@code { + // TODO: Properly implement page to be more useful + private ObservableCollection<string> log { get; set; } = new(); + private AuthenticatedHomeserverGeneric hs { get; set; } + + [Parameter, SupplyParameterFromQuery(Name = "room")] + public string roomId { get; set; } + + + protected override async Task OnInitializedAsync() { + log.CollectionChanged += (sender, args) => StateHasChanged(); + hs = await RMUStorage.GetCurrentSessionOrNavigate(); + if (hs is null) return; + + StateHasChanged(); + Console.WriteLine("Rerendered!"); + await base.OnInitializedAsync(); + } + + private async Task<string> Execute() { + var room = hs.GetRoom("!fTjMjIzNKEsFlUIiru:neko.dev"); + // var room = hs.GetRoom("!yf7OpOiRDXx6zUGpT6:conduit.rory.gay"); + var users = roomId.Split("\n").Select(x => x.Trim()).Where(x=>x.StartsWith('@')).ToList(); + foreach (var user in users) { + var exists = false; + try { + exists = !string.IsNullOrWhiteSpace((await room.GetStateAsync<UserPolicyRuleEventContent>(UserPolicyRuleEventContent.EventId, user.Replace('@', '_'))).Entity); + } catch (Exception e) { + log.Add($"Failed to get {user}"); + } + + if (!exists) { + var evt = await room.SendStateEventAsync(UserPolicyRuleEventContent.EventId, user.Replace('@', '_'), new UserPolicyRuleEventContent() { + Entity = user, + Reason = "spam (invite)", + Recommendation = "m.ban" + }); + log.Add($"Sent {evt.EventId} to ban {user}"); + } + else { + log.Add($"User {user} already exists"); + } + } + + + StateHasChanged(); + + return ""; + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Pages/Tools/UserTrace.razor b/MatrixUtils.Web/Pages/Tools/UserTrace.razor index d78c58a..4ad9874 100644 --- a/MatrixUtils.Web/Pages/Tools/UserTrace.razor +++ b/MatrixUtils.Web/Pages/Tools/UserTrace.razor @@ -5,6 +5,7 @@ @using LibMatrix @using System.Collections.Frozen @using LibMatrix.EventTypes.Spec.State +@using MatrixUtils.Abstractions <h3>User Trace</h3> <hr/> @@ -16,7 +17,7 @@ <details> <summary>Rooms to be searched (@rooms.Count)</summary> @foreach (var room in rooms) { - <span>@room.RoomId</span> + <span>@room.Room.RoomId</span> <br/> } </details> @@ -29,8 +30,13 @@ @foreach (var (userId, events) in matches) { <h4>@userId</h4> <ul> - @foreach (var eventResponse in events) { - <li>@eventResponse.Room.RoomId</li> + @foreach (var match in events) { + <li> + <ul> + <li>@match.RoomName (<span>@match.Room.RoomId</span>)</li> + <li>Membership: @(match.Event.RawContent.ToJson(indent: false))</li> + </ul> + </li> } </ul> } @@ -43,10 +49,8 @@ @code { private ObservableCollection<string> log { get; set; } = new(); - List<AuthenticatedHomeserverGeneric> hss { get; set; } = new(); - ObservableCollection<GenericRoom> rooms { get; set; } = new(); - Dictionary<GenericRoom, FrozenSet<StateEventResponse>> roomMembers { get; set; } = new(); - Dictionary<string, List<Matches>> matches = new(); + List<RoomInfo> rooms { get; set; } = new(); + Dictionary<string, List<Match>> matches = new(); private string UserIdString { get => string.Join("\n", UserIDs); @@ -59,16 +63,13 @@ log.CollectionChanged += (sender, args) => StateHasChanged(); var hs = await RMUStorage.GetCurrentSessionOrNavigate(); if (hs is null) return; - rooms.CollectionChanged += (sender, args) => StateHasChanged(); var sessions = await RMUStorage.GetAllTokens(); + var baseRooms = new List<GenericRoom>(); foreach (var userAuth in sessions) { var session = await RMUStorage.GetSession(userAuth); if (session is not null) { - var sessionRooms = await session.GetJoinedRooms(); - foreach (var room in sessionRooms) { - rooms.Add(room); - } - + baseRooms.AddRange(await session.GetJoinedRooms()); + var sessionRooms = (await session.GetJoinedRooms()).Where(x => !rooms.Any(y => y.Room.RoomId == x.RoomId)).ToList(); StateHasChanged(); log.Add($"Got {sessionRooms.Count} rooms for {userAuth.UserId}"); } @@ -76,32 +77,28 @@ log.Add("Done fetching rooms!"); - var distinctRooms = rooms.DistinctBy(x => x.RoomId).ToArray(); - Random.Shared.Shuffle(distinctRooms); - rooms = new ObservableCollection<GenericRoom>(distinctRooms); - rooms.CollectionChanged += (sender, args) => StateHasChanged(); - try { - var stateTasks = rooms.Select(async x => { - for (int i = 0; i < 10; i++) { - try { - return (x, await x.GetMembersListAsync(false)); - } - catch { - // - } + baseRooms = baseRooms.DistinctBy(x => x.RoomId).ToList(); + + // rooms.CollectionChanged += (sender, args) => StateHasChanged(); + var tasks = baseRooms.Select(async newRoom => { + bool success = false; + while (!success) + try { + var state = await newRoom.GetFullStateAsListAsync(); + var newRoomInfo = new RoomInfo(newRoom, state); + rooms.Add(newRoomInfo); + log.Add($"Got {newRoomInfo.StateEvents.Count} events for {newRoomInfo.RoomName}"); + success = true; } - - return (x, new List<StateEventResponse>().ToFrozenSet()); - }).ToAsyncEnumerable(); - - await foreach (var (room, state) in stateTasks) { - roomMembers.Add(room, state); - log.Add($"Got {state.Count} members for {room.RoomId}..."); - } - } - catch { - // - } + catch (MatrixException e) { + log.Add($"Failed to fetch room {newRoom.RoomId}! {e}"); + throw; + } + catch (HttpRequestException e) { + log.Add($"Failed to fetch room {newRoom.RoomId}! {e}"); + } + }); + await Task.WhenAll(tasks); log.Add($"Done fetching members!"); @@ -114,17 +111,22 @@ private async Task<string> Execute() { foreach (var userId in UserIDs) { - matches.Add(userId, new List<Matches>()); - foreach (var (room, events) in roomMembers) { - if (events.Any(x => x.Type == RoomMemberEventContent.EventId && x.StateKey == userId)) { + matches.Add(userId, new List<Match>()); + + foreach (var room in rooms) { + var state = room.StateEvents.Where(x => x!.Type == RoomMemberEventContent.EventId).ToList(); + if (state!.Any(x => x.StateKey == userId)) { matches[userId].Add(new() { - Event = events.First(x => x.StateKey == userId && x.Type == RoomMemberEventContent.EventId), - Room = room, + Event = state.First(x => x.StateKey == userId), + Room = room.Room, + RoomName = room.RoomName ?? "No name" }); } } } + StateHasChanged(); + return ""; } @@ -133,8 +135,8 @@ private async Task DoImportFromRoomId() { try { if (ImportFromRoomId is null) return; - var room = rooms.FirstOrDefault(x => x.RoomId == ImportFromRoomId); - UserIdString = string.Join("\n", (await room.GetMembersListAsync()).Select(x => x.StateKey)); + var room = rooms.FirstOrDefault(x => x.Room.RoomId == ImportFromRoomId); + UserIdString = string.Join("\n", (await room.Room.GetMembersListAsync()).Select(x => x.StateKey)); } catch (Exception e) { Console.WriteLine(e); @@ -144,11 +146,10 @@ StateHasChanged(); } - private class Matches { + private class Match { public GenericRoom Room; - public StateEventResponse Event; - // public + public string RoomName { get; set; } } } \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/MainLayout.razor b/MatrixUtils.Web/Shared/MainLayout.razor index 41c3d69..c67f73c 100644 --- a/MatrixUtils.Web/Shared/MainLayout.razor +++ b/MatrixUtils.Web/Shared/MainLayout.razor @@ -1,4 +1,5 @@ -@inherits LayoutComponentBase +@using ArcaneLibs +@inherits LayoutComponentBase <div class="page"> <div class="sidebar"> @@ -7,7 +8,8 @@ <main> <div class="top-row px-4"> - <PortableDevTools></PortableDevTools> + @* <PortableDevTools/> *@ + @* <ResourceUsage/> *@ <a style="color: #ccc; text-decoration: underline" href="https://cgit.rory.gay/matrix/MatrixRoomUtils.git/" target="_blank">Git</a> <a style="color: #ccc; text-decoration: underline" href="https://matrix.to/#/%23mru%3Arory.gay?via=rory.gay&via=matrix.org&via=feline.support" target="_blank">Matrix</a> </div> @@ -15,6 +17,8 @@ <article class="Content px-4"> @Body </article> + + </main> </div> diff --git a/MatrixUtils.Web/Shared/ResourceUsage.razor b/MatrixUtils.Web/Shared/ResourceUsage.razor new file mode 100644 index 0000000..2a6365f --- /dev/null +++ b/MatrixUtils.Web/Shared/ResourceUsage.razor @@ -0,0 +1,64 @@ +@using ArcaneLibs +@using System.Diagnostics +<h3>ResourceUsage</h3> +<ModalWindow Title="Resource usage"> + <div style="background-color: white; color: black;"> + <span>Memory usage: @lastMemoryUsage</span> + <br/> + <TimelineGraph Data="MemoryUsage" ValueFormatter="@((double val) => Util.BytesToString((long)val))" Width="400"></TimelineGraph> + </div> + + <div style="background-color: white; color: black;"> + <span>Time jitter: @lastCpuJitter</span> + <br/> + <TimelineGraph Data="CpuUsage" ValueFormatter="@(val => TimeSpan.FromTicks((long)val).ToString())" Width="400"></TimelineGraph> + </div> +</ModalWindow> + +@code { + private Dictionary<DateTime, double> MemoryUsage = new(); + private Dictionary<DateTime, double> CpuUsage = new(); + private string lastMemoryUsage = ""; + private string lastCpuJitter = ""; + + protected override async Task OnInitializedAsync() { + Task.Run(async () => { + try { + while (true) { + lastMemoryUsage = Util.BytesToString((long)(MemoryUsage[DateTime.Now] = GC.GetTotalMemory(false))); + if (MemoryUsage.Count > 60) + MemoryUsage.Remove(MemoryUsage.Keys.First()); + await Task.Delay(1000); + } + } + catch (Exception e) { + Console.WriteLine(e); + } + }); + + // calculate cpu usage estimate without Process or PerformanceCounter + Task.Run(async () => { + try { + var sw = new Stopwatch(); + while (true) { + sw.Restart(); + await Task.Delay(1000); + sw.Stop(); + // CpuUsage[DateTime.Now] = sw.ElapsedTicks - TimeSpan.TicksPerSecond; + var usage = sw.Elapsed - TimeSpan.FromSeconds(1); + CpuUsage[DateTime.Now] = usage.Ticks - TimeSpan.TicksPerSecond; + lastCpuJitter = usage.ToString(); + if (CpuUsage.Count > 60) + CpuUsage.Remove(MemoryUsage.Keys.First()); + StateHasChanged(); + } + } + catch (Exception e) { + Console.WriteLine(e); + } + }); + + await base.OnInitializedAsync(); + } + +} \ No newline at end of file diff --git a/MatrixUtils.Web/Shared/RoomListItem.razor b/MatrixUtils.Web/Shared/RoomListItem.razor index 623a03a..5a33b65 100644 --- a/MatrixUtils.Web/Shared/RoomListItem.razor +++ b/MatrixUtils.Web/Shared/RoomListItem.razor @@ -11,7 +11,7 @@ <div class="roomListItem @(HasDangerousRoomVersion ? "dangerousRoomVersion" : HasOldRoomVersion ? "oldRoomVersion" : "")" id="@RoomInfo.Room.RoomId"> @if (OwnMemberState != null) { @* Class="@("avatar32" + (OwnMemberState?.AvatarUrl != GlobalProfile?.AvatarUrl ? " highlightChange" : "") + (ChildContent is not null ? " vcenter" : ""))" *@ - <MxcImage Circular="true" Height="32" Width="32" MxcUri="@(OwnMemberState.AvatarUrl ?? GlobalProfile.AvatarUrl)"/> + <MxcImage Homeserver="hs" Circular="true" Height="32" Width="32" MxcUri="@(OwnMemberState.AvatarUrl ?? GlobalProfile.AvatarUrl)"/> <span class="centerVertical border75 @(OwnMemberState?.AvatarUrl != GlobalProfile?.AvatarUrl ? "highlightChange" : "")"> @(OwnMemberState?.DisplayName ?? GlobalProfile?.DisplayName ?? "Loading...") </span> diff --git a/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor index c7bfd51..08aeffe 100644 --- a/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor +++ b/MatrixUtils.Web/Shared/TimelineComponents/BaseTimelineItem.razor @@ -14,9 +14,9 @@ [Parameter] public AuthenticatedHomeserverGeneric Homeserver { get; set; } - public List<StateEventResponse> EventsBefore => Events.TakeWhile(e => e.EventId != Event.EventId).ToList(); + public IEnumerable<StateEventResponse> EventsBefore => Events.TakeWhile(e => e.EventId != Event.EventId); - public List<StateEventResponse> MatchingEventsBefore => EventsBefore.Where(x => x.Type == Event.Type && x.StateKey == Event.StateKey).ToList(); + public IEnumerable<StateEventResponse> MatchingEventsBefore => EventsBefore.Where(x => x.Type == Event.Type && x.StateKey == Event.StateKey); public StateEventResponse? PreviousState => MatchingEventsBefore.LastOrDefault(); diff --git a/MatrixUtils.Web/Shared/TimelineComponents/TimelineUnknownStateItem.razor b/MatrixUtils.Web/Shared/TimelineComponents/TimelineUnknownStateItem.razor new file mode 100644 index 0000000..4f05b30 --- /dev/null +++ b/MatrixUtils.Web/Shared/TimelineComponents/TimelineUnknownStateItem.razor @@ -0,0 +1,16 @@ +@using ArcaneLibs.Extensions +@inherits BaseTimelineItem + +<div> + <details style="display: inline;"> + <summary> + <i style="color: red;">Unknown event type: <pre style="display: inline;">@Event.Type</pre></i> + </summary> + <pre>@Event.ToJson(ignoreNull: true)</pre> + </details> +</div> + +@code { + + +} |