Add fluid loading animation when notes are loading

This commit is contained in:
Joey Payne 2016-03-18 07:29:54 -06:00
commit 14b0e61373
4 changed files with 185 additions and 33 deletions

View file

@ -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 (
<div key="loader">
<div className="loader">
<div className="spinner">
<RefreshIndicator
size={50}
left={0}
top={0}
loadingColor={"#FF9800"}
status="loading"
style={style.refresh}
/>
</div>
</div>
</div>
)
}
else{
return (<div key="loaded"></div>)
}
};
render(){
return (
<div
id={this.props.id || "entry-loader"}
style={style.container}
>
<ReactCSSTransitionGroup
transitionName="entry-loader"
transitionLeaveTimeout={200}
transitionEnterTimeout={200}
>
{this.getLoader()}
</ReactCSSTransitionGroup>
{this.getContent()}
</div>
)
}
}

View file

@ -12,6 +12,7 @@ import Tag from 'material-ui/lib/svg-icons/maps/local-offer'
import SearchBar from 'SearchBar' import SearchBar from 'SearchBar'
import SelectableList from 'SelectableList' import SelectableList from 'SelectableList'
import Item from 'Item' import Item from 'Item'
import EntryLoader from 'EntryLoader'
import uuid from 'node-uuid' import uuid from 'node-uuid'
import path from 'path-extra' import path from 'path-extra'
@ -44,7 +45,7 @@ export default class EntrySelector extends React.Component {
constructor(props, context){ constructor(props, context){
super(props, context) super(props, context)
this.state = {notes: []} this.state = {notes: [], loaded: false}
this.loadNotes() this.loadNotes()
const { store } = this.context const { store } = this.context
store.subscribe(this.stateChanged) store.subscribe(this.stateChanged)
@ -75,18 +76,19 @@ export default class EntrySelector extends React.Component {
loadNotes = () => { loadNotes = () => {
var notebook = this.props.navigation.selection var notebook = this.props.navigation.selection
if(!utils.isEmpty(notebook)){ if(!utils.isEmpty(notebook)){
var notes = utils.loadNotes(notebook) var notes = utils.loadNotesAsync(notebook, (notes)=>{
notes.sort(utils.compareNotes()) notes.sort(utils.compareNotes())
this.state.notes = notes this.setState({notes: notes, loaded: true})
})
} }
} }
reloadNotes = (selection) => { reloadNotes = (selection) => {
this.setState({notes: []}, ()=>{ this.setState({loaded: false}, ()=>{
var notebook = selection || this.props.navigation.selection var notebook = selection || this.props.navigation.selection
utils.loadNotesAsync(notebook, (notes) => { utils.loadNotesAsync(notebook, (notes) => {
notes.sort(utils.compareNotes()) 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 = () => { addNoteTapped = () => {
this.createNewNote((note, err)=>{ this.createNewNote((note, err)=>{
this.props.refreshNavigation() this.props.noteAdded()
}) })
}; };
@ -209,6 +211,32 @@ export default class EntrySelector extends React.Component {
</div>) </div>)
}; };
renderNotes = () => {
return this.state.notes.map((note, i) =>{
return (<ListItem
key={i}
value={i}
leftIcon={<Description color={Colors.grey600}/>}
innerDivStyle={{paddingBottom: 40}}
style={{borderBottom: '1px solid #F1F1F1'}}
secondaryText={
<p>
{note.summary}
</p>
}
secondaryTextLines={2}
>
<div>
{note.title || "Untitled Note"}
</div>
{this.renderNoteInfo(note)}
</ListItem>
)
})
};
render(){ render(){
return ( return (
<Paper id={this.props.id} className={this.props.className+ " noselect"} zDepth={0}> <Paper id={this.props.id} className={this.props.className+ " noselect"} zDepth={0}>
@ -230,32 +258,12 @@ export default class EntrySelector extends React.Component {
<SelectableList <SelectableList
id="entry-list" id="entry-list"
ref="entryList" ref="entryList"
selectedItemStyle={{backgroundColor: colors.grey300}}> selectedItemStyle={{backgroundColor: Colors.grey100}}>
<EntryLoader
{this.state.notes.map((note, i) =>{ loaded={this.state.loaded}
return (<ListItem >
key={i} {this.renderNotes()}
value={i} </EntryLoader>
leftIcon={<Description color={colors.grey600}/>}
innerDivStyle={{paddingBottom: 40}}
style={{borderBottom: '1px solid #F1F1F1'}}
secondaryText={
<p>
{note.summary}
</p>
}
secondaryTextLines={2}
>
<div>
{note.title || "Untitled Note"}
</div>
{this.renderNoteInfo(note)}
</ListItem>
)
})
}
</SelectableList> </SelectableList>
</Paper> </Paper>
) )

View file

@ -65,6 +65,7 @@
"node-uuid": "^1.4.7", "node-uuid": "^1.4.7",
"path-extra": "^3.0.0", "path-extra": "^3.0.0",
"react": "^0.14.7", "react": "^0.14.7",
"react-addons-css-transition-group": "^0.14.7",
"react-dom": "^0.14.7", "react-dom": "^0.14.7",
"react-quill": "^0.4.1", "react-quill": "^0.4.1",
"react-redux": "^4.4.0", "react-redux": "^4.4.0",

View file

@ -17,6 +17,38 @@ body, html{
padding-bottom: 0px !important; 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 { .list {
background-color: #EFEFEF !important; background-color: #EFEFEF !important;
} }