about summary refs log tree commit diff
path: root/MatrixUtils.Web/Pages/User
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2024-02-23 13:57:06 +0100
committerRory& <root@rory.gay>2024-02-23 13:57:06 +0100
commitd0d11db2209a8be65c27e15ca9d8a3b594f1a352 (patch)
treeb42b7de4b09888a1439d0939707ba1331becf626 /MatrixUtils.Web/Pages/User
parentHS emulator (diff)
downloadMatrixUtils-d0d11db2209a8be65c27e15ca9d8a3b594f1a352.tar.xz
Add eons of work because I forgot to push
Diffstat (limited to 'MatrixUtils.Web/Pages/User')
-rw-r--r--MatrixUtils.Web/Pages/User/DMManager.razor22
-rw-r--r--MatrixUtils.Web/Pages/User/DMSpace.razor55
-rw-r--r--MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage0.razor2
-rw-r--r--MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage1.razor149
-rw-r--r--MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage2.razor116
-rw-r--r--MatrixUtils.Web/Pages/User/DMSpaceStages/DMSpaceStage3.razor142
-rw-r--r--MatrixUtils.Web/Pages/User/Profile.razor3
7 files changed, 271 insertions, 218 deletions
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 {