about summary refs log tree commit diff
path: root/MatrixUtils.Web/Pages/Rooms/Index.razor
diff options
context:
space:
mode:
Diffstat (limited to 'MatrixUtils.Web/Pages/Rooms/Index.razor')
-rw-r--r--MatrixUtils.Web/Pages/Rooms/Index.razor250
1 files changed, 250 insertions, 0 deletions
diff --git a/MatrixUtils.Web/Pages/Rooms/Index.razor b/MatrixUtils.Web/Pages/Rooms/Index.razor
new file mode 100644
index 0000000..0ec9487
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/Index.razor
@@ -0,0 +1,250 @@
+@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 MatrixUtils.Abstractions
+@inject ILogger<Index> logger
+<h3>Room list</h3>
+
+<p>@Status</p>
+<p>@Status2</p>
+
+<LinkButton href="/Rooms/Create">Create new room</LinkButton>
+
+<RoomList Rooms="Rooms" GlobalProfile="@GlobalProfile" @bind-StillFetching="RenderContents"></RoomList>
+
+@code {
+    private ObservableCollection<RoomInfo> 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<string> { "*" },
+            Limit = 1
+        },
+        Presence = new SyncFilter.EventFilter {
+            NotTypes = new List<string> { "*" },
+            Limit = 1
+        },
+        Room = new SyncFilter.RoomFilter {
+            AccountData = new SyncFilter.RoomFilter.StateFilter {
+                NotTypes = new List<string> { "*" },
+                Limit = 1
+            },
+            Ephemeral = new SyncFilter.RoomFilter.StateFilter {
+                NotTypes = new List<string> { "*" },
+                Limit = 1
+            },
+            State = new SyncFilter.RoomFilter.StateFilter {
+                Types = new List<string> {
+                    "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<string> { "*" },
+                Limit = 1
+            }
+        }
+    };
+
+    // private static SyncFilter profileUpdateFilter = new() {
+    //     AccountData = new SyncFilter.EventFilter {
+    //         NotTypes = new List<string> { "*" },
+    //         Limit = 1
+    //     },
+    //     Presence = new SyncFilter.EventFilter {
+    //         NotTypes = new List<string> { "*" },
+    //         Limit = 1
+    //     },
+    //     Room = new SyncFilter.RoomFilter {
+    //         AccountData = new SyncFilter.RoomFilter.StateFilter {
+    //             NotTypes = new List<string> { "*" },
+    //             Limit = 1
+    //         },
+    //         Ephemeral = new SyncFilter.RoomFilter.StateFilter {
+    //             NotTypes = new List<string> { "*" },
+    //             Limit = 1
+    //         },
+    //         State = new SyncFilter.RoomFilter.StateFilter {
+    //             Types = new List<string> {
+    //                 "m.room.member"
+    //             },
+    //             Senders = new()
+    //         },
+    //         Timeline = new SyncFilter.RoomFilter.StateFilter {
+    //             NotTypes = new List<string> { "*" },
+    //             Limit = 1
+    //         }
+    //     }
+    // };
+
+    private SyncHelper syncHelper;
+
+    // SyncHelper profileSyncHelper;
+
+    protected override async Task OnInitializedAsync() {
+        Homeserver = await RMUStorage.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<KeyValuePair<string, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure>> 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}";
+        }
+    }
+
+}
\ No newline at end of file