diff --git a/shared/index.tsx b/shared/index.tsx index 70b6a930..3a73b5a5 100644 --- a/shared/index.tsx +++ b/shared/index.tsx @@ -60,6 +60,7 @@ export { createGroup, createGroupInviteCode, getGroupBasicInfo, + applyGroupInvite, } from './model/group'; export type { GroupPanel, GroupInfo, GroupBasicInfo } from './model/group'; export type { ChatMessage } from './model/message'; diff --git a/shared/model/group.ts b/shared/model/group.ts index ac674afd..9cb44530 100644 --- a/shared/model/group.ts +++ b/shared/model/group.ts @@ -106,3 +106,13 @@ export async function findGroupInviteByCode( return data; } + +/** + * 使用群组邀请 + * 即通过群组邀请加入群组 + */ +export async function applyGroupInvite(inviteCode: string): Promise { + await request.post('/api/group/invite/applyInvite', { + code: inviteCode, + }); +} diff --git a/web/src/App.tsx b/web/src/App.tsx index 80244fda..97232b01 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -4,6 +4,7 @@ import { TcProvider, useStorage } from 'tailchat-shared'; import clsx from 'clsx'; import { Loadable } from './components/Loadable'; import { ConfigProvider as AntdProvider } from 'antd'; +import { PortalHost } from './components/Portal'; const MainRoute = Loadable(() => import('./routes/Main').then((module) => module.MainRoute) @@ -34,7 +35,7 @@ const AppProvider: React.FC = React.memo((props) => { - {props.children} + {props.children} diff --git a/web/src/components/Modal.tsx b/web/src/components/Modal.tsx index f784eb6f..e778ff96 100644 --- a/web/src/components/Modal.tsx +++ b/web/src/components/Modal.tsx @@ -50,13 +50,17 @@ export const Modal: React.FC = React.memo((props) => { } = props; const [showing, setShowing] = useState(true); + const closeModal = useCallback(() => { + setShowing(false); + }, []); + const handleClose = useCallback(() => { if (maskClosable === false) { return; } - setShowing(false); - }, [maskClosable]); + closeModal(); + }, [maskClosable, closeModal]); const stopPropagation = useCallback((e: React.BaseSyntheticEvent) => { e.stopPropagation(); @@ -83,7 +87,7 @@ export const Modal: React.FC = React.memo((props) => { className="absolute left-0 right-0 top-0 bottom-0 bg-black bg-opacity-60 flex justify-center items-center" onClick={handleClose} > - + {/* Inner */}
+ props?: Pick ): number { const key = PortalAdd( = React.memo((props) => { } if (!value) { - return
群组信息加载失败
; + return
{t('群组信息加载失败')}
; } return ( diff --git a/web/src/routes/Invite/JoinBtn.tsx b/web/src/routes/Invite/JoinBtn.tsx index f08e9bd7..1a712029 100644 --- a/web/src/routes/Invite/JoinBtn.tsx +++ b/web/src/routes/Invite/JoinBtn.tsx @@ -1,11 +1,21 @@ +import { openModal } from '@/components/Modal'; import { getUserJWT } from '@/utils/jwt-helper'; import { Button } from 'antd'; -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { useHistory } from 'react-router'; -import { checkTokenValid, t, useAsync } from 'tailchat-shared'; +import { + applyGroupInvite, + checkTokenValid, + getCachedGroupInviteInfo, + showToasts, + t, + useAsync, + useAsyncRequest, +} from 'tailchat-shared'; +import { SuccessModal } from './SuccessModal'; interface Props { - onJoinGroup: () => void; + inviteCode: string; } export const JoinBtn: React.FC = React.memo((props) => { const history = useHistory(); @@ -14,6 +24,7 @@ export const JoinBtn: React.FC = React.memo((props) => { const isTokenValid = await checkTokenValid(token); return isTokenValid; }); + const [isJoined, setIsJoined] = useState(false); const handleRegister = useCallback(() => { history.push( @@ -21,16 +32,36 @@ export const JoinBtn: React.FC = React.memo((props) => { ); }, []); + const [{ loading: joinLoading }, handleJoinGroup] = + useAsyncRequest(async () => { + await applyGroupInvite(props.inviteCode); + + const invite = await getCachedGroupInviteInfo(props.inviteCode); + openModal(, { + maskClosable: false, + }); + setIsJoined(true); + }, [props.inviteCode]); + if (loading) { return null; } + if (isJoined) { + return ( + + ); + } + return isTokenValid ? ( diff --git a/web/src/routes/Invite/SuccessModal.tsx b/web/src/routes/Invite/SuccessModal.tsx new file mode 100644 index 00000000..6574214e --- /dev/null +++ b/web/src/routes/Invite/SuccessModal.tsx @@ -0,0 +1,28 @@ +import { ModalWrapper, useModalContext } from '@/components/Modal'; +import { Button } from 'antd'; +import React, { useCallback } from 'react'; +import { useHistory } from 'react-router'; +import { t } from 'tailchat-shared'; + +interface Props { + groupId: string; +} +export const SuccessModal: React.FC = React.memo((props) => { + const { closeModal } = useModalContext(); + const history = useHistory(); + const handleNav = useCallback(() => { + closeModal(); + history.push(`/main/group/${props.groupId}`); + }, [closeModal, props.groupId]); + + return ( + +
+ +
+
+ ); +}); +SuccessModal.displayName = 'SuccessModal'; diff --git a/web/src/routes/Invite/index.tsx b/web/src/routes/Invite/index.tsx index 7a18a005..4f4cf82e 100644 --- a/web/src/routes/Invite/index.tsx +++ b/web/src/routes/Invite/index.tsx @@ -11,11 +11,6 @@ import { JoinBtn } from './JoinBtn'; export const InviteRoute: React.FC = React.memo(() => { const { inviteCode } = useParams<{ inviteCode: string }>(); - const handleJoinGroup = useCallback(() => { - // TODO - console.log('TODO'); - }, []); - return (
{ - +
); diff --git a/web/src/routes/Main/Provider.tsx b/web/src/routes/Main/Provider.tsx index 6b76058c..cf15d83a 100644 --- a/web/src/routes/Main/Provider.tsx +++ b/web/src/routes/Main/Provider.tsx @@ -14,7 +14,6 @@ import _isNil from 'lodash/isNil'; import { getUserJWT } from '../../utils/jwt-helper'; import { useHistory } from 'react-router'; import { SidebarContextProvider } from './SidebarContext'; -import { PortalHost } from '@/components/Portal'; /** * 应用状态管理hooks @@ -80,9 +79,7 @@ export const MainProvider: React.FC = React.memo((props) => { return ( - - {props.children} - + {props.children} ); });