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:
Alex Yang
2023-08-29 05:07:05 -05:00
committed by GitHub
parent d0145c6f38
commit 2f6c4e3696
414 changed files with 19469 additions and 7591 deletions
@@ -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)',
});