From 6e8be4d8e9d5f3d6c912a668b56d9109ee80a77d Mon Sep 17 00:00:00 2001 From: Jason Park Date: Fri, 7 Dec 2018 03:06:53 -0500 Subject: [PATCH] Fix minor bugs and do frontend code refactoring --- src/frontend/common/config.js | 2 +- src/frontend/common/util.js | 27 +--- src/frontend/components/App/index.jsx | 131 ++++++++---------- src/frontend/components/CodeEditor/index.jsx | 3 +- src/frontend/components/Header/index.jsx | 78 +++++++---- src/frontend/components/Navigator/index.jsx | 11 +- .../files/algorithm-visualizer/README.md | 22 +++ src/frontend/files/index.js | 20 +++ .../scratch-paper/README.md} | 0 src/frontend/{ => files}/skeletons/code.cpp | 0 src/frontend/{ => files}/skeletons/code.java | 0 src/frontend/{ => files}/skeletons/code.js | 0 src/frontend/reducers/current.js | 112 ++++++++------- src/frontend/skeletons/README.md | 1 - src/frontend/skeletons/index.js | 5 - webpack.frontend.config.js | 2 +- 16 files changed, 213 insertions(+), 201 deletions(-) create mode 100644 src/frontend/files/algorithm-visualizer/README.md create mode 100644 src/frontend/files/index.js rename src/frontend/{skeletons/SCRATCH_PAPER.md => files/scratch-paper/README.md} (100%) rename src/frontend/{ => files}/skeletons/code.cpp (100%) rename src/frontend/{ => files}/skeletons/code.java (100%) rename src/frontend/{ => files}/skeletons/code.js (100%) delete mode 120000 src/frontend/skeletons/README.md delete mode 100644 src/frontend/skeletons/index.js diff --git a/src/frontend/common/config.js b/src/frontend/common/config.js index 17f8052..e6aa940 100644 --- a/src/frontend/common/config.js +++ b/src/frontend/common/config.js @@ -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', diff --git a/src/frontend/common/util.js b/src/frontend/common/util.js index 1c547cf..dfff0d0 100644 --- a/src/frontend/common/util.js +++ b/src/frontend/common/util.js @@ -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, }; diff --git a/src/frontend/components/App/index.jsx b/src/frontend/components/App/index.jsx index 4c9698f..3d3a727 100644 --- a/src/frontend/components/App/index.jsx +++ b/src/frontend/components/App/index.jsx @@ -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} -
this.toggleNavigatorOpened()} - navigatorOpened={navigatorOpened} loadScratchPapers={() => this.loadScratchPapers()} - loadAlgorithm={this.loadAlgorithm.bind(this)} gistSaved={gistSaved} - file={file} /> +
this.toggleNavigatorOpened()} saved={saved} + navigatorOpened={navigatorOpened} loadScratchPapers={() => this.loadScratchPapers()} file={file} /> this.handleChangeWorkspaceWeights(weights)}> - + this.handleChangeEditorTabIndex(tabIndex)}> diff --git a/src/frontend/components/CodeEditor/index.jsx b/src/frontend/components/CodeEditor/index.jsx index 59ac207..71f7743 100644 --- a/src/frontend/components/CodeEditor/index.jsx +++ b/src/frontend/components/CodeEditor/index.jsx @@ -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); diff --git a/src/frontend/components/Header/index.jsx b/src/frontend/components/Header/index.jsx index 0fee6bf..5e78a31 100644 --- a/src/frontend/components/Header/index.jsx +++ b/src/frontend/components/Header/index.jsx @@ -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 (
- - - + { + permitted && + + } + diff --git a/src/frontend/components/Navigator/index.jsx b/src/frontend/components/Navigator/index.jsx index a4128aa..07ab061 100644 --- a/src/frontend/components/Navigator/index.jsx +++ b/src/frontend/components/Navigator/index.jsx @@ -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 => ( loadAlgorithm({ - categoryKey: category.key, - algorithmKey: algorithm.key, - })} label={algorithm.name} /> + to={`/${category.key}/${algorithm.key}`} label={algorithm.name} /> )) } @@ -113,11 +110,11 @@ class Navigator extends React.Component {
this.toggleScratchPaper()} opened={scratchPaperOpened}> - loadAlgorithm({ gistId: 'new' })} /> + { scratchPapers.map(scratchPaper => ( loadAlgorithm({ gistId: scratchPaper.key })} label={scratchPaper.name} /> + to={`/scratch-paper/${scratchPaper.key}`} label={scratchPaper.name} /> )) } diff --git a/src/frontend/files/algorithm-visualizer/README.md b/src/frontend/files/algorithm-visualizer/README.md new file mode 100644 index 0000000..14c25cb --- /dev/null +++ b/src/frontend/files/algorithm-visualizer/README.md @@ -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. diff --git a/src/frontend/files/index.js b/src/frontend/files/index.js new file mode 100644 index 0000000..42662ee --- /dev/null +++ b/src/frontend/files/index.js @@ -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'); diff --git a/src/frontend/skeletons/SCRATCH_PAPER.md b/src/frontend/files/scratch-paper/README.md similarity index 100% rename from src/frontend/skeletons/SCRATCH_PAPER.md rename to src/frontend/files/scratch-paper/README.md diff --git a/src/frontend/skeletons/code.cpp b/src/frontend/files/skeletons/code.cpp similarity index 100% rename from src/frontend/skeletons/code.cpp rename to src/frontend/files/skeletons/code.cpp diff --git a/src/frontend/skeletons/code.java b/src/frontend/files/skeletons/code.java similarity index 100% rename from src/frontend/skeletons/code.java rename to src/frontend/files/skeletons/code.java diff --git a/src/frontend/skeletons/code.js b/src/frontend/files/skeletons/code.js similarity index 100% rename from src/frontend/skeletons/code.js rename to src/frontend/files/skeletons/code.js diff --git a/src/frontend/reducers/current.js b/src/frontend/reducers/current.js index 1f8a9b9..9da7f8f 100644 --- a/src/frontend/reducers/current.js +++ b/src/frontend/reducers/current.js @@ -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); diff --git a/src/frontend/skeletons/README.md b/src/frontend/skeletons/README.md deleted file mode 120000 index 8a33348..0000000 --- a/src/frontend/skeletons/README.md +++ /dev/null @@ -1 +0,0 @@ -../../../README.md \ No newline at end of file diff --git a/src/frontend/skeletons/index.js b/src/frontend/skeletons/index.js deleted file mode 100644 index ee1266f..0000000 --- a/src/frontend/skeletons/index.js +++ /dev/null @@ -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'; diff --git a/webpack.frontend.config.js b/webpack.frontend.config.js index 77278fe..54a53ac 100644 --- a/webpack.frontend.config.js +++ b/webpack.frontend.config.js @@ -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([