react-native-readium 5.0.0-rc.10 → 5.0.0-rc.12

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 (34) hide show
  1. package/README.md +11 -7
  2. package/android/src/main/java/com/reactnativereadium/HybridReadiumView.kt +12 -15
  3. package/ios/HybridReadiumView.swift +9 -24
  4. package/lib/nitrogen/generated/shared/json/ReadiumViewConfig.json +0 -1
  5. package/lib/src/components/ReadiumView.js +1 -0
  6. package/lib/src/components/ReadiumView.types.d.ts +1 -1
  7. package/lib/src/components/ReadiumView.web.js +12 -3
  8. package/lib/src/specs/ReadiumView.nitro.d.ts +1 -1
  9. package/lib/web/hooks/index.d.ts +0 -1
  10. package/lib/web/hooks/index.js +0 -1
  11. package/lib/web/hooks/useNavigator.js +12 -1
  12. package/nitrogen/generated/android/c++/JHybridReadiumViewSpec.cpp +4 -9
  13. package/nitrogen/generated/android/c++/JHybridReadiumViewSpec.hpp +1 -2
  14. package/nitrogen/generated/android/c++/views/JHybridReadiumViewStateUpdater.cpp +0 -4
  15. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativereadium/HybridReadiumViewSpec.kt +4 -6
  16. package/nitrogen/generated/ios/c++/HybridReadiumViewSpecSwift.hpp +6 -7
  17. package/nitrogen/generated/ios/c++/views/HybridReadiumViewComponent.mm +0 -5
  18. package/nitrogen/generated/ios/swift/HybridReadiumViewSpec.swift +1 -1
  19. package/nitrogen/generated/ios/swift/HybridReadiumViewSpec_cxx.swift +11 -17
  20. package/nitrogen/generated/shared/c++/HybridReadiumViewSpec.cpp +1 -2
  21. package/nitrogen/generated/shared/c++/HybridReadiumViewSpec.hpp +4 -5
  22. package/nitrogen/generated/shared/c++/views/HybridReadiumViewComponent.cpp +0 -11
  23. package/nitrogen/generated/shared/c++/views/HybridReadiumViewComponent.hpp +1 -2
  24. package/nitrogen/generated/shared/json/ReadiumViewConfig.json +0 -1
  25. package/package.json +1 -1
  26. package/src/components/ReadiumView.tsx +1 -0
  27. package/src/components/ReadiumView.types.ts +1 -1
  28. package/src/components/ReadiumView.web.tsx +9 -3
  29. package/src/specs/ReadiumView.nitro.ts +1 -1
  30. package/web/hooks/index.ts +0 -1
  31. package/web/hooks/useNavigator.ts +11 -1
  32. package/lib/web/hooks/useLocationObserver.d.ts +0 -3
  33. package/lib/web/hooks/useLocationObserver.js +0 -22
  34. package/web/hooks/useLocationObserver.ts +0 -34
package/README.md CHANGED
@@ -326,8 +326,7 @@ DRM is not supported at this time. However, there is a clear path to [support it
326
326
 
327
327
  | Name | Type | Optional | Description |
328
328
  | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
329
- | `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. |
330
- | `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). <br/><br/>:warning: If you want to set the `location` of an ebook on initial load, you should use the `File.initialLocation` property (look at the `file` prop). See more [here](https://github.com/5-stones/react-native-readium/issues/16#issuecomment-1344128937) |
329
+ | `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. Use `File.initialLocation` to set the reader's position on mount. |
331
330
  | `preferences` | [`Partial<Preferences>`](https://github.com/readium/swift-toolkit/blob/main/docs/Guides/Navigator%20Preferences.md#appendix-preference-constraints) | :white_check_mark: | An object that allows you to control various aspects of the reader's UI (epub only) |
332
331
  | `decorations` | [`DecorationGroup[]`](https://github.com/5-stones/react-native-readium/blob/main/src/interfaces/Decoration.ts) | :white_check_mark: | An array of decoration groups to render in the publication (e.g. highlights, underlines). |
333
332
  | `selectionActions` | [`SelectionAction[]`](https://github.com/5-stones/react-native-readium/blob/main/src/interfaces/SelectionAction.ts) | :white_check_mark: | Custom actions to show in the context menu when the user selects text. |
@@ -345,11 +344,15 @@ The `ReadiumView` component accepts a ref that exposes imperative navigation met
345
344
  ```tsx
346
345
  import React, { useRef } from 'react';
347
346
  import { ReadiumView } from 'react-native-readium';
348
- import type { ReadiumViewRef } from 'react-native-readium';
347
+ import type { ReadiumViewRef, Locator } from 'react-native-readium';
349
348
 
350
349
  const MyComponent: React.FC = () => {
351
350
  const ref = useRef<ReadiumViewRef>(null);
352
351
 
352
+ const goToChapter = (locator: Locator) => {
353
+ ref.current?.goTo(locator);
354
+ };
355
+
353
356
  return (
354
357
  <>
355
358
  <ReadiumView ref={ref} file={file} />
@@ -360,10 +363,11 @@ const MyComponent: React.FC = () => {
360
363
  };
361
364
  ```
362
365
 
363
- | Method | Description |
364
- | ------------- | -------------------------------------------------------- |
365
- | `goForward()` | Navigate forward in the publication (e.g. next page). |
366
- | `goBackward()`| Navigate backward in the publication (e.g. previous page). |
366
+ | Method | Description |
367
+ | ------------------ | -------------------------------------------------------------------------------- |
368
+ | `goTo(locator)` | Navigate to a specific location in the publication (e.g. a chapter or bookmark). |
369
+ | `goForward()` | Navigate forward in the publication (e.g. next page). |
370
+ | `goBackward()` | Navigate backward in the publication (e.g. previous page). |
367
371
 
368
372
  #### :warning: Web vs Native File URLs
369
373
 
@@ -79,12 +79,6 @@ class HybridReadiumView(private val context: android.content.Context) : HybridRe
79
79
  }
80
80
  }
81
81
 
82
- override var location: Locator? = null
83
- set(value) {
84
- field = value
85
- updateLocation()
86
- }
87
-
88
82
  override var preferences: Preferences? = null
89
83
  set(value) {
90
84
  field = value
@@ -118,15 +112,6 @@ class HybridReadiumView(private val context: android.content.Context) : HybridRe
118
112
  }
119
113
  }
120
114
 
121
- // MARK: - Location
122
-
123
- private fun updateLocation() {
124
- val loc = location ?: return
125
- val frag = fragment ?: return
126
- val readiumLocator = nitroLocatorToReadium(loc) ?: return
127
- frag.go(com.reactnativereadium.utils.LinkOrLocator.Locator(readiumLocator), true)
128
- }
129
-
130
115
  // MARK: - Preferences
131
116
 
132
117
  private fun updatePreferences() {
@@ -159,6 +144,18 @@ class HybridReadiumView(private val context: android.content.Context) : HybridRe
159
144
 
160
145
  // MARK: - Imperative navigation
161
146
 
147
+ override fun goTo(locator: Locator) {
148
+ val action = Runnable {
149
+ val readiumLocator = nitroLocatorToReadium(locator) ?: return@Runnable
150
+ fragment?.go(com.reactnativereadium.utils.LinkOrLocator.Locator(readiumLocator), true)
151
+ }
152
+ if (android.os.Looper.myLooper() == android.os.Looper.getMainLooper()) {
153
+ action.run()
154
+ } else {
155
+ hostView.post(action)
156
+ }
157
+ }
158
+
162
159
  override fun goForward() { fragment?.goForward() }
163
160
  override fun goBackward() { fragment?.goBackward() }
164
161
  override fun destroy() {
@@ -21,12 +21,6 @@ class HybridReadiumView: HybridReadiumViewSpec {
21
21
  }
22
22
  }
23
23
 
24
- var location: Locator? = nil {
25
- didSet {
26
- updateLocation()
27
- }
28
- }
29
-
30
24
  var preferences: Preferences? = nil {
31
25
  didSet {
32
26
  updatePreferences()
@@ -115,24 +109,6 @@ class HybridReadiumView: HybridReadiumViewSpec {
115
109
  )
116
110
  }
117
111
 
118
- // MARK: - Location
119
-
120
- private func updateLocation() {
121
- Task { @MainActor [weak self] in
122
- guard let self = self else { return }
123
- guard let navigator = self.readerViewController?.navigator else { return }
124
- guard let loc = self.location else { return }
125
- guard let locator = nitroLocatorToReadium(loc) else { return }
126
-
127
- let currentLocation = navigator.currentLocation
128
- if let currentLocation, locator.hashValue == currentLocation.hashValue {
129
- return
130
- }
131
-
132
- _ = await navigator.go(to: locator, options: .animated)
133
- }
134
- }
135
-
136
112
  // MARK: - Preferences
137
113
 
138
114
  private func updatePreferences() {
@@ -267,6 +243,15 @@ class HybridReadiumView: HybridReadiumViewSpec {
267
243
 
268
244
  // MARK: - Imperative navigation
269
245
 
246
+ func goTo(locator: Locator) {
247
+ Task { @MainActor [weak self] in
248
+ guard let self else { return }
249
+ guard let navigator = self.readerViewController?.navigator else { return }
250
+ guard let readiumLocator = nitroLocatorToReadium(locator) else { return }
251
+ _ = await navigator.go(to: readiumLocator, options: .animated)
252
+ }
253
+ }
254
+
270
255
  func goForward() {
271
256
  Task { @MainActor in
272
257
  guard let navigator = readerViewController?.navigator else { return }
@@ -5,7 +5,6 @@
5
5
  "directEventTypes": {},
6
6
  "validAttributes": {
7
7
  "file": true,
8
- "location": true,
9
8
  "preferences": true,
10
9
  "decorations": true,
11
10
  "selectionActions": true,
@@ -25,6 +25,7 @@ export const ReadiumView = forwardRef(({ onLocationChange, onPublicationReady, o
25
25
  });
26
26
  }, [onPublicationReady]);
27
27
  useImperativeHandle(forwardedRef, () => ({
28
+ goTo: (locator) => hybridRef.current?.goTo(locator),
28
29
  goForward: () => hybridRef.current?.goForward(),
29
30
  goBackward: () => hybridRef.current?.goBackward(),
30
31
  }), []);
@@ -1,11 +1,11 @@
1
1
  import type { Preferences, Locator, File, DecorationGroup, SelectionAction, PublicationReadyEvent, DecorationActivatedEvent, SelectionEvent, SelectionActionEvent } from '../interfaces';
2
2
  export type ReadiumViewRef = {
3
+ goTo: (locator: Locator) => void;
3
4
  goForward: () => void;
4
5
  goBackward: () => void;
5
6
  };
6
7
  export type ReadiumProps = {
7
8
  file: File;
8
- location?: Locator;
9
9
  preferences: Preferences;
10
10
  decorations?: DecorationGroup[];
11
11
  selectionActions?: SelectionAction[];
@@ -1,7 +1,8 @@
1
1
  import React, { useEffect, useImperativeHandle, useState } from 'react';
2
2
  import { View, StyleSheet } from 'react-native';
3
- import { useNavigator, usePreferencesObserver, useLocationObserver, useDecorationsObserver, } from '../../web/hooks';
4
- export const ReadiumView = React.forwardRef(({ file, preferences, location, decorations, onLocationChange, onPublicationReady, onDecorationActivated, style = {}, height, width, }, ref) => {
3
+ import { useNavigator, usePreferencesObserver, useDecorationsObserver, } from '../../web/hooks';
4
+ import { convertToNavigatorLocator } from '../../web/utils/locationNormalizer';
5
+ export const ReadiumView = React.forwardRef(({ file, preferences, decorations, onLocationChange, onPublicationReady, onDecorationActivated, style = {}, height, width, }, ref) => {
5
6
  const [container, setContainer] = useState(null);
6
7
  const [currentPosition, setCurrentPosition] = useState(null);
7
8
  // Convert DecorationGroup[] to DecorationGroups record for web hooks
@@ -16,6 +17,15 @@ export const ReadiumView = React.forwardRef(({ file, preferences, location, deco
16
17
  onPositionChange: setCurrentPosition,
17
18
  });
18
19
  useImperativeHandle(ref, () => ({
20
+ goTo: (locator) => {
21
+ if (!navigator)
22
+ return;
23
+ const navLocator = convertToNavigatorLocator(locator);
24
+ if (navLocator) {
25
+ // @ts-ignore
26
+ navigator.go(navLocator, true, () => { });
27
+ }
28
+ },
19
29
  goForward: () => {
20
30
  navigator?.goForward(true, () => { });
21
31
  },
@@ -32,7 +42,6 @@ export const ReadiumView = React.forwardRef(({ file, preferences, location, deco
32
42
  },
33
43
  }), [navigator]);
34
44
  usePreferencesObserver(navigator, preferences);
35
- useLocationObserver(navigator, location);
36
45
  useDecorationsObserver(navigator, decorationsRecord, onDecorationActivated);
37
46
  // Generate position label text
38
47
  const positionLabel = currentPosition && positions.length > 0
@@ -180,7 +180,6 @@ export interface ReadiumFile {
180
180
  }
181
181
  export interface ReadiumViewProps extends HybridViewProps {
182
182
  file?: ReadiumFile;
183
- location?: Locator;
184
183
  preferences?: Preferences;
185
184
  decorations?: DecorationGroup[];
186
185
  selectionActions?: SelectionAction[];
@@ -191,6 +190,7 @@ export interface ReadiumViewProps extends HybridViewProps {
191
190
  onSelectionAction?: (event: SelectionActionEvent) => void;
192
191
  }
193
192
  export interface ReadiumViewMethods extends HybridViewMethods {
193
+ goTo(locator: Locator): void;
194
194
  goForward(): void;
195
195
  goBackward(): void;
196
196
  destroy(): void;
@@ -1,5 +1,4 @@
1
1
  export * from './useNavigator';
2
2
  export * from './usePreferencesObserver';
3
- export * from './useLocationObserver';
4
3
  export * from './usePositionLabel';
5
4
  export * from './useDecorationsObserver';
@@ -1,5 +1,4 @@
1
1
  export * from './useNavigator';
2
2
  export * from './usePreferencesObserver';
3
- export * from './useLocationObserver';
4
3
  export * from './usePositionLabel';
5
4
  export * from './useDecorationsObserver';
@@ -53,7 +53,18 @@ export const useNavigator = ({ file, onLocationChange, onPublicationReady, conta
53
53
  // 3. Create the publication
54
54
  const publication = new Publication({ manifest, fetcher });
55
55
  // 4. Create positions array for navigation
56
- const positionsArray = createPositions(publication);
56
+ // Try loading granular positions from manifest (generated server-side),
57
+ // fall back to chapter-based positions for older manifests
58
+ let positionsArray;
59
+ try {
60
+ const manifestPositions = await publication.positionsFromManifest();
61
+ positionsArray = manifestPositions.length > 0
62
+ ? manifestPositions
63
+ : createPositions(publication);
64
+ }
65
+ catch {
66
+ positionsArray = createPositions(publication);
67
+ }
57
68
  readingOrder.current = positionsArray;
58
69
  setPositions(positionsArray);
59
70
  // 5. Create navigator listeners
@@ -157,15 +157,6 @@ namespace margelo::nitro::readium {
157
157
  static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<JReadiumFile> /* file */)>("setFile");
158
158
  method(_javaPart, file.has_value() ? JReadiumFile::fromCpp(file.value()) : nullptr);
159
159
  }
160
- std::optional<Locator> JHybridReadiumViewSpec::getLocation() {
161
- static const auto method = javaClassStatic()->getMethod<jni::local_ref<JLocator>()>("getLocation");
162
- auto __result = method(_javaPart);
163
- return __result != nullptr ? std::make_optional(__result->toCpp()) : std::nullopt;
164
- }
165
- void JHybridReadiumViewSpec::setLocation(const std::optional<Locator>& location) {
166
- static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<JLocator> /* location */)>("setLocation");
167
- method(_javaPart, location.has_value() ? JLocator::fromCpp(location.value()) : nullptr);
168
- }
169
160
  std::optional<Preferences> JHybridReadiumViewSpec::getPreferences() {
170
161
  static const auto method = javaClassStatic()->getMethod<jni::local_ref<JPreferences>()>("getPreferences");
171
162
  auto __result = method(_javaPart);
@@ -316,6 +307,10 @@ namespace margelo::nitro::readium {
316
307
  }
317
308
 
318
309
  // Methods
310
+ void JHybridReadiumViewSpec::goTo(const Locator& locator) {
311
+ static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<JLocator> /* locator */)>("goTo");
312
+ method(_javaPart, JLocator::fromCpp(locator));
313
+ }
319
314
  void JHybridReadiumViewSpec::goForward() {
320
315
  static const auto method = javaClassStatic()->getMethod<void()>("goForward");
321
316
  method(_javaPart);
@@ -53,8 +53,6 @@ namespace margelo::nitro::readium {
53
53
  // Properties
54
54
  std::optional<ReadiumFile> getFile() override;
55
55
  void setFile(const std::optional<ReadiumFile>& file) override;
56
- std::optional<Locator> getLocation() override;
57
- void setLocation(const std::optional<Locator>& location) override;
58
56
  std::optional<Preferences> getPreferences() override;
59
57
  void setPreferences(const std::optional<Preferences>& preferences) override;
60
58
  std::optional<std::vector<DecorationGroup>> getDecorations() override;
@@ -74,6 +72,7 @@ namespace margelo::nitro::readium {
74
72
 
75
73
  public:
76
74
  // Methods
75
+ void goTo(const Locator& locator) override;
77
76
  void goForward() override;
78
77
  void goBackward() override;
79
78
  void destroy() override;
@@ -41,10 +41,6 @@ void JHybridReadiumViewStateUpdater::updateViewProps(jni::alias_ref<jni::JClass>
41
41
  view->setFile(props->file.value);
42
42
  props->file.isDirty = false;
43
43
  }
44
- if (props->location.isDirty) {
45
- view->setLocation(props->location.value);
46
- props->location.isDirty = false;
47
- }
48
44
  if (props->preferences.isDirty) {
49
45
  view->setPreferences(props->preferences.value);
50
46
  props->preferences.isDirty = false;
@@ -48,12 +48,6 @@ abstract class HybridReadiumViewSpec: HybridView() {
48
48
  @set:Keep
49
49
  abstract var file: ReadiumFile?
50
50
 
51
- @get:DoNotStrip
52
- @get:Keep
53
- @set:DoNotStrip
54
- @set:Keep
55
- abstract var location: Locator?
56
-
57
51
  @get:DoNotStrip
58
52
  @get:Keep
59
53
  @set:DoNotStrip
@@ -143,6 +137,10 @@ abstract class HybridReadiumViewSpec: HybridView() {
143
137
  }
144
138
 
145
139
  // Methods
140
+ @DoNotStrip
141
+ @Keep
142
+ abstract fun goTo(locator: Locator): Unit
143
+
146
144
  @DoNotStrip
147
145
  @Keep
148
146
  abstract fun goForward(): Unit
@@ -141,13 +141,6 @@ namespace margelo::nitro::readium {
141
141
  inline void setFile(const std::optional<ReadiumFile>& file) noexcept override {
142
142
  _swiftPart.setFile(file);
143
143
  }
144
- inline std::optional<Locator> getLocation() noexcept override {
145
- auto __result = _swiftPart.getLocation();
146
- return __result;
147
- }
148
- inline void setLocation(const std::optional<Locator>& location) noexcept override {
149
- _swiftPart.setLocation(location);
150
- }
151
144
  inline std::optional<Preferences> getPreferences() noexcept override {
152
145
  auto __result = _swiftPart.getPreferences();
153
146
  return __result;
@@ -207,6 +200,12 @@ namespace margelo::nitro::readium {
207
200
 
208
201
  public:
209
202
  // Methods
203
+ inline void goTo(const Locator& locator) override {
204
+ auto __result = _swiftPart.goTo(std::forward<decltype(locator)>(locator));
205
+ if (__result.hasError()) [[unlikely]] {
206
+ std::rethrow_exception(__result.error());
207
+ }
208
+ }
210
209
  inline void goForward() override {
211
210
  auto __result = _swiftPart.goForward();
212
211
  if (__result.hasError()) [[unlikely]] {
@@ -77,11 +77,6 @@ using namespace margelo::nitro::readium::views;
77
77
  swiftPart.setFile(newViewProps.file.value);
78
78
  newViewProps.file.isDirty = false;
79
79
  }
80
- // location: optional
81
- if (newViewProps.location.isDirty) {
82
- swiftPart.setLocation(newViewProps.location.value);
83
- newViewProps.location.isDirty = false;
84
- }
85
80
  // preferences: optional
86
81
  if (newViewProps.preferences.isDirty) {
87
82
  swiftPart.setPreferences(newViewProps.preferences.value);
@@ -11,7 +11,6 @@ import NitroModules
11
11
  public protocol HybridReadiumViewSpec_protocol: HybridObject, HybridView {
12
12
  // Properties
13
13
  var file: ReadiumFile? { get set }
14
- var location: Locator? { get set }
15
14
  var preferences: Preferences? { get set }
16
15
  var decorations: [DecorationGroup]? { get set }
17
16
  var selectionActions: [SelectionAction]? { get set }
@@ -22,6 +21,7 @@ public protocol HybridReadiumViewSpec_protocol: HybridObject, HybridView {
22
21
  var onSelectionAction: ((_ event: SelectionActionEvent) -> Void)? { get set }
23
22
 
24
23
  // Methods
24
+ func goTo(locator: Locator) throws -> Void
25
25
  func goForward() throws -> Void
26
26
  func goBackward() throws -> Void
27
27
  func destroy() throws -> Void
@@ -138,23 +138,6 @@ open class HybridReadiumViewSpec_cxx {
138
138
  }
139
139
  }
140
140
 
141
- public final var location: bridge.std__optional_Locator_ {
142
- @inline(__always)
143
- get {
144
- return { () -> bridge.std__optional_Locator_ in
145
- if let __unwrappedValue = self.__implementation.location {
146
- return bridge.create_std__optional_Locator_(__unwrappedValue)
147
- } else {
148
- return .init()
149
- }
150
- }()
151
- }
152
- @inline(__always)
153
- set {
154
- self.__implementation.location = newValue.value
155
- }
156
- }
157
-
158
141
  public final var preferences: bridge.std__optional_Preferences_ {
159
142
  @inline(__always)
160
143
  get {
@@ -393,6 +376,17 @@ open class HybridReadiumViewSpec_cxx {
393
376
  }
394
377
 
395
378
  // Methods
379
+ @inline(__always)
380
+ public final func goTo(locator: Locator) -> bridge.Result_void_ {
381
+ do {
382
+ try self.__implementation.goTo(locator: locator)
383
+ return bridge.create_Result_void_()
384
+ } catch (let __error) {
385
+ let __exceptionPtr = __error.toCpp()
386
+ return bridge.create_Result_void_(__exceptionPtr)
387
+ }
388
+ }
389
+
396
390
  @inline(__always)
397
391
  public final func goForward() -> bridge.Result_void_ {
398
392
  do {
@@ -16,8 +16,6 @@ namespace margelo::nitro::readium {
16
16
  registerHybrids(this, [](Prototype& prototype) {
17
17
  prototype.registerHybridGetter("file", &HybridReadiumViewSpec::getFile);
18
18
  prototype.registerHybridSetter("file", &HybridReadiumViewSpec::setFile);
19
- prototype.registerHybridGetter("location", &HybridReadiumViewSpec::getLocation);
20
- prototype.registerHybridSetter("location", &HybridReadiumViewSpec::setLocation);
21
19
  prototype.registerHybridGetter("preferences", &HybridReadiumViewSpec::getPreferences);
22
20
  prototype.registerHybridSetter("preferences", &HybridReadiumViewSpec::setPreferences);
23
21
  prototype.registerHybridGetter("decorations", &HybridReadiumViewSpec::getDecorations);
@@ -34,6 +32,7 @@ namespace margelo::nitro::readium {
34
32
  prototype.registerHybridSetter("onSelectionChange", &HybridReadiumViewSpec::setOnSelectionChange);
35
33
  prototype.registerHybridGetter("onSelectionAction", &HybridReadiumViewSpec::getOnSelectionAction);
36
34
  prototype.registerHybridSetter("onSelectionAction", &HybridReadiumViewSpec::setOnSelectionAction);
35
+ prototype.registerHybridMethod("goTo", &HybridReadiumViewSpec::goTo);
37
36
  prototype.registerHybridMethod("goForward", &HybridReadiumViewSpec::goForward);
38
37
  prototype.registerHybridMethod("goBackward", &HybridReadiumViewSpec::goBackward);
39
38
  prototype.registerHybridMethod("destroy", &HybridReadiumViewSpec::destroy);
@@ -15,14 +15,14 @@
15
15
 
16
16
  // Forward declaration of `ReadiumFile` to properly resolve imports.
17
17
  namespace margelo::nitro::readium { struct ReadiumFile; }
18
- // Forward declaration of `Locator` to properly resolve imports.
19
- namespace margelo::nitro::readium { struct Locator; }
20
18
  // Forward declaration of `Preferences` to properly resolve imports.
21
19
  namespace margelo::nitro::readium { struct Preferences; }
22
20
  // Forward declaration of `DecorationGroup` to properly resolve imports.
23
21
  namespace margelo::nitro::readium { struct DecorationGroup; }
24
22
  // Forward declaration of `SelectionAction` to properly resolve imports.
25
23
  namespace margelo::nitro::readium { struct SelectionAction; }
24
+ // Forward declaration of `Locator` to properly resolve imports.
25
+ namespace margelo::nitro::readium { struct Locator; }
26
26
  // Forward declaration of `PublicationReadyEvent` to properly resolve imports.
27
27
  namespace margelo::nitro::readium { struct PublicationReadyEvent; }
28
28
  // Forward declaration of `DecorationActivatedEvent` to properly resolve imports.
@@ -34,11 +34,11 @@ namespace margelo::nitro::readium { struct SelectionActionEvent; }
34
34
 
35
35
  #include "ReadiumFile.hpp"
36
36
  #include <optional>
37
- #include "Locator.hpp"
38
37
  #include "Preferences.hpp"
39
38
  #include "DecorationGroup.hpp"
40
39
  #include <vector>
41
40
  #include "SelectionAction.hpp"
41
+ #include "Locator.hpp"
42
42
  #include <functional>
43
43
  #include "PublicationReadyEvent.hpp"
44
44
  #include "DecorationActivatedEvent.hpp"
@@ -74,8 +74,6 @@ namespace margelo::nitro::readium {
74
74
  // Properties
75
75
  virtual std::optional<ReadiumFile> getFile() = 0;
76
76
  virtual void setFile(const std::optional<ReadiumFile>& file) = 0;
77
- virtual std::optional<Locator> getLocation() = 0;
78
- virtual void setLocation(const std::optional<Locator>& location) = 0;
79
77
  virtual std::optional<Preferences> getPreferences() = 0;
80
78
  virtual void setPreferences(const std::optional<Preferences>& preferences) = 0;
81
79
  virtual std::optional<std::vector<DecorationGroup>> getDecorations() = 0;
@@ -95,6 +93,7 @@ namespace margelo::nitro::readium {
95
93
 
96
94
  public:
97
95
  // Methods
96
+ virtual void goTo(const Locator& locator) = 0;
98
97
  virtual void goForward() = 0;
99
98
  virtual void goBackward() = 0;
100
99
  virtual void destroy() = 0;
@@ -36,16 +36,6 @@ namespace margelo::nitro::readium::views {
36
36
  throw std::runtime_error(std::string("ReadiumView.file: ") + exc.what());
37
37
  }
38
38
  }()),
39
- location([&]() -> CachedProp<std::optional<Locator>> {
40
- try {
41
- const react::RawValue* rawValue = rawProps.at("location", nullptr, nullptr);
42
- if (rawValue == nullptr) return sourceProps.location;
43
- const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
44
- return CachedProp<std::optional<Locator>>::fromRawValue(*runtime, value, sourceProps.location);
45
- } catch (const std::exception& exc) {
46
- throw std::runtime_error(std::string("ReadiumView.location: ") + exc.what());
47
- }
48
- }()),
49
39
  preferences([&]() -> CachedProp<std::optional<Preferences>> {
50
40
  try {
51
41
  const react::RawValue* rawValue = rawProps.at("preferences", nullptr, nullptr);
@@ -140,7 +130,6 @@ namespace margelo::nitro::readium::views {
140
130
  bool HybridReadiumViewProps::filterObjectKeys(const std::string& propName) {
141
131
  switch (hashString(propName)) {
142
132
  case hashString("file"): return true;
143
- case hashString("location"): return true;
144
133
  case hashString("preferences"): return true;
145
134
  case hashString("decorations"): return true;
146
135
  case hashString("selectionActions"): return true;
@@ -18,11 +18,11 @@
18
18
 
19
19
  #include "ReadiumFile.hpp"
20
20
  #include <optional>
21
- #include "Locator.hpp"
22
21
  #include "Preferences.hpp"
23
22
  #include "DecorationGroup.hpp"
24
23
  #include <vector>
25
24
  #include "SelectionAction.hpp"
25
+ #include "Locator.hpp"
26
26
  #include <functional>
27
27
  #include "PublicationReadyEvent.hpp"
28
28
  #include "DecorationActivatedEvent.hpp"
@@ -52,7 +52,6 @@ namespace margelo::nitro::readium::views {
52
52
 
53
53
  public:
54
54
  CachedProp<std::optional<ReadiumFile>> file;
55
- CachedProp<std::optional<Locator>> location;
56
55
  CachedProp<std::optional<Preferences>> preferences;
57
56
  CachedProp<std::optional<std::vector<DecorationGroup>>> decorations;
58
57
  CachedProp<std::optional<std::vector<SelectionAction>>> selectionActions;
@@ -5,7 +5,6 @@
5
5
  "directEventTypes": {},
6
6
  "validAttributes": {
7
7
  "file": true,
8
- "location": true,
9
8
  "preferences": true,
10
9
  "decorations": true,
11
10
  "selectionActions": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-readium",
3
- "version": "5.0.0-rc.10",
3
+ "version": "5.0.0-rc.12",
4
4
  "description": "A react-native wrapper for https://readium.org/",
5
5
  "main": "lib/src/index",
6
6
  "types": "lib/src/index.d.ts",
@@ -67,6 +67,7 @@ export const ReadiumView = forwardRef<ReadiumViewRef, ReadiumProps>(
67
67
  useImperativeHandle(
68
68
  forwardedRef,
69
69
  () => ({
70
+ goTo: (locator) => hybridRef.current?.goTo(locator),
70
71
  goForward: () => hybridRef.current?.goForward(),
71
72
  goBackward: () => hybridRef.current?.goBackward(),
72
73
  }),
@@ -11,13 +11,13 @@ import type {
11
11
  } from '../interfaces';
12
12
 
13
13
  export type ReadiumViewRef = {
14
+ goTo: (locator: Locator) => void;
14
15
  goForward: () => void;
15
16
  goBackward: () => void;
16
17
  };
17
18
 
18
19
  export type ReadiumProps = {
19
20
  file: File;
20
- location?: Locator;
21
21
  preferences: Preferences;
22
22
  decorations?: DecorationGroup[];
23
23
  selectionActions?: SelectionAction[];
@@ -5,9 +5,9 @@ import { View, StyleSheet } from 'react-native';
5
5
  import {
6
6
  useNavigator,
7
7
  usePreferencesObserver,
8
- useLocationObserver,
9
8
  useDecorationsObserver,
10
9
  } from '../../web/hooks';
10
+ import { convertToNavigatorLocator } from '../../web/utils/locationNormalizer';
11
11
  import type { ReadiumProps as BaseReadiumProps, ReadiumViewRef as BaseReadiumViewRef } from './ReadiumView.types';
12
12
 
13
13
  export type ReadiumProps = BaseReadiumProps & {
@@ -27,7 +27,6 @@ export const ReadiumView = React.forwardRef<ReadiumViewRef, ReadiumProps>(
27
27
  {
28
28
  file,
29
29
  preferences,
30
- location,
31
30
  decorations,
32
31
  onLocationChange,
33
32
  onPublicationReady,
@@ -57,6 +56,14 @@ export const ReadiumView = React.forwardRef<ReadiumViewRef, ReadiumProps>(
57
56
  useImperativeHandle(
58
57
  ref,
59
58
  () => ({
59
+ goTo: (locator) => {
60
+ if (!navigator) return;
61
+ const navLocator = convertToNavigatorLocator(locator);
62
+ if (navLocator) {
63
+ // @ts-ignore
64
+ navigator.go(navLocator, true, () => {});
65
+ }
66
+ },
60
67
  goForward: () => {
61
68
  navigator?.goForward(true, () => {});
62
69
  },
@@ -76,7 +83,6 @@ export const ReadiumView = React.forwardRef<ReadiumViewRef, ReadiumProps>(
76
83
  );
77
84
 
78
85
  usePreferencesObserver(navigator, preferences);
79
- useLocationObserver(navigator, location);
80
86
  useDecorationsObserver(navigator, decorationsRecord, onDecorationActivated);
81
87
 
82
88
  // Generate position label text
@@ -228,7 +228,6 @@ export interface ReadiumFile {
228
228
 
229
229
  export interface ReadiumViewProps extends HybridViewProps {
230
230
  file?: ReadiumFile;
231
- location?: Locator;
232
231
  preferences?: Preferences;
233
232
  decorations?: DecorationGroup[];
234
233
  selectionActions?: SelectionAction[];
@@ -240,6 +239,7 @@ export interface ReadiumViewProps extends HybridViewProps {
240
239
  }
241
240
 
242
241
  export interface ReadiumViewMethods extends HybridViewMethods {
242
+ goTo(locator: Locator): void;
243
243
  goForward(): void;
244
244
  goBackward(): void;
245
245
  destroy(): void;
@@ -1,5 +1,4 @@
1
1
  export * from './useNavigator';
2
2
  export * from './usePreferencesObserver';
3
- export * from './useLocationObserver';
4
3
  export * from './usePositionLabel';
5
4
  export * from './useDecorationsObserver';
@@ -95,7 +95,17 @@ export const useNavigator = ({
95
95
  const publication = new Publication({ manifest, fetcher });
96
96
 
97
97
  // 4. Create positions array for navigation
98
- const positionsArray = createPositions(publication);
98
+ // Try loading granular positions from manifest (generated server-side),
99
+ // fall back to chapter-based positions for older manifests
100
+ let positionsArray: Locator[];
101
+ try {
102
+ const manifestPositions = await publication.positionsFromManifest();
103
+ positionsArray = manifestPositions.length > 0
104
+ ? manifestPositions
105
+ : createPositions(publication);
106
+ } catch {
107
+ positionsArray = createPositions(publication);
108
+ }
99
109
  readingOrder.current = positionsArray;
100
110
  setPositions(positionsArray);
101
111
 
@@ -1,3 +0,0 @@
1
- import { EpubNavigator } from '@readium/navigator';
2
- import type { Link, Locator } from '../../src/interfaces';
3
- export declare const useLocationObserver: (navigator?: EpubNavigator | null, location?: Link | Locator | null) => void;
@@ -1,22 +0,0 @@
1
- import { useRef } from 'react';
2
- import { useDeepCompareEffect } from 'use-deep-compare';
3
- export const useLocationObserver = (navigator, location) => {
4
- const lastHrefRef = useRef(null);
5
- useDeepCompareEffect(() => {
6
- // Only navigate if we have a navigator, a location, and the href has changed.
7
- // Skip navigation if location.locations exists with progression and totalProgression
8
- // (it's from the navigator's positionChanged callback)
9
- const hasFullLocationData = 'locations' in (location ?? {}) &&
10
- location.locations?.progression !== undefined &&
11
- location.locations?.totalProgression !== undefined;
12
- if (navigator &&
13
- location &&
14
- location.href !== lastHrefRef.current &&
15
- !hasFullLocationData) {
16
- lastHrefRef.current = location.href;
17
- // For Link objects (from TOC), we can pass them directly
18
- // @ts-ignore
19
- navigator.go(location, true, () => { });
20
- }
21
- }, [location?.href, !!navigator]);
22
- };
@@ -1,34 +0,0 @@
1
- import { useRef } from 'react';
2
- import { useDeepCompareEffect } from 'use-deep-compare';
3
- import { EpubNavigator } from '@readium/navigator';
4
-
5
- import type { Link, Locator } from '../../src/interfaces';
6
-
7
- export const useLocationObserver = (
8
- navigator?: EpubNavigator | null,
9
- location?: Link | Locator | null
10
- ) => {
11
- const lastHrefRef = useRef<string | null>(null);
12
-
13
- useDeepCompareEffect(() => {
14
- // Only navigate if we have a navigator, a location, and the href has changed.
15
- // Skip navigation if location.locations exists with progression and totalProgression
16
- // (it's from the navigator's positionChanged callback)
17
- const hasFullLocationData =
18
- 'locations' in (location ?? {}) &&
19
- (location as Locator).locations?.progression !== undefined &&
20
- (location as Locator).locations?.totalProgression !== undefined;
21
-
22
- if (
23
- navigator &&
24
- location &&
25
- location.href !== lastHrefRef.current &&
26
- !hasFullLocationData
27
- ) {
28
- lastHrefRef.current = location.href;
29
- // For Link objects (from TOC), we can pass them directly
30
- // @ts-ignore
31
- navigator.go(location, true, () => {});
32
- }
33
- }, [location?.href, !!navigator]);
34
- };