milestone: publish alpha version (#637)

- document folder
- full-text search
- blob storage
- basic edgeless support

Co-authored-by: tzhangchi <terry.zhangchi@outlook.com>
Co-authored-by: QiShaoXuan <qishaoxuan777@gmail.com>
Co-authored-by: DiamondThree <diamond.shx@gmail.com>
Co-authored-by: MingLiang Wang <mingliangwang0o0@gmail.com>
Co-authored-by: JimmFly <yangjinfei001@gmail.com>
Co-authored-by: Yifeng Wang <doodlewind@toeverything.info>
Co-authored-by: Himself65 <himself65@outlook.com>
Co-authored-by: lawvs <18554747+lawvs@users.noreply.github.com>
Co-authored-by: Qi <474021214@qq.com>
This commit is contained in:
DarkSky
2022-12-30 21:40:15 +08:00
committed by GitHub
parent cc790dcbc2
commit 6c2c7dcd48
296 changed files with 16139 additions and 2072 deletions
@@ -0,0 +1,84 @@
import Modal from '@/ui/modal';
import Input from '@/ui/input';
import {
StyledModalHeader,
StyledTextContent,
StyledModalWrapper,
StyledInputContent,
StyledButtonContent,
StyledWorkspaceName,
} from './style';
import { useState } from 'react';
import { ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import { deleteWorkspace } from '@affine/data-services';
import { useRouter } from 'next/router';
import { useAppState } from '@/providers/app-state-provider';
interface WorkspaceDeleteProps {
open: boolean;
onClose: () => void;
workspaceName: string;
workspaceId: string;
nextWorkSpaceId: string;
}
export const WorkspaceDelete = ({
open,
onClose,
workspaceId,
workspaceName,
nextWorkSpaceId,
}: WorkspaceDeleteProps) => {
const [deleteStr, setDeleteStr] = useState<string>('');
const { refreshWorkspacesMeta } = useAppState();
const router = useRouter();
const handlerInputChange = (workspaceName: string) => {
setDeleteStr(workspaceName);
};
const handleDelete = async () => {
await deleteWorkspace({ id: workspaceId });
router.push(`/workspace/${nextWorkSpaceId}`);
refreshWorkspacesMeta();
onClose();
};
return (
<Modal open={open} onClose={onClose}>
<StyledModalWrapper>
<ModalCloseButton onClick={onClose} />
<StyledModalHeader>Delete Workspace</StyledModalHeader>
<StyledTextContent>
This action cannot be undone. This will permanently delete (
<StyledWorkspaceName>{workspaceName}</StyledWorkspaceName>) along with
all its content.
</StyledTextContent>
<StyledInputContent>
<Input
onChange={handlerInputChange}
placeholder="Please type “Delete” to confirm"
value={deleteStr}
></Input>
</StyledInputContent>
<StyledButtonContent>
<Button shape="circle" onClick={onClose}>
Cancel
</Button>
<Button
disabled={deleteStr.toLowerCase() !== 'delete'}
onClick={handleDelete}
type="danger"
shape="circle"
style={{ marginLeft: '24px' }}
>
Delete
</Button>
</StyledButtonContent>
</StyledModalWrapper>
</Modal>
);
};
export default WorkspaceDelete;
@@ -0,0 +1 @@
export * from './delete';
@@ -0,0 +1,75 @@
import { styled } from '@/styles';
export const StyledModalWrapper = styled('div')(({ theme }) => {
return {
position: 'relative',
padding: '0px',
width: '460px',
background: theme.colors.popoverBackground,
borderRadius: '12px',
};
});
export const StyledModalHeader = styled('div')(({ theme }) => {
return {
margin: '44px 0px 12px 0px',
width: '460px',
fontWeight: '600',
fontSize: '20px;',
textAlign: 'center',
color: theme.colors.popoverColor,
};
});
// export const StyledModalContent = styled('div')(({ theme }) => {});
export const StyledTextContent = styled('div')(() => {
return {
margin: 'auto',
width: '425px',
fontFamily: 'Avenir Next',
fontStyle: 'normal',
fontWeight: '400',
fontSize: '18px',
lineHeight: '26px',
textAlign: 'center',
};
});
export const StyledInputContent = styled('div')(() => {
return {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
margin: '40px 0 24px 0',
};
});
export const StyledButtonContent = styled('div')(() => {
return {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
margin: '0px 0 32px 0',
};
});
export const StyledWorkspaceName = styled('span')(() => {
return {
color: '#E8178A',
};
});
// export const StyledCancelButton = styled(Button)(({ theme }) => {
// return {
// width: '100px',
// justifyContent: 'center',
// };
// });
// export const StyledDeleteButton = styled(Button)(({ theme }) => {
// return {
// width: '100px',
// justifyContent: 'center',
// };
// });
@@ -0,0 +1,166 @@
import {
StyledDeleteButtonContainer,
StyledSettingAvatar,
StyledSettingAvatarContent,
StyledSettingInputContainer,
} from './style';
import { StyledSettingH2 } from '../style';
import { useState } from 'react';
import { Button } from '@/ui/button';
import Input from '@/ui/input';
import { uploadBlob, Workspace, WorkspaceType } from '@affine/data-services';
import { useAppState } from '@/providers/app-state-provider';
import { WorkspaceDetails } from '@/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent';
import { WorkspaceDelete } from './delete';
import { Workspace as StoreWorkspace } from '@blocksuite/store';
import { debounce } from '@/utils';
import { WorkspaceLeave } from './leave';
import { Upload } from '@/components/file-upload';
export const GeneralPage = ({
workspace,
owner,
}: {
workspace: Workspace;
owner: WorkspaceDetails[string]['owner'];
workspaces: Record<string, StoreWorkspace | null>;
}) => {
const {
user,
currentWorkspace,
workspacesMeta,
workspaces,
refreshWorkspacesMeta,
} = useAppState();
const [showDelete, setShowDelete] = useState<boolean>(false);
const [showLeave, setShowLeave] = useState<boolean>(false);
const [uploading, setUploading] = useState<boolean>(false);
const [workspaceName, setWorkspaceName] = useState<string>(
workspaces[workspace.id]?.meta.name ||
(workspace.type === WorkspaceType.Private && user ? user.name : '')
);
const debouncedRefreshWorkspacesMeta = debounce(() => {
refreshWorkspacesMeta();
}, 100);
const isOwner = user && owner.id === user.id;
const handleChangeWorkSpaceName = (newName: string) => {
setWorkspaceName(newName);
currentWorkspace?.meta.setName(newName);
workspaces[workspace.id]?.meta.setName(newName);
debouncedRefreshWorkspacesMeta();
};
const currentWorkspaceIndex = workspacesMeta.findIndex(
meta => meta.id === workspace.id
);
const nextWorkSpaceId =
currentWorkspaceIndex === workspacesMeta.length - 1
? workspacesMeta[currentWorkspaceIndex - 1]?.id
: workspacesMeta[currentWorkspaceIndex + 1]?.id;
const handleClickDelete = () => {
setShowDelete(true);
};
const handleCloseDelete = () => {
setShowDelete(false);
};
const handleClickLeave = () => {
setShowLeave(true);
};
const handleCloseLeave = () => {
setShowLeave(false);
};
const fileChange = async (file: File) => {
setUploading(true);
const blob = new Blob([file], { type: file.type });
const blobId = await uploadBlob({ blob }).finally(() => {
setUploading(false);
});
if (blobId) {
currentWorkspace?.meta.setAvatar(blobId);
workspaces[workspace.id]?.meta.setAvatar(blobId);
setUploading(false);
debouncedRefreshWorkspacesMeta();
}
};
return workspace ? (
<div>
<StyledSettingH2 marginTop={56}>Workspace Avatar</StyledSettingH2>
<StyledSettingAvatarContent>
<StyledSettingAvatar
alt="workspace avatar"
src={
workspaces[workspace.id]?.meta.avatar
? '/api/blob/' + workspaces[workspace.id]?.meta.avatar
: ''
}
>
{workspaces[workspace.id]?.meta.name[0]}
</StyledSettingAvatar>
<Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={fileChange}
>
<Button loading={uploading}>Upload</Button>
</Upload>
{/* TODO: add upload logic */}
{/* {isOwner ? (
<StyledAvatarUploadBtn shape="round">upload</StyledAvatarUploadBtn>
) : null} */}
{/* <Button shape="round">remove</Button> */}
</StyledSettingAvatarContent>
<StyledSettingH2 marginTop={36}>Workspace Name</StyledSettingH2>
<StyledSettingInputContainer>
<Input
width={327}
value={workspaceName}
placeholder="Workspace Name"
disabled={!isOwner}
maxLength={14}
minLength={1}
onChange={handleChangeWorkSpaceName}
></Input>
</StyledSettingInputContainer>
<StyledSettingH2 marginTop={36}>Workspace Owner</StyledSettingH2>
<StyledSettingInputContainer>
<Input
width={327}
disabled
value={owner.name}
placeholder="Workspace Owner"
></Input>
</StyledSettingInputContainer>
<StyledDeleteButtonContainer>
{isOwner ? (
<>
<Button type="danger" shape="circle" onClick={handleClickDelete}>
Delete Workspace
</Button>
<WorkspaceDelete
open={showDelete}
onClose={handleCloseDelete}
workspaceName={workspaceName}
workspaceId={workspace.id}
nextWorkSpaceId={nextWorkSpaceId}
/>
</>
) : (
<>
<Button type="danger" shape="circle" onClick={handleClickLeave}>
Leave Workspace
</Button>
<WorkspaceLeave
open={showLeave}
onClose={handleCloseLeave}
workspaceName={workspaceName}
workspaceId={workspace.id}
nextWorkSpaceId={nextWorkSpaceId}
/>
</>
)}
</StyledDeleteButtonContainer>
</div>
) : null;
};
@@ -0,0 +1 @@
export * from './general';
@@ -0,0 +1 @@
export * from './leave';
@@ -0,0 +1,64 @@
import Modal from '@/ui/modal';
import {
StyledModalHeader,
StyledTextContent,
StyledModalWrapper,
StyledButtonContent,
} from './style';
import { ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import { leaveWorkspace } from '@affine/data-services';
import { useRouter } from 'next/router';
import { useAppState } from '@/providers/app-state-provider';
interface WorkspaceDeleteProps {
open: boolean;
onClose: () => void;
workspaceName: string;
workspaceId: string;
nextWorkSpaceId: string;
}
export const WorkspaceLeave = ({
open,
onClose,
nextWorkSpaceId,
workspaceId,
}: WorkspaceDeleteProps) => {
const router = useRouter();
const { refreshWorkspacesMeta } = useAppState();
const handleLeave = async () => {
await leaveWorkspace({ id: workspaceId });
router.push(`/workspace/${nextWorkSpaceId}`);
refreshWorkspacesMeta();
onClose();
};
return (
<Modal open={open} onClose={onClose}>
<StyledModalWrapper>
<ModalCloseButton onClick={onClose} />
<StyledModalHeader>Leave Workspace</StyledModalHeader>
<StyledTextContent>
After you leave, you will not be able to access all the contents of
this workspace.
</StyledTextContent>
<StyledButtonContent>
<Button shape="circle" onClick={onClose}>
Cancel
</Button>
<Button
onClick={handleLeave}
type="danger"
shape="circle"
style={{ marginLeft: '24px' }}
>
Leave
</Button>
</StyledButtonContent>
</StyledModalWrapper>
</Modal>
);
};
export default WorkspaceLeave;
@@ -0,0 +1,46 @@
import { styled } from '@/styles';
export const StyledModalWrapper = styled('div')(({ theme }) => {
return {
position: 'relative',
padding: '0px',
width: '460px',
background: theme.colors.popoverBackground,
borderRadius: '12px',
};
});
export const StyledModalHeader = styled('div')(({ theme }) => {
return {
margin: '44px 0px 12px 0px',
width: '460px',
fontWeight: '600',
fontSize: '20px;',
textAlign: 'center',
color: theme.colors.popoverColor,
};
});
// export const StyledModalContent = styled('div')(({ theme }) => {});
export const StyledTextContent = styled('div')(() => {
return {
margin: 'auto',
width: '425px',
fontFamily: 'Avenir Next',
fontStyle: 'normal',
fontWeight: '400',
fontSize: '18px',
lineHeight: '26px',
textAlign: 'center',
};
});
export const StyledButtonContent = styled('div')(() => {
return {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
margin: '0px 0 32px 0',
};
});
@@ -0,0 +1,28 @@
import { styled } from '@/styles';
import MuiAvatar from '@mui/material/Avatar';
export const StyledSettingInputContainer = styled('div')(() => {
return {
marginTop: '12px',
};
});
export const StyledDeleteButtonContainer = styled('div')(() => {
return {
marginTop: '154px',
};
});
export const StyledSettingAvatarContent = styled('div')(() => {
return {
marginTop: '12px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
height: '72px',
};
});
export const StyledSettingAvatar = styled(MuiAvatar)(() => {
return { height: '72px', width: '72px', marginRight: '24px' };
});
@@ -0,0 +1 @@
export * from './workspace-setting';
@@ -0,0 +1,230 @@
import { styled } from '@/styles';
import { Button } from '@/ui/button';
import MuiAvatar from '@mui/material/Avatar';
export const StyledSettingContainer = styled('div')(({ theme }) => {
return {
position: 'relative',
display: 'flex',
padding: '0px',
width: '961px',
background: theme.colors.popoverBackground,
borderRadius: '12px',
overflow: 'hidden',
};
});
export const StyledSettingSidebar = styled('div')(({ theme }) => {
{
return {
width: '212px',
height: '620px',
background: theme.mode === 'dark' ? '#272727' : '#FBFBFC',
flexShrink: 0,
flexGrow: 0,
};
}
});
export const StyledSettingContent = styled('div')(() => {
return {
paddingLeft: '48px',
height: '620px',
};
});
export const StyledSetting = styled('div')(({ theme }) => {
{
return {
width: '236px',
height: '620px',
background: theme.mode === 'dark' ? '#272727' : '#FBFBFC',
};
}
});
export const StyledSettingSidebarHeader = styled('div')(() => {
{
return {
fontWeight: '500',
fontSize: '18px',
lineHeight: '26px',
textAlign: 'center',
marginTop: '37px',
};
}
});
export const StyledSettingTabContainer = styled('ul')(() => {
{
return {
display: 'flex',
flexDirection: 'column',
marginTop: '25px',
};
}
});
export const WorkspaceSettingTagItem = styled('li')<{ isActive?: boolean }>(
({ theme, isActive }) => {
{
return {
display: 'flex',
marginBottom: '12px',
padding: '0 24px',
height: '32px',
color: isActive ? theme.colors.primaryColor : theme.colors.textColor,
fontWeight: '400',
fontSize: '16px',
lineHeight: '32px',
cursor: 'pointer',
};
}
}
);
export const StyledSettingTagIconContainer = styled('div')(() => {
return {
display: 'flex',
alignItems: 'center',
marginRight: '14.64px',
width: '14.47px',
fontSize: '14.47px',
};
});
export const StyledSettingH2 = styled('h2')<{ marginTop?: number }>(
({ marginTop }) => {
return {
fontWeight: '500',
fontSize: '18px',
lineHeight: '26px',
marginTop: marginTop ? `${marginTop}px` : '0px',
};
}
);
export const StyledAvatarUploadBtn = styled(Button)(({ theme }) => {
return {
backgroundColor: theme.colors.hoverBackground,
color: theme.colors.primaryColor,
margin: '0 12px 0 24px',
};
});
export const StyledMemberTitleContainer = styled('div')(() => {
return {
display: 'flex',
marginTop: '60px',
fontWeight: '500',
};
});
export const StyledMemberAvatar = styled(MuiAvatar)(() => {
return { height: '40px', width: '40px' };
});
export const StyledMemberNameContainer = styled('div')(() => {
return {
display: 'flex',
alignItems: 'center',
width: '402px',
};
});
export const StyledMemberRoleContainer = styled('div')(() => {
return {
display: 'flex',
alignItems: 'center',
width: '222px',
};
});
export const StyledMemberListContainer = styled('ul')(() => {
return {
marginTop: '15px',
height: '432px',
overflowY: 'scroll',
};
});
export const StyledMemberListItem = styled('li')(() => {
return {
display: 'flex',
alignItems: 'center',
height: '72px',
};
});
export const StyledMemberInfo = styled('div')(() => {
return {
paddingLeft: '12px',
};
});
export const StyledMemberName = styled('div')(({ theme }) => {
return {
fontWeight: '400',
fontSize: '18px',
lineHeight: '16px',
color: theme.colors.textColor,
};
});
export const StyledMemberEmail = styled('div')(({ theme }) => {
return {
fontWeight: '400',
fontSize: '16px',
lineHeight: '22px',
color: theme.colors.iconColor,
};
});
export const StyledMemberButtonContainer = styled('div')(() => {
return {
marginTop: '14px',
};
});
export const StyledMoreVerticalButton = styled('button')(() => {
return {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '24px',
height: '24px',
cursor: 'pointer',
};
});
export const StyledPublishExplanation = styled('div')(() => {
return {
marginTop: '56px',
paddingRight: '48px',
fontWeight: '500',
fontSize: '18px',
lineHeight: '26px',
};
});
export const StyledPublishCopyContainer = styled('div')(() => {
return {
marginTop: '12px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
height: '38px',
};
});
export const StyledCopyButtonContainer = styled('div')(() => {
return {
marginLeft: '12px',
};
});
export const StyledPublishContent = styled('div')(() => {
return {
height: '494px',
};
});
@@ -0,0 +1,361 @@
import Modal, { ModalCloseButton } from '@/ui/modal';
import {
StyledCopyButtonContainer,
StyledMemberAvatar,
StyledMemberButtonContainer,
StyledMemberEmail,
StyledMemberInfo,
StyledMemberListContainer,
StyledMemberListItem,
StyledMemberName,
StyledMemberNameContainer,
StyledMemberRoleContainer,
StyledMemberTitleContainer,
StyledMoreVerticalButton,
StyledPublishContent,
StyledPublishCopyContainer,
StyledPublishExplanation,
StyledSettingContainer,
StyledSettingContent,
StyledSettingH2,
StyledSettingSidebar,
StyledSettingSidebarHeader,
StyledSettingTabContainer,
StyledSettingTagIconContainer,
WorkspaceSettingTagItem,
} from './style';
import {
EditIcon,
UsersIcon,
PublishIcon,
MoreVerticalIcon,
EmailIcon,
TrashIcon,
} from '@blocksuite/icons';
import { useCallback, useEffect, useState } from 'react';
import { Button, IconButton } from '@/ui/button';
import Input from '@/ui/input';
import { InviteMembers } from '../invite-members/index';
import {
getWorkspaceMembers,
Workspace,
Member,
removeMember,
updateWorkspace,
} from '@affine/data-services';
import { Avatar } from '@mui/material';
import { Menu, MenuItem } from '@/ui/menu';
import { toast } from '@/ui/toast';
import { Empty } from '@/ui/empty';
import { useAppState } from '@/providers/app-state-provider';
import { WorkspaceDetails } from '../workspace-slider-bar/WorkspaceSelector/SelectorPopperContent';
import { GeneralPage } from './general';
enum ActiveTab {
'general' = 'general',
'members' = 'members',
'publish' = 'publish',
}
type SettingTabProps = {
activeTab: ActiveTab;
onTabChange?: (tab: ActiveTab) => void;
};
type WorkspaceSettingProps = {
isShow: boolean;
onClose?: () => void;
workspace: Workspace;
owner: WorkspaceDetails[string]['owner'];
};
const WorkspaceSettingTab = ({ activeTab, onTabChange }: SettingTabProps) => {
return (
<StyledSettingTabContainer>
<WorkspaceSettingTagItem
isActive={activeTab === ActiveTab.general}
onClick={() => {
onTabChange && onTabChange(ActiveTab.general);
}}
>
<StyledSettingTagIconContainer>
<EditIcon />
</StyledSettingTagIconContainer>
General
</WorkspaceSettingTagItem>
<WorkspaceSettingTagItem
isActive={activeTab === ActiveTab.members}
onClick={() => {
onTabChange && onTabChange(ActiveTab.members);
}}
>
<StyledSettingTagIconContainer>
<UsersIcon />
</StyledSettingTagIconContainer>
Members
</WorkspaceSettingTagItem>
<WorkspaceSettingTagItem
isActive={activeTab === ActiveTab.publish}
onClick={() => {
onTabChange && onTabChange(ActiveTab.publish);
}}
>
<StyledSettingTagIconContainer>
<PublishIcon />
</StyledSettingTagIconContainer>
Publish
</WorkspaceSettingTagItem>
</StyledSettingTabContainer>
);
};
export const WorkspaceSetting = ({
isShow,
onClose,
workspace,
owner,
}: WorkspaceSettingProps) => {
const { user, workspaces } = useAppState();
const [activeTab, setActiveTab] = useState<ActiveTab>(ActiveTab.general);
const handleTabChange = (tab: ActiveTab) => {
setActiveTab(tab);
};
const handleClickClose = () => {
onClose && onClose();
};
const isOwner = user && owner.id === user.id;
useEffect(() => {
// reset tab when modal is closed
if (!isShow) {
setActiveTab(ActiveTab.general);
}
}, [isShow]);
return (
<Modal open={isShow}>
<StyledSettingContainer>
<ModalCloseButton onClick={handleClickClose} />
{isOwner ? (
<StyledSettingSidebar>
<StyledSettingSidebarHeader>
Workspace Settings
</StyledSettingSidebarHeader>
<WorkspaceSettingTab
activeTab={activeTab}
onTabChange={handleTabChange}
/>
</StyledSettingSidebar>
) : null}
<StyledSettingContent>
{activeTab === ActiveTab.general && (
<GeneralPage
workspace={workspace}
owner={owner}
workspaces={workspaces}
/>
)}
{activeTab === ActiveTab.members && workspace && (
<MembersPage workspace={workspace} />
)}
{activeTab === ActiveTab.publish && (
<PublishPage workspace={workspace} />
)}
</StyledSettingContent>
</StyledSettingContainer>
</Modal>
);
};
const MembersPage = ({ workspace }: { workspace: Workspace }) => {
const [isInviteModalShow, setIsInviteModalShow] = useState(false);
const [members, setMembers] = useState<Member[]>([]);
const refreshMembers = useCallback(() => {
getWorkspaceMembers({
id: workspace.id,
})
.then(data => {
setMembers(data);
})
.catch(err => {
console.log(err);
});
}, [workspace.id]);
useEffect(() => {
refreshMembers();
}, [refreshMembers]);
return (
<div>
<StyledMemberTitleContainer>
<StyledMemberNameContainer>
Users({members.length})
</StyledMemberNameContainer>
<StyledMemberRoleContainer>Access level</StyledMemberRoleContainer>
</StyledMemberTitleContainer>
<StyledMemberListContainer>
{members.length === 0 && (
<Empty width={648} sx={{ marginTop: '60px' }} height={300}></Empty>
)}
{members.length ? (
members.map(member => {
return (
<StyledMemberListItem key={member.id}>
<StyledMemberNameContainer>
{member.user.type === 'Registered' ? (
<Avatar src={member.user.avatar_url}></Avatar>
) : (
<StyledMemberAvatar alt="member avatar">
<EmailIcon></EmailIcon>
</StyledMemberAvatar>
)}
<StyledMemberInfo>
{member.user.type === 'Registered' ? (
<StyledMemberName>{member.user.name}</StyledMemberName>
) : (
<></>
)}
<StyledMemberEmail>{member.user.email}</StyledMemberEmail>
</StyledMemberInfo>
</StyledMemberNameContainer>
<StyledMemberRoleContainer>
{member.accepted
? member.type !== 99
? 'Member'
: 'Workspace Owner'
: 'Pending'}
</StyledMemberRoleContainer>
<StyledMoreVerticalButton>
<Menu
content={
<>
<MenuItem
onClick={() => {
// confirm({
// title: 'Delete Member?',
// content: `will delete member`,
// confirmText: 'Delete',
// confirmType: 'danger',
// }).then(confirm => {
removeMember({
permissionId: member.id,
}).then(() => {
// console.log('data: ', data);
toast('Moved to Trash');
refreshMembers();
});
// });
}}
icon={<TrashIcon />}
>
Delete
</MenuItem>
</>
}
placement="bottom-end"
disablePortal={true}
>
<IconButton>
<MoreVerticalIcon />
</IconButton>
</Menu>
</StyledMoreVerticalButton>
</StyledMemberListItem>
);
})
) : (
<></>
)}
</StyledMemberListContainer>
<StyledMemberButtonContainer>
<Button
onClick={() => {
setIsInviteModalShow(true);
}}
type="primary"
shape="circle"
>
Invite Members
</Button>
<InviteMembers
onClose={() => {
setIsInviteModalShow(false);
}}
onInviteSuccess={() => {
refreshMembers();
}}
workspaceId={workspace.id}
open={isInviteModalShow}
></InviteMembers>
</StyledMemberButtonContainer>
</div>
);
};
const PublishPage = ({ workspace }: { workspace: Workspace }) => {
const shareUrl = window.location.host + '/workspace/' + workspace.id;
const [publicStatus, setPublicStatus] = useState<boolean | null>(
workspace.public
);
const togglePublic = (flag: boolean) => {
updateWorkspace({
id: workspace.id,
public: flag,
}).then(data => {
setPublicStatus(data?.public);
toast('Updated Public Status Success');
});
};
const copyUrl = () => {
navigator.clipboard.writeText(shareUrl);
toast('Copied url to clipboard');
};
return (
<div>
<StyledPublishContent>
{publicStatus ? (
<>
<StyledPublishExplanation>
The current workspace has been published to the web, everyone can
view the contents of this workspace through the link.
</StyledPublishExplanation>
<StyledSettingH2 marginTop={48}>Share with link</StyledSettingH2>
<StyledPublishCopyContainer>
<Input width={500} value={shareUrl} disabled={true}></Input>
<StyledCopyButtonContainer>
<Button onClick={copyUrl} type="primary" shape="circle">
Copy Link
</Button>
</StyledCopyButtonContainer>
</StyledPublishCopyContainer>
</>
) : (
<StyledPublishExplanation>
After publishing to the web, everyone can view the content of this
workspace through the link.
</StyledPublishExplanation>
)}
</StyledPublishContent>
{!publicStatus ? (
<Button
onClick={() => {
togglePublic(true);
}}
type="primary"
shape="circle"
>
Publish to web
</Button>
) : (
<Button
onClick={() => {
togglePublic(false);
}}
type="primary"
shape="circle"
>
Stop publishing
</Button>
)}
</div>
);
};