From 14b0e6137375d618b77bb45e91846e45ee15251a Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Fri, 18 Mar 2016 07:29:54 -0600 Subject: [PATCH] Add fluid loading animation when notes are loading --- app/components/EntryLoader.jsx | 111 +++++++++++++++++++++++++++++++ app/components/EntrySelector.jsx | 74 ++++++++++++--------- package.json | 1 + style.css | 32 +++++++++ 4 files changed, 185 insertions(+), 33 deletions(-) create mode 100644 app/components/EntryLoader.jsx diff --git a/app/components/EntryLoader.jsx b/app/components/EntryLoader.jsx new file mode 100644 index 0000000..318ee6d --- /dev/null +++ b/app/components/EntryLoader.jsx @@ -0,0 +1,111 @@ +import React from 'react' +import ReactDOM from 'react-dom' + +import mui from 'material-ui' +import getMuiTheme from 'material-ui/lib/styles/getMuiTheme' + +import RefreshIndicator from 'material-ui/lib/refresh-indicator' +import ReactCSSTransitionGroup from 'react-addons-css-transition-group' + +const style = { + container: { + position: 'relative', + }, + refresh: { + display: 'inline-block', + position: 'relative', + }, +} + +const { + Styles +} = mui + +const Colors = Styles.Colors + +const DefaultRawTheme = Styles.LightRawTheme + +export default class EntryLoader extends React.Component { + constructor(props, context){ + super(props, context) + this.state = {loaded: false} + } + + static get childContextTypes(){ + return {muiTheme: React.PropTypes.object} + } + + getChildContext() { + return { + muiTheme: this.context.muiTheme || getMuiTheme(DefaultRawTheme) + } + } + + componentDidMount() { + this.updateState(this.props) + } + + componentWillReceiveProps(nextProps) { + this.updateState(nextProps) + } + + updateState = (props) => { + props || (props = {}) + + var loaded = this.state.loaded + + // update loaded state, if supplied + if ('loaded' in props) { + loaded = !!props.loaded + } + this.setState({loaded: loaded}) + }; + + getContent = () => { + return this.props.children + } + + getLoader = () => { + if(!this.state.loaded){ + return ( +
+
+
+ +
+
+
+ ) + + } + else{ + return (
) + } + }; + + render(){ + return ( +
+ + {this.getLoader()} + + {this.getContent()} +
+ ) + } + +} diff --git a/app/components/EntrySelector.jsx b/app/components/EntrySelector.jsx index e6a2ef0..c358287 100644 --- a/app/components/EntrySelector.jsx +++ b/app/components/EntrySelector.jsx @@ -12,6 +12,7 @@ import Tag from 'material-ui/lib/svg-icons/maps/local-offer' import SearchBar from 'SearchBar' import SelectableList from 'SelectableList' import Item from 'Item' +import EntryLoader from 'EntryLoader' import uuid from 'node-uuid' import path from 'path-extra' @@ -44,7 +45,7 @@ export default class EntrySelector extends React.Component { constructor(props, context){ super(props, context) - this.state = {notes: []} + this.state = {notes: [], loaded: false} this.loadNotes() const { store } = this.context store.subscribe(this.stateChanged) @@ -75,18 +76,19 @@ export default class EntrySelector extends React.Component { loadNotes = () => { var notebook = this.props.navigation.selection if(!utils.isEmpty(notebook)){ - var notes = utils.loadNotes(notebook) - notes.sort(utils.compareNotes()) - this.state.notes = notes + var notes = utils.loadNotesAsync(notebook, (notes)=>{ + notes.sort(utils.compareNotes()) + this.setState({notes: notes, loaded: true}) + }) } } reloadNotes = (selection) => { - this.setState({notes: []}, ()=>{ + this.setState({loaded: false}, ()=>{ var notebook = selection || this.props.navigation.selection utils.loadNotesAsync(notebook, (notes) => { notes.sort(utils.compareNotes()) - this.setState({notes: notes}) + this.setState({notes: notes, loaded: true}) }) }) }; @@ -168,7 +170,7 @@ export default class EntrySelector extends React.Component { addNoteTapped = () => { this.createNewNote((note, err)=>{ - this.props.refreshNavigation() + this.props.noteAdded() }) }; @@ -209,6 +211,32 @@ export default class EntrySelector extends React.Component { ) }; + renderNotes = () => { + return 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)} + +
+ ) + }) + }; + render(){ return ( @@ -230,32 +258,12 @@ export default class EntrySelector extends React.Component { - - {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)} - -
- ) - }) - } + selectedItemStyle={{backgroundColor: Colors.grey100}}> + + {this.renderNotes()} +
) diff --git a/package.json b/package.json index fc8730b..63d3b8c 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "node-uuid": "^1.4.7", "path-extra": "^3.0.0", "react": "^0.14.7", + "react-addons-css-transition-group": "^0.14.7", "react-dom": "^0.14.7", "react-quill": "^0.4.1", "react-redux": "^4.4.0", diff --git a/style.css b/style.css index ee3151e..97dbe1c 100644 --- a/style.css +++ b/style.css @@ -17,6 +17,38 @@ body, html{ padding-bottom: 0px !important; } +.entry-loader-enter { + opacity: 0.01; +} + +.entry-loader-enter.entry-loader-enter-active { + opacity: 1; + transition: opacity 200ms ease-in; +} + +.entry-loader-leave { + opacity: 1; +} + +.entry-loader-leave.entry-loader-leave-active { + opacity: 0.01; + transition: opacity 200ms ease-in; +} + +#entry-list .loader{ + height: 100% !important; + position: fixed; + width: 338px; + background-color: rgba(0,0,0, 0.1); + z-index: 100; +} + +#entry-list .loader .spinner{ + position: relative !important; + top: -webkit-calc(50% - 50px); + left: -webkit-calc(50% - 25px); +} + .list { background-color: #EFEFEF !important; }