我弄了一坨史山, 可能在下一个 commit 会撤销更改, 或者继续完善

This commit is contained in:
CrescentLeaf
2025-12-20 17:30:14 +08:00
parent 76d518f229
commit 989933d07c
10 changed files with 472 additions and 368 deletions

View File

@@ -1,7 +1,10 @@
import { useSearchParams } from "react-router"
import { useSearchParams, Outlet } from "react-router"
import useRouterDialogRef from "./useRouterDialogRef"
import * as React from 'react'
import LazyChatFragment from "../chat-fragment/LazyChatFragment"
import useEventListener from "../../utils/useEventListener"
import useAsyncEffect from "../../utils/useAsyncEffect"
import sleep from "../../utils/sleep"
export default function ChatFragmentDialog() {
const [searchParams] = useSearchParams()
@@ -9,7 +12,7 @@ export default function ChatFragmentDialog() {
const dialogRef = useRouterDialogRef()
React.useEffect(() => {
useEventListener(dialogRef, 'open', () => {
const shadow = dialogRef.current!.shadowRoot as ShadowRoot
const panel = shadow.querySelector(".panel") as HTMLElement
panel.style.padding = '0'
@@ -21,7 +24,15 @@ export default function ChatFragmentDialog() {
body.style.display = 'flex'
}, [])
return <mdui-dialog fullscreen ref={dialogRef}>
<LazyChatFragment chatId={id!} openedWithRouter={true} />
</mdui-dialog>
return (<>
<mdui-dialog fullscreen ref={dialogRef}>
<div style={{
display: 'flex',
width: '100%',
}}>
<LazyChatFragment chatId={id!} openedWithRouter={true} />
</div>
</mdui-dialog>
<Outlet />
</>)
}

View File

@@ -0,0 +1,20 @@
import { LoaderFunctionArgs } from "react-router"
import getClient from "../../getClient"
import { Chat } from "lingchair-client-protocol"
import ClientCache from "../../ClientCache"
export default async function ChatInfoDialogDataLoader({ params, request }: LoaderFunctionArgs) {
const searchParams = new URL(request.url).searchParams
let id = searchParams.get('id')
const chat = await Chat.getByIdOrThrow(getClient(), id!)
if (chat.getType() == 'private')
id = await chat.getTheOtherUserIdOrThrow()
return {
mySelf: await ClientCache.getMySelf(),
chat,
id,
}
}

View File

@@ -0,0 +1,5 @@
import * as React from 'react'
const RouterDialogsContext = React.createContext(() => {})
export default RouterDialogsContext

View File

@@ -0,0 +1,62 @@
import { Dialog } from 'mdui'
import * as React from 'react'
import RouterDialogsContext from './RouterDialogsContext'
import { BlockerFunction, useBlocker, useNavigate } from "react-router"
import sleep from "../../utils/sleep"
const routerDialogsList = []
export default function RouterDialogsContextWrapper({ children }: React.HTMLAttributes<HTMLElement>) {
const proceedRef = React.useRef<() => void>()
const nav = useNavigate()
// 进入子路由不会拦截上一个路由对话框的关闭
// 没有路由对话框不会拦截
const blocker = useBlocker(React.useCallback<BlockerFunction>(({ nextLocation, currentLocation }) => {
// 只有当有对话框时,才检查路由变化
if (routerDialogsList.length === 0) {
return false // 没有对话框,允许所有导航
}
// 检查是否是同一个路由
if (nextLocation.pathname === currentLocation.pathname) {
return false // 相同路由,允许
}
// 检查是否是子路由
if (nextLocation.pathname.startsWith(currentLocation.pathname + '/')) {
return false // 是子路由,允许
}
// 其他情况:阻止导航
return true
}, []))
// 避免用户手动返回导致动画丢失
React.useEffect(() => {
if (blocker.state === "blocked") {
console.log(location)
console.log(routerDialogsList[routerDialogsList.length - 1].current)
console.log(blocker)
proceedRef.current = blocker.proceed
// 这个让姐姐来就好啦
routerDialogsList.length != 0 && (routerDialogsList[routerDialogsList.length - 1].current!.open = false)
}
}, [blocker.state])
// 注册
// 理应在 Effect 里
function registerRouterDialog(ref: React.MutableRefObject<Dialog>) {
routerDialogsList.push(ref)
// 正常情况下不可能同时关掉两个对话框
// 不过要是真有的话, 再说吧
ref.current!.addEventListener('closed', async () => {
routerDialogsList.splice(routerDialogsList.length - 1, 1)
await sleep(10)
proceedRef.current ? proceedRef.current() : nav(-1)
})
}
return <RouterDialogsContext.Provider value={registerRouterDialog}>
{ children }
</RouterDialogsContext.Provider>
}

View File

@@ -3,31 +3,17 @@ import useAsyncEffect from "../../utils/useAsyncEffect"
import sleep from "../../utils/sleep"
import { BlockerFunction, useBlocker, useNavigate } from "react-router"
import * as React from 'react'
import RouterDialogsContext from './RouterDialogsContext'
export default function useRouterDialogRef() {
const dialogRef = React.useRef<Dialog>()
const proceedRef = React.useRef<() => void>()
const shouldBlock = React.useRef(true)
const nav = useNavigate()
const blocker = useBlocker(React.useCallback<BlockerFunction>(() => shouldBlock.current, []))
// 避免用户手动返回导致动画丢失
React.useEffect(() => {
if (blocker.state === "blocked") {
proceedRef.current = blocker.proceed
// 这个让姐姐来就好啦
dialogRef.current!.open = false
}
}, [blocker.state])
const dialogRef = React.useRef<Dialog>(RouterDialogsContext)
const registerRouterDialog = React.useContext(RouterDialogsContext)
useAsyncEffect(async () => {
registerRouterDialog(dialogRef)
await sleep(10)
dialogRef.current!.open = true
dialogRef.current!.addEventListener('closed', async () => {
shouldBlock.current = false
await sleep(10)
proceedRef.current ? proceedRef.current() : nav(-1)
})
}, [])
return dialogRef
}