refactor: 邀请与创建邀请界面的UI设计

pull/13/head
moonrailgun 4 years ago
parent 7951e095c5
commit 764554e2f7

@ -1,4 +1,5 @@
import { ChatConverseInfo, fetchConverseInfo } from '../model/converse'; import { ChatConverseInfo, fetchConverseInfo } from '../model/converse';
import { findGroupInviteByCode, GroupInvite } from '../model/group';
import { fetchUserInfo, UserBaseInfo } from '../model/user'; import { fetchUserInfo, UserBaseInfo } from '../model/user';
import { queryClient } from './index'; import { queryClient } from './index';
@ -16,6 +17,7 @@ export async function getCachedUserInfo(
staleTime: refetch ? 0 : 2 * 60 * 60 * 1000, // 缓存2小时 staleTime: refetch ? 0 : 2 * 60 * 60 * 1000, // 缓存2小时
} }
); );
return data; return data;
} }
@ -28,5 +30,19 @@ export async function getCachedConverseInfo(
const data = await queryClient.fetchQuery(['converse', converseId], () => const data = await queryClient.fetchQuery(['converse', converseId], () =>
fetchConverseInfo(converseId) fetchConverseInfo(converseId)
); );
return data;
}
/**
*
*/
export async function getCachedGroupInviteInfo(
inviteCode: string
): Promise<GroupInvite | null> {
const data = await queryClient.fetchQuery(['groupInvite', inviteCode], () =>
findGroupInviteByCode(inviteCode)
);
return data; return data;
} }

@ -5,7 +5,7 @@ export { createSocket } from './api/socket';
export type { AppSocket } from './api/socket'; export type { AppSocket } from './api/socket';
// cache // cache
export { getCachedUserInfo } from './cache/cache'; export { getCachedUserInfo, getCachedGroupInviteInfo } from './cache/cache';
export { useCachedUserInfo } from './cache/useCache'; export { useCachedUserInfo } from './cache/useCache';
// components // components
@ -59,8 +59,9 @@ export {
GroupPanelType, GroupPanelType,
createGroup, createGroup,
createGroupInviteCode, createGroupInviteCode,
getGroupBasicInfo,
} from './model/group'; } from './model/group';
export type { GroupPanel, GroupInfo } from './model/group'; export type { GroupPanel, GroupInfo, GroupBasicInfo } from './model/group';
export type { ChatMessage } from './model/message'; export type { ChatMessage } from './model/message';
export type { UserBaseInfo, UserLoginInfo } from './model/user'; export type { UserBaseInfo, UserLoginInfo } from './model/user';
export { export {
@ -87,6 +88,8 @@ export {
shouldShowMessageTime, shouldShowMessageTime,
getMessageTimeDiff, getMessageTimeDiff,
formatShortTime, formatShortTime,
datetimeToNow,
datetimeFromNow,
} from './utils/date-helper'; } from './utils/date-helper';
export { isBrowser, isNavigator, isDevelopment } from './utils/environment'; export { isBrowser, isNavigator, isDevelopment } from './utils/environment';
export { getTextColorHex } from './utils/string-helper'; export { getTextColorHex } from './utils/string-helper';

@ -22,15 +22,26 @@ export interface GroupInfo {
_id: string; _id: string;
name: string; name: string;
avatar?: string; avatar?: string;
creator: string; owner: string;
members: GroupMember[]; members: GroupMember[];
panels: GroupPanel[]; panels: GroupPanel[];
} }
/**
* 访
*/
export interface GroupBasicInfo {
name: string;
avatar?: string;
owner: string;
memberCount: GroupMember[];
}
export interface GroupInvite { export interface GroupInvite {
code: string; code: string;
groupId: string; groupId: string;
expiredAt?: Date; creator: string;
expiredAt?: string;
} }
/** /**
@ -50,6 +61,21 @@ export async function createGroup(
return data; return data;
} }
/**
*
*/
export async function getGroupBasicInfo(
groupId: string
): Promise<GroupBasicInfo | null> {
const { data } = await request.get('/api/group/getGroupBasicInfo', {
params: {
groupId,
},
});
return data;
}
/** /**
* *
* 7 * 7
@ -64,3 +90,19 @@ export async function createGroupInviteCode(
return data; return data;
} }
/**
*
* @param inviteCode
*/
export async function findGroupInviteByCode(
inviteCode: string
): Promise<GroupInvite | null> {
const { data } = await request.get('/api/group/invite/findInviteByCode', {
params: {
code: inviteCode,
},
});
return data;
}

