Skip to content

Commit 846a6f1

Browse files
authored
Add a new experimental EPUB FXL navigator (#567)
1 parent bbf59e9 commit 846a6f1

File tree

132 files changed

+12141
-12
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+12141
-12
lines changed

.github/workflows/checks.yml

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- name: Lint
4040
run: ./gradlew ktlintCheck
4141

42-
lint-js:
42+
lint-legacy-js:
4343
name: Lint JavaScript
4444
runs-on: macos-latest
4545
if: ${{ !github.event.pull_request.draft }}
@@ -67,5 +67,35 @@ jobs:
6767
run: pnpm --dir "$scripts" run checkformat
6868
- name: Check if bundled scripts are up-to-date
6969
run: |
70-
make scripts
70+
make scripts-legacy
7171
git diff --exit-code --name-only src/main/assets/readium/scripts/*.js
72+
lint-new-ts:
73+
name: Lint TypeScript
74+
runs-on: macos-latest
75+
if: ${{ !github.event.pull_request.draft }}
76+
env:
77+
scripts: ${{ 'readium/navigators/web/scripts' }}
78+
steps:
79+
- name: Checkout
80+
uses: actions/checkout@v4
81+
- name: Install pnpm
82+
uses: pnpm/action-setup@v4
83+
with:
84+
package_json_file: readium/navigators/web/scripts/package.json
85+
run_install: false
86+
- name: Setup cache
87+
uses: actions/setup-node@v4
88+
with:
89+
node-version: 20
90+
cache: 'pnpm'
91+
cache-dependency-path: readium/navigators/web/scripts/pnpm-lock.yaml
92+
- name: Install dependencies
93+
run: pnpm --dir "$scripts" install --frozen-lockfile
94+
- name: Lint
95+
run: pnpm --dir "$scripts" run lint
96+
- name: Check formatting
97+
run: pnpm --dir "$scripts" run checkformat
98+
- name: Check if bundled scripts are up-to-date
99+
run: |
100+
make scripts-new
101+
git diff --exit-code --name-only readium/navigators/web/src/main/assets/*

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,6 @@ lcp.patch
9494

9595
# Kotlin
9696
.kotlin/
97+
98+
# Script outpouts
99+
readium/navigators/web/scripts/dist/

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
SCRIPTS_PATH := readium/navigator/src/main/assets/_scripts
2+
SCRIPTS_NAVIGATOR_WEB_PATH := readium/navigators/web/scripts
23

34
help:
45
@echo "Usage: make <target>\n\n\
@@ -16,7 +17,7 @@ format:
1617
./gradlew ktlintFormat
1718

1819
.PHONY: scripts
19-
scripts:
20+
scripts-legacy:
2021
@which corepack >/dev/null 2>&1 || (echo "ERROR: corepack is required, please install it first\nhttps://pnpm.io/installation#using-corepack"; exit 1)
2122

2223
cd $(SCRIPTS_PATH); \
@@ -25,3 +26,18 @@ scripts:
2526
pnpm run format; \
2627
pnpm run lint; \
2728
pnpm run bundle
29+
30+
.PHONY: scripts
31+
scripts-new:
32+
@which corepack >/dev/null 2>&1 || (echo "ERROR: corepack is required, please install it first\nhttps://pnpm.io/installation#using-corepack"; exit 1)
33+
34+
cd $(SCRIPTS_NAVIGATOR_WEB_PATH); \
35+
corepack install; \
36+
pnpm install --frozen-lockfile; \
37+
pnpm run format; \
38+
pnpm run lint; \
39+
pnpm run bundle; \
40+
mv dist/* ../src/main/assets/readium/navigators/web/
41+
42+
.PHONY: scripts
43+
scripts: scripts-legacy scripts-new

gradle/libs.versions.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22

33
kotlin = "2.0.21"
44
agp = "8.7.2"
5-
desugar_jdk_libs = "2.0.4"
5+
desugar_jdk_libs = "2.1.2"
66
gradle-maven-publish-plugin = "0.28.0"
77

88
androidx-activity = "1.9.3"
99
androidx-annotation = "1.9.1"
1010
androidx-appcompat = "1.7.0"
1111
androidx-browser = "1.8.0"
1212
androidx-cardview = "1.0.0"
13-
androidx-compose-animation = "1.6.7"
14-
androidx-compose-foundation = "1.6.7"
15-
androidx-compose-material = "1.6.7"
16-
androidx-compose-material3 = "1.2.1"
17-
androidx-compose-runtime = "1.6.7"
18-
androidx-compose-ui = "1.6.7"
19-
androidx-constraintlayout = "2.1.4"
13+
androidx-compose-animation = "1.7.5"
14+
androidx-compose-foundation = "1.7.5"
15+
androidx-compose-material = "1.7.5"
16+
androidx-compose-material3 = "1.3.1"
17+
androidx-compose-runtime = "1.7.5"
18+
androidx-compose-ui = "1.7.5"
19+
androidx-constraintlayout = "2.2.0"
2020
androidx-core = "1.15.0"
2121
androidx-datastore = "1.1.1"
2222
androidx-fragment-ktx = "1.8.5"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2022 Readium Foundation. All rights reserved.
3+
* Use of this source code is governed by the BSD-style license
4+
* available in the top-level LICENSE file of the project.
5+
*/
6+
7+
plugins {
8+
id("readium.library-conventions")
9+
alias(libs.plugins.kotlin.serialization)
10+
alias(libs.plugins.compose.compiler)
11+
}
12+
13+
android {
14+
namespace = "org.readium.navigators.common"
15+
16+
buildFeatures {
17+
compose = true
18+
}
19+
}
20+
21+
dependencies {
22+
api(project(":readium:readium-shared"))
23+
api(project(":readium:readium-navigator"))
24+
25+
implementation(libs.kotlinx.serialization.json)
26+
implementation(libs.bundles.compose)
27+
implementation(libs.timber)
28+
implementation(libs.kotlinx.coroutines.android)
29+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pom.artifactId=readium-navigator-common
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest />
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2024 Readium Foundation. All rights reserved.
3+
* Use of this source code is governed by the BSD-style license
4+
* available in the top-level LICENSE file of the project.
5+
*/
6+
7+
package org.readium.navigator.common
8+
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.runtime.rememberCoroutineScope
11+
import kotlinx.coroutines.CoroutineScope
12+
import kotlinx.coroutines.launch
13+
import org.readium.r2.shared.ExperimentalReadiumApi
14+
import org.readium.r2.shared.util.AbsoluteUrl
15+
import org.readium.r2.shared.util.Url
16+
17+
/**
18+
* This listener lets you decide what to do when hyperlinks are activated, whether they point to
19+
* a readingOrder item, a non-linear resource or external content.
20+
*/
21+
@ExperimentalReadiumApi
22+
public interface HyperlinkListener {
23+
24+
public fun onReadingOrderLinkActivated(location: HyperlinkLocation, context: LinkContext?)
25+
26+
public fun onNonLinearLinkActivated(location: HyperlinkLocation, context: LinkContext?)
27+
28+
public fun onExternalLinkActivated(url: AbsoluteUrl, context: LinkContext?)
29+
}
30+
31+
@ExperimentalReadiumApi
32+
public data class HyperlinkLocation(
33+
public val href: Url,
34+
public val fragment: String? = null
35+
)
36+
37+
@ExperimentalReadiumApi
38+
public sealed interface LinkContext
39+
40+
@ExperimentalReadiumApi
41+
public data class FootnoteContext(
42+
public val noteContent: String
43+
) : LinkContext
44+
45+
@ExperimentalReadiumApi
46+
public class NullHyperlinkListener : HyperlinkListener {
47+
override fun onReadingOrderLinkActivated(location: HyperlinkLocation, context: LinkContext?) {
48+
}
49+
50+
override fun onNonLinearLinkActivated(location: HyperlinkLocation, context: LinkContext?) {
51+
}
52+
53+
override fun onExternalLinkActivated(url: AbsoluteUrl, context: LinkContext?) {
54+
}
55+
}
56+
57+
/**
58+
* A [HyperlinkListener] following links to readingOrder items.
59+
*
60+
* Activations of links to external content or non-linear items are ignored by default.
61+
* To handle them, pass [onNonLinearLinkActivated] and [onExternalLinkActivated] delegates.
62+
*/
63+
@ExperimentalReadiumApi
64+
@Composable
65+
public fun <L : Location> defaultHyperlinkListener(
66+
controller: NavigationController<L, *>,
67+
shouldFollowReadingOrderLink: (HyperlinkLocation, LinkContext?) -> Boolean = { _, _ -> true },
68+
onNonLinearLinkActivated: (HyperlinkLocation, LinkContext?) -> Unit = { _, _ -> },
69+
onExternalLinkActivated: (AbsoluteUrl, LinkContext?) -> Unit = { _, _ -> }
70+
): HyperlinkListener {
71+
val coroutineScope = rememberCoroutineScope()
72+
73+
return DefaultHyperlinkListener(
74+
coroutineScope = coroutineScope,
75+
controller = controller,
76+
shouldFollowReadingOrderLink = shouldFollowReadingOrderLink,
77+
onNonLinearLinkActivatedDelegate = onNonLinearLinkActivated,
78+
onExternalLinkActivatedDelegate = onExternalLinkActivated
79+
)
80+
}
81+
82+
@ExperimentalReadiumApi
83+
private class DefaultHyperlinkListener<L : Location>(
84+
private val coroutineScope: CoroutineScope,
85+
private val controller: NavigationController<L, *>,
86+
private val shouldFollowReadingOrderLink: (HyperlinkLocation, LinkContext?) -> Boolean,
87+
private val onNonLinearLinkActivatedDelegate: (HyperlinkLocation, LinkContext?) -> Unit,
88+
private val onExternalLinkActivatedDelegate: (AbsoluteUrl, LinkContext?) -> Unit
89+
) : HyperlinkListener {
90+
91+
override fun onReadingOrderLinkActivated(location: HyperlinkLocation, context: LinkContext?) {
92+
if (shouldFollowReadingOrderLink(location, context)) {
93+
coroutineScope.launch { controller.goTo(location) }
94+
}
95+
}
96+
97+
override fun onNonLinearLinkActivated(location: HyperlinkLocation, context: LinkContext?) {
98+
onNonLinearLinkActivatedDelegate(location, context)
99+
}
100+
101+
override fun onExternalLinkActivated(url: AbsoluteUrl, context: LinkContext?) {
102+
onExternalLinkActivatedDelegate(url, context)
103+
}
104+
}

0 commit comments

Comments
 (0)