ref: use react router

fix: a critical performance issue
add: inpage-link for onesearch
This commit is contained in:
alikia2x 2024-08-04 03:26:33 +08:00
parent 263b82c06e
commit 609f1abe31
12 changed files with 162 additions and 121 deletions

View File

@ -1,30 +1,30 @@
import { normalizeURL } from "lib/normalizeURL";
import { useNavigate } from "react-router";
export default function Link(props: { children: React.ReactNode; query: string; selected: boolean }) {
if (props.selected) {
return (
<div
className={`w-full h-10 leading-10 bg-zinc-300 dark:bg-zinc-700
px-5 z-10 cursor-pointer duration-100`}
onClick={() => {
window.open(normalizeURL(props.query));
}}
>
{props.children}
</div>
);
}
else {
return (
<div
className={`w-full h-10 leading-10 bg-zinc-100 hover:bg-zinc-300
dark:bg-zinc-800 hover:dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100`}
onClick={() => {
window.open(normalizeURL(props.query));
}}
>
{props.children}
</div>
);
}
interface LinkSuggestionProps {
children: React.ReactNode;
query: string;
selected: boolean;
inPage?: boolean;
}
export default function LinkSuggestion(props: LinkSuggestionProps) {
const className = props.selected
? `w-full h-10 leading-10 bg-zinc-300 dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100`
: `w-full h-10 leading-10 bg-zinc-100 hover:bg-zinc-300 dark:bg-zinc-800 hover:dark:bg-zinc-700 px-5 z-10 cursor-pointer duration-100`;
const navigate = useNavigate();
return (
<div
className={className}
onClick={() => {
if (props.inPage) {
navigate(props.query);
} else {
window.open(normalizeURL(props.query));
}
}}
>
{props.children}
</div>
);
}

View File

@ -6,7 +6,7 @@ import getSearchEngineName from "lib/onesearch/getSearchEngineName";
import PlainSearch from "./plainSearch";
import { suggestionAtom } from "lib/state/suggestion";
import validLink from "lib/url/validLink";
import Link from "./link";
import LinkSuggestion from "./link";
import { selectedSuggestionAtom } from "lib/state/suggestionSelection";
import { settingsAtom } from "lib/state/settings";
import PlainText from "./plainText";
@ -86,12 +86,14 @@ export default function OneSearch() {
});
}
(async function () {
const NLU = await import("lib/nlp/load");
const mainNLUModel = new NLU.NLU();
setNLUModel(mainNLUModel);
setNLUModelLoaded(true);
})();
useEffect(() => {
(async function () {
const NLU = await import("lib/nlp/load");
const mainNLUModel = new NLU.NLU();
setNLUModel(mainNLUModel);
setNLUModelLoaded(true);
})();
}, []);
useEffect(() => {
if (NLUModel === null || NLUModel === undefined) {
@ -172,7 +174,7 @@ export default function OneSearch() {
s.type === "link"
) {
return (
<Link key={i} query={s.suggestion} selected={i == selected}>
<LinkSuggestion key={i} query={s.suggestion} selected={i == selected}>
{s.prompt && (
<span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>
)}
@ -182,7 +184,7 @@ export default function OneSearch() {
{s.relevance}
</span>
)}
</Link>
</LinkSuggestion>
);
} else if (s.type === "text") {
return (
@ -198,6 +200,25 @@ export default function OneSearch() {
)}
</PlainText>
);
} else if (s.type === "inpage-link") {
return (
<LinkSuggestion
key={i}
query={s.suggestion}
selected={i == selected}
inPage={true}
>
{s.prompt && (
<span className="text-zinc-700 dark:text-zinc-400">{s.prompt}</span>
)}
{s.suggestion}
{devMode && (
<span className="absolute text-zinc-700 dark:text-zinc-400 text-sm leading-10 h-10 right-2">
{s.relevance}
</span>
)}
</LinkSuggestion>
);
}
})}
</SuggestionBox>

View File

@ -18,8 +18,11 @@ export default function (
} else if (selected.type === "NAVIGATION" || selected.type === "default-link") {
window.open(normalizeURL(selected.suggestion));
} else if (selected.type === "text") {
console.log("????");
copyToClipboard(selected.suggestion);
searchBoxRef.current?.focus();
} else if (selected.type === "link") {
window.open(normalizeURL(selected.suggestion));
} else if (selected.type === "inpage-link") {
location.href = normalizeURL(selected.suggestion);
}
}

View File

