diff --git a/.editorconfig b/.editorconfig
index 2536d66bf..5760be583 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -3,7 +3,7 @@ root = true
[*]
indent_style = space
-indent_size = 4
+indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
diff --git a/client.js b/client.js
index 7a6cf8e1b..b4491dd80 100644
--- a/client.js
+++ b/client.js
@@ -24,7 +24,11 @@ const client = new ApiClient();
const store = createStore(browserHistory, client, window.reduxData);
const history = syncHistoryWithStore(browserHistory, store);
-Raven.config(config.sentryClient).install();
+try {
+ Raven.config(config.sentryClient).install();
+} catch (error) {
+ console.log(error);
+}
window.quranDebug = debug;
window.ReactDOM = ReactDOM; // For chrome dev tool support
diff --git a/package.json b/package.json
index 57e1bfbf2..d88d26674 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"express-useragent": "^0.2.0",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.4",
+ "fontfaceobserver": "^1.7.1",
"html-webpack-plugin": "^1.4.0",
"http-proxy": "^1.13.2",
"humps": "^1.0.0",
@@ -92,6 +93,7 @@
"react-scroll": "^1.0.4",
"redux": "^3.3.1",
"redux-connect": "^2.4.0",
+ "reselect": "^2.5.1",
"resolve-url": "^0.2.1",
"sass-loader": "2.0.1",
"scroll-behavior": "^0.3.3",
diff --git a/server.js b/server.js
index 3b1ae7f13..55413f0f8 100644
--- a/server.js
+++ b/server.js
@@ -101,11 +101,9 @@ const port = process.env.PORT || 8000;
export default function serve(cb) {
return server.listen(port, function() {
- console.info(`
- ==> 🌎 ENV=${process.env.NODE_ENV}
- ==> ✅ Server is listening at http://localhost:${port}
- ==> 🎯 API at ${process.env.API_URL}
- `);
+ console.info(`==> 🌎 ENV=${process.env.NODE_ENV}`);
+ console.info(`==> ✅ Server is listening at http://localhost:${port}`);
+ console.info(`==> 🎯 API at ${process.env.API_URL}`);
Object.keys(config).forEach(key => config[key].constructor.name !== 'Object' && console.info(`==> ${key}`, config[key]));
cb && cb(this);
diff --git a/src/components/Ayah/index.js b/src/components/Ayah/index.js
index ab71d3eff..0d5d7cd2c 100644
--- a/src/components/Ayah/index.js
+++ b/src/components/Ayah/index.js
@@ -88,13 +88,14 @@ export default class Ayah extends Component {
}
renderText() {
- if (!this.props.ayah.words[0].code) {
+ const { ayah, currentWord } = this.props;
+
+ if (!ayah.words[0].code) {
return false;
}
- const { currentWord } = this.props;
let position = 0;
- let text = this.props.ayah.words.map(word => {
+ 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
@@ -138,6 +139,12 @@ export default class Ayah extends Component {
return (
-
-
{this.renderStatsBar()}
diff --git a/src/containers/Surah/connect.js b/src/containers/Surah/connect.js
index 3d2942dc8..0dbe6a322 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');
@@ -60,4 +60,4 @@ export const ayahsConnect = ({ store: { dispatch, getState }, params }) => {
}
return true;
-}
+};
diff --git a/src/containers/Surah/index.js b/src/containers/Surah/index.js
index 27d626b56..7e01fc4ef 100644
--- a/src/containers/Surah/index.js
+++ b/src/containers/Surah/index.js
@@ -64,22 +64,15 @@ let lastScroll = 0;
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)));
- ayahIds.first = function first() { return [...this][0]; };
- ayahIds.last = function last() { return [...this][[...this].length - 1]; };
-
- const currentWord = state.ayahs.currentWord;
- const currentAyah = state.ayahs.currentAyah;
- const isStarted = state.audioplayer.isStarted;
- const isEndOfSurah = ayahIds.last() === surah.ayat;
return {
- isStarted,
- currentWord,
- currentAyah,
surah,
ayahs,
- isEndOfSurah,
ayahIds,
+ isStarted: state.audioplayer.isStarted,
+ currentWord: state.ayahs.currentWord,
+ currentAyah: state.ayahs.currentAyah,
+ isEndOfSurah: ayahIds.length === surah.ayat,
surahs: state.surahs.entities,
isLoading: state.ayahs.loading,
isLoaded: state.ayahs.loaded,
@@ -201,58 +194,14 @@ export default class Surah extends Component {
}
}
- title() {
- const { params, surah } = this.props;
-
- if (params.range) {
- return `Surah ${surah.name.simple} [${surah.id}:${params.range}]`;
- }
-
- return `Surah ${surah.name.simple} [${surah.id}]`;
- }
-
- description() {
- const { params, ayahs, surah } = this.props;
-
- if (params.range) {
- if (params.range.includes('-')) {
- const [from, to] = params.range.split('-').map(num => parseInt(num, 10));
- const array = Array(to - from).fill(from);
- const translations = array.map((fromAyah, index) => {
- const ayah = ayahs[`${surah.id}:${fromAyah + index}`];
-
- if (ayah && ayah.content && ayah.content[0]) {
- return ayah.content[0].text;
- }
-
- return '';
- });
-
- const content = translations.join(' - ').slice(0, 250);
-
- return `Surat ${surah.name.simple} [verse ${params.range}] - ${content}`;
- }
-
- const ayah = ayahs[`${surah.id}:${params.range}`];
-
- if (ayah && ayah.content && ayah.content[0]) {
- return `Surat ${surah.name.simple} [verse ${params.range}] - ${ayah.content[0].text}`;
- }
-
- return `Surat ${surah.name.simple} [verse ${params.range}]`;
- }
-
- return `${descriptions[surah.id]} This Surah has ${surah.ayat} ayahs and resides between pages ${surah.page[0]} to ${surah.page[1]} in the Quran.`; // eslint-disable-line max-len
- }
-
handleOptionChange = (payload) => {
- const { setOption, loadAyahs, surah, ayahIds, options } = this.props; // eslint-disable-line no-shadow
+ const { setOption, loadAyahs, surah, ayahIds, options } = this.props; // eslint-disable-line no-shadow max-len
const from = ayahIds.first();
const to = ayahIds.last();
- setOptionDispatch(payload);
+ setOption(payload);
- return loadAyahsDispatch(surah.id, from, to, Object.assign({}, options, payload));
+ return loadAyahs(surah.id, from, to, Object.assign({}, options, payload));
}
handleFontSizeChange = (payload) => {
@@ -289,7 +238,7 @@ export default class Surah extends Component {
return false;
}
- if (ayahNum > (ayahIds.last() + 10) || ayahNum < ayahIds.first()) {
+ if (ayahNum > (this.getLast() + 10) || ayahNum < this.getFirst()) {
// This is beyond lazy loading next page.
return push(`/${surah.id}/${ayahNum}-${ayahNum + 10}`);
}
@@ -300,8 +249,9 @@ export default class Surah extends Component {
}
handleLazyLoadAyahs = (callback) => {
- const { loadAyahs, ayahIds, surah, isEndOfSurah, options } = this.props; // eslint-disable-line no-shadow
+ const { loadAyahs, ayahIds, surah, isEndOfSurah, options } = this.props; // eslint-disable-line no-shadow max-len
const range = [ayahIds.first(), ayahIds.last()];
+
let size = 10;
if ((range[1] - range[0] + 1) < 10) {
@@ -323,6 +273,62 @@ 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;
+
+ if (params.range) {
+ return `Surah ${surah.name.simple} [${surah.id}:${params.range}]`;
+ }
+
+ return `Surah ${surah.name.simple} [${surah.id}]`;
+ }
+
+ description() {
+ const { params, ayahs, surah } = this.props;
+
+ if (params.range) {
+ if (params.range.includes('-')) {
+ const [from, to] = params.range.split('-').map(num => parseInt(num, 10));
+ const array = Array(to - from).fill(from);
+ const translations = array.map((fromAyah, index) => {
+ const ayah = ayahs[`${surah.id}:${fromAyah + index}`];
+
+ if (ayah && ayah.content && ayah.content[0]) {
+ return ayah.content[0].text;
+ }
+
+ return '';
+ });
+
+ const content = translations.join(' - ').slice(0, 250);
+
+ return `Surat ${surah.name.simple} [verse ${params.range}] - ${content}`;
+ }
+
+ const ayah = ayahs[`${surah.id}:${params.range}`];
+
+ if (ayah && ayah.content && ayah.content[0]) {
+ return `Surat ${surah.name.simple} [verse ${params.range}] - ${ayah.content[0].text}`;
+ }
+
+ return `Surat ${surah.name.simple} [verse ${params.range}]`;
+ }
+
+ return `${descriptions[surah.id]} This Surah has ${surah.ayat} ayahs and resides between pages ${surah.page[0]} to ${surah.page[1]} in the Quran.`; // eslint-disable-line max-len
+ }
+
renderPagination() {
const { isLoading, isEndOfSurah, surah } = this.props;
diff --git a/src/helpers/Html.js b/src/helpers/Html.js
index cef283fa5..f2dc1b551 100644
--- a/src/helpers/Html.js
+++ b/src/helpers/Html.js
@@ -16,6 +16,7 @@ const Html = ({ store, component, assets }) => {
{head.meta.toComponent()}
{head.link.toComponent()}
{head.script.toComponent()}
+ {head.style.toComponent()}
{Object.keys(assets.styles).map((style, i) => (
{
:
null
}
-
diff --git a/src/helpers/buildFontFaces.js b/src/helpers/buildFontFaces.js
index 4977fb525..afb722bd4 100644
--- a/src/helpers/buildFontFaces.js
+++ b/src/helpers/buildFontFaces.js
@@ -1,5 +1,20 @@
/* eslint-disable max-len */
+export const fontFaceStyle = (fontClassName) => (
+ `@font-face {font-family: '${fontClassName}';
+ src: url('//quran-1f14.kxcdn.com/fonts/compressed/eot/${fontClassName}.eot?#iefix') format('embedded-opentype'),
+ url('//quran-1f14.kxcdn.com/fonts/ttf/${fontClassName}.ttf') format('truetype'),
+ url('//quran-1f14.kxcdn.com/fonts/woff/${fontClassName}.woff?-snx2rh') format('woff'),
+ url('//quran-1f14.kxcdn.com/fonts/compressed/svg/${fontClassName}.svg#') format('svg');}
+ .${fontClassName} {font-family: '${fontClassName}';}
+ .${fontClassName} {display: none;}`
+);
+
+export const fontFaceStyleLoaded = (fontClassName) => (
+ `.${fontClassName} {display: block;}
+ .text-${fontClassName} {display: none;}`
+);
+
export function createFontFacesArray(ayahs) {
const fontFaces = [];
const fontFacesArray = [];
diff --git a/src/redux/modules/ayahs.js b/src/redux/modules/ayahs.js
index 780cea66d..80fd8715c 100644
--- a/src/redux/modules/ayahs.js
+++ b/src/redux/modules/ayahs.js
@@ -2,8 +2,6 @@ import { ayahsSchema } from '../schemas';
import { arrayOf } from 'normalizr';
-import { createFontFacesArray } from '../../helpers/buildFontFaces';
-
export const LOAD = '@@quran/ayahs/LOAD';
export const LOAD_SUCCESS = '@@quran/ayahs/LOAD_SUCCESS';
export const LOAD_FAIL = '@@quran/ayahs/LOAD_FAIL';
@@ -19,8 +17,7 @@ const initialState = {
loaded: false,
loading: false,
entities: {},
- result: [],
- fontFaces: []
+ result: []
};
export default function reducer(state = initialState, action = {}) {
@@ -81,13 +78,7 @@ export default function reducer(state = initialState, action = {}) {
action.result.entities.ayahs
)
},
- result: Object.assign({}, state.result, action.result.result),
- fontFaces: new Set([
- ...state.fontFaces,
- ...createFontFacesArray(
- action.result.result.map(key => action.result.entities.ayahs[key])
- )
- ]),
+ result: Object.assign({}, state.result, action.result.result)
};
case LOAD_FAIL:
console.log(action);
diff --git a/src/redux/modules/fontFaces.js b/src/redux/modules/fontFaces.js
new file mode 100644
index 000000000..4b8070978
--- /dev/null
+++ b/src/redux/modules/fontFaces.js
@@ -0,0 +1,39 @@
+import { LOAD_SUCCESS } from './ayahs';
+import { SEARCH_SUCCESS } from './searchResults';
+
+export const LOAD = '@@quran/fontFaces/LOAD';
+
+export default function reducer(state = {}, action = {}) {
+ switch (action.type) {
+ case LOAD_SUCCESS:
+ case SEARCH_SUCCESS:
+ const ayahs = action.result.entities.ayahs;
+ const classNames = {};
+
+ Object.keys(ayahs).forEach(ayahId => {
+ const ayah = ayahs[ayahId];
+
+ classNames[`p${ayah.pageNum}`] = false;
+ });
+
+ return {
+ ...state,
+ ...classNames
+ };
+
+ case LOAD:
+ return {
+ ...state,
+ [action.className]: true
+ };
+ default:
+ return state;
+ }
+}
+
+export function load(className) {
+ return {
+ type: LOAD,
+ className
+ };
+}
diff --git a/src/redux/modules/reducer.js b/src/redux/modules/reducer.js
index e77ffd56e..0a94699c1 100644
--- a/src/redux/modules/reducer.js
+++ b/src/redux/modules/reducer.js
@@ -8,6 +8,7 @@ import audioplayer from './audioplayer';
import lines from './lines';
import options from './options';
import searchResults from './searchResults';
+import fontFaces from './fontFaces';
export default combineReducers({
routing: routerReducer,
@@ -15,6 +16,7 @@ export default combineReducers({
surahs,
ayahs,
audioplayer,
+ fontFaces,
lines,
searchResults,
options
diff --git a/src/redux/modules/searchResults.js b/src/redux/modules/searchResults.js
index be23d3a5b..c97969b73 100644
--- a/src/redux/modules/searchResults.js
+++ b/src/redux/modules/searchResults.js
@@ -1,8 +1,6 @@
import { ayahsSchema } from '../schemas';
import { arrayOf } from 'normalizr';
-import { createFontFacesArray } from '../../helpers/buildFontFaces';
-
export const SEARCH = '@@quran/search/LOAD';
export const SEARCH_SUCCESS = '@@quran/search/LOAD_SUCCESS';
export const SEARCH_FAIL = '@@quran/search/LOAD_FAIL';
@@ -11,8 +9,7 @@ const initialState = {
errored: false,
loaded: false,
entities: {},
- results: [],
- fontFaces: []
+ results: []
};
export default function reducer(state = initialState, action = {}) {
@@ -38,10 +35,7 @@ export default function reducer(state = initialState, action = {}) {
took: action.result.result.took,
query: action.result.result.query,
results: action.result.result.results,
- entities: Object.assign({}, state.entities, action.result.entities.ayahs),
- fontFaces: [].concat(state.fontFaces, createFontFacesArray(
- action.result.result.results.map(result => action.result.entities.ayahs[result.ayah])
- ))
+ entities: Object.assign({}, state.entities, action.result.entities.ayahs)
};
case SEARCH_FAIL:
return {