From 7684e53c47b0dda07569fe10dfd7a630e6c26f33 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Fri, 11 Mar 2016 15:21:50 -0700 Subject: [PATCH] Add ability to add notes to notebooks Notes are added to the selected notebook on the left and they appropriately update the numbers next to the labels. Notes will also load based on notebook/menu item selection. --- app/components/EntrySelector.jsx | 194 ++++++++++++++++++++++++++++--- app/components/LibraryNav.jsx | 167 +++++++++++++------------- 2 files changed, 263 insertions(+), 98 deletions(-) diff --git a/app/components/EntrySelector.jsx b/app/components/EntrySelector.jsx index e1a3fd4..4a46806 100644 --- a/app/components/EntrySelector.jsx +++ b/app/components/EntrySelector.jsx @@ -7,6 +7,7 @@ import List from 'material-ui/lib/lists/list' import ListItem from 'material-ui/lib/lists/list-item' import Description from 'material-ui/lib/svg-icons/action/description' import Add from 'material-ui/lib/svg-icons/content/add' +import Tag from 'material-ui/lib/svg-icons/maps/local-offer' import SearchBar from 'SearchBar' import SelectableList from 'SelectableList' @@ -16,13 +17,19 @@ import uuid from 'node-uuid' import path from 'path-extra' import * as utils from 'utils' -import glob from 'glob' +import glob from 'glob' +import moment from 'moment' import fs from 'fs' import mkdirp from 'mkdirp' import jsfile from 'jsonfile' import rmdir from 'rimraf' +import { + NOTEBOOK_TYPE, + MENU_TYPE +} from '../constants/navigation' + const { IconButton, AutoComplete, @@ -37,11 +44,20 @@ const DefaultRawTheme = Styles.LightRawTheme export default class EntrySelector extends React.Component { - constructor(props){ - super(props) + constructor(props, context){ + super(props, context) this.state = {notes: []} + this.loadNotes() + const { store } = this.context + store.subscribe(this.stateChanged) } + stateChanged = () => { + const { store } = this.context + var selection = store.getState().navigation.selection + this.reloadNotes(selection) + }; + static get childContextTypes(){ return {muiTheme: React.PropTypes.object} } @@ -52,18 +68,146 @@ export default class EntrySelector extends React.Component { } } + loadNotes = () => { + var notebook = this.props.navigation.selection + var notes = utils.loadNotes(notebook) + this.state.notes = notes + } + + reloadNotes = (selection) => { + var notebook = selection || this.props.navigation.selection + + var notes = utils.loadNotes(notebook) + this.setState({notes: notes}) + }; + blank(){ } - + + createNotePath = (note, callback) => { + mkdirp(path.join(note.path, 'resources'), (err) => { + if(err){ + console.log('There was an error creating the directory '+note.path) + console.log(err) + } + else{ + var notes = this.state.notes + notes.splice(0, 0, note) + + this.setState({notes: notes}, () => { + this.createNoteFiles(note, callback) + }) + } + }) + }; + + createNoteFiles = (note, callback) => { + var noteMeta = { + 'created_at': note.created_at, + 'updated_at': note.updated_at, + 'tags': note.tags, + 'title': note.title, + 'uuid': note.uuid + } + + var content = { + 'title': note.title, + 'cells': [] + } + + var metaPath = path.join(note.path, 'meta.json') + var contentPath = path.join(note.path, 'content.json') + + jsfile.writeFile(metaPath, noteMeta, (err) => { + if(err){ + console.log(err) + } + + jsfile.writeFile(contentPath, content, (err) => { + if(err){ + console.log(err) + } + + if(utils.isFunction(callback)){ + callback(note, err) + } + }) + }) + + }; + + createNewNote = (callback) => { + var notebook = this.props.navigation.selection + var noteUUID = uuid.v4().toUpperCase() + var notePath = utils.getNotePathFromUUID(notebook, noteUUID) + + var noteCreation = moment.utc().valueOf()/1000 + var noteUpdated = noteCreation + + var note = { + 'created_at': noteCreation, + 'updated_at': noteUpdated, + 'tags': [], + 'title': '', + 'path': notePath, + 'uuid': noteUUID, + 'summary': '', + 'notebook': notebook + } + + this.createNotePath(note, callback) + + }; + addNoteTapped = () => { - console.log(this.props.navigation) + this.createNewNote((note, err)=>{ + this.props.refreshNavigation() + }) + }; + + renderNoteTags = (note) => { + if (note.tags.length > 0){ + return ( +
+ + + +
+ {utils.trunc(note.tags.join(", "), 30)} +
+
+ ) + } + }; + + renderNoteInfo = (note) => { + let noteInfoStyle = { + position: "absolute", + top: 70, + left: 20, + color: colors.grey400, + fontSize: 14 + } + + return (
+
+ {moment.unix(note.created_at).format('MMM D, YYYY')} +
+ {this.renderNoteTags(note)} +
) }; render(){ return ( - } - primaryText="Today's Notes" - secondaryText={ -

- I did things -- - I did so many things today, it's not even funny. You think it's funny? - Well it's not! -

- } - secondaryTextLines={2} - /> + + {this.state.notes.map((note, i) =>{ + return (} + innerDivStyle={{paddingBottom: 40}} + style={{borderBottom: '1px solid #F1F1F1'}} + secondaryText={ +

+ {note.summary} +

+ } + secondaryTextLines={2} + > +
+ {note.title || "Untitled Note"} +
+ + {this.renderNoteInfo(note)} + +
+ ) + }) + }
) } } +EntrySelector.contextTypes = { + store: React.PropTypes.object +} diff --git a/app/components/LibraryNav.jsx b/app/components/LibraryNav.jsx index efb77a6..4d8e03e 100644 --- a/app/components/LibraryNav.jsx +++ b/app/components/LibraryNav.jsx @@ -23,7 +23,6 @@ import path from 'path-extra' import * as utils from 'utils' import glob from 'glob' -import fs from 'fs' import mkdirp from 'mkdirp' import jsfile from 'jsonfile' import rmdir from 'rimraf' @@ -51,49 +50,51 @@ export default class LibraryNav extends React.Component { super(props, context) this.state = { open: true, - navItems: [ - { - 'name': 'Entries', - 'isNotebook': true, - 'icon': , - 'clicked': this.props.entriesTapped || this.entriesTapped - }, - { - 'name': 'Starred', - 'notes': 0, - 'icon': , - 'clicked': this.props.starredTapped || this.starredTapped - }, - { - 'name': 'Recents', - 'notes': 0, - 'icon': , - 'clicked': this.props.recentsTapped || this.recentsTapped - }, - { - 'name': 'Trash', - 'isNotebook': true, - 'icon': , - 'clicked': this.props.trashTapped || this.trashTapped - }, - { - 'name': 'All Notes', - 'notes': 0, - 'glob': '*.qvnotebook/*.qvnote', - 'icon': , - 'clicked': this.props.allNotesTapped || this.allNotesTapped - }, - - ], - notebooks: [ - ] } + this.navItems = [ + { + 'name': 'Entries', + 'isNotebook': true, + 'icon': , + 'clicked': this.props.entriesTapped || this.entriesTapped + }, + { + 'name': 'Starred', + 'notes': 0, + 'icon': , + 'clicked': this.props.starredTapped || this.starredTapped + }, + { + 'name': 'Recents', + 'notes': 0, + 'icon': , + 'clicked': this.props.recentsTapped || this.recentsTapped + }, + { + 'name': 'Trash', + 'isNotebook': true, + 'icon': , + 'clicked': this.props.trashTapped || this.trashTapped + }, + { + 'name': 'All Notes', + 'notes': 0, + 'glob': '*.qvnotebook/*.qvnote', + 'icon': , + 'clicked': this.props.allNotesTapped || this.allNotesTapped + } + + ] + this.loadDefaultNotebooks() this.getNotebooks() + + const { store } = this.context + store.subscribe(this.stateChanged) } loadDefaultNotebooks = () => { - var notebooks = this.state.navItems + var notebooks = this.navItems for(var i=0; i { - let atitle = a.title.toLowerCase() - let btitle = b.title.toLowerCase() - - if(atitle > btitle) - return 1 - if(atitle < btitle) - return -1 - return 0 - }; - - sortNotebooks = (dontSet, func) => { - this.state.notebooks.sort(this.compareNotebooks) - if (!dontSet){ - this.setState({notebooks: this.state.notebooks}, func) + this.props.addNotebook(nb) } + this.props.sortNotebooks(utils.compareNotebooks) }; static get childContextTypes(){ @@ -203,14 +190,17 @@ export default class LibraryNav extends React.Component { scrollToRenamedNotebook = (original) => { var newIndex = 0 - for (var j = 0; j < this.state.notebooks.length; j++){ - var n = this.state.notebooks[j] + for (var j = 0; j < this.props.navigation.notebooks.length; j++){ + var n = this.props.navigation.notebooks[j] if (n == original){ newIndex = j this.refs.notebookList.setIndex(j, () => { var nbitem = $(ReactDOM.findDOMNode(this.refs[n.title+newIndex])) var cont = $('#notebook-list') - var newPos = nbitem.offset().top - cont.offset().top + cont.scrollTop() - cont.height()/2; + var newPos = 0 + if(typeof nbitem.offset() != 'undefined'){ + newPos = nbitem.offset().top - cont.offset().top + cont.scrollTop() - cont.height()/2; + } $('#notebook-list').animate({ scrollTop: newPos }) @@ -243,12 +233,8 @@ export default class LibraryNav extends React.Component { console.log(err) } else{ - var nbs = this.state.notebooks - nbs.splice(0, 0, notebook) - - this.setState({notebooks: nbs}, () => { - this.createNotebookMeta(notebook, callback) - }) + this.props.addNotebook(notebook) + this.createNotebookMeta(notebook, callback) } }) @@ -288,22 +274,24 @@ export default class LibraryNav extends React.Component { }; newNotebookUnfocus = (i) => { - var nb = this.state.notebooks[i] + var nb = this.props.navigation.notebooks[i] var tf = this.refs["textField"+i] var notebookName = tf.getValue() if (notebookName){ + this.refs.mainList.setIndex(-1) nb.title = notebookName nb.state = 'displaying' - this.setState({notebooks: this.state.notebooks}) - this.sortNotebooks(false, ()=>{ - this.scrollToRenamedNotebook(nb) + this.props.updateNotebook(nb, i, () => { + this.props.sortNotebooks(this.compareNotebooks, ()=>{ + this.scrollToRenamedNotebook(nb) + }) }) this.createNotebookMeta(nb) } else if(nb.title){ nb.state = 'displaying' - this.setState({notebooks: this.state.notebooks}) + this.props.updateNotebook(nb, i) } }; @@ -325,7 +313,7 @@ export default class LibraryNav extends React.Component { menuItemClicked = (i, ev) => { this.refs.notebookList.setIndex(-1) - var item = this.state.navItems[i] + var item = this.props.navigation.menuItems[i] var type = 'leftClick' var nativeEvent = ev.nativeEvent if(nativeEvent.button == 2){ @@ -335,6 +323,7 @@ export default class LibraryNav extends React.Component { if (item.isNotebook){ var notebook = utils.loadNotebookByName(item.name) + notebook = utils.updateObject(item, notebook) this.props.updateSelection(notebook) } else if(item.glob){ @@ -345,23 +334,23 @@ export default class LibraryNav extends React.Component { }; renameTapped = (i) => { - var nbs = this.state.notebooks + var nbs = this.props.navigation.notebooks nbs[i].state = 'editing' - this.setState({notebooks: nbs}, () => { + this.props.updateNotebook(nbs[i], i, () => { this.refs['textField'+i].focus() }) this.props.closeContextMenu() } deleteTapped = (i, callback) => { - var nbs = this.state.notebooks - var nb = nbs.splice(i, 1)[0] + var nbs = this.props.navigation.notebooks + var nb = nbs.slice(i, 1)[0] rmdir(nb.path, (err)=>{ if(err){ console.log(err) } - this.setState({notebooks: nbs}, ()=>{ + this.props.removeNotebook(i, () =>{ if(utils.isFunction(callback)){ callback(nb, err) } @@ -399,7 +388,7 @@ export default class LibraryNav extends React.Component { this.props.openContextMenu(x, y) } else{ - this.props.updateSelection(this.state.notebooks[i]) + this.props.updateSelection(this.props.navigation.notebooks[i]) } this.refs.mainList.setIndex(-1) @@ -412,7 +401,7 @@ export default class LibraryNav extends React.Component { notebookList = () => { return (
- {this.state.notebooks.map((notebook, i) =>{ + {this.props.navigation.notebooks.map((notebook, i) =>{ var l = null if (notebook.state == 'editing'){ l = @@ -470,16 +461,26 @@ export default class LibraryNav extends React.Component { ev.stopPropagation() }; + stateChanged = () => { + const { store } = this.context + const state = store.getState() + if(utils.isFunction(state.navigation.callback)){ + state.navigation.callback(state) + } + }; + render(){ + return (
- {this.state.navItems.map((item, i) => { + {this.props.navigation.menuItems.map((item, i) => { return {}, updateSelection: () => {}