192 lines
6.6 KiB
C#
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()
|
|
?? [];
|
|
}
|
|
}
|