big performance changes
All checks were successful
Build and Release to Staging / Build Vision Start (push) Successful in 8s
Build and Release to Staging / Build Vision Start Image (push) Successful in 1m3s
Build and Release to Staging / Deploy Vision Start (staging) (push) Successful in 2s

This commit is contained in:
2026-07-03 14:28:26 -03:00
parent 83dcf65069
commit 1580187689
14 changed files with 298 additions and 444 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
import { Website } from '../types';
import { getWebsiteIcon } from './utils/iconService';
@@ -22,47 +22,64 @@ interface IconMetadata {
name: string;
};
};
colors: any; // this can be anything I guess
colors: any;
}
let iconMetadataCache: IconMetadata[] | null = null;
const WebsiteEditModal: React.FC<WebsiteEditModalProps> = ({ website, edit, onClose, onSave, onDelete }) => {
const [name, setName] = useState(website ? website.name : '');
const [url, setUrl] = useState(website ? website.url : '');
const [icon, setIcon] = useState(website ? website.icon : '');
const [iconQuery, setIconQuery] = useState('');
const [iconMetadata, setIconMetadata] = useState<IconMetadata[]>([]);
const [filteredIcons, setFilteredIcons] = useState<IconMetadata[]>([]);
const [iconMetadata, setIconMetadata] = useState<IconMetadata[]>([]);
const [iconsFetched, setIconsFetched] = useState(false);
const debounceRef = useRef<number | null>(null);
useEffect(() => {
fetch('/icon-metadata.json')
const ensureIconMetadata = () => {
if (iconMetadataCache || iconsFetched) return;
setIconsFetched(true);
fetch('/icon-metadata.json', { cache: 'force-cache' })
.then(response => response.json())
.then(data => {
const iconsArray = Object.entries(data).map(([name, details]) => ({
const iconsArray: IconMetadata[] = Object.entries(data).map(([name, details]) => ({
name,
...details
}));
// Expand colors into separate entries
iconsArray.forEach(icon => {
if (icon.colors) {
const colors = Object.values(icon.colors).filter(key => key !== icon.name);
for (const color of colors) {
const newIcon = { ...icon };
newIcon.name = color;
iconsArray.push(newIcon);
}
}
});
...(details as object),
})) as IconMetadata[];
iconMetadataCache = iconsArray;
setIconMetadata(iconsArray);
});
}, []);
})
.catch(err => console.error('Failed to load icon metadata', err));
};
useEffect(() => {
if (iconQuery && Array.isArray(iconMetadata)) {
const lowerCaseQuery = iconQuery.toLowerCase();
const filtered = iconMetadata
.filter(icon => icon.name.toLowerCase().includes(lowerCaseQuery))
.slice(0, 50);
setFilteredIcons(filtered);
if (iconQuery && Array.isArray(iconMetadata) && iconMetadata.length > 0) {
if (debounceRef.current) window.clearTimeout(debounceRef.current);
debounceRef.current = window.setTimeout(() => {
const lowerCaseQuery = iconQuery.toLowerCase();
const filtered: IconMetadata[] = [];
for (const ic of iconMetadata) {
if (ic.name.toLowerCase().includes(lowerCaseQuery)) {
filtered.push(ic);
if (filtered.length >= 50) break;
}
if (ic.colors) {
const colors = Object.values(ic.colors).filter(key => key !== ic.name);
for (const color of colors) {
if (typeof color === 'string' && color.toLowerCase().includes(lowerCaseQuery)) {
filtered.push({ ...ic, name: color });
if (filtered.length >= 50) break;
}
}
if (filtered.length >= 50) break;
}
}
setFilteredIcons(filtered);
}, 150);
return () => {
if (debounceRef.current) window.clearTimeout(debounceRef.current);
};
} else {
setFilteredIcons([]);
}
@@ -76,7 +93,6 @@ const WebsiteEditModal: React.FC<WebsiteEditModalProps> = ({ website, edit, onCl
};
const handleSave = () => {
console.log({ id: website?.id, name, url, icon });
onSave({ id: website?.id, name, url, icon });
};
@@ -128,6 +144,7 @@ const WebsiteEditModal: React.FC<WebsiteEditModalProps> = ({ website, edit, onCl
setIcon(e.target.value);
setIconQuery(e.target.value);
}}
onFocus={ensureIconMetadata}
className="bg-white/10 p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-400 w-full"
/>
{filteredIcons.length > 0 && (