refactor: new Main.tsx

This commit is contained in:
tiddlygit-test 2021-02-17 22:17:38 +08:00
parent c2282b7ed4
commit fd580c6462
28 changed files with 455 additions and 492 deletions

View file

@ -1,55 +0,0 @@
// this component is to fix
// -webkit-app-region: drag; not working when set to an element in BrowserView
// This is a workaround for the issue
// You can put the draggable divs at the same region in BrowserWindow,
// then even if you put a BrowserView on top of that region, that region is still draggable.
import React from 'react';
import classNames from 'classnames';
import connectComponent from '../../helpers/connect-component';
const styles = () => ({
root: {
height: 22,
width: '100vw',
WebkitAppRegion: 'drag',
WebkitUserSelect: 'none',
background: 'transparent',
position: 'absolute',
top: 0,
left: 0,
},
// BrowserView has different position & width because of sidebar
rootWithSidebar: {
width: 'calc(100vw - 68px)', // sidebar width is 68px
left: 68,
},
});
interface DraggableRegionProps {
classes: any;
navigationBar: boolean;
sidebar: boolean;
titleBar: boolean;
}
const DraggableRegion = ({ classes, navigationBar, sidebar, titleBar }: DraggableRegionProps) => {
// on macOS or menubar mode, if all bars are hidden
// the top 22px part of BrowserView should be draggable
if ((window.remote.getPlatform() === 'darwin' || window.meta.windowName === 'menubar') && !navigationBar && !titleBar) {
return <div className={classNames(classes.root, sidebar && classes.rootWithSidebar)} />;
}
return null;
};
const mapStateToProps = (state: any) => ({
navigationBar:
(window.remote.getPlatform() === 'linux' && state.preferences.attachToMenubar && !state.preferences.sidebar) || state.preferences.navigationBar,
sidebar: state.preferences.sidebar,
titleBar: state.preferences.titleBar,
});
export default connectComponent(DraggableRegion, mapStateToProps, null, styles);

View file

@ -1,68 +0,0 @@
import React from 'react';
import classnames from 'classnames';
import connectComponent from '../../helpers/connect-component';
const titleBarHeight = 22;
const styles = (theme: any) => ({
root: {
background: theme.palette.type === 'dark' ? '#2a2b2c' : 'linear-gradient(top, #e4e4e4, #cecece)',
height: titleBarHeight,
WebkitAppRegion: 'drag',
WebkitUserSelect: 'none',
textAlign: 'center',
lineHeight: '22px',
fontSize: '13px',
color: theme.palette.type === 'dark' ? 'rgba(255, 255, 255, 0.7)' : 'rgb(77, 77, 77)',
fontFamily: '-apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif',
fontWeight: 500,
paddingLeft: 72,
paddingRight: 72,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
},
rootMenubar: {
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
},
});
interface OwnFakeTitleBarProps {
classes: any;
title?: string;
}
// @ts-expect-error ts-migrate(2456) FIXME: Type alias 'FakeTitleBarProps' circularly referenc... Remove this comment to see the full error message
type FakeTitleBarProps = OwnFakeTitleBarProps & typeof FakeTitleBar.defaultProps;
// @ts-expect-error ts-migrate(7022) FIXME: 'FakeTitleBar' implicitly has type 'any' because i... Remove this comment to see the full error message
const FakeTitleBar = (props: FakeTitleBarProps) => {
const { classes, title } = props;
if (window.remote.getPlatform() !== 'darwin') return null;
return (
<div
className={classnames(classes.root, window.meta.windowName === 'menubar' && classes.rootMenubar)}
onDoubleClick={() => {
// feature: double click on title bar to expand #656
// https://github.com/atomery/webcatalog/issues/656
window.remote.toggleMaximize();
}}>
{(window.meta.windowName === 'main' || window.meta.windowName === 'menubar') && title ? title : window.remote.getAppName()}
</div>
);
};
FakeTitleBar.defaultProps = {
title: '',
};
const mapStateToProps = (state: any) => ({
title: state.general.title,
});
export default connectComponent(FakeTitleBar, mapStateToProps, null, styles);

View file

@ -1,156 +0,0 @@
import React, { useCallback, useEffect, useRef } from 'react';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import connectComponent from '../../helpers/connect-component';
import { closeFindInPage, updateFindInPageText } from '../../state/find-in-page/actions';
const styles = (theme: any) => ({
root: {
background: theme.palette.background.default,
display: 'flex',
alignItems: 'center',
padding: '0 4px',
zIndex: 1,
height: 41,
borderBottom: '1px solid rgba(0, 0, 0, 0.2)',
width: '100%',
},
infoContainer: {
flex: 1,
padding: '0 12px',
},
});
interface FindInPageProps {
activeMatch: number;
classes: any;
matches: number;
onCloseFindInPage: (...arguments_: any[]) => any;
onUpdateFindInPageText: (...arguments_: any[]) => any;
open: boolean;
text: string;
}
const FindInPage = (props: FindInPageProps) => {
const { activeMatch, classes, matches, onCloseFindInPage, onUpdateFindInPageText, open, text } = props;
const inputReference = useRef(null);
// https://stackoverflow.com/a/57556594
// Event handler utilizing useCallback ...
// ... so that reference never changes.
const handleOpenFindInPage = useCallback(() => {
inputReference.current.focus();
inputReference.current.select();
}, [inputReference]);
useEffect(() => {
const { ipcRenderer } = window.remote;
ipcRenderer.on('open-find-in-page', handleOpenFindInPage);
// Remove event listener on cleanup
return () => {
ipcRenderer.removeListener('open-find-in-page', handleOpenFindInPage);
};
}, [handleOpenFindInPage]);
if (!open) {
return null;
}
return (
<div className={classes.root}>
<div className={classes.infoContainer}>
<Typography variant="body2">
<strong>{activeMatch}</strong>
<span> / </span>
<strong>{matches}</strong>
<span> matches</span>
</Typography>
</div>
<div>
<TextField
autoFocus
inputRef={inputReference}
placeholder="Find"
value={text}
margin="dense"
onChange={(e) => {
const value = e.target.value;
onUpdateFindInPageText(value);
if (value.length > 0) {
void window.service.window.findInPage(value, true);
} else {
void window.service.window.stopFindInPage();
}
}}
onInput={(e) => {
const value = (e.target as any).value;
onUpdateFindInPageText(value);
if (value.length > 0) {
void window.service.window.findInPage(value, true);
} else {
void window.service.window.stopFindInPage();
}
}}
onKeyDown={(e) => {
if ((e.keyCode || e.which) === 13) {
// Enter
const value = (e.target as any).value;
if (value.length > 0) {
void window.service.window.findInPage(value, true);
}
}
if ((e.keyCode || e.which) === 27) {
// Escape
void window.service.window.stopFindInPage(true);
onCloseFindInPage();
}
}}
/>
</div>
<Button
size="small"
disabled={text.length < 1 || matches < 1}
onClick={() => {
if (text.length > 0) {
void window.service.window.findInPage(text, false);
}
}}>
Previous
</Button>
<Button
size="small"
disabled={text.length < 1 || matches < 1}
onClick={() => {
if (text.length > 0) {
void window.service.window.findInPage(text, true);
}
}}>
Next
</Button>
<Button
size="small"
disabled={text.length < 1}
onClick={() => {
if (text.length > 0) {
void window.service.window.findInPage(text, true);
}
}}>
Find
</Button>
<Button
size="small"
onClick={() => {
void window.service.window.stopFindInPage(true);
onCloseFindInPage();
}}>
Close
</Button>
</div>
);
};
const mapStateToProps = (state: any) => ({
open: state.findInPage.open,
activeMatch: state.findInPage.activeMatch,
matches: state.findInPage.matches,
text: state.findInPage.text,
});
const actionCreators = {
closeFindInPage,
updateFindInPageText,
};
export default connectComponent(FindInPage, mapStateToProps, actionCreators, styles);

View file

