🐛 Fixes member issue
This commit is contained in:
313
DysonNetwork.Pass/Wallet/SubscriptionGiftController.cs
Normal file
313
DysonNetwork.Pass/Wallet/SubscriptionGiftController.cs
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NodaTime;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using DysonNetwork.Shared.Models;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass.Wallet;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/subscriptions/gifts")]
|
||||||
|
public class GiftController(SubscriptionService subscriptions, AppDatabase db) : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lists gifts purchased by the current user.
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("sent")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<List<SnWalletGift>>> ListSentGifts(
|
||||||
|
[FromQuery] int offset = 0,
|
||||||
|
[FromQuery] int take = 20
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var query = await subscriptions.GetGiftsByGifterAsync(currentUser.Id);
|
||||||
|
var totalCount = query.Count;
|
||||||
|
|
||||||
|
var gifts = query
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(take)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Response.Headers["X-Total"] = totalCount.ToString();
|
||||||
|
|
||||||
|
return gifts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lists gifts received by the current user (both direct and redeemed open gifts).
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("received")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<List<SnWalletGift>>> ListReceivedGifts(
|
||||||
|
[FromQuery] int offset = 0,
|
||||||
|
[FromQuery] int take = 20
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var gifts = await subscriptions.GetGiftsByRecipientAsync(currentUser.Id);
|
||||||
|
var totalCount = gifts.Count;
|
||||||
|
|
||||||
|
gifts = gifts
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(take)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Response.Headers["X-Total"] = totalCount.ToString();
|
||||||
|
|
||||||
|
return gifts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a specific gift by ID (only if user is the gifter or recipient).
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("{giftId}")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<SnWalletGift>> GetGift(Guid giftId)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var gift = await db.WalletGifts
|
||||||
|
.Include(g => g.Gifter)
|
||||||
|
.Include(g => g.Recipient)
|
||||||
|
.Include(g => g.Redeemer)
|
||||||
|
.Include(g => g.Subscription)
|
||||||
|
.Include(g => g.Coupon)
|
||||||
|
.FirstOrDefaultAsync(g => g.Id == giftId);
|
||||||
|
|
||||||
|
if (gift is null) return NotFound();
|
||||||
|
if (gift.GifterId != currentUser.Id && gift.RecipientId != currentUser.Id &&
|
||||||
|
!(gift.IsOpenGift && gift.RedeemerId == currentUser.Id))
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return gift;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a gift code is valid and redeemable.
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("check/{giftCode}")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<GiftCheckResponse>> CheckGiftCode(string giftCode)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var gift = await subscriptions.GetGiftByCodeAsync(giftCode);
|
||||||
|
if (gift is null) return NotFound("Gift code not found.");
|
||||||
|
|
||||||
|
var canRedeem = false;
|
||||||
|
var error = "";
|
||||||
|
|
||||||
|
if (gift.Status != DysonNetwork.Shared.Models.GiftStatus.Sent)
|
||||||
|
{
|
||||||
|
error = gift.Status switch
|
||||||
|
{
|
||||||
|
DysonNetwork.Shared.Models.GiftStatus.Created => "Gift has not been sent yet.",
|
||||||
|
DysonNetwork.Shared.Models.GiftStatus.Redeemed => "Gift has already been redeemed.",
|
||||||
|
DysonNetwork.Shared.Models.GiftStatus.Expired => "Gift has expired.",
|
||||||
|
DysonNetwork.Shared.Models.GiftStatus.Cancelled => "Gift has been cancelled.",
|
||||||
|
_ => "Gift is not redeemable."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (gift.ExpiresAt < SystemClock.Instance.GetCurrentInstant())
|
||||||
|
{
|
||||||
|
error = "Gift has expired.";
|
||||||
|
}
|
||||||
|
else if (!gift.IsOpenGift && gift.RecipientId != currentUser.Id)
|
||||||
|
{
|
||||||
|
error = "This gift is intended for someone else.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check if user already has this subscription type
|
||||||
|
var subscriptionInfo = SubscriptionTypeData
|
||||||
|
.SubscriptionDict.TryGetValue(gift.SubscriptionIdentifier, out var template)
|
||||||
|
? template
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (subscriptionInfo != null)
|
||||||
|
{
|
||||||
|
var subscriptionsInGroup = subscriptionInfo.GroupIdentifier is not null
|
||||||
|
? SubscriptionTypeData.SubscriptionDict
|
||||||
|
.Where(s => s.Value.GroupIdentifier == subscriptionInfo.GroupIdentifier)
|
||||||
|
.Select(s => s.Value.Identifier)
|
||||||
|
.ToArray()
|
||||||
|
: [gift.SubscriptionIdentifier];
|
||||||
|
|
||||||
|
var existingSubscription = await subscriptions.GetSubscriptionAsync(currentUser.Id, subscriptionsInGroup);
|
||||||
|
if (existingSubscription is not null)
|
||||||
|
{
|
||||||
|
error = "You already have an active subscription of this type.";
|
||||||
|
}
|
||||||
|
else if (subscriptionInfo.RequiredLevel > 0)
|
||||||
|
{
|
||||||
|
var profile = await db.AccountProfiles.FirstOrDefaultAsync(p => p.AccountId == currentUser.Id);
|
||||||
|
if (profile is null || profile.Level < subscriptionInfo.RequiredLevel)
|
||||||
|
{
|
||||||
|
error = $"Account level must be at least {subscriptionInfo.RequiredLevel} to redeem this gift.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
canRedeem = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
canRedeem = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GiftCheckResponse
|
||||||
|
{
|
||||||
|
GiftCode = giftCode,
|
||||||
|
SubscriptionIdentifier = gift.SubscriptionIdentifier,
|
||||||
|
CanRedeem = canRedeem,
|
||||||
|
Error = error,
|
||||||
|
Message = gift.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GiftCheckResponse
|
||||||
|
{
|
||||||
|
public string GiftCode { get; set; } = null!;
|
||||||
|
public string SubscriptionIdentifier { get; set; } = null!;
|
||||||
|
public bool CanRedeem { get; set; }
|
||||||
|
public string Error { get; set; } = null!;
|
||||||
|
public string? Message { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PurchaseGiftRequest
|
||||||
|
{
|
||||||
|
[Required] public string SubscriptionIdentifier { get; set; } = null!;
|
||||||
|
public Guid? RecipientId { get; set; }
|
||||||
|
[Required] public string PaymentMethod { get; set; } = null!;
|
||||||
|
[Required] public SnPaymentDetails PaymentDetails { get; set; } = null!;
|
||||||
|
public string? Message { get; set; }
|
||||||
|
public string? Coupon { get; set; }
|
||||||
|
public int? GiftDurationDays { get; set; } = 30; // Gift expires in 30 days by default
|
||||||
|
public int? SubscriptionDurationDays { get; set; } = 30; // Subscription lasts 30 days when redeemed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Purchases a gift subscription.
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("purchase")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<SnWalletGift>> PurchaseGift([FromBody] PurchaseGiftRequest request)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
Duration? giftDuration = null;
|
||||||
|
if (request.GiftDurationDays.HasValue)
|
||||||
|
giftDuration = Duration.FromDays(request.GiftDurationDays.Value);
|
||||||
|
|
||||||
|
Duration? subscriptionDuration = null;
|
||||||
|
if (request.SubscriptionDurationDays.HasValue)
|
||||||
|
subscriptionDuration = Duration.FromDays(request.SubscriptionDurationDays.Value);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var gift = await subscriptions.PurchaseGiftAsync(
|
||||||
|
currentUser,
|
||||||
|
request.RecipientId,
|
||||||
|
request.SubscriptionIdentifier,
|
||||||
|
request.PaymentMethod,
|
||||||
|
request.PaymentDetails,
|
||||||
|
request.Message,
|
||||||
|
request.Coupon,
|
||||||
|
giftDuration,
|
||||||
|
subscriptionDuration
|
||||||
|
);
|
||||||
|
|
||||||
|
return gift;
|
||||||
|
}
|
||||||
|
catch (ArgumentOutOfRangeException ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RedeemGiftRequest
|
||||||
|
{
|
||||||
|
[Required] public string GiftCode { get; set; } = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redeems a gift using its code, creating a subscription for the current user.
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("redeem")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<RedeemGiftResponse>> RedeemGift([FromBody] RedeemGiftRequest request)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var (gift, subscription) = await subscriptions.RedeemGiftAsync(currentUser, request.GiftCode);
|
||||||
|
|
||||||
|
return new RedeemGiftResponse
|
||||||
|
{
|
||||||
|
Gift = gift,
|
||||||
|
Subscription = subscription
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RedeemGiftResponse
|
||||||
|
{
|
||||||
|
public SnWalletGift Gift { get; set; } = null!;
|
||||||
|
public SnWalletSubscription Subscription { get; set; } = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks a gift as sent (ready for redemption).
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("{giftId}/send")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<SnWalletGift>> SendGift(Guid giftId)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var gift = await subscriptions.MarkGiftAsSentAsync(giftId, currentUser.Id);
|
||||||
|
return gift;
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cancels a gift before it's redeemed.
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("{giftId}/cancel")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<SnWalletGift>> CancelGift(Guid giftId)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var gift = await subscriptions.CancelGiftAsync(giftId, currentUser.Id);
|
||||||
|
return gift;
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -85,7 +85,7 @@ public partial class ChatController(
|
|||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member == null || member.Role < ChatMemberRole.Member)
|
if (member == null || member.Role < ChatMemberRole.Member)
|
||||||
return StatusCode(403, "You are not a member of this chat room.");
|
return StatusCode(403, "You are not a member of this chat room.");
|
||||||
@@ -127,7 +127,7 @@ public partial class ChatController(
|
|||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member == null || member.Role < ChatMemberRole.Member)
|
if (member == null || member.Role < ChatMemberRole.Member)
|
||||||
return StatusCode(403, "You are not a member of this chat room.");
|
return StatusCode(403, "You are not a member of this chat room.");
|
||||||
@@ -221,7 +221,8 @@ public partial class ChatController(
|
|||||||
.Select(a => Guid.Parse(a.Id))
|
.Select(a => Guid.Parse(a.Id))
|
||||||
.ToList();
|
.ToList();
|
||||||
var mentionedMembers = await db.ChatMembers
|
var mentionedMembers = await db.ChatMembers
|
||||||
.Where(m => mentionedId.Contains(m.AccountId))
|
.Where(m => m.ChatRoomId == roomId && mentionedId.Contains(m.AccountId))
|
||||||
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Select(m => m.Id)
|
.Select(m => m.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
message.MembersMentioned = mentionedMembers;
|
message.MembersMentioned = mentionedMembers;
|
||||||
@@ -321,7 +322,7 @@ public partial class ChatController(
|
|||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var isMember = await db.ChatMembers
|
var isMember = await db.ChatMembers
|
||||||
.AnyAsync(m => m.AccountId == accountId && m.ChatRoomId == roomId);
|
.AnyAsync(m => m.AccountId == accountId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null);
|
||||||
if (!isMember)
|
if (!isMember)
|
||||||
return StatusCode(403, "You are not a member of this chat room.");
|
return StatusCode(403, "You are not a member of this chat room.");
|
||||||
|
|
||||||
|
@@ -56,8 +56,7 @@ public class ChatRoomController(
|
|||||||
|
|
||||||
var chatRooms = await db.ChatMembers
|
var chatRooms = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId)
|
.Where(m => m.AccountId == accountId)
|
||||||
.Where(m => m.JoinedAt != null)
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Where(m => m.LeaveAt == null)
|
|
||||||
.Include(m => m.ChatRoom)
|
.Include(m => m.ChatRoom)
|
||||||
.Select(m => m.ChatRoom)
|
.Select(m => m.ChatRoom)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
@@ -166,7 +165,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; }
|
||||||
@@ -475,6 +474,7 @@ public class ChatRoomController(
|
|||||||
|
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == Guid.Parse(currentUser.Id) && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == Guid.Parse(currentUser.Id) && m.ChatRoomId == roomId)
|
||||||
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (member == null)
|
if (member == null)
|
||||||
@@ -496,13 +496,14 @@ public class ChatRoomController(
|
|||||||
{
|
{
|
||||||
if (currentUser is null) return Unauthorized();
|
if (currentUser is null) return Unauthorized();
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.FirstOrDefaultAsync(m => m.ChatRoomId == roomId && m.AccountId == Guid.Parse(currentUser.Id));
|
.Where(m => m.ChatRoomId == roomId && m.AccountId == Guid.Parse(currentUser.Id) && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
if (member is null) return StatusCode(403, "You need to be a member to see online count of private chat room.");
|
if (member is null) return StatusCode(403, "You need to be a member to see online count of private chat room.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var members = await db.ChatMembers
|
var members = await db.ChatMembers
|
||||||
.Where(m => m.ChatRoomId == roomId)
|
.Where(m => m.ChatRoomId == roomId)
|
||||||
.Where(m => m.LeaveAt == null)
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Select(m => m.AccountId)
|
.Select(m => m.AccountId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
@@ -530,13 +531,14 @@ public class ChatRoomController(
|
|||||||
{
|
{
|
||||||
if (currentUser is null) return Unauthorized();
|
if (currentUser is null) return Unauthorized();
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.FirstOrDefaultAsync(m => m.ChatRoomId == roomId && m.AccountId == Guid.Parse(currentUser.Id));
|
.Where(m => m.ChatRoomId == roomId && m.AccountId == Guid.Parse(currentUser.Id) && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
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
|
var query = db.ChatMembers
|
||||||
.Where(m => m.ChatRoomId == roomId)
|
.Where(m => m.ChatRoomId == roomId)
|
||||||
.Where(m => m.LeaveAt == null);
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null);
|
||||||
|
|
||||||
if (withStatus)
|
if (withStatus)
|
||||||
{
|
{
|
||||||
@@ -633,6 +635,7 @@ public class ChatRoomController(
|
|||||||
var chatMember = await db.ChatMembers
|
var chatMember = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId)
|
.Where(m => m.AccountId == accountId)
|
||||||
.Where(m => m.ChatRoomId == roomId)
|
.Where(m => m.ChatRoomId == roomId)
|
||||||
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (chatMember is null) return StatusCode(403, "You are not even a member of the targeted chat room.");
|
if (chatMember is null) return StatusCode(403, "You are not even a member of the targeted chat room.");
|
||||||
if (chatMember.Role < ChatMemberRole.Moderator)
|
if (chatMember.Role < ChatMemberRole.Moderator)
|
||||||
@@ -645,7 +648,7 @@ public class ChatRoomController(
|
|||||||
var hasExistingMember = await db.ChatMembers
|
var hasExistingMember = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == request.RelatedUserId)
|
.Where(m => m.AccountId == request.RelatedUserId)
|
||||||
.Where(m => m.ChatRoomId == roomId)
|
.Where(m => m.ChatRoomId == roomId)
|
||||||
.Where(m => m.LeaveAt == null)
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.AnyAsync();
|
.AnyAsync();
|
||||||
if (hasExistingMember)
|
if (hasExistingMember)
|
||||||
return BadRequest("This user has been joined the chat cannot be invited again.");
|
return BadRequest("This user has been joined the chat cannot be invited again.");
|
||||||
@@ -775,7 +778,7 @@ public class ChatRoomController(
|
|||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var targetMember = await db.ChatMembers
|
var targetMember = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (targetMember is null) return BadRequest("You have not joined this chat room.");
|
if (targetMember is null) return BadRequest("You have not joined this chat room.");
|
||||||
if (request.NotifyLevel is not null)
|
if (request.NotifyLevel is not null)
|
||||||
@@ -816,7 +819,7 @@ public class ChatRoomController(
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var targetMember = await db.ChatMembers
|
var targetMember = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == memberId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == memberId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (targetMember is null) return NotFound();
|
if (targetMember is null) return NotFound();
|
||||||
|
|
||||||
@@ -884,7 +887,7 @@ public class ChatRoomController(
|
|||||||
|
|
||||||
// Find the target member
|
// Find the target member
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == memberId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == memberId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member is null) return NotFound();
|
if (member is null) return NotFound();
|
||||||
|
|
||||||
@@ -929,7 +932,17 @@ public class ChatRoomController(
|
|||||||
var existingMember = await db.ChatMembers
|
var existingMember = await db.ChatMembers
|
||||||
.FirstOrDefaultAsync(m => m.AccountId == Guid.Parse(currentUser.Id) && m.ChatRoomId == roomId);
|
.FirstOrDefaultAsync(m => m.AccountId == Guid.Parse(currentUser.Id) && m.ChatRoomId == roomId);
|
||||||
if (existingMember != null)
|
if (existingMember != null)
|
||||||
|
{
|
||||||
|
if (existingMember.LeaveAt == null)
|
||||||
|
{
|
||||||
|
existingMember.LeaveAt = null;
|
||||||
|
db.Update(existingMember);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
_ = crs.PurgeRoomMembersCache(roomId);
|
||||||
|
return Ok(existingMember);
|
||||||
|
}
|
||||||
return BadRequest("You are already a member of this chat room.");
|
return BadRequest("You are already a member of this chat room.");
|
||||||
|
}
|
||||||
|
|
||||||
var newMember = new SnChatMember
|
var newMember = new SnChatMember
|
||||||
{
|
{
|
||||||
@@ -962,6 +975,7 @@ public class ChatRoomController(
|
|||||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Where(m => m.AccountId == Guid.Parse(currentUser.Id))
|
.Where(m => m.AccountId == Guid.Parse(currentUser.Id))
|
||||||
.Where(m => m.ChatRoomId == roomId)
|
.Where(m => m.ChatRoomId == roomId)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
@@ -981,6 +995,7 @@ public class ChatRoomController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
member.LeaveAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
member.LeaveAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
||||||
|
db.Update(member);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
await crs.PurgeRoomMembersCache(roomId);
|
await crs.PurgeRoomMembersCache(roomId);
|
||||||
|
|
||||||
|
@@ -45,7 +45,7 @@ public class ChatRoomService(
|
|||||||
if (member is not null) return member;
|
if (member is not null) return member;
|
||||||
|
|
||||||
member = await db.ChatMembers
|
member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == chatRoomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == chatRoomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Include(m => m.ChatRoom)
|
.Include(m => m.ChatRoom)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ public class ChatRoomService(
|
|||||||
? await db.ChatMembers
|
? await db.ChatMembers
|
||||||
.Where(m => directRoomsId.Contains(m.ChatRoomId))
|
.Where(m => directRoomsId.Contains(m.ChatRoomId))
|
||||||
.Where(m => m.AccountId != userId)
|
.Where(m => m.AccountId != userId)
|
||||||
.Where(m => m.LeaveAt == null)
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.ToListAsync()
|
.ToListAsync()
|
||||||
: [];
|
: [];
|
||||||
members = await LoadMemberAccounts(members);
|
members = await LoadMemberAccounts(members);
|
||||||
@@ -121,7 +121,7 @@ public class ChatRoomService(
|
|||||||
if (room.Type != ChatRoomType.DirectMessage) return room;
|
if (room.Type != ChatRoomType.DirectMessage) return room;
|
||||||
var members = await db.ChatMembers
|
var members = await db.ChatMembers
|
||||||
.Where(m => m.ChatRoomId == room.Id && m.AccountId != userId)
|
.Where(m => m.ChatRoomId == room.Id && m.AccountId != userId)
|
||||||
.Where(m => m.LeaveAt == null)
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
if (members.Count <= 0) return room;
|
if (members.Count <= 0) return room;
|
||||||
@@ -139,7 +139,8 @@ public class ChatRoomService(
|
|||||||
|
|
||||||
var maxRequiredRole = requiredRoles.Max();
|
var maxRequiredRole = requiredRoles.Max();
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.FirstOrDefaultAsync(m => m.ChatRoomId == roomId && m.AccountId == accountId);
|
.Where(m => m.ChatRoomId == roomId && m.AccountId == accountId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
return member?.Role >= maxRequiredRole;
|
return member?.Role >= maxRequiredRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -441,7 +441,7 @@ public partial class ChatService(
|
|||||||
public async Task ReadChatRoomAsync(Guid roomId, Guid userId)
|
public async Task ReadChatRoomAsync(Guid roomId, Guid userId)
|
||||||
{
|
{
|
||||||
var sender = await db.ChatMembers
|
var sender = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == userId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == userId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (sender is null) throw new ArgumentException("User is not a member of the chat room.");
|
if (sender is null) throw new ArgumentException("User is not a member of the chat room.");
|
||||||
|
|
||||||
@@ -452,7 +452,7 @@ public partial class ChatService(
|
|||||||
public async Task<int> CountUnreadMessage(Guid userId, Guid chatRoomId)
|
public async Task<int> CountUnreadMessage(Guid userId, Guid chatRoomId)
|
||||||
{
|
{
|
||||||
var sender = await db.ChatMembers
|
var sender = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == userId && m.ChatRoomId == chatRoomId)
|
.Where(m => m.AccountId == userId && m.ChatRoomId == chatRoomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Select(m => new { m.LastReadAt })
|
.Select(m => new { m.LastReadAt })
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (sender?.LastReadAt is null) return 0;
|
if (sender?.LastReadAt is null) return 0;
|
||||||
|
@@ -52,7 +52,7 @@ public class RealtimeCallController(
|
|||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (member == null || member.Role < ChatMemberRole.Member)
|
if (member == null || member.Role < ChatMemberRole.Member)
|
||||||
@@ -78,7 +78,7 @@ public class RealtimeCallController(
|
|||||||
// Check if the user is a member of the chat room
|
// Check if the user is a member of the chat room
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (member == null || member.Role < ChatMemberRole.Member)
|
if (member == null || member.Role < ChatMemberRole.Member)
|
||||||
@@ -151,7 +151,7 @@ public class RealtimeCallController(
|
|||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Include(m => m.ChatRoom)
|
.Include(m => m.ChatRoom)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member == null || member.Role < ChatMemberRole.Member)
|
if (member == null || member.Role < ChatMemberRole.Member)
|
||||||
@@ -171,7 +171,7 @@ public class RealtimeCallController(
|
|||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == accountId && m.ChatRoomId == roomId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member == null || member.Role < ChatMemberRole.Member)
|
if (member == null || member.Role < ChatMemberRole.Member)
|
||||||
return StatusCode(403, "You need to be a normal member to end a call.");
|
return StatusCode(403, "You need to be a normal member to end a call.");
|
||||||
|
@@ -105,10 +105,10 @@ public class RealmController(
|
|||||||
var hasExistingMember = await db.RealmMembers
|
var hasExistingMember = await db.RealmMembers
|
||||||
.Where(m => m.AccountId == Guid.Parse(relatedUser.Id))
|
.Where(m => m.AccountId == Guid.Parse(relatedUser.Id))
|
||||||
.Where(m => m.RealmId == realm.Id)
|
.Where(m => m.RealmId == realm.Id)
|
||||||
.Where(m => m.LeaveAt == null)
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.AnyAsync();
|
.AnyAsync();
|
||||||
if (hasExistingMember)
|
if (hasExistingMember)
|
||||||
return BadRequest("This user has been joined the realm or leave cannot be invited again.");
|
return BadRequest("This user already in the realm cannot be invited again.");
|
||||||
|
|
||||||
var member = new SnRealmMember
|
var member = new SnRealmMember
|
||||||
{
|
{
|
||||||
@@ -232,7 +232,7 @@ public class RealmController(
|
|||||||
|
|
||||||
var query = db.RealmMembers
|
var query = db.RealmMembers
|
||||||
.Where(m => m.RealmId == realm.Id)
|
.Where(m => m.RealmId == realm.Id)
|
||||||
.Where(m => m.LeaveAt == null);
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null);
|
||||||
|
|
||||||
if (withStatus)
|
if (withStatus)
|
||||||
{
|
{
|
||||||
@@ -289,6 +289,7 @@ public class RealmController(
|
|||||||
var member = await db.RealmMembers
|
var member = await db.RealmMembers
|
||||||
.Where(m => m.AccountId == accountId)
|
.Where(m => m.AccountId == accountId)
|
||||||
.Where(m => m.Realm.Slug == slug)
|
.Where(m => m.Realm.Slug == slug)
|
||||||
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (member is null) return NotFound();
|
if (member is null) return NotFound();
|
||||||
@@ -305,7 +306,7 @@ public class RealmController(
|
|||||||
var member = await db.RealmMembers
|
var member = await db.RealmMembers
|
||||||
.Where(m => m.AccountId == accountId)
|
.Where(m => m.AccountId == accountId)
|
||||||
.Where(m => m.Realm.Slug == slug)
|
.Where(m => m.Realm.Slug == slug)
|
||||||
.Where(m => m.JoinedAt != null)
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member is null) return NotFound();
|
if (member is null) return NotFound();
|
||||||
|
|
||||||
@@ -444,7 +445,7 @@ public class RealmController(
|
|||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
var member = await db.RealmMembers
|
var member = await db.RealmMembers
|
||||||
.Where(m => m.AccountId == accountId && m.RealmId == realm.Id && m.JoinedAt != null)
|
.Where(m => m.AccountId == accountId && m.RealmId == realm.Id && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member is null || member.Role < RealmMemberRole.Moderator)
|
if (member is null || member.Role < RealmMemberRole.Moderator)
|
||||||
return StatusCode(403, "You do not have permission to update this realm.");
|
return StatusCode(403, "You do not have permission to update this realm.");
|
||||||
@@ -555,7 +556,7 @@ public class RealmController(
|
|||||||
return StatusCode(403, "Only community realms can be joined without invitation.");
|
return StatusCode(403, "Only community realms can be joined without invitation.");
|
||||||
|
|
||||||
var existingMember = await db.RealmMembers
|
var existingMember = await db.RealmMembers
|
||||||
.Where(m => m.AccountId == Guid.Parse(currentUser.Id) && m.RealmId == realm.Id)
|
.Where(m => m.AccountId == Guid.Parse(currentUser.Id) && m.RealmId == realm.Id && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (existingMember is not null)
|
if (existingMember is not null)
|
||||||
return BadRequest("You are already a member of this realm.");
|
return BadRequest("You are already a member of this realm.");
|
||||||
@@ -600,7 +601,7 @@ public class RealmController(
|
|||||||
if (realm is null) return NotFound();
|
if (realm is null) return NotFound();
|
||||||
|
|
||||||
var member = await db.RealmMembers
|
var member = await db.RealmMembers
|
||||||
.Where(m => m.AccountId == memberId && m.RealmId == realm.Id)
|
.Where(m => m.AccountId == memberId && m.RealmId == realm.Id && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member is null) return NotFound();
|
if (member is null) return NotFound();
|
||||||
|
|
||||||
@@ -640,7 +641,7 @@ public class RealmController(
|
|||||||
if (realm is null) return NotFound();
|
if (realm is null) return NotFound();
|
||||||
|
|
||||||
var member = await db.RealmMembers
|
var member = await db.RealmMembers
|
||||||
.Where(m => m.AccountId == memberId && m.RealmId == realm.Id)
|
.Where(m => m.AccountId == memberId && m.RealmId == realm.Id && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member is null) return NotFound();
|
if (member is null) return NotFound();
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ public class RealmService(
|
|||||||
var realms = await db.RealmMembers
|
var realms = await db.RealmMembers
|
||||||
.Include(m => m.Realm)
|
.Include(m => m.Realm)
|
||||||
.Where(m => m.AccountId == accountId)
|
.Where(m => m.AccountId == accountId)
|
||||||
|
.Where(m => m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Select(m => m.Realm!.Id)
|
.Select(m => m.Realm!.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
@@ -67,7 +68,8 @@ public class RealmService(
|
|||||||
|
|
||||||
var maxRequiredRole = requiredRoles.Max();
|
var maxRequiredRole = requiredRoles.Max();
|
||||||
var member = await db.RealmMembers
|
var member = await db.RealmMembers
|
||||||
.FirstOrDefaultAsync(m => m.RealmId == realmId && m.AccountId == accountId);
|
.Where(m => m.RealmId == realmId && m.AccountId == accountId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
return member?.Role >= maxRequiredRole;
|
return member?.Role >= maxRequiredRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -360,7 +360,7 @@ public class BroadcastEventHandler(
|
|||||||
|
|
||||||
// Get user's joined chat rooms
|
// Get user's joined chat rooms
|
||||||
var userRooms = await db.ChatMembers
|
var userRooms = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == evt.AccountId && m.LeaveAt == null)
|
.Where(m => m.AccountId == evt.AccountId && m.JoinedAt != null && m.LeaveAt == null)
|
||||||
.Select(m => m.ChatRoomId)
|
.Select(m => m.ChatRoomId)
|
||||||
.ToListAsync(cancellationToken: stoppingToken);
|
.ToListAsync(cancellationToken: stoppingToken);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user