优化用户数据初始化时机
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||||
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||||
{
|
{
|
||||||
"language_servers": ["...", "!biome"],
|
"language_servers": ["...", "!biome", "!typescript-language-server"],
|
||||||
"formatter": {
|
"formatter": {
|
||||||
"code_action": "source.fixAll.eslint"
|
"code_action": "source.fixAll.eslint"
|
||||||
}
|
}
|
||||||
|
|||||||
2
bun.lock
2
bun.lock
@@ -63,6 +63,8 @@
|
|||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"rehype-highlight": "^7.0.2",
|
"rehype-highlight": "^7.0.2",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -67,7 +67,9 @@
|
|||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"rehype-highlight": "^7.0.2",
|
"rehype-highlight": "^7.0.2",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
},
|
},
|
||||||
"packageManager": "bun@1.2.19"
|
"packageManager": "bun@1.2.19"
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
import {useCallback, useEffect, useMemo, useState, PointerEvent, ComponentProps} from 'react'
|
import {useCallback, useEffect, useMemo, useState, PointerEvent, ComponentProps} from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import SolutionMenu from './menu-solution'
|
import {HeaderContext} from './_components/header/common'
|
||||||
import ProductMenu from './menu-product'
|
import SolutionMenu from './_components/header/menu-solution'
|
||||||
import HelpMenu from './menu-help'
|
import ProductMenu from './_components/header/menu-product'
|
||||||
import MobileMenu from './menu-mobile'
|
import HelpMenu from './_components/header/menu-help'
|
||||||
|
import MobileMenu from './_components/header/menu-mobile'
|
||||||
import Wrap from '@/components/wrap'
|
import Wrap from '@/components/wrap'
|
||||||
import logo from '@/assets/logo.webp'
|
import logo from '@/assets/logo.webp'
|
||||||
import {Button} from '@/components/ui/button'
|
import {Button} from '@/components/ui/button'
|
||||||
@@ -14,11 +15,11 @@ import UserCenter from '@/components/composites/user-center'
|
|||||||
import {MenuIcon} from 'lucide-react'
|
import {MenuIcon} from 'lucide-react'
|
||||||
import down from '@/assets/header/down.svg'
|
import down from '@/assets/header/down.svg'
|
||||||
import {merge} from '@/lib/utils'
|
import {merge} from '@/lib/utils'
|
||||||
import {HeaderContext} from './common'
|
import {User} from '@/lib/models'
|
||||||
|
|
||||||
export type ProviderProps = {}
|
export type HeaderProps = {}
|
||||||
|
|
||||||
export default function Page(props: ProviderProps) {
|
export default function Header(props: HeaderProps) {
|
||||||
// ======================
|
// ======================
|
||||||
// 滚动条状态
|
// 滚动条状态
|
||||||
// ======================
|
// ======================
|
||||||
@@ -163,7 +164,7 @@ export default function Page(props: ProviderProps) {
|
|||||||
|
|
||||||
{/* 登录 */}
|
{/* 登录 */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{profile == undefined
|
{profile == null
|
||||||
? (
|
? (
|
||||||
<>
|
<>
|
||||||
<Link
|
<Link
|
||||||
@@ -175,7 +176,7 @@ export default function Page(props: ProviderProps) {
|
|||||||
<Link
|
<Link
|
||||||
href="/login?type=sms"
|
href="/login?type=sms"
|
||||||
className={[
|
className={[
|
||||||
`w-20 lg:w-24 h-10 lg:h-12 bg-gradient-to-r rounded-sm flex items-center justify-center lg:text-lg text-white`,
|
`w-20 lg:w-24 h-10 lg:h-12 bg-linear-to-r rounded-sm flex items-center justify-center lg:text-lg text-white`,
|
||||||
`transition-colors duration-200 ease-in-out`,
|
`transition-colors duration-200 ease-in-out`,
|
||||||
`from-blue-500 to-cyan-400 hover:from-blue-500 hover:to-cyan-300`,
|
`from-blue-500 to-cyan-400 hover:from-blue-500 hover:to-cyan-300`,
|
||||||
].join(' ')}
|
].join(' ')}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Header from '@/app/(home)/@header/page'
|
|
||||||
import Footer from '@/app/(home)/@footer/page'
|
|
||||||
import {ReactNode} from 'react'
|
import {ReactNode} from 'react'
|
||||||
|
import Header from './header'
|
||||||
|
import Footer from './footer'
|
||||||
|
|
||||||
export type HomeLayoutProps = {
|
export type HomeLayoutProps = {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import {ReactNode} from 'react'
|
import {ReactNode} from 'react'
|
||||||
import Wrap from '@/components/wrap'
|
import Wrap from '@/components/wrap'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import React, {useState} from 'react'
|
import React from 'react'
|
||||||
import {useRouter} from 'next/navigation'
|
import {useRouter} from 'next/navigation'
|
||||||
import {useProfileStore} from '@/components/stores-provider'
|
import {useProfileStore} from '@/components/stores-provider'
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ export default function Home() {
|
|||||||
<main className="flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32">
|
<main className="flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32">
|
||||||
|
|
||||||
{/* banner */}
|
{/* banner */}
|
||||||
<section className={`w-full bg-[url('/banner.webp')] bg-cover bg-[center_right_40%]`}>
|
<section className={`w-full bg-[url('/banner.webp')] bg-cover bg-position-[center_right_40%]`}>
|
||||||
<Wrap className="pt-64 pb-48 max-md:pt-32 max-md:pb-24">
|
<Wrap className="pt-64 pb-48 max-md:pt-32 max-md:pb-24">
|
||||||
<h1 className="text-4xl">安全,稳定,快速,合规的代理服务器</h1>
|
<h1 className="text-4xl">安全,稳定,快速,合规的代理服务器</h1>
|
||||||
<p className="mt-10 text-gray-500">遍布全国的代理服务器节点为用户提供智能可靠的IP代理服务</p>
|
<p className="mt-10 text-gray-500">遍布全国的代理服务器节点为用户提供智能可靠的IP代理服务</p>
|
||||||
@@ -38,7 +38,7 @@ export default function Home() {
|
|||||||
<button
|
<button
|
||||||
className={[
|
className={[
|
||||||
`mt-32 max-md:mt-20 w-96 max-md:w-full h-16 md:h-24 rounded-lg shadow-lg`,
|
`mt-32 max-md:mt-20 w-96 max-md:w-full h-16 md:h-24 rounded-lg shadow-lg`,
|
||||||
`bg-gradient-to-r from-blue-500 to-cyan-400 text-white text-xl lg:text-4xl`,
|
`bg-linear-to-r from-blue-500 to-cyan-400 text-white text-xl lg:text-4xl`,
|
||||||
].join(' ')}
|
].join(' ')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (profile) {
|
if (profile) {
|
||||||
@@ -59,22 +59,22 @@ export default function Home() {
|
|||||||
<ul className="shadow-[0_0_20px_4px] shadow-blue-50 p-8 flex max-lg:flex-col">
|
<ul className="shadow-[0_0_20px_4px] shadow-blue-50 p-8 flex max-lg:flex-col">
|
||||||
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
|
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
|
||||||
<p className="text-xl">全国城市线路数量</p>
|
<p className="text-xl">全国城市线路数量</p>
|
||||||
<p className="mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">350+</p>
|
<p className="mt-9 max-lg:mt-2 text-5xl bg-linear-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">350+</p>
|
||||||
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
|
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
|
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
|
||||||
<p className="text-xl">每日更新IP数量</p>
|
<p className="text-xl">每日更新IP数量</p>
|
||||||
<p className="mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">1,350,129</p>
|
<p className="mt-9 max-lg:mt-2 text-5xl bg-linear-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">1,350,129</p>
|
||||||
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
|
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
|
<li className="flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200">
|
||||||
<p className="text-xl">用户量</p>
|
<p className="text-xl">用户量</p>
|
||||||
<p className="mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">26,578</p>
|
<p className="mt-9 max-lg:mt-2 text-5xl bg-linear-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">26,578</p>
|
||||||
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
|
<div className="lg:hidden w-24 border-b mt-4 border-gray-200"></div>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex-1 flex flex-col items-center justify-center">
|
<li className="flex-1 flex flex-col items-center justify-center">
|
||||||
<p className="text-xl">IP可用率</p>
|
<p className="text-xl">IP可用率</p>
|
||||||
<p className="mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">99%</p>
|
<p className="mt-9 max-lg:mt-2 text-5xl bg-linear-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2">99%</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<img src="/map.webp" alt="map" className="w-[1200px]"/>
|
<img src="/map.webp" alt="map" className="w-[1200px]"/>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import './globals.css'
|
|||||||
import localFont from 'next/font/local'
|
import localFont from 'next/font/local'
|
||||||
import {Toaster} from '@/components/ui/sonner'
|
import {Toaster} from '@/components/ui/sonner'
|
||||||
import StoresProvider from '@/components/stores-provider'
|
import StoresProvider from '@/components/stores-provider'
|
||||||
import {getProfile} from '@/actions/auth'
|
|
||||||
import Effects from '@/app/effects'
|
import Effects from '@/app/effects'
|
||||||
|
|
||||||
const font = localFont({
|
const font = localFont({
|
||||||
@@ -23,16 +22,11 @@ export default async function RootLayout({
|
|||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}>) {
|
}>) {
|
||||||
const result = await getProfile()
|
|
||||||
const user = result.success ? result.data : null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<body className={`${font.className}`}>
|
<body className={`${font.className}`}>
|
||||||
<StoresProvider user={user}>
|
<StoresProvider>
|
||||||
<Effects>
|
<Effects>{children}</Effects>
|
||||||
{children}
|
|
||||||
</Effects>
|
|
||||||
</StoresProvider>
|
</StoresProvider>
|
||||||
<Toaster position="top-center" richColors expand/>
|
<Toaster position="top-center" richColors expand/>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import {User} from '@/lib/models'
|
|
||||||
import {createContext, ReactNode, useContext, useEffect, useRef} from 'react'
|
import {createContext, ReactNode, useContext, useEffect, useRef} from 'react'
|
||||||
import {StoreApi} from 'zustand/vanilla'
|
import {StoreApi} from 'zustand/vanilla'
|
||||||
import {useStore} from 'zustand/react'
|
import {useStore} from 'zustand/react'
|
||||||
@@ -38,16 +37,20 @@ export function useClientStore<T>(selector: (store: ClientStore) => T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ProfileProviderProps = {
|
export type ProfileProviderProps = {
|
||||||
user: User | null
|
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function StoresProvider(props: ProfileProviderProps) {
|
export default function StoresProvider(props: ProfileProviderProps) {
|
||||||
|
// 用户信息
|
||||||
const profile = useRef<StoreApi<ProfileStore>>(null)
|
const profile = useRef<StoreApi<ProfileStore>>(null)
|
||||||
if (!profile.current) {
|
if (!profile.current) {
|
||||||
console.log('📦 create profile store')
|
console.log('📦 create profile store')
|
||||||
profile.current = createProfileStore(props.user)
|
profile.current = createProfileStore()
|
||||||
}
|
}
|
||||||
|
const profileStore = useRef(useStore(profile.current))
|
||||||
|
useEffect(() => {
|
||||||
|
profileStore.current.refreshProfile()
|
||||||
|
}, [])
|
||||||
|
|
||||||
const layout = useRef<StoreApi<LayoutStore>>(null)
|
const layout = useRef<StoreApi<LayoutStore>>(null)
|
||||||
if (!layout.current) {
|
if (!layout.current) {
|
||||||
|
|||||||
@@ -12,14 +12,12 @@ export type ProfileActions = {
|
|||||||
refreshProfile: () => Promise<void>
|
refreshProfile: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createProfileStore = (init: User | null) => {
|
export const createProfileStore = () => {
|
||||||
return createStore<ProfileStore>()(setState => ({
|
return createStore<ProfileStore>()(set => ({
|
||||||
profile: init,
|
profile: null,
|
||||||
refreshProfile: async () => {
|
refreshProfile: async () => {
|
||||||
const profile = await getProfile()
|
const result = await getProfile()
|
||||||
setState({
|
set({profile: result.success ? result.data : null})
|
||||||
profile: profile.success ? profile.data : null,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user