Skip to content

Commit 49d000b

Browse files
committed
Handle errors better and fork a gist when modifying other's scratch paper
1 parent e7d7c10 commit 49d000b

File tree

14 files changed

+92
-90
lines changed

14 files changed

+92
-90
lines changed

src/backend/common/error.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
class ClientError extends Error {
2-
toJSON() {
3-
return { name: this.constructor.name, message: this.message };
4-
}
52
}
63

74
class NotFoundError extends ClientError {
@@ -26,4 +23,4 @@ export {
2623
UnauthorizedError,
2724
CompileError,
2825
RuntimeError,
29-
};
26+
};

src/backend/controllers/tracers.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@ const trace = lang => (req, res, next) => {
2828
const { code } = req.body;
2929
const tempPath = getCodesPath(uuid.v4());
3030
fs.outputFile(path.resolve(tempPath, `Main.${lang}`), code)
31-
.then(() => execute(`LANG=${lang} TEMP_PATH=${tempPath} ./bin/compile`, repoPath, { stdout: null, stderr: null })
32-
.catch(error => Promise.reject(new CompileError(error.message))))
33-
.then(() => execute(`LANG=${lang} TEMP_PATH=${tempPath} ./bin/run`, repoPath, { stdout: null, stderr: null })
34-
.catch(error => Promise.reject(new RuntimeError(error.message))))
31+
.then(() => execute(`LANG=${lang} TEMP_PATH=${tempPath} ./bin/compile`, repoPath, { stdout: null, stderr: null }))
32+
.then(() => execute(`LANG=${lang} TEMP_PATH=${tempPath} ./bin/run`, repoPath, { stdout: null, stderr: null }))
3533
.then(() => res.sendFile(path.resolve(tempPath, 'traces.json')))
3634
.catch(next)
3735
.finally(() => fs.remove(tempPath));

src/backend/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ app.use((err, req, res, next) => {
3131
];
3232
const [, status] = statusMap.find(([Error]) => err instanceof Error);
3333
res.status(status);
34-
res.json(err);
34+
res.send(err.message);
3535
console.error(err);
3636
});
3737

38-
export default app;
38+
export default app;

src/frontend/apis/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import Promise from 'bluebird';
22
import axios from 'axios';
3-
import { RuntimeError } from '/common/error';
43

54
axios.interceptors.response.use(
65
response => response.data,
7-
error => Promise.reject(error.response.data),
6+
error => {
7+
const { data } = error.response;
8+
const message = typeof data === 'string' ? data : JSON.stringify(data);
9+
return Promise.reject(new Error(message));
10+
},
811
);
912

1013
const request = (url, process) => {
@@ -66,6 +69,7 @@ const GitHubApi = {
6669
editGist: PATCH('https://api.github.com/gists/:id'),
6770
getGist: GET('https://api.github.com/gists/:id'),
6871
deleteGist: DELETE('https://api.github.com/gists/:id'),
72+
forkGist: POST('https://api.github.com/gists/:id/forks'),
6973
};
7074

7175
let jsWorker = null;
@@ -83,7 +87,7 @@ const TracerApi = {
8387
if (jsWorker) jsWorker.terminate();
8488
jsWorker = new Worker('/api/tracers/js');
8589
jsWorker.onmessage = e => resolve(e.data);
86-
jsWorker.onerror = e => reject(new RuntimeError(e.message));
90+
jsWorker.onerror = reject;
8791
jsWorker.postMessage(code);
8892
}),
8993
cpp: POST('/tracers/cpp'),

src/frontend/common/error.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/frontend/common/util.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@ const refineGist = gist => {
1818
content: file.content,
1919
contributors: [{ login, avatar_url }],
2020
}));
21-
return { gistId, titles, files };
21+
return { gistId, titles, files, gist };
22+
};
23+
24+
const handleError = function (error) {
25+
console.error(error);
26+
this.props.showErrorToast(error.message);
2227
};
2328

2429
export {
2530
classes,
2631
distance,
2732
extension,
2833
refineGist,
29-
};
34+
handleError,
35+
};

