166 lines
5.6 KiB
JavaScript
166 lines
5.6 KiB
JavaScript
(() => {
|
|
const root = document.querySelector("[data-writing-context]");
|
|
if (!root) {
|
|
return;
|
|
}
|
|
|
|
const contentHost = root.querySelector("[data-writing-context-content]");
|
|
const statusText = root.querySelector("[data-writing-context-status-text]");
|
|
const endpoint = root.dataset.contextUrl || "/Writing/Context/Scene";
|
|
const tabStorageKey = "plotdirector.writingContext.activeTab";
|
|
const scopedProjectId = Number.parseInt(root.dataset.scopeProjectId || "", 10);
|
|
const scopedBookId = Number.parseInt(root.dataset.scopeBookId || "", 10);
|
|
let currentSceneId = Number.parseInt(contentHost?.querySelector("[data-writing-context-fragment]")?.dataset.sceneId || "", 10);
|
|
let loadingSceneId = null;
|
|
|
|
const setStatus = (value) => {
|
|
if (statusText && value) {
|
|
statusText.textContent = value;
|
|
}
|
|
};
|
|
|
|
const activeTab = () => {
|
|
const active = contentHost?.querySelector("[data-writing-context-tab].active")?.dataset.writingContextTab;
|
|
if (active) {
|
|
return active;
|
|
}
|
|
|
|
try {
|
|
return localStorage.getItem(tabStorageKey) || "overview";
|
|
} catch {
|
|
return "overview";
|
|
}
|
|
};
|
|
|
|
const activateTab = (tabName) => {
|
|
const nextTab = tabName || "overview";
|
|
contentHost?.querySelectorAll("[data-writing-context-tab]").forEach((button) => {
|
|
const active = button.dataset.writingContextTab === nextTab;
|
|
button.classList.toggle("active", active);
|
|
button.setAttribute("aria-selected", String(active));
|
|
});
|
|
contentHost?.querySelectorAll("[data-writing-context-panel]").forEach((panel) => {
|
|
const active = panel.dataset.writingContextPanel === nextTab;
|
|
panel.classList.toggle("active", active);
|
|
panel.hidden = !active;
|
|
});
|
|
try {
|
|
localStorage.setItem(tabStorageKey, nextTab);
|
|
} catch {
|
|
// Remembering the tab is a convenience only.
|
|
}
|
|
};
|
|
|
|
const wireFragment = () => {
|
|
activateTab(activeTab());
|
|
const fragment = contentHost?.querySelector("[data-writing-context-fragment]");
|
|
const fragmentStatus = fragment?.dataset.followStatus;
|
|
if (fragmentStatus) {
|
|
setStatus(fragmentStatus);
|
|
}
|
|
|
|
const fragmentSceneId = Number.parseInt(fragment?.dataset.sceneId || "", 10);
|
|
if (Number.isInteger(fragmentSceneId) && fragmentSceneId > 0) {
|
|
currentSceneId = fragmentSceneId;
|
|
}
|
|
|
|
contentHost?.querySelectorAll("[data-writing-context-tab]").forEach((button) => {
|
|
button.addEventListener("click", () => activateTab(button.dataset.writingContextTab));
|
|
});
|
|
|
|
contentHost?.querySelector("[data-writing-context-scene-select]")?.addEventListener("change", (event) => {
|
|
const sceneId = Number.parseInt(event.target.value || "", 10);
|
|
if (!Number.isInteger(sceneId) || sceneId <= 0) {
|
|
return;
|
|
}
|
|
|
|
const fragment = contentHost?.querySelector("[data-writing-context-fragment]");
|
|
window.PlotDirectorStoryPosition?.publishChanged({
|
|
source: "WritingContext",
|
|
projectId: fragment?.dataset.projectId,
|
|
bookId: fragment?.dataset.bookId,
|
|
chapterId: fragment?.dataset.chapterId,
|
|
sceneId
|
|
}, "WritingContext");
|
|
});
|
|
};
|
|
|
|
const inScope = (position) => {
|
|
if (Number.isInteger(scopedProjectId) && scopedProjectId > 0 && position.projectId !== scopedProjectId) {
|
|
return false;
|
|
}
|
|
|
|
if (Number.isInteger(scopedBookId) && scopedBookId > 0 && position.bookId && position.bookId !== scopedBookId) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const loadPosition = async (position) => {
|
|
if (!position || !Number.isInteger(position.sceneId) || position.sceneId <= 0 || !inScope(position)) {
|
|
return;
|
|
}
|
|
|
|
if (position.sceneId === currentSceneId || position.sceneId === loadingSceneId) {
|
|
return;
|
|
}
|
|
|
|
const tab = activeTab();
|
|
loadingSceneId = position.sceneId;
|
|
setStatus("Updating");
|
|
const url = new URL(endpoint, window.location.origin);
|
|
if (position.projectId) url.searchParams.set("projectId", position.projectId);
|
|
if (position.bookId) url.searchParams.set("bookId", position.bookId);
|
|
if (position.chapterId) url.searchParams.set("chapterId", position.chapterId);
|
|
url.searchParams.set("sceneId", position.sceneId);
|
|
|
|
try {
|
|
const response = await fetch(url, { headers: { "X-Requested-With": "XMLHttpRequest" } });
|
|
if (!response.ok) {
|
|
throw new Error(`Context request failed: ${response.status}`);
|
|
}
|
|
|
|
contentHost.innerHTML = await response.text();
|
|
currentSceneId = position.sceneId;
|
|
wireFragment();
|
|
activateTab(tab);
|
|
} catch (error) {
|
|
console.debug("Writing Context could not update.", error);
|
|
setStatus("Connected, waiting for scene");
|
|
} finally {
|
|
loadingSceneId = null;
|
|
}
|
|
};
|
|
|
|
document.addEventListener(window.PlotDirectorStoryPosition?.eventName || "plotdirector:story-position-changed", (event) => {
|
|
void loadPosition(event.detail);
|
|
});
|
|
|
|
if (window.signalR) {
|
|
const connection = new signalR.HubConnectionBuilder()
|
|
.withUrl("/hubs/word-companion-follow")
|
|
.withAutomaticReconnect()
|
|
.build();
|
|
|
|
connection.on("storyPositionChanged", (payload) => {
|
|
window.PlotDirectorStoryPosition?.publishChanged(payload, payload?.source || payload?.Source || "WordCompanion");
|
|
});
|
|
|
|
connection.start()
|
|
.then(() => {
|
|
if (!currentSceneId) {
|
|
setStatus("Connected, waiting for scene");
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.debug("Writing Context follow connection unavailable.", error);
|
|
setStatus("Word not connected");
|
|
});
|
|
} else {
|
|
setStatus("Word not connected");
|
|
}
|
|
|
|
wireFragment();
|
|
})();
|