-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrated CommentThreads to Immediate.Apis
- 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
Showing
25 changed files
with
2,724 additions
and
337 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.