Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
306f5b2
fix: install the right version of hermes compiler
CD-Z Apr 6, 2026
3de93c5
feat: migrate from rn community cli to rock (#1812)
CD-Z Apr 13, 2026
01596c0
fix: Apply active filters to chapter count display (Closes #1793) (#1…
Definisi Apr 13, 2026
2a919ec
chore: gitignore tsconfig.tsbuildinfo build cache (#1829)
tinywind May 3, 2026
b680a07
fix(browse): use gesture-handler Pressable in PluginListItem (#1828)
tinywind May 3, 2026
eafa545
Use novel refactor (#1838)
CD-Z May 6, 2026
8f47e8f
Theme switcher refactor (#1839)
CD-Z May 7, 2026
2cd5a97
fix: await database operations inside transactions (#1845)
VictorPersona May 10, 2026
25c93af
fix(reader): restore saved chapter progress from db if available (#1850)
Jaky-gh May 24, 2026
60f42ed
chore(build): patch @rock-js/plugin-metro for Windows compatibility (…
tinywind May 24, 2026
34be7a1
fix(native-file): prevent fatal crash when unlinking missing files (#…
tinywind May 24, 2026
57b9d41
fix(service): Correct Notification Throttle Logic in ServiceManager.s…
alirafiqmalik Jul 1, 2026
e8c5550
perf: Optimize Regex for Removing Extra Paragraph Spacing (#1872)
Palloxin Jul 1, 2026
54fedc2
fix(tts): Remove Leading and Trailing Quotes from Normalized Text (#1…
vikas-movva Jul 1, 2026
bbb2645
chore: Clean Reader Preview Content (#1867)
Palloxin Jul 1, 2026
5d996f1
feat(downloads): Make Chapter Download Cooldown Configurable (#1834)
tinywind Jul 1, 2026
93bc5e5
fix: Handle Missing MAL List Entries and Null Progress (Closes #1875)
rajarsheechatterjee Jul 1, 2026
52d5c99
fix: Order EPUB Chapters Across Paginated Pages (Closes #1762)
rajarsheechatterjee Jul 1, 2026
7abee71
test: Generate Debug Environment Variables In Testing Workflow
rajarsheechatterjee Jul 13, 2026
788f6ce
fix: Prevent Table Overflow and Clean Up CSS (#1876) (#1876)
Palloxin Jul 13, 2026
98a2a1d
feat: Implement Scanlator Filtering (#1864)
luigidotmoe Jul 15, 2026
4404494
fix(database): Repair Migration History For Scanlator Column
rajarsheechatterjee Jul 19, 2026
71da09e
feat(reader): Add Current Chapter Search (#1877)
Shisii Jul 19, 2026
02b6b98
fix: Prevent WebView Reset When Reader Settings Change (#1884)
Ecasept Jul 19, 2026
3e1e211
feat: Add Address Bar in WebView (#1870)
luigidotmoe Jul 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 17 additions & 45 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Build

on:
push:
branches:
Expand All @@ -14,6 +15,7 @@ jobs:
name: Build App
runs-on: ubuntu-latest
timeout-minutes: 60

steps:
- name: Checkout Repository
uses: actions/checkout@v4
Expand All @@ -23,7 +25,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '24'

- name: Setup pnpm
uses: pnpm/action-setup@v4
Expand All @@ -41,54 +43,24 @@ jobs:
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-read-only: false

- name: Install Dependencies
run: pnpm install --frozen-lockfile

- name: Create Environment File
run: |
cat > .env << EOF
MYANIMELIST_CLIENT_ID=${{ vars.MYANIMELIST_CLIENT_ID }}
ANILIST_CLIENT_ID=${{ vars.ANILIST_CLIENT_ID }}
GIT_HASH=$(git rev-parse --short HEAD)
RELEASE_DATE=$(date --utc +'%d/%m/%y %I:%M %p %Z')
BUILD_TYPE=Github Action
EOF

- name: Make Gradlew Executable
run: cd android && chmod +x ./gradlew

- name: Set Environment Variables
run: |
set -x
echo "COMMIT_COUNT=$(git rev-list --count HEAD)" >> $GITHUB_ENV
echo "COMMIT_ID=$(git rev-parse --short HEAD)" >> $GITHUB_ENV

- name: Build Android Release
env:
COMMIT_COUNT: ${{ env.COMMIT_COUNT }}
COMMIT_ID: ${{ env.COMMIT_ID }}
- name: Generate Environment Variables
run: |
sed -i 's/lnreader/lnreader-r${{ env.COMMIT_COUNT }}(${{ env.COMMIT_ID }})/g' android/app/src/main/res/values/strings.xml
cd android && ./gradlew assembleRelease -PcustomAppId=com.rajarsheechatterjee.LNReader.commit_${{ env.COMMIT_ID }} --build-cache
mv app/build/outputs/apk/release/app-release.apk app/build/outputs/apk/release/LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }}.apk
pnpm generate:env:release \
--build-type "Github Action" \
--node-env "production" \
--myanimelist-client-id "${{ vars.MYANIMELIST_CLIENT_ID }}" \
--anilist-client-id "${{ vars.ANILIST_CLIENT_ID }}"

- name: Upload Release Artifact
- name: Build Android Release with Rock
uses: callstackincubator/android@v3
env:
COMMIT_COUNT: ${{ env.COMMIT_COUNT }}
COMMIT_ID: ${{ env.COMMIT_ID }}
uses: actions/upload-artifact@v4
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
with:
name: LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }}
path: android/app/build/outputs/apk/release/LNReader-r${{ env.COMMIT_COUNT }}-${{ env.COMMIT_ID }}.apk
retention-days: 30
re-sign: true
github-token: ${{ secrets.GITHUB_TOKEN }}
variant: preRelease
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '24'

- name: Setup pnpm
uses: pnpm/action-setup@v4
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '24'

- name: Setup pnpm
uses: pnpm/action-setup@v4
Expand All @@ -44,5 +44,8 @@ jobs:
- name: Install Dependencies
run: pnpm install --frozen-lockfile

- name: Generate Environment Variables
run: pnpm generate:env:debug

- name: Run Tests
run: pnpm run test
2 changes: 1 addition & 1 deletion .github/workflows/types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '24'

- name: Setup pnpm
uses: pnpm/action-setup@v4
Expand Down
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ npm-debug.*
*.mobileprovision
*.orig.*
web-build/
.env
.*env
reader_playground/index.html
# macOS
.DS_Store
Expand Down Expand Up @@ -89,8 +89,16 @@ flake.lock
# pnpm
.pnpm-store

src/generated/**/*

.cursor/
.agents/
.claude/
.jj/
.sisyphus/

# Rock
.rock/

# TypeScript
*.tsbuildinfo
20 changes: 13 additions & 7 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import AppErrorBoundary, {
import Main from './src/navigators/Main';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { useInitDatabase } from '@database/db';
import { ThemeProvider } from '@hooks/persisted/useTheme';

Notifications.setNotificationHandler({
handleNotification: async () => {
Expand All @@ -30,7 +31,6 @@ Notifications.setNotificationHandler({
},
});


const App = () => {
const state = useInitDatabase();

Expand All @@ -44,17 +44,23 @@ const App = () => {
return <ErrorFallback error={state.error} resetError={() => null} />;
}

if (!state.success) {
return null;
}

return (
<Suspense fallback={null}>
<GestureHandlerRootView style={styles.flex}>
<AppErrorBoundary>
<SafeAreaProvider>
<PaperProvider>
<BottomSheetModalProvider>
<StatusBar translucent={true} backgroundColor="transparent" />
<Main />
</BottomSheetModalProvider>
</PaperProvider>
<ThemeProvider>
<PaperProvider>
<BottomSheetModalProvider>
<StatusBar translucent={true} backgroundColor="transparent" />
<Main />
</BottomSheetModalProvider>
</PaperProvider>
</ThemeProvider>
</SafeAreaProvider>
</AppErrorBoundary>
</GestureHandlerRootView>
Expand Down
5 changes: 5 additions & 0 deletions __mocks__/database.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
jest.mock('@database/queries/NovelQueries', () => ({
getNovelById: jest.fn(),
getNovelByPath: jest.fn(),
deleteCachedNovels: jest.fn(),
getCachedNovels: jest.fn(),
Expand Down Expand Up @@ -30,9 +31,13 @@ jest.mock('@database/queries/ChapterQueries', () => ({
insertChapters: jest.fn(),
getCustomPages: jest.fn(),
getChapterCount: jest.fn(),
getChapterCountSync: jest.fn(),
getPageChaptersBatched: jest.fn(),
getNovelChaptersSync: jest.fn(),
getFirstUnreadChapter: jest.fn(),
updateChapterProgress: jest.fn(),
getNovelScanlators: jest.fn(() => []),
getNovelScanlatorsSync: jest.fn(() => []),
}));

jest.mock('@database/queries/HistoryQueries', () => ({
Expand Down
13 changes: 8 additions & 5 deletions __tests-modules__/test-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { Provider as PaperProvider } from 'react-native-paper';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { ThemeProvider } from '@hooks/persisted/useTheme';

import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary';
import { NovelContextProvider } from '@screens/novel/NovelContext';
Expand All @@ -13,11 +14,13 @@ const AllTheProviders = ({ children }: { children: React.ReactElement }) => {
return (
<GestureHandlerRootView>
<SafeAreaProvider>
<PaperProvider>
<BottomSheetModalProvider>
<AppErrorBoundary>{children}</AppErrorBoundary>
</BottomSheetModalProvider>
</PaperProvider>
<ThemeProvider>
<PaperProvider>
<BottomSheetModalProvider>
<AppErrorBoundary>{children}</AppErrorBoundary>
</BottomSheetModalProvider>
</PaperProvider>
</ThemeProvider>
</SafeAreaProvider>
</GestureHandlerRootView>
);
Expand Down
17 changes: 14 additions & 3 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ react {
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
// codegenDir = file("../../node_modules/@react-native/codegen")
// The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
// cliFile = file("../../node_modules/react-native/cli.js")
cliFile = file("../../node_modules/rock/dist/src/bin.js")

/* Variants */
// The list of variants to that are debuggable. For those we're going to
Expand Down Expand Up @@ -84,7 +84,7 @@ android {
compileSdk rootProject.ext.compileSdkVersion
namespace "com.rajarsheechatterjee.LNReader"
defaultConfig {
applicationId project.hasProperty('customAppId') ? project.getProperty('customAppId') : 'com.rajarsheechatterjee.LNReader'
applicationId 'com.rajarsheechatterjee.LNReader'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
// Generated version code. Supports versions up to 1024.1024.2048
Expand All @@ -105,6 +105,17 @@ android {
applicationIdSuffix 'debug'
versionNameSuffix '-debug'
}
preRelease {
initWith release
matchingFallbacks = ["release"]
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
applicationIdSuffix 'preRelease'
versionNameSuffix '-pre-release'
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
Expand Down Expand Up @@ -134,4 +145,4 @@ dependencies {
} else {
implementation jscFlavor
}
}
}
22 changes: 20 additions & 2 deletions android/app/src/main/assets/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ img {
max-width: 100%;
}

div:has(> table):not(#LNReader-chapter) {
overflow: auto;
}

table {
background-color: var(--theme-onPrimary);
border-collapse: collapse;
Expand Down Expand Up @@ -83,8 +87,6 @@ td {
margin-left: var(--readerSettings-padding);
margin-right: var(--readerSettings-padding);
border-radius: 50px;
border-width: 1;
color: var(--theme-onPrimary);
background-color: var(--theme-primary);
font-family: var(--readerSettings-fontFamily);
font-size: 16px;
Expand All @@ -93,6 +95,7 @@ td {
}

.next-button {
color: var(--theme-onPrimary);
min-height: 40px;
text-overflow: ellipsis;
overflow: hidden;
Expand Down Expand Up @@ -160,6 +163,21 @@ td {
background-color: transparent;
}

mark.lnreader-search-match {
border-radius: 2px;
color: inherit;
background-color: color-mix(
in srgb,
var(--theme-tertiary) 55%,
transparent
);
}

mark.lnreader-search-match-active {
color: var(--theme-onPrimary);
background-color: var(--theme-primary);
}

#Image-Modal {
position: fixed;
left: 0;
Expand Down
11 changes: 9 additions & 2 deletions android/app/src/main/assets/js/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ window.tts = new (function () {
'BR',
'STRONG',
'A',
'MARK',
];
this.prevElement = null;
this.currentElement = reader.chapterElement;
Expand Down Expand Up @@ -167,6 +168,7 @@ window.tts = new (function () {
this.normalizeText = text => {
if (!text) return '';
return text
.replace(/^["'“”‘’]+|["'“”‘’]+$/g, '')
.replace(/\s+/g, ' ')
.replace(/\s*([.,!?;:])\s*/g, '$1 ')
.trim();
Expand Down Expand Up @@ -780,14 +782,19 @@ window.addEventListener('load', () => {
`${
/\/p>/.test(_)
? _.replace(
/<br>\s*<br>(?:(?=\s*<\/?p[> ])|(?<=<\/?p\b[^>]*><br>\s*<br>))\s*/g,
/<br>\s*<br>(?:(?=\s*<\/?p[> ])|(?<=<\/?p(?:>| [^>]+>)<br>\s*<br>))\s*/g,
'',
)
: _
}`,
) //if p found, delete all double br near p
.replace(/<br>(?:(?=\s*<\/?p[> ])|(?<=<\/?p>\s*<br>))\s*/g, '');
.replace(/<br>(?:(?=\s*<\/?p[> ])|(?<=<\/?p(?:>| [^>]+>)(?:<[^>]+>)*\s*<br>))\s*/g, '');
}
reader.chapterElement.innerHTML = html;
const searchQuery = window.readerSearch?.query;
const searchIndex = window.readerSearch?.index;
if (searchQuery) {
window.readerSearch.search(searchQuery, searchIndex);
}
});
})();
Loading