feat: allow open a nodejs wiki folder as workspace

This commit is contained in:
Lin Onetwo 2020-07-12 23:31:21 +08:00
parent ade57adb69
commit d7d8d96f33
10 changed files with 441 additions and 66 deletions

View file

@ -26,12 +26,12 @@ export default function Description({ isCreateMainWorkspace, isCreateMainWorkspa
onChange={event => isCreateMainWorkspaceSetter(event.target.checked)}
/>
}
label={`创建${isCreateMainWorkspace ? '主' : '子'}知识库`}
label={`${isCreateMainWorkspace ? '主' : '子'}知识库`}
/>
<Typography variant="body2" display="inline">
{isCreateMainWorkspace
? '主知识库包含了TiddlyWiki的配置文件以及发布为博客时的公开内容。'
: '子知识库必须依附于一个主知识库可用于存放私有内容同步到一个私有的Github仓库内仅本人可读写。子知识库通过创建一个到主知识库的软链接快捷方式来生效创建链接后主知识库内便可看到子知识库内的内容了。'}
? '包含了TiddlyWiki的配置文件以及发布为博客时的公开内容。'
: '必须依附于一个主知识库可用于存放私有内容同步到一个私有的Github仓库内仅本人可读写。子知识库通过创建一个到主知识库的软链接快捷方式来生效创建链接后主知识库内便可看到子知识库内的内容了。'}
</Typography>
</Container>
);

View file

@ -0,0 +1,133 @@
// @flow
import React from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import { basename, dirname } from 'path';
import * as actions from '../../state/dialog-add-workspace/actions';
import type { IUserInfo } from './user-info';
import { requestCreateSubWiki, getIconPath, initWikiGit } from '../../senders';
const CloseButton = styled(Button)`
white-space: nowrap;
width: 100%;
`;
interface Props {
isCreateMainWorkspace: boolean;
wikiPort: number;
mainWikiToLink: string;
githubWikiUrl: string;
existedFolderLocation: string;
userInfo: IUserInfo;
}
interface ActionProps {
updateForm: Object => void;
setWikiCreationMessage: string => void;
save: () => void;
}
function DoneButton({
isCreateMainWorkspace,
wikiPort,
mainWikiToLink,
githubWikiUrl,
existedFolderLocation,
updateForm,
setWikiCreationMessage,
save,
userInfo,
}: Props & ActionProps) {
const workspaceFormData = {
name: existedFolderLocation,
isSubWiki: !isCreateMainWorkspace,
mainWikiToLink,
port: wikiPort,
homeUrl: `http://localhost:${wikiPort}/`,
gitUrl: githubWikiUrl, // don't need .git suffix
picturePath: getIconPath(),
userInfo,
};
return isCreateMainWorkspace ? (
<CloseButton
variant="contained"
color="secondary"
disabled={!existedFolderLocation}
onClick={async () => {
updateForm(workspaceFormData);
save();
}}
>
{existedFolderLocation && (
<>
<Typography variant="body1" display="inline">
打开位于
</Typography>
<Typography
variant="body2"
noWrap
display="inline"
align="center"
style={{ direction: 'rtl', textTransform: 'none' }}
>
{existedFolderLocation}
</Typography>
</>
)}
<Typography variant="body1" display="inline">
的WIKI
</Typography>
</CloseButton>
) : (
<CloseButton
variant="contained"
color="secondary"
disabled={!existedFolderLocation || !mainWikiToLink}
onClick={async () => {
const wikiFolderName = basename(existedFolderLocation);
const parentFolderLocation = dirname(existedFolderLocation);
updateForm(workspaceFormData);
const creationError = await requestCreateSubWiki(parentFolderLocation, wikiFolderName, mainWikiToLink, true);
if (creationError) {
setWikiCreationMessage(creationError);
} else {
save();
}
}}
>
{existedFolderLocation && (
<>
<Typography variant="body1" display="inline">
打开位于
</Typography>
<Typography
variant="body2"
noWrap
display="inline"
align="center"
style={{ direction: 'rtl', textTransform: 'none' }}
>
{existedFolderLocation}
</Typography>
</>
)}
<Typography variant="body1" display="inline">
的WIKI
</Typography>
<Typography variant="body1" display="inline">
并链接到主知识库
</Typography>
</CloseButton>
);
}
const mapStateToProps = state => ({
wikiCreationMessage: state.dialogAddWorkspace.wikiCreationMessage,
});
export default connect(mapStateToProps, dispatch => bindActionCreators(actions, dispatch))(DoneButton);

View file

@ -0,0 +1,170 @@
// @flow
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import InputLabel from '@material-ui/core/InputLabel';
import FormHelperText from '@material-ui/core/FormHelperText';
import Select from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';
import FolderIcon from '@material-ui/icons/Folder';
import * as actions from '../../state/dialog-add-workspace/actions';
import { getWorkspaces } from '../../senders';
import { log } from 'isomorphic-git';
const CreateContainer = styled(Paper)`
margin-top: 5px;
`;
const LocationPickerContainer = styled.div`
display: flex;
flex-direction: row;
`;
const LocationPickerInput = styled(TextField)``;
const LocationPickerButton = styled(Button)`
white-space: nowrap;
width: fit-content;
`;
const SoftLinkToMainWikiSelect = styled(Select)`
width: 100%;
`;
const SoftLinkToMainWikiSelectInputLabel = styled(InputLabel)`
margin-top: 5px;
`;
interface Props {
wikiCreationMessage?: string;
existedFolderLocationSetter: string => void;
wikiFolderName: string;
wikiFolderNameSetter: string => void;
mainWikiToLink: string;
mainWikiToLinkSetter: string => void;
existedFolderLocation: string;
wikiPort: Number;
wikiPortSetter: number => void;
isCreateMainWorkspace: boolean;
}
interface ActionProps {
setWikiCreationMessage: string => void;
}
interface StateProps {
wikiCreationMessage: string;
}
function WikiPathForm({
setWikiCreationMessage,
wikiCreationMessage = '',
existedFolderLocation,
existedFolderLocationSetter,
wikiFolderName,
wikiFolderNameSetter,
mainWikiToLink,
mainWikiToLinkSetter,
wikiPort,
wikiPortSetter,
isCreateMainWorkspace,
}: Props & ActionProps & StateProps) {
const [workspaces, workspacesSetter] = useState({});
useEffect(() => {
workspacesSetter(getWorkspaces());
}, []);
return (
<CreateContainer elevation={2} square>
<LocationPickerContainer>
<LocationPickerInput
error={!!wikiCreationMessage}
helperText={wikiCreationMessage}
fullWidth
onChange={event => {
existedFolderLocationSetter(event.target.value);
setWikiCreationMessage('');
}}
label="知识库所在的的文件夹"
value={existedFolderLocation}
/>
<LocationPickerButton
onClick={() => {
const { remote } = window.require('electron');
// eslint-disable-next-line promise/catch-or-return
remote.dialog
.showOpenDialog(remote.getCurrentWindow(), {
properties: ['openDirectory'],
})
.then(({ canceled, filePaths }) => {
console.log(filePaths)
// eslint-disable-next-line promise/always-return
if (!canceled && filePaths.length > 0) {
existedFolderLocationSetter(filePaths[0]);
}
});
}}
variant="outlined"
color={existedFolderLocation ? 'default' : 'primary'}
disableElevation
endIcon={<FolderIcon />}
>
<Typography variant="button" display="inline">
选择
</Typography>
</LocationPickerButton>
</LocationPickerContainer>
<LocationPickerInput
fullWidth
onChange={event => {
wikiPortSetter(event.target.value);
}}
label="WIKI服务器端口号出现冲突再改一般默认即可"
value={wikiPort}
/>
{!isCreateMainWorkspace && (
<>
<SoftLinkToMainWikiSelectInputLabel id="main-wiki-select-label">
主知识库位置
</SoftLinkToMainWikiSelectInputLabel>
<SoftLinkToMainWikiSelect
labelId="main-wiki-select-label"
id="main-wiki-select"
value={mainWikiToLink}
onChange={event => mainWikiToLinkSetter(event.target.value)}
>
{Object.keys(workspaces).map(workspaceID => (
<MenuItem key={workspaceID} value={workspaces[workspaceID].name}>
{workspaces[workspaceID].name}
</MenuItem>
))}
</SoftLinkToMainWikiSelect>
{mainWikiToLink && (
<FormHelperText>
<Typography variant="body1" display="inline" component="span">
子知识库将链接到
</Typography>
<Typography
variant="body2"
component="span"
noWrap
display="inline"
align="center"
style={{ direction: 'rtl', textTransform: 'none' }}
>
{mainWikiToLink}/tiddlers/{wikiFolderName}
</Typography>
</FormHelperText>
)}
</>
)}
</CreateContainer>
);
}
const mapStateToProps = state => ({
wikiCreationMessage: state.dialogAddWorkspace.wikiCreationMessage,
});
export default connect(mapStateToProps, dispatch => bindActionCreators(actions, dispatch))(WikiPathForm);

View file

