refactor: remove electron-related code

This commit is contained in:
codytseng
2024-12-21 23:20:30 +08:00
parent bed8df06e8
commit 2b1e6fe8f5
200 changed files with 2771 additions and 8432 deletions

228
src/PageManager.tsx Normal file
View File

@@ -0,0 +1,228 @@
import Sidebar from '@/components/Sidebar'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'
import { cn } from '@/lib/utils'
import HomePage from '@/pages/secondary/HomePage'
import { cloneElement, createContext, useContext, useEffect, useState } from 'react'
import { useScreenSize } from './providers/ScreenSizeProvider'
import { routes } from './routes'
type TPrimaryPageContext = {
refresh: () => void
}
type TSecondaryPageContext = {
push: (url: string) => void
pop: () => void
}
type TStackItem = {
index: number
url: string
component: React.ReactNode | null
}
const PrimaryPageContext = createContext<TPrimaryPageContext | undefined>(undefined)
const SecondaryPageContext = createContext<TSecondaryPageContext | undefined>(undefined)
export function usePrimaryPage() {
const context = useContext(PrimaryPageContext)
if (!context) {
throw new Error('usePrimaryPage must be used within a PrimaryPageContext.Provider')
}
return context
}
export function useSecondaryPage() {
const context = useContext(SecondaryPageContext)
if (!context) {
throw new Error('usePrimaryPage must be used within a SecondaryPageContext.Provider')
}
return context
}
export function PageManager({
children,
maxStackSize = 5
}: {
children: React.ReactNode
maxStackSize?: number
}) {
const [primaryPageKey, setPrimaryPageKey] = useState<number>(0)
const [secondaryStack, setSecondaryStack] = useState<TStackItem[]>([])
const { isSmallScreen } = useScreenSize()
useEffect(() => {
if (window.location.pathname !== '/') {
pushSecondary(window.location.pathname + window.location.search)
}
const onPopState = (e: PopStateEvent) => {
const state = e.state ?? { index: -1, url: '/' }
setSecondaryStack((pre) => {
const currentItem = pre[pre.length - 1]
const currentIndex = currentItem ? currentItem.index : -1
if (state.index === currentIndex) {
return pre
}
if (state.index < currentIndex) {
const newStack = pre.filter((item) => item.index <= state.index)
const topItem = newStack[newStack.length - 1]
if (topItem && !topItem.component) {
topItem.component = findAndCreateComponent(topItem.url)
}
return newStack
}
const { newStack } = pushNewPageToStack(pre, state.url, maxStackSize)
return newStack
})
}
window.addEventListener('popstate', onPopState)
return () => {
window.removeEventListener('popstate', onPopState)
}
}, [])
const refreshPrimary = () => setPrimaryPageKey((prevKey) => prevKey + 1)
const pushSecondary = (url: string) => {
setSecondaryStack((prevStack) => {
if (isCurrentPage(prevStack, url)) return prevStack
const { newStack, newItem } = pushNewPageToStack(prevStack, url, maxStackSize)
if (newItem) {
window.history.pushState({ index: newItem.index, url }, '', url)
}
return newStack
})
}
const popSecondary = () => {
window.history.back()
}
if (isSmallScreen) {
return (
<PrimaryPageContext.Provider value={{ refresh: refreshPrimary }}>
<SecondaryPageContext.Provider value={{ push: pushSecondary, pop: popSecondary }}>
<div className="h-full">
{!!secondaryStack.length &&
secondaryStack.map((item, index) => (
<div
key={item.index}
className="absolute top-0 left-0 w-full h-full bg-background"
style={{ zIndex: index + 1 }}
>
{item.component}
</div>
))}
<div
key={primaryPageKey}
className="absolute top-0 left-0 w-full h-full bg-background"
style={{ zIndex: 0 }}
>
{children}
</div>
</div>
</SecondaryPageContext.Provider>
</PrimaryPageContext.Provider>
)
}
return (
<PrimaryPageContext.Provider value={{ refresh: refreshPrimary }}>
<SecondaryPageContext.Provider value={{ push: pushSecondary, pop: popSecondary }}>
<div className="flex h-full">
<Sidebar />
<ResizablePanelGroup direction="horizontal">
<ResizablePanel minSize={30}>
<div key={primaryPageKey} className="h-full">
{children}
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel minSize={30} className="relative">
{secondaryStack.length ? (
secondaryStack.map((item, index) => (
<div
key={item.index}
className="absolute top-0 left-0 w-full h-full bg-background"
style={{ zIndex: index + 1 }}
>
{item.component}
</div>
))
) : (
<HomePage />
)}
</ResizablePanel>
</ResizablePanelGroup>
</div>
</SecondaryPageContext.Provider>
</PrimaryPageContext.Provider>
)
}
export function SecondaryPageLink({
to,
children,
className,
onClick
}: {
to: string
children: React.ReactNode
className?: string
onClick?: (e: React.MouseEvent) => void
}) {
const { push } = useSecondaryPage()
return (
<span
className={cn('cursor-pointer', className)}
onClick={(e) => {
if (onClick) {
onClick(e)
}
push(to)
}}
>
{children}
</span>
)
}
function isCurrentPage(stack: TStackItem[], url: string) {
const currentPage = stack[stack.length - 1]
if (!currentPage) return false
return currentPage.url === url
}
function findAndCreateComponent(url: string) {
const path = url.split('?')[0]
for (const { matcher, element } of routes) {
const match = matcher(path)
if (!match) continue
if (!element) return null
return cloneElement(element, match.params)
}
return null
}
function pushNewPageToStack(stack: TStackItem[], url: string, maxStackSize = 5) {
const component = findAndCreateComponent(url)
if (!component) return { newStack: stack, newItem: null }
const currentStack = stack[stack.length - 1]
const newItem = { component, url, index: currentStack ? currentStack.index + 1 : 0 }
const newStack = [...stack, newItem]
const lastCachedIndex = newStack.findIndex((stack) => stack.component)
if (newStack.length - lastCachedIndex > maxStackSize) {
newStack[lastCachedIndex].component = null
}
return { newStack, newItem }
}