@ -1,419 +0,0 @@
import React from 'react';
import classNames from 'classnames';
import { withTranslation } from 'react-i18next';
import SimpleBar from 'simplebar-react';
import 'simplebar/dist/simplebar.min.css';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import NotificationsIcon from '@material-ui/icons/Notifications';
import NotificationsPausedIcon from '@material-ui/icons/NotificationsPaused';
import SettingsIcon from '@material-ui/icons/Settings';
import { WindowNames } from '@services/windows/WindowProperties';
import { SortableContainer as sortableContainer, SortableElement as sortableElement } from 'react-sortable-hoc';
import connectComponent from '../../helpers/connect-component';
import WorkspaceSelector from './workspace-selector';
import FindInPage from './find-in-page';
import NavigationBar from './navigation-bar';
import FakeTitleBar from './fake-title-bar';
import DraggableRegion from './draggable-region';
// @ts-expect-error ts-migrate(2307) FIXME: Cannot find module '../../images/arrow-white.png' ... Remove this comment to see the full error message
import arrowWhite from '../../images/arrow-white.png';
// @ts-expect-error ts-migrate(2307) FIXME: Cannot find module '../../images/arrow-black.png' ... Remove this comment to see the full error message
import arrowBlack from '../../images/arrow-black.png';
// https://github.com/sindresorhus/array-move/blob/master/index.js
const arrayMove = (array: any, from: any, to: any) => {
const newArray = array.slice();
const startIndex = to < 0 ? newArray.length + to : to;
const item = newArray.splice(from, 1)[0];
newArray.splice(startIndex, 0, item);
return newArray;
};
const styles = (theme: any) => ({
outerRoot: {
display: 'flex',
flexDirection: 'column',
height: '100vh',
width: '100vw',
overflow: 'hidden',
},
root: {
display: 'flex',
flexDirection: 'row',
flex: 1,
height: '100%',
width: '100%',
overflow: 'hidden',
},
sidebarRoot: {
height: '100%',
width: 68,
borderRight: '1px solid rgba(0, 0, 0, 0.2)',
backgroundColor: theme.palette.background.paper,
WebkitAppRegion: 'drag',
WebkitUserSelect: 'none',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
paddingBottom: theme.spacing(1),
boxSizing: 'border-box',
overflowY: 'auto',
overflowX: 'hidden',
},
sidebarTop: {
flex: 1,
paddingTop: window.remote.getPlatform() === 'darwin' ? theme.spacing(3) : 0,
},
sidebarTopFullScreen: {
paddingTop: 0,
},
innerContentRoot: {
flex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: theme.spacing(1),
},
contentRoot: {
flex: 1,
display: 'flex',
flexDirection: 'column',
width: '100%',
},
arrow: {
height: 202,
width: 150,
position: 'absolute',
top: 50,
left: 72,
backgroundImage: `url('${theme.palette.type === 'dark' ? arrowWhite : arrowBlack}')`,
backgroundSize: '150px 202px',
},
avatar: {
fontFamily: theme.typography.fontFamily,
display: 'inline-block',
height: 32,
width: 32,
background: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black,
borderRadius: 4,
color: theme.palette.getContrastText(theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black),
lineHeight: '32px',
textAlign: 'center',
fontWeight: 500,
textTransform: 'uppercase',
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
border: theme.palette.type === 'dark' ? 'none' : '1px solid rgba(0, 0, 0, 0.12)',
},
inlineBlock: {
display: 'inline-block',
fontSize: '18px',
color: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black,
},
tip: {
position: 'absolute',
top: 112,
left: 180,
fontFamily: theme.typography.fontFamily,
userSelect: 'none',
},
tip2: {
fontFamily: theme.typography.fontFamily,
userSelect: 'none',
},
grabbing: {
cursor: 'grabbing !important',
pointerEvents: 'auto !important',
},
end: {
display: 'flex',
flexDirection: 'column',
},
ul: {
marginTop: 0,
marginBottom: '1.5rem',
},
});
const SortableItem = withTranslation()(
sortableElement(({ value, t }: any) => {
const { index, workspace } = value;
const { active, id, name, picturePath, hibernated, transparentBackground, isSubWiki, tagName } = workspace;
return (
<WorkspaceSelector
active={active}
id={id}
key={id}
name={name}
picturePath={picturePath}
transparentBackground={transparentBackground}
order={index}
hibernated={hibernated}
onClick={async () => {
if (isSubWiki) {
await window.service.wiki.requestOpenTiddlerInWiki(tagName);
} else {
const activeWorkspace = await window.service.workspace.getActiveWorkspace();
if (activeWorkspace?.id === id) {
await window.service.wiki.requestWikiSendActionMessage('tm-home');
} else {
await window.service.workspaceView.setActiveWorkspaceView(id);
}
}
}}
onContextMenu={(event: any) => {
event.preventDefault();
const template = [
{
label: t('WorkspaceSelector.EditWorkspace'),
click: async () => await window.service.window.open(WindowNames.editWorkspace, { workspaceID: id }),
},
{
label: t('WorkspaceSelector.RemoveWorkspace'),
click: async () => await window.service.workspaceView.removeWorkspaceView(id),
},
];
if (!active && !isSubWiki) {
template.splice(1, 0, {
label: hibernated ? 'Wake Up Workspace' : 'Hibernate Workspace',
click: async () => {
if (hibernated) {
return await window.service.workspaceView.wakeUpWorkspaceView(id);
}
return await window.service.workspaceView.hibernateWorkspaceView(id);
},
});
}
window.remote.menu.buildFromTemplateAndPopup(template);
}}
/>
);
}),
);
const SortableContainer = sortableContainer(({ children }: any) => <div>{children}</div>);
interface SidebarContainerProps {
className: string;
children: React.ReactNode;
}
const SidebarContainer = ({ className, children }: SidebarContainerProps) => {
// use native scroll bar on macOS
if (window.remote.getPlatform() === 'darwin') {
return <div className={className}>{children}</div>;
}
return <SimpleBar className={className}>{children}</SimpleBar>;
};
interface OwnMainProps {
classes: any;
didFailLoad?: string;
isFullScreen: boolean;
isLoading?: boolean;
navigationBar: boolean;
shouldPauseNotifications: boolean;
sidebar: boolean;
titleBar: boolean;
workspaces: any;
}
// @ts-expect-error ts-migrate(2456) FIXME: Type alias 'MainProps' circularly references itsel... Remove this comment to see the full error message
type MainProps = OwnMainProps & typeof Main.defaultProps;
// @ts-expect-error ts-migrate(7022) FIXME: 'Main' implicitly has type 'any' because it does n... Remove this comment to see the full error message
const Main = ({ classes, didFailLoad, isFullScreen, isLoading, navigationBar, shouldPauseNotifications, sidebar, titleBar, workspaces }: MainProps) => {
const workspacesList = Object.values(workspaces);
const showTitleBar = window.remote.getPlatform() === 'darwin' && titleBar && !isFullScreen;
const requestReload = async () => await window.service.window.reload(window.meta.windowName);
return (
<div className={classes.outerRoot}>
{workspacesList.length > 0 && <DraggableRegion />}
{showTitleBar && <FakeTitleBar />}
<div className={classes.root}>
{sidebar && (
<SidebarContainer className={classes.sidebarRoot}>
<div
className={classNames(
classes.sidebarTop,
(isFullScreen || showTitleBar || window.meta.windowName === 'menubar') && classes.sidebarTopFullScreen,
)}>
<SortableContainer
distance={10}
helperClass={classes.grabbing}
onSortEnd={async ({ oldIndex, newIndex }: any) => {
if (oldIndex === newIndex) return;
const newWorkspacesList = arrayMove(workspacesList, oldIndex, newIndex);
const newWorkspaces = { ...workspaces };
newWorkspacesList.forEach((workspace: any, index: any) => {
newWorkspaces[workspace.id].order = index;
});
await window.service.workspace.set(newWorkspaces);
}}>
{workspacesList.map((workspace, index) => (
// @ts-expect-error ts-migrate(2571) FIXME: Object is of type 'unknown'.
<SortableItem key={`item-${workspace.id}`} index={index} value={{ index: index, workspace }} />
))}
</SortableContainer>
<WorkspaceSelector id="add" onClick={async () => await window.service.window.open(WindowNames.addWorkspace)} />
</div>
{!navigationBar && (
<div className={classes.end}>
<IconButton
aria-label="Notifications"
onClick={async () => await window.service.window.open(WindowNames.notifications)}
className={classes.iconButton}>
{shouldPauseNotifications ? <NotificationsPausedIcon /> : <NotificationsIcon />}
</IconButton>
{window.meta.windowName === 'menubar' && (
<IconButton
aria-label="Preferences"
onClick={async () => await window.service.window.open(WindowNames.preferences)}
className={classes.iconButton}>
<SettingsIcon />
</IconButton>
)}
</div>
)}
</SidebarContainer>
)}
<div className={classes.contentRoot}>
{navigationBar && <NavigationBar />}
<FindInPage />
<div className={classes.innerContentRoot}>
{Object.keys(workspaces).length > 0 && didFailLoad && !isLoading && (
<div>
<Typography align="left" variant="h5">
Wiki is not started or not loaded
</Typography>
<Typography align="left" variant="body2">
{didFailLoad}
</Typography>
<br />
<Typography align="left" variant="body2">
<>
Try:
<ul className={classes.ul}>
<li>
Click{' '}
<b onClick={requestReload} onKeyPress={requestReload} role="button" tabIndex={0} style={{ cursor: 'pointer' }}>
Reload
</b>{' '}
button below or press <b>CMD_or_Ctrl + R</b> to reload the page.
</li>
<li>
Check the{' '}
<b
onClick={async () => await window.service.native.open(getLogFolderPath(), true)}
onKeyPress={async () => await window.service.native.open(getLogFolderPath(), true)}
role="button"
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'number | ... Remove this comment to see the full error message
tabIndex="0"
style={{ cursor: 'pointer' }}>
Log Folder
</b>{' '}
to see what happened.
</li>
<li>Backup your file, remove workspace and recreate one.</li>
</ul>
</>
</Typography>
<Button variant="outlined" onClick={requestReload}>
Reload
</Button>
</div>
)}
{Object.keys(workspaces).length > 0 && isLoading && (
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
<Typography type="body1" color="textSecondary">
Loading..
</Typography>
)}
{Object.keys(workspaces).length === 0 && (
<div>
{sidebar ? (
<>
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ alt: string; className: any; }' is not ass... Remove this comment to see the full error message */}
<div alt="Arrow" className={classes.arrow} />
<div className={classes.tip}>
<span className={classes.inlineBlock}>Click</span>
<div className={classes.avatar}>+</div>
<span className={classes.inlineBlock}>to get started!</span>
</div>
</>
) : (
<div className={classes.tip2}>
<span className={classes.inlineBlock}>
<span>Click </span>
<strong>Workspaces &gt; Add Workspace</strong>
<span> to get started!</span>
</span>
</div>
)}
</div>
)}
</div>
</div>
</div>
// @ts-expect-error ts-migrate(2304) FIXME: Cannot find name 'div'.
</div>
);
};
Main.defaultProps = {
isLoading: false,
};
const mapStateToProps = (state: any) => {
// @ts-expect-error ts-migrate(2571) FIXME: Object is of type 'unknown'.
const activeWorkspace = Object.values(state.workspaces).find((workspace) => workspace.active);
return {
// @ts-expect-error ts-migrate(2571) FIXME: Object is of type 'unknown'.
didFailLoad: activeWorkspace && state.workspaceMetas[activeWorkspace.id] ? state.workspaceMetas[activeWorkspace.id].didFailLoad : undefined,
isFullScreen: state.general.isFullScreen,
// @ts-expect-error ts-migrate(2571) FIXME: Object is of type 'unknown'.
isLoading: activeWorkspace && state.workspaceMetas[activeWorkspace.id] ? Boolean(state.workspaceMetas[activeWorkspace.id].isLoading) : false,
navigationBar:
(window.remote.getPlatform() === 'linux' && state.preferences.attachToMenubar && !state.preferences.sidebar) || state.preferences.navigationBar,
shouldPauseNotifications: state.notifications.pauseNotificationsInfo !== null,
sidebar: state.preferences.sidebar,
titleBar: state.preferences.titleBar,
workspaces: state.workspaces,
};
};
export default connectComponent(Main, mapStateToProps, undefined, styles);

