perf(directory): home.jsx performance optimization

This commit is contained in:
Haoyu Xu
2023-03-02 18:42:14 -05:00
parent 62c7e2b956
commit a8a31dac51
3 changed files with 168 additions and 108 deletions

View File

@@ -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]} />
} }

View File

@@ -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()

View File

@@ -0,0 +1,10 @@
import { atom, useAtom } from 'jotai';
const extraAreaAtom = atom([]);
export function useAppbar() {
const [extraArea, setExtraArea] = useAtom(extraAreaAtom);
return {
extraArea, setExtraArea
}
}