Add state for navigation

Currently selected navigation item is available under the navigation
variable so that the whole application has access to it.
This commit is contained in:
Joey Payne 2016-03-06 11:08:42 -07:00
commit d14eed9965
10 changed files with 154 additions and 45 deletions

View file

@ -0,0 +1,5 @@
import * as types from '../constants/navigation'
export function updateSelection(selection) {
return { type: types.UPDATE_SELECTION, selection }
}

View file

@ -39,7 +39,7 @@ export default class EntrySelector extends React.Component {
constructor(props){ constructor(props){
super(props) super(props)
this.state = {entries: []} this.state = {notes: []}
} }
static get childContextTypes(){ static get childContextTypes(){
@ -57,14 +57,7 @@ export default class EntrySelector extends React.Component {
} }
addNoteTapped = () => { addNoteTapped = () => {
this.createNewNote((note) => { console.log(this.props.navigation)
var notes = this.state.notes
notes.splice(0, 0, note)
this.setState({notes: notes}, () => {
//this.refs['textField0'].focus()
})
})
}; };
render(){ render(){

View file

@ -54,32 +54,33 @@ export default class LibraryNav extends React.Component {
navItems: [ navItems: [
{ {
'name': 'Entries', 'name': 'Entries',
'isNotebook': true,
'icon': <img src="images/note.svg"/>, 'icon': <img src="images/note.svg"/>,
'notes': 10,
'clicked': this.props.entriesTapped || this.entriesTapped 'clicked': this.props.entriesTapped || this.entriesTapped
}, },
{ {
'name': 'Starred', 'name': 'Starred',
'notes': 0,
'icon': <ActionGrade color={colors.amberA700}/>, 'icon': <ActionGrade color={colors.amberA700}/>,
'notes': 1,
'clicked': this.props.starredTapped || this.starredTapped 'clicked': this.props.starredTapped || this.starredTapped
}, },
{ {
'name': 'Recents', 'name': 'Recents',
'notes': 0,
'icon': <History color="#4BAE4E"/>, 'icon': <History color="#4BAE4E"/>,
'notes': 10,
'clicked': this.props.recentsTapped || this.recentsTapped 'clicked': this.props.recentsTapped || this.recentsTapped
}, },
{ {
'name': 'Trash', 'name': 'Trash',
'isNotebook': true,
'icon': <Delete color={colors.grey500}/>, 'icon': <Delete color={colors.grey500}/>,
'notes': 0,
'clicked': this.props.trashTapped || this.trashTapped 'clicked': this.props.trashTapped || this.trashTapped
}, },
{ {
'name': 'All Notes', 'name': 'All Notes',
'icon': <Folder color="#FFCC5F" />,
'notes': 0, 'notes': 0,
'glob': '*.qvnotebook/*.qvnote',
'icon': <Folder color="#FFCC5F" />,
'clicked': this.props.allNotesTapped || this.allNotesTapped 'clicked': this.props.allNotesTapped || this.allNotesTapped
}, },
@ -87,12 +88,44 @@ export default class LibraryNav extends React.Component {
notebooks: [ notebooks: [
] ]
} }
this.loadDefaultNotebooks()
this.getNotebooks() this.getNotebooks()
} }
loadDefaultNotebooks = () => {
var notebooks = this.state.navItems
for(var i=0; i<notebooks.length; i++){
var nb = notebooks[i]
if(nb.isNotebook){
var temp = {
title: nb.name,
uuid: nb.name,
notes: 0
}
this.initDefaultNotebookPath(temp)
var loaded = utils.loadNotebookByName(nb.name)
nb.title = loaded.title
nb.uuid = loaded.uuid
nb.path = loaded.path
nb.notes = loaded.notes
}
else if(nb.glob){
var dataPath = utils.getAppDataPath()
var notes = glob.sync(path.join(dataPath, nb.glob))
nb.title = nb.name
nb.uuid = nb.name
nb.notes = notes.length
}
}
};
getNotebooks = () => { getNotebooks = () => {
var dataPath = utils.getAppDataPath() var dataPath = utils.getAppDataPath()
var notebooks = glob.sync(path.join(dataPath, '*.qvnotebook')) var notebooks = glob.sync(path.join(dataPath, '!(Entries|Trash).qvnotebook'))
for(var i=0; i<notebooks.length; i++){ for(var i=0; i<notebooks.length; i++){
var nbFile = notebooks[i] var nbFile = notebooks[i]
var obj = jsfile.readFileSync(path.join(nbFile, 'meta.json')) var obj = jsfile.readFileSync(path.join(nbFile, 'meta.json'))
@ -187,6 +220,39 @@ export default class LibraryNav extends React.Component {
}; };
initDefaultNotebookPath = (notebook) => {
var nbPath = utils.getNotebookPath(notebook)
var dir = mkdirp.sync(nbPath)
var notePath = utils.getNotebookPath(notebook)
var meta = {
'name': notebook.title,
'uuid': notebook.uuid
}
var metaPath = path.join(nbPath, 'meta.json')
var t = jsfile.writeFileSync(metaPath, meta)
};
createNotebookPath = (notebook, callback) => {
mkdirp(notebook.path, (err) => {
if(err){
console.log('There was an error creating the directory '+notebook.path)
console.log(err)
}
else{
var nbs = this.state.notebooks
nbs.splice(0, 0, notebook)
this.setState({notebooks: nbs}, () => {
this.createNotebookMeta(notebook, callback)
})
}
})
};
createNewNotebook = (callback) => { createNewNotebook = (callback) => {
var nbUuid = uuid.v4().toUpperCase() var nbUuid = uuid.v4().toUpperCase()
@ -200,23 +266,7 @@ export default class LibraryNav extends React.Component {
'notes': 0 'notes': 0
} }
mkdirp(nbPath, (err) => { this.createNotebookPath(notebook, callback)
if(err){
console.log('There was an error creating the directory '+notePath)
console.log(err)
}
else{
var nbs = this.state.notebooks
nbs.splice(0, 0, notebook)
this.setState({notebooks: nbs}, () => {
if(this.refs['textField0']){
this.refs['textField0'].focus()
}
this.createNotebookMeta(notebook, callback)
})
}
})
}; };
createNotebookMeta = (notebook, callback) => { createNotebookMeta = (notebook, callback) => {
@ -230,7 +280,7 @@ export default class LibraryNav extends React.Component {
if(err){ if(err){
console.log(err) console.log(err)
} }
if(callback){ if(utils.isFunction(callback)){
callback(notebook, err) callback(notebook, err)
} }
}) })
@ -258,6 +308,14 @@ export default class LibraryNav extends React.Component {
}; };
addNotebookTapped = (callback) => { addNotebookTapped = (callback) => {
if(!utils.isFunction(callback)){
callback = () => {
if(this.refs['textField0']){
this.refs['textField0'].focus()
}
}
}
this.createNewNotebook(callback) this.createNewNotebook(callback)
}; };
@ -274,6 +332,15 @@ export default class LibraryNav extends React.Component {
//Right click //Right click
type = 'rightClick' type = 'rightClick'
} }
if (item.isNotebook){
var notebook = utils.loadNotebookByName(item.name)
this.props.updateSelection(notebook)
}
else if(item.glob){
this.props.updateSelection(item)
}
item.clicked(i, item, type, ev) item.clicked(i, item, type, ev)
}; };
@ -295,7 +362,7 @@ export default class LibraryNav extends React.Component {
console.log(err) console.log(err)
} }
this.setState({notebooks: nbs}, ()=>{ this.setState({notebooks: nbs}, ()=>{
if(callback){ if(utils.isFunction(callback)){
callback(nb, err) callback(nb, err)
} }
}) })
@ -331,7 +398,11 @@ export default class LibraryNav extends React.Component {
this.props.updateContextMenu(this.contextMenuItems(i)) this.props.updateContextMenu(this.contextMenuItems(i))
this.props.openContextMenu(x, y) this.props.openContextMenu(x, y)
} }
else{
this.props.updateSelection(this.state.notebooks[i])
}
this.refs.mainList.setIndex(-1) this.refs.mainList.setIndex(-1)
}; };
preventEventProp = (ev) => { preventEventProp = (ev) => {
@ -343,7 +414,6 @@ export default class LibraryNav extends React.Component {
{this.state.notebooks.map((notebook, i) =>{ {this.state.notebooks.map((notebook, i) =>{
var l = null var l = null
if (notebook.state == 'editing'){ if (notebook.state == 'editing'){
l = <ListItem l = <ListItem
key={notebook.uuid || i} key={notebook.uuid || i}
@ -447,6 +517,7 @@ export default class LibraryNav extends React.Component {
} }
LibraryNav.defaultProps = { LibraryNav.defaultProps = {
closeContextMenu: () => {} closeContextMenu: () => {},
updateSelection: () => {}
}; };

View file

@ -12,6 +12,6 @@ describe('EntrySelector', () => {
var entrySelector = TestUtils.renderIntoDocument( var entrySelector = TestUtils.renderIntoDocument(
<EntrySelector id="entry-selector" className="left inline fill-height" /> <EntrySelector id="entry-selector" className="left inline fill-height" />
) )
expect(entrySelector.state.entries.length).toEqual(0) expect(entrySelector.state.notes.length).toEqual(0)
}) })
}) })

View file

@ -0,0 +1 @@
export const UPDATE_SELECTION = 'UPDATE_SELECTION'

View file

@ -3,6 +3,7 @@ import getMuiTheme from 'material-ui/lib/styles/getMuiTheme'
import Styles from 'material-ui/lib/styles' import Styles from 'material-ui/lib/styles'
import mui from 'material-ui' import mui from 'material-ui'
import * as ContextMenuActions from '../actions/contextMenu' import * as ContextMenuActions from '../actions/contextMenu'
import * as NavigationActions from '../actions/navigation'
import { bindActionCreators } from 'redux' import { bindActionCreators } from 'redux'
import { connect } from 'react-redux' import { connect } from 'react-redux'
@ -51,7 +52,13 @@ class App extends React.Component {
render() { render() {
const { contextMenu, contextMenuActions } = this.props const {
contextMenu,
contextMenuActions,
navigation,
navigationActions
} = this.props
return ( return (
<div className="fill-height"> <div className="fill-height">
<div style={{position: 'absolute', <div style={{position: 'absolute',
@ -77,11 +84,15 @@ class App extends React.Component {
ref="libraryNav" ref="libraryNav"
entriesTapped={this.entriesTapped} entriesTapped={this.entriesTapped}
className="left inline fill-height" className="left inline fill-height"
navigation={navigation}
{...navigationActions}
{...contextMenuActions} {...contextMenuActions}
/> />
<EntrySelector <EntrySelector
id="entry-selector" id="entry-selector"
className="left inline fill-height" className="left inline fill-height"
navigation={navigation}
{...navigationActions}
/> />
</div> </div>
@ -91,18 +102,22 @@ class App extends React.Component {
App.propTypes = { App.propTypes = {
contextMenu: React.PropTypes.object.isRequired, contextMenu: React.PropTypes.object.isRequired,
contextMenuActions: React.PropTypes.object.isRequired contextMenuActions: React.PropTypes.object.isRequired,
navigation: React.PropTypes.object.isRequired,
navigationActions: React.PropTypes.object.isRequired
} }
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
contextMenu: state.contextMenu contextMenu: state.contextMenu,
navigation: state.navigation
} }
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return { return {
contextMenuActions: bindActionCreators(ContextMenuActions, dispatch) contextMenuActions: bindActionCreators(ContextMenuActions, dispatch),
navigationActions: bindActionCreators(NavigationActions, dispatch)
} }
} }

View file

@ -1,13 +1,12 @@
import { UPDATE_CONTEXT_MENU, OPEN_CONTEXT_MENU, CLOSE_CONTEXT_MENU } from '../constants/contextMenu' import { UPDATE_CONTEXT_MENU, OPEN_CONTEXT_MENU, CLOSE_CONTEXT_MENU } from '../constants/contextMenu'
const initialState = const initialState = {
{
opened: false, opened: false,
x: 0, x: 0,
y: 0, y: 0,
items: [] items: []
} }
export default function contextMenu(state = initialState, action){ export default function contextMenu(state = initialState, action){
switch (action.type) { switch (action.type) {

View file

@ -1,8 +1,10 @@
import { combineReducers } from 'redux' import { combineReducers } from 'redux'
import contextMenu from './contextMenu' import contextMenu from './contextMenu'
import navigation from './navigation'
const rootReducer = combineReducers({ const rootReducer = combineReducers({
contextMenu contextMenu,
navigation
}) })
export default rootReducer export default rootReducer

View file

@ -0,0 +1,18 @@
import { UPDATE_SELECTION } from '../constants/navigation'
const initialState =
{
selection: {
}
}
export default function navigation(state = initialState, action){
switch (action.type) {
case UPDATE_SELECTION:
return Object.assign({}, state, {
selection: action.selection
})
default:
return state
}
}

View file

@ -51,3 +51,8 @@ export function loadNotebookByName(nameOrUUID){
export function getNotebookPathFromUUID(uuid){ export function getNotebookPathFromUUID(uuid){
return getNotebookPath({uuid: uuid}) return getNotebookPath({uuid: uuid})
} }
export function isFunction(functionToCheck) {
var getType = {}
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'
}