View file

@ -1,207 +0,0 @@
import React, { useState } from 'react';
import classNames from 'classnames';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
import HomeIcon from '@material-ui/icons/Home';
import IconButton from '@material-ui/core/IconButton';
import NotificationsIcon from '@material-ui/icons/Notifications';
import NotificationsPausedIcon from '@material-ui/icons/NotificationsPaused';
import RefreshIcon from '@material-ui/icons/Refresh';
import SettingsIcon from '@material-ui/icons/SettingsSharp';
import InputBase from '@material-ui/core/InputBase';
import { WindowNames } from '@services/windows/WindowProperties';
import connectComponent from '../../helpers/connect-component';
import isUrl from '../../helpers/is-url';
import { updateAddressBarInfo } from '../../state/general/actions';
const styles = (theme: any) => ({
root: {
width: '100%',
height: 36,
backgroundColor: theme.palette.background.paper,
borderBottom: '1px solid rgba(0, 0, 0, 0.2)',
display: 'flex',
alignItems: 'center',
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
WebkitAppRegion: 'drag',
WebkitUserSelect: 'none',
},
rootWithTrafficLights: {
paddingLeft: 68 + theme.spacing(1),
},
center: {
flex: 1,
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
},
iconButton: {
padding: 6,
},
icon: {
fontSize: '18px',
},
addressBarRoot: {
width: '100%',
background: theme.palette.background.default,
borderRadius: 16,
WebkitAppRegion: 'none',
WebkitUserSelect: 'text',
},
addressBarInput: {
fontSize: '0.8em',
paddingLeft: 12,
paddingRight: 12,
paddingTop: 4,
paddingBottom: 4,
},
goButton: {
padding: 4,
},
});
const processUrl = (url: any) => {
if (!url) {
return url;
}
if (isUrl(url)) {
return url;
}
const httpUrl = `http://${url}`;
if (/[\dA-Za-z]+(\.[\dA-Za-z]+)+/.test(url) && isUrl(httpUrl)) {
// match common url format
return httpUrl;
}
const processedUrl = `http://google.com/search?q=${encodeURIComponent(url)}`;
return processedUrl;
};
interface OwnNavigationBarProps {
address?: string;
addressEdited: boolean;
canGoBack: boolean;
canGoForward: boolean;
classes: any;
hasTrafficLights: boolean;
hasWorkspaces: boolean;
onUpdateAddressBarInfo: (...arguments_: any[]) => any;
shouldPauseNotifications: boolean;
}
// @ts-expect-error ts-migrate(2456) FIXME: Type alias 'NavigationBarProps' circularly referen... Remove this comment to see the full error message
type NavigationBarProps = OwnNavigationBarProps & typeof NavigationBar.defaultProps;
// @ts-expect-error ts-migrate(7022) FIXME: 'NavigationBar' implicitly has type 'any' because ... Remove this comment to see the full error message
const NavigationBar = ({
address,
addressEdited,
canGoBack,
canGoForward,
classes,
hasTrafficLights,
hasWorkspaces,
onUpdateAddressBarInfo,
shouldPauseNotifications,
}: NavigationBarProps) => {
const [addressInputClicked, setAddressInputClicked] = useState(false);
return (
<div className={classNames(classes.root, hasTrafficLights && classes.rootWithTrafficLights)}>
<div className={classes.left}>
<IconButton
aria-label="Go back"
className={classes.iconButton}
disabled={!hasWorkspaces || !canGoBack}
onClick={async () => await window.service.window.goBack(window.meta.windowName)}>
<ArrowBackIcon className={classes.icon} />
</IconButton>
<IconButton
aria-label="Go forward"
className={classes.iconButton}
disabled={!hasWorkspaces || !canGoForward}
onClick={async () => await window.service.window.goForward(window.meta.windowName)}>
<ArrowForwardIcon className={classes.icon} />
</IconButton>
<IconButton
aria-label="Reload"
className={classes.iconButton}
onClick={async () => await window.service.window.reload(window.meta.windowName)}
disabled={!hasWorkspaces}>
<RefreshIcon className={classes.icon} />
</IconButton>
<IconButton
aria-label="Go home"
className={classes.iconButton}
onClick={async () => await window.service.window.goHome(window.meta.windowName)}
disabled={!hasWorkspaces}>
<HomeIcon className={classes.icon} />
</IconButton>
</div>
<div className={classes.center}>
<InputBase
classes={{ root: classes.addressBarRoot, input: classes.addressBarInput }}
placeholder="Search Google or type a URL"
type="text"
value={hasWorkspaces ? address : ''}
disabled={!hasWorkspaces}
endAdornment={
addressEdited &&
address &&
hasWorkspaces && (
<IconButton
aria-label="Go"
className={classes.goButton}
onClick={async () => {
const processedUrl = processUrl(address);
onUpdateAddressBarInfo(processedUrl, false);
await window.service.workspaceView.loadURL(processedUrl);
}}>
<ArrowForwardIcon fontSize="small" />
</IconButton>
)
}
onChange={(e) => {
onUpdateAddressBarInfo(e.target.value, true);
}}
onKeyDown={async (e) => {
if (e.key === 'Enter') {
(e.target as any).blur();
const processedUrl = processUrl(address);
onUpdateAddressBarInfo(processedUrl, false);
await window.service.workspaceView.loadURL(processedUrl);
}
}}
// https://stackoverflow.com/a/16659291
onClick={(e) => {
if (!addressInputClicked) {
(e.target as any).select();
setAddressInputClicked(true);
}
}}
onBlur={() => {
setAddressInputClicked(false);
}}
/>
</div>
<div>
<IconButton aria-label="Notifications" onClick={async () => await window.service.window.open(WindowNames.notifications)} className={classes.iconButton}>
{shouldPauseNotifications ? <NotificationsPausedIcon className={classes.icon} /> : <NotificationsIcon className={classes.icon} />}
</IconButton>
<IconButton aria-label="Preferences" className={classes.iconButton} onClick={async () => await window.service.window.open(WindowNames.preferences)}>
<SettingsIcon className={classes.icon} />
</IconButton>
</div>
</div>
);
};
NavigationBar.defaultProps = {
address: '',
};
const mapStateToProps = (state: any) => ({
address: state.general.address || '',
addressEdited: Boolean(state.general.addressEdited),
canGoBack: state.general.canGoBack,
canGoForward: state.general.canGoForward,
hasTrafficLights: window.remote.getPlatform() === 'darwin' && !state.preferences.titleBar && !state.preferences.sidebar,
hasWorkspaces: Object.keys(state.workspaces).length > 0,
shouldPauseNotifications: state.notifications.pauseNotificationsInfo !== null,
});
const actionCreators = {
updateAddressBarInfo,
};
export default connectComponent(NavigationBar, mapStateToProps, actionCreators, styles);

