Skip to content

Commit

Permalink
Multi docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Raathigesh committed Aug 23, 2021
1 parent a75fb83 commit 285e135
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 32 deletions.
109 changes: 109 additions & 0 deletions src/extension/api/DocsManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import * as vscode from 'vscode';

const StateKey = 'paper-documents';

interface Document {
content: string;
id: string;
type: 'doc' | 'mindmap';
name: string;
}

interface State {
activeDocument: null | string;
documents: Document[];
}

export class DocsManager {
context: vscode.ExtensionContext;

constructor(context: vscode.ExtensionContext) {
this.context = context;
}

getActiveDocument(): Document {
if (this.context.workspaceState.get(StateKey) === undefined) {
const newDocId = new Date().valueOf().toString();
this.context.workspaceState.update(StateKey, {
activeDocument: newDocId,
documents: [
{
content:
this.context.workspaceState.get('paper-content') ||
'',
id: newDocId,
type: 'doc',
name: 'Untitled',
},
],
} as State);
}

const state: State = this.context.workspaceState.get(StateKey) as any;
const activeDocument = state.documents.find(
item => item.id === state.activeDocument
);
if (!activeDocument) {
throw new Error('Active document not found.');
}
return activeDocument;
}

setActiveDocument(id: string) {
const state: State = this.context.workspaceState.get(StateKey) as any;
this.context.workspaceState.update(StateKey, {
...state,
activeDocument: id,
} as State);
}

updateDocument(id: string, content: string) {
const state: State = this.context.workspaceState.get(StateKey) as any;
const updatedDocuments = state.documents.map(item => {
if (item.id === id) {
return {
...item,
content,
};
}
return item;
});

const nextState: State = {
...state,
documents: updatedDocuments,
};
this.context.workspaceState.update(StateKey, nextState);
}

createDocument(name: string, content: string, type: 'doc' | 'mindmap') {
const state: State = this.context.workspaceState.get(StateKey) as any;
const nextState: State = {
...state,
documents: [
...state.documents,
{
id: new Date().valueOf().toString(),
content,
type,
name,
},
],
};
this.context.workspaceState.update(StateKey, nextState);
}

deleteDocument(id: string) {
const state: State = this.context.workspaceState.get(StateKey) as any;
const nextState: State = {
...state,
documents: state.documents.filter(item => item.id !== id),
};
this.context.workspaceState.update(StateKey, nextState);
}

getDocumentsList(): Document[] {
const state: State = this.context.workspaceState.get(StateKey) as any;
return state.documents;
}
}
37 changes: 32 additions & 5 deletions src/extension/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import * as dirTree from 'directory-tree';

import { initializeStaticRoutes } from './static-files';
import { isAbsolute, join } from 'path';
import { DocsManager } from './DocsManager';

export async function startApiServer(
port: number,
context: vscode.ExtensionContext
) {
const docsManager = new DocsManager(context);

const app = express();
app.use(bodyParser());
app.use(cors());
Expand All @@ -35,22 +38,46 @@ export async function startApiServer(
)
);
}

vscode.window.showTextDocument(vscode.Uri.file(fullPath), {
selection,
});

res.send('OK');
});

app.post('/create', (req, res) => {
const name = req.body.name;
const type = req.body.type;

docsManager.createDocument(name, '', type);
const documents = docsManager.getDocumentsList();
res.json(documents);
});

app.get('/content', (req, res) => {
const content = context.workspaceState.get('paper-content');
res.send(content);
const document = docsManager.getActiveDocument();
res.json(document);
});

app.post('/content', (req, res) => {
const content = req.body.content;
context.workspaceState.update('paper-content', content);
const id = req.body.id;

if (!id) {
res.sendStatus(500);
} else {
docsManager.updateDocument(id, content);
res.send('OK');
}
});

app.get('/documents', (req, res) => {
const documents = docsManager.getDocumentsList();
res.json(documents);
});

app.post('/changeActiveDocument', (req, res) => {
const id = req.body.id;
docsManager.setActiveDocument(id);
res.send('OK');
});

