mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 00:56:26 +08:00
feat!: affine cloud support (#3813)
Co-authored-by: Hongtao Lye <codert.sn@gmail.com> Co-authored-by: liuyi <forehalo@gmail.com> Co-authored-by: LongYinan <lynweklm@gmail.com> Co-authored-by: X1a0t <405028157@qq.com> Co-authored-by: JimmFly <yangjinfei001@gmail.com> Co-authored-by: Peng Xiao <pengxiao@outlook.com> Co-authored-by: xiaodong zuo <53252747+zuoxiaodong0815@users.noreply.github.com> Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com> Co-authored-by: Qi <474021214@qq.com> Co-authored-by: danielchim <kahungchim@gmail.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { AuthPageContainer } from '@affine/component/auth-components';
|
||||
import { type GetInviteInfoQuery } from '@affine/graphql';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { Avatar } from '@toeverything/components/avatar';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
|
||||
import { FlexWrapper } from '../../ui/layout';
|
||||
import * as styles from './styles.css';
|
||||
export const AcceptInvitePage = ({
|
||||
onOpenWorkspace,
|
||||
inviteInfo,
|
||||
}: {
|
||||
onOpenWorkspace: () => void;
|
||||
inviteInfo: GetInviteInfoQuery['getInviteInfo'];
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
return (
|
||||
<AuthPageContainer
|
||||
title={t['Successfully joined!']()}
|
||||
subtitle={
|
||||
<FlexWrapper alignItems="center">
|
||||
<Avatar
|
||||
url={inviteInfo.user.avatarUrl || ''}
|
||||
name={inviteInfo.user.name}
|
||||
size={20}
|
||||
// FIXME: fix it in @toeverything/components/avatar
|
||||
imageProps={{
|
||||
style: {
|
||||
objectFit: 'cover',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<span className={styles.inviteName}>{inviteInfo.user.name}</span>
|
||||
{t['invited you to join']()}
|
||||
<Avatar
|
||||
url={`data:image/png;base64,${inviteInfo.workspace.avatar}`}
|
||||
name={inviteInfo.workspace.name}
|
||||
size={20}
|
||||
style={{ marginLeft: 4 }}
|
||||
colorfulFallback={true}
|
||||
/>
|
||||
<span className={styles.inviteName}>{inviteInfo.workspace.name}</span>
|
||||
</FlexWrapper>
|
||||
}
|
||||
>
|
||||
<Button type="primary" size="large" onClick={onOpenWorkspace}>
|
||||
{t['Visit Workspace']()}
|
||||
</Button>
|
||||
</AuthPageContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './accept-invite-page';
|
||||
export * from './invite-modal';
|
||||
@@ -0,0 +1,147 @@
|
||||
import { Permission } from '@affine/graphql';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { Modal, ModalCloseButton, ModalWrapper } from '../../ui/modal';
|
||||
import { AuthInput } from '..//auth-components';
|
||||
import { emailRegex } from '..//auth-components/utils';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
export interface InviteModalProps {
|
||||
open: boolean;
|
||||
setOpen: (value: boolean) => void;
|
||||
onConfirm: (params: { email: string; permission: Permission }) => void;
|
||||
isMutating: boolean;
|
||||
}
|
||||
|
||||
const PermissionMenu = ({
|
||||
currentPermission,
|
||||
onChange,
|
||||
}: {
|
||||
currentPermission: Permission;
|
||||
onChange: (permission: Permission) => void;
|
||||
}) => {
|
||||
console.log('currentPermission', currentPermission);
|
||||
console.log('onChange', onChange);
|
||||
|
||||
return null;
|
||||
// return (
|
||||
// <Menu
|
||||
// trigger="click"
|
||||
// content={
|
||||
// <>
|
||||
// {Object.entries(Permission).map(([permission]) => {
|
||||
// return (
|
||||
// <MenuItem
|
||||
// key={permission}
|
||||
// onClick={() => {
|
||||
// onChange(permission as Permission);
|
||||
// }}
|
||||
// >
|
||||
// {permission}
|
||||
// </MenuItem>
|
||||
// );
|
||||
// })}
|
||||
// </>
|
||||
// }
|
||||
// >
|
||||
// <MenuTrigger
|
||||
// type="plain"
|
||||
// style={{
|
||||
// marginRight: -10,
|
||||
// height: '100%',
|
||||
// }}
|
||||
// >
|
||||
// {currentPermission}
|
||||
// </MenuTrigger>
|
||||
// </Menu>
|
||||
// );
|
||||
};
|
||||
|
||||
export const InviteModal = ({
|
||||
open,
|
||||
setOpen,
|
||||
onConfirm,
|
||||
isMutating,
|
||||
}: InviteModalProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const [inviteEmail, setInviteEmail] = useState('');
|
||||
const [permission, setPermission] = useState(Permission.Write);
|
||||
const [isValidEmail, setIsValidEmail] = useState(true);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
setOpen(false);
|
||||
}, [setOpen]);
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
if (!emailRegex.test(inviteEmail)) {
|
||||
setIsValidEmail(false);
|
||||
return;
|
||||
}
|
||||
setIsValidEmail(true);
|
||||
|
||||
onConfirm({
|
||||
email: inviteEmail,
|
||||
permission,
|
||||
});
|
||||
}, [inviteEmail, onConfirm, permission]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setInviteEmail('');
|
||||
setIsValidEmail(true);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
wrapperPosition={['center', 'center']}
|
||||
data-testid="invite-modal"
|
||||
open={open}
|
||||
>
|
||||
<ModalWrapper
|
||||
width={480}
|
||||
height={254}
|
||||
style={{
|
||||
padding: '20px 26px',
|
||||
}}
|
||||
>
|
||||
<ModalCloseButton top={20} right={20} onClick={handleCancel} />
|
||||
|
||||
<div className={styles.inviteModalTitle}>{t['Invite Members']()}</div>
|
||||
<div className={styles.inviteModalContent}>
|
||||
{t['Invite Members Message']()}
|
||||
{/*TODO: check email & add placeholder*/}
|
||||
<AuthInput
|
||||
disabled={isMutating}
|
||||
placeholder="email@example.com"
|
||||
value={inviteEmail}
|
||||
onChange={setInviteEmail}
|
||||
error={!isValidEmail}
|
||||
errorHint={
|
||||
isValidEmail ? '' : t['com.affine.auth.sign.email.error']()
|
||||
}
|
||||
onEnter={handleConfirm}
|
||||
style={{ marginTop: 20 }}
|
||||
size="large"
|
||||
endFix={
|
||||
<PermissionMenu
|
||||
currentPermission={permission}
|
||||
onChange={setPermission}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.inviteModalButtonContainer}>
|
||||
<Button style={{ marginRight: 20 }} onClick={handleCancel}>
|
||||
{t['Cancel']()}
|
||||
</Button>
|
||||
<Button type="primary" onClick={handleConfirm} loading={isMutating}>
|
||||
{t['Invite']()}
|
||||
</Button>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const inviteModalTitle = style({
|
||||
fontWeight: '600',
|
||||
fontSize: 'var(--affine-font-h-6)',
|
||||
marginBottom: '20px',
|
||||
});
|
||||
|
||||
export const inviteModalContent = style({
|
||||
marginBottom: '10px',
|
||||
});
|
||||
|
||||
export const inviteModalButtonContainer = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
// marginTop: 10,
|
||||
});
|
||||
|
||||
export const inviteName = style({
|
||||
marginLeft: '4px',
|
||||
marginRight: '10px',
|
||||
color: 'var(--affine-black)',
|
||||
});
|
||||
Reference in New Issue
Block a user