The Appview for the kipclip.com atproto bookmarking service
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Clean up debug logging from PWA OAuth

+23 -60
+1 -8
frontend/components/Login.tsx
··· 68 e.preventDefault(); 69 setError(null); 70 71 - console.log("[Login] handleLogin called"); 72 - console.log("[Login] isStandalonePwa():", isStandalonePwa()); 73 - 74 // Read directly from input in case Web Component updated it without firing input event 75 const currentHandle = inputRef.current?.value || handle; 76 ··· 98 99 // PWA mode: use popup OAuth to avoid losing PWA context 100 if (isStandalonePwa()) { 101 - console.log("[PWA OAuth] Detected standalone PWA mode"); 102 loginUrl += "&pwa=true"; 103 try { 104 - console.log("[PWA OAuth] Opening popup for:", loginUrl); 105 - const result = await openOAuthPopup(loginUrl); 106 - console.log("[PWA OAuth] Popup returned success:", result); 107 // Success - reload to pick up the new session cookie 108 - console.log("[PWA OAuth] Reloading page..."); 109 globalThis.location.reload(); 110 } catch (popupError) { 111 const message = popupError instanceof Error
··· 68 e.preventDefault(); 69 setError(null); 70 71 // Read directly from input in case Web Component updated it without firing input event 72 const currentHandle = inputRef.current?.value || handle; 73 ··· 95 96 // PWA mode: use popup OAuth to avoid losing PWA context 97 if (isStandalonePwa()) { 98 loginUrl += "&pwa=true"; 99 try { 100 + await openOAuthPopup(loginUrl); 101 // Success - reload to pick up the new session cookie 102 globalThis.location.reload(); 103 } catch (popupError) { 104 const message = popupError instanceof Error
+22 -52
frontend/utils/pwa.ts
··· 7 */ 8 export function isStandalonePwa(): boolean { 9 // Check display-mode media query (works on Android Chrome) 10 - const displayModeStandalone = globalThis.matchMedia && 11 - globalThis.matchMedia("(display-mode: standalone)").matches; 12 // Check iOS standalone mode 13 // deno-lint-ignore no-explicit-any 14 - const iosStandalone = (globalThis.navigator as any).standalone === true; 15 // Check if launched from TWA (Trusted Web Activity on Android) 16 - const isTwa = document.referrer.includes("android-app://"); 17 18 - console.log("[PWA] Detection checks:", { 19 - displayModeStandalone, 20 - iosStandalone, 21 - isTwa, 22 - referrer: document.referrer, 23 - }); 24 - 25 - return displayModeStandalone || iosStandalone || isTwa; 26 } 27 28 /** ··· 36 export function openOAuthPopup( 37 loginUrl: string, 38 ): Promise<{ did: string; handle: string }> { 39 - console.log("[PWA OAuth] openOAuthPopup called with:", loginUrl); 40 - 41 return new Promise((resolve, reject) => { 42 - console.log("[PWA OAuth] Inside promise, clearing previous result"); 43 // Clear any previous OAuth result 44 localStorage.removeItem("pwa-oauth-result"); 45 ··· 50 const top = Math.max(0, (screen.height - height) / 2); 51 52 // Open popup 53 - console.log("[PWA OAuth] Opening popup window..."); 54 const popup = globalThis.open( 55 loginUrl, 56 "oauth-popup", 57 `width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=yes,status=no`, 58 ); 59 - 60 - console.log("[PWA OAuth] Popup result:", popup ? "opened" : "blocked"); 61 62 if (!popup) { 63 reject( ··· 66 return; 67 } 68 69 - console.log("[PWA OAuth] Setting up event listeners and polling..."); 70 - 71 // Handle successful OAuth result 72 function handleSuccess(data: { did: string; handle: string }) { 73 - console.log("[PWA OAuth] handleSuccess called with:", data); 74 cleanup(); 75 localStorage.removeItem("pwa-oauth-result"); 76 - console.log("[PWA OAuth] Resolving promise and triggering reload..."); 77 resolve(data); 78 } 79 ··· 101 102 // Poll localStorage frequently - the storage event doesn't always fire reliably 103 // especially after OAuth redirects through external providers 104 - console.log("[PWA OAuth] Starting localStorage polling..."); 105 - let pollCount = 0; 106 - 107 - // Keep reference for cleanup 108 let pollingStopped = false; 109 110 function pollForResult() { 111 - if (pollingStopped) { 112 - console.log("[PWA OAuth] Polling stopped"); 113 - return; 114 - } 115 try { 116 - pollCount++; 117 const result = localStorage.getItem("pwa-oauth-result"); 118 - // Log every 5th poll (every 1 second) to show we're still running 119 - if (pollCount % 5 === 0) { 120 - console.log( 121 - "[PWA OAuth] Poll #" + pollCount + ", localStorage result:", 122 - result ? "FOUND" : "empty", 123 - ); 124 - } 125 if (result) { 126 - console.log("[PWA OAuth] Found result in localStorage:", result); 127 const data = JSON.parse(result); 128 - console.log("[PWA OAuth] Parsed data:", data); 129 if (data?.type === "oauth-callback" && data.success) { 130 - console.log("[PWA OAuth] Calling handleSuccess"); 131 handleSuccess({ did: data.did, handle: data.handle }); 132 - return; // Stop polling 133 } 134 } 135 // Schedule next poll 136 setTimeout(pollForResult, 200); 137 - } catch (e) { 138 - console.error("[PWA OAuth] Poll error:", e); 139 // Continue polling despite error 140 if (!pollingStopped) { 141 setTimeout(pollForResult, 200); ··· 144 } 145 146 // Start polling immediately 147 - console.log("[PWA OAuth] Starting first poll now..."); 148 pollForResult(); 149 - console.log("[PWA OAuth] First poll executed"); 150 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(); 155 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); 163 } 164 - }, 10000); // Check every 10 seconds 165 166 function cleanup() { 167 - console.log("[PWA OAuth] Cleanup called"); 168 pollingStopped = true; 169 globalThis.removeEventListener("message", handleMessage); 170 globalThis.removeEventListener("storage", handleStorage);
··· 7 */ 8 export function isStandalonePwa(): boolean { 9 // Check display-mode media query (works on Android Chrome) 10 + if ( 11 + globalThis.matchMedia && 12 + globalThis.matchMedia("(display-mode: standalone)").matches 13 + ) { 14 + return true; 15 + } 16 + 17 // Check iOS standalone mode 18 // deno-lint-ignore no-explicit-any 19 + if ((globalThis.navigator as any).standalone === true) { 20 + return true; 21 + } 22 + 23 // Check if launched from TWA (Trusted Web Activity on Android) 24 + if (document.referrer.includes("android-app://")) { 25 + return true; 26 + } 27 28 + return false; 29 } 30 31 /** ··· 39 export function openOAuthPopup( 40 loginUrl: string, 41 ): Promise<{ did: string; handle: string }> { 42 return new Promise((resolve, reject) => { 43 // Clear any previous OAuth result 44 localStorage.removeItem("pwa-oauth-result"); 45 ··· 50 const top = Math.max(0, (screen.height - height) / 2); 51 52 // Open popup 53 const popup = globalThis.open( 54 loginUrl, 55 "oauth-popup", 56 `width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=yes,status=no`, 57 ); 58 59 if (!popup) { 60 reject( ··· 63 return; 64 } 65 66 // Handle successful OAuth result 67 function handleSuccess(data: { did: string; handle: string }) { 68 cleanup(); 69 localStorage.removeItem("pwa-oauth-result"); 70 resolve(data); 71 } 72 ··· 94 95 // Poll localStorage frequently - the storage event doesn't always fire reliably 96 // especially after OAuth redirects through external providers 97 let pollingStopped = false; 98 99 function pollForResult() { 100 + if (pollingStopped) return; 101 + 102 try { 103 const result = localStorage.getItem("pwa-oauth-result"); 104 if (result) { 105 const data = JSON.parse(result); 106 if (data?.type === "oauth-callback" && data.success) { 107 handleSuccess({ did: data.did, handle: data.handle }); 108 + return; 109 } 110 } 111 // Schedule next poll 112 setTimeout(pollForResult, 200); 113 + } catch { 114 // Continue polling despite error 115 if (!pollingStopped) { 116 setTimeout(pollForResult, 200); ··· 119 } 120 121 // Start polling immediately 122 pollForResult(); 123 124 // Don't rely on popup.closed - it returns true when popup navigates cross-origin 125 // Instead, use a timeout. OAuth should complete within 5 minutes max. 126 + const OAUTH_TIMEOUT_MS = 5 * 60 * 1000; 127 const startTime = Date.now(); 128 129 const checkTimeout = setInterval(() => { 130 + if (Date.now() - startTime > OAUTH_TIMEOUT_MS) { 131 cleanup(); 132 reject(new Error("Login timed out")); 133 clearInterval(checkTimeout); 134 } 135 + }, 10000); 136 137 function cleanup() { 138 pollingStopped = true; 139 globalThis.removeEventListener("message", handleMessage); 140 globalThis.removeEventListener("storage", handleStorage);