This repository has no description
0

Configure Feed

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

fix auth and plumb stats

+221 -16
+26 -7
app/src/app/auth/callback/page.tsx
··· 245 245 pdsEndpoint: pdsEndpoint // Store the PDS endpoint for later use 246 246 }); 247 247 248 - // Clear all auth-related storage items 248 + // Save the final PDS endpoint to use 249 + // Prioritize the one from token extraction, then the stored one 250 + const finalPdsEndpoint = pdsEndpoint || storedPdsEndpoint; 251 + 252 + // Clear only the temporary auth-related items 249 253 clearAuthData('oauth_state'); 250 254 clearAuthData('code_verifier'); 251 255 clearAuthData('key_pair'); 252 - clearAuthData('pds_endpoint'); 253 256 clearAuthData('auth_server'); 254 257 255 - // Also try to clear any leftover sessionStorage items 256 - try { 257 - sessionStorage.clear(); 258 - } catch (e) { 259 - console.warn('Failed to clear session storage:', e); 258 + // IMPORTANT: Do NOT clear pdsEndpoint since we need it for API calls! 259 + 260 + // Special handling for PDS endpoint - make sure it's in localStorage 261 + // as both a regular key and in our auth format 262 + if (finalPdsEndpoint) { 263 + console.log('Ensuring PDS endpoint is saved for API calls:', finalPdsEndpoint); 264 + 265 + // Save directly to localStorage for legacy code 266 + localStorage.setItem('pdsEndpoint', finalPdsEndpoint); 267 + 268 + // Also save in our auth format 269 + localStorage.setItem('bsky_auth_pdsEndpoint', finalPdsEndpoint); 270 + 271 + // And in sessionStorage for good measure 272 + try { 273 + sessionStorage.setItem('pdsEndpoint', finalPdsEndpoint); 274 + } catch (e) { 275 + console.warn('Could not save pdsEndpoint to sessionStorage:', e); 276 + } 277 + } else { 278 + console.warn('No PDS endpoint found to save. API calls may fail.'); 260 279 } 261 280 262 281 // Redirect to home page
+38 -3
app/src/app/stats/page.tsx
··· 4 4 import Link from 'next/link'; 5 5 import styles from './stats.module.css'; 6 6 import { formatRelativeTime } from '@/lib/time-utils'; 7 + import { useAuth } from '@/lib/auth-context'; 7 8 8 9 interface StatsData { 9 10 totalCount: number; ··· 13 14 } 14 15 15 16 export default function StatsPage() { 17 + const { isAuthenticated, handle, clearAuth } = useAuth(); 16 18 const [statsData, setStatsData] = useState<StatsData | null>(null); 17 19 const [loading, setLoading] = useState(true); 18 20 const [error, setError] = useState<string | null>(null); 21 + 22 + // Function to handle logout 23 + const handleLogout = () => { 24 + clearAuth(); 25 + }; 19 26 20 27 useEffect(() => { 21 28 // Fetch stats data when the component mounts ··· 80 87 return ( 81 88 <div className={styles.container}> 82 89 <header className={styles.header}> 83 - <h1>Plumbing Stats 🪠</h1> 84 - <p className={styles.subtitle}> 90 + <div className={styles.headerContent}> 91 + <h1 className={styles.title}>im.flushing 🧻</h1> 92 + <p className={styles.subtitle}>https://flushing.im 🚽</p> 93 + <p className={styles.description}> 94 + The world&apos;s first decentralized social media app for sharing when you&apos;re on the toilet. Connect with other bathroom enjoyers all over the world by posting &quot;flushes&quot;! Powered by the AT Protocol. Your status updates are saved to your PDS with the im.flushing lexicon.<br /> 95 + <span className={styles.creditLine}> 96 + Made by <a href="https://bsky.app/profile/dame.is" target="_blank" rel="noopener noreferrer">@dame.is</a>. 97 + Like the app? Consider contributing to <a href="https://ko-fi.com/dameis" target="_blank" rel="noopener noreferrer" className={styles.kofiLink}>my toilet paper fund</a>. 98 + </span> 99 + </p> 100 + </div> 101 + <div className={styles.headerActions}> 102 + {isAuthenticated ? ( 103 + <> 104 + <Link href={`/profile/${handle}`} className={styles.userInfo}>@{handle}</Link> 105 + <button onClick={handleLogout} className={styles.logoutButton}> 106 + Logout 107 + </button> 108 + </> 109 + ) : ( 110 + <Link href="/auth/login" className={styles.loginButton}> 111 + Login with Bluesky 112 + </Link> 113 + )} 114 + </div> 115 + </header> 116 + 117 + <div className={styles.statsHeader}> 118 + <h2>Plumbing Stats 🪠</h2> 119 + <p className={styles.statsSubtitle}> 85 120 Global statistics for the im.flushing network 86 121 </p> 87 - </header> 122 + </div> 88 123 89 124 <div className={styles.controls}> 90 125 <button
+140 -5
app/src/app/stats/stats.module.css
··· 4 4 padding: 2rem 1rem; 5 5 } 6 6 7 + /* Main Header - Copied from dashboard styles */ 7 8 .header { 8 - text-align: center; 9 + display: flex; 10 + justify-content: space-between; 11 + align-items: flex-start; 9 12 margin-bottom: 2rem; 13 + flex-wrap: wrap; 14 + gap: 1rem; 10 15 } 11 16 12 - .header h1 { 17 + .headerContent { 18 + flex: 1 1 300px; 19 + min-width: 0; 20 + } 21 + 22 + .title { 13 23 font-size: 2.5rem; 14 - margin-bottom: 0.5rem; 24 + margin-bottom: 0.25rem; 15 25 color: var(--primary-color); 26 + word-wrap: break-word; 27 + hyphens: auto; 28 + line-height: 1.2; 16 29 } 17 30 18 31 .subtitle { 32 + font-size: 1rem; 33 + color: #888; 34 + margin: 0 0 0.5rem 0; 35 + font-weight: normal; 36 + word-wrap: break-word; 37 + } 38 + 39 + .description { 40 + font-size: 1.1rem; 41 + color: #666; 42 + margin: 0; 43 + line-height: 1.5; 44 + word-wrap: break-word; 45 + } 46 + 47 + .creditLine { 48 + display: block; 49 + font-size: 0.85rem; 50 + margin-top: 0.5rem; 51 + color: #888; 52 + } 53 + 54 + .kofiLink { 55 + color: var(--primary-color); 56 + text-decoration: none; 57 + transition: color 0.2s; 58 + } 59 + 60 + .kofiLink:hover { 61 + text-decoration: underline; 62 + } 63 + 64 + .headerActions { 65 + display: flex; 66 + gap: 1rem; 67 + align-items: center; 68 + } 69 + 70 + .userInfo { 71 + color: #555; 72 + font-weight: 500; 73 + text-decoration: none; 74 + transition: color 0.2s; 75 + } 76 + 77 + .userInfo:hover { 78 + color: var(--primary-color); 79 + text-decoration: underline; 80 + } 81 + 82 + .logoutButton { 83 + background-color: transparent; 84 + color: var(--primary-color); 85 + border: 1px solid var(--primary-color); 86 + padding: 0.3rem 0.8rem; 87 + font-size: 0.9rem; 88 + border-radius: 4px; 89 + cursor: pointer; 90 + transition: all 0.2s; 91 + } 92 + 93 + .logoutButton:hover { 94 + background-color: rgba(91, 173, 240, 0.1); 95 + } 96 + 97 + .loginButton { 98 + display: inline-block; 99 + background-color: var(--primary-color); 100 + color: white; 101 + padding: 0.6rem 1.2rem; 102 + border-radius: 4px; 103 + font-size: 1rem; 104 + font-weight: 500; 105 + transition: all 0.2s ease; 106 + text-decoration: none; 107 + } 108 + 109 + .loginButton:hover { 110 + background-color: var(--secondary-color); 111 + transform: translateY(-2px); 112 + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 113 + } 114 + 115 + /* Stats Page Specific Styles */ 116 + .statsHeader { 117 + text-align: center; 118 + margin-bottom: 2rem; 119 + } 120 + 121 + .statsHeader h2 { 122 + font-size: 2rem; 123 + margin-bottom: 0.5rem; 124 + color: var(--primary-color); 125 + } 126 + 127 + .statsSubtitle { 19 128 color: #666; 20 129 font-size: 1.2rem; 130 + margin: 0; 21 131 } 22 132 23 133 .controls { ··· 273 383 padding: 1rem; 274 384 } 275 385 276 - .header h1 { 277 - font-size: 1.8rem; 386 + /* Header responsive styles - from main page */ 387 + .header { 388 + flex-direction: column; 389 + align-items: flex-start; 390 + } 391 + 392 + .headerContent { 393 + flex: 1 1 100%; 394 + text-align: left; 395 + } 396 + 397 + .headerActions { 398 + width: 100%; 399 + justify-content: flex-start; 400 + } 401 + 402 + .title { 403 + font-size: 2rem; 278 404 } 279 405 280 406 .subtitle { 407 + font-size: 1rem; 408 + } 409 + 410 + /* Stats page specific responsive */ 411 + .statsHeader h2 { 412 + font-size: 1.7rem; 413 + } 414 + 415 + .statsSubtitle { 281 416 font-size: 1rem; 282 417 } 283 418
+17 -1
app/src/lib/auth-context.tsx
··· 52 52 const storedHandle = localStorage.getItem('handle'); 53 53 const storedKeyPair = localStorage.getItem('keyPair'); 54 54 const storedDpopNonce = localStorage.getItem('dpopNonce'); 55 - const storedPdsEndpoint = localStorage.getItem('pdsEndpoint'); 55 + 56 + // Special handling for PDS endpoint - check all possible storage locations 57 + let storedPdsEndpoint = localStorage.getItem('pdsEndpoint'); 58 + 59 + // If not found, try our auth-prefixed format 60 + if (!storedPdsEndpoint) { 61 + storedPdsEndpoint = localStorage.getItem('bsky_auth_pdsEndpoint'); 62 + } 63 + 64 + // Last resort - check sessionStorage 65 + if (!storedPdsEndpoint && typeof sessionStorage !== 'undefined') { 66 + try { 67 + storedPdsEndpoint = sessionStorage.getItem('pdsEndpoint'); 68 + } catch (e) { 69 + console.warn('Failed to check sessionStorage for PDS endpoint:', e); 70 + } 71 + } 56 72 57 73 if (storedAccessToken && storedDid && storedKeyPair) { 58 74 setAccessToken(storedAccessToken);