react-native-readium 4.0.1 → 5.0.0-rc.0

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 (118) hide show
  1. package/README.md +93 -9
  2. package/android/build.gradle +32 -29
  3. package/android/gradle.properties +3 -3
  4. package/android/src/main/java/com/reactnativereadium/ReadiumView.kt +88 -28
  5. package/android/src/main/java/com/reactnativereadium/ReadiumViewManager.kt +20 -19
  6. package/android/src/main/java/com/reactnativereadium/reader/BaseReaderFragment.kt +27 -6
  7. package/android/src/main/java/com/reactnativereadium/reader/EpubReaderFragment.kt +53 -69
  8. package/android/src/main/java/com/reactnativereadium/reader/PositionLabelManager.kt +132 -0
  9. package/android/src/main/java/com/reactnativereadium/reader/ReaderService.kt +64 -65
  10. package/android/src/main/java/com/reactnativereadium/reader/ReaderViewModel.kt +6 -85
  11. package/android/src/main/java/com/reactnativereadium/reader/VisualReaderFragment.kt +32 -2
  12. package/android/src/main/java/com/reactnativereadium/utils/FragmentFactory.kt +2 -3
  13. package/android/src/main/java/com/reactnativereadium/utils/JsonExtensions.kt +61 -0
  14. package/android/src/main/java/com/reactnativereadium/utils/MetadataNormalizer.kt +183 -0
  15. package/android/src/main/java/com/reactnativereadium/utils/NormalizedMetadata.kt +179 -0
  16. package/android/src/main/java/com/reactnativereadium/utils/extensions/InputStream.kt +0 -9
  17. package/ios/App/AppModule.swift +3 -9
  18. package/ios/Common/Toolkit/Extensions/Locator.swift +1 -1
  19. package/ios/Data/Bookmark.swift +1 -1
  20. package/ios/Reader/Common/ReaderViewController.swift +118 -21
  21. package/ios/Reader/EPUB/AssociatedColors.swift +1 -1
  22. package/ios/Reader/EPUB/EPUBHTTPServer.swift +13 -0
  23. package/ios/Reader/EPUB/EPUBModule.swift +1 -1
  24. package/ios/Reader/EPUB/EPUBViewController.swift +3 -4
  25. package/ios/Reader/ReaderFormatModule.swift +1 -1
  26. package/ios/Reader/ReaderModule.swift +1 -1
  27. package/ios/Reader/ReaderService.swift +70 -35
  28. package/ios/ReadiumView.swift +62 -25
  29. package/ios/ReadiumViewManager.m +1 -1
  30. package/lib/src/ReadiumViewNativeComponent.d.ts +19 -0
  31. package/lib/src/ReadiumViewNativeComponent.js +10 -0
  32. package/lib/src/components/BaseReadiumView.d.ts +1 -2
  33. package/lib/src/components/BaseReadiumView.js +3 -7
  34. package/lib/src/components/ReadiumView.js +15 -15
  35. package/lib/src/components/ReadiumView.web.js +100 -21
  36. package/lib/src/interfaces/BaseReadiumViewProps.d.ts +2 -1
  37. package/lib/src/interfaces/Preferences.d.ts +3 -2
  38. package/lib/src/interfaces/PublicationMetadata.d.ts +114 -0
  39. package/lib/src/interfaces/PublicationMetadata.js +1 -0
  40. package/lib/src/interfaces/PublicationReady.d.ts +15 -0
  41. package/lib/src/interfaces/PublicationReady.js +1 -0
  42. package/lib/src/interfaces/index.d.ts +2 -0
  43. package/lib/src/interfaces/index.js +2 -0
  44. package/lib/src/utils/index.d.ts +0 -1
  45. package/lib/src/utils/index.js +0 -1
  46. package/lib/web/hooks/index.d.ts +3 -2
  47. package/lib/web/hooks/index.js +3 -2
  48. package/lib/web/hooks/useLocationObserver.d.ts +2 -1
  49. package/lib/web/hooks/useLocationObserver.js +18 -11
  50. package/lib/web/hooks/useNavigator.d.ts +12 -0
  51. package/lib/web/hooks/useNavigator.js +87 -0
  52. package/lib/web/hooks/usePositionLabel.d.ts +9 -0
  53. package/lib/web/hooks/usePositionLabel.js +33 -0
  54. package/lib/web/hooks/usePreferencesObserver.d.ts +2 -0
  55. package/lib/web/hooks/usePreferencesObserver.js +54 -0
  56. package/lib/web/utils/manifestFetcher.d.ts +8 -0
  57. package/lib/web/utils/manifestFetcher.js +28 -0
  58. package/lib/web/utils/manifestNormalizer.d.ts +8 -0
  59. package/lib/web/utils/manifestNormalizer.js +70 -0
  60. package/lib/web/utils/metadataNormalizer.d.ts +53 -0
  61. package/lib/web/utils/metadataNormalizer.js +220 -0
  62. package/lib/web/utils/navigatorListeners.d.ts +6 -0
  63. package/lib/web/utils/navigatorListeners.js +50 -0
  64. package/lib/web/utils/publicationUtils.d.ts +15 -0
  65. package/lib/web/utils/publicationUtils.js +39 -0
  66. package/package.json +24 -14
  67. package/react-native-readium.podspec +7 -5
  68. package/src/ReadiumViewNativeComponent.ts +35 -0
  69. package/src/components/BaseReadiumView.tsx +3 -10
  70. package/src/components/ReadiumView.tsx +15 -15
  71. package/src/components/ReadiumView.web.tsx +120 -27
  72. package/src/interfaces/BaseReadiumViewProps.ts +2 -1
  73. package/src/interfaces/Preferences.ts +3 -2
  74. package/src/interfaces/PublicationMetadata.ts +141 -0
  75. package/src/interfaces/PublicationReady.ts +18 -0
  76. package/src/interfaces/index.ts +2 -0
  77. package/src/utils/index.ts +0 -1
  78. package/web/hooks/index.ts +3 -2
  79. package/web/hooks/useLocationObserver.ts +24 -11
  80. package/web/hooks/useNavigator.ts +146 -0
  81. package/web/hooks/usePositionLabel.ts +51 -0
  82. package/web/hooks/usePreferencesObserver.ts +69 -0
  83. package/web/utils/manifestFetcher.ts +38 -0
  84. package/web/utils/manifestNormalizer.ts +74 -0
  85. package/web/utils/metadataNormalizer.ts +238 -0
  86. package/web/utils/navigatorListeners.ts +60 -0
  87. package/web/utils/publicationUtils.ts +47 -0
  88. package/android/src/main/java/com/reactnativereadium/search/SearchFragment.kt +0 -100
  89. package/android/src/main/java/com/reactnativereadium/search/SearchPagingSource.kt +0 -44
  90. package/android/src/main/java/com/reactnativereadium/search/SearchResultAdapter.kt +0 -68
  91. package/android/src/main/java/com/reactnativereadium/utils/R2DispatcherActivity.kt +0 -45
  92. package/android/src/main/java/com/reactnativereadium/utils/SectionDecoration.kt +0 -98
  93. package/android/src/main/java/com/reactnativereadium/utils/SingleClickListener.kt +0 -32
  94. package/android/src/main/java/com/reactnativereadium/utils/extensions/Bitmap.kt +0 -23
  95. package/android/src/main/java/com/reactnativereadium/utils/extensions/Context.kt +0 -16
  96. package/android/src/main/java/com/reactnativereadium/utils/extensions/File.kt +0 -22
  97. package/android/src/main/java/com/reactnativereadium/utils/extensions/Link.kt +0 -6
  98. package/android/src/main/java/com/reactnativereadium/utils/extensions/Metadata.kt +0 -6
  99. package/android/src/main/java/com/reactnativereadium/utils/extensions/URL.kt +0 -29
  100. package/android/src/main/java/com/reactnativereadium/utils/extensions/Uri.kt +0 -17
  101. package/android/src/main/res/layout/fragment_search.xml +0 -39
  102. package/android/src/main/res/layout/item_recycle_search.xml +0 -14
  103. package/ios/Common/EPUBPreferences.swift +0 -8
  104. package/ios/Common/Paths.swift +0 -52
  105. package/ios/Common/Publication.swift +0 -15
  106. package/ios/Common/Toolkit/Extensions/HTTPClient.swift +0 -65
  107. package/ios/Common/Toolkit/Extensions/UIImage.swift +0 -12
  108. package/ios/Common/Toolkit/Extensions/UIViewController.swift +0 -19
  109. package/ios/Common/Toolkit/ScreenOrientation.swift +0 -13
  110. package/lib/src/utils/createFragment.d.ts +0 -1
  111. package/lib/src/utils/createFragment.js +0 -10
  112. package/lib/web/hooks/useReaderRef.d.ts +0 -3
  113. package/lib/web/hooks/useReaderRef.js +0 -85
  114. package/lib/web/hooks/useSettingsObserver.d.ts +0 -2
  115. package/lib/web/hooks/useSettingsObserver.js +0 -44
  116. package/src/utils/createFragment.ts +0 -15
  117. package/web/hooks/useReaderRef.ts +0 -109
  118. package/web/hooks/useSettingsObserver.ts +0 -61
