mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
feat: add fuse.js to store search
This commit is contained in:
@@ -72,6 +72,7 @@
|
|||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"dompurify": "^3.1.6",
|
"dompurify": "^3.1.6",
|
||||||
|
"fuse.js": "^7.0.0",
|
||||||
"idb": "^8.0.0",
|
"idb": "^8.0.0",
|
||||||
"kolorist": "^1.8.0",
|
"kolorist": "^1.8.0",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import { setTheme } from '@/seqta/ui/themes/setTheme';
|
import { setTheme } from '@/seqta/ui/themes/setTheme';
|
||||||
import Spinner from '../Spinner.svelte';
|
import Spinner from '../Spinner.svelte';
|
||||||
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
|
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
|
||||||
|
import Fuse from 'fuse.js';
|
||||||
|
import { backgroundUpdates } from '@/svelte-interface/hooks/BackgroundUpdates'
|
||||||
|
|
||||||
type Background = { id: string; category: string; type: string; lowResUrl: string; highResUrl: string; name: string; description: string; featured?: boolean };
|
type Background = { id: string; category: string; type: string; lowResUrl: string; highResUrl: string; name: string; description: string; featured?: boolean };
|
||||||
let { searchTerm } = $props<{ searchTerm: string }>();
|
let { searchTerm } = $props<{ searchTerm: string }>();
|
||||||
@@ -21,6 +23,14 @@
|
|||||||
let activeTab = $state<'all' | 'installed' | 'photos' | 'videos'>('all');
|
let activeTab = $state<'all' | 'installed' | 'photos' | 'videos'>('all');
|
||||||
let sortBy = $state<'newest' | 'popular' | 'name'>('newest');
|
let sortBy = $state<'newest' | 'popular' | 'name'>('newest');
|
||||||
|
|
||||||
|
// Add Fuse.js options
|
||||||
|
const fuseOptions = {
|
||||||
|
keys: ['name', 'description'],
|
||||||
|
threshold: 0.4,
|
||||||
|
ignoreLocation: true
|
||||||
|
};
|
||||||
|
let fuse: Fuse<Background>;
|
||||||
|
|
||||||
// Existing functions
|
// Existing functions
|
||||||
const loadStore = async () => {
|
const loadStore = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -31,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
backgrounds = data.backgrounds;
|
backgrounds = data.backgrounds;
|
||||||
console.log(data.backgrounds);
|
fuse = new Fuse(backgrounds, fuseOptions);
|
||||||
debugInfo = `Loaded ${backgrounds.length} backgrounds`;
|
debugInfo = `Loaded ${backgrounds.length} backgrounds`;
|
||||||
await loadSavedBackgrounds();
|
await loadSavedBackgrounds();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -60,15 +70,20 @@
|
|||||||
|
|
||||||
// Derived states
|
// Derived states
|
||||||
let filteredBackgrounds = $derived((() => {
|
let filteredBackgrounds = $derived((() => {
|
||||||
let filtered = backgrounds.filter((bg: Background) => {
|
let filtered = backgrounds;
|
||||||
const matchesCategory = selectedCategory === 'All'
|
|
||||||
|
// Use Fuse.js search if there's a search term
|
||||||
|
if (searchTerm.trim()) {
|
||||||
|
filtered = fuse?.search(searchTerm).map((result: any) => result.item) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply category filtering
|
||||||
|
filtered = filtered.filter((bg: Background) => {
|
||||||
|
return selectedCategory === 'All'
|
||||||
? true
|
? true
|
||||||
: selectedCategory === 'Featured'
|
: selectedCategory === 'Featured'
|
||||||
? bg.featured
|
? bg.featured
|
||||||
: bg.category === selectedCategory;
|
: bg.category === selectedCategory;
|
||||||
const matchesSearch = bg.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
bg.description.toLowerCase().includes(searchTerm.toLowerCase());
|
|
||||||
return matchesCategory && matchesSearch;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply sorting
|
// Apply sorting
|
||||||
@@ -133,6 +148,7 @@
|
|||||||
installingBackgrounds = new Set(installingBackgrounds).add(background.id);
|
installingBackgrounds = new Set(installingBackgrounds).add(background.id);
|
||||||
try {
|
try {
|
||||||
await saveBackgroundFromUrl(background.highResUrl, background.id, background.type);
|
await saveBackgroundFromUrl(background.highResUrl, background.id, background.type);
|
||||||
|
backgroundUpdates.triggerUpdate();
|
||||||
} finally {
|
} finally {
|
||||||
installingBackgrounds = new Set(installingBackgrounds);
|
installingBackgrounds = new Set(installingBackgrounds);
|
||||||
installingBackgrounds.delete(background.id);
|
installingBackgrounds.delete(background.id);
|
||||||
@@ -191,7 +207,7 @@
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="sticky top-0 z-10 p-4 bg-white border-b dark:bg-zinc-900 dark:border-zinc-700">
|
<div class="sticky top-0 z-10 p-4 bg-white border-b dark:bg-zinc-900 dark:border-zinc-700">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<h1 class="text-2xl font-bold">Explore Backgrounds</h1>
|
<h1 class="text-2xl font-bold">Explore Backgrounds {searchTerm ? `- "${searchTerm}"` : ''}</h1>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<select
|
<select
|
||||||
bind:value={sortBy}
|
bind:value={sortBy}
|
||||||
@@ -281,9 +297,6 @@
|
|||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute bottom-0 left-0 right-0 p-2 bg-gradient-to-t from-black to-transparent">
|
|
||||||
<h3 class="text-sm font-semibold text-white">{background.name}</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { onMount, onDestroy } from 'svelte'
|
import { onMount, onDestroy } from 'svelte'
|
||||||
import { loadBackground } from '@/seqta/ui/ImageBackgrounds'
|
import { loadBackground } from '@/seqta/ui/ImageBackgrounds'
|
||||||
import { delay } from 'lodash'
|
import { delay } from 'lodash'
|
||||||
|
import { backgroundUpdates } from '@/svelte-interface/hooks/BackgroundUpdates'
|
||||||
|
|
||||||
let { isEditMode, selectNoBackground = $bindable(), selectedBackground = $bindable() } = $props<{ isEditMode: boolean, selectNoBackground: () => void, selectedBackground: string | null }>();
|
let { isEditMode, selectNoBackground = $bindable(), selectedBackground = $bindable() } = $props<{ isEditMode: boolean, selectNoBackground: () => void, selectedBackground: string | null }>();
|
||||||
let backgrounds = $state<{ id: string; type: string; blob: Blob | null; url?: string }[]>([]);
|
let backgrounds = $state<{ id: string; type: string; blob: Blob | null; url?: string }[]>([]);
|
||||||
@@ -77,7 +78,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFullBackgrounds(): Promise<void> {
|
async function syncBackgrounds(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
error = null;
|
error = null;
|
||||||
|
|
||||||
@@ -85,8 +86,25 @@
|
|||||||
throw new Error("Your browser doesn't support IndexedDB. Unable to load backgrounds.");
|
throw new Error("Your browser doesn't support IndexedDB. Unable to load backgrounds.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await readAllData();
|
const dbData = await readAllData();
|
||||||
backgrounds = await preloadBackgrounds(data);
|
|
||||||
|
// Release existing object URLs to prevent memory leaks
|
||||||
|
backgrounds.forEach(bg => {
|
||||||
|
if (bg.url) URL.revokeObjectURL(bg.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create fresh background objects with new object URLs
|
||||||
|
backgrounds = dbData.map(bg => ({
|
||||||
|
id: bg.id,
|
||||||
|
type: bg.type,
|
||||||
|
blob: bg.blob,
|
||||||
|
url: URL.createObjectURL(bg.blob)
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Check if selected background still exists
|
||||||
|
if (selectedBackground && !backgrounds.some(bg => bg.id === selectedBackground)) {
|
||||||
|
selectNoBackground();
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
error = e.message;
|
error = e.message;
|
||||||
@@ -96,13 +114,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function preloadBackgrounds(data: { id: string; type: string; blob: Blob }[]): Promise<{ id: string; type: string; blob: Blob; url: string }[]> {
|
|
||||||
return data.map(bg => ({
|
|
||||||
...bg,
|
|
||||||
url: URL.createObjectURL(bg.blob)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectBackground(fileId: string): void {
|
function selectBackground(fileId: string): void {
|
||||||
if (selectedBackground === fileId) {
|
if (selectedBackground === fileId) {
|
||||||
selectNoBackground();
|
selectNoBackground();
|
||||||
@@ -150,13 +161,14 @@
|
|||||||
if (parentElement?.classList.contains('active')) {
|
if (parentElement?.classList.contains('active')) {
|
||||||
delay(() => {
|
delay(() => {
|
||||||
isVisible = true;
|
isVisible = true;
|
||||||
loadFullBackgrounds();
|
syncBackgrounds();
|
||||||
}, 600);
|
}, 600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
loadBackgroundMetadata();
|
loadBackgroundMetadata();
|
||||||
|
backgroundUpdates.addListener(syncBackgrounds);
|
||||||
|
|
||||||
parentElement = element.closest('.tab');
|
parentElement = element.closest('.tab');
|
||||||
if (parentElement) {
|
if (parentElement) {
|
||||||
@@ -165,6 +177,7 @@
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
|
backgroundUpdates.removeListener(syncBackgrounds);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
type BackgroundUpdateCallback = () => void;
|
||||||
|
|
||||||
|
class BackgroundUpdates {
|
||||||
|
private static instance: BackgroundUpdates;
|
||||||
|
private listeners: Set<BackgroundUpdateCallback> = new Set();
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
public static getInstance(): BackgroundUpdates {
|
||||||
|
if (!BackgroundUpdates.instance) {
|
||||||
|
BackgroundUpdates.instance = new BackgroundUpdates();
|
||||||
|
}
|
||||||
|
return BackgroundUpdates.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addListener(callback: BackgroundUpdateCallback): void {
|
||||||
|
this.listeners.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeListener(callback: BackgroundUpdateCallback): void {
|
||||||
|
this.listeners.delete(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public triggerUpdate(): void {
|
||||||
|
this.listeners.forEach(callback => callback());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const backgroundUpdates = BackgroundUpdates.getInstance();
|
||||||
Reference in New Issue
Block a user