speed up discord loading time

this shouldn't break anything on mine, and I tested it, but still may
give some issues
This commit is contained in:
kmmiio99o 2025-12-22 16:12:22 +01:00
parent 4c93b98a68
commit da0a9bebe4
No known key found for this signature in database
GPG key ID: 13A107AB06ACF9FA
2 changed files with 155 additions and 24 deletions

View file

@ -22,13 +22,14 @@ import {
} from "@metro/common/components";
import { ErrorBoundary, Search } from "@ui/components";
import { isNotNil } from "es-toolkit";
import fuzzysort from "fuzzysort";
import {
ComponentType,
ReactNode,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { Image, ScrollView, View } from "react-native";
@ -198,6 +199,27 @@ export default function AddonPage<T extends object>({
}
}, [navigation]);
const fuzzysortRef = useRef<any>(null);
const [fuzzLoaded, setFuzzLoaded] = useState(false);
useEffect(() => {
let cancelled = false;
if (!fuzzysortRef.current) {
import("fuzzysort")
.then((m) => {
if (cancelled) return;
fuzzysortRef.current = m.default || m;
setFuzzLoaded(true);
})
.catch(() => {
// ignore dynamic import failures; we'll fallback to simple filtering
});
}
return () => {
cancelled = true;
};
}, []);
const results = useMemo(() => {
let values = props.items;
if (props.resolveItem)
@ -205,11 +227,84 @@ export default function AddonPage<T extends object>({
const items = values.filter((i) => isNotNil(i) && typeof i === "object");
if (!search && sortFn) items.sort(sortFn);
return fuzzysort.go(search, items, {
keys: props.searchKeywords,
all: true,
// Prefer fuzzysort when loaded and a search query exists
if (fuzzysortRef.current && search) {
try {
return fuzzysortRef.current.go(search, items, {
keys: props.searchKeywords,
all: true,
});
} catch {
// If fuzzysort throws for some reason, fall back to the simple filter below
}
}
// If there is no search query, return items shaped similarly to fuzzysort results
const makeFallbackResult = (obj: any, keys: any[]) => {
const r: any = { obj };
// current search query in the outer scope; only highlight when there's an active query
const q = String(search ?? "")
.toLowerCase()
.trim();
for (let k = 0; k < keys.length; k++) {
const key = keys[k];
let text = "";
try {
text =
typeof key === "string" ? String(obj[key] ?? "") : String(key(obj));
} catch {
text = "";
}
r[k] = {
target: text,
highlight: (cb: any) => {
// Do not highlight anything when there is no active search.
if (!q) return [];
const lower = text.toLowerCase();
const idx = lower.indexOf(q);
if (idx === -1) return [];
// Split into before/match/after and invoke the callback for each segment so
// consumers can render matched substring(s) with highlight styling while
// preserving surrounding context.
const parts: any[] = [];
let i = 0;
const before = text.slice(0, idx);
const match = text.slice(idx, idx + q.length);
const after = text.slice(idx + q.length);
if (before) parts.push(cb(before, i++));
parts.push(cb(match, i++));
if (after) parts.push(cb(after, i++));
return parts;
},
};
}
return r;
};
if (!search) {
return items.map((obj: any) =>
makeFallbackResult(obj, props.searchKeywords),
);
}
// Basic fallback: case-insensitive substring match across configured keys
const q = String(search).toLowerCase();
const filtered = items.filter((it: any) => {
for (const key of props.searchKeywords) {
try {
const text =
typeof key === "string" ? String(it[key] ?? "") : String(key(it));
if (text.toLowerCase().includes(q)) return true;
} catch {
// ignore any accessor errors and continue
}
}
return false;
});
}, [props.items, sortFn, search]);
return filtered.map((obj: any) =>
makeFallbackResult(obj, props.searchKeywords),
);
}, [props.items, sortFn, search, fuzzLoaded]);
const onInstallPress = useCallback(() => {
if (!props.installAction) return () => {};

View file

@ -16,9 +16,8 @@ import {
Text,
} from "@metro/common/components";
import { findByProps } from "@metro";
import { useMemo, useState } from "react";
import { useMemo, useState, useEffect } from "react";
import { PixelRatio, View } from "react-native";
import { WebView } from "react-native-webview";
import previewHtml from "./preview.html";
import FontEditor from "./FontEditor";
@ -36,6 +35,7 @@ const useStyles = createStyles({
function FontPreview({ font }: { font: FontDefinition }) {
const [loaded, setLoaded] = useState(false);
const [WebViewComponent, setWebViewComponent] = useState<any>(null);
const styles = useStyles();
const TEXT_DEFAULT = useToken(tokens.colors.TEXT_DEFAULT);
@ -53,25 +53,61 @@ function FontPreview({ font }: { font: FontDefinition }) {
[font.main, fontFamily, fontSize, TEXT_DEFAULT],
);
// Memoize generated HTML to avoid string replacement on every render
const html = useMemo(
() => previewHtml.replace("$$props", JSON.stringify(props)),
[props],
);
// Lazy-load the WebView implementation so it's not included in the main bundle
useEffect(() => {
let cancelled = false;
import("react-native-webview")
.then((m) => {
if (cancelled) return;
// support both default export and named WebView export
setWebViewComponent(m.WebView || m.default || m);
})
.catch(() => {
// ignore dynamic import failures; component will show placeholder
});
return () => {
cancelled = true;
};
}, []);
return (
<View style={{ width: "100%", height: 64 }}>
<WebView
onMessage={() => setLoaded(true)}
source={{
html: previewHtml.replace("$$props", JSON.stringify(props)),
}}
javaScriptEnabled
scrollEnabled={false}
overScrollMode="never"
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
pointerEvents="none"
style={[
styles.full,
{ backgroundColor: "transparent", opacity: Number(loaded) },
]}
/>
{!loaded && (
{WebViewComponent ? (
<>
<WebViewComponent
onMessage={() => setLoaded(true)}
source={{ html }}
javaScriptEnabled
scrollEnabled={false}
overScrollMode="never"
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
pointerEvents="none"
style={[
styles.full,
{ backgroundColor: "transparent", opacity: Number(loaded) },
]}
/>
{!loaded && (
<View
style={[
styles.full,
{ justifyContent: "center", alignItems: "center" },
]}
>
<Text color="text-muted" variant="heading-lg/semibold">
Loading...
</Text>
</View>
)}
</>
) : (
<View
style={[
styles.full,