View file

@ -1,163 +0,0 @@
import React, { useState, useEffect } from 'react';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import Badge from '@material-ui/core/Badge';
import connectComponent from '../../helpers/connect-component';
import defaultIcon from '../../images/default-icon.png';
const styles = (theme: any) => ({
root: {
height: 'fit-content',
width: 68,
padding: '10px 0',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
fontFamily: theme.typography.fontFamily,
outline: 'none',
'&:hover': {
background: theme.palette.action.hover,
cursor: 'pointer',
opacity: 1,
},
WebkitAppRegion: 'no-drag',
opacity: 0.8,
position: 'relative',
borderLeft: '4px solid',
borderColor: 'transparent',
},
rootHibernate: {
opacity: 0.4,
},
rootActive: {
borderColor: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black,
opacity: 1,
},
avatar: {
height: 36,
width: 36,
background: theme.palette.type === 'dark' ? theme.palette.common.black : theme.palette.common.white,
borderRadius: 4,
color: theme.palette.getContrastText(theme.palette.type === 'dark' ? theme.palette.common.black : theme.palette.common.white),
lineHeight: '36px',
textAlign: 'center',
fontWeight: 500,
textTransform: 'uppercase',
border: theme.palette.type === 'dark' ? 'none' : '1px solid rgba(0, 0, 0, 0.12)',
overflow: 'hidden',
},
avatarLarge: {
height: 44,
width: 44,
lineHeight: '44px',
},
avatarPicture: {
height: 36 - 2,
width: 36 - 2,
},
avatarPictureLarge: {
height: 44,
width: 44,
},
transparentAvatar: {
background: 'transparent',
border: 'none',
borderRadius: 0,
},
addAvatar: {
background: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black,
color: theme.palette.getContrastText(theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black),
},
shortcutText: {
marginTop: 2,
marginBottom: 0,
padding: 0,
fontSize: '12px',
fontWeight: 500,
display: 'inline-block',
wordBreak: 'break-all',
color: theme.palette.text.primary,
},
badge: {
lineHeight: '20px',
},
});
interface Props {
active: boolean;
badgeCount: number;
classes: Object;
hibernated: boolean;
id: string;
onClick: Function;
onContextMenu: Function;
order: number;
picturePath: string;
sidebarShortcutHints: boolean;
transparentBackground: boolean;
workspaceName: string;
}
function WorkspaceSelector({
active = false,
badgeCount = 0,
classes,
hibernated = false,
id,
onClick = () => {},
onContextMenu = () => {},
order = 0,
picturePath,
sidebarShortcutHints,
transparentBackground = false,
workspaceName,
}: Props) {
const { t } = useTranslation();
const [shortWorkspaceName, shortWorkspaceNameSetter] = useState<string>(t('Loading'));
useEffect(() => {
void (async () => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
shortWorkspaceNameSetter(workspaceName ? await window.remote.getBaseName(workspaceName) : t('WorkspaceSelector.BadWorkspacePath'));
});
}, []);
return (
<div
role="button"
className={classNames((classes as any).root, hibernated && (classes as any).rootHibernate, active && (classes as any).rootActive)}
onClick={onClick}
onKeyDown={onClick}
onContextMenu={onContextMenu}
tabIndex="0">
<Badge color="secondary" badgeContent={badgeCount} max={99} classes={{ badge: (classes as any).badge }}>
<div
className={classNames(
(classes as any).avatar,
!sidebarShortcutHints && (classes as any).avatarLarge,
transparentBackground && (classes as any).transparentAvatar,
id === 'add' && (classes as any).addAvatar,
)}>
{id !== 'add' ? (
<img
alt="Icon"
className={classNames((classes as any).avatarPicture, !sidebarShortcutHints && (classes as any).avatarPictureLarge)}
src={picturePath ? `file:///${picturePath}` : defaultIcon}
draggable={false}
/>
) : (
'+'
)}
</div>
</Badge>
{sidebarShortcutHints && (id === 'add' || order < 9) && (
<p className={(classes as any).shortcutText}>{id === 'add' ? t('WorkspaceSelector.Add') : shortWorkspaceName}</p>
)}
</div>
);
}
const mapStateToProps = (state: any, ownProps: any) => {
return {
badgeCount: state.workspaceMetas[ownProps.id] ? state.workspaceMetas[ownProps.id].badgeCount : 0,
workspaceName: state.workspaces?.[ownProps.id]?.name,
sidebarShortcutHints: state.preferences.sidebarShortcutHints,
};
};
export default connectComponent(WorkspaceSelector, mapStateToProps, undefined, styles);