extension: detect activatable Topstep accounts
Add a popup button that scans the Topstep dashboard's GraphQL API (crystal.topstep.com) for passed evaluations that can still be activated into an Express Funded account. An eval is activatable when its status is "Passed" and its subscription id is not shared by ANY funded account it ever spawned — regardless of that funded account's current status, since activation is one-way. The result and its progress are mirrored to chrome.storage so the popup renders them independent of the message port (avoids the "message port closed" error on this ~20s scan). Activatable accounts link to their Topstep detail page. Adds https://*.topstep.com/* host permission; bumps to 0.15.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
b1dccd7dec
commit
1bf8ba8693
@@ -45,6 +45,64 @@ document.getElementById('now').addEventListener('click', () => {
|
||||
chrome.runtime.sendMessage({ type: 'capture-now' }, () => setTimeout(render, 200));
|
||||
});
|
||||
|
||||
document.getElementById('tsCheck').addEventListener('click', () => {
|
||||
// Kick off the scan and return. The worker writes progress and the result to
|
||||
// storage; renderTopstepState (below) reflects whatever is there, so the
|
||||
// popup can be closed and reopened mid-scan without losing the answer. The
|
||||
// callback only acknowledges the request was received — it carries no result.
|
||||
chrome.runtime.sendMessage({ type: 'topstep-check' }, () => void chrome.runtime.lastError);
|
||||
});
|
||||
|
||||
/** Paint the Topstep panel from the stored scan state. */
|
||||
function renderTopstepState(state) {
|
||||
const btn = document.getElementById('tsCheck');
|
||||
const out = document.getElementById('tsResult');
|
||||
if (!state) { out.innerHTML = ''; return; }
|
||||
|
||||
if (state.running) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Checking…';
|
||||
out.innerHTML = '<div class="muted">Scanning the Topstep dashboard…</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Check activatable accounts';
|
||||
|
||||
if (state.error) {
|
||||
out.innerHTML = `<div class="err">${esc(state.error)}</div>`;
|
||||
return;
|
||||
}
|
||||
const r = state.result;
|
||||
if (!r) { out.innerHTML = ''; return; }
|
||||
if (r.signedOut) {
|
||||
out.innerHTML = '<div class="err">Not signed in to Topstep. Open the dashboard, sign in, then check again.</div>';
|
||||
return;
|
||||
}
|
||||
if (r.error) {
|
||||
out.innerHTML = `<div class="err">${esc(r.error)}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const n = r.activatableCount || 0;
|
||||
const acctUrl = (id) => `https://dashboard.topstep.com/dashboard/accounts/${encodeURIComponent(id)}`;
|
||||
const list = (r.activatable || [])
|
||||
.map((a) => `<li><a href="${acctUrl(a.id)}" target="_blank" rel="noopener">${esc(a.name)}</a>${a.type ? ` · ${esc(a.type)}` : ''}</li>`)
|
||||
.join('');
|
||||
const when = r.checkedAt ? ` · ${new Date(r.checkedAt).toLocaleTimeString()}` : '';
|
||||
out.innerHTML =
|
||||
`<div class="big">${n} activatable</div>` +
|
||||
`<div class="muted">${r.alreadyActivated || 0} already activated · ${r.passedTotal || 0} passed total${when}</div>` +
|
||||
(list ? `<ul>${list}</ul>` : '<div class="muted" style="margin-top:6px">No passed accounts waiting on activation.</div>');
|
||||
}
|
||||
|
||||
// Render whatever the last scan left behind when the popup opens…
|
||||
chrome.storage.local.get('topstepScan').then(({ topstepScan }) => renderTopstepState(topstepScan));
|
||||
// …and keep up with a scan that finishes while the popup is open.
|
||||
chrome.storage.onChanged.addListener((c, area) => {
|
||||
if (area === 'local' && 'topstepScan' in c) renderTopstepState(c.topstepScan.newValue);
|
||||
});
|
||||
|
||||
chrome.storage.onChanged.addListener((c, area) => { if (area === 'local' && 'state' in c) render(); });
|
||||
|
||||
function esc(s) {
|
||||
|
||||
Reference in New Issue
Block a user