src/frontend/components/App/index.jsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
} from '/components';
2020
import { CategoryApi, GitHubApi } from '/apis';
2121
import { actions } from '/reducers';
22-
import { extension, refineGist } from '/common/util';
22+
import { extension, handleError, refineGist } from '/common/util';
2323
import { exts, languages, us } from '/common/config';
2424
import { README_MD, SCRATCH_PAPER_MD } from '/skeletons';
2525
import styles from './stylesheet.scss';
@@ -51,7 +51,7 @@ class App extends React.Component {
5151

5252
CategoryApi.getCategories()
5353
.then(({ categories }) => this.props.setCategories(categories))
54-
.catch(this.props.showErrorToast);
54+
.catch(handleError.bind(this));
5555

5656
window.onbeforeunload = () => this.isGistSaved() ? undefined : 'Changes you made will not be saved.';
5757
}
@@ -90,6 +90,7 @@ class App extends React.Component {
9090
.then(user => {
9191
const { login, avatar_url } = user;
9292
this.props.setUser({ login, avatar_url });
93+
Cookies.set('login', login);
9394
})
9495
.then(() => this.loadScratchPapers())
9596
.catch(() => this.signOut());
@@ -98,7 +99,10 @@ class App extends React.Component {
9899
signOut() {
99100
Cookies.remove('access_token');
100101
GitHubApi.auth(undefined)
101-
.then(() => this.props.setUser(undefined))
102+
.then(() => {
103+
this.props.setUser(undefined);
104+
Cookies.remove('login');
105+
})
102106
.then(() => this.props.setScratchPapers([]));
103107
}
104108

@@ -122,7 +126,7 @@ class App extends React.Component {
122126
});
123127
return paginateGists()
124128
.then(scratchPapers => this.props.setScratchPapers(scratchPapers))
125-
.catch(this.props.showErrorToast);
129+
.catch(handleError.bind(this));
126130
}
127131

