diff --git a/client/package.json b/client/package.json index 2775056..5e4560e 100644 --- a/client/package.json +++ b/client/package.json @@ -14,7 +14,6 @@ "pinch-zoom-element": "1.1.1", "react": "18.3.1", "react-dom": "18.3.1", - "react-router": "7.10.1", "socket.io-client": "4.8.1", "split.js": "1.3.2", "ua-parser-js": "2.0.6", diff --git a/client/ui/Main.tsx b/client/ui/Main.tsx index 48bbd4e..0fddb9d 100644 --- a/client/ui/Main.tsx +++ b/client/ui/Main.tsx @@ -3,7 +3,6 @@ import useEventListener from "../utils/useEventListener.ts" import AvatarMySelf from "./AvatarMySelf.tsx" import MainSharedContext from './MainSharedContext.ts' import * as React from 'react' -import { createBrowserRouter, Outlet, RouterProvider, useNavigate, useRouteError } from "react-router" import LoginDialog from "./main-page/LoginDialog.tsx" import useAsyncEffect from "../utils/useAsyncEffect.ts" import performAuth from "../performAuth.ts" @@ -17,20 +16,12 @@ import showSnackbar from "../utils/showSnackbar.ts" import AllChatsList from "./main-page/AllChatsList.tsx" import FavouriteChatsList from "./main-page/FavouriteChatsList.tsx" import RecentChatsList from "./main-page/RecentChatsList.tsx" -import UserOrChatInfoDialog from "./routers/UserOrChatInfoDialog.tsx" -import UserOrChatInfoDialogLoader from "./routers/UserOrChatInfoDialogDataLoader.ts" -import ChatInfoDialogDataLoader from "./routers/ChatInfoDialogDataLoader.ts" -import ChatFragmentDialog from "./routers/ChatFragmentDialog.tsx" -import EffectOnly from "./EffectOnly.tsx" import MainSharedReducer from "./MainSharedReducer.ts" -import gotoUserInfo from "./routers/gotoUserInfo.ts" -import EditMyProfileDialog from "./routers/EditMyProfileDialog.tsx" -import ProgressDialogFallback from "./ProgressDialogFallback.tsx" import Split from 'split.js' import data from "../data.ts" import LazyChatFragment from "./chat-fragment/LazyChatFragment.tsx" -import AddFavourtieChatDialog from "./routers/AddFavourtieChatDialog.tsx" -import RouterDialogsContextWrapper from './routers/RouterDialogsContextWrapper.tsx' +import DialogContextWrapper from "./app-state/AppStateContextWrapper.tsx" +import { AppState } from "./app-state/AppStateContext.ts" function Root() { const [myProfileCache, setMyProfileCache] = React.useState() @@ -59,8 +50,6 @@ function Root() { const [showLoginDialog, setShowLoginDialog] = React.useState(false) const [showRegisterDialog, setShowRegisterDialog] = React.useState(false) - const nav = useNavigate() - const [state, dispatch] = React.useReducer(MainSharedReducer, { favouriteChats: [], currentSelectedChatId: '', @@ -118,9 +107,11 @@ function Root() { } }, []) + const AppStateRef = React.useRef() + return ( - - + +
- { - // 将子路由渲染到此处 - - } - gotoUserInfo(nav, myProfileCache!.getId())}> + AppStateRef.current!.openUserInfo(myProfileCache!.getId())}> {myProfileCache?.getNickName()} @@ -147,7 +134,7 @@ function Root() { margin: '10px', }}> 客户端设置 - nav('/add/favourite_chat')}>添加收藏对话 + AppStateRef.current!.openAddFavouriteChat()}>添加收藏对话 创建新的群组
drawerRef.current!.open = true}> - + @@ -229,7 +216,7 @@ function Root() { }}> { (state.currentSelectedChatId && state.currentSelectedChatId != '') - ? + ? :
}
- - + + ) } -function SnackbarErrorBoundary() { - const error = useRouteError() - return { - const d = dialog({ - headline: "错误", - description: error instanceof Error ? ('[' + error.name + '] ' + (error.stack || error.message)) : error + '', - closeOnEsc: true, - closeOnOverlayClick: true, - }) - return () => { - d.open = false - } - }} deps={[]} /> -} - export default function Main() { - const router = createBrowserRouter([{ - path: "/", - Component: Root, - hydrateFallbackElement: , - ErrorBoundary: SnackbarErrorBoundary, - children: [ - { - path: 'info/:type', - Component: UserOrChatInfoDialog, - loader: UserOrChatInfoDialogLoader, - }, - { - path: 'add', - children: [ - { - path: 'favourite_chat', - Component: AddFavourtieChatDialog, - }, - ], - }, - { - path: 'settings', - children: [ - { - path: 'edit_profile', - Component: EditMyProfileDialog, - } - ], - }, - { - path: 'chat', - Component: ChatFragmentDialog, - children: [ - { - path: 'info', - Component: UserOrChatInfoDialog, - loader: ChatInfoDialogDataLoader, - }, - ], - }, - ], - }]) - - return + return } diff --git a/client/ui/chat-fragment/ChatFragment.tsx b/client/ui/chat-fragment/ChatFragment.tsx index e0d40f0..904aa68 100644 --- a/client/ui/chat-fragment/ChatFragment.tsx +++ b/client/ui/chat-fragment/ChatFragment.tsx @@ -2,7 +2,6 @@ import { $, Tab, TextField } from "mdui" import useEventListener from "../../utils/useEventListener" import useEffectRef from "../../utils/useEffectRef" import isMobileUI from "../../utils/isMobileUI" -import { Outlet, useLocation, useNavigate, NavigateFunction } from "react-router" import { Chat } from "lingchair-client-protocol" import Preference from "../preference/Preference" import PreferenceHeader from "../preference/PreferenceHeader" @@ -12,11 +11,7 @@ import SwitchPreference from "../preference/SwitchPreference" import TextFieldPreference from "../preference/TextFieldPreference" import * as React from 'react' import ChatMessageContainer from "./ChatMessageContainer" -import gotoChatInfo from "../routers/gotoChatInfo" - -function gotoChatInfo2(nav: NavigateFunction, id: string, useWithRouterChatFragment?: boolean) { - useWithRouterChatFragment ? nav('/chat/info?id=' + id) : gotoChatInfo(nav, id) -} +import AppStateContext from "../app-state/AppStateContext" interface MduiTabFitSizeArgs extends React.HTMLAttributes { value: string @@ -32,13 +27,12 @@ function MduiTabFitSize({ children, ...props }: MduiTabFitSizeArgs) { export default function ChatFragment({ chatInfo, - openedWithRouter, + openedInDialog, }: { chatInfo: Chat - openedWithRouter: boolean + openedInDialog: boolean }) { - const nav = useNavigate() - + const AppState = React.useContext(AppStateContext) const [tabItemSelected, setTabItemSelected] = React.useState('Chat') const tabRef = React.useRef() useEventListener(tabRef, 'change', () => { @@ -66,7 +60,7 @@ export default function ChatFragment({ flexDirection: "column", }}> { - openedWithRouter && nav(-1)} style={{ + openedInDialog && AppState.closeChat()} style={{ alignSelf: 'center', marginLeft: '5px', marginRight: '5px', @@ -107,7 +101,7 @@ export default function ChatFragment({ marginLeft: '5px', marginRight: '5px', }}> - gotoChatInfo2(nav, chatInfo.getId(), openedWithRouter)} style={{ + AppState.openChatInfo(chatInfo.getId())} style={{ alignSelf: 'center', marginLeft: '5px', marginRight: '5px', diff --git a/client/ui/chat-fragment/LazyChatFragment.tsx b/client/ui/chat-fragment/LazyChatFragment.tsx index 0cf64a9..e2fd963 100644 --- a/client/ui/chat-fragment/LazyChatFragment.tsx +++ b/client/ui/chat-fragment/LazyChatFragment.tsx @@ -5,10 +5,10 @@ import ChatFragment from "./ChatFragment" import * as React from 'react' import EffectOnly from "../EffectOnly" -export default function LazyChatFragment({ chatId, openedWithRouter }: { chatId: string, openedWithRouter: boolean }) { +export default function LazyChatFragment({ chatId, openedInDialog }: { chatId: string, openedInDialog: boolean }) { return {}} deps={[]} />}> Chat.getByIdOrThrow(getClient(), chatId), [chatId])} - children={(chatInfo: Chat) => } /> + children={(chatInfo: Chat) => } /> } diff --git a/client/ui/main-page/AllChatsList.tsx b/client/ui/main-page/AllChatsList.tsx index 13895fd..311fb3b 100644 --- a/client/ui/main-page/AllChatsList.tsx +++ b/client/ui/main-page/AllChatsList.tsx @@ -9,8 +9,7 @@ import isMobileUI from "../../utils/isMobileUI.ts" import { useContextSelector } from "use-context-selector" import MainSharedContext, { Shared } from "../MainSharedContext.ts" import ClientCache from "../../ClientCache.ts" -import gotoChatInfo from "../routers/gotoChatInfo.ts" -import { useNavigate } from "react-router" +import AppStateContext from "../app-state/AppStateContext.ts" export default function AllChatsList({ ...props }: React.HTMLAttributes) { const shared = useContextSelector(MainSharedContext, (context: Shared) => ({ @@ -22,7 +21,7 @@ export default function AllChatsList({ ...props }: React.HTMLAttributes([]) - const nav = useNavigate() + const DialogState = React.useContext(AppStateContext) useEventListener(searchRef, 'input', (e) => { setSearchText((e.target as unknown as TextField).value) @@ -76,7 +75,7 @@ export default function AllChatsList({ ...props }: React.HTMLAttributes { - gotoChatInfo(nav, v.getId()) + DialogState.openChatInfo(v.getId()) }} chat={v} /> ) diff --git a/client/ui/main-page/FavouriteChatsList.tsx b/client/ui/main-page/FavouriteChatsList.tsx index add215d..f0835db 100644 --- a/client/ui/main-page/FavouriteChatsList.tsx +++ b/client/ui/main-page/FavouriteChatsList.tsx @@ -11,6 +11,7 @@ import isMobileUI from "../../utils/isMobileUI.ts" import gotoChatInfo from "../routers/gotoChatInfo.ts" import ClientCache from "../../ClientCache.ts" import { useNavigate } from "react-router" +import AppStateContext from "../app-state/AppStateContext.ts" export default function FavouriteChatsList({ ...props }: React.HTMLAttributes) { const shared = useContextSelector(MainSharedContext, (context: Shared) => ({ @@ -24,7 +25,7 @@ export default function FavouriteChatsList({ ...props }: React.HTMLAttributes({}) - const nav = useNavigate() + const DialogState = React.useContext(AppStateContext) useEventListener(searchRef, 'input', (e) => { setSearchText((e.target as unknown as TextField).value) @@ -74,7 +75,7 @@ export default function FavouriteChatsList({ ...props }: React.HTMLAttributes nav('/add/favourite_chat')}>添加收藏 + }} icon="person_add" onClick={() => DialogState.openAddFavouriteChat()}>添加收藏 shared.functions_lazy.current.updateFavouriteChats()}>刷新列表 @@ -161,7 +162,7 @@ export default function FavouriteChatsList({ ...props }: React.HTMLAttributes diff --git a/client/ui/routers/AddFavourtieChatDialog.tsx b/client/ui/routers/AddFavourtieChatDialog.tsx deleted file mode 100644 index 6ec130a..0000000 --- a/client/ui/routers/AddFavourtieChatDialog.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import * as React from 'react' -import { Button, Dialog, snackbar, TextField } from "mdui" -import { data, useNavigate } from 'react-router' -import { useContextSelector } from 'use-context-selector' -import MainSharedContext, { Shared } from '../MainSharedContext' -import showSnackbar from '../../utils/showSnackbar' -import { CallbackError } from 'lingchair-client-protocol' -import useEventListener from '../../utils/useEventListener' -import ClientCache from '../../ClientCache' -import useRouterDialogRef from './useRouterDialogRef' - -export default function AddFavourtieChatDialog({ ...props }: { open: boolean } & React.HTMLAttributes) { - const dialogRef = useRouterDialogRef() - const nav = useNavigate() - - const inputTargetRef = React.useRef(null) - - async function addFavouriteChat() { - try { - await (await ClientCache.getMySelf())!.addFavouriteChatsOrThrow([inputTargetRef.current!.value]) - inputTargetRef.current!.value = '' - showSnackbar({ - message: '添加成功!' - }) - } catch (e) { - if (e instanceof CallbackError) - showSnackbar({ - message: '添加收藏对话失败: ' + e.message - }) - } - } - - return ( - - { - if (event.key == 'Enter') - addFavouriteChat() - }}> - nav(-1)}>取消 - addFavouriteChat()}>添加 - - ) -} diff --git a/client/ui/routers/ChatFragmentDialog.tsx b/client/ui/routers/ChatFragmentDialog.tsx deleted file mode 100644 index 9cd2a1a..0000000 --- a/client/ui/routers/ChatFragmentDialog.tsx +++ /dev/null @@ -1,38 +0,0 @@ -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() - const id = searchParams.get('id') - - const dialogRef = useRouterDialogRef() - - useEventListener(dialogRef, 'open', () => { - const shadow = dialogRef.current!.shadowRoot as ShadowRoot - const panel = shadow.querySelector(".panel") as HTMLElement - panel.style.padding = '0' - panel.style.color = 'inherit' - panel.style.backgroundColor = 'rgb(var(--mdui-color-background))' - panel.style.setProperty('--mdui-color-background', 'inherit') - const body = shadow.querySelector(".body") as HTMLElement - body.style.height = '100%' - body.style.display = 'flex' - }) - - return (<> - -
- -
-
- - ) -} diff --git a/client/ui/routers/ChatInfoDialog.tsx b/client/ui/routers/ChatInfoDialog.tsx deleted file mode 100644 index 8983661..0000000 --- a/client/ui/routers/ChatInfoDialog.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import React from 'react' -import { dialog, Dialog } from "mdui" -import Avatar from "../Avatar.tsx" -import { CallbackError, Chat } from 'lingchair-client-protocol' -import { data, useLocation, useNavigate, useSearchParams } from 'react-router' -import useAsyncEffect from '../../utils/useAsyncEffect.ts' -import { useContextSelector } from 'use-context-selector' -import MainSharedContext, { Shared } from '../MainSharedContext.ts' -import getClient from '../../getClient.ts' -import useEventListener from '../../utils/useEventListener.ts' -import showSnackbar from '../../utils/showSnackbar.ts' - -export default function ChatInfoDialog({ ...props }: React.HTMLAttributes) { - const shared = useContextSelector(MainSharedContext, (context: Shared) => ({ - myProfileCache: context.myProfileCache, - favouriteChats: context.favouriteChats, - })) - - const [chat, setChat] = React.useState() - const [userId, setUserId] = React.useState() - - const [searchParams] = useSearchParams() - let pathName = useLocation().pathname - const navigate = useNavigate() - function back() { - navigate('/') - } - - const dialogRef = React.useRef() - useEventListener(dialogRef, 'overlay-click', () => back()) - const id = searchParams.get('id') - - const [favourited, setIsFavourited] = React.useState(false) - React.useEffect(() => { - setIsFavourited(shared.favouriteChats.map((v) => v.getId()).indexOf(chat?.getId() || '') != -1) - }, [chat, shared]) - - React.useEffect(() => { - console.log("挂载喵!", pathName) - return () => console.log("被抛弃了喵!", pathName) - }, [pathName]) - - const isUser = pathName.startsWith('/info/user') - useAsyncEffect(async () => { - console.log(id, pathName) - try { - if (isUser) { - setChat(await Chat.getOrCreatePrivateChatOrThrow(getClient(), id!)) - setUserId(id!) - } else - setChat(await Chat.getByIdOrThrow(getClient(), id!)) - dialogRef.current!.open = true - } catch (e) { - if (e instanceof CallbackError) - showSnackbar({ - message: '打开资料卡失败: ' + e.message - }) - console.log(e) - back() - } - }, [id, isUser]) - - const avatarUrl = getClient().getUrlForFileByHash(chat?.getAvatarFileHash())! - - return ( - -
- avatarUrl && openImageViewer(avatarUrl)} /> -
- {chat?.getTitle()} - ({chat?.getType()}) ID: {chat?.getType() == 'private' ? userId : chat?.getId()} -
-
- - - - dialog({ - headline: favourited ? "取消收藏对话" : "收藏对话", - description: favourited ? "确定从收藏对话列表中移除吗? (虽然这不会导致聊天记录丢失)" : "确定要添加到收藏对话列表吗?", - actions: [ - { - text: "取消", - onClick: () => { - return true - }, - }, - { - text: "确定", - onClick: () => { - ; (async () => { - const re = await Client.invoke(favourited ? "User.removeContacts" : "User.addContacts", { - token: data.access_token, - targets: [ - chat!.id - ], - }) - if (re.code != 200) - checkApiSuccessOrSncakbar(re, favourited ? "取消收藏失败" : "收藏失败") - EventBus.emit('ContactsList.updateContacts') - })() - return true - }, - } - ], - })}>{favourited ? '取消收藏' : '收藏对话'} - { - chatInfoDialogRef.current!.open = false - openChatFragment(chat!.id) - }}>打开对话 - -
- ) -} diff --git a/client/ui/routers/ChatInfoDialogDataLoader.ts b/client/ui/routers/ChatInfoDialogDataLoader.ts deleted file mode 100644 index 1f0f2ae..0000000 --- a/client/ui/routers/ChatInfoDialogDataLoader.ts +++ /dev/null @@ -1,20 +0,0 @@ -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, - } -} diff --git a/client/ui/routers/EditMyProfileDialog.tsx b/client/ui/routers/EditMyProfileDialog.tsx deleted file mode 100644 index ea99f9e..0000000 --- a/client/ui/routers/EditMyProfileDialog.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { CallbackError, UserMySelf } from "lingchair-client-protocol" -import ClientCache from "../../ClientCache" -import AvatarMySelf from "../AvatarMySelf" -import useRouterDialogRef from "./useRouterDialogRef" -import useAsyncEffect from "../../utils/useAsyncEffect" -import getClient from "../../getClient" -import { useNavigate } from "react-router" -import showSnackbar from "../../utils/showSnackbar" -import useEventListener from "../../utils/useEventListener" -import { TextField } from "mdui" -import * as React from 'react' - -export default function EditMyProfileDialog() { - const dialogRef = useRouterDialogRef() - const nav = useNavigate() - - const [mySelf, setMySelf] = React.useState() - useAsyncEffect(async () => setMySelf(await ClientCache.getMySelf() as UserMySelf)) - - const chooseAvatarFileRef = React.useRef(null) - - const editNickNameRef = React.useRef(null) - const editUserNameRef = React.useRef(null) - - useEventListener(chooseAvatarFileRef, 'change', async (_) => { - const file = chooseAvatarFileRef.current!.files?.[0] as File - if (file == null) return - - try { - const hash = await getClient().uploadFile({ - fileName: 'UserAvatar', - fileData: file, - }) - await mySelf?.setAvatarFileHashOrThrow(hash) - showSnackbar({ - message: "修改成功, 刷新页面以更新", - }) - } catch (e) { - console.error(e) - if (e instanceof CallbackError) - showSnackbar({ - message: '上传头像失败: ' + e.message - }) - showSnackbar({ - message: '上传头像失败: ' + (e instanceof Error ? e.message : e) - }) - } - }) - - return ( - -
- -
- -
- { - chooseAvatarFileRef.current!.value = '' - chooseAvatarFileRef.current!.click() - }} style={{ - width: '50px', - height: '50px', - }} /> - -
- - - { - const input = e.target as HTMLInputElement - input.select() - input.setSelectionRange(0, 1145141919810) - }}> - - - nav(-1)}>取消 - { - try { - await mySelf?.updateProfileOrThrow({ - nickname: editNickNameRef.current?.value, - username: editUserNameRef.current?.value, - }) - } catch (e) { - if (e instanceof CallbackError) - showSnackbar({ - message: '更新资料失败: ' + e.message - }) - } - showSnackbar({ - message: "修改成功, 刷新页面以更新", - }) - }}>更新 -
- ) -} \ No newline at end of file diff --git a/client/ui/routers/RouterDialogsContext.ts b/client/ui/routers/RouterDialogsContext.ts deleted file mode 100644 index f69d7fd..0000000 --- a/client/ui/routers/RouterDialogsContext.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Dialog } from 'mdui' -import * as React from 'react' - -const RouterDialogsContext = React.createContext((ref: React.MutableRefObject) => {}) - -export default RouterDialogsContext diff --git a/client/ui/routers/RouterDialogsContextWrapper.tsx b/client/ui/routers/RouterDialogsContextWrapper.tsx deleted file mode 100644 index ca50e2e..0000000 --- a/client/ui/routers/RouterDialogsContextWrapper.tsx +++ /dev/null @@ -1,65 +0,0 @@ -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: React.MutableRefObject[] = [] - -export default function RouterDialogsContextWrapper({ children }: React.HTMLAttributes) { - const proceedRef = React.useRef<() => void>() - const nav = useNavigate() - // 进入子路由不会拦截上一个路由对话框的关闭 - // 没有路由对话框不会拦截 - const blocker = useBlocker(React.useCallback(({ 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) - } - return () => { - blocker.reset?.() - } - }, [blocker.state]) - - // 注册 - // 理应在 Effect 里 - function registerRouterDialog(ref: React.MutableRefObject) { - routerDialogsList.push(ref) - // 正常情况下不可能同时关掉两个对话框 - // 不过要是真有的话, 再说吧 - ref.current!.addEventListener('closed', async () => { - routerDialogsList.splice(routerDialogsList.length - 1, 1) - await sleep(10) - proceedRef.current ? proceedRef.current() : nav(-1) - }) - } - - return - { children } - -} diff --git a/client/ui/routers/UserOrChatInfoDialog.tsx b/client/ui/routers/UserOrChatInfoDialog.tsx deleted file mode 100644 index 00e2e94..0000000 --- a/client/ui/routers/UserOrChatInfoDialog.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { dialog } from "mdui" -import useRouterDialogRef from "./useRouterDialogRef" -import { useLoaderData, useNavigate } from "react-router" -import { CallbackError } from "lingchair-client-protocol" -import showSnackbar from "../../utils/showSnackbar" -import Avatar from "../Avatar" -import { useContextSelector } from "use-context-selector" -import MainSharedContext, { Shared } from "../MainSharedContext" -import * as React from 'react' -import UserOrChatInfoDialogLoader from "./UserOrChatInfoDialogDataLoader" -import ClientCache from "../../ClientCache" -import getClient from "../../getClient" -import gotoChat from "./gotoChat" -import isMobileUI from "../../utils/isMobileUI" - -export default function UserOrChatInfoDialog() { - const favouriteChats = useContextSelector( - MainSharedContext, - (context: Shared) => context.state.favouriteChats - ) - const setCurrentSelectedChatId = useContextSelector( - MainSharedContext, - (context: Shared) => context.setCurrentSelectedChatId - ) - - const nav = useNavigate() - - const dialogRef = useRouterDialogRef() - const { chat, id, mySelf } = useLoaderData() - - const isMySelf = mySelf?.getId() == id - - const favourited = React.useMemo(() => favouriteChats.map((v) => v.getId()).indexOf(chat.getId() || '') != -1, [chat, favouriteChats]) - - return ( - -
- -
- {chat.getTitle() + (isMySelf ? ' (我)' : '')} - ({chat.getType()}) ID: {chat.getType() == 'private' ? id : chat.getId()} -
-
- - - { - isMySelf && nav('/settings/edit_profile')}> - 编辑资料 - - } - { - !isMySelf && dialog({ - headline: favourited ? "取消收藏对话" : "收藏对话", - description: favourited ? "确定从收藏对话列表中移除吗? (虽然这不会导致聊天记录丢失)" : "确定要添加到收藏对话列表吗?", - actions: [ - { - text: "取消", - onClick: () => { - return true - }, - }, - { - text: "确定", - onClick: () => { - ; (async () => { - try { - if (favourited) - await (await ClientCache.getMySelf())!.removeFavouriteChatsOrThrow([chat.getId()]) - else - await (await ClientCache.getMySelf())!.addFavouriteChatsOrThrow([chat.getId()]) - } catch (e) { - if (e instanceof CallbackError) - showSnackbar({ - message: (favourited ? "取消收藏对话" : "收藏对话") + '失败: ' + e.message - }) - } - })() - return true - }, - } - ], - })}>{favourited ? '取消收藏' : '收藏对话'} - } - { - await nav(-1) - gotoChat(isMobileUI() ? { - nav: nav, - id: chat.getId(), - } : { - setter: setCurrentSelectedChatId, - id: chat.getId(), - }) - }}>打开对话 - -
- ) - - /* const location = useLocation() - const searchParams = useSearchParams() - const params = useParams() - - return ( - -
" - + Object.keys(location) - // @ts-ignore 懒 - .map((k) => `${k} = ${location[k]}`) - .join('

') - + "

↓ searchParams

" + Object.keys(searchParams) - // @ts-ignore 懒 - .map((k) => `${k} = ${searchParams[k]}`) - .join('

') - + "

↓ params

" + Object.keys(params) - // @ts-ignore 懒 - .map((k) => `${k} = ${params[k]}`) - .join('

') - }}>
-
- ) */ -} diff --git a/client/ui/routers/UserOrChatInfoDialogDataLoader.ts b/client/ui/routers/UserOrChatInfoDialogDataLoader.ts deleted file mode 100644 index 7a71c11..0000000 --- a/client/ui/routers/UserOrChatInfoDialogDataLoader.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { LoaderFunctionArgs } from "react-router" -import getClient from "../../getClient" -import { Chat } from "lingchair-client-protocol" -import ClientCache from "../../ClientCache" - -export default async function UserOrChatInfoDialogLoader({ params, request }: LoaderFunctionArgs) { - const searchParams = new URL(request.url).searchParams - - let id = searchParams.get('id') - let chat: Chat - if (params.type == 'user') - chat = await Chat.getOrCreatePrivateChatOrThrow(getClient(), id!) - else - chat = await Chat.getByIdOrThrow(getClient(), id!) - - if (chat.getType() == 'private') - id = await chat.getTheOtherUserIdOrThrow() - - return { - mySelf: await ClientCache.getMySelf(), - chat, - id, - } -} diff --git a/client/ui/routers/gotoChat.ts b/client/ui/routers/gotoChat.ts deleted file mode 100644 index 52ea772..0000000 --- a/client/ui/routers/gotoChat.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { NavigateFunction } from "react-router" - -export default async function gotoChat({ nav, setter, id }: { nav?: NavigateFunction, setter?: (id: string) => void, id: string }) { - await nav?.('/chat?id=' + id) - setter?.(id) -} diff --git a/client/ui/routers/gotoChatInfo.ts b/client/ui/routers/gotoChatInfo.ts deleted file mode 100644 index 20ab059..0000000 --- a/client/ui/routers/gotoChatInfo.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NavigateFunction } from "react-router" - -export default function gotoChatInfo(nav: NavigateFunction, id: string) { - nav('/info/chat?id=' + id) -} diff --git a/client/ui/routers/gotoUserInfo.ts b/client/ui/routers/gotoUserInfo.ts deleted file mode 100644 index 5e59dea..0000000 --- a/client/ui/routers/gotoUserInfo.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NavigateFunction } from "react-router" - -export default function gotoUserInfo(nav: NavigateFunction, id: string) { - nav('/info/user?id=' + id) -} diff --git a/client/ui/routers/useRouterDialogRef.ts b/client/ui/routers/useRouterDialogRef.ts deleted file mode 100644 index 2d61cd3..0000000 --- a/client/ui/routers/useRouterDialogRef.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Dialog } from "mdui" -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() - const nav = useNavigate() - - useAsyncEffect(async () => { - dialogRef.current!.addEventListener('closed', async () => { - nav(-1) - }) - await sleep(10) - dialogRef.current!.open = true - }, []) - - return dialogRef -}