react-native-simple-epub-reader 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/lib/module/components/GestureHandler/index.js +8 -2
  2. package/lib/module/components/GestureHandler/index.js.map +1 -1
  3. package/lib/module/components/Reader.js +125 -22
  4. package/lib/module/components/Reader.js.map +1 -1
  5. package/lib/module/constants/template.js +189 -226
  6. package/lib/module/constants/template.js.map +1 -1
  7. package/lib/module/constants/theme.js +28 -11
  8. package/lib/module/constants/theme.js.map +1 -1
  9. package/lib/module/helpers/downloadEpub.js +5 -1
  10. package/lib/module/helpers/downloadEpub.js.map +1 -1
  11. package/lib/module/helpers/saveTemplateToFile.js +4 -0
  12. package/lib/module/helpers/saveTemplateToFile.js.map +1 -1
  13. package/lib/module/hooks/useInjectWebviewVariables.js +4 -2
  14. package/lib/module/hooks/useInjectWebviewVariables.js.map +1 -1
  15. package/lib/typescript/src/components/GestureHandler/index.d.ts.map +1 -1
  16. package/lib/typescript/src/components/Reader.d.ts +1 -1
  17. package/lib/typescript/src/components/Reader.d.ts.map +1 -1
  18. package/lib/typescript/src/constants/template.d.ts +1 -1
  19. package/lib/typescript/src/constants/template.d.ts.map +1 -1
  20. package/lib/typescript/src/constants/theme.d.ts.map +1 -1
  21. package/lib/typescript/src/helpers/downloadEpub.d.ts.map +1 -1
  22. package/lib/typescript/src/helpers/saveTemplateToFile.d.ts +1 -0
  23. package/lib/typescript/src/helpers/saveTemplateToFile.d.ts.map +1 -1
  24. package/lib/typescript/src/hooks/useInjectWebviewVariables.d.ts +2 -1
  25. package/lib/typescript/src/hooks/useInjectWebviewVariables.d.ts.map +1 -1
  26. package/lib/typescript/src/types/index.d.ts +4 -0
  27. package/lib/typescript/src/types/index.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/components/GestureHandler/index.tsx +8 -0
  30. package/src/components/Reader.tsx +161 -27
  31. package/src/constants/template.ts +189 -226
  32. package/src/constants/theme.ts +28 -11
  33. package/src/helpers/downloadEpub.ts +7 -1
  34. package/src/helpers/saveTemplateToFile.ts +5 -0
  35. package/src/hooks/useInjectWebviewVariables.ts +9 -0
  36. package/src/types/index.ts +4 -0
@@ -1,8 +1,8 @@
1
1
  export default `
2
- <!doctype html>
3
- <html>
2
+ <!DOCTYPE html>
3
+ <html>
4
4
  <head>
5
- <meta charset="utf-8" />
5
+ <meta charset="utf-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>EPUB.js</title>
8
8
  <script id="jszip"></script>
@@ -11,7 +11,7 @@ export default `
11
11
  <style type="text/css">
12
12
  body {
13
13
  margin: 0;
14
- background-color: #211f26;
14
+ background-color: #211F26;
15
15
  }
16
16
 
17
17
  #viewer {
