refactor: move WikiBackground and Sidebar outof Main

This commit is contained in:
linonetwo 2023-07-05 20:20:48 +08:00 committed by lin onetwo
parent 89cdebdbc8
commit c1b83b0531
8 changed files with 232 additions and 197 deletions

1
src/constants/style.ts Normal file
View file

@ -0,0 +1 @@
export const sidebarWidth = 68;

163
src/pages/Main/Sidebar.tsx Normal file
View file

@ -0,0 +1,163 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
import { Settings as SettingsIcon, Upgrade as UpgradeIcon } from '@material-ui/icons';
import { t } from 'i18next';
import SimpleBar from 'simplebar-react';
import styled, { css } from 'styled-components';
import { CommandPaletteIcon } from '@/components/icon/CommandPaletteSVG';
import { SortableWorkspaceSelectorList, WorkspaceSelector } from '@/components/WorkspaceIconAndSelector';
import { sidebarWidth } from '@/constants/style';
import { latestStableUpdateUrl } from '@/constants/urls';
import { usePromiseValue } from '@/helpers/useServiceValue';
import { IconButton as IconButtonRaw, Tooltip } from '@material-ui/core';
import { usePreferenceObservable } from '@services/preferences/hooks';
import { useUpdaterObservable } from '@services/updater/hooks';
import { IUpdaterStatus } from '@services/updater/interface';
import { WindowNames } from '@services/windows/WindowProperties';
import { useWorkspacesListObservable } from '@services/workspaces/hooks';
const SideBarEnd = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;
const sideBarStyle = css`
height: 100%;
width: ${sidebarWidth}px;
min-width: ${sidebarWidth}px;
background-color: ${({ theme }) => theme.palette.background.default};
-webkit-app-region: drag;
user-select: none;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 10px;
box-sizing: border-box;
overflow-y: auto;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 0;
}
`;
const SidebarRoot = styled.div`
${sideBarStyle}
`;
const SidebarWithStyle = styled(SimpleBar)`
${sideBarStyle}
`;
const SidebarTop = styled.div<{ titleBar?: boolean }>`
overflow-y: scroll;
&::-webkit-scrollbar {
width: 0;
}
flex: 1;
width: 100%;
${({ titleBar }) =>
titleBar === true
? css`
padding-top: 0;
`
: css`
padding-top: 30px;
`}
`;
const IconButton = styled(IconButtonRaw)`
aspect-ratio: 1;
overflow: hidden;
width: 80%;
color: ${({ theme }) => theme.palette.action.active};
`;
const SidebarContainer = ({ children }: { children: React.ReactNode }): JSX.Element => {
const platform = usePromiseValue(async () => await window.service.context.get('platform'));
// use native scroll bar on macOS
if (platform === 'darwin') {
return <SidebarRoot>{children}</SidebarRoot>;
}
return <SidebarWithStyle>{children}</SidebarWithStyle>;
};
export function SideBar(): JSX.Element {
/** is title bar on. This only take effect after reload, so we don't want to get this preference from observable */
const titleBar = usePromiseValue<boolean>(async () => await window.service.preference.get('titleBar'), false)!;
const workspacesList = useWorkspacesListObservable();
const activeWorkspace = workspacesList?.find((workspace) => workspace.active);
const preferences = usePreferenceObservable();
const updaterMetaData = useUpdaterObservable();
if (preferences === undefined) return <div>{t('Loading')}</div>;
const { sidebarShortcutHints, hideSideBarIcon } = preferences;
return (
<SidebarContainer>
<SidebarTop titleBar={titleBar}>
{workspacesList === undefined
? <div>{t('Loading')}</div>
: <SortableWorkspaceSelectorList sidebarShortcutHints={sidebarShortcutHints} workspacesList={workspacesList} hideSideBarIcon={hideSideBarIcon} />}
<WorkspaceSelector
id='add'
hideSideBarIcon={hideSideBarIcon}
index={workspacesList?.length ?? 0}
showSidebarShortcutHints={sidebarShortcutHints}
onClick={() => void window.service.window.open(WindowNames.addWorkspace)}
/>
{workspacesList === undefined ||
(workspacesList.length === 0 && (
<WorkspaceSelector
id='guide'
hideSideBarIcon={hideSideBarIcon}
index={workspacesList?.length ? workspacesList.length ?? 0 + 1 : 1}
active={activeWorkspace?.id === undefined}
showSidebarShortcutHints={sidebarShortcutHints}
onClick={() => void window.service.workspace.clearActiveWorkspace(activeWorkspace?.id)}
/>
))}
</SidebarTop>
<SideBarEnd>
{(workspacesList?.length ?? 0) > 0 && (
<>
<IconButton
id='open-command-palette-button'
aria-label={t('SideBar.CommandPalette')}
onClick={async () => {
await window.service.wiki.requestWikiSendActionMessage('open-command-palette');
}}
>
<Tooltip title={<span>{t('SideBar.CommandPalette')}</span>} placement='top'>
<CommandPaletteIcon />
</Tooltip>
</IconButton>
</>
)}
{updaterMetaData?.status === IUpdaterStatus.updateAvailable && (
<IconButton
id='update-available'
aria-label={t('SideBar.UpdateAvailable')}
onClick={async () => {
await window.service.native.open(updaterMetaData.info?.latestReleasePageUrl ?? latestStableUpdateUrl);
}}
>
<Tooltip title={<span>{t('SideBar.UpdateAvailable')}</span>} placement='top'>
<UpgradeIcon />
</Tooltip>
</IconButton>
)}
<IconButton
id='open-preferences-button'
aria-label={t('SideBar.Preferences')}
onClick={async () => {
await window.service.window.open(WindowNames.preferences);
}}
>
<Tooltip title={<span>{t('SideBar.Preferences')}</span>} placement='top'>
<SettingsIcon />
</Tooltip>
</IconButton>
</SideBarEnd>
</SidebarContainer>
);
}