Expand Down
59 changes: 54 additions & 5 deletions src/ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import {
GlobalStyle,
theme as defaultTheme,
} from '@chakra-ui/react';
import React, { Fragment, useContext, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { ThemeProvider } from '@devtools-ds/themes';
import copy from 'copy-to-clipboard';
import { Grid } from 'react-feather';
import Editor from './Editor';
import { ClientDoc } from './types';
import CreateDoc from './CreateDoc';

const theme = {
...defaultTheme,
Expand Down Expand Up @@ -39,13 +41,60 @@ const theme = {
const API_URL = `http://localhost:${(window as any).port || '4545'}`;

function App() {
const [activeDoc, setActiveDoc] = useState<ClientDoc | null>(null);

const updateContent = useCallback(
async (content: string) => {
if (activeDoc === null) {
return;
}

fetch(`${API_URL}/content`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
content,
id: activeDoc.id,
}),
});
},
[activeDoc]
);

async function getContent() {
const document: ClientDoc = await (
await fetch(`${API_URL}/content`)
).json();
setActiveDoc(document);
}

useEffect(() => {
getContent();
}, [setActiveDoc]);

return (
<ThemeProvider theme={'chrome'} colorScheme={'dark'}>
<ChakraProvider theme={theme}>
<GlobalStyle />
<div>
<Editor />
</div>

<Flex flexDir="column">
<Flex justifyContent="flex-end">
<CreateDoc
onActiveDocumentChange={() => {
getContent();
}}
/>
</Flex>

{activeDoc && (
<Editor
content={activeDoc?.content}
onChange={updateContent}
/>
)}
</Flex>
</ChakraProvider>
</ThemeProvider>
);
Expand Down
136 changes: 136 additions & 0 deletions src/ui/CreateDoc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import {
Button,
Flex,
Input,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverFooter,
PopoverHeader,
PopoverTrigger,
Portal,
} from '@chakra-ui/react';
import React, { useEffect, useState } from 'react';
import { PlusSquare, ArrowRight } from 'react-feather';
import { ClientDoc } from './types';

const API_URL = `http://localhost:${(window as any).port || '4545'}`;

interface Props {
onActiveDocumentChange: () => void;
}

export default function CreateDoc({ onActiveDocumentChange }: Props) {
const [docs, setDocs] = useState<ClientDoc[]>([]);
const [docName, setDocName] = useState('');

const getDocs = async () => {
const response = await fetch(`${API_URL}/documents`);
const documents = await response.json();
setDocs(documents);
};

const changeActiveDocument = async (id: string) => {
await fetch(`${API_URL}/changeActiveDocument`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
id,
}),
});

onActiveDocumentChange();
};

useEffect(() => {
getDocs();
}, []);

const createDoc = async (name: string) => {
const response = await fetch(`${API_URL}/create`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name,
type: 'doc',
}),
});
const documents = await response.json();
setDocs(documents);
};

return (
<Popover colorScheme="blackAlpha">
<PopoverTrigger>
<Button
size="sm"
backgroundColor="#2F2E31"
color="#f1f0ee"
_hover={{ backgroundColor: '#090909' }}
onClick={() => {}}
>
<PlusSquare size="18px" strokeWidth="2px" />
</Button>
</PopoverTrigger>
<Portal>
<PopoverContent
zIndex={9999999999}
backgroundColor="#2F2E31"
colorScheme="blackAlpha"
>
<PopoverArrow backgroundColor="#2F2E31" />
<PopoverHeader>Docs</PopoverHeader>
<PopoverCloseButton />
<PopoverBody>
{docs.map(item => (
<Flex
_hover={{
backgroundColor: '#090909',
}}
cursor="pointer"
padding="3px"
borderRadius="2px"
onClick={() => {
changeActiveDocument(item.id);
}}
>
{item.name || item.id}
</Flex>
))}
</PopoverBody>
<PopoverFooter>
<Flex>
<Input
size="medium"
borderRadius="3px"
padding="2px"
variant="outline"
placeholder="Name your doc"
value={docName}
onChange={e => setDocName(e.target.value)}
/>
<Button
size="sm"
backgroundColor="#2F2E31"
color="#f1f0ee"
_hover={{ backgroundColor: '#090909' }}
onClick={() => {
createDoc(docName);
}}
isDisabled={docName.trim() === ''}
>
<ArrowRight size="18px" strokeWidth="2px" />
</Button>
</Flex>
</PopoverFooter>
</PopoverContent>
</Portal>
</Popover>
);
}
Loading

0 comments on commit 285e135

Please sign in to comment.