feat(core): remove empty workspace (#13317)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added the ability to remove an empty workspace directly from the
workspace card when you are the owner.
* Workspace cards now display a "Remove" button for eligible workspaces.
* **Improvements**
* Workspace information now indicates if a workspace is empty, improving
clarity for users.
* **Bug Fixes**
* Enhanced accuracy in displaying workspace status by updating how
workspace profile data is handled.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
EYHN
2025-07-25 18:26:55 +08:00
committed by GitHub
parent 1dd4bbbaba
commit be55442f38
4 changed files with 47 additions and 12 deletions
@@ -1,11 +1,12 @@
import { Button, Skeleton, Tooltip } from '@affine/component';
import { Button, notify, Skeleton, Tooltip } from '@affine/component';
import { Loading } from '@affine/component/ui/loading';
import { useSystemOnline } from '@affine/core/components/hooks/use-system-online';
import { useWorkspace } from '@affine/core/components/hooks/use-workspace';
import { useWorkspaceInfo } from '@affine/core/components/hooks/use-workspace-info';
import type {
WorkspaceMetadata,
WorkspaceProfileInfo,
import {
type WorkspaceMetadata,
type WorkspaceProfileInfo,
WorkspacesService,
} from '@affine/core/modules/workspace';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { useI18n } from '@affine/i18n';
@@ -21,13 +22,15 @@ import {
TeamWorkspaceIcon,
UnsyncIcon,
} from '@blocksuite/icons/rc';
import { LiveData, useLiveData } from '@toeverything/infra';
import { LiveData, useLiveData, useService } from '@toeverything/infra';
import { cssVar } from '@toeverything/theme';
import clsx from 'clsx';
import type { HTMLAttributes } from 'react';
import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
import { useAsyncCallback } from '../../hooks/affine-async-hooks';
import { useCatchEventCallback } from '../../hooks/use-catch-event-hook';
import { useNavigateHelper } from '../../hooks/use-navigate-helper';
import { WorkspaceAvatar } from '../../workspace-avatar';
import * as styles from './styles.css';
export { PureWorkspaceCard } from './pure-workspace-card';
@@ -284,6 +287,8 @@ export const WorkspaceCard = forwardRef<
) => {
const t = useI18n();
const information = useWorkspaceInfo(workspaceMetadata);
const workspacesService = useService(WorkspacesService);
const navigate = useNavigateHelper();
const name = information?.name ?? UNTITLED_WORKSPACE_NAME;
@@ -291,10 +296,24 @@ export const WorkspaceCard = forwardRef<
onClickEnableCloud?.(workspaceMetadata);
}, [onClickEnableCloud, workspaceMetadata]);
const onRemoveWorkspace = useAsyncCallback(async () => {
await workspacesService
.deleteWorkspace(workspaceMetadata)
.then(() => {
notify.success({ title: t['Successfully removed workspace']() });
navigate.jumpToIndex();
})
.catch(() => {
notify.error({ title: t['Failed to remove workspace']() });
});
}, [workspacesService, workspaceMetadata, t, navigate]);
const onOpenSettings = useCatchEventCallback(() => {
onClickOpenSettings?.(workspaceMetadata);
}, [onClickOpenSettings, workspaceMetadata]);
console.log(information);
return (
<div
className={clsx(
@@ -337,6 +356,9 @@ export const WorkspaceCard = forwardRef<
<Skeleton width={100} />
)}
</div>
{information?.isEmpty && information.isOwner ? (
<Button onClick={onRemoveWorkspace}>Remove</Button>
) : null}
<div className={styles.showOnCardHover}>
{onClickEnableCloud && workspaceMetadata.flavour === 'local' ? (
<Button