Skip to content

Commit

Permalink
try using width atom for syncing app headers position
Browse files Browse the repository at this point in the history
  • Loading branch information
pengx17 committed Jul 31, 2024
1 parent 596ea69 commit 45e961e
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 180 deletions.
12 changes: 0 additions & 12 deletions packages/frontend/core/src/layouts/styles.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,3 @@ export const desktopTabsHeader = style({
width: '100%',
overflow: 'hidden',
});

export const desktopTabsHeaderTopLeft = style({
display: 'flex',
flexFlow: 'row',
alignItems: 'center',
transition: 'width 0.3s, padding 0.3s',
justifyContent: 'space-between',
marginRight: -8, // make room for tab's padding
padding: '0 16px',
flexShrink: 0,
['WebkitAppRegion' as string]: 'drag',
});
31 changes: 8 additions & 23 deletions packages/frontend/core/src/layouts/workspace-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,9 @@ import { WorkspaceAIOnboarding } from '../components/affine/ai-onboarding';
import { AppContainer } from '../components/affine/app-container';
import { SyncAwareness } from '../components/affine/awareness';
import {
appSidebarFloatingAtom,
appSidebarOpenAtom,
appSidebarResizingAtom,
SidebarSwitch,
} from '../components/app-sidebar';
import { appSidebarWidthAtom } from '../components/app-sidebar/index.jotai';
import { AIIsland } from '../components/pure/ai-island';
import { RootAppSidebar } from '../components/root-app-sidebar';
import { MainContainer } from '../components/workspace';
Expand Down Expand Up @@ -181,29 +178,17 @@ const WorkspaceLayoutProviders = ({ children }: PropsWithChildren) => {
};

