diff --git a/DysonNetwork.Sphere/Chat/ChatRoom.cs b/DysonNetwork.Sphere/Chat/ChatRoom.cs index e7f0941..d26733b 100644 --- a/DysonNetwork.Sphere/Chat/ChatRoom.cs +++ b/DysonNetwork.Sphere/Chat/ChatRoom.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; using DysonNetwork.Sphere.Storage; using NodaTime; @@ -28,6 +29,11 @@ public class ChatRoom : ModelBase public long? RealmId { get; set; } public Realm.Realm? Realm { get; set; } + + [NotMapped] + [JsonPropertyName("members")] + public ICollection DirectMembers { get; set; } = + new List(); } public enum ChatMemberRole @@ -58,4 +64,38 @@ public class ChatMember : ModelBase public ChatMemberNotify Notify { get; set; } = ChatMemberNotify.All; public Instant? JoinedAt { get; set; } public bool IsBot { get; set; } = false; +} + +public class ChatMemberTransmissionObject : ModelBase +{ + public Guid Id { get; set; } + public long ChatRoomId { get; set; } + public long AccountId { get; set; } + public Account.Account Account { get; set; } = null!; + + [MaxLength(1024)] public string? Nick { get; set; } + + public ChatMemberRole Role { get; set; } = ChatMemberRole.Member; + public ChatMemberNotify Notify { get; set; } = ChatMemberNotify.All; + public Instant? JoinedAt { get; set; } + public bool IsBot { get; set; } = false; + + public static ChatMemberTransmissionObject FromEntity(ChatMember member) + { + return new ChatMemberTransmissionObject + { + Id = member.Id, + ChatRoomId = member.ChatRoomId, + AccountId = member.AccountId, + Account = member.Account, + Nick = member.Nick, + Role = member.Role, + Notify = member.Notify, + JoinedAt = member.JoinedAt, + IsBot = member.IsBot, + CreatedAt = member.CreatedAt, + UpdatedAt = member.UpdatedAt, + DeletedAt = member.DeletedAt + }; + } } \ No newline at end of file diff --git a/DysonNetwork.Sphere/Chat/ChatRoomController.cs b/DysonNetwork.Sphere/Chat/ChatRoomController.cs index 904e9bc..a3b1043 100644 --- a/DysonNetwork.Sphere/Chat/ChatRoomController.cs +++ b/DysonNetwork.Sphere/Chat/ChatRoomController.cs @@ -20,6 +20,16 @@ public class ChatRoomController(AppDatabase db, FileService fs, ChatRoomService .Include(e => e.Realm) .FirstOrDefaultAsync(); if (chatRoom is null) return NotFound(); + if (chatRoom.Type != ChatRoomType.DirectMessage) return Ok(chatRoom); + + // Preload members for direct messages + var currentUser = HttpContext.Items["CurrentUser"] as Account.Account; + var directMembers = await db.ChatMembers + .Where(m => (currentUser != null && m.AccountId != currentUser!.Id)) + .Include(m => m.Account) + .Include(m => m.Account.Profile) + .ToListAsync(); + chatRoom.DirectMembers = directMembers.Select(ChatMemberTransmissionObject.FromEntity).ToList(); return Ok(chatRoom); } @@ -55,21 +65,22 @@ public class ChatRoomController(AppDatabase db, FileService fs, ChatRoomService var result = chatRooms.Select(r => { if (r.Type == ChatRoomType.DirectMessage && directMembers.TryGetValue(r.Id, out var otherMember)) - r.Members = new List { otherMember }; + r.DirectMembers = new List + { ChatMemberTransmissionObject.FromEntity(otherMember) }; return r; }).ToList(); return Ok(result); } - public class DmCreationRequest + public class DirectMessageRequest { [Required] public long RelatedUserId { get; set; } } [HttpPost("direct")] [Authorize] - public async Task> CreateDirectMessage([FromBody] DmCreationRequest request) + public async Task> CreateDirectMessage([FromBody] DirectMessageRequest request) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); diff --git a/DysonNetwork.Sphere/Realm/Realm.cs b/DysonNetwork.Sphere/Realm/Realm.cs index 82bda68..a6bbf1e 100644 --- a/DysonNetwork.Sphere/Realm/Realm.cs +++ b/DysonNetwork.Sphere/Realm/Realm.cs @@ -41,9 +41,9 @@ public enum RealmMemberRole public class RealmMember : ModelBase { public long RealmId { get; set; } - [JsonIgnore] public Realm Realm { get; set; } = null!; + public Realm Realm { get; set; } = null!; public long AccountId { get; set; } - [JsonIgnore] public Account.Account Account { get; set; } = null!; + public Account.Account Account { get; set; } = null!; public RealmMemberRole Role { get; set; } = RealmMemberRole.Normal; public Instant? JoinedAt { get; set; } diff --git a/DysonNetwork.Sphere/Realm/RealmController.cs b/DysonNetwork.Sphere/Realm/RealmController.cs index 739d1fd..1766ca2 100644 --- a/DysonNetwork.Sphere/Realm/RealmController.cs +++ b/DysonNetwork.Sphere/Realm/RealmController.cs @@ -39,8 +39,6 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) : return members.ToList(); } - - [HttpGet("/")] [HttpGet("invites")] [Authorize] @@ -147,27 +145,84 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs) : return NoContent(); } + + + [HttpGet("{slug}/members")] + public async Task>> ListMembers( + string slug, + [FromQuery] int offset = 0, + [FromQuery] int take = 20 + ) + { + var realm = await db.Realms + .Where(r => r.Slug == slug) + .FirstOrDefaultAsync(); + if (realm is null) return NotFound(); - [HttpDelete("{slug}/members/me")] + if (!realm.IsPublic) + { + if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); + var isMember = await db.RealmMembers + .AnyAsync(m => m.AccountId == currentUser.Id && m.RealmId == realm.Id && m.JoinedAt != null); + if (!isMember) return StatusCode(403, "You must be a member to view this realm's members."); + } + + var query = db.RealmMembers + .Where(m => m.RealmId == realm.Id) + .Where(m => m.JoinedAt != null); + + var total = await query.CountAsync(); + Response.Headers["X-Total"] = total.ToString(); + + var members = await query + .OrderBy(m => m.CreatedAt) + .Skip(offset) + .Take(take) + .Include(m => m.Account) + .Include(m => m.Account.Profile) + .ToListAsync(); + + return Ok(members); + } + + [HttpGet("{slug}/members/me")] [Authorize] - public async Task LeaveRealm(string slug) + public async Task> GetCurrentIdentity(string slug) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); var userId = currentUser.Id; + var member = await db.RealmMembers + .Where(m => m.AccountId == userId) + .Where(m => m.Realm.Slug == slug) + .Include(m => m.Account) + .Include(m => m.Account.Profile) + .FirstOrDefaultAsync(); + + if (member is null) return NotFound(); + return Ok(member); + } + + [HttpDelete("{slug}/members/me")] + [Authorize] + public async Task LeaveRealm(string slug) + { + if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); + var userId = currentUser.Id; + var member = await db.RealmMembers .Where(m => m.AccountId == userId) .Where(m => m.Realm.Slug == slug) .Where(m => m.JoinedAt != null) .FirstOrDefaultAsync(); if (member is null) return NotFound(); - + if (member.Role == RealmMemberRole.Owner) return StatusCode(403, "Owner cannot leave their own realm."); - + db.RealmMembers.Remove(member); await db.SaveChangesAsync(); - + return NoContent(); }