@page "/Rooms" @using LibMatrix.Filters @using LibMatrix.Helpers @using LibMatrix.Extensions @using LibMatrix.Responses @using System.Collections.ObjectModel @using System.Diagnostics @using ArcaneLibs.Extensions @using MatrixRoomUtils.Abstractions @inject ILogger logger

Room list

@Status

@Status2

Create new room @code { private ObservableCollection Rooms { get; } = new(); private UserProfileResponse GlobalProfile { get; set; } private AuthenticatedHomeserverGeneric? Homeserver { get; set; } private static SyncFilter filter = new() { AccountData = new SyncFilter.EventFilter { NotTypes = new List { "*" }, Limit = 1 }, Presence = new SyncFilter.EventFilter { NotTypes = new List { "*" }, Limit = 1 }, Room = new SyncFilter.RoomFilter { AccountData = new SyncFilter.RoomFilter.StateFilter { NotTypes = new List { "*" }, Limit = 1 }, Ephemeral = new SyncFilter.RoomFilter.StateFilter { NotTypes = new List { "*" }, Limit = 1 }, State = new SyncFilter.RoomFilter.StateFilter { Types = new List { "m.room.create", "m.room.name", "m.room.avatar", "org.matrix.mjolnir.shortcode", "m.room.power_levels", } }, Timeline = new SyncFilter.RoomFilter.StateFilter { NotTypes = new List { "*" }, Limit = 1 } } }; // private static SyncFilter profileUpdateFilter = new() { // AccountData = new SyncFilter.EventFilter { // NotTypes = new List { "*" }, // Limit = 1 // }, // Presence = new SyncFilter.EventFilter { // NotTypes = new List { "*" }, // Limit = 1 // }, // Room = new SyncFilter.RoomFilter { // AccountData = new SyncFilter.RoomFilter.StateFilter { // NotTypes = new List { "*" }, // Limit = 1 // }, // Ephemeral = new SyncFilter.RoomFilter.StateFilter { // NotTypes = new List { "*" }, // Limit = 1 // }, // State = new SyncFilter.RoomFilter.StateFilter { // Types = new List { // "m.room.member" // }, // Senders = new() // }, // Timeline = new SyncFilter.RoomFilter.StateFilter { // NotTypes = new List { "*" }, // Limit = 1 // } // } // }; private SyncHelper syncHelper; // SyncHelper profileSyncHelper; protected override async Task OnInitializedAsync() { Homeserver = await MRUStorage.GetCurrentSessionOrNavigate(); if (Homeserver is null) return; var rooms = await Homeserver.GetJoinedRooms(); // SemaphoreSlim _semaphore = new(160, 160); var roomTasks = rooms.Select(async room => { RoomInfo ri; // await _semaphore.WaitAsync(); ri = new() { Room = room }; await Task.WhenAll((filter.Room?.State?.Types ?? []).Select(x => ri.GetStateEvent(x))); return ri; }).ToAsyncEnumerable(); await foreach (var room in roomTasks) { Rooms.Add(room); StateHasChanged(); // await Task.Delay(50); // _semaphore.Release(); } if (rooms.Count >= 150) RenderContents = true; GlobalProfile = await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId); syncHelper = new SyncHelper(Homeserver, logger) { Timeout = 30000, Filter = filter, MinimumDelay = TimeSpan.FromMilliseconds(5000) }; // profileSyncHelper = new SyncHelper(Homeserver, logger) { // Timeout = 10000, // Filter = profileUpdateFilter, // MinimumDelay = TimeSpan.FromMilliseconds(5000) // }; // profileUpdateFilter.Room.State.Senders.Add(Homeserver.WhoAmI.UserId); RunSyncLoop(syncHelper); // RunSyncLoop(profileSyncHelper); RunQueueProcessor(); await base.OnInitializedAsync(); } private async Task RunQueueProcessor() { var renderTimeSw = Stopwatch.StartNew(); var isInitialSync = true; while (true) { try { while (queue.Count == 0) { Console.WriteLine("Queue is empty, waiting..."); await Task.Delay(isInitialSync ? 100 : 2500); } Console.WriteLine($"Queue no longer empty after {renderTimeSw.Elapsed}!"); int maxUpdates = 10; isInitialSync = false; while (maxUpdates-- > 0 && queue.TryDequeue(out var queueEntry)) { var (roomId, roomData) = queueEntry; Console.WriteLine($"Dequeued room {roomId}"); RoomInfo room; if (Rooms.Any(x => x.Room.RoomId == roomId)) { room = Rooms.First(x => x.Room.RoomId == roomId); Console.WriteLine($"QueueWorker: {roomId} already known with {room.StateEvents?.Count ?? 0} state events"); } else { Console.WriteLine($"QueueWorker: encountered new room {roomId}!"); room = new RoomInfo() { Room = Homeserver.GetRoom(roomId) }; Rooms.Add(room); } if (room.StateEvents is null) { Console.WriteLine($"QueueWorker: {roomId} does not have state events on record?"); throw new InvalidDataException("Somehow this is null???"); } if (roomData.State?.Events is { Count: > 0 }) room.StateEvents.MergeStateEventLists(roomData.State.Events); else { Console.WriteLine($"QueueWorker: could not merge state for {room.Room.RoomId} as new data contains no state events!"); } } Console.WriteLine($"QueueWorker: {queue.Count} entries left in queue, {maxUpdates} maxUpdates left, RenderContents: {RenderContents}"); Status = $"Got {Rooms.Count} rooms so far! {queue.Count} entries in processing queue..."; RenderContents |= queue.Count == 0; await Task.Delay(Rooms.Count); } catch (Exception e) { Console.WriteLine("QueueWorker exception: " + e); } } } private bool RenderContents { get; set; } = false; private string _status; public string Status { get => _status; set { _status = value; StateHasChanged(); } } private string _status2; public string Status2 { get => _status2; set { _status2 = value; StateHasChanged(); } } private Queue> queue = new(); private async Task RunSyncLoop(SyncHelper syncHelper) { Status = "Initial syncing..."; Console.WriteLine("starting sync"); var syncs = syncHelper.EnumerateSyncAsync(); await foreach (var sync in syncs) { Console.WriteLine("trying sync"); if (sync is null) continue; 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) if ( /*joinedRoom.Value.AccountData?.Events?.Count > 0 ||*/ joinedRoom.Value.State?.Events?.Count > 0) { joinedRoom.Value.State.Events.RemoveAll(x => x.Type == "m.room.member" && x.StateKey != Homeserver.WhoAmI?.UserId); // We can't trust servers to give us what we ask for, and this ruins performance // Thanks, Conduit. joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.Types?.Contains(x.Type) == false); if (filter.Room?.State?.NotSenders?.Any() ?? false) joinedRoom.Value.State.Events.RemoveAll(x => filter.Room?.State?.NotSenders?.Contains(x.Sender) ?? false); queue.Enqueue(joinedRoom); } if (sync.Rooms.Leave is {Count: > 0}) foreach (var leftRoom in sync.Rooms.Leave) if (Rooms.Any(x => x.Room.RoomId == leftRoom.Key)) Rooms.Remove(Rooms.First(x => x.Room.RoomId == leftRoom.Key)); Status = $"Got {Rooms.Count} rooms so far! {queue.Count} entries in processing queue... " + $"{sync?.Rooms?.Join?.Count ?? 0} new updates!"; Status2 = $"Next batch: {sync.NextBatch}"; } } }