/* global React, ReactDOM, VariantCard, VariantPortal */
/* eslint-disable no-unused-vars */
// =====================================================================
// Forge Auth Proxy — root app
//
// On mount we fetch /auth/me to learn whether the caller has a live
// session. Two terminal renders:
//
// signed_in=false → sign-in card (Slack button + error/unauthorized states)
// signed_in=true → portal (apps list + sign-out)
//
// The server owns the OAuth round-trip, so the design-scaffold
// `connecting` / `success` mid-flow states never render in production.
// The "Continue with Slack" button is a plain link to /auth/login so
// the flow works without JS and stays server-driven; any return_to
// query param on this page is preserved into the login link.
// =====================================================================
function readErrorState() {
const params = new URLSearchParams(window.location.search);
const err = params.get('error');
if (err === 'auth_failed') return 'error';
if (err === 'not_in_workspace') return 'unauthorized';
return 'logged-out';
}
function readReturnTo() {
const params = new URLSearchParams(window.location.search);
return params.get('return_to') || '';
}
function loginHref(returnTo) {
if (!returnTo) return '/auth/login';
return '/auth/login?return_to=' + encodeURIComponent(returnTo);
}
// useMe fetches /auth/me once on mount. Returns { loaded, me } where
// `me` is null until the fetch resolves. We treat any fetch failure as
// "signed out" — same fail-open posture the server uses — so a flaky
// network never blocks the sign-in card from rendering.
function useMe() {
const [me, setMe] = React.useState(null);
const [loaded, setLoaded] = React.useState(false);
React.useEffect(() => {
let cancelled = false;
fetch('/auth/me', { credentials: 'same-origin', cache: 'no-store' })
.then(r => (r.ok ? r.json() : { signed_in: false }))
.catch(() => ({ signed_in: false }))
.then(data => {
if (!cancelled) {
setMe(data);
setLoaded(true);
}
});
return () => { cancelled = true; };
}, []);
return { loaded, me };
}
function AuthApp() {
const { loaded, me } = useMe();
const errorState = readErrorState();
const returnTo = readReturnTo();
// Show the destination host only when we actually have one (via
// return_to). No hardcoded fallback — stale app names outlive the
// apps they reference.
let displayHost = '';
if (returnTo) {
try {
displayHost = new URL(returnTo).host || '';
} catch (_) {
displayHost = '';
}
}
const startConnecting = React.useCallback(() => {
window.location.assign(loginHref(returnTo));
}, [returnTo]);
// Pre-fetch render: keep the slot empty so we don't flash the
// sign-in card and then swap to the portal a few ms later. The fetch
// is local and fast — the blank moment is barely perceptible.
if (!loaded) return null;
// Signed-in and no error to surface → portal.
if (me && me.signed_in && errorState === 'logged-out') {
return ;
}
// Signed-out (or signed-in with an explicit error query) → card.
const flow = {
state: errorState,
stepIdx: -1,
countdown: 0,
startConnecting,
};
return ;
}
ReactDOM.createRoot(document.getElementById('root')).render();