refactor: remove React.FC for affine core (#3617)

This commit is contained in:
Garfield Lee
2023-08-08 06:54:31 +00:00
committed by GitHub
parent ceeabfeb00
commit 3009d729fa
32 changed files with 314 additions and 242 deletions
@@ -3,7 +3,6 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
import { getOrCreateWorkspace } from '@affine/workspace/manager';
import type { EditorContainer } from '@blocksuite/editor';
import type { Page } from '@blocksuite/store';
import type React from 'react';
import { useCallback } from 'react';
import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor';
@@ -15,7 +14,7 @@ const blockSuiteWorkspace = getOrCreateWorkspace(
const page = blockSuiteWorkspace.createPage({ id: 'page0' });
const Editor: React.FC = () => {
const Editor = () => {
const onLoad = useCallback((page: Page, editor: EditorContainer) => {
// @ts-expect-error
globalThis.page = page;
@@ -2,7 +2,6 @@ import { Modal, ModalWrapper, Wrapper } from '@affine/component';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon } from '@blocksuite/icons';
import { Button, IconButton } from '@toeverything/components/button';
import type React from 'react';
import { Content, ContentTitle, Header, StyleTips } from './style';
@@ -12,11 +11,11 @@ interface EnableAffineCloudModalProps {
onClose: () => void;
}
export const EnableAffineCloudModal: React.FC<EnableAffineCloudModalProps> = ({
export const EnableAffineCloudModal = ({
onConfirm,
open,
onClose,
}) => {
}: EnableAffineCloudModalProps) => {
const t = useAFFiNEI18N();
return (
@@ -2,17 +2,20 @@ import { Menu, MenuItem, MenuTrigger, styled } from '@affine/component';
import { LOCALES } from '@affine/i18n';
import { useI18N } from '@affine/i18n';
import type { ButtonProps } from '@toeverything/components/button';
import type { FC, ReactElement } from 'react';
import type { ReactElement } from 'react';
import { useCallback } from 'react';
export const StyledListItem = styled(MenuItem)(() => ({
width: '132px',
height: '38px',
textTransform: 'capitalize',
}));
const LanguageMenuContent: FC<{
interface LanguageMenuContentProps {
currentLanguage?: string;
}> = ({ currentLanguage }) => {
}
const LanguageMenuContent = ({ currentLanguage }: LanguageMenuContentProps) => {
const i18n = useI18N();
const changeLanguage = useCallback(
(event: string) => {
@@ -20,6 +23,7 @@ const LanguageMenuContent: FC<{
},
[i18n]
);
return (
<>
{LOCALES.map(option => {
@@ -41,9 +45,12 @@ const LanguageMenuContent: FC<{
</>
);
};
export const LanguageMenu: FC<{ triggerProps?: ButtonProps }> = ({
triggerProps,
}) => {
interface LanguageMenuProps {
triggerProps?: ButtonProps;
}
export const LanguageMenu = ({ triggerProps }: LanguageMenuProps) => {
const i18n = useI18N();
const currentLanguage = LOCALES.find(item => item.tag === i18n.language);
@@ -1,23 +1,29 @@
import { SettingRow } from '@affine/component/setting-components';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowRightSmallIcon } from '@blocksuite/icons';
import { type FC, useState } from 'react';
import { useState } from 'react';
import type { AffineOfficialWorkspace } from '../../../../shared';
import type { WorkspaceSettingDetailProps } from '../index';
import { WorkspaceDeleteModal } from './delete';
import { WorkspaceLeave } from './leave';
export const DeleteLeaveWorkspace: FC<{
interface DeleteLeaveWorkspaceProps {
workspace: AffineOfficialWorkspace;
onDeleteWorkspace: WorkspaceSettingDetailProps['onDeleteWorkspace'];
}> = ({ workspace, onDeleteWorkspace }) => {
}
export const DeleteLeaveWorkspace = ({
workspace,
onDeleteWorkspace,
}: DeleteLeaveWorkspaceProps) => {
const t = useAFFiNEI18N();
// fixme: cloud regression
const isOwner = true;
const [showDelete, setShowDelete] = useState(false);
const [showLeave, setShowLeave] = useState(false);
return (
<>
<SettingRow
@@ -3,7 +3,7 @@ import { SettingRow } from '@affine/component/setting-components';
import { isDesktop } from '@affine/env/constant';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import { type FC, useCallback } from 'react';
import { useCallback } from 'react';
import type { AffineOfficialWorkspace } from '../../../shared';
@@ -28,9 +28,11 @@ async function syncBlobsToSqliteDb(workspace: AffineOfficialWorkspace) {
}
}
export const ExportPanel: FC<{
interface ExportPanelProps {
workspace: AffineOfficialWorkspace;
}> = ({ workspace }) => {
}
export const ExportPanel = ({ workspace }: ExportPanelProps) => {
const workspaceId = workspace.id;
const t = useAFFiNEI18N();
const onExport = useCallback(async () => {
@@ -42,6 +44,7 @@ export const ExportPanel: FC<{
toast(t['Export success']());
}
}, [t, workspace, workspaceId]);
return (
<>
<SettingRow name={t['Export']()} desc={t['Export Description']()}>
@@ -9,7 +9,7 @@ import type {
} from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { type FC, useMemo } from 'react';
import { useMemo } from 'react';
import { useWorkspace } from '../../../hooks/use-workspace';
import { DeleteLeaveWorkspace } from './delete-leave-workspace';
@@ -18,7 +18,7 @@ import { ProfilePanel } from './profile';
import { PublishPanel } from './publish';
import { StoragePanel } from './storage';
export type WorkspaceSettingDetailProps = {
export interface WorkspaceSettingDetailProps {
workspaceId: string;
onDeleteWorkspace: (id: string) => Promise<void>;
onTransferWorkspace: <
@@ -29,13 +29,13 @@ export type WorkspaceSettingDetailProps = {
to: To,
workspace: WorkspaceRegistry[From]
) => void;
};
}
export const WorkspaceSettingDetail: FC<WorkspaceSettingDetailProps> = ({
export const WorkspaceSettingDetail = ({
workspaceId,
onDeleteWorkspace,
...props
}) => {
}: WorkspaceSettingDetailProps) => {
const t = useAFFiNEI18N();
const workspace = useWorkspace(workspaceId);
const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace);
@@ -5,7 +5,7 @@ import { DoneIcon } from '@blocksuite/icons';
import { IconButton } from '@toeverything/components/button';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { type FC, useCallback, useState } from 'react';
import { useCallback, useState } from 'react';
import type { AffineOfficialWorkspace } from '../../../shared';
import { Upload } from '../../pure/file-upload';
@@ -30,9 +30,11 @@ const CameraIcon = () => {
);
};
export const ProfilePanel: FC<{
interface ProfilePanelProps {
workspace: AffineOfficialWorkspace;
}> = ({ workspace }) => {
}
export const ProfilePanel = ({ workspace }: ProfilePanelProps) => {
const t = useAFFiNEI18N();
const [, update] = useBlockSuiteWorkspaceAvatarUrl(
@@ -9,7 +9,6 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import type { FC } from 'react';
import { useCallback, useEffect, useState } from 'react';
import type { AffineOfficialWorkspace } from '../../../shared';
@@ -19,26 +18,20 @@ import { TmpDisableAffineCloudModal } from '../tmp-disable-affine-cloud-modal';
import type { WorkspaceSettingDetailProps } from './index';
import * as style from './style.css';
export type PublishPanelProps = Omit<
WorkspaceSettingDetailProps,
'workspaceId'
> & {
export interface PublishPanelProps
extends Omit<WorkspaceSettingDetailProps, 'workspaceId'> {
workspace: AffineOfficialWorkspace;
};
export type PublishPanelLocalProps = Omit<
WorkspaceSettingDetailProps,
'workspaceId'
> & {
}
export interface PublishPanelLocalProps
extends Omit<WorkspaceSettingDetailProps, 'workspaceId'> {
workspace: LocalWorkspace;
};
export type PublishPanelAffineProps = Omit<
WorkspaceSettingDetailProps,
'workspaceId'
> & {
}
export interface PublishPanelAffineProps
extends Omit<WorkspaceSettingDetailProps, 'workspaceId'> {
workspace: AffineCloudWorkspace;
};
}
const PublishPanelAffine: FC<PublishPanelAffineProps> = props => {
const PublishPanelAffine = (props: PublishPanelAffineProps) => {
const { workspace } = props;
const t = useAFFiNEI18N();
// const toggleWorkspacePublish = useToggleWorkspacePublish(workspace);
@@ -58,6 +51,7 @@ const PublishPanelAffine: FC<PublishPanelAffineProps> = props => {
await navigator.clipboard.writeText(shareUrl);
toast(t['Copied link to clipboard']());
}, [shareUrl, t]);
return (
<>
<SettingRow
@@ -91,10 +85,13 @@ const PublishPanelAffine: FC<PublishPanelAffineProps> = props => {
);
};
const FakePublishPanelAffine: FC<{
interface FakePublishPanelAffineProps {
workspace: AffineOfficialWorkspace;
}> = () => {
}
const FakePublishPanelAffine = (_props: FakePublishPanelAffineProps) => {
const t = useAFFiNEI18N();
return (
<Tooltip
content={t['com.affine.settings.workspace.publish.local-tooltip']()}
@@ -108,10 +105,11 @@ const FakePublishPanelAffine: FC<{
</Tooltip>
);
};
const PublishPanelLocal: FC<PublishPanelLocalProps> = ({
const PublishPanelLocal = ({
workspace,
onTransferWorkspace,
}) => {
}: PublishPanelLocalProps) => {
const t = useAFFiNEI18N();
const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace);
@@ -167,7 +165,7 @@ const PublishPanelLocal: FC<PublishPanelLocalProps> = ({
);
};
export const PublishPanel: FC<PublishPanelProps> = props => {
export const PublishPanel = (props: PublishPanelProps) => {
if (props.workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD) {
return <PublishPanelAffine {...props} workspace={props.workspace} />;
} else if (props.workspace.flavour === WorkspaceFlavour.LOCAL) {
@@ -3,7 +3,7 @@ import { SettingRow } from '@affine/component/setting-components';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import { useMemo } from 'react';
import { type FC, useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import type { AffineOfficialWorkspace } from '../../../shared';
import * as style from './style.css';
@@ -31,9 +31,11 @@ const useDBFileSecondaryPath = (workspaceId: string) => {
return path;
};
export const StoragePanel: FC<{
interface StoragePanelProps {
workspace: AffineOfficialWorkspace;
}> = ({ workspace }) => {
}
export const StoragePanel = ({ workspace }: StoragePanelProps) => {
const workspaceId = workspace.id;
const t = useAFFiNEI18N();
const secondaryPath = useDBFileSecondaryPath(workspaceId);
@@ -1,12 +1,11 @@
import { TourModal } from '@affine/component/tour-modal';
import { useAtom } from 'jotai';
import type { FC } from 'react';
import { memo, useCallback } from 'react';
import { openOnboardingModalAtom } from '../../atoms';
import { guideOnboardingAtom } from '../../atoms/guide';
export const OnboardingModal: FC = memo(function OnboardingModal() {
export const OnboardingModal = memo(function OnboardingModal() {
const [open, setOpen] = useAtom(openOnboardingModalAtom);
const [guideOpen, setShowOnboarding] = useAtom(guideOnboardingAtom);
const onCloseTourModal = useCallback(() => {
@@ -1,6 +1,6 @@
import { Menu, MenuItem, MenuTrigger } from '@affine/component';
import dayjs from 'dayjs';
import { type FC, useCallback } from 'react';
import { useCallback } from 'react';
import {
dateFormatOptions,
@@ -8,10 +8,15 @@ import {
useAppSetting,
} from '../../../../../atoms/settings';
const DateFormatMenuContent: FC<{
interface DateFormatMenuContentProps {
currentOption: DateFormats;
onSelect: (option: DateFormats) => void;
}> = ({ onSelect, currentOption }) => {
}
const DateFormatMenuContent = ({
onSelect,
currentOption,
}: DateFormatMenuContentProps) => {
return (
<>
{dateFormatOptions.map(option => {
@@ -30,6 +35,7 @@ const DateFormatMenuContent: FC<{
</>
);
};
export const DateFormatSetting = () => {
const [appearanceSettings, setAppSettings] = useAppSetting();
const handleSelect = useCallback(
@@ -38,6 +44,7 @@ export const DateFormatSetting = () => {
},
[setAppSettings]
);
return (
<Menu
content={
@@ -5,7 +5,7 @@ import {
InformationIcon,
KeyboardIcon,
} from '@blocksuite/icons';
import type { FC, SVGProps } from 'react';
import type { ReactElement, SVGProps } from 'react';
import { AboutAffine } from './about';
import { AppearanceSettings } from './appearance';
@@ -18,15 +18,18 @@ export type GeneralSettingKeys =
| 'plugins'
| 'about';
export type GeneralSettingList = {
interface GeneralSettingListItem {
key: GeneralSettingKeys;
title: string;
icon: FC<SVGProps<SVGSVGElement>>;
icon: (props: SVGProps<SVGSVGElement>) => ReactElement;
testId: string;
}[];
}
export type GeneralSettingList = GeneralSettingListItem[];
export const useGeneralSettingList = (): GeneralSettingList => {
const t = useAFFiNEI18N();
return [
{
key: 'appearance',
@@ -55,11 +58,11 @@ export const useGeneralSettingList = (): GeneralSettingList => {
];
};
export const GeneralSetting = ({
generalKey,
}: {
interface GeneralSettingProps {
generalKey: GeneralSettingKeys;
}) => {
}
export const GeneralSetting = ({ generalKey }: GeneralSettingProps) => {
switch (generalKey) {
case 'shortcuts':
return <Shortcuts />;
@@ -1,10 +1,9 @@
import {
SettingModal as SettingModalBase,
type SettingModalProps,
type SettingModalProps as SettingModalBaseProps,
} from '@affine/component/setting-components';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ContactWithUsIcon } from '@blocksuite/icons';
import type React from 'react';
import { useCallback } from 'react';
import { AccountSetting } from './account-setting';
@@ -18,21 +17,25 @@ import { settingContent } from './style.css';
import { WorkspaceSetting } from './workspace-setting';
type ActiveTab = GeneralSettingKeys | 'workspace' | 'account';
export type SettingProps = {
export interface SettingProps {
activeTab: ActiveTab;
workspaceId: string | null;
onSettingClick: (params: {
activeTab: ActiveTab;
workspaceId: string | null;
}) => void;
};
export const SettingModal: React.FC<SettingModalProps & SettingProps> = ({
}
type SettingModalProps = SettingModalBaseProps & SettingProps;
export const SettingModal = ({
open,
setOpen,
activeTab = 'appearance',
workspaceId = null,
onSettingClick,
}) => {
}: SettingModalProps) => {
const t = useAFFiNEI18N();
const generalSettingList = useGeneralSettingList();
@@ -11,7 +11,6 @@ import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-
import { useStaticBlockSuiteWorkspace } from '@toeverything/infra/__internal__/react';
import clsx from 'clsx';
import { useAtomValue } from 'jotai';
import type { FC } from 'react';
import { Suspense } from 'react';
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
@@ -28,21 +27,24 @@ import {
sidebarTitle,
} from './style.css';
export const SettingSidebar: FC<{
interface SettingSidebarProps {
generalSettingList: GeneralSettingList;
onGeneralSettingClick: (key: GeneralSettingKeys) => void;
onWorkspaceSettingClick: (workspaceId: string) => void;
selectedWorkspaceId: string | null;
selectedGeneralKey: string | null;
onAccountSettingClick: () => void;
}> = ({
}
export const SettingSidebar = ({
generalSettingList,
onGeneralSettingClick,
onWorkspaceSettingClick,
selectedWorkspaceId,
selectedGeneralKey,
}) => {
}: SettingSidebarProps) => {
const t = useAFFiNEI18N();
return (
<div className={settingSlideBar} data-testid="settings-sidebar">
<div className={sidebarTitle}>{t['Settings']()}</div>
@@ -88,10 +90,15 @@ export const SettingSidebar: FC<{
);
};
export const WorkspaceList: FC<{
interface WorkspaceListProps {
onWorkspaceSettingClick: (workspaceId: string) => void;
selectedWorkspaceId: string | null;
}> = ({ onWorkspaceSettingClick, selectedWorkspaceId }) => {
}
export const WorkspaceList = ({
onWorkspaceSettingClick,
selectedWorkspaceId,
}: WorkspaceListProps) => {
const workspaces = useAtomValue(rootWorkspacesMetadataAtom);
const [currentWorkspace] = useCurrentWorkspace();
return (
@@ -114,19 +121,22 @@ export const WorkspaceList: FC<{
);
};
interface WorkspaceListItemProps {
meta: RootWorkspaceMetadata;
onClick: () => void;
isCurrent: boolean;
isActive: boolean;
}
const WorkspaceListItem = ({
meta,
onClick,
isCurrent,
isActive,
}: {
meta: RootWorkspaceMetadata;
onClick: () => void;
isCurrent: boolean;
isActive: boolean;
}) => {
}: WorkspaceListItemProps) => {
const workspace = useStaticBlockSuiteWorkspace(meta.id);
const [workspaceName] = useBlockSuiteWorkspaceName(workspace);
return (
<div
className={clsx(sidebarSelectItem, { active: isActive })}
@@ -3,7 +3,6 @@ import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon } from '@blocksuite/icons';
import { IconButton } from '@toeverything/components/button';
import type React from 'react';
import {
Content,
@@ -20,10 +19,12 @@ interface TmpDisableAffineCloudModalProps {
onClose: () => void;
}
export const TmpDisableAffineCloudModal: React.FC<
TmpDisableAffineCloudModalProps
> = ({ open, onClose }) => {
export const TmpDisableAffineCloudModal = ({
open,
onClose,
}: TmpDisableAffineCloudModalProps) => {
const t = useAFFiNEI18N();
return (
<Modal
data-testid="disable-affine-cloud-modal"
@@ -2,19 +2,20 @@ import { Modal, ModalWrapper } from '@affine/component';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon } from '@blocksuite/icons';
import { IconButton } from '@toeverything/components/button';
import type React from 'react';
import { Content, ContentTitle, Header, StyleButton, StyleTips } from './style';
export type TransformWorkspaceToAffineModalProps = {
export interface TransformWorkspaceToAffineModalProps {
open: boolean;
onClose: () => void;
onConform: () => void;
};
}
export const TransformWorkspaceToAffineModal: React.FC<
TransformWorkspaceToAffineModalProps
> = ({ open, onClose, onConform }) => {
export const TransformWorkspaceToAffineModal = ({
open,
onClose,
onConform,
}: TransformWorkspaceToAffineModalProps) => {
const t = useAFFiNEI18N();
return (
@@ -11,7 +11,6 @@ import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-
import { useBlockSuitePagePreview } from '@toeverything/hooks/use-block-suite-page-preview';
import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page';
import { useAtom, useAtomValue } from 'jotai';
import type React from 'react';
import { Suspense, useCallback, useMemo } from 'react';
import { allPageModeSelectAtom } from '../../../atoms';
@@ -23,13 +22,13 @@ import { filterPage } from '../../../utils/filter';
import { emptyDescButton, emptyDescKbd, pageListEmptyStyle } from './index.css';
import { usePageHelper } from './utils';
export type BlockSuitePageListProps = {
export interface BlockSuitePageListProps {
blockSuiteWorkspace: BlockSuiteWorkspace;
listType: 'all' | 'trash' | 'shared' | 'public';
isPublic?: true;
isPublic?: boolean;
onOpenPage: (pageId: string, newTab?: boolean) => void;
collection?: Collection;
};
}
const filter = {
all: (pageMeta: PageMeta) => !pageMeta.trash,
@@ -41,13 +40,12 @@ const filter = {
shared: (pageMeta: PageMeta) => pageMeta.isPublic && !pageMeta.trash,
};
const PagePreviewInner = ({
workspace,
pageId,
}: {
interface PagePreviewInnerProps {
workspace: Workspace;
pageId: string;
}) => {
}
const PagePreviewInner = ({ workspace, pageId }: PagePreviewInnerProps) => {
const page = useBlockSuiteWorkspacePage(workspace, pageId);
assertExists(page);
const previewAtom = useBlockSuitePagePreview(page);
@@ -55,13 +53,12 @@ const PagePreviewInner = ({
return preview;
};
const PagePreview = ({
workspace,
pageId,
}: {
interface PagePreviewProps {
workspace: Workspace;
pageId: string;
}) => {
}
const PagePreview = ({ workspace, pageId }: PagePreviewProps) => {
return (
<Suspense>
<PagePreviewInner workspace={workspace} pageId={pageId} />
@@ -69,10 +66,12 @@ const PagePreview = ({
);
};
const PageListEmpty = (props: {
interface PageListEmptyProps {
createPage?: ReturnType<typeof usePageHelper>['createPage'];
listType: BlockSuitePageListProps['listType'];
}) => {
}
const PageListEmpty = (props: PageListEmptyProps) => {
const { listType, createPage } = props;
const t = useAFFiNEI18N();
@@ -124,13 +123,13 @@ const PageListEmpty = (props: {
);
};
export const BlockSuitePageList: React.FC<BlockSuitePageListProps> = ({
export const BlockSuitePageList = ({
blockSuiteWorkspace,
onOpenPage,
listType,
isPublic = false,
collection,
}) => {
}: BlockSuitePageListProps) => {
const pageMetas = useBlockSuitePageMeta(blockSuiteWorkspace);
const {
toggleFavorite,
@@ -262,6 +261,7 @@ export const BlockSuitePageList: React.FC<BlockSuitePageListProps> = ({
},
};
});
return (
<PageList
workspaceId={blockSuiteWorkspace.id}
@@ -3,10 +3,10 @@ import { LOCALES } from '@affine/i18n';
import { useI18N } from '@affine/i18n';
import { ArrowDownSmallIcon, LanguageIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button';
import type { FC, ReactElement } from 'react';
import type { ReactElement } from 'react';
import { useCallback } from 'react';
const LanguageMenuContent: FC = () => {
const LanguageMenuContent = () => {
const i18n = useI18N();
const changeLanguage = useCallback(
(event: string) => {
@@ -16,6 +16,7 @@ const LanguageMenuContent: FC = () => {
},
[i18n]
);
return (
<>
{LOCALES.map(option => {
@@ -34,7 +35,8 @@ const LanguageMenuContent: FC = () => {
</>
);
};
export const LanguageMenu: React.FC = () => {
export const LanguageMenu = () => {
const i18n = useI18N();
const currentLanguage = LOCALES.find(item => item.tag === i18n.language);
@@ -87,6 +89,7 @@ const StyledContainer = styled('div')(() => {
padding: '0 14px',
};
});
const StyledIconContainer = styled('div')(() => {
return {
width: '20px',
@@ -96,6 +99,7 @@ const StyledIconContainer = styled('div')(() => {
...displayFlex('flex-start', 'center'),
};
});
const StyledButtonContainer = styled('div')(() => {
return {
width: '100%',
@@ -107,6 +111,7 @@ const StyledButtonContainer = styled('div')(() => {
marginLeft: '12px',
};
});
const StyledButton = styled(Button)(() => {
return {
width: '100%',
@@ -118,6 +123,7 @@ const StyledButton = styled(Button)(() => {
padding: '0',
};
});
const StyledArrowDownContainer = styled('div')(() => {
return {
height: '32px',
@@ -128,6 +134,7 @@ const StyledArrowDownContainer = styled('div')(() => {
fontSize: '24px',
};
});
const StyledCurrentLanguage = styled('div')(() => {
return {
marginLeft: '12px',
@@ -13,7 +13,7 @@ import {
} from '@toeverything/infra/__internal__/plugin';
import clsx from 'clsx';
import { useAtom, useAtomValue } from 'jotai';
import type { FC, HTMLAttributes, PropsWithChildren, ReactNode } from 'react';
import type { HTMLAttributes, ReactElement, ReactNode } from 'react';
import {
forwardRef,
startTransition,
@@ -32,21 +32,21 @@ import { EditorOptionMenu } from './header-right-items/editor-option-menu';
import * as styles from './styles.css';
import { OSWarningMessage, shouldShowWarning } from './utils';
export type BaseHeaderProps<
export interface BaseHeaderProps<
Workspace extends AffineOfficialWorkspace = AffineOfficialWorkspace,
> = {
> {
workspace: Workspace;
currentPage: Page | null;
isPublic: boolean;
leftSlot?: ReactNode;
};
}
export enum HeaderRightItemName {
EditorOptionMenu = 'editorOptionMenu',
}
type HeaderItem = {
Component: FC<BaseHeaderProps>;
interface HeaderItem {
Component: (props: BaseHeaderProps) => ReactElement;
// todo: public workspace should be one of the flavour
availableWhen: (
workspace: AffineOfficialWorkspace,
@@ -55,7 +55,7 @@ type HeaderItem = {
isPublic: boolean;
}
) => boolean;
};
}
const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
[HeaderRightItemName.EditorOptionMenu]: {
@@ -68,7 +68,6 @@ const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
},
};
export type HeaderProps = BaseHeaderProps;
const WindowsAppControls = () => {
const handleMinimizeApp = useCallback(() => {
window.apis?.ui.handleMinimizeApp().catch(err => {
@@ -85,6 +84,7 @@ const WindowsAppControls = () => {
console.error(err);
});
}, []);
return (
<div
data-platform-target="win32"
@@ -118,6 +118,7 @@ const WindowsAppControls = () => {
const PluginHeader = () => {
const headerItem = useAtomValue(pluginHeaderItemAtom);
const pluginsRef = useRef<string[]>([]);
return (
<div
className={styles.pluginHeaderItems}
@@ -151,10 +152,13 @@ const PluginHeader = () => {
);
};
export const Header = forwardRef<
HTMLDivElement,
PropsWithChildren<HeaderProps> & HTMLAttributes<HTMLDivElement>
>((props, ref) => {
export interface HeaderProps
extends BaseHeaderProps,
HTMLAttributes<HTMLDivElement> {
children?: ReactNode;
}
export const Header = forwardRef<HTMLDivElement, HeaderProps>((props, ref) => {
const [showWarning, setShowWarning] = useState(false);
const [showDownloadTip, setShowDownloadTip] = useAtom(
guideDownloadClientTipAtom
@@ -167,6 +171,7 @@ export const Header = forwardRef<
const mode = useAtomValue(currentModeAtom);
const isWindowsDesktop = globalThis.platform === 'win32' && isDesktop;
return (
<div
className={styles.headerContainer}
@@ -5,12 +5,7 @@ import {
usePageMetaHelper,
} from '@toeverything/hooks/use-block-suite-page-meta';
import { useSetAtom } from 'jotai';
import type {
FC,
HTMLAttributes,
PropsWithChildren,
ReactElement,
} from 'react';
import type { HTMLAttributes, ReactElement, ReactNode } from 'react';
import { useCallback, useRef, useState } from 'react';
import { openQuickSearchModalAtom } from '../../../atoms';
@@ -20,11 +15,15 @@ import type { BaseHeaderProps } from './header';
import { Header } from './header';
import * as styles from './styles.css';
export type WorkspaceHeaderProps = BaseHeaderProps;
export interface WorkspaceHeaderProps
extends BaseHeaderProps,
HTMLAttributes<HTMLDivElement> {
children?: ReactNode;
}
export const BlockSuiteEditorHeader: FC<
PropsWithChildren<WorkspaceHeaderProps> & HTMLAttributes<HTMLDivElement>
> = (props): ReactElement => {
export const BlockSuiteEditorHeader = (
props: WorkspaceHeaderProps
): ReactElement => {
const { workspace, currentPage, children, isPublic } = props;
// fixme(himself65): remove this atom and move it to props
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
@@ -50,6 +49,7 @@ export const BlockSuiteEditorHeader: FC<
const headerRef = useRef<HTMLDivElement>(null);
assertExists(pageMeta);
const title = pageMeta?.title;
return (
<Header ref={headerRef} {...props}>
{children}
@@ -1,7 +1,6 @@
import { isDesktop } from '@affine/env/constant';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type React from 'react';
import { useEffect, useState } from 'react';
const minimumChromeVersion = 102;
@@ -23,7 +22,7 @@ export const shouldShowWarning = () => {
}
};
export const OSWarningMessage: React.FC = () => {
export const OSWarningMessage = () => {
const t = useAFFiNEI18N();
const [notChrome, setNotChrome] = useState(false);
const [notGoodVersion, setNotGoodVersion] = useState(false);
+32 -28
View File
@@ -16,7 +16,7 @@ import {
import { contentLayoutAtom, rootStore } from '@toeverything/infra/atom';
import clsx from 'clsx';
import { useAtomValue, useSetAtom } from 'jotai';
import type { CSSProperties, FC, ReactElement } from 'react';
import type { CSSProperties, ReactElement } from 'react';
import { memo, Suspense, useCallback, useMemo } from 'react';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
@@ -27,13 +27,13 @@ import { TrashButtonGroup } from './blocksuite/workspace-header/header-right-ite
import * as styles from './page-detail-editor.css';
import { pluginContainer } from './page-detail-editor.css';
export type PageDetailEditorProps = {
export interface PageDetailEditorProps {
isPublic?: boolean;
workspace: Workspace;
pageId: string;
onInit: (page: Page, editor: Readonly<EditorContainer>) => void;
onLoad?: (page: Page, editor: EditorContainer) => () => void;
};
}
const EditorWrapper = memo(function EditorWrapper({
workspace,
@@ -128,35 +128,39 @@ const EditorWrapper = memo(function EditorWrapper({
);
});
const PluginContentAdapter = memo<{
interface PluginContentAdapterProps {
windowItem: (div: HTMLDivElement) => () => void;
pluginName: string;
}>(function PluginContentAdapter({ windowItem, pluginName }) {
return (
<div
className={pluginContainer}
ref={useCallback(
(ref: HTMLDivElement | null) => {
if (ref) {
const div = document.createElement('div');
const cleanup = windowItem(div);
ref.appendChild(div);
addCleanup(pluginName, () => {
cleanup();
ref.removeChild(div);
});
}
},
[pluginName, windowItem]
)}
/>
);
});
}
type LayoutPanelProps = {
const PluginContentAdapter = memo<PluginContentAdapterProps>(
function PluginContentAdapter({ windowItem, pluginName }) {
return (
<div
className={pluginContainer}
ref={useCallback(
(ref: HTMLDivElement | null) => {
if (ref) {
const div = document.createElement('div');
const cleanup = windowItem(div);
ref.appendChild(div);
addCleanup(pluginName, () => {
cleanup();
ref.removeChild(div);
});
}
},
[pluginName, windowItem]
)}
/>
);
}
);
interface LayoutPanelProps {
node: LayoutNode;
editorProps: PageDetailEditorProps;
};
}
const LayoutPanel = memo(function LayoutPanel(
props: LayoutPanelProps
@@ -199,7 +203,7 @@ const LayoutPanel = memo(function LayoutPanel(
}
});
export const PageDetailEditor: FC<PageDetailEditorProps> = props => {
export const PageDetailEditor = (props: PageDetailEditorProps) => {
const { workspace, pageId } = props;
const page = useBlockSuiteWorkspacePage(workspace, pageId);
if (!page) {
@@ -1,22 +1,21 @@
import { styled } from '@affine/component';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import type { ChangeEvent } from 'react';
import type React from 'react';
import type { ChangeEvent, PropsWithChildren } from 'react';
import { useRef } from 'react';
export type UploadProps = React.PropsWithChildren<{
export interface UploadProps {
uploadType?: string;
accept?: string;
fileChange: (file: File) => void;
}>;
}
export const Upload: React.FC<UploadProps> = ({
export const Upload = ({
fileChange,
accept,
children,
...props
}) => {
}: PropsWithChildren<UploadProps>) => {
const t = useAFFiNEI18N();
const input_ref = useRef<HTMLInputElement>(null);
const _chooseFile = () => {
@@ -35,6 +34,7 @@ export const Upload: React.FC<UploadProps> = ({
input_ref.current.value = '';
}
};
return (
<UploadStyle onClick={_chooseFile}>
{children ?? <Button>{t['Upload']()}</Button>}
@@ -1,15 +1,16 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloudWorkspaceIcon } from '@blocksuite/icons';
import { useSetAtom } from 'jotai';
import { type CSSProperties, type FC, forwardRef } from 'react';
import { type CSSProperties, forwardRef } from 'react';
import { openDisableCloudAlertModalAtom } from '../../../atoms';
import { stringToColour } from '../../../utils';
import { StyledFooter, StyledSignInButton } from './styles';
export const Footer: FC = () => {
export const Footer = () => {
const t = useAFFiNEI18N();
const setOpen = useSetAtom(openDisableCloudAlertModalAtom);
return (
<StyledFooter data-testid="workspace-list-modal-footer">
<StyledSignInButton
@@ -6,26 +6,31 @@ import {
SettingsIcon,
} from '@blocksuite/icons';
import { useAtom } from 'jotai';
import type { FC, SVGProps } from 'react';
import type { ReactElement, SVGProps } from 'react';
import { useMemo } from 'react';
import { openSettingModalAtom } from '../../../atoms';
export type Config =
| {
title: string;
icon: FC<SVGProps<SVGSVGElement>>;
subPath: WorkspaceSubPath;
}
| {
title: string;
icon: FC<SVGProps<SVGSVGElement>>;
onClick: () => void;
};
type IconComponent = (props: SVGProps<SVGSVGElement>) => ReactElement;
interface ConfigItem {
title: string;
icon: IconComponent;
onClick: () => void;
}
interface ConfigPathItem {
title: string;
icon: IconComponent;
subPath: WorkspaceSubPath;
}
export type Config = ConfigItem | ConfigPathItem;
export const useSwitchToConfig = (workspaceId: string): Config[] => {
const t = useAFFiNEI18N();
const [, setOpenSettingModalAtom] = useAtom(openSettingModalAtom);
return useMemo(
() => [
{
@@ -6,24 +6,23 @@ import { PlusIcon } from '@blocksuite/icons';
import { nanoid } from '@blocksuite/store';
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
import { Command } from 'cmdk';
import type React from 'react';
import { useCallback } from 'react';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
import type { BlockSuiteWorkspace } from '../../../shared';
import { StyledModalFooterContent } from './style';
export type FooterProps = {
export interface FooterProps {
query: string;
onClose: () => void;
blockSuiteWorkspace: BlockSuiteWorkspace;
};
}
export const Footer: React.FC<FooterProps> = ({
export const Footer = ({
query,
onClose,
blockSuiteWorkspace,
}) => {
}: FooterProps) => {
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
const t = useAFFiNEI18N();
const { jumpToPage } = useNavigateHelper();
@@ -32,6 +31,7 @@ export const Footer: React.FC<FooterProps> = ({
query.length > MAX_QUERY_SHOW_LENGTH
? query.slice(0, MAX_QUERY_SHOW_LENGTH) + '...'
: query;
return (
<Command.Item
data-testid="quick-search-add-new-page"
@@ -16,17 +16,17 @@ import {
StyledShortcut,
} from './style';
export type QuickSearchModalProps = {
export interface QuickSearchModalProps {
workspace: AllWorkspace;
open: boolean;
setOpen: (value: boolean) => void;
};
}
export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({
export const QuickSearchModal = ({
open,
setOpen,
workspace,
}) => {
}: QuickSearchModalProps) => {
const blockSuiteWorkspace = workspace?.blockSuiteWorkspace;
const t = useAFFiNEI18N();
const inputRef = useRef<HTMLInputElement>(null);
@@ -40,6 +40,7 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);
// Add ‘⌘+K shortcut keys as switches
useEffect(() => {
const keydown = (e: KeyboardEvent) => {
@@ -59,6 +60,7 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({
return () =>
document.removeEventListener('keydown', keydown, { capture: true });
}, [open, setOpen, setQuery]);
useEffect(() => {
if (open) {
// Waiting for DOM rendering
@@ -68,6 +70,7 @@ export const QuickSearchModal: React.FC<QuickSearchModalProps> = ({
});
}
}, [open]);
return (
<Modal
open={open}
@@ -6,7 +6,7 @@ import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
import { Command } from 'cmdk';
import { useAtomValue } from 'jotai';
import type { Dispatch, FC, SetStateAction } from 'react';
import type { Dispatch, SetStateAction } from 'react';
import { recentPageSettingsAtom } from '../../../atoms';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
@@ -14,18 +14,18 @@ import type { AllWorkspace } from '../../../shared';
import { useSwitchToConfig } from './config';
import { StyledListItem, StyledNotFound } from './style';
export type ResultsProps = {
export interface ResultsProps {
workspace: AllWorkspace;
query: string;
onClose: () => void;
setShowCreatePage: Dispatch<SetStateAction<boolean>>;
};
export const Results: FC<ResultsProps> = ({
}
export const Results = ({
query,
workspace,
setShowCreatePage,
onClose,
}) => {
}: ResultsProps) => {
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
const pageList = useBlockSuitePageMeta(blockSuiteWorkspace);
@@ -52,7 +52,9 @@ export const Results: FC<ResultsProps> = ({
return page.trash !== true;
}
});
setShowCreatePage(resultsPageMeta.length === 0);
if (!query) {
return (
<>
@@ -14,19 +14,19 @@ import {
StyledWorkspaceStatus,
} from './styles';
export type WorkspaceSelectorProps = {
export interface WorkspaceSelectorProps {
currentWorkspace: AllWorkspace;
onClick: () => void;
};
}
/**
* @todo-Doma Co-locate WorkspaceListModal with {@link WorkspaceSelector},
* because it's never used elsewhere.
*/
export const WorkspaceSelector: React.FC<WorkspaceSelectorProps> = ({
export const WorkspaceSelector = ({
currentWorkspace,
onClick,
}) => {
}: WorkspaceSelectorProps) => {
const [name] = useBlockSuiteWorkspaceName(
currentWorkspace?.blockSuiteWorkspace
);
@@ -14,17 +14,16 @@ import { Header } from '../../blocksuite/workspace-header/header';
import * as styles from '../../blocksuite/workspace-header/styles.css';
import { QuickSearchButton } from '../quick-search-button';
export type WorkspaceTitleProps = React.PropsWithChildren<
HeaderProps & {
icon?: ReactNode;
}
>;
export interface WorkspaceTitleProps
extends React.PropsWithChildren<HeaderProps> {
icon?: ReactNode;
}
export const WorkspaceTitle: React.FC<WorkspaceTitleProps> = ({
export const WorkspaceTitle = ({
icon,
children,
...props
}) => {
}: WorkspaceTitleProps) => {
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
return (
<Header {...props}>
@@ -50,6 +49,7 @@ export const WorkspaceModeFilterTab = ({ ...props }: WorkspaceTitleProps) => {
}
setMode(value);
};
return (
<Header {...props}>
<div className={styles.allPageListTitleWrapper}>
+35 -31
View File
@@ -29,7 +29,7 @@ import {
import { usePassiveWorkspaceEffect } from '@toeverything/infra/__internal__/react';
import { currentWorkspaceIdAtom } from '@toeverything/infra/atom';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import type { FC, PropsWithChildren, ReactElement } from 'react';
import type { PropsWithChildren, ReactElement } from 'react';
import { lazy, Suspense, useCallback, useMemo } from 'react';
import { useLocation, useParams } from 'react-router-dom';
@@ -69,15 +69,17 @@ function DefaultProvider({ children }: PropsWithChildren) {
return <>{children}</>;
}
export const QuickSearch: FC = () => {
export const QuickSearch = () => {
const [currentWorkspace] = useCurrentWorkspace();
const [openQuickSearchModal, setOpenQuickSearchModalAtom] = useAtom(
openQuickSearchModalAtom
);
const blockSuiteWorkspace = currentWorkspace?.blockSuiteWorkspace;
if (!blockSuiteWorkspace) {
return null;
}
return (
<QuickSearchModal
workspace={currentWorkspace}
@@ -118,39 +120,41 @@ export const CurrentWorkspaceContext = ({
return <>{children}</>;
};
export const WorkspaceLayout: FC<PropsWithChildren> =
function WorkspacesSuspense({ children }) {
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom);
const jotaiWorkspaces = useAtomValue(rootWorkspacesMetadataAtom);
const meta = useMemo(
() => jotaiWorkspaces.find(x => x.id === currentWorkspaceId),
[currentWorkspaceId, jotaiWorkspaces]
);
export const WorkspaceLayout = function WorkspacesSuspense({
children,
}: PropsWithChildren) {
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom);
const jotaiWorkspaces = useAtomValue(rootWorkspacesMetadataAtom);
const meta = useMemo(
() => jotaiWorkspaces.find(x => x.id === currentWorkspaceId),
[currentWorkspaceId, jotaiWorkspaces]
);
const Provider =
(meta && WorkspaceAdapters[meta.flavour].UI.Provider) ?? DefaultProvider;
return (
<>
{/* load all workspaces is costly, do not block the whole UI */}
<Suspense fallback={null}>
<AllWorkspaceModals />
<CurrentWorkspaceContext>
{/* fixme(himself65): don't re-render whole modals */}
<CurrentWorkspaceModals key={currentWorkspaceId} />
</CurrentWorkspaceContext>
</Suspense>
const Provider =
(meta && WorkspaceAdapters[meta.flavour].UI.Provider) ?? DefaultProvider;
return (
<>
{/* load all workspaces is costly, do not block the whole UI */}
<Suspense fallback={null}>
<AllWorkspaceModals />
<CurrentWorkspaceContext>
<Suspense fallback={<WorkspaceFallback />}>
<Provider>
<WorkspaceLayoutInner>{children}</WorkspaceLayoutInner>
</Provider>
</Suspense>
{/* fixme(himself65): don't re-render whole modals */}
<CurrentWorkspaceModals key={currentWorkspaceId} />
</CurrentWorkspaceContext>
</>
);
};
</Suspense>
<CurrentWorkspaceContext>
<Suspense fallback={<WorkspaceFallback />}>
<Provider>
<WorkspaceLayoutInner>{children}</WorkspaceLayoutInner>
</Provider>
</Suspense>
</CurrentWorkspaceContext>
</>
);
};
export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
export const WorkspaceLayoutInner = ({ children }: PropsWithChildren) => {
const [currentWorkspace] = useCurrentWorkspace();
const { openPage } = useNavigateHelper();
+4 -2
View File
@@ -7,7 +7,7 @@ import {
currentWorkspaceIdAtom,
} from '@toeverything/infra/atom';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import type { FC, ReactElement } from 'react';
import type { ReactElement } from 'react';
import { lazy, Suspense, useCallback, useTransition } from 'react';
import type { SettingAtom } from '../atoms';
@@ -52,7 +52,7 @@ const OnboardingModal = lazy(() =>
}))
);
export const Setting: FC = () => {
export const Setting = () => {
const [currentWorkspace] = useCurrentWorkspace();
const [{ open, workspaceId, activeTab }, setOpenSettingModalAtom] =
useAtom(openSettingModalAtom);
@@ -89,6 +89,7 @@ export function CurrentWorkspaceModals() {
const [openDisableCloudAlertModal, setOpenDisableCloudAlertModal] = useAtom(
openDisableCloudAlertModalAtom
);
return (
<>
<Suspense>
@@ -139,6 +140,7 @@ export const AllWorkspaceModals = (): ReactElement => {
},
[setOpenSettingModalAtom, setOpenWorkspacesModal]
);
return (
<>
<Suspense>