mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2026-01-26 14:31:23 -08:00
refactor: new Main.tsx
This commit is contained in:
parent
c2282b7ed4
commit
fd580c6462
28 changed files with 455 additions and 492 deletions
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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 > 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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue