diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000000..4dbbc8eabe --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-04-29 - O(n log n) overhead in localeCompare for ISO dates +**Learning:** `localeCompare` is heavily used across the codebase for sorting simple ISO timestamp strings (e.g., `createdAt`), which incurs a significant and unnecessary O(n log n) performance hit compared to raw string comparison. +**Action:** When sorting standard ISO 8601 strings, always replace `localeCompare` with raw comparison (`b > a ? 1 : b < a ? -1 : 0`). Additionally, ensure derived filtered lists in React are wrapped in `useMemo` to avoid redundant O(n) filtering on re-renders. diff --git a/apps/app/src/components/MediaGalleryView.tsx b/apps/app/src/components/MediaGalleryView.tsx index 2f3aec8f2e..c33429ec07 100644 --- a/apps/app/src/components/MediaGalleryView.tsx +++ b/apps/app/src/components/MediaGalleryView.tsx @@ -6,7 +6,7 @@ * APIs (getDatabaseTables, executeDatabaseQuery). */ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { client, type QueryResult } from "../api-client"; type MediaType = "all" | "image" | "video" | "audio"; @@ -177,11 +177,16 @@ export function MediaGalleryView() { } // Sort by date descending + // ⚡ Bolt: Use raw string comparison instead of localeCompare for faster timestamp sorting allMedia.sort((a, b) => { if (!a.createdAt && !b.createdAt) return 0; if (!a.createdAt) return 1; if (!b.createdAt) return -1; - return b.createdAt.localeCompare(a.createdAt); + return b.createdAt > a.createdAt + ? 1 + : b.createdAt < a.createdAt + ? -1 + : 0; }); setMedia(allMedia); @@ -197,16 +202,20 @@ export function MediaGalleryView() { loadMedia(); }, [loadMedia]); - const filtered = media.filter((m) => { - if (filter !== "all" && m.type !== filter) return false; - if ( - search && - !m.filename.toLowerCase().includes(search.toLowerCase()) && - !m.url.toLowerCase().includes(search.toLowerCase()) - ) - return false; - return true; - }); + // ⚡ Bolt: Memoize the derived filtered list and hoist lowercase conversion to prevent O(n) reallocation and redundant conversions on re-renders + const filtered = useMemo(() => { + const searchLower = search?.toLowerCase() ?? ""; + return media.filter((m) => { + if (filter !== "all" && m.type !== filter) return false; + if ( + searchLower && + !m.filename.toLowerCase().includes(searchLower) && + !m.url.toLowerCase().includes(searchLower) + ) + return false; + return true; + }); + }, [media, filter, search]); return (