Unify browser story position follow events
This commit is contained in:
parent
9f0fdbc366
commit
90307757b4
@ -136,7 +136,19 @@ public sealed class WordCompanionController(
|
||||
response.BookId,
|
||||
response.ChapterId,
|
||||
response.SceneId,
|
||||
response.SceneUrl
|
||||
response.SceneUrl,
|
||||
response.Position
|
||||
});
|
||||
|
||||
await BroadcastToCurrentUserAsync("storyPositionChanged", new
|
||||
{
|
||||
Source = "WordCompanion",
|
||||
response.ProjectId,
|
||||
response.BookId,
|
||||
response.ChapterId,
|
||||
response.SceneId,
|
||||
CanonicalUrl = response.SceneUrl,
|
||||
response.Position
|
||||
});
|
||||
|
||||
return Ok(response);
|
||||
|
||||
@ -245,6 +245,7 @@ public sealed class WordCompanionRuntimeCurrentSceneResponse
|
||||
public int ChapterId { get; init; }
|
||||
public int SceneId { get; init; }
|
||||
public string SceneUrl { get; init; } = string.Empty;
|
||||
public StoryPosition? Position => StoryPosition.FromRuntimeCurrentScene(this);
|
||||
}
|
||||
|
||||
public sealed class WordCompanionRuntimeCharacterAliasDto
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
SET ANSI_NULLS ON;
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WordCompanion_Runtime_CurrentScene_Validate
|
||||
@UserID int,
|
||||
@DocumentGuid uniqueidentifier,
|
||||
@ProjectID int,
|
||||
@BookID int,
|
||||
@ChapterID int,
|
||||
@SceneID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
IF @DocumentGuid IS NULL
|
||||
OR @DocumentGuid = '00000000-0000-0000-0000-000000000000'
|
||||
OR @ProjectID <= 0
|
||||
OR @BookID <= 0
|
||||
OR @ChapterID <= 0
|
||||
OR @SceneID <= 0
|
||||
RETURN;
|
||||
|
||||
SELECT p.ProjectID AS ProjectId,
|
||||
b.BookID AS BookId,
|
||||
c.ChapterID AS ChapterId,
|
||||
s.SceneID AS SceneId,
|
||||
CONCAT(N'/Scenes/Details/', s.SceneID) AS SceneUrl
|
||||
FROM dbo.Projects p
|
||||
INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID
|
||||
INNER JOIN dbo.Books b ON b.ProjectID = p.ProjectID
|
||||
INNER JOIN dbo.Chapters c ON c.BookID = b.BookID
|
||||
INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID
|
||||
INNER JOIN dbo.ManuscriptDocuments md ON md.ProjectID = p.ProjectID
|
||||
AND md.BookID = b.BookID
|
||||
WHERE p.ProjectID = @ProjectID
|
||||
AND b.BookID = @BookID
|
||||
AND c.ChapterID = @ChapterID
|
||||
AND s.SceneID = @SceneID
|
||||
AND md.DocumentGuid = @DocumentGuid
|
||||
AND md.IsActive = 1
|
||||
AND s.IsLinkedToManuscript = 1
|
||||
AND s.ManuscriptDocumentID = md.ManuscriptDocumentID
|
||||
AND p.IsArchived = 0
|
||||
AND b.IsArchived = 0
|
||||
AND c.IsArchived = 0
|
||||
AND s.IsArchived = 0
|
||||
AND pua.UserID = @UserID
|
||||
AND pua.IsActive = 1;
|
||||
END;
|
||||
GO
|
||||
@ -1170,6 +1170,23 @@
|
||||
refreshOccupancyTrays();
|
||||
}
|
||||
|
||||
function applyStoryPosition(position) {
|
||||
if (!occupancySceneSelect || !position?.sceneId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextSceneId = String(position.sceneId);
|
||||
const option = [...occupancySceneSelect.options].find(item => item.value === nextSceneId);
|
||||
if (!option) {
|
||||
return;
|
||||
}
|
||||
|
||||
occupancySceneSelect.value = nextSceneId;
|
||||
setFloorPlanMode("occupancy");
|
||||
applyOccupancySceneFloor();
|
||||
refreshOccupancyTrays();
|
||||
}
|
||||
|
||||
function applyOccupancySceneFloor() {
|
||||
const option = occupancySceneSelect?.selectedOptions?.[0];
|
||||
const initialFloorId = option?.dataset.initialFloorId;
|
||||
@ -2429,6 +2446,10 @@
|
||||
toolboxTabs.forEach(tab => tab.addEventListener("click", () => setToolboxTab(tab.dataset.toolboxTab)));
|
||||
modeButtons.forEach(button => button.addEventListener("click", () => setFloorPlanMode(button.dataset.floorPlanMode)));
|
||||
occupancySceneSelect?.addEventListener("change", () => {
|
||||
window.PlotDirectorStoryPosition?.publishChanged({
|
||||
projectId: @Model.Project.ProjectID,
|
||||
sceneId: selectedOccupancySceneId()
|
||||
}, "FloorPlan");
|
||||
applyOccupancySceneFloor();
|
||||
refreshOccupancyTrays();
|
||||
});
|
||||
@ -2467,6 +2488,9 @@
|
||||
});
|
||||
addBlockForm?.addEventListener("submit", addBlockAsync);
|
||||
editor.addEventListener("floor-plan-restore-state", event => restoreWorkspaceState(event.detail || {}));
|
||||
document.addEventListener(window.PlotDirectorStoryPosition?.eventName || "plotdirector:story-position-changed", event => {
|
||||
applyStoryPosition(event.detail);
|
||||
});
|
||||
workspace?.addEventListener("submit", async event => {
|
||||
if (event.defaultPrevented) return;
|
||||
const form = event.target;
|
||||
|
||||
@ -63,6 +63,52 @@
|
||||
});
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const storyPositionChangedEventName = "plotdirector:story-position-changed";
|
||||
|
||||
const normaliseStoryPositionPayload = (payload, source) => {
|
||||
const position = payload?.position || payload?.Position || payload || {};
|
||||
const readNumber = (...values) => {
|
||||
for (const value of values) {
|
||||
const parsed = Number.parseInt(value || "", 10);
|
||||
if (Number.isInteger(parsed) && parsed > 0) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return {
|
||||
source: source || payload?.source || payload?.Source || "Navigation",
|
||||
projectId: readNumber(position.projectId, position.ProjectId, payload?.projectId, payload?.ProjectId),
|
||||
bookId: readNumber(position.bookId, position.BookId, payload?.bookId, payload?.BookId),
|
||||
chapterId: readNumber(position.chapterId, position.ChapterId, payload?.chapterId, payload?.ChapterId),
|
||||
sceneId: readNumber(position.sceneId, position.SceneId, payload?.sceneId, payload?.SceneId),
|
||||
canonicalUrl: payload?.canonicalUrl || payload?.CanonicalUrl || payload?.sceneUrl || payload?.SceneUrl || ""
|
||||
};
|
||||
};
|
||||
|
||||
const publishStoryPositionChanged = (payload, source) => {
|
||||
const detail = normaliseStoryPositionPayload(payload, source);
|
||||
if (!detail.projectId) {
|
||||
return detail;
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent(storyPositionChangedEventName, {
|
||||
bubbles: true,
|
||||
detail
|
||||
}));
|
||||
return detail;
|
||||
};
|
||||
|
||||
window.PlotDirectorStoryPosition = {
|
||||
eventName: storyPositionChangedEventName,
|
||||
normalise: normaliseStoryPositionPayload,
|
||||
publishChanged: publishStoryPositionChanged
|
||||
};
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const toggle = document.querySelector("[data-word-companion-follow-toggle]");
|
||||
if (!toggle || !window.signalR) {
|
||||
@ -160,7 +206,9 @@
|
||||
.build();
|
||||
|
||||
connection.on("wordCompanionSceneChanged", (payload) => {
|
||||
const sceneId = Number.parseInt(payload?.sceneId || payload?.SceneId || "", 10);
|
||||
const position = window.PlotDirectorStoryPosition?.publishChanged(payload, "WordCompanion")
|
||||
|| { sceneId: Number.parseInt(payload?.sceneId || payload?.SceneId || "", 10) };
|
||||
const sceneId = position.sceneId;
|
||||
if (!Number.isInteger(sceneId) || sceneId <= 0 || sceneId === lastSceneId) {
|
||||
return;
|
||||
}
|
||||
@ -170,6 +218,10 @@
|
||||
navigateIfAllowed(sceneUrl);
|
||||
});
|
||||
|
||||
connection.on("storyPositionChanged", (payload) => {
|
||||
window.PlotDirectorStoryPosition?.publishChanged(payload, payload?.source || payload?.Source || "WordCompanion");
|
||||
});
|
||||
|
||||
connection.on("wordCompanionCharactersCreated", (payload) => {
|
||||
const projectId = Number.parseInt(payload?.projectId || payload?.ProjectId || "", 10);
|
||||
if (!Number.isInteger(projectId) || projectId <= 0 || projectId === lastCharactersProjectId) {
|
||||
|
||||
2
PlotLine/wwwroot/js/site.min.js
vendored
2
PlotLine/wwwroot/js/site.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user