Skip to content

Commit 5100b0f

Browse files
committed
Visualize local codes
1 parent c3c6ded commit 5100b0f

File tree

10 files changed

+131
-37
lines changed

10 files changed

+131
-37
lines changed

package-lock.json

Lines changed: 25 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"node-sass": "^4.5.3",
7373
"postcss-flexboxfixer": "0.0.5",
7474
"postcss-loader": "^2.0.6",
75+
"query-string": "^6.2.0",
7576
"raw-loader": "^0.5.1",
7677
"react": "^16.3.1",
7778
"react-ace": "^6.1.4",

src/backend/controllers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { default as auth } from './auth';
22
export { default as algorithms } from './algorithms';
33
export { default as tracers } from './tracers';
4+
export { default as visualizations } from './visualizations';

src/backend/controllers/tracers.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const router = express.Router();
1414
const trace = lang => (req, res, next) => {
1515
const { code } = req.body;
1616
const tempPath = path.resolve(__dirname, '..', 'public', 'codes', uuid.v4());
17-
const tracesPath = path.resolve(tempPath, 'traces.json');
1817
fs.outputFile(path.resolve(tempPath, `Main.${lang}`), code)
1918
.then(() => {
2019
const builder = builderMap[lang];
@@ -37,11 +36,13 @@ const trace = lang => (req, res, next) => {
3736
throw error;
3837
}).finally(() => clearTimeout(timer));
3938
})
40-
.then(() => fs.pathExists(tracesPath))
41-
.then(exists => {
42-
if (!exists) throw new Error('Traces Not Found');
43-
res.sendFile(tracesPath);
44-
})
39+
.then(() => new Promise((resolve, reject) => {
40+
const visualizationPath = path.resolve(tempPath, 'traces.json');
41+
res.sendFile(visualizationPath, err => {
42+
if (err) return reject(new Error('Visualization Not Found'));
43+
resolve();
44+
});
45+
}))
4546
.catch(next)
4647
.finally(() => fs.remove(tempPath));
4748
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import express from 'express';
2+
import path from 'path';
3+
import uuid from 'uuid';
4+
import fs from 'fs-extra';
5+
import Promise from 'bluebird';
6+
7+
const router = express.Router();
8+
9+
const uploadPath = path.resolve(__dirname, '..', 'public', 'visualizations');
10+
const getVisualizationPath = visualizationId => path.resolve(uploadPath, `${visualizationId}.json`);
11+
12+
fs.remove(uploadPath).catch(console.error);
13+
14+
const uploadVisualization = (req, res, next) => {
15+
const { content } = req.body;
16+
const visualizationId = uuid.v4();
17+
const tracesPath = getVisualizationPath(visualizationId);
18+
const url = `https://algorithm-visualizer.org/scratch-paper/new?visualizationId=${visualizationId}`;
19+
fs.outputFile(tracesPath, content)
20+
.then(() => res.send(url))
21+
.catch(next);
22+
};
23+
24+
const getVisualization = (req, res, next) => {
25+
const { visualizationId } = req.params;
26+
const visualizationPath = getVisualizationPath(visualizationId);
27+
new Promise((resolve, reject) => {
28+
res.sendFile(visualizationPath, err => {
29+
if (err) return reject(new Error('Visualization Expired'));
30+
resolve();
31+
});
32+
}).catch(next)
33+
.finally(() => fs.remove(visualizationPath));
34+
};
35+
36+
router.route('/')
37+
.post(uploadVisualization);
38+
39+
router.route('/:visualizationId')
40+
.get(getVisualization);
41+
42+
export default router;

src/frontend/apis/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ const AlgorithmApi = {
5252
getAlgorithm: GET('/algorithms/:categoryKey/:algorithmKey'),
5353
};
5454