128132
loadAlgorithm({ categoryKey, algorithmKey, gistId }, forceLoad = false) {
@@ -133,7 +137,8 @@ class App extends React.Component {
133137
if (categoryKey && algorithmKey) {
134138
fetchPromise = CategoryApi.getAlgorithm(categoryKey, algorithmKey)
135139
.then(({ algorithm }) => algorithm);
136-
} else if (gistId === 'new') {
140+
} else if (['new', 'forked'].includes(gistId)) {
141+
gistId = 'new';
137142
const language = languages.find(language => language.ext === ext);
138143
fetchPromise = Promise.resolve({
139144
titles: ['Scratch Paper', 'Untitled'],
@@ -153,12 +158,15 @@ class App extends React.Component {
153158
fetchPromise = Promise.reject(new Error());
154159
}
155160
fetchPromise
156-
.then(algorithm => this.props.setCurrent(categoryKey, algorithmKey, gistId, algorithm.titles, algorithm.files))
157-
.catch(() => this.props.setCurrent(undefined, undefined, undefined, ['Algorithm Visualizer'], [{
158-
name: 'README.md',
159-
content: README_MD,
160-
contributors: [us],
161-
}]))
161+
.then(algorithm => this.props.setCurrent(categoryKey, algorithmKey, gistId, algorithm.titles, algorithm.files, algorithm.gist))
162+
.catch(error => {
163+
if (error.message) handleError.bind(this)(error);
164+
this.props.setCurrent(undefined, undefined, undefined, ['Algorithm Visualizer'], [{
165+
name: 'README.md',
166+
content: README_MD,
167+
contributors: [us],
168+
}], undefined);
169+
})
162170
.finally(() => {
163171
const { files } = this.props.current;
164172
let editorTabIndex = files.findIndex(file => extension(file.name) === ext);

src/frontend/components/Header/index.jsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import faSave from '@fortawesome/fontawesome-free-solid/faSave';
1414
import faFacebook from '@fortawesome/fontawesome-free-brands/faFacebook';
1515
import faStar from '@fortawesome/fontawesome-free-solid/faStar';
1616
import { GitHubApi } from '/apis';
17-
import { classes, refineGist } from '/common/util';
17+
import { classes, handleError, refineGist } from '/common/util';
1818
import { actions } from '/reducers';
1919
import { languages } from '/common/config';
2020
import { Button, Ellipsis, ListItem, Player } from '/components';
@@ -38,7 +38,8 @@ class Header extends React.Component {
3838
}
3939

4040
saveGist() {
41-
const { categoryKey, algorithmKey, gistId, titles, files, lastFiles } = this.props.current;
41+
const { user } = this.props.env;
42+
const { categoryKey, algorithmKey, gistId, titles, files, lastFiles, lastGist } = this.props.current;
4243
const gist = {
4344
description: titles[1],
4445
files: {},
@@ -56,21 +57,28 @@ class Header extends React.Component {
5657
gist.files['algorithm-visualizer'] = {
5758
content: 'https://algorithm-visualizer.org/',
5859
};
59-
const savePromise = gistId === 'new' ? GitHubApi.createGist(gist) : GitHubApi.editGist(gistId, gist);
60-
savePromise
60+
const save = gist => {
61+
if (!user) return Promise.reject(new Error('Sign In Required'));
62+
if (gistId === 'new') return GitHubApi.createGist(gist);
63+
if (gistId === 'forked') {
64+
return GitHubApi.forkGist(lastGist.id).then(forkedGist => GitHubApi.editGist(forkedGist.id, gist));
65+
}
66+
return GitHubApi.editGist(gistId, gist);
67+
};
68+
save(gist)
6169
.then(refineGist)
62-
.then(algorithm => this.props.setCurrent(categoryKey, algorithmKey, algorithm.gistId, algorithm.titles, algorithm.files))
70+
.then(algorithm => this.props.setCurrent(categoryKey, algorithmKey, algorithm.gistId, algorithm.titles, algorithm.files, algorithm.gist))
6371
.then(this.props.loadScratchPapers)
64-
.catch(this.props.showErrorToast);
72+
.catch(handleError.bind(this));
6573
}
6674

6775
deleteGist() {
6876
const { gistId } = this.props.current;
69-
const deletePromise = gistId === 'new' ? Promise.resolve() : GitHubApi.deleteGist(gistId);
77+
const deletePromise = ['new', 'forked'].includes(gistId) ? Promise.resolve() : GitHubApi.deleteGist(gistId);
7078
deletePromise
7179
.then(() => this.props.loadAlgorithm({}, true))
7280
.then(this.props.loadScratchPapers)
73-
.catch(this.props.showErrorToast);
81+
.catch(handleError.bind(this));
7482
}
7583

7684
render() {
@@ -102,7 +110,7 @@ class Header extends React.Component {
102110
onClick={() => this.saveGist()}>Save</Button>
103111
<Button icon={faTrashAlt} primary disabled={!gistId} onClick={() => this.deleteGist()}
104112
confirmNeeded>Delete</Button>
105-
<Button icon={faFacebook} primary disabled={gistId === 'new'}
113+
<Button icon={faFacebook} primary disabled={['new', 'forked'].includes(gistId)}
106114
href={`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(window.location.href)}`}>Share</Button>
107115
<Button icon={faExpandArrowsAlt} primary
108116
onClick={() => this.handleClickFullScreen()}>Fullscreen</Button>

src/frontend/components/Player/index.jsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@ import faChevronLeft from '@fortawesome/fontawesome-free-solid/faChevronLeft';
77
import faChevronRight from '@fortawesome/fontawesome-free-solid/faChevronRight';
88
import faPause from '@fortawesome/fontawesome-free-solid/faPause';
99
import faWrench from '@fortawesome/fontawesome-free-solid/faWrench';
10-
import { classes, extension } from '/common/util';
10+
import { classes, extension, handleError } from '/common/util';
1111
import { TracerApi } from '/apis';
12-
import { CompileError } from '/common/error';
1312
import { actions } from '/reducers';
14-
import { Button } from '/components';
13+
import { Button, ProgressBar } from '/components';
1514
import styles from './stylesheet.scss';
16-
import ProgressBar from '../ProgressBar';
1715

1816
@connect(({ player }) => ({ player }), actions)
1917
class Player extends React.Component {
@@ -73,10 +71,10 @@ class Player extends React.Component {
7371
const ext = extension(file.name);
7472
(ext in TracerApi ?
7573
TracerApi[ext]({ code: file.content }) :
76-
Promise.reject(new CompileError('Language Not Supported')))
74+
Promise.reject(new Error('Language Not Supported')))
7775
.then(traces => this.reset(traces))
7876
.then(() => this.next())
79-
.catch(error => this.handleError(error))
77+
.catch(handleError.bind(this))
8078
.finally(() => this.setState({ building: false }));
8179
}
8280

@@ -117,11 +115,6 @@ class Player extends React.Component {
117115
return true;
118116
}
119117

120-
handleError(error) {
121-
console.error(error);
122-
this.props.showErrorToast({ name: error.name, message: error.message });
123-
}
124-
125118
handleChangeInterval(interval) {
126119
this.setState({ interval });
127120
}

src/frontend/components/ProgressBar/index.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class ProgressBar extends React.Component {
1313

1414
handleMouseDown(e) {
1515
this.target = e.target;
16-
console.log(this.target)
1716
this.handleMouseMove(e);
1817
document.addEventListener('mousemove', this.handleMouseMove);
1918
document.addEventListener('mouseup', this.handleMouseUp);

0 commit comments

Comments
 (0)