Compare commits

...

405 Commits

Author SHA1 Message Date
github-actions[bot] f53546cc01 chore: Release v2.1.1 2026-07-28 04:28:46 +00:00
Rajarshee Chatterjee 594b078fad fix: Harden Database Migration Recovery 2026-07-28 07:32:55 +05:30
Rajarshee Chatterjee ac1b2f5ce4 feat: Add Durable Completion Notifications 2026-07-27 22:27:04 +05:30
Rajarshee Chatterjee 648d21062f feat: Add Parallel Task Queues With Per-Plugin Download Limits 2026-07-27 22:15:02 +05:30
Rajarshee Chatterjee 89d715ce6b docs: Replace Beta Badge With Build Preview Status 2026-07-27 21:54:45 +05:30
Rajarshee Chatterjee bd527a02ef feat: Improve Backup Selection, Notifications, And Plugin Restore 2026-07-27 20:03:03 +05:30
Rajarshee Chatterjee 11117c1b54 feat: Add GitHub-Style Embed For Nightly Notifications 2026-07-27 17:12:50 +05:30
Patrick Loser ba25d5b8fe feat: Add Rozenite DevTools Integration (#1912)
* add rozenite

* add opsqlite adapter

* fix lint

* Update scripts/generate-env-file.cjs

Co-authored-by: Rajarshee Chatterjee <rajarshee.adm@gmail.com>

---------

Co-authored-by: Rajarshee Chatterjee <rajarshee.adm@gmail.com>
2026-07-27 17:07:41 +05:30
Rajarshee Chatterjee 31cb4b994a fix: Align Bottom Navigation With Material 3 Specs 2026-07-27 15:11:38 +05:30
Rajarshee Chatterjee 72ef618af1 fix: Preserve Existing Data During Novel Counter Migration 2026-07-27 15:06:36 +05:30
Rajarshee Chatterjee 936fd223bb feat: Add Discord Notifications For Nightly Builds 2026-07-27 10:49:30 +05:30
Rajarshee Chatterjee 6d26438d2d test: Update Expected Database Migrations 2026-07-27 08:47:32 +05:30
Rajarshee Chatterjee 5eb5a55c4c fix: Repair Skipped Novel Counter Migrations 2026-07-27 08:41:55 +05:30
Rajarshee Chatterjee 6cd86d1422 chore: Update Translations (#1906)
* New translations strings.json (Romanian)

[ci skip]
[ci skip]

* New translations strings.json (French)

[ci skip]
[ci skip]

* New translations strings.json (Spanish)

[ci skip]
[ci skip]

* New translations strings.json (Afrikaans)

[ci skip]
[ci skip]

* New translations strings.json (Arabic)

[ci skip]
[ci skip]

* New translations strings.json (Catalan)

[ci skip]
[ci skip]

* New translations strings.json (Czech)

[ci skip]
[ci skip]

* New translations strings.json (Danish)

[ci skip]
[ci skip]

* New translations strings.json (German)

[ci skip]
[ci skip]

* New translations strings.json (Greek)

[ci skip]
[ci skip]

* New translations strings.json (Finnish)

[ci skip]
[ci skip]

* New translations strings.json (Hebrew)

[ci skip]
[ci skip]

* New translations strings.json (Hungarian)

[ci skip]
[ci skip]

* New translations strings.json (Italian)

[ci skip]
[ci skip]

* New translations strings.json (Japanese)

[ci skip]
[ci skip]

* New translations strings.json (Korean)

[ci skip]
[ci skip]

* New translations strings.json (Dutch)

[ci skip]
[ci skip]

* New translations strings.json (Norwegian)

[ci skip]
[ci skip]

* New translations strings.json (Polish)

[ci skip]
[ci skip]

* New translations strings.json (Portuguese)

[ci skip]
[ci skip]

* New translations strings.json (Russian)

[ci skip]
[ci skip]

* New translations strings.json (Albanian)

[ci skip]
[ci skip]

* New translations strings.json (Serbian (Cyrillic))

[ci skip]
[ci skip]

* New translations strings.json (Swedish)

[ci skip]
[ci skip]

* New translations strings.json (Turkish)

[ci skip]
[ci skip]

* New translations strings.json (Ukrainian)

[ci skip]
[ci skip]

* New translations strings.json (Chinese Simplified)

[ci skip]
[ci skip]

* New translations strings.json (Chinese Traditional)

[ci skip]
[ci skip]

* New translations strings.json (Vietnamese)

[ci skip]
[ci skip]

* New translations strings.json (Portuguese, Brazilian)

[ci skip]
[ci skip]

* New translations strings.json (Indonesian)

[ci skip]
[ci skip]

* New translations strings.json (Hindi)

[ci skip]
[ci skip]

* New translations strings.json (Assamese)

[ci skip]
[ci skip]

* New translations strings.json (Odia)

[ci skip]
[ci skip]
2026-07-27 08:19:16 +05:30
Patrick Loser 9783c4d5e3 fix: Normalize Repository Back Navigation (#1905)
* remove useBackHandler

* Remove popToTop
2026-07-27 00:53:11 +05:30
github-actions[bot] 87f602a1fd chore: Release v2.1.0 2026-07-26 17:41:31 +00:00
Rajarshee Chatterjee 3d32ddd6e9 fix: Improve Release Selection And APK Verification 2026-07-26 23:10:38 +05:30
Rajarshee Chatterjee 7ddd934c2b fix: Handle Missing Tracker Client IDs 2026-07-26 22:46:23 +05:30
Rajarshee Chatterjee b53c91f748 chore: Separate Jest And Type Check Workflows 2026-07-26 22:33:56 +05:30
Rajarshee Chatterjee 9d106a3716 feat: Add Semantic Version Bump Selection 2026-07-26 22:23:46 +05:30
Patrick Loser 8b0a8dc4c0 chore: Update Husky Pre-Commit and Pre-Push Hooks 2026-07-26 22:22:38 +05:30
Patrick Loser 72bfcad034 fix: Resolve Type and Lint Issues 2026-07-26 22:22:24 +05:30
Rajarshee Chatterjee 345d084eba feat: Add Chapter Numbers To EPUB Titles (Closes #1405) 2026-07-26 21:07:04 +05:30
Rajarshee Chatterjee 10b6042a9c feat: Add EPUB Export Progress Notifications
Closes #799
2026-07-26 20:53:25 +05:30
Rajarshee Chatterjee bd71611725 feat: Improve Chapter Drawer And WebView App Bar 2026-07-26 20:26:34 +05:30
Rajarshee Chatterjee 8f53550d2c fix: Restore Tracker Search Requests 2026-07-26 19:42:48 +05:30
Rajarshee Chatterjee 52914b978d fix: Migrate Volume Button Events to Expo Listener API 2026-07-26 18:41:47 +05:30
Rajarshee Chatterjee ff12759a66 ci: Run Preview Builds Daily When Missing 2026-07-26 18:35:45 +05:30
Rajarshee Chatterjee e62868b82c feat: Automate Draft Android Releases
[skip ci]
2026-07-26 18:27:18 +05:30
Rajarshee Chatterjee 97be48dfe1 feat: Add Migration Review Options
Closes #1037
Closes #937
2026-07-26 15:44:09 +05:30
Rajarshee Chatterjee cbe7d70e2d fix: Restore Collapsed TTS Button Interaction
[skip ci]
2026-07-26 15:08:02 +05:30
Rajarshee Chatterjee 6548e9a6f8 fix: Improve Paged Reader Layout and Performance
Closes #1721
2026-07-26 15:07:46 +05:30
Rajarshee Chatterjee 138fdff2e5 fix: Prevent Duplicate WebView Bottom Insets
Closes #1757
2026-07-26 14:35:44 +05:30
Rajarshee Chatterjee f1dd4b77a8 feat: Improve Empty Chapter Reporting
[skip ci]
2026-07-26 14:19:37 +05:30
Rajarshee Chatterjee 9b356ffd17 fix: Preserve Client IDs And Improve Environment Generation
[skip ci]
2026-07-26 13:42:41 +05:30
Rajarshee Chatterjee 5cf7e15d76 feat: Add Reader Setting Descriptions 2026-07-26 12:59:53 +05:30
Rajarshee Chatterjee 6a7e90c179 fix: Align Advanced Reader Tabs And Localize Actions 2026-07-26 12:55:39 +05:30
Rajarshee Chatterjee 99c31d56bb fix: Use MD3 Outline Variant For Tab Borders 2026-07-26 12:38:39 +05:30
Rajarshee Chatterjee 4d98247980 feat: Add Android DNS Over HTTPS Support
[skip ci]

Closes #478
2026-07-26 00:23:07 +05:30
Rajarshee Chatterjee 164f5d2592 feat: Add Automatic Library Update Scheduling
Closes #13
Closes #953
2026-07-25 20:48:37 +05:30
Rajarshee Chatterjee 89cddf9e3d fix: Expand Discover Row Ripple 2026-07-25 20:34:30 +05:30
Rajarshee Chatterjee eac6ad584e feat: Improve Novel App Bar And EPUB Export UX
[skip ci]
2026-07-25 19:15:43 +05:30
Rajarshee Chatterjee 93fe04c28c feat: Polish Reader Controls And Navigation
[skip ci]
2026-07-25 18:16:08 +05:30
Rajarshee Chatterjee c9358b3a9f feat: Update Android Branding Assets 2026-07-25 15:52:13 +05:30
Rajarshee Chatterjee 67d2c7c01b feat: Add Collapsible TTS Controls And Stable Notification Progress
[skip ci]
2026-07-25 14:47:36 +05:30
Rajarshee Chatterjee c30441b949 feat: Add Smart Library Update Filters
Closes #423
2026-07-25 14:29:41 +05:30
Rajarshee Chatterjee 6d2a9f8e15 fix: Prevent Slider Flicker After Release 2026-07-25 14:13:25 +05:30
Rajarshee Chatterjee 799845426c feat: Add Parallel Library Updates Across Sources 2026-07-25 13:54:46 +05:30
Rajarshee Chatterjee 8ac628b3de fix: Improve History Removal Flow.
[skip ci]

Closes #885
2026-07-25 13:47:26 +05:30
Rajarshee Chatterjee d02291c22c feat: Remember Last Used Library Category
[skip ci]

Closes #1026
2026-07-25 12:21:53 +05:30
Rajarshee Chatterjee 7af0911f5b feat: Add High-Resolution Android Flavor Assets
[skip ci]
2026-07-25 11:54:21 +05:30
Rajarshee Chatterjee 098782d6aa refactor: Simplify Settings Layout And Theme Selection
[skip ci]
2026-07-25 11:23:41 +05:30
Rajarshee Chatterjee 732628c40b feat: Add Date Format And Relative Timestamp Settings.
Closes #834
2026-07-25 11:03:20 +05:30
Rajarshee Chatterjee 5e62c36a9b feat: Add Library Settings And Category-Based Global Updates
Closes #381
Closes #1423
Closes #1444
2026-07-25 10:48:39 +05:30
Rajarshee Chatterjee 3d102a52a7 perf(reader): Cut Chapter Open Latency and Reading-Time Re-Renders (#1899)
[skip ci]

* perf(reader): Cut Chapter Open Latency and Reading-Time Re-Renders

Opening a chapter blocked the first paint on work that is not needed to
show text, and mounted several heavy subtrees while that work was running.

Load path (useChapter):
- Render the chapter as soon as its text is ready. Resolving the next and
  previous chapter (two full-scan queries, plus a plugin page fetch over
  the network at a page boundary) now runs after the chapter is on screen
  and is pushed into the WebView instead of being baked into its HTML.
- Read downloaded chapters with a single native call instead of
  exists() + readFile(), and start that read in parallel with the
  database lookup instead of after it.
- Cache render-ready (sanitized) HTML, so prefetched chapters skip
  sanitize-html entirely, and prefetch when the JS thread is idle rather
  than in the middle of the current load.
- Drop failed loads from the cache so a retry actually retries.
- Guard every load with a generation id, keep getChapter and friends
  referentially stable, and defer history/last-read writes off the
  critical path.

Mounting:
- The chapter drawer (a list of every chapter) and the settings sheet
  (a tab view that enumerates TTS engines and voices) are mounted on
  first use instead of at reader open; the sheet's tabs are lazy.
- The loading skeleton shimmers its first lines only instead of
  animating a full screen of gradient views.

While reading:
- Progress and mark-as-read writes keep the chapter array identity when
  nothing changed, and a chapter is marked read (and pushed to the
  tracker) once instead of on every progress report.
- Drawer rows are a memoized component with stable props, the drawer
  stylesheet is memoized, and the WebView document is built once per
  chapter instead of on every reader re-render.

Also fixes the drawer's loading branch, which compared a ref object to
undefined and was therefore unreachable, and guards page-reader
transitions against a not-yet-resolved neighbour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac6DNTqrKqsQjbdMSmU3nm

* perf(reader): Stop Double-Parsing the Chapter and Cut Per-Frame Work

Follow-up to the previous pass, mostly inside the reader document itself.

Chapter open:
- The chapter body was written back into the DOM on every open. The
  document already arrives with the chapter parsed, so unless bionic
  reading or paragraph-spacing removal is on, that re-parsed and
  re-laid-out the whole chapter (and restarted image loads) for nothing.
  It now only writes when the transformed markup actually differs.
- The reading position was restored from `window.load` via
  `document.fonts.ready`, both of which wait for every image in the
  chapter - measured at ~1.5s with a single slow image, during which the
  reader sat at the top of the chapter. It is now restored once the
  chapter is parsed and styled (~100ms in the same scenario) with an
  instant scroll, and corrected afterwards if fonts or images changed the
  height - but only while the reader is still where it was put, so it
  cannot yank the page from under someone already reading.

While reading:
- The scrollbar and the footer percentage each had their own unthrottled
  scroll listener recomputing the same ratio; they now share one
  listener coalesced to a frame. The footer clock no longer ticks state
  when the footer is hidden.
- Reading time is persisted without rebuilding the in-memory chapter
  list. It was rebuilt every few seconds for a field no screen renders -
  the statistics screen aggregates it from the database - and on Fabric
  react-navigation deliberately does not freeze the screen below the
  focused one, so it re-rendered the novel screen's chapter list too.
- `hidden` moved into its own context. It flips on every tap on the page,
  and a context value change re-renders all consumers, so the WebView,
  drawer, appbar and searchbar were all re-rendering for a flag only the
  reader screen reads.
- `openPage` no longer re-queries the chapter list when the requested
  page is already open, which the drawer does on every mount.

Smaller: the battery level is read asynchronously instead of through two
synchronous native calls per chapter, the sanitizer options object is
built once instead of per chapter, the general settings tab draws its
dozen fixed rows directly instead of virtualising them, and the reader
settings hook stops allocating a new settings object per render.

Verified the document changes by loading the real reader assets in
Chromium: position restored before load, no body rewrite when no
transform applies, both scroll indicators driven by the shared listener,
progress still reported, transforms still applied and reverted, and the
post-load correction only firing on an untouched reader.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac6DNTqrKqsQjbdMSmU3nm

* perf(reader): Optimize Search Functionality with Caching and Batch Processing

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-25 10:47:58 +05:30
Rajarshee Chatterjee 4dcf213037 fix: Restore Reader Changes Reverted During Expo Migration
[skip ci]
2026-07-25 09:20:13 +05:30
Rajarshee Chatterjee 4de82472ee fix: Resolve Require Cycles And LegendList Render Updates 2026-07-25 09:09:23 +05:30
Rajarshee Chatterjee 58db0352e6 fix: Restore Metro Reader Asset Middleware 2026-07-25 08:55:23 +05:30
Rajarshee Chatterjee 74808f086e feat: Replace Legacy Media Controls with Nitro-Powered Playback (#1896)
* feat: Replace Legacy Media Controls with Nitro-Powered Playback

* feat: Add TTS Engine Selection

* chore: Remove User-Facing Error Reporting

* refactor: Combine `Tts` and `TtsSession` Imports from `nitro-tts` Module

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-24 23:42:17 +05:30
amir-rezaei b96351ea81 docs: Fix Contraction and Missing Article in TESTING.md (#1895)
[skip ci]

* docs: fix trailing period, organization expansion, and comma in CONTRIBUTING.md

* docs: fix withoud typo in CONTRIBUTING-NIX.md

* docs: fix contraction and missing article in TESTING.md

---------

Co-authored-by: ferkans-amir <amir.rezaei@tu-berlin.de>
Co-authored-by: Rajarshee Chatterjee <rajarshee.adm@gmail.com>
2026-07-24 22:20:20 +05:30
amir-rezaei eb8dc6df36 docs: Fix "Withoud" Typo in CONTRIBUTING-NIX.md (#1894)
[skip ci]

* docs: fix trailing period, organization expansion, and comma in CONTRIBUTING.md

* docs: fix withoud typo in CONTRIBUTING-NIX.md

---------

Co-authored-by: ferkans-amir <amir.rezaei@tu-berlin.de>
Co-authored-by: Rajarshee Chatterjee <rajarshee.adm@gmail.com>
2026-07-24 22:19:15 +05:30
Rajarshee Chatterjee 0c8546188e feat: Add MD3 Slider Component. 2026-07-24 22:16:17 +05:30
Rajarshee Chatterjee e9f6bdaa20 fix: Modernize Menu Styling And Outside-Tap Dismissal 2026-07-24 22:16:17 +05:30
Rajarshee Chatterjee 3d34658d0f feat: Add Material 3 Top Tab Indicators
[skip ci]
2026-07-24 22:16:17 +05:30
amir-rezaei d44ce7412a docs: Fix Trailing Period, Organization Expansion, and Comma in CONTRIBUTING.md (#1893)
Co-authored-by: ferkans-amir <amir.rezaei@tu-berlin.de>
2026-07-24 20:22:03 +05:30
Rajarshee Chatterjee 17c891e133 refactor: Standardize Bottom Sheet UX and Styling
[skip ci]
2026-07-24 20:14:33 +05:30
Rajarshee Chatterjee 9805da63cf refactor: Optimize Novel Screen Performance And Architecture
[skip ci]
2026-07-24 17:07:06 +05:30
Rajarshee Chatterjee afc20277e0 fix: Restore Android Build Types For CI 2026-07-24 15:46:29 +05:30
amir-rezaei 14061d207b docs: Fix Title Case Headings, GitHub Capitalization, and E.g. Formatting in CONTRIBUTING.md (#1892)
Co-authored-by: ferkans-amir <amir.rezaei@tu-berlin.de>
2026-07-24 15:19:37 +05:30
Rajarshee Chatterjee 916374b5df refactor: Reorganize Repository Structure
[skip ci]
2026-07-24 15:13:50 +05:30
Rajarshee Chatterjee 0a2a6c96ca feat: Add Variant-Specific Android Splash Screens
[skip ci]
2026-07-24 14:59:28 +05:30
Rajarshee Chatterjee 65e8610030 refactor: Replace Bottom Navigation Shift With Fade
[skip ci]
2026-07-24 13:40:41 +05:30
Rajarshee Chatterjee 1322065290 feat: Add Variant-Specific Android App Icons 2026-07-24 13:18:44 +05:30
Rajarshee Chatterjee 22a1efd1e5 perf: Optimize React Native Skeleton Loading Components
[skip ci]
2026-07-24 13:00:51 +05:30
Rajarshee Chatterjee 269606ebb8 fix: Resolve Nitro EPUB Import Alias
[skip ci]
2026-07-24 12:45:47 +05:30
Rajarshee Chatterjee 4cb1890b62 feat: Add Nitro EPUB Exporter 2026-07-24 12:30:43 +05:30
Rajarshee Chatterjee a421177f0c fix: Improve Category Badge Layout And Drag Reordering
[skip ci]
2026-07-24 11:23:59 +05:30
Rajarshee Chatterjee a16ec87631 perf: Optimize Novel Covers, Lists, and Chapter Groups 2026-07-24 11:15:35 +05:30
Rajarshee Chatterjee dfd620aa29 feat: Standardize Dialogs with Material 3 Patterns 2026-07-23 23:05:53 +05:30
Rajarshee Chatterjee a5a6e8326f chore: Update @legendapp/list to 3.3.3 2026-07-23 22:08:26 +05:30
Rajarshee Chatterjee 7f48a8ba82 refactor: Improve Plugins Screen UX and Code 2026-07-23 21:44:01 +05:30
Rajarshee Chatterjee ae34d7b4ec feat: Add Dynamic Preview APK Metadata 2026-07-23 10:07:36 +05:30
Rajarshee Chatterjee 44c8e54ed8 feat: Add Dynamic Material Theme Colors (Closes #1863) 2026-07-23 09:10:48 +05:30
Ecasept 4a4208e336 feat: Add Time-Tracking Toggle and Inactivity Timeout (#1890)
* feat: implement on-off switch and inactivity timeout for time-tracking

* chore: remove stray console.log
2026-07-23 09:10:14 +05:30
CD-Z d2385b8dc2 Update build.yml 2026-07-23 00:08:46 +02:00
Rajarshee Chatterjee 9babe1676d refactor: Remove React Native Background Actions Manifest Customization 2026-07-22 23:59:11 +05:30
Rajarshee Chatterjee 8c38f499e4 chore: Update AppState Test Setup And pnpm Configuration 2026-07-22 23:54:22 +05:30
Ecasept 89c43c1eef feat: Implement Time Tracking and Statistics (#1886) 2026-07-22 23:30:38 +05:30
Patrick Loser c3c891cea0 refactor: Migrate to Expo Managed Workflow (#1885)
* enable predictive back gesture

* upgrade to rn 85

* remove deprecated

* added timeout to theme switch

* fix plugin items hit registration

* fix SearchbarV2 hit registration

* fix loading spinner

* migrated to expo

* migrated to expo

* Fix reader footer animation

* working again

* added icons back

* added splashscreen

* clean up

* nitro epub

* fix

* remove .env.local from git tracking

* fix rn background actions not working

* moving custom permissions to app.json

* more cleanup

* fix epub import

* remove unused deps

* Further cleanup for gradle build types

* Update build.yml

* ignore android/ and ios/ as they are generated by CNG

* fix lint

* Delete ios directory

* Delete android directory

* fix lint

* fix test mocks

* update packages

* update eslint

* fix ThemeProvider placement

* fix test and lint

* Add BackgroundTask native module

* Add NativeFile changes

* first round of fixing compatibility issues

* added types for native modules
2026-07-22 23:29:53 +05:30
Rajarshee Chatterjee e4cf64a112 refactor: Migrate Background Tasks to Native Android Scheduling (#1889)
* refactor: Restructure Application Architecture

* feat: Support Batched Downloads And EPUB Imports

* feat: Add Download Checkpoint Recovery
2026-07-22 09:21:18 +05:30
Luigi 3e1e211098 feat: Add Address Bar in WebView (#1870) 2026-07-19 19:25:58 +05:30
Ecasept 02b6b9848f fix: Prevent WebView Reset When Reader Settings Change (#1884)
fix: reader being reset when reader settings change
2026-07-19 19:23:10 +05:30
Shisii 71da09ed44 feat(reader): Add Current Chapter Search (#1877)
* feat(reader): add search button and search input for word search

* fix(reader): make keyboard close on hideHeader

* chore(reader): format reader files

* chore(strings): regenerate string types

* feat(reader): add chapter word search

* fix(reader): improve chapter search reliability

* fix(reader): hide ReaderFooter when search is open

* fix(reader): add support for search in bionic reading mode

* fix(reader): localize chapter search placeholder

* feat(reader): block short word search queries

* feat(reader): make android back button exit search

* feat(reader): add exception for special character search

* refactor(reader): split chapter search script from core
2026-07-19 19:22:40 +05:30
Rajarshee Chatterjee 4404494ec9 fix(database): Repair Migration History For Scanlator Column 2026-07-19 19:21:19 +05:30
Luigi 98a2a1d725 feat: Implement Scanlator Filtering (#1864)
* feat: implement scanlator filtering

* fix test
2026-07-15 16:42:35 +05:30
Palloxin 788f6cede7 fix: Prevent Table Overflow and Clean Up CSS (#1876) (#1876) 2026-07-13 16:11:50 +05:30
Rajarshee Chatterjee 7abee71ac3 test: Generate Debug Environment Variables In Testing Workflow 2026-07-13 16:03:02 +05:30
Rajarshee Chatterjee 52d5c99c46 fix: Order EPUB Chapters Across Paginated Pages (Closes #1762) 2026-07-02 01:27:44 +05:30
Rajarshee Chatterjee 93bc5e5e4b fix: Handle Missing MAL List Entries and Null Progress (Closes #1875) 2026-07-02 01:27:44 +05:30
Jeon JaeHyeong 5d996f1388 feat(downloads): Make Chapter Download Cooldown Configurable (#1834)
* feat(downloads): make chapter download cooldown configurable

The chapter download service used a hard-coded 1s sleep between
sequential chapter writes. That worked as a polite default but is
either too long for users with patient sources or too short for
sources that 429 on tight loops. Surface the value as a single
global setting instead.

- AppSettings.chapterDownloadCooldownMs (optional, ms)
- getChapterDownloadCooldownMs(): synchronous fallback used by the
  background task runner; returns the historical 1000ms when the
  user has not configured an override (no regression on upgrade)
- downloadChapter.ts: sleep(getChapterDownloadCooldownMs()) replaces
  the previous hard-coded sleep(1000)
- New "Chapter download cooldown" entry in General Settings, placed
  directly above "Disable haptic feedback". Tapping opens a modal
  that takes seconds (decimal allowed) and writes back as ms
- The input is sanitised on every keystroke to digits + a single
  decimal point so paste / hardware keyboard input cannot smuggle
  in non-numeric characters
- Reset writes the default explicitly so the user sees feedback
  even when the override happened to equal the default

* feat(downloads): warn about source rate limits in cooldown modal

Per review feedback (#1834): the cooldown modal should make the
trade-off explicit so users don't lower the value below what their
sources can take. Add a single error-coloured line under the input.
2026-07-01 16:39:56 +05:30
Palloxin bbb2645a6f chore: Clean Reader Preview Content (#1867)
* Remove pointless break-lines (just use soft wrap to view this file)

* Encase the table in a div
2026-07-01 12:46:30 +05:30
Vikas Movva 54fedc2f53 fix(tts): Remove Leading and Trailing Quotes from Normalized Text (#1869) 2026-07-01 12:35:13 +05:30
Palloxin e8c5550256 perf: Optimize Regex for Removing Extra Paragraph Spacing (#1872) 2026-07-01 12:32:28 +05:30
Ali Hamza Malik 57b9d41b5c fix(service): Correct Notification Throttle Logic in ServiceManager.setMeta (#1873)
fix(service): correct notification throttle in ServiceManager.setMeta
2026-07-01 12:30:57 +05:30
Jeon JaeHyeong 34be7a1234 fix(native-file): prevent fatal crash when unlinking missing files (#1832)
fix(native-file): prevent crash when unlinking missing files

NativeFileSpec.unlink is codegened as a void-returning TurboModule
method, which dispatches asynchronously on the mqt_v_native thread.
Throwing "File does not exist" there bypasses every JS try/catch and
surfaces as a fatal AndroidRuntime exception, killing the process.

Make unlink idempotent (POSIX rm -f semantics): no-op when the path
does not exist. The Downloads "delete all" and individual delete
flows previously crashed whenever a chapter row had isDownloaded=true
while its on-disk folder had already been removed (e.g. after backup
restore or external cleanup). The same fix also protects useNovel,
pluginManager, fetch, epub/import, backup/local, backup/utils, and
AdvancedTab call sites that share the same native handler.
2026-05-24 10:41:17 +05:30
Jeon JaeHyeong 60f42ed624 chore(build): patch @rock-js/plugin-metro for Windows compatibility (#1830)
- patches/@rock-js__plugin-metro@0.12.12.patch: wrap absolute paths
  in pathToFileURL() so dynamic imports work on Windows
- pnpm-workspace.yaml: register the patch and pin nodeLinker: hoisted
- pnpm-lock.yaml: lock the patched version with hash
- android/settings.gradle: invoke npx.cmd on Windows for rock autolink
2026-05-24 10:40:24 +05:30
Anthony Truong 25c93afabe fix(reader): restore saved chapter progress from db if available (#1850) 2026-05-24 10:38:23 +05:30
Gaurav Tupe 2cd5a97ee8 fix: await database operations inside transactions (#1845)
fix:await db write operations in transactions
2026-05-10 19:31:30 +02:00
Patrick Loser 8f47e8fd1e Theme switcher refactor (#1839)
* use new ids

* refactor in appearance settings

* fix lint

* remove setThemeMode from handleThemeSelect

* fix type logic

* use context for theme

* shorten animation

* adjusted LanguagePickerModal

* Fix Accent Color picker

* fix bottom navigation coloring

* log error instead of crashing when no theme context is available
2026-05-07 20:25:47 +02:00
Patrick Loser eafa545b2a Use novel refactor (#1838)
* init

* feat: add zustand dependency and persistence key contract

* refactor: extract bootstrap data loading into reusable service

* refactor: move chapter mutations into store-ready action helpers

* feat: add zustand novel store with cache and core actions

* refactor: bridge novel persistence contracts for migration safety

* refactor: migrate NovelScreen domain flows to zustand selectors

* refactor: migrate NovelScreenList to selector-based store access

* refactor: move reader chapter flows onto store boundaries

* refactor: decouple useNovelSettings from broad context domain state

* refactor: align migrateNovel with stable persistence contracts

* refactor: cut novel-reader consumers to store-only context boundary

* refactor: retire legacy useNovel and route cache cleanup export

* test: update suites for store-only context boundary cutover

* test: modernize store-era mocks and add contract coverage

* test: finalize Task-15 sweep—remove dead useNovelData and lint clear mocksContract

Final validation confirms mock-contract test suite clean and target file deletion verified with zero stale references in src/ scope.

* remove imports from NovelScreen

* reworked ai output

* improvements

* implemented synchronus novel and chapter fetch

* refactor tests

* fix db tests

* Update remaining tests.

* Harden chapter actions and bootstrap flows

* Only count filtered chapters

* improved chapter insert speed by optimizing triggers

* Improved the batching function

* Added drizzle support to dbManager.batch

* removed better-sqlite3 for testing

* fix tests

* Update updateNovelChapters fucntion

* reverse read filter

* fix snackbar

* fixed page bottomsheet

* resolved paged novels showing wrong chapter number on opening

* use openPage instead of setPageIndex in chapterDrawer

* fix lint & tests

* fix type issues

* fix various smaller issues

* updated novel restore

* Delete tsconfig.tsbuildinfo
2026-05-06 16:55:55 +02:00
Jeon JaeHyeong b680a07750 fix(browse): use gesture-handler Pressable in PluginListItem (#1828)
The plugin row sits inside a react-native-gesture-handler Swipeable but
used the React Native Pressable, whose JS responder lost mouse-derived
tap events on WSA. Switch to the gesture-handler Pressable so taps share
the same responder system as the surrounding Swipeable.
2026-05-03 10:37:52 +02:00
Jeon JaeHyeong 2a919ec0f9 chore: gitignore tsconfig.tsbuildinfo build cache (#1829)
* chore: ignore *.tsbuildinfo build cache

* chore: stop tracking tsconfig.tsbuildinfo build cache
2026-05-03 10:37:08 +02:00
Definisi 01596c045a fix: Apply active filters to chapter count display (Closes #1793) (#1797)
* fix: Apply active filters to chapter count display (Closes #1793)

getChapterCount was always returning the total count regardless of
the active filter. When filtering by unread/downloaded/bookmarked,
the count shown in the novel header stayed the same as the total.

Added an optional filter parameter to getChapterCount and pass the
current settingsFilter from useNovel so the displayed count matches
the filtered chapter list.

* fix lint warning and add tests for filtered chapter count

- Use settingsFilter instead of novelSettings.filter in useNovel
  to fix the react-hooks/exhaustive-deps warning
- Add tests for getChapterCount with filter parameter
- Update chapterFilterToSQL mock in test setup to actually apply
  filter conditions so filtered count tests work correctly
2026-04-13 20:39:48 +02:00
Patrick Loser 3de93c5c09 feat: migrate from rn community cli to rock (#1812)
* migrate to rock

* Update build.yml

* Update build.yml again and changed build.gradle

* fixed actions build crash

* add flashlist to avoid bottomsheet crash

* fix Novelscreen filter highlight

* upload artifact

* update rock config

* update build.yml apk path

* update rock.config again

* Test env file

* Update build.yml

* remove react-native-config

* Update to use split apk and enable custom rock provider paths.

* added upload artifact

* remove split apk

* remove test env
2026-04-13 20:37:47 +02:00
Patrick Loser 306f5b27e2 fix: install the right version of hermes compiler
* install the correct version of the hermes compiler

* update node version of gh workflows
2026-04-06 13:59:13 +02:00
Patrick Loser 06f0f66e3e fix: Add hermes-compiler dev dep 2026-04-05 22:40:21 +02:00
Patrick Loser 64921a054e feat: Expo 55 upgrade
* init

* Change from react-native-dotenv to react-native-config as dotenv leaks
env variables that set mmkv in test mode.

* upgrade debs

* upgrade mmkv

* fix db migration for new app builds

* update reanimated

* fix tests

* fix lint

* cleanup

* Update JumpToChapterModal.tsx

* fix settings.gradle
2026-04-05 21:39:52 +02:00
Rajarshee Chatterjee 44e035ad38 chore: Update Translations (#1683)
* New translations strings.json (Polish)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Romanian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Spanish)
[ci skip]

* New translations strings.json (Afrikaans)
[ci skip]

* New translations strings.json (Catalan)
[ci skip]

* New translations strings.json (Czech)
[ci skip]

* New translations strings.json (Danish)
[ci skip]

* New translations strings.json (Greek)
[ci skip]

* New translations strings.json (Finnish)
[ci skip]

* New translations strings.json (Hebrew)
[ci skip]

* New translations strings.json (Hungarian)
[ci skip]

* New translations strings.json (Italian)
[ci skip]

* New translations strings.json (Japanese)
[ci skip]

* New translations strings.json (Korean)
[ci skip]

* New translations strings.json (Dutch)
[ci skip]

* New translations strings.json (Norwegian)
[ci skip]

* New translations strings.json (Polish)
[ci skip]

* New translations strings.json (Portuguese)
[ci skip]

* New translations strings.json (Albanian)
[ci skip]

* New translations strings.json (Serbian (Cyrillic))
[ci skip]

* New translations strings.json (Swedish)
[ci skip]

* New translations strings.json (Turkish)
[ci skip]

* New translations strings.json (Ukrainian)
[ci skip]

* New translations strings.json (Chinese Traditional)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Hindi)
[ci skip]

* New translations strings.json (Assamese)
[ci skip]

* New translations strings.json (Odia)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Korean)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]
2026-03-12 08:15:12 +05:30
Patrick Loser 1803cd071d feat: Add testing support for react code (#1763)
* first attempt

* first attempt

* added first useNovel tests

* more tests

* more tests

* resolved all test issues

* added test-utils

* add a few component tests

* add TESTIGN.md

* add testing gh action

* fixed minor issues

* fixed minor issues
2026-02-25 14:51:46 +01:00
Patrick Loser e4d7ba306b fix: Safety level may not be changed inside a transaction error (#1758) 2026-02-24 12:22:46 +01:00
Rajarshee Chatterjee cc0428752b feat: Replace TTS Notifications with Android MediaSession Controls (Closes #1711)
Replace expo-notifications based TTS controls with a native
NativeTTSMediaControl TurboModule using Android's MediaSessionCompat
and MediaStyle notification. This provides proper media integration
with lock screen controls, cover art display, and seekbar.

- Add NativeTTSMediaControl Kotlin TurboModule with MediaSession
- Support Prev, Rewind, Play/Pause, Next notification buttons
- Show novel cover as notification album art
- Add seekbar with seek-to-element support
- Replace MMKV polling with NativeEventEmitter for instant response
- Add rewind and seekTo methods to TTS in core.js
- Clean up expo-notifications TTS setup from App.tsx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 09:15:54 +05:30
Patrick Loser b8c177bc55 refactor: Migrate to Drizzle and op-sqlite (#1735)
* added .jj to .gitignore

* chore: Add drizzle-orm and drizzle-kit dependencies to package.json and pnpm-lock.yaml

* update debs

* Edit configs

* create drizzle schema

* generate drizzle migration

* db manager stuff

* Delete unused files

* Update DB Manager

* Rework CategoryQueries and related

* rewrite chapterQueries

* Type safety for chapter order and filter

* Better filter handling

* added queue

* added back database triggers

* Refactor HistoryQueries and LibraryQueries

* fix type issues

* refactor StatsQueries

* refactor NovelQueries

* renamed schemas

* refactor more db calls

* refactor libraryUpdateQueries

* removed deprecated code

* added extracted queries

* fix updates screen issue

* change to op-sqlite

* Update manager.ts

* Update RepositoryQueries

* update packages

* Update NovelQueries

* Update CategoryQueries.ts

* db testing environment v1 (AI)

* Refactor App component to use Suspense for loading state and update database initialization logic. Change drizzle-orm version to 0.45.1 and adjust related database queries for improved error handling and async operations. Clean up unused code and update types for better type safety.

* updated tests

* change db location to default expo-sqlite location

* fix async/sync bugs

* resolve rebase problems

* Added tests for db creation + migration

* update drizzle

* add missing journals to migrations.js

* wrap testDbManager in Proxy to be async

* add db initialization test

* fix lint errors

* improved dbManager.batch function

* remove unused file

* fix rebase conflicts

* use getColumns instead of getTableColumns

* fix lint errors

* fix release build not being able to start

* remove disable lint rule

* improved useLiveQuery

* Resolve issue with the db queue constructor options

* Fix database query issues in library and stats

- Add missing await to all stats query functions
- Remove unnecessary type casts on query results
- Fix downloaded-only filter to use gt(0) instead of eq(1)

---------

Co-authored-by: CD-Z <github.verify097@passmail.com>
2026-02-20 01:17:01 +05:30
Rajarshee Chatterjee 7083c51dea fix: Include selectedIds in Chapter List extraData for Selection Highlight (Closes #1750)
LegendList's extraData only included downloadingChapterIds, so chapter
items weren't re-rendered when selection state changed via long press.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:37:33 +05:30
tomsLt0 f0693204e1 fix: Improve Next Chapter Selection Logic (#1744)
improve next chapter selection logic
2026-02-19 01:18:17 +05:30
Rajarshee Chatterjee 3f8071e48e chore: Release v2.0.3 2026-02-18 22:31:06 +05:30
Rajarshee Chatterjee a1c5350e8e fix: Miscellaneous UI Tweaks 2026-02-12 21:23:40 +05:30
Rajarshee Chatterjee 8f237909d5 feat: Show First Unread Chapter in ReadButton and FAB When No Last Read
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 20:19:52 +05:30
Rajarshee Chatterjee 11ff69d4f5 fix: Pre-Fetch Adjacent Page Chapters in Reader and Library Updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 17:40:37 +05:30
Rajarshee Chatterjee d15cf98742 fix: Order Chapter Pages Numerically in Queries for Accurate Retrieval (Closes #1742) 2026-02-12 01:01:20 +05:30
Rajarshee Chatterjee 29bd6612e9 refactor: Optimize Library Screen Performance and Fix Category Bug
Decouple selection state from renderScene via SelectionContext to prevent
all TabView tabs from re-rendering on every selection toggle. Extract
LibraryNovelItem as a memo-wrapped component with stable callbacks and
primitive props so only affected items re-render. Replace O(n) array
lookups with Set-based O(1) checks throughout. Memoize all inline
closures, list furniture, and style objects. Fix library[index] bug that
used the wrong array for "Update Category" menu action.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 00:41:59 +05:30
Rajarshee Chatterjee ab1e36d280 refactor: Update Components to Follow Material Design 3 Guidelines
Update 13 components to comply with MD3 specs:
- Switch: rewrite with MD3 dimensions (52x32 track, 16/24 thumb),
  animated thumb size, proper color tokens and border
- BottomTabBar: fix active indicator (64x32, borderRadius 16),
  label weight/color, container padding
- SearchBar: fix placeholder color, explicit borderRadius 28
- Chip: fix fontSize 14, consistent MD3 color tokens
- Actionbar: remove rounded corners, tonal elevation
- Menu: borderRadius 4, elevation 2, surfaceContainer bg
- SegmentedControl: height 40, borderRadius 20
- List: icon/subheader use onSurfaceVariant, description fontSize 14,
  divider uses outlineVariant
- BottomSheet: add 28dp top corner radius
- SwitchItem: add paddingHorizontal 16, description fontSize 14
- ConfirmationDialog: content fontSize 14
- IconButtonV2: disabled color uses onSurface at 38% opacity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:41:00 +05:30
Rajarshee Chatterjee f1fdafd37f fix: Prevent Tab Bar Height Jump and Shadow Flash on Browse Screen
Fixes initial render height jump by adding initialLayout with window dimensions to TabView. Removes shadow flash on tab switch by setting elevation to 0 and adding consistent border styling.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 22:54:35 +05:30
Rajarshee Chatterjee 4c7684a3a7 refactor: Optimize Novel Screen Performance and Fix Download State Tracking
- Optimize Novel screen components with useCallback, useMemo, and memo to prevent unnecessary re-renders
- Replace download queue array iteration with downloadingChapterIds Set for O(1) lookups
- Add loading shimmer to chapter count display (replaces "Loading..." text)
- Fix state mutation bug in useNovel updateChapter (now creates new array)
- Refactor ChapterItem API (onDeleteChapter, onDownloadChapter props for better stability)
- Track download completion via useEffect to update chapter isDownloaded state
- Fix pagination control logic and improve spacing
- Remove unnecessary local state in ChapterDownloadButtons

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 22:48:14 +05:30
Rajarshee Chatterjee 2f45db659b fix: Replace Sync Database Operations With Async to Prevent Database Lock (Closes #1716)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:49:57 +05:30
Rajarshee Chatterjee 9e38ad114d fix: Sanitize EPUB FileName to Remove Filesystem-Unsafe Characters (Closes #1480)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 16:31:31 +05:30
Rajarshee Chatterjee 45c4ea8ca0 fix: Handle IOException in NativeFile to Prevent EPUB Import Crash Loop (Closes #1315)
Synchronous TurboModule methods throwing IOException (FileNotFoundException)
crash the app process before JS catch blocks execute. Wrap file I/O in
try/catch to convert IOException to Exception for proper bridge serialization,
breaking the crash loop caused by stale tasks in the MMKV queue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 16:19:07 +05:30
Rajarshee Chatterjee d2b95ae316 fix: Use NativeFile for Custom CSS/JS Import to Fix Content URI Read Failure (Closes #1661)
Replace expo StorageAccessFramework.readAsStringAsync with NativeFile
copyFile+readFile to properly handle content:// URIs from DocumentPicker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:57:14 +05:30
Rajarshee Chatterjee 02121152b3 fix: Stabilize Scroll Handler References to Prevent Infinite Re-render Loop (Closes #1679)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:40:14 +05:30
Rajarshee Chatterjee caed965b6b fix: Prevent EPUB Import Crash from DocumentPicker Cache Cleanup (Closes #1725)
Use content:// URI directly instead of copying to cache directory, which
could be cleaned up by the system before the background import task runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:20:16 +05:30
Rajarshee Chatterjee 4088d5966b fix: Close OkHttp Response and Streams in NativeFile.downloadFile (Closes #1740)
Unclosed Response objects leaked HTTP connections and memory buffers,
causing OOM crashes when batch-downloading chapters with images.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 15:11:18 +05:30
Rajarshee Chatterjee a062beee87 fix: Add Max Height to Set Categories Modal (Closes #1738) 2026-02-06 14:56:32 +05:30
Patrick Loser 3095882516 Replace react-native-paper menus with custom implementation (#1733)
* improve custom Menu component

* replace react-native-paper menu

---------

Co-authored-by: CD-Z <github.verify097@passmail.com>
2026-02-01 17:29:23 +01:00
Patrick Loser 4ad0639796 Fix: Chapter loading for jump to chapter modal and chapter drawer
* updated debs

* Jump to chapter now loads not preloaded chapters

* chapter drawer scrolls to current chapter

* Load new chapters when reaching end of chapter drawer

* fix JumpToChapterModal rebase

* fix Chapter drawer current chapter position detection

---------

Co-authored-by: CD-Z <github.verify097@passmail.com>
2026-01-31 13:11:07 +01:00
Rajarshee Chatterjee b53ec3352f feat: Support Additional Plugin Setting Types (Dropdown and Checkboxes) 2026-01-28 22:23:00 +05:30
Rajarshee Chatterjee c40edd5b2c feat: Add Kitsu Tracker (Closes #416) 2026-01-25 14:50:17 +05:30
Rajarshee Chatterjee bdd42caaaf chore: Update Repository Links to Use lneader Instead of LNReader 2026-01-20 12:20:15 +05:30
Rajarshee Chatterjee ab4eee31ce feat: Allow All Safe Tags and Attributes When Sanitizing Chapter HTML (Closes #1715) 2026-01-20 09:26:32 +05:30
Rajarshee Chatterjee 3f9886a2e5 chore: Update Repository Links to Use lneader Instead of LNReader 2026-01-20 08:45:12 +05:30
kingstar1111 198b7443ae feat: Add RTL (Right-to-Left) Language Support (#1717)
feat: RTL support
2026-01-20 08:32:58 +05:30
Rajarshee Chatterjee f9c5ca303d chore: Update Indonesian Translations (Closes #1653) 2026-01-16 09:28:56 +05:30
Rajarshee Chatterjee 9922d43940 fix: Remove Missing Image References During EPUB Export (Closes #1696)
This is a cherry-pick from @IstarVin’s PR #1558. Thanks for the fix and for contributing. I cherry-picked it for the next release since the original PR had merge conflicts.
2026-01-15 09:24:43 +05:30
Rajarshee Chatterjee ae99b077d4 feat: Add @noble/ciphers for Plugins (Closes #1709) 2026-01-15 09:08:43 +05:30
Rajarshee Chatterjee 0cb9da9027 refactor: Replace withTransactionAsync With withExclusiveTransactionAsync for Improved Concurrency Handling (Closes #1636) 2026-01-15 08:20:37 +05:30
Mousica 467a97dcf7 feat: Add Volume Button Offset Setting and Fix Related UI Issues (#1685) 2025-12-24 19:13:52 +05:30
Rajarshee Chatterjee 8d15e418e7 chore: Update Translations (#1631)
* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Romanian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Spanish)
[ci skip]

* New translations strings.json (Afrikaans)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Catalan)
[ci skip]

* New translations strings.json (Czech)
[ci skip]

* New translations strings.json (Danish)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Greek)
[ci skip]

* New translations strings.json (Finnish)
[ci skip]

* New translations strings.json (Hebrew)
[ci skip]

* New translations strings.json (Hungarian)
[ci skip]

* New translations strings.json (Italian)
[ci skip]

* New translations strings.json (Japanese)
[ci skip]

* New translations strings.json (Korean)
[ci skip]

* New translations strings.json (Dutch)
[ci skip]

* New translations strings.json (Norwegian)
[ci skip]

* New translations strings.json (Polish)
[ci skip]

* New translations strings.json (Portuguese)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Albanian)
[ci skip]

* New translations strings.json (Serbian (Cyrillic))
[ci skip]

* New translations strings.json (Swedish)
[ci skip]

* New translations strings.json (Turkish)
[ci skip]

* New translations strings.json (Ukrainian)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Chinese Traditional)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Hindi)
[ci skip]

* New translations strings.json (Assamese)
[ci skip]

* New translations strings.json (Odia)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Romanian)
[ci skip]

* New translations strings.json (Spanish)
[ci skip]

* New translations strings.json (Afrikaans)
[ci skip]

* New translations strings.json (Catalan)
[ci skip]

* New translations strings.json (Czech)
[ci skip]

* New translations strings.json (Danish)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Greek)
[ci skip]

* New translations strings.json (Finnish)
[ci skip]

* New translations strings.json (Hebrew)
[ci skip]

* New translations strings.json (Hungarian)
[ci skip]

* New translations strings.json (Italian)
[ci skip]

* New translations strings.json (Japanese)
[ci skip]

* New translations strings.json (Korean)
[ci skip]

* New translations strings.json (Dutch)
[ci skip]

* New translations strings.json (Norwegian)
[ci skip]

* New translations strings.json (Polish)
[ci skip]

* New translations strings.json (Portuguese)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Albanian)
[ci skip]

* New translations strings.json (Serbian (Cyrillic))
[ci skip]

* New translations strings.json (Swedish)
[ci skip]

* New translations strings.json (Turkish)
[ci skip]

* New translations strings.json (Ukrainian)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Chinese Traditional)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Hindi)
[ci skip]

* New translations strings.json (Assamese)
[ci skip]

* New translations strings.json (Odia)
[ci skip]
2025-12-08 20:51:08 +05:30
Rajarshee Chatterjee 3849b797c9 fix: Show Plugin Settings When Update Adds New Settings (Closes #1674)
[skip ci]
2025-12-08 20:50:30 +05:30
Rajarshee Chatterjee d43c95f98c chore: Release v2.0.2
[skip ci]
2025-12-07 00:44:30 +05:30
Rajarshee Chatterjee 52d9d9ea1d feat: Continue Reading While App Runs in Background 2025-12-06 19:03:44 +05:30
Rajarshee Chatterjee 2cabc9849c fix: Skip Failed Entities During Backup Creation and Add Error Handling 2025-12-06 15:16:22 +05:30
Rajarshee Chatterjee 17049aca1c feat: Use All Chapters in Jump-to-Chapter Modal 2025-12-06 14:39:17 +05:30
Rajarshee Chatterjee 9608d59efe fix: Update Battery Level on Chapter Change (Closes #1650) 2025-12-06 13:27:19 +05:30
Rajarshee Chatterjee a7942212a0 fix: Show Page Names Instead of Numbers (Closes #1647) 2025-12-06 13:09:39 +05:30
Rajarshee Chatterjee a22aa53c51 fix: Add Unique Keys to All LegendList Implementations 2025-12-06 13:03:00 +05:30
Rajarshee Chatterjee 1ce16dc643 chore: Cleanup Unused Imports 2025-12-06 12:56:51 +05:30
Rajarshee Chatterjee 0ed6e9a463 fix: Start TTS From First Paragraph on Initial Press 2025-12-06 12:55:02 +05:30
Rajarshee Chatterjee b7ecd1d749 chore: Update Website Link (Closes #1666) 2025-12-06 12:48:42 +05:30
Rajarshee Chatterjee 44e177a5ca feat: Allow <ref> Links in Sanitised HTML (Closes #1672) 2025-12-06 12:48:21 +05:30
Rajarshee Chatterjee b09cd3d587 fix: Sort Updates Correctly (Closes #1652) 2025-12-06 12:44:11 +05:30
Rajarshee Chatterjee b70ebcf5f9 chore: Bump Version Patch to 1 in build.gradle (Closes #1662) 2025-12-06 12:20:19 +05:30
Gabriel Vasconcelos 9d58161729 feat(tts): Refactor Reading Logic to Fix Overflow and Add New Settings (#1664)
feat(tts): refactor reading logic and add new settings (voice, speed, pitch, auto page advance)

- Improved how TTS reads page content for smoother playback
- Added settings for voice selection, speed, and pitch control
- Added auto page advance support
- Added scroll-to-top behavior after advancing pages
- Updated UI to expose new TTS configuration options

[skip ci]
2025-12-06 12:18:35 +05:30
Rajarshee Chatterjee e07eed4a95 fix: Prevent App Crash in Offline Mode (Closes #1651) 2025-12-06 12:14:54 +05:30
Patrick Loser e09704c711 fix: Replace react native paper components
* updated debs

* Added Custom Menu component

* Added new BottomTabBar

---------

Co-authored-by: CD-Z <github.verify097@passmail.com>
2025-11-29 12:39:01 +01:00
Rajarshee Chatterjee 3af24f0815 fix: Improve Migration Error Handling 2025-11-16 13:18:58 +05:30
Rajarshee Chatterjee 4c0e7f6013 fix: Add Backward Compatibility to Tasks Queue 2025-11-16 12:36:42 +05:30
Rajarshee Chatterjee ea298273af fix: Categories Not Sorting
Closes #1642
2025-11-15 20:54:55 +05:30
Rajarshee Chatterjee f60a5af3f2 fix: Add Error Handling Around PRAGMA Query 2025-11-15 20:29:05 +05:30
Rajarshee Chatterjee 6ae5c1cdac chore: Release v2.0.1
[skip ci]
2025-11-15 15:37:34 +05:30
Rajarshee Chatterjee dcf667b176 fix: Rerun Migrations and Update Insert Chapter Queries 2025-11-15 14:36:57 +05:30
Rajarshee Chatterjee 3a313b9441 feat: Add Progress Notifications for Local Backup Creation and Restore 2025-11-15 11:07:48 +05:30
Rajarshee Chatterjee ae8211a4c0 fix: Use INSERT OR IGNORE for Library Updates 2025-11-15 10:08:55 +05:30
Rajarshee Chatterjee 3452478aa1 fix: Create Default Category Only If It Does Not Exist
[skip ci]
2025-11-14 21:32:01 +05:30
Rajarshee Chatterjee 0c14018996 chore: Release v2.0.0
[skip ci]
2025-11-14 21:22:04 +05:30
Palloxin 41ea7eb7b7 chore: Add onScrollEnd Polyfill (#1609) 2025-11-14 19:01:27 +05:30
Rajarshee Chatterjee e31eb9d457 fix: Move WebView Button to Right Actions
[skip ci]
2025-11-14 18:55:30 +05:30
Rajarshee Chatterjee 7842e860cb chore: Update Translations (#1346)
[skip ci]
2025-11-14 15:46:29 +05:30
Rajarshee Chatterjee daf7bf2ccb refactor: Move to Pagination Component and Replace Drawer with BottomSheet 2025-11-14 14:14:41 +05:30
Rajarshee Chatterjee a712100cfc feat: TTS Improvements
Closes #1500
Closes #1210
Closes #1244
Closes #1110
2025-11-14 13:07:37 +05:30
Rajarshee Chatterjee 92e1210fc8 feat: Add In-App Language Selector in Appearance Settings
Closes #1577
2025-11-13 17:19:20 +05:30
Rajarshee Chatterjee 5b4058c093 feat: Add “Has Result” Filter for Global Search
Closes #1512
2025-11-13 16:48:56 +05:30
Rajarshee Chatterjee 77ebdebcca fix: Center Align Button Labels 2025-11-12 21:25:23 +05:30
Rajarshee Chatterjee ea72de0113 fix: Ensure 'Download All' and 'Unread' Download Entire List, Not Just Batch 2025-11-12 21:17:18 +05:30
Rajarshee Chatterjee a1c0820da8 fix: BottomSheets After React Native 0.81 Upgrade 2025-11-12 20:11:29 +05:30
Rajarshee Chatterjee c9eb42ff4d chore: Upgrade React Native to 0.81
This is a manual cherry-pick from @CD-Z’s PR that updates React Native to 0.81. Thanks for originally working on it
2025-10-23 16:39:11 +05:30
Rajarshee Chatterjee 8f0c39ab41 chore: Update Kotlin Version to 2.1.20 2025-10-23 15:35:14 +05:30
Rajarshee Chatterjee 4de059d5bb chore: Add Missing Gradle Plugin 2025-10-23 15:29:32 +05:30
Rajarshee Chatterjee 969572b5a0 chore: Remove Unnecessary PNPM Setup Option 2025-10-23 14:55:02 +05:30
Rajarshee Chatterjee 4236037b6b chore: Switch to PNPM and Rename Scripts 2025-10-23 14:48:12 +05:30
Rajarshee Chatterjee a800c98d02 feat: Revamp Reader Settings UI (#1612)
[skip ci]
2025-10-23 14:10:55 +05:30
Rajarshee Chatterjee f1c825f826 feat: Implement Structured Database Migration System (#1621) 2025-10-23 13:00:10 +05:30
Rider21 fba8974699 fix: Pass imageRequestInit at Missing Places
Closes #1771
2025-10-23 00:49:23 +05:30
Rajarshee Chatterjee 10b90de171 feat: Replace FlashList with LegendList (Closes #1589) 2025-10-22 22:17:31 +05:30
Rajarshee Chatterjee 3785dddebc feat: Add Drag and Drop Support in Categories Screen
Closes #1592
2025-10-22 19:49:45 +05:30
Rajarshee Chatterjee 259dd6acc7 feat: Fetch All Chapters for EPUB Export and Add Range Selection
Closes #1605
Closes #1606
2025-10-22 18:45:08 +05:30
Rajarshee Chatterjee 90160f7f40 fix: Miscellaneous Issues
[skip ci]
2025-10-22 18:22:20 +05:30
Rajarshee Chatterjee 8bdc86c974 feat: Check for New Release Once Per Day
[skip ci]
2025-10-22 17:31:33 +05:30
Rajarshee Chatterjee 5b441562f8 feat: Add Toasts for Creating and Restoring Backups
[skip ci]
2025-10-22 17:24:59 +05:30
Rajarshee Chatterjee 4e1a67efc8 fix: Refactor and Fix EPUB Export (Closes #1611) 2025-10-22 17:03:55 +05:30
Rajarshee Chatterjee cc217e2b04 feat: Add Scroll-to-Top Functionality in Chapter List (Closes #1470)
[skip ci]
2025-10-22 16:19:27 +05:30
Rajarshee Chatterjee f8d4f50307 feat: Add Pinned Plugins and Refactor Plugin List Components
[skip ci]
2025-10-22 15:56:37 +05:30
Rajarshee Chatterjee d0e14d518e feat: Implement Multi-Tracker Support (Closes #1381) 2025-10-22 12:54:27 +05:30
Rajarshee Chatterjee c06d6f9326 feat: Add MangaUpdates Tracker (#1613)
* feat: Add MangaUpdates Tracker

* refactor: Migrate Tracker Components to TypeScript
2025-10-22 12:46:50 +05:30
Rajarshee Chatterjee 1200b7f75b fix: Make Advanced Section Scrollable (Closes #1513)
[skip ci]
2025-10-21 20:33:52 +05:30
Rajarshee Chatterjee ee7d96daf9 feat: Add System Theme Support, Yin & Yang Theme, and Adaptive Plugin Badge Color
Closes #437
Closes #1374
Closes #1578
2025-10-21 19:05:00 +05:30
Rajarshee Chatterjee 5a6f324638 feat: Add Sort by Unread Chapters (Closes #1524) 2025-10-21 16:36:53 +05:30
Rajarshee Chatterjee 4b1ff0efc1 fix: Skip Updating Imported Entries During Library Update (Closes #1404)
[skip ci]
2025-10-21 15:26:09 +05:30
Rajarshee Chatterjee 45174af3c2 fix: Include Imported Entries in “Downloaded Only” Mode (Closes #1261) 2025-10-21 15:20:34 +05:30
Rajarshee Chatterjee baabc073f0 fix: Search Functionality on History Screen (Closes #1418) 2025-10-21 14:11:35 +05:30
Rajarshee Chatterjee 37969da3e9 fix: Prevent Crash When App Is Closed During Onboarding (Closes #1517) 2025-10-21 14:02:56 +05:30
Rajarshee Chatterjee a81b32ae7f chore: Update README 2025-10-18 23:55:38 +05:30
Rajarshee Chatterjee 389fee8ecb chore: Update CI Workflows 2025-10-18 23:55:12 +05:30
Rajarshee Chatterjee 8fcfd22767 chore: Update CI Workflows 2025-10-18 22:20:50 +05:30
Rajarshee Chatterjee b216d3739a chore: Update CI Workflows 2025-10-18 22:17:33 +05:30
yammyy dcceea064a fix: Update Only the Selected Category During Category Update (#1594)
For this:
1) Select all novelIds that are in chosen category
2) Select novels by chosen Ids
2025-10-18 21:56:08 +05:30
Rajarshee Chatterjee 64c027c695 chore: Temporarily Disable Type Check 2025-10-18 21:39:54 +05:30
ilona 0dd9f2bd94 feat: Reimplement Local Backup Based on Self-Hosted Backup (#1603)
Closes #1276
2025-10-18 21:33:59 +05:30
ilona 80228ada1e chore: Improve General Plugin DX (#1604)
* enable webview debugging in dev builds only

* Fixed error downloading empty files (#1601)

* Allow data attributes in chapter HTML #1602
2025-10-18 09:51:10 +05:30
Palloxin 00c180927e Fix broken Bionic Reading (#1588)
fix broken Bionic Reading
2025-09-21 21:11:21 +07:00
Palloxin 7f61219bb0 Improve ‘remove extra spacing’ feature (#1579)
* Improve ‘remove extra paragraphs’ feature

* fix lint

* fix little mistake

* lil edit of code comment

* a little bit faster regex

* forgot to add final \s*
2025-09-21 21:10:11 +07:00
Palloxin 00d3a441e3 Improve regex of import and bionic reading (#1485)
* Improve regex of import

* Update import.ts

* Update import.ts

* lint

* lint

* bionic reading

* lint

* resolve conflict

* fix lint

* Update import.ts

* [^]

* lil fix
2025-09-07 22:07:43 +07:00
Palloxin f6b82939de chore: Update Readme to Display 2.0 Prereleases (#1572)
* Update readme to display 2.0 prereleases

* Change "preview" to "beta"

* last
2025-09-01 23:51:46 +05:30
Patrick Loser 3f2cfc253b added nix flake for a reproducible environment (#1565)
* added flake.nix

* added documentation

* added reference to CONTRIBUTING.md
2025-08-16 13:44:47 +02:00
Palloxin b107d3ca54 fix: Default Line Height ofsup Tag in Reader Mode (#1546) 2025-08-14 21:02:56 +05:30
rvx05 6bda33efd4 feat: Add Catppuccin Theme (#1561)
* Added Catppuccin Theme

* catppuccin theme fixes
2025-08-14 20:52:48 +05:30
Palloxin 1baa3e914c feat: Allow "details" And "summary" HTML Tags In The Sanitizer (#1551) 2025-08-14 20:50:19 +05:30
Patrick Loser ccbbe36ce6 Readd commits from before force push
Fixing mistake
2025-06-06 18:51:32 +02:00
nyagami 9e81f805c0 fix: epub parsing (#1518)
* fix: decode path before save

* fix: absolute path to label
2025-05-25 16:39:18 +07:00
nyagami 47428ec484 feat: import epub cpp (android) (#1514) 2025-05-25 08:15:58 +07:00
Patrick Loser 58f9427008 Small fixes & rendering time improvements (#1511)
* fix not being able to change from chapter drawer

* add UpdatesContext

* fix library search

* Add Switch for plugin settings

* improved BrowseScreen rendering time

* improved rendering time further

* fix download button
2025-05-24 18:18:16 +02:00
Patrick Loser 5e0f375f7f chore: fix all lint warnings (#1510)
* fix all lint warnings

* fix types
2025-05-20 00:02:24 +02:00
Patrick Loser 21611de3aa feat: Added chapter caching & page reader improvements
* update dependencies

* added stopPropagation

* change setInterval to onscrollend

* update progress save logic

* update chapter progress on reader exit

* chapter preloading

* reduced rerender on chapter change

* organize index.css

* added styles

* context restructure

* working page reader upgrade

* reduced unnecessary rerenders

* preserve reader settings during chapter change
2025-05-19 22:33:48 +02:00
nyagami 70520223cd feat: ios native modules (unstable) 2025-05-18 13:49:45 +07:00
nyagami 2c8880c9af [skip ci] splash screen ios (unusable) 2025-05-18 01:47:10 +07:00
nyagami ee864d3e81 [skip ci] feat: IOS (unusable) (#1504)
* feat: IOS (unusable)

* rename action

* remove Gem.lock
2025-05-11 15:46:16 +07:00
nyagami 6730c773f3 [skip ci]: new action for types checking 2025-05-11 10:14:24 +07:00
Patrick Loser 241cb0a664 Improvement: change bottom tabs implementation
* change bottom tabs implementation

* show navbar outside of chapter
2025-05-09 23:13:51 +02:00
Patrick Loser 3d59b8f774 fix: categories and update library on novel import (#1498)
* update epub

* fix categorization of novels

* fix: update library when importing novel

* update categories on change
2025-05-09 20:39:41 +02:00
Patrick Loser 188700112a fix library update crashing the app (#1496) 2025-05-09 19:11:50 +02:00
Patrick Loser 03f582da98 fix backup modal positioning (#1495) 2025-05-09 10:34:21 +02:00
Patrick Loser ed215a1d54 fix focusmode and readerscreen animations (#1494)
* update addToLibrary logic

* replace GlobalSearchNovelItem with NovelCover

* fix NovelCover and related type issues

* fix addToLibrary logic

* installed react-native-edge-to-edge

* fix Footer behaviour in Reader

* fix type issue

* fix Header behaviour in Reader

* fix ToolWrapper animation

* fix
2025-05-09 10:29:31 +02:00
Patrick Loser c487bd2ca6 fix: type errors
* fix app.tsx

* fix InfoItem

* fix helpers

* fix webview ref

* fix MoreScreen

* fix NovelScreenButtonGroup

* fix ReaderScreen

* fix readerTextAlignSelector

* fix ReaderValueChange

* fix UpdateNovelCard

* fix myAnimeList

* fixed legacy backup

* fix migrateNovel
2025-05-08 22:39:02 +02:00
Patrick Loser f90a4d1103 fix: logic related to LibraryContext
* update addToLibrary logic

* replace GlobalSearchNovelItem with NovelCover

* fix replace chaptersRead with chaptersUnread

* remove LegendList from browseTabs

* remove Legendlist from LibraryBottomSheet

* remove LegendList from JumpToChapterModal

* remove LegendList from NovelDrawer

* remove LegendList from ChapterDrawer

* fix NovelCover and related type issues

* added check for pages

* fix addToLibrary logic

* fix lint
2025-05-08 21:38:41 +02:00
nyagami 8306296736 added typescript checking 2025-05-04 21:06:12 +07:00
nyagami ae022052d5 [skip ci] more strict linter 2025-05-04 20:58:09 +07:00
Patrick Loser 7299e53df6 [skip ci] fix: correct save novel cover alignment
fix: fix alignment of save novel cover button
2025-05-04 15:22:03 +02:00
Neel Narayan Shetty c513361a5e fix: fix alignment of save novel cover button 2025-05-04 18:36:27 +05:30
Neel Shetty 429829b80b feat: Save Novel Cover (#1481)
* feat: Save novel cover (#1454)

* fix: handle storage permission for saving novel cover image

* fix: resolve conflicts with new arch
2025-05-04 17:35:16 +05:30
CD-Z ad1ccd815a lint + prettier 2025-05-04 10:48:50 +02:00
CD-Z 036608f7a8 back to Flashlist 2025-05-04 10:41:17 +02:00
CD-Z 8747e8ad39 fix themepicker style 2025-05-04 10:10:12 +02:00
CD-Z 537cf48fb4 fix chapter insert 2025-05-04 09:49:55 +02:00
CD-Z 9f51adb196 update packages 2025-05-04 09:49:55 +02:00
nyagami f2d3de4470 [skip ci] fix: eslint (except insert chapters) (#1487) 2025-05-03 22:53:04 +07:00
Soopyboo32 5f4507cfd5 Better chapter download notification (#1401)
* Better chapter download notification

* fix

* Update starting tasks list when new tasks are added to queue

* Give tasks an id

* Requested changes
2025-05-03 22:28:33 +07:00
Rajarshee Chatterjee ecc7a7a6e7 fix: Database Schema 2025-05-03 18:46:07 +05:30
nyagami 8d5b92bc9e Feat: React Native new architecture (#1420)
* update depedencies (buidable)

* fix: init db once

* upgrade react navigation v7

* better deeplink handler

* upgrade react naitve vector icons

* updated sql querries (may need further adjustments)

* fix: added type for icon name

* update react-native-reanimated, react-native-gesture-handler

* update google signin

* update tabivew & bottom sheet

* make all repository queries synchroniuously

* update categoryqueries

* import db in categoryqueries

* update chapter queries

* update import novel

* updated LibraryUpdateQueries

* fix download chapter

* fixed stats

* update history queries

* improved updates tab logic

* update LibraryQueries

* update NovelQueries

* improved import novel

* fixed notifications for android 14 and up

* fix Cant parse on first opening

* fixed loss of precision error

* Partial Loading of NovelScreen

* NovelScreenButtonGroup animation

* NovelScreen appbar animations

* update migrate novel

* feat: rewrite VolumeButtonListener to turbor module

* minor: clean up

* fixed type errors

* Novelscreen updates

* fix: better material-design-icons for react-native-paper

* migrate react-native to 0.77, good bye metro console streaming

* replace elevation with boxShadow

* rewrite FileManager -> NativeFile in TurborModule

* rewrite ZipArchive -> NativeZipArchive

* rewrite EpubUtil -> NativeEpubUtil

* dev improvement: active arch only for ndk and run launch debug id

* experimental React compiler (works but no significant improvements)

* ReaderBotttomSheet rewrite

* remove db from all query helper functions

* simplified db helper types

* better helper functions

* switch to native-stack

* removed console.log

* Change loading in UpdatesScreen

* pass novelcover on opening novel

* cleanup UpdateNovelCard

* fixed lint errors

* changed novelscreen animation behaviour

* fix UpdatesScreen

* fix lint errors

* fix unwanted downloadbutton behaviour

* fix: wrong key for multi

* fix: react-native-screens override

* remove abi filters for debug

* fix selecting chapters not working correctly

* adjust novelappbar animation

* preserve Layout when opening novel image

* added SafeAreaViews

* set Modal shadow to transparent

* replace rn paper modal with @components modal

* fix go prev chapter goes to first chapter

* fix open drawer with button

* fix SafeAreaView

* fix LibraryScreen

* improve BottomSheet performance

* SafeAreaView for global search

* fix NovelScreen SafeArea

* restructure useNovel

* remove useNovel from NovelScreenList due to possible db lock

* fix NovelCover not showing on first opening

* fix library not updating  changes to novels

* fix menu covering taskbar

* added FadeIn transition to appbar elements

* added SafeSnapPoints to keep bottomsheet inside of the safe area

* upgrade packages

* maybe round download button for jacubb

* fix space after autoscroll

* fix ReaderBottomSheet switch

* fix Jump to Chapter style

* fix Jump to chapter behaviour

* add Loading state to novelcover

* insertChapters performance improvement

* getChapter and getNovel db optimization

* updated db indexes

* New Skeleton for NovelScreen

* smoother FAB button

* add fetching state to useNovel

* fix paged novel chapter navigation

* fix type issues

* fix expanded novelCover on rotation

* add batched loading for chapters

* fix download button

* fix novelScreen loading state

* upgrade react native 0.78

* update to LegendList and fixed type errors

* fix chapter drawer

* added novel context + moved chapter & novel into ReaderStack

* fix lint

* improved chapterlist performance

* improved ChapterListSkeleton performance

* improved updates skeleton

* reworked onboading screen

* fix chapter drawer render error

* add NovelContext [unstable]

* fix filter compatibility

* upgrade eslint + add tanstack query

* eslint auto fix

* fix eslint errors

* upgrade expo 53, react native 0.79.1

* remove console

* remove unused package

* fix: exact vesion for core packages

* update contributing versions

---------

Co-authored-by: CD-Z <p_loser_Austria@gmx.at>
2025-05-01 23:05:30 +07:00
Batorian 0f5eee1bdb Improvement: Updated Sanitizer (#1129)
* update: GitHub workflow-dispatch

* fix: separate app for testing

* fix: separate app for testing

* fix: separate app for testing

* fix: new css for chapter content

* fix: separate app for testing

* branch: new test branch

* Enhancement: added more CSS

* build

* Enhancement: git build

* Fix: git build

* merge

* Enhancement: added more CSS

* dev: update build.yml

* dev: update sanitizer

* sanitizer: updated sanitizer

* dev: update index css

* dev: update sanitizer

* dev: update sanitizer

* sanitizer: update sanitizer

* sanitizer: added more allowed tags

* filter: fix filters

* progress-tracking: initial commit

* progress-tracking: implemented tracking

* progress-tracking: update tracker

* progress-tracking: complete rework

* progress-tracking: improvements

* sanitizer: update sanitizer

* sanitizer: block text-align

* sanitizer: update img

* sanitizer: reworked

* sanitizer: reworked

* sanitizer: fixed lazyloaded images

* sanitizer: revert sanitizer

* sanitizer: remove alt and title from images

* sanitizer: revert other changes

* sanitizer: fix

* sanitizer: update
2025-03-01 11:23:21 +07:00
Adam Kemish 4e3f817792 Update global search result if added to library (#1414) 2025-02-25 21:56:34 +07:00
Adam Kemish c61c4d0105 Fix app crash when downloading novel entry if it is on the recent updates page (#1395) 2025-01-20 23:23:07 +07:00
Adam Kemish 659988330c Fix some bugs with task queue (#1384)
* Fix novel chapter downloading spinner not showing in chapter list

* Fix novel chapter download notification staying on 'Prepairing'
2025-01-18 15:43:04 +07:00
jacksin125 430a067105 Fixed last read not updating and bookmark button in reader mode. (#1368)
* Fixed "Continue Reading" button

The lastRead chapter object that the "Continue Reading" button uses for everything is now updated on entering and leaving the chapter, instead of never updating when information changes.

* Fixed bookmark button not saving state

Done by passing the bookmark state down from the parent.

* Fixed bug caused by my own code

More thorough testing revealed that lastRead did not update properly on chapter switch

* Fixed issue that rendered previous fix ineffective

The original fix caused a new bug when changing chapters, and fixing that caused some of the old bugs to return. It should now be completely fixed.

---------

Co-authored-by: orionduffy <45501204+orionduffy@users.noreply.github.com>
2025-01-13 09:06:35 +07:00
MOAYAD c2c8935068 fix : ERROR $ open failed: EPERM (Operation not permitted) (#1371)
When you try to create a backup file on Android 11+ device you get this error 
/storage/emulated/0/h/inreader_backup_YYYY-MM-DD_HH_mm.json: open failed: EPERM (Operation not permitted)
2025-01-04 16:16:28 +07:00
Adam Kemish 9636aab53f fix: Resolve Service Manager Issue Caused by Unbound ‘this’ (#1375)
* Fix servicemanager broken due to unbound this

* Can use  here now!
2025-01-01 17:27:32 +05:30
Palloxin 22915914fb feat: Improve ‘Remove Extra Paragraph Spacing’ Feature (#1203)
* Improve "Remove extra paragraph spacing" feature

* Adjust in case of p with classes

* Lint

* Lint - part 2

* Lint - part 3

* forgot \s*

* \u00a exception

found here https://readnovelfull.com/the-wife-of-a-powerful-family-huo-shao-how-dare-you-flirt-with-me/chapter-1.html

* Better

* Fix

* fix lint

* forgot ``?:``

* Remove zero width

Removed until a concrete source example has them.

* fix lint

* lint

* better v2

* better 2.1

* Lint

* \s

* Exception: double br are used to simulate paragraphs

NovelUpdates - The Extra of The Bad Ending Game, chapter 159

* double br exception correction

* -

* better exception

* Lint

* flaw correction

doest match ``ahahah<br><br><br><br><br>ahahahaha``

* fix little flaw

* Shortening regex

* faster regex

* comment + faster regex

* lint

* lint

* Improve double br exception

* Correct flaw

* forgot

* \b for p

* Revert correct flaw

* Minimal faster regex

* remove useless piece

* forgot

* Faster regex

* very minor change

* Max level fast

* forgot a thing

* missing \s

* plus ultra

* suppefast

* ops

* u200b
2024-12-29 20:20:14 +05:30
Adam Kemish d39dabef68 Improve Task Queue (#1363)
* Improve Task Queue

* Seperate setMeta function
2024-12-29 13:00:24 +07:00
MOAYAD e01cbefdae fix: legacy backup file name (#1365)
in android file system you can't use the char  ' : ' 
so lnreader file name:
"Inreader_backup_YYYY-MM-DD_HH:mm.json"
you can't write it as a file. 
so i edit it to looks like
"Inreader_backup_YYYY-MM-DD_HH_mm.json"
2024-12-27 22:17:18 +07:00
Brian Fernandez 73fcba007a fix: "Add tag" input does not work correctly (#1357)
* fix: "Add tag" entry does not add tags correctly if there is at least 1 tag

* fix: "Add tag" entry allows to add empty tags when there is at least 1 tag
2024-12-20 22:34:53 +07:00
Brian Fernandez 54a3feb08c fix: "Edit cover" function is not called (#1354) 2024-12-15 12:52:31 +07:00
Rajarshee Chatterjee ecb3875fe2 chore: Update Translations (#1317)
* New translations strings.json (Romanian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Spanish)
[ci skip]

* New translations strings.json (Afrikaans)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Catalan)
[ci skip]

* New translations strings.json (Czech)
[ci skip]

* New translations strings.json (Danish)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Greek)
[ci skip]

* New translations strings.json (Finnish)
[ci skip]

* New translations strings.json (Hebrew)
[ci skip]

* New translations strings.json (Hungarian)
[ci skip]

* New translations strings.json (Italian)
[ci skip]

* New translations strings.json (Japanese)
[ci skip]

* New translations strings.json (Korean)
[ci skip]

* New translations strings.json (Dutch)
[ci skip]

* New translations strings.json (Norwegian)
[ci skip]

* New translations strings.json (Polish)
[ci skip]

* New translations strings.json (Portuguese)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Albanian)
[ci skip]

* New translations strings.json (Serbian (Cyrillic))
[ci skip]

* New translations strings.json (Swedish)
[ci skip]

* New translations strings.json (Turkish)
[ci skip]

* New translations strings.json (Ukrainian)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Chinese Traditional)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Hindi)
[ci skip]

* New translations strings.json (Assamese)
[ci skip]

* New translations strings.json (Odia)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Romanian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Spanish)
[ci skip]

* New translations strings.json (Afrikaans)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Catalan)
[ci skip]

* New translations strings.json (Czech)
[ci skip]

* New translations strings.json (Danish)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Greek)
[ci skip]

* New translations strings.json (Finnish)
[ci skip]

* New translations strings.json (Hebrew)
[ci skip]

* New translations strings.json (Hungarian)
[ci skip]

* New translations strings.json (Italian)
[ci skip]

* New translations strings.json (Japanese)
[ci skip]

* New translations strings.json (Korean)
[ci skip]

* New translations strings.json (Dutch)
[ci skip]

* New translations strings.json (Norwegian)
[ci skip]

* New translations strings.json (Polish)
[ci skip]

* New translations strings.json (Portuguese)
[ci skip]

* New translations strings.json (Albanian)
[ci skip]

* New translations strings.json (Serbian (Cyrillic))
[ci skip]

* New translations strings.json (Swedish)
[ci skip]

* New translations strings.json (Turkish)
[ci skip]

* New translations strings.json (Ukrainian)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Chinese Traditional)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Hindi)
[ci skip]

* New translations strings.json (Assamese)
[ci skip]

* New translations strings.json (Odia)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Romanian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Spanish)
[ci skip]

* New translations strings.json (Afrikaans)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Catalan)
[ci skip]

* New translations strings.json (Czech)
[ci skip]

* New translations strings.json (Danish)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Greek)
[ci skip]

* New translations strings.json (Finnish)
[ci skip]

* New translations strings.json (Hebrew)
[ci skip]

* New translations strings.json (Hungarian)
[ci skip]

* New translations strings.json (Italian)
[ci skip]

* New translations strings.json (Japanese)
[ci skip]

* New translations strings.json (Korean)
[ci skip]

* New translations strings.json (Dutch)
[ci skip]

* New translations strings.json (Norwegian)
[ci skip]

* New translations strings.json (Polish)
[ci skip]

* New translations strings.json (Portuguese)
[ci skip]

* New translations strings.json (Albanian)
[ci skip]

* New translations strings.json (Serbian (Cyrillic))
[ci skip]

* New translations strings.json (Swedish)
[ci skip]

* New translations strings.json (Turkish)
[ci skip]

* New translations strings.json (Ukrainian)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Chinese Traditional)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Hindi)
[ci skip]

* New translations strings.json (Assamese)
[ci skip]

* New translations strings.json (Odia)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (German)
[ci skip]
2024-12-01 21:11:44 +05:30
nyagami 2c9df1d93f more useful empty chapter message 2024-11-27 20:46:58 +07:00
nyagami 5db79a09eb fix: missing history navigation 2024-11-26 23:39:10 +07:00
Adam Kemish ff771fdd68 Better bottom navigation (#1335)
* Better bottom navigation

* Add double press actions to the rest of the tabs

* Fix history screen double press

* Fix lint error

* Fix more lint errors 😢

* 😢

* memo
2024-11-26 23:09:49 +07:00
Nil Silva 169c4b9de1 Add 'multi' language option (#1314)
* Added plugin settings

* Get plugin settings from plugin.

* Added multi language option

* Translate multi string

---------

Co-authored-by: nyagami <hoangquan05112002@gmail.com>
2024-11-26 23:08:04 +07:00
nyagami 4bcd22b9de fix: margin for flatlist container not content container 2024-11-26 22:44:54 +07:00
nyagami 8a2dc15da6 fix: novel status 2024-11-26 22:39:56 +07:00
nyagami 3f3a407820 closes #1263; fix: invoke old refreshChapters 2024-11-26 22:06:20 +07:00
nyagami c72808d72f closes #1322; fix: tracking sheet position 2024-11-26 21:21:29 +07:00
nyagami dabe4710bb Fix: reader page padding & back press (#1339)
* fix: remove bottom padding in page reader

* fix: disable drag transition when holding

* reader bottom sheet backpress handler

* close drawer on backpress

* fix: undefined index

* chores: added onboading strings
2024-11-26 20:47:58 +07:00
Adam Kemish cdc4ddcc97 perf: Small changes following faster global search pr (#1320)
* Small changes following faster global search pr

* Fix lint error
2024-11-25 22:17:18 +07:00
Rajarshee Chatterjee 8f783fd4c2 refactor: Miscellaneous Cleanup (Closes #1307) 2024-11-10 11:12:08 +05:30
Rajarshee Chatterjee 048119e530 chore: Update Translations (#1308)
* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Russian)
[ci skip]
2024-11-09 08:14:52 +05:30
nyagami b68d44d500 refactor: Remove Unused Translations (#1316) 2024-11-09 08:13:00 +05:30
Rider21 4bc3638b38 link fixing (#1302)
* test

* Update downloadChapter.ts

* react-native-url-polyfill

* -urlencode

* oops

* +urlencode

* fix prettier

* wrong
2024-11-06 21:07:22 +07:00
Rajarshee Chatterjee bc5e2f1fd1 fix: Use onSurface Color for Chapter Names (Closes #1259) 2024-11-05 09:23:41 +05:30
Rajarshee Chatterjee e8e7519c40 fix: Show Empty Search State When No Results in Available Tab 2024-11-05 08:39:19 +05:30
Rajarshee Chatterjee 5460047e41 fix: Use Safe Area Insets for WebView Screen (Closes #1301) 2024-11-05 08:16:53 +05:30
Rajarshee Chatterjee bdd00cd87d perf: Add Lazy Loading for All Screens 2024-11-05 08:12:35 +05:30
Rajarshee Chatterjee f83c017e09 fix: fix: Display Loading Animation on Novel Screen 2024-11-05 08:07:31 +05:30
Rajarshee Chatterjee e38cb9972f chore: Update Translations (#1305)
* New translations strings.json (Romanian)
[ci skip]

* New translations strings.json (French)
[ci skip]

* New translations strings.json (Spanish)
[ci skip]

* New translations strings.json (Afrikaans)
[ci skip]

* New translations strings.json (Arabic)
[ci skip]

* New translations strings.json (Catalan)
[ci skip]

* New translations strings.json (Czech)
[ci skip]

* New translations strings.json (Danish)
[ci skip]

* New translations strings.json (German)
[ci skip]

* New translations strings.json (Greek)
[ci skip]

* New translations strings.json (Finnish)
[ci skip]

* New translations strings.json (Hebrew)
[ci skip]

* New translations strings.json (Hungarian)
[ci skip]

* New translations strings.json (Italian)
[ci skip]

* New translations strings.json (Japanese)
[ci skip]

* New translations strings.json (Korean)
[ci skip]

* New translations strings.json (Dutch)
[ci skip]

* New translations strings.json (Norwegian)
[ci skip]

* New translations strings.json (Polish)
[ci skip]

* New translations strings.json (Portuguese)
[ci skip]

* New translations strings.json (Russian)
[ci skip]

* New translations strings.json (Albanian)
[ci skip]

* New translations strings.json (Serbian (Cyrillic))
[ci skip]

* New translations strings.json (Swedish)
[ci skip]

* New translations strings.json (Turkish)
[ci skip]

* New translations strings.json (Ukrainian)
[ci skip]

* New translations strings.json (Chinese Simplified)
[ci skip]

* New translations strings.json (Chinese Traditional)
[ci skip]

* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[ci skip]

* New translations strings.json (Indonesian)
[ci skip]

* New translations strings.json (Hindi)
[ci skip]

* New translations strings.json (Assamese)
[ci skip]

* New translations strings.json (Odia)
[ci skip]

* Update crowdin.yml
2024-11-04 20:21:22 +05:30
Rajarshee Chatterjee ac9c789a89 chore: Update Translations 2024-11-04 20:09:48 +05:30
Adam Kemish 445187cf11 perf: Optimize Global Search Performance (#1255)
* Faster and less laggy global search

- Memoise each row in the search results page
- Limit number of concurrent searches (has setting, defaults to 5 sources at once)
- Load sources in the same order as they show in menu so from the user's pov it loads in faster
- Cancel search when u close global search
- Made the order of sources that are still loading follow the sorted order so theres less popin
- Added setting to remove animations from the loading placeholders
- Pause global search if u open a sub window (eg opening a novel)
- Change no results color to look less active

* Cancel previous global search when starting another

* Dont clear global search on unfocus
2024-11-04 18:30:11 +05:30
Rajarshee Chatterjee 52ec11a33d refactor: Prevent Multiple Database Connections 2024-11-04 17:30:36 +05:30
Palloxin c498d7e9cd feat: Increase Transparency of Android Navigation Bar (#1267)
Makes it more transparent in the reader mode.
2024-10-30 21:36:48 +05:30
Nil Silva 0fad9b907f feat: Add User Input Support for Plugin Configurations (#1273)
* Added plugin settings

* Get plugin settings from plugin.
2024-10-30 21:36:00 +05:30
Adam Kemish 0a170f14bc fix: Adjust Cover Height for Screen Width Changes (#1295) 2024-10-30 21:32:21 +05:30
Adam Kemish 8245e77672 feat: Add Switch and TextInput Support to Plugin Filters (#1271)
* Add FilterTypes.Switch support to plugin filters

* Add FilterTypes.TextInput support to plugin filters
2024-10-12 15:39:49 +05:30
nguyd1 9c68260592 fix: Resolve 'Can't Parse Page' SQL Syntax Error (#1266)
fix cant parse page sql syntax error
2024-10-04 17:24:37 +05:30
Palloxin b1778c5802 chore: Genericize Preview Novel Details (#1239) 2024-09-05 10:54:10 +05:30
Justin COLLON be4ed6de79 setEnvFile: fix error when no .env file (#1237) 2024-09-01 17:33:14 +07:00
Skeltons 3597b542d3 Fix: novel info not saving (#1232) 2024-08-27 20:44:56 +07:00
Palloxin fd6d912391 Disable user-select on next-button and info-text (#1225)
Disable user-select on next-button and info-text
2024-08-21 23:01:54 +07:00
Rider21 d9dc18da43 fix: download files with relative url (#1216)
* hard fix

* test

* Revert "test"

This reverts commit 1fe9247752fcbadf348b6017f2d1fbf05165cfce.

* error handler

[URL](https://github.com/facebook/react-native/blob/v0.72.10/packages/react-native/Libraries/Blob/URL.js)
2024-08-21 21:25:39 +07:00
Skeltons a22d754f95 feat: Reduce Sensitivity of Swiping for Plugins/Sources (#1220)
* Enhancement: Make swiping of plugins/sources less sensitive

* Update swiping offset to be less aggressive
2024-08-17 22:15:52 +05:30
Skeltons d9d7d3aaf0 feat: Reduce Sensitivity of Swiping for Plugins/Sources (#1219) 2024-08-17 15:15:09 +05:30
S.M.A Sayem Prodhan a399927264 fix: Resolve Search Error with Query Containing ' (#1133)
* fix search error when query contains '

* change search to prepared statement

* added missing semicolons
2024-08-17 15:12:12 +05:30
Palloxin b4b00a5c66 fix: Improve Progress Bar Proportions (#1197)
Progress bar better proportions
2024-08-17 15:10:04 +05:30
Justin COLLON 9861f81589 fix: Infinite Plugin Updates in Development Mode (#1217)
* re fix setEnvFile.cjs

* infinit plugin update if in dev mode
2024-08-17 15:09:08 +05:30
Skeltons d06aecb5f8 feat: Sort Source List Alphabetically (#1218) 2024-08-17 15:07:30 +05:30
nyagami 86c82c1a61 Fix reader bugs (#1212)
* fix: tts algorithm

* update: turn off tts by default

* update default padding

* change tts transition to 500ms

* update reader body padding

* cleanup

* fix: throw Exception
2024-08-15 23:07:01 +07:00
nyagami 1131446ddc Improvemed bottomsheets (#1195)
* smooth scroll behavior for reader bottom sheet

* fix: BottomSheet flatlist

* add bottom inset for reader bottom sheet
2024-08-11 16:15:15 +07:00
nyagami f6b2516643 Feat: rewrite Reader in vanjs (#1194)
* setup van.js

* scrollbar

* Reader Footer

* move state to reader object

* scroll to previous progress

* image modal

* TTS bubble

* swipe gestures

* binoic reading & remove extra spacing

* tap to scroll

* tap & scroll to move page

* page reader move chapter

* display page in footer

* hide progress bar in page reader mode

* remember postion when switch reader mode

* refactor: move initial scroll out of scrollbar

* scroll bar in page reader mode

* chapter ending content

* fix: tts controller

* fix: tts highlight & stopping

* optional tts

* custom js

* update preview reader

* cleanup

* fix: css
2024-08-11 10:37:28 +07:00
Palloxin 7cf8692e77 fix: Reduce Font Size of Reader Progress Bar (#1153)
From 16px to 14px.
It's better looking.
2024-07-19 11:12:46 +05:30
nyagami d6290294f7 Improvement: not using replace screen to navigate chpater 2024-07-17 21:58:38 +07:00
nyagami d5cb0a234f improvement: simplify shared chapter states with react context 2024-07-16 23:11:41 +07:00
nyagami 306d8d1d42 Improvement: move chapter logic to useChapter (almost) 2024-07-16 22:04:48 +07:00
nyagami 7b1eb7dd9d Improvement: Optimise available plugins by flashlist 2024-07-15 23:42:51 +07:00
nyagami 1ad2c98473 Feat: search plugin with id 2024-07-15 22:39:03 +07:00
nyagami f0b8df93b4 Fix: better download chapter error handling
at least
2024-07-15 22:30:11 +07:00
nyagami 19ce7da32e Feat: voice rate/pitch settings 2024-07-15 21:47:33 +07:00
nyagami 9d35f6d303 minor: allow title tag 2024-07-15 20:44:13 +07:00
Patrick Loser 31d0aa1787 feat: Add Horizontal Reading Mode (#1021)
* Horizontal Reading Mode (#733)

Closes #29
Closes #149

* fixed first few compatibility issues

* added WebView debugging help

* fixed pages logic + open next chapter at end

* hopefully fixed infinity glitch

* removed logs

* fixed small issues

* working page numbering

* double L shape navigation

* changed let to const

* fixed scrollTo function

* cleanup and fixed css issues

* fixed display bug

* fixed settings bug

* added change padding setting

* added strings

* save + scroll on opening chapter logic

* resolved totalpages detection issue

* simplified getInt function

* css cleanup

* removed useless

* fixed padding scaling bug

* deleted debug items

* additional top padding

* smaller chapter height

* fixed contextMenu

* replaced name: readerPages » pageReader

* fixed ReaderSettings Preview Reader behaviour

* cleanup

* ReaderValueChange improvements

* moved page state to js

* fixed type issues

* functionality test

* fix: NaN page on scrollbar

---------

Co-authored-by: nyagami <hoangquan05112002@gmail.com>
2024-07-14 18:10:38 +05:30
nyagami ea5fde4156 Fix: add check multiplicable tasks & repace download queue by task queue 2024-07-14 00:42:52 +07:00
Rajarshee Chatterjee 03e3816ab8 fix: Optimize Marking Chapters as Read or Unread 2024-07-13 20:10:50 +05:30
Justin COLLON 9d6e407c40 fix: Retain Existing Data in .env File (#1130)
* setEnvFile: keeps data from .env file

* prettier for scripts
2024-07-13 18:37:32 +05:30
Batorian 6781b5d6a4 fix: Display Filter Label if Present (#1134)
filter: fix filters
2024-07-13 18:34:30 +05:30
Palloxin 352913f00e fix: Improve Image Placement in Preview (#1094)
* Better place the image in preview

The preview has to.. preview decently:
1. the image is too big (screen space taken)
2. in raw-chapter-text mode the img link is HUGE and one has to scroll a lot.

* Better place the image preview V2
2024-07-13 18:20:32 +05:30
Rajarshee Chatterjee 73f7ebf7b5 fix: Reset Chapter Progress When Marked as Unread (Closes #678) 2024-07-13 13:16:24 +05:30
Rajarshee Chatterjee 045d6419d8 fix: Fix Default Chapter Sorting (Closes #1060) 2024-07-13 12:59:35 +05:30
Rajarshee Chatterjee acc9d8e7ed feat: Pre-fill Edit Information with Existing Data and Add a Reset Option
Closes #1081
Closes #960
2024-07-13 12:52:47 +05:30
Rajarshee Chatterjee a29cce9eba feat: Add Description for "Disable haptic feedback" (Closes #1057) 2024-07-13 12:25:40 +05:30
Rajarshee Chatterjee 1292f520bb feat: Export as EPUB (Closes #1137) 2024-07-13 12:19:02 +05:30
Rajarshee Chatterjee 8bd6131862 feat: Add Option for "Keep screen on" (Closes #1137) 2024-07-13 11:06:29 +05:30
Rajarshee Chatterjee ed6c2b7ba2 feat: Add Empty State in Available Plugins Tab 2024-07-13 10:53:25 +05:30
nyagami f68a7a4e6a Fix: legacy restore 2024-07-12 10:07:50 +07:00
nyagami c9d2ce382f [skip ci] Dev improvement: Different configs for debug app 2024-07-12 09:29:53 +07:00
nyagami 5262567d61 Minor: hide reload button for custom page novels 2024-07-07 07:30:11 +07:00
nyagami b45a30f0cc Fix: avoid re-render in novel screen 2024-07-07 07:20:47 +07:00
nyagami 867b63af22 Feat: Plugin Custom JS/CSS 2024-07-06 11:42:18 +07:00
nyagami b3a5e237f4 added default cover 2024-07-06 11:41:29 +07:00
nyagami 7aae3a81c3 Fix: inlibrary badge 2024-07-06 11:32:25 +07:00
nyagami 078796784b Feat: Service Manager (#1124)
* feat: ServiceManager & import multiple epub files

* Fix: merge chapter based on index of toc in file list

* notifcation when jobs get done

* Fix epub: only read table of content in navmap

* add update library to service manager

* add drive backup/restore to service manager

* add self host backup/restore to service manager

* add migrate novel to service manager

* add download chapter to service manager

* clean up
2024-07-05 23:32:10 +07:00
Patrick Loser 15aed61aa6 Feat: tailing skeletons (#1064)
* tailing loading covers

* fix type issues

* fixed compatibility issues

* show skeleton even if row complete

* cleanup

* replaced !! with Boolean()

* Update type names

Co-authored-by: nyagami <hoangquan05112002@gmail.com>

* updated type occurances

* fixed key issue

* deleted unused vars and comments

---------

Co-authored-by: nyagami <hoangquan05112002@gmail.com>
2024-07-05 23:31:53 +07:00
Justin COLLON b67d6f8088 fix: update defaultCover URL (#1122) 2024-06-30 02:14:04 +05:30
nyagami 9b4bdfea89 Merge epub chapters (#1117)
* Fix: ignore non existed files

* merge chapters & scan cover

* remove unused tocMap

* clean code
2024-06-27 16:04:50 +07:00
r1di 70220a5a87 Fix: Volume scrolling (#1114)
* Volume scrolling fix

the scrolling by volume button right now scrolls 100% of the screen, which hides text when scrolled so, the commit is just changing the the scrolling to 50%.

* typo

* 50% to 75% scroll

---------

Co-authored-by: Ridi <ridi.culous.progger@gmail.com>
2024-06-25 20:48:10 +07:00
Patrick Loser aa95743d1c Fix: hide progressbar on opening drawer (#1102) 2024-06-25 20:20:47 +07:00
nyagami eb818ef55b add website to about page 2024-06-23 17:37:35 +07:00
nyagami 0bda5fd055 No default repo 2024-06-23 17:37:29 +07:00
Patrick Loser ea336902f2 Fix: Scroll to current Chapter (#1101)
* cleanup + fixed scroll to current Chapter

* removed unused memoization

* removed unused import

* ChapterDrawer.tsx => index.tsx

* added correct types

* applied suggestion

* changed chapter.position to scrollIndex + 2

* refactored index.ts
2024-06-14 23:17:22 +07:00
nyagami 58a633e5b9 [skip ci] ignore prettier for android folder 2024-06-13 00:39:19 +07:00
nyagami 74ed5a446d Fix: background color for plugin icon 2024-06-13 00:17:41 +07:00
nyagami a5d073a1ec Fix: Adding Category/Repo buttons position 2024-06-12 23:45:51 +07:00
nyagami f4d3d9d4eb Fix: navigation bar content color after closing reader 2024-06-12 23:40:51 +07:00
nyagami 772a9ef3a2 Feat: Plugin lazy init(#1104)
* move plugins to single folder

* lazy initial

* clean up

* fix: restore novels
2024-06-12 16:26:55 +07:00
Patrick Loser 6f3278f6ff Fix: Allow id and class attributes (#1066)
added exceptions to sanitizeHtml

Co-authored-by: nyagami <hoangquan05112002@gmail.com>
2024-06-10 20:26:34 +07:00
Adam Kemish f11a10ba22 Feat: Add open random entry button (#1098)
* Add open random entry button

* Move menu logic to searchbarv2
2024-06-10 20:20:54 +07:00
Justin COLLON 02304fd394 [skip ci] version increase for default plugins repo(#1074) 2024-06-09 12:18:05 +07:00
nyagami 94d11a12ff Fix: detect gzip images 2024-06-09 10:42:08 +07:00
nyagami fca37868f1 Feat: NOVEL and CHAPTER reader context 2024-06-08 23:01:07 +07:00
nyagami 574f54a9bc Fix: update novel logic for custom page 2024-06-08 22:12:38 +07:00
nyagami fb0649f07f Minor fix: update novel toast 2024-06-08 22:06:12 +07:00
nyagami e3def3821e Fix: Repository add button position 2024-06-08 21:44:44 +07:00
nyagami 334c981013 Fix: add imageRequestInit for Reader instead of fetching base64 2024-06-08 21:42:07 +07:00
nyagami b62c8e3aea Download file with cookie persisted from RN (#1100)
* download file

* clean up
2024-06-08 20:15:41 +07:00
nyagami 09a482e4b7 Fix: do not modify available plugins 2024-06-08 01:57:51 +07:00
nyagami b7bbbbe962 Fix: revert to app storage 2024-06-08 01:42:47 +07:00
Elfing 30265471b5 [skip ci] Adding support for French localization (#1092)
synchro Text translate
2024-06-04 21:35:02 +07:00
nyagami b4b038dd42 fix: use back button instead of previous 2024-06-04 19:37:45 +07:00
nyagami 6517028cd4 minor fix: more header & error on first time launching 2024-06-04 19:28:21 +07:00
nyagami 3f62f6f225 Feat: Permissions requesting (#1096)
fix: app permissions & remove restore errors
2024-06-04 18:52:51 +07:00
nyagami 822256229f Refactor: convert Java to Kotlin (#1095)
* kotlin migrating

* fix: kotlin generations

* fix: unzip & cleanup
2024-06-04 14:14:03 +07:00
nyagami 4f23e1814e Feat: Onboarding screen & FileManager (#1093)
* base onboard screen

* pick theme step

* cleanup

* refactor: TextFile -> FileManager

* pick storage folder

* default root storage

* refactor: fixed storage -> custom storage

* replace rnfs by native module FileManager

* clean up

* refactor: corresponding theme list
2024-06-03 21:21:46 +07:00
Justin COLLON aec6c34b66 Update: Version utils & Github actions (#1072)
* github action:
 - custom apk:
   - packet name with git commit id
   - app name with git commit id
 - cached `npm install`
 - update actions versions

* About page:
 - Version based on package.json
 - added git hash to the version if not Stable
 - RELEASE_DATE, GIT_HASH, BUILD_TYPE created at build time from setEnvFile.cjs
 - special BUILD_TYPE for github actions
 - Version modal copy on click
 - removed versionUtils

* expo-clipboard instead of react-native clipboard

* buildRelease Release instead of Debug ^^'
Gihtub Actions => Github Action
2024-05-31 00:00:07 +07:00
Rider21 e50b6c4a47 Feat: Plugin storages (#986)
* test

* fix

* Update pluginManager.ts

* test

* fix lint

* test

* test webview

* sync

* /

* Update WebviewScreen.tsx

* CookieManager

* Update storage.ts

* Update сookie.ts

* fix title

* MarqueeText...

* Slow

* Even slower

* Squashed commit of the following:

commit da4d7e878aced6567b98fc802c34715b27dfbe00
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Fri Mar 8 13:21:19 2024 +0300

    /

commit 592c4dbfca2d04931ffffba19d1eec20db3ac183
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Fri Mar 8 13:20:34 2024 +0300

    test

commit 63240502d67cda6e851d602728b873e0b6d2aed0
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Fri Mar 8 12:38:13 2024 +0300

    /

commit 591d2592a7f64e65da4f2c61216b564f3eafa2e6
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Fri Mar 8 11:42:03 2024 +0300

    fix progressbar

commit 3d34cfb72827fdcf61267eafbb128b9e893ef40f
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 21:13:49 2024 +0300

    forgot

commit c36d090a60eadfcc4bee1f0cae04bf9213a694a4
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 20:51:31 2024 +0300

    /

commit 3c4e43b7bb6f845d94737aa8cba96b0dc1714b4b
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 20:15:38 2024 +0300

    /

commit 0a20a483968fa42d24996a1d74f4c8e91f86d49a
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 19:46:31 2024 +0300

    add zIndex

commit 7d4490110457f1ae189890268731fc1726a87825
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 19:27:32 2024 +0300

    test

commit b585d5ee0a6d36707ddc323547fdd1976451ee5a
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 18:28:49 2024 +0300

    /

commit cb7241c33d6cea3c1493f41b46fdd67f70455731
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 18:06:30 2024 +0300

    Update Appbar.tsx

commit 5fab766e17708a1be6a9f53029918cad122110bb
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 17:55:24 2024 +0300

    Update WebviewScreen.tsx

commit aec9b92d033156936b97a8455f6198fa20570906
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 17:15:16 2024 +0300

    xd

commit 1fa385b4bd85bea4da34ff9c23021fb92bc622d9
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 17:11:23 2024 +0300

    2

commit 589870de27cee2fcf29c894bd055a936bb0e7a5d
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 16:28:24 2024 +0300

    1

commit 343d2dd5b64404a3ea54d8e0366d8c856c9ea429
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 16:11:08 2024 +0300

    test

commit f2dbf34a7962b04cee46fdb37a9d0ea83ca541fe
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Thu Mar 7 14:36:48 2024 +0300

    test

commit 04c04d97a9dd87815c36145b3e0bd63e1bb3d118
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Wed Mar 6 22:37:45 2024 +0300

    test zoom

commit 6b80559e053a0d1a31f4c4762570b894bb5f70e5
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Wed Mar 6 22:33:55 2024 +0300

    fix

commit 287ce94efc18036ff9cca3054e9ca182137ffe8c
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Wed Mar 6 19:00:01 2024 +0300

    test

commit 67bdddc4d10920b559122f3d7df0225cea65497b
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Wed Mar 6 11:10:22 2024 +0300

    Test newArch

commit 762353788716341474832815f332ad6ff9ebe5be
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Tue Mar 5 20:49:29 2024 +0300

    Update Appbar.tsx

commit 2a876cb942847fb4c67085a6736b2d9e6e9a3012
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Tue Mar 5 20:42:28 2024 +0300

    modal

commit ba4cadec3f87d565fe045e3c977f604444a06571
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Tue Mar 5 16:50:38 2024 +0300

    1

commit 6c14a18b7083fc3292f796408e2defdad2728b10
Author: Rider21 <58046032+Rider21@users.noreply.github.com>
Date:   Tue Mar 5 14:54:27 2024 +0300

    test

* fix

* fix

* Update pluginManager.ts

* test

* fix

* fix lint

* -marquee

* fix defaultCover

* test

* Update src/plugins/helpers/storage.ts

Co-authored-by: nyagami <hoangquan05112002@gmail.com>

* test

* Typescript fixes

* private pluginID

---------

Co-authored-by: nyagami <hoangquan05112002@gmail.com>
2024-05-28 15:50:24 +07:00
nyagami 1ac6d116a2 Fix: Reader footer position & system bar (#1076)
* replace native Navigation bar by expo-navigation-bar

* fix fullscreen logic

* fix: navigation button color

* fix: navigation color when theme changed

* fix: status bar color on full screen mode

* 40 is too large

* no 40 is acceptable :)

* refactor setBarColor

* refactor: rename setStatusBarColor

* no padding for landscape mode
2024-05-28 15:13:46 +07:00
K1ngfish3r 6864d07dd9 Remove html entity replace function when sanitizing text (#1049)
* add spacing between <>

* remove check entirely

* fix ol tag
2024-05-28 11:21:36 +05:30
Lukas Richter 671e4f4bfd Fix: updateNovelTotalPages query (#1069) 2024-05-16 23:29:06 +07:00
nyagami e2d22a5b05 enhance TTS (#1054)
* fix: read by paragraph and empty string

* add voice picker modal

* add tts voice picker for preview

* fix: tts for bionic reading

* stop speak when get out of reader

* search voice

* temporary fix: original font

* fix: start & resume

* fix: close context menu on copy
2024-05-01 00:29:53 +07:00
Rajarshee Chatterjee 3cb2eb6b11 Make Reader Appbar and Footer Animation Smoother 2024-04-30 21:09:03 +05:30
Rajarshee Chatterjee 4597a95e9c Prevent Crash on Promise Rejection 2024-04-30 20:19:28 +05:30
Rajarshee Chatterjee cf4d97e7f9 Use Promise.allSettled and Update Plugin Repository Regex 2024-04-30 20:13:57 +05:30
Rajarshee Chatterjee 09f7319f2f feat: Repositories (#1051)
* feat: Repositories

* feat: Repositories

* Cleanup

* Cleanup

* Cleanup
2024-04-28 18:38:52 +05:30
Rajarshee Chatterjee c77dc52f29 Tweak UX (#1052)
* Tweak UX

* Prevent Crash in Migrate Screen

* Cleanup
2024-04-28 18:38:30 +05:30
Rajarshee Chatterjee 3cd5b40c25 Show Chapter Titles By Default 2024-04-28 09:37:49 +05:30
Rajarshee Chatterjee 42570d29f3 Move Update Icon Outside of Swipe Actions 2024-04-28 09:29:42 +05:30
nyagami 7e7a8866ca Fix: update novel pages (#1048)
* refreshPage button

* no latestChapter

* no hasUpdate in novel page
2024-04-27 09:44:54 +07:00
K1ngfish3r ed5e599edb add table style (#1020) 2024-04-26 00:16:46 +07:00
Patrick Loser addcadeba2 Feature: dynamic novel appbar (#998)
* update depd

* dynamic novel appbar

* fixed merge problems

* changed color from background to surface2

* added gitignore
2024-04-26 00:15:06 +07:00
Palloxin 484c2b7e33 Fix arabic alignment and language list order (#1024)
* Fix arabic alignment and language list order

- Left-to-Right for arabic language with u200e
- Fix alpabethic order

* Cleanup

---------

Co-authored-by: Rajarshee Chatterjee <rajarshee.adm@gmail.com>
2024-04-13 10:59:23 +05:30
nyagami b6fea9111c Fix: insert/update novel/chapter logic (#1019)
* fix: always create novelDir

* fix: download new chapters

* update novel totalPages if refreshNovelMetadata is off

* fix: updateChapter logic

* fix: insertId could be -1
2024-03-30 23:49:48 +07:00
K1ngfish3r 9ef8cab475 fix chapter.page when containing special character (#1015)
* fix page when containing special character

* page always defaults to 1

* AND page = ?
2024-03-26 22:29:19 +07:00
nyagami 7ea8ebacd1 Update plugin sanbox (#1014)
* add htmlparser2

* fix typo

* flatten plugins
2024-03-26 17:46:38 +07:00
nyagami e171dfebc1 Update lastest chapter logic (#1011)
update lastest chapter logic
2024-03-24 13:55:23 +07:00
nyagami 9f6ffc23bf Fix bugs & Reader improvement (#1009)
* fix: wrong chapter order

* fix: reader footer styles

* make swipe getures less sensitive

* fix: update only ongoing novels option

* synchonize update library

* add horizontal scrollbar

* update swipe gesture sensitive

* no swipe when image modal is visible

* add bionic reading

* fix: import untitled epub
2024-03-23 11:50:35 +07:00
nyagami 8906253527 Feat: protobuf support (#1003)
* feat: protobuf support

* fix: spead operator is slower

* fix: redundant JSON encoding

* add fetchProto to plugin sandbox
2024-03-21 16:37:07 +07:00
nyagami 817e4dc9d1 Cheaper epub parsing (#1007)
* cheaper epub parsing

* cleaner process

* no empty page list

* more epub detail

* fix nullable
2024-03-20 13:37:55 +07:00
1074 changed files with 124830 additions and 47259 deletions
-19
View File
@@ -1,19 +0,0 @@
module.exports = {
root: true,
extends: '@react-native',
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
overrides: [
{
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
rules: {
'@typescript-eslint/no-shadow': ['warn'],
'no-shadow': 'off',
'no-undef': 'off',
'react-native/no-inline-styles': 'warn',
'no-console': 'warn',
'react-hooks/exhaustive-deps': 'warn',
},
},
],
};
+3 -3
View File
@@ -45,7 +45,7 @@ body:
label: LNReader version
description: You can find your LNReader version in **More → About**.
placeholder: |
Example: "2.0.0"
Example: "2.1.1"
validations:
required: true
@@ -86,9 +86,9 @@ body:
required: true
- label: I have written a short but informative title.
required: true
- label: If this is an issue with a source, I should be opening an issue in the [sources repository](https://github.com/lnreader/lnreader-sources/issues/new/choose).
- label: If this is an issue with a plugin, I should be opening an issue in the [plugins repository](https://github.com/lnreader/lnreader-plugins/issues/new/choose).
required: true
- label: I have updated the app to version **[2.0.0](https://github.com/lnreader/lnreader/releases/latest)**.
- label: I have updated the app to version **[2.1.1](https://github.com/lnreader/lnreader/releases/latest)**.
required: true
- label: I will fill out all of the requested information in this form.
required: true
+2 -2
View File
@@ -32,9 +32,9 @@ body:
required: true
- label: I have written a short but informative title.
required: true
- label: If this is an issue with a source, I should be opening an issue in the [sources repository](https://github.com/LNReader/lnreader-sources/issues/new/choose).
- label: If this is an issue with a source, I should be opening an issue in the [sources repository](https://github.com/lnreader/lnreader-sources/issues/new/choose).
required: true
- label: I have updated the app to version **[2.0.0](https://github.com/LNReader/lnreader/releases/latest)**.
- label: I have updated the app to version **[2.1.1](https://github.com/lnreader/lnreader/releases/latest)**.
required: true
- label: I will fill out all of the requested information in this form.
required: true
Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

+174 -41
View File
@@ -1,57 +1,190 @@
name: Build
name: Build Preview
on:
push:
branches:
- master
schedule:
- cron: '30 0 * * *'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
actions: read
contents: read
env:
CI: 'true'
EXPO_NO_TELEMETRY: '1'
NODE_ENV: 'production'
jobs:
build-android:
name: Build App
runs-on: ubuntu-latest
check-preview:
name: Check Preview
runs-on: ubuntu-24.04
outputs:
should-build: ${{ steps.preview.outputs.should-build }}
steps:
- name: Clone Repository (Latest)
uses: actions/checkout@v3
- name: Check For Existing Preview
id: preview
uses: actions/github-script@v8
with:
script: |
if (context.eventName === 'workflow_dispatch') {
core.notice('Manual dispatch requested; building a new preview.')
core.setOutput('should-build', 'true')
return
}
const shortSha = context.sha.slice(0, 8)
const artifactName = `LNReader-${shortSha}`
const { data: { artifacts } } = await github.rest.actions.listArtifactsForRepo(
{
owner: context.repo.owner,
repo: context.repo.repo,
name: artifactName,
per_page: 100,
},
)
const previewExists = artifacts.some(
artifact =>
!artifact.expired &&
artifact.workflow_run?.head_sha === context.sha,
)
core.notice(
previewExists
? `A preview artifact already exists for ${shortSha}.`
: `No preview artifact exists for ${shortSha}; starting a build.`,
)
core.setOutput('should-build', String(!previewExists))
build-android:
name: APK
needs: check-preview
if: needs.check-preview.outputs.should-build == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- name: Checkout Repository
uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-node@v3
persist-credentials: false
- name: Set Up pnpm
uses: pnpm/action-setup@v6
- name: Set Up Node.js
uses: actions/setup-node@v7
with:
node-version: '18'
node-version: '22'
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Set Up Java
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '17'
- name: Install Dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: Configure Release Environment
env:
MYANIMELIST_CLIENT_ID: ${{ vars.MYANIMELIST_CLIENT_ID }}
ANILIST_CLIENT_ID: ${{ vars.ANILIST_CLIENT_ID }}
run: |
npm install
- name: Create .env
pnpm generate:env:release \
--build-type "GitHub Action" \
--node-env "production" \
--myanimelist-client-id "$MYANIMELIST_CLIENT_ID" \
--anilist-client-id "$ANILIST_CLIENT_ID"
- name: Generate Native Android Project
run: |
cat > .env << EOF
MYANIMELIST_CLIENT_ID=${{ vars.MYANIMELIST_CLIENT_ID }}
ANILIST_CLIENT_ID=${{ vars.ANILIST_CLIENT_ID }}
EOF
- name: Cache Gradle Wrapper
uses: actions/cache@v3
pnpm exec expo prebuild \
--platform android \
--clean \
--no-install \
--non-interactive
- name: Configure Gradle
uses: gradle/actions/setup-gradle@v6
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
cache-provider: enhanced
cache-read-only: >-
${{ github.ref_name != github.event.repository.default_branch }}
cache-cleanup: on-success
add-job-summary: always
- name: Cache Gradle Dependencies
uses: actions/cache@v3
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-caches-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-caches-
- name: Make Gradlew Executable
run: cd android && chmod +x ./gradlew
- name: Build Android Release
- name: Generate Preview Metadata
run: |
cd android && ./gradlew assembleRelease --no-daemon
short_sha="${GITHUB_SHA::8}"
base_version_name="$(node --print "require('./app.json').expo.version")"
base_version_code="$(node --print "require('./app.json').expo.android.versionCode")"
- name: Set Env
echo "PRERELEASE_VERSION_CODE=$((base_version_code + GITHUB_RUN_NUMBER))" >> "$GITHUB_ENV"
echo "PRERELEASE_VERSION_NAME=${base_version_name}-pre.r${GITHUB_RUN_NUMBER}.${short_sha}" >> "$GITHUB_ENV"
echo "PRERELEASE_ARTIFACT_NAME=LNReader-${short_sha}" >> "$GITHUB_ENV"
echo "PRERELEASE_APK_NAME=LNReader-r${GITHUB_RUN_NUMBER}-${short_sha}.apk" >> "$GITHUB_ENV"
- name: Build Preview APK
working-directory: android
run: |
set -x
echo "COMMIT_COUNT=$(git rev-list --count HEAD)" >> $GITHUB_ENV
mkdir -p app/build/intermediates/sourcemaps/react/preRelease
- name: Upload Artifact
uses: actions/upload-artifact@v1
./gradlew :app:assemblePreRelease \
--build-cache \
--parallel \
-PpreReleaseVersionCode="$PRERELEASE_VERSION_CODE" \
-PpreReleaseVersionName="$PRERELEASE_VERSION_NAME" \
--stacktrace
mv \
app/build/outputs/apk/preRelease/app-preRelease.apk \
"app/build/outputs/apk/preRelease/$PRERELEASE_APK_NAME"
- name: Upload Preview APK
id: upload-nightly
uses: actions/upload-artifact@v4
with:
name: LNReader-r${{ env.COMMIT_COUNT }}
path: android/app/build/outputs/apk/release/app-release.apk
name: ${{ env.PRERELEASE_ARTIFACT_NAME }}
path: android/app/build/outputs/apk/preRelease/${{ env.PRERELEASE_APK_NAME }}
if-no-files-found: error
retention-days: 14
compression-level: 0
- name: Notify Discord
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_NIGHTLY_WEBHOOK }}
ARTIFACT_URL: ${{ steps.upload-nightly.outputs.artifact-url }}
run: |
short_sha="${GITHUB_SHA::8}"
message="[${GITHUB_REPOSITORY}] New nightly build published: \`${short_sha}\`"
payload=$(jq -n \
--arg title "$message" \
--arg url "$ARTIFACT_URL" \
'{
username: "GitHub",
avatar_url: "https://github.com/github.png",
embeds: [{
author: {
name: "github-actions[bot]",
icon_url: "https://github.com/github.png"
},
title: $title,
url: $url,
color: 3092790
}]
}')
curl --fail-with-body \
--request POST \
--header "Content-Type: application/json" \
--data "$payload" \
"$DISCORD_WEBHOOK_URL"
+36 -4
View File
@@ -7,13 +7,45 @@ on:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: ESLint
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Install modules
run: npm install
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Get pnpm Store Directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm Cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Generate Environment Variables
run: pnpm generate:env:debug
- name: Run ESLint
run: npm run lint
run: pnpm run lint
+227
View File
@@ -0,0 +1,227 @@
name: Release Android
run-name: Draft ${{ inputs.bump }} Android release
on:
workflow_dispatch:
inputs:
bump:
description: Version component to increment
required: true
default: Patch
type: choice
options:
- Patch
- Minor
- Major
concurrency:
group: release-android
cancel-in-progress: false
permissions:
contents: write
env:
CI: 'true'
EXPO_NO_TELEMETRY: '1'
NODE_ENV: 'production'
RELEASE_BUMP: ${{ inputs.bump }}
jobs:
release-android:
name: Build Draft Release
if: >-
github.ref == 'refs/heads/master' &&
github.actor_id == vars.RELEASE_ACTOR_ID &&
github.triggering_actor == vars.RELEASE_ACTOR_LOGIN
environment: release
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- name: Checkout Repository
uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: true
- name: Set Up pnpm
uses: pnpm/action-setup@v6
- name: Set Up Node.js
uses: actions/setup-node@v7
with:
node-version: '22'
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Set Up Java
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '17'
- name: Install Dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: Resolve Release Version
id: version
run: |
RELEASE_VERSION="$(
node scripts/resolve-release-version.cjs "$RELEASE_BUMP"
)"
echo "Releasing $RELEASE_BUMP version: $RELEASE_VERSION"
echo "RELEASE_VERSION=$RELEASE_VERSION" >> "$GITHUB_ENV"
echo "version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
- name: Validate Release
env:
GH_TOKEN: ${{ github.token }}
run: |
if ! [[ "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Version must use stable semver, for example 2.0.4"
exit 1
fi
if git rev-parse --verify --quiet "refs/tags/v$RELEASE_VERSION"; then
echo "Tag v$RELEASE_VERSION already exists"
exit 1
fi
if gh release view "v$RELEASE_VERSION" >/dev/null 2>&1; then
echo "Release v$RELEASE_VERSION already exists"
exit 1
fi
- name: Prepare Release Commit
run: |
pnpm release:prepare "$RELEASE_VERSION"
if git diff --quiet -- \
package.json \
app.json \
.github/ISSUE_TEMPLATE/report_issue.yml \
.github/ISSUE_TEMPLATE/request_feature.yml; then
CURRENT_VERSION="$(node -p "require('./package.json').version")"
if [ "$CURRENT_VERSION" != "$RELEASE_VERSION" ]; then
echo "No release changes were generated for $RELEASE_VERSION"
exit 1
fi
echo "Release version $RELEASE_VERSION is already committed; resuming"
else
git diff --check
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add \
package.json \
app.json \
.github/ISSUE_TEMPLATE/report_issue.yml \
.github/ISSUE_TEMPLATE/request_feature.yml
git commit -m "chore: Release v$RELEASE_VERSION"
fi
git fetch origin master
REMOTE_VERSION="$(
node -e "
const { execFileSync } = require('child_process');
const contents = execFileSync(
'git',
['show', 'origin/master:package.json'],
{ encoding: 'utf8' },
);
process.stdout.write(JSON.parse(contents).version);
"
)"
if [ "$REMOTE_VERSION" = "$RELEASE_VERSION" ]; then
echo "Release commit is already on master; resuming from it"
git checkout --detach origin/master
echo "RELEASE_ALREADY_PUSHED=true" >> "$GITHUB_ENV"
else
echo "RELEASE_ALREADY_PUSHED=false" >> "$GITHUB_ENV"
fi
echo "RELEASE_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
- name: Configure Release Environment
env:
MYANIMELIST_CLIENT_ID: ${{ vars.MYANIMELIST_CLIENT_ID }}
ANILIST_CLIENT_ID: ${{ vars.ANILIST_CLIENT_ID }}
run: |
pnpm generate:env:release \
--build-type "Release" \
--node-env "production" \
--myanimelist-client-id "$MYANIMELIST_CLIENT_ID" \
--anilist-client-id "$ANILIST_CLIENT_ID"
- name: Generate Native Android Project
run: |
pnpm exec expo prebuild \
--platform android \
--clean \
--no-install
- name: Configure Gradle
uses: gradle/actions/setup-gradle@v6
with:
cache-provider: enhanced
cache-cleanup: on-success
add-job-summary: always
- name: Build Release APKs
working-directory: android
run: |
mkdir -p app/build/intermediates/sourcemaps/react/release
./gradlew :app:assembleRelease \
--build-cache \
--parallel \
-PreleaseAbiSplits=true \
--stacktrace
- name: Package Release APKs
run: pnpm release:package "$RELEASE_VERSION"
- name: Verify APK Signatures
run: |
APKSIGNER="$(find "$ANDROID_HOME/build-tools" -type f -name apksigner |
sort -V |
tail -1)"
if [ -z "$APKSIGNER" ]; then
echo "Android apksigner was not found"
exit 1
fi
for APK in release-artifacts/*.apk; do
"$APKSIGNER" verify "$APK"
done
- name: Upload Release APKs
uses: actions/upload-artifact@v4
with:
name: LNReader-v${{ steps.version.outputs.version }}
path: release-artifacts/
if-no-files-found: error
retention-days: 30
compression-level: 0
- name: Push Release Commit
run: |
if [ "$RELEASE_ALREADY_PUSHED" = "true" ]; then
echo "Release commit is already on master"
else
git push origin HEAD:master
fi
- name: Create Draft GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "v$RELEASE_VERSION" \
release-artifacts/* \
--target "$RELEASE_SHA" \
--title "LNReader v$RELEASE_VERSION" \
--generate-notes \
--draft
+51
View File
@@ -0,0 +1,51 @@
name: Tests
on:
push:
branches:
- master
pull_request:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Jest
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Get pnpm Store Path
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Cache pnpm
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Generate Environment
run: pnpm generate:env:debug
- name: Run Tests
run: pnpm run test
+51
View File
@@ -0,0 +1,51 @@
name: Type Check
on:
push:
branches:
- master
pull_request:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
type-check:
name: TypeScript
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Get pnpm Store Path
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Cache pnpm
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Generate Environment
run: pnpm generate:env:debug
- name: Check Types
run: pnpm run type-check
+42 -3
View File
@@ -8,7 +8,8 @@ npm-debug.*
*.mobileprovision
*.orig.*
web-build/
.env
.*env
.env.local
reader_playground/index.html
# macOS
.DS_Store
@@ -38,7 +39,7 @@ DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
**/.xcode.env.local
# Android/IntelliJ
#
@@ -48,6 +49,10 @@ build/
local.properties
*.iml
*.hprof
.cxx/
*.keystore
!debug.keystore
.kotlin/
# node.js
#
@@ -66,6 +71,7 @@ buck-out/
# CocoaPods
/ios/Pods/
Gemfile.lock
# Expo
.expo/
@@ -74,4 +80,37 @@ web-build/
# @end expo-cli
.vscode/
src/sources/en/generators/
src/sources/en/generators/
*.lockb
*.lock
!pnpm-lock.yaml
.tool-versions
flake.lock
# pnpm
.pnpm-store
src/generated/**/*
.cursor/
.agents/
.claude/
.jj/
.sisyphus/
# Rock
.rock/
# TypeScript
*.tsbuildinfo
*/**/org.eclipse.buildship.core.prefs
*/**/org.eclipse.jdt.core.prefs
*/**/.classpath
*/**/.project
android/
release-artifacts/
!modules/**/android/
!plugins/android/
ios/
!modules/**/ios/
+1
View File
@@ -0,0 +1 @@
pnpm run type-check
+3 -1
View File
@@ -1 +1,3 @@
**/node_modules
**/node_modules
android/**/js/van.js
android/**/js/text-vibe.js
+1
View File
@@ -7,4 +7,5 @@ module.exports = {
trailingComma: 'all',
arrowParens: 'avoid',
quoteProps: 'preserve',
endOfLine: 'auto', // stop prettier from getting mad on windows
};
+80 -39
View File
@@ -1,57 +1,98 @@
import 'react-native-gesture-handler';
import 'react-native-url-polyfill/auto';
import { enableFreeze } from 'react-native-screens';
enableFreeze(true);
import React from 'react';
import { StatusBar } from 'react-native';
import { PropsWithChildren, Suspense, useEffect, useMemo } from 'react';
import { StatusBar, StyleSheet } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import LottieSplashScreen from 'react-native-lottie-splash-screen';
import * as SplashScreen from 'expo-splash-screen';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { Provider as PaperProvider } from 'react-native-paper';
import * as Notifications from 'expo-notifications';
import BackgroundService from 'react-native-background-actions';
import {
MD3DarkTheme,
MD3LightTheme,
Provider as PaperProvider,
} from 'react-native-paper';
import { createTables } from '@database/db';
import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary';
import { deserializePlugins } from '@plugins/pluginManager';
import AppErrorBoundary, {
ErrorFallback,
} from '@components/AppErrorBoundary/AppErrorBoundary';
import Main from './src/navigators/Main';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { MMKVStorage } from '@utils/mmkv/mmkv';
import { BACKGROUND_ACTION } from '@services/constants';
import { useInitDatabase } from '@database/db';
import { useInitializeAppServices } from '@hooks/common/useInitializeAppServices';
import { opSqliteAdapter } from './src/rozenite/opSqliteAdapter';
import { useRozeniteSqlitePlugin } from '@rozenite/sqlite-plugin';
import { ThemeProvider, useTheme } from '@hooks/persisted/useTheme';
enableFreeze(true);
const sqliteAdapters = __DEV__ && opSqliteAdapter ? [opSqliteAdapter] : [];
const ThemedPaperProvider = ({ children }: PropsWithChildren) => {
const theme = useTheme();
const paperTheme = useMemo(() => {
const baseTheme = theme.isDark ? MD3DarkTheme : MD3LightTheme;
Notifications.setNotificationHandler({
handleNotification: async () => {
return {
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
...baseTheme,
colors: {
...baseTheme.colors,
...theme,
},
};
},
});
}, [theme]);
createTables();
deserializePlugins().then(() => LottieSplashScreen.hide());
if (!BackgroundService.isRunning()) {
MMKVStorage.delete(BACKGROUND_ACTION);
}
return <PaperProvider theme={paperTheme}>{children}</PaperProvider>;
};
const App = () => {
useRozeniteSqlitePlugin({ adapters: sqliteAdapters });
const { success: databaseReady, error: databaseError } = useInitDatabase();
const { ready: servicesReady, error: servicesError } =
useInitializeAppServices(Boolean(databaseReady));
useEffect(() => {
if ((databaseReady && servicesReady) || databaseError || servicesError) {
SplashScreen.hideAsync();
}
}, [databaseReady, databaseError, servicesReady, servicesError]);
const initializationError = databaseError || servicesError;
if (initializationError) {
return (
<ThemeProvider>
<ErrorFallback error={initializationError} resetError={() => null} />
</ThemeProvider>
);
}
if (!databaseReady || !servicesReady) {
return null;
}
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<AppErrorBoundary>
<SafeAreaProvider>
<PaperProvider>
<BottomSheetModalProvider>
<StatusBar translucent={true} backgroundColor="transparent" />
<Main />
</BottomSheetModalProvider>
</PaperProvider>
</SafeAreaProvider>
</AppErrorBoundary>
</GestureHandlerRootView>
<Suspense fallback={null}>
<GestureHandlerRootView style={styles.flex}>
<ThemeProvider>
<AppErrorBoundary>
<SafeAreaProvider>
<ThemedPaperProvider>
<BottomSheetModalProvider>
<StatusBar translucent={true} backgroundColor="transparent" />
<Main />
</BottomSheetModalProvider>
</ThemedPaperProvider>
</SafeAreaProvider>
</AppErrorBoundary>
</ThemeProvider>
</GestureHandlerRootView>
</Suspense>
);
};
export default App;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
});
+108
View File
@@ -0,0 +1,108 @@
# React Native Development Nix Environment Setup
This document outlines how to set up your React Native development environment using Nix flakes for reproducibility and isolation.
## Disclaimer
This was tested on an Arch Linux system with AMD CPU and GPU. There are likely problems with NVIDIA GPUs.
Compatibility with Microsoft WSL2 is not guaranteed, but should work without major issues, except for the emulator.
## Prerequisites
- **Nix installed**: [https://nixos.org/download.html](https://nixos.org/download.html)
- **Git**: Required for cloning the repository.
- **KVM (for emulator, if enabled)**: Ensure KVM is enabled and your user is in the `kvm` group for optimal emulator performance:
```bash
sudo usermod -aG kvm $USER && newgrp kvm # (or log out/in)
kvm-ok # Verify KVM acceleration
```
- **GPU Drivers (for emulator, if enabled)**: For hardware-accelerated emulator (AMD/Intel), ensure host Vulkan drivers are working:
```bash
vulkaninfo | grep -i "device name" # Should show your GPU
```
If not, install `vulkan-radeon` (AMD) or `vulkan-intel` (Intel) on your host system and reboot.
## Getting Started
1. **Clone the Project:**
```bash
git clone <your-fork-url>
cd <your-fork-name>
```
2. **Enter the Development Shell:**
If you have `pnpm` installed, use the defined scripts:
- **For Physical Device Only (default):**
```bash
pnpm run nix:shell
```
- **For Emulator Development (explicitly enable):**
```bash
pnpm run nix:emulator
```
If you do not have `pnpm` installed, execute the commands manually:
- **For Physical Device Only (default):**
```bash
nix develop --extra-experimental-features nix-command --extra-experimental-features flakes
```
- **For Emulator Development (explicitly enable):**
```
nix develop .#emulator --extra-experimental-features nix-command --extra-experimental-features flakes
```
The first time, this will download and set up all required tools, which can take considerable time. Subsequent entries will be much faster. You will also see a message about "Setting up writable Android SDK..." which only happens once.
## Development Workflow
Once inside the `nix develop` shell, all tools are available.
### 1. Start Metro Bundler
Open a new terminal or run in the background:
```bash
pnpm run dev:start
```
### 2. Physical Device Development
1. **Connect Device**: Enable USB debugging on your Android device and connect it via USB.
2. **Verify Device**:
```bash
adb devices
```
3. **Mirror Screen (Optional)**:
```bash
scrcpy # Mirrors screen, provides control
scrcpy -s <device_serial> # If multiple devices connected
```
4. **Run App**:
```bash
npx run android
```
### 3. Android Emulator Development (if enabled)
1. **Launch Emulator**:
```bash
emulator -avd ReactNative_API35 -gpu host -no-metrics -no-audio
```
- Use `-gpu swiftshader_indirect` if hardware acceleration (`-gpu host`) fails.
2. **Verify Emulator**:
```bash
adb devices
```
3. **Run App**:
Launches the the emulater automatically, if it is not already running.
```bash
npx run android
```
+19 -57
View File
@@ -2,14 +2,19 @@
Contributions are welcome and are greatly appreciated!
## Setting up your environment
## Setup Your Environment with Nix
After forking to your own github org or account, do the following steps to get started:
If you are on a Linux system, you can install the nix package manager and use the nix flakes to set up your development environment.
See [CONTRIBUTING-NIX.md](CONTRIBUTING-NIX.md).
## Setting Up Your Environment
After forking to your own GitHub organization or account, take the following steps to get started:
```bash
# prerequisites
node --version <= 16.13.1 (for version management, get nvm [recommended])
java sdk --version <= 11 (for version management, get jenv [optional])
node --version >= 20 (for version management, get nvm [recommended])
java sdk --version >= 17 (for version management, get jenv [optional])
android sdk (https://developer.android.com/studio)
# clone your fork to your local machine
@@ -18,16 +23,19 @@ git clone https://github.com/<your-account-name>/lnreader.git
# step into local repo
cd lnreader
# install pnpm (if not already installed)
npm install -g pnpm
# install dependencies
npm install
pnpm install
# build the apk (the built apk will be found in ~/lnreader/android/app/build/outputs/apk/release/)
npm run buildRelease
pnpm run build:release:android
```
### Developing on Android
You will need an Android device or emulator connected to your computer as well as an IDE of your choice. (eg: vscode)
You will need an Android device or emulator connected to your computer as well as an IDE of your choice (e.g., VS Code).
```bash
# prerequisites
@@ -38,65 +46,19 @@ IDE
adb devices
# run metro for development
npm start
pnpm run dev:start
# then to view on your android device (new terminal)
npm run android
pnpm run dev:android
```
To view any changes to the app with new code, save your code and press "r" on the metro terminal to
reload it. The app on the android device/emulator will reload shortly.
### Style & Linting
This codebase's linting rules are enforced using [ESLint](http://eslint.org/).
It is recommended that you install an eslint plugin for your editor of choice when working on this
codebase, however you can always check to see if the source code is compliant by running:
codebase, however, you can always check to see if the source code is compliant by running:
```bash
npm run lint
```
# Database
```mermaid
erDiagram
Category ||--|{ NovelCategory : contains
Category{
INTEGER id PK
TEXT name
INTEGER sort
}
NovelCategory{
INTEGER id PK
INTEGER novelId FK
INTEGER categoryId FK "default: whose sort = 1"
}
Novel ||--|{ NovelCategory : contains
Novel{
INTEGER id PK
TEXT url
TEXT pluginId
TEXT name
TEXT cover
TEXT summary
TEXT author
TEXT artist
TEXT status
TEXT genres
INTEGER inLibrary "default: 0"
}
Novel ||--o| Chapter : has
Chapter{
INTEGER id PK
INTEGER novelId FK
text url
TEXT name
TEXT releaseTime
INTEGER bookmark "default: 0"
INTEGER unread "default: 1"
TEXT readTime
INTEGER isDownloaded "default: 0"
TEXT updatedTime
TEXT chapterNumber
}
pnpm run lint
```
+17
View File
@@ -0,0 +1,17 @@
source 'https://rubygems.org'
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby ">= 2.6.10"
# Exclude problematic versions of cocoapods and activesupport that causes build failures.
gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
gem 'xcodeproj', '< 1.26.0'
gem 'concurrent-ruby', '< 1.3.4'
# Ruby 3.4.0 has removed some libraries from the standard library.
gem 'bigdecimal'
gem 'logger'
gem 'benchmark'
gem 'mutex_m'
gem 'nkf'
+50 -29
View File
@@ -1,47 +1,68 @@
<p align="center">
<img src="./.github/readme-images/icon_alt.png" align="center" width="128" />
<p>
<h1 align="center">
LNReader
</h1>
<p align="center">
LNReader is a Tachiyomi-like, free and open source light novel reader for Android.
<a href="https://lnreader.app">
<img src="./.github/readme-images/icon_new.png" align="center" width="128" />
</a>
</p>
<h1 align="center">LNReader</h1>
<p align="center">
LNReader is a free and open source light novel reader for Android, inspired by Tachiyomi.
</p>
<div align="center">
<a href="https://github.com/LNReader/lnreader/actions/workflows/build.yml">
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/LNReader/lnreader/build.yml?branch=main">
</a>
<a href="https://discord.gg/QdcWN4MD63">
<img alt="Discord Chat" src="https://img.shields.io/discord/835746409357246465.svg?logo=discord&logoColor=white&logoWidth=20&labelColor=7289DA&label=Discord&color=17cf48">
<img alt="Discord Chat" src="https://img.shields.io/discord/835746409357246465.svg?logo=discord&logoColor=white&logoWidth=20&labelColor=5865F2&color=4752C4&label=discord&style=flat">
</a>
<a href="https://github.com/LNReader/lnreader/releases/latest">
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/LNReader/lnreader">
</a>
<a href="https://github.com/LNReader/lnreader-preview/releases/latest">
<img alt="GitHub release (latest SemVer)" src="https://img.shields.io/github/v/release/LNReader/lnreader-preview?color=red&label=preview">
</a>
<a title="Crowdin" target="_blank" href="https://crowdin.com/project/lnreader"><img src="https://badges.crowdin.net/lnreader/localized.svg"></a>
<a href="https://github.com/LNReader/lnreader/blob/main/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/LNReader/lnreader">
<a href="https://github.com/lnreader/lnreader/releases">
<img alt="GitHub Downloads" src="https://img.shields.io/github/downloads/lnreader/lnreader/total?label=downloads&labelColor=27303D&color=0D1117&logo=github&logoColor=FFFFFF&style=flat">
</a>
</div>
<br>
<br>
<div align="center">
<img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/lnreader/lnreader/build.yml?labelColor=27303D&style=flat">
<a href="https://github.com/lnreader/lnreader/blob/main/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/lnreader/lnreader?labelColor=27303D&color=1a73e8&style=flat">
</a>
<a title="Crowdin" target="_blank" href="https://crowdin.com/project/lnreader">
<img src="https://badges.crowdin.net/lnreader/localized.svg">
</a>
</div>
<h2 align="center">Download</h2>
<p align="center">
<a href="https://github.com/lnreader/lnreader/releases/latest">
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/lnreader/lnreader?label=Stable&labelColor=0d7377&color=084c4e&style=flat">
</a>
<a href="https://github.com/lnreader/lnreader/actions/workflows/build.yml">
<img alt="Build Preview" src="https://github.com/lnreader/lnreader/actions/workflows/build.yml/badge.svg">
</a>
</p>
<p align="center">
Get the app from our <a href="https://github.com/lnreader/lnreader/releases">releases page</a>.
</p>
<p align="center">
<em>Android 7.0 or higher.</em>
</p>
<h2 align="center">Screenshots</h2>
<p align="center">
<img src="./.github/readme-images/screenshots.png" align="center" />
</p>
## Download
Get the app from our [releases page](https://github.com/rajarsheechatterjee/LNReader/releases).
## Sources
## Plugins
LNReader does not have any affiliation with the content providers available.
Source requests should be created at [LNReader/lnreader-sources](https://github.com/LNReader/lnreader-sources).
Plugin requests should be created at [lnreader-plugins](https://github.com/lnreader/lnreader-plugins).
## Translation
Help translate LNReader into your language on [Crowdin](https://crowdin.com/project/lnreader).
## Building & Contributing
@@ -49,4 +70,4 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md)
## License
[MIT](https://github.com/rajarsheechatterjee/lnreader/blob/main/LICENSE)
[MIT](https://github.com/lnreader/lnreader/blob/main/LICENSE)
+80
View File
@@ -0,0 +1,80 @@
# Testing Guide for LNReader
This guide explains how to write tests in this React Native project using Jest and React Testing Library.
## Existing Mocks
### Global Mocks
The project has global mocks configured in Jest. These are automatically applied:
- `test/mocks/` - Global mocks for native modules (react-native-mmkv, react-navigation, all database queries, etc.)
- `src/hooks/__mocks__/index.ts` - Hook-specific mocks (showToast, getString, parseChapterNumber, etc.)
- `src/hooks/__tests__/mocks.ts` - Extended mocks for persisted hooks
### Using @test-utils
There is a custom render wrapper at `test/test-utils.tsx` with:
- `render` - wraps with GestureHandlerRootView, SafeAreaProvider, PaperProvider, etc.
- `renderNovel` - includes NovelContextProvider
- `AllTheProviders` - the full provider wrapper
Usage:
```typescript
import { render, renderNovel } from '@test-utils';
```
## Common Issues
### 1. ESM Modules Not Transforming
If you see `Cannot use import statement outside a module`, you need to add mocks for the module:
```typescript
jest.mock('@hooks/persisted/usePlugins');
// Add more specific mocks as needed
```
### 2. Mock Functions Not Working
If `mockReturnValue` throws "not a function", create mock functions at the module level:
```typescript
// CORRECT: Module-level mock functions
const mockUseTheme = jest.fn();
jest.mock('@hooks/persisted', () => ({
useTheme: () => mockUseTheme(),
}));
// INCORRECT: Trying to use jest.Mock type casting
// (useTheme as jest.Mock).mockReturnValue(...) // This fails!
```
### 3. Test Isolation
Tests must mock at module level, not in `beforeEach`:
```typescript
// CORRECT
const mockFn = jest.fn();
jest.mock('module', () => ({ useHook: () => mockFn() }));
// INCORRECT - mocks get reset between tests
jest.mock('module');
beforeEach(() => {
// This doesn't work properly
});
```
## Running Tests
```bash
pnpm test # Run all tests
pnpm test:watch # Watch mode
pnpm test:coverage # With coverage
pnpm test:rn # React Native only
pnpm test:db # Database only
```
-55
View File
@@ -1,55 +0,0 @@
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
create_aar_targets(glob(["libs/*.aar"]))
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "com.rajarsheechatterjee.LNReader",
)
android_resource(
name = "res",
package = "com.rajarsheechatterjee.LNReader",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)
-147
View File
@@ -1,147 +0,0 @@
apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
/**
* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
*/
react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// 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")
/* Variants */
// The list of variants to that are debuggable. For those we're going to
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
// debuggableVariants = ["liteDebug", "prodDebug"]
/* Bundling */
// A list containing the node command and its flags. Default is just 'node'.
// nodeExecutableAndArgs = ["node"]
//
// The command to run when bundling. By default is 'bundle'
// bundleCommand = "ram-bundle"
//
// The path to the CLI configuration file. Default is empty.
// bundleConfig = file(../rn-cli.config.js)
//
// The name of the generated asset file containing your JS bundle
// bundleAssetName = "MyApplication.android.bundle"
//
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
// entryFile = file("../js/index.android.js")
//
// A list of extra flags to pass to the 'bundle' commands.
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
// extraPackagerArgs = []
/* Hermes Commands */
// The hermes compiler command to run. By default it is 'hermesc'
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
//
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"]
}
/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore (JSC).
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
ext {
versionMajor = 2
versionMinor = 0
versionPatch = 0
}
android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
namespace "com.rajarsheechatterjee.LNReader"
defaultConfig {
applicationId 'com.rajarsheechatterjee.LNReader'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
// Generated version code. Supports versions up to 1024.1024.2048
versionCode ((((versionMajor << 10) | versionMinor) << 11) | versionPatch)
versionName "$versionMajor.$versionMinor.$versionPatch"
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
}
dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation 'com.facebook.soloader:soloader:0.10.5+'
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
if (isGifEnabled || isWebpEnabled) {
implementation 'com.facebook.fresco:fresco:2.0.0'
implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.0.0'
}
if(isGifEnabled){
// For animated gif support
implementation 'com.facebook.fresco:animated-gif:2.0.0'
}
if(isWebpEnabled){
// For webp support
implementation 'com.facebook.fresco:webpsupport:2.0.0'
if(isWebpAnimatedEnabled){
// Animated webp support
implementation 'com.facebook.fresco:animated-webp:2.0.0'
}
}
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
} else {
implementation jscFlavor
}
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
Binary file not shown.
-47
View File
@@ -1,47 +0,0 @@
{
"project_info": {
"project_number": "523872485654",
"project_id": "lnreader-backup",
"storage_bucket": "lnreader-backup.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:523872485654:android:169cd062458db962ce1037",
"android_client_info": {
"package_name": "com.rajarsheechatterjee.LNReader"
}
},
"oauth_client": [
{
"client_id": "523872485654-14jtut8orr7dbrk2chea279d7k8889sr.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.rajarsheechatterjee.LNReader",
"certificate_hash": "5e8f16062ea3cd2c4a0d547876baa6f38cabf625"
}
},
{
"client_id": "523872485654-liarmq8nl0g5an2cki3bpg9jc0d8a21j.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBH_j-0Jyo9aFJ_KV8Nr3te2hs_L_ZYhrE"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "523872485654-liarmq8nl0g5an2cki3bpg9jc0d8a21j.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}
-14
View File
@@ -1,14 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
-keep class com.swmansion.reanimated.** { *; }
-keep class com.facebook.react.turbomodule.** { *; }
-keep class com.facebook.hermes.unicode.** { *; }
-keep class com.facebook.jni.** { *; }
-12
View File
@@ -1,12 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:usesCleartextTraffic="true"
tools:targetApi="28"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
</application>
</manifest>
-71
View File
@@ -1,71 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.rajarsheechatterjee.LNReader">
<!-- Internet -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<!-- Storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- For background jobs -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<queries>
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
</intent>
</queries>
<application
android:name=".MainApplication"
android:largeHeap="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher"
android:allowBackup="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<meta-data
android:name="expo.modules.notifications.default_notification_icon"
android:resource="@drawable/notification_icon" />
<meta-data
android:name="expo.modules.notifications.default_notification_color"
android:resource="@color/notification_icon_color" />
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true"
android:theme="@style/Theme.App.SplashScreen"
android:screenOrientation="unspecified">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="lnreader" />
<data android:scheme="com.rajarsheechatterjee.LNReader" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
</application>
</manifest>
-334
View File
@@ -1,334 +0,0 @@
html {
scroll-behavior: smooth;
overflow-x: hidden;
padding-top: var(--StatusBar-currentHeight);
word-wrap: break-word;
}
::selection {
color: var(--theme-onSecondary);
background-color: var(--theme-secondary);
}
::-moz-selection {
color: var(--theme-onSecondary);
background-color: var(--theme-secondary);
}
body {
padding-left: var(--readerSettings-padding);
padding-right: var(--readerSettings-padding);
padding-bottom: 40px;
font-size: var(--readerSettings-textSize);
background-color: var(--readerSettings-theme);
color: var(--readerSettings-textColor);
text-align: var(--readerSettings-textAlign);
line-height: var(--readerSettings-lineHeight);
font-family: var(--readerSettings-fontFamily);
}
chapter {
display: block;
}
hr {
margin-top: 20px;
margin-bottom: 20px;
}
a {
color: var(--theme-primary);
}
img {
display: block;
width: auto;
height: auto;
max-width: 100%;
}
.hidden {
visibility: hidden;
}
.nextButton,
.infoText {
width: 100%;
border-radius: 50px;
border-width: 1;
color: var(--theme-onPrimary);
background-color: var(--theme-primary);
font-family: var(--readerSettings-fontFamily);
font-size: 16px;
border-width: 0;
user-select: none;
}
.nextButton {
min-height: 40px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
padding: 0 16px;
}
.infoText {
background-color: transparent;
text-align: center;
border: none;
margin: 0px;
color: inherit;
padding-top: 16px;
padding-bottom: 16px;
}
.chapterCtn {
min-height: var(--chapterCtn-height);
margin-bottom: auto;
}
.d-none {
display: none;
}
#ToolWrapper {
position: fixed;
top: 50%;
transform: translateY(-50%);
right: 5vw;
transition: 150ms;
}
#ToolWrapper.hidden {
opacity: 0;
right: 0;
}
#TTS-Controller {
display: block;
}
#TTS-Controller button{
background: var(--theme-surface-0-9);
border: 0;
}
#TTS-Controller svg {
fill: var(--theme-onSurface);
}
#ScrollBar {
margin-top: 8px;
width: 2.4rem;
height: 45vh;
min-height: 200px;
border-radius: 1.2rem;
background-color: var(--theme-surface-0-9);
touch-action: none;
font-size: 16px;
user-select: none;
}
.scrollbar-item {
display: flex;
justify-content: center;
align-items: center;
color: var(--theme-onSurface);
}
.scrollbar-item.scrollbar-text {
height: 12.5%;
}
.scrollbar-item#scrollbar-slider {
height: 75%;
align-items: unset;
}
#scrollbar-track {
width: 0.1rem;
height: 100%;
background-color: var(--theme-outline);
}
#scrollbar-progress {
width: 0.1rem;
height: 0;
background-color: var(--theme-primary);
}
#scrollbar-thumb-wrapper {
position: relative;
top: 100%;
height: 2rem;
width: 2rem;
display: flex;
align-items: center;
justify-content: center;
transform: translate(-0.95rem, -1rem);
}
#scrollbar-thumb {
top: 100%;
height: 1rem;
width: 1rem;
border-radius: 100%;
background-color: var(--theme-primary);
}
#reader-footer-wrapper {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
}
#reader-footer {
display: flex;
flex: 1;
justify-content: space-between;
min-height: 2rem;
padding: 0.5rem 2rem 0 2rem;
background-color: var(--readerSettings-theme);
color: var(--readerSettings-textColor);
font-size: 1rem;
text-align: center;
user-select: none;
}
.reader-footer-item {
display: flex;
}
tts.highlight {
color: var(--theme-onSecondary);
background-color: var(--theme-secondary);
}
.contextMenu {
position: absolute;
height: 0;
overflow: hidden;
background: var(--theme-surface-0-9);
-webkit-backdrop-filter: blur(1px);
backdrop-filter: blur(1px);
position: fixed;
top: var(--top);
left: var(--left);
-webkit-animation: menuAnimation 0.4s 0s both;
animation: menuAnimation 0.4s 0s both;
transform-origin: left;
list-style: none;
margin: 4px;
padding: 0;
display: flex;
flex-direction: column;
z-index: 999999999;
}
.contextMenu-item {
padding: 4px,
}
.contextMenu-button {
color: var(--theme-onSurface);
background: 0;
border: 0;
white-space: nowrap;
width: 100%;
border-radius: 4px;
padding: 6px 24px 6px 7px;
text-align: left;
display: flex;
align-items: center;
font-size: 14px;
width: 100%;
-webkit-animation: menuItemAnimation 0.2s 0s both;
animation: menuItemAnimation 0.2s 0s both;
font-family: var(--readerSettings-fontFamily);
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: var(--theme-rippleColor);
}
.contextMenu-button svg {
fill: var(--theme-onSurface);
}
.contextMenu-button span {
margin-left: 4px;
}
@-webkit-keyframes menuAnimation {
0% {
opacity: 0;
transform: scale(0.5);
}
100% {
height: var(--height);
opacity: 1;
border-radius: 8px;
transform: scale(1);
}
}
@keyframes menuAnimation {
0% {
opacity: 0;
transform: scale(0.5);
}
100% {
height: var(--height);
opacity: 1;
border-radius: 8px;
transform: scale(1);
}
}
@-webkit-keyframes menuItemAnimation {
0% {
opacity: 0;
transform: translateX(-10px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes menuItemAnimation {
0% {
opacity: 0;
transform: translateX(-10px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
#Image-Modal {
position: fixed;
left: 0;
top: 0;
user-select: none;
}
#Image-Modal.show {
display: flex;
height: 100%;
width: 100%;
background-color: var(--theme-surface-0-9);
align-items: center;
justify-content: center;
overflow: scroll;
}
#Image img {
display: block;
width: 100%;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

-610
View File
@@ -1,610 +0,0 @@
/**
* https://fonts.google.com/icons
* Filled
* other settings are default
*/
const copyIcon =
'<svg height="24" viewBox="0 -960 960 960" width="24"><path d="M360-240q-33 0-56.5-23.5T280-320v-480q0-33 23.5-56.5T360-880h360q33 0 56.5 23.5T800-800v480q0 33-23.5 56.5T720-240H360ZM200-80q-33 0-56.5-23.5T120-160v-560h80v560h440v80H200Z"/></svg>';
const volumeIcon =
'<svg height="24" viewBox="0 -960 960 960" width="24"><path d="M560-131v-82q90-26 145-100t55-168q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 127-78 224.5T560-131ZM120-360v-240h160l200-200v640L280-360H120Zm440 40v-322q47 22 73.5 66t26.5 96q0 51-26.5 94.5T560-320Z"/></svg>';
const selectAllIcon =
'<svg height="24" viewBox="0 -960 960 960" width="24"><path d="M280-280v-400h400v400H280Zm80-80h240v-240H360v240ZM200-200v80q-33 0-56.5-23.5T120-200h80Zm-80-80v-80h80v80h-80Zm0-160v-80h80v80h-80Zm0-160v-80h80v80h-80Zm80-160h-80q0-33 23.5-56.5T200-840v80Zm80 640v-80h80v80h-80Zm0-640v-80h80v80h-80Zm160 640v-80h80v80h-80Zm0-640v-80h80v80h-80Zm160 640v-80h80v80h-80Zm0-640v-80h80v80h-80Zm160 640v-80h80q0 33-23.5 56.5T760-120Zm0-160v-80h80v80h-80Zm0-160v-80h80v80h-80Zm0-160v-80h80v80h-80Zm0-160v-80q33 0 56.5 23.5T840-760h-80Z"/></svg>';
const translateIcon =
'<svg height="24" viewBox="0 -960 960 960" width="24"><path d="m476-80 182-480h84L924-80h-84l-43-122H603L560-80h-84ZM160-200l-56-56 202-202q-35-35-63.5-80T190-640h84q20 39 40 68t48 58q33-33 68.5-92.5T484-720H40v-80h280v-80h80v80h280v80H564q-21 72-63 148t-83 116l96 98-30 82-122-125-202 201Zm468-72h144l-72-204-72 204Z"/></svg>';
const resumeIcon =
'<svg height="24" viewBox="0 -960 960 960" width="24"><path d="M240-240v-480h80v480h-80Zm160 0 400-240-400-240v480Z"/></svg>';
const pauseIcon =
'<svg height="24" viewBox="0 -960 960 960" width="24"><path d="M560-200v-560h160v560H560Zm-320 0v-560h160v560H240Z"/></svg>';
const stopIcon =
'<svg height="24" viewBox="0 -960 960 960" width="24"><path d="M320-320h320v-320H320v320ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z"/></svg>';
const selectVolumeIcon =
'<svg height="24" viewBox="0 -960 960 960" width="24"><path d="M560-131v-82q90-26 145-100t55-168q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 127-78 224.5T560-131Zm-80-29L280-360H120v-240h160l200-200v640Zm80-160v-322q47 22 73.5 66t26.5 96q0 51-26.5 94.5T560-320ZM40-680v-240h240v80H120v160H40ZM680-40v-80h160v-160h80v240H680Z"/></svg>';
/**
* @type {import("./type").Reader}
*/
class Reader {
constructor() {
this.selection = window.getSelection();
this.viewport = document.querySelector('meta[name=viewport]');
this.footerWrapper = document.getElementById('reader-footer-wrapper');
this.percentage = document.getElementById('reader-percentage');
this.battery = document.getElementById('reader-battery');
this.time = document.getElementById('reader-time');
this.paddingTop = parseInt(
getComputedStyle(document.querySelector('html')).getPropertyValue(
'padding-top',
),
);
this.chapter = document.querySelector('chapter');
this.chapterHeight = this.chapter.scrollHeight + this.paddingTop;
this.layoutHeight = window.innerHeight;
this.pluginId = this.chapter.getAttribute('data-plugin-id');
this.novelId = this.chapter.getAttribute('data-novel-id');
this.chapterId = this.chapter.getAttribute('data-chapter-id');
this.saveProgressInterval = setInterval(
() =>
this.post({
type: 'save',
data: parseInt(
((window.scrollY + this.layoutHeight) / this.chapterHeight) * 100,
),
}),
autoSaveInterval,
);
this.time.innerText = new Date().toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
});
this.timeInterval = setInterval(() => {
this.time.innerText = new Date().toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
});
}, 60000);
this.updateBatteryLevel(batteryLevel);
this.updateGeneralSettings(initSettings);
}
refresh = () => {
this.chapterHeight = this.chapter.scrollHeight + this.paddingTop;
};
post = obj => window.ReactNativeWebView.postMessage(JSON.stringify(obj));
updateReaderSettings = settings => {
document.documentElement.style.setProperty(
'--readerSettings-theme',
settings.theme,
);
document.documentElement.style.setProperty(
'--readerSettings-padding',
settings.padding + '%',
);
document.documentElement.style.setProperty(
'--readerSettings-textSize',
settings.textSize + 'px',
);
document.documentElement.style.setProperty(
'--readerSettings-textColor',
settings.textColor,
);
document.documentElement.style.setProperty(
'--readerSettings-textAlign',
settings.textAlign,
);
document.documentElement.style.setProperty(
'--readerSettings-lineHeight',
settings.lineHeight,
);
document.documentElement.style.setProperty(
'--readerSettings-fontFamily',
settings.fontFamily,
);
new FontFace(
settings.fontFamily,
'url("file:///android_asset/fonts/' + settings.fontFamily + '.ttf")',
)
.load()
.then(function (loadedFont) {
document.fonts.add(loadedFont);
});
};
updateGeneralSettings = settings => {
this.showScrollPercentage = settings.showScrollPercentage;
this.showBatteryAndTime = settings.showBatteryAndTime;
if (settings.swipeGestures) {
swipeHandler.enable();
} else {
swipeHandler.disable();
}
if (!this.showScrollPercentage && !this.showBatteryAndTime) {
this.footerWrapper.classList.add('d-none');
} else {
this.footerWrapper.classList.remove('d-none');
if (this.showScrollPercentage) {
this.percentage.classList.remove('hidden');
} else {
this.percentage.classList.add('hidden');
}
if (this.showBatteryAndTime) {
this.battery.classList.remove('hidden');
this.time.classList.remove('hidden');
} else {
this.battery.classList.add('hidden');
this.time.classList.add('hidden');
}
}
};
updateBatteryLevel = level => {
this.battery.innerText = Math.ceil(level * 100) + '%';
};
}
class ToolWrapper {
constructor() {
this.$ = document.getElementById('ToolWrapper');
this.$.onclick = e => {
e.stopPropagation();
};
this.tools = [];
}
hide = () => {
this.$.classList.add('hidden');
this.visible = false;
};
show = () => {
this.$.classList.remove('hidden');
this.visible = true;
for (const tool of this.tools) {
tool.onShow?.();
}
};
}
class ScrollHandler {
constructor(reader, toolWrapper) {
this.reader = reader;
this.toolWrapper = toolWrapper;
this.$ = document.getElementById('ScrollBar');
this.$.innerHTML = `
<div class="scrollbar-item scrollbar-text" id="scrollbar-percentage">
0
</div>
<div class="scrollbar-item" id="scrollbar-slider">
<div id="scrollbar-track">
<div id="scrollbar-progress">
<div id="scrollbar-thumb-wrapper">
<div id="scrollbar-thumb"></div>
</div>
</div>
</div>
</div>
<div class="scrollbar-item scrollbar-text">
100
</div>
`;
this.percentage = this.$.querySelector('#scrollbar-percentage');
this.progress = this.$.querySelector('#scrollbar-progress');
this.thumb = this.$.querySelector('#scrollbar-thumb-wrapper');
this.slider = this.$.querySelector('#scrollbar-slider');
this.sliderHeight = this.slider.clientHeight;
this.sliderOffsetY = this.slider.getBoundingClientRect().top;
this.lock = false;
window.onscroll = () => !this.lock && this.update();
this.thumb.ontouchstart = () => (this.lock = true);
this.thumb.ontouchend = () => (this.lock = false);
this.thumb.ontouchmove = e => {
const ratio =
(e.changedTouches[0].clientY - this.sliderOffsetY) / this.sliderHeight;
this.update(ratio < 0 ? 0 : ratio);
};
}
update = ratio => {
if (ratio === undefined) {
ratio =
(window.scrollY + this.reader.layoutHeight) / this.reader.chapterHeight;
}
if (ratio > 1) {
ratio = 1;
}
const percentage = parseInt(ratio * 100);
if (this.toolWrapper.visible) {
this.progress.style.height = percentage + '%';
this.percentage.innerText = percentage;
}
if (this.lock) {
window.scrollTo({
top: this.reader.chapterHeight * ratio - this.reader.layoutHeight,
behavior: 'instant',
});
}
if (this.reader.percentage) {
this.reader.percentage.innerText = percentage + '%';
}
};
refresh = () => {
this.sliderHeight = this.slider.clientHeight;
this.sliderOffsetY = this.slider.getBoundingClientRect().top;
};
onShow = () => {
this.reader.refresh();
this.refresh();
this.update();
};
}
class SwipeHandler {
constructor() {
this.initialX = null;
this.initialY = null;
}
touchStartHandler = e => {
this.initialX = e.changedTouches[0].screenX;
this.initialY = e.changedTouches[0].screenY;
};
touchEndHandler = e => {
let diffX = e.changedTouches[0].screenX - this.initialX;
let diffY = e.changedTouches[0].screenY - this.initialY;
if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 10) {
e.preventDefault();
reader.post({ type: diffX < 0 ? 'next' : 'prev' });
}
};
enable = () => {
document.addEventListener('touchstart', this.touchStartHandler);
document.addEventListener('touchend', this.touchEndHandler);
};
disable = () => {
document.removeEventListener('touchstart', this.touchStartHandler);
document.removeEventListener('touchend', this.touchEndHandler);
};
}
/**
* @type {import('./type').TextToSpeech}
*/
class TextToSpeech {
constructor(reader) {
this.reader = reader;
(this.leaf = this.reader.chapter), (this.TTSWrapper = null); // swap these 2 elements
this.TTSEle = null;
this.reading = false;
this.$ = document.getElementById('TTS-Controller');
this.$.classList.add('d-none');
this.$.onclick = () => {
if (this.reading) {
this.pause();
} else {
this.resume();
}
};
}
start = () => {
this.$.innerHTML = `<button>${pauseIcon}</button>`;
this.$.classList.remove('d-none');
this.stop();
this.next();
};
startHere = () => {
this.$.innerHTML = `<button>${pauseIcon}</button>`;
this.$.classList.remove('d-none');
this.stop();
if (this.reader.selection.type === 'Range') {
if (this.leaf && this.TTSWrapper) {
this.TTSWrapper.replaceWith(this.leaf);
}
this.leaf = this.reader.selection.anchorNode;
this.makeLeafSpeakable();
this.speak();
} else {
this.next();
}
};
resume = () => {
this.$.innerHTML = `<button>${pauseIcon}</button>`;
this.next();
};
findLeaf() {
while (this.leaf.firstChild) {
this.leaf = this.leaf.firstChild;
}
}
findNextLeaf() {
if (this.reader.chapter.isSameNode(this.leaf)) {
this.findLeaf();
} else if (this.leaf.nextSibling) {
this.leaf = this.leaf.nextSibling;
this.findLeaf();
} else {
this.leaf = this.leaf.parentNode;
if (this.reader.chapter.isSameNode(this.leaf)) {
return;
}
this.findNextLeaf();
}
}
readable() {
return (
this.leaf.nodeName === '#text' && /[^\s\n,.?!;":“”]/.test(this.leaf.data)
);
}
findNextTextNode() {
if (this.leaf && this.TTSWrapper) {
this.TTSWrapper.replaceWith(this.leaf);
this.TTSWrapper = null;
}
do {
this.findNextLeaf();
} while (!this.readable() && !this.reader.chapter.isSameNode(this.leaf));
if (this.reader.chapter.isSameNode(this.leaf)) {
return;
}
this.makeLeafSpeakable();
}
makeLeafSpeakable() {
this.TTSWrapper = document.createElement('tts-wrapper');
this.TTSWrapper.innerHTML = this.leaf.data.replace(
/([^\n,.?!;":“”]+)/g,
matched => {
if (matched.trim().length) {
return `<tts>${matched}</tts>`;
}
return matched;
},
);
this.TTSEle = this.TTSWrapper.firstElementChild;
this.leaf.replaceWith(this.TTSWrapper);
}
next = () => {
try {
if (this.TTSEle) {
this.TTSEle.classList.remove('highlight');
}
if (this.reading) {
if (this.TTSEle && this.TTSEle.nextElementSibling) {
this.TTSEle = this.TTSEle.nextElementSibling;
} else {
this.findNextTextNode();
}
} else {
if (!this.TTSEle) {
this.findNextTextNode();
}
}
this.speak();
} catch (e) {
alert(e);
}
};
stop = () => {
this.$.classList.add('d-none');
this.reading = false;
if (this.leaf && this.TTSWrapper) {
this.TTSWrapper.replaceWith(this.leaf);
this.TTSWrapper = null;
this.TTSEle = null;
this.leaf = this.reader.chapter;
}
this.reader.post({ type: 'stop-speak' });
};
pause = () => {
this.$.innerHTML = `<button>${resumeIcon}</button>`;
this.reading = false;
this.reader.post({ type: 'stop-speak' });
};
started = () => this.TTSWrapper !== null;
speak = () => {
this.reading = true;
if (this.reader.chapter.isSameNode(this.leaf)) {
return;
}
this.TTSEle.classList.add('highlight');
this.reader.post({ type: 'speak', data: this.TTSEle?.innerText.trim() });
};
}
/**
* @type {import("./type").ContextMenu}
*/
class ContextMenu {
/**
* @param {import("./type").Reader} reader
*/
constructor(reader) {
this.reader = reader;
this.items = {
START_READING: this.renderItem({
name: 'Start Reading',
icon: volumeIcon,
action: () => {
tts.start();
this.closeMenu();
},
}),
START_HERE: this.renderItem({
name: 'Start Here',
icon: selectVolumeIcon,
action: () => {
tts.startHere();
this.closeMenu();
},
}),
COPY: this.renderItem({
name: 'Copy',
icon: copyIcon,
action: () => {
this.reader.post({
type: 'copy',
data: this.reader.selection.toString(),
});
},
}),
SELECT_ALL: this.renderItem({
name: 'Select All',
icon: selectAllIcon,
action: () => {
const range = document.createRange();
range.selectNodeContents(this.reader.chapter);
this.reader.selection.removeAllRanges();
this.reader.selection.addRange(range);
},
}),
};
this.commonItems = [this.items.COPY, this.items.SELECT_ALL];
this.contextMenu = document.createElement('ul');
this.contextMenu.classList.add('contextMenu');
this.isOpened = false;
}
/**
* @param {{name: string, icon: string, action: () => void}} data
* @returns
*/
renderItem(data) {
const item = document.createElement('li');
const button = document.createElement('button');
button.innerHTML = `${data.icon} <span>${data.name}</span>`;
button.classList.add('contextMenu-button');
item.classList.add('contextMenu-item');
item.appendChild(button);
button.addEventListener('click', event => {
event.stopPropagation();
data.action();
});
return item;
}
renderMenu(items) {
this.contextMenu.innerHTML = '';
items.concat(this.commonItems).forEach((item, index) => {
item.firstChild.setAttribute('style', `animation-delay: ${index * 0.1}s`);
this.contextMenu.appendChild(item);
});
}
closeMenu() {
if (this.isOpened) {
this.isOpened = false;
this.contextMenu.remove();
}
}
init() {
this.reader.chapter.addEventListener('click', () => {
this.closeMenu(this.contextMenu);
});
document.addEventListener('contextmenu', e => {
e.preventDefault();
if (e.target instanceof HTMLImageElement) {
if (imageModal.showing) {
imageModal.hide();
} else {
imageModal.show(e.target);
}
return;
}
this.isOpened = true;
if (this.reader.selection.type === 'Range') {
this.renderMenu([this.items.START_HERE, this.items.START_READING]);
} else {
this.renderMenu([this.items.START_READING]);
}
const { clientX, clientY } = e;
e.target.appendChild(this.contextMenu);
const positionY =
clientY + this.contextMenu.scrollHeight >= window.innerHeight
? window.innerHeight - this.contextMenu.scrollHeight - 20
: clientY;
const positionX =
clientX + this.contextMenu.scrollWidth >= window.innerWidth
? window.innerWidth - this.contextMenu.scrollWidth - 20
: clientX;
this.contextMenu.setAttribute(
'style',
`--width: ${this.contextMenu.scrollWidth}px;
--height: ${this.contextMenu.scrollHeight}px;
--top: ${positionY}px;
--left: ${positionX}px;`,
);
});
}
}
/**
* @type {import("./type").ImageModal}
*/
class ImageModal {
/**
* @param {import('./type').Reader} reader
*/
constructor(reader) {
this.reader = reader;
this.$ = document.getElementById('Image-Modal');
this.img = this.$.querySelector('img');
this.showing = false;
this.$.onclick = e => {
if (e.target.id !== 'Image-Modal-img') {
e.stopPropagation();
this.hide();
}
};
this.$.ontouchmove = e => {
e.stopImmediatePropagation();
};
}
/**
*
* @param {HTMLImageElement} image
*/
show(image) {
this.img.src = image.src;
this.img.alt = `Can not render image from ${image.src}`;
this.reader.viewport.setAttribute(
'content',
'width=device-width, initial-scale=1.0, maximum-scale=10',
);
this.$.classList.add('show');
this.showing = true;
}
hide() {
this.$.classList.remove('show');
this.showing = false;
this.img.src = '';
this.img.alt = '';
this.reader.viewport.setAttribute(
'content',
'width=device-width, initial-scale=1.0, maximum-scale=1.0',
);
}
}
try {
var swipeHandler = new SwipeHandler();
var toolWrapper = new ToolWrapper();
var reader = new Reader();
var scrollHandler = new ScrollHandler(reader, toolWrapper);
var tts = new TextToSpeech(reader);
toolWrapper.tools = [scrollHandler, tts];
imageModal = new ImageModal(reader);
const contextMenu = new ContextMenu(reader);
contextMenu.init();
} catch (e) {
alert(e);
}
-70
View File
@@ -1,70 +0,0 @@
import {
ChapterGeneralSettings,
ChapterReaderSettings,
} from '@hooks/persisted/useSettings';
import 'typescript';
import 'typescript/lib/lib.dom';
interface Reader {
selection: Selection;
viewport: HTMLMetaElement;
footerWrapper: HTMLDivElement;
percentage: HTMLDivElement;
battery: HTMLDivElement;
time: HTMLDivElement;
paddingTop: number;
chapter: HTMLElement;
chapterHeight: number;
layoutHeight: number;
pluginId: string;
novelId: string;
chapterId: string;
saveProgressInterval: NodeJS.Timeout;
timeInterval: NodeJS.Timeout;
refresh: () => void;
post: (message: { type: string; data?: any }) => void;
updateReaderSettings: (settings: ChapterReaderSettings) => void;
updateGeneralSettings: (settings: ChapterGeneralSettings) => void;
updateBatteryLevel: (level: number) => void;
}
interface TextToSpeech {
reader: Reader;
leaf: HTMLElement;
TTSWrapper: HTMLElement;
TTSEle: HTMLElement;
reading: boolean;
start: () => void;
startHere: () => void;
resume: () => void;
pause: () => void;
stop: () => void;
started: () => boolean;
}
export enum ContextMenuItem {
START_READING = 'START_READING',
START_HERE = 'START_HERE',
COPY = 'COPY',
SELECT_ALL = 'SELECT_ALL',
}
interface ContextMenu {
reader: Reader;
contextMenu: HTMLUListElement;
items: Record<ContextMenuItem, HTMLLIElement>;
renderItem: (data: {
name: string;
icon: string;
action: () => void;
}) => HTMLLIElement;
renderMenu: (items: HTMLLIElement[]) => void;
closeMenu: (menu: HTMLUListElement) => void;
init: () => void;
}
interface ImageModal {
reader: Reader;
showing: boolean;
show: (image: HTMLImageElement) => void;
hide: () => void;
}
@@ -1,87 +0,0 @@
package com.rajarsheechatterjee.LNReader;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import expo.modules.ReactActivityDelegateWrapper;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
import com.rajarsheechatterjee.VolumeButtonListener.VolumeButtonListener;
import org.devio.rn.splashscreen.SplashScreen;
public class MainActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(layoutParams);
}
Window w = getWindow();
w.setStatusBarColor(Color.TRANSPARENT);
w.setNavigationBarColor(Color.TRANSPARENT);
w.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
super.onCreate(null);
SplashScreen.show(this,R.style.SplashScreenTheme, R.id.lottie);
SplashScreen.setAnimationFinished(true);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (VolumeButtonListener.isActive()) {
int action = event.getAction();
int keyCode = event.getKeyCode();
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
if (action == KeyEvent.ACTION_DOWN) {
// volup
VolumeButtonListener.up();
if (!VolumeButtonListener.prevent)
return super.dispatchKeyEvent(event);
}
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
if (action == KeyEvent.ACTION_DOWN) {
// voldown
VolumeButtonListener.down();
if (!VolumeButtonListener.prevent)
return super.dispatchKeyEvent(event);
}
return true;
default:
return super.dispatchKeyEvent(event);
}
}
return super.dispatchKeyEvent(event);
}
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "main";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, new DefaultReactActivityDelegate(
this,
getMainComponentName(),
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
DefaultNewArchitectureEntryPoint.getFabricEnabled()
));
}
}
@@ -1,80 +0,0 @@
package com.rajarsheechatterjee.LNReader;
import android.app.Application;
import android.content.res.Configuration;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import com.rajarsheechatterjee.NavigationBarColor.NavigationBarColorPackage;
import com.rajarsheechatterjee.TextFile.TextFilePackage;
import com.rajarsheechatterjee.VolumeButtonListener.VolumeButtonListenerPackage;
import com.rajarsheechatterjee.ZipArchive.ZipArchivePackage;
import expo.modules.ApplicationLifecycleDispatcher;
import expo.modules.ReactNativeHostWrapper;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new NavigationBarColorPackage());
packages.add(new VolumeButtonListenerPackage());
packages.add(new ZipArchivePackage());
packages.add(new TextFilePackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@Override
protected boolean isNewArchEnabled() {
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
@Override
protected Boolean isHermesEnabled() {
return BuildConfig.IS_HERMES_ENABLED;
}
});
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
DefaultNewArchitectureEntryPoint.load();
}
ApplicationLifecycleDispatcher.onApplicationCreate(this);
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
}
}
@@ -1,167 +0,0 @@
package com.rajarsheechatterjee.NavigationBarColor;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.graphics.Color;
import android.os.Build;
import android.app.Activity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import java.util.HashMap;
import java.util.Map;
import com.facebook.react.uimanager.IllegalViewOperationException;
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
public class NavigationBarColorModule extends ReactContextBaseJavaModule {
public static final String REACT_CLASS = "NavigationBarColor";
private static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY";
private static final String ERROR_NO_ACTIVITY_MESSAGE = "Tried to change the navigation bar while not attached to an Activity";
private static final String ERROR_API_LEVEL = "API_LEVEl";
private static final String ERROR_API_LEVEL_MESSAGE = "Only Android Oreo and above is supported";
private static ReactApplicationContext reactContext = null;
private static final int UI_FLAG_HIDE_NAV_BAR = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
public NavigationBarColorModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
public void setNavigationBarTheme(Activity activity, Boolean light) {
if (activity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Window window = activity.getWindow();
int flags = window.getDecorView().getSystemUiVisibility();
if (light) {
flags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
} else {
flags &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
}
window.getDecorView().setSystemUiVisibility(flags);
}
}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("EXAMPLE_CONSTANT", "example");
return constants;
}
@ReactMethod
public void changeNavigationBarColor(final String color, final Boolean light, final Boolean animated, final Promise promise) {
final WritableMap map = Arguments.createMap();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (getCurrentActivity() != null) {
try {
final Window window = getCurrentActivity().getWindow();
runOnUiThread(new Runnable() {
@Override
public void run() {
if (color.equals("transparent") || color.equals("translucent")) {
window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
if (color.equals("transparent")) {
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
} else {
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
setNavigationBarTheme(getCurrentActivity(), light);
map.putBoolean("success", true);
promise.resolve(map);
return;
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
if (animated) {
Integer colorFrom = window.getNavigationBarColor();
Integer colorTo = Color.parseColor(String.valueOf(color));
//window.setNavigationBarColor(colorTo);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
window.setNavigationBarColor((Integer) animator.getAnimatedValue());
}
});
colorAnimation.start();
} else {
window.setNavigationBarColor(Color.parseColor(String.valueOf(color)));
}
setNavigationBarTheme(getCurrentActivity(), light);
WritableMap map = Arguments.createMap();
map.putBoolean("success", true);
promise.resolve(map);
}
});
} catch (IllegalViewOperationException e) {
map.putBoolean("success", false);
promise.reject("error", e);
}
} else {
promise.reject(ERROR_NO_ACTIVITY, new Throwable(ERROR_NO_ACTIVITY_MESSAGE));
}
} else {
promise.reject(ERROR_API_LEVEL, new Throwable(ERROR_API_LEVEL_MESSAGE));
}
}
@ReactMethod
public void hideNavigationBar(Promise promise) {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (getCurrentActivity() != null) {
View decorView = getCurrentActivity().getWindow().getDecorView();
decorView.setSystemUiVisibility(UI_FLAG_HIDE_NAV_BAR);
}
}
});
} catch (IllegalViewOperationException e) {
WritableMap map = Arguments.createMap();
map.putBoolean("success", false);
promise.reject("error", e);
}
}
@ReactMethod
public void showNavigationBar(Promise promise) {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (getCurrentActivity() != null) {
Window w = getCurrentActivity().getWindow();
w.setStatusBarColor(Color.TRANSPARENT);
w.setNavigationBarColor(Color.TRANSPARENT);
View decorView = w.getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
}
});
} catch (IllegalViewOperationException e) {
WritableMap map = Arguments.createMap();
map.putBoolean("success", false);
promise.reject("error", e);
}
}
}
@@ -1,25 +0,0 @@
package com.rajarsheechatterjee.NavigationBarColor;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class NavigationBarColorPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new NavigationBarColorModule(reactContext));
return modules;
}
}
@@ -1,53 +0,0 @@
package com.rajarsheechatterjee.TextFile;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
public class TextFile extends ReactContextBaseJavaModule {
TextFile(ReactApplicationContext context) {
super(context);
}
@NonNull
@Override
public String getName() {
return "TextFile";
}
@ReactMethod
public void writeFile(String path, String content, Promise promise) {
try {
FileWriter fw = new FileWriter(path);
fw.write(content);
fw.close();
promise.resolve(null);
} catch (Exception e) {
promise.reject(e);
}
}
@ReactMethod void readFile(String path, Promise promise) {
try {
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new FileReader(path));
String line;
while ((line = br.readLine()) != null) sb.append(line).append('\n');
promise.resolve(sb.toString());
} catch (Exception e) {
promise.reject(e);
}
}
}
@@ -1,32 +0,0 @@
package com.rajarsheechatterjee.TextFile;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TextFilePackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactApplicationContext) {
List<NativeModule> modules = new ArrayList<>();
try {
modules.add(new TextFile(reactApplicationContext));
} catch (Exception e) {
throw new RuntimeException(e);
}
return modules;
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactApplicationContext) {
return Collections.emptyList();
}
}
@@ -1,93 +0,0 @@
package com.rajarsheechatterjee.VolumeButtonListener;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
public class VolumeButtonListener extends ReactContextBaseJavaModule {
static ReactApplicationContext appContext;
public VolumeButtonListener(ReactApplicationContext context) {
super(context);
appContext = context;
}
private static boolean active = false;
public static boolean isActive() {
return active;
}
public static boolean prevent = false;
public static void sendEvent(boolean up) {
if (active) {
WritableMap args = Arguments.createMap();
if (up)
appContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("VolumeUp", args);
else
appContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("VolumeDown", args);
}
}
public static void down() {
sendEvent(false);
}
public static void up() {
sendEvent(true);
}
@ReactMethod
public void connect() {
active = true;
}
@ReactMethod
void disconnect() {
active = false;
}
@ReactMethod
public void pause() {
active = false;
}
@ReactMethod
public void unpause() {
active = true;
}
@ReactMethod
public void preventDefault() {
prevent = true;
}
@ReactMethod
public void noPreventDefault() {
prevent = false;
}
@NonNull
@Override
public String getName() {
return "VolumeButtonListener";
}
@ReactMethod
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}
}
@@ -1,30 +0,0 @@
package com.rajarsheechatterjee.VolumeButtonListener;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class VolumeButtonListenerPackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<NativeModule>();
modules.add(new VolumeButtonListener(reactContext));
return modules;
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
@@ -1,169 +0,0 @@
package com.rajarsheechatterjee.ZipArchive;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class ZipArchive extends ReactContextBaseJavaModule {
ZipArchive(ReactApplicationContext context) throws Exception {
super(context);
}
@NonNull
@Override
public String getName() {
return "ZipArchive";
}
private String escapeFilePath(String filePath){
return filePath.replaceAll(":", "\uA789");
}
private void unzipProcess(ZipInputStream zis, String distDirPath) throws Exception {
ZipEntry zipEntry;
int len;
byte[] buffer = new byte[4096];
while ((zipEntry = zis.getNextEntry()) != null) {
if(zipEntry.getName().endsWith("/")) continue;
String escapedFilePath = this.escapeFilePath(zipEntry.getName());
File newFile = new File(distDirPath, escapedFilePath);
newFile.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(newFile);
while ((len = zis.read(buffer)) > 0) fos.write(buffer, 0, len);
fos.close();
}
zis.closeEntry();
}
@ReactMethod
public void unzip(String sourceFilePath, String distDirPath, Promise promise) {
new Thread(new Runnable() {
@Override
public void run() {
try{
ZipInputStream zis = new ZipInputStream(new FileInputStream(sourceFilePath));
unzipProcess(zis, distDirPath);
zis.close();
promise.resolve(null);
} catch (Exception e) {
promise.reject(e.getCause());
}
}
}).start();
}
@ReactMethod
public void remoteUnzip(String distDirPath, String _url, @Nullable ReadableMap headers, Promise promise){
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL(_url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
if(headers != null){
Iterator<Map.Entry<String, Object>> it = headers.getEntryIterator();
while (it.hasNext()){
Map.Entry<String, Object> entry = it.next();
connection.setRequestProperty(entry.getKey(), entry.getValue().toString());
}
}
ZipInputStream zis = new ZipInputStream(connection.getInputStream());
unzipProcess(zis, distDirPath);
connection.disconnect();
promise.resolve(null);
}catch (Exception e){
promise.reject(e.getCause());
}
}
}).start();
}
private ArrayList<String> walkDir(String path) {
ArrayList<String> res = new ArrayList<>();
File node = new File(path);
if(node.isFile()){
res.add(path);
}else{
String[] children = node.list();
if(children != null){
for(String filename: children){
ArrayList<String> childPaths = walkDir(new File(path, filename).toString());
res.addAll(childPaths);
}
}
}
return res;
}
private void zipProcess(String sourceDirPath, ZipOutputStream zos) throws Exception{
ArrayList<String> paths = walkDir(sourceDirPath);
byte[] buffer = new byte[4096];
int len;
for(String path: paths){
ZipEntry zipEntry = new ZipEntry(path.replace(sourceDirPath + "/", ""));
zos.putNextEntry(zipEntry);
try (FileInputStream fis = new FileInputStream(path)) {
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
}
zos.closeEntry();
}
zos.close();
}
@ReactMethod
public void remoteZip(String sourceDirPath, String _url, @Nullable ReadableMap headers, Promise promise){
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL(_url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
if(headers != null){
Iterator<Map.Entry<String, Object>> it = headers.getEntryIterator();
while (it.hasNext()){
Map.Entry<String, Object> entry = it.next();
connection.setRequestProperty(entry.getKey(), entry.getValue().toString());
}
}
ZipOutputStream zos = new ZipOutputStream(connection.getOutputStream());
zipProcess(sourceDirPath, zos);
if(connection.getResponseCode() == 200){
InputStream is = connection.getInputStream();
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
for (int length; (length = is.read(buffer)) != -1; ) {
result.write(buffer, 0, length);
}
is.close();
connection.disconnect();
promise.resolve(result.toString());
}else{
throw new Exception("Network request failed");
}
}catch (Exception e){
promise.reject(e.getCause());
}
}
}).start();
}
}
@@ -1,32 +0,0 @@
package com.rajarsheechatterjee.ZipArchive;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ZipArchivePackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactApplicationContext) {
List<NativeModule> modules = new ArrayList<>();
try {
modules.add(new ZipArchive(reactApplicationContext));
} catch (Exception e) {
throw new RuntimeException(e);
}
return modules;
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactApplicationContext) {
return Collections.emptyList();
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

@@ -1,93 +0,0 @@
<vector android:alpha="0" android:height="1dp"
android:viewportHeight="1024" android:viewportWidth="1024"
android:width="1dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="0.994" android:fillColor="#132c33"
android:pathData="M479.5,-0.5C500.8,-0.5 522.2,-0.5 543.5,-0.5C704.4,13.5 832.9,84.8 929,213.5C985.1,293.3 1016.5,382 1023.5,479.5C1023.5,500.8 1023.5,522.2 1023.5,543.5C1009.2,709.4 934.5,840.2 799.5,936C722,987.8 636.6,1017 543.5,1023.5C522.2,1023.5 500.8,1023.5 479.5,1023.5C333.8,1011.4 213.3,950.4 118,840.5C47.3,754.3 7.8,655.3 -0.5,543.5C-0.5,522.2 -0.5,500.8 -0.5,479.5C13.5,318.6 84.8,190.1 213.5,94C293.3,37.9 382,6.4 479.5,-0.5Z" android:strokeAlpha="0.994"/>
<path android:fillColor="#106476" android:pathData="M870.5,325.5C876.8,340.6 882.7,355.9 888,371.5C889.7,373.2 891.1,375.2 892,377.5C895.1,386.3 897.4,395.3 899,404.5C900,406.7 901.5,408.4 903.5,409.5C903.5,410.2 903.5,410.8 903.5,411.5C907.2,436.2 910.8,460.8 914.5,485.5C915.4,500.5 915.7,515.5 915.5,530.5C911.7,612.8 885.4,686.8 836.5,752.5C836.5,750.7 837,749 838,747.5C894.4,667.2 918.8,577.8 911,479.5C899,365 848.5,271.8 759.5,200C652.9,120.8 534.9,97.5 405.5,130C299,161.6 218.1,225.4 163,321.5C108.7,424.4 99.1,531.4 134,642.5C147,680.2 165,715.2 188,747.5C189,749 189.5,750.7 189.5,752.5C141.4,688 115.1,615.3 110.5,534.5C110.3,518.5 110.5,502.5 111,486.5C112.8,477.3 113.8,467.9 114,458.5C119,435.8 124.4,413.1 130,390.5C140,359.6 153.3,330.6 170,303.5C184.2,283.1 199.4,263.5 215.5,244.5C231.1,229.1 246.7,213.9 262.5,199C273.8,191.7 285.1,184.4 296.5,177C297.3,175.5 298.3,174.2 299.5,173C303.2,172.6 306.5,171.3 309.5,169C313,166 316.3,162.9 319.5,159.5C330.1,155.2 340.1,149.9 349.5,143.5C360.1,142.3 370.1,139.2 379.5,134C385.7,132.1 392.1,130.5 398.5,129C400.5,128 402.5,127 404.5,126C452.1,113.9 500.4,109.7 549.5,113.5C567,115.2 584.3,117.7 601.5,121C620.6,126.7 639.8,132.6 659,138.5C668,141.9 676.8,145.8 685.5,150C695.4,156.1 705.8,161.5 716.5,166C717.2,167 717.8,168 718.5,169C733.7,175.2 747.1,184.2 758.5,196C764.2,199.9 769.5,204.2 774.5,209C776.6,210.8 778.9,212.1 781.5,213C785.3,218.2 789.8,222.7 795,226.5C797.1,228.3 798.4,230.7 799,233.5C807,241.6 815,249.6 823,257.5C823.3,258.8 823.7,260.2 824,261.5C828,266.5 832.4,271.2 837,275.5C841.8,284.4 847.1,293.1 853,301.5C858.3,307.4 862.6,314.1 866,321.5C867,323.4 868.5,324.7 870.5,325.5Z"/>
<path android:fillColor="#126e81" android:pathData="M836.5,752.5C791,813.4 733,858.3 662.5,887C545.5,931 431.5,924.3 320.5,867C268.3,838.7 224.7,800.5 189.5,752.5C189.5,750.7 189,749 188,747.5C165,715.2 147,680.2 134,642.5C99.1,531.4 108.7,424.4 163,321.5C218.1,225.4 299,161.6 405.5,130C534.9,97.5 652.9,120.8 759.5,200C848.5,271.8 899,365 911,479.5C918.8,577.8 894.4,667.2 838,747.5C837,749 836.5,750.7 836.5,752.5Z"/>
<path android:fillColor="#a1abaf" android:pathData="M660.5,203.5C657.7,203.4 655,202.8 652.5,201.5C641.6,195.4 629.9,191.5 617.5,190C609.7,188 602.4,185 595.5,181C580.9,179.2 566.6,176.6 552.5,173C521.2,169 490.2,169.9 459.5,175.5C447.6,178.3 435.6,180.5 423.5,182C418.2,183.8 412.9,185.3 407.5,186.5C388.5,192.1 370.1,199.6 352.5,209C351.2,209.5 349.9,209.7 348.5,209.5C423.4,170 502.4,159.1 585.5,177C611.8,182.6 636.8,191.4 660.5,203.5Z"/>
<path android:fillColor="#d7e2e6" android:pathData="M688.5,223.5C690.9,224.3 692.9,225.7 694.5,227.5C695.4,229.8 696.7,232 698.5,234C734,257.8 763.8,287.3 788,322.5C789.3,323.9 790.8,324.9 792.5,325.5C793.2,325.8 793.8,326.2 794.5,326.5C794.3,327.9 794.5,329.2 795,330.5C851.6,424.2 862.9,523.2 829,627.5C824.9,638.7 820.3,649.7 815,660.5C812.6,667.1 810.4,673.7 808.5,680.5C806.4,682.7 803.9,684.7 801,686.5C790.6,704.3 778.8,720.7 765.5,735.5C765.5,737.2 765.5,738.8 765.5,740.5C721,790.4 666.4,824.1 601.5,841.5C586.8,843.9 572.2,846.4 557.5,849C523.2,853.2 488.9,853.4 454.5,849.5C364.7,834.1 292.3,790.4 237.5,718.5C236.2,715.7 235.4,712.7 235,709.5C188.1,642.5 167.8,567.8 174,485.5C176.7,456.8 182.5,428.8 191.5,401.5C194.1,391.7 197.3,382 201,372.5C201.9,372 202.8,371.3 203.5,370.5C208.1,361.7 212.3,352.7 216,343.5C220.2,338.6 223.9,333.3 227,327.5C242.1,304 260,282.5 280.5,263C290.7,256.1 300.3,248.4 309.5,240C314.3,237.6 318.9,235.1 323.5,232.5C408.4,179.1 499.7,164.2 597.5,188C611.5,191.7 625.2,196.3 638.5,202C641.5,202.3 644.5,202.7 647.5,203C661.2,209.8 674.9,216.7 688.5,223.5Z"/>
<path android:fillColor="#cfdade" android:pathData="M556.5,177.5C577.5,179.3 597.9,184.2 617.5,192C621.3,192.3 624.9,192.9 628.5,194C636.6,198 645,201.2 653.5,203.5C663.6,208.4 673.6,213.6 683.5,219C685.6,220 687.3,221.5 688.5,223.5C674.9,216.7 661.2,209.8 647.5,203C644.5,202.7 641.5,202.3 638.5,202C625.2,196.3 611.5,191.7 597.5,188C499.7,164.2 408.4,179.1 323.5,232.5C330.8,226.2 338.8,220.9 347.5,216.5C348.5,215.1 349.5,213.8 350.5,212.5C363.9,207 377.2,201.1 390.5,195C402.2,191.3 413.8,187.7 425.5,184C448.7,180 472.1,177 495.5,175C505.5,174.3 515.5,174.3 525.5,175C535.8,176.1 546.2,176.9 556.5,177.5Z"/>
<path android:fillColor="#c4ced2" android:pathData="M556.5,177.5C546.2,176.9 535.8,176.1 525.5,175C515.5,174.3 505.5,174.3 495.5,175C472.1,177 448.7,180 425.5,184C413.8,187.7 402.2,191.3 390.5,195C377.2,201.1 363.9,207 350.5,212.5C368.9,202.5 387.9,193.8 407.5,186.5C412.9,185.3 418.2,183.8 423.5,182C435.6,180.5 447.6,178.3 459.5,175.5C491.2,171.4 522.9,171.5 554.5,176C555.4,176.3 556.1,176.8 556.5,177.5Z"/>
<path android:fillColor="#bac3c7" android:pathData="M652.5,201.5C652.8,202.2 653.2,202.8 653.5,203.5C645,201.2 636.6,198 628.5,194C624.9,192.9 621.3,192.3 617.5,192C597.9,184.2 577.5,179.3 556.5,177.5C556.1,176.8 555.4,176.3 554.5,176C522.9,171.5 491.2,171.4 459.5,175.5C490.2,169.9 521.2,169 552.5,173C566.6,176.6 580.9,179.2 595.5,181C602.4,185 609.7,188 617.5,190C629.9,191.5 641.6,195.4 652.5,201.5Z"/>
<path android:fillColor="#0e5565" android:pathData="M870.5,325.5C868.5,324.7 867,323.4 866,321.5C862.6,314.1 858.3,307.4 853,301.5C847.1,293.1 841.8,284.4 837,275.5C832.4,271.2 828,266.5 824,261.5C823.7,260.2 823.3,258.8 823,257.5C815,249.6 807,241.6 799,233.5C798.4,230.7 797.1,228.3 795,226.5C789.8,222.7 785.3,218.2 781.5,213C778.9,212.1 776.6,210.8 774.5,209C769.5,204.2 764.2,199.9 758.5,196C747.1,184.2 733.7,175.2 718.5,169C717.8,168 717.2,167 716.5,166C705.8,161.5 695.4,156.1 685.5,150C676.8,145.8 668,141.9 659,138.5C639.8,132.6 620.6,126.7 601.5,121C584.3,117.7 567,115.2 549.5,113.5C500.4,109.7 452.1,113.9 404.5,126C402.5,127 400.5,128 398.5,129C392.1,130.5 385.7,132.1 379.5,134C370.1,139.2 360.1,142.3 349.5,143.5C420.7,111.5 495,101.3 572.5,113C678.8,130.2 765.3,180.7 832,264.5C846.7,283.7 859.5,304 870.5,325.5Z"/>
<path android:fillColor="#a9b3b7" android:pathData="M652.5,201.5C655,202.8 657.7,203.4 660.5,203.5C704.9,224.6 742.6,254.2 773.5,292.5C765,283.5 756.4,274.7 747.5,266C744,263.6 741,260.8 738.5,257.5C733.8,254.1 729.1,250.8 724.5,247.5C722.4,245.3 720.1,243.3 717.5,241.5C709.8,236.7 702.1,232 694.5,227.5C692.9,225.7 690.9,224.3 688.5,223.5C687.3,221.5 685.6,220 683.5,219C673.6,213.6 663.6,208.4 653.5,203.5C653.2,202.8 652.8,202.2 652.5,201.5Z"/>
<path android:fillColor="#cfdadd" android:pathData="M694.5,227.5C702.1,232 709.8,236.7 717.5,241.5C720.1,243.3 722.4,245.3 724.5,247.5C740.5,264 756.6,280.3 773,296.5C779.5,304.9 785.8,313.3 792,321.5C792.5,322.8 792.7,324.1 792.5,325.5C790.8,324.9 789.3,323.9 788,322.5C763.8,287.3 734,257.8 698.5,234C696.7,232 695.4,229.8 694.5,227.5Z"/>
<path android:fillColor="#a8b3b6" android:pathData="M407.5,186.5C387.9,193.8 368.9,202.5 350.5,212.5C348.1,212.5 345.7,213 343.5,214C280.9,251.6 233.3,303.1 200.5,368.5C200,369.3 199.4,369.6 198.5,369.5C231,299.7 281,246.4 348.5,209.5C349.9,209.7 351.2,209.5 352.5,209C370.1,199.6 388.5,192.1 407.5,186.5Z"/>
<path android:fillColor="#c7d1d5" android:pathData="M350.5,212.5C349.5,213.8 348.5,215.1 347.5,216.5C338.8,220.9 330.8,226.2 323.5,232.5C318.9,235.1 314.3,237.6 309.5,240C300.3,248.4 290.7,256.1 280.5,263C260,282.5 242.1,304 227,327.5C223.9,333.3 220.2,338.6 216,343.5C212.3,352.7 208.1,361.7 203.5,370.5C202.8,371.3 201.9,372 201,372.5C197.3,382 194.1,391.7 191.5,401.5C187.7,410.3 184.4,419.3 181.5,428.5C183.2,412.5 188.1,397.5 196,383.5C196.7,378.7 197.5,374.1 198.5,369.5C199.4,369.6 200,369.3 200.5,368.5C233.3,303.1 280.9,251.6 343.5,214C345.7,213 348.1,212.5 350.5,212.5Z"/>
<path android:fillColor="#0f5969" android:pathData="M349.5,143.5C340.1,149.9 330.1,155.2 319.5,159.5C316.3,162.9 313,166 309.5,169C306.5,171.3 303.2,172.6 299.5,173C298.3,174.2 297.3,175.5 296.5,177C285.1,184.4 273.8,191.7 262.5,199C246.7,213.9 231.1,229.1 215.5,244.5C199.4,263.5 184.2,283.1 170,303.5C153.3,330.6 140,359.6 130,390.5C124.4,413.1 119,435.8 114,458.5C113.8,467.9 112.8,477.3 111,486.5C110.5,502.5 110.3,518.5 110.5,534.5C105.6,408 149,301.1 240.5,214C273,184.7 309.3,161.2 349.5,143.5Z"/>
<path android:fillColor="#b5bfc3" android:pathData="M724.5,247.5C729.1,250.8 733.8,254.1 738.5,257.5C741,260.8 744,263.6 747.5,266C756.4,274.7 765,283.5 773.5,292.5C804,327.4 825.6,367.4 838.5,412.5C837.5,412 836.7,411.4 836,410.5C833.5,403.3 831,396.2 828.5,389C820.3,372.7 812.4,356.2 805,339.5C803.9,338.7 802.7,338 801.5,337.5C799.9,333.3 797.6,329.6 794.5,326.5C793.8,326.2 793.2,325.8 792.5,325.5C792.7,324.1 792.5,322.8 792,321.5C785.8,313.3 779.5,304.9 773,296.5C756.6,280.3 740.5,264 724.5,247.5Z"/>
<path android:fillColor="#d0dade" android:pathData="M558.5,314.5C570.2,314.3 581.8,314.5 593.5,315C603.6,318.9 605.1,325 598,333.5C597.2,342.4 597.3,351.2 598.5,360C629.8,360.3 661.2,360.7 692.5,361C694.7,361.8 696.2,363.3 697,365.5C697.7,376.5 697.7,387.5 697,398.5C696.2,400 695,401.2 693.5,402C661.6,403.5 629.6,404 597.5,403.5C597.2,411.8 597.5,419.9 598.5,428C623.3,428 647.9,428.7 672.5,430C673.7,430.9 674.5,432.1 675,433.5C675.7,444.2 675.7,454.8 675,465.5C674.2,468.8 672,470.7 668.5,471C638.7,471.2 608.8,471.3 579,471.5C547.8,471.7 516.6,471.2 485.5,470C483.5,468.7 482.3,466.8 482,464.5C481.3,454.5 481.3,444.5 482,434.5C482.3,432.7 483.1,431.2 484.5,430C507.1,428.5 529.7,428 552.5,428.5C552.5,420.2 552.5,411.8 552.5,403.5C525.2,403.7 497.8,403.5 470.5,403C467.2,403.1 465.1,401.6 464,398.5C462,387.2 462,375.8 464,364.5C465.2,363 466.7,361.8 468.5,361C496.5,360.5 524.5,360.3 552.5,360.5C552.1,346.5 552.6,332.5 554,318.5C555.4,316.9 556.9,315.6 558.5,314.5Z"/>
<path android:fillColor="#849094" android:pathData="M591.5,317.5C580.5,317.5 569.5,317.5 558.5,317.5C558.8,332.7 558.5,347.7 557.5,362.5C557.5,347.2 557.5,331.8 557.5,316.5C569,316.2 580.3,316.5 591.5,317.5Z"/>
<path android:fillColor="#132c33" android:pathData="M591.5,317.5C599,318.8 599.5,321.8 593,326.5C592.5,338.5 592.3,350.5 592.5,362.5C625.8,362.5 659.2,362.5 692.5,362.5C692.5,373.2 692.5,383.8 692.5,394.5C658.8,394.5 625.2,394.5 591.5,394.5C591.2,406.7 591.5,418.7 592.5,430.5C618,431.5 643.7,431.8 669.5,431.5C669.5,441.8 669.5,452.2 669.5,462.5C608.8,462.5 548.2,462.5 487.5,462.5C487.5,451.8 487.5,441.2 487.5,430.5C510.2,430.5 532.8,430.5 555.5,430.5C556.2,430.5 556.8,430.5 557.5,430.5C558.5,418.7 558.8,406.7 558.5,394.5C528.5,394.5 498.5,394.5 468.5,394.5C468.5,383.8 468.5,373.2 468.5,362.5C498.2,362.5 527.8,362.5 557.5,362.5C558.5,347.7 558.8,332.7 558.5,317.5C569.5,317.5 580.5,317.5 591.5,317.5Z"/>
<path android:fillColor="#cfdade" android:pathData="M331.5,324.5C366.9,324 402.2,324.5 437.5,326C439.5,327.3 440.7,329.2 441,331.5C441.9,342.2 441.6,352.9 440,363.5C439,365.3 437.5,366.4 435.5,367C399.8,368 364.1,367.6 328.5,366C327.3,365.1 326.5,363.9 326,362.5C325.3,351.5 325.3,340.5 326,329.5C327.4,327.3 329.2,325.6 331.5,324.5Z"/>
<path android:fillColor="#b8c1c4" android:pathData="M330.5,331.5C330.2,341 330.5,350.3 331.5,359.5C331.6,360.1 331.9,360.6 332.5,361C349.3,361.2 366.2,361.3 383,361.5C400.2,361.5 417,360.8 433.5,359.5C434.2,359.5 434.8,359.5 435.5,359.5C435.8,359.5 436.2,359.5 436.5,359.5C436.4,361.4 435.4,362.6 433.5,363C399.2,363.7 364.8,363.7 330.5,363C329.9,362.3 329.4,361.4 329,360.5C328.3,351.2 328.3,341.8 329,332.5C329.4,331.9 329.9,331.6 330.5,331.5Z"/>
<path android:fillColor="#a3abaf" android:pathData="M331.5,359.5C365.5,359.5 399.5,359.5 433.5,359.5C417,360.8 400.2,361.5 383,361.5C366.2,361.3 349.3,361.2 332.5,361C331.9,360.6 331.6,360.1 331.5,359.5Z"/>
<path android:fillColor="#142d34" android:pathData="M330.5,327.5C365.5,327.5 400.5,327.5 435.5,327.5C435.5,338.2 435.5,348.8 435.5,359.5C434.8,359.5 434.2,359.5 433.5,359.5C399.5,359.5 365.5,359.5 331.5,359.5C330.5,350.3 330.2,341 330.5,331.5C330.5,330.2 330.5,328.8 330.5,327.5Z"/>
<path android:fillColor="#79868a" android:pathData="M330.5,327.5C365.7,326.5 401,326.2 436.5,326.5C436.5,337.5 436.5,348.5 436.5,359.5C436.2,359.5 435.8,359.5 435.5,359.5C435.5,348.8 435.5,338.2 435.5,327.5C400.5,327.5 365.5,327.5 330.5,327.5Z"/>
<path android:fillColor="#0f5a6b" android:pathData="M870.5,325.5C884.8,352.1 895.8,380.1 903.5,409.5C901.5,408.4 900,406.7 899,404.5C897.4,395.3 895.1,386.3 892,377.5C891.1,375.2 889.7,373.2 888,371.5C882.7,355.9 876.8,340.6 870.5,325.5Z"/>
<path android:fillColor="#8e979a" android:pathData="M468.5,394.5C498.5,394.5 528.5,394.5 558.5,394.5C558.8,406.7 558.5,418.7 557.5,430.5C558.1,419.1 557.5,407.9 555.5,397C528.2,396.7 500.8,396.3 473.5,396C471.6,395.8 470,395.3 468.5,394.5Z"/>
<path android:fillColor="#a6aeb1" android:pathData="M690.5,395.5C690.4,396.1 690.1,396.6 689.5,397C658.2,397.3 626.8,397.7 595.5,398C594.6,399 593.6,399.9 592.5,400.5C592.5,398.8 592.5,397.2 592.5,395.5C625.2,395.5 657.8,395.5 690.5,395.5Z"/>
<path android:fillColor="#b4bec1" android:pathData="M468.5,362.5C468.5,373.2 468.5,383.8 468.5,394.5C470,395.3 471.6,395.8 473.5,396C500.8,396.3 528.2,396.7 555.5,397C557.5,407.9 558.1,419.1 557.5,430.5C556.8,430.5 556.2,430.5 555.5,430.5C556.6,420.9 556.8,411.2 556,401.5C555.5,401 555,400.5 554.5,400C526.5,399.7 498.5,399.3 470.5,399C469.7,398.2 468.8,397.3 468,396.5C467.2,385 467.3,373.7 468.5,362.5Z"/>
<path android:fillColor="#b9c3c6" android:pathData="M690.5,395.5C691.2,395.5 691.8,395.5 692.5,395.5C691.9,396.5 691.2,397.5 690.5,398.5C658.8,399.3 627.2,399.8 595.5,400C595,400.5 594.5,401 594,401.5C593.2,411.2 593.4,420.9 594.5,430.5C593.8,430.5 593.2,430.5 592.5,430.5C592.5,420.5 592.5,410.5 592.5,400.5C593.6,399.9 594.6,399 595.5,398C626.8,397.7 658.2,397.3 689.5,397C690.1,396.6 690.4,396.1 690.5,395.5Z"/>
<path android:fillColor="#515f63" android:pathData="M692.5,394.5C692.5,394.8 692.5,395.2 692.5,395.5C691.8,395.5 691.2,395.5 690.5,395.5C657.8,395.5 625.2,395.5 592.5,395.5C592.5,397.2 592.5,398.8 592.5,400.5C592.5,410.5 592.5,420.5 592.5,430.5C591.5,418.7 591.2,406.7 591.5,394.5C625.2,394.5 658.8,394.5 692.5,394.5Z"/>
<path android:fillColor="#ced9dd" android:pathData="M191.5,401.5C182.5,428.8 176.7,456.8 174,485.5C167.8,567.8 188.1,642.5 235,709.5C235.4,712.7 236.2,715.7 237.5,718.5C214.2,688.9 196.9,655.9 185.5,619.5C182.6,606.1 179.5,592.8 176,579.5C174,566 171.8,552.6 169.5,539.5C167.9,501.8 171.9,464.8 181.5,428.5C184.4,419.3 187.7,410.3 191.5,401.5Z"/>
<path android:fillColor="#ced8dc" android:pathData="M313.5,384.5C359.2,384.3 404.8,384.5 450.5,385C452,386.2 453.2,387.7 454,389.5C454.7,400.5 454.7,411.5 454,422.5C451.8,425.8 448.6,427.3 444.5,427C400.5,428 356.5,427.6 312.5,426C311,424.8 309.8,423.3 309,421.5C308.3,410.8 308.3,400.2 309,389.5C310,387.3 311.5,385.6 313.5,384.5Z"/>
<path android:fillColor="#bbc4c8" android:pathData="M313.5,389.5C313.5,399.2 313.5,408.8 313.5,418.5C314.4,419.3 315.4,420.1 316.5,421C360.2,421.7 403.8,421.7 447.5,421C448.4,420.3 449,419.5 449.5,418.5C449.5,408.8 449.5,399.2 449.5,389.5C450.7,399.7 450.8,410 450,420.5C449.1,421.7 447.9,422.5 446.5,423C402.5,423.7 358.5,423.7 314.5,423C313.8,421.7 313,420.6 312,419.5C311.3,409.8 311.3,400.2 312,390.5C312.4,389.9 312.9,389.6 313.5,389.5Z"/>
<path android:fillColor="#9da6a9" android:pathData="M313.5,418.5C358.8,418.5 404.2,418.5 449.5,418.5C449,419.5 448.4,420.3 447.5,421C403.8,421.7 360.2,421.7 316.5,421C315.4,420.1 314.4,419.3 313.5,418.5Z"/>
<path android:fillColor="#132c33" android:pathData="M449.5,389.5C449.5,399.2 449.5,408.8 449.5,418.5C404.2,418.5 358.8,418.5 313.5,418.5C313.5,408.8 313.5,399.2 313.5,389.5C313.5,388.5 313.5,387.5 313.5,386.5C358.8,386.5 404.2,386.5 449.5,386.5C449.5,387.5 449.5,388.5 449.5,389.5Z"/>
<path android:fillColor="#273b41" android:pathData="M592.5,430.5C593.2,430.5 593.8,430.5 594.5,430.5C620.2,429.8 645.5,430.5 670.5,432.5C670.5,432.8 670.5,433.2 670.5,433.5C670.5,443.2 670.5,452.8 670.5,462.5C670.2,462.5 669.8,462.5 669.5,462.5C669.5,452.2 669.5,441.8 669.5,431.5C643.7,431.8 618,431.5 592.5,430.5Z"/>
<path android:fillColor="#b8c4c8" android:pathData="M198.5,369.5C197.5,374.1 196.7,378.7 196,383.5C188.1,397.5 183.2,412.5 181.5,428.5C171.9,464.8 167.9,501.8 169.5,539.5C163.8,480.3 173.5,423.6 198.5,369.5Z"/>
<path android:fillColor="#cfdadd" android:pathData="M330.5,445.5C365.8,445.3 401.2,445.5 436.5,446C439.1,447.3 440.6,449.5 441,452.5C441.9,463.2 441.6,473.9 440,484.5C439.2,485.3 438.3,486.2 437.5,487C401.5,487.7 365.5,487.7 329.5,487C327.3,486.3 326.1,484.8 326,482.5C325.3,471.8 325.3,461.2 326,450.5C327,448.3 328.5,446.6 330.5,445.5Z"/>
<path android:fillColor="#132c32" android:pathData="M435.5,448.5C435.5,458.8 435.5,469.2 435.5,479.5C400.8,479.5 366.2,479.5 331.5,479.5C331.5,469.2 331.5,458.8 331.5,448.5C366.2,448.5 400.8,448.5 435.5,448.5Z"/>
<path android:fillColor="#273b41" android:pathData="M435.5,447.5C435.5,447.8 435.5,448.2 435.5,448.5C400.8,448.5 366.2,448.5 331.5,448.5C331.5,458.8 331.5,469.2 331.5,479.5C330.5,470.7 330.2,461.7 330.5,452.5C330.5,450.8 330.5,449.2 330.5,447.5C365.5,447.5 400.5,447.5 435.5,447.5Z"/>
<path android:fillColor="#6b7679" android:pathData="M487.5,430.5C487.5,441.2 487.5,451.8 487.5,462.5C548.2,462.5 608.8,462.5 669.5,462.5C669.8,462.5 670.2,462.5 670.5,462.5C670.2,463.3 669.5,463.7 668.5,463.5C608.5,463.5 548.5,463.5 488.5,463.5C488.2,463.5 487.8,463.5 487.5,463.5C486.5,453 486.2,442.3 486.5,431.5C486.5,430.8 486.8,430.5 487.5,430.5Z"/>
<path android:fillColor="#c8d3d6" android:pathData="M486.5,431.5C486.2,442.3 486.5,453 487.5,463.5C487.8,465.8 489.1,466.9 491.5,467C550.8,467.7 610.2,467.7 669.5,467C670.9,465.8 671.7,464.3 672,462.5C672.7,453.2 672.7,443.8 672,434.5C671.6,433.9 671.1,433.6 670.5,433.5C670.5,433.2 670.5,432.8 670.5,432.5C672.4,432.6 673.6,433.6 674,435.5C674.7,445.2 674.7,454.8 674,464.5C672.6,466.1 671.1,467.6 669.5,469C608.8,469.7 548.2,469.7 487.5,469C486.2,467.6 485,466.1 484,464.5C483.3,454.2 483.3,443.8 484,433.5C484.7,432.6 485.5,432 486.5,431.5Z"/>
<path android:fillColor="#bac3c7" android:pathData="M670.5,433.5C671.1,433.6 671.6,433.9 672,434.5C672.7,443.8 672.7,453.2 672,462.5C671.7,464.3 670.9,465.8 669.5,467C610.2,467.7 550.8,467.7 491.5,467C489.1,466.9 487.8,465.8 487.5,463.5C487.8,463.5 488.2,463.5 488.5,463.5C488.6,464.1 488.9,464.6 489.5,465C548.8,465.7 608.2,465.7 667.5,465C668.1,464.6 668.4,464.1 668.5,463.5C669.5,463.7 670.2,463.3 670.5,462.5C670.5,452.8 670.5,443.2 670.5,433.5Z"/>
<path android:fillColor="#a6aeb1" android:pathData="M488.5,463.5C548.5,463.5 608.5,463.5 668.5,463.5C668.4,464.1 668.1,464.6 667.5,465C608.2,465.7 548.8,465.7 489.5,465C488.9,464.6 488.6,464.1 488.5,463.5Z"/>
<path android:fillColor="#0f5b6c" android:pathData="M903.5,411.5C914.1,450.5 918.1,490.2 915.5,530.5C915.7,515.5 915.4,500.5 914.5,485.5C910.8,460.8 907.2,436.2 903.5,411.5Z"/>
<path android:fillColor="#9fa8ab" android:pathData="M435.5,447.5C436.4,448.6 436.8,449.9 436.5,451.5C436.8,461.5 436.5,471.5 435.5,481.5C434.7,481.8 434,482.3 433.5,483C399.5,483.7 365.5,483.7 331.5,483C331,482.3 330.3,481.8 329.5,481.5C329.2,471.7 329.5,462 330.5,452.5C330.2,461.7 330.5,470.7 331.5,479.5C366.2,479.5 400.8,479.5 435.5,479.5C435.5,469.2 435.5,458.8 435.5,448.5C435.5,448.2 435.5,447.8 435.5,447.5Z"/>
<path android:fillColor="#c2ccd0" android:pathData="M436.5,451.5C437.1,451.6 437.6,451.9 438,452.5C438.7,462.2 438.7,471.8 438,481.5C437.6,482.4 437.1,483.3 436.5,484C418.2,485.1 399.9,485.6 381.5,485.5C364.5,485.2 347.5,484.8 330.5,484.5C329.9,483.6 329.5,482.6 329.5,481.5C330.3,481.8 331,482.3 331.5,483C365.5,483.7 399.5,483.7 433.5,483C434,482.3 434.7,481.8 435.5,481.5C436.5,471.5 436.8,461.5 436.5,451.5Z"/>
<path android:fillColor="#142c33" android:pathData="M459.5,485.5C538.8,485.5 618.2,485.5 697.5,485.5C697.5,509.5 697.5,533.5 697.5,557.5C686.2,557.5 674.8,557.5 663.5,557.5C663.5,545.8 663.5,534.2 663.5,522.5C663.5,521.8 663.5,521.2 663.5,520.5C663.5,519.5 663.5,518.5 663.5,517.5C606.8,517.5 550.2,517.5 493.5,517.5C493.5,531.5 493.5,545.5 493.5,559.5C482.2,559.5 470.8,559.5 459.5,559.5C459.5,536.5 459.5,513.5 459.5,490.5C459.5,489.2 459.5,487.8 459.5,486.5C459.5,486.2 459.5,485.8 459.5,485.5Z"/>
<path android:fillColor="#8f9a9e" android:pathData="M459.5,485.5C499.2,484.8 539,484.5 579,484.5C619.2,484.5 659,485.2 698.5,486.5C698.8,510.7 698.5,534.7 697.5,558.5C686,558.8 674.7,558.5 663.5,557.5C674.8,557.5 686.2,557.5 697.5,557.5C697.5,533.5 697.5,509.5 697.5,485.5C618.2,485.5 538.8,485.5 459.5,485.5Z"/>
<path android:fillColor="#bac6ca" android:pathData="M838.5,412.5C841.3,419.5 843.4,426.9 845,434.5C854.7,478.4 855.8,522.4 848.5,566.5C851.3,530.5 851.1,494.5 848,458.5C846.2,447.9 843.5,437.6 840,427.5C839.5,422.5 839,417.5 838.5,412.5Z"/>
<path android:fillColor="#132c32" android:pathData="M331.5,507.5C366.2,507.5 400.8,507.5 435.5,507.5C435.5,517.8 435.5,528.2 435.5,538.5C400.8,538.5 366.2,538.5 331.5,538.5C331.5,528.2 331.5,517.8 331.5,507.5Z"/>
<path android:fillColor="#cbd6d9" android:pathData="M459.5,486.5C459.5,487.8 459.5,489.2 459.5,490.5C458.3,514.3 458.2,538.3 459,562.5C459.5,563 460,563.5 460.5,564C471.2,564.7 481.8,564.7 492.5,564C493.3,563.7 494,563.2 494.5,562.5C495.3,549.9 495.8,537.2 496,524.5C496.9,523.3 498.1,522.5 499.5,522C553.2,521.3 606.8,521.3 660.5,522C661,522.5 661.5,523 662,523.5C662.3,535.5 662.7,547.5 663,559.5C663.8,560.3 664.7,561.2 665.5,562C675.2,562.7 684.8,562.7 694.5,562C695.7,561 696.7,559.8 697.5,558.5C698.5,534.7 698.8,510.7 698.5,486.5C699.5,487 700.3,487.6 701,488.5C701.7,511.8 701.7,535.2 701,558.5C700.3,561.3 698.4,562.8 695.5,563C684.5,563.7 673.5,563.7 662.5,563C662,562.5 661.5,562 661,561.5C659.9,549.4 659,537.2 658.5,525C605.2,524.5 551.8,524.3 498.5,524.5C498.7,537.5 498.5,550.5 498,563.5C496.2,566.3 493.7,567.8 490.5,568C479.8,568.9 469.1,568.5 458.5,567C456.3,565.4 454.9,563.2 454.5,560.5C455.3,537.2 455.8,513.9 456,490.5C456.7,488.7 457.8,487.3 459.5,486.5Z"/>
<path android:fillColor="#aab3b7" android:pathData="M663.5,520.5C663.5,521.2 663.5,521.8 663.5,522.5C662.7,522.2 662,521.7 661.5,521C605.8,520.2 550.1,520.3 494.5,521.5C494.9,520.1 495.9,519.2 497.5,519C552.2,518.3 606.8,518.3 661.5,519C662,519.7 662.7,520.2 663.5,520.5Z"/>
<path android:fillColor="#9ca4a7" android:pathData="M663.5,520.5C662.7,520.2 662,519.7 661.5,519C606.8,518.3 552.2,518.3 497.5,519C495.9,519.2 494.9,520.1 494.5,521.5C494.8,534.3 494.5,547 493.5,559.5C493.5,545.5 493.5,531.5 493.5,517.5C550.2,517.5 606.8,517.5 663.5,517.5C663.5,518.5 663.5,519.5 663.5,520.5Z"/>
<path android:fillColor="#132c33" android:pathData="M629.5,538.5C629.9,541.8 628.5,544.1 625.5,545.5C624.2,582 624.2,618.3 625.5,654.5C625.7,658.6 627.7,661.4 631.5,663C641.5,663.7 651.5,663.7 661.5,663C665.1,661.1 667.6,658.3 669,654.5C671.2,646.6 673,638.6 674.5,630.5C675,629.1 675.6,627.8 676.5,626.5C686.4,629.9 696.4,632.9 706.5,635.5C707.1,636.4 707.4,637.4 707.5,638.5C704.9,648.7 702.4,659 700,669.5C698.8,673.6 697.3,677.6 695.5,681.5C691.1,688.5 684.8,692.9 676.5,694.5C655.5,695.5 634.5,695.8 613.5,695.5C600.6,694.3 593.1,687.3 591,674.5C590.5,630.5 590.3,586.5 590.5,542.5C590.5,541.2 590.5,539.8 590.5,538.5C596.1,537.2 602.1,536.5 608.5,536.5C614.5,536.7 620.5,536.8 626.5,537C627.7,537.2 628.7,537.7 629.5,538.5Z"/>
<path android:fillColor="#2b3e44" android:pathData="M331.5,507.5C331.5,517.8 331.5,528.2 331.5,538.5C366.2,538.5 400.8,538.5 435.5,538.5C435.2,539.3 434.5,539.7 433.5,539.5C399.8,539.5 366.2,539.5 332.5,539.5C330.5,531.2 329.9,522.6 330.5,513.5C330.5,512.2 330.5,510.8 330.5,509.5C330.3,508.5 330.7,507.8 331.5,507.5Z"/>
<path android:fillColor="#b3bcbf" android:pathData="M435.5,507.5C436.2,508.8 437.1,510.1 438,511.5C438.7,521.2 438.7,530.8 438,540.5C437.6,542.4 436.4,543.4 434.5,543.5C400.5,543.7 366.5,543.5 332.5,543C331.7,542.2 330.8,541.3 330,540.5C329.2,531.3 329.3,522.3 330.5,513.5C329.9,522.6 330.5,531.2 332.5,539.5C332.8,540.2 333.1,540.8 333.5,541.5C366.5,542.7 399.5,542.8 432.5,542C433.3,541.3 433.6,540.5 433.5,539.5C434.5,539.7 435.2,539.3 435.5,538.5C435.5,528.2 435.5,517.8 435.5,507.5Z"/>
<path android:fillColor="#a5aeb1" android:pathData="M332.5,539.5C366.2,539.5 399.8,539.5 433.5,539.5C433.6,540.5 433.3,541.3 432.5,542C399.5,542.8 366.5,542.7 333.5,541.5C333.1,540.8 332.8,540.2 332.5,539.5Z"/>
<path android:fillColor="#c7d1d5" android:pathData="M330.5,509.5C330.5,510.8 330.5,512.2 330.5,513.5C329.3,522.3 329.2,531.3 330,540.5C330.8,541.3 331.7,542.2 332.5,543C366.5,543.5 400.5,543.7 434.5,543.5C417.5,544.8 400.2,545.5 382.5,545.5C365.2,545.3 347.8,545.2 330.5,545C330,544.1 329.3,543.2 328.5,542.5C327.3,532.2 327.2,521.8 328,511.5C328.7,510.6 329.5,510 330.5,509.5Z"/>
<path android:fillColor="#cdd8dc" android:pathData="M794.5,326.5C797.6,329.6 799.9,333.3 801.5,337.5C802.7,338 803.9,338.7 805,339.5C812.4,356.2 820.3,372.7 828.5,389C831,396.2 833.5,403.3 836,410.5C836.7,411.4 837.5,412 838.5,412.5C839,417.5 839.5,422.5 840,427.5C843.5,437.6 846.2,447.9 848,458.5C851.1,494.5 851.3,530.5 848.5,566.5C842.5,607.2 829.2,645.2 808.5,680.5C810.4,673.7 812.6,667.1 815,660.5C820.3,649.7 824.9,638.7 829,627.5C862.9,523.2 851.6,424.2 795,330.5C794.5,329.2 794.3,327.9 794.5,326.5Z"/>
<path android:fillColor="#b4bdc0" android:pathData="M459.5,490.5C459.5,513.5 459.5,536.5 459.5,559.5C470.8,559.5 482.2,559.5 493.5,559.5C494.5,547 494.8,534.3 494.5,521.5C550.1,520.3 605.8,520.2 661.5,521C662,521.7 662.7,522.2 663.5,522.5C663.5,534.2 663.5,545.8 663.5,557.5C674.7,558.5 686,558.8 697.5,558.5C696.7,559.8 695.7,561 694.5,562C684.8,562.7 675.2,562.7 665.5,562C664.7,561.2 663.8,560.3 663,559.5C662.7,547.5 662.3,535.5 662,523.5C661.5,523 661,522.5 660.5,522C606.8,521.3 553.2,521.3 499.5,522C498.1,522.5 496.9,523.3 496,524.5C495.8,537.2 495.3,549.9 494.5,562.5C494,563.2 493.3,563.7 492.5,564C481.8,564.7 471.2,564.7 460.5,564C460,563.5 459.5,563 459,562.5C458.2,538.3 458.3,514.3 459.5,490.5Z"/>
<path android:fillColor="#becacd" android:pathData="M169.5,539.5C171.8,552.6 174,566 176,579.5C179.5,592.8 182.6,606.1 185.5,619.5C176.5,593.7 171.1,567 169.5,539.5Z"/>
<path android:fillColor="#ccd7db" android:pathData="M674.5,630.5C673,638.6 671.2,646.6 669,654.5C667.6,658.3 665.1,661.1 661.5,663C651.5,663.7 641.5,663.7 631.5,663C627.7,661.4 625.7,658.6 625.5,654.5C625.3,619.5 625.5,584.5 626,549.5C630.1,546.6 631.3,542.9 629.5,538.5C628.7,537.7 627.7,537.2 626.5,537C620.5,536.8 614.5,536.7 608.5,536.5C602.1,536.5 596.1,537.2 590.5,538.5C589.8,538.8 589.2,539.1 588.5,539.5C587.3,586.2 587.2,632.8 588,679.5C588.6,683.6 590.1,687.2 592.5,690.5C593.3,691.4 594.3,691.7 595.5,691.5C599.4,695.2 604.1,697.7 609.5,699C631.2,699.7 652.8,699.7 674.5,699C678.4,697.5 682.4,696.4 686.5,695.5C691,693.2 694.3,689.7 696.5,685C697.8,684.7 698.9,684.2 700,683.5C702.3,679.1 703.9,674.4 705,669.5C705.5,659.1 706.3,648.7 707.5,638.5C707.4,637.4 707.1,636.4 706.5,635.5C708,636.2 708.9,637.6 709,639.5C710.4,656.8 707.1,673.1 699,688.5C693.4,696.1 685.9,701 676.5,703C653.8,703.7 631.2,703.7 608.5,703C595.4,699.9 587.9,691.8 586,678.5C585.3,631.8 585.3,585.2 586,538.5C587.1,536.2 588.9,534.7 591.5,534C602.5,533.3 613.5,533.3 624.5,534C630.5,534.3 633.8,537.3 634.5,543C633.9,546.9 632.4,550.4 630,553.5C629,588.5 629.4,623.5 631,658.5C632.5,660 634.3,660.8 636.5,661C644.6,661.9 652.6,661.6 660.5,660C667.7,652.6 671.7,643.8 672.5,633.5C672.6,632 673.3,631 674.5,630.5Z"/>
<path android:fillColor="#aeb7ba" android:pathData="M590.5,542.5C590.3,586.5 590.5,630.5 591,674.5C593.1,687.3 600.6,694.3 613.5,695.5C633.7,696.7 654,696.8 674.5,696C675.4,695.7 676.1,695.2 676.5,694.5C684.8,692.9 691.1,688.5 695.5,681.5C694,687.1 691,691.8 686.5,695.5C682.4,696.4 678.4,697.5 674.5,699C652.8,699.7 631.2,699.7 609.5,699C604.1,697.7 599.4,695.2 595.5,691.5C594.5,687.4 592.7,683.8 590,680.5C589.2,634.3 589.3,588.3 590.5,542.5Z"/>
<path android:fillColor="#838d90" android:pathData="M676.5,694.5C676.1,695.2 675.4,695.7 674.5,696C654,696.8 633.7,696.7 613.5,695.5C634.5,695.8 655.5,695.5 676.5,694.5Z"/>
<path android:fillColor="#bdc7ca" android:pathData="M707.5,638.5C706.3,648.7 705.5,659.1 705,669.5C703.9,674.4 702.3,679.1 700,683.5C698.9,684.2 697.8,684.7 696.5,685C694.3,689.7 691,693.2 686.5,695.5C691,691.8 694,687.1 695.5,681.5C697.3,677.6 698.8,673.6 700,669.5C702.4,659 704.9,648.7 707.5,638.5Z"/>
<path android:fillColor="#aeb7bb" android:pathData="M629.5,538.5C631.3,542.9 630.1,546.6 626,549.5C625.5,584.5 625.3,619.5 625.5,654.5C624.2,618.3 624.2,582 625.5,545.5C628.5,544.1 629.9,541.8 629.5,538.5Z"/>
<path android:fillColor="#c3cdd1" android:pathData="M590.5,538.5C590.5,539.8 590.5,541.2 590.5,542.5C589.3,588.3 589.2,634.3 590,680.5C592.7,683.8 594.5,687.4 595.5,691.5C594.3,691.7 593.3,691.4 592.5,690.5C590.1,687.2 588.6,683.6 588,679.5C587.2,632.8 587.3,586.2 588.5,539.5C589.2,539.1 589.8,538.8 590.5,538.5Z"/>
<path android:fillColor="#cad6db" android:pathData="M808.5,680.5C796.6,702.3 782.3,722.3 765.5,740.5C765.5,738.8 765.5,737.2 765.5,735.5C778.8,720.7 790.6,704.3 801,686.5C803.9,684.7 806.4,682.7 808.5,680.5Z"/>
<path android:fillColor="#d0dbdf" android:pathData="M474.5,666.5C466.2,669.7 457.6,671.4 448.5,671.5C447.8,671.9 447.3,672.6 447,673.5C446.7,673.2 446.3,672.8 446,672.5C445.7,640.8 445.3,609.2 445,577.5C444.8,576.3 444.3,575.3 443.5,574.5C443.5,572.8 443.5,571.2 443.5,569.5C403.3,569.2 363.3,569.5 323.5,570.5C323.5,571.5 323.5,572.5 323.5,573.5C322.9,573.6 322.4,573.9 322,574.5C321.3,619.5 321.3,664.5 322,709.5C322.8,710.3 323.7,711.2 324.5,712C333.8,714 343.2,714 352.5,712C353.9,710.8 354.7,709.3 355,707.5C355.3,703.8 355.7,700.2 356,696.5C356.4,695.6 356.9,694.8 357.5,694C374.2,693.3 390.8,693.3 407.5,694C408.4,694.4 409.3,694.9 410,695.5C409.7,700.1 411.2,703.9 414.5,707C423.2,707.7 431.8,707.7 440.5,707C442.3,706.2 443.8,705 445,703.5C446,697.9 446.9,692.2 447.5,686.5C448.7,687.3 449.7,688.3 450.5,689.5C452.8,693.9 454.6,698.5 456,703.5C459.1,710.8 464.6,713.3 472.5,711C481.2,708.8 489.2,705.3 496.5,700.5C502.5,698 507.8,694.5 512.5,690C513.8,689.5 515.1,689.3 516.5,689.5C518.3,688.9 519.9,688.1 521.5,687C527.7,679.8 534.2,673 541,666.5C552.5,646.5 558.9,624.8 560,601.5C560.3,584.5 560.7,567.5 561,550.5C563.7,548.3 564.8,545.5 564.5,542C564.4,540 563.8,538.5 562.5,537.5C560.9,537 559.2,536.6 557.5,536.5C546.3,535.5 535,535.2 523.5,535.5C523.5,536.5 523.5,537.5 523.5,538.5C522.2,539.1 521.4,540.1 521,541.5C520.8,565.5 520.2,589.5 519,613.5C517.2,619.1 515.7,624.8 514.5,630.5C508.7,644 499.4,654.3 486.5,661.5C486.6,660.5 486.3,659.7 485.5,659C499.8,650.4 509.3,637.9 514,621.5C515.8,614.9 517.2,608.3 518,601.5C518.3,582.2 518.7,562.8 519,543.5C518.8,538.6 521,535.5 525.5,534C536.5,533.3 547.5,533.3 558.5,534C569.5,537.3 571.3,543.5 564,552.5C563.9,572.5 563.2,592.5 562,612.5C554.2,670.8 521.4,705.1 463.5,715.5C461,715.2 458.8,714.2 457,712.5C454.1,706.2 451.5,699.9 449,693.5C448.2,697.1 447.6,700.8 447,704.5C445.2,707.3 442.7,708.8 439.5,709C430.4,709.8 421.4,709.5 412.5,708C411.7,707.2 410.8,706.3 410,705.5C409.8,702.2 409.3,699.1 408.5,696C391.8,695.3 375.2,695.3 358.5,696C357.7,701.1 357.2,706.3 357,711.5C356.5,713.3 355.3,714.5 353.5,715C343.5,715.7 333.5,715.7 323.5,715C322.1,714.5 320.9,713.7 320,712.5C318.3,666.3 318,619.9 319,573.5C319.9,570.2 322.1,568.4 325.5,568C365.2,567 404.9,567.4 444.5,569C445.7,569.9 446.5,571.1 447,572.5C447.5,605.2 447.7,637.8 447.5,670.5C456.5,669.2 465.5,667.8 474.5,666.5Z"/>
<path android:fillColor="#c5d0d3" android:pathData="M323.5,573.5C323.5,574.2 323.5,574.8 323.5,575.5C322.5,619.7 322.2,664 322.5,708.5C331.6,710.5 341,711 350.5,710C351.4,709.3 352,708.5 352.5,707.5C353.5,703 353.8,698.4 353.5,693.5C354.3,693.2 355,692.7 355.5,692C373.5,691.3 391.5,691.3 409.5,692C410,692.7 410.7,693.2 411.5,693.5C412.2,696.3 412.8,699 413.5,701.5C413.8,701.5 414.2,701.5 414.5,701.5C414.4,702.5 414.7,703.3 415.5,704C424.9,704.9 434.2,704.6 443.5,703C444.1,693.9 444.9,684.7 446,675.5C446.9,677.7 448,679.7 449.5,681.5C450.5,684.1 450.8,686.8 450.5,689.5C449.7,688.3 448.7,687.3 447.5,686.5C446.9,692.2 446,697.9 445,703.5C443.8,705 442.3,706.2 440.5,707C431.8,707.7 423.2,707.7 414.5,707C411.2,703.9 409.7,700.1 410,695.5C409.3,694.9 408.4,694.4 407.5,694C390.8,693.3 374.2,693.3 357.5,694C356.9,694.8 356.4,695.6 356,696.5C355.7,700.2 355.3,703.8 355,707.5C354.7,709.3 353.9,710.8 352.5,712C343.2,714 333.8,714 324.5,712C323.7,711.2 322.8,710.3 322,709.5C321.3,664.5 321.3,619.5 322,574.5C322.4,573.9 322.9,573.6 323.5,573.5Z"/>
<path android:fillColor="#737e82" android:pathData="M413.5,687.5C414.5,691.6 414.8,696 414.5,700.5C423.8,700.5 433.2,700.5 442.5,700.5C433.3,701.5 424,701.8 414.5,701.5C414.2,701.5 413.8,701.5 413.5,701.5C413.5,698.5 413.5,695.5 413.5,692.5C413.5,690.8 413.5,689.2 413.5,687.5Z"/>
<path android:fillColor="#b6bfc2" android:pathData="M413.5,692.5C413.5,695.5 413.5,698.5 413.5,701.5C412.8,699 412.2,696.3 411.5,693.5C410.7,693.2 410,692.7 409.5,692C391.5,691.3 373.5,691.3 355.5,692C355,692.7 354.3,693.2 353.5,693.5C353.5,692.2 353.5,690.8 353.5,689.5C372.2,689.3 390.8,689.5 409.5,690C411,690.7 412.3,691.5 413.5,692.5Z"/>
<path android:fillColor="#a2aaae" android:pathData="M413.5,687.5C413.5,689.2 413.5,690.8 413.5,692.5C412.3,691.5 411,690.7 409.5,690C390.8,689.5 372.2,689.3 353.5,689.5C353.5,690.8 353.5,692.2 353.5,693.5C353.8,698.4 353.5,703 352.5,707.5C352.5,700.8 352.5,694.2 352.5,687.5C372.8,687.5 393.2,687.5 413.5,687.5Z"/>
<path android:fillColor="#a5aeb1" android:pathData="M547.5,645.5C546.6,651.6 544.5,657.3 541,662.5C533.3,672 525.1,681 516.5,689.5C515.1,689.3 513.8,689.5 512.5,690C507.8,694.5 502.5,698 496.5,700.5C486.3,703.7 475.9,706.7 465.5,709.5C461.2,709.8 459.2,707.8 459.5,703.5C460.6,704.6 461.4,705.9 462,707.5C490.8,703.1 514.8,690.1 534,668.5C539.2,661.2 543.7,653.5 547.5,645.5Z"/>
<path android:fillColor="#b8c2c6" android:pathData="M562.5,537.5C563.8,538.5 564.4,540 564.5,542C564.8,545.5 563.7,548.3 561,550.5C560.7,567.5 560.3,584.5 560,601.5C558.9,624.8 552.5,646.5 541,666.5C534.2,673 527.7,679.8 521.5,687C519.9,688.1 518.3,688.9 516.5,689.5C525.1,681 533.3,672 541,662.5C544.5,657.3 546.6,651.6 547.5,645.5C553,630.3 556.5,614.6 558,598.5C558.3,580.8 558.7,563.2 559,545.5C562.5,543.6 563.7,540.9 562.5,537.5Z"/>
<path android:fillColor="#b4bdc0" android:pathData="M323.5,575.5C323.5,619.5 323.5,663.5 323.5,707.5C333.2,707.5 342.8,707.5 352.5,707.5C352,708.5 351.4,709.3 350.5,710C341,711 331.6,710.5 322.5,708.5C322.2,664 322.5,619.7 323.5,575.5Z"/>
<path android:fillColor="#bcc5c9" android:pathData="M443.5,574.5C444.3,575.3 444.8,576.3 445,577.5C445.3,609.2 445.7,640.8 446,672.5C446.3,672.8 446.7,673.2 447,673.5C447.3,672.6 447.8,671.9 448.5,671.5C452.2,682.2 455.8,692.8 459.5,703.5C459.2,707.8 461.2,709.8 465.5,709.5C475.9,706.7 486.3,703.7 496.5,700.5C489.2,705.3 481.2,708.8 472.5,711C464.6,713.3 459.1,710.8 456,703.5C454.6,698.5 452.8,693.9 450.5,689.5C450.8,686.8 450.5,684.1 449.5,681.5C448,679.7 446.9,677.7 446,675.5C444.9,684.7 444.1,693.9 443.5,703C434.2,704.6 424.9,704.9 415.5,704C414.7,703.3 414.4,702.5 414.5,701.5C424,701.8 433.3,701.5 442.5,700.5C443.5,658.7 443.8,616.7 443.5,574.5Z"/>
<path android:fillColor="#8e999d" android:pathData="M323.5,570.5C363.3,569.5 403.3,569.2 443.5,569.5C443.5,571.2 443.5,572.8 443.5,574.5C443.8,616.7 443.5,658.7 442.5,700.5C442.5,657.2 442.5,613.8 442.5,570.5C402.8,570.5 363.2,570.5 323.5,570.5Z"/>
<path android:fillColor="#142c33" android:pathData="M323.5,570.5C363.2,570.5 402.8,570.5 442.5,570.5C442.5,613.8 442.5,657.2 442.5,700.5C433.2,700.5 423.8,700.5 414.5,700.5C414.8,696 414.5,691.6 413.5,687.5C393.2,687.5 372.8,687.5 352.5,687.5C352.5,694.2 352.5,700.8 352.5,707.5C342.8,707.5 333.2,707.5 323.5,707.5C323.5,663.5 323.5,619.5 323.5,575.5C323.5,574.8 323.5,574.2 323.5,573.5C323.5,572.5 323.5,571.5 323.5,570.5Z"/>
<path android:fillColor="#728084" android:pathData="M413.5,597.5C414.5,618.3 414.8,639.3 414.5,660.5C393.7,660.8 373,660.5 352.5,659.5C352.8,659.5 353.2,659.5 353.5,659.5C354.2,659.5 354.8,659.5 355.5,659.5C374.5,659.5 393.5,659.5 412.5,659.5C413.5,640 413.8,620.3 413.5,600.5C413.5,599.5 413.5,598.5 413.5,597.5Z"/>
<path android:fillColor="#bcc6ca" android:pathData="M413.5,600.5C413.8,620.3 413.5,640 412.5,659.5C412.8,640.8 412.5,622.2 411.5,603.5C393.2,602.3 374.9,602.2 356.5,603C356,603.5 355.5,604 355,604.5C354.2,622.9 354.3,641.2 355.5,659.5C354.8,659.5 354.2,659.5 353.5,659.5C353.2,639.9 353.5,620.4 354.5,601C364.4,599.9 374.4,599.4 384.5,599.5C394.2,599.5 403.9,599.9 413.5,600.5Z"/>
<path android:fillColor="#cfdade" android:pathData="M412.5,659.5C393.5,659.5 374.5,659.5 355.5,659.5C354.3,641.2 354.2,622.9 355,604.5C355.5,604 356,603.5 356.5,603C374.9,602.2 393.2,602.3 411.5,603.5C412.5,622.2 412.8,640.8 412.5,659.5Z"/>
<path android:fillColor="#d7e2e6" android:pathData="M358.5,605.5C375.2,605.5 391.8,605.5 408.5,605.5C408.5,622.8 408.5,640.2 408.5,657.5C391.6,657.8 374.8,657.5 358,656.5C357.2,639.4 357.3,622.4 358.5,605.5Z"/>
<path android:fillColor="#a0a8ab" android:pathData="M413.5,597.5C413.5,598.5 413.5,599.5 413.5,600.5C403.9,599.9 394.2,599.5 384.5,599.5C374.4,599.4 364.4,599.9 354.5,601C353.5,620.4 353.2,639.9 353.5,659.5C353.2,659.5 352.8,659.5 352.5,659.5C352.5,638.8 352.5,618.2 352.5,597.5C372.8,597.5 393.2,597.5 413.5,597.5Z"/>
<path android:fillColor="#8e999d" android:pathData="M557.5,536.5C546.5,536.5 535.5,536.5 524.5,536.5C524.8,555 524.5,573.3 523.5,591.5C523.5,573.8 523.5,556.2 523.5,538.5C523.5,537.5 523.5,536.5 523.5,535.5C535,535.2 546.3,535.5 557.5,536.5Z"/>
<path android:fillColor="#c4ced2" android:pathData="M523.5,538.5C523.5,556.2 523.5,573.8 523.5,591.5C523.6,605.4 520.6,618.4 514.5,630.5C515.7,624.8 517.2,619.1 519,613.5C520.2,589.5 520.8,565.5 521,541.5C521.4,540.1 522.2,539.1 523.5,538.5Z"/>
<path android:fillColor="#142c33" android:pathData="M557.5,536.5C559.2,536.6 560.9,537 562.5,537.5C563.7,540.9 562.5,543.6 559,545.5C558.7,563.2 558.3,580.8 558,598.5C556.5,614.6 553,630.3 547.5,645.5C543.7,653.5 539.2,661.2 534,668.5C514.8,690.1 490.8,703.1 462,707.5C461.4,705.9 460.6,704.6 459.5,703.5C455.8,692.8 452.2,682.2 448.5,671.5C457.6,671.4 466.2,669.7 474.5,666.5C478.9,665.6 482.9,663.9 486.5,661.5C499.4,654.3 508.7,644 514.5,630.5C520.6,618.4 523.6,605.4 523.5,591.5C524.5,573.3 524.8,555 524.5,536.5C535.5,536.5 546.5,536.5 557.5,536.5Z"/>
<path android:fillColor="#cad8dc" android:pathData="M601.5,841.5C553.1,855.4 504.1,858.1 454.5,849.5C488.9,853.4 523.2,853.2 557.5,849C572.2,846.4 586.8,843.9 601.5,841.5Z"/>
</vector>
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
android:insetTop="@dimen/abc_edit_text_inset_top_material"
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
<selector>
<!--
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
-->
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
</selector>
</inset>
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file was created by '@expo/configure-splash-screen' and some of it's content shouldn't be modified by hand
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/splashscreen_background"/>
</layer-list>
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@color/splashscreen_background"
android:gravity="center"
android:orientation="vertical"
android:scaleType="centerCrop"
android:navigationBarColor="#333333"
tools:context=".MainActivity">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="130dp"
app:lottie_rawRes="@raw/loading"
app:lottie_autoPlay="true"
app:lottie_loop="false"
/>
</LinearLayout>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources>
<color name="iconBackground">#FFFFFF</color>
<color name="splashscreen_background">#1F2024</color>
<color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#202125</color>
<color name="navigationBarColor">#202125</color>
<color name="notification_icon_color">#00adb5</color>
</resources>
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources>
<string name="app_name">LNReader</string>
</resources>
@@ -1,33 +0,0 @@
<?xml
version="1.0"
encoding="UTF-8"
standalone="yes"
?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
<item name="colorControlActivated">#2596be</item>
</style>
<style name="SplashScreenTheme" parent="SplashScreen_SplashTheme">
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
</style>
<style name="Theme.App.SplashScreen" parent="AppTheme">
<!-- Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually -->
<!-- Customize your splash screen theme here -->
<item name="android:windowSplashScreenBackground" tools:targetApi="s">@color/colorPrimaryDark</item>
<item name="android:windowSplashScreenAnimatedIcon" tools:targetApi="s" >@drawable/invisible</item>
<item name="android:windowSplashScreenAnimationDuration" tools:targetApi="s">1000</item>
<!-- <item name="icon">@drawable/no_icon</item> -->
<item name="android:windowBackground">@drawable/splashscreen</item>
</style>
</resources>
@@ -1,20 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.rajarsheechatterjee.LNReader;
import android.content.Context;
import com.facebook.react.ReactInstanceManager;
/**
* Class responsible of loading Flipper inside your React Native application. This is the release
* flavor of it so it's empty as we don't want to load Flipper.
*/
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
// Do nothing as we don't want to initialize Flipper on Release.
}
}
-23
View File
@@ -1,23 +0,0 @@
import org.apache.tools.ant.taskdefs.condition.Os
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "33.0.0"
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
kotlinVersion = "1.8.10"
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle")
classpath("com.facebook.react:react-native-gradle-plugin")
}
}
-51
View File
@@ -1,51 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using
# ./gradlew <task> -PreactNativeArchitectures=x86_64
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true
# Enable GIF support in React Native images (~200 B increase)
expo.gif.enabled=false
# Enable webp support in React Native images (~85 KB increase)
expo.webp.enabled=false
# Enable animated webp support (~3.4 MB increase)
# Disabled by default because iOS doesn't support animated webp
expo.webp.animated=false
Binary file not shown.
-6
View File
@@ -1,6 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-244
View File
@@ -1,244 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
-92
View File
@@ -1,92 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
-9
View File
@@ -1,9 +0,0 @@
rootProject.name = 'LNReader'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
includeBuild('../node_modules/@react-native/gradle-plugin')
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle")
useExpoModules()
+70 -2
View File
@@ -1,10 +1,78 @@
{
"expo": {
"version": "2.1.1",
"name": "LNReader",
"slug": "LNReader",
"scheme": "lnreader",
"icon": "./assets/native/ic_launcher.png",
"plugins": [
"expo-localization"
]
"./plugins/withAndroidCustomizations",
[
"expo-splash-screen",
{
"image": "./assets/native/ic_launcher_round.png",
"backgroundColor": "#1D1B20",
"resizeMode": "contain",
"dark": {
"image": "./assets/native/ic_launcher_round.png",
"backgroundColor": "#000000"
},
"android": {
"image": "./assets/native/splash_icon.png",
"backgroundColor": "#132C33",
"imageWidth": 288,
"resizeMode": "native",
"dark": {
"image": "./assets/native/splash_icon.png",
"backgroundColor": "#132C33"
}
}
}
],
"expo-localization",
"react-native-edge-to-edge",
"expo-web-browser",
"./plugins/withReaderAssets",
[
"expo-build-properties",
{
"android": {
"usesCleartextTraffic": true,
"manifestApplicationAttributes": {
"android:largeHeap": "true",
"android:allowBackup": "true"
}
}
}
]
],
"android": {
"versionCode": 4196353,
"package": "com.rajarsheechatterjee.LNReader",
"adaptiveIcon": {
"foregroundImage": "./assets/native/ic_launcher_foreground.png",
"backgroundImage": "./assets/native/ic_launcher_background.png",
"monochromeImage": "./assets/native/ic_launcher_monochrome.png"
},
"permissions": [
"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION",
"android.permission.WAKE_LOCK",
"android.permission.FOREGROUND_SERVICE",
"android.permission.FOREGROUND_SERVICE_DATA_SYNC",
"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK",
"android.permission.POST_NOTIFICATIONS"
]
},
"extra": {
"eas": {
"projectId": "3a63596f-e80c-4342-96b7-a8ac48910489"
}
},
"ios": {
"bundleIdentifier": "com.rajarsheechatterjee.LNReader",
"infoPlist": {
"UIBackgroundModes": ["audio"]
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More