55+
const VisualizationApi = {
56+
getVisualization: GET('/visualizations/:visualizationId'),
57+
};
58+
5559
const GitHubApi = {
5660
auth: token => Promise.resolve(axios.defaults.headers.common['Authorization'] = token && `token ${token}`),
5761
getUser: GET('https://api.github.com/user'),
@@ -73,6 +77,7 @@ const TracerApi = {
7377
method: 'set',
7478
args: [code],
7579
}]),
80+
json: ({ code }) => new Promise(resolve => resolve(JSON.parse(code))),
7681
js: ({ code }, params, cancelToken) => new Promise((resolve, reject) => {
7782
const worker = new Worker('/api/tracers/js');
7883
if (cancelToken) {
@@ -97,6 +102,7 @@ const TracerApi = {
97102

98103
export {
99104
AlgorithmApi,
105+
VisualizationApi,
100106
GitHubApi,
101107
TracerApi,
102108
};

src/frontend/common/util.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,21 @@ const refineGist = gist => {
2121
return { login, gistId, title, files };
2222
};
2323

24+
const createFile = (name, content, contributors) => ({ name, content, contributors });
25+
26+
const createProjectFile = (name, content) => createFile(name, content, [{
27+
login: 'algorithm-visualizer',
28+
avatar_url: 'https://github.com/algorithm-visualizer.png',
29+
}]);
30+
31+
const createUserFile = (name, content) => createFile(name, content, undefined);
32+
2433
export {
2534
classes,
2635
distance,
2736
extension,
2837
refineGist,
38+
createFile,
39+
createProjectFile,
40+
createUserFile,
2941
};

src/frontend/components/App/index.jsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
44
import Promise from 'bluebird';
55
import { Helmet } from 'react-helmet';
66
import AutosizeInput from 'react-input-autosize';
7+
import queryString from 'query-string';
78
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
89
import faPlus from '@fortawesome/fontawesome-free-solid/faPlus';
910
import {
@@ -16,9 +17,9 @@ import {
1617
ToastContainer,
1718
VisualizationViewer,
1819
} from '/components';
19-
import { AlgorithmApi, GitHubApi } from '/apis';
20+
import { AlgorithmApi, GitHubApi, VisualizationApi } from '/apis';
2021
import { actions } from '/reducers';
21-
import { extension, refineGist } from '/common/util';
22+
import { createUserFile, extension, refineGist } from '/common/util';
2223
import { exts, languages } from '/common/config';
2324
import { CONTRIBUTING_MD } from '/files';
2425
import styles from './stylesheet.scss';
@@ -43,7 +44,9 @@ class App extends BaseComponent {
4344
window.signIn = this.signIn.bind(this);
4445
window.signOut = this.signOut.bind(this);
4546

46-
this.loadAlgorithm(this.props.match.params);
47+
const { params } = this.props.match;
48+
const { search } = this.props.location;
49+
this.loadAlgorithm(params, queryString.parse(search));
4750

4851
const accessToken = Cookies.get('access_token');
4952
if (accessToken) this.signIn(accessToken);
@@ -64,12 +67,13 @@ class App extends BaseComponent {
6467

6568
componentWillReceiveProps(nextProps) {
6669
const { params } = nextProps.match;
67-
if (params !== this.props.match.params) {
70+
const { search } = nextProps.location;
71+
if (params !== this.props.match.params || search !== this.props.location.search) {
6872
const { categoryKey, algorithmKey, gistId } = params;
6973
const { algorithm, scratchPaper } = nextProps.current;
7074
if (algorithm && algorithm.categoryKey === categoryKey && algorithm.algorithmKey === algorithmKey) return;
7175
if (scratchPaper && scratchPaper.gistId === gistId) return;
72-
this.loadAlgorithm(params);
76+
this.loadAlgorithm(params, queryString.parse(search));
7377
}
7478
}
7579

@@ -138,7 +142,7 @@ class App extends BaseComponent {
138142
.catch(this.handleError);
139143
}
140144

141-
loadAlgorithm({ categoryKey, algorithmKey, gistId }) {
145+
loadAlgorithm({ categoryKey, algorithmKey, gistId }, { visualizationId }) {
142146
const { ext } = this.props.env;
143147
const fetch = () => {
144148
if (window.__PRELOADED_ALGORITHM__) {
@@ -147,6 +151,16 @@ class App extends BaseComponent {
147151
} else if (categoryKey && algorithmKey) {
148152
return AlgorithmApi.getAlgorithm(categoryKey, algorithmKey)
149153
.then(({ algorithm }) => this.props.setAlgorithm(algorithm));
154+
} else if (gistId === 'new' && visualizationId) {
155+
return VisualizationApi.getVisualization(visualizationId)
156+
.then(content => {
157+
this.props.setScratchPaper({
158+
login: undefined,
159+
gistId,
160+
title: 'Untitled',
161+
files: [CONTRIBUTING_MD, createUserFile('traces.json', JSON.stringify(content))],
162+
});
163+
});
150164
} else if (gistId === 'new') {
151165
const language = languages.find(language => language.ext === ext);
152166
this.props.setScratchPaper({
@@ -175,7 +189,8 @@ class App extends BaseComponent {
175189
selectDefaultTab() {
176190
const { ext } = this.props.env;
177191
const { files } = this.props.current;
178-
let editorTabIndex = files.findIndex(file => extension(file.name) === ext);
192+
let editorTabIndex = files.findIndex(file => extension(file.name) === 'json');
193+
if (!~editorTabIndex) files.findIndex(file => extension(file.name) === ext);
179194
if (!~editorTabIndex) editorTabIndex = files.findIndex(file => exts.includes(extension(file.name)));
180195
if (!~editorTabIndex) editorTabIndex = Math.min(0, files.length - 1);
181196
this.handleChangeEditorTabIndex(editorTabIndex);

src/frontend/components/CodeEditor/index.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import AceEditor from 'react-ace';
33
import 'brace/mode/plain_text';
44
import 'brace/mode/markdown';
5+
import 'brace/mode/json';
56
import 'brace/mode/javascript';
67
import 'brace/mode/c_cpp';
78
import 'brace/mode/java';
@@ -43,7 +44,10 @@ class CodeEditor extends React.Component {
4344

4445
const fileExt = extension(file.name);
4546
const language = languages.find(language => language.ext === fileExt);
46-
const mode = language ? language.mode : fileExt === 'md' ? 'markdown' : 'plain_text';
47+
const mode = language ? language.mode :
48+
fileExt === 'md' ? 'markdown' :
49+
fileExt === 'json' ? 'json' :
50+
'plain_text';
4751

4852
return (
4953
<div className={classes(styles.code_editor, className)}>

src/frontend/files/index.js

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
1-
const createProjectFile = filePath => ({
2-
name: filePath.split('/').pop(),
3-
content: require('raw-loader!./' + filePath),
4-
contributors: [{
5-
login: 'algorithm-visualizer',
6-
avatar_url: 'https://github.com/algorithm-visualizer.png',
7-
}],
8-
});
1+
import { createProjectFile, createUserFile } from '/common/util';
92

10-
const createUserFile = filePath => ({
11-
name: filePath.split('/').pop(),
12-
content: require('raw-loader!./' + filePath),
13-
contributors: undefined,
14-
});
3+
const getName = filePath => filePath.split('/').pop();
4+
const getContent = filePath => require('raw-loader!./' + filePath);
5+
const readProjectFile = filePath => createProjectFile(getName(filePath), getContent(filePath));
6+
const readUserFile = filePath => createUserFile(getName(filePath), getContent(filePath));
157

16-
export const CODE_CPP = createUserFile('skeletons/code.cpp');
17-
export const CODE_JAVA = createUserFile('skeletons/code.java');
18-
export const CODE_JS = createUserFile('skeletons/code.js');
19-
export const README_MD = createProjectFile('algorithm-visualizer/README.md');
20-
export const CONTRIBUTING_MD = createProjectFile('scratch-paper/CONTRIBUTING.md');
8+
export const CODE_CPP = readUserFile('skeletons/code.cpp');
9+
export const CODE_JAVA = readUserFile('skeletons/code.java');
10+
export const CODE_JS = readUserFile('skeletons/code.js');
11+
export const README_MD = readProjectFile('algorithm-visualizer/README.md');
12+
export const CONTRIBUTING_MD = readProjectFile('scratch-paper/CONTRIBUTING.md');

0 commit comments

Comments
 (0)