@ -43,3 +43,23 @@ export function shouldShowMessageTime(date1: Date, date2: Date): boolean {
export function formatShortTime(date: dayjs.ConfigType): string { export function formatShortTime(date: dayjs.ConfigType): string {
return dayjs(date).format('HH:mm'); return dayjs(date).format('HH:mm');
} }
/**
*
* @example
* dayjs('1999-01-01').toNow() // 22 年后
*/
export function datetimeToNow(input: dayjs.ConfigType): string {
const date = dayjs(input);
return date.toNow();
}
/**
*
* @example
* dayjs('1999-01-01').toNow() // 22 年前
*/
export function datetimeFromNow(input: dayjs.ConfigType): string {
const date = dayjs(input);
return date.fromNow();
}

@ -0,0 +1,13 @@
import React from 'react';
import { useCachedUserInfo } from 'tailchat-shared';
export const UserName: React.FC<{
userId: string;
className?: string;
}> = React.memo((props) => {
const { userId, className } = props;
const cachedUserInfo = useCachedUserInfo(userId);
return <span className={className}>{cachedUserInfo.nickname}</span>;
});
UserName.displayName = 'UserName';

@ -1,8 +1,8 @@
import { Button, Divider, Input, Typography } from 'antd'; import { Button, Typography } from 'antd';
import React, { useCallback, useState } from 'react'; import React, { useState } from 'react';
import { import {
createGroupInviteCode, createGroupInviteCode,
useAsyncFn, useAsyncRequest,
useGroupInfo, useGroupInfo,
} from 'tailchat-shared'; } from 'tailchat-shared';
import { isValidStr } from '../../../../shared/utils/string-helper'; import { isValidStr } from '../../../../shared/utils/string-helper';
@ -25,8 +25,7 @@ export const GroupInvite: React.FC<GroupInviteProps> = React.memo((props) => {
// console.log('searchName', searchName); // console.log('searchName', searchName);
// }, []); // }, []);
const [{ loading }, handleCreateInviteLink] = useAsyncFn(async () => { const [{ loading }, handleCreateInviteLink] = useAsyncRequest(async () => {
console.log('handleCreateInviteLink');
const invite = await createGroupInviteCode(groupId); const invite = await createGroupInviteCode(groupId);
setInviteLink(`${location.origin}/invite/${invite.code}`); setInviteLink(`${location.origin}/invite/${invite.code}`);
}, [groupId]); }, [groupId]);
@ -63,7 +62,7 @@ export const GroupInvite: React.FC<GroupInviteProps> = React.memo((props) => {
> >
{inviteLink} {inviteLink}
</Typography.Title> </Typography.Title>
<p className="text-gray-500 text-sm">7</p> <p className="text-gray-500 text-sm">7</p>
</div> </div>
) : ( ) : (
<Button <Button

@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import { Redirect, Route, Switch } from 'react-router-dom'; import { Redirect, Route, Switch } from 'react-router-dom';
import { LoginView } from './LoginView'; import { LoginView } from './LoginView';
import bgImage from '../../../assets/images/bg.jpg'; import bgImage from '@assets/images/bg.jpg';
import clsx from 'clsx'; import clsx from 'clsx';
import styles from './index.module.less'; import styles from './index.module.less';
import loginPatternUrl from '../../../assets/images/login-pattern.svg'; import loginPatternUrl from '@assets/images/login-pattern.svg';
import { RegisterView } from './RegisterView'; import { RegisterView } from './RegisterView';
export const EntryRoute = React.memo(() => { export const EntryRoute = React.memo(() => {

@ -0,0 +1,78 @@
import { Avatar } from '@/components/Avatar';
import { LoadingSpinner } from '@/components/LoadingSpinner';
import { UserName } from '@/components/UserName';
import React from 'react';
import {
datetimeFromNow,
getCachedGroupInviteInfo,
getGroupBasicInfo,
showErrorToasts,
t,
useAsync,
} from 'tailchat-shared';
interface Props {
inviteCode: string;
}
export const InviteInfo: React.FC<Props> = React.memo((props) => {
const { inviteCode } = props;
const { loading, value } = useAsync(async () => {
try {
const invite = await getCachedGroupInviteInfo(inviteCode);
if (invite === null) {
throw new Error(t('找不到邀请信息'));
}
const groupBasicInfo = await getGroupBasicInfo(invite.groupId);
if (groupBasicInfo === null) {
throw new Error(t('找不到群组信息'));
}
return {
group: groupBasicInfo,
creator: invite.creator,
expired: invite.expiredAt,
};
} catch (err) {
showErrorToasts(err);
}
}, [inviteCode]);
if (loading) {
return <LoadingSpinner />;
}
if (!value) {
return <div></div>;
}
return (
<div>
<div>
<Avatar
className="mb-4"
size={64}
src={value.group.avatar}
name={value.group.name}
/>
</div>
<div>
<UserName className="font-bold" userId={value.creator} />{' '}
{t('邀请您加入群组')}
</div>
<div className="text-xl my-2 font-bold">{value.group.name}</div>
<div>
{t('当前成员数')}: {value.group.memberCount}
</div>
{value.expired && (
<div>
{' '}
<span className="font-bold">{datetimeFromNow(value.expired)}</span>{' '}
</div>
)}
</div>
);
});
InviteInfo.displayName = 'InviteInfo';

@ -1,6 +1,9 @@
import { Button, Divider } from 'antd'; import { Button, Divider } from 'antd';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useHistory, useParams } from 'react-router'; import { useHistory, useParams } from 'react-router';
import { InviteInfo } from './InviteInfo';
import bgImage from '@assets/images/bg.jpg';
import { t } from 'tailchat-shared';
/** /**
* *
@ -22,19 +25,33 @@ export const InviteRoute: React.FC = React.memo(() => {
}, []); }, []);
return ( return (
<div className="h-full w-full"> <div
<div className="w-96 h-72"> className="h-full w-full bg-gray-600 flex justify-center items-center bg-center bg-cover bg-no-repeat"
<div>{inviteCode}</div> style={{ backgroundImage: `url(${bgImage})` }}
<div>xxx </div> >
<div>[]</div> <div className="w-96 p-4 rounded-lg shadow-lg bg-black bg-opacity-60 text-center">
<div>: </div> <InviteInfo inviteCode={inviteCode} />
<Divider /> <Divider />
{isLogin ? ( {isLogin ? (
<Button onClick={handleJoinGroup}></Button> <Button
block={true}
type="primary"
size="large"
onClick={handleJoinGroup}
>
{t('加入群组')}
</Button>
) : ( ) : (
<Button onClick={handleRegister}></Button> <Button
block={true}
type="primary"
size="large"
onClick={handleRegister}
>
{t('立即注册')}
</Button>
)} )}
</div> </div>
</div> </div>

@ -3,7 +3,8 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"],
"@assets/*": ["./assets/*"]
} }
} }
} }

Loading…
Cancel
Save