PlotDirector/PlotLine/Controllers/FeatureRequestsController.cs

126 lines
4.1 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using PlotLine.Services;
using PlotLine.ViewModels;
namespace PlotLine.Controllers;
[Authorize]
public sealed class FeatureRequestsController(
ICurrentUserService currentUser,
IFeatureRequestService featureRequests,
IEmailService emails) : Controller
{
[HttpGet]
public async Task<IActionResult> Index()
{
var userId = RequireUserId();
var requests = await featureRequests.ListForUserAsync(userId);
return View(new FeatureRequestIndexViewModel { Requests = requests });
}
[HttpGet]
public IActionResult Create()
{
return View(new FeatureRequestCreateViewModel());
}
[HttpGet]
public async Task<IActionResult> Roadmap()
{
return View(new FeatureRequestRoadmapViewModel
{
Sections = await featureRequests.ListRoadmapAsync()
});
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(FeatureRequestCreateViewModel model)
{
if (!ModelState.IsValid)
{
return View(WithOptions(model));
}
var created = await featureRequests.CreateAsync(RequireUserId(), model);
TempData["FeatureRequestMessage"] = "Your feature request has been submitted.";
return RedirectToAction(nameof(Details), new { id = created.FeatureRequestID });
}
[HttpGet]
public async Task<IActionResult> Details(int id)
{
var request = await featureRequests.GetForUserAsync(id, RequireUserId());
if (request is null)
{
return NotFound();
}
await featureRequests.MarkViewedForUserAsync(request.FeatureRequestID, request.UserID);
request.UserNeedsAttention = false;
request.UserAttentionUtc = null;
return View(new FeatureRequestDetailsViewModel
{
Request = request,
Messages = await featureRequests.ListMessagesAsync(request.FeatureRequestID),
Reply = new FeatureRequestReplyViewModel { FeatureRequestID = request.FeatureRequestID }
});
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Reply([Bind(Prefix = "Reply")] FeatureRequestReplyViewModel model)
{
var userId = RequireUserId();
var request = await featureRequests.GetForUserAsync(model.FeatureRequestID, userId);
if (request is null)
{
return NotFound();
}
if (!ModelState.IsValid)
{
return View(nameof(Details), new FeatureRequestDetailsViewModel
{
Request = request,
Messages = await featureRequests.ListMessagesAsync(request.FeatureRequestID),
Reply = model
});
}
await featureRequests.AddUserReplyAsync(model.FeatureRequestID, userId, model.MessageBody);
await emails.SendFeatureRequestUserReplyAsync(
request,
Preview(model.MessageBody),
BuildAbsoluteUrl("FeatureRequestDetails", "Admin", new { id = request.FeatureRequestID }),
currentUser.Email ?? string.Empty);
return RedirectToAction(nameof(Details), new { id = model.FeatureRequestID });
}
private int RequireUserId()
{
return currentUser.UserId ?? throw new InvalidOperationException("Authenticated user id was not available.");
}
private static FeatureRequestCreateViewModel WithOptions(FeatureRequestCreateViewModel model)
{
model.AppAreas = PlotLine.Models.FeatureRequestOptions.AppAreas;
model.ImportanceOptions = PlotLine.Models.FeatureRequestOptions.ImportanceOptions;
return model;
}
private string BuildAbsoluteUrl(string action, string controller, object routeValues)
{
return Url.Action(action, controller, routeValues, Request.Scheme)
?? $"{Request.Scheme}://{Request.Host}";
}
private static string Preview(string value)
{
var cleaned = value.ReplaceLineEndings(" ").Trim();
return cleaned.Length <= 240 ? cleaned : $"{cleaned[..240]}...";
}
}