View file

@ -1,35 +1,16 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
/* eslint-disable @typescript-eslint/promise-function-async */
import React, { useState } from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import styled, { css } from 'styled-components';
import styled from 'styled-components';
import is, { isNot } from 'typescript-styled-is';
import SimpleBar from 'simplebar-react';
import 'simplebar/dist/simplebar.min.css';
import { IconButton as IconButtonRaw, Tooltip, Typography } from '@material-ui/core';
import { Settings as SettingsIcon, Upgrade as UpgradeIcon } from '@material-ui/icons';
import { IUpdaterStatus } from '@services/updater/interface';
import { WindowNames } from '@services/windows/WindowProperties';
import { usePromiseValue } from '@/helpers/useServiceValue';
import { useUpdaterObservable } from '@services/updater/hooks';
import { latestStableUpdateUrl } from '@/constants/urls';
import FindInPage from '../../components/FindInPage';
import { CommandPaletteIcon } from '@/components/icon/CommandPaletteSVG';
import { SortableWorkspaceSelectorList, WorkspaceSelector } from '@/components/WorkspaceIconAndSelector';
import { sidebarWidth } from '@/constants/style';
import { usePreferenceObservable } from '@services/preferences/hooks';
import { useWorkspacesListObservable } from '@services/workspaces/hooks';
import { Languages } from '../Preferences/sections/Languages';
import { TiddlyWiki } from '../Preferences/sections/TiddlyWiki';
import { ViewLoadErrorMessages, WikiErrorMessages } from './ErrorMessage';
import { NewUserMessage } from './NewUserMessage';
import { useAutoCreateFirstWorkspace } from './useAutoCreateFirstWorkspace';
import FindInPage from '../../components/FindInPage';
import { WikiBackground } from '../WikiBackground';
import { SideBar } from './Sidebar';
const OuterRoot = styled.div`
display: flex;
@ -57,67 +38,6 @@ const Root = styled.div`
}
`;
const sidebarWidth = 68;
const sideBarStyle = css`
height: 100%;
width: ${sidebarWidth}px;
min-width: ${sidebarWidth}px;
background-color: ${({ theme }) => theme.palette.background.default};
-webkit-app-region: drag;
user-select: none;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 10px;
box-sizing: border-box;
overflow-y: auto;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 0;
}
`;
const SidebarRoot = styled.div`
${sideBarStyle}
`;
const SidebarWithStyle = styled(SimpleBar)`
${sideBarStyle}
`;
const SidebarTop = styled.div<{ titleBar?: boolean }>`
overflow-y: scroll;
&::-webkit-scrollbar {
width: 0;
}
flex: 1;
width: 100%;
${({ titleBar }) =>
titleBar === true
? css`
padding-top: 0;
`
: css`
padding-top: 30px;
`}
`;
const IconButton = styled(IconButtonRaw)`
aspect-ratio: 1;
overflow: hidden;
width: 80%;
color: ${({ theme }) => theme.palette.action.active};
`;
const InnerContentRoot = styled.div`
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10px;
width: 100%;
height: 100%;
`;
const ContentRoot = styled.div`
flex: 1;
display: flex;
@ -135,41 +55,12 @@ const ContentRoot = styled.div`
height: 100%;
`;
const SideBarEnd = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;
const SidebarContainer = ({ children }: { children: React.ReactNode }): JSX.Element => {
const platform = usePromiseValue(async () => await window.service.context.get('platform'));
// use native scroll bar on macOS
if (platform === 'darwin') {
return <SidebarRoot>{children}</SidebarRoot>;
}
return <SidebarWithStyle>{children}</SidebarWithStyle>;
};
export default function Main(): JSX.Element {
const { t } = useTranslation();
const workspacesList = useWorkspacesListObservable();
const [wikiCreationMessage, wikiCreationMessageSetter] = useState('');
useAutoCreateFirstWorkspace(workspacesList, wikiCreationMessageSetter);
const preferences = usePreferenceObservable();
/** is title bar on. This only take effect after reload, so we don't want to get this preference from observable */
const titleBar = usePromiseValue<boolean>(() => window.service.preference.get('titleBar'), false)!;
const activeWorkspaceMetadata = workspacesList
?.map((workspace) => ({ active: workspace.active, ...workspace.metadata }))
?.find((workspace) => workspace.active);
const activeWorkspace = workspacesList?.find((workspace) => workspace.active);
const updaterMetaData = useUpdaterObservable();
if (preferences === undefined) return <div>{t('Loading')}</div>;
const { sidebar } = preferences;
const { sidebar, themeSource, sidebarShortcutHints, hideSideBarIcon } = preferences;
const hasError = typeof activeWorkspaceMetadata?.didFailLoadErrorMessage === 'string' &&
activeWorkspaceMetadata?.didFailLoadErrorMessage.length > 0 &&
activeWorkspaceMetadata?.isLoading === false;
return (
<OuterRoot>
<div id='test' data-usage='For spectron automating testing' />
@ -177,89 +68,10 @@ export default function Main(): JSX.Element {
<title>{t('Menu.TidGi')}</title>
</Helmet>
<Root>
{sidebar && (
<SidebarContainer>
<SidebarTop titleBar={titleBar}>
{workspacesList === undefined
? <div>{t('Loading')}</div>
: <SortableWorkspaceSelectorList sidebarShortcutHints={sidebarShortcutHints} workspacesList={workspacesList} hideSideBarIcon={hideSideBarIcon} />}
<WorkspaceSelector
id='add'
hideSideBarIcon={hideSideBarIcon}
index={workspacesList?.length ?? 0}
showSidebarShortcutHints={sidebarShortcutHints}
onClick={() => void window.service.window.open(WindowNames.addWorkspace)}
/>
{workspacesList === undefined ||
(workspacesList.length === 0 && (
<WorkspaceSelector
id='guide'
hideSideBarIcon={hideSideBarIcon}
index={workspacesList?.length ? workspacesList.length ?? 0 + 1 : 1}
active={activeWorkspace?.id === undefined}
showSidebarShortcutHints={sidebarShortcutHints}
onClick={() => void window.service.workspace.clearActiveWorkspace(activeWorkspace?.id)}
/>
))}
</SidebarTop>
<SideBarEnd>
{(workspacesList?.length ?? 0) > 0 && (
<>
<IconButton
id='open-command-palette-button'
aria-label={t('SideBar.CommandPalette')}
onClick={async () => {
await window.service.wiki.requestWikiSendActionMessage('open-command-palette');
}}
>
<Tooltip title={<span>{t('SideBar.CommandPalette')}</span>} placement='top'>
<CommandPaletteIcon />
</Tooltip>
</IconButton>
</>
)}
{updaterMetaData?.status === IUpdaterStatus.updateAvailable && (
<IconButton
id='update-available'
aria-label={t('SideBar.UpdateAvailable')}
onClick={async () => {
await window.service.native.open(updaterMetaData.info?.latestReleasePageUrl ?? latestStableUpdateUrl);
}}
>
<Tooltip title={<span>{t('SideBar.UpdateAvailable')}</span>} placement='top'>
<UpgradeIcon />
</Tooltip>
</IconButton>
)}
<IconButton
id='open-preferences-button'
aria-label={t('SideBar.Preferences')}
onClick={async () => {
await window.service.window.open(WindowNames.preferences);
}}
>
<Tooltip title={<span>{t('SideBar.Preferences')}</span>} placement='top'>
<SettingsIcon />
</Tooltip>
</IconButton>
</SideBarEnd>
</SidebarContainer>
)}
{sidebar && <SideBar />}
<ContentRoot sidebar={sidebar}>
<FindInPage />
<InnerContentRoot>
{activeWorkspace !== undefined && hasError && <WikiErrorMessages activeWorkspace={activeWorkspace} />}
{Array.isArray(workspacesList) && activeWorkspace !== undefined && workspacesList.length > 0 && hasError && (
<ViewLoadErrorMessages activeWorkspace={activeWorkspace} activeWorkspaceMetadata={activeWorkspaceMetadata} />
)}
{Array.isArray(workspacesList) && workspacesList.length > 0 && activeWorkspaceMetadata?.isLoading === true && (
<Typography color='textSecondary'>{t('Loading')}</Typography>
)}
{wikiCreationMessage && <Typography color='textSecondary'>{wikiCreationMessage}</Typography>}
{Array.isArray(workspacesList) && workspacesList.length === 0 && <NewUserMessage sidebar={sidebar} themeSource={themeSource} />}
</InnerContentRoot>
<Languages languageSelectorOnly />
<TiddlyWiki />
<WikiBackground />
</ContentRoot>
</Root>
</OuterRoot>

View file

@ -0,0 +1,59 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
/* eslint-disable @typescript-eslint/promise-function-async */
import { Typography } from '@material-ui/core';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { usePreferenceObservable } from '@services/preferences/hooks';
import { useWorkspacesListObservable } from '@services/workspaces/hooks';
import { useState } from 'react';
import { useAutoCreateFirstWorkspace } from '../Main/useAutoCreateFirstWorkspace';
import { Languages } from '../Preferences/sections/Languages';
import { TiddlyWiki } from '../Preferences/sections/TiddlyWiki';
import { ViewLoadErrorMessages, WikiErrorMessages } from './ErrorMessage';
import { NewUserMessage } from './NewUserMessage';
const InnerContentRoot = styled.div`
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10px;
width: 100%;
height: 100%;
`;
export function WikiBackground(): JSX.Element {
const { t } = useTranslation();
const workspacesList = useWorkspacesListObservable();
const activeWorkspaceMetadata = workspacesList
?.map((workspace) => ({ active: workspace.active, ...workspace.metadata }))
?.find((workspace) => workspace.active);
const activeWorkspace = workspacesList?.find((workspace) => workspace.active);
const hasError = typeof activeWorkspaceMetadata?.didFailLoadErrorMessage === 'string' &&
activeWorkspaceMetadata?.didFailLoadErrorMessage.length > 0 &&
activeWorkspaceMetadata?.isLoading === false;
const [wikiCreationMessage, wikiCreationMessageSetter] = useState('');
useAutoCreateFirstWorkspace(workspacesList, wikiCreationMessageSetter);
const preferences = usePreferenceObservable();
if (preferences === undefined) return <div>{t('Loading')}</div>;
const { sidebar, themeSource } = preferences;
return (
<>
<InnerContentRoot>
{activeWorkspace !== undefined && hasError && <WikiErrorMessages activeWorkspace={activeWorkspace} />}
{Array.isArray(workspacesList) && activeWorkspace !== undefined && workspacesList.length > 0 && hasError && (
<ViewLoadErrorMessages activeWorkspace={activeWorkspace} activeWorkspaceMetadata={activeWorkspaceMetadata} />
)}
{Array.isArray(workspacesList) && workspacesList.length > 0 && activeWorkspaceMetadata?.isLoading === true && <Typography color='textSecondary'>{t('Loading')}</Typography>}
{wikiCreationMessage && <Typography color='textSecondary'>{wikiCreationMessage}</Typography>}
{Array.isArray(workspacesList) && workspacesList.length === 0 && <NewUserMessage sidebar={sidebar} themeSource={themeSource} />}
</InnerContentRoot>
<Languages languageSelectorOnly />
<TiddlyWiki />
</>
);
}

View file

@ -1,5 +1,4 @@
import { WindowNames } from '@services/windows/WindowProperties';
import React from 'react';
import AboutPage from './About';
import { AddWorkspace as DialogAddWorkspace } from './AddWorkspace';

View file

@ -11,6 +11,7 @@ import DateFnsUtils from '@material-ui/lab/AdapterDateFns';
import LocalizationProvider from '@material-ui/lab/LocalizationProvider';
import { I18nextProvider } from 'react-i18next';
import 'typeface-roboto/index.css';
import 'simplebar/dist/simplebar.min.css';
import { darkTheme, lightTheme } from '@services/theme/defaultTheme';
import { useThemeObservable } from '@services/theme/hooks';