mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-22 19:53:48 +08:00
@@ -1,9 +1,10 @@
|
|||||||
import { Tooltip } from '@affine/component/ui/tooltip';
|
import { Tooltip } from '@affine/component/ui/tooltip';
|
||||||
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useServices } from '@toeverything/infra';
|
import { useLiveData, useServices } from '@toeverything/infra';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { openSettingModalAtom } from '../../../atoms';
|
import { openSettingModalAtom } from '../../../atoms';
|
||||||
import {
|
import {
|
||||||
@@ -35,17 +36,13 @@ export const UserPlanButton = () => {
|
|||||||
}, [subscriptionService]);
|
}, [subscriptionService]);
|
||||||
|
|
||||||
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
|
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||||
const handleClick = useCallback(
|
const handleClick = useCatchEventCallback(() => {
|
||||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
setSettingModalAtom({
|
||||||
e.stopPropagation();
|
open: true,
|
||||||
setSettingModalAtom({
|
activeTab: 'plans',
|
||||||
open: true,
|
scrollAnchor: 'cloudPricingPlan',
|
||||||
activeTab: 'plans',
|
});
|
||||||
scrollAnchor: 'cloudPricingPlan',
|
}, [setSettingModalAtom]);
|
||||||
});
|
|
||||||
},
|
|
||||||
[setSettingModalAtom]
|
|
||||||
);
|
|
||||||
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
|
|||||||
+6
-9
@@ -6,6 +6,7 @@ import {
|
|||||||
import { Avatar } from '@affine/component/ui/avatar';
|
import { Avatar } from '@affine/component/ui/avatar';
|
||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import { track } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { SubscriptionPlan } from '@affine/graphql';
|
import { SubscriptionPlan } from '@affine/graphql';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -17,7 +18,7 @@ import {
|
|||||||
useServices,
|
useServices,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import type { FC, MouseEvent } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -53,14 +54,10 @@ export const UserAvatar = () => {
|
|||||||
[session]
|
[session]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRemoveUserAvatar = useAsyncCallback(
|
const handleRemoveUserAvatar = useCatchEventCallback(async () => {
|
||||||
async (e: MouseEvent<HTMLButtonElement>) => {
|
track.$.settingsPanel.accountSettings.removeAvatar();
|
||||||
track.$.settingsPanel.accountSettings.removeAvatar();
|
await session.removeAvatar();
|
||||||
e.stopPropagation();
|
}, [session]);
|
||||||
await session.removeAvatar();
|
|
||||||
},
|
|
||||||
[session]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Upload
|
<Upload
|
||||||
|
|||||||
+5
-9
@@ -2,14 +2,14 @@ import { FlexWrapper, Input, notify, Wrapper } from '@affine/component';
|
|||||||
import { Button } from '@affine/component/ui/button';
|
import { Button } from '@affine/component/ui/button';
|
||||||
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
|
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
|
||||||
import { Upload } from '@affine/core/components/pure/file-upload';
|
import { Upload } from '@affine/core/components/pure/file-upload';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||||
import { validateAndReduceImage } from '@affine/core/utils/reduce-image';
|
import { validateAndReduceImage } from '@affine/core/utils/reduce-image';
|
||||||
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
|
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { CameraIcon } from '@blocksuite/icons/rc';
|
import { CameraIcon } from '@blocksuite/icons/rc';
|
||||||
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||||
import type { KeyboardEvent, MouseEvent } from 'react';
|
import type { KeyboardEvent } from 'react';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import * as style from './style.css';
|
import * as style from './style.css';
|
||||||
@@ -106,13 +106,9 @@ export const ProfilePanel = () => {
|
|||||||
handleUpdateWorkspaceName(input);
|
handleUpdateWorkspaceName(input);
|
||||||
}, [handleUpdateWorkspaceName, input]);
|
}, [handleUpdateWorkspaceName, input]);
|
||||||
|
|
||||||
const handleRemoveUserAvatar = useAsyncCallback(
|
const handleRemoveUserAvatar = useCatchEventCallback(async () => {
|
||||||
async (e: MouseEvent<HTMLButtonElement>) => {
|
await setWorkspaceAvatar(null);
|
||||||
e.stopPropagation();
|
}, [setWorkspaceAvatar]);
|
||||||
await setWorkspaceAvatar(null);
|
|
||||||
},
|
|
||||||
[setWorkspaceAvatar]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleUploadAvatar = useCallback(
|
const handleUploadAvatar = useCallback(
|
||||||
(file: File) => {
|
(file: File) => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import { track } from '@affine/core/mixpanel';
|
import { track } from '@affine/core/mixpanel';
|
||||||
import { CloseIcon, DownloadIcon } from '@blocksuite/icons/rc';
|
import { CloseIcon, DownloadIcon } from '@blocksuite/icons/rc';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
@@ -15,7 +16,7 @@ export function AppDownloadButton({
|
|||||||
}) {
|
}) {
|
||||||
const [show, setShow] = useState(true);
|
const [show, setShow] = useState(true);
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCatchEventCallback(() => {
|
||||||
setShow(false);
|
setShow(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -39,13 +40,7 @@ export function AppDownloadButton({
|
|||||||
<DownloadIcon className={styles.icon} />
|
<DownloadIcon className={styles.icon} />
|
||||||
<span className={styles.ellipsisTextOverflow}>Download App</span>
|
<span className={styles.ellipsisTextOverflow}>Download App</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className={styles.closeIcon} onClick={handleClose}>
|
||||||
className={styles.closeIcon}
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleClose();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.particles} aria-hidden="true"></div>
|
<div className={styles.particles} aria-hidden="true"></div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Tooltip } from '@affine/component';
|
import { Tooltip } from '@affine/component';
|
||||||
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import { popupWindow } from '@affine/core/utils';
|
import { popupWindow } from '@affine/core/utils';
|
||||||
import { Unreachable } from '@affine/env/constant';
|
import { Unreachable } from '@affine/env/constant';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -118,13 +119,9 @@ function OpenDownloadPage({ updateAvailable }: ButtonContentProps) {
|
|||||||
|
|
||||||
function WhatsNew({ onDismissChangelog }: ButtonContentProps) {
|
function WhatsNew({ onDismissChangelog }: ButtonContentProps) {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const onClickClose: React.MouseEventHandler = useCallback(
|
const onClickClose = useCatchEventCallback(() => {
|
||||||
e => {
|
onDismissChangelog();
|
||||||
onDismissChangelog();
|
}, [onDismissChangelog]);
|
||||||
e.stopPropagation();
|
|
||||||
},
|
|
||||||
[onDismissChangelog]
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={clsx([styles.whatsNewLabel])}>
|
<div className={clsx([styles.whatsNewLabel])}>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Menu } from '@affine/component';
|
import { Menu } from '@affine/component';
|
||||||
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import type { Tag } from '@affine/core/modules/tag';
|
import type { Tag } from '@affine/core/modules/tag';
|
||||||
import { CloseIcon, MoreHorizontalIcon } from '@blocksuite/icons/rc';
|
import { CloseIcon, MoreHorizontalIcon } from '@blocksuite/icons/rc';
|
||||||
import { LiveData, useLiveData } from '@toeverything/infra';
|
import { LiveData, useLiveData } from '@toeverything/infra';
|
||||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { MouseEventHandler } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
|
|
||||||
import { stopPropagation } from '../utils';
|
import { stopPropagation } from '../utils';
|
||||||
import * as styles from './page-tags.css';
|
import * as styles from './page-tags.css';
|
||||||
@@ -62,13 +62,9 @@ export const TagItem = ({
|
|||||||
}: TagItemProps) => {
|
}: TagItemProps) => {
|
||||||
const value = useLiveData(tag?.value$);
|
const value = useLiveData(tag?.value$);
|
||||||
const color = useLiveData(tag?.color$);
|
const color = useLiveData(tag?.color$);
|
||||||
const handleRemove: MouseEventHandler = useCallback(
|
const handleRemove = useCatchEventCallback(() => {
|
||||||
e => {
|
onRemoved?.();
|
||||||
e.stopPropagation();
|
}, [onRemoved]);
|
||||||
onRemoved?.();
|
|
||||||
},
|
|
||||||
[onRemoved]
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-testid="page-tag"
|
data-testid="page-tag"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Button, Input, Modal } from '@affine/component';
|
import { Button, Input, Modal } from '@affine/component';
|
||||||
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import type { KeyboardEvent } from 'react';
|
import type { KeyboardEvent } from 'react';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
@@ -74,7 +75,7 @@ export const CreateCollection = ({
|
|||||||
}
|
}
|
||||||
onConfirm(value);
|
onConfirm(value);
|
||||||
}, [onConfirm, value, isNameEmpty]);
|
}, [onConfirm, value, isNameEmpty]);
|
||||||
const onKeyDown = useCallback(
|
const onKeyDown = useCatchEventCallback(
|
||||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
if (isNameEmpty) {
|
if (isNameEmpty) {
|
||||||
@@ -83,7 +84,6 @@ export const CreateCollection = ({
|
|||||||
e.currentTarget.blur();
|
e.currentTarget.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.stopPropagation();
|
|
||||||
},
|
},
|
||||||
[isNameEmpty]
|
[isNameEmpty]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { type DependencyList } from 'react';
|
||||||
|
|
||||||
export type AsyncErrorHandler = (error: Error) => void;
|
export type AsyncErrorHandler = (error: Error) => void;
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ export const AsyncCallbackContext = React.createContext<AsyncErrorHandler>(
|
|||||||
*/
|
*/
|
||||||
export function useAsyncCallback<T extends any[]>(
|
export function useAsyncCallback<T extends any[]>(
|
||||||
callback: (...args: T) => Promise<void>,
|
callback: (...args: T) => Promise<void>,
|
||||||
deps: any[]
|
deps: DependencyList
|
||||||
): (...args: T) => void {
|
): (...args: T) => void {
|
||||||
const handleAsyncError = React.useContext(AsyncCallbackContext);
|
const handleAsyncError = React.useContext(AsyncCallbackContext);
|
||||||
return React.useCallback(
|
return React.useCallback(
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { type DependencyList, type SyntheticEvent } from 'react';
|
||||||
|
|
||||||
|
import { useAsyncCallback } from './affine-async-hooks';
|
||||||
|
|
||||||
|
export const useCatchEventCallback = <E extends SyntheticEvent>(
|
||||||
|
cb: (e: E) => void | Promise<void>,
|
||||||
|
deps: DependencyList
|
||||||
|
) => {
|
||||||
|
return useAsyncCallback(
|
||||||
|
async (e: E) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
await cb(e);
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
deps
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
import { appSidebarWidthAtom } from '@affine/core/components/app-sidebar/index.jotai';
|
import { appSidebarWidthAtom } from '@affine/core/components/app-sidebar/index.jotai';
|
||||||
import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls';
|
import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { apis, events } from '@affine/electron-api';
|
import { apis, events } from '@affine/electron-api';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -92,15 +93,19 @@ const WorkbenchTab = ({
|
|||||||
},
|
},
|
||||||
[tabsHeaderService, workbench.id]
|
[tabsHeaderService, workbench.id]
|
||||||
);
|
);
|
||||||
const onCloseTab: MouseEventHandler = useAsyncCallback(
|
const handleAuxClick: MouseEventHandler = useCatchEventCallback(
|
||||||
async e => {
|
async e => {
|
||||||
e.stopPropagation();
|
if (e.button === 1) {
|
||||||
|
await tabsHeaderService.closeTab?.(workbench.id);
|
||||||
await tabsHeaderService.closeTab?.(workbench.id);
|
}
|
||||||
},
|
},
|
||||||
[tabsHeaderService, workbench.id]
|
[tabsHeaderService, workbench.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCloseTab = useCatchEventCallback(async () => {
|
||||||
|
await tabsHeaderService.closeTab?.(workbench.id);
|
||||||
|
}, [tabsHeaderService, workbench.id]);
|
||||||
|
|
||||||
const { dropTargetRef, closestEdge } = useDropTarget<AffineDNDData>(
|
const { dropTargetRef, closestEdge } = useDropTarget<AffineDNDData>(
|
||||||
() => ({
|
() => ({
|
||||||
closestEdge: {
|
closestEdge: {
|
||||||
@@ -154,6 +159,7 @@ const WorkbenchTab = ({
|
|||||||
onContextMenu={() => {
|
onContextMenu={() => {
|
||||||
onContextMenu(viewIdx);
|
onContextMenu(viewIdx);
|
||||||
}}
|
}}
|
||||||
|
onAuxClick={handleAuxClick}
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onActivateView(viewIdx);
|
onActivateView(viewIdx);
|
||||||
@@ -185,7 +191,7 @@ const WorkbenchTab = ({
|
|||||||
<button
|
<button
|
||||||
data-testid="close-tab-button"
|
data-testid="close-tab-button"
|
||||||
className={styles.tabCloseButton}
|
className={styles.tabCloseButton}
|
||||||
onClick={onCloseTab}
|
onClick={handleCloseTab}
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { type To } from 'history';
|
import { type To } from 'history';
|
||||||
import { forwardRef, type MouseEvent } from 'react';
|
import { forwardRef, type MouseEvent } from 'react';
|
||||||
@@ -21,10 +21,9 @@ export const WorkbenchLink = forwardRef<
|
|||||||
const link =
|
const link =
|
||||||
basename +
|
basename +
|
||||||
(typeof to === 'string' ? to : `${to.pathname}${to.search}${to.hash}`);
|
(typeof to === 'string' ? to : `${to.pathname}${to.search}${to.hash}`);
|
||||||
const handleClick = useAsyncCallback(
|
const handleClick = useCatchEventCallback(
|
||||||
async (event: React.MouseEvent<HTMLAnchorElement>) => {
|
async (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
|
||||||
if (onClick?.(event) === false) {
|
if (onClick?.(event) === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user