Skip to content

Commit

Permalink
fix(android, ios): allow location and initialLocation to be either Li…
Browse files Browse the repository at this point in the history
…nk or Locator objects

Links and Locators are not the same and need to be handled differently. Regardless of which object
is passed to `location`, convert it to the proper Locator and navigate to it.

#11
  • Loading branch information
jspizziri committed Dec 5, 2022
1 parent b9167d7 commit ebc1030
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 44 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ DRM is not supported at this time. However, there is a clear path to [support it
| Name | Type | Optional | Description |
|------|------|----------|-------------|
| `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. |
| `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)|
| `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)|
| `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) |
| `style` | `ViewStyle` | :white_check_mark: | A traditional style object. |
| `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)|
Expand Down
6 changes: 3 additions & 3 deletions android/src/main/java/com/reactnativereadium/ReadiumView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import com.reactnativereadium.reader.EpubReaderFragment
import com.reactnativereadium.reader.ReaderViewModel
import com.reactnativereadium.utils.Dimensions
import com.reactnativereadium.utils.File
import com.reactnativereadium.utils.LinkOrLocator
import org.readium.r2.shared.extensions.toMap
import org.readium.r2.shared.publication.Locator

class ReadiumView(
val reactContext: ThemedReactContext
Expand All @@ -22,11 +22,11 @@ class ReadiumView(
var fragment: BaseReaderFragment? = null
var isViewInitialized: Boolean = false

fun updateLocation(locator: Locator) : Boolean {
fun updateLocation(location: LinkOrLocator) : Boolean {
if (fragment == null) {
return false
} else {
return this.fragment!!.go(locator, true)
return this.fragment!!.go(location, true)
}
}

Expand Down
39 changes: 31 additions & 8 deletions android/src/main/java/com/reactnativereadium/ReadiumViewManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager
import com.reactnativereadium.reader.ReaderService
import com.reactnativereadium.utils.File
import com.reactnativereadium.utils.LinkOrLocator
import kotlinx.coroutines.runBlocking
import org.json.JSONObject
import org.readium.r2.shared.publication.Link
import org.readium.r2.shared.publication.Locator


class ReadiumViewManager(
val reactContext: ReactApplicationContext
) : ViewGroupManager<ReadiumView>() {
Expand Down Expand Up @@ -69,23 +70,45 @@ class ReadiumViewManager(
fun setFile(view: ReadiumView, file: ReadableMap) {
val path = (file.getString("url") ?: "")
.replace("^(file:/+)?(/.*)$".toRegex(), "$2")
val locatorMap = file.getMap("initialLocation")
var initialLocation: Locator? = null
val location = file.getMap("initialLocation")
var initialLocation: LinkOrLocator? = null

if (locatorMap != null) {
initialLocation = Locator.fromJSON(JSONObject(locatorMap.toHashMap()))
if (location != null) {
initialLocation = locationToLinkOrLocator(location)
}

view.file = File(path, initialLocation)
this.buildForViewIfReady(view)
}

fun locationToLinkOrLocator(location: ReadableMap): LinkOrLocator? {
val json = JSONObject(location.toHashMap())
val hasLocations = json.has("locations")
val hasChildren = json.has("children")
val hasHashHref = (json.get("href") as String).contains("#")
var linkOrLocator: LinkOrLocator? = null

if ((hasChildren || hasHashHref) && !hasLocations) {
val link = Link.fromJSON(json)
if (link != null) {
linkOrLocator = LinkOrLocator.Link(link)
}
} else {
val locator = Locator.fromJSON(json)
if (locator != null) {
linkOrLocator = LinkOrLocator.Locator(locator)
}
}

return linkOrLocator;
}

@ReactProp(name = "location")
fun setLocation(view: ReadiumView, location: ReadableMap) {
val locator = Locator.fromJSON(JSONObject(location.toHashMap()))
var linkOrLocator: LinkOrLocator? = locationToLinkOrLocator(location)

if (locator != null) {
view.updateLocation(locator)
if (linkOrLocator != null) {
view.updateLocation(linkOrLocator)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.reactnativereadium.utils.LinkOrLocator
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.readium.r2.navigator.*
import org.readium.r2.shared.publication.Locator
import com.reactnativereadium.utils.EventChannel
import kotlinx.coroutines.channels.Channel
import org.readium.r2.shared.publication.Link

/*
* Base reader fragment class
Expand Down Expand Up @@ -49,7 +49,21 @@ abstract class BaseReaderFragment : Fragment() {
requireActivity().invalidateOptionsMenu()
}

fun go(locator: Locator, animated: Boolean): Boolean {
fun go(location: LinkOrLocator, animated: Boolean): Boolean {
var locator: Locator? = null
when (location) {
is LinkOrLocator.Link -> {
locator = navigator.publication.locatorFromLink(location.link)
}
is LinkOrLocator.Locator -> {
locator = location.locator
}
}

if (locator == null) {
return false
}

// don't attempt to navigate if we're already there
val currentLocator = navigator.currentLocator.value
if (locator.hashCode() == currentLocator.hashCode()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import androidx.lifecycle.ViewModelStore
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.util.RNLog
import com.reactnativereadium.utils.LinkOrLocator
import java.io.File
import java.io.IOException
import java.net.ServerSocket
Expand Down Expand Up @@ -46,9 +47,28 @@ class ReaderService(
this.startServer()
}

fun locatorFromLinkOrLocator(
location: LinkOrLocator?,
publication: Publication,
): Locator? {

if (location == null) return null

when (location) {
is LinkOrLocator.Link -> {
return publication.locatorFromLink(location.link)
}
is LinkOrLocator.Locator -> {
return location.locator
}
}

return null
}

suspend fun openPublication(
fileName: String,
initialLocation: Locator?,
initialLocation: LinkOrLocator?,
callback: suspend (fragment: BaseReaderFragment) -> Unit
) {
val file = File(fileName)
Expand All @@ -62,8 +82,9 @@ class ReaderService(
.onSuccess {
val url = prepareToServe(it)
if (url != null) {
val locator = locatorFromLinkOrLocator(initialLocation, it)
val readerFragment = EpubReaderFragment.newInstance(url)
readerFragment.initFactory(it, initialLocation)
readerFragment.initFactory(it, locator)
callback.invoke(readerFragment)
}
}
Expand Down
4 changes: 1 addition & 3 deletions android/src/main/java/com/reactnativereadium/utils/File.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.reactnativereadium.utils

import org.readium.r2.shared.publication.Locator

class File(
var path: String,
var initialLocation: Locator?
var initialLocation: LinkOrLocator?
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.reactnativereadium.utils

import org.readium.r2.shared.publication.Link as BaseLink
import org.readium.r2.shared.publication.Locator as BaseLocator

sealed class LinkOrLocator {
class Link(val link: BaseLink): LinkOrLocator()
class Locator(val locator: BaseLocator): LinkOrLocator()
}
9 changes: 2 additions & 7 deletions example/src/Reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { Settings as ReaderSettings } from './Settings';
const Reader: React.FC = () => {
const [toc, setToc] = useState<Link[] | null>([]);
const [file, setFile] = useState<File>();
const [location, setLocation] = useState<Locator>();
const [location, setLocation] = useState<Locator | Link>();
const [settings, setSettings] = useState<Partial<Settings>>(DEFAULT_SETTINGS);

useEffect(() => {
Expand Down Expand Up @@ -59,12 +59,7 @@ const Reader: React.FC = () => {
}}>
<TableOfContents
items={toc}
onPress={(link) => {
setLocation({
href: link.href,
type: 'application/xhtml+xml',
});
}}
onPress={(loc) => setLocation(loc)}
/>
<ReaderSettings
settings={settings}
Expand Down
4 changes: 2 additions & 2 deletions example/src/TableOfContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Link } from 'react-native-readium';

export interface TableOfContentsProps {
items?: Link[] | null;
onPress?: (link: Link) => void;
onPress?: (locator: Link) => void;
}

export const TableOfContents: React.FC<TableOfContentsProps> = ({
Expand Down Expand Up @@ -36,7 +36,7 @@ export const TableOfContents: React.FC<TableOfContentsProps> = ({
<Text>Table of Contents</Text>
{items.map((item, idx) => (
<ListItem
key={item.href}
key={idx}
onPress={() => {
if (onPress) {
onPress(item);
Expand Down
32 changes: 31 additions & 1 deletion ios/Reader/ReaderService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,40 @@ final class ReaderService: Loggable {
print(error)
}
}

static func locatorFromLocation(
_ location: NSDictionary?,
_ publication: Publication?
) -> Locator? {
guard location != nil else {
return nil
}

let hasLocations = location?["locations"] != nil
let hasChildren = location?["children"] != nil
let hasHashHref = (location!["href"] as! String).contains("#")

// check that we're not dealing with a Link
if ((hasChildren || hasHashHref) && !hasLocations) {
guard let publication = publication else {
return nil
}
guard let link = try? Link(json: location) else {
return nil
}

return publication.locate(link)
} else {
return try? Locator(json: location)
}

return nil
}

func buildViewController(
url: String,
bookId: String,
locator: Locator?,
location: NSDictionary?,
sender: UIViewController?,
completion: @escaping (ReaderViewController) -> Void
) {
Expand All @@ -37,6 +66,7 @@ final class ReaderService: Loggable {
},
receiveValue: { pub in
self.preparePresentation(of: pub)
let locator: Locator? = ReaderService.locatorFromLocation(location, pub)
let vc = reader.getViewController(
for: pub,
bookId: bookId,
Expand Down
22 changes: 9 additions & 13 deletions ios/ReadiumView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ class ReadiumView : UIView, Loggable {

@objc var file: NSDictionary? = nil {
didSet {
if let initialLocation = file?["initialLocation"] as? NSDictionary {
location = initialLocation
}

let initialLocation = file?["initialLocation"] as? NSDictionary
if let url = file?["url"] as? String {
self.loadBook(url: url)
self.loadBook(url: url, location: initialLocation)
}
}
}
Expand All @@ -39,27 +36,26 @@ class ReadiumView : UIView, Loggable {
@objc var onLocationChange: RCTDirectEventBlock?
@objc var onTableOfContents: RCTDirectEventBlock?

func loadBook(url: String) {
func loadBook(
url: String,
location: NSDictionary?
) {
guard let rootViewController = UIApplication.shared.delegate?.window??.rootViewController else { return }
let locator: Locator? = self.getLocator()

self.readerService.buildViewController(
url: url,
bookId: url,
locator: locator,
location: location,
sender: rootViewController,
completion: { vc in
self.addViewControllerAsSubview(vc)
self.location = location
}
)
}

func getLocator() -> Locator? {
if (location != nil) {
return try? Locator(json: location)
} else {
return nil
}
return ReaderService.locatorFromLocation(location, readerViewController?.publication)
}

func updateLocation() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/BaseReadiumView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { COMPONENT_NAME, LINKING_ERROR } from '../utils';

export type BaseReadiumViewProps = {
file: File;
location?: Locator;
location?: Locator | Link;
settings?: Partial<Settings>;
style?: ViewStyle;
onLocationChange?: (locator: Locator) => void;
Expand Down
3 changes: 2 additions & 1 deletion src/interfaces/File.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Link } from './Link';
import type { Locator } from './Locator';


Expand All @@ -10,5 +11,5 @@ export interface File {
/**
* An optional location that the eBook will be opened at.
*/
initialLocation?: Locator;
initialLocation?: Locator | Link;
}

0 comments on commit ebc1030

Please sign in to comment.