@@ -13,14 +13,15 @@ import android.view.ViewGroup
13
13
  import android.view.WindowInsets
14
14
  import android.widget.FrameLayout
15
15
  import androidx.fragment.app.Fragment
16
- import org.readium.r2.navigator.DecorableNavigator
17
- import org.readium.r2.navigator.ExperimentalDecorator
16
+ import androidx.lifecycle.lifecycleScope
18
17
  import com.reactnativereadium.R
19
18
  import com.reactnativereadium.databinding.FragmentReaderBinding
20
19
  import com.reactnativereadium.utils.clearPadding
21
20
  import com.reactnativereadium.utils.hideSystemUi
22
21
  import com.reactnativereadium.utils.padSystemUi
23
22
  import com.reactnativereadium.utils.showSystemUi
23
+ import kotlinx.coroutines.flow.launchIn
24
+ import kotlinx.coroutines.flow.onEach
24
25
 
25
26
  /*
26
27
  * Adds fullscreen support to the BaseReaderFragment
@@ -32,6 +33,8 @@ abstract class VisualReaderFragment : BaseReaderFragment() {
32
33
  private var _binding: FragmentReaderBinding? = null
33
34
  val binding get() = _binding!!
34
35
 
36
+ private var positionLabelManager: PositionLabelManager? = null
37
+
35
38
  override fun onCreateView(
36
39
  inflater: LayoutInflater,
37
40
  container: ViewGroup?,
@@ -45,6 +48,23 @@ abstract class VisualReaderFragment : BaseReaderFragment() {
45
48
  super.onViewCreated(view, savedInstanceState)
46
49
  navigatorFragment = navigator as Fragment
47
50
 
51
+ // Initialize position label manager - simple overlay, matching iOS approach
52
+ positionLabelManager = PositionLabelManager(
53
+ containerView = binding.fragmentReaderContainer,
54
+ publication = model.publication,
55
+ lifecycleScope = viewLifecycleOwner.lifecycleScope
56
+ )
57
+
58
+ // Update position label when navigator location changes
59
+ navigator.currentLocator
60
+ .onEach { locator ->
61
+ positionLabelManager?.update(
62
+ position = locator.locations.position,
63
+ totalProgression = locator.locations.totalProgression
64
+ )
65
+ }
66
+ .launchIn(viewLifecycleOwner.lifecycleScope)
67
+
48
68
  childFragmentManager.addOnBackStackChangedListener {
49
69
  updateSystemUiVisibility()
50
70
  }
@@ -55,10 +75,20 @@ abstract class VisualReaderFragment : BaseReaderFragment() {
55
75
  }
56
76
 
57
77
  override fun onDestroyView() {
78
+ positionLabelManager?.cleanup()
79
+ positionLabelManager = null
58
80
  _binding = null
59
81
  super.onDestroyView()
60
82
  }
61
83
 
84
+ /**
85
+ * Update the text color of the position label.
86
+ * @param color Android color integer
87
+ */
88
+ fun setPositionLabelColor(color: Int) {
89
+ positionLabelManager?.setTextColor(color)
90
+ }
91
+
62
92
  fun updateSystemUiVisibility() {
63
93
  if (navigatorFragment.isHidden)
64
94
  requireActivity().showSystemUi()
@@ -2,7 +2,6 @@ package com.reactnativereadium.utils
2
2
 
3
3
  import androidx.fragment.app.Fragment
4
4
  import androidx.fragment.app.FragmentFactory
5
- import org.readium.r2.shared.extensions.tryOrNull
6
5
 
7
6
  /**
8
7
  * Creates a [FragmentFactory] for a single type of [Fragment] using the result of the given
@@ -36,8 +35,8 @@ class CompositeFragmentFactory(private val factories: List<FragmentFactory>) : F
36
35
 
37
36
  override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
38
37
  for (factory in factories) {
39
- tryOrNull { factory.instantiate(classLoader, className) }
40
- ?.let { return it }
38
+ runCatching { factory.instantiate(classLoader, className) }
39
+ .getOrNull()?.let { return it }
41
40
  }
42
41
 
43
42
  return super.instantiate(classLoader, className)
@@ -0,0 +1,61 @@
1
+ package com.reactnativereadium.utils
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableArray
5
+ import com.facebook.react.bridge.WritableMap
6
+ import org.json.JSONArray
7
+ import org.json.JSONObject
8
+ import org.readium.r2.shared.publication.Link
9
+ import org.readium.r2.shared.publication.Locator
10
+
11
+ internal fun Locator.toWritableMap(): WritableMap =
12
+ toJSON().toWritableMap()
13
+
14
+ internal fun Link.toWritableMap(): WritableMap =
15
+ toJSON().toWritableMap()
16
+
17
+ internal fun List<Link>.toWritableArray(): WritableArray =
18
+ Arguments.createArray().apply {
19
+ forEach { pushMap(it.toWritableMap()) }
20
+ }
21
+
22
+ internal fun JSONObject.toWritableMap(): WritableMap {
23
+ val map = Arguments.createMap()
24
+ val iterator = keys()
25
+ while (iterator.hasNext()) {
26
+ val key = iterator.next()
27
+ when (val value = opt(key)) {
28
+ JSONObject.NULL -> map.putNull(key)
29
+ is JSONObject -> map.putMap(key, value.toWritableMap())
30
+ is JSONArray -> map.putArray(key, value.toWritableArray())
31
+ is String -> map.putString(key, value)
32
+ is Boolean -> map.putBoolean(key, value)
33
+ is Int -> map.putInt(key, value)
34
+ is Long -> map.putDouble(key, value.toDouble())
35
+ is Double -> map.putDouble(key, value)
36
+ is Float -> map.putDouble(key, value.toDouble())
37
+ else -> map.putString(key, value?.toString() ?: "")
38
+ }
39
+ }
40
+ return map
41
+ }
42
+
43
+ internal fun JSONArray.toWritableArray(): WritableArray {
44
+ val array = Arguments.createArray()
45
+ for (index in 0 until length()) {
46
+ when (val value = opt(index)) {
47
+ JSONObject.NULL -> array.pushNull()
48
+ is JSONObject -> array.pushMap(value.toWritableMap())
49
+ is JSONArray -> array.pushArray(value.toWritableArray())
50
+ is String -> array.pushString(value)
51
+ is Boolean -> array.pushBoolean(value)
52
+ is Int -> array.pushInt(value)
53
+ is Long -> array.pushDouble(value.toDouble())
54
+ is Double -> array.pushDouble(value)
55
+ is Float -> array.pushDouble(value.toDouble())
56
+ null -> array.pushNull()
57
+ else -> array.pushString(value.toString())
58
+ }
59
+ }
60
+ return array
61
+ }
@@ -0,0 +1,183 @@
1
+ package com.reactnativereadium.utils
2
+
3
+ import com.facebook.react.bridge.*
4
+ import org.readium.r2.shared.publication.Metadata
5
+ import org.readium.r2.shared.publication.LocalizedString
6
+ import org.readium.r2.shared.publication.Contributor
7
+ import org.readium.r2.shared.publication.Subject
8
+
9
+ /**
10
+ * Normalizes metadata to ensure consistent structure per RWPM spec.
11
+ *
12
+ * This normalizer works directly with Readium's native Kotlin types (Metadata, LocalizedString,
13
+ * Contributor, Subject) and converts them to normalized, spec-compliant structures.
14
+ *
15
+ * This approach is:
16
+ * - More efficient (no intermediate WritableMap conversion)
17
+ * - More type-safe (working with actual Kotlin types)
18
+ * - Based on the stable RWPM specification
19
+ *
20
+ * https://readium.org/webpub-manifest/
21
+ */
22
+ object MetadataNormalizer {
23
+
24
+ /**
25
+ * Normalizes a Metadata object to a consistent React Native-compatible format.
26
+ * Works directly with Readium native types for efficiency and type safety.
27
+ */
28
+ fun normalize(metadata: Metadata): WritableMap {
29
+ // Build typed NormalizedMetadata object from Readium's native types
30
+ val normalized = NormalizedMetadata(
31
+ // Required fields (provide default empty string if null)
32
+ title = normalizeLocalizedString(metadata.localizedTitle) ?: "",
33
+
34
+ // Optional localized string fields
35
+ sortAs = normalizeLocalizedString(metadata.localizedSortAs),
36
+ subtitle = normalizeLocalizedString(metadata.localizedSubtitle),
37
+
38
+ // Simple string fields
39
+ identifier = metadata.identifier,
40
+ description = metadata.description,
41
+ readingProgression = metadata.readingProgression?.name?.lowercase(),
42
+
43
+ // Date fields (convert Instant to ISO string)
44
+ modified = metadata.modified?.toString(),
45
+ published = metadata.published?.toString(),
46
+
47
+ // Layout is not directly on Metadata - would need Publication.metadata.presentation
48
+ // For now, pass null and extract from JSON if needed
49
+ layout = null,
50
+
51
+ // Array fields
52
+ language = if (metadata.languages.isNotEmpty()) metadata.languages else null,
53
+ conformsTo = metadata.conformsTo.map { it.uri }.takeIf { it.isNotEmpty() },
54
+
55
+ // Numeric fields
56
+ duration = metadata.duration,
57
+ numberOfPages = metadata.numberOfPages,
58
+
59
+ // Normalize contributors (all types)
60
+ author = normalizeContributors(metadata.authors),
61
+ translator = normalizeContributors(metadata.translators),
62
+ editor = normalizeContributors(metadata.editors),
63
+ artist = normalizeContributors(metadata.artists),
64
+ illustrator = normalizeContributors(metadata.illustrators),
65
+ letterer = normalizeContributors(metadata.letterers),
66
+ penciler = normalizeContributors(metadata.pencilers),
67
+ colorist = normalizeContributors(metadata.colorists),
68
+ inker = normalizeContributors(metadata.inkers),
69
+ narrator = normalizeContributors(metadata.narrators),
70
+ contributor = normalizeContributors(metadata.contributors),
71
+ publisher = normalizeContributors(metadata.publishers),
72
+ imprint = normalizeContributors(metadata.imprints),
73
+
74
+ // Normalize subjects
75
+ subject = normalizeSubjects(metadata.subjects),
76
+
77
+ // Complex objects - these need special handling
78
+ // For now, convert via JSON if they exist
79
+ accessibility = metadata.accessibility?.let { convertAccessibilityToWritableMap(it) },
80
+ belongsTo = if (metadata.belongsTo.isNotEmpty()) convertBelongsToToWritableMap(metadata.belongsTo) else null
81
+ )
82
+
83
+ // Convert to WritableMap for React Native
84
+ return normalized.toWritableMap()
85
+ }
86
+
87
+ /**
88
+ * Normalizes a LocalizedString per RWPM spec.
89
+ *
90
+ * Per RWPM spec, we extract a single string from the localized string using priority:
91
+ * 1. null key (undefined/undetermined language)
92
+ * 2. "en" (English fallback)
93
+ * 3. First available translation
94
+ *
95
+ * https://readium.org/webpub-manifest/contexts/default/
96
+ */
97
+ private fun normalizeLocalizedString(value: LocalizedString?): String? {
98
+ if (value == null) return null
99
+
100
+ // Priority: null key (undefined) -> "en" -> first available
101
+ return value.translations[null]?.string
102
+ ?: value.translations["en"]?.string
103
+ ?: value.translations.values.firstOrNull()?.string
104
+ }
105
+
106
+ /**
107
+ * Normalizes contributors per RWPM spec.
108
+ *
109
+ * Converts Readium's Contributor objects to our normalized format.
110
+ * https://readium.org/webpub-manifest/schema/contributor-object.schema.json
111
+ */
112
+ private fun normalizeContributors(contributors: List<Contributor>): List<NormalizedContributor>? {
113
+ if (contributors.isEmpty()) return null
114
+
115
+ return contributors.mapNotNull { contributor ->
116
+ val name = normalizeLocalizedString(contributor.localizedName) ?: return@mapNotNull null
117
+
118
+ NormalizedContributor(
119
+ name = name,
120
+ sortAs = normalizeLocalizedString(contributor.localizedSortAs),
121
+ identifier = contributor.identifier,
122
+ role = contributor.roles.firstOrNull(),
123
+ position = contributor.position?.toInt()
124
+ )
125
+ }.takeIf { it.isNotEmpty() }
126
+ }
127
+
128
+ /**
129
+ * Normalizes subjects per RWPM spec.
130
+ *
131
+ * Converts Readium's Subject objects to our normalized format.
132
+ * https://readium.org/webpub-manifest/schema/subject.schema.json
133
+ */
134
+ private fun normalizeSubjects(subjects: List<Subject>): List<NormalizedSubject>? {
135
+ if (subjects.isEmpty()) return null
136
+
137
+ return subjects.mapNotNull { subject ->
138
+ val name = normalizeLocalizedString(subject.localizedName) ?: return@mapNotNull null
139
+
140
+ NormalizedSubject(
141
+ name = name,
142
+ sortAs = normalizeLocalizedString(subject.localizedSortAs),
143
+ code = subject.code,
144
+ scheme = subject.scheme
145
+ )
146
+ }.takeIf { it.isNotEmpty() }
147
+ }
148
+
149
+ /**
150
+ * Converts Readium's Accessibility object to WritableMap.
151
+ * This is a complex nested structure that we pass through as-is.
152
+ */
153
+ private fun convertAccessibilityToWritableMap(accessibility: org.readium.r2.shared.publication.Accessibility): WritableMap? {
154
+ return try {
155
+ // Use Readium's built-in JSON serialization
156
+ val json = accessibility.toJSON()
157
+ json.toWritableMap()
158
+ } catch (e: Exception) {
159
+ null
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Converts Readium's belongsTo map to WritableMap.
165
+ * This is a complex nested structure that we pass through as-is.
166
+ */
167
+ private fun convertBelongsToToWritableMap(belongsTo: Map<String, List<org.readium.r2.shared.publication.Collection>>): WritableMap? {
168
+ return try {
169
+ Arguments.createMap().apply {
170
+ belongsTo.forEach { (key, collections) ->
171
+ val array = Arguments.createArray().apply {
172
+ collections.forEach { collection ->
173
+ collection.toJSON().toWritableMap()?.let { pushMap(it) }
174
+ }
175
+ }
176
+ putArray(key, array)
177
+ }
178
+ }
179
+ } catch (e: Exception) {
180
+ null
181
+ }
182
+ }
183
+ }
@@ -0,0 +1,179 @@
1
+ package com.reactnativereadium.utils
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableArray
5
+ import com.facebook.react.bridge.WritableMap
6
+
7
+ /**
8
+ * Normalized metadata data classes that mirror the TypeScript PublicationMetadata interface.
9
+ * These provide type safety and structure enforcement during metadata normalization.
10
+ */
11
+
12
+ /**
13
+ * Contributor information for publication metadata
14
+ */
15
+ data class NormalizedContributor(
16
+ val name: String,
17
+ val sortAs: String? = null,
18
+ val identifier: String? = null,
19
+ val role: String? = null,
20
+ val position: Int? = null
21
+ ) {
22
+ fun toWritableMap(): WritableMap {
23
+ return Arguments.createMap().apply {
24
+ putString("name", name)
25
+ sortAs?.let { putString("sortAs", it) }
26
+ identifier?.let { putString("identifier", it) }
27
+ role?.let { putString("role", it) }
28
+ position?.let { putInt("position", it) }
29
+ }
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Subject/tag information
35
+ */
36
+ data class NormalizedSubject(
37
+ val name: String,
38
+ val sortAs: String? = null,
39
+ val code: String? = null,
40
+ val scheme: String? = null
41
+ ) {
42
+ fun toWritableMap(): WritableMap {
43
+ return Arguments.createMap().apply {
44
+ putString("name", name)
45
+ sortAs?.let { putString("sortAs", it) }
46
+ code?.let { putString("code", it) }
47
+ scheme?.let { putString("scheme", it) }
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Normalized publication metadata following the RWPM spec
54
+ * @see https://readium.org/webpub-manifest/
55
+ */
56
+ data class NormalizedMetadata(
57
+ // Required fields
58
+ val title: String,
59
+
60
+ // Optional localized string fields
61
+ val sortAs: String? = null,
62
+ val subtitle: String? = null,
63
+
64
+ // Simple fields
65
+ val identifier: String? = null,
66
+ val description: String? = null,
67
+ val readingProgression: String? = null,
68
+ val layout: String? = null,
69
+ val modified: String? = null,
70
+ val published: String? = null,
71
+
72
+ // Array fields
73
+ val language: List<String>? = null,
74
+ val conformsTo: List<String>? = null,
75
+
76
+ // Numeric fields
77
+ val duration: Double? = null,
78
+ val numberOfPages: Int? = null,
79
+
80
+ // Contributors (all types)
81
+ val author: List<NormalizedContributor>? = null,
82
+ val translator: List<NormalizedContributor>? = null,
83
+ val editor: List<NormalizedContributor>? = null,
84
+ val artist: List<NormalizedContributor>? = null,
85
+ val illustrator: List<NormalizedContributor>? = null,
86
+ val letterer: List<NormalizedContributor>? = null,
87
+ val penciler: List<NormalizedContributor>? = null,
88
+ val colorist: List<NormalizedContributor>? = null,
89
+ val inker: List<NormalizedContributor>? = null,
90
+ val narrator: List<NormalizedContributor>? = null,
91
+ val contributor: List<NormalizedContributor>? = null,
92
+ val publisher: List<NormalizedContributor>? = null,
93
+ val imprint: List<NormalizedContributor>? = null,
94
+
95
+ // Subjects
96
+ val subject: List<NormalizedSubject>? = null,
97
+
98
+ // Complex objects (preserved as-is from source)
99
+ val accessibility: WritableMap? = null,
100
+ val belongsTo: WritableMap? = null
101
+ ) {
102
+ /**
103
+ * Converts this normalized metadata to a WritableMap for React Native
104
+ */
105
+ fun toWritableMap(): WritableMap {
106
+ return Arguments.createMap().apply {
107
+ // Required fields
108
+ putString("title", title)
109
+
110
+ // Optional localized fields
111
+ sortAs?.let { putString("sortAs", it) }
112
+ subtitle?.let { putString("subtitle", it) }
113
+
114
+ // Simple fields
115
+ identifier?.let { putString("identifier", it) }
116
+ description?.let { putString("description", it) }
117
+ readingProgression?.let { putString("readingProgression", it) }
118
+ layout?.let { putString("layout", it) }
119
+ modified?.let { putString("modified", it) }
120
+ published?.let { putString("published", it) }
121
+
122
+ // Array fields
123
+ language?.let {
124
+ putArray("language", Arguments.createArray().apply {
125
+ it.forEach { lang -> pushString(lang) }
126
+ })
127
+ }
128
+ conformsTo?.let {
129
+ putArray("conformsTo", Arguments.createArray().apply {
130
+ it.forEach { item -> pushString(item) }
131
+ })
132
+ }
133
+
134
+ // Numeric fields
135
+ duration?.let { putDouble("duration", it) }
136
+ numberOfPages?.let { putInt("numberOfPages", it) }
137
+
138
+ // Contributors
139
+ author?.let { putArray("author", it.toContributorWritableArray()) }
140
+ translator?.let { putArray("translator", it.toContributorWritableArray()) }
141
+ editor?.let { putArray("editor", it.toContributorWritableArray()) }
142
+ artist?.let { putArray("artist", it.toContributorWritableArray()) }
143
+ illustrator?.let { putArray("illustrator", it.toContributorWritableArray()) }
144
+ letterer?.let { putArray("letterer", it.toContributorWritableArray()) }
145
+ penciler?.let { putArray("penciler", it.toContributorWritableArray()) }
146
+ colorist?.let { putArray("colorist", it.toContributorWritableArray()) }
147
+ inker?.let { putArray("inker", it.toContributorWritableArray()) }
148
+ narrator?.let { putArray("narrator", it.toContributorWritableArray()) }
149
+ contributor?.let { putArray("contributor", it.toContributorWritableArray()) }
150
+ publisher?.let { putArray("publisher", it.toContributorWritableArray()) }
151
+ imprint?.let { putArray("imprint", it.toContributorWritableArray()) }
152
+
153
+ // Subjects
154
+ subject?.let { putArray("subject", it.toSubjectWritableArray()) }
155
+
156
+ // Complex objects
157
+ accessibility?.let { putMap("accessibility", it) }
158
+ belongsTo?.let { putMap("belongsTo", it) }
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Extension function to convert a list of contributors to WritableArray
165
+ */
166
+ private fun List<NormalizedContributor>.toContributorWritableArray(): WritableArray {
167
+ return Arguments.createArray().apply {
168
+ this@toContributorWritableArray.forEach { pushMap(it.toWritableMap()) }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Extension function to convert a list of subjects to WritableArray
174
+ */
175
+ private fun List<NormalizedSubject>.toSubjectWritableArray(): WritableArray {
176
+ return Arguments.createArray().apply {
177
+ this@toSubjectWritableArray.forEach { pushMap(it.toWritableMap()) }
178
+ }
179
+ }
@@ -2,11 +2,8 @@ package com.reactnativereadium.utils.extensions
2
2
 
3
3
  import kotlinx.coroutines.Dispatchers
4
4
  import kotlinx.coroutines.withContext
5
- import org.readium.r2.shared.extensions.tryOrNull
6
5
  import java.io.File
7
6
  import java.io.InputStream
8
- import java.util.*
9
-
10
7
 
11
8
  suspend fun InputStream.toFile(path: String) {
12
9
  withContext(Dispatchers.IO) {
@@ -15,9 +12,3 @@ suspend fun InputStream.toFile(path: String) {
15
12
  }
16
13
  }
17
14
  }
18
-
19
- suspend fun InputStream.copyToTempFile(dir: String): File? = tryOrNull {
20
- val filename = UUID.randomUUID().toString()
21
- File(dir + filename)
22
- .also { toFile(it.path) }
23
- }
@@ -1,8 +1,7 @@
1
1
  import Combine
2
2
  import Foundation
3
3
  import UIKit
4
- import R2Shared
5
- import R2Streamer
4
+ import ReadiumShared
6
5
 
7
6
 
8
7
  /// Base module delegate, that sub-modules' delegate can extend.
@@ -22,15 +21,10 @@ final class AppModule {
22
21
  var reader: ReaderModuleAPI! = nil
23
22
 
24
23
  init() throws {
25
- guard let server = PublicationServer() else {
26
- /// FIXME: we should recover properly if the publication server can't start, maybe this should only forbid opening a publication?
27
- fatalError("Can't start publication server")
28
- }
29
-
30
24
  reader = ReaderModule(delegate: self)
31
25
 
32
- // Set Readium 2's logging minimum level.
33
- R2EnableLog(withMinimumSeverityLevel: .debug)
26
+ // Set Readium logging minimum level for debugging
27
+ ReadiumEnableLog(withMinimumSeverityLevel: .debug)
34
28
  }
35
29
  }
36
30
 
@@ -1,5 +1,5 @@
1
1
  import Foundation
2
- import R2Shared
2
+ import ReadiumShared
3
3
 
4
4
  extension Locator: Codable {
5
5
  public init(from decoder: Decoder) throws {
@@ -1,6 +1,6 @@
1
1
  import Combine
2
2
  import Foundation
3
- import R2Shared
3
+ import ReadiumShared
4
4
 
5
5
  struct Bookmark {
6
6
  let id: String?