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

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

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

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

@ -22,15 +22,26 @@ export interface GroupInfo {
_id: string;
name: string;
avatar?: string;
creator: string;
owner: string;
members: GroupMember[];
panels: GroupPanel[];
}
/**
* 访
*/
export interface GroupBasicInfo {
name: string;
avatar?: string;
owner: string;
memberCount: GroupMember[];
}
export interface GroupInvite {
code: string;
groupId: string;
expiredAt?: Date;
creator: string;
expiredAt?: string;
}
/**
@ -50,6 +61,21 @@ export async function createGroup(
return data;
}
/**
*
*/
export async function getGroupBasicInfo(
groupId: string
): Promise<GroupBasicInfo | null> {
const { data } = await request.get('/api/group/getGroupBasicInfo', {
params: {
groupId,
},
});
return data;
}
/**
*
* 7
@ -64,3 +90,19 @@ export async function createGroupInviteCode(
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 {
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 React, { useCallback, useState } from 'react';
import { Button, Typography } from 'antd';
import React, { useState } from 'react';
import {
createGroupInviteCode,
useAsyncFn,
useAsyncRequest,
useGroupInfo,
} from 'tailchat-shared';
import { isValidStr } from '../../../../shared/utils/string-helper';
@ -25,8 +25,7 @@ export const GroupInvite: React.FC<GroupInviteProps> = React.memo((props) => {
// console.log('searchName', searchName);
// }, []);
const [{ loading }, handleCreateInviteLink] = useAsyncFn(async () => {
console.log('handleCreateInviteLink');
const [{ loading }, handleCreateInviteLink] = useAsyncRequest(async () => {
const invite = await createGroupInviteCode(groupId);
setInviteLink(`${location.origin}/invite/${invite.code}`);
}, [groupId]);
@ -63,7 +62,7 @@ export const GroupInvite: React.FC<GroupInviteProps> = React.memo((props) => {
>
{inviteLink}
</Typography.Title>
<p className="text-gray-500 text-sm">7</p>
<p className="text-gray-500 text-sm">7</p>
</div>
) : (
<Button

@ -1,10 +1,10 @@
import React from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import { LoginView } from './LoginView';
import bgImage from '../../../assets/images/bg.jpg';
import bgImage from '@assets/images/bg.jpg';
import clsx from 'clsx';
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';
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 React, { useCallback } from 'react';
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 (
<div className="h-full w-full">
<div className="w-96 h-72">
<div>{inviteCode}</div>
<div>xxx </div>
<div>[]</div>
<div>: </div>
<div
className="h-full w-full bg-gray-600 flex justify-center items-center bg-center bg-cover bg-no-repeat"
style={{ backgroundImage: `url(${bgImage})` }}
>
<div className="w-96 p-4 rounded-lg shadow-lg bg-black bg-opacity-60 text-center">
<InviteInfo inviteCode={inviteCode} />
<Divider />
{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>

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

Loading…
Cancel
Save