(function () { if (window.__LOGIN_WITH_TOKEN__) return; window.__LOGIN_WITH_TOKEN__ = true; async function loginWithToken(token) { const res = await fetch('https://stoat.chat/api/users/@me', { headers: { 'x-session-token': token } }); if (!res.ok) throw new Error('Invalid token'); const user = await res.json(); const db = await new Promise((resolve, reject) => { const r = indexedDB.open('localforage'); r.onsuccess = () => resolve(r.result); r.onerror = () => reject(r.error); }); const tx = db.transaction('keyvaluepairs', 'readwrite'); await new Promise((resolve, reject) => { const r = tx.objectStore('keyvaluepairs').put({ session: { _id: user._id, token: token, userId: user._id, valid: true } }, 'auth'); r.onsuccess = () => resolve(); r.onerror = () => reject(r.error); }); location.reload(); } function openTokenDialog() { const backdrop = document.createElement('div'); backdrop.className = 'top_0 left_0 right_0 bottom_0 pos_fixed z_100 max-h_100% d_grid us_none place-items_center pointer-events_all anim-n_scrimFadeIn anim-dur_0.1s anim-fm_forwards trs_var(--transitions-medium)_all p_80px ov-y_auto'; backdrop.style.cssText = '--background: rgba(0, 0, 0, 0.6);'; backdrop.innerHTML = `
Login With Token
`; document.body.appendChild(backdrop); const closeBtn = backdrop.querySelector('#lwt-close-btn'); const loginBtn = backdrop.querySelector('#lwt-login-btn'); const tokenInput = backdrop.querySelector('#lwt-token-input'); function close() { backdrop.remove(); } function setLoading(loading) { loginBtn.disabled = loading; loginBtn.style.cursor = loading ? 'not-allowed' : 'pointer'; const ripple = loginBtn.querySelector('md-ripple'); loginBtn.textContent = loading ? 'Logging in…' : 'Login'; if (ripple) loginBtn.prepend(ripple); } function setError(msg) { loginBtn.disabled = false; loginBtn.style.cursor = 'pointer'; const ripple = loginBtn.querySelector('md-ripple'); loginBtn.textContent = msg; if (ripple) loginBtn.prepend(ripple); setTimeout(() => { loginBtn.textContent = 'Login'; if (ripple) loginBtn.prepend(ripple); }, 2000); } backdrop.addEventListener('click', (e) => { if (e.target === backdrop) close(); }); closeBtn.addEventListener('click', close); loginBtn.addEventListener('click', async () => { const token = tokenInput.value?.trim(); if (!token) { setError('Enter a token!'); return; } setLoading(true); try { await loginWithToken(token); } catch (err) { setError('Invalid token!'); } }); } function injectLoginButton() { const signUpBtn = [...document.querySelectorAll('button')] .find(b => b.textContent.trim() === 'Sign Up'); if (!signUpBtn) return; const parent = signUpBtn.parentElement; if (parent.querySelector('[data-lwt-btn]')) return; const clone = signUpBtn.cloneNode(false); clone.dataset.lwtBtn = 'true'; clone.textContent = 'Login With Token'; const ripple = document.createElement('md-ripple'); ripple.setAttribute('aria-hidden', 'true'); clone.prepend(ripple); clone.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); openTokenDialog(); }); signUpBtn.insertAdjacentElement('afterend', clone); } let debounceTimer = null; new MutationObserver(() => { clearTimeout(debounceTimer); debounceTimer = setTimeout(injectLoginButton, 150); }).observe(document.body, { childList: true, subtree: true }); injectLoginButton(); })();