The Appview for the kipclip.com atproto bookmarking service

Fix: don't use popup.closed (false positive on cross-origin nav), use timeout instead

Changed files
+13 -37
frontend
utils
+13 -37
frontend/utils/pwa.ts
··· 148 148 pollForResult(); 149 149 console.log("[PWA OAuth] First poll executed"); 150 150 151 - // Check if popup was closed 152 - let checkCount = 0; 153 - const checkClosed = setInterval(() => { 154 - checkCount++; 155 - const isClosed = popup.closed; 156 - // Log every check to debug 157 - console.log( 158 - "[PWA OAuth] Check #" + checkCount + ", popup.closed:", 159 - isClosed, 160 - ); 151 + // Don't rely on popup.closed - it returns true when popup navigates cross-origin 152 + // Instead, use a timeout. OAuth should complete within 5 minutes max. 153 + const OAUTH_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes 154 + const startTime = Date.now(); 161 155 162 - if (isClosed) { 163 - console.log("[PWA OAuth] Popup detected as closed, waiting 300ms..."); 164 - // Give a brief moment for any final localStorage write 165 - setTimeout(() => { 166 - const result = localStorage.getItem("pwa-oauth-result"); 167 - console.log( 168 - "[PWA OAuth] Final localStorage check:", 169 - result ? "FOUND" : "empty", 170 - ); 171 - if (result) { 172 - try { 173 - const data = JSON.parse(result); 174 - if (data?.type === "oauth-callback" && data.success) { 175 - handleSuccess({ did: data.did, handle: data.handle }); 176 - return; 177 - } 178 - } catch { 179 - // Ignore parse errors 180 - } 181 - } 182 - console.log("[PWA OAuth] No result found, rejecting with cancelled"); 183 - cleanup(); 184 - reject(new Error("Login cancelled")); 185 - }, 300); 186 - clearInterval(checkClosed); // Stop checking for closed 156 + const checkTimeout = setInterval(() => { 157 + const elapsed = Date.now() - startTime; 158 + if (elapsed > OAUTH_TIMEOUT_MS) { 159 + console.log("[PWA OAuth] Timeout reached after 5 minutes"); 160 + cleanup(); 161 + reject(new Error("Login timed out")); 162 + clearInterval(checkTimeout); 187 163 } 188 - }, 500); 164 + }, 10000); // Check every 10 seconds 189 165 190 166 function cleanup() { 191 167 console.log("[PWA OAuth] Cleanup called"); 192 168 pollingStopped = true; 193 169 globalThis.removeEventListener("message", handleMessage); 194 170 globalThis.removeEventListener("storage", handleStorage); 195 - clearInterval(checkClosed); 171 + clearInterval(checkTimeout); 196 172 } 197 173 198 174 globalThis.addEventListener("message", handleMessage);