react-native-readium 1.0.2 → 1.0.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.
package/README.md CHANGED
@@ -154,7 +154,7 @@ DRM is not supported at this time. However, there is a clear path to [support it
154
154
  | Name | Type | Optional | Description |
155
155
  |------|------|----------|-------------|
156
156
  | `file` | [`File`](https://github.com/5-stones/react-native-readium/blob/main/src/interfaces/File.ts) | :x: | A file object containing the path to the eBook file on disk. |
157
- | `location` | [`Locator`](https://github.com/5-stones/react-native-readium/blob/main/src/interfaces/Locator.ts) | :white_check_mark: | A locator prop that allows you to externally control the location of the reader (e.g. Chapters or Bookmarks)|
157
+ | `location` | [`Locator`](https://github.com/5-stones/react-native-readium/blob/main/src/interfaces/Locator.ts) | [`Link`](https://github.com/5-stones/react-native-readium/blob/main/src/interfaces/Link.ts) | :white_check_mark: | A locator prop that allows you to externally control the location of the reader (e.g. Chapters or Bookmarks)|
158
158
  | `settings` | [`Partial<Settings>`](https://github.com/5-stones/react-native-readium/blob/main/src/interfaces/Settings.ts) | :white_check_mark: | An object that allows you to control various aspects of the reader's UI (epub only) |
159
159
  | `style` | `ViewStyle` | :white_check_mark: | A traditional style object. |
160
160
  | `onLocationChange` | `(locator: Locator) => void` | :white_check_mark: | A callback that fires whenever the location is changed (e.g. the user transitions to a new page)|
@@ -11,8 +11,8 @@ import com.reactnativereadium.reader.EpubReaderFragment
11
11
  import com.reactnativereadium.reader.ReaderViewModel
12
12
  import com.reactnativereadium.utils.Dimensions
13
13
  import com.reactnativereadium.utils.File
14
+ import com.reactnativereadium.utils.LinkOrLocator
14
15
  import org.readium.r2.shared.extensions.toMap
15
- import org.readium.r2.shared.publication.Locator
16
16
 
17
17
  class ReadiumView(
18
18
  val reactContext: ThemedReactContext
@@ -21,24 +21,35 @@ class ReadiumView(
21
21
  var file: File? = null
22
22
  var fragment: BaseReaderFragment? = null
23
23
  var isViewInitialized: Boolean = false
24
+ var lateInitSettings: Map<String, Any>? = null
24
25
 
25
- fun updateLocation(locator: Locator) : Boolean {
26
+ fun updateLocation(location: LinkOrLocator) : Boolean {
26
27
  if (fragment == null) {
27
28
  return false
28
29
  } else {
29
- return this.fragment!!.go(locator, true)
30
+ return this.fragment!!.go(location, true)
30
31
  }
31
32
  }
32
33
 
33
- fun updateSettingsFromMap(map: Map<String, Any>) {
34
- if (fragment != null && fragment is EpubReaderFragment) {
34
+ fun updateSettingsFromMap(map: Map<String, Any>?) {
35
+ if (map == null) {
36
+ return
37
+ } else if (fragment == null) {
38
+ lateInitSettings = map
39
+ return
40
+ }
41
+
42
+ if (fragment is EpubReaderFragment) {
35
43
  (fragment as EpubReaderFragment).updateSettingsFromMap(map)
36
44
  }
45
+
46
+ lateInitSettings = null
37
47
  }
38
48
 
39
49
  fun addFragment(frag: BaseReaderFragment) {
40
50
  fragment = frag
41
51
  setupLayout()
52
+ updateSettingsFromMap(lateInitSettings)
42
53
  val activity: FragmentActivity? = reactContext.currentActivity as FragmentActivity?
43
54
  activity!!.supportFragmentManager
44
55
  .beginTransaction()
@@ -8,11 +8,12 @@ import com.facebook.react.uimanager.ThemedReactContext
8
8
  import com.facebook.react.uimanager.ViewGroupManager
9
9
  import com.reactnativereadium.reader.ReaderService
10
10
  import com.reactnativereadium.utils.File
11
+ import com.reactnativereadium.utils.LinkOrLocator
11
12
  import kotlinx.coroutines.runBlocking
12
13
  import org.json.JSONObject
14
+ import org.readium.r2.shared.publication.Link
13
15
  import org.readium.r2.shared.publication.Locator
14
16
 
15
-
16
17
  class ReadiumViewManager(
17
18
  val reactContext: ReactApplicationContext
18
19
  ) : ViewGroupManager<ReadiumView>() {
@@ -69,23 +70,45 @@ class ReadiumViewManager(
69
70
  fun setFile(view: ReadiumView, file: ReadableMap) {
70
71
  val path = (file.getString("url") ?: "")
71
72
  .replace("^(file:/+)?(/.*)$".toRegex(), "$2")
72
- val locatorMap = file.getMap("initialLocation")
73
- var initialLocation: Locator? = null
73
+ val location = file.getMap("initialLocation")
74
+ var initialLocation: LinkOrLocator? = null
74
75
 
75
- if (locatorMap != null) {
76
- initialLocation = Locator.fromJSON(JSONObject(locatorMap.toHashMap()))
76
+ if (location != null) {
77
+ initialLocation = locationToLinkOrLocator(location)
77
78
  }
78
79
 
79
80
  view.file = File(path, initialLocation)
80
81
  this.buildForViewIfReady(view)
81
82
  }
82
83
 
84
+ fun locationToLinkOrLocator(location: ReadableMap): LinkOrLocator? {
85
+ val json = JSONObject(location.toHashMap())
86
+ val hasLocations = json.has("locations")
87
+ val hasChildren = json.has("children")
88
+ val hasHashHref = (json.get("href") as String).contains("#")
89
+ var linkOrLocator: LinkOrLocator? = null
90
+
91
+ if ((hasChildren || hasHashHref) && !hasLocations) {
92
+ val link = Link.fromJSON(json)
93
+ if (link != null) {
94
+ linkOrLocator = LinkOrLocator.Link(link)
95
+ }
96
+ } else {
97
+ val locator = Locator.fromJSON(json)
98
+ if (locator != null) {
99
+ linkOrLocator = LinkOrLocator.Locator(locator)
100
+ }
101
+ }
102
+
103
+ return linkOrLocator;
104
+ }
105
+
83
106
  @ReactProp(name = "location")
84
107
  fun setLocation(view: ReadiumView, location: ReadableMap) {
85
- val locator = Locator.fromJSON(JSONObject(location.toHashMap()))
108
+ var linkOrLocator: LinkOrLocator? = locationToLinkOrLocator(location)
86
109
 
87
- if (locator != null) {
88
- view.updateLocation(locator)
110
+ if (linkOrLocator != null) {
111
+ view.updateLocation(linkOrLocator)
89
112
  }
90
113
  }
91
114
 
@@ -4,13 +4,13 @@ import android.os.Bundle
4
4
  import android.view.*
5
5
  import androidx.fragment.app.Fragment
6
6
  import androidx.lifecycle.lifecycleScope
7
+ import com.reactnativereadium.utils.LinkOrLocator
7
8
  import kotlinx.coroutines.flow.launchIn
8
9
  import kotlinx.coroutines.flow.onEach
9
10
  import org.readium.r2.navigator.*
10
11
  import org.readium.r2.shared.publication.Locator
11
12
  import com.reactnativereadium.utils.EventChannel
12
13
  import kotlinx.coroutines.channels.Channel
13
- import org.readium.r2.shared.publication.Link
14
14
 
15
15
  /*
16
16
  * Base reader fragment class
@@ -49,7 +49,21 @@ abstract class BaseReaderFragment : Fragment() {
49
49
  requireActivity().invalidateOptionsMenu()
50
50
  }
51
51
 
52
- fun go(locator: Locator, animated: Boolean): Boolean {
52
+ fun go(location: LinkOrLocator, animated: Boolean): Boolean {
53
+ var locator: Locator? = null
54
+ when (location) {
55
+ is LinkOrLocator.Link -> {
56
+ locator = navigator.publication.locatorFromLink(location.link)
57
+ }
58
+ is LinkOrLocator.Locator -> {
59
+ locator = location.locator
60
+ }
61
+ }
62
+
63
+ if (locator == null) {
64
+ return false
65
+ }
66
+
53
67
  // don't attempt to navigate if we're already there
54
68
  val currentLocator = navigator.currentLocator.value
55
69
  if (locator.hashCode() == currentLocator.hashCode()) {
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
4
4
  import androidx.lifecycle.ViewModelStore
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
6
  import com.facebook.react.util.RNLog
7
+ import com.reactnativereadium.utils.LinkOrLocator
7
8
  import java.io.File
8
9
  import java.io.IOException
9
10
  import java.net.ServerSocket
@@ -46,9 +47,28 @@ class ReaderService(
46
47
  this.startServer()
47
48
  }
48
49
 
50
+ fun locatorFromLinkOrLocator(
51
+ location: LinkOrLocator?,
52
+ publication: Publication,
53
+ ): Locator? {
54
+
55
+ if (location == null) return null
56
+
57
+ when (location) {
58
+ is LinkOrLocator.Link -> {
59
+ return publication.locatorFromLink(location.link)
60
+ }
61
+ is LinkOrLocator.Locator -> {
62
+ return location.locator
63
+ }
64
+ }
65
+
66
+ return null
67
+ }
68
+
49
69
  suspend fun openPublication(
50
70
  fileName: String,
51
- initialLocation: Locator?,
71
+ initialLocation: LinkOrLocator?,
52
72
  callback: suspend (fragment: BaseReaderFragment) -> Unit
53
73
  ) {
54
74
  val file = File(fileName)
@@ -62,8 +82,9 @@ class ReaderService(
62
82
  .onSuccess {
63
83
  val url = prepareToServe(it)
64
84
  if (url != null) {
85
+ val locator = locatorFromLinkOrLocator(initialLocation, it)
65
86
  val readerFragment = EpubReaderFragment.newInstance(url)
66
- readerFragment.initFactory(it, initialLocation)
87
+ readerFragment.initFactory(it, locator)
67
88
  callback.invoke(readerFragment)
68
89
  }
69
90
  }
@@ -1,8 +1,6 @@
1
1
  package com.reactnativereadium.utils
2
2
 
3
- import org.readium.r2.shared.publication.Locator
4
-
5
3
  class File(
6
4
  var path: String,
7
- var initialLocation: Locator?
5
+ var initialLocation: LinkOrLocator?
8
6
  ) {}
@@ -0,0 +1,9 @@
1
+ package com.reactnativereadium.utils
2
+
3
+ import org.readium.r2.shared.publication.Link as BaseLink
4
+ import org.readium.r2.shared.publication.Locator as BaseLocator
5
+
6
+ sealed class LinkOrLocator {
7
+ class Link(val link: BaseLink): LinkOrLocator()
8
+ class Locator(val locator: BaseLocator): LinkOrLocator()
9
+ }
@@ -19,11 +19,40 @@ final class ReaderService: Loggable {
19
19
  print(error)
20
20
  }
21
21
  }
22
+
23
+ static func locatorFromLocation(
24
+ _ location: NSDictionary?,
25
+ _ publication: Publication?
26
+ ) -> Locator? {
27
+ guard location != nil else {
28
+ return nil
29
+ }
30
+
31
+ let hasLocations = location?["locations"] != nil
32
+ let hasChildren = location?["children"] != nil
33
+ let hasHashHref = (location!["href"] as! String).contains("#")
34
+
35
+ // check that we're not dealing with a Link
36
+ if ((hasChildren || hasHashHref) && !hasLocations) {
37
+ guard let publication = publication else {
38
+ return nil
39
+ }
40
+ guard let link = try? Link(json: location) else {
41
+ return nil
42
+ }
43
+
44
+ return publication.locate(link)
45
+ } else {
46
+ return try? Locator(json: location)
47
+ }
48
+
49
+ return nil
50
+ }
22
51
 
23
52
  func buildViewController(
24
53
  url: String,
25
54
  bookId: String,
26
- locator: Locator?,
55
+ location: NSDictionary?,
27
56
  sender: UIViewController?,
28
57
  completion: @escaping (ReaderViewController) -> Void
29
58
  ) {
@@ -37,6 +66,7 @@ final class ReaderService: Loggable {
37
66
  },
38
67
  receiveValue: { pub in
39
68
  self.preparePresentation(of: pub)
69
+ let locator: Locator? = ReaderService.locatorFromLocation(location, pub)
40
70
  let vc = reader.getViewController(
41
71
  for: pub,
42
72
  bookId: bookId,
@@ -17,12 +17,9 @@ class ReadiumView : UIView, Loggable {
17
17
 
18
18
  @objc var file: NSDictionary? = nil {
19
19
  didSet {
20
- if let initialLocation = file?["initialLocation"] as? NSDictionary {
21
- location = initialLocation
22
- }
23
-
20
+ let initialLocation = file?["initialLocation"] as? NSDictionary
24
21
  if let url = file?["url"] as? String {
25
- self.loadBook(url: url)
22
+ self.loadBook(url: url, location: initialLocation)
26
23
  }
27
24
  }
28
25
  }
@@ -39,27 +36,26 @@ class ReadiumView : UIView, Loggable {
39
36
  @objc var onLocationChange: RCTDirectEventBlock?
40
37
  @objc var onTableOfContents: RCTDirectEventBlock?
41
38
 
42
- func loadBook(url: String) {
39
+ func loadBook(
40
+ url: String,
41
+ location: NSDictionary?
42
+ ) {
43
43
  guard let rootViewController = UIApplication.shared.delegate?.window??.rootViewController else { return }
44
- let locator: Locator? = self.getLocator()
45
44
 
46
45
  self.readerService.buildViewController(
47
46
  url: url,
48
47
  bookId: url,
49
- locator: locator,
48
+ location: location,
50
49
  sender: rootViewController,
51
50
  completion: { vc in
52
51
  self.addViewControllerAsSubview(vc)
52
+ self.location = location
53
53
  }
54
54
  )
55
55
  }
56
56
 
57
57
  func getLocator() -> Locator? {
58
- if (location != nil) {
59
- return try? Locator(json: location)
60
- } else {
61
- return nil
62
- }
58
+ return ReaderService.locatorFromLocation(location, readerViewController?.publication)
63
59
  }
64
60
 
65
61
  func updateLocation() {
@@ -1 +1 @@
1
- {"version":3,"sources":["BaseReadiumView.tsx"],"names":["BaseReadiumView","UIManager","getViewManagerConfig","COMPONENT_NAME","Error","LINKING_ERROR"],"mappings":";;;;;;;AAAA;;AAOA;;AAcO,MAAMA,eAAe,GAC1BC,uBAAUC,oBAAV,CAA+BC,qBAA/B,KAAkD,IAAlD,GACI,yCAA6CA,qBAA7C,CADJ,GAEI,MAAM;AACJ,QAAM,IAAIC,KAAJ,CAAUC,oBAAV,CAAN;AACD,CALA","sourcesContent":["import {\n requireNativeComponent,\n UIManager,\n ViewStyle,\n} from 'react-native';\n\nimport type { Settings, Link, Locator, File, } from '../interfaces';\nimport { COMPONENT_NAME, LINKING_ERROR } from '../utils';\n\nexport type BaseReadiumViewProps = {\n file: File;\n location?: Locator;\n settings?: Partial<Settings>;\n style?: ViewStyle;\n onLocationChange?: (locator: Locator) => void;\n onTableOfContents?: (toc: Link[] | null) => void;\n ref?: any;\n height?: number;\n width?: number;\n};\n\nexport const BaseReadiumView =\n UIManager.getViewManagerConfig(COMPONENT_NAME) != null\n ? requireNativeComponent<BaseReadiumViewProps>(COMPONENT_NAME)\n : () => {\n throw new Error(LINKING_ERROR);\n };\n"]}
1
+ {"version":3,"sources":["BaseReadiumView.tsx"],"names":["BaseReadiumView","UIManager","getViewManagerConfig","COMPONENT_NAME","Error","LINKING_ERROR"],"mappings":";;;;;;;AAAA;;AAOA;;AAcO,MAAMA,eAAe,GAC1BC,uBAAUC,oBAAV,CAA+BC,qBAA/B,KAAkD,IAAlD,GACI,yCAA6CA,qBAA7C,CADJ,GAEI,MAAM;AACJ,QAAM,IAAIC,KAAJ,CAAUC,oBAAV,CAAN;AACD,CALA","sourcesContent":["import {\n requireNativeComponent,\n UIManager,\n ViewStyle,\n} from 'react-native';\n\nimport type { Settings, Link, Locator, File, } from '../interfaces';\nimport { COMPONENT_NAME, LINKING_ERROR } from '../utils';\n\nexport type BaseReadiumViewProps = {\n file: File;\n location?: Locator | Link;\n settings?: Partial<Settings>;\n style?: ViewStyle;\n onLocationChange?: (locator: Locator) => void;\n onTableOfContents?: (toc: Link[] | null) => void;\n ref?: any;\n height?: number;\n width?: number;\n};\n\nexport const BaseReadiumView =\n UIManager.getViewManagerConfig(COMPONENT_NAME) != null\n ? requireNativeComponent<BaseReadiumViewProps>(COMPONENT_NAME)\n : () => {\n throw new Error(LINKING_ERROR);\n };\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["BaseReadiumView.tsx"],"names":["requireNativeComponent","UIManager","COMPONENT_NAME","LINKING_ERROR","BaseReadiumView","getViewManagerConfig","Error"],"mappings":"AAAA,SACEA,sBADF,EAEEC,SAFF,QAIO,cAJP;AAOA,SAASC,cAAT,EAAyBC,aAAzB,QAA8C,UAA9C;AAcA,OAAO,MAAMC,eAAe,GAC1BH,SAAS,CAACI,oBAAV,CAA+BH,cAA/B,KAAkD,IAAlD,GACIF,sBAAsB,CAAuBE,cAAvB,CAD1B,GAEI,MAAM;AACJ,QAAM,IAAII,KAAJ,CAAUH,aAAV,CAAN;AACD,CALA","sourcesContent":["import {\n requireNativeComponent,\n UIManager,\n ViewStyle,\n} from 'react-native';\n\nimport type { Settings, Link, Locator, File, } from '../interfaces';\nimport { COMPONENT_NAME, LINKING_ERROR } from '../utils';\n\nexport type BaseReadiumViewProps = {\n file: File;\n location?: Locator;\n settings?: Partial<Settings>;\n style?: ViewStyle;\n onLocationChange?: (locator: Locator) => void;\n onTableOfContents?: (toc: Link[] | null) => void;\n ref?: any;\n height?: number;\n width?: number;\n};\n\nexport const BaseReadiumView =\n UIManager.getViewManagerConfig(COMPONENT_NAME) != null\n ? requireNativeComponent<BaseReadiumViewProps>(COMPONENT_NAME)\n : () => {\n throw new Error(LINKING_ERROR);\n };\n"]}
1
+ {"version":3,"sources":["BaseReadiumView.tsx"],"names":["requireNativeComponent","UIManager","COMPONENT_NAME","LINKING_ERROR","BaseReadiumView","getViewManagerConfig","Error"],"mappings":"AAAA,SACEA,sBADF,EAEEC,SAFF,QAIO,cAJP;AAOA,SAASC,cAAT,EAAyBC,aAAzB,QAA8C,UAA9C;AAcA,OAAO,MAAMC,eAAe,GAC1BH,SAAS,CAACI,oBAAV,CAA+BH,cAA/B,KAAkD,IAAlD,GACIF,sBAAsB,CAAuBE,cAAvB,CAD1B,GAEI,MAAM;AACJ,QAAM,IAAII,KAAJ,CAAUH,aAAV,CAAN;AACD,CALA","sourcesContent":["import {\n requireNativeComponent,\n UIManager,\n ViewStyle,\n} from 'react-native';\n\nimport type { Settings, Link, Locator, File, } from '../interfaces';\nimport { COMPONENT_NAME, LINKING_ERROR } from '../utils';\n\nexport type BaseReadiumViewProps = {\n file: File;\n location?: Locator | Link;\n settings?: Partial<Settings>;\n style?: ViewStyle;\n onLocationChange?: (locator: Locator) => void;\n onTableOfContents?: (toc: Link[] | null) => void;\n ref?: any;\n height?: number;\n width?: number;\n};\n\nexport const BaseReadiumView =\n UIManager.getViewManagerConfig(COMPONENT_NAME) != null\n ? requireNativeComponent<BaseReadiumViewProps>(COMPONENT_NAME)\n : () => {\n throw new Error(LINKING_ERROR);\n };\n"]}
@@ -2,7 +2,7 @@ import { ViewStyle } from 'react-native';
2
2
  import type { Settings, Link, Locator, File } from '../interfaces';
3
3
  export declare type BaseReadiumViewProps = {
4
4
  file: File;
5
- location?: Locator;
5
+ location?: Locator | Link;
6
6
  settings?: Partial<Settings>;
7
7
  style?: ViewStyle;
8
8
  onLocationChange?: (locator: Locator) => void;
@@ -1,3 +1,4 @@
1
+ import type { Link } from './Link';
1
2
  import type { Locator } from './Locator';
2
3
  export interface File {
3
4
  /**
@@ -7,5 +8,5 @@ export interface File {
7
8
  /**
8
9
  * An optional location that the eBook will be opened at.
9
10
  */
10
- initialLocation?: Locator;
11
+ initialLocation?: Locator | Link;
11
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-readium",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "A react-native wrapper for https://readium.org/",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -9,7 +9,7 @@ import { COMPONENT_NAME, LINKING_ERROR } from '../utils';
9
9
 
10
10
  export type BaseReadiumViewProps = {
11
11
  file: File;
12
- location?: Locator;
12
+ location?: Locator | Link;
13
13
  settings?: Partial<Settings>;
14
14
  style?: ViewStyle;
15
15
  onLocationChange?: (locator: Locator) => void;
@@ -1,3 +1,4 @@
1
+ import type { Link } from './Link';
1
2
  import type { Locator } from './Locator';
2
3
 
3
4
 
@@ -10,5 +11,5 @@ export interface File {
10
11
  /**
11
12
  * An optional location that the eBook will be opened at.
12
13
  */
13
- initialLocation?: Locator;
14
+ initialLocation?: Locator | Link;
14
15
  }