const DesktopLayout = ({ children }: PropsWithChildren) => {
const resizing = useAtomValue(appSidebarResizingAtom);
const sidebarWidth = useAtomValue(appSidebarWidthAtom);
const sidebarOpen = useAtomValue(appSidebarOpenAtom);
const sidebarFloating = useAtomValue(appSidebarFloatingAtom);
const sidebarResizing = useAtomValue(appSidebarResizingAtom);
const isMacosDesktop = environment.isDesktop && environment.isMacOs;

return (
<div className={styles.desktopAppViewContainer}>
<div className={styles.desktopTabsHeader}>
<div
className={styles.desktopTabsHeaderTopLeft}
style={{
transition: sidebarResizing ? 'none' : undefined,
paddingLeft:
isMacosDesktop && sidebarOpen && !sidebarFloating ? 90 : 16,
width: sidebarOpen && !sidebarFloating ? sidebarWidth : 130,
}}
>
<SidebarSwitch show />
<NavigationButtons />
</div>
<AppTabsHeader reportBoundingUpdate={!resizing} />
<AppTabsHeader
left={
<>
<SidebarSwitch show />
<NavigationButtons />
</>
}
/>
</div>
<div className={styles.desktopAppViewMain}>
<RootAppSidebar />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { IconButton, Loading, observeResize } from '@affine/component';
import { IconButton, Loading } from '@affine/component';
import {
appSidebarFloatingAtom,
appSidebarOpenAtom,
appSidebarResizingAtom,
} from '@affine/core/components/app-sidebar';
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 { DesktopStateSynchronizer } from '@affine/core/modules/workbench/services/desktop-state-synchronizer';
import type { WorkbenchMeta } from '@affine/electron-api';
import { apis } from '@affine/electron-api';
import { apis, events } from '@affine/electron-api';
import {
CloseIcon,
DeleteIcon,
Expand All @@ -21,13 +27,15 @@ import {
useService,
useServiceOptional,
} from '@toeverything/infra';
import { debounce, partition } from 'lodash-es';
import clsx from 'clsx';
import { useAtomValue } from 'jotai';
import { partition } from 'lodash-es';
import {
Fragment,
type MouseEventHandler,
type ReactNode,
useEffect,
useRef,
useState,
} from 'react';

import {
Expand Down Expand Up @@ -140,13 +148,39 @@ const WorkbenchTab = ({
);
};

const useIsFullScreen = () => {
const [fullScreen, setFullScreen] = useState(false);

useEffect(() => {
apis?.ui
.isFullScreen()
.then(setFullScreen)
.then(() => {
events?.ui.onFullScreen(setFullScreen);
})
.catch(console.error);
}, []);
return fullScreen;
};

export const AppTabsHeader = ({
style,
reportBoundingUpdate,
mode = 'app',
className,
left,
}: {
style?: React.CSSProperties;
reportBoundingUpdate?: boolean;
mode?: 'app' | 'shell';
className?: string;
left?: ReactNode;
}) => {
const sidebarWidth = useAtomValue(appSidebarWidthAtom);
const sidebarOpen = useAtomValue(appSidebarOpenAtom);
const sidebarFloating = useAtomValue(appSidebarFloatingAtom);
const sidebarResizing = useAtomValue(appSidebarResizingAtom);
const isMacosDesktop = environment.isDesktop && environment.isMacOs;
const fullScreen = useIsFullScreen();

const tabsHeaderService = useService(AppTabsHeaderService);
const tabs = useLiveData(tabsHeaderService.tabsStatus$);

Expand All @@ -160,42 +194,36 @@ export const AppTabsHeader = ({
await tabsHeaderService.onToggleRightSidebar();
}, [tabsHeaderService]);

const ref = useRef<HTMLDivElement | null>(null);

useServiceOptional(DesktopStateSynchronizer);

useEffect(() => {
if (ref.current && reportBoundingUpdate) {
return observeResize(
ref.current,
debounce(() => {
if (document.visibilityState === 'visible') {
const rect = ref.current?.getBoundingClientRect();
if (!rect) {
return;
}
const toInt = (value: number) => Math.round(value);
const boundRect = {
height: toInt(rect.height),
width: toInt(rect.width),
x: toInt(rect.x),
y: toInt(rect.y),
};
apis?.ui.updateTabsBoundingRect(boundRect).catch(console.error);
}
}, 50)
);
if (mode === 'app') {
apis?.ui.pingAppLayoutReady().catch(console.error);
}
return;
}, [reportBoundingUpdate]);
}, [mode]);

return (
<div
className={styles.root}
ref={ref}
className={clsx(styles.root, className)}
style={style}
data-mode={mode}
data-is-windows={environment.isDesktop && environment.isWindows}
>
<div
style={{
transition: sidebarResizing ? 'none' : undefined,
paddingLeft:
isMacosDesktop && sidebarOpen && !sidebarFloating && !fullScreen
? 90
: 16,
width: sidebarOpen && !sidebarFloating ? sidebarWidth : 130,
// minus 16 to account for the padding on the right side of the header (for box shadow)
marginRight: sidebarOpen && !sidebarFloating ? -16 : 0,
}}
className={styles.headerLeft}
>
{left}
</div>
<div className={styles.tabs}>
{pinned.map(tab => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ export const root = style({
},
});

export const headerLeft = style({
display: 'flex',
flexFlow: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 16px',
flexShrink: 0,
selectors: {
[`${root}[data-mode="app"] &`]: {
transition: 'width 0.3s, padding 0.3s',
},
},
});

export const tabs = style({
display: 'flex',
flexDirection: 'row',
Expand Down
1 change: 0 additions & 1 deletion packages/frontend/core/src/pages/404.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export const PageNotFound = ({
style={{
paddingLeft: environment.isMacOs ? 80 : 0,
}}
reportBoundingUpdate
/>
) : null}
{noPermission ? (
Expand Down
1 change: 0 additions & 1 deletion packages/frontend/core/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ export const Component = () => {
style={{
paddingLeft: environment.isMacOs ? 80 : 0,
}}
reportBoundingUpdate
/>
) : null}
<div
Expand Down
17 changes: 13 additions & 4 deletions packages/frontend/electron/renderer/shell/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'setimmediate';
import '@affine/component/theme/global.css';
import '@affine/component/theme/theme.css';
import '@affine/core/bootstrap/preload';

import { ThemeProvider } from '@affine/component/theme-provider';
import { appConfigProxy } from '@affine/core/hooks/use-app-config-storage';
import { configureAppTabsHeaderModule } from '@affine/core/modules/app-tabs-header';
import { configureElectronStateStorageImpls } from '@affine/core/modules/storage';
import { performanceLogger } from '@affine/core/shared';
import { apis, events } from '@affine/electron-api';
import {
configureGlobalStorageModule,
Framework,
Expand All @@ -26,9 +27,17 @@ const frameworkProvider = framework.provider();
const logger = performanceLogger.namespace('shell');

function main() {
appConfigProxy
.getSync()
.catch(() => console.error('failed to load app config'));
const handleMaximized = (maximized: boolean | undefined) => {
document.documentElement.dataset.maximized = String(maximized);
};
const handleFullscreen = (fullscreen: boolean | undefined) => {
document.documentElement.dataset.fullscreen = String(fullscreen);
};

apis?.ui.isMaximized().then(handleMaximized).catch(console.error);
apis?.ui.isFullScreen().then(handleFullscreen).catch(console.error);
events?.ui.onMaximized(handleMaximized);
events?.ui.onFullScreen(handleFullscreen);
}

function mountApp() {
Expand Down
5 changes: 4 additions & 1 deletion packages/frontend/electron/renderer/shell/shell.css.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { cssVar } from '@toeverything/theme';
import { globalStyle, style } from '@vanilla-extract/css';
import { createVar, globalStyle, style } from '@vanilla-extract/css';

export const sidebarOffsetVar = createVar();

export const root = style({
width: '100vw',
height: '100vh',
opacity: 1,
display: 'flex',
transition: 'opacity 0.1s',
background: cssVar('backgroundPrimaryColor'),
selectors: {
Expand Down
53 changes: 3 additions & 50 deletions packages/frontend/electron/renderer/shell/shell.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { AppTabsHeader } from '@affine/core/modules/app-tabs-header';
import { apis, events } from '@affine/electron-api';
import { events } from '@affine/electron-api';
import { useEffect, useState } from 'react';

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

const useIsShellActive = () => {
const [active, setActive] = useState(true);
const [active, setActive] = useState(false);

useEffect(() => {
const unsub = events?.ui.onTabShellViewActiveChange(active => {
Expand All @@ -20,48 +20,9 @@ const useIsShellActive = () => {
return active;
};

const useTabsBoundingRect = () => {
const [rect, setRect] = useState<{
x: number;
y: number;
width: number;
height: number;
}>({
x: environment.isDesktop && environment.isMacOs ? 80 : 0,
y: 0,
width: window.innerWidth,
height: 52,
});

useEffect(() => {
let unsub: (() => void) | undefined;
apis?.ui
.getTabsBoundingRect()
.then(rect => {
if (rect) {
setRect(rect);
}
unsub = events?.ui.onTabsBoundingRectChanged(rect => {
if (rect) {
setRect(rect);
}
});
})
.catch(err => {
console.error(err);
});
return () => {
unsub?.();
};
}, []);

return rect;
};

export function ShellRoot() {
const active = useIsShellActive();
const { appSettings } = useAppSettingHelper();
const rect = useTabsBoundingRect();
const translucent =
environment.isDesktop &&
environment.isMacOs &&
Expand All @@ -72,15 +33,7 @@ export function ShellRoot() {
data-translucent={translucent}
data-active={active}
>
<AppTabsHeader
style={{
position: 'fixed',
top: rect.y,
left: rect.x,
width: rect.width,
height: rect.height,
}}
/>
<AppTabsHeader mode="shell" />
</div>
);
}
2 changes: 0 additions & 2 deletions packages/frontend/electron/src/main/ui/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { MainEventRegister } from '../type';
import {
onActiveTabChanged,
onTabAction,
onTabsBoundingRectChanged,
onTabShellViewActiveChange,
onTabsStatusChange,
onTabViewsMetaChanged,
Expand Down Expand Up @@ -36,5 +35,4 @@ export const uiEvents = {
onTabsStatusChange,
onActiveTabChanged,
onTabShellViewActiveChange,
onTabsBoundingRectChanged,
} satisfies Record<string, MainEventRegister>;
Loading

0 comments on commit 45e961e

Please sign in to comment.