Skip to content
This repository was archived by the owner on Jun 28, 2021. It is now read-only.

Commit

Permalink
End of surah loading (#289)
Browse files Browse the repository at this point in the history
* Add lazyload component

* Better lazy load

* pass tests
  • Loading branch information
mmahalwy committed May 1, 2016
1 parent c142837 commit 55baf76
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 72 deletions.
4 changes: 2 additions & 2 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ module.exports = function(config) {

plugins:[
new webpack.DefinePlugin({
__CLIENT__: false,
__SERVER__: true,
__CLIENT__: true,
__SERVER__: false,
__DEVELOPMENT__: true,
__DEVTOOLS__: false // <-------- DISABLE redux-devtools HERE
})
Expand Down
51 changes: 51 additions & 0 deletions src/components/LazyLoad/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';

import debug from '../../helpers/debug';

export default class LazyLoad extends Component {
static propTypes = {
isLoading: PropTypes.bool.isRequired,
isEnd: PropTypes.bool.isRequired,
isLoaded: PropTypes.bool,
onLazyLoad: PropTypes.func.isRequired,
loadingComponent: PropTypes.any,
endComponent: PropTypes.any,
offset: PropTypes.number
}

static defaultProps = {
loadingComponent: 'Loading...',
endComponent: 'End.',
offset: 1000
}

componentDidMount() {
if (__CLIENT__) {
window.removeEventListener('scroll', this.onScroll, true);
window.addEventListener('scroll', this.onScroll, true);
}
}

onScroll = () => {
const { isLoading, isEnd, offset } = this.props;
const dom = ReactDOM.findDOMNode(this);

if ((!isLoading && !isEnd) && (dom.offsetParent || dom).offsetTop - (window.pageYOffset + window.innerHeight) < offset) {
debug('component:LazyLoad', 'onLazyLoad called');
return this.props.onLazyLoad();
}

return false;
}

render() {
const { isEnd, loadingComponent, endComponent } = this.props;

if (isEnd) {
return endComponent;
}

return loadingComponent;
}
}
58 changes: 58 additions & 0 deletions src/components/LazyLoad/spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { mount } from 'enzyme';

import LazyLoad from './index';

let wrapper;
let onLazyLoad;
let makeComponent;

describe('<LazyLoad />', () => {
beforeEach(() => {
makeComponent = (isEnd = false, isLoading = false) => {
onLazyLoad = sinon.stub();
wrapper = mount(
<LazyLoad
onLazyLoad={onLazyLoad}
isEnd={isEnd}
isLoading={isLoading}
endComponent={<p>End</p>}
loadingComponent={<p>Loading</p>}
/>
);
};

makeComponent();
});

it('should render', () => {
expect(wrapper).to.be.ok;
});

it('should show loading component', () => {
expect(wrapper.text()).to.eql('Loading');
});

it('should show end component when no more lazy loading needed', () => {
makeComponent(true);
expect(wrapper.text()).to.eql('End');
});

it('should call onLazyLoad when not end and not loading', () => {
wrapper.instance().onScroll();
expect(wrapper.props().onLazyLoad).to.have.been.called;
});

it('should not call onLazyLoad when at end', () => {
makeComponent(true);
wrapper.instance().onScroll();
expect(wrapper.props().onLazyLoad).not.to.have.been.called;
});

it('should not call onLazyLoad when loading', () => {
makeComponent(false, true);
wrapper.instance().onScroll();
expect(wrapper.props().onLazyLoad).not.to.have.been.called;
});
});
108 changes: 38 additions & 70 deletions src/containers/Surah/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Col from 'react-bootstrap/lib/Col';
import Helmet from 'react-helmet';

// components
import LazyLoad from '../../components/LazyLoad';
import PageBreak from '../../components/PageBreak';
import Audioplayer from '../../components/Audioplayer';
import ContentDropdown from '../../components/ContentDropdown';
Expand Down Expand Up @@ -126,8 +127,6 @@ const ayahRangeSize = 30;
export default class Surah extends Component {
constructor() {
super(...arguments);

this.onScroll = this.onScroll.bind(this);
}

state = {
Expand All @@ -136,31 +135,15 @@ export default class Surah extends Component {

componentDidMount() {
if (__CLIENT__) {
window.removeEventListener('scroll', this.onScroll, true);
window.addEventListener('scroll', this.onScroll, true);
window.removeEventListener('scroll', this.handleNavbar, true);
window.addEventListener('scroll', this.handleNavbar, true);
lastScroll = window.pageYOffset;
}
}

shouldComponentUpdate(nextProps) {
const sameSurahIdRouting = this.props.params.surahId === nextProps.params.surahId;
const lazyLoadFinished = sameSurahIdRouting && (!this.props.isLoaded && nextProps.isLoaded);
const hasReadingModeChange = this.props.options.isReadingMode !== nextProps.options.isReadingMode;
const hasFontSizeChange = this.props.options.fontSize !== nextProps.options.fontSize;
const hasSurahInfoChange = this.props.options.isShowingSurahInfo !== nextProps.options.isShowingSurahInfo;

return (
!sameSurahIdRouting ||
lazyLoadFinished ||
hasReadingModeChange ||
hasFontSizeChange ||
hasSurahInfoChange
);
}

componentWillUnmount() {
if (__CLIENT__) {
window.removeEventListener('scroll', this.onScroll, true);
window.removeEventListener('scroll', this.handleNavbar, true);
}
}

Expand Down Expand Up @@ -224,7 +207,7 @@ export default class Surah extends Component {
return setOptionDispatch(payload);
}

handleNavbar() {
handleNavbar = () => {
// TODO: This should be done with react!
if (window.pageYOffset > lastScroll) {
document.querySelector('nav').classList.add('scroll-up');
Expand Down Expand Up @@ -254,24 +237,6 @@ export default class Surah extends Component {
}, 1000)); // then scroll to it
}

onScroll() {
const { isLoading, isEndOfSurah } = this.props;

this.handleNavbar();

if (isEndOfSurah) {
return false;
}

if (!isLoading && !this.state.lazyLoading && window.pageYOffset > (document.body.scrollHeight - window.innerHeight - 1000)) {
// Reached the end.
this.setState({
lazyLoading: true
});

this.lazyLoadAyahs();
}
}

lazyLoadAyahs(callback) {
const { loadAyahsDispatch, ayahIds, surah, options } = this.props;
Expand All @@ -297,38 +262,41 @@ export default class Surah extends Component {
}

renderPagination() {
const { isEndOfSurah, surah } = this.props;
const { lazyLoading } = this.state;

if (isEndOfSurah && !lazyLoading) {
return (
<ul className="pager">
{
surah.id > 1 &&
<li className="previous">
<Link to={`/${surah.id * 1 - 1}`}>
&larr; Previous Surah
</Link>
</li>
}
<li className="text-center">
<Link to={`/${surah.id}`}>
Beginning of Surah
</Link>
</li>
{
surah.id < 114 &&
<li className="next">
<Link to={`/${surah.id * 1 + 1}`}>
Next Surah &rarr;
const { isLoading, isEndOfSurah, surah } = this.props;

return (
<LazyLoad
onLazyLoad={this.lazyLoadAyahs.bind(this)}
isEnd={isEndOfSurah && !isLoading}
isLoading={isLoading}
endComponent={
<ul className="pager">
{
surah.id > 1 &&
<li className="previous">
<Link to={`/${surah.id * 1 - 1}`}>
&larr; Previous Surah
</Link>
</li>
}
<li className="text-center">
<Link to={`/${surah.id}`}>
Beginning of Surah
</Link>
</li>
}
</ul>
);
}

return <p>Loading...</p>;
{
surah.id < 114 &&
<li className="next">
<Link to={`/${surah.id * 1 + 1}`}>
Next Surah &rarr;
</Link>
</li>
}
</ul>
}
loadingComponent={<p>Loading...</p>}
/>
);
}

renderAyahs() {
Expand Down
1 change: 1 addition & 0 deletions src/redux/modules/ayahs.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const initialState = {
current: null,
errored: false,
loaded: false,
loading: false,
entities: {},
result: [],
fontFaces: []
Expand Down

0 comments on commit 55baf76

Please sign in to comment.