Skip to content

Commit

Permalink
feat(electron): multi tabs support (#7440)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
pengx17 committed Jul 29, 2024
1 parent 622715d commit 1efc1d0
Show file tree
Hide file tree
Showing 88 changed files with 3,157 additions and 942 deletions.
9 changes: 5 additions & 4 deletions packages/common/infra/src/app-config-storage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { z } from 'zod';

const _appConfigSchema = z.object({
export const appConfigSchema = z.object({
/** whether to show onboarding first */
onBoarding: z.boolean().optional().default(true),
});
export type AppConfigSchema = z.infer<typeof _appConfigSchema>;
export const defaultAppConfig = _appConfigSchema.parse({});

export type AppConfigSchema = z.infer<typeof appConfigSchema>;
export const defaultAppConfig = appConfigSchema.parse({});

const _storage: Record<number, any> = {};
let _inMemoryId = 0;
Expand Down Expand Up @@ -48,7 +49,7 @@ class Storage<T extends object> {
}

get(): T;
get(key: keyof T): T[keyof T];
get<K extends keyof T>(key: K): T[K];
/**
* get config, if key is provided, return the value of the key
* @param key
Expand Down
1 change: 1 addition & 0 deletions packages/common/infra/src/atom/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export function setupEditorFlags(docCollection: DocCollection) {

type SetStateAction<Value> = Value | ((prev: Value) => Value);

// todo(@pengx17): use global state instead
const appSettingEffect = atomEffect(get => {
const settings = get(appSettingBaseAtom);
// some values in settings should be synced into electron side
Expand Down
2 changes: 1 addition & 1 deletion packages/common/infra/src/framework/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export { Framework } from './framework';
export { createIdentifier } from './identifier';
export type { ResolveOptions } from './provider';
export { FrameworkProvider } from './provider';
export type { GeneralIdentifier } from './types';
export type { GeneralIdentifier, Identifier } from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -265,7 +260,6 @@ export const OnboardingPage = ({
}
return (
<ScrollableLayout
headerItems={isWindowsDesktop ? windowControl : null}
isMacosDesktop={isMacosDesktop}
isWindowsDesktop={isWindowsDesktop}
>
Expand Down
14 changes: 14 additions & 0 deletions packages/frontend/component/src/theme/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -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%;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
6 changes: 2 additions & 4 deletions packages/frontend/component/src/ui/scrollbar/scrollbar.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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}
Expand All @@ -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>
Expand Down
48 changes: 22 additions & 26 deletions packages/frontend/component/src/ui/scrollbar/use-has-scroll-top.tsx
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {

private _renderAIOnboarding() {
return this.isLoading ||
!this.host.doc.awarenessStore.getFlag('enable_ai_onboarding')
!this.host?.doc.awarenessStore.getFlag('enable_ai_onboarding')
? nothing
: html`<div
style=${styleMap({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const tableHeaderTimestamp = style({

export const tableHeaderDivider = style({
height: 0,
borderTop: `1px solid ${cssVar('borderColor')}`,
borderTop: `0.5px solid ${cssVar('borderColor')}`,
width: '100%',
margin: '8px 0',
});
Expand Down
12 changes: 2 additions & 10 deletions packages/frontend/core/src/components/app-sidebar/index.css.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cssVar } from '@toeverything/theme';
import { globalStyle, style } from '@vanilla-extract/css';
import { style } from '@vanilla-extract/css';
export const floatingMaxWidth = 768;
export const navWrapperStyle = style({
paddingBottom: '8px',
Expand All @@ -11,7 +11,7 @@ export const navWrapperStyle = style({
},
selectors: {
'&[data-has-border=true]': {
borderRight: `1px solid ${cssVar('borderColor')}`,
borderRight: `0.5px solid ${cssVar('borderColor')}`,
},
'&[data-is-floating="true"]': {
backgroundColor: cssVar('backgroundPrimaryColor'),
Expand Down Expand Up @@ -45,14 +45,6 @@ export const navHeaderStyle = style({
['WebkitAppRegion' as string]: 'drag',
});

globalStyle(
`html[data-fullscreen="false"]
${navHeaderStyle}[data-is-macos-electron="true"]`,
{
paddingLeft: '90px',
}
);

export const navBodyStyle = style({
flex: '1 1 auto',
height: 'calc(100% - 52px)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';

export const APP_SIDEBAR_OPEN = 'app-sidebar-open';
export const isMobile = window.innerWidth < 768;
export const isMobile = window.innerWidth < 768 && !environment.isDesktop;

export const appSidebarOpenAtom = atomWithStorage(APP_SIDEBAR_OPEN, !isMobile);
export const appSidebarFloatingAtom = atom(isMobile);
Expand Down
13 changes: 8 additions & 5 deletions packages/frontend/core/src/components/app-sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,18 @@ const MIN_WIDTH = 248;
export function AppSidebar({
children,
clientBorder,
translucentUI,
}: AppSidebarProps): ReactElement {
const [open, setOpen] = useAtom(appSidebarOpenAtom);
const [width, setWidth] = useAtom(appSidebarWidthAtom);
const [floating, setFloating] = useAtom(appSidebarFloatingAtom);
const [resizing, setResizing] = useAtom(appSidebarResizingAtom);

useEffect(() => {
// do not float app sidebar on desktop
if (environment.isDesktop) {
return;
}

function onResize() {
const isFloatingMaxWidth = window.matchMedia(
`(max-width: ${floatingMaxWidth}px)`
Expand All @@ -75,10 +79,8 @@ export function AppSidebar({
};
}, [open, setFloating, setOpen, width]);

const hasRightBorder = !environment.isDesktop && !clientBorder;
const isMacosDesktop = environment.isDesktop && environment.isMacOs;
const hasRightBorder =
!environment.isDesktop || (!clientBorder && !translucentUI);

return (
<>
<ResizePanel
Expand All @@ -96,12 +98,13 @@ export function AppSidebar({
resizeHandleOffset={clientBorder ? 8 : 0}
resizeHandleVerticalPadding={clientBorder ? 16 : 0}
data-transparent
data-open={open}
data-has-border={hasRightBorder}
data-testid="app-sidebar-wrapper"
data-is-macos-electron={isMacosDesktop}
>
<nav className={navStyle} data-testid="app-sidebar">
<SidebarHeader />
{!environment.isDesktop && <SidebarHeader />}
<div className={navBodyStyle} data-testid="sliderBar-inner">
{children}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useHasScrollTop } from '@affine/component';
import * as ScrollArea from '@radix-ui/react-scroll-area';
import clsx from 'clsx';
import type { PropsWithChildren } from 'react';
import { useRef } from 'react';
import { type PropsWithChildren } from 'react';

import * as styles from './index.css';

Expand All @@ -11,8 +10,7 @@ export function SidebarContainer({ children }: PropsWithChildren) {
}

export function SidebarScrollableContainer({ children }: PropsWithChildren) {
const ref = useRef<HTMLDivElement>(null);
const hasScrollTop = useHasScrollTop(ref);
const [setContainer, hasScrollTop] = useHasScrollTop();
return (
<ScrollArea.Root className={styles.scrollableContainerRoot}>
<div
Expand All @@ -21,7 +19,7 @@ export function SidebarScrollableContainer({ children }: PropsWithChildren) {
/>
<ScrollArea.Viewport
className={clsx([styles.scrollableViewport])}
ref={ref}
ref={setContainer}
>
<div className={clsx([styles.scrollableContainer])}>{children}</div>
</ScrollArea.Viewport>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useAtomValue } from 'jotai';

import { NavigationButtons } from '../../../modules/navigation';
import { navHeaderStyle } from '../index.css';
import { appSidebarOpenAtom } from '../index.jotai';
import { SidebarSwitch } from './sidebar-switch';
Expand All @@ -9,13 +8,8 @@ export const SidebarHeader = () => {
const open = useAtomValue(appSidebarOpenAtom);

return (
<div
className={navHeaderStyle}
data-open={open}
data-is-macos-electron={environment.isDesktop && environment.isMacOs}
>
<div className={navHeaderStyle} data-open={open}>
<SidebarSwitch show={open} />
<NavigationButtons />
</div>
);
};
Expand Down
8 changes: 3 additions & 5 deletions packages/frontend/core/src/components/page-list/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
useCallback,
useEffect,
useImperativeHandle,
useRef,
} from 'react';

import { usePageHeaderColsDef } from './header-col-def';
Expand Down Expand Up @@ -156,8 +155,7 @@ export const ListScrollContainer = forwardRef<
HTMLDivElement,
PropsWithChildren<ListScrollContainerProps>
>(({ className, children, style }, ref) => {
const containerRef = useRef<HTMLDivElement | null>(null);
const hasScrollTop = useHasScrollTop(containerRef);
const [setContainer, hasScrollTop] = useHasScrollTop();

const setNodeRef = useCallback(
(r: HTMLDivElement) => {
Expand All @@ -168,9 +166,9 @@ export const ListScrollContainer = forwardRef<
ref.current = r;
}
}
containerRef.current = r;
return setContainer(r);
},
[ref]
[ref, setContainer]
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const tableHeader = style({
transform: 'translateY(-0.5px)', // fix sticky look through issue
});
globalStyle(`[data-has-scroll-top=true] ${tableHeader}`, {
boxShadow: `0 1px ${cssVar('borderColor')}`,
boxShadow: `0 0.5px ${cssVar('borderColor')}`,
});
export const headerTitleSelectionIconWrapper = style({
display: 'flex',
Expand Down
Loading

0 comments on commit 1efc1d0

Please sign in to comment.