mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 17:21:05 +00:00
feat: optimisations
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
import { hasEnoughStorageSpace, isIndexedDBSupported, writeData, openDatabase, readAllData, deleteData } from '@/interface/hooks/BackgroundDataLoader';
|
||||
import Spinner from '../Spinner.svelte';
|
||||
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
|
||||
import { Index } from 'flexsearch';
|
||||
import { backgroundUpdates } from '@/interface/hooks/BackgroundUpdates'
|
||||
import { ThemeManager } from '@/plugins/built-in/themes/theme-manager'
|
||||
|
||||
@@ -20,8 +19,6 @@
|
||||
let savedBackgrounds = $state<string[]>([]);
|
||||
let installingBackgrounds = $state<Set<string>>(new Set());
|
||||
let debugInfo = $state<string>('');
|
||||
let searchIndex = $state<Index | null>(null);
|
||||
|
||||
// New state variables
|
||||
let activeTab = $state<'all' | 'installed' | 'photos' | 'videos'>('all');
|
||||
let sortBy = $state<'newest' | 'popular' | 'name'>('newest');
|
||||
@@ -36,19 +33,6 @@
|
||||
}
|
||||
const data = await response.json();
|
||||
backgrounds = data.backgrounds;
|
||||
|
||||
// Initialize FlexSearch index
|
||||
const index = new Index({
|
||||
tokenize: "forward",
|
||||
preset: "score"
|
||||
});
|
||||
|
||||
// Add backgrounds to the index
|
||||
backgrounds.forEach((bg, i) => {
|
||||
index.add(i, bg.name + " " + bg.description);
|
||||
});
|
||||
|
||||
searchIndex = index;
|
||||
debugInfo = `Loaded ${backgrounds.length} backgrounds`;
|
||||
await loadSavedBackgrounds();
|
||||
} catch (e) {
|
||||
@@ -79,10 +63,11 @@
|
||||
let filteredBackgrounds = $derived((() => {
|
||||
let filtered = backgrounds;
|
||||
|
||||
// Use FlexSearch if there's a search term
|
||||
if (searchTerm.trim() && searchIndex) {
|
||||
const results = searchIndex.search(searchTerm) as number[];
|
||||
filtered = results.map(i => backgrounds[i]);
|
||||
if (searchTerm.trim()) {
|
||||
const q = searchTerm.trim().toLowerCase();
|
||||
filtered = backgrounds.filter((bg) =>
|
||||
`${bg.name} ${bg.description}`.toLowerCase().includes(q),
|
||||
);
|
||||
}
|
||||
|
||||
// Apply category filtering
|
||||
|
||||
@@ -1,149 +1,55 @@
|
||||
import { type DBSchema, type IDBPDatabase, openDB } from "idb";
|
||||
import localforage from "localforage";
|
||||
|
||||
/**
|
||||
* Defines the schema for the IndexedDB database used for storing background image data.
|
||||
*
|
||||
* @interface BackgroundDB
|
||||
* @extends {DBSchema}
|
||||
* @property {object} backgrounds - The object store for background images.
|
||||
* @property {string} backgrounds.key - The type of the key for the object store (in this case, it's `id` as defined in `keyPath`).
|
||||
* @property {object} backgrounds.value - The structure of the objects stored.
|
||||
* @property {string} backgrounds.value.id - The unique identifier for the background image record.
|
||||
* @property {string} backgrounds.value.type - The MIME type of the image (e.g., "image/png", "image/jpeg").
|
||||
* @property {Blob} backgrounds.value.blob - The binary large object (Blob) containing the image data.
|
||||
*/
|
||||
interface BackgroundDB extends DBSchema {
|
||||
backgrounds: {
|
||||
key: string; // Corresponds to the 'id' property due to keyPath: "id"
|
||||
value: {
|
||||
id: string;
|
||||
type: string;
|
||||
blob: Blob;
|
||||
};
|
||||
};
|
||||
type BackgroundRecord = { id: string; type: string; blob: Blob };
|
||||
|
||||
const store = localforage.createInstance({
|
||||
name: "BackgroundDB",
|
||||
storeName: "backgrounds",
|
||||
});
|
||||
|
||||
export async function openDatabase(): Promise<typeof store> {
|
||||
await store.ready();
|
||||
return store;
|
||||
}
|
||||
|
||||
let db: IDBPDatabase<BackgroundDB> | null = null;
|
||||
|
||||
/**
|
||||
* Initializes and opens an IndexedDB connection or returns an existing one.
|
||||
* If the database doesn't exist or needs an upgrade, the `upgrade` callback
|
||||
* creates the 'backgrounds' object store with 'id' as the keyPath.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<IDBPDatabase<BackgroundDB>>} A promise that resolves with the database instance.
|
||||
*/
|
||||
export async function openDatabase(): Promise<IDBPDatabase<BackgroundDB>> {
|
||||
if (db) return db;
|
||||
|
||||
db = await openDB<BackgroundDB>("BackgroundDB", 1, {
|
||||
upgrade(db: IDBPDatabase<BackgroundDB>) {
|
||||
db.createObjectStore("backgrounds", { keyPath: "id" });
|
||||
},
|
||||
export async function readAllData(): Promise<BackgroundRecord[]> {
|
||||
await store.ready();
|
||||
const items: BackgroundRecord[] = [];
|
||||
await store.iterate<BackgroundRecord, void>((value) => {
|
||||
if (value?.id && value.blob) items.push(value);
|
||||
});
|
||||
|
||||
return db;
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all background image records from the 'backgrounds' object store in IndexedDB.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<Array<{id: string, type: string, blob: Blob}>>} A promise that resolves with an array of all background image records.
|
||||
*/
|
||||
export async function readAllData(): Promise<
|
||||
Array<{ id: string; type: string; blob: Blob }>
|
||||
> {
|
||||
const db = await openDatabase();
|
||||
return db.getAll("backgrounds");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes or updates a background image record in the 'backgrounds' object store.
|
||||
* If a record with the given `id` already exists, it will be updated. Otherwise, a new record is created.
|
||||
*
|
||||
* @async
|
||||
* @param {string} id - The unique identifier for the background image record.
|
||||
* @param {string} type - The MIME type of the image (e.g., "image/png").
|
||||
* @param {Blob} blob - The Blob object containing the image data.
|
||||
* @returns {Promise<void>} A promise that resolves when the data has been successfully written.
|
||||
*/
|
||||
export async function writeData(
|
||||
id: string,
|
||||
type: string,
|
||||
blob: Blob,
|
||||
): Promise<void> {
|
||||
const db = await openDatabase();
|
||||
await db.put("backgrounds", { id, type, blob });
|
||||
await store.setItem(id, { id, type, blob });
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a background image record from the 'backgrounds' object store by its ID.
|
||||
*
|
||||
* @async
|
||||
* @param {string} id - The unique identifier of the background image record to delete.
|
||||
* @returns {Promise<void>} A promise that resolves when the data has been successfully deleted.
|
||||
*/
|
||||
export async function deleteData(id: string): Promise<void> {
|
||||
const db = await openDatabase();
|
||||
await db.delete("backgrounds", id);
|
||||
await store.removeItem(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all records from the 'backgrounds' object store in IndexedDB.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>} A promise that resolves when all data has been successfully cleared.
|
||||
*/
|
||||
export async function clearAllData(): Promise<void> {
|
||||
const db = await openDatabase();
|
||||
await db.clear("backgrounds");
|
||||
await store.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single background image record from the 'backgrounds' object store by its ID.
|
||||
*
|
||||
* @async
|
||||
* @param {string} id - The unique identifier of the background image record to retrieve.
|
||||
* @returns {Promise<{id: string, type: string, blob: Blob} | undefined>} A promise that resolves with the
|
||||
* background image record if found, or undefined otherwise.
|
||||
*/
|
||||
export async function getDataById(
|
||||
id: string,
|
||||
): Promise<{ id: string; type: string; blob: Blob } | undefined> {
|
||||
const db = await openDatabase();
|
||||
return db.get("backgrounds", id);
|
||||
): Promise<BackgroundRecord | undefined> {
|
||||
const item = await store.getItem<BackgroundRecord>(id);
|
||||
return item ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the active IndexedDB connection and nullifies the global `db` variable.
|
||||
* This is important to release resources and allow for proper database management.
|
||||
*/
|
||||
export function closeDatabase(): void {
|
||||
if (db) {
|
||||
db.close();
|
||||
db = null;
|
||||
}
|
||||
}
|
||||
export function closeDatabase(): void {}
|
||||
|
||||
/**
|
||||
* Checks if IndexedDB is supported by the current browser environment.
|
||||
*
|
||||
* @returns {boolean} True if IndexedDB is supported, false otherwise.
|
||||
*/
|
||||
export function isIndexedDBSupported(): boolean {
|
||||
return "indexedDB" in window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates available storage space and checks if it's sufficient for the specified `requiredSpace`.
|
||||
* Uses the `navigator.storage.estimate()` API if available.
|
||||
* If the API is not available or cannot determine space, it defaults to assuming enough space is available.
|
||||
*
|
||||
* @async
|
||||
* @param {number} requiredSpace - The amount of storage space required, in bytes.
|
||||
* @returns {Promise<boolean>} A promise that resolves with true if enough space is estimated to be available, false otherwise.
|
||||
*/
|
||||
export async function hasEnoughStorageSpace(
|
||||
requiredSpace: number,
|
||||
): Promise<boolean> {
|
||||
@@ -153,6 +59,5 @@ export async function hasEnoughStorageSpace(
|
||||
return quota - usage > requiredSpace;
|
||||
}
|
||||
}
|
||||
// If we can't determine, assume there's enough space
|
||||
return true;
|
||||
}
|
||||
|
||||
+11
-2
@@ -1,11 +1,20 @@
|
||||
import { mount } from "svelte";
|
||||
import type { SvelteComponent } from "svelte";
|
||||
import style from "./index.css?inline";
|
||||
import settingsStyle from "./index.css?inline";
|
||||
import contentShadowStyle from "./contentShadow.css?inline";
|
||||
|
||||
const shadowStyles = {
|
||||
settings: settingsStyle,
|
||||
content: contentShadowStyle,
|
||||
} as const;
|
||||
|
||||
export type ShadowStyleVariant = keyof typeof shadowStyles;
|
||||
|
||||
export default function renderSvelte(
|
||||
Component: SvelteComponent | any,
|
||||
mountPoint: ShadowRoot | HTMLElement,
|
||||
props: Record<string, any> = {},
|
||||
shadowStyle: ShadowStyleVariant = "settings",
|
||||
) {
|
||||
const app = mount(Component, {
|
||||
target: mountPoint,
|
||||
@@ -17,7 +26,7 @@ export default function renderSvelte(
|
||||
|
||||
if (mountPoint instanceof ShadowRoot) {
|
||||
const styleElement = document.createElement("style");
|
||||
styleElement.textContent = style;
|
||||
styleElement.textContent = shadowStyles[shadowStyle];
|
||||
mountPoint.appendChild(styleElement);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
import { closeExtensionPopup } from "@/seqta/utils/Closers/closeExtensionPopup";
|
||||
import { OpenAboutPage } from "@/seqta/utils/Openers/OpenAboutPage";
|
||||
import { OpenWhatsNewPopup } from "@/seqta/utils/Openers/OpenWhatsNewPopup";
|
||||
//import { OpenMinecraftServerPopup } from "@/seqta/utils/Openers/OpenMinecraftServerPopup";
|
||||
|
||||
import type { Component } from "svelte";
|
||||
import FontPickerModal from "../components/FontPickerModal.svelte";
|
||||
@@ -87,11 +86,6 @@
|
||||
closeExtensionPopup();
|
||||
};
|
||||
|
||||
/* const openMinecraftServer = () => {
|
||||
OpenMinecraftServerPopup();
|
||||
closeExtensionPopup();
|
||||
}; */
|
||||
|
||||
const openPrivacyStatement = () => {
|
||||
window.open("https://betterseqta.org/privacy", "_blank");
|
||||
closeExtensionPopup();
|
||||
@@ -221,140 +215,6 @@
|
||||
{"\uecba"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- <button
|
||||
onclick={openMinecraftServer}
|
||||
class="flex justify-center items-center p-1 w-8 h-8 rounded-xl bg-zinc-100 dark:bg-zinc-700"
|
||||
aria-label="Open Minecraft Server"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 64 70"
|
||||
fill="none"
|
||||
class="w-full h-full"
|
||||
>
|
||||
<path
|
||||
d="M0 0 C3.96 0 7.92 0 12 0 C12 3.96 12 7.92 12 12 C10.68 12 9.36 12 8 12 C8 10.68 8 9.36 8 8 C6.68 8 5.36 8 4 8 C4 6.68 4 5.36 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(42,10)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 6.6 4 13.2 4 20 C2.68 20 1.36 20 0 20 C0 13.4 0 6.8 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(54,22)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C6.6 0 13.2 0 20 0 C20 1.32 20 2.64 20 4 C13.4 4 6.8 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(22,6)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 5.28 4 10.56 4 16 C2.68 16 1.36 16 0 16 C0 10.72 0 5.44 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(46,26)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C5.28 0 10.56 0 16 0 C16 1.32 16 2.64 16 4 C10.72 4 5.44 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(22,14)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C5.32 4 6.64 4 8 4 C8 5.32 8 6.64 8 8 C5.36 8 2.72 8 0 8 C0 5.36 0 2.72 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(6,50)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(14,50)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(18,46)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(10,46)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(50,42)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(22,42)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(14,42)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(26,38)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(18,38)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(30,34)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(22,34)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(34,30)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(26,30)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(38,26)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(30,26)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(42,22)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(34,22)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(38,18)"
|
||||
/>
|
||||
<path
|
||||
d="M0 0 C1.32 0 2.64 0 4 0 C4 1.32 4 2.64 4 4 C2.68 4 1.36 4 0 4 C0 2.68 0 1.36 0 0 Z "
|
||||
fill="currentColor"
|
||||
transform="translate(18,10)"
|
||||
/>
|
||||
</svg>
|
||||
</button> -->
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { onMount } from 'svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { fade } from 'svelte/transition';
|
||||
@@ -35,7 +34,7 @@
|
||||
const themeManager = ThemeManager.getInstance();
|
||||
|
||||
let theme = $state<LoadedCustomTheme>({
|
||||
id: uuidv4(),
|
||||
id: crypto.randomUUID(),
|
||||
name: '',
|
||||
description: '',
|
||||
defaultColour: 'blue',
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { mount } from "svelte";
|
||||
import type { SvelteComponent } from "svelte";
|
||||
import style from "./contentShadow.css?inline";
|
||||
|
||||
/** Mount Svelte UI inside a shadow root from content scripts (decoupled from settings popup CSS). */
|
||||
export default function renderInShadow(
|
||||
Component: SvelteComponent | any,
|
||||
mountPoint: ShadowRoot | HTMLElement,
|
||||
props: Record<string, any> = {},
|
||||
) {
|
||||
const app = mount(Component, {
|
||||
target: mountPoint,
|
||||
props: {
|
||||
standalone: false,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
|
||||
if (mountPoint instanceof ShadowRoot) {
|
||||
const styleElement = document.createElement("style");
|
||||
styleElement.textContent = style;
|
||||
mountPoint.appendChild(styleElement);
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
Reference in New Issue
Block a user