Skip to content

Commit

Permalink
Migrated CommentThreads to Immediate.Apis
Browse files Browse the repository at this point in the history
- Comment threads now get their initial data from the API, not from hacky embedded SSR data
- Sped up data loading by parallelizing requests for thread data, comments, and subscription status
- CommentsThread now has a computed `IsLocked` column to make the lock status more explicit than a nullable datetime
- Removed the ability for club owners and moderators to lock club comments
  • Loading branch information
Atulin committed Sep 12, 2024
1 parent 52eaa9c commit 702a0b1
Show file tree
Hide file tree
Showing 25 changed files with 2,724 additions and 337 deletions.
97 changes: 0 additions & 97 deletions Ogma3/Api/V1/CommentThreads/Commands/LockThread.cs

This file was deleted.

42 changes: 0 additions & 42 deletions Ogma3/Api/V1/CommentThreads/CommentsThreadController.cs

This file was deleted.

70 changes: 70 additions & 0 deletions Ogma3/Api/V1/CommentThreads/GetThreadDetails.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Immediate.Apis.Shared;
using Immediate.Handlers.Shared;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;
using Ogma3.Data;
using Ogma3.Data.Comments;
using Ogma3.Infrastructure.Constants;
using Ogma3.Infrastructure.Extensions;
using Ogma3.Services.UserService;

namespace Ogma3.Api.V1.CommentThreads;

using ReturnType = Results<UnauthorizedHttpResult, NotFound, Ok<GetThreadDetails.Result>>;

[Handler]
[MapGet("api/CommentsThread/{threadId:long}")]
public static partial class GetThreadDetails
{
[UsedImplicitly]
public sealed record Query(long ThreadId);

private static async ValueTask<ReturnType> HandleAsync(
Query request,
ApplicationDbContext context,
IUserService userService,
IHttpContextAccessor httpContextAccessor,
OgmaConfig config,
CancellationToken cancellationToken
)
{
var isStaff = userService.User?.HasAnyRole(RoleNames.Admin, RoleNames.Moderator) ?? false;

var perPage = config.CommentsPerPage;
const int minCommentLength = CTConfig.CComment.MinBodyLength;
const int maxCommentLength = CTConfig.CComment.MaxBodyLength;

var threadData = await context.CommentThreads
.Where(ct => ct.Id == request.ThreadId)
.Select(ct => new
{
Blogpost = ct.BlogpostId != null,
Profile = ct.UserId != null,
Chapter = ct.ChapterId != null,
Thread = ct.ClubThreadId != null,
Locked = ct.IsLocked,
})
.FirstOrDefaultAsync(cancellationToken);

if (threadData is null) return TypedResults.NotFound();

var source = threadData switch
{
{ Blogpost: true } => CommentSource.Blogpost,
{ Profile: true } => CommentSource.Profile,
{ Chapter: true } => CommentSource.Chapter,
{ Thread: true } => CommentSource.ForumPost,
_ => CommentSource.Invalid,
};

if (isStaff)
{
httpContextAccessor.HttpContext?.Response.Headers.Append("X-IsStaff", isStaff.ToString());
}

return TypedResults.Ok(new Result(perPage, minCommentLength, maxCommentLength, source, threadData.Locked));
}

public sealed record Result(int PerPage, int MinCommentLength, int MaxCommentLength, CommentSource Source, bool IsLocked);
}
69 changes: 69 additions & 0 deletions Ogma3/Api/V1/CommentThreads/LockThread.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Immediate.Apis.Shared;
using Immediate.Handlers.Shared;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;
using Ogma3.Data;
using Ogma3.Data.ModeratorActions;
using Ogma3.Infrastructure.Constants;
using Ogma3.Infrastructure.Extensions;
using Ogma3.Infrastructure.ServiceRegistrations;
using Ogma3.Services.UserService;

namespace Ogma3.Api.V1.CommentThreads;

using ReturnType = Results<UnauthorizedHttpResult, NotFound, Ok<bool>>;

[Handler]
[MapPost("api/CommentsThread/lock")]
[Authorize(Policy = AuthorizationPolicies.RequireAdminOrModeratorRole)]
public static partial class LockThread
{
[UsedImplicitly]
public sealed record Command(long ThreadId);

private static async ValueTask<ReturnType> HandleAsync(
Command request,
ApplicationDbContext context,
IUserService userService,
CancellationToken cancellationToken
)
{
if (userService.User is not {} user) return TypedResults.Unauthorized();
if (user.GetUsername() is not {} uname) return TypedResults.Unauthorized();
if (user.GetNumericId() is not {} uid) return TypedResults.Unauthorized();
if (!user.HasAnyRole(RoleNames.Admin, RoleNames.Moderator)) return TypedResults.Unauthorized();

var thread = await context.CommentThreads
.Where(ct => ct.Id == request.ThreadId)
.FirstOrDefaultAsync(cancellationToken);

if (thread is null) return TypedResults.NotFound();

thread.LockDate = thread.LockDate is null ? DateTime.Now : null;

string type;
if (thread.BlogpostId is not null) type = "blogpost";
else if (thread.ChapterId is not null) type = "chapter";
else if (thread.ClubThreadId is not null) type = "club";
else if (thread.UserId is not null) type = "user profile";
else type = "unknown";

var typeId = thread.BlogpostId ?? thread.ChapterId ?? thread.ClubThreadId ?? thread.UserId ?? 0;

var message = thread.IsLocked
? ModeratorActionTemplates.ThreadLocked(type, typeId, thread.Id, uname)
: ModeratorActionTemplates.ThreadUnlocked(type, typeId, thread.Id, uname);

context.ModeratorActions.Add(new ModeratorAction
{
StaffMemberId = uid,
Description = message,
});

await context.SaveChangesAsync(cancellationToken);

return TypedResults.Ok(thread.LockDate is not null);
}
}
23 changes: 0 additions & 23 deletions Ogma3/Api/V1/CommentThreads/Queries/GetLockStatus.cs

This file was deleted.

54 changes: 0 additions & 54 deletions Ogma3/Api/V1/CommentThreads/Queries/GetPermissions.cs

This file was deleted.

Loading

0 comments on commit 702a0b1

Please sign in to comment.