Optimiere LifeWave-Timeline-Performance und entferne toten Code in _src

- TimelineView: Shader-Event-Mapping in computed ausgelagert, deep-Watch
  entfernt, natives Wheel-Scrolling statt @wheel.prevent
- Events-Store: shallowRef statt tiefer Reaktivität, String-Sortierung
  statt Date-Objekten, getGlowColor memoized, DB-Writes debounced
- Edits werden jetzt dedupliziert als 'update' in die SyncQueue geschrieben
  (vorher gingen Änderungen beim Sync verloren)
- useImageCache: In-Flight-Deduping, Cache-Limit mit revokeObjectURL
- FloatingLines: Antialiasing aus (Fullscreen-Quad), deep-Watches entfernt
- Ungenutzte Seiten/Komponenten entfernt (~6.700 Zeilen, nicht geroutet)
- implementation-plan.md: Hinweis zu update-Action im Sync ergänzt
This commit is contained in:
Kevin Adametz 2026-07-07 15:54:35 +00:00
parent c1514999be
commit f79dcb9928
20 changed files with 185 additions and 6781 deletions

View file

@ -4,9 +4,25 @@ import { db } from 'src/db'
const THUMB_SIZE = 200
// In-memory URL cache: avoids repeated IndexedDB reads and blob URL creation
// Shared across all component instances
// Shared across all component instances. Bounded: oldest entries are evicted
// and their blob URLs revoked once the cap is reached.
const MEMORY_CACHE_MAX = 300
const memoryCache = new Map()
// In-flight resolutions: parallel requests for the same URL share one promise
// (many dots can mount at once referencing the same image)
const inFlight = new Map()
function memoryCacheSet(url, blobUrl) {
if (memoryCache.size >= MEMORY_CACHE_MAX) {
// Map iterates in insertion order → first entry is the oldest
const [oldestUrl, oldestBlobUrl] = memoryCache.entries().next().value
URL.revokeObjectURL(oldestBlobUrl)
memoryCache.delete(oldestUrl)
}
memoryCache.set(url, blobUrl)
}
/**
* Create a thumbnail (THUMB_SIZE x THUMB_SIZE) from a source image blob.
* Returns a new Blob (JPEG, quality 0.8).
@ -68,7 +84,7 @@ async function fetchAndCache(imageUrl, eventId) {
})
const blobUrl = URL.createObjectURL(thumbBlob)
memoryCache.set(imageUrl, blobUrl)
memoryCacheSet(imageUrl, blobUrl)
return blobUrl
}
@ -84,7 +100,7 @@ async function getCachedImage(imageUrl) {
const entry = await db.imageCache.get(imageUrl)
if (entry?.blob) {
const blobUrl = URL.createObjectURL(entry.blob)
memoryCache.set(imageUrl, blobUrl)
memoryCacheSet(imageUrl, blobUrl)
return blobUrl
}
} catch (e) {
@ -93,6 +109,38 @@ async function getCachedImage(imageUrl) {
return null
}
/**
* Resolve an image URL to a displayable src (memory → IndexedDB → fetch).
* Concurrent calls for the same URL share a single in-flight promise.
* Falls back to the original URL if caching fails.
*/
export function resolveImage(imageUrl, eventId) {
if (!imageUrl) return Promise.resolve(null)
if (memoryCache.has(imageUrl)) {
return Promise.resolve(memoryCache.get(imageUrl))
}
if (inFlight.has(imageUrl)) {
return inFlight.get(imageUrl)
}
const promise = (async () => {
const cached = await getCachedImage(imageUrl)
if (cached) return cached
try {
return await fetchAndCache(imageUrl, eventId)
} catch (e) {
// Fallback: use original URL directly (works when online)
console.warn('Image cache failed, using direct URL:', e)
return imageUrl
}
})().finally(() => inFlight.delete(imageUrl))
inFlight.set(imageUrl, promise)
return promise
}
/**
* Composable: resolves an event's image to a displayable src.
* - Checks memory cache → IndexedDB cache → fetches & caches thumbnail.
@ -100,42 +148,11 @@ async function getCachedImage(imageUrl) {
*/
export function useImageCache(imageUrl, eventId) {
const resolvedSrc = ref(null)
const loading = ref(false)
const loading = ref(true)
async function resolve() {
if (!imageUrl) {
resolvedSrc.value = null
return
}
// 1. Memory cache (instant)
if (memoryCache.has(imageUrl)) {
resolvedSrc.value = memoryCache.get(imageUrl)
return
}
// 2. IndexedDB cache
const cached = await getCachedImage(imageUrl)
if (cached) {
resolvedSrc.value = cached
return
}
// 3. Fetch, create thumbnail, cache
loading.value = true
try {
const blobUrl = await fetchAndCache(imageUrl, eventId)
resolvedSrc.value = blobUrl
} catch (e) {
// Fallback: use original URL directly (works when online)
console.warn('Image cache failed, using direct URL:', e)
resolvedSrc.value = imageUrl
} finally {
loading.value = false
}
}
resolve()
resolveImage(imageUrl, eventId)
.then((src) => { resolvedSrc.value = src })
.finally(() => { loading.value = false })
return { resolvedSrc, loading }
}