Coffee journaling on ATProto (alpha) alpha.arabica.social
coffee
17
fork

Configure Feed

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

feat: caching improvements

pdewey 686fdf43 66d691ef

+37 -34
+2 -1
internal/atproto/cache.go
··· 8 8 ) 9 9 10 10 // CacheTTL is how long cached data remains valid 11 - const CacheTTL = 5 * time.Minute 11 + // Set to 2 minutes to balance multi-device sync with PDS request load 12 + const CacheTTL = 2 * time.Minute 12 13 13 14 // UserCache holds cached data for a single user. 14 15 // This struct is immutable once created - modifications create new instances.
+12 -12
templates/home.tmpl
··· 9 9 <p class="text-sm text-brown-700 italic mb-6">Note: Arabica is currently in alpha. Features and data structures may change.</p> 10 10 11 11 {{if .IsAuthenticated}} 12 - <!-- Authenticated: Show app actions and logout --> 12 + <!-- Authenticated: Show app actions --> 13 13 <div class="mb-6"> 14 14 <p class="text-sm text-brown-700">Logged in as: <span class="font-mono text-brown-900 font-semibold">{{.UserDID}}</span></p> 15 15 </div> 16 - <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4"> 16 + <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> 17 17 <a href="/brews/new" 18 - class="block bg-gradient-to-br from-brown-700 to-brown-800 text-white text-center py-4 px-6 rounded-xl hover:from-brown-800 hover:to-brown-900 transition-all shadow-lg hover:shadow-xl transform hover:-translate-y-0.5"> 18 + class="block bg-gradient-to-br from-brown-700 to-brown-800 text-white text-center py-4 px-6 rounded-xl hover:from-brown-800 hover:to-brown-900 transition-all shadow-lg hover:shadow-xl transform"> 19 19 <span class="text-xl font-semibold">☕ Add New Brew</span> 20 20 </a> 21 21 <a href="/brews" 22 - class="block bg-gradient-to-br from-brown-500 to-brown-600 text-white text-center py-4 px-6 rounded-xl hover:from-brown-600 hover:to-brown-700 transition-all shadow-lg hover:shadow-xl transform hover:-translate-y-0.5"> 22 + class="block bg-gradient-to-br from-brown-500 to-brown-600 text-white text-center py-4 px-6 rounded-xl hover:from-brown-600 hover:to-brown-700 transition-all shadow-lg hover:shadow-xl"> 23 23 <span class="text-xl font-semibold">📋 View All Brews</span> 24 24 </a> 25 25 </div> 26 - <div class="text-center mt-6"> 27 - <form action="/logout" method="POST" class="inline-block"> 28 - <button type="submit" 29 - class="bg-brown-400 text-brown-900 py-3 px-8 rounded-lg hover:bg-brown-500 transition-all text-lg font-medium shadow-md hover:shadow-lg"> 30 - Logout 31 - </button> 32 - </form> 33 - </div> 26 + <!-- <div class="text-center mt-6"> --> 27 + <!-- <form action="/logout" method="POST" class="inline-block"> --> 28 + <!-- <button type="submit" --> 29 + <!-- class="bg-brown-400 text-brown-900 py-3 px-8 rounded-lg hover:bg-brown-500 transition-all text-lg font-medium shadow-md hover:shadow-lg"> --> 30 + <!-- Logout --> 31 + <!-- </button> --> 32 + <!-- </form> --> 33 + <!-- </div> --> 34 34 {{else}} 35 35 <!-- Not authenticated: Show login --> 36 36 <div>
-3
web/static/js/brew-form.js
··· 210 210 method: 'POST', 211 211 headers: { 212 212 'Content-Type': 'application/json', 213 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 214 213 }, 215 214 body: JSON.stringify(payload) 216 215 }); ··· 244 243 method: 'POST', 245 244 headers: { 246 245 'Content-Type': 'application/json', 247 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 248 246 }, 249 247 body: JSON.stringify(this.newGrinder) 250 248 }); ··· 278 276 method: 'POST', 279 277 headers: { 280 278 'Content-Type': 'application/json', 281 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 282 279 }, 283 280 body: JSON.stringify(this.newBrewer) 284 281 });
+23 -2
web/static/js/data-cache.js
··· 6 6 7 7 const CACHE_KEY = 'arabica_data_cache'; 8 8 const CACHE_VERSION = 1; 9 - const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes 10 - const REFRESH_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes 9 + const CACHE_TTL_MS = 30 * 1000; // 30 seconds (shorter for multi-device sync) 10 + const REFRESH_INTERVAL_MS = 30 * 1000; // 30 seconds 11 11 12 12 // Module state 13 13 let refreshTimer = null; ··· 227 227 console.warn('Initial cache load failed:', e); 228 228 } 229 229 } 230 + 231 + // Refresh when user returns to tab/app (handles multi-device sync) 232 + document.addEventListener('visibilitychange', () => { 233 + if (document.visibilityState === 'visible' && !isCacheValid()) { 234 + refreshCache().catch(e => console.warn('Visibility refresh failed:', e)); 235 + } 236 + }); 237 + 238 + // For iOS PWA: refresh on focus 239 + window.addEventListener('focus', () => { 240 + if (!isCacheValid()) { 241 + refreshCache().catch(e => console.warn('Focus refresh failed:', e)); 242 + } 243 + }); 244 + 245 + // Refresh on page show (back button, bfcache restore) 246 + window.addEventListener('pageshow', (event) => { 247 + if (event.persisted && !isCacheValid()) { 248 + refreshCache().catch(e => console.warn('Pageshow refresh failed:', e)); 249 + } 250 + }); 230 251 } 231 252 232 253 /**
-16
web/static/js/manage-page.js
··· 48 48 method, 49 49 headers: { 50 50 'Content-Type': 'application/json', 51 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 52 51 }, 53 52 body: JSON.stringify(this.beanForm) 54 53 }); ··· 70 69 71 70 const response = await fetch(`/api/beans/${rkey}`, { 72 71 method: 'DELETE', 73 - headers: { 74 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 75 - } 76 72 }); 77 73 if (response.ok) { 78 74 // Invalidate cache and reload ··· 105 101 method, 106 102 headers: { 107 103 'Content-Type': 'application/json', 108 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 109 104 }, 110 105 body: JSON.stringify(this.roasterForm) 111 106 }); ··· 127 122 128 123 const response = await fetch(`/api/roasters/${rkey}`, { 129 124 method: 'DELETE', 130 - headers: { 131 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 132 - } 133 125 }); 134 126 if (response.ok) { 135 127 // Invalidate cache and reload ··· 162 154 method, 163 155 headers: { 164 156 'Content-Type': 'application/json', 165 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 166 157 }, 167 158 body: JSON.stringify(this.grinderForm) 168 159 }); ··· 184 175 185 176 const response = await fetch(`/api/grinders/${rkey}`, { 186 177 method: 'DELETE', 187 - headers: { 188 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 189 - } 190 178 }); 191 179 if (response.ok) { 192 180 // Invalidate cache and reload ··· 227 215 method, 228 216 headers: { 229 217 'Content-Type': 'application/json', 230 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 231 218 }, 232 219 body: JSON.stringify(this.brewerForm) 233 220 }); ··· 249 236 250 237 const response = await fetch(`/api/brewers/${rkey}`, { 251 238 method: 'DELETE', 252 - headers: { 253 - 'X-CSRF-Token': window.getCSRFToken ? window.getCSRFToken() : '' 254 - } 255 239 }); 256 240 if (response.ok) { 257 241 // Invalidate cache and reload