diff --git a/packages/app/src/components/file-upload/index.tsx b/packages/app/src/components/file-upload/index.tsx index dccd37bd87..7d49a6526e 100644 --- a/packages/app/src/components/file-upload/index.tsx +++ b/packages/app/src/components/file-upload/index.tsx @@ -3,7 +3,7 @@ import { FC, useRef, ChangeEvent, ReactElement } from 'react'; import { styled } from '@/styles'; interface Props { uploadType?: string; - view?: ReactElement; + children?: ReactElement; accept?: string; fileChange: (file: File) => void; } @@ -28,7 +28,7 @@ export const Upload: FC = props => { }; return ( - {props.view ?? } + {props.children ?? } (false); const [showLeave, setShowLeave] = useState(false); + const [uploading, setUploading] = useState(false); const [workspaceName, setWorkspaceName] = useState( workspaces[workspace.id]?.meta.name || (workspace.type === WorkspaceType.Private && user ? user.name : '') @@ -75,11 +76,17 @@ export const GeneralPage = ({ }; const fileChange = async (file: File) => { + setUploading(true); const blob = new Blob([file], { type: file.type }); - const blobId = await uploadBlob({ blob }); - currentWorkspace?.meta.setAvatar(blobId); - workspaces[workspace.id]?.meta.setAvatar(blobId); - debouncedRefreshWorkspacesMeta(); + 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 ? ( @@ -99,7 +106,9 @@ export const GeneralPage = ({ + > + + {/* TODO: add upload logic */} {/* {isOwner ? ( upload diff --git a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/CreateWorkspaceItem/workspace-create/workspace-create.tsx b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/CreateWorkspaceItem/workspace-create/workspace-create.tsx index 61c03ae1b0..d5ecb67b89 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/CreateWorkspaceItem/workspace-create/workspace-create.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/CreateWorkspaceItem/workspace-create/workspace-create.tsx @@ -29,7 +29,7 @@ const DefaultHeadImgColors = [ export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => { const [workspaceName, setWorkspaceId] = useState(''); - const [canCreate, setCanCreate] = useState(false); + const [creating, setCreating] = useState(false); const { refreshWorkspacesMeta } = useAppState(); const handlerInputChange = (workspaceName: string) => { setWorkspaceId(workspaceName); @@ -64,22 +64,26 @@ export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => { }); }; const handleCreateWorkspace = async () => { - setCanCreate(true); - const blobId = await createDefaultHeadImg(workspaceName); - createWorkspace({ name: workspaceName, avatar: blobId }) - .then(async data => { - await refreshWorkspacesMeta(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - router.push(`/workspace/${data.id}`); - onClose(); - }) - .catch(err => { - console.log(err, 'err'); - }) - .finally(() => { - setCanCreate(false); - }); + setCreating(true); + const blobId = await createDefaultHeadImg(workspaceName).catch(() => { + setCreating(false); + }); + if (blobId) { + createWorkspace({ name: workspaceName, avatar: blobId }) + .then(async data => { + await refreshWorkspacesMeta(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + router.push(`/workspace/${data.id}`); + onClose(); + }) + .catch(err => { + console.log(err, 'err'); + }) + .finally(() => { + setCreating(false); + }); + } }; return ( @@ -99,8 +103,9 @@ export const WorkspaceCreate = ({ open, onClose }: WorkspaceCreateProps) => { Create diff --git a/packages/app/src/pages/affine.tsx b/packages/app/src/pages/affine.tsx index 76eaa9e1f4..fa994e85cc 100644 --- a/packages/app/src/pages/affine.tsx +++ b/packages/app/src/pages/affine.tsx @@ -57,6 +57,8 @@ const Affine = () => { + + ); }; diff --git a/packages/app/src/ui/button/button.tsx b/packages/app/src/ui/button/button.tsx index 7e57202717..def9211503 100644 --- a/packages/app/src/ui/button/button.tsx +++ b/packages/app/src/ui/button/button.tsx @@ -3,6 +3,7 @@ import { StyledButton } from './styles'; import { ButtonProps } from './interface'; import { getSize } from './utils'; +import { Loading } from './loading'; export const Button = forwardRef( ( @@ -17,6 +18,7 @@ export const Button = forwardRef( type = 'default', children, bold = false, + loading = false, ...props }, ref @@ -38,12 +40,16 @@ export const Button = forwardRef( bold={bold} {...props} > - {icon && + {loading ? ( + + ) : ( + icon && cloneElement(Children.only(icon), { width: iconSize, height: iconSize, className: `affine-button-icon ${icon.props.className ?? ''}`, - })} + }) + )} {children && {children}} ); diff --git a/packages/app/src/ui/button/interface.ts b/packages/app/src/ui/button/interface.ts index 0086a35f87..6b5a55996b 100644 --- a/packages/app/src/ui/button/interface.ts +++ b/packages/app/src/ui/button/interface.ts @@ -20,4 +20,5 @@ export type ButtonProps = PropsWithChildren & shape?: 'default' | 'round' | 'circle'; type?: 'primary' | 'warning' | 'danger' | 'default'; bold?: boolean; + loading?: boolean; }; diff --git a/packages/app/src/ui/button/loading.tsx b/packages/app/src/ui/button/loading.tsx new file mode 100644 index 0000000000..8bada7628a --- /dev/null +++ b/packages/app/src/ui/button/loading.tsx @@ -0,0 +1,60 @@ +import { styled } from '@/styles'; +import { ButtonProps } from './interface'; +import { getButtonColors } from './utils'; + +export const LoadingContainer = styled('div')>( + ({ theme, type = 'default' }) => { + const { color } = getButtonColors(theme, type); + return ` + margin: 0px auto; + width: 38px; + text-align: center; + .load { + width: 8px; + height: 8px; + background-color: ${color}; + + border-radius: 100%; + display: inline-block; + -webkit-animation: bouncedelay 1.4s infinite ease-in-out; + animation: bouncedelay 1.4s infinite ease-in-out; + /* Prevent first frame from flickering when animation starts */ + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + } + .load1 { + -webkit-animation-delay: -0.32s; + animation-delay: -0.32s; + } + .load2 { + -webkit-animation-delay: -0.16s; + animation-delay: -0.16s; + } + + @-webkit-keyframes bouncedelay { + 0%, 80%, 100% { -webkit-transform: scale(0) } + 40% { -webkit-transform: scale(1.0) } + } + + @keyframes bouncedelay { + 0%, 80%, 100% { + transform: scale(0); + -webkit-transform: scale(0); + } 40% { + transform: scale(1.0); + -webkit-transform: scale(1.0); + } + } + `; + } +); + +export const Loading = ({ type }: Pick) => { + return ( + +
+
+
+
+ ); +};