diff --git a/packages/frontend/core/src/mobile/components/app-tabs/constants.ts b/packages/frontend/core/src/mobile/components/app-tabs/constants.ts new file mode 100644 index 0000000000..11a3fcffbd --- /dev/null +++ b/packages/frontend/core/src/mobile/components/app-tabs/constants.ts @@ -0,0 +1 @@ +export const cacheKey = 'activeAppTabId'; diff --git a/packages/frontend/core/src/mobile/components/app-tabs/create.tsx b/packages/frontend/core/src/mobile/components/app-tabs/create.tsx index 568d4e4e9c..aa6aac0473 100644 --- a/packages/frontend/core/src/mobile/components/app-tabs/create.tsx +++ b/packages/frontend/core/src/mobile/components/app-tabs/create.tsx @@ -8,8 +8,8 @@ import track from '@affine/track'; import { EditIcon } from '@blocksuite/icons/rc'; import { useLiveData, useService } from '@toeverything/infra'; -import type { AppTabCustomFCProps } from './data'; import { TabItem } from './tab-item'; +import type { AppTabCustomFCProps } from './type'; export const AppTabCreate = ({ tab }: AppTabCustomFCProps) => { const workbench = useService(WorkbenchService).workbench; diff --git a/packages/frontend/core/src/mobile/components/app-tabs/data.tsx b/packages/frontend/core/src/mobile/components/app-tabs/data.tsx index 6b41808405..7876cf789d 100644 --- a/packages/frontend/core/src/mobile/components/app-tabs/data.tsx +++ b/packages/frontend/core/src/mobile/components/app-tabs/data.tsx @@ -1,28 +1,8 @@ import { AllDocsIcon, HomeIcon } from '@blocksuite/icons/rc'; -import type { Framework } from '@toeverything/infra'; import { AppTabCreate } from './create'; import { AppTabJournal } from './journal'; - -interface AppTabBase { - key: string; - onClick?: (framework: Framework, isActive: boolean) => void; -} -export interface AppTabLink extends AppTabBase { - Icon: React.FC; - to: string; - LinkComponent?: React.FC; -} - -export interface AppTabCustom extends AppTabBase { - custom: (props: AppTabCustomFCProps) => React.ReactNode; -} - -export type Tab = AppTabLink | AppTabCustom; - -export interface AppTabCustomFCProps { - tab: Tab; -} +import type { Tab } from './type'; export const tabs: Tab[] = [ { diff --git a/packages/frontend/core/src/mobile/components/app-tabs/index.tsx b/packages/frontend/core/src/mobile/components/app-tabs/index.tsx index 53e1c4c9b2..b13e4fb1c7 100644 --- a/packages/frontend/core/src/mobile/components/app-tabs/index.tsx +++ b/packages/frontend/core/src/mobile/components/app-tabs/index.tsx @@ -1,14 +1,20 @@ import { SafeArea } from '@affine/component'; -import { WorkbenchLink } from '@affine/core/modules/workbench'; +import { GlobalCacheService } from '@affine/core/modules/storage'; +import { + WorkbenchLink, + WorkbenchService, +} from '@affine/core/modules/workbench'; import { useLiveData, useService } from '@toeverything/infra'; import { assignInlineVars } from '@vanilla-extract/dynamic'; -import React from 'react'; +import React, { useEffect } from 'react'; import { createPortal } from 'react-dom'; import { VirtualKeyboardService } from '../../modules/virtual-keyboard/services/virtual-keyboard'; -import { type AppTabLink, tabs } from './data'; +import { cacheKey } from './constants'; +import { tabs } from './data'; import * as styles from './styles.css'; import { TabItem } from './tab-item'; +import type { AppTabLink } from './type'; export const AppTabs = ({ background, @@ -19,6 +25,16 @@ export const AppTabs = ({ }) => { const virtualKeyboardService = useService(VirtualKeyboardService); const virtualKeyboardVisible = useLiveData(virtualKeyboardService.visible$); + const workbench = useService(WorkbenchService).workbench; + const location = useLiveData(workbench.location$); + const globalCache = useService(GlobalCacheService).globalCache; + + // always set the active tab to home when the location is changed to home + useEffect(() => { + if (location.pathname === '/home') { + globalCache.set(cacheKey, 'home'); + } + }, [globalCache, location.pathname]); const tab = ( { const workbench = useService(WorkbenchService).workbench; diff --git a/packages/frontend/core/src/mobile/components/app-tabs/tab-item.tsx b/packages/frontend/core/src/mobile/components/app-tabs/tab-item.tsx index abc6f8e8f9..d3b4e0d85f 100644 --- a/packages/frontend/core/src/mobile/components/app-tabs/tab-item.tsx +++ b/packages/frontend/core/src/mobile/components/app-tabs/tab-item.tsx @@ -1,7 +1,8 @@ import { GlobalCacheService } from '@affine/core/modules/storage'; import { LiveData, useLiveData, useService } from '@toeverything/infra'; -import { type PropsWithChildren, useCallback, useEffect, useMemo } from 'react'; +import { type PropsWithChildren, useCallback, useMemo } from 'react'; +import { cacheKey } from './constants'; import { tabItem } from './styles.css'; export interface TabItemProps extends PropsWithChildren { @@ -10,8 +11,6 @@ export interface TabItemProps extends PropsWithChildren { onClick?: (isActive: boolean) => void; } -const cacheKey = 'activeAppTabId'; -let isInitialized = false; export const TabItem = ({ id, label, children, onClick }: TabItemProps) => { const globalCache = useService(GlobalCacheService).globalCache; const activeTabId$ = useMemo( @@ -27,14 +26,6 @@ export const TabItem = ({ id, label, children, onClick }: TabItemProps) => { onClick?.(isActive); }, [globalCache, id, isActive, onClick]); - useEffect(() => { - if (isInitialized) return; - isInitialized = true; - if (BUILD_CONFIG.isIOS || BUILD_CONFIG.isAndroid) { - globalCache.set(cacheKey, 'home'); - } - }, [globalCache]); - return (
  • void; +} +export interface AppTabLink extends AppTabBase { + Icon: React.FC; + to: string; + LinkComponent?: React.FC; +} + +export interface AppTabCustom extends AppTabBase { + custom: (props: AppTabCustomFCProps) => React.ReactNode; +} + +export type Tab = AppTabLink | AppTabCustom; + +export interface AppTabCustomFCProps { + tab: Tab; +} diff --git a/packages/frontend/core/src/mobile/pages/workspace/detail/page-header-more-button.tsx b/packages/frontend/core/src/mobile/pages/workspace/detail/page-header-more-button.tsx index 9d5439e4f0..342c1e962e 100644 --- a/packages/frontend/core/src/mobile/pages/workspace/detail/page-header-more-button.tsx +++ b/packages/frontend/core/src/mobile/pages/workspace/detail/page-header-more-button.tsx @@ -1,4 +1,4 @@ -import { IconButton, notify } from '@affine/component'; +import { IconButton, notify, toast, useConfirmModal } from '@affine/component'; import { MenuSeparator, MenuSub, @@ -6,7 +6,7 @@ import { MobileMenuItem, } from '@affine/component/ui/menu'; import { useFavorite } from '@affine/core/blocksuite/block-suite-header/favorite'; -import { useGuard } from '@affine/core/components/guard'; +import { Guard, useGuard } from '@affine/core/components/guard'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { DocInfoSheet } from '@affine/core/mobile/components'; import { MobileTocMenu } from '@affine/core/mobile/components/toc-menu'; @@ -17,6 +17,7 @@ import { preventDefault } from '@affine/core/utils'; import { useI18n } from '@affine/i18n'; import { track } from '@affine/track'; import { + DeleteIcon, EdgelessIcon, InformationIcon, MoreHorizontalIcon, @@ -34,7 +35,8 @@ import * as styles from './page-header-more-button.css'; export const PageHeaderMenuButton = () => { const t = useI18n(); - const docId = useService(DocService).doc.id; + const doc = useService(DocService).doc; + const docId = doc?.id; const canEdit = useGuard('Doc_Update', docId); const editorService = useService(EditorService); @@ -50,6 +52,7 @@ export const PageHeaderMenuButton = () => { const title = useLiveData(editorService.editor.doc.title$); const { favorite, toggleFavorite } = useFavorite(docId); + const { openConfirmModal } = useConfirmModal(); const handleSwitchMode = useCallback(() => { const mode = primaryMode === 'page' ? 'edgeless' : 'page'; @@ -88,6 +91,32 @@ export const PageHeaderMenuButton = () => { toggleFavorite(); }, [toggleFavorite]); + const handleMoveToTrash = useCallback(() => { + if (!doc) { + return; + } + openConfirmModal({ + title: t['com.affine.moveToTrash.title'](), + description: t['com.affine.moveToTrash.confirmModal.description']({ + title: doc.title$.value, + }), + confirmText: t['com.affine.moveToTrash.confirmModal.confirm'](), + cancelText: t['com.affine.moveToTrash.confirmModal.cancel'](), + confirmButtonOptions: { + variant: 'error', + }, + onConfirm() { + doc.moveToTrash(); + track.$.navigationPanel.docs.deleteDoc({ + control: 'button', + }); + toast(t['com.affine.toastMessage.movedTrash']()); + // navigate back + history.back(); + }, + }); + }, [doc, openConfirmModal, t]); + const EditMenu = ( <> @@ -135,6 +164,18 @@ export const PageHeaderMenuButton = () => { + + {canMoveToTrash => ( + } + type="danger" + disabled={!canMoveToTrash} + onSelect={handleMoveToTrash} + > + {t['com.affine.moveToTrash.title']()} + + )} + ); if (isInTrash) {