Refactoring routing to be more predictable

This commit is contained in:
Alex Hunt 2018-05-02 20:29:45 -04:00
parent d24d444683
commit fde36c08d1
10 changed files with 155 additions and 160 deletions

View File

@ -26,6 +26,17 @@
NEXT: Bring everything under single entry point w/ React Router
REFACTORING CHECKLIST:
- Split Up joyceAPI
- Single Action Object
- Switch to Webpack Dev Server and Remove Webpack/Flask Dependencies
- Write ReadMe
- One Pass Through Every Module's Imports
NEED:
- Deploy Process
- Testing
SHORT LIST:
- Make Annotate Mode Ignore Keyboard Input
- More Responsive CSS

View File

@ -1,7 +1,7 @@
import React from 'react'
import { Link } from 'react-router-dom'
import romanize from '../mixins/romanize'
import romanize from '../modules/romanize'
export const ReaderEditButton = ({onClick}) =>
<div className='edit_note_button'>

View File

@ -1,5 +1,5 @@
import React from 'react'
import romanize from '../mixins/romanize'
import romanize from '../modules/romanize'
const DocumentTitle = ({docType, currentDocument}) =>
<h4>

View File

@ -1,7 +1,4 @@
import axios from 'axios'
import { stateToHTML } from 'draft-js-export-html'
import { stateToMarkdown } from 'draft-js-export-markdown'
import { convertToRaw } from 'draft-js'
import { push } from 'react-router-redux'
import {
@ -21,102 +18,14 @@ import {
HTTPDeleteDocument,
HTTPPutCreateDocument,
HTTPPostWriteDocument,
HTTPPostSearchResults } from './http.js'
HTTPPostSearchResults } from './http'
const html_export_options = {
entityStyleFn: (entity) => {
const entityType = entity.get('type').toUpperCase()
if (entityType === 'LINK') {
const data = entity.getData()
return {
element: 'a',
attributes: {
'href': data.url,
'data-target': '#annotation_modal',
'data-toggle': 'modal'
}
}
}
}
}
const convertToPlainText = contentState => {
const rawState = convertToRaw(contentState)
return rawState.blocks.reduce(
(plaintText, block) => plaintText + block.text + '\n',
''
)
}
// const selectChapterIDByNumber = (store, number) => {
// for (const chapter of store.getState().chapters) {
// if (number === chapter.number) {
// return chapter.id
// }
// }
// }
const parseNumberFromPath = path => {
const match = /\/([0-9]*)$/.exec(path)
if (match && parseInt(match[1])) {
return Number(match[1])
} else {
return null
}
}
const parseIDFromPath = path => {
const match = /\/([A-Za-z0-9\-\_]{18,})$/.exec(path)
if (match) {
return match[1]
} else {
return null
}
}
const checkIfRedirectPath = path => {
const match = /\/(\:id)$/.exec(path)
if (match) {
return true
} else {
return false
}
}
const selectDocumentIDByPath = (path, docs, docType) => {
if (docType === 'chapters') {
const number = parseNumberFromPath(path)
for (const chapter of docs) {
if (chapter.number === number) {return chapter.id}
}
} else {
const id = parseIDFromPath(path)
for (const doc of docs) {
if (doc.id === id) {return doc.id}
}
}
}
const selectDocTypeIdentifier = (doc, docType) => {
if (docType === 'chapters') {
return String(doc.number)
} else {
return doc.id
}
}
import helpers from '../modules/helpers'
// API Middleware
export const joyceAPI = store => next => action => {
next(action)
switch(action.type) {
case '@@router/LOCATION_CHANGE':
if (checkIfRedirectPath(action.payload.pathname) || action.payload.pathname === '/') {
if (store.getState().currentDocument.id) {
const id = selectDocTypeIdentifier(store.getState().currentDocument, store.getState().docType)
store.dispatch(push(id))
}
}
break
// API Calls
case 'GET_DOCUMENT_LIST':
if (action.status === 'request') {
@ -125,12 +34,12 @@ export const joyceAPI = store => next => action => {
)
}
if (action.status === 'success' && action.docType === store.getState().docType && !store.getState().currentDocument.id) {
const path = store.getState().routerReducer.location.pathname
if (checkIfRedirectPath(path)) {
store.dispatch(setCurrentDocument(action.data[0].id, action.docType))
} else {
const id = selectDocumentIDByPath(path, action.data, action.docType)
store.dispatch(setCurrentDocument(id, action.docType))
if (helpers.checkIfRedirectPath(store.getState().routerReducer.location.pathname)) {
if (action.docType === 'chapters') {
store.dispatch(push(action.data[0].number))
} else {
store.dispatch(push(action.data[0].id))
}
}
}
break
@ -139,14 +48,7 @@ export const joyceAPI = store => next => action => {
HTTPGetDocumentText(action.id, action.docType, action.state).then(response =>
store.dispatch(getDocumentText(response))
)
}
if (action.status === 'success' && action.state === 'currentDocument') {
if (action.docType === 'chapters') {
store.dispatch(push(String(action.data.number)))
} else if (action.docType === 'notes') {
store.dispatch(push(action.data.id))
}
}
}
break
case 'SAVE_DOCUMENT':
if (action.status === 'request') {
@ -172,52 +74,6 @@ export const joyceAPI = store => next => action => {
}
}
break
case 'SET_CURRENT_DOCUMENT':
store.dispatch(getDocumentText({id: action.id, docType: action.docType, state: 'currentDocument'}))
break
case 'SUBMIT_DOCUMENT_EDIT':
const textContent = action.editorState.getCurrentContent()
const data = { title: action.documentTitleInput, html_source: stateToHTML(textContent, html_export_options), plain_text: convertToPlainText(textContent) }
if (action.currentDocument.id) {
data.id = action.currentDocument.id
}
if (action.docType === 'chapters') {
if (!action.currentDocument.id) {
const nextNumber = store.getState().chapters.length + 1
data.number = nextNumber
} else {
data.number = action.currentDocument.number
}
}
store.dispatch(saveDocument({id: data.id ? data.id : null, docType: action.docType, data: data}))
break
case 'DELETE_CURRENT_DOCUMENT':
store.dispatch(deleteDocument({id: action.id, docType: action.docType}))
break
case 'CANCEL_EDIT':
const docType = store.getState().docType
const currentDocument = store.getState().currentDocument
if (currentDocument.id) {
store.dispatch(getDocumentText({id: currentDocument.id, docType: docType, state: 'currentDocument'}))
} else {
store.dispatch(getDocumentText({id: notes[0].id, docType: docType, state: 'currentDocument'}))
}
break
// Annotation Action Middleware
case 'SELECT_ANNOTATION_NOTE':
store.dispatch(getDocumentText({id: action.id, docType: 'notes', state: 'annotationNote'}))
break
// Search Action Middleware
case 'CLICK_SEARCH':
store.dispatch(getSearchResults({data: action.data}))
break
case 'GET_SEARCH_RESULTS':
if (action.status === 'request') {
HTTPPostSearchResults(action.data).then(response =>
store.dispatch(getSearchResults(response))
)
}
break
default:
break
}

View File

@ -0,0 +1,78 @@
import axios from 'axios'
import { stateToHTML } from 'draft-js-export-html'
import { stateToMarkdown } from 'draft-js-export-markdown'
import { convertToRaw } from 'draft-js'
import helpers from '../modules/helpers'
const html_export_options = {
entityStyleFn: (entity) => {
const entityType = entity.get('type').toUpperCase()
if (entityType === 'LINK') {
const data = entity.getData()
return {
element: 'a',
attributes: {
'href': data.url,
'data-target': '#annotation_modal',
'data-toggle': 'modal'
}
}
}
}
}
// API Middleware
export const joyceInterface = store => next => action => {
next(action)
switch(action.type) {
case 'SET_CURRENT_DOCUMENT':
store.dispatch(getDocumentText({id: action.id, docType: action.docType, state: 'currentDocument'}))
break
case 'SUBMIT_DOCUMENT_EDIT':
const textContent = action.editorState.getCurrentContent()
const data = { title: action.documentTitleInput, html_source: stateToHTML(textContent, html_export_options), plain_text: convertToPlainText(textContent) }
if (action.currentDocument.id) {
data.id = action.currentDocument.id
}
if (action.docType === 'chapters') {
if (!action.currentDocument.id) {
const nextNumber = store.getState().chapters.length + 1
data.number = nextNumber
} else {
data.number = action.currentDocument.number
}
}
store.dispatch(saveDocument({id: data.id ? data.id : null, docType: action.docType, data: data}))
break
case 'DELETE_CURRENT_DOCUMENT':
store.dispatch(deleteDocument({id: action.id, docType: action.docType}))
break
case 'CANCEL_EDIT':
const docType = store.getState().docType
const currentDocument = store.getState().currentDocument
if (currentDocument.id) {
store.dispatch(getDocumentText({id: currentDocument.id, docType: docType, state: 'currentDocument'}))
} else {
store.dispatch(getDocumentText({id: notes[0].id, docType: docType, state: 'currentDocument'}))
}
break
// Annotation Action Middleware
case 'SELECT_ANNOTATION_NOTE':
store.dispatch(getDocumentText({id: action.id, docType: 'notes', state: 'annotationNote'}))
break
// Search Action Middleware
case 'CLICK_SEARCH':
store.dispatch(getSearchResults({data: action.data}))
break
case 'GET_SEARCH_RESULTS':
if (action.status === 'request') {
HTTPPostSearchResults(action.data).then(response =>
store.dispatch(getSearchResults(response))
)
}
break
default:
break
}
}

View File

@ -0,0 +1,27 @@
export const joyceRouter = store => next => action => {
next(action)
switch(action.type) {
case '@@router/LOCATION_CHANGE':
const path = action.payload.pathname
const docType = store.getState().docType
const currentDocument = store.getState().currentDocument
switch(path) {
case /^\/[0-9]{,3}/.test(path):
// set docType = 'chapters'
// set Current Chapter
case /^\/notes\/[a-zA-Z\-\_]{18,}/.test(path):
// set docType = 'notes'
// set current note
case /^\/edit\/notes\/[a-zA-Z\-\_]{18,}]/.test(path):
// set docType = 'chapters'
// set Current Chapter
// case: if /edit/ and docType = notes => /edit/notes/
// case: if redirectURL
}
default:
break
}
}
export default joyceRouter

View File

@ -1,5 +0,0 @@
const helpers = {
}
export default helpers

28
src/modules/helpers.js Normal file
View File

@ -0,0 +1,28 @@
const helpers = {
checkIfRedirectPath: path => {
const match = /\/(\:id)$/.exec(path)
if (match) {
return true
} else {
return false
}
},
parseNumberFromPath: path => {
const match = /\/([0-9]*)$/.exec(path)
if (match && parseInt(match[1])) {
return Number(match[1])
} else {
return null
}
},
parseIDFromPath: path => {
const match = /\/([A-Za-z0-9\-\_]{18,})$/.exec(path)
if (match) {
return match[1]
} else {
return null
}
}
}
export default helpers