feat(electron): multi tabs support (#7440)

use https://www.electronjs.org/docs/latest/api/web-contents-view to serve different tab views
added tabs view manager in electron to handle multi-view actions and events.

fix AF-1111
fix AF-999
fix PD-1459
fix AF-964
PD-1458
This commit is contained in:
pengx17
2024-07-29 11:05:22 +00:00
parent 622715d2f3
commit 1efc1d0f5b
88 changed files with 3160 additions and 945 deletions

View File

@@ -100,11 +100,9 @@ export const ScrollableLayout = ({
export const OnboardingPage = ({
user,
onOpenAffine,
windowControl,
}: {
user: User;
onOpenAffine: () => void;
windowControl?: React.ReactNode;
}) => {
const location = useLocation();
const navigate = useNavigate();
@@ -150,19 +148,16 @@ export const OnboardingPage = ({
return (
<ScrollableLayout
headerItems={
<>
{isWindowsDesktop ? windowControl : null}
<Button
className={clsx(styles.button, {
[styles.disableButton]: questionIdx === 0,
[styles.windowsAppButton]: isWindowsDesktop,
})}
size="extraLarge"
onClick={() => setQuestionIdx(questions.length)}
>
Skip
</Button>
</>
<Button
className={clsx(styles.button, {
[styles.disableButton]: questionIdx === 0,
[styles.windowsAppButton]: isWindowsDesktop,
})}
size="extraLarge"
onClick={() => setQuestionIdx(questions.length)}
>
Skip
</Button>
}
isMacosDesktop={isMacosDesktop}
isWindowsDesktop={isWindowsDesktop}
@@ -265,7 +260,6 @@ export const OnboardingPage = ({
}
return (
<ScrollableLayout
headerItems={isWindowsDesktop ? windowControl : null}
isMacosDesktop={isMacosDesktop}
isWindowsDesktop={isWindowsDesktop}
>

View File

@@ -288,6 +288,20 @@ textarea
-webkit-app-region: no-drag;
}
html[data-active='false'] {
opacity: 0;
transition: opacity 0.2s 0.1s;
}
html[data-active='true']:has([data-blur-background='true']) {
opacity: 1;
transition: opacity 0.2s;
}
html[data-active='false'] * {
-webkit-app-region: no-drag !important;
}
html,
body {
height: 100%;

View File

@@ -80,7 +80,10 @@ export const NotificationCard = ({ notification }: NotificationCardProps) => {
data-float={!!thumb}
className={clsx(styles.headAlignWrapper, styles.closeButton)}
>
<IconButton onClick={onDismiss}>
<IconButton
data-testid="notification-close-button"
onClick={onDismiss}
>
<CloseIcon className={styles.closeIcon} width={16} height={16} />
</IconButton>
</div>

View File

@@ -1,7 +1,6 @@
import * as ScrollArea from '@radix-ui/react-scroll-area';
import clsx from 'clsx';
import type { PropsWithChildren } from 'react';
import { useRef } from 'react';
import * as styles from './index.css';
import { useHasScrollTop } from './use-has-scroll-top';
@@ -24,8 +23,7 @@ export const ScrollableContainer = ({
viewPortClassName,
scrollBarClassName,
}: PropsWithChildren<ScrollableContainerProps>) => {
const ref = useRef<HTMLDivElement>(null);
const hasScrollTop = useHasScrollTop(ref);
const [setContainer, hasScrollTop] = useHasScrollTop();
return (
<ScrollArea.Root
style={_styles}
@@ -37,7 +35,7 @@ export const ScrollableContainer = ({
/>
<ScrollArea.Viewport
className={clsx([styles.scrollableViewport, viewPortClassName])}
ref={ref}
ref={setContainer}
>
<div className={styles.scrollableContainer}>{children}</div>
</ScrollArea.Viewport>

View File

@@ -1,31 +1,27 @@
import type { RefObject } from 'react';
import { useEffect, useState } from 'react';
import { debounce } from 'lodash-es';
import { useMemo, useState } from 'react';
export function useHasScrollTop(ref: RefObject<HTMLElement> | null) {
export function useHasScrollTop() {
const [hasScrollTop, setHasScrollTop] = useState(false);
useEffect(() => {
if (!ref?.current) {
return;
}
const container = ref.current;
function updateScrollTop() {
if (container) {
setTimeout(() => {
const hasScrollTop = container.scrollTop > 0;
setHasScrollTop(hasScrollTop);
});
}
}
container.addEventListener('scroll', updateScrollTop);
updateScrollTop();
return () => {
container.removeEventListener('scroll', updateScrollTop);
const containerRefFn = useMemo(() => {
let unsub: (() => void) | null = null;
return (container: HTMLElement | null) => {
unsub?.();
const updateScrollTop = debounce(() => {
if (container) {
setTimeout(() => {
const hasScrollTop = container.scrollTop > 0;
setHasScrollTop(hasScrollTop);
});
}
}, 50);
container?.addEventListener('scroll', updateScrollTop);
updateScrollTop();
unsub = () => {
container?.removeEventListener('scroll', updateScrollTop);
};
};
}, [ref]);
}, []);
return hasScrollTop;
return [containerRefFn, hasScrollTop] as const;
}