mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2026-02-04 14:52:40 -08:00
refactor: new Main.tsx
This commit is contained in:
parent
c2282b7ed4
commit
fd580c6462
28 changed files with 455 additions and 492 deletions
|
|
@ -59,6 +59,8 @@ module.exports = {
|
|||
],
|
||||
'comma-dangle': [2, 'always-multiline'],
|
||||
'no-undef': 'off',
|
||||
'unicorn/no-array-for-each': 'off',
|
||||
'multiline-ternary': 'off',
|
||||
'unicorn/filename-case': [
|
||||
0,
|
||||
{
|
||||
|
|
@ -68,6 +70,8 @@ module.exports = {
|
|||
],
|
||||
'unicorn/consistent-function-scoping': [0],
|
||||
'no-void': [0],
|
||||
'unicorn/prefer-ternary': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
semi: [0],
|
||||
'no-use-before-define': [0],
|
||||
'@typescript-eslint/no-use-before-define': [1],
|
||||
|
|
|
|||
5
package-lock.json
generated
5
package-lock.json
generated
|
|
@ -4777,6 +4777,11 @@
|
|||
"integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
|
||||
"dev": true
|
||||
},
|
||||
"array-move": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/array-move/-/array-move-3.0.1.tgz",
|
||||
"integrity": "sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg=="
|
||||
},
|
||||
"array-reduce": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
"dependencies": {
|
||||
"@rematch/core": "^2.0.0",
|
||||
"@tiddlygit/tiddlywiki": "5.1.24-prerelease.20210103",
|
||||
"array-move": "^3.0.1",
|
||||
"bluebird": "^3.7.2",
|
||||
"chokidar": "^3.5.1",
|
||||
"darkreader": "4.9.27",
|
||||
|
|
|
|||
|
|
@ -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,4 +1,5 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { AsyncReturnType } from 'type-fest';
|
||||
|
||||
/**
|
||||
* Use value from service, especially constant value that never changes
|
||||
|
|
@ -6,11 +7,14 @@ import { useEffect, useState } from 'react';
|
|||
* @param valuePromise A promise contain the value we want to use in React
|
||||
* @param defaultValue empty array or undefined, as initial value
|
||||
*/
|
||||
export function usePromiseValue<T>(valuePromise: Promise<T> | (() => Promise<T>), defaultValue?: T): T | undefined {
|
||||
const [value, valueSetter] = useState<T | undefined>(defaultValue);
|
||||
export function usePromiseValue<T, DefaultValueType = T | undefined>(
|
||||
asyncValue: () => Promise<T>,
|
||||
defaultValue?: AsyncReturnType<typeof asyncValue>,
|
||||
): T | DefaultValueType {
|
||||
const [value, valueSetter] = useState<T | DefaultValueType>(defaultValue as T | DefaultValueType);
|
||||
useEffect(() => {
|
||||
void (async () => {
|
||||
valueSetter(typeof valuePromise === 'function' ? await valuePromise() : await valuePromise);
|
||||
valueSetter(await asyncValue());
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Button, DialogContent } from '@material-ui/core';
|
||||
import { usePromiseValue } from '@/helpers/use-service-value';
|
||||
|
|
@ -54,16 +54,17 @@ export default function About(): JSX.Element {
|
|||
{ name: 'Node Version', version: processVersions.node },
|
||||
{ name: 'Chromium Version', version: processVersions.chrome },
|
||||
];
|
||||
}, []);
|
||||
}, [] as Array<{ name: string; version: string }>);
|
||||
|
||||
const iconPath = usePromiseValue<string | undefined>(window.service.context.get('ICON_PATH') as Promise<string>);
|
||||
const appVersion = usePromiseValue<string | undefined>(window.service.context.get('appVersion') as Promise<string>);
|
||||
const iconPath = usePromiseValue<string>(async () => (await window.service.context.get('ICON_PATH')) as string);
|
||||
const appVersion = usePromiseValue<string>(async () => (await window.service.context.get('appVersion')) as string);
|
||||
const platform = usePromiseValue<string>(async () => (await window.service.context.get('platform')) as string);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DialogContentSC>
|
||||
<Icon src={iconPath} alt="TiddlyGit" />
|
||||
<Title>TiddlyGit</Title>
|
||||
<Title>TiddlyGit ({platform ?? 'Unknown Platform'})</Title>
|
||||
<Version>{`Version v${appVersion ?? ' - '}.`}</Version>
|
||||
<VersionSmallContainer>
|
||||
{versions?.map(({ name, version }) => (
|
||||
|
|
|
|||
75
src/pages/Main/SortableWorkspaceSelector.tsx
Normal file
75
src/pages/Main/SortableWorkspaceSelector.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import { withTranslation, WithTranslation } from 'react-i18next';
|
||||
import { SortableContainer as sortableContainer, SortableElement as sortableElement } from 'react-sortable-hoc';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import WorkspaceSelector from './workspace-selector';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
|
||||
export const SortableContainer = sortableContainer(({ children }: { children: React.ReactNode }) => <div>{children}</div>);
|
||||
|
||||
export interface ISortableItemProps {
|
||||
value: {
|
||||
index: number;
|
||||
workspace: IWorkspace;
|
||||
};
|
||||
t: WithTranslation['t'];
|
||||
}
|
||||
|
||||
export const SortableItem = sortableElement(
|
||||
withTranslation()(({ value, t }: ISortableItemProps) => {
|
||||
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: Event) => {
|
||||
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);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
void window.service.menu.buildContextMenuAndPopup(template);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
);
|
||||
309
src/pages/Main/index.tsx
Normal file
309
src/pages/Main/index.tsx
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
import React from 'react';
|
||||
import arrayMove from 'array-move';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { AsyncReturnType } from 'type-fest';
|
||||
|
||||
import SimpleBar from 'simplebar-react';
|
||||
import 'simplebar/dist/simplebar.min.css';
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
import IconButtonRaw 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 { usePromiseValue } from '@/helpers/use-service-value';
|
||||
|
||||
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';
|
||||
|
||||
import arrowWhite from '@/images/arrow-white.png';
|
||||
import arrowBlack from '@/images/arrow-black.png';
|
||||
import { SortableContainer, SortableItem } from './SortableWorkspaceSelector';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
|
||||
const OuterRoot = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const Root = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const sideBarStyle = css`
|
||||
height: 100%;
|
||||
width: 68;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
background-color: #fffff0;
|
||||
-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;
|
||||
`;
|
||||
const SidebarRoot = styled.div`
|
||||
${sideBarStyle}
|
||||
`;
|
||||
const SidebarWithStyle = styled(SimpleBar)`
|
||||
${sideBarStyle}
|
||||
`;
|
||||
|
||||
const SidebarTop = styled.div<{ fullscreen?: boolean }>`
|
||||
flex: 1;
|
||||
/** // TODO: darwin theme */
|
||||
/* padding-top-window-remote-get-platform-darwin-theme-spacing-3: 0; */
|
||||
${({ fullscreen }) => fullscreen === true && `padding-top: 0;`}
|
||||
`;
|
||||
|
||||
const IconButton = styled(IconButtonRaw)``;
|
||||
|
||||
const InnerContentRoot = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
`;
|
||||
|
||||
const ContentRoot = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const Arrow = styled.div<{ image: string }>`
|
||||
height: 202;
|
||||
width: 150;
|
||||
position: absolute;
|
||||
top: 50;
|
||||
left: 72;
|
||||
|
||||
background-image: url(${({ image }) => image});
|
||||
background-size: 150px 202px;
|
||||
`;
|
||||
|
||||
const Avatar = styled.div`
|
||||
display: inline-block;
|
||||
height: 32;
|
||||
width: 32;
|
||||
/** // TODO: dark theme */
|
||||
/* background: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black; */
|
||||
border-radius: 4;
|
||||
/** // TODO: dark theme */
|
||||
/* color: theme.palette.getContrastText(theme.palette.type === 'dark' ? theme.palette.common.white: theme.palette.common.black); */
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
/** // TODO: dark theme */
|
||||
/* border: theme.palette.type === 'dark' ? 'none' : 1px solid rgba(0, 0, 0, 0.12); */
|
||||
`;
|
||||
|
||||
const Tip2Text = styled.span`
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
/** // TODO: dark theme */
|
||||
/* color: theme.palette.type === 'dark' ? theme.palette.common.white : theme.palette.common.black; */
|
||||
`;
|
||||
|
||||
const Tip = styled.div`
|
||||
position: absolute;
|
||||
top: 112;
|
||||
left: 180;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const Tip2 = styled.div`
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const End = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const HelperTextsList = styled.ul`
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5rem;
|
||||
`;
|
||||
|
||||
const SidebarContainer = ({ children }: { children: React.ReactNode }): JSX.Element => {
|
||||
const platform = usePromiseValue(async () => (await window.service.context.get('platform')) as string);
|
||||
// use native scroll bar on macOS
|
||||
if (platform === 'darwin') {
|
||||
return <SidebarRoot>{children}</SidebarRoot>;
|
||||
}
|
||||
return <SidebarWithStyle>{children}</SidebarWithStyle>;
|
||||
};
|
||||
|
||||
function Main(): JSX.Element {
|
||||
// TODO: make workspacesList observable
|
||||
const workspacesList = usePromiseValue(
|
||||
window.service.workspace.getWorkspacesAsList,
|
||||
[] as AsyncReturnType<typeof window.service.workspace.getWorkspacesAsList>,
|
||||
)!;
|
||||
const platform = usePromiseValue<string>(async () => (await window.service.context.get('platform')) as string);
|
||||
const attachToMenubar = usePromiseValue(async () => (await window.service.preference.get('attachToMenubar')) as boolean);
|
||||
const titleBar = usePromiseValue(async () => (await window.service.preference.get('titleBar')) as boolean);
|
||||
const sidebar = usePromiseValue(async () => (await window.service.preference.get('sidebar')) as boolean);
|
||||
const pauseNotifications = usePromiseValue(async () => (await window.service.preference.get('pauseNotifications')) as string);
|
||||
const navigationBar = usePromiseValue(async () => (await window.service.preference.get('navigationBar')) as boolean);
|
||||
const themeSource = usePromiseValue(async () => (await window.service.preference.get('themeSource')) as 'system' | 'light' | 'dark');
|
||||
const isFullScreen = usePromiseValue(window.service.window.isFullScreen);
|
||||
const showTitleBar = platform === 'darwin' && titleBar === true && isFullScreen === false;
|
||||
const mainWorkspaceMetaData = usePromiseValue(async () => {
|
||||
const activeWorkspace = await window.service.workspace.getActiveWorkspace();
|
||||
if (activeWorkspace !== undefined) {
|
||||
const metadata = await window.service.workspace.getMetaData(activeWorkspace.id);
|
||||
return metadata;
|
||||
}
|
||||
return { didFailLoadErrorMessage: 'No ActiveWorkspace' };
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
}, {} as AsyncReturnType<typeof window.service.workspace.getMetaData>);
|
||||
const requestReload = async (): Promise<void> => await window.service.window.reload(window.meta.windowName);
|
||||
|
||||
return (
|
||||
<OuterRoot>
|
||||
{workspacesList.length > 0 && <DraggableRegion />}
|
||||
{showTitleBar && <FakeTitleBar />}
|
||||
<Root>
|
||||
{sidebar === true && (
|
||||
<SidebarContainer>
|
||||
<SidebarTop fullscreen={isFullScreen === true || showTitleBar || attachToMenubar}>
|
||||
<SortableContainer
|
||||
distance={10}
|
||||
onSortEnd={async ({ oldIndex, newIndex }) => {
|
||||
if (oldIndex === newIndex) return;
|
||||
|
||||
const newWorkspacesList = arrayMove(workspacesList, oldIndex, newIndex);
|
||||
const newWorkspaces: Record<string, IWorkspace> = {};
|
||||
newWorkspacesList.forEach((workspace, index) => {
|
||||
newWorkspaces[workspace.id] = workspace;
|
||||
newWorkspaces[workspace.id].order = index;
|
||||
});
|
||||
|
||||
await window.service.workspace.setWorkspaces(newWorkspaces);
|
||||
}}>
|
||||
{workspacesList.map((workspace, index) => (
|
||||
<SortableItem key={`item-${workspace.id}`} index={index} value={{ index: index, workspace }} />
|
||||
))}
|
||||
</SortableContainer>
|
||||
<WorkspaceSelector id="add" onClick={async () => await window.service.window.open(WindowNames.addWorkspace)} />
|
||||
</SidebarTop>
|
||||
{navigationBar === false && (
|
||||
<End>
|
||||
<IconButton aria-label="Notifications" onClick={async () => await window.service.window.open(WindowNames.notifications)}>
|
||||
{typeof pauseNotifications === 'string' && pauseNotifications.length > 0 ? <NotificationsPausedIcon /> : <NotificationsIcon />}
|
||||
</IconButton>
|
||||
{attachToMenubar === true && (
|
||||
<IconButton aria-label="Preferences" onClick={async () => await window.service.window.open(WindowNames.preferences)}>
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</End>
|
||||
)}
|
||||
</SidebarContainer>
|
||||
)}
|
||||
<ContentRoot>
|
||||
{navigationBar === true && <NavigationBar />}
|
||||
<FindInPage />
|
||||
<InnerContentRoot>
|
||||
{workspacesList.length > 0 && mainWorkspaceMetaData?.didFailLoadErrorMessage && mainWorkspaceMetaData?.isLoading === false && (
|
||||
<div>
|
||||
<Typography align="left" variant="h5">
|
||||
Wiki is not started or not loaded
|
||||
</Typography>
|
||||
<Typography align="left" variant="body2">
|
||||
{mainWorkspaceMetaData.didFailLoadErrorMessage}
|
||||
</Typography>
|
||||
|
||||
<br />
|
||||
<Typography align="left" variant="body2">
|
||||
<>
|
||||
Try:
|
||||
<HelperTextsList>
|
||||
<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((await window.service.context.get('LOG_FOLDER')) as string, true)}
|
||||
onKeyPress={async () => await window.service.native.open((await window.service.context.get('LOG_FOLDER')) as string, true)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
style={{ cursor: 'pointer' }}>
|
||||
Log Folder
|
||||
</b>{' '}
|
||||
to see what happened.
|
||||
</li>
|
||||
<li>Backup your file, remove workspace and recreate one.</li>
|
||||
</HelperTextsList>
|
||||
</>
|
||||
</Typography>
|
||||
|
||||
<Button variant="outlined" onClick={requestReload}>
|
||||
Reload
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{workspacesList.length > 0 && mainWorkspaceMetaData?.isLoading && <Typography color="textSecondary">Loading..</Typography>}
|
||||
{workspacesList.length === 0 && (
|
||||
<div>
|
||||
{sidebar === true ? (
|
||||
<>
|
||||
<Arrow image={themeSource === 'dark' ? arrowWhite : arrowBlack} />
|
||||
<Tip>
|
||||
<Tip2Text>Click</Tip2Text>
|
||||
<Avatar>+</Avatar>
|
||||
<Tip2Text>to get started!</Tip2Text>
|
||||
</Tip>
|
||||
</>
|
||||
) : (
|
||||
<Tip2>
|
||||
<Tip2Text>
|
||||
<span>Click </span>
|
||||
<strong>Workspaces > Add Workspace</strong>
|
||||
<span> to get started!</span>
|
||||
</Tip2Text>
|
||||
</Tip2>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</InnerContentRoot>
|
||||
</ContentRoot>
|
||||
</Root>
|
||||
</OuterRoot>
|
||||
);
|
||||
}
|
||||
|
||||
Main.defaultProps = {
|
||||
isLoading: false,
|
||||
};
|
||||
|
|
@ -2,7 +2,6 @@ import { contextBridge, remote, ipcRenderer, webFrame, desktopCapturer } from 'e
|
|||
import { Channels } from '@/constants/channels';
|
||||
|
||||
// contextBridge.exposeInMainWorld('remote', {
|
||||
// isFullScreen: () => remote.getCurrentWindow().isFullScreen(),
|
||||
// ipcRenderer: {
|
||||
// on: (channel: Channels, callback: (event: Electron.IpcRendererEvent, ...arguments_: unknown[]) => void) =>
|
||||
// ipcRenderer.on(channel, (event, ...arguments_) => callback(event, ...arguments_)),
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ declare global {
|
|||
if (windowName === WindowNames.view) {
|
||||
void import('./view');
|
||||
}
|
||||
if (window.meta.windowName === 'main') {
|
||||
if (browserViewMetaData.windowName === 'main') {
|
||||
// automatically reload page when wifi/network is connected
|
||||
// https://www.electronjs.org/docs/tutorial/online-offline-events
|
||||
const handleOnlineOffline = (): void => {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const DialogOpenUrlWith = React.lazy(async () => await import('./components/dial
|
|||
const DialogPreferences = React.lazy(async () => await import('./components/dialog-preferences'));
|
||||
const DialogProxy = React.lazy(async () => await import('./components/dialog-proxy'));
|
||||
const DialogSpellcheckLanguages = React.lazy(async () => await import('./components/dialog-spellcheck-languages'));
|
||||
const Main = React.lazy(async () => await import('./components/main'));
|
||||
const Main = React.lazy(async () => await import('./pages/Main'));
|
||||
|
||||
const App = (): JSX.Element => {
|
||||
switch (window.meta.windowName) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const isMac = process.platform === 'darwin';
|
|||
const sourcePath = path.resolve(__dirname, '..', '..');
|
||||
export const buildResourcePath = path.resolve(sourcePath, '..', 'build-resources');
|
||||
|
||||
const REACT_PATH = MAIN_WINDOW_WEBPACK_ENTRY;
|
||||
const REACT_PATH = 'MAIN_WINDOW_WEBPACK_ENTRY';
|
||||
// .app/Contents/Resources/wiki/
|
||||
const TIDDLYWIKI_TEMPLATE_FOLDER_PATH = isDev ? path.resolve(sourcePath, '..', 'template', 'wiki') : path.resolve(process.resourcesPath, '..', 'wiki');
|
||||
const TIDDLERS_PATH = 'tiddlers';
|
||||
|
|
|
|||
|
|
@ -2,9 +2,15 @@ import { Menu, MenuItemConstructorOptions, shell } from 'electron';
|
|||
import { debounce, take, drop } from 'lodash';
|
||||
import { injectable } from 'inversify';
|
||||
import { IMenuService, DeferredMenuItemConstructorOptions } from './interface';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { lazyInject } from '@services/container';
|
||||
import serviceIdentifier from '@services/serviceIdentifier';
|
||||
import { IWindowService } from '@services/windows/interface';
|
||||
|
||||
@injectable()
|
||||
export class MenuService implements IMenuService {
|
||||
@lazyInject(serviceIdentifier.Window) private readonly windowService!: IWindowService;
|
||||
|
||||
private readonly menuTemplate: DeferredMenuItemConstructorOptions[];
|
||||
|
||||
/**
|
||||
|
|
@ -169,4 +175,12 @@ export class MenuService implements IMenuService {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
public buildContextMenuAndPopup(template: MenuItemConstructorOptions[], windowName = WindowNames.main): void {
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
const windowToPopMenu = this.windowService.get(windowName);
|
||||
if (windowToPopMenu !== undefined) {
|
||||
menu.popup({ window: windowToPopMenu });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Menu, MenuItemConstructorOptions, shell } from 'electron';
|
|||
|
||||
import { ProxyPropertyType } from '@/helpers/electron-ipc-proxy/common';
|
||||
import { MenuChannel } from '@/constants/channels';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
|
||||
/**
|
||||
* MenuItemConstructorOptions that allows properties like "label", "enabled", "submenu" to be () => xxx
|
||||
|
|
@ -21,11 +22,13 @@ export interface DeferredMenuItemConstructorOptions extends Omit<MenuItemConstru
|
|||
export interface IMenuService {
|
||||
buildMenu(): void;
|
||||
insertMenu(menuID: string, menuItems: DeferredMenuItemConstructorOptions[], afterSubMenu?: string | null, withSeparator?: boolean): void;
|
||||
buildContextMenuAndPopup(template: MenuItemConstructorOptions[], windowName?: WindowNames): void;
|
||||
}
|
||||
export const MenuServiceIPCDescriptor = {
|
||||
channel: MenuChannel.name,
|
||||
properties: {
|
||||
buildMenu: ProxyPropertyType.Function,
|
||||
insertMenu: ProxyPropertyType.Function,
|
||||
buildContextMenuAndPopup: ProxyPropertyType.Function,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -112,10 +112,10 @@ export default function setupViewEventHandlers(
|
|||
if (workspaceObject === undefined) {
|
||||
return;
|
||||
}
|
||||
// isLoading is now controlled by wiki-worker-manager.js
|
||||
// workspaceService.updateMetaData(workspace.id, {
|
||||
// isLoading: false,
|
||||
// });
|
||||
// FIXME: seems isLoading is now controlled by wiki-worker-manager.js , and should not update here
|
||||
workspaceService.updateMetaData(workspace.id, {
|
||||
isLoading: false,
|
||||
});
|
||||
if (workspaceObject.active) {
|
||||
windowService.sendToAllWindows(WindowChannel.updateAddress, view.webContents.getURL(), false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,6 +276,10 @@ export class Window implements IWindowService {
|
|||
});
|
||||
}
|
||||
|
||||
public isFullScreen(windowName = WindowNames.main): boolean | undefined {
|
||||
return this.windows[windowName]?.isFullScreen();
|
||||
}
|
||||
|
||||
public setWindowMeta<N extends WindowNames>(windowName: N, meta: WindowMeta[N]): void {
|
||||
this.windowMeta[windowName] = meta;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export interface IWindowService {
|
|||
getWindowMeta<N extends WindowNames>(windowName: N): WindowMeta[N] | undefined;
|
||||
sendToAllWindows: (channel: Channels, ...arguments_: unknown[]) => void;
|
||||
requestShowRequireRestartDialog(): Promise<void>;
|
||||
isFullScreen(windowName?: WindowNames): boolean | undefined;
|
||||
goHome(windowName: WindowNames): Promise<void>;
|
||||
goBack(windowName: WindowNames): void;
|
||||
goForward(windowName: WindowNames): void;
|
||||
|
|
@ -33,6 +34,7 @@ export const WindowServiceIPCDescriptor = {
|
|||
getWindowMeta: ProxyPropertyType.Function,
|
||||
requestShowRequireRestartDialog: ProxyPropertyType.Function,
|
||||
sendToAllWindows: ProxyPropertyType.Function,
|
||||
isFullScreen: ProxyPropertyType.Function,
|
||||
goHome: ProxyPropertyType.Function,
|
||||
goBack: ProxyPropertyType.Function,
|
||||
goForward: ProxyPropertyType.Function,
|
||||
|
|
|
|||
|
|
@ -15,18 +15,19 @@ import { logger } from '@services/libs/log';
|
|||
import { IAuthenticationService } from '@services/auth/interface';
|
||||
import { IGitService } from '@services/git/interface';
|
||||
import { IWorkspaceViewService } from './interface';
|
||||
import { lazyInject } from '@services/container';
|
||||
|
||||
@injectable()
|
||||
export class WorkspaceView implements IWorkspaceViewService {
|
||||
constructor(
|
||||
@inject(serviceIdentifier.Authentication) private readonly authService: IAuthenticationService,
|
||||
@inject(serviceIdentifier.View) private readonly viewService: IViewService,
|
||||
@inject(serviceIdentifier.Git) private readonly gitService: IGitService,
|
||||
@inject(serviceIdentifier.Workspace) private readonly workspaceService: IWorkspaceService,
|
||||
@inject(serviceIdentifier.Window) private readonly windowService: IWindowService,
|
||||
@inject(serviceIdentifier.Preference) private readonly preferenceService: IPreferenceService,
|
||||
@inject(serviceIdentifier.MenuService) private readonly menuService: IMenuService,
|
||||
) {
|
||||
@lazyInject(serviceIdentifier.Authentication) private readonly authService!: IAuthenticationService;
|
||||
@lazyInject(serviceIdentifier.View) private readonly viewService!: IViewService;
|
||||
@lazyInject(serviceIdentifier.Git) private readonly gitService!: IGitService;
|
||||
@lazyInject(serviceIdentifier.Workspace) private readonly workspaceService!: IWorkspaceService;
|
||||
@lazyInject(serviceIdentifier.Window) private readonly windowService!: IWindowService;
|
||||
@lazyInject(serviceIdentifier.Preference) private readonly preferenceService!: IPreferenceService;
|
||||
@lazyInject(serviceIdentifier.MenuService) private readonly menuService!: IMenuService;
|
||||
|
||||
constructor() {
|
||||
this.registerMenu();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const updateForm = (changes: any) => (dispatch: any) =>
|
|||
changes,
|
||||
});
|
||||
|
||||
export const save = async () => (dispatch: any, getState: any) => {
|
||||
export const save = async () => async (dispatch: any, getState: any) => {
|
||||
const { form } = getState().dialogCodeInjection;
|
||||
|
||||
const { codeInjectionType } = window.meta as WindowMeta[WindowNames.codeInjection];
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const updateForm = (changes: any) => (dispatch: any, getState: any) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const save = async () => (dispatch: any, getState: any) => {
|
||||
export const save = async () => async (dispatch: any, getState: any) => {
|
||||
const state = getState();
|
||||
|
||||
const { form } = state.dialogProxy;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const removeLanguage = (code: any) => (dispatch: any, getState: any) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const save = async () => (dispatch: any, getState: any) => {
|
||||
export const save = async () => async (dispatch: any, getState: any) => {
|
||||
const { form } = getState().dialogSpellcheckLanguages;
|
||||
|
||||
void window.service.preference.set('spellcheckLanguages', form.spellcheckLanguages);
|
||||
|
|
|
|||
|
|
@ -1,47 +1,3 @@
|
|||
import { applyMiddleware, combineReducers, createStore } from 'redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
|
||||
import dialogAddWorkspace from './dialog-add-workspace/reducers';
|
||||
import dialogCodeInjection from './dialog-code-injection/reducers';
|
||||
import dialogCustomUserAgent from './dialog-custom-user-agent/reducers';
|
||||
import dialogEditWorkspace from './dialog-edit-workspace/reducers';
|
||||
import dialogGoToUrl from './dialog-go-to-url/reducers';
|
||||
import dialogProxy from './dialog-proxy/reducers';
|
||||
import dialogSpellcheckLanguages from './dialog-spellcheck-languages/reducers';
|
||||
import findInPage from './find-in-page/reducers';
|
||||
import general from './general/reducers';
|
||||
import notifications from './notifications/reducers';
|
||||
import preferences from './preferences/reducers';
|
||||
import systemPreferences from './system-preferences/reducers';
|
||||
import updater from './updater/reducers';
|
||||
import workspaces from './workspaces/reducers';
|
||||
import workspaceMetas from './workspace-metas/reducers';
|
||||
|
||||
import loadListeners from '../listeners';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
dialogAddWorkspace,
|
||||
dialogCodeInjection,
|
||||
dialogCustomUserAgent,
|
||||
dialogEditWorkspace,
|
||||
dialogGoToUrl,
|
||||
dialogProxy,
|
||||
dialogSpellcheckLanguages,
|
||||
findInPage,
|
||||
general,
|
||||
notifications,
|
||||
preferences,
|
||||
systemPreferences,
|
||||
updater,
|
||||
workspaces,
|
||||
workspaceMetas,
|
||||
});
|
||||
|
||||
const configureStore = (initialState: any) => createStore(rootReducer, initialState, applyMiddleware(thunkMiddleware));
|
||||
|
||||
// init store
|
||||
const store = configureStore({});
|
||||
|
||||
loadListeners(store);
|
||||
|
||||
export default store;
|
||||
export default {};
|
||||
|
|
|
|||
4
src/type.d.ts
vendored
4
src/type.d.ts
vendored
|
|
@ -1 +1,5 @@
|
|||
declare module 'errio';
|
||||
declare module '*.png' {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue