From 532d655ffb102efd8bf00f4e62aee76acb30b54f Mon Sep 17 00:00:00 2001 From: JimmFly <447268514@qq.com> Date: Sat, 23 Mar 2024 12:03:14 +0000 Subject: [PATCH] feat(core): add confirm modal for delete tag action (#6268) --- .../page-properties/tags-inline-editor.tsx | 37 +++++++++-- .../components/page-list/operation-cell.tsx | 4 +- .../page-list/tags/virtualized-tag-list.tsx | 6 +- .../frontend/core/src/modules/tag/index.ts | 1 + .../src/modules/tag/view/delete-tag-modal.tsx | 65 +++++++++++++++++++ .../src/pages/workspace/all-tag/index.tsx | 30 ++++++--- packages/frontend/i18n/src/resources/en.json | 3 + 7 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 packages/frontend/core/src/modules/tag/view/delete-tag-modal.tsx diff --git a/packages/frontend/core/src/components/affine/page-properties/tags-inline-editor.tsx b/packages/frontend/core/src/components/affine/page-properties/tags-inline-editor.tsx index eb45cd670f..6cb0c4b8c9 100644 --- a/packages/frontend/core/src/components/affine/page-properties/tags-inline-editor.tsx +++ b/packages/frontend/core/src/components/affine/page-properties/tags-inline-editor.tsx @@ -6,7 +6,7 @@ import { Scrollable, } from '@affine/component'; import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; -import { TagService } from '@affine/core/modules/tag'; +import { DeleteTagConfirmModal, TagService } from '@affine/core/modules/tag'; import { WorkspaceLegacyProperties } from '@affine/core/modules/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { DeleteIcon, MoreHorizontalIcon, TagsIcon } from '@blocksuite/icons'; @@ -74,8 +74,12 @@ const InlineTagsList = ({ export const EditTagMenu = ({ tagId, + onTagDelete, children, -}: PropsWithChildren<{ tagId: string }>) => { +}: PropsWithChildren<{ + tagId: string; + onTagDelete: (tagIds: string[]) => void; +}>) => { const t = useAFFiNEI18N(); const legacyProperties = useService(WorkspaceLegacyProperties); const tagService = useService(TagService); @@ -116,7 +120,7 @@ export const EditTagMenu = ({ icon: , type: 'danger', onClick() { - tagService.deleteTag(tag?.id || ''); + onTagDelete([tag?.id || '']); }, }); @@ -164,10 +168,10 @@ export const EditTagMenu = ({ }, [ legacyProperties.workspaceId, navigate, + onTagDelete, t, tag, tagColor, - tagService, tagValue, ]); @@ -180,6 +184,24 @@ export const TagsEditor = ({ pageId, readonly }: TagsEditorProps) => { const tags = useLiveData(tagService.tags); const tagIds = useLiveData(tagService.tagIdsByPageId(pageId)); const [inputValue, setInputValue] = useState(''); + const [open, setOpen] = useState(false); + const [selectedTagIds, setSelectedTagIds] = useState([]); + + const handleCloseModal = useCallback( + (open: boolean) => { + setOpen(open); + setSelectedTagIds([]); + }, + [setOpen] + ); + + const onTagDelete = useCallback( + (tagIds: string[]) => { + setOpen(true); + setSelectedTagIds(tagIds); + }, + [setOpen, setSelectedTagIds] + ); const exactMatch = useLiveData(tagService.tagByTagValue(inputValue)); @@ -280,7 +302,7 @@ export const TagsEditor = ({ pageId, readonly }: TagsEditorProps) => { >
- + {
+ ); }; diff --git a/packages/frontend/core/src/components/page-list/operation-cell.tsx b/packages/frontend/core/src/components/page-list/operation-cell.tsx index 9fd9328c66..9c546ed40c 100644 --- a/packages/frontend/core/src/components/page-list/operation-cell.tsx +++ b/packages/frontend/core/src/components/page-list/operation-cell.tsx @@ -4,7 +4,6 @@ import { Menu, MenuIcon, MenuItem, - toast, Tooltip, } from '@affine/component'; import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper'; @@ -334,8 +333,7 @@ export const TagOperationCell = ({ const handleDelete = useCallback(() => { onTagDelete([tag.id]); - toast(t['com.affine.tags.delete-tags.toast']()); - }, [onTagDelete, t, tag.id]); + }, [onTagDelete, tag.id]); return ( <>
diff --git a/packages/frontend/core/src/components/page-list/tags/virtualized-tag-list.tsx b/packages/frontend/core/src/components/page-list/tags/virtualized-tag-list.tsx index 6c7290f7f1..c44f9ab4e3 100644 --- a/packages/frontend/core/src/components/page-list/tags/virtualized-tag-list.tsx +++ b/packages/frontend/core/src/components/page-list/tags/virtualized-tag-list.tsx @@ -1,7 +1,5 @@ -import { toast } from '@affine/component'; import type { Tag } from '@affine/core/modules/tag'; import { Trans } from '@affine/i18n'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useService } from '@toeverything/infra'; import { Workspace } from '@toeverything/infra'; import { useCallback, useMemo, useRef, useState } from 'react'; @@ -25,7 +23,6 @@ export const VirtualizedTagList = ({ tagMetas: TagMeta[]; onTagDelete: (tagIds: string[]) => void; }) => { - const t = useAFFiNEI18N(); const listRef = useRef(null); const [showFloatingToolbar, setShowFloatingToolbar] = useState(false); const [showCreateTagInput, setShowCreateTagInput] = useState(false); @@ -74,10 +71,9 @@ export const VirtualizedTagList = ({ const handleDelete = useCallback(() => { onTagDelete(selectedTagIds); - toast(t['com.affine.delete-tags.count']({ count: selectedTagIds.length })); hideFloatingToolbar(); return; - }, [hideFloatingToolbar, onTagDelete, selectedTagIds, t]); + }, [hideFloatingToolbar, onTagDelete, selectedTagIds]); const onOpenCreate = useCallback(() => { setShowCreateTagInput(true); diff --git a/packages/frontend/core/src/modules/tag/index.ts b/packages/frontend/core/src/modules/tag/index.ts index a0fab4c382..c485f457b9 100644 --- a/packages/frontend/core/src/modules/tag/index.ts +++ b/packages/frontend/core/src/modules/tag/index.ts @@ -1,3 +1,4 @@ export { Tag } from './entities/tag'; export { tagColorMap } from './entities/utils'; export { TagService } from './service/tag'; +export { DeleteTagConfirmModal } from './view/delete-tag-modal'; diff --git a/packages/frontend/core/src/modules/tag/view/delete-tag-modal.tsx b/packages/frontend/core/src/modules/tag/view/delete-tag-modal.tsx new file mode 100644 index 0000000000..9b3c3641a4 --- /dev/null +++ b/packages/frontend/core/src/modules/tag/view/delete-tag-modal.tsx @@ -0,0 +1,65 @@ +import { ConfirmModal, toast } from '@affine/component'; +import { Trans } from '@affine/i18n'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useLiveData, useService } from '@toeverything/infra'; +import { useCallback, useMemo } from 'react'; + +import { TagService } from '../service/tag'; + +export const DeleteTagConfirmModal = ({ + open, + onOpenChange, + selectedTagIds, +}: { + open: boolean; + onOpenChange: (open: boolean) => void; + selectedTagIds: string[]; +}) => { + const t = useAFFiNEI18N(); + const tagService = useService(TagService); + const tags = useLiveData(tagService.tags); + const selectedTags = useMemo(() => { + return tags.filter(tag => selectedTagIds.includes(tag.id)); + }, [selectedTagIds, tags]); + const tagName = useLiveData(selectedTags[0]?.value || ''); + + const handleDelete = useCallback(() => { + selectedTagIds.forEach(tagId => { + tagService.deleteTag(tagId); + }); + + toast( + selectedTagIds.length > 1 + ? t['com.affine.delete-tags.count']({ count: selectedTagIds.length }) + : t['com.affine.tags.delete-tags.toast']() + ); + + onOpenChange(false); + }, [onOpenChange, selectedTagIds, t, tagService]); + + return ( + }} + /> + ) : ( + t['com.affine.delete-tags.confirm.multi-tag-description']({ + count: selectedTags.length.toString(), + }) + ) + } + confirmButtonOptions={{ + type: 'warning', + children: t['Delete'](), + }} + onConfirm={handleDelete} + /> + ); +}; diff --git a/packages/frontend/core/src/pages/workspace/all-tag/index.tsx b/packages/frontend/core/src/pages/workspace/all-tag/index.tsx index 1c4c239e85..c1aa71c47a 100644 --- a/packages/frontend/core/src/pages/workspace/all-tag/index.tsx +++ b/packages/frontend/core/src/pages/workspace/all-tag/index.tsx @@ -4,7 +4,7 @@ import { } from '@affine/core/components/page-list/tags'; import { CreateOrEditTag } from '@affine/core/components/page-list/tags/create-tag'; import type { TagMeta } from '@affine/core/components/page-list/types'; -import { TagService } from '@affine/core/modules/tag'; +import { DeleteTagConfirmModal, TagService } from '@affine/core/modules/tag'; import { useLiveData, useService } from '@toeverything/infra'; import { useCallback, useState } from 'react'; @@ -33,16 +33,25 @@ const EmptyTagListHeader = () => { export const AllTag = () => { const tagService = useService(TagService); const tags = useLiveData(tagService.tags); + const [open, setOpen] = useState(false); + const [selectedTagIds, setSelectedTagIds] = useState([]); const tagMetas: TagMeta[] = useLiveData(tagService.tagMetas); - const handleDelete = useCallback( - (tagIds: string[]) => { - tagIds.forEach(tagId => { - tagService.deleteTag(tagId); - }); + const handleCloseModal = useCallback( + (open: boolean) => { + setOpen(open); + setSelectedTagIds([]); }, - [tagService] + [setOpen] + ); + + const onTagDelete = useCallback( + (tagIds: string[]) => { + setOpen(true); + setSelectedTagIds(tagIds); + }, + [setOpen, setSelectedTagIds] ); return ( @@ -56,13 +65,18 @@ export const AllTag = () => { ) : ( } /> )}
+ ); }; diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index 45167f5c49..bc6406ceed 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1165,6 +1165,9 @@ "com.affine.delete-tags.count": "{{count}} tag deleted", "com.affine.delete-tags.count_one": "{{count}} tag deleted", "com.affine.delete-tags.count_other": "{{count}} tags deleted", + "com.affine.delete-tags.confirm.title": "Delete Tag?", + "com.affine.delete-tags.confirm.description": "Deleting <1>{{tag}} cannot be undone, please proceed with caution.", + "com.affine.delete-tags.confirm.multi-tag-description": "Deleting {{count}} tags cannot be undone, please proceed with caution.", "com.affine.workbench.split-view-menu.keep-this-one": "Solo View", "com.affine.workbench.split-view.page-menu-open": "Open in split view", "com.affine.search-tags.placeholder": "Type here ...",