Refactoring routing to be more predictable
This commit is contained in:
parent
d24d444683
commit
fde36c08d1
11
TODO.txt
11
TODO.txt
|
@ -26,6 +26,17 @@
|
||||||
|
|
||||||
NEXT: Bring everything under single entry point w/ React Router
|
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:
|
SHORT LIST:
|
||||||
- Make Annotate Mode Ignore Keyboard Input
|
- Make Annotate Mode Ignore Keyboard Input
|
||||||
- More Responsive CSS
|
- More Responsive CSS
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
import romanize from '../mixins/romanize'
|
import romanize from '../modules/romanize'
|
||||||
|
|
||||||
export const ReaderEditButton = ({onClick}) =>
|
export const ReaderEditButton = ({onClick}) =>
|
||||||
<div className='edit_note_button'>
|
<div className='edit_note_button'>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import romanize from '../mixins/romanize'
|
import romanize from '../modules/romanize'
|
||||||
|
|
||||||
const DocumentTitle = ({docType, currentDocument}) =>
|
const DocumentTitle = ({docType, currentDocument}) =>
|
||||||
<h4>
|
<h4>
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import axios from 'axios'
|
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 { push } from 'react-router-redux'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -21,102 +18,14 @@ import {
|
||||||
HTTPDeleteDocument,
|
HTTPDeleteDocument,
|
||||||
HTTPPutCreateDocument,
|
HTTPPutCreateDocument,
|
||||||
HTTPPostWriteDocument,
|
HTTPPostWriteDocument,
|
||||||
HTTPPostSearchResults } from './http.js'
|
HTTPPostSearchResults } from './http'
|
||||||
|
|
||||||
const html_export_options = {
|
import helpers from '../modules/helpers'
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// API Middleware
|
// API Middleware
|
||||||
export const joyceAPI = store => next => action => {
|
export const joyceAPI = store => next => action => {
|
||||||
next(action)
|
next(action)
|
||||||
switch(action.type) {
|
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
|
// API Calls
|
||||||
case 'GET_DOCUMENT_LIST':
|
case 'GET_DOCUMENT_LIST':
|
||||||
if (action.status === 'request') {
|
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) {
|
if (action.status === 'success' && action.docType === store.getState().docType && !store.getState().currentDocument.id) {
|
||||||
const path = store.getState().routerReducer.location.pathname
|
if (helpers.checkIfRedirectPath(store.getState().routerReducer.location.pathname)) {
|
||||||
if (checkIfRedirectPath(path)) {
|
if (action.docType === 'chapters') {
|
||||||
store.dispatch(setCurrentDocument(action.data[0].id, action.docType))
|
store.dispatch(push(action.data[0].number))
|
||||||
} else {
|
} else {
|
||||||
const id = selectDocumentIDByPath(path, action.data, action.docType)
|
store.dispatch(push(action.data[0].id))
|
||||||
store.dispatch(setCurrentDocument(id, action.docType))
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -139,14 +48,7 @@ export const joyceAPI = store => next => action => {
|
||||||
HTTPGetDocumentText(action.id, action.docType, action.state).then(response =>
|
HTTPGetDocumentText(action.id, action.docType, action.state).then(response =>
|
||||||
store.dispatch(getDocumentText(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
|
break
|
||||||
case 'SAVE_DOCUMENT':
|
case 'SAVE_DOCUMENT':
|
||||||
if (action.status === 'request') {
|
if (action.status === 'request') {
|
||||||
|
@ -172,52 +74,6 @@ export const joyceAPI = store => next => action => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
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:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
||||||
const helpers = {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default helpers
|
|
|
@ -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
|
Loading…
Reference in New Issue