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

Commit 1fff01f

Browse files
authored
Fixes #529 PDF export (#773)
* Fixes #529 PDF export * PDF container * PDF html * try all media * another trial * another trial * again * base instead of url * zoom out
1 parent bfb0d75 commit 1fff01f

File tree

9 files changed

+489
-149
lines changed

9 files changed

+489
-149
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"test:dev:lint": "./node_modules/eslint/bin/eslint.js ./src",
1313
"test:stylelint": "stylelint './src/**/*.scss' --config ./webpack/.stylelintrc",
1414
"dev-old": "env NODE_PATH='./src' PORT=8000 UV_THREADPOOL_SIZE=100 node ./webpack/webpack-dev-server.js & env NODE_PATH='./src' PORT=8000 node ./bin/server.js",
15-
"dev": "env NODE_PATH='./src' PORT=8000 UV_THREADPOOL_SIZE=100 node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config ./webpack/dev.config.js & env NODE_PATH='./src' PORT=8000 node ./bin/server.js",
15+
"dev": "env NODE_PATH='./src' PORT=8000 UV_THREADPOOL_SIZE=100 node --expose-gc ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config ./webpack/dev.config.js & env NODE_PATH='./src' PORT=8000 node --expose-gc ./bin/server.js",
1616
"start": "NODE_PATH='src' node ./start",
1717
"build": "npm run build:client & npm run build:server",
1818
"build:server": "babel ./src -d ./dist -D",
@@ -86,6 +86,7 @@
8686
"file-loader": "0.8.5",
8787
"fontfaceobserver": "1.7.3",
8888
"history": "^3.0.0",
89+
"html-pdf": "^2.1.0",
8990
"html-webpack-plugin": "1.7.0",
9091
"http-proxy": "1.14.0",
9192
"humps": "2.0.0",

src/components/Verse/index.js

+68-47
Original file line numberDiff line numberDiff line change
@@ -89,64 +89,73 @@ class Verse extends Component {
8989
const array = match || verse.translations || [];
9090

9191
return array.map(translation => (
92-
<Translation translation={translation} index={translation.id} key={translation.id} />
92+
<Translation
93+
translation={translation}
94+
index={translation.id}
95+
key={translation.id}
96+
/>
9397
));
9498
}
9599

96100
renderMedia() {
97-
const { verse, mediaActions, isSearched } = this.props;
101+
const { verse, mediaActions, isSearched, isPdf } = this.props;
98102

99103
if (isSearched || !verse.mediaContents) return false;
104+
if (isPdf) return false;
100105

101106
return (
102107
<div>
103-
{
104-
verse.mediaContents.map((content, index) => (
105-
<div
106-
className={`${styles.translation} translation`}
107-
key={index}
108-
>
109-
<h2 className="text-translation times-new">
110-
<small>
111-
<a
112-
tabIndex="-1"
113-
className="pointer"
114-
onClick={() => mediaActions.setMedia(content)}
115-
data-metrics-event-name="Media Click"
116-
data-metrics-media-content-url={content.url}
117-
data-metrics-media-content-id={content.id}
118-
data-metrics-media-content-verse-key={verse.verseKey}
119-
>
120-
<LocaleFormattedMessage
121-
id="verse.media.lectureFrom"
122-
defaultMessage="Watch lecture by {from}"
123-
values={{ from: content.authorName }}
124-
/>
125-
</a>
126-
</small>
127-
</h2>
128-
</div>
129-
))
130-
}
108+
{verse.mediaContents.map((content, index) => (
109+
<div className={`${styles.translation} translation`} key={index}>
110+
<h2 className="text-translation times-new">
111+
<small>
112+
<a
113+
tabIndex="-1"
114+
className="pointer"
115+
onClick={() => mediaActions.setMedia(content)}
116+
data-metrics-event-name="Media Click"
117+
data-metrics-media-content-url={content.url}
118+
data-metrics-media-content-id={content.id}
119+
data-metrics-media-content-verse-key={verse.verseKey}
120+
>
121+
<LocaleFormattedMessage
122+
id="verse.media.lectureFrom"
123+
defaultMessage="Watch lecture by {from}"
124+
values={{ from: content.authorName }}
125+
/>
126+
</a>
127+
</small>
128+
</h2>
129+
</div>
130+
))}
131131
</div>
132132
);
133133
}
134134

