PlotDirector/PlotLine/Controllers/WritingController.cs

192 lines
6.6 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using PlotLine.Models;
using PlotLine.Services;
using PlotLine.ViewModels;
namespace PlotLine.Controllers;
[Authorize]
[Route("Writing")]
public sealed class WritingController(
IStoryContextService storyContext,
IRelationshipMapService relationshipMap,
IFloorPlanService floorPlans,
IWriterWorkspaceService writerWorkspace) : Controller
{
[HttpGet("Context")]
public async Task<IActionResult> Context(int? projectId, int? bookId, int? chapterId, int? sceneId)
{
var model = new WritingContextPageViewModel
{
ProjectID = projectId,
BookID = bookId,
ChapterID = chapterId,
SceneID = sceneId,
Display = sceneId.HasValue
? await BuildDisplayAsync(projectId, bookId, chapterId, sceneId, "Manual context")
: new WritingContextDisplayViewModel { FollowStatus = "Connected, waiting for scene" }
};
return View(model);
}
[HttpGet("Intent")]
public async Task<IActionResult> Intent(int? projectId, int? bookId, int? chapterId, int? sceneId)
{
var model = new WritingIntentPageViewModel
{
ProjectID = projectId,
BookID = bookId,
ChapterID = chapterId,
SceneID = sceneId,
Display = sceneId.HasValue || chapterId.HasValue
? await BuildIntentDisplayAsync(projectId, bookId, chapterId, sceneId, "Manual intent")
: new WritingIntentDisplayViewModel { FollowStatus = "Connected, waiting for scene" }
};
return View(model);
}
[HttpGet("Context/Scene")]
public async Task<IActionResult> ContextScene(int? projectId, int? bookId, int? chapterId, int? sceneId)
{
if (!sceneId.HasValue)
{
return PartialView("~/Views/Writing/_WritingContextContent.cshtml", new WritingContextDisplayViewModel
{
FollowStatus = "Connected, waiting for scene"
});
}
var model = await BuildDisplayAsync(projectId, bookId, chapterId, sceneId, "Following Word");
return model?.HasScene == true
? PartialView("~/Views/Writing/_WritingContextContent.cshtml", model)
: NotFound();
}
[HttpGet("Intent/Scene")]
public async Task<IActionResult> IntentScene(int? projectId, int? bookId, int? chapterId, int? sceneId)
{
if (!sceneId.HasValue && !chapterId.HasValue)
{
return PartialView("~/Views/Writing/_WritingIntentContent.cshtml", new WritingIntentDisplayViewModel
{
FollowStatus = "Connected, waiting for scene"
});
}
var model = await BuildIntentDisplayAsync(projectId, bookId, chapterId, sceneId, "Following Word");
return model is not null && (model.HasScene || model.HasChapter)
? PartialView("~/Views/Writing/_WritingIntentContent.cshtml", model)
: NotFound();
}
private async Task<WritingContextDisplayViewModel?> BuildDisplayAsync(int? projectId, int? bookId, int? chapterId, int? sceneId, string followStatus)
{
if (!sceneId.HasValue)
{
return new WritingContextDisplayViewModel { FollowStatus = followStatus };
}
var context = await storyContext.GetForSceneAsync(sceneId.Value, projectId, bookId);
if (context?.Scene is null)
{
return null;
}
var position = context.Position;
var relationships = await relationshipMap.GetAsync(position, null, true);
var floorPlan = context.Scene.FloorPlanID.HasValue
? await floorPlans.GetEditorAsync(context.Scene.FloorPlanID.Value)
: null;
return new WritingContextDisplayViewModel
{
Context = context,
RelationshipMap = relationships,
FloorPlan = floorPlan,
FollowStatus = followStatus,
SceneOptions = BuildSceneOptions(context.Scene)
};
}
private async Task<WritingIntentDisplayViewModel?> BuildIntentDisplayAsync(int? projectId, int? bookId, int? chapterId, int? sceneId, string followStatus)
{
StoryContextViewModel? context = null;
if (sceneId.HasValue)
{
context = await storyContext.GetForSceneAsync(sceneId.Value, projectId, bookId);
if (context?.Scene is null)
{
return null;
}
}
var resolvedChapterId = context?.Position.ChapterId ?? chapterId;
var chapterWorkflow = resolvedChapterId.HasValue
? await writerWorkspace.GetChapterWorkflowAsync(resolvedChapterId.Value)
: null;
if (chapterWorkflow is not null)
{
if (projectId.HasValue && chapterWorkflow.Project.ProjectID != projectId.Value)
{
return null;
}
if (bookId.HasValue && chapterWorkflow.Book.BookID != bookId.Value)
{
return null;
}
}
if (context is null && chapterWorkflow is null)
{
return null;
}
return new WritingIntentDisplayViewModel
{
Context = context,
ChapterWorkflow = chapterWorkflow,
FollowStatus = followStatus,
SceneOptions = context?.Scene is not null
? BuildSceneOptions(context.Scene)
: BuildSceneOptions(chapterWorkflow)
};
}
private static IReadOnlyList<SelectListItem> BuildSceneOptions(SceneEditViewModel scene)
{
var options = scene.SceneOptions
.Select(x => new SelectListItem(x.Text, x.Value, x.Value == scene.SceneID.ToString()))
.ToList();
if (!options.Any(x => x.Value == scene.SceneID.ToString()))
{
options.Insert(0, new SelectListItem(
$"{scene.Book?.BookDisplayTitle ?? "Book"} / Ch {scene.Chapter?.ChapterNumber:g} / Scene {scene.SceneNumber:g}: {scene.SceneTitle}",
scene.SceneID.ToString(),
true));
}
return options
.OrderByDescending(x => x.Selected)
.ThenBy(x => x.Text)
.ToList();
}
private static IReadOnlyList<SelectListItem> BuildSceneOptions(ChapterWorkflowViewModel? chapterWorkflow)
{
return chapterWorkflow?.Scenes
.Select(x => new SelectListItem(
$"Scene {x.SceneNumber:g}: {x.SceneTitle}",
x.SceneID.ToString()))
.OrderBy(x => x.Text)
.ToList()
?? [];
}
}