@@ -23,18 +23,16 @@ export default `
23
23
  align-items: center;
24
24
  }
25
25
 
26
- [ref='epubjs-mk-balloon'] {
27
- background: url('data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPScxLjEnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZycgeG1sbnM6eGxpbms9J2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsnIHg9JzBweCcgeT0nMHB4JyB2aWV3Qm94PScwIDAgNzUgNzUnPjxnIGZpbGw9JyNCREJEQkQnIGlkPSdidWJibGUnPjxwYXRoIGNsYXNzPSdzdDAnIGQ9J00zNy41LDkuNEMxOS42LDkuNCw1LDIwLjUsNSwzNC4zYzAsNS45LDIuNywxMS4zLDcuMSwxNS42TDkuNiw2NS42bDE5LTcuM2MyLjgsMC42LDUuOCwwLjksOC45LDAuOSBDNTUuNSw1OS4yLDcwLDQ4LjEsNzAsMzQuM0M3MCwyMC41LDU1LjQsOS40LDM3LjUsOS40eicvPjwvZz48L3N2Zz4=')
28
- no-repeat;
26
+ [ref="epubjs-mk-balloon"] {
27
+ background: url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPScxLjEnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZycgeG1sbnM6eGxpbms9J2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsnIHg9JzBweCcgeT0nMHB4JyB2aWV3Qm94PScwIDAgNzUgNzUnPjxnIGZpbGw9JyNCREJEQkQnIGlkPSdidWJibGUnPjxwYXRoIGNsYXNzPSdzdDAnIGQ9J00zNy41LDkuNEMxOS42LDkuNCw1LDIwLjUsNSwzNC4zYzAsNS45LDIuNywxMS4zLDcuMSwxNS42TDkuNiw2NS42bDE5LTcuM2MyLjgsMC42LDUuOCwwLjksOC45LDAuOSBDNTUuNSw1OS4yLDcwLDQ4LjEsNzAsMzQuM0M3MCwyMC41LDU1LjQsOS40LDM3LjUsOS40eicvPjwvZz48L3N2Zz4=") no-repeat;
29
28
  width: 20px;
30
29
  height: 20px;
31
30
  cursor: pointer;
32
31
  margin-left: 0;
33
32
  }
34
33
 
35
- [ref='epubjs-mk-heart'] {
36
- background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOUlEQVR4nLWUTWgTURDH14Oe9JiPNqFNujvvzdsm3bdvPxKMFUEPag/iwdaD3j1JDymlCMXiqUeRHvWgFRQUxKPirUU8eFARvCnUj7QXP7DiJtk8easJjRjzIQ784bEz82Pe7MzTtP9tpmnu8UbNpOM4uzvFKF+3GM1BHHIAbwjA7xyY5AaGPuCarZtHmzGcsGM+YevKp2JUrAN4XeW2wSxKMy6wrSkKtbsiJZ96SfnAGZbl8bG6DawhdLwqAK9xYI25XLaufCrmjkjJKQpVF3DLzrDRFtAHXJ9hUNsoxOTH8hn5afGcrBRjkR66w3I/0GoJaPWRO9T63tRGISanmVHzgK1FMBvGmSr/iZeUn5fL8svlRbl5aKQt6bGXjPQ7bKefA5MOIahZOpsuAQmUY3t1pWNSN5WABtwwT2kW4Mki0OqgoMov+YA1rrMTmk3IhCr3hd/5St303EtEV54Yw5xq4y4PcHOFt/etH12xRqQHWFGsn/MFuHAQaPCmGO8b9roQl5OEBpaB862xoZTuc4F+uJDLhv0CF/LZ0DPoe9M097YNNwd2hAMLb9rpnmGrdlr1LrQJO/zH9bMMnBWA4X0n1RV2T6TU6oUc2Pm/vQ0aN/CSAKzfFp0rvWWnI5gNbEnrxWwD59UOL+UzjXc7ftTbYlxezGca0X4Dm+sJ1jQO7LgA/Hoa9eCln5Cv/IQ8i3ogAL+pZdAGMYcQdAGfHSAkmCQkUOc8pXQgWNPUgysAl5XU+Z9gg9gPaBjV+CGbZVoAAAAASUVORK5CYII=')
37
- no-repeat;
34
+ [ref="epubjs-mk-heart"] {
35
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOUlEQVR4nLWUTWgTURDH14Oe9JiPNqFNujvvzdsm3bdvPxKMFUEPag/iwdaD3j1JDymlCMXiqUeRHvWgFRQUxKPirUU8eFARvCnUj7QXP7DiJtk8easJjRjzIQ784bEz82Pe7MzTtP9tpmnu8UbNpOM4uzvFKF+3GM1BHHIAbwjA7xyY5AaGPuCarZtHmzGcsGM+YevKp2JUrAN4XeW2wSxKMy6wrSkKtbsiJZ96SfnAGZbl8bG6DawhdLwqAK9xYI25XLaufCrmjkjJKQpVF3DLzrDRFtAHXJ9hUNsoxOTH8hn5afGcrBRjkR66w3I/0GoJaPWRO9T63tRGISanmVHzgK1FMBvGmSr/iZeUn5fL8svlRbl5aKQt6bGXjPQ7bKefA5MOIahZOpsuAQmUY3t1pWNSN5WABtwwT2kW4Mki0OqgoMov+YA1rrMTmk3IhCr3hd/5St303EtEV54Yw5xq4y4PcHOFt/etH12xRqQHWFGsn/MFuHAQaPCmGO8b9roQl5OEBpaB862xoZTuc4F+uJDLhv0CF/LZ0DPoe9M097YNNwd2hAMLb9rpnmGrdlr1LrQJO/zH9bMMnBWA4X0n1RV2T6TU6oUc2Pm/vQ0aN/CSAKzfFp0rvWWnI5gNbEnrxWwD59UOL+UzjXc7ftTbYlxezGca0X4Dm+sJ1jQO7LgA/Hoa9eCln5Cv/IQ8i3ogAL+pZdAGMYcQdAGfHSAkmCQkUOc8pXQgWNPUgysAl5XU+Z9gg9gPaBjV+CGbZVoAAAAASUVORK5CYII=") no-repeat;
38
36
  width: 20px;
39
37
  height: 20px;
40
38
  cursor: pointer;
@@ -43,7 +41,7 @@ export default `
43
41
  </style>
