feature: engine change & nextui
This commit is contained in:
parent
090154115a
commit
09a7941eca
@ -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
12
app/providers.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
@ -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)} />
|
||||||
)}
|
)}
|
||||||
|
74
components/search/engineSelector.tsx
Normal file
74
components/search/engineSelector.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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";
|
||||||
|
1
components/search/translatedEngineList.ts
Normal file
1
components/search/translatedEngineList.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const engineTranslation = ["google", "bing", "baidu", "duckduckgo", "yandex", "ecosia", "yahoo"];
|
@ -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,
|
|
||||||
}
|
|
||||||
|
10
lib/isLocalStorageAvailable.ts
Normal file
10
lib/isLocalStorageAvailable.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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){
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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": "未找到"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
2934
pnpm-lock.yaml
2934
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user