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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy