diff --git a/src/components/Ayah/index.js b/src/components/Ayah/index.js index a7dcc67a9..bbb7aad26 100644 --- a/src/components/Ayah/index.js +++ b/src/components/Ayah/index.js @@ -22,12 +22,13 @@ export default class Ayah extends Component { isSearch: PropTypes.bool, tooltip: PropTypes.string, currentWord: PropTypes.any, // gets passed in an integer, null by default - onWordClick: PropTypes.func.isRequired + onWordClick: PropTypes.func, + actions: PropTypes.object }; static defaultProps = { currentWord: null, - isSearched: false, + isSearched: false }; shouldComponentUpdate(nextProps) { @@ -44,10 +45,12 @@ export default class Ayah extends Component { return conditions.some(condition => condition); } - handlePlay() { - this.setState({ - open: false - }); + handlePlay(ayah) { + const {stop, setAyah, start} = this.props.actions; + + stop(); + setAyah(ayah); + start(); } renderTranslations() { @@ -140,12 +143,12 @@ export default class Ayah extends Component { } renderPlayLink() { - const { isSearch, ayah } = this.props; + const { isSearched, ayah } = this.props; - if (!isSearch) { + if (!isSearched) { return ( this.handlePlay(ayah.ayahNum)} + onClick={() => this.handlePlay(ayah.ayahKey)} className="text-muted" > Play diff --git a/src/containers/App/index.js b/src/containers/App/index.js index d50098182..b37e74563 100644 --- a/src/containers/App/index.js +++ b/src/containers/App/index.js @@ -17,13 +17,7 @@ import FontStyles from 'components/FontStyles'; const styles = require('./style.scss'); -@metrics(metricsConfig) -@connect( - state => ({ - surahs: state.surahs.entities - }) -) -export default class App extends Component { +class App extends Component { static propTypes = { surahs: PropTypes.object, children: PropTypes.any @@ -107,3 +101,7 @@ export default class App extends Component { ); } } + +const metricsApp = metrics(metricsConfig)(App); + +export default connect(state => ({surahs: state.surahs.entities }))(metricsApp); diff --git a/src/containers/Home/index.js b/src/containers/Home/index.js index 99e13bbed..42cecc777 100644 --- a/src/containers/Home/index.js +++ b/src/containers/Home/index.js @@ -11,18 +11,6 @@ import { isAllLoaded, loadAll } from '../../redux/modules/surahs'; const styles = require('./style.scss'); -@asyncConnect([{ - promise({ store: { getState, dispatch } }) { - if (!isAllLoaded(getState())) { - return dispatch(loadAll()); - } - - return true; - } -}]) -@connect( - state => ({surahs: state.surahs.entities}) -) class Home extends Component { static propTypes = { lastVisit: PropTypes.any, @@ -189,4 +177,15 @@ class Home extends Component { } } -export default Home; +const AsyncHome = asyncConnect([{ + promise({ store: { getState, dispatch } }) { + if (!isAllLoaded(getState())) { + return dispatch(loadAll()); + } + + return true; + } +}])(Home); + +export default connect(state => ({surahs: state.surahs.entities}))(AsyncHome); + diff --git a/src/containers/Search/index.js b/src/containers/Search/index.js index 32a09f376..1af7eb7b6 100644 --- a/src/containers/Search/index.js +++ b/src/containers/Search/index.js @@ -1,5 +1,6 @@ import React, { Component, PropTypes } from 'react'; import { PropTypes as MetricsPropTypes } from 'react-metrics'; + import { asyncConnect } from 'redux-connect'; import { connect } from 'react-redux'; import { push } from 'react-router-redux'; @@ -19,28 +20,7 @@ import { search } from '../../redux/modules/searchResults'; const style = require('./style.scss'); -@asyncConnect([{ - promise({ store: { dispatch }, location: { query } }) { - return dispatch(search(query)); - } -}]) -@connect( - state => ({ - isErrored: state.searchResults.errored, - isLoading: state.searchResults.loading, - total: state.searchResults.total, - page: state.searchResults.page, - size: state.searchResults.size, - from: state.searchResults.from, - took: state.searchResults.took, - query: state.searchResults.query, - results: state.searchResults.results, - ayahs: state.searchResults.entities, - options: state.options - }), - { push } -) -export default class Search extends Component { +class Search extends Component { static propTypes = { isErrored: PropTypes.bool, isLoading: PropTypes.bool, @@ -177,3 +157,27 @@ export default class Search extends Component { ); } } + +const AsyncSearch = asyncConnect([{ + promise({ store: { dispatch }, location: { query } }) { + return dispatch(search(query)); + } +}])(Search); + +function mapStateToProps(state) { + return { + isErrored: state.searchResults.errored, + isLoading: state.searchResults.loading, + total: state.searchResults.total, + page: state.searchResults.page, + size: state.searchResults.size, + from: state.searchResults.from, + took: state.searchResults.took, + query: state.searchResults.query, + results: state.searchResults.results, + ayahs: state.searchResults.entities, + options: state.options + }; +} + +export default connect(mapStateToProps, {push})(AsyncSearch); diff --git a/src/containers/Surah/index.js b/src/containers/Surah/index.js index b600945ab..ee58305da 100644 --- a/src/containers/Surah/index.js +++ b/src/containers/Surah/index.js @@ -1,8 +1,12 @@ import React, { Component, PropTypes } from 'react'; import Link from 'react-router/lib/Link'; +// redux +import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { asyncConnect } from 'redux-connect'; import { push } from 'react-router-redux'; + +// bootstrap import Row from 'react-bootstrap/lib/Row'; import Col from 'react-bootstrap/lib/Col'; import Helmet from 'react-helmet'; @@ -36,64 +40,20 @@ import debug from 'helpers/debug'; import descriptions from './descriptions'; -const style = require('./style.scss'); - - import { surahsConnect, ayahsConnect } from './connect'; -import { - load as loadAyahs, - setCurrentAyah, - clearCurrentWord -} from '../../redux/modules/ayahs'; -import { setCurrentWord } from '../../redux/modules/audioplayer'; +import * as AudioActions from '../../redux/modules/audioplayer'; +import * as AyahActions from '../../redux/modules/ayahs'; +import * as OptionsActions from '../../redux/modules/options'; -import { setOption, toggleReadingMode } from '../../redux/modules/options'; +const style = require('./style.scss'); let lastScroll = 0; -@asyncConnect([ - { - promise: surahsConnect, - }, - { - promise: ayahsConnect - } -]) -@connect( - (state, ownProps) => { - const surahId = parseInt(ownProps.params.surahId, 10); - const surah: Object = state.surahs.entities[surahId]; - const ayahs: Object = state.ayahs.entities[surahId]; - const ayahIds = new Set(Object.keys(ayahs).map(key => parseInt(key.split(':')[1], 10))); - - return { - surah, - ayahs, - ayahIds, - isStarted: state.audioplayer.isStarted, - currentWord: state.ayahs.currentWord, - isEndOfSurah: ayahIds.size === surah.ayat, - surahs: state.surahs.entities, - isLoading: state.ayahs.loading, - isLoaded: state.ayahs.loaded, - lines: state.lines.lines, - options: state.options, - }; - }, - { - loadAyahs, - setOption, - toggleReadingMode, - setCurrentAyah, - setCurrentWord, - clearCurrentWord, - push - } -) -export default class Surah extends Component { +class Surah extends Component { static propTypes = { surah: PropTypes.object.isRequired, + actions: PropTypes.object.isRequired, lines: PropTypes.object.isRequired, isEndOfSurah: PropTypes.bool.isRequired, ayahIds: PropTypes.any, @@ -102,15 +62,8 @@ export default class Surah extends Component { isLoading: PropTypes.bool.isRequired, isLoaded: PropTypes.bool.isRequired, options: PropTypes.object.isRequired, - push: PropTypes.func.isRequired, params: PropTypes.object.isRequired, ayahs: PropTypes.object.isRequired, - loadAyahs: PropTypes.func.isRequired, - setOption: PropTypes.func.isRequired, - toggleReadingMode: PropTypes.func.isRequired, - setCurrentAyah: PropTypes.func.isRequired, - setCurrentWord: PropTypes.func.isRequired, - clearCurrentWord: PropTypes.func.isRequired, isStarted: PropTypes.bool }; @@ -119,13 +72,13 @@ export default class Surah extends Component { }; componentWillMount() { - const { params, surah, push } = this.props; // eslint-disable-line no-shadow + const { params, surah, actions } = this.props; // eslint-disable-line no-shadow if (params.range && params.range.includes('-')) { const start = parseInt(params.range.split('-')[0], 10); if (start > surah.ayat || isNaN(start)) { - return push('/error/invalid-ayah-range'); + return actions.push.push('/error/invalid-ayah-range'); } return false; @@ -178,13 +131,13 @@ export default class Surah extends Component { } handleOptionChange = (payload) => { - const { setOption, loadAyahs, surah, options } = this.props; // eslint-disable-line no-shadow, max-len + const {surah, options, actions} = this.props; // eslint-disable-line no-shadow, max-len const from = this.getFirst(); const to = this.getLast(); - setOption(payload); + actions.options.setOption(payload); - return loadAyahs(surah.id, from, to, Object.assign({}, options, payload)); + return actions.ayah.load(surah.id, from, to, Object.assign({}, options, payload)); } handleNavbar = () => { @@ -201,9 +154,9 @@ export default class Surah extends Component { } handleVerseDropdownClick = (ayahNum) => { - const { ayahIds, push, surah, setCurrentAyah } = this.props; // eslint-disable-line no-shadow + const { ayahIds, surah, actions } = this.props; // eslint-disable-line no-shadow - setCurrentAyah(`${surah.id}:${ayahNum}`); + actions.ayah.setCurrentAyah(`${surah.id}:${ayahNum}`); if (ayahIds.has(ayahNum)) { return false; @@ -211,7 +164,7 @@ export default class Surah extends Component { if (ayahNum > (this.getLast() + 10) || ayahNum < this.getFirst()) { // This is beyond lazy loading next page. - return push(`/${surah.id}/${ayahNum}-${ayahNum + 10}`); + return actions.push.push(`/${surah.id}/${ayahNum}-${ayahNum + 10}`); } return this.handleLazyLoadAyahs(() => setTimeout(() => @@ -220,7 +173,7 @@ export default class Surah extends Component { } handleLazyLoadAyahs = (callback) => { - const { loadAyahs, ayahIds, surah, isEndOfSurah, options } = this.props; // eslint-disable-line no-shadow, max-len + const { ayahIds, surah, isEndOfSurah, options, actions } = this.props; // eslint-disable-line no-shadow, max-len const range = [this.getFirst(), this.getLast()]; let size = 10; @@ -233,7 +186,7 @@ export default class Surah extends Component { const to = (from + size); if (!isEndOfSurah && !ayahIds.has(to)) { - loadAyahs(surah.id, from, to, options).then(() => { + actions.ayah.load(surah.id, from, to, options).then(() => { this.setState({lazyLoading: false}); if (callback) { callback(); @@ -327,13 +280,14 @@ export default class Surah extends Component { } renderAyahs() { - const { ayahs, setCurrentWord, options } = this.props; // eslint-disable-line no-shadow + const { ayahs, actions, options } = this.props; // eslint-disable-line no-shadow return Object.values(ayahs).map(ayah => ( )); @@ -361,10 +315,9 @@ export default class Surah extends Component { renderTopOptions() { const { - toggleReadingMode, // eslint-disable-line no-shadow options, surah, - setOption // eslint-disable-line no-shadow + actions // eslint-disable-line no-shadow } = this.props; return ( @@ -373,7 +326,7 @@ export default class Surah extends Component {
  • @@ -381,21 +334,21 @@ export default class Surah extends Component {
  • |
  • |
  • @@ -407,6 +360,7 @@ export default class Surah extends Component { render() { const { surah, surahs, ayahIds, options } = this.props; + debug('component:Surah', 'Render'); return ( @@ -505,3 +459,39 @@ export default class Surah extends Component { ); } } + +const AsyncSurah = asyncConnect([ { promise: surahsConnect }, { promise: ayahsConnect } ])(Surah); + +function mapStateToProps(state, ownProps) { + const surahId = parseInt(ownProps.params.surahId, 10); + const surah: Object = state.surahs.entities[surahId]; + const ayahs: Object = state.ayahs.entities[surahId]; + const ayahIds = new Set(Object.keys(ayahs).map(key => parseInt(key.split(':')[1], 10))); + + return { + surah, + ayahs, + ayahIds, + isStarted: state.audioplayer.isStarted, + currentWord: state.ayahs.currentWord, + isEndOfSurah: ayahIds.size === surah.ayat, + surahs: state.surahs.entities, + isLoading: state.ayahs.loading, + isLoaded: state.ayahs.loaded, + lines: state.lines.lines, + options: state.options + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: { + options: bindActionCreators(OptionsActions, dispatch), + ayah: bindActionCreators(AyahActions, dispatch), + audio: bindActionCreators(AudioActions, dispatch), + push: bindActionCreators(push, dispatch) + } + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(AsyncSurah); diff --git a/src/redux/modules/audioplayer.js b/src/redux/modules/audioplayer.js index a2d34d1a4..6ef7d598b 100644 --- a/src/redux/modules/audioplayer.js +++ b/src/redux/modules/audioplayer.js @@ -16,6 +16,7 @@ const SET_CURRENT_WORD = '@@quran/audioplayer/SET_CURRENT_WORD'; const START = '@@quran/audioplayer/START'; const STOP = '@@quran/audioplayer/STOP'; export const NEXT = '@@quran/audioplayer/NEXT'; +export const SET_AYAH = '@@quran/audioplayer/SET'; const PREVIOUS = '@@quran/audioplayer/PREVIOUS'; const TOGGLE_REPEAT = '@@quran/audioplayer/TOGGLE_REPEAT'; const TOGGLE_SCROLL = '@@quran/audioplayer/TOGGLE_SCROLL'; @@ -171,6 +172,27 @@ export default function reducer(state = initialState, action = {}) { currentTime: 0 }; } + + case SET_AYAH: { + + const [surahId, ayahNum] = action.currentAyah.split(':'); + const currentAyah = `${surahId}:${parseInt(ayahNum, 10)}`; + + return { + ...state, + segments: { + ...state.segments, + [surahId]: { + ...state.segments[surahId], + [currentAyah]: buildSegments(state.segments[surahId][currentAyah]) + } + }, + currentAyah, + currentFile: state.files[surahId][currentAyah], + currentTime: 0 + }; + } + case PREVIOUS: { const [surahId, ayahNum] = action.currentAyah.split(':'); const nextId = `${surahId}:${parseInt(ayahNum, 10) - 1}`; @@ -289,6 +311,13 @@ export function next(currentAyah) { }; } +export function setAyah(currentAyah) { + return { + type: SET_AYAH, + currentAyah + }; +} + export function previous(currentAyah) { return { type: PREVIOUS,