diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpace.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpace.razor
new file mode 100644
index 0000000..c0dc8a6
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpace.razor
@@ -0,0 +1,104 @@
+@page "/Labs/DMSpace/Setup"
+@using LibMatrix
+@using LibMatrix.Responses
+@using MatrixUtils.Abstractions
+@using MatrixUtils.LibDMSpace
+@using MatrixUtils.LibDMSpace.StateEvents
+@using MatrixUtils.Web.Pages.Labs.DMSpace.DMSpaceStages
+@using System.Text.Json.Serialization
+<h3>DM Space Management</h3>
+<hr/>
+<CascadingValue Value="@SetupData">
+ @switch (Stage) {
+ case -1:
+ <p>Initialising...</p>
+ break;
+ case 0:
+ <DMSpaceStage0/>
+ break;
+ case 1:
+ <DMSpaceStage1/>
+ break;
+ case 2:
+ <DMSpaceStage2/>
+ break;
+ case 3:
+ <DMSpaceStage3/>
+ break;
+ default:
+ <p>Stage is unknown value: @Stage!</p>
+ break;
+ }
+</CascadingValue>
+
+@code {
+ private int _stage = -1;
+
+ [Parameter, SupplyParameterFromQuery(Name = "stage")]
+ public int Stage {
+ get => _stage;
+ set {
+ _stage = value;
+ Console.WriteLine($"Stage is now {value}");
+ StateHasChanged();
+ }
+ }
+
+
+ public DMSpace? DMSpaceRootPage { get; set; }
+
+ protected override async Task OnInitializedAsync() {
+ if (NavigationManager.Uri.Contains("?stage=")) {
+ NavigationManager.NavigateTo(NavigationManager.Uri.Replace("stage=", ""), true); //"/User/DMSpace/Setup"
+ }
+ DMSpaceRootPage = this;
+ SetupData.Homeserver ??= await RMUStorage.GetCurrentSessionOrNavigate();
+ if (SetupData.Homeserver is null) return;
+ try {
+ 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 is "M_NOT_FOUND" or "M_FORBIDDEN") {
+ Stage = 0;
+ SetupData.DmSpaceConfiguration = new();
+ }
+ else throw;
+ }
+ finally {
+ StateHasChanged();
+ }
+ await base.OnInitializedAsync();
+ }
+
+ protected override async Task OnParametersSetAsync() {
+ StateHasChanged();
+ 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/Labs/DMSpace/DMSpaceStages/DMSpaceStage0.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage0.razor
new file mode 100644
index 0000000..5f6508c
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage0.razor
@@ -0,0 +1,11 @@
+<b>
+ <u>Welcome to the DM Space tool!</u>
+</b>
+<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/Setup?stage=1">Get started</LinkButton>
+
+@code {
+
+}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor
new file mode 100644
index 0000000..2176467
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage1.razor
@@ -0,0 +1,151 @@
+@using LibMatrix.Homeservers
+@using LibMatrix.RoomTypes
+@using LibMatrix
+@using LibMatrix.Responses
+@using MatrixUtils.LibDMSpace
+@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 (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: Setup data is null!</b>
+}
+
+
+@if (!string.IsNullOrWhiteSpace(Status)) {
+ <p>@Status</p>
+}
+
+@code {
+
+ private string? Status {
+ get => _status;
+ set {
+ _status = value;
+ StateHasChanged();
+ }
+ }
+
+ private string? _status;
+
+ [CascadingParameter]
+ public DMSpace.DMSpaceSetupData SetupData { get; set; }
+
+ SemaphoreSlim _semaphoreSlim = new(1, 1);
+
+ protected override async Task OnInitializedAsync() {
+ if (SetupData is null)
+ return;
+
+ await _semaphoreSlim.WaitAsync();
+
+ 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(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 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, RoomInfo roomInfo)?> GetFeasibleSpaces(GenericRoom room) {
+ try {
+ 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;
+ }
+
+ 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));
+ }
+ }
+
+ 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/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor
new file mode 100644
index 0000000..a70e9c5
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage2.razor
@@ -0,0 +1,240 @@
+@using LibMatrix.Homeservers
+@using LibMatrix.RoomTypes
+@using LibMatrix
+@using LibMatrix.EventTypes.Spec.State
+@using LibMatrix.Responses
+@using MatrixUtils.LibDMSpace
+@using MatrixUtils.LibDMSpace.StateEvents
+@using ArcaneLibs.Extensions
+@using System.Text.Json.Serialization
+@using MatrixUtils.Abstractions
+<b>
+ <u>DM Space setup tool - stage 2: Fix DM room attribution</u>
+</b>
+<p>This is just to make sure that your DMs are attributed to the right person!</p>
+
+@if (!string.IsNullOrWhiteSpace(Status)) {
+ <p>@Status</p>
+}
+
+@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: DMSpaceRootPage is null!</b>
+}
+
+<br/>
+<LinkButton OnClick="@Execute">Next</LinkButton>
+
+@{
+ var _offset = 0;
+}
+@foreach (var (room, usersList) in duplicateDmRooms) {
+ <ModalWindow Title="Duplicate room found" X="_offset += 30" Y="_offset">
+ <p>Found room assigned to multiple users: <RoomListItem RoomInfo="@room"></RoomListItem></p>
+ <p>Users:</p>
+ @foreach (var userProfileResponse in usersList) {
+ <LinkButton OnClick="@(() => SetRoomAssignment(room.Room.RoomId, userProfileResponse.Id))">
+ <span>Assign to </span>
+ <InlineUserItem User="userProfileResponse"></InlineUserItem>
+ </LinkButton>
+ <br/>
+ }
+ </ModalWindow>
+}
+
+@if (DmToReassign is not null) {
+ <ModalWindow Title="Re-assign DM" OnCloseClicked="@(() => DmToReassign = null)">
+ <RoomListItem RoomInfo="@DmToReassign"></RoomListItem>
+ @foreach (var userProfileResponse in roomMembers[DmToReassign]) {
+ <LinkButton OnClick="@(() => SetRoomAssignment(DmToReassign.Room.RoomId, userProfileResponse.Id))">
+ <span>Assign to </span>
+ <InlineUserItem User="userProfileResponse"></InlineUserItem>
+ </LinkButton>
+ <br/>
+ }
+ </ModalWindow>
+}
+
+@code {
+
+ private string newMxid { get; set; } = "";
+
+ private RoomInfo? DmToReassign {
+ get => _dmToReassign;
+ set {
+ _dmToReassign = value;
+ StateHasChanged();
+ }
+ }
+
+ private string? Status {
+ get => _status;
+ set {
+ _status = value;
+ StateHasChanged();
+ }
+ }
+
+ private string? _status;
+ private RoomInfo? _dmToReassign;
+
+ [CascadingParameter]
+ public DMSpace.DMSpaceSetupData SetupData { get; set; }
+
+ 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 OnInitializedAsync() {
+ if (SetupData is null)
+ return;
+ await _semaphore.WaitAsync();
+ DmToReassign = null;
+ var hs = SetupData.Homeserver;
+ Status = "Loading DM list from account data...";
+ 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) {
+ for (var i = rooms.Count - 1; i >= 0; i--) {
+ var roomId = rooms[i];
+ if (!joinedRooms.Contains(roomId))
+ rooms.RemoveAt(i);
+ }
+
+ dms[user] = rooms.Distinct().ToList();
+ }
+
+ 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;
+ DMSpace.DMSpaceSetupData.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);
+ // StateHasChanged();
+ }).ToAsyncEnumerable();
+ await foreach (var res in results) {
+ SetupData.DMRooms.Add(res.userProfile, res.roomList);
+ // Status = $"Listed {dmRooms.Count} users";
+ }
+
+ _semaphore.Release();
+ 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(SetupData.DMRooms.First(x => x.Value.Any(x => x.Room.RoomId == roomId)).Value.First(x => x.Room.RoomId == roomId), users);
+ }
+
+ // StateHasChanged();
+ Status = null;
+ await base.OnParametersSetAsync();
+ }
+
+ private async Task Execute() {
+ NavigationManager.NavigateTo("/User/DMSpace/Setup?stage=3");
+ }
+
+ private async Task<RoomInfo> GetRoomInfo(GenericRoom room) {
+ var roomInfo = new RoomInfo(room);
+ await roomInfo.FetchAllStateAsync();
+ roomMembers[roomInfo] = new();
+ // 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 });
+
+ try {
+ string? roomIcon = (await room.GetAvatarUrlAsync())?.Url;
+ if (room is not null)
+ roomInfo.RoomIcon = roomIcon;
+ }
+ catch { }
+
+ return roomInfo;
+ }
+
+ private async Task<List<RoomInfo>> GetRoomInfoForRooms(List<GenericRoom> rooms) {
+ var tasks = rooms.Select(GetRoomInfo).ToList();
+ await Task.WhenAll(tasks);
+ return tasks.Select(x => x.Result).ToList();
+ }
+
+ private async Task SetRoomAssignment(string roomId, string userId) {
+ var hs = SetupData.Homeserver;
+ Status = "Loading DM list from account data...";
+ 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))
+ dms.Add(userId, new());
+ dms[userId].Add(roomId);
+ 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();
+ }
+
+}
diff --git a/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor
new file mode 100644
index 0000000..865e956
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Labs/DMSpace/DMSpaceStages/DMSpaceStage3.razor
@@ -0,0 +1,191 @@
+@using LibMatrix.Homeservers
+@using LibMatrix.RoomTypes
+@using LibMatrix
+@using LibMatrix.EventTypes.Spec.State
+@using LibMatrix.Responses
+@using MatrixUtils.LibDMSpace
+@using MatrixUtils.LibDMSpace.StateEvents
+@using ArcaneLibs.Extensions
+@using System.Text.Json.Serialization
+@using MatrixUtils.Abstractions
+
+<b>
+ <u>DM Space setup tool - stage 3: Preview space layout</u>
+</b>
+<p>This gives you a preview of how your settings would impact layout!</p>
+
+@if (!string.IsNullOrWhiteSpace(Status)) {
+ <p>@Status</p>
+}
+
+@if (SetupData is not null) {
+ @if (SetupData.DMSpaceRoomInfo is not null) {
+ <p>
+ <InputCheckbox @bind-Value="SetupData.DmSpaceInfo.LayerByUser"></InputCheckbox>
+ Create sub-spaces per user
+ </p>
+ @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>
+ </div>
+ }
+ }
+ }
+ else {
+ <RoomListItem RoomInfo="@SetupData.DMSpaceRoomInfo"></RoomListItem>
+ @foreach (var (user, room) in SetupData.DMRooms.OrderBy(x => x.Key.DisplayName)) {
+ <div style="margin-left: 32px;">
+ @{
+ 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;">
+ <RoomListItem RoomInfo="@roomInfo"></RoomListItem>
+ </div>
+ }
+ }
+ }
+ }
+ else {
+ <b>Error: SetupData.DMSpaceRoomInfo is null!</b>
+ }
+}
+else {
+ <b>Error: DMSpaceRootPageConfiguration is null!</b>
+}
+
+<br/>
+<LinkButton OnClick="@Execute">Next</LinkButton>
+
+@code {
+
+ private string? Status {
+ get => _status;
+ set {
+ _status = value;
+ StateHasChanged();
+ }
+ }
+
+ private string? _status;
+
+ [CascadingParameter]
+ public DMSpace.DMSpaceSetupData SetupData { get; set; }
+
+ SemaphoreSlim _semaphore = new(1, 1);
+
+ protected override async Task OnInitializedAsync() {
+ if (SetupData is null)
+ return;
+ await _semaphore.WaitAsync();
+ 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 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 = 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);
+ var roomMembers = new List<UserProfileWithId>();
+ roomInfo.CreationEventContent = await room.GetCreateEventAsync();
+ try {
+ roomInfo.RoomName = await room.GetNameAsync();
+ }
+ catch { }
+
+ var membersEnum = room.GetMembersEnumerableAsync(true);
+ await foreach (var member in membersEnum)
+ if (member.TypedContent is RoomMemberEventContent memberEvent)
+ roomMembers.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)
+ 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;
+ }
+
+ private async Task<List<RoomInfo>> GetRoomInfoForRooms(List<GenericRoom> rooms) {
+ var tasks = rooms.Select(GetRoomInfo).ToList();
+ await Task.WhenAll(tasks);
+ return tasks.Select(x => x.Result).ToList();
+ }
+
+ private class UserProfileWithId : UserProfileResponse {
+ [JsonIgnore]
+ public string Id { get; set; }
+ }
+
+}
\ No newline at end of file
|