✨ Chat and realm members with status
This commit is contained in:
parent
8432436fcf
commit
661b612537
@ -75,6 +75,70 @@ public class AccountEventService(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<Guid, Status>> GetStatuses(List<Guid> userIds)
|
||||||
|
{
|
||||||
|
var results = new Dictionary<Guid, Status>();
|
||||||
|
var cacheMissUserIds = new List<Guid>();
|
||||||
|
|
||||||
|
foreach (var userId in userIds)
|
||||||
|
{
|
||||||
|
var cacheKey = $"{StatusCacheKey}{userId}";
|
||||||
|
var cachedStatus = await cache.GetAsync<Status>(cacheKey);
|
||||||
|
if (cachedStatus != null)
|
||||||
|
{
|
||||||
|
cachedStatus.IsOnline = !cachedStatus.IsInvisible && ws.GetAccountIsConnected(userId);
|
||||||
|
results[userId] = cachedStatus;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cacheMissUserIds.Add(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheMissUserIds.Any())
|
||||||
|
{
|
||||||
|
var now = SystemClock.Instance.GetCurrentInstant();
|
||||||
|
var statusesFromDb = await db.AccountStatuses
|
||||||
|
.Where(e => cacheMissUserIds.Contains(e.AccountId))
|
||||||
|
.Where(e => e.ClearedAt == null || e.ClearedAt > now)
|
||||||
|
.GroupBy(e => e.AccountId)
|
||||||
|
.Select(g => g.OrderByDescending(e => e.CreatedAt).First())
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var foundUserIds = new HashSet<Guid>();
|
||||||
|
|
||||||
|
foreach (var status in statusesFromDb)
|
||||||
|
{
|
||||||
|
var isOnline = ws.GetAccountIsConnected(status.AccountId);
|
||||||
|
status.IsOnline = !status.IsInvisible && isOnline;
|
||||||
|
results[status.AccountId] = status;
|
||||||
|
var cacheKey = $"{StatusCacheKey}{status.AccountId}";
|
||||||
|
await cache.SetAsync(cacheKey, status, TimeSpan.FromMinutes(5));
|
||||||
|
foundUserIds.Add(status.AccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var usersWithoutStatus = cacheMissUserIds.Except(foundUserIds).ToList();
|
||||||
|
if (usersWithoutStatus.Any())
|
||||||
|
{
|
||||||
|
foreach (var userId in usersWithoutStatus)
|
||||||
|
{
|
||||||
|
var isOnline = ws.GetAccountIsConnected(userId);
|
||||||
|
var defaultStatus = new Status
|
||||||
|
{
|
||||||
|
Attitude = StatusAttitude.Neutral,
|
||||||
|
IsOnline = isOnline,
|
||||||
|
IsCustomized = false,
|
||||||
|
Label = isOnline ? "Online" : "Offline",
|
||||||
|
AccountId = userId,
|
||||||
|
};
|
||||||
|
results[userId] = defaultStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Status> CreateStatus(Account user, Status status)
|
public async Task<Status> CreateStatus(Account user, Status status)
|
||||||
{
|
{
|
||||||
var now = SystemClock.Instance.GetCurrentInstant();
|
var now = SystemClock.Instance.GetCurrentInstant();
|
||||||
|
@ -22,7 +22,8 @@ public class ChatRoomController(
|
|||||||
ActionLogService als,
|
ActionLogService als,
|
||||||
NotificationService nty,
|
NotificationService nty,
|
||||||
RelationshipService rels,
|
RelationshipService rels,
|
||||||
IStringLocalizer<NotificationResource> localizer
|
IStringLocalizer<NotificationResource> localizer,
|
||||||
|
AccountEventService aes
|
||||||
) : ControllerBase
|
) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet("{id:guid}")]
|
[HttpGet("{id:guid}")]
|
||||||
@ -149,7 +150,7 @@ public class ChatRoomController(
|
|||||||
|
|
||||||
public class ChatRoomRequest
|
public class ChatRoomRequest
|
||||||
{
|
{
|
||||||
[Required][MaxLength(1024)] public string? Name { get; set; }
|
[Required] [MaxLength(1024)] public string? Name { get; set; }
|
||||||
[MaxLength(4096)] public string? Description { get; set; }
|
[MaxLength(4096)] public string? Description { get; set; }
|
||||||
[MaxLength(32)] public string? PictureId { get; set; }
|
[MaxLength(32)] public string? PictureId { get; set; }
|
||||||
[MaxLength(32)] public string? BackgroundId { get; set; }
|
[MaxLength(32)] public string? BackgroundId { get; set; }
|
||||||
@ -384,7 +385,7 @@ public class ChatRoomController(
|
|||||||
|
|
||||||
[HttpGet("{roomId:guid}/members")]
|
[HttpGet("{roomId:guid}/members")]
|
||||||
public async Task<ActionResult<List<ChatMember>>> ListMembers(Guid roomId, [FromQuery] int take = 20,
|
public async Task<ActionResult<List<ChatMember>>> ListMembers(Guid roomId, [FromQuery] int take = 20,
|
||||||
[FromQuery] int skip = 0)
|
[FromQuery] int skip = 0, [FromQuery] bool withStatus = false, [FromQuery] string? status = null)
|
||||||
{
|
{
|
||||||
var currentUser = HttpContext.Items["CurrentUser"] as Account.Account;
|
var currentUser = HttpContext.Items["CurrentUser"] as Account.Account;
|
||||||
|
|
||||||
@ -400,12 +401,39 @@ public class ChatRoomController(
|
|||||||
if (member is null) return StatusCode(403, "You need to be a member to see members of private chat room.");
|
if (member is null) return StatusCode(403, "You need to be a member to see members of private chat room.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = db.ChatMembers
|
IQueryable<ChatMember> query = db.ChatMembers
|
||||||
.Where(m => m.ChatRoomId == roomId)
|
.Where(m => m.ChatRoomId == roomId)
|
||||||
.Where(m => m.LeaveAt == null) // Add this condition to exclude left members
|
.Where(m => m.LeaveAt == null) // Add this condition to exclude left members
|
||||||
.Include(m => m.Account)
|
.Include(m => m.Account)
|
||||||
.Include(m => m.Account.Profile);
|
.Include(m => m.Account.Profile);
|
||||||
|
|
||||||
|
if (withStatus)
|
||||||
|
{
|
||||||
|
var members = await query
|
||||||
|
.OrderBy(m => m.JoinedAt)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var memberStatuses = await aes.GetStatuses(members.Select(m => m.AccountId).ToList());
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(status))
|
||||||
|
{
|
||||||
|
members = members.Where(m =>
|
||||||
|
memberStatuses.TryGetValue(m.AccountId, out var s) && s.Label != null &&
|
||||||
|
s.Label.Equals(status, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
members = members.OrderByDescending(m => memberStatuses.TryGetValue(m.AccountId, out var s) && s.IsOnline)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var total = members.Count;
|
||||||
|
Response.Headers.Append("X-Total", total.ToString());
|
||||||
|
|
||||||
|
var result = members.Skip(skip).Take(take).ToList();
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
var total = await query.CountAsync();
|
var total = await query.CountAsync();
|
||||||
Response.Headers.Append("X-Total", total.ToString());
|
Response.Headers.Append("X-Total", total.ToString());
|
||||||
|
|
||||||
@ -417,6 +445,9 @@ public class ChatRoomController(
|
|||||||
|
|
||||||
return Ok(members);
|
return Ok(members);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class ChatMemberRequest
|
public class ChatMemberRequest
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,8 @@ public class RealmController(
|
|||||||
RealmService rs,
|
RealmService rs,
|
||||||
FileReferenceService fileRefService,
|
FileReferenceService fileRefService,
|
||||||
RelationshipService rels,
|
RelationshipService rels,
|
||||||
ActionLogService als
|
ActionLogService als,
|
||||||
|
AccountEventService aes
|
||||||
) : Controller
|
) : Controller
|
||||||
{
|
{
|
||||||
[HttpGet("{slug}")]
|
[HttpGet("{slug}")]
|
||||||
@ -179,7 +180,9 @@ public class RealmController(
|
|||||||
public async Task<ActionResult<List<RealmMember>>> ListMembers(
|
public async Task<ActionResult<List<RealmMember>>> ListMembers(
|
||||||
string slug,
|
string slug,
|
||||||
[FromQuery] int offset = 0,
|
[FromQuery] int offset = 0,
|
||||||
[FromQuery] int take = 20
|
[FromQuery] int take = 20,
|
||||||
|
[FromQuery] bool withStatus = false,
|
||||||
|
[FromQuery] string? status = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var realm = await db.Realms
|
var realm = await db.Realms
|
||||||
@ -194,10 +197,39 @@ public class RealmController(
|
|||||||
return StatusCode(403, "You must be a member to view this realm's members.");
|
return StatusCode(403, "You must be a member to view this realm's members.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = db.RealmMembers
|
IQueryable<RealmMember> query = db.RealmMembers
|
||||||
.Where(m => m.RealmId == realm.Id)
|
.Where(m => m.RealmId == realm.Id)
|
||||||
.Where(m => m.LeaveAt == null);
|
.Where(m => m.LeaveAt == null)
|
||||||
|
.Include(m => m.Account)
|
||||||
|
.Include(m => m.Account.Profile);
|
||||||
|
|
||||||
|
if (withStatus)
|
||||||
|
{
|
||||||
|
var members = await query
|
||||||
|
.OrderBy(m => m.CreatedAt)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var memberStatuses = await aes.GetStatuses(members.Select(m => m.AccountId).ToList());
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(status))
|
||||||
|
{
|
||||||
|
members = members.Where(m =>
|
||||||
|
memberStatuses.TryGetValue(m.AccountId, out var s) && s.Label != null &&
|
||||||
|
s.Label.Equals(status, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
members = members.OrderByDescending(m => memberStatuses.TryGetValue(m.AccountId, out var s) && s.IsOnline)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var total = members.Count;
|
||||||
|
Response.Headers["X-Total"] = total.ToString();
|
||||||
|
|
||||||
|
var result = members.Skip(offset).Take(take).ToList();
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
var total = await query.CountAsync();
|
var total = await query.CountAsync();
|
||||||
Response.Headers["X-Total"] = total.ToString();
|
Response.Headers["X-Total"] = total.ToString();
|
||||||
|
|
||||||
@ -205,12 +237,13 @@ public class RealmController(
|
|||||||
.OrderBy(m => m.CreatedAt)
|
.OrderBy(m => m.CreatedAt)
|
||||||
.Skip(offset)
|
.Skip(offset)
|
||||||
.Take(take)
|
.Take(take)
|
||||||
.Include(m => m.Account)
|
|
||||||
.Include(m => m.Account.Profile)
|
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Ok(members);
|
return Ok(members);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("{slug}/members/me")]
|
[HttpGet("{slug}/members/me")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user