我弄了一坨史山, 可能在下一个 commit 会撤销更改, 或者继续完善
This commit is contained in:
@@ -19,6 +19,7 @@ import FavouriteChatsList from "./main-page/FavouriteChatsList.tsx"
|
|||||||
import RecentChatsList from "./main-page/RecentChatsList.tsx"
|
import RecentChatsList from "./main-page/RecentChatsList.tsx"
|
||||||
import UserOrChatInfoDialog from "./routers/UserOrChatInfoDialog.tsx"
|
import UserOrChatInfoDialog from "./routers/UserOrChatInfoDialog.tsx"
|
||||||
import UserOrChatInfoDialogLoader from "./routers/UserOrChatInfoDialogDataLoader.ts"
|
import UserOrChatInfoDialogLoader from "./routers/UserOrChatInfoDialogDataLoader.ts"
|
||||||
|
import ChatInfoDialogDataLoader from "./routers/ChatInfoDialogDataLoader.ts"
|
||||||
import ChatFragmentDialog from "./routers/ChatFragmentDialog.tsx"
|
import ChatFragmentDialog from "./routers/ChatFragmentDialog.tsx"
|
||||||
import EffectOnly from "./EffectOnly.tsx"
|
import EffectOnly from "./EffectOnly.tsx"
|
||||||
import MainSharedReducer from "./MainSharedReducer.ts"
|
import MainSharedReducer from "./MainSharedReducer.ts"
|
||||||
@@ -29,6 +30,7 @@ import Split from 'split.js'
|
|||||||
import data from "../data.ts"
|
import data from "../data.ts"
|
||||||
import LazyChatFragment from "./chat-fragment/LazyChatFragment.tsx"
|
import LazyChatFragment from "./chat-fragment/LazyChatFragment.tsx"
|
||||||
import AddFavourtieChatDialog from "./routers/AddFavourtieChatDialog.tsx"
|
import AddFavourtieChatDialog from "./routers/AddFavourtieChatDialog.tsx"
|
||||||
|
import RouterDialogsContextWrapper from './routers/RouterDialogsContextWrapper.tsx'
|
||||||
|
|
||||||
function Root() {
|
function Root() {
|
||||||
const [myProfileCache, setMyProfileCache] = React.useState<UserMySelf>()
|
const [myProfileCache, setMyProfileCache] = React.useState<UserMySelf>()
|
||||||
@@ -117,140 +119,144 @@ function Root() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainSharedContext.Provider value={sharedContext}>
|
<RouterDialogsContextWrapper>
|
||||||
<div style={{
|
<MainSharedContext.Provider value={sharedContext}>
|
||||||
display: "flex",
|
<div style={{
|
||||||
position: 'relative',
|
display: "flex",
|
||||||
flexDirection: isMobileUI() ? 'column' : 'row',
|
position: 'relative',
|
||||||
width: '100%',
|
flexDirection: isMobileUI() ? 'column' : 'row',
|
||||||
height: 'var(--whitesilk-window-height)',
|
width: '100%',
|
||||||
}}>
|
height: 'var(--whitesilk-window-height)',
|
||||||
{
|
}}>
|
||||||
// 将子路由渲染到此处
|
{
|
||||||
<Outlet />
|
// 将子路由渲染到此处
|
||||||
}
|
<Outlet />
|
||||||
<LoginDialog open={showLoginDialog} />
|
}
|
||||||
<RegisterDialog open={showRegisterDialog} />
|
<LoginDialog open={showLoginDialog} />
|
||||||
<mdui-navigation-drawer ref={drawerRef} modal close-on-esc close-on-overlay-click>
|
<RegisterDialog open={showRegisterDialog} />
|
||||||
<mdui-list style={{
|
<mdui-navigation-drawer ref={drawerRef} modal close-on-esc close-on-overlay-click>
|
||||||
padding: '10px',
|
<mdui-list style={{
|
||||||
}}>
|
padding: '10px',
|
||||||
<mdui-list-item rounded onClick={() => gotoUserInfo(nav, myProfileCache!.getId())}>
|
}}>
|
||||||
<span>{myProfileCache?.getNickName()}</span>
|
<mdui-list-item rounded onClick={() => gotoUserInfo(nav, myProfileCache!.getId())}>
|
||||||
<AvatarMySelf slot="icon" />
|
<span>{myProfileCache?.getNickName()}</span>
|
||||||
</mdui-list-item>
|
<AvatarMySelf slot="icon" />
|
||||||
<mdui-list-item rounded icon="manage_accounts">账号设置</mdui-list-item>
|
</mdui-list-item>
|
||||||
<mdui-divider style={{
|
<mdui-list-item rounded icon="manage_accounts">账号设置</mdui-list-item>
|
||||||
margin: '10px',
|
<mdui-divider style={{
|
||||||
}}></mdui-divider>
|
margin: '10px',
|
||||||
<mdui-list-item rounded icon="settings">客户端设置</mdui-list-item>
|
}}></mdui-divider>
|
||||||
<mdui-list-item rounded icon="person_add" onClick={() => nav('/add/favourite_chat')}>添加收藏对话</mdui-list-item>
|
<mdui-list-item rounded icon="settings">客户端设置</mdui-list-item>
|
||||||
<mdui-list-item rounded icon="group_add">创建新的群组</mdui-list-item>
|
<mdui-list-item rounded icon="person_add" onClick={() => nav('/add/favourite_chat')}>添加收藏对话</mdui-list-item>
|
||||||
</mdui-list>
|
<mdui-list-item rounded icon="group_add">创建新的群组</mdui-list-item>
|
||||||
<div style={{
|
</mdui-list>
|
||||||
flexGrow: 1,
|
<div style={{
|
||||||
}}></div>
|
flexGrow: 1,
|
||||||
<span style={{
|
}}></div>
|
||||||
padding: '10px',
|
<span style={{
|
||||||
fontSize: 'small',
|
padding: '10px',
|
||||||
}}>
|
fontSize: 'small',
|
||||||
LingChair Web v{__APP_VERSION__}<br />
|
}}>
|
||||||
Build: <a href={`https://codeberg.org/CrescentLeaf/LingChair/src/commit/${__GIT_HASH_FULL__}`}>{__GIT_HASH__}</a> ({__BUILD_TIME__})<br />
|
LingChair Web v{__APP_VERSION__}<br />
|
||||||
在 Codeberg 上<a href="https://codeberg.org/CrescentLeaf/LingChair">查看源代码</a>
|
Build: <a href={`https://codeberg.org/CrescentLeaf/LingChair/src/commit/${__GIT_HASH_FULL__}`}>{__GIT_HASH__}</a> ({__BUILD_TIME__})<br />
|
||||||
</span>
|
在 Codeberg 上<a href="https://codeberg.org/CrescentLeaf/LingChair">查看源代码</a>
|
||||||
</mdui-navigation-drawer>
|
</span>
|
||||||
{
|
</mdui-navigation-drawer>
|
||||||
/**
|
{
|
||||||
* Default: 侧边列表提供列表切换
|
/**
|
||||||
*/
|
* Default: 侧边列表提供列表切换
|
||||||
!isMobileUI() ?
|
*/
|
||||||
<mdui-navigation-rail ref={navigationRef} contained value="Recents">
|
!isMobileUI() ?
|
||||||
<mdui-button-icon slot="top" icon="menu" onClick={() => drawerRef.current!.open = true}></mdui-button-icon>
|
<mdui-navigation-rail ref={navigationRef} contained value="Recents">
|
||||||
|
<mdui-button-icon slot="top" icon="menu" onClick={() => drawerRef.current!.open = true}></mdui-button-icon>
|
||||||
<mdui-navigation-rail-item icon="watch_later--outlined" active-icon="watch_later--filled" value="Recents"></mdui-navigation-rail-item>
|
|
||||||
<mdui-navigation-rail-item icon="favorite_border" active-icon="favorite" value="Favourites"></mdui-navigation-rail-item>
|
<mdui-navigation-rail-item icon="watch_later--outlined" active-icon="watch_later--filled" value="Recents"></mdui-navigation-rail-item>
|
||||||
<mdui-navigation-rail-item icon="chat--outlined" active-icon="chat--filled" value="AllChats"></mdui-navigation-rail-item>
|
<mdui-navigation-rail-item icon="favorite_border" active-icon="favorite" value="Favourites"></mdui-navigation-rail-item>
|
||||||
</mdui-navigation-rail>
|
<mdui-navigation-rail-item icon="chat--outlined" active-icon="chat--filled" value="AllChats"></mdui-navigation-rail-item>
|
||||||
|
</mdui-navigation-rail>
|
||||||
|
/**
|
||||||
|
* Mobile: 底部导航栏提供列表切换
|
||||||
|
*/
|
||||||
|
: <mdui-top-app-bar style={{
|
||||||
|
position: 'sticky',
|
||||||
|
marginTop: '3px',
|
||||||
|
marginRight: '6px',
|
||||||
|
marginLeft: '15px',
|
||||||
|
top: '0px',
|
||||||
|
}}>
|
||||||
|
<mdui-button-icon icon="menu" onClick={() => drawerRef.current!.open = true}></mdui-button-icon>
|
||||||
|
<mdui-top-app-bar-title>{
|
||||||
|
({
|
||||||
|
Recents: "最近对话",
|
||||||
|
Favourites: "收藏对话",
|
||||||
|
AllChats: "所有对话",
|
||||||
|
})[currentShowPage]
|
||||||
|
}</mdui-top-app-bar-title>
|
||||||
|
<div style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
}}></div>
|
||||||
|
</mdui-top-app-bar>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Mobile: 指定高度的容器
|
||||||
|
* Default: 侧边列表
|
||||||
|
*/
|
||||||
|
<div style={isMobileUI() ? {
|
||||||
|
height: 'calc(100% - 80px - 67px)',
|
||||||
|
marginLeft: '15px',
|
||||||
|
marginRight: '15px',
|
||||||
|
marginTop: '5px',
|
||||||
|
marginBottom: '5px',
|
||||||
|
} : {
|
||||||
|
paddingRight: '8px',
|
||||||
|
}} id="SideBar">
|
||||||
|
<RecentChatsList style={{
|
||||||
|
display: currentShowPage == 'Recents' ? undefined : 'none'
|
||||||
|
}} />
|
||||||
|
<FavouriteChatsList style={{
|
||||||
|
display: currentShowPage == 'Favourites' ? undefined : 'none'
|
||||||
|
}} />
|
||||||
|
<AllChatsList style={{
|
||||||
|
display: currentShowPage == 'AllChats' ? undefined : 'none'
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!isMobileUI() && <div id="ChatFragment" style={{
|
||||||
|
display: "flex",
|
||||||
|
width: '100%'
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
(state.currentSelectedChatId && state.currentSelectedChatId != '')
|
||||||
|
? <LazyChatFragment openedWithRouter={false} chatId={state.currentSelectedChatId!} />
|
||||||
|
: <div style={{
|
||||||
|
width: '100%',
|
||||||
|
textAlign: 'center',
|
||||||
|
alignSelf: 'center',
|
||||||
|
}}>
|
||||||
|
选择以开始对话......
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Mobile: 底部导航栏提供列表切换
|
* Mobile: 底部导航栏提供列表切换
|
||||||
|
* Default: 侧边列表提供列表切换
|
||||||
*/
|
*/
|
||||||
: <mdui-top-app-bar style={{
|
isMobileUI() && <mdui-navigation-bar ref={navigationRef} label-visibility="selected" value="Recents" style={{
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
marginTop: '3px',
|
bottom: '0',
|
||||||
marginRight: '6px',
|
|
||||||
marginLeft: '15px',
|
|
||||||
top: '0px',
|
|
||||||
}}>
|
}}>
|
||||||
<mdui-button-icon icon="menu" onClick={() => drawerRef.current!.open = true}></mdui-button-icon>
|
<mdui-navigation-bar-item icon="watch_later--outlined" active-icon="watch_later--filled" value="Recents">最近对话</mdui-navigation-bar-item>
|
||||||
<mdui-top-app-bar-title>{
|
<mdui-navigation-bar-item icon="favorite_border" active-icon="favorite" value="Favourites">收藏对话</mdui-navigation-bar-item>
|
||||||
({
|
<mdui-navigation-bar-item icon="chat--outlined" active-icon="chat--filled" value="AllChats">全部对话</mdui-navigation-bar-item>
|
||||||
Recents: "最近对话",
|
</mdui-navigation-bar>
|
||||||
Favourites: "收藏对话",
|
}
|
||||||
AllChats: "所有对话",
|
</div>
|
||||||
})[currentShowPage]
|
</MainSharedContext.Provider>
|
||||||
}</mdui-top-app-bar-title>
|
</RouterDialogsContextWrapper>
|
||||||
<div style={{
|
|
||||||
flexGrow: 1,
|
|
||||||
}}></div>
|
|
||||||
</mdui-top-app-bar>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Mobile: 指定高度的容器
|
|
||||||
* Default: 侧边列表
|
|
||||||
*/
|
|
||||||
<div style={isMobileUI() ? {
|
|
||||||
display: 'flex',
|
|
||||||
height: 'calc(100% - 80px - 67px)',
|
|
||||||
width: '100%',
|
|
||||||
} : {
|
|
||||||
paddingRight: '8px',
|
|
||||||
}} id="SideBar">
|
|
||||||
<RecentChatsList style={{
|
|
||||||
display: currentShowPage == 'Recents' ? undefined : 'none'
|
|
||||||
}} />
|
|
||||||
<FavouriteChatsList style={{
|
|
||||||
display: currentShowPage == 'Favourites' ? undefined : 'none'
|
|
||||||
}} />
|
|
||||||
<AllChatsList style={{
|
|
||||||
display: currentShowPage == 'AllChats' ? undefined : 'none'
|
|
||||||
}} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
<div id="ChatFragment" style={{
|
|
||||||
display: "flex",
|
|
||||||
width: '100%'
|
|
||||||
}}>
|
|
||||||
{
|
|
||||||
(state.currentSelectedChatId && state.currentSelectedChatId != '')
|
|
||||||
? <LazyChatFragment openedWithRouter={false} chatId={state.currentSelectedChatId!} />
|
|
||||||
: <div style={{
|
|
||||||
width: '100%',
|
|
||||||
textAlign: 'center',
|
|
||||||
alignSelf: 'center',
|
|
||||||
}}>
|
|
||||||
选择以开始对话......
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Mobile: 底部导航栏提供列表切换
|
|
||||||
* Default: 侧边列表提供列表切换
|
|
||||||
*/
|
|
||||||
isMobileUI() && <mdui-navigation-bar ref={navigationRef} label-visibility="selected" value="Recents" style={{
|
|
||||||
position: 'sticky',
|
|
||||||
bottom: '0',
|
|
||||||
}}>
|
|
||||||
<mdui-navigation-bar-item icon="watch_later--outlined" active-icon="watch_later--filled" value="Recents">最近对话</mdui-navigation-bar-item>
|
|
||||||
<mdui-navigation-bar-item icon="favorite_border" active-icon="favorite" value="Favourites">收藏对话</mdui-navigation-bar-item>
|
|
||||||
<mdui-navigation-bar-item icon="chat--outlined" active-icon="chat--filled" value="AllChats">全部对话</mdui-navigation-bar-item>
|
|
||||||
</mdui-navigation-bar>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</MainSharedContext.Provider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +308,13 @@ export default function Main() {
|
|||||||
{
|
{
|
||||||
path: 'chat',
|
path: 'chat',
|
||||||
Component: ChatFragmentDialog,
|
Component: ChatFragmentDialog,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'info',
|
||||||
|
Component: UserOrChatInfoDialog,
|
||||||
|
loader: ChatInfoDialogDataLoader,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}])
|
}])
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import { $, Tab, TextField } from "mdui"
|
|||||||
import useEventListener from "../../utils/useEventListener"
|
import useEventListener from "../../utils/useEventListener"
|
||||||
import useEffectRef from "../../utils/useEffectRef"
|
import useEffectRef from "../../utils/useEffectRef"
|
||||||
import isMobileUI from "../../utils/isMobileUI"
|
import isMobileUI from "../../utils/isMobileUI"
|
||||||
import { useLocation, useNavigate } from "react-router"
|
import { Outlet, useLocation, useNavigate, NavigateFunction } from "react-router"
|
||||||
import { Chat } from "lingchair-client-protocol"
|
import { Chat } from "lingchair-client-protocol"
|
||||||
import gotoChatInfo from "../routers/gotoChatInfo"
|
|
||||||
import Preference from "../preference/Preference"
|
import Preference from "../preference/Preference"
|
||||||
import PreferenceHeader from "../preference/PreferenceHeader"
|
import PreferenceHeader from "../preference/PreferenceHeader"
|
||||||
import PreferenceLayout from "../preference/PreferenceLayout"
|
import PreferenceLayout from "../preference/PreferenceLayout"
|
||||||
@@ -14,6 +13,10 @@ import TextFieldPreference from "../preference/TextFieldPreference"
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import ChatMessageContainer from "./ChatMessageContainer"
|
import ChatMessageContainer from "./ChatMessageContainer"
|
||||||
|
|
||||||
|
function gotoChatInfo(nav: NavigateFunction, id: string) {
|
||||||
|
nav('/chat/info?id=' + id)
|
||||||
|
}
|
||||||
|
|
||||||
interface MduiTabFitSizeArgs extends React.HTMLAttributes<HTMLElement & Tab> {
|
interface MduiTabFitSizeArgs extends React.HTMLAttributes<HTMLElement & Tab> {
|
||||||
value: string
|
value: string
|
||||||
}
|
}
|
||||||
@@ -44,59 +47,60 @@ export default function ChatFragment({
|
|||||||
const chatPanelRef = React.useRef<HTMLElement>()
|
const chatPanelRef = React.useRef<HTMLElement>()
|
||||||
const inputRef = React.useRef<TextField>()
|
const inputRef = React.useRef<TextField>()
|
||||||
|
|
||||||
return <div style={{
|
return (
|
||||||
width: '100%',
|
<div style={{
|
||||||
height: '100%',
|
width: '100%',
|
||||||
display: 'flex',
|
height: '100%',
|
||||||
flexDirection: 'column',
|
display: 'flex',
|
||||||
overflowY: 'auto',
|
flexDirection: 'column',
|
||||||
}}>
|
overflowY: 'auto',
|
||||||
<mdui-tabs ref={useEffectRef<HTMLElement>((ref) => {
|
|
||||||
$(ref.current!.shadowRoot).append(`<style>.container::after { height: 0 !important; }</style>`)
|
|
||||||
$(tabRef.current!.shadowRoot).append(`<style>.container::after { height: 0 !important; }</style>`)
|
|
||||||
; (!isMobileUI()) && $(tabRef.current!.shadowRoot).append(`<style>.no-scroll-bar::-webkit-scrollbar{width:0px !important}*::-webkit-scrollbar{width:7px;height:10px}*::-webkit-scrollbar-track{width:6px;background:rgba(#101f1c,0.1);-webkit-border-radius:2em;-moz-border-radius:2em;border-radius:2em}*::-webkit-scrollbar-thumb{background-color:rgba(144,147,153,0.5);background-clip:padding-box;min-height:28px;-webkit-border-radius:2em;-moz-border-radius:2em;border-radius:2em;transition:background-color 0.3s;cursor:pointer}*::-webkit-scrollbar-thumb:hover{background-color:rgba(144,147,153,0.3)}</style>`)
|
|
||||||
}, [])} style={{
|
|
||||||
position: 'sticky',
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
}}>
|
}}>
|
||||||
{
|
<mdui-tabs ref={useEffectRef<HTMLElement>((ref) => {
|
||||||
openedWithRouter && <mdui-button-icon icon="arrow_back" onClick={() => nav(-1)} style={{
|
$(ref.current!.shadowRoot).append(`<style>.container::after { height: 0 !important; }</style>`)
|
||||||
alignSelf: 'center',
|
$(tabRef.current!.shadowRoot).append(`<style>.container::after { height: 0 !important; }</style>`)
|
||||||
marginLeft: '5px',
|
; (!isMobileUI()) && $(tabRef.current!.shadowRoot).append(`<style>.no-scroll-bar::-webkit-scrollbar{width:0px !important}*::-webkit-scrollbar{width:7px;height:10px}*::-webkit-scrollbar-track{width:6px;background:rgba(#101f1c,0.1);-webkit-border-radius:2em;-moz-border-radius:2em;border-radius:2em}*::-webkit-scrollbar-thumb{background-color:rgba(144,147,153,0.5);background-clip:padding-box;min-height:28px;-webkit-border-radius:2em;-moz-border-radius:2em;border-radius:2em;transition:background-color 0.3s;cursor:pointer}*::-webkit-scrollbar-thumb:hover{background-color:rgba(144,147,153,0.3)}</style>`)
|
||||||
marginRight: '5px',
|
}, [])} style={{
|
||||||
}}></mdui-button-icon>
|
|
||||||
}
|
|
||||||
<mdui-tabs ref={tabRef} value={tabItemSelected} style={{
|
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
height: "100%",
|
|
||||||
width: '100%',
|
|
||||||
overflowX: 'auto',
|
|
||||||
}}>
|
}}>
|
||||||
{
|
{
|
||||||
chatInfo.isMember() ? <>
|
openedWithRouter && <mdui-button-icon icon="arrow_back" onClick={() => nav(-1)} style={{
|
||||||
<MduiTabFitSize value="Chat">{chatInfo.getTitle()}</MduiTabFitSize>
|
alignSelf: 'center',
|
||||||
{chatInfo.getType() == 'group' && chatInfo.isAdmin() && <MduiTabFitSize value="NewMemberRequests">加入请求</MduiTabFitSize>}
|
marginLeft: '5px',
|
||||||
{chatInfo.getType() == 'group' && <MduiTabFitSize value="GroupMembers">群组成员</MduiTabFitSize>}
|
marginRight: '5px',
|
||||||
</>
|
}}></mdui-button-icon>
|
||||||
: <MduiTabFitSize value="RequestJoin">{chatInfo.getTitle()}</MduiTabFitSize>
|
|
||||||
}
|
}
|
||||||
{chatInfo.getType() == 'group' && <MduiTabFitSize value="Settings">设置</MduiTabFitSize>}
|
<mdui-tabs ref={tabRef} value={tabItemSelected} style={{
|
||||||
|
position: 'sticky',
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: "100%",
|
||||||
|
width: '100%',
|
||||||
|
overflowX: 'auto',
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
chatInfo.isMember() ? <>
|
||||||
|
<MduiTabFitSize value="Chat">{chatInfo.getTitle()}</MduiTabFitSize>
|
||||||
|
{chatInfo.getType() == 'group' && chatInfo.isAdmin() && <MduiTabFitSize value="NewMemberRequests">加入请求</MduiTabFitSize>}
|
||||||
|
{chatInfo.getType() == 'group' && <MduiTabFitSize value="GroupMembers">群组成员</MduiTabFitSize>}
|
||||||
|
</>
|
||||||
|
: <MduiTabFitSize value="RequestJoin">{chatInfo.getTitle()}</MduiTabFitSize>
|
||||||
|
}
|
||||||
|
{chatInfo.getType() == 'group' && <MduiTabFitSize value="Settings">设置</MduiTabFitSize>}
|
||||||
|
</mdui-tabs>
|
||||||
<div style={{
|
<div style={{
|
||||||
flexGrow: '1',
|
flexGrow: '1',
|
||||||
}}></div>
|
}}></div>
|
||||||
<mdui-button-icon icon="open_in_new" onClick={() => {
|
<mdui-button-icon icon="open_in_new" onClick={() => {
|
||||||
window.open('/chat?id=' + chatInfo.getId(), '_blank')
|
window.open('/chat?id=' + chatInfo.getId(), '_blank')
|
||||||
|
|
||||||
}} style={{
|
}} style={{
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
marginLeft: '5px',
|
marginLeft: '5px',
|
||||||
marginRight: '5px',
|
marginRight: '5px',
|
||||||
}}></mdui-button-icon>
|
}}></mdui-button-icon>
|
||||||
<mdui-button-icon icon="refresh" onClick={() => {
|
<mdui-button-icon icon="refresh" onClick={() => {
|
||||||
|
|
||||||
}} style={{
|
}} style={{
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
marginLeft: '5px',
|
marginLeft: '5px',
|
||||||
@@ -108,189 +112,189 @@ export default function ChatFragment({
|
|||||||
marginRight: '5px',
|
marginRight: '5px',
|
||||||
}}></mdui-button-icon>
|
}}></mdui-button-icon>
|
||||||
</mdui-tabs>
|
</mdui-tabs>
|
||||||
</mdui-tabs>
|
<mdui-tab-panel slot="panel" value="RequestJoin" style={{
|
||||||
<mdui-tab-panel slot="panel" value="RequestJoin" style={{
|
display: tabItemSelected == "RequestJoin" ? "flex" : "none",
|
||||||
display: tabItemSelected == "RequestJoin" ? "flex" : "none",
|
flexDirection: "column",
|
||||||
flexDirection: "column",
|
height: "100%",
|
||||||
height: "100%",
|
justifyContent: 'center',
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}>
|
|
||||||
<div>
|
|
||||||
{/* 非群成员 */}
|
|
||||||
</div>
|
|
||||||
</mdui-tab-panel>
|
|
||||||
<mdui-tab-panel slot="panel" value="Chat" ref={chatPanelRef} style={{
|
|
||||||
display: tabItemSelected == "Chat" ? "flex" : "none",
|
|
||||||
flexDirection: "column",
|
|
||||||
height: "100%",
|
|
||||||
}} onScroll={async (e: WheelEvent) => {
|
|
||||||
const scrollTop = (e.target as HTMLDivElement).scrollTop
|
|
||||||
if (scrollTop == 0) {
|
|
||||||
// 加载更多
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<div style={{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: "center",
|
|
||||||
paddingTop: "15px",
|
|
||||||
}}>
|
|
||||||
{/* 这里显示一些提示 */}
|
|
||||||
</div>
|
|
||||||
<ChatMessageContainer chatInfo={chatInfo} />
|
|
||||||
{
|
|
||||||
// 输入框
|
|
||||||
}
|
|
||||||
<div style={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingBottom: '2px',
|
|
||||||
paddingTop: '0.1rem',
|
|
||||||
position: 'sticky',
|
|
||||||
bottom: '0',
|
|
||||||
paddingLeft: '5px',
|
|
||||||
paddingRight: '4px',
|
|
||||||
backgroundColor: 'rgb(var(--mdui-color-surface))',
|
|
||||||
}} onDrop={(e) => {
|
|
||||||
// 文件拽入
|
|
||||||
}}>
|
}}>
|
||||||
<mdui-text-field variant="outlined" placeholder="(。・ω・。)" autosize ref={inputRef} max-rows={6} onChange={() => {
|
<div>
|
||||||
if (inputRef.current?.value.trim() == '') {
|
{/* 非群成员 */}
|
||||||
// 清空缓存的文件
|
</div>
|
||||||
}
|
</mdui-tab-panel>
|
||||||
}} onKeyDown={(event: KeyboardEvent) => {
|
<mdui-tab-panel slot="panel" value="Chat" ref={chatPanelRef} style={{
|
||||||
if (event.ctrlKey && event.key == 'Enter') {
|
display: tabItemSelected == "Chat" ? "flex" : "none",
|
||||||
// 发送消息
|
flexDirection: "column",
|
||||||
}
|
height: "100%",
|
||||||
}} onPaste={(event: ClipboardEvent) => {
|
}} onScroll={async (e: WheelEvent) => {
|
||||||
for (const item of event.clipboardData?.items || []) {
|
const scrollTop = (e.target as HTMLDivElement).scrollTop
|
||||||
if (item.kind == 'file') {
|
if (scrollTop == 0) {
|
||||||
event.preventDefault()
|
// 加载更多
|
||||||
const file = item.getAsFile() as File
|
}
|
||||||
// 添加文件
|
}}>
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingTop: "15px",
|
||||||
|
}}>
|
||||||
|
{/* 这里显示一些提示 */}
|
||||||
|
</div>
|
||||||
|
<ChatMessageContainer chatInfo={chatInfo} />
|
||||||
|
{
|
||||||
|
// 输入框
|
||||||
|
}
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingBottom: '2px',
|
||||||
|
paddingTop: '0.1rem',
|
||||||
|
position: 'sticky',
|
||||||
|
bottom: '0',
|
||||||
|
paddingLeft: '5px',
|
||||||
|
paddingRight: '4px',
|
||||||
|
backgroundColor: 'rgb(var(--mdui-color-surface))',
|
||||||
|
}} onDrop={(e) => {
|
||||||
|
// 文件拽入
|
||||||
|
}}>
|
||||||
|
<mdui-text-field variant="outlined" placeholder="(。・ω・。)" autosize ref={inputRef} max-rows={6} onChange={() => {
|
||||||
|
if (inputRef.current?.value.trim() == '') {
|
||||||
|
// 清空缓存的文件
|
||||||
}
|
}
|
||||||
}
|
}} onKeyDown={(event: KeyboardEvent) => {
|
||||||
}} style={{
|
if (event.ctrlKey && event.key == 'Enter') {
|
||||||
marginRight: '10px',
|
// 发送消息
|
||||||
marginTop: '3px',
|
}
|
||||||
marginBottom: '3px',
|
}} onPaste={(event: ClipboardEvent) => {
|
||||||
}}></mdui-text-field>
|
for (const item of event.clipboardData?.items || []) {
|
||||||
<mdui-button-icon slot="end-icon" icon="attach_file" style={{
|
if (item.kind == 'file') {
|
||||||
marginRight: '6px',
|
event.preventDefault()
|
||||||
}} onClick={() => {
|
const file = item.getAsFile() as File
|
||||||
// 添加文件
|
// 添加文件
|
||||||
}}></mdui-button-icon>
|
}
|
||||||
<mdui-button-icon icon="send" style={{
|
}
|
||||||
marginRight: '7px',
|
}} style={{
|
||||||
}} onClick={() => {
|
marginRight: '10px',
|
||||||
// 发送消息
|
marginTop: '3px',
|
||||||
}}></mdui-button-icon>
|
marginBottom: '3px',
|
||||||
|
}}></mdui-text-field>
|
||||||
|
<mdui-button-icon slot="end-icon" icon="attach_file" style={{
|
||||||
|
marginRight: '6px',
|
||||||
|
}} onClick={() => {
|
||||||
|
// 添加文件
|
||||||
|
}}></mdui-button-icon>
|
||||||
|
<mdui-button-icon icon="send" style={{
|
||||||
|
marginRight: '7px',
|
||||||
|
}} onClick={() => {
|
||||||
|
// 发送消息
|
||||||
|
}}></mdui-button-icon>
|
||||||
|
<div style={{
|
||||||
|
display: 'none'
|
||||||
|
}}>
|
||||||
|
<input accept="*/*" type="file" name="添加文件" multiple ></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mdui-tab-panel>
|
||||||
|
{
|
||||||
|
chatInfo.getType() == 'group' && <mdui-tab-panel slot="panel" value="GroupMembers" style={{
|
||||||
|
display: tabItemSelected == "GroupMembers" ? "flex" : "none",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: "100%",
|
||||||
|
}}>
|
||||||
|
{/* <GroupMembersList chat={chatInfo} /> */}
|
||||||
|
</mdui-tab-panel>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
chatInfo.getType() == 'group' && <mdui-tab-panel slot="panel" value="NewMemberRequests" style={{
|
||||||
|
display: tabItemSelected == "NewMemberRequests" ? "flex" : "none",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: "100%",
|
||||||
|
}}>
|
||||||
|
{/* {chatInfo.isAdmin() && <JoinRequestsList chat={chatInfo} />} */}
|
||||||
|
</mdui-tab-panel>
|
||||||
|
}
|
||||||
|
<mdui-tab-panel slot="panel" value="Settings" style={{
|
||||||
|
display: tabItemSelected == "Settings" ? "flex" : "none",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: "100%",
|
||||||
|
}}>
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'none'
|
display: 'none'
|
||||||
}}>
|
}}>
|
||||||
<input accept="*/*" type="file" name="添加文件" multiple ></input>
|
<input accept="image/*" type="file" name="上传对话头像"></input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{
|
||||||
</mdui-tab-panel>
|
/* chatInfo.getType() == 'group' && <PreferenceLayout>
|
||||||
{
|
<PreferenceUpdater.Provider value={groupPreferenceStore.createUpdater()}>
|
||||||
chatInfo.getType() == 'group' && <mdui-tab-panel slot="panel" value="GroupMembers" style={{
|
<PreferenceHeader
|
||||||
display: tabItemSelected == "GroupMembers" ? "flex" : "none",
|
title="群组资料" />
|
||||||
flexDirection: "column",
|
<Preference
|
||||||
height: "100%",
|
title="上传新的头像"
|
||||||
}}>
|
icon="image"
|
||||||
{/* <GroupMembersList chat={chatInfo} /> */}
|
disabled={!chatInfo.isAdmin()}
|
||||||
</mdui-tab-panel>
|
onClick={() => {
|
||||||
}
|
uploadChatAvatarRef.current!.click()
|
||||||
{
|
}} />
|
||||||
chatInfo.getType() == 'group' && <mdui-tab-panel slot="panel" value="NewMemberRequests" style={{
|
<TextFieldPreference
|
||||||
display: tabItemSelected == "NewMemberRequests" ? "flex" : "none",
|
title="设置群名称"
|
||||||
flexDirection: "column",
|
icon="edit"
|
||||||
height: "100%",
|
id="group_title"
|
||||||
}}>
|
state={groupPreferenceStore.state.group_title || ''}
|
||||||
{/* {chatInfo.isAdmin() && <JoinRequestsList chat={chatInfo} />} */}
|
disabled={!chatInfo.isAdmin()} />
|
||||||
</mdui-tab-panel>
|
<TextFieldPreference
|
||||||
}
|
title="设置群别名"
|
||||||
<mdui-tab-panel slot="panel" value="Settings" style={{
|
icon="edit"
|
||||||
display: tabItemSelected == "Settings" ? "flex" : "none",
|
id="group_name"
|
||||||
flexDirection: "column",
|
description="以便于添加, 可留空"
|
||||||
height: "100%",
|
state={groupPreferenceStore.state.group_name || ''}
|
||||||
}}>
|
disabled={!chatInfo.isAdmin()} />
|
||||||
<div style={{
|
<PreferenceHeader
|
||||||
display: 'none'
|
title="入群设定" />
|
||||||
}}>
|
<SwitchPreference
|
||||||
<input accept="image/*" type="file" name="上传对话头像"></input>
|
title="允许入群"
|
||||||
</div>
|
icon="person_add"
|
||||||
{
|
id="allow_new_member_join"
|
||||||
/* chatInfo.getType() == 'group' && <PreferenceLayout>
|
disabled={!chatInfo.isAdmin()}
|
||||||
<PreferenceUpdater.Provider value={groupPreferenceStore.createUpdater()}>
|
state={groupPreferenceStore.state.allow_new_member_join || false} />
|
||||||
<PreferenceHeader
|
<SwitchPreference
|
||||||
title="群组资料" />
|
title="允许成员邀请"
|
||||||
<Preference
|
description="目前压根没有这项功能, 甚至还不能查看成员列表, 以后再说吧"
|
||||||
title="上传新的头像"
|
id="allow_new_member_from_invitation"
|
||||||
icon="image"
|
|
||||||
disabled={!chatInfo.isAdmin()}
|
|
||||||
onClick={() => {
|
|
||||||
uploadChatAvatarRef.current!.click()
|
|
||||||
}} />
|
|
||||||
<TextFieldPreference
|
|
||||||
title="设置群名称"
|
|
||||||
icon="edit"
|
|
||||||
id="group_title"
|
|
||||||
state={groupPreferenceStore.state.group_title || ''}
|
|
||||||
disabled={!chatInfo.isAdmin()} />
|
|
||||||
<TextFieldPreference
|
|
||||||
title="设置群别名"
|
|
||||||
icon="edit"
|
|
||||||
id="group_name"
|
|
||||||
description="以便于添加, 可留空"
|
|
||||||
state={groupPreferenceStore.state.group_name || ''}
|
|
||||||
disabled={!chatInfo.isAdmin()} />
|
|
||||||
<PreferenceHeader
|
|
||||||
title="入群设定" />
|
|
||||||
<SwitchPreference
|
|
||||||
title="允许入群"
|
|
||||||
icon="person_add"
|
|
||||||
id="allow_new_member_join"
|
|
||||||
disabled={!chatInfo.isAdmin()}
|
|
||||||
state={groupPreferenceStore.state.allow_new_member_join || false} />
|
|
||||||
<SwitchPreference
|
|
||||||
title="允许成员邀请"
|
|
||||||
description="目前压根没有这项功能, 甚至还不能查看成员列表, 以后再说吧"
|
|
||||||
id="allow_new_member_from_invitation"
|
|
||||||
icon="_"
|
|
||||||
disabled={true || !chatInfo.isAdmin()}
|
|
||||||
state={groupPreferenceStore.state.allow_new_member_from_invitation || false} />
|
|
||||||
<SelectPreference
|
|
||||||
title="入群验证方式"
|
|
||||||
icon="_"
|
|
||||||
id="new_member_join_method"
|
|
||||||
selections={{
|
|
||||||
disabled: "无需验证",
|
|
||||||
allowed_by_admin: "只需要管理员批准 (WIP)",
|
|
||||||
answered_and_allowed_by_admin: "需要回答问题并获得管理员批准 (WIP)",
|
|
||||||
}}
|
|
||||||
disabled={!chatInfo.isAdmin() || !groupPreferenceStore.state.allow_new_member_join}
|
|
||||||
state={groupPreferenceStore.state.new_member_join_method || 'disabled'} />
|
|
||||||
{
|
|
||||||
groupPreferenceStore.state.new_member_join_method == 'answered_and_allowed_by_admin'
|
|
||||||
&& <TextFieldPreference
|
|
||||||
title="设置问题"
|
|
||||||
icon="_"
|
icon="_"
|
||||||
id="answered_and_allowed_by_admin_question"
|
disabled={true || !chatInfo.isAdmin()}
|
||||||
description="WIP"
|
state={groupPreferenceStore.state.allow_new_member_from_invitation || false} />
|
||||||
state={groupPreferenceStore.state.answered_and_allowed_by_admin_question || ''}
|
<SelectPreference
|
||||||
disabled={true || !chatInfo.isAdmin()} />
|
title="入群验证方式"
|
||||||
}
|
icon="_"
|
||||||
</PreferenceUpdater.Provider>
|
id="new_member_join_method"
|
||||||
</PreferenceLayout> */
|
selections={{
|
||||||
}
|
disabled: "无需验证",
|
||||||
{
|
allowed_by_admin: "只需要管理员批准 (WIP)",
|
||||||
chatInfo.getType() == 'private' && (
|
answered_and_allowed_by_admin: "需要回答问题并获得管理员批准 (WIP)",
|
||||||
<div>
|
}}
|
||||||
未制作
|
disabled={!chatInfo.isAdmin() || !groupPreferenceStore.state.allow_new_member_join}
|
||||||
</div>
|
state={groupPreferenceStore.state.new_member_join_method || 'disabled'} />
|
||||||
)
|
{
|
||||||
}
|
groupPreferenceStore.state.new_member_join_method == 'answered_and_allowed_by_admin'
|
||||||
</mdui-tab-panel>
|
&& <TextFieldPreference
|
||||||
</div >
|
title="设置问题"
|
||||||
|
icon="_"
|
||||||
|
id="answered_and_allowed_by_admin_question"
|
||||||
|
description="WIP"
|
||||||
|
state={groupPreferenceStore.state.answered_and_allowed_by_admin_question || ''}
|
||||||
|
disabled={true || !chatInfo.isAdmin()} />
|
||||||
|
}
|
||||||
|
</PreferenceUpdater.Provider>
|
||||||
|
</PreferenceLayout> */
|
||||||
|
}
|
||||||
|
{
|
||||||
|
chatInfo.getType() == 'private' && (
|
||||||
|
<div>
|
||||||
|
未制作
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</mdui-tab-panel>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export default function AllChatsList({ ...props }: React.HTMLAttributes<HTMLElem
|
|||||||
...props?.style,
|
...props?.style,
|
||||||
}} {...props}>
|
}} {...props}>
|
||||||
<mdui-text-field icon="search" type="search" clearable ref={searchRef} variant="outlined" placeholder="搜索..." style={{
|
<mdui-text-field icon="search" type="search" clearable ref={searchRef} variant="outlined" placeholder="搜索..." style={{
|
||||||
|
padding: isMobileUI() ? '12px' : undefined,
|
||||||
paddingTop: '4px',
|
paddingTop: '4px',
|
||||||
paddingBottom: '13px',
|
paddingBottom: '13px',
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export default function FavouriteChatsList({ ...props }: React.HTMLAttributes<HT
|
|||||||
zIndex: '10',
|
zIndex: '10',
|
||||||
}}>
|
}}>
|
||||||
<mdui-text-field icon="search" type="search" clearable ref={searchRef} variant="outlined" placeholder="搜索..." style={{
|
<mdui-text-field icon="search" type="search" clearable ref={searchRef} variant="outlined" placeholder="搜索..." style={{
|
||||||
|
padding: isMobileUI() ? '12px' : undefined,
|
||||||
paddingTop: '4px',
|
paddingTop: '4px',
|
||||||
}}></mdui-text-field>
|
}}></mdui-text-field>
|
||||||
<mdui-list-item rounded style={{
|
<mdui-list-item rounded style={{
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export default function RecentChatsList({ ...props }: React.HTMLAttributes<HTMLE
|
|||||||
...props?.style,
|
...props?.style,
|
||||||
}} {...props}>
|
}} {...props}>
|
||||||
<mdui-text-field icon="search" type="search" clearable ref={searchRef} variant="outlined" placeholder="搜索..." style={{
|
<mdui-text-field icon="search" type="search" clearable ref={searchRef} variant="outlined" placeholder="搜索..." style={{
|
||||||
|
padding: isMobileUI() ? '12px' : undefined,
|
||||||
paddingTop: '4px',
|
paddingTop: '4px',
|
||||||
marginBottom: '13px',
|
marginBottom: '13px',
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { useSearchParams } from "react-router"
|
import { useSearchParams, Outlet } from "react-router"
|
||||||
import useRouterDialogRef from "./useRouterDialogRef"
|
import useRouterDialogRef from "./useRouterDialogRef"
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import LazyChatFragment from "../chat-fragment/LazyChatFragment"
|
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() {
|
export default function ChatFragmentDialog() {
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
@@ -9,7 +12,7 @@ export default function ChatFragmentDialog() {
|
|||||||
|
|
||||||
const dialogRef = useRouterDialogRef()
|
const dialogRef = useRouterDialogRef()
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEventListener(dialogRef, 'open', () => {
|
||||||
const shadow = dialogRef.current!.shadowRoot as ShadowRoot
|
const shadow = dialogRef.current!.shadowRoot as ShadowRoot
|
||||||
const panel = shadow.querySelector(".panel") as HTMLElement
|
const panel = shadow.querySelector(".panel") as HTMLElement
|
||||||
panel.style.padding = '0'
|
panel.style.padding = '0'
|
||||||
@@ -21,7 +24,15 @@ export default function ChatFragmentDialog() {
|
|||||||
body.style.display = 'flex'
|
body.style.display = 'flex'
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <mdui-dialog fullscreen ref={dialogRef}>
|
return (<>
|
||||||
<LazyChatFragment chatId={id!} openedWithRouter={true} />
|
<mdui-dialog fullscreen ref={dialogRef}>
|
||||||
</mdui-dialog>
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
width: '100%',
|
||||||
|
}}>
|
||||||
|
<LazyChatFragment chatId={id!} openedWithRouter={true} />
|
||||||
|
</div>
|
||||||
|
</mdui-dialog>
|
||||||
|
<Outlet />
|
||||||
|
</>)
|
||||||
}
|
}
|
||||||
|
|||||||
20
client/ui/routers/ChatInfoDialogDataLoader.ts
Normal file
20
client/ui/routers/ChatInfoDialogDataLoader.ts
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
5
client/ui/routers/RouterDialogsContext.ts
Normal file
5
client/ui/routers/RouterDialogsContext.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
const RouterDialogsContext = React.createContext(() => {})
|
||||||
|
|
||||||
|
export default RouterDialogsContext
|
||||||
62
client/ui/routers/RouterDialogsContextWrapper.tsx
Normal file
62
client/ui/routers/RouterDialogsContextWrapper.tsx
Normal 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>
|
||||||
|
}
|
||||||
@@ -3,31 +3,17 @@ import useAsyncEffect from "../../utils/useAsyncEffect"
|
|||||||
import sleep from "../../utils/sleep"
|
import sleep from "../../utils/sleep"
|
||||||
import { BlockerFunction, useBlocker, useNavigate } from "react-router"
|
import { BlockerFunction, useBlocker, useNavigate } from "react-router"
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
import RouterDialogsContext from './RouterDialogsContext'
|
||||||
|
|
||||||
export default function useRouterDialogRef() {
|
export default function useRouterDialogRef() {
|
||||||
const dialogRef = React.useRef<Dialog>()
|
const dialogRef = React.useRef<Dialog>(RouterDialogsContext)
|
||||||
const proceedRef = React.useRef<() => void>()
|
const registerRouterDialog = React.useContext(RouterDialogsContext)
|
||||||
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])
|
|
||||||
|
|
||||||
useAsyncEffect(async () => {
|
useAsyncEffect(async () => {
|
||||||
|
registerRouterDialog(dialogRef)
|
||||||
await sleep(10)
|
await sleep(10)
|
||||||
dialogRef.current!.open = true
|
dialogRef.current!.open = true
|
||||||
dialogRef.current!.addEventListener('closed', async () => {
|
|
||||||
shouldBlock.current = false
|
|
||||||
await sleep(10)
|
|
||||||
proceedRef.current ? proceedRef.current() : nav(-1)
|
|
||||||
})
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return dialogRef
|
return dialogRef
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user