perf(directory): home.jsx performance optimization
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
useState,
|
useState,
|
||||||
useEffect,
|
useEffect,
|
||||||
useCallback
|
useCallback,
|
||||||
|
useMemo
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {
|
import {
|
||||||
NavLink,
|
NavLink,
|
||||||
@@ -13,8 +14,9 @@ import {
|
|||||||
useI18n
|
useI18n
|
||||||
} from '@/state/language'
|
} from '@/state/language'
|
||||||
import { useHeader } from '@/state/header';
|
import { useHeader } from '@/state/header';
|
||||||
|
import { useAppbar } from '@/state/appbar';
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { atomWithStorage } from 'jotai/utils';
|
import { atom } from 'jotai';
|
||||||
import CharIcon from '@/component/char_icon';
|
import CharIcon from '@/component/char_icon';
|
||||||
import MainBorder from '@/component/main_border';
|
import MainBorder from '@/component/main_border';
|
||||||
import useUmami from '@parcellab/react-use-umami';
|
import useUmami from '@parcellab/react-use-umami';
|
||||||
@@ -22,61 +24,20 @@ import Switch from '@/component/switch';
|
|||||||
|
|
||||||
const audioEl = new Audio()
|
const audioEl = new Audio()
|
||||||
let isPlaying = false
|
let isPlaying = false
|
||||||
const voiceOnAtom = atomWithStorage('voiceOn', false)
|
let voiceOnState = false
|
||||||
|
const voiceOnStateAtom = atom(voiceOnState)
|
||||||
export default function Home() {
|
const voiceOnAtom = atom(
|
||||||
const _trackEvt = useUmami('/')
|
(get) => get(voiceOnStateAtom),
|
||||||
const {
|
(get, set, newState) => {
|
||||||
setTitle,
|
voiceOnState = newState
|
||||||
setTabs,
|
set(voiceOnStateAtom, voiceOnState)
|
||||||
currentTab,
|
// you can set as many atoms as you want at the same time
|
||||||
setAppbarExtraArea,
|
}
|
||||||
setHeaderIcon
|
|
||||||
} = useHeader()
|
|
||||||
const { config } = useConfig()
|
|
||||||
const { textDefaultLang, language, alternateLang } = useLanguage()
|
|
||||||
const [content, setContent] = useState([])
|
|
||||||
const [voiceOn, setVoiceOn] = useAtom(voiceOnAtom)
|
|
||||||
const { i18n } = useI18n()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTitle('dynamic_compile')
|
|
||||||
setTabs([{
|
|
||||||
key: 'all'
|
|
||||||
}, {
|
|
||||||
key: 'operator'
|
|
||||||
}, {
|
|
||||||
key: 'skin'
|
|
||||||
}])
|
|
||||||
setHeaderIcon(null)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setContent(config?.operators || [])
|
|
||||||
}, [config])
|
|
||||||
|
|
||||||
const toggleVoice = useCallback(() => {
|
|
||||||
setVoiceOn(!voiceOn)
|
|
||||||
}, [voiceOn])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setAppbarExtraArea([
|
|
||||||
(
|
|
||||||
<Switch
|
|
||||||
key="voice"
|
|
||||||
text={i18n('voice')}
|
|
||||||
on={voiceOn}
|
|
||||||
handleOnClick={() => toggleVoice()}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
])
|
|
||||||
}, [voiceOn, language])
|
|
||||||
|
|
||||||
const isShown = useCallback((type) => currentTab === 'all' || currentTab === type, [currentTab])
|
const playVoice = (link) => {
|
||||||
|
|
||||||
const playVoice = useCallback((link) => {
|
|
||||||
const audioUrl = `/${link}/assets/${JSON.parse(import.meta.env.VITE_VOICE_FOLDERS).main}/${import.meta.env.VITE_APP_VOICE_URL}`
|
const audioUrl = `/${link}/assets/${JSON.parse(import.meta.env.VITE_VOICE_FOLDERS).main}/${import.meta.env.VITE_APP_VOICE_URL}`
|
||||||
if (!voiceOn || (audioEl.src === (window.location.href.replace(/\/$/g, '') + audioUrl) && isPlaying)) return
|
if (!voiceOnState || (audioEl.src === (window.location.href.replace(/\/$/g, '') + audioUrl) && isPlaying)) return
|
||||||
audioEl.src = audioUrl
|
audioEl.src = audioUrl
|
||||||
let startPlayPromise = audioEl.play()
|
let startPlayPromise = audioEl.play()
|
||||||
if (startPlayPromise !== undefined) {
|
if (startPlayPromise !== undefined) {
|
||||||
@@ -94,7 +55,36 @@ export default function Home() {
|
|||||||
return
|
return
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [voiceOn])
|
}
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const _trackEvt = useUmami('/')
|
||||||
|
const {
|
||||||
|
setTitle,
|
||||||
|
setTabs,
|
||||||
|
currentTab,
|
||||||
|
setHeaderIcon
|
||||||
|
} = useHeader()
|
||||||
|
const { config } = useConfig()
|
||||||
|
const [content, setContent] = useState([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTitle('dynamic_compile')
|
||||||
|
setTabs([{
|
||||||
|
key: 'all'
|
||||||
|
}, {
|
||||||
|
key: 'operator'
|
||||||
|
}, {
|
||||||
|
key: 'skin'
|
||||||
|
}])
|
||||||
|
setHeaderIcon(null)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setContent(config?.operators || [])
|
||||||
|
}, [config])
|
||||||
|
|
||||||
|
const isShown = useCallback((type) => currentTab === 'all' || currentTab === type, [currentTab])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="home">
|
<section className="home">
|
||||||
@@ -105,12 +95,38 @@ export default function Home() {
|
|||||||
<section className="item-group-wrapper" key={v[0].date} hidden={length === 0}>
|
<section className="item-group-wrapper" key={v[0].date} hidden={length === 0}>
|
||||||
<section className="item-group">
|
<section className="item-group">
|
||||||
{v.map(item => {
|
{v.map(item => {
|
||||||
|
return (
|
||||||
|
<OperatorElement
|
||||||
|
key={item.link}
|
||||||
|
item={item}
|
||||||
|
handleOnMouseEnter={playVoice}
|
||||||
|
hidden={!isShown(item.type)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<section className='item-group-date'>{v[0].date}</section>
|
||||||
|
</section>
|
||||||
|
<MainBorder />
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
<VoiceSwitchElement />
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function OperatorElement({ item, hidden }) {
|
||||||
|
const { textDefaultLang, language, alternateLang } = useLanguage()
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`/${item.link}`}
|
to={`/${item.link}`}
|
||||||
className="item"
|
className="item"
|
||||||
key={item.link}
|
hidden={hidden}
|
||||||
hidden={!isShown(item.type)}
|
>
|
||||||
|
<section
|
||||||
onMouseEnter={() => playVoice(item.link)}
|
onMouseEnter={() => playVoice(item.link)}
|
||||||
>
|
>
|
||||||
<section className="item-background-filler" />
|
<section className="item-background-filler" />
|
||||||
@@ -118,7 +134,6 @@ export default function Home() {
|
|||||||
<section className="item-img">
|
<section className="item-img">
|
||||||
<ImageElement
|
<ImageElement
|
||||||
item={item}
|
item={item}
|
||||||
language={language}
|
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
<section className="item-info">
|
<section className="item-info">
|
||||||
@@ -139,20 +154,44 @@ export default function Home() {
|
|||||||
color: item.color
|
color: item.color
|
||||||
}} />
|
}} />
|
||||||
</section>
|
</section>
|
||||||
|
</section>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
)
|
)
|
||||||
})}
|
}, [language, alternateLang, hidden])
|
||||||
<section className='item-group-date'>{v[0].date}</section>
|
|
||||||
</section>
|
|
||||||
<MainBorder />
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ImageElement({ item, language }) {
|
function VoiceSwitchElement() {
|
||||||
|
const [voiceOn, setVoiceOn] = useAtom(voiceOnAtom)
|
||||||
|
const {
|
||||||
|
setExtraArea,
|
||||||
|
} = useAppbar()
|
||||||
|
const { i18n } = useI18n()
|
||||||
|
|
||||||
|
const toggleVoice = useCallback(() => {
|
||||||
|
setVoiceOn(!voiceOn)
|
||||||
|
}, [voiceOn])
|
||||||
|
|
||||||
|
const appbarSwitch = useMemo(() => {
|
||||||
|
return [
|
||||||
|
(
|
||||||
|
<Switch
|
||||||
|
key="voice"
|
||||||
|
text={i18n('voice')}
|
||||||
|
on={voiceOn}
|
||||||
|
handleOnClick={() => toggleVoice()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}, [voiceOn])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setExtraArea(appbarSwitch)
|
||||||
|
}, [voiceOn])
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function ImageElement({ item }) {
|
||||||
|
const { language } = useLanguage()
|
||||||
return <img src={`/${item.link}/assets/${item.fallback_name.replace("#", "%23")}_portrait.png`} alt={item.codename[language]} />
|
return <img src={`/${item.link}/assets/${item.fallback_name.replace("#", "%23")}_portrait.png`} alt={item.codename[language]} />
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ import './root.css'
|
|||||||
import routes from '@/routes'
|
import routes from '@/routes'
|
||||||
import { useConfig } from '@/state/config';
|
import { useConfig } from '@/state/config';
|
||||||
import { useHeader } from '@/state/header';
|
import { useHeader } from '@/state/header';
|
||||||
|
import { useAppbar } from '@/state/appbar';
|
||||||
import {
|
import {
|
||||||
useI18n,
|
useI18n,
|
||||||
useLanguage,
|
useLanguage,
|
||||||
@@ -32,36 +33,29 @@ export default function Root(props) {
|
|||||||
title,
|
title,
|
||||||
tabs,
|
tabs,
|
||||||
currentTab, setCurrentTab,
|
currentTab, setCurrentTab,
|
||||||
appbarExtraArea,
|
|
||||||
headerIcon
|
headerIcon
|
||||||
} = useHeader()
|
} = useHeader()
|
||||||
|
const {
|
||||||
|
extraArea,
|
||||||
|
} = useAppbar()
|
||||||
const { version, fetchConfig, fetchVersion } = useConfig()
|
const { version, fetchConfig, fetchVersion } = useConfig()
|
||||||
const [drawerDestinations, setDrawerDestinations] = useState(null)
|
const [drawerDestinations, setDrawerDestinations] = useState(null)
|
||||||
const currentYear = useMemo(() => new Date().getFullYear(), [])
|
const currentYear = useMemo(() => new Date().getFullYear(), [])
|
||||||
const [headerTabs, setHeaderTabs] = useState(null)
|
|
||||||
const { i18n } = useI18n()
|
const { i18n } = useI18n()
|
||||||
const { fetchBackgrounds } = useBackgrounds()
|
const { fetchBackgrounds } = useBackgrounds()
|
||||||
|
|
||||||
const renderHeaderTabs = (tabs) => {
|
const headerTabs = useMemo(() => {
|
||||||
setHeaderTabs(tabs?.map((item) => {
|
|
||||||
return (
|
return (
|
||||||
<section
|
tabs?.map((item) => {
|
||||||
|
return (
|
||||||
|
<HeaderTabsElement
|
||||||
key={item.key}
|
key={item.key}
|
||||||
className={`main-tab-item ${currentTab === item.key ? 'active' : ''}`}
|
item={item}
|
||||||
onClick={(e) => {
|
/>
|
||||||
setCurrentTab(item.key)
|
|
||||||
item.onClick && item.onClick(e, currentTab)
|
|
||||||
}}
|
|
||||||
style={item.style}
|
|
||||||
>
|
|
||||||
<section className='main-tab-text-wrapper'>
|
|
||||||
<span className='text'>{i18n(item.key)}</span>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
)
|
)
|
||||||
}))
|
})
|
||||||
}
|
)
|
||||||
|
}, [tabs])
|
||||||
|
|
||||||
const toggleDrawer = useCallback((value) => {
|
const toggleDrawer = useCallback((value) => {
|
||||||
setDrawerHidden(value || !drawerHidden)
|
setDrawerHidden(value || !drawerHidden)
|
||||||
@@ -110,10 +104,6 @@ export default function Root(props) {
|
|||||||
setDrawerDestinations(renderDrawerDestinations())
|
setDrawerDestinations(renderDrawerDestinations())
|
||||||
}, [alternateLang])
|
}, [alternateLang])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
renderHeaderTabs(tabs)
|
|
||||||
}, [tabs, currentTab, language])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tabs.length > 0) {
|
if (tabs.length > 0) {
|
||||||
setCurrentTab(tabs[0].key)
|
setCurrentTab(tabs[0].key)
|
||||||
@@ -140,7 +130,7 @@ export default function Root(props) {
|
|||||||
<section className='bar'></section>
|
<section className='bar'></section>
|
||||||
</section>
|
</section>
|
||||||
<section className='spacer' />
|
<section className='spacer' />
|
||||||
{appbarExtraArea}
|
{extraArea}
|
||||||
<LanguageDropdown />
|
<LanguageDropdown />
|
||||||
</header>
|
</header>
|
||||||
<nav className={`drawer ${drawerHidden ? '' : 'active'}`}>
|
<nav className={`drawer ${drawerHidden ? '' : 'active'}`}>
|
||||||
@@ -217,8 +207,6 @@ function LanguageDropdown() {
|
|||||||
const { language, setLanguage } = useLanguage()
|
const { language, setLanguage } = useLanguage()
|
||||||
const { i18n, i18nValues } = useI18n()
|
const { i18n, i18nValues } = useI18n()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
text={i18n(language)}
|
text={i18n(language)}
|
||||||
@@ -235,6 +223,29 @@ function LanguageDropdown() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function HeaderTabsElement({ item }) {
|
||||||
|
const {
|
||||||
|
currentTab, setCurrentTab,
|
||||||
|
} = useHeader()
|
||||||
|
const { i18n } = useI18n()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className={`main-tab-item ${currentTab === item.key ? 'active' : ''}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
setCurrentTab(item.key)
|
||||||
|
item.onClick && item.onClick(e, currentTab)
|
||||||
|
}}
|
||||||
|
style={item.style}
|
||||||
|
>
|
||||||
|
<section className='main-tab-text-wrapper'>
|
||||||
|
<span className='text'>{i18n(item.key)}</span>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function HeaderReturnButton() {
|
function HeaderReturnButton() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
|||||||
10
directory/src/state/appbar.js
Normal file
10
directory/src/state/appbar.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { atom, useAtom } from 'jotai';
|
||||||
|
|
||||||
|
const extraAreaAtom = atom([]);
|
||||||
|
|
||||||
|
export function useAppbar() {
|
||||||
|
const [extraArea, setExtraArea] = useAtom(extraAreaAtom);
|
||||||
|
return {
|
||||||
|
extraArea, setExtraArea
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user