fix: theme not being persisted issue (#2283)

This commit is contained in:
Peng Xiao
2023-05-10 11:04:36 +08:00
committed by himself65
parent cf6341d00b
commit 64f4e634e8
21 changed files with 359 additions and 278 deletions

View File

@@ -1,4 +1,4 @@
import { BrowserWindow, nativeTheme } from 'electron'; import { app, BrowserWindow, nativeTheme } from 'electron';
import { isMacOS } from '../../../../utils'; import { isMacOS } from '../../../../utils';
import type { NamespaceHandlers } from '../type'; import type { NamespaceHandlers } from '../type';
@@ -17,6 +17,25 @@ export const uiHandlers = {
}); });
} }
}, },
handleMinimizeApp: async () => {
const windows = BrowserWindow.getAllWindows();
windows.forEach(w => {
w.minimize();
});
},
handleMaximizeApp: async () => {
const windows = BrowserWindow.getAllWindows();
windows.forEach(w => {
if (w.isMaximized()) {
w.unmaximize();
} else {
w.maximize();
}
});
},
handleCloseApp: async () => {
app.quit();
},
getGoogleOauthCode: async () => { getGoogleOauthCode: async () => {
return getGoogleOauthCode(); return getGoogleOauthCode();
}, },

View File

@@ -2,7 +2,7 @@ import { BrowserWindow, nativeTheme } from 'electron';
import electronWindowState from 'electron-window-state'; import electronWindowState from 'electron-window-state';
import { join } from 'path'; import { join } from 'path';
import { isMacOS } from '../../utils'; import { isMacOS, isWindows } from '../../utils';
import { logger } from './logger'; import { logger } from './logger';
const IS_DEV: boolean = const IS_DEV: boolean =
@@ -18,7 +18,11 @@ async function createWindow() {
}); });
const browserWindow = new BrowserWindow({ const browserWindow = new BrowserWindow({
titleBarStyle: isMacOS() ? 'hiddenInset' : 'default', titleBarStyle: isMacOS()
? 'hiddenInset'
: isWindows()
? 'hidden'
: 'default',
trafficLightPosition: { x: 24, y: 18 }, trafficLightPosition: { x: 24, y: 18 },
x: mainWindowState.x, x: mainWindowState.x,
y: mainWindowState.y, y: mainWindowState.y,

View File

@@ -1,3 +1,7 @@
export const isMacOS = () => { export const isMacOS = () => {
return process.platform === 'darwin'; return process.platform === 'darwin';
}; };
export const isWindows = () => {
return process.platform === 'win32';
};

View File

@@ -6,7 +6,7 @@ const mainDistDir = path.resolve(__dirname, '../dist/layers/main');
// be careful and avoid any side effects in // be careful and avoid any side effects in
const { handlers, events } = await import( const { handlers, events } = await import(
path.resolve(mainDistDir, 'exposed.js') 'file://' + path.resolve(mainDistDir, 'exposed.js')
); );
const handlersMeta = Object.entries(handlers).map( const handlersMeta = Object.entries(handlers).map(

View File

@@ -12,7 +12,6 @@ test('new page', async ({ page, workspace }) => {
}); });
test('app theme', async ({ page, electronApp }) => { test('app theme', async ({ page, electronApp }) => {
await page.waitForSelector('v-line');
const root = page.locator('html'); const root = page.locator('html');
{ {
const themeMode = await root.evaluate(element => const themeMode = await root.evaluate(element =>
@@ -20,30 +19,25 @@ test('app theme', async ({ page, electronApp }) => {
); );
expect(themeMode).toBe('light'); expect(themeMode).toBe('light');
// check if electron theme source is set to light const theme = await electronApp.evaluate(({ nativeTheme }) => {
const themeSource = await electronApp.evaluate(({ nativeTheme }) => { return nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
return nativeTheme.themeSource;
}); });
expect(themeSource).toBe('light'); expect(theme).toBe('light');
} }
{ {
await page.getByTestId('editor-option-menu').click(); await page.getByTestId('editor-option-menu').click();
await page.getByTestId('change-theme-dark').click(); await page.getByTestId('change-theme-dark').click();
await page.waitForTimeout(50); await page.waitForTimeout(50);
{ const themeMode = await root.evaluate(element =>
const themeMode = await root.evaluate(element => element.getAttribute('data-theme')
element.getAttribute('data-theme') );
); expect(themeMode).toBe('dark');
expect(themeMode).toBe('dark'); const theme = await electronApp.evaluate(({ nativeTheme }) => {
} return nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
const themeSource = await electronApp.evaluate(({ nativeTheme }) => {
return nativeTheme.themeSource;
}); });
expect(theme).toBe('dark');
expect(themeSource).toBe('dark');
} }
}); });

View File

@@ -42,6 +42,8 @@ export const test = base.extend<{
const logFilePath = await page.evaluate(async () => { const logFilePath = await page.evaluate(async () => {
return window.apis?.debug.logFilePath(); return window.apis?.debug.logFilePath();
}); });
// wat for blocksuite to be loaded
await page.waitForSelector('v-line');
await use(page); await use(page);
await page.close(); await page.close();
if (logFilePath) { if (logFilePath) {
@@ -57,11 +59,12 @@ export const test = base.extend<{
executablePath: resolve(__dirname, '../node_modules/.bin/electron'), executablePath: resolve(__dirname, '../node_modules/.bin/electron'),
colorScheme: 'light', colorScheme: 'light',
}); });
const sessionDataPath = await electronApp.evaluate(async ({ app }) => {
return app.getPath('sessionData');
});
await use(electronApp); await use(electronApp);
await fs.rm(sessionDataPath, { recursive: true, force: true }); // FIXME: the following does not work well on CI
// const sessionDataPath = await electronApp.evaluate(async ({ app }) => {
// return app.getPath('sessionData');
// });
// await fs.rm(sessionDataPath, { recursive: true, force: true });
}, },
appInfo: async ({ electronApp }, use) => { appInfo: async ({ electronApp }, use) => {
const appInfo = await electronApp.evaluate(async ({ app }) => { const appInfo = await electronApp.evaluate(async ({ app }) => {

View File

@@ -110,7 +110,8 @@ export const settingItemLabelHint = style({
export const row = style({ export const row = style({
padding: '40px 0', padding: '40px 0',
display: 'flex', display: 'flex',
gap: '60px', columnGap: '60px',
rowGap: '12px',
selectors: { selectors: {
'&': { '&': {
borderBottom: '1px solid var(--affine-border-color)', borderBottom: '1px solid var(--affine-border-color)',
@@ -119,22 +120,22 @@ export const row = style({
paddingTop: 0, paddingTop: 0,
}, },
}, },
flexWrap: 'wrap',
}); });
export const col = style({ export const col = style({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignItems: 'flex-start', alignItems: 'flex-start',
flexShrink: 0,
selectors: { selectors: {
[`${row} &:nth-child(1)`]: { [`${row} &:nth-child(1)`]: {
flex: 3, flex: '3 0 200px',
}, },
[`${row} &:nth-child(2)`]: { [`${row} &:nth-child(2)`]: {
flex: 5, flex: '5 0 240px',
}, },
[`${row} &:nth-child(3)`]: { [`${row} &:nth-child(3)`]: {
flex: 2, flex: '2 0 200px',
alignItems: 'flex-end', alignItems: 'flex-end',
}, },
}, },

View File

@@ -88,7 +88,6 @@ export const GeneralPanel: React.FC<PanelProps> = ({
<div className={style.col}> <div className={style.col}>
<StyledInput <StyledInput
width={284}
height={38} height={38}
value={input} value={input}
data-testid="workspace-name-input" data-testid="workspace-name-input"

View File

@@ -1,13 +1,7 @@
import { displayFlex, styled } from '@affine/component'; import { displayFlex, styled } from '@affine/component';
import { Input } from '@affine/component'; import { Input } from '@affine/component';
export const StyledInput = styled(Input)(() => { export const StyledInput = Input;
return {
border: '1px solid var(--affine-border-color)',
borderRadius: '8px',
fontSize: 'var(--affine-font-sm)',
};
});
export const StyledWorkspaceInfo = styled('div')(() => { export const StyledWorkspaceInfo = styled('div')(() => {
return { return {

View File

@@ -24,10 +24,7 @@ import { useCurrentPageId } from '../../../../hooks/current/use-current-page-id'
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace'; import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
import { toast } from '../../../../utils'; import { toast } from '../../../../utils';
import { MenuThemeModeSwitch } from '../header-right-items/theme-mode-switch'; import { MenuThemeModeSwitch } from '../header-right-items/theme-mode-switch';
import { import * as styles from '../styles.css';
StyledHorizontalDivider,
StyledHorizontalDividerContainer,
} from '../styles';
import { LanguageMenu } from './language-menu'; import { LanguageMenu } from './language-menu';
const CommonMenu = () => { const CommonMenu = () => {
const content = ( const content = (
@@ -120,9 +117,9 @@ const PageMenu = () => {
}} }}
/> />
)} )}
<StyledHorizontalDividerContainer> <div className={styles.horizontalDividerContainer}>
<StyledHorizontalDivider /> <div className={styles.horizontalDivider} />
</StyledHorizontalDividerContainer> </div>
</> </>
<div <div

View File

@@ -2,6 +2,7 @@ import { BrowserWarning } from '@affine/component/affine-banner';
import { appSidebarOpenAtom } from '@affine/component/app-sidebar'; import { appSidebarOpenAtom } from '@affine/component/app-sidebar';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { WorkspaceFlavour } from '@affine/workspace/type'; import { WorkspaceFlavour } from '@affine/workspace/type';
import { CloseIcon, MinusIcon, RoundedRectangleIcon } from '@blocksuite/icons';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import type { FC, HTMLAttributes, PropsWithChildren } from 'react'; import type { FC, HTMLAttributes, PropsWithChildren } from 'react';
@@ -24,11 +25,7 @@ import { HeaderShareMenu } from './header-right-items/share-menu';
import SyncUser from './header-right-items/sync-user'; import SyncUser from './header-right-items/sync-user';
import TrashButtonGroup from './header-right-items/trash-button-group'; import TrashButtonGroup from './header-right-items/trash-button-group';
import UserAvatar from './header-right-items/user-avatar'; import UserAvatar from './header-right-items/user-avatar';
import { import * as styles from './styles.css';
StyledHeader,
StyledHeaderContainer,
StyledHeaderRightSide,
} from './styles';
import { OSWarningMessage, shouldShowWarning } from './utils'; import { OSWarningMessage, shouldShowWarning } from './utils';
const SidebarSwitch = lazy(() => const SidebarSwitch = lazy(() =>
@@ -53,6 +50,9 @@ export const enum HeaderRightItemName {
ShareMenu = 'shareMenu', ShareMenu = 'shareMenu',
EditPage = 'editPage', EditPage = 'editPage',
UserAvatar = 'userAvatar', UserAvatar = 'userAvatar',
// some windows only items
WindowsAppControls = 'windowsAppControls',
} }
type HeaderItem = { type HeaderItem = {
@@ -67,6 +67,7 @@ type HeaderItem = {
} }
) => boolean; ) => boolean;
}; };
const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = { const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
[HeaderRightItemName.TrashButtonGroup]: { [HeaderRightItemName.TrashButtonGroup]: {
Component: TrashButtonGroup, Component: TrashButtonGroup,
@@ -104,6 +105,44 @@ const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
return !isPublic && !isPreview; return !isPublic && !isPreview;
}, },
}, },
[HeaderRightItemName.WindowsAppControls]: {
Component: () => {
return (
<div className={styles.windowAppControlsWrapper}>
<button
data-type="minimize"
className={styles.windowAppControl}
onClick={() => {
window.apis?.ui.handleMinimizeApp();
}}
>
<MinusIcon />
</button>
<button
data-type="maximize"
className={styles.windowAppControl}
onClick={() => {
window.apis?.ui.handleMaximizeApp();
}}
>
<RoundedRectangleIcon />
</button>
<button
data-type="close"
className={styles.windowAppControl}
onClick={() => {
window.apis?.ui.handleCloseApp();
}}
>
<CloseIcon />
</button>
</div>
);
},
availableWhen: () => {
return environment.isDesktop && environment.isWindows;
},
},
}; };
export type HeaderProps = BaseHeaderProps; export type HeaderProps = BaseHeaderProps;
@@ -127,9 +166,10 @@ export const Header = forwardRef<
const mode = useCurrentMode(); const mode = useCurrentMode();
return ( return (
<StyledHeaderContainer <div
className={styles.headerContainer}
ref={ref} ref={ref}
hasWarning={showWarning} data-has-warning={showWarning}
data-open={open} data-open={open}
{...props} {...props}
> >
@@ -145,11 +185,12 @@ export const Header = forwardRef<
/> />
)} )}
<StyledHeader <div
hasWarning={showWarning} className={styles.header}
data-has-warning={showWarning}
data-testid="editor-header-items" data-testid="editor-header-items"
data-tauri-drag-region data-tauri-drag-region
isEdgeless={mode === 'edgeless'} data-is-edgeless={mode === 'edgeless'}
> >
<Suspense> <Suspense>
<SidebarSwitch <SidebarSwitch
@@ -160,7 +201,7 @@ export const Header = forwardRef<
</Suspense> </Suspense>
{props.children} {props.children}
<StyledHeaderRightSide> <div className={styles.headerRightSide}>
{useMemo(() => { {useMemo(() => {
return Object.entries(HeaderRightItems).map( return Object.entries(HeaderRightItems).map(
([name, { availableWhen, Component }]) => { ([name, { availableWhen, Component }]) => {
@@ -184,10 +225,9 @@ export const Header = forwardRef<
} }
); );
}, [props])} }, [props])}
{/*<ShareMenu />*/} </div>
</StyledHeaderRightSide> </div>
</StyledHeader> </div>
</StyledHeaderContainer>
); );
}); });

View File

@@ -15,15 +15,7 @@ import { QuickSearchButton } from '../../pure/quick-search-button';
import { EditorModeSwitch } from './editor-mode-switch'; import { EditorModeSwitch } from './editor-mode-switch';
import type { BaseHeaderProps } from './header'; import type { BaseHeaderProps } from './header';
import { Header } from './header'; import { Header } from './header';
import { import * as styles from './styles.css';
StyledQuickSearchTipButton,
StyledQuickSearchTipContent,
StyledSearchArrowWrapper,
StyledSwitchWrapper,
StyledTitle,
StyledTitleContainer,
StyledTitleWrapper,
} from './styles';
export type WorkspaceHeaderProps = BaseHeaderProps; export type WorkspaceHeaderProps = BaseHeaderProps;
@@ -61,7 +53,7 @@ export const WorkspaceHeader = forwardRef<
); );
const TipsContent = ( const TipsContent = (
<StyledQuickSearchTipContent> <div className={styles.quickSearchTipContent}>
<div> <div>
Click button Click button
{ {
@@ -78,22 +70,23 @@ export const WorkspaceHeader = forwardRef<
{isMac() ? ' ⌘ + K' : ' Ctrl + K'} to activate Quick Search. Then you {isMac() ? ' ⌘ + K' : ' Ctrl + K'} to activate Quick Search. Then you
can search keywords or quickly open recently viewed pages. can search keywords or quickly open recently viewed pages.
</div> </div>
<StyledQuickSearchTipButton <div
className={styles.quickSearchTipButton}
data-testid="quick-search-got-it" data-testid="quick-search-got-it"
onClick={() => setShowQuickSearchTips(false)} onClick={() => setShowQuickSearchTips(false)}
> >
Got it Got it
</StyledQuickSearchTipButton> </div>
</StyledQuickSearchTipContent> </div>
); );
return ( return (
<Header ref={ref} {...props}> <Header ref={ref} {...props}>
{children} {children}
{!isPublic && currentPage && ( {!isPublic && currentPage && (
<StyledTitleContainer data-tauri-drag-region> <div className={styles.titleContainer}>
<StyledTitleWrapper> <div className={styles.titleWrapper}>
<StyledSwitchWrapper> <div className={styles.switchWrapper}>
<EditorModeSwitch <EditorModeSwitch
blockSuiteWorkspace={workspace.blockSuiteWorkspace} blockSuiteWorkspace={workspace.blockSuiteWorkspace}
pageId={currentPage.id} pageId={currentPage.id}
@@ -101,8 +94,8 @@ export const WorkspaceHeader = forwardRef<
marginRight: '12px', marginRight: '12px',
}} }}
/> />
</StyledSwitchWrapper> </div>
<StyledTitle>{title || 'Untitled'}</StyledTitle> <div className={styles.title}>{title || 'Untitled'}</div>
<QuickSearchTips <QuickSearchTips
data-testid="quick-search-tips" data-testid="quick-search-tips"
content={TipsContent} content={TipsContent}
@@ -111,16 +104,16 @@ export const WorkspaceHeader = forwardRef<
open={showQuickSearchTips} open={showQuickSearchTips}
offset={[0, -5]} offset={[0, -5]}
> >
<StyledSearchArrowWrapper> <div className={styles.searchArrowWrapper}>
<QuickSearchButton <QuickSearchButton
onClick={() => { onClick={() => {
setOpenQuickSearch(true); setOpenQuickSearch(true);
}} }}
/> />
</StyledSearchArrowWrapper> </div>
</QuickSearchTips> </QuickSearchTips>
</StyledTitleWrapper> </div>
</StyledTitleContainer> </div>
)} )}
</Header> </Header>
); );

View File

@@ -0,0 +1,216 @@
import { style } from '@vanilla-extract/css';
export const headerContainer = style({
height: '52px',
flexShrink: 0,
position: 'sticky',
top: 0,
background: 'var(--affine-background-primary-color)',
WebkitAppRegion: 'drag',
zIndex: 'var(--affine-z-index-popover)',
'@media': {
'(max-width: 768px)': {
selectors: {
'&[data-open="true"]': {
// @ts-ignore
WebkitAppRegion: 'no-drag',
},
},
},
},
selectors: {
'&[data-has-warning="true"]': {
height: '96px',
},
},
});
export const header = style({
flexShrink: 0,
height: '52px',
width: '100%',
padding: '0 20px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
background: 'var(--affine-background-primary-color)',
zIndex: 99,
position: 'relative',
selectors: {
'&[data-is-edgeless="true"]': {
borderBottom: `1px solid var(--affine-border-color)`,
},
},
});
export const titleContainer = style({
width: '100%',
height: '100%',
margin: 'auto',
position: 'absolute',
inset: 'auto auto auto 50%',
transform: 'translate(-50%, 0px)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
alignContent: 'unset',
fontSize: 'var(--affine-font-base)',
});
export const title = style({
maxWidth: '620px',
transition: 'max-width .15s',
userSelect: 'none',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
'@media': {
'(max-width: 768px)': {
selectors: {
'&[data-open="true"]': {
// @ts-ignore
WebkitAppRegion: 'no-drag',
},
},
},
},
});
export const titleWrapper = style({
height: '100%',
position: 'relative',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
});
export const headerRightSide = style({
height: '100%',
display: 'flex',
alignItems: 'center',
gap: '12px',
zIndex: 1,
});
export const browserWarning = style({
backgroundColor: 'var(--affine-background-warning-color)',
color: 'var(--affine-warning-color)',
height: '36px',
fontSize: 'var(--affine-font-sm)',
display: 'none',
justifyContent: 'center',
alignItems: 'center',
selectors: {
'&[data-show="true"]': {
display: 'flex',
},
},
});
export const closeButton = style({
width: '36px',
height: '36px',
color: 'var(--affine-icon-color)',
cursor: 'pointer',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
right: '15px',
top: 0,
});
export const switchWrapper = style({
position: 'absolute',
right: '100%',
top: 0,
bottom: 0,
margin: 'auto',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
});
export const searchArrowWrapper = style({
position: 'absolute',
left: 'calc(100% + 4px)',
top: 0,
bottom: 0,
margin: 'auto',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
});
export const pageListTitleWrapper = style({
fontSize: 'var(--affine-font-base)',
color: 'var(--affine-text-primary-color)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
});
export const pageListTitleIcon = style({
fontSize: '20px',
height: '1em',
marginRight: '12px',
});
export const quickSearchTipButton = style({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginTop: '12px',
color: '#FFFFFF',
width: '48px',
height: ' 26px',
fontSize: 'var(--affine-font-sm)',
lineHeight: '22px',
background: 'var(--affine-primary-color)',
borderRadius: '8px',
textAlign: 'center',
cursor: 'pointer',
});
export const quickSearchTipContent = style({
display: 'flex',
justifyContent: 'center',
alignItems: 'flex-end',
flexDirection: 'column',
});
export const horizontalDivider = style({
width: '100%',
borderTop: `1px solid var(--affine-border-color)`,
});
export const horizontalDividerContainer = style({
width: '100%',
padding: '14px',
});
export const windowAppControlsWrapper = style({
display: 'flex',
gap: '2px',
transform: 'translateX(8px)',
});
export const windowAppControl = style({
// @ts-ignore
WebkitAppRegion: 'no-drag',
cursor: 'pointer',
display: 'inline-flex',
width: '32px',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '2px',
selectors: {
'&[data-type="close"]:hover': {
background: 'var(--affine-error-color)',
color: '#FFFFFF',
},
'&:hover': {
background: 'var(--affine-background-tertiary-color)',
},
},
});

View File

@@ -1,186 +0,0 @@
import {
absoluteCenter,
displayFlex,
styled,
textEllipsis,
} from '@affine/component';
export const StyledHeaderContainer = styled('div')<{
hasWarning: boolean;
}>(({ hasWarning }) => {
return {
height: hasWarning ? '96px' : '52px',
flexShrink: 0,
position: 'sticky',
top: 0,
background: 'var(--affine-background-primary-color)',
WebkitAppRegion: 'drag',
zIndex: 'var(--affine-z-index-popover)',
'@media (max-width: 768px)': {
'&[data-open="true"]': {
WebkitAppRegion: 'no-drag',
},
},
};
});
export const StyledHeader = styled('div')<{
hasWarning: boolean;
isEdgeless: boolean;
}>(({ isEdgeless }) => {
return {
flexShrink: 0,
height: '52px',
width: '100%',
padding: '0 20px',
...displayFlex('space-between', 'center'),
background: 'var(--affine-background-primary-color)',
zIndex: 99,
position: 'relative',
borderBottom: isEdgeless ? '1px solid var(--affine-border-color)' : 'none',
};
});
export const StyledTitleContainer = styled('div')(() => ({
width: '100%',
height: '100%',
margin: 'auto',
...absoluteCenter({ horizontal: true, position: { top: 0 } }),
...displayFlex('center', 'center'),
fontSize: 'var(--affine-font-base)',
}));
export const StyledTitle = styled('div')(({ theme }) => {
return {
maxWidth: '620px',
[theme.breakpoints.down('lg')]: {
maxWidth: '480px',
},
[theme.breakpoints.down('md')]: {
maxWidth: '240px',
},
[theme.breakpoints.down('sm')]: {
maxWidth: '180px',
},
transition: 'max-width .15s',
userSelect: 'none',
...textEllipsis(1),
};
});
export const StyledTitleWrapper = styled('div')({
height: '100%',
position: 'relative',
...displayFlex('center', 'center'),
});
export const StyledHeaderRightSide = styled('div')({
height: '100%',
display: 'flex',
alignItems: 'center',
'>*:not(:last-child)': {
marginRight: '12px',
},
});
export const StyledBrowserWarning = styled('div')<{ show: boolean }>(
({ show }) => {
return {
backgroundColor: 'var(--affine-background-warning-color)',
color: 'var(--affine-background-warning-color)',
height: '36px',
fontSize: 'var(--affine-font-sm)',
display: show ? 'flex' : 'none',
justifyContent: 'center',
alignItems: 'center',
};
}
);
export const StyledCloseButton = styled('div')(() => {
return {
width: '36px',
height: '36px',
color: 'var(--affine-icon-color)',
cursor: 'pointer',
...displayFlex('center', 'center'),
position: 'absolute',
right: '15px',
top: '0',
svg: {
width: '15px',
height: '15px',
position: 'relative',
zIndex: 1,
},
};
});
export const StyledSwitchWrapper = styled('div')(() => {
return {
position: 'absolute',
right: '100%',
top: 0,
bottom: 0,
margin: 'auto',
...displayFlex('center', 'center'),
};
});
export const StyledSearchArrowWrapper = styled('div')(() => {
return {
position: 'absolute',
left: 'calc(100% + 4px)',
top: 0,
bottom: 0,
margin: 'auto',
...displayFlex('center', 'center'),
};
});
export const StyledPageListTittleWrapper = styled(StyledTitle)(() => {
return {
fontSize: 'var(--affine-font-base)',
color: 'var(--affine-text-primary-color)',
...displayFlex('center', 'center'),
'>svg': {
fontSize: '20px',
marginRight: '12px',
},
};
});
export const StyledQuickSearchTipButton = styled('div')(() => {
return {
...displayFlex('center', 'center'),
marginTop: '12px',
color: '#FFFFFF',
width: '48px',
height: ' 26px',
fontSize: 'var(--affine-font-sm)',
lineHeight: '22px',
background: 'var(--affine-primary-color)',
borderRadius: '8px',
textAlign: 'center',
cursor: 'pointer',
};
});
export const StyledQuickSearchTipContent = styled('div')(() => {
return {
...displayFlex('center', 'flex-end'),
flexDirection: 'column',
};
});
export const StyledHorizontalDivider = styled('div')(() => {
return {
width: '100%',
borderTop: `1px solid var(--affine-border-color)`,
};
});
export const StyledHorizontalDividerContainer = styled('div')(() => {
return {
width: '100%',
padding: '14px',
};
});

View File

@@ -5,7 +5,7 @@ import type React from 'react';
import { openQuickSearchModalAtom } from '../../../atoms'; import { openQuickSearchModalAtom } from '../../../atoms';
import type { HeaderProps } from '../../blocksuite/workspace-header/header'; import type { HeaderProps } from '../../blocksuite/workspace-header/header';
import { Header } from '../../blocksuite/workspace-header/header'; import { Header } from '../../blocksuite/workspace-header/header';
import { StyledPageListTittleWrapper } from '../../blocksuite/workspace-header/styles'; import * as styles from '../../blocksuite/workspace-header/styles.css';
import { QuickSearchButton } from '../quick-search-button'; import { QuickSearchButton } from '../quick-search-button';
export type WorkspaceTitleProps = React.PropsWithChildren< export type WorkspaceTitleProps = React.PropsWithChildren<
@@ -22,15 +22,15 @@ export const WorkspaceTitle: React.FC<WorkspaceTitleProps> = ({
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom); const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
return ( return (
<Header {...props}> <Header {...props}>
<StyledPageListTittleWrapper> <div className={styles.pageListTitleWrapper}>
{icon} <div className={styles.pageListTitleIcon}>{icon}</div>
{children} {children}
<QuickSearchButton <QuickSearchButton
onClick={() => { onClick={() => {
setOpenQuickSearch(true); setOpenQuickSearch(true);
}} }}
/> />
</StyledPageListTittleWrapper> </div>
</Header> </Header>
); );
}; };

View File

@@ -1,19 +1,22 @@
import type { ThemeProviderProps } from '@affine/component'; import type { ThemeProviderProps } from '@affine/component';
import { ThemeProvider as NextThemeProvider, useTheme } from 'next-themes'; import { ThemeProvider as NextThemeProvider, useTheme } from 'next-themes';
import type { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
import type React from 'react';
import { memo, useRef } from 'react'; import { memo, useRef } from 'react';
const themes = ['dark', 'light']; const themes = ['dark', 'light'];
// a workaround to sync theme to electron
let firstRender = true;
const DesktopThemeSync = memo(function DesktopThemeSync() { const DesktopThemeSync = memo(function DesktopThemeSync() {
const { theme } = useTheme(); const { theme } = useTheme();
const lastThemeRef = useRef(theme); const lastThemeRef = useRef(theme);
if (lastThemeRef.current !== theme) { if (lastThemeRef.current !== theme || firstRender) {
if (environment.isDesktop && theme) { if (environment.isDesktop && theme) {
window.apis?.ui.handleThemeChange(theme as 'dark' | 'light' | 'system'); window.apis?.ui.handleThemeChange(theme as 'dark' | 'light' | 'system');
} }
lastThemeRef.current = theme; lastThemeRef.current = theme;
firstRender = false;
} }
return null; return null;
}); });

View File

@@ -22,7 +22,7 @@
"AFFiNE Cloud": "AFFiNE Cloud", "AFFiNE Cloud": "AFFiNE Cloud",
"It takes up more space on your device": "Es verbraucht mehr Speicherplatz auf deinem Gerät.", "It takes up more space on your device": "Es verbraucht mehr Speicherplatz auf deinem Gerät.",
"Sign out description": "Nach dem Abmelden gehen alle nicht synchronisierten Inhalte verloren.", "Sign out description": "Nach dem Abmelden gehen alle nicht synchronisierten Inhalte verloren.",
"Export AFFiNE backup file": "AFFiNE-Backup als Datei exportieren (bald verfügbar)", "Export AFFiNE backup file": "AFFiNE-Backup als Datei exportieren",
"Members": "Mitglieder", "Members": "Mitglieder",
"Saved then enable AFFiNE Cloud": "Alle Änderungen werden lokal gespeichert. Klicke hier, um AFFiNE Cloud zu aktivieren.", "Saved then enable AFFiNE Cloud": "Alle Änderungen werden lokal gespeichert. Klicke hier, um AFFiNE Cloud zu aktivieren.",
"Joined Workspace": "Workspace beigetreten", "Joined Workspace": "Workspace beigetreten",

View File

@@ -6,7 +6,7 @@
"Members": "Members", "Members": "Members",
"Add to favorites": "Add to favorites", "Add to favorites": "Add to favorites",
"It takes up more space on your device": "It takes up more space on your device.", "It takes up more space on your device": "It takes up more space on your device.",
"Export AFFiNE backup file": "Export AFFiNE backup file (coming soon)", "Export AFFiNE backup file": "Export AFFiNE backup file",
"Saved then enable AFFiNE Cloud": "All changes are saved locally, click to enable AFFiNE Cloud.", "Saved then enable AFFiNE Cloud": "All changes are saved locally, click to enable AFFiNE Cloud.",
"Help and Feedback": "Help and Feedback", "Help and Feedback": "Help and Feedback",
"Not now": "Not now", "Not now": "Not now",

View File

@@ -188,7 +188,7 @@
"will delete member": "supprimera le membre", "will delete member": "supprimera le membre",
"is a Local Workspace": "est un espace de travail local", "is a Local Workspace": "est un espace de travail local",
"Cloud Workspace Description": "Toutes les données vont être synchronisées et sauvegardées sur le compte AFFiNE <1>{{email}}</1>", "Cloud Workspace Description": "Toutes les données vont être synchronisées et sauvegardées sur le compte AFFiNE <1>{{email}}</1>",
"Export AFFiNE backup file": "Exporter un fichier de sauvegarde AFFiNE (à venir...)", "Export AFFiNE backup file": "Exporter un fichier de sauvegarde AFFiNE",
"Export Description": "Vous pouvez exporter l'intégralité des données de l'espace de travail à titre de sauvegarde ; les données ainsi exportées peuvent être réimportées.", "Export Description": "Vous pouvez exporter l'intégralité des données de l'espace de travail à titre de sauvegarde ; les données ainsi exportées peuvent être réimportées.",
"Download data Description1": "Cela prend davantage despace sur votre appareil.", "Download data Description1": "Cela prend davantage despace sur votre appareil.",
"It takes up little space on your device": "Cela prend peu despace sur votre appareil.", "It takes up little space on your device": "Cela prend peu despace sur votre appareil.",

View File

@@ -9,7 +9,7 @@
"Check Our Docs": "Проверьте нашу документацию", "Check Our Docs": "Проверьте нашу документацию",
"is a Cloud Workspace": "это облачное рабочее пространство.", "is a Cloud Workspace": "это облачное рабочее пространство.",
"Owner": "Владелец", "Owner": "Владелец",
"Export AFFiNE backup file": "Экспорт файла резервной копии AFFiNE (скоро)", "Export AFFiNE backup file": "Экспорт файла резервной копии AFFiNE",
"is a Local Workspace": "это локальное рабочее пространство", "is a Local Workspace": "это локальное рабочее пространство",
"Wait for Sync": "Дождитесь синхронизации", "Wait for Sync": "Дождитесь синхронизации",
"Joined Workspace": "Присоединенное рабочее пространство", "Joined Workspace": "Присоединенное рабочее пространство",

View File

@@ -178,7 +178,7 @@
"Owner": "所有者", "Owner": "所有者",
"Published to Web": "公开到互联网", "Published to Web": "公开到互联网",
"Data sync mode": "数据同步模式", "Data sync mode": "数据同步模式",
"Export AFFiNE backup file": "导出 AFFiNE 备份文件(即将到来)", "Export AFFiNE backup file": "导出 AFFiNE 备份文件",
"AFFiNE Cloud": "AFFiNE 云服务", "AFFiNE Cloud": "AFFiNE 云服务",
"Export Description": "您可以导出整个工作区数据进行备份,导出的数据可以重新被导入。", "Export Description": "您可以导出整个工作区数据进行备份,导出的数据可以重新被导入。",
"It takes up little space on your device": "此操作会在你的设备上占用少许空间。", "It takes up little space on your device": "此操作会在你的设备上占用少许空间。",