@ -1,5 +1,5 @@
// @flow
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { GraphQLClient, ClientContext } from 'graphql-hooks';
@ -10,10 +10,13 @@ import { GITHUB_GRAPHQL_API } from '../../constants/auth';
import Description from './description-and-mode-switch';
import SearchRepo from './search-repo';
import DoneButton from './done-button';
import WikiPathForm from './wiki-path-form';
import NewWikiDoneButton from './new-wiki-done-button';
import NewWikiPathForm from './new-wiki-path-form';
import ExistedWikiPathForm from './existed-wiki-path-form';
import ExistedWikiDoneButton from './existed-wiki-done-button';
import { getGithubUserInfo, setGithubUserInfo } from './user-info';
import type { IUserInfo } from './user-info';
import TabBar from './tab-bar';
import { requestSetPreference, getPreference, getDesktopPath, countWorkspace } from '../../senders';
@ -22,14 +25,12 @@ const graphqlClient = new GraphQLClient({
});
const Container = styled.main`
height: 100vh;
display: flex;
flex-direction: column;
overflow: scroll;
&::-webkit-scrollbar {
width: 0;
}
padding-bottom: 35px;
`;
const SyncContainer = styled(Paper)`
margin-top: 5px;
@ -42,8 +43,10 @@ const previousToken = getGithubToken();
previousToken && setHeaderToGraphqlClient(previousToken);
export default function AddWorkspace() {
const [currentTab, currentTabSetter] = useState(0);
const [isCreateMainWorkspace, isCreateMainWorkspaceSetter] = useState(countWorkspace() === 0);
const [parentFolderLocation, parentFolderLocationSetter] = useState(getDesktopPath());
const [existedFolderLocation, existedFolderLocationSetter] = useState(getDesktopPath());
const [wikiPort, wikiPortSetter] = useState(5212 + countWorkspace());
// try get token on start up
@ -71,50 +74,79 @@ export default function AddWorkspace() {
return (
<ClientContext.Provider value={graphqlClient}>
<Container>
<Description
isCreateMainWorkspace={isCreateMainWorkspace}
isCreateMainWorkspaceSetter={isCreateMainWorkspaceSetter}
/>
<TabBar currentTab={currentTab} currentTabSetter={currentTabSetter} />
{currentTab === 0 ? (
<Container>
<Description
isCreateMainWorkspace={isCreateMainWorkspace}
isCreateMainWorkspaceSetter={isCreateMainWorkspaceSetter}
/>
<SyncContainer elevation={2} square>
<Typography variant="subtitle1" align="center">
同步到云端
</Typography>
{userInfo && (
<SearchRepo
githubWikiUrl={githubWikiUrl}
accessToken={accessToken}
accessTokenSetter={accessTokenSetter}
githubWikiUrlSetter={githubWikiUrlSetter}
userInfoSetter={userInfoSetter}
userInfo={userInfo}
/>
)}
</SyncContainer>
<SyncContainer elevation={2} square>
<Typography variant="subtitle1" align="center">
同步到云端
</Typography>
{userInfo && (
<SearchRepo
githubWikiUrl={githubWikiUrl}
accessToken={accessToken}
accessTokenSetter={accessTokenSetter}
githubWikiUrlSetter={githubWikiUrlSetter}
userInfoSetter={userInfoSetter}
userInfo={userInfo}
/>
)}
</SyncContainer>
<WikiPathForm
parentFolderLocation={parentFolderLocation}
parentFolderLocationSetter={parentFolderLocationSetter}
wikiFolderName={wikiFolderName}
wikiFolderNameSetter={wikiFolderNameSetter}
mainWikiToLink={mainWikiToLink}
mainWikiToLinkSetter={mainWikiToLinkSetter}
wikiPort={wikiPort}
wikiPortSetter={wikiPortSetter}
isCreateMainWorkspace={isCreateMainWorkspace}
/>
<NewWikiPathForm
parentFolderLocation={parentFolderLocation}
parentFolderLocationSetter={parentFolderLocationSetter}
wikiFolderName={wikiFolderName}
wikiFolderNameSetter={wikiFolderNameSetter}
mainWikiToLink={mainWikiToLink}
mainWikiToLinkSetter={mainWikiToLinkSetter}
wikiPort={wikiPort}
wikiPortSetter={wikiPortSetter}
isCreateMainWorkspace={isCreateMainWorkspace}
/>
<DoneButton
isCreateMainWorkspace={isCreateMainWorkspace}
wikiPort={wikiPort}
mainWikiToLink={mainWikiToLink}
githubWikiUrl={githubWikiUrl}
wikiFolderName={wikiFolderName}
parentFolderLocation={parentFolderLocation}
userInfo={userInfo}
/>
</Container>
<NewWikiDoneButton
isCreateMainWorkspace={isCreateMainWorkspace}
wikiPort={wikiPort}
mainWikiToLink={mainWikiToLink}
githubWikiUrl={githubWikiUrl}
wikiFolderName={wikiFolderName}
parentFolderLocation={parentFolderLocation}
userInfo={userInfo}
/>
</Container>
) : (
<Container>
<Description
isCreateMainWorkspace={isCreateMainWorkspace}
isCreateMainWorkspaceSetter={isCreateMainWorkspaceSetter}
/>
<ExistedWikiPathForm
existedFolderLocationSetter={existedFolderLocationSetter}
existedFolderLocation={existedFolderLocation}
wikiFolderName={wikiFolderName}
wikiFolderNameSetter={wikiFolderNameSetter}
mainWikiToLink={mainWikiToLink}
mainWikiToLinkSetter={mainWikiToLinkSetter}
wikiPort={wikiPort}
wikiPortSetter={wikiPortSetter}
isCreateMainWorkspace={isCreateMainWorkspace}
/>
<ExistedWikiDoneButton
isCreateMainWorkspace={isCreateMainWorkspace}
wikiPort={wikiPort}
mainWikiToLink={mainWikiToLink}
githubWikiUrl={githubWikiUrl}
existedFolderLocation={existedFolderLocation}
userInfo={userInfo}
/>
</Container>
)}
</ClientContext.Provider>
);
}

View file

@ -15,8 +15,6 @@ import { requestCopyWikiTemplate, requestCreateSubWiki, getIconPath, initWikiGit
const CloseButton = styled(Button)`
white-space: nowrap;
width: 100%;
position: absolute;
bottom: 0;
`;
interface Props {
@ -34,7 +32,7 @@ interface ActionProps {
save: () => void;
}
function DoneButton({
function NewWikiDoneButton({
isCreateMainWorkspace,
wikiPort,
mainWikiToLink,
@ -145,4 +143,4 @@ const mapStateToProps = state => ({
wikiCreationMessage: state.dialogAddWorkspace.wikiCreationMessage,
});
export default connect(mapStateToProps, dispatch => bindActionCreators(actions, dispatch))(DoneButton);
export default connect(mapStateToProps, dispatch => bindActionCreators(actions, dispatch))(NewWikiDoneButton);

View file

@ -39,7 +39,6 @@ const SoftLinkToMainWikiSelectInputLabel = styled(InputLabel)`
interface Props {
wikiCreationMessage?: string;
parentFolderLocation: string;
parentFolderLocationSetter: string => void;
wikiFolderName: string;
wikiFolderNameSetter: string => void;
@ -57,7 +56,7 @@ interface StateProps {
wikiCreationMessage: string;
}
function WikiPathForm({
function NewWikiPathForm({
setWikiCreationMessage,
wikiCreationMessage = '',
parentFolderLocation,
@ -121,7 +120,7 @@ function WikiPathForm({
wikiFolderNameSetter(event.target.value);
setWikiCreationMessage('');
}}
label="知识库文件夹名"
label="即将新建的知识库文件夹名"
value={wikiFolderName}
/>
<LocationPickerInput
@ -176,4 +175,4 @@ const mapStateToProps = state => ({
wikiCreationMessage: state.dialogAddWorkspace.wikiCreationMessage,
});
export default connect(mapStateToProps, dispatch => bindActionCreators(actions, dispatch))(WikiPathForm);
export default connect(mapStateToProps, dispatch => bindActionCreators(actions, dispatch))(NewWikiPathForm);

View file

@ -0,0 +1,34 @@
// @flow
import React from 'react';
import Paper from '@material-ui/core/Paper';
import AppBar from '@material-ui/core/AppBar';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
export interface IProps {
currentTab: number;
currentTabSetter: number => void;
}
export default function TabBar({ currentTab, currentTabSetter }: IProps) {
return (
<AppBar position="static">
<Paper square>
<Tabs
value={currentTab}
onChange={(event, newValue) => currentTabSetter(newValue)}
aria-label="切换创建新的还是打开现有的WIKI"
>
<Tab label="创建新WIKI" {...a11yProps(0)} />
<Tab label="打开现有WIKI" {...a11yProps(1)} />
</Tabs>
</Paper>
</AppBar>
);
}

View file

@ -3,8 +3,8 @@ const { ipcRenderer } = window.require('electron');
export const requestCopyWikiTemplate = (newFolderPath, folderName) =>
ipcRenderer.invoke('copy-wiki-template', newFolderPath, folderName);
export const requestCreateSubWiki = (newFolderPath, folderName, mainWikiToLink) =>
ipcRenderer.invoke('create-sub-wiki', newFolderPath, folderName, mainWikiToLink);
export const requestCreateSubWiki = (newFolderPath: string, folderName: string, mainWikiToLink: string, onlyLink?: boolean) =>
ipcRenderer.invoke('create-sub-wiki', newFolderPath, folderName, mainWikiToLink, onlyLink);
export const requestOpenInBrowser = url => ipcRenderer.send('request-open-in-browser', url);
export const requestShowMessageBox = (message, type) => ipcRenderer.send('request-show-message-box', message, type);
export const requestLoadUrl = (url, id) => ipcRenderer.send('request-load-url', url, id);