135135
renderText() {
136-
const { verse, tooltip, currentVerse, isPlaying, audioActions, isSearched } = this.props; // eslint-disable-line max-len
136+
const {
137+
verse,
138+
tooltip,
139+
currentVerse,
140+
isPlaying,
141+
audioActions,
142+
isSearched
143+
} = this.props; // eslint-disable-line max-len
137144
// NOTE: Some 'word's are glyphs (jeem). Not words and should not be clicked for audio
138145
let wordAudioPosition = -1;
139146
const renderText = false; // userAgent.isBot;
140147

141-
const text = verse.words.map(word => ( // eslint-disable-line
148+
const text = verse.words.map((word) => ( // eslint-disable-line
142149
<Word
143150
word={word}
144151
key={`${word.position}-${word.code}-${word.lineNum}`}
145152
currentVerse={currentVerse}
146153
tooltip={tooltip}
147154
isPlaying={isPlaying}
148155
audioActions={audioActions}
149-
audioPosition={word.charType === 'word' ? wordAudioPosition += 1 : null}
156+
audioPosition={
157+
word.charType === 'word' ? (wordAudioPosition += 1) : null
158+
}
150159
isSearched={isSearched}
151160
useTextFont={renderText}
152161
/>
@@ -162,17 +171,22 @@ class Verse extends Component {
162171
}
163172

164173
renderPlayLink() {
165-
const { isSearched, verse, currentVerse, isPlaying } = this.props;
174+
const { isSearched, verse, currentVerse, isPlaying, isPdf } = this.props;
166175
const playing = verse.verseKey === currentVerse && isPlaying;
167176

177+
if (isPdf) return false;
178+
168179
if (!isSearched) {
169180
return (
170181
<a
171182
tabIndex="-1"
172183
onClick={() => this.handlePlay(verse)}
173184
className="text-muted"
174185
>
175-
<i className={`ss-icon ${playing ? 'ss-pause' : 'ss-play'} vertical-align-middle`} />{' '}
186+
<i
187+
className={`ss-icon ${playing ? 'ss-pause' : 'ss-play'} vertical-align-middle`}
188+
/>
189+
{' '}
176190
<LocaleFormattedMessage
177191
id={playing ? 'actions.pause' : 'actions.play'}
178192
defaultMessage={playing ? 'Pause' : 'Play'}
@@ -185,19 +199,25 @@ class Verse extends Component {
185199
}
186200

187201
renderCopyLink() {
188-
const { isSearched, verse } = this.props;
202+
const { isSearched, verse, isPdf } = this.props;
203+
204+
if (isPdf) return false;
189205

190206
if (!isSearched) {
191-
return (
192-
<Copy text={verse.textMadani} verseKey={verse.verseKey} />
193-
);
207+
return <Copy text={verse.textMadani} verseKey={verse.verseKey} />;
194208
}
195209

196210
return false;
197211
}
198212

199213
renderBookmark() {
200-
const { verse, bookmarked, isAuthenticated, bookmarkActions, isSearched } = this.props;
214+
const {
215+
verse,
216+
bookmarked,
217+
isAuthenticated,
218+
bookmarkActions,
219+
isSearched
220+
} = this.props;
201221

202222
if (isSearched || !isAuthenticated) return false;
203223

@@ -226,10 +246,7 @@ class Verse extends Component {
226246
className="text-muted"
227247
>
228248
<i className="ss-icon ss-bookmark vertical-align-middle" />{' '}
229-
<LocaleFormattedMessage
230-
id="verse.bookmark"
231-
defaultMessage="Bookmark"
232-
/>
249+
<LocaleFormattedMessage id="verse.bookmark" defaultMessage="Bookmark" />
233250
</a>
234251
);
235252
}
@@ -271,13 +288,15 @@ class Verse extends Component {
271288
}
272289

273290
renderControls() {
291+
const { isPdf } = this.props;
292+
274293
return (
275294
<div className={`col-md-1 col-sm-1 ${styles.controls}`}>
276295
{this.renderAyahBadge()}
277296
{this.renderPlayLink()}
278297
{this.renderCopyLink()}
279298
{this.renderBookmark()}
280-
{this.renderShare()}
299+
{!isPdf && this.renderShare()}
281300
</div>
282301
);
283302
}
@@ -319,12 +338,14 @@ Verse.propTypes = {
319338
currentVerse: PropTypes.string,
320339
userAgent: PropTypes.object, // eslint-disable-line
321340
audio: PropTypes.number.isRequired,
322-
loadAudio: PropTypes.func.isRequired
341+
loadAudio: PropTypes.func.isRequired,
342+
isPdf: PropTypes.bool
323343
};
324344

325345
Verse.defaultProps = {
326346
currentWord: null,
327-
isSearched: false
347+
isSearched: false,
348+
isPdf: false
328349
};
329350

330351
export default connect(() => ({}), { loadAudio })(Verse);

src/containers/Pdf/index.js

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import React, { PropTypes, Component } from 'react';
2+
3+
import * as customPropTypes from 'customPropTypes';
4+
// redux
5+
import { connect } from 'react-redux';
6+
import { asyncConnect } from 'redux-connect';
7+
8+
import Helmet from 'react-helmet';
9+
10+
// components
11+
import Verse from 'components/Verse';
12+
import Bismillah from 'components/Bismillah';
13+
14+
// Helpers
15+
import debug from 'helpers/debug';
16+
17+
import { chaptersConnect, versesConnect } from '../Surah/connect';
18+
19+
const style = require('../Surah/style.scss');
20+
21+
class Pdf extends Component {
22+
hasVerses() {
23+
return Object.keys(this.props.verses).length;
24+
}
25+
26+
renderVerses() {
27+
const {
28+
chapter,
29+
verses,
30+
options,
31+
isPlaying,
32+
isAuthenticated,
33+
currentVerse
34+
} = this.props; // eslint-disable-line no-shadow
35+
36+
return Object.values(verses).map(verse => (
37+
<Verse
38+
verse={verse}
39+
chapter={chapter}
40+
currentVerse={currentVerse}
41+
iscurrentVerse={isPlaying && verse.verseKey === currentVerse}
42+
tooltip={options.tooltip}
43+
isPlaying={isPlaying}
44+
isAuthenticated={isAuthenticated}
45+
key={`${verse.chapterId}-${verse.id}-verse`}
46+
userAgent={options.userAgent}
47+
audio={options.audio}
48+
isPdf
49+
/>
50+
));
51+
}
52+
53+
render() {
54+
const { chapter, options } = this.props; // eslint-disable-line no-shadow
55+
debug('component:Surah', 'Render');
56+
57+
if (!this.hasVerses()) {
58+
return (
59+
<div className={style.container} style={{ margin: '50px auto' }}>
60+
{this.renderNoAyah()}
61+
</div>
62+
);
63+
}
64+
65+
return (
66+
<div className="chapter-body">
67+
<Helmet
68+
style={[
69+
{
70+
cssText: `.text-arabic{font-size: ${options.fontSize.arabic}rem;} .text-translation{font-size: ${options.fontSize.translation}rem;}` // eslint-disable-line max-len
71+
}
72+
]}
73+
/>
74+
<div className={`container-fluid ${style.container}`}>
75+
<div className="row">
76+
<div className="col-md-10 col-md-offset-1">
77+
<Bismillah chapter={chapter} />
78+
{options.isReadingMode ? this.renderLines() : this.renderVerses()}
79+
</div>
80+
</div>
81+
</div>
82+
</div>
83+
);
84+
}
85+
}
86+
87+
Pdf.propTypes = {
88+
chapter: customPropTypes.surahType.isRequired,
89+
lines: PropTypes.object.isRequired, // eslint-disable-line
90+
currentVerse: PropTypes.string,
91+
isAuthenticated: PropTypes.bool.isRequired,
92+
options: PropTypes.object.isRequired, // eslint-disable-line
93+
verses: customPropTypes.verses,
94+
isPlaying: PropTypes.bool
95+
};
96+
97+
const AsyncPdf = asyncConnect([
98+
{ promise: chaptersConnect },
99+
{ promise: versesConnect }
100+
])(Pdf);
101+
102+
function mapStateToProps(state, ownProps) {
103+
const chapterId = parseInt(ownProps.params.chapterId, 10);
104+
const chapter: Object = state.chapters.entities[chapterId];
105+
const verses: Object = state.verses.entities[chapterId];
106+
const verseArray = verses
107+
? Object.keys(verses).map(key => parseInt(key.split(':')[1], 10))
108+
: [];
109+
const verseIds = new Set(verseArray);
110+
const lastAyahInArray = verseArray.slice(-1)[0];
111+
const isSingleAyah =
112+
!!ownProps.params.range && !ownProps.params.range.includes('-');
113+
const currentVerse = state.audioplayer.currentVerse || Object.keys(verses)[0];
114+
115+
return {
116+
chapter,
117+
verses,
118+
verseIds,
119+
isSingleAyah,
120+
currentVerse,
121+
info: state.chapters.infos[ownProps.params.chapterId],
122+
isStarted: state.audioplayer.isStarted,
123+
isPlaying: state.audioplayer.isPlaying,
124+
isAuthenticated: state.auth.loaded,
125+
currentWord: state.verses.currentWord,
126+
isEndOfSurah: lastAyahInArray === chapter.versesCount,
127+
chapters: state.chapters.entities,
128+
isLoading: state.verses.loading,
129+
isLoaded: state.verses.loaded,
130+
lines: state.lines.lines,
131+
options: state.options
132+
};
133+
}
134+
135+
export default connect(mapStateToProps)(AsyncPdf);

0 commit comments

Comments
 (0)