@ -16,7 +16,7 @@ export function keywordSuggestion(query: string) {
for (const keyword in dict_cn) {
if (query.includes(keyword)) {
const result: suggestionItem = {
type: "link",
type: "inpage-link",
suggestion: dict_cn[keyword],
prompt: keyword,
relevance: 3000
@ -27,7 +27,7 @@ export function keywordSuggestion(query: string) {
for (const keyword in dict_en) {
if (query.includes(keyword)) {
const result: suggestionItem = {
type: "link",
type: "inpage-link",
suggestion: dict_en[keyword],
prompt: keyword,
relevance: 3000

View File

@ -1,7 +1,7 @@
{
"name": "sparkhome",
"private": false,
"version": "5.6.0",
"version": "5.7.0",
"type": "module",
"scripts": {
"dev": "bun server.ts",

View File

@ -58,7 +58,7 @@ function Version(props: { title: string; version: string; versionClass?: string
<span
className={
"relative px-2 py-1 text-sm font-bold rounded-md text-nowrap text-white " +
props.versionClass ?? ""
props.versionClass || ""
}
>
{props.version}

View File

@ -12,8 +12,9 @@ export default function Homepage() {
const setBgFocus = useSetAtom(bgFocusAtom);
return (
<div className="h-screen fixed overflow-hidden w-screen bg-black">
<div className="h-screen w-screen overflow-x-hidden bg-white dark:bg-[rgb(23,25,29)]">
<Background />
<EngineSelector
className="absolute top-20 lg:top-44 short:top-0 translate-x-[-50%] translate-y-[-0.2rem]
left-1/2 w-11/12 sm:w-[700px] text:black text-right

View File

@ -1,74 +1,31 @@
import { Suspense } from "react";
import { useRoutes } from "react-router-dom";
import routes from "~react-pages";
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from 'i18next-browser-languagedetector';
import ICU from 'i18next-icu';
import * as en from "i18n/en.json"
import * as zh from "i18n/zh.json"
import * as ja from "i18n/ja.json"
import * as ar from "i18n/ar.json"
import * as de from "i18n/de.json"
import * as es from "i18n/es.json"
import * as fr from "i18n/fr.json"
import * as it from "i18n/it.json"
import * as ko from "i18n/ko.json"
import * as pt from "i18n/pt.json"
import * as ru from "i18n/ru.json"
import { createBrowserRouter, RouterProvider } from "react-router-dom";
i18n.use(initReactI18next) // passes i18n down to react-i18next
.use(LanguageDetector)
.use(ICU)
.init({
resources: {
en: {
translation: en
},
zh: {
translation: zh
},
ja: {
translation: ja
},
ar: {
translation: ar
},
de: {
translation: de
},
es: {
translation: es
},
fr: {
translation: fr
},
it: {
translation: it
},
ko: {
translation: ko
},
pt: {
translation: pt
},
ru: {
translation: ru
import "./i18n";
import Homepage from "pages";
import AboutPage from "pages/about";
import LicensePage from "pages/about/license";
const router = createBrowserRouter([
{
path: "/",
element: <Homepage />
},
{
path: "about",
element: <AboutPage />,
children: [
{
path: "license",
element: <LicensePage />
}
},
fallbackLng: "en",
interpolation: {
escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
},
detection: {
order: ['navigator'],
caches: []
}
});
]
}
]);
export function App() {
return <Suspense fallback={<p>Loading...</p>}>{useRoutes(routes)}</Suspense>;
return (
<div className="relative bg-white dark:bg-black dark:text-white min-h-screen w-screen">
<RouterProvider router={router} />
</div>
);
}

65
src/i18n.ts Normal file
View File

@ -0,0 +1,65 @@
import * as en from "i18n/en.json"
import * as zh from "i18n/zh.json"
import * as ja from "i18n/ja.json"
import * as ar from "i18n/ar.json"
import * as de from "i18n/de.json"
import * as es from "i18n/es.json"
import * as fr from "i18n/fr.json"
import * as it from "i18n/it.json"
import * as ko from "i18n/ko.json"
import * as pt from "i18n/pt.json"
import * as ru from "i18n/ru.json"
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from 'i18next-browser-languagedetector';
import ICU from 'i18next-icu';
i18n.use(initReactI18next) // passes i18n down to react-i18next
.use(LanguageDetector)
.use(ICU)
.init({
resources: {
en: {
translation: en
},
zh: {
translation: zh
},
ja: {
translation: ja
},
ar: {
translation: ar
},
de: {
translation: de
},
es: {
translation: es
},
fr: {
translation: fr
},
it: {
translation: it
},
ko: {
translation: ko
},
pt: {
translation: pt
},
ru: {
translation: ru
}
},
fallbackLng: "en",
interpolation: {
escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
},
detection: {
order: ['navigator'],
caches: []
}
});

View File

@ -1,6 +1,5 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { App } from "./app";
import "./index.css";
import { NextUIProvider } from "@nextui-org/react";
@ -9,10 +8,8 @@ const app = createRoot(document.getElementById("root")!);
app.render(
<StrictMode>
<BrowserRouter>
<NextUIProvider>
<App />
</NextUIProvider>
</BrowserRouter>
<NextUIProvider>
<App />
</NextUIProvider>
</StrictMode>
);

View File

@ -7,6 +7,7 @@ const config: Config = {
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./node_modules/@nextui-org/theme/**/*.{js,ts,jsx,tsx}",
"./src/**/*.{js,ts,jsx,tsx,mdx}"
],
theme: {
extend: {

View File

@ -1,6 +1,5 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import Pages from "vite-plugin-pages";
import tsconfigPaths from 'vite-tsconfig-paths';
import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
@ -8,9 +7,6 @@ import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
export default defineConfig({
plugins: [
react(),
Pages({
dirs: "./pages/"
}),
tsconfigPaths(),
chunkSplitPlugin()
]