feat(electron): mouse middle click to close tab (#7759)

fix AF-1200
This commit is contained in:
pengx17
2024-08-07 05:19:45 +00:00
parent 352ceca94b
commit b5e543c406
11 changed files with 66 additions and 66 deletions

View File

@@ -1,9 +1,10 @@
import { Tooltip } from '@affine/component/ui/tooltip';
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { useLiveData, useServices } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
import { useCallback, useEffect } from 'react';
import { useEffect } from 'react';
import { openSettingModalAtom } from '../../../atoms';
import {
@@ -35,17 +36,13 @@ export const UserPlanButton = () => {
}, [subscriptionService]);
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
const handleClick = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
setSettingModalAtom({
open: true,
activeTab: 'plans',
scrollAnchor: 'cloudPricingPlan',
});
},
[setSettingModalAtom]
);
const handleClick = useCatchEventCallback(() => {
setSettingModalAtom({
open: true,
activeTab: 'plans',
scrollAnchor: 'cloudPricingPlan',
});
}, [setSettingModalAtom]);
const t = useI18n();

View File

@@ -6,6 +6,7 @@ import {
import { Avatar } from '@affine/component/ui/avatar';
import { Button } from '@affine/component/ui/button';
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 { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
@@ -17,7 +18,7 @@ import {
useServices,
} from '@toeverything/infra';
import { useSetAtom } from 'jotai';
import type { FC, MouseEvent } from 'react';
import type { FC } from 'react';
import { useCallback, useEffect, useState } from 'react';
import {
@@ -53,14 +54,10 @@ export const UserAvatar = () => {
[session]
);
const handleRemoveUserAvatar = useAsyncCallback(
async (e: MouseEvent<HTMLButtonElement>) => {
track.$.settingsPanel.accountSettings.removeAvatar();
e.stopPropagation();
await session.removeAvatar();
},
[session]
);
const handleRemoveUserAvatar = useCatchEventCallback(async () => {
track.$.settingsPanel.accountSettings.removeAvatar();
await session.removeAvatar();
}, [session]);
return (
<Upload

View File

@@ -2,14 +2,14 @@ import { FlexWrapper, Input, notify, Wrapper } from '@affine/component';
import { Button } from '@affine/component/ui/button';
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
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 { validateAndReduceImage } from '@affine/core/utils/reduce-image';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { useI18n } from '@affine/i18n';
import { CameraIcon } from '@blocksuite/icons/rc';
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 * as style from './style.css';
@@ -106,13 +106,9 @@ export const ProfilePanel = () => {
handleUpdateWorkspaceName(input);
}, [handleUpdateWorkspaceName, input]);
const handleRemoveUserAvatar = useAsyncCallback(
async (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
await setWorkspaceAvatar(null);
},
[setWorkspaceAvatar]
);
const handleRemoveUserAvatar = useCatchEventCallback(async () => {
await setWorkspaceAvatar(null);
}, [setWorkspaceAvatar]);
const handleUploadAvatar = useCallback(
(file: File) => {

View File

@@ -1,3 +1,4 @@
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
import { track } from '@affine/core/mixpanel';
import { CloseIcon, DownloadIcon } from '@blocksuite/icons/rc';
import clsx from 'clsx';
@@ -15,7 +16,7 @@ export function AppDownloadButton({
}) {
const [show, setShow] = useState(true);
const handleClose = useCallback(() => {
const handleClose = useCatchEventCallback(() => {
setShow(false);
}, []);
@@ -39,13 +40,7 @@ export function AppDownloadButton({
<DownloadIcon className={styles.icon} />
<span className={styles.ellipsisTextOverflow}>Download App</span>
</div>
<div
className={styles.closeIcon}
onClick={e => {
e.stopPropagation();
handleClose();
}}
>
<div className={styles.closeIcon} onClick={handleClose}>
<CloseIcon />
</div>
<div className={styles.particles} aria-hidden="true"></div>

View File

@@ -1,4 +1,5 @@
import { Tooltip } from '@affine/component';
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
import { popupWindow } from '@affine/core/utils';
import { Unreachable } from '@affine/env/constant';
import { useI18n } from '@affine/i18n';
@@ -118,13 +119,9 @@ function OpenDownloadPage({ updateAvailable }: ButtonContentProps) {
function WhatsNew({ onDismissChangelog }: ButtonContentProps) {
const t = useI18n();
const onClickClose: React.MouseEventHandler = useCallback(
e => {
onDismissChangelog();
e.stopPropagation();
},
[onDismissChangelog]
);
const onClickClose = useCatchEventCallback(() => {
onDismissChangelog();
}, [onDismissChangelog]);
return (
<>
<div className={clsx([styles.whatsNewLabel])}>

View File

@@ -1,11 +1,11 @@
import { Menu } from '@affine/component';
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
import type { Tag } from '@affine/core/modules/tag';
import { CloseIcon, MoreHorizontalIcon } from '@blocksuite/icons/rc';
import { LiveData, useLiveData } from '@toeverything/infra';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import type { MouseEventHandler } from 'react';
import { useCallback, useMemo } from 'react';
import { useMemo } from 'react';
import { stopPropagation } from '../utils';
import * as styles from './page-tags.css';
@@ -62,13 +62,9 @@ export const TagItem = ({
}: TagItemProps) => {
const value = useLiveData(tag?.value$);
const color = useLiveData(tag?.color$);
const handleRemove: MouseEventHandler = useCallback(
e => {
e.stopPropagation();
onRemoved?.();
},
[onRemoved]
);
const handleRemove = useCatchEventCallback(() => {
onRemoved?.();
}, [onRemoved]);
return (
<div
data-testid="page-tag"

View File

@@ -1,4 +1,5 @@
import { Button, Input, Modal } from '@affine/component';
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
import { useI18n } from '@affine/i18n';
import type { KeyboardEvent } from 'react';
import { useCallback, useMemo, useState } from 'react';
@@ -74,7 +75,7 @@ export const CreateCollection = ({
}
onConfirm(value);
}, [onConfirm, value, isNameEmpty]);
const onKeyDown = useCallback(
const onKeyDown = useCatchEventCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Escape') {
if (isNameEmpty) {
@@ -83,7 +84,6 @@ export const CreateCollection = ({
e.currentTarget.blur();
}
}
e.stopPropagation();
},
[isNameEmpty]
);

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { type DependencyList } from 'react';
export type AsyncErrorHandler = (error: Error) => void;
@@ -17,7 +17,7 @@ export const AsyncCallbackContext = React.createContext<AsyncErrorHandler>(
*/
export function useAsyncCallback<T extends any[]>(
callback: (...args: T) => Promise<void>,
deps: any[]
deps: DependencyList
): (...args: T) => void {
const handleAsyncError = React.useContext(AsyncCallbackContext);
return React.useCallback(

View File

@@ -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
);
};

View File

@@ -14,6 +14,7 @@ import {
import { appSidebarWidthAtom } from '@affine/core/components/app-sidebar/index.jotai';
import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls';
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 { apis, events } from '@affine/electron-api';
import { useI18n } from '@affine/i18n';
@@ -92,15 +93,19 @@ const WorkbenchTab = ({
},
[tabsHeaderService, workbench.id]
);
const onCloseTab: MouseEventHandler = useAsyncCallback(
const handleAuxClick: MouseEventHandler = useCatchEventCallback(
async e => {
e.stopPropagation();
await tabsHeaderService.closeTab?.(workbench.id);
if (e.button === 1) {
await tabsHeaderService.closeTab?.(workbench.id);
}
},
[tabsHeaderService, workbench.id]
);
const handleCloseTab = useCatchEventCallback(async () => {
await tabsHeaderService.closeTab?.(workbench.id);
}, [tabsHeaderService, workbench.id]);
const { dropTargetRef, closestEdge } = useDropTarget<AffineDNDData>(
() => ({
closestEdge: {
@@ -154,6 +159,7 @@ const WorkbenchTab = ({
onContextMenu={() => {
onContextMenu(viewIdx);
}}
onAuxClick={handleAuxClick}
onClick={e => {
e.stopPropagation();
onActivateView(viewIdx);
@@ -185,7 +191,7 @@ const WorkbenchTab = ({
<button
data-testid="close-tab-button"
className={styles.tabCloseButton}
onClick={onCloseTab}
onClick={handleCloseTab}
>
<CloseIcon />
</button>

View File

@@ -1,5 +1,5 @@
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 { type To } from 'history';
import { forwardRef, type MouseEvent } from 'react';
@@ -21,10 +21,9 @@ export const WorkbenchLink = forwardRef<
const link =
basename +
(typeof to === 'string' ? to : `${to.pathname}${to.search}${to.hash}`);
const handleClick = useAsyncCallback(
const handleClick = useCatchEventCallback(
async (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
event.stopPropagation();
if (onClick?.(event) === false) {
return;
}