diff --git a/package.json b/package.json
index 9aa0fb197..44993ad3f 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"cookie-parser": "^1.3.5",
"copy-to-clipboard": "^1.0.4",
"cors": "^2.7.1",
+ "crypto-js": "^3.1.6",
"css-loader": "^0.23.1",
"debug": "^2.2.0",
"dotenv": "^1.2.0",
@@ -97,7 +98,6 @@
"serialize-javascript": "^1.0.0",
"serve-favicon": "^2.2.1",
"sitemap": "^1.5.0",
- "sjcl": "~1.0.3",
"strip-loader": "^0.1.2",
"style-loader": "^0.13.1",
"superagent": "^1.2.0",
diff --git a/src/components/Audioplayer/RepeatButton/index.js b/src/components/Audioplayer/RepeatButton/index.js
new file mode 100644
index 000000000..423e0a0c5
--- /dev/null
+++ b/src/components/Audioplayer/RepeatButton/index.js
@@ -0,0 +1,39 @@
+import React, { PropTypes } from 'react';
+import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
+import Tooltip from 'react-bootstrap/lib/Tooltip';
+
+const style = require('../style.scss');
+
+const RepeatButton = ({ shouldRepeat, onRepeatToggle }) => {
+ const tooltip = (
+
+ Repeats the current ayah on end...
+
+ );
+
+ return (
+
-
+
);
}
diff --git a/src/components/Audioplayer/Track/spec.js b/src/components/Audioplayer/Track/spec.js
new file mode 100644
index 000000000..d4676bba1
--- /dev/null
+++ b/src/components/Audioplayer/Track/spec.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import { mount } from 'enzyme';
+
+import Track from './index';
+
+let component, onTrackChange;
+
+describe('
', () => {
+ beforeEach(() => {
+ onTrackChange = sinon.stub();
+ component = mount(
+
+ );
+ });
+
+ it('should show the progress to be 50%', () => {
+ expect(component.children().first().props().style.width).to.eql('50%');
+ });
+
+ it('should return click progress', () => {
+ component.simulate('click', {nativeEvent: {offsetX: 1}});
+
+ expect(onTrackChange).to.have.been.called;
+ expect(onTrackChange).to.have.been.calledWith(Infinity); // because the bounding box is 0!
+ });
+});
diff --git a/src/components/Audioplayer/Track/style.scss b/src/components/Audioplayer/Track/style.scss
index d6214649e..9ee646119 100644
--- a/src/components/Audioplayer/Track/style.scss
+++ b/src/components/Audioplayer/Track/style.scss
@@ -1,10 +1,30 @@
@import '../../../styles/variables.scss';
:local .track{
- background: $track-bg;
+ background-color: $olive;
display: block;
height: 100%;
width: 100%;
user-select: none;
cursor: pointer;
}
+
+.progress{
+ background: $brand-primary;
+ position: relative;
+ height: 100%;
+
+ &:after{
+ content: '';
+ height: 12px;
+ width: 12px;
+ border-radius: 10px;
+ position: absolute;
+ right: 0px;
+ display: block;
+ background: #fff;
+ top: -5px;
+ box-shadow: 0 1px 2px rgba(0,0,0,0.45);
+ transition: height 0.5s;
+ }
+}
diff --git a/src/components/Audioplayer/index.js b/src/components/Audioplayer/index.js
index 7553db722..e62ca62d4 100644
--- a/src/components/Audioplayer/index.js
+++ b/src/components/Audioplayer/index.js
@@ -1,22 +1,24 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
-import Row from 'react-bootstrap/lib/Row';
-import Col from 'react-bootstrap/lib/Col';
-import { Tooltip, OverlayTrigger } from 'react-bootstrap';
+import { camelize } from 'humps';
// Redux
import {
start,
stop,
+ next,
+ previous,
toggleRepeat,
toggleScroll,
- buildOnClient
+ buildOnClient,
+ update
} from '../../redux/modules/audioplayer';
-import { setCurrentAyah, setCurrentWord } from '../../redux/modules/ayahs';
// Components
import Track from './Track';
import Segments from './Segments';
+import ScrollButton from './ScrollButton';
+import RepeatButton from './RepeatButton';
// Helpers
import debug from '../../helpers/debug';
@@ -25,44 +27,30 @@ import scroller from '../../utils/scroller';
const style = require('./style.scss');
@connect(
- state => ({
- files: state.audioplayer.files,
- segments: state.audioplayer.segments,
- currentAyah: state.ayahs.current,
- currentWord: state.ayahs.currentWord,
+ (state, ownProps) => ({
+ files: state.audioplayer.files[ownProps.surah.id],
+ segments: state.audioplayer.segments[ownProps.surah.id],
+ currentFile: state.audioplayer.currentFile,
+ currentAyah: state.audioplayer.currentAyah,
surahId: state.audioplayer.surahId,
isSupported: state.audioplayer.isSupported,
- isStarted: state.audioplayer.isStarted,
+ isPlaying: state.audioplayer.isPlaying,
isLoadedOnClient: state.audioplayer.isLoadedOnClient,
+ isLoading: state.audioplayer.isLoading,
shouldRepeat: state.audioplayer.shouldRepeat,
- shouldScroll: state.audioplayer.shouldScroll
+ shouldScroll: state.audioplayer.shouldScroll,
+ duration: state.audioplayer.duration,
+ currentTime: state.audioplayer.currentTime,
}),
{
start,
stop,
+ next,
+ previous,
toggleRepeat,
toggleScroll,
- setCurrentAyah,
- setCurrentWord,
- buildOnClient
- },
- (stateProps, dispatchProps, ownProps) => {
- if (!stateProps.isSupported) {
- return {
- ...stateProps, ...dispatchProps, ...ownProps
- };
- }
-
- const files = stateProps.files[stateProps.surahId];
- const ayahIds = files ? Object.keys(files) : [];
- const segments = stateProps.segments[stateProps.surahId];
-
- return {
- ...stateProps, ...dispatchProps, ...ownProps,
- files,
- segments,
- ayahIds
- };
+ buildOnClient,
+ update
}
)
export default class Audioplayer extends Component {
@@ -73,34 +61,31 @@ export default class Audioplayer extends Component {
segments: PropTypes.object,
files: PropTypes.object,
currentAyah: PropTypes.string,
- currentWord: PropTypes.string,
buildOnClient: PropTypes.func.isRequired,
isLoadedOnClient: PropTypes.bool.isRequired,
isSupported: PropTypes.bool.isRequired,
- shouldRepeat: PropTypes.bool.isRequired,
- shouldScroll: PropTypes.bool.isRequired,
- setCurrentAyah: PropTypes.func.isRequired,
- setCurrentWord: PropTypes.func.isRequired,
+ isLoading: PropTypes.bool.isRequired,
start: PropTypes.func.isRequired,
stop: PropTypes.func.isRequired,
+ next: PropTypes.func.isRequired,
+ previous: PropTypes.func.isRequired,
+ update: PropTypes.func.isRequired,
+ shouldRepeat: PropTypes.bool.isRequired,
+ shouldScroll: PropTypes.bool.isRequired,
toggleRepeat: PropTypes.func.isRequired,
toggleScroll: PropTypes.func.isRequired,
- ayahIds: PropTypes.array,
- isStarted: PropTypes.bool
+ isPlaying: PropTypes.bool,
+ currentTime: PropTypes.number,
+ duration: PropTypes.number,
+ currentFile: PropTypes.object
};
static defaultProps = {
className: 'col-md-3'
};
- state = {
- isAudioLoaded: false,
- currentAudio: null,
- currentAyah: null
- };
-
componentDidMount() {
- const { isLoadedOnClient, buildOnClient, surah } = this.props; // eslint-disable-line no-shadow
+ const { isLoadedOnClient, buildOnClient, surah, currentFile } = this.props; // eslint-disable-line no-shadow, max-len
debug('component:Audioplayer', 'componentDidMount');
@@ -110,131 +95,109 @@ export default class Audioplayer extends Component {
return buildOnClient(surah.id);
}
- return false;
- }
-
- componentWillUnmount() {
- debug('component:Audioplayer', 'componentWillUnmount');
-
- return this.stop();
+ return this.handleAddFileListeners(currentFile);
}
- getCurrent() {
- const { currentAyah, ayahIds } = this.props;
- const index = ayahIds.findIndex(id => id === currentAyah);
-
- return ayahIds[index];
- }
+ componentWillReceiveProps(nextProps) {
+ // When you go directly to the surah page, /2, the files are not loaded yet
+ if (this.props.isLoadedOnClient !== nextProps.isLoadedOnClient) {
+ return this.handleAddFileListeners(nextProps.currentFile);
+ }
- getPrevious() {
- // TODO BUGFIX, we should be able to go to the previous ayah even when
- // we started from within a range
- // i.e. lazyloading upwards; as this is defined, if you go to /2/100-110
- // then you can't go to 99 from
- // the previous button
- const { currentAyah, ayahIds } = this.props;
- const index = ayahIds.findIndex(id => id === currentAyah) - 1;
- return ayahIds[index];
- }
+ if (this.props.currentAyah !== nextProps.currentAyah) {
+ this.handleAddFileListeners(nextProps.currentFile);
- getNext() {
- const { currentAyah, ayahIds, onLoadAyahs } = this.props;
- const index = ayahIds.findIndex(id => id === currentAyah) + 1;
+ if (this.props.currentFile) {
+ this.handleRemoveFileListeneres(this.props.currentFile);
+ }
- if ((ayahIds.length - 3) <= index) {
- onLoadAyahs(); // this doesnt look right, should probably be returned or promise.then?
+ return false;
}
- return ayahIds[index];
+ return false;
}
- handlePreviousAyah = () => {
- const { setCurrentAyah, isStarted, shouldScroll } = this.props; // eslint-disable-line no-shadow
- const prevAyah = this.getPrevious();
-
- if (prevAyah) {
- const ayahNum = prevAyah.replace(/^\d+:/, '');
-
- setCurrentAyah(prevAyah);
-
- if (shouldScroll) {
- scroller.scrollTo(`ayah:${ayahNum}`, -150);
- }
-
- if (isStarted) return this.props.files[prevAyah].play();
+ componentWillUnmount() {
+ const { files, currentFile } = this.props;
+ debug('component:Audioplayer', 'componentWillUnmount');
- return false;
+ if (files[currentFile]) {
+ return this.handleRemoveFileListeneres(files[currentFile]);
}
return false;
}
- handleNextAyah = () => {
- const { setCurrentAyah, isStarted, shouldScroll } = this.props; // eslint-disable-line no-shadow
+ getPrevious() {
+ const { currentAyah, files } = this.props;
+ const ayahIds = Object.keys(files);
+ const index = ayahIds.findIndex(id => id === currentAyah);
- const nextAyah = this.getNext();
- if (!nextAyah) return this.stop();
- const ayahNum = nextAyah.replace(/^\d+:/, '');
+ return ayahIds[index - 1];
+ }
- setCurrentAyah(nextAyah);
+ getNext() {
+ const { currentAyah, surah, files, onLoadAyahs } = this.props;
+ const ayahIds = Object.keys(files);
+ const ayahNum = currentAyah.split(':')[1];
+ const index = ayahIds.findIndex(id => id === currentAyah);
- if (shouldScroll) {
- scroller.scrollTo(`ayah:${ayahNum}`, -80);
+ if (surah.ayat === ayahNum + 1) {
+ // We are at the end of the surah!
+ return false;
}
- this.preloadNext();
-
- if (isStarted) return this.props.files[nextAyah].play();
+ if (ayahIds.length - 3 <= index + 1) {
+ // Need to load the next set of ayahs!
+ onLoadAyahs();
+ }
- return false;
+ return ayahIds[index + 1];
}
- scrollTo(name, offset = 0) {
- const node = document.getElementsByName(name)[0];
+ handleAyahChange = (direction = 'next') => {
+ const { isPlaying, start, stop, currentAyah } = this.props; // eslint-disable-line no-shadow, max-len
+ const previouslyPlaying = isPlaying;
- if (!node) return;
+ if (isPlaying) stop();
- const nodeRect = node.getBoundingClientRect();
- const bodyRect = document.body.getBoundingClientRect();
- const scrollOffset = nodeRect.top - bodyRect.top;
+ if (!this[camelize(`get_${direction}`)]()) return stop();
- window.scrollTo(0, scrollOffset + offset);
- }
+ this.props[direction](currentAyah);
- handleStartStopPlayer = (event) => {
- event.preventDefault();
- const { isStarted } = this.props;
+ this.handleScrollTo(currentAyah);
- if (isStarted) {
- return this.stop();
- }
+ this.preloadNext();
- return this.start();
+ if (previouslyPlaying) start();
+
+ return false;
}
- start() {
+ handleScrollTo = (ayahNum = this.props.currentAyah) => {
const { shouldScroll } = this.props;
- const currentAyah = this.getCurrent();
- const ayahNum = currentAyah.replace(/^\d+:/, '');
if (shouldScroll) {
scroller.scrollTo(`ayah:${ayahNum}`, -150);
}
+ }
+
+ start = () => {
+ this.handleScrollTo();
this.props.start();
this.preloadNext();
}
- stop() {
- this.props.stop();
- }
-
preloadNext() {
- const { currentAyah, ayahIds, files } = this.props;
+ const { currentAyah, files } = this.props;
+ const ayahIds = Object.keys(files);
const index = ayahIds.findIndex(id => id === currentAyah) + 1;
- for (let i = index; i <= index + 2; i++) {
- if (ayahIds[i]) {
- const ayahKey = ayahIds[i];
+
+ for (let id = index; id <= index + 2; id++) {
+ if (ayahIds[id]) {
+ const ayahKey = ayahIds[id];
+
if (files[ayahKey]) {
files[ayahKey].setAttribute('preload', 'auto');
}
@@ -242,63 +205,130 @@ export default class Audioplayer extends Component {
}
}
- handleToggleScroll = (event) => {
+ handleScrollToggle = (event) => {
event.preventDefault();
- const { shouldScroll } = this.props;
- const currentAyah = this.getCurrent();
- const ayahNum = currentAyah.replace(/^\d+:/, '');
+ const { shouldScroll, currentAyah } = this.props;
if (!shouldScroll) { // we use the inverse (!) here because we're toggling, so false is true
- const elem = document.getElementsByName(`ayah:${ayahNum}`)[0];
+ const elem = document.getElementsByName(`ayah:${currentAyah}`)[0];
if (elem && elem.getBoundingClientRect().top < 0) { // if the ayah is above our scroll offset
- scroller.scrollTo(`ayah:${ayahNum}`, -150);
+ scroller.scrollTo(`ayah:${currentAyah}`, -150);
} else {
- scroller.scrollTo(`ayah:${ayahNum}`, -80);
+ scroller.scrollTo(`ayah:${currentAyah}`, -80);
}
}
this.props.toggleScroll();
}
- renderLoader() {
- return (
-
- );
+ handleAddFileListeners(file) {
+ const { update, currentTime } = this.props; // eslint-disable-line no-shadow
+ debug('component:Audioplayer', `Attaching listeners to ${file.src}`);
+
+ // Preload file
+ file.setAttribute('preload', 'auto');
+
+ const onLoadeddata = () => {
+ // Default current time to zero. This will change
+ file.currentTime = ( // eslint-disable-line no-param-reassign
+ file.currentTime ||
+ currentTime ||
+ 0
+ );
+
+ return update({
+ duration: file.duration,
+ isLoading: false
+ });
+ };
+
+ const onTimeupdate = () => update({
+ currentTime: file.currentTime,
+ duration: file.duration
+ });
+
+ const onEnded = () => {
+ const { shouldRepeat } = this.props;
+
+ if (shouldRepeat) {
+ file.pause();
+ file.currentTime = 0; // eslint-disable-line no-param-reassign
+ return file.play();
+ }
+
+ if (file.readyState >= 3 && file.paused) {
+ file.pause();
+ }
+
+ return this.handleAyahChange();
+ };
+
+ const onPlay = () => {
+ file.ontimeupdate = onTimeupdate; // eslint-disable-line no-param-reassign
+ };
+
+ const onPause = () => {
+ file.ontimeupdate = null; // eslint-disable-line no-param-reassign
+ };
+
+ const onProgress = () => {
+ };
+
+ file.onloadeddata = onLoadeddata; // eslint-disable-line no-param-reassign
+ file.onpause = onPause; // eslint-disable-line no-param-reassign
+ file.onplay = onPlay; // eslint-disable-line no-param-reassign
+ file.onended = onEnded; // eslint-disable-line no-param-reassign
+ file.onprogress = onProgress; // eslint-disable-line no-param-reassign
+
+ return file;
+ }
+
+ handleRemoveFileListeneres(file) {
+ file.pause();
+ file.currentTime = 0; // eslint-disable-line no-param-reassign
+ file.onloadeddata = null; // eslint-disable-line no-param-reassign
+ file.ontimeupdate = null; // eslint-disable-line no-param-reassign
+ file.onplay = null; // eslint-disable-line no-param-reassign
+ file.onPause = null; // eslint-disable-line no-param-reassign
+ file.onended = null; // eslint-disable-line no-param-reassign
+ file.onprogress = null; // eslint-disable-line no-param-reassign
+ }
+
+ handleTrackChange = (fraction) => {
+ const { currentFile, update } = this.props; // eslint-disable-line no-shadow
+
+ update({
+ currentTime: fraction * currentFile.duration
+ });
+
+ currentFile.currentTime = fraction * currentFile.duration;
}
renderPlayStopButtons() {
- const { isStarted } = this.props;
+ const { isPlaying, stop } = this.props; // eslint-disable-line no-shadow
let icon =
;
- if (isStarted) {
+ if (isPlaying) {
icon =
;
}
return (
-
+
{icon}
);
}
renderPreviousButton() {
- const { currentAyah, ayahIds } = this.props;
- const index = ayahIds.findIndex(id => id === currentAyah);
+ const { currentAyah, files } = this.props;
+ const index = Object.keys(files).findIndex(id => id === currentAyah);
return (
index && this.handleAyahChange('previous')}
>
@@ -306,85 +336,35 @@ export default class Audioplayer extends Component {
}
renderNextButton() {
+ const { surah, currentAyah } = this.props;
+ const isEnd = surah.ayat === parseInt(currentAyah.split(':')[1], 10);
+
return (
-
+ !isEnd && this.handleAyahChange()}
+ >
);
}
- renderRepeatButton() {
- const { shouldRepeat, toggleRepeat } = this.props; // eslint-disable-line no-shadow
- const tooltip = (
-
- Repeats the current ayah on end...
-
- );
-
- return (
-
-
-
-
-
-
- );
- }
-
- renderScrollButton() {
- const { shouldScroll } = this.props;
- const tooltip = (
-
- Automatically scrolls to the currently playing ayah on transitions...
-
- );
-
- return (
-
-
-
-
-
-
- );
- }
-
-
render() {
debug('component:Audioplayer', 'render');
const {
className,
- files,
+ currentFile,
segments,
+ isLoading,
currentAyah,
- currentWord,
- setCurrentWord, // eslint-disable-line no-shadow
- isStarted,
- shouldRepeat,
+ currentTime,
isSupported,
+ duration,
isLoadedOnClient,
- surah
+ shouldRepeat, // eslint-disable-line no-shadow
+ shouldScroll, // eslint-disable-line no-shadow
+ toggleRepeat // eslint-disable-line no-shadow
} = this.props;
if (!isSupported) {
@@ -395,54 +375,48 @@ export default class Audioplayer extends Component {
);
}
- let content = (
-
-
- {this.renderPreviousButton()}
-
-
- {this.renderPlayStopButtons()}
-
-
- {this.renderNextButton()}
-
-
- {this.renderRepeatButton()}
-
- {this.renderScrollButton()}
-
- );
-
- if (!currentAyah) {
+ if (isLoading) {
return (
- {this.renderLoader()}
+ Loading...
);
}
return (
-
{currentAyah.split(':')[1]}
- {content}
+
+ -
+ Playing: {currentAyah.split(':')[1]}
+
+ -
+ {this.renderPreviousButton()}
+
+ -
+ {this.renderPlayStopButtons()}
+
+ -
+ {this.renderNextButton()}
+
+ -
+
+
+ -
+
+
+
{isLoadedOnClient ?
: null}
- {isLoadedOnClient && segments[currentAyah] ?
+ {isLoadedOnClient && segments[currentAyah] && typeof segments[currentAyah] !== 'string' ?
: null}
diff --git a/src/components/Audioplayer/style.scss b/src/components/Audioplayer/style.scss
index 46a205b89..7bc80fe37 100644
--- a/src/components/Audioplayer/style.scss
+++ b/src/components/Audioplayer/style.scss
@@ -1,15 +1,11 @@
@import '../../styles/variables.scss';
-:local .padding_left {
- padding-left: 40px
-}
-
:local .container{
position:relative;
display: block;
user-select: none;
height: 40px;
- padding: 10px 20px 8px 40px;
+ padding: 10px 20px;
}
:local .wrapper{
@@ -18,11 +14,6 @@
top: 90%;
left: 0px;
height: 10%;
- transition: all 0.5s;
- &:hover{
- height: 20%;
- top: 80%;
- }
}
:local .options{
@@ -32,43 +23,55 @@
margin: 0px;
height: 90%;
text-align: center;
+}
+.list{
+ margin-bottom: 0px;
+ display: table;
+ width: 100%;
+ padding-left: 0px;
+ text-align: center;
- .buttons{
- width: 100%;
- display: inline-block;
- cursor: pointer;
- padding-right: 1.5%;
- color: $olive;
- outline: none;
-
- &.playing{
- color: $brand-primary;
- }
- i.fa{
- color: inherit;
- font-size: 100%;
- }
- }
- .checkbox{
- display: none;
+ :global(li){
+ padding-left: 15px;
+ padding-right: 15px;
+ display: table-cell;
}
- .repeat{
+}
+
+.buttons{
+ width: 100%;
+ display: inline-block;
+ cursor: pointer;
+ padding-right: 1.5%;
+ color: $olive;
+ outline: none;
+
+ &.playing{
color: $brand-primary;
}
- .scroll{
- color: $brand-primary;
+ i.fa{
+ color: inherit;
+ font-size: 100%;
}
}
+.checkbox{
+ display: none;
+}
+.repeat{
+ color: $brand-primary;
+}
+.scroll{
+ color: $brand-primary;
+}
+
:local .disabled{
opacity: 0.5;
cursor: not-allowed !important;
}
-:local .verse{
- position: absolute;
- left: 20px;
+.verse{
+ padding: 0px;
color: $olive;
- opacity: 0.5;
}
diff --git a/src/components/Ayah/index.js b/src/components/Ayah/index.js
index 0d5d7cd2c..aa0764a9f 100644
--- a/src/components/Ayah/index.js
+++ b/src/components/Ayah/index.js
@@ -21,8 +21,7 @@ export default class Ayah extends Component {
match: PropTypes.array,
isSearch: PropTypes.bool,
currentWord: PropTypes.any, // gets passed in an integer, null by default
- onWordFocus: PropTypes.func,
- onWordClick: PropTypes.func
+ onWordClick: PropTypes.func.isRequired
};
static defaultProps = {
@@ -43,20 +42,6 @@ export default class Ayah extends Component {
return conditions.some(condition => condition);
}
- handleWordClick = (event) => {
- if (event.target && /^word-/.test(event.target.id)) {
- // call onWordClick in Surah
- this.props.onWordClick(event.target.id.match(/\d+/g).join(':'));
- }
- }
-
- handleWordFocus = (event) => {
- if (event.target && /^word-/.test(event.target.id)) {
- // call onWordFocus in Surah
- this.props.onWordFocus(event.target.id.match(/\d+/g).join(':'), event.target);
- }
- }
-
handlePlay() {
this.setState({
open: false
@@ -88,20 +73,21 @@ export default class Ayah extends Component {
}
renderText() {
- const { ayah, currentWord } = this.props;
+ const { ayah, onWordClick } = this.props;
if (!ayah.words[0].code) {
return false;
}
- let position = 0;
+ // position is important as it will differentiate between words and symbols, see 2:25:13
+ let position = -1;
let text = ayah.words.map(word => {
let id = null;
- const active = word.charTypeId === CHAR_TYPE_WORD && currentWord === position;
- const className = `${word.className} ${word.highlight && word.highlight} ${active && styles.active}`; // eslint-disable-line max-len
+ const className = `${word.className} ${word.highlight && word.highlight}`;
if (word.charTypeId === CHAR_TYPE_WORD) {
- id = `word-${word.ayahKey.replace(/:/, '-')}-${position++}`;
+ position = position + 1;
+ id = `word-${word.ayahKey.replace(/:/, '-')}-${position}`;
} else {
id = `${word.className}-${word.codeDec}`; // just don't include id
}
@@ -113,12 +99,11 @@ export default class Ayah extends Component {
onWordClick(event.target.dataset.key)}
+ data-key={`${word.ayahKey}:${position}`}
className={`${className} pointer`}
data-toggle="tooltip"
data-trigger="hover"
- // tabIndex="1" <-- disable word focus
data-placement="top" title={tooltip}
dangerouslySetInnerHTML={{__html: word.code}}
/>
@@ -128,7 +113,8 @@ export default class Ayah extends Component {
return (
onWordClick(event.target.dataset.key)}
+ data-key={`${word.ayahKey}:${position}`}
className={`${className} pointer`}
key={word.code}
dangerouslySetInnerHTML={{__html: word.code}}
@@ -217,7 +203,7 @@ export default class Ayah extends Component {
debug('component:Ayah', `Render ${this.props.ayah.ayahNum}`);
return (
-
+
{this.renderControls()}
{this.renderText()}
diff --git a/src/components/Ayah/spec.js b/src/components/Ayah/spec.js
index cac819b7e..1942a87c5 100644
--- a/src/components/Ayah/spec.js
+++ b/src/components/Ayah/spec.js
@@ -17,7 +17,7 @@ describe('
', () => {
});
it('should have correct ayah number', () => {
- expect(wrapper.find('.label').text()).to.eql(ayah.ayahKey)
+ expect(wrapper.find('.label').text()).to.eql(ayah.ayahKey);
});
it('should contain translations', () => {
diff --git a/src/components/ContentDropdown/style.scss b/src/components/ContentDropdown/style.scss
index 0890737a7..f31c8f084 100644
--- a/src/components/ContentDropdown/style.scss
+++ b/src/components/ContentDropdown/style.scss
@@ -30,8 +30,6 @@
:global(.in) {
.item{
.label{
- color: $olive;
-
&:after, &:before{
color: $olive !important;
border-color: $olive !important;
diff --git a/src/components/FontStyles/index.js b/src/components/FontStyles/index.js
index 90face967..a887bb375 100644
--- a/src/components/FontStyles/index.js
+++ b/src/components/FontStyles/index.js
@@ -29,10 +29,10 @@ export default class FontStyles extends Component {
if (__CLIENT__) {
const FontFaceObserver = require('fontfaceobserver'); // eslint-disable-line global-require
- Object.keys(fontFaces).filter(className => !fontFaces[className]).map(className => {
+ Object.keys(fontFaces).filter(className => !fontFaces[className]).forEach(className => {
const font = new FontFaceObserver(className);
- return font.load().then(() => load(className), () => load(className));
+ font.load().then(() => load(className), () => load(className));
});
}
diff --git a/src/components/VersesDropdown/index.js b/src/components/VersesDropdown/index.js
index 970a5ce1b..5aff40e7c 100644
--- a/src/components/VersesDropdown/index.js
+++ b/src/components/VersesDropdown/index.js
@@ -8,6 +8,7 @@ export default class VersesDropdown extends Component {
static propTypes = {
ayat: PropTypes.number.isRequired,
loadedAyahs: PropTypes.object.isRequired, // Set
+ surah: PropTypes.object.isRequired, // Set
onClick: PropTypes.func.isRequired,
isReadingMode: PropTypes.bool,
className: PropTypes.string
@@ -18,7 +19,7 @@ export default class VersesDropdown extends Component {
};
renderItem = (ayah, index) => {
- const { loadedAyahs, isReadingMode, onClick } = this.props;
+ const { surah, loadedAyahs, isReadingMode, onClick } = this.props;
const ayahNum = index + 1;
if (loadedAyahs.has(ayahNum) && !isReadingMode) {
@@ -26,7 +27,7 @@ export default class VersesDropdown extends Component {
onClick(ayahNum)}
- to={`ayah:${ayahNum}`}
+ to={`ayah:${surah.id}:${ayahNum}`}
smooth
spy
offset={-120}
diff --git a/src/containers/Surah/connect.js b/src/containers/Surah/connect.js
index 0dbe6a322..965888db0 100644
--- a/src/containers/Surah/connect.js
+++ b/src/containers/Surah/connect.js
@@ -15,7 +15,7 @@ export const surahsConnect = ({ store: { getState, dispatch } }) => {
}
return true;
-;}
+};
export const ayahsConnect = ({ store: { dispatch, getState }, params }) => {
debug('component:Surah:ayahsConnect', 'Init');
diff --git a/src/containers/Surah/index.js b/src/containers/Surah/index.js
index 7e01fc4ef..d444e999c 100644
--- a/src/containers/Surah/index.js
+++ b/src/containers/Surah/index.js
@@ -42,9 +42,9 @@ import { surahsConnect, ayahsConnect } from './connect';
import {
load as loadAyahs,
setCurrentAyah,
- setCurrentWord,
clearCurrentWord
} from '../../redux/modules/ayahs';
+import { setCurrentWord } from '../../redux/modules/audioplayer';
import { setOption, toggleReadingMode } from '../../redux/modules/options';
@@ -71,8 +71,7 @@ let lastScroll = 0;
ayahIds,
isStarted: state.audioplayer.isStarted,
currentWord: state.ayahs.currentWord,
- currentAyah: state.ayahs.currentAyah,
- isEndOfSurah: ayahIds.length === surah.ayat,
+ isEndOfSurah: ayahIds.size === surah.ayat,
surahs: state.surahs.entities,
isLoading: state.ayahs.loading,
isLoaded: state.ayahs.loaded,
@@ -94,10 +93,9 @@ export default class Surah extends Component {
static propTypes = {
surah: PropTypes.object.isRequired,
lines: PropTypes.object.isRequired,
- currentAyah: PropTypes.any,
isEndOfSurah: PropTypes.bool.isRequired,
ayahIds: PropTypes.any,
- currentWord: PropTypes.any,
+ currentWord: PropTypes.string,
surahs: PropTypes.object.isRequired,
isLoading: PropTypes.bool.isRequired,
isLoaded: PropTypes.bool.isRequired,
@@ -146,9 +144,8 @@ export default class Surah extends Component {
const conditions = [
this.state.lazyLoading !== nextState.lazyLoading,
this.props.surah !== nextProps.surah,
- this.props.currentAyah !== nextProps.currentAyah,
this.props.isEndOfSurah !== nextProps.isEndOfSurah,
- this.props.ayahIds !== nextProps.ayahIds,
+ this.props.ayahIds.length !== nextProps.ayahIds.length,
this.props.surahs !== nextProps.surahs,
this.props.isLoading !== nextProps.isLoading,
this.props.isLoaded !== nextProps.isLoaded,
@@ -166,38 +163,22 @@ export default class Surah extends Component {
return false;
}
- onWordClick = (id) => {
- const {
- setCurrentWord, // eslint-disable-line no-shadow
- clearCurrentWord, // eslint-disable-line no-shadow
- currentWord,
- isStarted
- } = this.props;
+ getLast() {
+ const { ayahIds } = this.props;
- if (id === currentWord && !isStarted) {
- clearCurrentWord();
- } else {
- setCurrentWord(id);
- }
+ return [...ayahIds][[...ayahIds].length - 1];
}
- onWordFocus = (id) => {
- const {
- setCurrentWord, // eslint-disable-line no-shadow
- currentWord,
- isStarted
- } = this.props;
+ getFirst() {
+ const { ayahIds } = this.props;
- if (id !== currentWord && isStarted) {
- // let tabbing around while playing trigger seek to word action
- setCurrentWord(id);
- }
+ return [...ayahIds][0];
}
handleOptionChange = (payload) => {
- const { setOption, loadAyahs, surah, ayahIds, options } = this.props; // eslint-disable-line no-shadow max-len
- const from = ayahIds.first();
- const to = ayahIds.last();
+ const { setOption, loadAyahs, surah, options } = this.props; // eslint-disable-line no-shadow, max-len
+ const from = this.getFirst();
+ const to = this.getLast();
setOption(payload);
@@ -244,13 +225,13 @@ export default class Surah extends Component {
}
return this.handleLazyLoadAyahs(() => setTimeout(() =>
- scroller.scrollTo(`ayah:${ayahNum}`),
+ scroller.scrollTo(`ayah:${surah.id}:${ayahNum}`),
1000)); // then scroll to it
}
handleLazyLoadAyahs = (callback) => {
- const { loadAyahs, ayahIds, surah, isEndOfSurah, options } = this.props; // eslint-disable-line no-shadow max-len
- const range = [ayahIds.first(), ayahIds.last()];
+ const { loadAyahs, ayahIds, surah, isEndOfSurah, options } = this.props; // eslint-disable-line no-shadow, max-len
+ const range = [this.getFirst(), this.getLast()];
let size = 10;
@@ -273,18 +254,6 @@ export default class Surah extends Component {
return false;
}
- getLast() {
- const { ayahIds } = this.props;
-
- return [...ayahIds][[...ayahIds].length - 1];
- }
-
- getFirst() {
- const { ayahIds } = this.props;
-
- return [...ayahIds][0];
- }
-
title() {
const { params, surah } = this.props;
@@ -368,19 +337,12 @@ export default class Surah extends Component {
}
renderAyahs() {
- const { ayahs, currentWord } = this.props;
+ const { ayahs, setCurrentWord } = this.props; // eslint-disable-line no-shadow
return Object.values(ayahs).map(ayah => (
));
@@ -478,7 +440,7 @@ export default class Surah extends Component {
/>
-
+
-
+
{
{head.script.toComponent()}
{head.style.toComponent()}
-