feature: engine change & nextui

This commit is contained in:
Alikia2x 2024-04-04 02:26:37 +08:00
parent 090154115a
commit 09a7941eca
15 changed files with 2870 additions and 311 deletions

1
.npmrc Normal file
View File

@ -0,0 +1 @@
public-hoist-pattern[]=*@nextui-org/*

View File

@ -2,6 +2,8 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import "./global.css"; import "./global.css";
import { NextIntlClientProvider, useMessages } from "next-intl"; import { NextIntlClientProvider, useMessages } from "next-intl";
import { ThemeProvider } from "next-themes";
import { Providers } from "../providers";
const inter = Inter({ subsets: ["latin"] }); const inter = Inter({ subsets: ["latin"] });
@ -22,11 +24,15 @@ export default function LocaleLayout({
}) { }) {
const messages = useMessages(); const messages = useMessages();
return ( return (
<html lang={locale}> <html lang={locale} suppressHydrationWarning>
<body className={inter.className}> <body className={inter.className}>
<ThemeProvider>
<Providers>
<NextIntlClientProvider locale={locale} messages={messages}> <NextIntlClientProvider locale={locale} messages={messages}>
{children} {children}
</NextIntlClientProvider> </NextIntlClientProvider>
</Providers>
</ThemeProvider>
</body> </body>
</html> </html>
); );

12
app/providers.tsx Normal file
View File

@ -0,0 +1,12 @@
// app/providers.tsx
'use client'
import {NextUIProvider} from '@nextui-org/react'
export function Providers({children}: { children: React.ReactNode }) {
return (
<NextUIProvider>
{children}
</NextUIProvider>
)
}

View File

@ -8,6 +8,7 @@ import Search from "./search/search";
import { bgFocusState } from "./state/background"; import { bgFocusState } from "./state/background";
import Time from "./time"; import Time from "./time";
import loadSettings from "@/lib/loadSettings"; import loadSettings from "@/lib/loadSettings";
import EngineSelector from "./search/engineSelector";
export default function Homepage() { export default function Homepage() {
const [settings, setSettings] = useRecoilState(settingsState); const [settings, setSettings] = useRecoilState(settingsState);
@ -36,6 +37,7 @@ export default function Homepage() {
return ( return (
<div className="h-full fixed overflow-hidden w-full bg-black"> <div className="h-full fixed overflow-hidden w-full bg-black">
<Time showSecond={settings.timeShowSecond} /> <Time showSecond={settings.timeShowSecond} />
<EngineSelector/>
{colorScheme === "dark" && ( {colorScheme === "dark" && (
<Background src="rgb(23,25,29)" isFocus={isFocus} onClick={() => setFocus(false)} /> <Background src="rgb(23,25,29)" isFocus={isFocus} onClick={() => setFocus(false)} />
)} )}

View File

@ -0,0 +1,74 @@
"use client";
import React, { SetStateAction, useEffect, useState } from "react";
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from "@nextui-org/react";
import { useTranslations } from "next-intl";
import { useRecoilValue, useSetRecoilState } from "recoil";
import { settingsState } from "../state/settings";
import { engineTranslation } from "./translatedEngineList";
export default function () {
const t = useTranslations("Search");
const settings: settings = useRecoilValue(settingsState);
const items = settings.searchEngines;
const currentEngine: string = settings.currentSearchEngine;
const displayEngine = getName(currentEngine);
const [selectedKeys, setSelectedKeys] = useState(new Set([currentEngine]) as any);
const selectedValue = React.useMemo(() => Array.from(selectedKeys).join(", "), [selectedKeys]);
const setSettings = useSetRecoilState(settingsState);
function setEngine(engine: string) {
setSettings((oldSettings) => {
return {
...oldSettings,
currentSearchEngine: engine
};
});
}
function getName(engineKey: string) {
return engineTranslation.includes(engineKey) ? t(`engine.${engineKey}`) : engineKey;
}
useEffect(() => {
if (selectedValue !== currentEngine) {
setEngine(selectedValue);
}
}, [selectedValue]);
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
return (
<div>
{
isClient &&
(
<Dropdown>
<DropdownTrigger>
<Button variant="bordered" className="capitalize">
{displayEngine}
</Button>
</DropdownTrigger>
<DropdownMenu
aria-label={t("engine-aria")}
variant="flat"
disallowEmptySelection
selectionMode="single"
selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys}
>
{Object.keys(items).map((item) => (
<DropdownItem key={item} suppressHydrationWarning>
{getName(item)}
</DropdownItem>
))}
</DropdownMenu>
</Dropdown>
)}
</div>
);
}

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { atom, useRecoilValue } from "recoil"; import { useRecoilValue } from "recoil";
import { settingsState } from "../state/settings"; import { settingsState } from "../state/settings";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useState } from "react"; import { useState } from "react";

View File

@ -0,0 +1 @@
export const engineTranslation = ["google", "bing", "baidu", "duckduckgo", "yandex", "ecosia", "yahoo"];

View File

@ -1,23 +1,43 @@
import isLocalStorageAvailable from "@/lib/isLocalStorageAvailable";
import { atom } from "recoil"; import { atom } from "recoil";
const settingsState = atom({ const defaultSettings: settings = {
key: "settings",
default: {
version: 1, version: 1,
elementBackdrop: true, elementBackdrop: true,
bgBlur: true, bgBlur: true,
timeShowSecond: false, timeShowSecond: false,
currentSearchEngine: "google", currentSearchEngine: "google",
searchEngines: { searchEngines: {
"google": "https://www.google.com/search?q=%s", google: "https://www.google.com/search?q=%s",
"bing": "https://www.bing.com/search?q=%s", bing: "https://www.bing.com/search?q=%s",
"baidu": "https://www.baidu.com/s?wd=%s", baidu: "https://www.baidu.com/s?wd=%s",
"duckduckgo": "https://duckduckgo.com/?q=%s", duckduckgo: "https://duckduckgo.com/?q=%s",
"yandex": "https://yandex.com/search/?text=%s", yandex: "https://yandex.com/search/?text=%s",
yahoo: "https://search.yahoo.com/search?p=%s",
ecosia: "https://www.ecosia.org/search?q=%s"
} }
};
const localStorageEffect =
(key: any) =>
({ setSelf, onSet }: any) => {
if (isLocalStorageAvailable()===false){
return;
} }
const savedValue = localStorage.getItem(key);
if (savedValue != null) {
setSelf(JSON.parse(savedValue));
}
onSet((newValue: settings) => {
localStorage.setItem(key, JSON.stringify(newValue));
});
};
const settingsState = atom({
key: "settings",
default: defaultSettings,
effects_UNSTABLE: [localStorageEffect("settings")]
}); });
export { export { settingsState };
settingsState,
}

View File

@ -0,0 +1,10 @@
export default function(){
var test = 'test';
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch(e) {
return false;
}
}

View File

@ -1,5 +1,7 @@
'use client'; 'use client';
import isLocalStorageAvailable from "./isLocalStorageAvailable";
const defaultSettings = { const defaultSettings = {
version: 1, version: 1,
elementBackdrop: true, elementBackdrop: true,
@ -14,16 +16,7 @@ const defaultSettings = {
"yandex": "https://yandex.com/search/?text=%s", "yandex": "https://yandex.com/search/?text=%s",
} }
} }
function isLocalStorageAvailable(){
var test = 'test';
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch(e) {
return false;
}
}
export default function (setSettings: any) { export default function (setSettings: any) {
if (isLocalStorageAvailable()===false){ if (isLocalStorageAvailable()===false){

View File

@ -1,8 +1,21 @@
{ {
"Search": { "Search": {
"placeholder": "Search or type a URL" "placeholder": "Search or type a URL",
"engine-aria": "Switch search engine",
"engine": {
"google": "Google",
"baidu": "Baidu",
"bing": "Bing",
"duckduckgo": "DuckDuckGo",
"yandex": "Yandex",
"yahoo": "Yahoo",
"ecosia": "Ecosia"
}
}, },
"404":{ "404": {
"title": "Page Not Found" "title": "Page Not Found"
},
"About": {
"title": "SparkHome"
} }
} }

View File

@ -1,8 +1,18 @@
{ {
"Search": { "Search": {
"placeholder": "搜索或输入网址" "placeholder": "搜索或输入网址",
"engine-aria": "搜索引擎切换",
"engine": {
"google": "谷歌",
"baidu": "百度",
"bing": "必应",
"duckduckgo": "DuckDuckGo",
"yandex": "Yandex",
"yahoo": "雅虎",
"ecosia": "Ecosia"
}
}, },
"404":{ "404": {
"title": "未找到" "title": "未找到"
} }
} }

View File

@ -10,10 +10,12 @@
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"@nextui-org/react": "^2.2.10",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"framer-motion": "^11.0.24", "framer-motion": "^11.0.24",
"next": "14.1.1", "next": "14.1.4",
"next-intl": "^3.10.0", "next-intl": "^3.10.0",
"next-themes": "^0.3.0",
"punycode": "^2.3.1", "punycode": "^2.3.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,21 @@
import type { Config } from "tailwindcss"; import type { Config } from "tailwindcss";
import {nextui} from "@nextui-org/react";
const config: Config = { const config: Config = {
content: [ content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}", "./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}",
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}"
], ],
theme: { theme: {
extend: { extend: {
backgroundImage: { backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))", "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic": "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))"
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", }
}
}, },
}, plugins: [nextui()]
},
plugins: [],
}; };
export default config; export default config;