Новый дизайн, удаление
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Vortex.Modules.Identity.Domain;
|
||||
using Vortex.Modules.Identity.Infrastructure.Persistence;
|
||||
@@ -16,12 +17,18 @@ public sealed class StoriesController : ControllerBase
|
||||
private readonly IdentityDbContext _context;
|
||||
private readonly IUserContext _userContext;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly Microsoft.AspNetCore.SignalR.IHubContext<Vortex.Modules.Chats.Infrastructure.SignalR.ChatHub> _hubContext;
|
||||
|
||||
public StoriesController(IdentityDbContext context, IUserContext userContext, IUserRepository userRepository)
|
||||
public StoriesController(
|
||||
IdentityDbContext context,
|
||||
IUserContext userContext,
|
||||
IUserRepository userRepository,
|
||||
Microsoft.AspNetCore.SignalR.IHubContext<Vortex.Modules.Chats.Infrastructure.SignalR.ChatHub> hubContext)
|
||||
{
|
||||
_context = context;
|
||||
_userContext = userContext;
|
||||
_userRepository = userRepository;
|
||||
_hubContext = hubContext;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@@ -37,17 +44,25 @@ public sealed class StoriesController : ControllerBase
|
||||
friendIds.Add(currentUserId);
|
||||
|
||||
var stories = await _context.Stories
|
||||
.Include(s => s.Viewers)
|
||||
.Where(s => s.ExpiresAt > DateTime.UtcNow && friendIds.Contains(s.UserId))
|
||||
.OrderByDescending(s => s.CreatedAt)
|
||||
.ToListAsync(ct);
|
||||
|
||||
var groups = stories.GroupBy(s => s.UserId);
|
||||
var groups = stories.GroupBy(s => s.UserId).ToList();
|
||||
var result = new List<object>();
|
||||
|
||||
var userIds = groups.Select(g => g.Key).ToList();
|
||||
var userMap = new Dictionary<Guid, dynamic>();
|
||||
foreach (var uid in userIds)
|
||||
{
|
||||
var u = await _userRepository.GetByIdAsync(uid, ct);
|
||||
if (u != null) userMap[uid] = u;
|
||||
}
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(group.Key, ct);
|
||||
if (user == null) continue;
|
||||
if (!userMap.TryGetValue(group.Key, out var user)) continue;
|
||||
|
||||
result.Add(new
|
||||
{
|
||||
@@ -67,10 +82,10 @@ public sealed class StoriesController : ControllerBase
|
||||
bgColor = s.BgColor,
|
||||
createdAt = s.CreatedAt,
|
||||
expiresAt = s.ExpiresAt,
|
||||
viewCount = 0,
|
||||
viewed = false
|
||||
}).ToList(),
|
||||
hasUnviewed = true
|
||||
viewCount = s.Viewers.Count,
|
||||
viewed = s.Viewers.Any(v => v.UserId == currentUserId)
|
||||
}).OrderBy(s => s.createdAt).ToList(),
|
||||
hasUnviewed = group.Any(s => !s.Viewers.Any(v => v.UserId == currentUserId))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,6 +114,84 @@ public sealed class StoriesController : ControllerBase
|
||||
|
||||
return Ok(new { id = story.Id });
|
||||
}
|
||||
|
||||
[HttpPost("{id}/view")]
|
||||
public async Task<IActionResult> ViewStory(Guid id, CancellationToken ct)
|
||||
{
|
||||
var story = await _context.Stories
|
||||
.Include(s => s.Viewers)
|
||||
.FirstOrDefaultAsync(s => s.Id == id, ct);
|
||||
|
||||
if (story == null) return NotFound();
|
||||
if (story.UserId == _userContext.UserId) return Ok(new { message = "Owner view" });
|
||||
|
||||
if (!story.Viewers.Any(v => v.UserId == _userContext.UserId))
|
||||
{
|
||||
story.AddViewer(_userContext.UserId);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
|
||||
// Notify owner via SignalR
|
||||
var viewer = await _userRepository.GetByIdAsync(_userContext.UserId, ct);
|
||||
await _hubContext.Clients.User(story.UserId.ToString()).SendAsync("story_viewed", new
|
||||
{
|
||||
storyId = story.Id,
|
||||
userId = _userContext.UserId,
|
||||
username = viewer?.Username,
|
||||
displayName = viewer?.DisplayName,
|
||||
avatar = viewer?.Avatar,
|
||||
viewedAt = DateTime.UtcNow,
|
||||
viewCount = story.Viewers.Count
|
||||
}, ct);
|
||||
}
|
||||
|
||||
return Ok(new { message = "Story viewed" });
|
||||
}
|
||||
|
||||
[HttpGet("{id}/viewers")]
|
||||
public async Task<IActionResult> GetStoryViewers(Guid id, CancellationToken ct)
|
||||
{
|
||||
var story = await _context.Stories
|
||||
.Include(s => s.Viewers)
|
||||
.FirstOrDefaultAsync(s => s.Id == id, ct);
|
||||
|
||||
if (story == null) return NotFound();
|
||||
if (story.UserId != _userContext.UserId) return Forbid();
|
||||
|
||||
var viewerIds = story.Viewers.Select(v => v.UserId).ToList();
|
||||
var viewers = new List<object>();
|
||||
|
||||
foreach (var viewerId in viewerIds)
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(viewerId, ct);
|
||||
if (user == null) continue;
|
||||
|
||||
var viewerRecord = story.Viewers.First(v => v.UserId == viewerId);
|
||||
|
||||
viewers.Add(new
|
||||
{
|
||||
userId = user.Id,
|
||||
username = user.Username,
|
||||
displayName = user.DisplayName,
|
||||
avatar = user.Avatar,
|
||||
viewedAt = viewerRecord.ViewedAt
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(viewers);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteStory(Guid id, CancellationToken ct)
|
||||
{
|
||||
var story = await _context.Stories.FindAsync(new object[] { id }, ct);
|
||||
if (story == null) return NotFound();
|
||||
if (story.UserId != _userContext.UserId) return Forbid();
|
||||
|
||||
_context.Stories.Remove(story);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
|
||||
return Ok(new { message = "Story deleted" });
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record CreateStoryRequest(string Type, string? MediaUrl, string? Content, string? BgColor);
|
||||
|
||||
Reference in New Issue
Block a user