diff --git a/MatrixUtils.Web/Pages/Dev/DevUtilities.razor b/MatrixUtils.Web/Pages/Dev/DevUtilities.razor
index 611d4c1..87416a2 100644
--- a/MatrixUtils.Web/Pages/Dev/DevUtilities.razor
+++ b/MatrixUtils.Web/Pages/Dev/DevUtilities.razor
@@ -12,9 +12,9 @@
else {
<details>
<summary>Room List</summary>
- @foreach (var room in Rooms) {
- <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@room.Replace('.', '~')">
- <RoomListItem RoomInfo="@(new RoomInfo() { Room = hs.GetRoom(room) })" LoadData="true"></RoomListItem>
+ @foreach (var roomId in Rooms) {
+ <a style="color: unset; text-decoration: unset;" href="/RoomStateViewer/@roomId.Replace('.', '~')">
+ <RoomListItem RoomInfo="@(new RoomInfo(hs.GetRoom(roomId)))" LoadData="true"></RoomListItem>
</a>
}
</details>
diff --git a/MatrixUtils.Web/Pages/HSEInit.razor b/MatrixUtils.Web/Pages/HSEInit.razor
index 3020ff7..b2fc0db 100644
--- a/MatrixUtils.Web/Pages/HSEInit.razor
+++ b/MatrixUtils.Web/Pages/HSEInit.razor
@@ -6,7 +6,7 @@
@code {
protected override async Task OnInitializedAsync() {
await base.OnInitializedAsync();
- var tasks = Enumerable.Range(0, 5000).Select(i => Login()).ToList();
+ var tasks = Enumerable.Range(0, 50).Select(i => Login()).ToList();
await Task.WhenAll(tasks);
Console.WriteLine("All logins complete!");
var userAuths = tasks.Select(t => t.Result).Where(t => t != null).ToList();
diff --git a/MatrixUtils.Web/Pages/Index.razor b/MatrixUtils.Web/Pages/Index.razor
index 0c0c87a..f216488 100644
--- a/MatrixUtils.Web/Pages/Index.razor
+++ b/MatrixUtils.Web/Pages/Index.razor
@@ -3,6 +3,8 @@
@using LibMatrix.Responses
@using LibMatrix
@using ArcaneLibs.Extensions
+@using ArcaneLibs
+@using System.Diagnostics
<PageTitle>Index</PageTitle>
@@ -12,7 +14,10 @@ Small collection of tools to do not-so-everyday things.
<br/><br/>
<h5>@totalSessions signed in sessions - <a href="/Login">Add new account</a></h5>
@if (scannedSessions != totalSessions) {
- <progress max="@totalSessions" value="@scannedSessions"></progress>
+ <span>
+ <span>@scannedSessions/@totalSessions</span>
+ <progress max="@totalSessions" value="@scannedSessions"></progress>
+ </span>
}
<hr/>
<form>
@@ -103,6 +108,7 @@ Small collection of tools to do not-so-everyday things.
private readonly List<UserAuth> _offlineSessions = [];
private LoginResponse? _currentSession;
int scannedSessions = 0, totalSessions = 1;
+ private SvgIdenticonGenerator _identiconGenerator = new();
protected override async Task OnInitializedAsync() {
Console.WriteLine("Index.OnInitializedAsync");
@@ -124,6 +130,7 @@ Small collection of tools to do not-so-everyday things.
List<string> offlineServers = [];
var sema = new SemaphoreSlim(64, 64);
+ var updateSw = Stopwatch.StartNew();
var tasks = tokens.Select(async token => {
await sema.WaitAsync();
scannedSessions++;
@@ -141,7 +148,7 @@ Small collection of tools to do not-so-everyday things.
var serverVersionTask = hs.FederationClient?.GetServerVersionAsync();
_sessions.Add(new() {
UserInfo = new() {
- AvatarUrl = "/blobfox_outage.gif",
+ AvatarUrl = string.IsNullOrWhiteSpace((await profileTask).AvatarUrl) ? _identiconGenerator.GenerateAsDataUri(hs.WhoAmI.UserId) : hs.ResolveMediaUri((await profileTask).AvatarUrl),
RoomCount = (await joinedRoomsTask).Count,
DisplayName = (await profileTask).DisplayName ?? hs.WhoAmI.UserId
},
@@ -149,6 +156,10 @@ Small collection of tools to do not-so-everyday things.
ServerVersion = await (serverVersionTask ?? Task.FromResult<ServerVersionResponse?>(null)!),
Homeserver = hs
});
+ if (updateSw.ElapsedMilliseconds > 250) {
+ updateSw.Restart();
+ StateHasChanged();
+ }
}
catch (MatrixException e) {
if (e is { ErrorCode: "M_UNKNOWN_TOKEN" }) _offlineSessions.Add(token);
@@ -166,50 +177,9 @@ Small collection of tools to do not-so-everyday things.
}
sema.Release();
-
- StateHasChanged();
}).ToList();
await Task.WhenAll(tasks);
-
- // var profileTasks = tokens.Select(async token => {
- // UserInfo userInfo = new();
- // AuthenticatedHomeserverGeneric hs;
- // Console.WriteLine($"Getting hs for {token.ToJson()}");
- // try {
- // hs = await hsProvider.GetAuthenticatedWithToken(token.Homeserver, token.AccessToken, token.Proxy);
- // }
- // catch (MatrixException e) {
- // if (e.ErrorCode != "M_UNKNOWN_TOKEN") throw;
- // _offlineSessions.Add(token);
- // return;
- // NavigationManager.NavigateTo("/InvalidSession?ctx=" + token.AccessToken);
- // }
- // catch (Exception e) {
- // logger.LogError(e, $"Failed to instantiate AuthenticatedHomeserver for {token.ToJson()}, homeserver may be offline?", token.UserId);
- // _offlineSessions.Add(token);
- // return;
- // }
- //
- // Console.WriteLine($"Got hs for {token.ToJson()}");
- //
- // var roomCountTask = hs.GetJoinedRooms();
- // var profile = await hs.GetProfileAsync(hs.WhoAmI.UserId);
- // userInfo.DisplayName = profile.DisplayName ?? hs.WhoAmI.UserId;
- // Console.WriteLine(profile.ToJson());
- // _sessions.Add(new() {
- // UserInfo = new() {
- // AvatarUrl = string.IsNullOrWhiteSpace(profile.AvatarUrl) ? "/blobfox_outage.gif" : hs.ResolveMediaUri(profile.AvatarUrl),
- // RoomCount = (await roomCountTask).Count,
- // DisplayName = profile.DisplayName ?? hs.WhoAmI.UserId
- // },
- // UserAuth = token,
- // ServerVersion = await (hs.FederationClient?.GetServerVersionAsync() ?? Task.FromResult<ServerVersionResponse?>(null)),
- // Homeserver = hs
- // });
- // }).ToList();
- // Console.WriteLine($"Waiting for {profileTasks.Count} profile tasks");
- // await Task.WhenAll(profileTasks);
- // Console.WriteLine("Done waiting for profile tasks");
+
await base.OnInitializedAsync();
}
diff --git a/MatrixUtils.Web/Pages/Moderation/DraupnirProtectedRoomsEditor.razor b/MatrixUtils.Web/Pages/Moderation/DraupnirProtectedRoomsEditor.razor
index 3cb9e40..f9cbfa2 100644
--- a/MatrixUtils.Web/Pages/Moderation/DraupnirProtectedRoomsEditor.razor
+++ b/MatrixUtils.Web/Pages/Moderation/DraupnirProtectedRoomsEditor.razor
@@ -59,7 +59,7 @@
if (hs is null) return;
data = await hs.GetAccountDataAsync<DraupnirProtectedRoomsData>("org.matrix.mjolnir.protected_rooms");
StateHasChanged();
- foreach (var room in await hs.GetJoinedRooms()) {
+ var tasks = (await hs.GetJoinedRooms()).Select(async room => {
var plTask = room.GetPowerLevelsAsync();
var roomNameTask = room.GetNameOrFallbackAsync();
var EditorRoomInfo = new EditorRoomInfo {
@@ -71,7 +71,11 @@
Rooms.Add(EditorRoomInfo);
StateHasChanged();
- }
+ return Task.CompletedTask;
+ }).ToList();
+ await Task.WhenAll(tasks);
+ await Task.Delay(500);
+ StateHasChanged();
}
private class DraupnirProtectedRoomsData {
@@ -87,7 +91,7 @@
}
private async Task Apply() {
- Console.WriteLine(string.Join('\n', Rooms.Where(x=>x.IsProtected).Select(x=>x.Room.RoomId)));
+ Console.WriteLine(string.Join('\n', Rooms.Where(x => x.IsProtected).Select(x => x.Room.RoomId)));
data.Rooms = Rooms.Where(x => x.IsProtected).Select(x => x.Room.RoomId).ToList();
await hs.SetAccountDataAsync("org.matrix.mjolnir.protected_rooms", data);
}
diff --git a/MatrixUtils.Web/Pages/Moderation/UserRoomHistory.razor b/MatrixUtils.Web/Pages/Moderation/UserRoomHistory.razor
index 775361a..9218c8c 100644
--- a/MatrixUtils.Web/Pages/Moderation/UserRoomHistory.razor
+++ b/MatrixUtils.Web/Pages/Moderation/UserRoomHistory.razor
@@ -82,9 +82,7 @@ else {
if (state is null) continue;
if (!matchingStates.ContainsKey(state.Membership))
matchingStates.Add(state.Membership, new());
- var roomInfo = new RoomInfo() {
- Room = room
- };
+ var roomInfo = new RoomInfo(room);
matchingStates[state.Membership].Add(roomInfo);
roomInfo.StateEvents.Add(new() {
Type = RoomNameEventContent.EventId,
diff --git a/MatrixUtils.Web/Pages/Rooms/Index.razor b/MatrixUtils.Web/Pages/Rooms/Index.razor
index 1813908..d7a3569 100644
--- a/MatrixUtils.Web/Pages/Rooms/Index.razor
+++ b/MatrixUtils.Web/Pages/Rooms/Index.razor
@@ -69,14 +69,14 @@
protected override async Task OnInitializedAsync() {
Homeserver = await RMUStorage.GetCurrentSessionOrNavigate();
if (Homeserver is null) return;
- var rooms = await Homeserver.GetJoinedRooms();
+ // var rooms = await Homeserver.GetJoinedRooms();
// SemaphoreSlim _semaphore = new(160, 160);
GlobalProfile = await Homeserver.GetProfileAsync(Homeserver.WhoAmI.UserId);
var filter = await Homeserver.GetOrUploadNamedFilterIdAsync(CommonSyncFilters.GetBasicRoomInfo);
var filterData = await Homeserver.GetFilterAsync(filter);
- Rooms = new ObservableCollection<RoomInfo>(rooms.Select(x => new RoomInfo() { Room = x }));
+ // Rooms = new ObservableCollection<RoomInfo>(rooms.Select(room => new RoomInfo(room)));
// foreach (var stateType in filterData.Room?.State?.Types ?? []) {
// var tasks = Rooms.Select(async room => {
// try {
@@ -126,7 +126,7 @@
Console.WriteLine($"Queue no longer empty after {renderTimeSw.Elapsed}!");
- int maxUpdates = 50;
+ int maxUpdates = 50000;
isInitialSync = false;
while (maxUpdates-- > 0 && queue.TryDequeue(out var queueEntry)) {
var (roomId, roomData) = queueEntry;
@@ -139,9 +139,7 @@
}
else {
Console.WriteLine($"QueueWorker: encountered new room {roomId}!");
- room = new RoomInfo() {
- Room = Homeserver.GetRoom(roomId)
- };
+ room = new RoomInfo(Homeserver.GetRoom(roomId), roomData.State?.Events);
Rooms.Add(room);
}
@@ -156,14 +154,14 @@
Console.WriteLine($"QueueWorker: could not merge state for {room.Room.RoomId} as new data contains no state events!");
}
- await Task.Delay(100);
+ // await Task.Delay(100);
}
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);
+ // RenderContents |= queue.Count == 0;
+ // await Task.Delay(Rooms.Count);
}
catch (Exception e) {
Console.WriteLine("QueueWorker exception: " + e);
diff --git a/MatrixUtils.Web/Pages/Rooms/Index2.razor b/MatrixUtils.Web/Pages/Rooms/Index2.razor
new file mode 100644
index 0000000..ae31126
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/Index2.razor
@@ -0,0 +1,85 @@
+@page "/Rooms2"
+@using LibMatrix.Responses
+@using System.Collections.ObjectModel
+@using System.ComponentModel
+@using MatrixUtils.Abstractions
+@using MatrixUtils.Web.Pages.Rooms.Index2Components
+@inject ILogger<Index> logger
+<h3>Room list</h3>
+
+<RoomsIndex2SyncContainer Data="@Data"></RoomsIndex2SyncContainer>
+@if (Data.Homeserver is null || Data.GlobalProfile is null) {
+ <p>Creating homeserver instance and fetching global profile...</p>
+ return;
+}
+
+<div>
+ <LinkButton Color="@(SelectedTab == Tab.Main ? null : "#0b0e62")" OnClick="() => Task.FromResult(SelectedTab = Tab.Main)">Main</LinkButton>
+ <LinkButton Color="@(SelectedTab == Tab.DMs ? null : "#0b0e62")" OnClick="() => Task.FromResult(SelectedTab = Tab.DMs)">DMs</LinkButton>
+ <LinkButton Color="@(SelectedTab == Tab.ByRoomType ? null : "#0b0e62")" OnClick="() => Task.FromResult(SelectedTab = Tab.ByRoomType)">By room type</LinkButton>
+</div>
+<br/>
+<CascadingValue Value="@Data">
+ @switch (SelectedTab) {
+ case Tab.Main:
+ <h3>Main tab</h3>
+ <RoomsIndex2MainTab></RoomsIndex2MainTab>
+ break;
+ case Tab.DMs:
+ <h3>DMs tab</h3>
+ break;
+ case Tab.ByRoomType:
+ <h3>By room type tab</h3>
+ break;
+ default:
+ throw new InvalidEnumArgumentException();
+ }
+</CascadingValue>
+<br/>
+
+@* <LinkButton href="/Rooms/Create">Create new room</LinkButton> *@
+
+
+@code {
+
+ private Tab SelectedTab {
+ get => _selectedTab;
+ set {
+ _selectedTab = value;
+ StateHasChanged();
+ }
+ }
+
+ public RoomListViewData Data { get; set; } = new RoomListViewData();
+
+ protected override async Task OnInitializedAsync() {
+ Data.Homeserver = await RMUStorage.GetCurrentSessionOrNavigate();
+ if (Data.Homeserver is null) return;
+ var rooms = await Data.Homeserver.GetJoinedRooms();
+ Data.GlobalProfile = await Data.Homeserver.GetProfileAsync(Data.Homeserver.WhoAmI.UserId);
+
+ foreach (var room in rooms) {
+ Data.Rooms.Add(new RoomInfo(room));
+ }
+ StateHasChanged();
+
+ await base.OnInitializedAsync();
+ }
+
+ private Tab _selectedTab = Tab.Main;
+
+ private enum Tab {
+ Main,
+ DMs,
+ ByRoomType
+ }
+
+ public class RoomListViewData {
+ public ObservableCollection<RoomInfo> Rooms { get; } = [];
+
+ public UserProfileResponse? GlobalProfile { get; set; }
+
+ public AuthenticatedHomeserverGeneric? Homeserver { get; set; }
+ }
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Rooms/Index2Components/MainTabComponents/MainTabSpaceItem.razor b/MatrixUtils.Web/Pages/Rooms/Index2Components/MainTabComponents/MainTabSpaceItem.razor
new file mode 100644
index 0000000..4216824
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/Index2Components/MainTabComponents/MainTabSpaceItem.razor
@@ -0,0 +1,27 @@
+@using MatrixUtils.Abstractions
+<div class="spaceListItem" onclick="@ToggleSpace">
+ <MxcImage Circular="true" Height="32" Width="32" Homeserver="Space.Room.Homeserver" MxcUri="@Space.RoomIcon"></MxcImage>
+ <span class="spaceNameEllipsis">@Space.RoomName</span>
+</div>
+
+@code {
+
+ [Parameter]
+ public RoomInfo Space { get; set; }
+
+ [Parameter]
+ public List<RoomInfo> OpenedSpaces { get; set; }
+
+ protected override Task OnInitializedAsync() {
+ Space.PropertyChanged += (sender, args) => { StateHasChanged(); };
+ return base.OnInitializedAsync();
+ }
+
+ public void ToggleSpace() {
+ if (OpenedSpaces.Contains(Space)) {
+ OpenedSpaces.Remove(Space);
+ } else {
+ OpenedSpaces.Add(Space);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Rooms/Index2Components/MainTabComponents/MainTabSpaceItem.razor.css b/MatrixUtils.Web/Pages/Rooms/Index2Components/MainTabComponents/MainTabSpaceItem.razor.css
new file mode 100644
index 0000000..c174567
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/Index2Components/MainTabComponents/MainTabSpaceItem.razor.css
@@ -0,0 +1,15 @@
+.spaceNameEllipsis {
+ padding-left: 8px;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ vertical-align: middle;
+ width: calc(100% - 38px);
+}
+
+.spaceListItem {
+ display: block;
+ width: 100%;
+ height: 50px;
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2DMsTab.razor b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2DMsTab.razor
new file mode 100644
index 0000000..f4cf849
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2DMsTab.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/RoomsIndex2MainTab.razor b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2MainTab.razor
new file mode 100644
index 0000000..2b7c5ac
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2MainTab.razor
@@ -0,0 +1,198 @@
+@using MatrixUtils.Abstractions
+@using System.Security.Cryptography
+@using ArcaneLibs.Extensions
+@using System.ComponentModel
+@using System.Diagnostics
+@using LibMatrix.EventTypes.Spec.State
+@using MatrixUtils.Web.Pages.Rooms.Index2Components.MainTabComponents
+@using Microsoft.AspNetCore.Components.Rendering
+<h3>RoomsIndex2MainTab</h3>
+
+@* <div> *@
+@* <div class="row"> *@
+@* <div class="col-3" style="background-color: #ffffff66;"> *@
+@* <LinkButton>Uncategorised rooms</LinkButton> *@
+@* @foreach (var space in GetTopLevelSpaces()) { *@
+@* <a style="@("display:block; width: 100%; height: 50px; background-color: #" + RandomNumberGenerator.GetBytes(3).Append((byte)0x11).ToArray().AsHexString().Replace(" ", ""))"> *@
+@* <div style="vertical-align: middle;"> *@
+@* <div style="overflow:hidden; text-overflow: ellipsis; white-space: nowrap; ">@space.RoomName</div> *@
+@* </div> *@
+@* </a> *@
+@* } *@
+@* </div> *@
+@* <div class="col-9" style="background-color: #ff00ff66;"> *@
+@* <p>Placeholder for rooms list...</p> *@
+@* </div> *@
+@* </div> *@
+@* </div> *@
+
+<div>
+ <div class="row">
+ <div class="col-3" style="background-color: #ffffff22;">
+ <LinkButton>Uncategorised rooms</LinkButton>
+ @foreach (var space in GetTopLevelSpaces()) {
+ @RecursingSpaceChildren(space)
+ }
+ </div>
+ <div class="col-9" style="background-color: #ff00ff66;">
+ <p>Placeholder for rooms list...</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 += OnRoomListChanged;
+ (newItem as RoomInfo).StateEvents.CollectionChanged += (sender, args) => { DebouncedStateHasChanged(); };
+ }
+ };
+ foreach (var newItem in Data.Rooms) {
+ newItem.PropertyChanged += OnRoomListChanged;
+ newItem.StateEvents.CollectionChanged += (sender, args) => { DebouncedStateHasChanged(); };
+ }
+
+ await base.OnInitializedAsync();
+ StateHasChanged();
+ }
+
+ private void OnRoomListChanged(object? sender, PropertyChangedEventArgs e) {
+ if (e.PropertyName == "RoomName" || e.PropertyName == "RoomType")
+ DebouncedStateHasChanged();
+ }
+
+ private CancellationTokenSource _debounceCts = new CancellationTokenSource();
+
+ private async Task DebouncedStateHasChanged() {
+ _debounceCts.Cancel();
+ _debounceCts = new CancellationTokenSource();
+ try {
+ Console.WriteLine("DebouncedStateHasChanged - Waiting 50ms...");
+ await Task.Delay(50, _debounceCts.Token);
+ Console.WriteLine("DebouncedStateHasChanged - Calling StateHasChanged!");
+ StateHasChanged();
+ }
+ catch (TaskCanceledException) { }
+ }
+
+ private List<RoomInfo> GetTopLevelSpaces() {
+ var spaces = Data.Rooms.Where(x => x.RoomType == "m.space").OrderBy(x => x.RoomName).ToList();
+ var allSpaceChildEvents = spaces.SelectMany(x => x.StateEvents.Where(y =>
+ y.Type == SpaceChildEventContent.EventId &&
+ y.RawContent!.Count > 0
+ )).ToList();
+
+ Console.WriteLine($"Child count: {allSpaceChildEvents.Count}");
+
+ spaces.RemoveAll(x => allSpaceChildEvents.Any(y => y.StateKey == x.Room.RoomId));
+
+ if (allSpaceChildEvents.Count == 0) {
+ Console.WriteLine("No space children found, returning nothing...");
+ return [];
+ }
+
+ return spaces.ToList();
+ }
+
+ private List<RoomInfo> GetSpaceChildren(RoomInfo space) {
+ var childEvents = space.StateEvents.Where(x =>
+ x.Type == SpaceChildEventContent.EventId &&
+ x.RawContent!.Count > 0
+ ).ToList();
+ var children = childEvents.Select(x => Data.Rooms.FirstOrDefault(y => y.Room.RoomId == x.StateKey)).Where(x => x is not null).ToList();
+ return children;
+ }
+
+ private List<RoomInfo> GetSpaceChildSpaces(RoomInfo space) {
+ var children = GetSpaceChildren(space);
+ var childSpaces = children.Where(x => x.RoomType == "m.space").ToList();
+ return childSpaces;
+ }
+
+ private RoomInfo? SelectedSpace { get; set; }
+ private List<RoomInfo> OpenedSpaces { get; set; } = new List<RoomInfo>();
+
+ private RenderFragment RecursingSpaceChildren(RoomInfo space, List<RoomInfo>? parents = null, int depth = 0) {
+ parents ??= [];
+ var totalSw = Stopwatch.StartNew();
+ var children = GetSpaceChildSpaces(space);
+
+ var randomColor = RandomNumberGenerator.GetBytes(3).Append((byte)0x33).ToArray().AsHexString().Replace(" ", "");
+ var isExpanded = OpenedSpaces.Contains(space);
+
+ // Console.WriteLine($"RecursingSpaceChildren::FetchData - Depth: {depth}, Space: {space.RoomName}, Children: {children.Count} - {totalSw.Elapsed}");
+
+ // var renderSw = Stopwatch.StartNew();
+ var rf = new RenderFragment(builder => {
+ builder.OpenElement(0, "div");
+ //space list entry render fragment
+ // builder.AddContent(1, SpaceListEntry(space));
+ builder.OpenComponent<MainTabSpaceItem>(1);
+ builder.AddAttribute(2, "Space", space);
+ builder.AddAttribute(2, "OpenedSpaces", OpenedSpaces);
+ builder.CloseComponent();
+ builder.CloseElement();
+ //space children render fragment
+ if (isExpanded) {
+ builder.OpenElement(2, "div");
+ builder.AddAttribute(3, "style", "padding-left: 10px;");
+ foreach (var child in children) {
+ builder.AddContent(4, RecursingSpaceChildren(child, parents.Append(space).ToList(), depth + 1));
+ }
+
+ builder.CloseElement();
+ }
+ });
+
+ // Console.WriteLine($"RecursingSpaceChildren::Render - Depth: {depth}, Space: {space.RoomName}, Children: {children.Count} - {renderSw.Elapsed}");
+ if (totalSw.ElapsedMilliseconds > 20)
+ Console.WriteLine($"RecursingSpaceChildren::Total - Depth: {depth}, Space: {space.RoomName}, Children: {children.Count} - {totalSw.Elapsed}");
+ // Console.WriteLine($"RecursingSpaceChildren::Total - Depth: {depth}, Space: {space.RoomName}, Children: {children.Count} - {totalSw.Elapsed}");
+ return rf;
+ }
+
+ // private RenderFragment SpaceListEntry(RoomInfo space) {
+ // return builder => {
+ // {
+ // builder.OpenElement(0, "div");
+ // builder.AddAttribute(1, "style", "display: block; width: 100%; height: 50px;");
+ // builder.AddAttribute(2, "onclick", EventCallback.Factory.Create(this, () => {
+ // if (OpenedSpaces.Contains(space)) {
+ // OpenedSpaces.Remove(space);
+ // }
+ // else {
+ // OpenedSpaces.Add(space);
+ // }
+ //
+ // StateHasChanged();
+ // }));
+ // {
+ // builder.OpenComponent<MxcImage>(5);
+ // builder.AddAttribute(6, "Homeserver", Data.Homeserver);
+ // builder.AddAttribute(7, "MxcUri", space.RoomIcon);
+ // builder.AddAttribute(8, "Circular", true);
+ // builder.AddAttribute(9, "Width", 32);
+ // builder.AddAttribute(10, "Height", 32);
+ // builder.CloseComponent();
+ // }
+ // {
+ // // room name, ellipsized
+ // builder.OpenElement(11, "span");
+ // builder.AddAttribute(12, "class", "spaceNameEllipsis");
+ // builder.AddContent(13, space.RoomName);
+ // builder.CloseElement();
+ // }
+ // builder.CloseElement();
+ // }
+ // };
+ // }
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2MainTab.razor.css b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2MainTab.razor.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2MainTab.razor.css
diff --git a/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2SyncContainer.razor b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2SyncContainer.razor
new file mode 100644
index 0000000..bbc63eb
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Rooms/Index2Components/RoomsIndex2SyncContainer.razor
@@ -0,0 +1,202 @@
+@using LibMatrix.Helpers
+@using LibMatrix.Responses
+@using MatrixUtils.Abstractions
+@using System.Diagnostics
+@using System.Diagnostics.CodeAnalysis
+@using LibMatrix.EventTypes.Spec.State
+@using LibMatrix.Extensions
+@using LibMatrix.Utilities
+@using System.Collections.ObjectModel
+@using ArcaneLibs
+@inject ILogger<RoomsIndex2SyncContainer> logger
+<pre>RoomsIndex2SyncContainer</pre>
+@foreach (var (name, value) in _statusList) {
+ <pre>[@name] @value.Status</pre>
+}
+
+@code {
+
+ [Parameter]
+ public Index2.RoomListViewData Data { get; set; } = null!;
+
+ private SyncHelper syncHelper;
+
+ private Queue<KeyValuePair<string, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure>> queue = new();
+
+ private ObservableCollection<(string name, ObservableStatus value)> _statusList = new();
+
+ protected override async Task OnInitializedAsync() {
+ _statusList.CollectionChanged += (sender, args) => {
+ StateHasChanged();
+ if (args.NewItems is { Count: > 0 })
+ foreach (var item in args.NewItems) {
+ if (item is not (string name, ObservableStatus value)) continue;
+ value.PropertyChanged += (sender, args) => {
+ if(value.Show) StateHasChanged();
+ };
+ }
+ };
+
+ while (Data.Homeserver is null) {
+ await Task.Delay(100);
+ }
+
+ await SetUpSync();
+ }
+
+ private async Task SetUpSync() {
+ var status = await GetOrAddStatus("Main");
+ var syncHelpers = new Dictionary<string, SyncHelper>() {
+ ["Main"] = new SyncHelper(Data.Homeserver, logger) {
+ Timeout = 30000,
+ FilterId = await Data.Homeserver.GetOrUploadNamedFilterIdAsync(CommonSyncFilters.GetBasicRoomInfo),
+ // MinimumDelay = TimeSpan.FromMilliseconds(5000)
+ }
+ };
+ status.Status = "Initial sync... Checking server filter capability...";
+ var syncRes = await syncHelpers["Main"].SyncAsync();
+ if (!syncRes.Rooms?.Join?.Any(x => x.Value.State?.Events?.Any(y => y.Type == SpaceChildEventContent.EventId) ?? false) ?? true) {
+ status.Status = "Initial sync indicates that server supports filters, starting helpers!";
+ syncHelpers.Add("SpaceRelations", new SyncHelper(Data.Homeserver, logger) {
+ Timeout = 30000,
+ FilterId = await Data.Homeserver.GetOrUploadNamedFilterIdAsync(CommonSyncFilters.GetSpaceRelations),
+ // MinimumDelay = TimeSpan.FromMilliseconds(5000)
+ });
+
+ syncHelpers.Add("Profile", new SyncHelper(Data.Homeserver, logger) {
+ Timeout = 30000,
+ FilterId = await Data.Homeserver.GetOrUploadNamedFilterIdAsync(CommonSyncFilters.GetOwnMemberEvents),
+ // MinimumDelay = TimeSpan.FromMilliseconds(5000)
+ });
+ }
+ else status.Status = "Initial sync indicates that server does not support filters, continuing without extra filters!";
+
+ await HandleSyncResponse(syncRes);
+
+ // profileSyncHelper = new SyncHelper(Homeserver, logger) {
+ // Timeout = 10000,
+ // Filter = profileUpdateFilter,
+ // MinimumDelay = TimeSpan.FromMilliseconds(5000)
+ // };
+ // profileUpdateFilter.Room.State.Senders.Add(Homeserver.WhoAmI.UserId);
+ RunQueueProcessor();
+ foreach (var helper in syncHelpers) {
+ Console.WriteLine($"Starting sync loop for {helper.Key}");
+ RunSyncLoop(helper.Value, helper.Key);
+ }
+ }
+
+ private async Task RunQueueProcessor() {
+ var status = await GetOrAddStatus("QueueProcessor");
+ var statusd = await GetOrAddStatus("QueueProcessor/D", show: false);
+ while (true) {
+ await Task.Delay(1000);
+ try {
+ var renderTimeSw = Stopwatch.StartNew();
+ while (queue.Count == 0) {
+ var delay = 1000;
+ Console.WriteLine("Queue is empty, waiting...");
+ // Status2 = $"Queue is empty, waiting for {delay}ms...";
+ await Task.Delay(delay);
+ }
+
+ status.Status = $"Queue no longer empty after {renderTimeSw.Elapsed}!";
+ renderTimeSw.Restart();
+
+ int maxUpdates = 5000;
+ while (maxUpdates-- > 0 && queue.TryDequeue(out var queueEntry)) {
+ var (roomId, roomData) = queueEntry;
+ statusd.Status = $"Dequeued room {roomId}";
+ RoomInfo room;
+
+ if (Data.Rooms.Any(x => x.Room.RoomId == roomId)) {
+ room = Data.Rooms.First(x => x.Room.RoomId == roomId);
+ statusd.Status = $"{roomId} already known with {room.StateEvents?.Count ?? 0} state events";
+ }
+ else {
+ statusd.Status = $"Eencountered new room {roomId}!";
+ room = new RoomInfo(Data.Homeserver!.GetRoom(roomId), roomData.State?.Events);
+ Data.Rooms.Add(room);
+ }
+
+ if (roomData.State?.Events is { Count: > 0 })
+ room.StateEvents!.MergeStateEventLists(roomData.State.Events);
+ else {
+ statusd.Status = $"Could not merge state for {room.Room.RoomId} as new data contains no state events!";
+ }
+
+ // await Task.Delay(10);
+ }
+
+ status.Status = $"Got {Data.Rooms.Count} rooms so far! {queue.Count} entries left in processing queue... Parsed last response in {renderTimeSw.Elapsed}";
+
+ // RenderContents |= queue.Count == 0;
+ // await Task.Delay(Data.Rooms.Count);
+ }
+ catch (Exception e) {
+ Console.WriteLine("QueueWorker exception: " + e);
+ }
+ }
+ }
+
+ private async Task RunSyncLoop(SyncHelper syncHelper, string name = "Unknown") {
+ var status = await GetOrAddStatus($"SYNC/{name}");
+ status.Status = $"Initial syncing...";
+
+ var syncs = syncHelper.EnumerateSyncAsync();
+ await foreach (var sync in syncs) {
+ var sw = Stopwatch.StartNew();
+ status.Status = $"[{DateTime.Now}] Got {Data.Rooms.Count} rooms so far! {sync.Rooms?.Join?.Count ?? 0} new updates!";
+
+ await HandleSyncResponse(sync);
+ status.Status += $"\nProcessed sync in {sw.ElapsedMilliseconds}ms, queue length: {queue.Count}";
+ }
+ }
+
+ private async Task HandleSyncResponse(SyncResponse? sync) {
+ if (sync?.Rooms?.Join is { Count: > 0 })
+ foreach (var joinedRoom in sync.Rooms.Join)
+ queue.Enqueue(joinedRoom);
+
+ if (sync.Rooms.Leave is { Count: > 0 })
+ foreach (var leftRoom in sync.Rooms.Leave)
+ if (Data.Rooms.Any(x => x.Room.RoomId == leftRoom.Key))
+ Data.Rooms.Remove(Data.Rooms.First(x => x.Room.RoomId == leftRoom.Key));
+ }
+
+ private SemaphoreSlim _syncLock = new(1, 1);
+
+ private async Task<ObservableStatus> GetOrAddStatus(string name, bool show = true, bool log = true) {
+ await _syncLock.WaitAsync();
+ try {
+ if (_statusList.Any(x => x.name == name))
+ return _statusList.First(x => x.name == name).value;
+ var status = new ObservableStatus() {
+ Name = name,
+ Log = log,
+ Show = show
+ };
+ _statusList.Add((name, status));
+ return status;
+ }
+ finally {
+ _syncLock.Release();
+ }
+ }
+
+ private class ObservableStatus : NotifyPropertyChanged {
+ private string _status = "Initialising...";
+ public string Name { get; set; } = "Unknown";
+ public bool Show { get; set; } = true;
+ public bool Log { get; set; } = true;
+
+ public string Status {
+ get => _status;
+ set {
+ if(SetField(ref _status, value) && Log)
+ Console.WriteLine($"[{Name}]: {value}");
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Tools/PolicyListActivity.razor b/MatrixUtils.Web/Pages/Tools/PolicyListActivity.razor
new file mode 100644
index 0000000..c94d0b0
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Tools/PolicyListActivity.razor
@@ -0,0 +1,158 @@
+@page "/Tools/PolicyListActivity"
+@using LibMatrix.EventTypes.Spec.State.Policy
+@using System.Diagnostics
+@using LibMatrix.RoomTypes
+@using LibMatrix.EventTypes.Common
+
+
+@if (RoomData.Count == 0)
+{
+ <p>Loading...</p>
+}
+else
+ foreach (var room in RoomData)
+ {
+ <h3>@room.Key</h3>
+ @foreach (var year in room.Value.OrderBy(x => x.Key))
+ {
+ <h5>@year.Key</h5>
+ <ActivityGraph Data="@year.Value" GlobalMax="MaxValue"
+ RLabel="removed" GLabel="new" BLabel="updated policies">
+ </ActivityGraph>
+ }
+ }
+
+
+@code {
+ public AuthenticatedHomeserverGeneric? Homeserver { get; set; }
+ public List<GenericRoom> FilteredRooms = new();
+
+ public Dictionary<DateOnly, ActivityGraph.RGB> TestData { get; set; } = new();
+
+ public ActivityGraph.RGB MaxValue { get; set; } = new()
+ {
+ R = 255, G = 255, B = 255
+ };
+
+ public Dictionary<string, Dictionary<int, Dictionary<DateOnly, ActivityGraph.RGB>>> RoomData { get; set; } = new();
+
+ protected override async Task OnInitializedAsync()
+ {
+ var sw = Stopwatch.StartNew();
+ await base.OnInitializedAsync();
+ Homeserver = (await RMUStorage.GetCurrentSessionOrNavigate())!;
+ if (Homeserver is null) return;
+
+ //random test data
+ for (DateOnly i = new DateOnly(2020, 1, 1); i < new DateOnly(2020, 12, 30); i = i.AddDays(Random.Shared.Next(5)))
+ {
+ TestData[i] = new()
+ {
+ R = (int)(Random.Shared.NextSingle() * 255),
+ G = (int)(Random.Shared.NextSingle() * 255),
+ B = (int)(Random.Shared.NextSingle() * 255)
+ };
+ }
+
+ StateHasChanged();
+ // return;
+
+ var rooms = await Homeserver.GetJoinedRooms();
+ // foreach (var room in rooms)
+ // {
+ // var type = await room.GetRoomType();
+ // if (type == "support.feline.policy.lists.msc.v1")
+ // {
+ // Console.WriteLine($"{room.RoomId} is policy list by type");
+ // FilteredRooms.Add(room);
+ // }
+ // else if(await room.GetStateOrNullAsync<MjolnirShortcodeEventContent>(MjolnirShortcodeEventContent.EventId) is not null)
+ // {
+ // Console.WriteLine($"{room.RoomId} is policy list by shortcode");
+ // FilteredRooms.Add(room);
+ // }
+ // }
+ var roomFilterTasks = rooms.Select(async room =>
+ {
+ var type = await room.GetRoomType();
+ if (type == "support.feline.policy.lists.msc.v1")
+ {
+ Console.WriteLine($"{room.RoomId} is policy list by type");
+ return room;
+ }
+ else if (await room.GetStateOrNullAsync<MjolnirShortcodeEventContent>(MjolnirShortcodeEventContent.EventId) is not null)
+ {
+ Console.WriteLine($"{room.RoomId} is policy list by shortcode");
+ return room;
+ }
+
+ return null;
+ }).ToList();
+ var filteredRooms = await Task.WhenAll(roomFilterTasks);
+ FilteredRooms.AddRange(filteredRooms.Where(x => x is not null).Cast<GenericRoom>());
+ Console.WriteLine($"Filtered {FilteredRooms.Count} rooms in {sw.ElapsedMilliseconds}ms");
+
+ var roomTasks = FilteredRooms.Select(FetchRoomHistory).ToList();
+ await Task.WhenAll(roomTasks);
+
+ Console.WriteLine($"Max value is {MaxValue.R} {MaxValue.G} {MaxValue.B}");
+ Console.WriteLine($"Filtered {FilteredRooms.Count} rooms in {sw.ElapsedMilliseconds}ms");
+ }
+
+ public async Task FetchRoomHistory(GenericRoom room)
+ {
+ var roomName = await room.GetNameOrFallbackAsync();
+ if (string.IsNullOrWhiteSpace(roomName)) roomName = room.RoomId;
+ if (!RoomData.ContainsKey(roomName))
+ {
+ RoomData[roomName] = new();
+ }
+
+ //use timeline
+ var timeline = room.GetManyMessagesAsync(limit: int.MaxValue, chunkSize: 5000);
+ await foreach (var response in timeline)
+ {
+ Console.WriteLine($"Got {response.State.Count} state, {response.Chunk.Count} timeline");
+ if (response.State.Count != 0) throw new Exception("Why the hell did we receive state events?");
+ foreach (var message in response.Chunk)
+ {
+ if (!message.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue;
+ //OriginServerTs to datetime
+ var dt = DateTimeOffset.FromUnixTimeMilliseconds((long)message.OriginServerTs!.Value).DateTime;
+ var date = new DateOnly(dt.Year, dt.Month, dt.Day);
+ if (!RoomData[roomName].ContainsKey(date.Year))
+ {
+ RoomData[roomName][date.Year] = new();
+ }
+
+ if (!RoomData[roomName][date.Year].ContainsKey(date))
+ {
+ // Console.WriteLine($"Adding {date} to {roomName}");
+ RoomData[roomName][date.Year][date] = new();
+ }
+
+ var rgb = RoomData[roomName][date.Year][date];
+ if (message.RawContent?.Count == 0) rgb.R++;
+ else if (string.IsNullOrWhiteSpace(message.Unsigned?.ReplacesState)) rgb.G++;
+ else rgb.B++;
+ RoomData[roomName][date.Year][date] = rgb;
+ }
+
+ var max = RoomData.SelectMany(x => x.Value.Values).Aggregate(new ActivityGraph.RGB(), (current, next) => new()
+ {
+ R = Math.Max(current.R, next.Average(x => x.Value.R)),
+ G = Math.Max(current.G, next.Average(x => x.Value.G)),
+ B = Math.Max(current.B, next.Average(x => x.Value.B))
+ });
+ MaxValue = new ActivityGraph.RGB(
+ r: Math.Max(max.R, Math.Max(max.G, max.B)),
+ g: Math.Max(max.R, Math.Max(max.G, max.B)),
+ b: Math.Max(max.R, Math.Max(max.G, max.B)));
+ Console.WriteLine($"Max value is {MaxValue.R} {MaxValue.G} {MaxValue.B}");
+ StateHasChanged();
+ await Task.Delay(100);
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Tools/UserTrace.razor b/MatrixUtils.Web/Pages/Tools/UserTrace.razor
index b3a7487..d78c58a 100644
--- a/MatrixUtils.Web/Pages/Tools/UserTrace.razor
+++ b/MatrixUtils.Web/Pages/Tools/UserTrace.razor
@@ -80,18 +80,33 @@
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 {
+ //
+ }
+ }
- var stateTasks = rooms.Select(async x => (x, await x.GetMembersListAsync(false))).ToAsyncEnumerable();
+ 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}...");
+ await foreach (var (room, state) in stateTasks) {
+ roomMembers.Add(room, state);
+ log.Add($"Got {state.Count} members for {room.RoomId}...");
+ }
+ }
+ catch {
+ //
}
log.Add($"Done fetching members!");
- UserIDs.RemoveAll(x=>sessions.Any(y=>y.UserId == x));
-
+ UserIDs.RemoveAll(x => sessions.Any(y => y.UserId == x));
+
StateHasChanged();
Console.WriteLine("Rerendered!");
await base.OnInitializedAsync();
@@ -105,7 +120,6 @@
matches[userId].Add(new() {
Event = events.First(x => x.StateKey == userId && x.Type == RoomMemberEventContent.EventId),
Room = room,
-
});
}
}
@@ -132,6 +146,7 @@
private class Matches {
public GenericRoom Room;
+
public StateEventResponse Event;
// public
}
diff --git a/MatrixUtils.Web/Pages/User/DMManager.razor b/MatrixUtils.Web/Pages/User/DMManager.razor
index df5cd6b..80bf3b2 100644
--- a/MatrixUtils.Web/Pages/User/DMManager.razor
+++ b/MatrixUtils.Web/Pages/User/DMManager.razor
@@ -2,6 +2,7 @@
@using LibMatrix.EventTypes.Spec.State
@using LibMatrix.Responses
@using MatrixUtils.Abstractions
+@using LibMatrix
<h3>Direct Messages</h3>
<hr/>
@@ -36,11 +37,19 @@
Status = "Loading DM list from account data...";
var dms = await Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
DMRooms.Clear();
- foreach (var (userId, rooms) in dms) {
+ var userTasks = dms.Select(async kv => {
+ var (userId, rooms) = kv;
var roomList = new List<RoomInfo>();
- DMRooms.Add(await Homeserver.GetProfileAsync(userId), roomList);
+ UserProfileResponse? profile = null;
+ try {
+ profile = await Homeserver.GetProfileAsync(userId);
+ }
+ catch (MatrixException e) {
+ if (e is { ErrorCode: "M_UNKNOWN" }) profile = new UserProfileResponse() { DisplayName = $"{userId}: {e.Error}" };
+ }
+
foreach (var room in rooms) {
- var roomInfo = new RoomInfo() { Room = Homeserver.GetRoom(room) };
+ var roomInfo = new RoomInfo(Homeserver.GetRoom(room));
roomList.Add(roomInfo);
roomInfo.StateEvents.Add(new() {
Type = RoomNameEventContent.EventId,
@@ -50,8 +59,13 @@
RoomId = room, Sender = null, EventId = null
});
}
+
+ DMRooms.Add(profile ?? new() { DisplayName = userId }, roomList);
StateHasChanged();
- }
+ }).ToList();
+
+ await Task.WhenAll(userTasks);
+ await Task.Delay(500);
StateHasChanged();
Status = null;
diff --git a/MatrixUtils.Web/Pages/User/DMSpace.razor b/MatrixUtils.Web/Pages/User/DMSpace.razor
index 519cfff..e3dba30 100644
--- a/MatrixUtils.Web/Pages/User/DMSpace.razor
+++ b/MatrixUtils.Web/Pages/User/DMSpace.razor
@@ -1,11 +1,14 @@
@page "/User/DMSpace/Setup"
@using LibMatrix
+@using LibMatrix.Responses
+@using MatrixUtils.Abstractions
@using MatrixUtils.LibDMSpace
@using MatrixUtils.LibDMSpace.StateEvents
@using MatrixUtils.Web.Pages.User.DMSpaceStages
+@using System.Text.Json.Serialization
<h3>DM Space Management</h3>
<hr/>
-<CascadingValue Value="@DmSpace">
+<CascadingValue Value="@SetupData">
@switch (Stage) {
case -1:
<p>Initialising...</p>
@@ -41,36 +44,29 @@
}
}
- public AuthenticatedHomeserverGeneric? Homeserver { get; set; }
- public DMSpaceConfiguration? DmSpaceConfiguration { get; set; }
-
- [Parameter]
- public DMSpace? DmSpace { get; set; }
+ public DMSpace? DMSpaceRootPage { get; set; }
protected override async Task OnInitializedAsync() {
if (NavigationManager.Uri.Contains("?stage=")) {
- NavigationManager.NavigateTo("/User/DMSpace", true);
+ NavigationManager.NavigateTo("/User/DMSpace/Setup", true);
}
- DmSpace = this;
- Homeserver ??= await RMUStorage.GetCurrentSessionOrNavigate();
- if (Homeserver is null) return;
+ DMSpaceRootPage = this;
+ SetupData.Homeserver ??= await RMUStorage.GetCurrentSessionOrNavigate();
+ if (SetupData.Homeserver is null) return;
try {
- DmSpaceConfiguration = await Homeserver.GetAccountDataAsync<DMSpaceConfiguration>("gay.rory.dm_space");
- var room = Homeserver.GetRoom(DmSpaceConfiguration.DMSpaceId);
- await room.GetStateAsync<object>(DMSpaceInfo.EventId);
+ SetupData.DmSpaceConfiguration = await SetupData.Homeserver.GetAccountDataAsync<DMSpaceConfiguration>("gay.rory.dm_space");
+ var room = SetupData.Homeserver.GetRoom(SetupData.DmSpaceConfiguration.DMSpaceId);
+ await room.GetStateAsync<DMSpaceInfo>(DMSpaceInfo.EventId);
Stage = 1;
}
catch (MatrixException e) {
- if (e.ErrorCode == "M_NOT_FOUND") {
+ if (e.ErrorCode is "M_NOT_FOUND" or "M_FORBIDDEN") {
Stage = 0;
- DmSpaceConfiguration = new();
+ SetupData.DmSpaceConfiguration = new();
}
else throw;
}
- catch (Exception e) {
- throw;
- }
finally {
StateHasChanged();
}
@@ -82,4 +78,27 @@
await base.OnParametersSetAsync();
}
+ public DMSpaceSetupData SetupData { get; set; } = new();
+
+ public class DMSpaceSetupData {
+
+ public AuthenticatedHomeserverGeneric? Homeserver { get; set; }
+
+ public DMSpaceConfiguration? DmSpaceConfiguration { get; set; }
+
+ public DMSpaceInfo? DmSpaceInfo { get; set; } = new();
+
+ public Dictionary<string, RoomInfo>? Spaces;
+
+ public Dictionary<UserProfileWithId, List<RoomInfo>>? DMRooms;
+
+ public RoomInfo? DMSpaceRoomInfo { get; set; }
+
+
+ public class UserProfileWithId : UserProfileResponse {
+ [JsonIgnore]
+ public string Id { get; set; }
+ }
+ }
+
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage0.razor b/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage0.razor
index 49fd5b4..5f6508c 100644
--- a/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage0.razor
+++ b/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage0.razor
@@ -4,7 +4,7 @@
<p>This wizard will help you set up a DM space.</p>
<p>This is useful for eg. sharing DM rooms across multiple accounts.</p>
<br/>
-<LinkButton href="/User/DMSpace?stage=1">Get started</LinkButton>
+<LinkButton href="/User/DMSpace/Setup?stage=1">Get started</LinkButton>
@code {
diff --git a/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage1.razor b/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage1.razor
index 6131617..2176467 100644
--- a/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage1.razor
+++ b/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage1.razor
@@ -6,30 +6,40 @@
@using MatrixUtils.LibDMSpace.StateEvents
@using Microsoft.Extensions.Primitives
@using ArcaneLibs.Extensions
+@using LibMatrix.EventTypes.Spec.State
+@using MatrixUtils.Abstractions
<b>
<u>DM Space setup tool - stage 1: Configure space</u>
</b>
<p>You will need a space to use for DM rooms.</p>
-@if (DmSpace is not null) {
- <p>
- Selected space:
- <InputSelect @bind-Value="DmSpace.DmSpaceConfiguration.DMSpaceId">
- @foreach (var (id, name) in spaces) {
- <option value="@id">@name</option>
- }
- </InputSelect>
- </p>
- <p>
- <InputCheckbox @bind-Value="DmSpaceInfo.LayerByUser"></InputCheckbox>
- Create sub-spaces per user
- </p>
+@if (SetupData is not null) {
+ if (SetupData.Spaces is not null) {
+ <p>
+ Selected space:
+ <InputSelect @bind-Value="SetupData.DmSpaceConfiguration.DMSpaceId">
+ <option value="">New space</option>
+ @foreach (var (id, roomInfo) in SetupData.Spaces) {
+ <option value="@id">@roomInfo.RoomName</option>
+ }
+ </InputSelect>
+ </p>
+ <p>
+ <InputCheckbox @bind-Value="SetupData.DmSpaceInfo.LayerByUser"></InputCheckbox>
+ Create sub-spaces per user
+ </p>
+
+ <br/>
+ <LinkButton OnClick="@Disband" Color="#FF0000">Disband</LinkButton>
+ <LinkButton OnClick="@Execute">Next</LinkButton>
+ }
+ else {
+ <p>Discovering spaces, please wait...</p>
+ }
}
else {
- <b>Error: DmSpaceConfiguration is null!</b>
+ <b>Error: Setup data is null!</b>
}
-<br/>
-<LinkButton OnClick="@Execute">Next</LinkButton>
@if (!string.IsNullOrWhiteSpace(Status)) {
<p>@Status</p>
@@ -45,84 +55,97 @@ else {
}
}
- private Dictionary<string, string> spaces = new() { { "", "New space" } };
private string? _status;
[CascadingParameter]
- public DMSpace? DmSpace { get; set; }
+ public DMSpace.DMSpaceSetupData SetupData { get; set; }
- public DMSpaceInfo? DmSpaceInfo { get; set; } = new();
+ SemaphoreSlim _semaphoreSlim = new(1, 1);
protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- }
-
- SemaphoreSlim _semaphoreSlim = new(1, 1);
- protected override async Task OnParametersSetAsync() {
- if (DmSpace is null)
+ if (SetupData is null)
return;
+
await _semaphoreSlim.WaitAsync();
- DmSpace.DmSpaceConfiguration ??= new();
- if (spaces.Count == 1) {
- Status = "Looking for spaces...";
- var userRoomsEnum = DmSpace.Homeserver.GetJoinedRoomsByType("m.space");
- List<GenericRoom> userRooms = new();
- await foreach (var room in userRoomsEnum) {
- userRooms.Add(room);
- }
- var roomChecks = userRooms.Select(GetFeasibleSpaces).ToAsyncEnumerable();
- await foreach(var room in roomChecks)
- if(room.HasValue)
- spaces.TryAdd(room.Value.id, room.Value.name);
-
- Status = "Done!";
+
+ Dictionary<string, RoomInfo> spaces = [];
+ SetupData.DmSpaceConfiguration ??= new();
+
+ Status = "Looking for spaces...";
+ var userRoomsEnum = SetupData.Homeserver!.GetJoinedRoomsByType("m.space");
+
+ List<GenericRoom> userRooms = new();
+ await foreach (var room in userRoomsEnum) {
+ userRooms.Add(room);
}
+
+ var roomChecks = userRooms.Select(GetFeasibleSpaces).ToAsyncEnumerable();
+ await foreach (var room in roomChecks)
+ if (room.HasValue)
+ spaces.TryAdd(room.Value.id, room.Value.roomInfo);
+
+ SetupData.Spaces = spaces;
+
+ Status = "Done!";
_semaphoreSlim.Release();
await base.OnParametersSetAsync();
}
private async Task Execute() {
- if (string.IsNullOrWhiteSpace(DmSpace.DmSpaceConfiguration.DMSpaceId)) {
- var crr = CreateRoomRequest.CreatePrivate(DmSpace.Homeserver, "Direct Messages");
- crr.CreationContentBaseType.Type = "m.space";
- DmSpace.DmSpaceConfiguration.DMSpaceId = (await DmSpace.Homeserver.CreateRoom(crr)).RoomId;
+ if (string.IsNullOrWhiteSpace(SetupData!.DmSpaceConfiguration!.DMSpaceId)) {
+ var createRoomRequest = CreateRoomRequest.CreatePrivate(SetupData.Homeserver!, "Direct Messages");
+ createRoomRequest.CreationContentBaseType.Type = "m.space";
+ SetupData.DmSpaceConfiguration.DMSpaceId = (await SetupData.Homeserver!.CreateRoom(createRoomRequest)).RoomId;
}
- await DmSpace.Homeserver!.SetAccountDataAsync(DMSpaceConfiguration.EventId, DmSpace.DmSpaceConfiguration);
- var space = DmSpace.Homeserver.GetRoom(DmSpace.DmSpaceConfiguration.DMSpaceId);
- await space.SendStateEventAsync(DMSpaceInfo.EventId, DmSpaceInfo);
+
+ await SetupData.Homeserver!.SetAccountDataAsync(DMSpaceConfiguration.EventId, SetupData.DmSpaceConfiguration);
+ var space = SetupData.Homeserver.GetRoom(SetupData.DmSpaceConfiguration.DMSpaceId);
+ await space.SendStateEventAsync(DMSpaceInfo.EventId, SetupData.DmSpaceInfo);
+ SetupData.DMSpaceRoomInfo = new RoomInfo(space);
+ await SetupData.DMSpaceRoomInfo.FetchAllStateAsync();
NavigationManager.NavigateTo("/User/DMSpace/Setup?stage=2");
}
- public async Task<(string id, string name)?> GetFeasibleSpaces(GenericRoom room) {
+ public async Task<(string id, RoomInfo roomInfo)?> GetFeasibleSpaces(GenericRoom room) {
try {
- var pls = await room.GetPowerLevelsAsync();
- if (!pls.UserHasStatePermission(DmSpace.Homeserver.WhoAmI.UserId, "m.space.child")) {
+ var ri = new RoomInfo(room);
+
+ await foreach(var evt in room.GetFullStateAsync())
+ ri.StateEvents.Add(evt);
+
+ var powerLevels = (await ri.GetStateEvent(RoomPowerLevelEventContent.EventId)).TypedContent as RoomPowerLevelEventContent;
+ if (!powerLevels.UserHasStatePermission(SetupData.Homeserver.WhoAmI.UserId, SpaceChildEventContent.EventId)) {
Console.WriteLine($"No permission to send m.space.child in {room.RoomId}...");
return null;
}
- var roomName = await room.GetNameAsync();
- Status = $"Found viable space: {roomName}";
- if (string.IsNullOrWhiteSpace(DmSpace.DmSpaceConfiguration.DMSpaceId)) {
- try {
- var dsi = await DmSpace.Homeserver.GetRoom(room.RoomId).GetStateOrNullAsync<DMSpaceInfo>(DMSpaceInfo.EventId) ?? new DMSpaceInfo();
- if (await room.GetStateOrNullAsync<DMSpaceInfo>(DMSpaceInfo.EventId) is not null && dsi is not null) {
- DmSpace.DmSpaceConfiguration.DMSpaceId = room.RoomId;
- DmSpaceInfo = dsi;
- }
- }
- catch (MatrixException e) {
- if (e.ErrorCode == "M_NOT_FOUND") Console.WriteLine($"{room.RoomId} is not a DM space.");
- else throw;
+
+ Status = $"Found viable space: {ri.RoomName}";
+ if (!string.IsNullOrWhiteSpace(SetupData.DmSpaceConfiguration!.DMSpaceId)) {
+ if (await room.GetStateOrNullAsync<DMSpaceInfo>(DMSpaceInfo.EventId) is { } dsi) {
+ SetupData.DmSpaceConfiguration.DMSpaceId = room.RoomId;
+ SetupData.DmSpaceInfo = dsi;
+ Console.WriteLine(dsi.ToJson(ignoreNull: true));
}
}
- return (room.RoomId, roomName);
+
+ if (ri.RoomName == room.RoomId)
+ ri.RoomName = await room.GetNameOrFallbackAsync();
+
+ return (room.RoomId, ri);
}
catch (MatrixException e) {
if (e.ErrorCode == "M_NOT_FOUND") Console.WriteLine($"m.room.power_levels does not exist in {room.RoomId}!!!");
else throw;
}
+
return null;
}
+ private async Task Disband() {
+ var space = new DMSpaceRoom(SetupData.Homeserver, SetupData.DmSpaceConfiguration.DMSpaceId);
+ await space.DisbandDMSpace();
+ NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
+ }
+
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor b/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor
index 5a53347..a70e9c5 100644
--- a/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor
+++ b/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor
@@ -17,18 +17,23 @@
<p>@Status</p>
}
-@if (DmSpace is not null) {
- @foreach (var (userId, room) in dmRooms.OrderBy(x => x.Key.Id)) {
- <InlineUserItem User="@userId"></InlineUserItem>
- @foreach (var roomInfo in room) {
- <RoomListItem RoomInfo="@roomInfo">
- <LinkButton Round="true" OnClick="@(async () => DmToReassign = roomInfo)">Reassign</LinkButton>
- </RoomListItem>
+@if (SetupData is not null) {
+ if (SetupData.DMRooms is { Count: > 0 }) {
+ @foreach (var (userId, room) in SetupData.DMRooms.OrderBy(x => x.Key.Id)) {
+ <InlineUserItem User="@userId"></InlineUserItem>
+ @foreach (var roomInfo in room) {
+ <RoomListItem RoomInfo="@roomInfo">
+ <LinkButton Round="true" OnClick="@(async () => DmToReassign = roomInfo)">Reassign</LinkButton>
+ </RoomListItem>
+ }
}
}
+ else {
+ <p>DM room list is loading, please wait...</p>
+ }
}
else {
- <b>Error: DmSpaceConfiguration is null!</b>
+ <b>Error: DMSpaceRootPage is null!</b>
}
<br/>
@@ -88,26 +93,21 @@ else {
private RoomInfo? _dmToReassign;
[CascadingParameter]
- public DMSpace? DmSpace { get; set; }
+ public DMSpace.DMSpaceSetupData SetupData { get; set; }
- private Dictionary<UserProfileWithId, List<RoomInfo>> dmRooms { get; set; } = new();
- private Dictionary<RoomInfo, List<UserProfileWithId>> duplicateDmRooms { get; set; } = new();
- private Dictionary<RoomInfo, List<UserProfileWithId>> roomMembers { get; set; } = new();
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- }
+ private Dictionary<RoomInfo, List<DMSpace.DMSpaceSetupData.UserProfileWithId>> duplicateDmRooms { get; set; } = new();
+ private Dictionary<RoomInfo, List<DMSpace.DMSpaceSetupData.UserProfileWithId>> roomMembers { get; set; } = new();
SemaphoreSlim _semaphore = new(1, 1);
- protected override async Task OnParametersSetAsync() {
- if (DmSpace is null)
+ protected override async Task OnInitializedAsync() {
+ if (SetupData is null)
return;
await _semaphore.WaitAsync();
DmToReassign = null;
- var hs = DmSpace.Homeserver;
+ var hs = SetupData.Homeserver;
Status = "Loading DM list from account data...";
- var dms = await DmSpace.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
+ var dms = await SetupData.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
Status = "Optimising DM list from account data...";
var joinedRooms = (await hs.GetJoinedRooms()).Select(x => x.RoomId).ToList();
foreach (var (user, rooms) in dms) {
@@ -116,18 +116,22 @@ else {
if (!joinedRooms.Contains(roomId))
rooms.RemoveAt(i);
}
+
dms[user] = rooms.Distinct().ToList();
}
- dms.RemoveAll((x, y) => y is {Count: 0});
- await DmSpace.Homeserver.SetAccountDataAsync("m.direct", dms);
- dmRooms.Clear();
+
+ dms.RemoveAll((x, y) => y is { Count: 0 });
+ await SetupData.Homeserver.SetAccountDataAsync("m.direct", dms);
Status = "DM list optimised, fetching info...";
+
+ SetupData.DMRooms = new Dictionary<DMSpace.DMSpaceSetupData.UserProfileWithId, List<RoomInfo>>();
+
var results = dms.Select(async x => {
var (userId, rooms) = x;
- UserProfileWithId userProfile;
+ DMSpace.DMSpaceSetupData.UserProfileWithId userProfile;
try {
- var profile = await DmSpace.Homeserver.GetProfileAsync(userId);
+ var profile = await SetupData.Homeserver.GetProfileAsync(userId);
userProfile = new() {
AvatarUrl = profile.AvatarUrl,
Id = userId,
@@ -141,32 +145,35 @@ else {
Id = userId
};
}
+
var roomList = new List<RoomInfo>();
var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncEnumerable();
await foreach (var result in tasks)
roomList.Add(result);
return (userProfile, roomList);
- // StateHasChanged();
+ // StateHasChanged();
}).ToAsyncEnumerable();
await foreach (var res in results) {
- dmRooms.Add(res.userProfile, res.roomList);
- // Status = $"Listed {dmRooms.Count} users";
+ SetupData.DMRooms.Add(res.userProfile, res.roomList);
+ // Status = $"Listed {dmRooms.Count} users";
}
+
_semaphore.Release();
- var duplicateDmRoomIds = new Dictionary<string, List<UserProfileWithId>>();
- foreach (var (user, rooms) in dmRooms) {
+ var duplicateDmRoomIds = new Dictionary<string, List<DMSpace.DMSpaceSetupData.UserProfileWithId>>();
+ foreach (var (user, rooms) in SetupData.DMRooms) {
foreach (var roomInfo in rooms) {
if (!duplicateDmRoomIds.ContainsKey(roomInfo.Room.RoomId))
duplicateDmRoomIds.Add(roomInfo.Room.RoomId, new());
duplicateDmRoomIds[roomInfo.Room.RoomId].Add(user);
}
}
+
duplicateDmRoomIds.RemoveAll((x, y) => y.Count == 1);
foreach (var (roomId, users) in duplicateDmRoomIds) {
- duplicateDmRooms.Add(dmRooms.First(x => x.Value.Any(x => x.Room.RoomId == roomId)).Value.First(x => x.Room.RoomId == roomId), users);
+ duplicateDmRooms.Add(SetupData.DMRooms.First(x => x.Value.Any(x => x.Room.RoomId == roomId)).Value.First(x => x.Room.RoomId == roomId), users);
}
- // StateHasChanged();
+ // StateHasChanged();
Status = null;
await base.OnParametersSetAsync();
}
@@ -176,34 +183,29 @@ else {
}
private async Task<RoomInfo> GetRoomInfo(GenericRoom room) {
- var roomInfo = new RoomInfo() {
- Room = room
- };
+ var roomInfo = new RoomInfo(room);
+ await roomInfo.FetchAllStateAsync();
roomMembers[roomInfo] = new();
- roomInfo.CreationEventContent = await room.GetCreateEventAsync();
- try {
- roomInfo.RoomName = await room.GetNameAsync();
- }
- catch { }
+ // roomInfo.CreationEventContent = await room.GetCreateEventAsync();
+
+ if(roomInfo.RoomName == room.RoomId)
+ try {
+ roomInfo.RoomName = await room.GetNameOrFallbackAsync();
+ }
+ catch { }
var membersEnum = room.GetMembersEnumerableAsync(true);
await foreach (var member in membersEnum)
if (member.TypedContent is RoomMemberEventContent memberEvent)
roomMembers[roomInfo].Add(new() { DisplayName = memberEvent.DisplayName, AvatarUrl = memberEvent.AvatarUrl, Id = member.StateKey });
-
- if (string.IsNullOrWhiteSpace(roomInfo.RoomName) || roomInfo.RoomName == room.RoomId) {
- List<string> displayNames = new List<string>();
- foreach (var member in roomMembers[roomInfo])
- if (!string.IsNullOrWhiteSpace(member.DisplayName))
- displayNames.Add(member.DisplayName);
- roomInfo.RoomName = string.Join(", ", displayNames);
- }
+
try {
string? roomIcon = (await room.GetAvatarUrlAsync())?.Url;
if (room is not null)
roomInfo.RoomIcon = roomIcon;
}
catch { }
+
return roomInfo;
}
@@ -214,29 +216,25 @@ else {
}
private async Task SetRoomAssignment(string roomId, string userId) {
- var hs = DmSpace.Homeserver;
+ var hs = SetupData.Homeserver;
Status = "Loading DM list from account data...";
- var dms = await DmSpace.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
+ var dms = await SetupData.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
Status = "Updating DM list from account data...";
foreach (var (user, rooms) in dms) {
rooms.RemoveAll(x => x == roomId);
dms[user] = rooms.Distinct().ToList();
}
- if(!dms.ContainsKey(userId))
+
+ if (!dms.ContainsKey(userId))
dms.Add(userId, new());
dms[userId].Add(roomId);
- dms.RemoveAll((x, y) => y is {Count: 0});
- await DmSpace.Homeserver.SetAccountDataAsync("m.direct", dms);
+ dms.RemoveAll((x, y) => y is { Count: 0 });
+ await SetupData.Homeserver.SetAccountDataAsync("m.direct", dms);
duplicateDmRooms.RemoveAll((x, y) => x.Room.RoomId == roomId);
StateHasChanged();
if (duplicateDmRooms.Count == 0) await OnParametersSetAsync();
}
- private class UserProfileWithId : UserProfileResponse {
- [JsonIgnore]
- public string Id { get; set; }
- }
-
-}
\ No newline at end of file
+}
diff --git a/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor b/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor
index 9307f6a..865e956 100644
--- a/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor
+++ b/MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor
@@ -18,15 +18,15 @@
<p>@Status</p>
}
-@if (DmSpace is not null) {
- @if (dmSpaceInfo is not null && dmSpaceRoomInfo is not null) {
+@if (SetupData is not null) {
+ @if (SetupData.DMSpaceRoomInfo is not null) {
<p>
- <InputCheckbox @bind-Value="dmSpaceInfo.LayerByUser"></InputCheckbox>
+ <InputCheckbox @bind-Value="SetupData.DmSpaceInfo.LayerByUser"></InputCheckbox>
Create sub-spaces per user
</p>
- @if (!dmSpaceInfo.LayerByUser) {
- <RoomListItem RoomInfo="@dmSpaceRoomInfo"></RoomListItem>
- @foreach (var (userId, room) in dmRooms.OrderBy(x => x.Key.RoomName)) {
+ @if (!SetupData.DmSpaceInfo.LayerByUser) {
+ <RoomListItem RoomInfo="@SetupData.DMSpaceRoomInfo"></RoomListItem>
+ @foreach (var (userId, room) in SetupData.DMRooms.OrderBy(x => x.Key.DisplayName)) {
@foreach (var roomInfo in room) {
<div style="margin-left: 32px;">
<RoomListItem RoomInfo="@roomInfo"></RoomListItem>
@@ -35,10 +35,16 @@
}
}
else {
- <RoomListItem RoomInfo="@dmSpaceRoomInfo"></RoomListItem>
- @foreach (var (userId, room) in dmRooms.OrderBy(x => x.Key.RoomName)) {
+ <RoomListItem RoomInfo="@SetupData.DMSpaceRoomInfo"></RoomListItem>
+ @foreach (var (user, room) in SetupData.DMRooms.OrderBy(x => x.Key.DisplayName)) {
<div style="margin-left: 32px;">
- <RoomListItem RoomInfo="@userId"></RoomListItem>
+ @{
+ RoomInfo fakeRoom = new(SetupData.DMSpaceRoomInfo.Room) {
+ RoomName = user.DisplayName ?? user.Id,
+ RoomIcon = user.AvatarUrl
+ };
+ }
+ <RoomListItem RoomInfo="@fakeRoom"></RoomListItem>
</div>
@foreach (var roomInfo in room) {
<div style="margin-left: 64px;">
@@ -49,11 +55,11 @@
}
}
else {
- <b>Error: dmSpaceInfo is null!</b>
+ <b>Error: SetupData.DMSpaceRoomInfo is null!</b>
}
}
else {
- <b>Error: DmSpaceConfiguration is null!</b>
+ <b>Error: DMSpaceRootPageConfiguration is null!</b>
}
<br/>
@@ -72,83 +78,75 @@ else {
private string? _status;
[CascadingParameter]
- public DMSpace? DmSpace { get; set; }
-
- private Dictionary<RoomInfo, List<RoomInfo>> dmRooms { get; set; } = new();
- private DMSpaceInfo? dmSpaceInfo { get; set; }
- private RoomInfo? dmSpaceRoomInfo { get; set; }
-
- protected override async Task OnInitializedAsync() {
- await base.OnInitializedAsync();
- }
+ public DMSpace.DMSpaceSetupData SetupData { get; set; }
SemaphoreSlim _semaphore = new(1, 1);
- protected override async Task OnParametersSetAsync() {
- if (DmSpace is null)
+ protected override async Task OnInitializedAsync() {
+ if (SetupData is null)
return;
await _semaphore.WaitAsync();
- var hs = DmSpace.Homeserver;
- var dmSpaceRoom = new DMSpaceRoom(hs, DmSpace.DmSpaceConfiguration.DMSpaceId);
- dmSpaceRoomInfo = new() {
- RoomName = await dmSpaceRoom.GetNameAsync(),
- CreationEventContent = await dmSpaceRoom.GetCreateEventAsync(),
- RoomIcon = "mxc://feline.support/uUxBwaboPkMGtbZcAGZaIzpK",
- Room = dmSpaceRoom
- };
- dmSpaceInfo = await dmSpaceRoom.GetDmSpaceInfo();
- Status = "Loading DM list from account data...";
- var dms = await DmSpace.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
- dmRooms.Clear();
+ var hs = SetupData.Homeserver;
+ // var dmSpaceRoom = new DMSpaceRoom(hs, SetupData.DmSpaceConfiguration.DMSpaceId);
+ // SetupData.
+ // dmSpaceRoomInfo = new() {
+ // RoomName = await dmSpaceRoom.GetNameAsync(),
+ // CreationEventContent = await dmSpaceRoom.GetCreateEventAsync(),
+ // RoomIcon = "mxc://feline.support/uUxBwaboPkMGtbZcAGZaIzpK",
+ // Room = dmSpaceRoom
+ // };
+ // dmSpaceInfo = await dmSpaceRoom.GetDMSpaceInfo();
+ // Status = "Loading DM list from account data...";
+ // var dms = await SetupData.Homeserver.GetAccountDataAsync<Dictionary<string, List<string>>>("m.direct");
Status = "DM list optimised, fetching info...";
- var results = dms.Select(async x => {
- var (userId, rooms) = x;
- UserProfileWithId userProfile;
- try {
- var profile = await DmSpace.Homeserver.GetProfileAsync(userId);
- userProfile = new() {
- AvatarUrl = profile.AvatarUrl,
- Id = userId,
- DisplayName = profile.DisplayName
- };
- }
- catch {
- userProfile = new() {
- AvatarUrl = "mxc://feline.support/uUxBwaboPkMGtbZcAGZaIzpK",
- DisplayName = userId,
- Id = userId
- };
- }
- var roomList = new List<RoomInfo>();
- var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncEnumerable();
- await foreach (var result in tasks)
- roomList.Add(result);
- return (userProfile, roomList);
- }).ToAsyncEnumerable();
- await foreach (var res in results) {
- dmRooms.Add(new RoomInfo() {
- Room = dmSpaceRoom,
- RoomIcon = res.userProfile.AvatarUrl,
- RoomName = res.userProfile.DisplayName,
- CreationEventContent = await dmSpaceRoom.GetCreateEventAsync()
- }, res.roomList);
- }
+ // var results = dms.Select(async x => {
+ // var (userId, rooms) = x;
+ // UserProfileWithId userProfile;
+ // try {
+ // var profile = await SetupData.Homeserver.GetProfileAsync(userId);
+ // userProfile = new() {
+ // AvatarUrl = profile.AvatarUrl,
+ // Id = userId,
+ // DisplayName = profile.DisplayName
+ // };
+ // }
+ // catch {
+ // userProfile = new() {
+ // AvatarUrl = "mxc://feline.support/uUxBwaboPkMGtbZcAGZaIzpK",
+ // DisplayName = userId,
+ // Id = userId
+ // };
+ // }
+ // var roomList = new List<RoomInfo>();
+ // var tasks = rooms.Select(x => GetRoomInfo(hs.GetRoom(x))).ToAsyncEnumerable();
+ // await foreach (var result in tasks)
+ // roomList.Add(result);
+ // return (userProfile, roomList);
+ // }).ToAsyncEnumerable();
+ // await foreach (var res in results) {
+ // dmRooms.Add(new RoomInfo() {
+ // Room = dmSpaceRoom,
+ // RoomIcon = res.userProfile.AvatarUrl,
+ // RoomName = res.userProfile.DisplayName,
+ // CreationEventContent = await dmSpaceRoom.GetCreateEventAsync()
+ // }, res.roomList);
+ // }
+ await SetupData.DMSpaceRoomInfo!.FetchAllStateAsync();
_semaphore.Release();
Status = null;
await base.OnParametersSetAsync();
}
private async Task Execute() {
- var hs = DmSpace.Homeserver;
- var dmSpaceRoom = new DMSpaceRoom(hs, DmSpace.DmSpaceConfiguration.DMSpaceId);
+ var hs = SetupData.Homeserver;
+ var dmSpaceRoom = new DMSpaceRoom(hs, SetupData.DmSpaceConfiguration!.DMSpaceId!);
+ await dmSpaceRoom.ImportNativeDMs();
NavigationManager.NavigateTo("/User/DMSpace/Setup?stage=3");
}
private async Task<RoomInfo> GetRoomInfo(GenericRoom room) {
- var roomInfo = new RoomInfo() {
- Room = room
- };
+ var roomInfo = new RoomInfo(room);
var roomMembers = new List<UserProfileWithId>();
roomInfo.CreationEventContent = await room.GetCreateEventAsync();
try {
@@ -168,12 +166,14 @@ else {
displayNames.Add(member.DisplayName);
roomInfo.RoomName = string.Join(", ", displayNames);
}
+
try {
string? roomIcon = (await room.GetAvatarUrlAsync())?.Url;
if (room is not null)
roomInfo.RoomIcon = roomIcon;
}
catch { }
+
return roomInfo;
}
diff --git a/MatrixUtils.Web/Pages/User/Profile.razor b/MatrixUtils.Web/Pages/User/Profile.razor
index 79b83ae..129f706 100644
--- a/MatrixUtils.Web/Pages/User/Profile.razor
+++ b/MatrixUtils.Web/Pages/User/Profile.razor
@@ -110,8 +110,7 @@
var room = Homeserver.GetRoom(roomId);
var roomNameTask = room.GetNameOrFallbackAsync();
var roomIconTask = room.GetAvatarUrlAsync();
- var roomInfo = new RoomInfo() {
- Room = room,
+ var roomInfo = new RoomInfo(room) {
OwnMembership = roomProfile
};
try {
|