Fix minor bugs and do frontend code refactoring

This commit is contained in:
Jason Park
2018-12-07 03:06:53 -05:00
parent eb89a866e3
commit 6e8be4d8e9
16 changed files with 213 additions and 201 deletions

View File

@@ -1,4 +1,4 @@
import { CODE_CPP, CODE_JAVA, CODE_JS } from '/skeletons';
import { CODE_CPP, CODE_JAVA, CODE_JS } from '/files';
const languages = [{
name: 'JavaScript',

View File

@@ -1,5 +1,3 @@
import { README_MD } from '/skeletons';
const classes = (...arr) => arr.filter(v => v).join(' ');
const distance = (a, b) => {
@@ -20,28 +18,7 @@ const refineGist = gist => {
content: file.content,
contributors: [{ login, avatar_url }],
}));
return { gistId, title, files, gist };
};
const getFiles = current => {
const { algorithm, scratchPaper } = current;
if (algorithm) return algorithm.files;
if (scratchPaper) return scratchPaper.files;
return [{
name: 'README.md',
content: README_MD,
contributors: [{
login: 'algorithm-visualizer',
avatar_url: 'https://github.com/algorithm-visualizer.png',
}],
}];
};
const getTitleArray = current => {
const { algorithm, scratchPaper } = current;
if (algorithm) return [algorithm.categoryName, algorithm.algorithmName];
if (scratchPaper) return ['Scratch Paper', scratchPaper.title];
return ['Algorithm Visualizer'];
return { login, gistId, title, files };
};
const handleError = function (error) {
@@ -54,7 +31,5 @@ export {
distance,
extension,
refineGist,
getFiles,
getTitleArray,
handleError,
};

View File

@@ -19,9 +19,9 @@ import {
} from '/components';
import { AlgorithmApi, GitHubApi } from '/apis';
import { actions } from '/reducers';
import { extension, getFiles, getTitleArray, handleError, refineGist } from '/common/util';
import { extension, handleError, refineGist } from '/common/util';
import { exts, languages } from '/common/config';
import { SCRATCH_PAPER_MD } from '/skeletons';
import { SCRATCH_PAPER_MD } from '/files';
import styles from './stylesheet.scss';
loadProgressBar();
@@ -53,7 +53,9 @@ class App extends React.Component {
.then(({ categories }) => this.props.setCategories(categories))
.catch(handleError.bind(this));
window.onbeforeunload = () => this.isGistSaved() ? undefined : 'Changes you made will not be saved.';
this.props.history.block(() => {
if (!this.isSaved()) return 'Are you sure want to discard changes?';
});
}
componentWillUnmount() {
@@ -65,26 +67,12 @@ class App extends React.Component {
componentWillReceiveProps(nextProps) {
const { params } = nextProps.match;
const { algorithm, scratchPaper } = nextProps.current;
const categoryKey = algorithm && algorithm.categoryKey;
const algorithmKey = algorithm && algorithm.algorithmKey;
const gistId = scratchPaper && scratchPaper.gistId;
if (params.categoryKey !== categoryKey ||
params.algorithmKey !== algorithmKey ||
params.gistId !== gistId) {
if (nextProps.location.pathname !== this.props.location.pathname) {
this.loadAlgorithm(params);
} else {
if (categoryKey && algorithmKey) {
this.props.history.push(`/${categoryKey}/${algorithmKey}`);
} else if (gistId) {
this.props.history.push(`/scratch-paper/${gistId}`);
} else {
this.props.history.push('/');
}
}
if (params !== this.props.match.params) {
const { categoryKey, algorithmKey, gistId } = params;
const { algorithm, scratchPaper } = nextProps.current;
if (algorithm && algorithm.categoryKey === categoryKey && algorithm.algorithmKey === algorithmKey) return;
if (scratchPaper && scratchPaper.gistId === gistId) return;
this.loadAlgorithm(params);
}
}
@@ -95,7 +83,6 @@ class App extends React.Component {
.then(user => {
const { login, avatar_url } = user;
this.props.setUser({ login, avatar_url });
Cookies.set('login', login);
})
.then(() => this.loadScratchPapers())
.catch(() => this.signOut());
@@ -106,7 +93,6 @@ class App extends React.Component {
GitHubApi.auth(undefined)
.then(() => {
this.props.setUser(undefined);
Cookies.remove('login');
})
.then(() => this.props.setScratchPapers([]));
}
@@ -134,44 +120,37 @@ class App extends React.Component {
.catch(handleError.bind(this));
}
loadAlgorithm({ categoryKey, algorithmKey, gistId }, forceLoad = false) {
if (!forceLoad && !this.isGistSaved() && !window.confirm('Are you sure want to discard changes?')) return;
loadAlgorithm({ categoryKey, algorithmKey, gistId }) {
const { ext } = this.props.env;
let fetchPromise = null;
if (categoryKey && algorithmKey) {
fetchPromise = AlgorithmApi.getAlgorithm(categoryKey, algorithmKey)
.then(({ algorithm }) => this.props.setAlgorithm(algorithm));
} else if (['new', 'forked'].includes(gistId)) {
gistId = 'new';
const language = languages.find(language => language.ext === ext);
fetchPromise = Promise.resolve(this.props.setScratchPaper({
gistId,
title: 'Untitled',
files: [{
name: 'README.md',
content: SCRATCH_PAPER_MD,
contributors: undefined,
}, {
name: `code.${ext}`,
content: language.skeleton,
contributors: undefined,
}],
}));
} else if (gistId) {
fetchPromise = GitHubApi.getGist(gistId, { timestamp: Date.now() })
.then(refineGist)
.then(this.props.setScratchPaper);
} else {
fetchPromise = Promise.reject(new Error());
}
fetchPromise
const fetch = () => {
if (categoryKey && algorithmKey) {
return AlgorithmApi.getAlgorithm(categoryKey, algorithmKey)
.then(({ algorithm }) => this.props.setAlgorithm(algorithm));
} else if (gistId === 'new') {
const language = languages.find(language => language.ext === ext);
this.props.setScratchPaper({
login: undefined,
gistId,
title: 'Untitled',
files: [SCRATCH_PAPER_MD, language.skeleton],
});
return Promise.resolve();
} else if (gistId) {
return GitHubApi.getGist(gistId, { timestamp: Date.now() })
.then(refineGist)
.then(this.props.setScratchPaper);
} else {
this.props.setHome();
return Promise.resolve();
}
};
fetch()
.catch(error => {
if (error.message) handleError.bind(this)(error);
handleError.bind(this)(error);
this.props.setHome();
})
.finally(() => {
const files = getFiles(this.props.current);
const { files } = this.props.current;
let editorTabIndex = files.findIndex(file => extension(file.name) === ext);
if (!~editorTabIndex) editorTabIndex = files.findIndex(file => exts.includes(extension(file.name)));
if (!~editorTabIndex) editorTabIndex = Math.min(0, files.length - 1);
@@ -185,7 +164,7 @@ class App extends React.Component {
}
handleChangeEditorTabIndex(editorTabIndex) {
const files = getFiles(this.props.current);
const { files } = this.props.current;
if (editorTabIndex === files.length) this.handleAddFile();
this.setState({ editorTabIndex });
this.props.shouldBuild();
@@ -193,7 +172,7 @@ class App extends React.Component {
handleAddFile() {
const { ext } = this.props.env;
const files = getFiles(this.props.current);
const { files } = this.props.current;
let name = `code.${ext}`;
let count = 0;
while (files.some(file => file.name === name)) name = `code-${++count}.${ext}`;
@@ -213,7 +192,7 @@ class App extends React.Component {
handleDeleteFile() {
const { editorTabIndex } = this.state;
const files = getFiles(this.props.current);
const { files } = this.props.current;
this.handleChangeEditorTabIndex(Math.min(editorTabIndex, files.length - 2));
this.props.deleteFile(editorTabIndex);
}
@@ -222,16 +201,17 @@ class App extends React.Component {
this.setState({ navigatorOpened });
}
isGistSaved() {
const { scratchPaper } = this.props.current;
if (!scratchPaper) return true;
const { title, files, lastTitle, lastFiles } = scratchPaper;
const serializeFiles = files => JSON.stringify(files.map(({ name, content }) => ({ name, content })));
return title === lastTitle && serializeFiles(files) === serializeFiles(lastFiles);
isSaved() {
const { titles, files, lastTitles, lastFiles } = this.props.current;
const serialize = (titles, files) => JSON.stringify({
titles,
files: files.map(({ name, content }) => ({ name, content })),
});
return serialize(titles, files) === serialize(lastTitles, lastFiles);
}
getDescription() {
const files = getFiles(this.props.current);
const { files } = this.props.current;
const readmeFile = files.find(file => file.name === 'README.md');
if (!readmeFile) return '';
const groups = /^\s*# .*\n+([^\n]+)/.exec(readmeFile.content);
@@ -241,10 +221,9 @@ class App extends React.Component {
render() {
const { navigatorOpened, workspaceWeights, editorTabIndex } = this.state;
const files = getFiles(this.props.current);
const titleArray = getTitleArray(this.props.current);
const gistSaved = this.isGistSaved();
const title = `${gistSaved ? '' : '(Unsaved) '}${titleArray.join(' - ')}`;
const { files, titles } = this.props.current;
const saved = this.isSaved();
const title = `${saved ? '' : '(Unsaved) '}${titles.join(' - ')}`;
const description = this.getDescription();
const file = files[editorTabIndex];
@@ -265,14 +244,12 @@ class App extends React.Component {
<title>{title}</title>
<meta name="description" content={description} />
</Helmet>
<Header className={styles.header} onClickTitleBar={() => this.toggleNavigatorOpened()}
navigatorOpened={navigatorOpened} loadScratchPapers={() => this.loadScratchPapers()}
loadAlgorithm={this.loadAlgorithm.bind(this)} gistSaved={gistSaved}
file={file} />
<Header className={styles.header} onClickTitleBar={() => this.toggleNavigatorOpened()} saved={saved}
navigatorOpened={navigatorOpened} loadScratchPapers={() => this.loadScratchPapers()} file={file} />
<ResizableContainer className={styles.workspace} horizontal weights={workspaceWeights}
visibles={[navigatorOpened, true, true]}
onChangeWeights={weights => this.handleChangeWorkspaceWeights(weights)}>
<Navigator loadAlgorithm={this.loadAlgorithm.bind(this)} />
<Navigator />
<VisualizationViewer className={styles.visualization_viewer} />
<TabContainer className={styles.editor_tab_container} titles={editorTitles} tabIndex={editorTabIndex}
onChangeTabIndex={tabIndex => this.handleChangeEditorTabIndex(tabIndex)}>

View File

@@ -5,7 +5,6 @@ import 'brace/mode/markdown';
import 'brace/mode/javascript';
import 'brace/mode/c_cpp';
import 'brace/mode/java';
import 'brace/mode/python';
import 'brace/theme/tomorrow_night_eighties';
import 'brace/ext/searchbox';
import faTrashAlt from '@fortawesome/fontawesome-free-solid/faTrashAlt';
@@ -17,7 +16,7 @@ import { languages } from '/common/config';
import { Button, Ellipsis } from '/components';
import styles from './stylesheet.scss';
@connect(({ current, env, player }) => ({ current, env, player }), actions, null, { withRef: true })
@connect(({ env, player }) => ({ env, player }), actions, null, { withRef: true })
class CodeEditor extends React.Component {
constructor(props) {
super(props);

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import AutosizeInput from 'react-input-autosize';
import screenfull from 'screenfull';
import Promise from 'bluebird';
@@ -7,6 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import faAngleRight from '@fortawesome/fontawesome-free-solid/faAngleRight';
import faCaretDown from '@fortawesome/fontawesome-free-solid/faCaretDown';
import faCaretRight from '@fortawesome/fontawesome-free-solid/faCaretRight';
import faCodeBranch from '@fortawesome/fontawesome-free-solid/faCodeBranch';
import faExpandArrowsAlt from '@fortawesome/fontawesome-free-solid/faExpandArrowsAlt';
import faGithub from '@fortawesome/fontawesome-free-brands/faGithub';
import faTrashAlt from '@fortawesome/fontawesome-free-solid/faTrashAlt';
@@ -19,8 +21,8 @@ import { actions } from '/reducers';
import { languages } from '/common/config';
import { Button, Ellipsis, ListItem, Player } from '/components';
import styles from './stylesheet.scss';
import { getTitleArray } from '../../common/util';
@withRouter
@connect(({ current, env }) => ({ current, env }), actions)
class Header extends React.Component {
handleClickFullScreen() {
@@ -40,10 +42,9 @@ class Header extends React.Component {
saveGist() {
const { user } = this.props.env;
const { scratchPaper } = this.props.current;
const { gistId, title, files, lastFiles, lastGist } = scratchPaper;
const { scratchPaper, titles, files, lastFiles } = this.props.current;
const gist = {
description: title,
description: titles[titles.length - 1],
files: {},
};
files.forEach(file => {
@@ -61,47 +62,74 @@ class Header extends React.Component {
};
const save = gist => {
if (!user) return Promise.reject(new Error('Sign In Required'));
if (gistId === 'new') return GitHubApi.createGist(gist);
if (gistId === 'forked') {
return GitHubApi.forkGist(lastGist.id).then(forkedGist => GitHubApi.editGist(forkedGist.id, gist));
if (scratchPaper && scratchPaper.login) {
if (scratchPaper.login === user.login) {
return GitHubApi.editGist(scratchPaper.gistId, gist);
} else {
return GitHubApi.forkGist(scratchPaper.gistId).then(forkedGist => GitHubApi.editGist(forkedGist.id, gist));
}
}
return GitHubApi.editGist(gistId, gist);
return GitHubApi.createGist(gist);
};
save(gist)
.then(refineGist)
.then(this.props.setScratchPaper)
.then(newScratchPaper => {
this.props.setScratchPaper(newScratchPaper);
if (!(scratchPaper && scratchPaper.gistId === newScratchPaper.gistId)) {
this.props.history.push(`/scratch-paper/${newScratchPaper.gistId}`);
}
})
.then(this.props.loadScratchPapers)
.catch(handleError.bind(this));
}
hasPermission() {
const { scratchPaper } = this.props.current;
const { user } = this.props.env;
if (!scratchPaper) return false;
if (scratchPaper.gistId !== 'new') {
if (!user) return false;
if (scratchPaper.login !== user.login) return false;
}
return true;
}
deleteGist() {
const { scratchPaper } = this.props.current;
const { gistId } = scratchPaper;
const deletePromise = ['new', 'forked'].includes(gistId) ? Promise.resolve() : GitHubApi.deleteGist(gistId);
deletePromise
.then(() => this.props.loadAlgorithm({}, true))
.then(this.props.loadScratchPapers)
.catch(handleError.bind(this));
if (gistId === 'new') {
this.props.markSaved();
this.props.history.push('/');
} else {
GitHubApi.deleteGist(gistId)
.then(() => {
this.props.markSaved();
this.props.history.push('/');
})
.then(this.props.loadScratchPapers)
.catch(handleError.bind(this));
}
}
render() {
const { className, onClickTitleBar, navigatorOpened, gistSaved, file } = this.props;
const { scratchPaper } = this.props.current;
const titleArray = getTitleArray(this.props.current);
const { className, onClickTitleBar, navigatorOpened, saved, file } = this.props;
const { scratchPaper, titles } = this.props.current;
const { ext, user } = this.props.env;
const permitted = this.hasPermission();
return (
<header className={classes(styles.header, className)}>
<div className={styles.row}>
<div className={styles.section}>
<Button className={styles.title_bar} onClick={onClickTitleBar}>
{
titleArray.map((title, i) => [
titles.map((title, i) => [
scratchPaper && i === 1 ?
<AutosizeInput className={styles.input_title} key={`title-${i}`} value={title}
onClick={e => e.stopPropagation()} onChange={e => this.handleChangeTitle(e)} /> :
<Ellipsis key={`title-${i}`}>{title}</Ellipsis>,
i < titleArray.length - 1 &&
i < titles.length - 1 &&
<FontAwesomeIcon className={styles.nav_arrow} fixedWidth icon={faAngleRight} key={`arrow-${i}`} />,
])
}
@@ -110,11 +138,13 @@ class Header extends React.Component {
</Button>
</div>
<div className={styles.section}>
<Button icon={faSave} primary disabled={!scratchPaper || gistSaved}
onClick={() => this.saveGist()}>Save</Button>
<Button icon={faTrashAlt} primary disabled={!scratchPaper} onClick={() => this.deleteGist()}
confirmNeeded>Delete</Button>
<Button icon={faFacebook} primary disabled={scratchPaper && ['new', 'forked'].includes(scratchPaper.gistId)}
<Button icon={permitted ? faSave : faCodeBranch} primary disabled={permitted && saved}
onClick={() => this.saveGist()}>{permitted ? 'Save' : 'Fork'}</Button>
{
permitted &&
<Button icon={faTrashAlt} primary onClick={() => this.deleteGist()} confirmNeeded>Delete</Button>
}
<Button icon={faFacebook} primary
href={`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(window.location.href)}`}>Share</Button>
<Button icon={faExpandArrowsAlt} primary
onClick={() => this.handleClickFullScreen()}>Fullscreen</Button>

View File

@@ -67,7 +67,7 @@ class Navigator extends React.Component {
render() {
const { categoriesOpened, scratchPaperOpened, query } = this.state;
const { className, loadAlgorithm } = this.props;
const { className } = this.props;
const { categories, scratchPapers } = this.props.directory;
const { algorithm, scratchPaper } = this.props.current;
@@ -99,10 +99,7 @@ class Navigator extends React.Component {
algorithms.map(algorithm => (
<ListItem indent key={algorithm.key}
selected={category.key === categoryKey && algorithm.key === algorithmKey}
onClick={() => loadAlgorithm({
categoryKey: category.key,
algorithmKey: algorithm.key,
})} label={algorithm.name} />
to={`/${category.key}/${algorithm.key}`} label={algorithm.name} />
))
}
</ExpandableListItem>
@@ -113,11 +110,11 @@ class Navigator extends React.Component {
<div className={styles.footer}>
<ExpandableListItem icon={faCode} label="Scratch Paper" onClick={() => this.toggleScratchPaper()}
opened={scratchPaperOpened}>
<ListItem indent label="New ..." onClick={() => loadAlgorithm({ gistId: 'new' })} />
<ListItem indent label="New ..." to="/scratch-paper/new" />
{
scratchPapers.map(scratchPaper => (
<ListItem indent key={scratchPaper.key} selected={scratchPaper.key === gistId}
onClick={() => loadAlgorithm({ gistId: scratchPaper.key })} label={scratchPaper.name} />
to={`/scratch-paper/${scratchPaper.key}`} label={scratchPaper.name} />
))
}
</ExpandableListItem>

View File

@@ -0,0 +1,22 @@
# Algorithm Visualizer
> Algorithm Visualizer is an interactive online platform that visualizes algorithms from code.
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square)](https://gitter.im/algorithm-visualizer)
[![GitHub contributors](https://img.shields.io/github/contributors/algorithm-visualizer/algorithm-visualizer.svg?style=flat-square)](https://github.com/algorithm-visualizer/algorithm-visualizer/graphs/contributors)
[![GitHub](https://img.shields.io/github/license/algorithm-visualizer/algorithm-visualizer.svg?style=flat-square)](https://github.com/algorithm-visualizer/algorithm-visualizer/blob/master/LICENSE)
Learning algorithms from text and static images is quite boring. For that, there have been many great websites that view animations of various algorithms. However, for us being coders, nothing can be more comprehensible than visualizing the actual working code. So here we introduce Algorithm Visualizer.
[![Screenshot](https://raw.githubusercontent.com/algorithm-visualizer/algorithm-visualizer/master/branding/screenshot.png)](https://algorithm-visualizer.org/)
## Contributing
The project [algorithm-visualizer](https://github.com/algorithm-visualizer) is composed of the following 3 repositories.
* [algorithm-visualizer/algorithms](https://github.com/algorithm-visualizer/algorithms): contains public algorithms shown on the sidebar. [Contribute](https://github.com/algorithm-visualizer/algorithms/blob/master/CONTRIBUTING.md)
* [algorithm-visualizer/tracers](https://github.com/algorithm-visualizer/tracers): contains visualization libraries written in each supported language. [Contribute](https://github.com/algorithm-visualizer/tracers/blob/master/CONTRIBUTING.md)
* [algorithm-visualizer/algorithm-visualizer](https://github.com/algorithm-visualizer/algorithm-visualizer): contains the front-end written in React.js and the back-end written in Node.js. [Contribute](https://github.com/algorithm-visualizer/algorithm-visualizer/blob/master/CONTRIBUTING.md)
Take a moment to read `CONTRIBUTING.md` in the repository you want to contribute to.

View File

@@ -0,0 +1,20 @@
const createProjectFile = filePath => ({
name: filePath.split('/').pop(),
content: require('raw-loader!./' + filePath),
contributors: [{
login: 'algorithm-visualizer',
avatar_url: 'https://github.com/algorithm-visualizer.png',
}],
});
const createUserFile = filePath => ({
name: filePath.split('/').pop(),
content: require('raw-loader!./' + filePath),
contributors: undefined,
});
export const CODE_CPP = createUserFile('skeletons/code.cpp');
export const CODE_JAVA = createUserFile('skeletons/code.java');
export const CODE_JS = createUserFile('skeletons/code.js');
export const README_MD = createProjectFile('algorithm-visualizer/README.md');
export const SCRATCH_PAPER_MD = createProjectFile('scratch-paper/README.md');

View File

@@ -1,17 +1,32 @@
import { combineActions, createAction, handleActions } from 'redux-actions';
import Cookies from 'js-cookie';
import { README_MD } from '/files';
const prefix = 'CURRENT';
const setHome = createAction(`${prefix}/SET_HOME`, () => ({ algorithm: undefined, scratchPaper: undefined }));
const setAlgorithm = createAction(`${prefix}/SET_ALGORITHM`, ({ categoryKey, categoryName, algorithmKey, algorithmName, files }) => ({
algorithm: { categoryKey, categoryName, algorithmKey, algorithmName, files },
scratchPaper: undefined,
}));
const setScratchPaper = createAction(`${prefix}/SET_SCRATCH_PAPER`, ({ gistId, title, files, gist }) => ({
algorithm: undefined,
scratchPaper: { gistId, title, files, lastTitle: title, lastFiles: files, lastGist: gist },
}));
const setHome = createAction(`${prefix}/SET_HOME`, () => defaultState);
const setAlgorithm = createAction(`${prefix}/SET_ALGORITHM`, ({ categoryKey, categoryName, algorithmKey, algorithmName, files }) => {
const titles = [categoryName, algorithmName];
return {
algorithm: { categoryKey, algorithmKey },
scratchPaper: undefined,
titles,
files,
lastTitles: titles,
lastFiles: files,
};
});
const setScratchPaper = createAction(`${prefix}/SET_SCRATCH_PAPER`, ({ login, gistId, title, files }) => {
const titles = ['Scratch Paper', title];
return {
algorithm: undefined,
scratchPaper: { login, gistId },
titles,
files,
lastTitles: titles,
lastFiles: files,
};
});
const markSaved = createAction(`${prefix}/MARK_SAVED`);
const modifyTitle = createAction(`${prefix}/MODIFY_TITLE`, title => ({ title }));
const addFile = createAction(`${prefix}/ADD_FILE`, file => ({ file }));
const modifyFile = createAction(`${prefix}/MODIFY_FILE`, file => ({ file }));
@@ -22,6 +37,7 @@ export const actions = {
setHome,
setAlgorithm,
setScratchPaper,
markSaved,
modifyTitle,
addFile,
modifyFile,
@@ -29,43 +45,20 @@ export const actions = {
renameFile,
};
const homeTitles = ['Algorithm Visualizer'];
const homeFiles = [README_MD];
const defaultState = {
algorithm: undefined,
algorithm: {
categoryKey: 'algorithm-visualizer',
algorithmKey: 'home',
},
scratchPaper: undefined,
titles: homeTitles,
files: homeFiles,
lastTitles: homeTitles,
lastFiles: homeFiles,
};
const getScratchPaper = state => {
const { algorithm, scratchPaper } = state;
if (algorithm) {
return {
gistId: 'new',
title: 'Untitled',
files: algorithm.files,
lastTitle: '',
lastFiles: [],
gist: undefined,
};
} else if (scratchPaper) {
if (['new', 'forked'].includes(scratchPaper.gistId)) {
return scratchPaper;
} else if (Cookies.get('login') !== scratchPaper.lastGist.owner.login) {
return {
...scratchPaper,
gistId: 'forked',
lastTitle: '',
lastFiles: [],
};
}
}
return scratchPaper;
};
const updateScratchPaper = (state, scratchPaper, update) => ({
...state,
algorithm: undefined,
scratchPaper: { ...scratchPaper, ...update },
});
export default handleActions({
[combineActions(
setHome,
@@ -75,33 +68,38 @@ export default handleActions({
...state,
...payload,
}),
[markSaved]: state => {
return {
...state,
lastTitles: state.titles,
lastFiles: state.files,
};
},
[modifyTitle]: (state, { payload }) => {
const { title } = payload;
const scratchPaper = getScratchPaper(state);
return updateScratchPaper(state, scratchPaper, { title });
return {
...state,
titles: [state.titles[0], title],
};
},
[addFile]: (state, { payload }) => {
const { file } = payload;
const scratchPaper = getScratchPaper(state);
const files = [...scratchPaper.files, file];
return updateScratchPaper(state, scratchPaper, { files });
const files = [...state.files, file];
return { ...state, files };
},
[modifyFile]: (state, { payload }) => {
const { file } = payload;
const scratchPaper = getScratchPaper(state);
const files = scratchPaper.files.map(oldFile => oldFile.name === file.name ? file : oldFile);
return updateScratchPaper(state, scratchPaper, { files });
const files = state.files.map(oldFile => oldFile.name === file.name ? file : oldFile);
return { ...state, files };
},
[deleteFile]: (state, { payload }) => {
const { index } = payload;
const scratchPaper = getScratchPaper(state);
const files = scratchPaper.files.filter((file, i) => i !== index);
return updateScratchPaper(state, scratchPaper, { files });
const files = state.files.filter((file, i) => i !== index);
return { ...state, files };
},
[renameFile]: (state, { payload }) => {
const { index, name } = payload;
const scratchPaper = getScratchPaper(state);
const files = scratchPaper.files.map((file, i) => i === index ? { ...file, name } : file);
return updateScratchPaper(state, scratchPaper, { files });
const files = state.files.map((file, i) => i === index ? { ...file, name } : file);
return { ...state, files };
},
}, defaultState);

View File

@@ -1 +0,0 @@
../../../README.md

View File

@@ -1,5 +0,0 @@
export { default as CODE_CPP } from 'raw-loader!./code.cpp';
export { default as CODE_JAVA } from 'raw-loader!./code.java';
export { default as CODE_JS } from 'raw-loader!./code.js';
export { default as README_MD } from 'raw-loader!./README.md';
export { default as SCRATCH_PAPER_MD } from 'raw-loader!./SCRATCH_PAPER.md';

View File

@@ -48,7 +48,7 @@ module.exports = {
test: /\.(js|jsx)$/,
use: 'babel-loader',
include: srcPath,
exclude: path.resolve(srcPath, 'skeletons'),
exclude: path.resolve(srcPath, 'files'),
}, {
test: /\.scss$/,
use: filter([