44
42
  </head>
45
43
 
46
- <body oncopy="return false;" oncut="return false;">
44
+ <body oncopy='return false' oncut='return false'>
47
45
  <div id="viewer"></div>
48
46
 
49
47
  <script>
@@ -57,153 +55,117 @@ export default `
57
55
  const enableSelection = window.enable_selection;
58
56
  const allowScriptedContent = window.allowScriptedContent || false;
59
57
  const allowPopups = window.allowPopups || false;
58
+ const LOCATION_GENERATION_CHARS = 2800;
60
59
 
61
60
  if (!file) {
62
- const reactNativeWebview =
63
- window.ReactNativeWebView !== undefined &&
64
- window.ReactNativeWebView !== null
65
- ? window.ReactNativeWebView
66
- : window;
67
- reactNativeWebview.postMessage(
68
- JSON.stringify({
69
- type: 'onDisplayError',
70
- reason: 'Book file is missing',
71
- })
72
- );
61
+ const reactNativeWebview = window.ReactNativeWebView !== undefined && window.ReactNativeWebView !== null ? window.ReactNativeWebView : window;
62
+ reactNativeWebview.postMessage(JSON.stringify({
63
+ type: "onDisplayError",
64
+ reason: "Book file is missing"
65
+ }));
73
66
  }
74
67
 
75
68
  if (type === 'epub' || type === 'opf' || type === 'binary') {
76
69
  book = ePub(file);
77
70
  } else if (type === 'base64') {
78
- book = ePub(file, { encoding: 'base64' });
71
+ book = ePub(file, { encoding: "base64" });
79
72
  } else {
80
- const reactNativeWebview =
81
- window.ReactNativeWebView !== undefined &&
82
- window.ReactNativeWebView !== null
83
- ? window.ReactNativeWebView
84
- : window;
85
- reactNativeWebview.postMessage(
86
- JSON.stringify({
87
- type: 'onDisplayError',
88
- reason: 'Missing or invalid file type',
89
- })
90
- );
73
+ const reactNativeWebview = window.ReactNativeWebView !== undefined && window.ReactNativeWebView !== null ? window.ReactNativeWebView : window;
74
+ reactNativeWebview.postMessage(JSON.stringify({
75
+ type: "onDisplayError",
76
+ reason: "Missing or invalid file type"
77
+ }));
91
78
  }
92
79
 
93
- rendition = book.renderTo('viewer', {
94
- width: '100%',
95
- height: '100%',
96
- manager: 'default',
97
- flow: 'auto',
80
+ rendition = book.renderTo("viewer", {
81
+ width: "100%",
82
+ height: "100%",
83
+ manager: "default",
84
+ flow: "auto",
98
85
  snap: undefined,
99
86
  spread: undefined,
100
87
  fullsize: undefined,
101
88
  allowPopups: allowPopups,
102
- allowScriptedContent: allowScriptedContent,
89
+ allowScriptedContent: allowScriptedContent
103
90
  });
104
-
105
- const reactNativeWebview =
106
- window.ReactNativeWebView !== undefined &&
107
- window.ReactNativeWebView !== null
108
- ? window.ReactNativeWebView
109
- : window;
110
- reactNativeWebview.postMessage(JSON.stringify({ type: 'onStarted' }));
91
+
92
+ const reactNativeWebview = window.ReactNativeWebView !== undefined && window.ReactNativeWebView!== null ? window.ReactNativeWebView: window;
93
+ reactNativeWebview.postMessage(JSON.stringify({ type: "onStarted" }));
111
94
 
112
95
  function flatten(chapters) {
113
- return [].concat.apply(
114
- [],
115
- chapters.map((chapter) =>
116
- [].concat.apply([chapter], flatten(chapter.subitems))
117
- )
118
- );
96
+ return [].concat.apply([], chapters.map((chapter) => [].concat.apply([chapter], flatten(chapter.subitems))));
119
97
  }
120
98
 
121
99
  function getCfiFromHref(book, href) {
122
- const [_, id] = href.split('#');
123
- let section =
124
- book.spine.get(href.split('/')[1]) ||
125
- book.spine.get(href) ||
126
- book.spine.get(href.split('/').slice(1).join('/'));
127
-
128
- const el = id
129
- ? section.document.getElementById(id)
130
- : section.document.body;
131
- return section.cfiFromElement(el);
100
+ const [_, id] = href.split('#')
101
+ let section = book.spine.get(href.split('/')[1]) || book.spine.get(href) || book.spine.get(href.split('/').slice(1).join('/'))
102
+
103
+ const el = (id ? section.document.getElementById(id) : section.document.body)
104
+ return section.cfiFromElement(el)
132
105
  }
133
106
 
134
107
  function getChapter(location) {
135
- const locationHref = location.start.href;
136
-
137
- let match = flatten(book.navigation.toc)
138
- .filter((chapter) => {
139
- return book.canonical(chapter.href).includes(locationHref);
140
- }, null)
141
- .reduce((result, chapter) => {
142
- const locationAfterChapter =
143
- ePub.CFI.prototype.compare(
144
- location.start.cfi,
145
- getCfiFromHref(book, chapter.href)
146
- ) > 0;
147
- return locationAfterChapter ? chapter : result;
148
- }, null);
149
-
150
- return match;
151
- }
108
+ const locationHref = location.start.href
109
+
110
+ let match = flatten(book.navigation.toc)
111
+ .filter((chapter) => {
112
+ return book.canonical(chapter.href).includes(locationHref)
113
+ }, null)
114
+ .reduce((result, chapter) => {
115
+ const locationAfterChapter = ePub.CFI.prototype.compare(location.start.cfi, getCfiFromHref(book, chapter.href)) > 0
116
+ return locationAfterChapter ? chapter : result
117
+ }, null);
118
+
119
+ return match;
120
+ };
152
121
 
153
122
  const makeRangeCfi = (a, b) => {
154
- const CFI = new ePub.CFI();
155
- const start = CFI.parse(a),
156
- end = CFI.parse(b);
123
+ const CFI = new ePub.CFI()
124
+ const start = CFI.parse(a), end = CFI.parse(b)
157
125
  const cfi = {
158
- range: true,
159
- base: start.base,
160
- path: {
161
- steps: [],
162
- terminal: null,
163
- },
164
- start: start.path,
165
- end: end.path,
166
- };
167
- const len = cfi.start.steps.length;
126
+ range: true,
127
+ base: start.base,
128
+ path: {
129
+ steps: [],
130
+ terminal: null
131
+ },
132
+ start: start.path,
133
+ end: end.path
134
+ }
135
+ const len = cfi.start.steps.length
168
136
  for (let i = 0; i < len; i++) {
169
137
  if (CFI.equalStep(cfi.start.steps[i], cfi.end.steps[i])) {
170
- if (i == len - 1) {
171
- // Last step is equal, check terminals
172
- if (cfi.start.terminal === cfi.end.terminal) {
173
- // CFI's are equal
174
- cfi.path.steps.push(cfi.start.steps[i]);
175
- // Not a range
176
- cfi.range = false;
177
- }
178
- } else cfi.path.steps.push(cfi.start.steps[i]);
179
- } else break;
138
+ if (i == len - 1) {
139
+ // Last step is equal, check terminals
140
+ if (cfi.start.terminal === cfi.end.terminal) {
141
+ // CFI's are equal
142
+ cfi.path.steps.push(cfi.start.steps[i])
143
+ // Not a range
144
+ cfi.range = false
145
+ }
146
+ } else cfi.path.steps.push(cfi.start.steps[i])
147
+ } else break
180
148
  }
181
- cfi.start.steps = cfi.start.steps.slice(cfi.path.steps.length);
182
- cfi.end.steps = cfi.end.steps.slice(cfi.path.steps.length);
183
-
184
- return (
185
- 'epubcfi(' +
186
- CFI.segmentString(cfi.base) +
187
- '!' +
188
- CFI.segmentString(cfi.path) +
189
- ',' +
190
- CFI.segmentString(cfi.start) +
191
- ',' +
192
- CFI.segmentString(cfi.end) +
193
- ')'
194
- );
195
- };
149
+ cfi.start.steps = cfi.start.steps.slice(cfi.path.steps.length)
150
+ cfi.end.steps = cfi.end.steps.slice(cfi.path.steps.length)
151
+
152
+ return 'epubcfi(' + CFI.segmentString(cfi.base)
153
+ + '!' + CFI.segmentString(cfi.path)
154
+ + ',' + CFI.segmentString(cfi.start)
155
+ + ',' + CFI.segmentString(cfi.end)
156
+ + ')'
157
+ }
196
158
 
197
159
  if (!enableSelection) {
198
160
  rendition.themes.default({
199
- body: {
200
- '-webkit-touch-callout': 'none' /* iOS Safari */,
201
- '-webkit-user-select': 'none' /* Safari */,
202
- '-khtml-user-select': 'none' /* Konqueror HTML */,
203
- '-moz-user-select': 'none' /* Firefox */,
204
- '-ms-user-select': 'none' /* Internet Explorer/Edge */,
205
- 'user-select': 'none',
206
- },
161
+ 'body': {
162
+ '-webkit-touch-callout': 'none', /* iOS Safari */
163
+ '-webkit-user-select': 'none', /* Safari */
164
+ '-khtml-user-select': 'none', /* Konqueror HTML */
165
+ '-moz-user-select': 'none', /* Firefox */
166
+ '-ms-user-select': 'none', /* Internet Explorer/Edge */
167
+ 'user-select': 'none'
168
+ }
207
169
  });
208
170
  }
209
171
 
@@ -217,21 +179,20 @@ export default `
217
179
  .then(function () {
218
180
  var currentLocation = rendition.currentLocation();
219
181
 
220
- reactNativeWebview.postMessage(
221
- JSON.stringify({
222
- type: 'onReady',
223
- totalLocations: book.locations.total,
224
- currentLocation: currentLocation,
225
- progress: currentLocation?.start?.cfi
226
- ? book.locations.percentageFromCfi(currentLocation.start.cfi)
227
- : 0,
228
- })
229
- );
230
-
231
- if (initialLocations) {
232
- reactNativeWebview.postMessage(
233
- JSON.stringify({
234
- type: 'onLocationsReady',
182
+ reactNativeWebview.postMessage(JSON.stringify({
183
+ type: "onReady",
184
+ totalLocations: book.locations.total,
185
+ currentLocation: currentLocation,
186
+ progress: currentLocation?.start?.cfi
187
+ ? book.locations.percentageFromCfi(currentLocation.start.cfi)
188
+ : 0,
189
+ }));
190
+
191
+ // Defer heavier work to let the onReady bridge message be delivered first.
192
+ setTimeout(function () {
193
+ if (initialLocations && initialLocations.length) {
194
+ reactNativeWebview.postMessage(JSON.stringify({
195
+ type: "onLocationsReady",
235
196
  epubKey: book.key(),
236
197
  locations: initialLocations,
237
198
  totalLocations: book.locations.total,
@@ -239,52 +200,41 @@ export default `
239
200
  progress: currentLocation?.start?.cfi
240
201
  ? book.locations.percentageFromCfi(currentLocation.start.cfi)
241
202
  : 0,
242
- })
243
- );
244
- return Promise.resolve();
245
- }
246
-
247
- return book.locations
248
- .generate(1600)
249
- .then(function () {
250
- var generatedLocation =
251
- rendition.currentLocation() || currentLocation;
252
- reactNativeWebview.postMessage(
253
- JSON.stringify({
254
- type: 'onLocationsReady',
203
+ }));
204
+ } else {
205
+ // Larger chunk size reduces startup cost for the first locations map.
206
+ book.locations.generate(LOCATION_GENERATION_CHARS).then(function () {
207
+ var generatedLocation = rendition.currentLocation() || currentLocation;
208
+ reactNativeWebview.postMessage(JSON.stringify({
209
+ type: "onLocationsReady",
255
210
  epubKey: book.key(),
256
211
  locations: book.locations.save(),
257
212
  totalLocations: book.locations.total,
258
213
  currentLocation: generatedLocation,
259
214
  progress: generatedLocation?.start?.cfi
260
- ? book.locations.percentageFromCfi(
261
- generatedLocation.start.cfi
262
- )
215
+ ? book.locations.percentageFromCfi(generatedLocation.start.cfi)
263
216
  : 0,
264
- })
265
- );
266
- })
267
- .catch(function () {
268
- reactNativeWebview.postMessage(
269
- JSON.stringify({
270
- type: 'onLocationsReady',
217
+ }));
218
+ }).catch(function () {
219
+ reactNativeWebview.postMessage(JSON.stringify({
220
+ type: "onLocationsReady",
271
221
  epubKey: book.key(),
272
222
  locations: [],
273
223
  totalLocations: book.locations.total,
274
224
  currentLocation: currentLocation,
275
225
  progress: 0,
276
- })
277
- );
278
- });
226
+ }));
227
+ });
228
+ }
279
229
 
280
- book
230
+ book
281
231
  .coverUrl()
282
232
  .then(async (url) => {
283
233
  var reader = new FileReader();
284
- reader.onload = (res) => {
234
+ reader.onload = () => {
285
235
  reactNativeWebview.postMessage(
286
236
  JSON.stringify({
287
- type: 'meta',
237
+ type: "meta",
288
238
  metadata: {
289
239
  cover: reader.result,
290
240
  author: book.package.metadata.creator,
@@ -302,7 +252,7 @@ export default `
302
252
  .catch(() => {
303
253
  reactNativeWebview.postMessage(
304
254
  JSON.stringify({
305
- type: 'meta',
255
+ type: "meta",
306
256
  metadata: {
307
257
  cover: undefined,
308
258
  author: book.package.metadata.creator,
@@ -316,37 +266,34 @@ export default `
316
266
  );
317
267
  });
318
268
 
319
- book.loaded.navigation.then(function (item) {
320
- reactNativeWebview.postMessage(
321
- JSON.stringify({
269
+ book.loaded.navigation.then(function (item) {
270
+ reactNativeWebview.postMessage(JSON.stringify({
322
271
  type: 'onNavigationLoaded',
323
272
  toc: item.toc,
324
- landmarks: item.landmarks,
325
- })
326
- );
327
- });
273
+ landmarks: item.landmarks
274
+ }));
275
+ });
276
+ }, 0);
328
277
  })
329
278
  .catch(function (err) {
330
- reactNativeWebview.postMessage(
331
- JSON.stringify({
332
- type: 'onDisplayError',
333
- reason: err.message || err.toString(),
334
- })
335
- );
336
- });
279
+ reactNativeWebview.postMessage(JSON.stringify({
280
+ type: "onDisplayError",
281
+ reason: err.message || err.toString()
282
+ }));
283
+ });
337
284
 
338
285
  let isAnimating = false;
339
286
  const originalNext = rendition.next.bind(rendition);
340
287
  const originalPrev = rendition.prev.bind(rendition);
341
288
 
342
- rendition.next = function () {
289
+ rendition.next = function() {
343
290
  if (isAnimating) return;
344
291
  isAnimating = true;
345
-
292
+
346
293
  const container = rendition.manager.container;
347
294
  container.style.transition = 'opacity 0.2s ease-out';
348
295
  container.style.opacity = '0.4';
349
-
296
+
350
297
  setTimeout(() => {
351
298
  originalNext();
352
299
  setTimeout(() => {
@@ -359,14 +306,14 @@ export default `
359
306
  }, 100);
360
307
  };
361
308
 
362
- rendition.prev = function () {
309
+ rendition.prev = function() {
363
310
  if (isAnimating) return;
364
311
  isAnimating = true;
365
-
312
+
366
313
  const container = rendition.manager.container;
367
314
  container.style.transition = 'opacity 0.2s ease-out';
368
315
  container.style.opacity = '0.4';
369
-
316
+
370
317
  setTimeout(() => {
371
318
  originalPrev();
372
319
  setTimeout(() => {
@@ -384,55 +331,71 @@ export default `
384
331
  rendition.themes.select('theme');
385
332
  });
386
333
 
387
- rendition.on('relocated', function (location) {
334
+ rendition.on("relocated", function (location) {
388
335
  var percent = book.locations.percentageFromCfi(location.start.cfi);
389
336
  var percentage = Math.floor(percent * 100);
390
337
  var chapter = getChapter(location);
391
338
 
392
- reactNativeWebview.postMessage(
393
- JSON.stringify({
394
- type: 'onLocationChange',
395
- totalLocations: book.locations.total,
396
- currentLocation: location,
397
- progress: percentage,
398
- currentSection: chapter,
399
- })
400
- );
339
+ reactNativeWebview.postMessage(JSON.stringify({
340
+ type: "onLocationChange",
341
+ totalLocations: book.locations.total,
342
+ currentLocation: location,
343
+ progress: percentage,
344
+ currentSection: chapter,
345
+ }));
401
346
 
402
347
  if (location.atStart) {
403
- reactNativeWebview.postMessage(
404
- JSON.stringify({
405
- type: 'onBeginning',
406
- })
407
- );
348
+ reactNativeWebview.postMessage(JSON.stringify({
349
+ type: "onBeginning",
350
+ }));
408
351
  }
409
352
 
410
353
  if (location.atEnd) {
411
- reactNativeWebview.postMessage(
412
- JSON.stringify({
413
- type: 'onFinish',
414
- })
415
- );
354
+ reactNativeWebview.postMessage(JSON.stringify({
355
+ type: "onFinish",
356
+ }));
416
357
  }
417
358
  });
418
359
 
419
- rendition.on('rendered', function (section) {
420
- reactNativeWebview.postMessage(
421
- JSON.stringify({
422
- type: 'onRendered',
423
- section: section,
424
- currentSection: book.navigation.get(section.href),
425
- })
426
- );
360
+ rendition.on("orientationchange", function (orientation) {
361
+ reactNativeWebview.postMessage(JSON.stringify({
362
+ type: 'onOrientationChange',
363
+ orientation: orientation
364
+ }));
365
+ });
366
+
367
+ rendition.on("rendered", function (section) {
368
+ reactNativeWebview.postMessage(JSON.stringify({
369
+ type: 'onRendered',
370
+ section: section,
371
+ currentSection: book.navigation.get(section.href),
372
+ }));
373
+ });
374
+
375
+ rendition.on("layout", function (layout) {
376
+ reactNativeWebview.postMessage(JSON.stringify({
377
+ type: 'onLayout',
378
+ layout: layout,
379
+ }));
380
+ });
381
+
382
+ rendition.on("selected", function (cfiRange, contents) {
383
+ book.getRange(cfiRange).then(function (range) {
384
+ if (range) {
385
+ reactNativeWebview.postMessage(JSON.stringify({
386
+ type: 'onSelected',
387
+ cfiRange: cfiRange,
388
+ text: range.toString(),
389
+ }));
390
+ }
391
+ });
427
392
  });
428
393
 
429
- rendition.on('layout', function (layout) {
430
- reactNativeWebview.postMessage(
431
- JSON.stringify({
432
- type: 'onLayout',
433
- layout: layout,
434
- })
435
- );
394
+ rendition.on("resized", function (layout) {
395
+ reactNativeWebview.postMessage(JSON.stringify({
396
+ type: 'onResized',
397
+ layout: layout,
398
+ }));
436
399
  });
437
400
  </script>
438
401
  </body>
@@ -2,41 +2,58 @@ import type { Theme } from '../types';
2
2
 
3
3
  export const defaultTheme: Theme = {
4
4
  'body': {
5
- background: '#FFF',
5
+ background: '#211F26',
6
6
  },
7
7
  'span': {
8
- color: '#000',
8
+ color: '#ffffff !important',
9
+ backgroundColor: '#211F26',
9
10
  },
10
11
  'p': {
11
- color: '#000',
12
+ color: '#ffffff !important',
13
+ backgroundColor: '#211F26',
12
14
  },
13
15
  'li': {
14
- color: '#000',
16
+ color: '#ffffff !important',
17
+ backgroundColor: '#211F26',
15
18
  },
16
19
  'h1': {
17
- color: '#000',
20
+ color: '#ffffff !important',
21
+ backgroundColor: '#211F26',
18
22
  },
19
23
  'h2': {
20
- color: '#000',
24
+ color: '#ffffff !important',
25
+ backgroundColor: '#211F26',
21
26
  },
22
27
  'h3': {
23
- color: '#000',
28
+ color: '#ffffff !important',
29
+ backgroundColor: '#211F26',
24
30
  },
25
31
  'h4': {
26
- color: '#000',
32
+ color: '#ffffff !important',
33
+ backgroundColor: '#211F26',
27
34
  },
28
35
  'h5': {
29
- color: '#000',
36
+ color: '#ffffff !important',
37
+ backgroundColor: '#211F26',
30
38
  },
31
39
  'h6': {
32
- color: '#000',
40
+ color: '#ffffff !important',
41
+ backgroundColor: '#211F26',
33
42
  },
34
43
  'a': {
35
- 'color': '#000',
44
+ 'color': '#ffffff !important',
45
+ 'backgroundColor': '#211F26',
36
46
  'pointer-events': 'auto',
37
47
  'cursor': 'pointer',
38
48
  },
49
+ '*': {
50
+ color: '#ffffff !important',
51
+ backgroundColor: '#211F26',
52
+ },
39
53
  '::selection': {
40
54
  background: 'lightskyblue',
41
55
  },
56
+ '::marker': {
57
+ color: '#ffffff !important',
58
+ },
42
59
  };
@@ -11,7 +11,13 @@ export const downloadEpub = async (
11
11
  return file.uri;
12
12
  }
13
13
 
14
- const downloadedFile = await File.downloadFileAsync(url, file);
14
+ const normalized = url.split('?X-Goog-Algorithm')[0];
15
+
16
+ if (!normalized) {
17
+ throw new Error('Invalid URL provided for EPUB download.');
18
+ }
19
+
20
+ const downloadedFile = await File.downloadFileAsync(normalized, file);
15
21
 
16
22
  return downloadedFile.uri;
17
23
  } catch (error) {