feat(main): wire Store + Dialog + DataLayer on /gym.php
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
|||||||
|
import { Store } from './store.js';
|
||||||
|
import { Dialog } from './ui.js';
|
||||||
|
import { currentAttribute } from './dom.js';
|
||||||
|
import { startRequestInterceptor } from './interceptor.js';
|
||||||
|
|
||||||
|
function findAnchorElement() {
|
||||||
|
// Torn's training form is the element containing the Train button.
|
||||||
|
// Selector is best-effort; the Dialog will fall back if missing.
|
||||||
|
const btn = document.querySelector('button[name="train"], a[href*="train"]');
|
||||||
|
if (!btn) return null;
|
||||||
|
return btn.closest('form') || btn.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
const store = new Store({
|
||||||
|
storage: localStorage,
|
||||||
|
onWarn: (m) => console.warn(m),
|
||||||
|
});
|
||||||
|
const prefs = store.getPrefs();
|
||||||
|
|
||||||
|
const dialog = new Dialog({
|
||||||
|
onTargetChange: (v) => {
|
||||||
|
const attr = currentAttribute()?.attr;
|
||||||
|
if (!attr) return;
|
||||||
|
store.setTarget(attr, v);
|
||||||
|
render();
|
||||||
|
},
|
||||||
|
onModeChange: (m) => {
|
||||||
|
store.setMode(m);
|
||||||
|
applyMode();
|
||||||
|
},
|
||||||
|
onPosChange: (pos) => store.setPos(pos),
|
||||||
|
onClose: () => dialog.destroy(),
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.mount({ initialMode: prefs.mode, initialPos: prefs.pos });
|
||||||
|
applyMode();
|
||||||
|
|
||||||
|
let lastSnapshot = null;
|
||||||
|
let lastAttr = null;
|
||||||
|
let lastDelta = 0;
|
||||||
|
|
||||||
|
function snapshot() {
|
||||||
|
const a = currentAttribute();
|
||||||
|
if (!a) {
|
||||||
|
return { error: "Couldn't read attribute — Torn may have updated the page." };
|
||||||
|
}
|
||||||
|
lastAttr = a.attr;
|
||||||
|
const summary = store.getSummary(a.attr);
|
||||||
|
return {
|
||||||
|
attr: a.attr,
|
||||||
|
gym: a.gym,
|
||||||
|
current: a.current,
|
||||||
|
target: store.getTarget(a.attr),
|
||||||
|
perTrain: lastDelta,
|
||||||
|
summary,
|
||||||
|
warn: store._saveDisabled ? 'saving disabled this session' : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
lastSnapshot = snapshot();
|
||||||
|
dialog.render(lastSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyMode() {
|
||||||
|
if (prefs.mode === 'anchored') {
|
||||||
|
const el = findAnchorElement();
|
||||||
|
if (el) {
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
dialog.setMode('anchored', { canAnchor: true });
|
||||||
|
dialog._positionAnchored(rect);
|
||||||
|
// observe
|
||||||
|
const ro = new ResizeObserver(() => {
|
||||||
|
if (prefs.mode === 'anchored') dialog._positionAnchored(el.getBoundingClientRect());
|
||||||
|
});
|
||||||
|
ro.observe(el);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialog.setMode('free');
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial paint
|
||||||
|
render();
|
||||||
|
|
||||||
|
// watch DOM for attribute changes
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
const a = currentAttribute();
|
||||||
|
if (a && (a.attr !== lastAttr || a.current !== lastSnapshot?.current)) render();
|
||||||
|
});
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true, characterData: true });
|
||||||
|
|
||||||
|
// intercept train requests
|
||||||
|
const prev = currentAttribute()?.current ?? 0;
|
||||||
|
startRequestInterceptor({
|
||||||
|
prevValue: prev,
|
||||||
|
currentAttr: lastAttr,
|
||||||
|
onTrain: ({ attr, delta, ts }) => {
|
||||||
|
store.recordTrain(attr, delta, ts);
|
||||||
|
lastDelta = delta;
|
||||||
|
render();
|
||||||
|
},
|
||||||
|
onParseFail: (url) => console.warn('[tat] could not parse train response from', url),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location.hash === '#tat-test') {
|
||||||
|
// Self-test path: the in-browser tests live in the bundled userscript.
|
||||||
|
// The bundle runs the test block; nothing to do here.
|
||||||
|
} else if (/\/gym\.php(\?|$)/.test(location.pathname + location.search)) {
|
||||||
|
start();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user