Compare commits

...

45 Commits

Author SHA1 Message Date
Rajarshee Chatterjee 49e4d78c92 chore: Update translations (#569)
* 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 (Turkish)
[ci skip]

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

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

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

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

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

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

* New translations strings.json (Russian)
[ci skip]
2023-04-06 21:54:50 +05:30
r1di c88b2e5d2a feat: extending Volume button feature, by scroll amount (#587)
Closes #447 

* Main
Added volume button scroll amount

* Volume button scroll amount

---------

Co-authored-by: Ridi <Ridi>
2023-04-06 20:42:36 +05:30
Rider21 be2dab5b5d feat: Add New Sources (#583)
* fix

* Update ranobelib.js

* fix

* test

* fix

* fix path

* test

* added sources

* added ALL sources on novel.tl

* add tags

* Update NovelTlScraper.js

* fix
2023-03-30 13:19:00 +05:30
Jaydeep Patel 2d986f263d feat: Add custom Javascript (Closes #572) (#575)
feat: Add custom Javascript
2023-03-11 11:52:16 +05:30
Rajarshee Chatterjee 684463754c chore: Update translations (#546)
* New translations strings.json (Vietnamese)
[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 (Indonesian)
[ci skip]

* New translations strings.json (Hindi)
[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 (Indonesian)
[ci skip]

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

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

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

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

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

* New translations strings.json (Vietnamese)
[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 (Indonesian)
[ci skip]

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

* New translations strings.json (Vietnamese)
[ci skip]
2023-03-05 12:08:28 +05:30
Rajarshee Chatterjee 6879d77ef1 feat: Add Last Updated Sort (Closes #482) 2023-03-05 11:56:02 +05:30
Rajarshee Chatterjee 41ed198a1d refactor: Remove totalPages 2023-03-05 11:39:52 +05:30
Rajarshee Chatterjee 550e116be5 feat: Add Chapter Name Sort (Closes #525) 2023-03-05 11:12:43 +05:30
Rajarshee Chatterjee 1c21c180ab refactor: Tweak Updates Card 2023-03-05 11:08:53 +05:30
nyagami 08860c0715 fix: Minor Fixes and Optimizations (#566)
Closes #557
2023-03-05 09:43:00 +05:30
K1ngfish3r d65170c85a Minor: prerequisites/build cmd to CONTRIBUTING.md (#565)
* Minor: prerequisites/build cmd to CONTRIBUTING.md

* switch npm start under developing section

* link CONTRIBUTING.md in README.md

* make clearer
2023-03-01 14:30:39 +05:30
Patrick Loser cf19eb6892 feat: Collapsible Updates and Downloads (#562)
* implemented extendable list for updates

* finished creating extendable list (buggy deleteChapter)

* fixed downloadbutton bug

* fixed ts problems

* set navigatetonovel to onLongPress

* removed

* key fixes

* implemented extendable list for updates

* finished creating extendable list (buggy deleteChapter)

* fixed downloadbutton bug

* fixed ts problems

* set navigatetonovel to onLongPress

* removed

* key fixes

* translation strings

* single chapter + style updates

* removed test method
2023-03-01 14:29:48 +05:30
nyagami 3289f357d9 refactor: Downloading Images Logic (#554)
* default language for browse source.
To test this. please clear all all app data

* fix: for some reason, locale from expo is uncorrect

* bypass some sites with referer in header.

* update logic for loading img on reader.
some sources should have headers when fetch image data

* minor fix

* fix warning: 2 childs have the same key

* fix(download): Download chapter image fixes

- Saving images in binary form, not utf-8 base64 so that gallery etc can see them
- Downloads work with new WebViewReader's image loading features
- Added loading icons when images are loading asynchronusely

* fix(dq.ts): DownloadQueries type

* fix(img): remove delayed-src from downloaded imgs

* fix(download): Fix promise rejection and css issue

- Fix promise rejection when trying to overwrite image file (shouldn't happen in production, but it miraculously did in dev)
- Fix CSS class 'loadIcon' not being applied to downladed chapter image loader icons and loader spinners not spinning
- Made loader spinners smaller. Now they look "a bit better" (according to my friend whom I showed the spinners)

* fix(loadimg): Change loadImage to smaller one

* fix(loadImg): change the class to load-img

* minor fix

* fix hako source: popular novels and novel cover

* update sendHeight logic

---------

Co-authored-by: SkillGG <skilletssgames@gmail.com>
2023-02-26 13:15:13 +05:30
K1ngfish3r fe2b2524a7 feat(source): Add Pawread (#561)
Closes LNReader/lnreader-sources#439
2023-02-23 07:40:27 +05:30
Patrick Loser 2e4ff916aa fix(source): Foxaholic (#563) 2023-02-23 07:38:38 +05:30
K1ngfish3r b40e9331d7 fix(source): SakuraNovel (#560)
Closes LNReader/lnreader-sources#456
2023-02-22 10:05:45 +05:30
Rajarshee Chatterjee 84a2561c41 Minor Fixes 2023-02-18 14:02:04 +05:30
K1ngfish3r 929bd2c97d feat(source): Add New multiSrc WQMangaStreamScraper & PandaMtl (#552)
Closes #LNReader/lnreader-sources#453
2023-02-18 09:00:20 +05:30
Rajarshee Chatterjee 9028a82318 Minor Fix 2023-02-17 23:22:17 +05:30
Rajarshee Chatterjee c6c41a2d1b Cleanup 2023-02-17 22:59:20 +05:30
Nyagami 9a0c08e7b8 refactor: Remove Swipe Margins (#553)
* refactor swipeGesture, remove swipeMargin

* trying to resolve conflict

* again :(

* cleanup
2023-02-17 22:21:17 +05:30
Patrick Loser 0abdff25eb feat: Add Settings and Translations (#548)
* started setting repositioning

* started setting repositioning

* added novel badges and sort to general + some js to ts

* translations General

* readersettingsscreen + fix font style selection in Bottomsheet

* started setting repositioning

* added novel badges and sort to general + some js to ts

* translations General

* readersettingsscreen + fix font style selection in Bottomsheet

* update

* ts map

* small fix
2023-02-17 21:39:30 +05:30
Patrick Loser f238b20f6d fix: Cleanup Sources (#545)
* removed Asada Translation (dead)

* update boxnovel.online to cratenovel

* fixed Daonovel

* updated fastnovel.net to fastnovel.org

* removed JPMTL (dead)

* removed Kisslightnovels (dead)

* removed Asada Translation (dead)

* update boxnovel.online to cratenovel

* fixed Daonovel

* updated fastnovel.net to fastnovel.org

* removed JPMTL (dead)

* removed Kisslightnovels (dead)

* update latestnovel to lightnovelfull (Doesn't work since there are multiple pages and can't find source API)

* fixed LightNovelReader search and metadata

* added ts-ignore to sourceManager

* removed LightNovelsHub (dead)

* fixed lnmtl metadata

* update mtnovel to novelfull.net

* fixed MTL-Novel

* removed MTL-Corner (dead)

* fixed mylovenovel

* removed NovelCake (dead)

* removed NovelOwl (is lightnovelfull.com)

* removed NovelPassion (dead)

* removed NovelsCafe (dead)

* removed NovelsRock (dead)

* removed noveltrench (dead)

* fixed novelupdatescc partly (can't open Novel)

* fixed OnlyMTL

* tried fix still broken but lists some novels

* removed SkyNovel (dead)

* changed scraping page of wuxiacity

* removed wuxiaworldcloud (dead)

* fixed wuxiaworld.site

* removed wuxiaworldsite (broken website)

* removed Asada Translation (dead)

* update boxnovel.online to cratenovel

* fixed Daonovel

* updated fastnovel.net to fastnovel.org

* removed JPMTL (dead)

* removed Kisslightnovels (dead)

* update latestnovel to lightnovelfull (Doesn't work since there are multiple pages and can't find source API)

* fixed LightNovelReader search and metadata

* added ts-ignore to sourceManager

* removed LightNovelsHub (dead)

* fixed lnmtl metadata

* update mtnovel to novelfull.net

* fixed MTL-Novel

* removed MTL-Corner (dead)

* fixed mylovenovel

* removed NovelCake (dead)

* removed NovelOwl (is lightnovelfull.com)

* removed NovelPassion (dead)

* removed NovelsCafe (dead)

* removed NovelsRock (dead)

* removed noveltrench (dead)

* fixed novelupdatescc partly (can't open Novel)

* fixed OnlyMTL

* tried fix still broken but lists some novels

* removed SkyNovel (dead)

* changed scraping page of wuxiacity

* removed wuxiaworldcloud (dead)

* fixed wuxiaworld.site

* removed wuxiaworldsite (broken website)

* fixed errors

* removed readfreenovel (without js list only kidsbooks and crashes the app)

* small change
2023-02-17 16:40:05 +05:30
Nyagami cc9022cf12 fix: App Crashing when Deleting Downloaded Chapter (#549)
* fix: app crashes when deleting downloaded chapter

* fix lint
2023-02-17 09:23:05 +05:30
Rajarshee Chatterjee fbadd41733 Minor Fix 2023-02-17 09:19:57 +05:30
Rajarshee Chatterjee c30b884428 refactor: Replace lodash with lodash-es 2023-02-17 09:17:28 +05:30
dependabot[bot] 8fc9e04d97 chore(deps): bump @sideway/formula from 3.0.0 to 3.0.1 (#532)
Bumps [@sideway/formula](https://github.com/sideway/formula) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/sideway/formula/releases)
- [Commits](https://github.com/sideway/formula/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: "@sideway/formula"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 09:00:44 +05:30
dependabot[bot] 84bc83b7a2 chore(deps): bump ua-parser-js from 0.7.31 to 0.7.33 (#519)
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.31 to 0.7.33.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Changelog](https://github.com/faisalman/ua-parser-js/blob/master/changelog.md)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.31...0.7.33)

---
updated-dependencies:
- dependency-name: ua-parser-js
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 09:00:36 +05:30
dependabot[bot] 2bb0c28591 chore(deps): bump minimatch from 3.0.4 to 3.1.2 (#469)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 09:00:09 +05:30
dependabot[bot] 52ae111e98 chore(deps): bump @xmldom/xmldom from 0.7.5 to 0.7.9 (#491)
Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.7.5 to 0.7.9.
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.7.5...0.7.9)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 08:59:51 +05:30
dependabot[bot] ecd383b586 chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#490)
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 08:59:41 +05:30
Rajarshee Chatterjee 606b2c8f12 Cleanup 2023-02-17 08:55:36 +05:30
Nyagami d9bbae444a refactor: Remove Text Reader (#538)
* auto scroll using saved position.
not using currentScroll cause it maybe baka sometimes

* merge 2 navigate chapter function

* merge text and web scroll. enable scrollToTop for web

* seekbar display better after sliding

* minor fix

* trying to remove wvUseNewSwipes var

* Starting remove TextView.
Firstly, always use WebView

* remove useWebViewForChapter var

* remove TextView related thing

* scroll to save position when layout on

* always scroll with mooth mode

* no need scrollview

* move volume scroll effect

* complete the removal

* Better logic for volumeButton.
change state name

* also rename showsipemargins state

* drawerSwipe doesnt support webview.
So maybe re-implement if it supports one day

* update logic for autoScroll

* wrap useCallback for saveProgress

* only position state is enough

* remove reset progree in mark as read action

* mini fix for volumebutton. rename state variables

* fix bug on volumeButton.
2023-02-17 05:08:16 +05:30
K1ngfish3r 2e9a7adc2c fix(source): Add Author and Status for WPMangaStreamScraper (#547)
Closes LNReader/lnreader-sources#444
2023-02-17 04:57:59 +05:30
K1ngfish3r 63909bf52e fix(source): Update WPMangaStreamScraper's Cover Selector (#543)
* fixes images on noblemtl

* fix images on noblemtl

* fix ESLint errors
2023-02-15 23:18:58 +05:30
Vigneshwar M 8fe1dacab0 feat(source): Add Agitoon (#542)
* add: kr sources

* add: latest/popular novels

* bug: parseNovelAndChapters()

* fix: parse parseNovelAndChapters bug and add: parseChapter

* add: search feature

* add: popular gets the popular list of the current day; refactor

* fix: 'chapterId doesnt exist'

* fix: reverted back the package-lock.json file

---------

Co-authored-by: Vigneshwar M <vigneshwarm@Vigneshwars-MacBook-Pro.local>
2023-02-15 22:39:20 +05:30
Rajarshee Chatterjee a193656cb2 Update Crowdin configuration file 2023-02-15 09:53:55 +05:30
Rajarshee Chatterjee ed23040287 chore: Update Translation Locales 2023-02-15 09:48:11 +05:30
Rajarshee Chatterjee c9d834a2f0 chore: Update translations (#521)
* New translations strings.json (Vietnamese)
[ci skip]

* New translations strings.json (Portuguese, Brazilian)
[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 (Indonesian)
[ci skip]

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

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

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

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

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

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

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

* New translations strings.json (Chinese Simplified)
[ci skip]
2023-02-14 15:21:32 +05:30
Nyagami 7faabc5d9c fix: Update Seekbar Position Logic (#535)
* fix slider displays uncorrectly, rearrange seekbar props

* Removal contensize variable, using math to solve.
Also need percen of device height as float.
Format readerscreen.js easilier to read (at least for me :>)

* set loadding when "scroll to" is unnecessary

* enable scroll even on loading (no making re-rerender)

* add attributes to chapter element for custom css

* shorten variable name. remove unuse function

* seekbar display more correctly for webview.
removal duplicate var

* trying to removal webViewScroll state

* Seekbar should work properly.
Need to implement save position.
And sync TextView and WebView

* just rearrange props to make easier for handling

* using state currentScroll instead of scrollPercentage

* share offsetY between text and web view.
Still issue when turn back from web to text. It cant scroll back.

* this should work well, unless users touch so fast

* clean up code

* use Promise.all instead

* the same logic but shoten code

* not saving croll when closing layout
2023-02-14 15:19:43 +05:30
Rajarshee Chatterjee 330abf25d7 fix(source): Add reverseChapters Option in WPMangaStreamScraper 2023-02-14 15:15:51 +05:30
Rajarshee Chatterjee d75ef3f444 feat(source): Add NobleMtl
Closes LNReader/lnreader-sources #409
2023-02-13 09:42:49 +05:30
Rajarshee Chatterjee 6e665dabf4 fix(source): Add sourceId in WPMangaStreamScraper 2023-02-13 09:42:49 +05:30
Nyagami 12b83f4de7 fix: Simplify TTS Logic to Prevent Freezing the App (#530)
* simply tts to prevent it freezes the app

* ensure tts uses the chapter text. correct spelling
2023-02-09 13:07:43 +05:30
Rajarshee Chatterjee 518f73f6fd Update README.md 2023-01-31 07:49:45 +05:30
328 changed files with 18001 additions and 12043 deletions
+6 -3
View File
@@ -8,19 +8,22 @@ jobs:
name: Build App
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install Dependencies
run: |
npm install --legacy-peer-deps
- name: Cache Gradle Wrapper
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle Dependencies
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-caches-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
+17 -7
View File
@@ -5,19 +5,29 @@ enableFreeze(true);
import React, { useEffect } from 'react';
import { StatusBar } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import LottieSplashScreen from 'react-native-lottie-splash-screen';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { Provider as PaperProvider } from 'react-native-paper';
import { Provider } from 'react-redux';
import { persistor, store } from './src/redux/store';
import { PersistGate } from 'redux-persist/lib/integration/react';
import * as Notifications from 'expo-notifications';
import { createDatabase } from '@database/db';
import { persistor, store } from '@redux/store';
import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary';
import Main from './src/navigators/Main';
import { createDatabase } from './src/database/db';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import LottieSplashScreen from 'react-native-lottie-splash-screen';
import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
Notifications.setNotificationHandler({
handleNotification: async () => {
return {
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
};
},
});
const App = () => {
useEffect(() => {
+24 -5
View File
@@ -7,6 +7,11 @@ Contributions are welcome and are greatly appreciated!
After forking to your own github org or account, do 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])
android sdk (https://developer.android.com/studio)
# clone your fork to your local machine
git clone https://github.com/<your-account-name>/lnreader.git
@@ -14,20 +19,34 @@ git clone https://github.com/<your-account-name>/lnreader.git
cd lnreader
# install dependencies
npm install
npm install --legacy-peer-deps
# run metro for development
npm start
# build the apk (the built apk will be found in ~/lnreader/android/app/build/outputs/apk/release/)
npm run buildRelease
```
### Developing on Android
While the packager is running and you have an Android device or emulator connected to your computer, build and launch the Android app.
You will need an Android device or emulator connected to your computer as well as an IDE of your choice. (eg: vscode)
```
```bash
# prerequisites
adb (https://developer.android.com/studio/command-line/adb)
IDE
# check if android device/emulator is connected
adb devices
# run metro for development
npm start
# then to view on your android device (new terminal)
npm run 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/).
+6 -2
View File
@@ -9,7 +9,7 @@
</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/workflow/status/LNReader/lnreader/CI">
<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">
@@ -43,6 +43,10 @@ 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).
## Building & Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md)
## License
[MIT ](https://github.com/rajarsheechatterjee/lnreader/blob/main/LICENSE)
[MIT](https://github.com/rajarsheechatterjee/lnreader/blob/main/LICENSE)
@@ -8,6 +8,7 @@ import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.widget.ScrollView;
import android.view.WindowManager;
import com.facebook.react.ReactActivity;
@@ -60,7 +61,7 @@ public class MainActivity extends ReactActivity {
case KeyEvent.KEYCODE_VOLUME_UP:
if (action == KeyEvent.ACTION_DOWN) {
// volup
VolumeButtonListener.up();
VolumeButtonListener.up(VolumeButtonListener.getScrollAmount());
if (!VolumeButtonListener.prevent)
return super.dispatchKeyEvent(event);
}
@@ -68,7 +69,7 @@ public class MainActivity extends ReactActivity {
case KeyEvent.KEYCODE_VOLUME_DOWN:
if (action == KeyEvent.ACTION_DOWN) {
// voldown
VolumeButtonListener.down();
VolumeButtonListener.down(VolumeButtonListener.getScrollAmount());
if (!VolumeButtonListener.prevent)
return super.dispatchKeyEvent(event);
}
@@ -79,7 +80,6 @@ public class MainActivity extends ReactActivity {
}
return super.dispatchKeyEvent(event);
}
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
@@ -99,4 +99,4 @@ public class MainActivity extends ReactActivity {
};
}
}
}
@@ -1,11 +1,11 @@
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.Promise;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
@@ -27,9 +27,10 @@ public class VolumeButtonListener extends ReactContextBaseJavaModule {
public static boolean prevent = false;
public static void sendEvent(boolean up) {
public static void sendEvent(boolean up, int amount) {
if (active) {
WritableMap args = Arguments.createMap();
args.putInt("scrollAmount", amount); // add scroll amount to event data
if (up)
appContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("VolumeUp", args);
else
@@ -37,12 +38,22 @@ public class VolumeButtonListener extends ReactContextBaseJavaModule {
}
}
public static void down() {
sendEvent(false);
public static void down(int amount) {
sendEvent(false, amount);
}
public static void up() {
sendEvent(true);
public static void up(int amount) {
sendEvent(true, amount);
}
private static int scrollAmount = 100;
public static void setScrollAmount(int amount) {
scrollAmount = amount;
}
public static int getScrollAmount() {
return scrollAmount;
}
@ReactMethod
@@ -90,4 +101,14 @@ public class VolumeButtonListener extends ReactContextBaseJavaModule {
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}
}
@ReactMethod
public void setVolumeButtonScrollAmount(int amount) {
setScrollAmount(amount);
}
@ReactMethod
public void getVolumeButtonScrollAmount(Promise promise) {
promise.resolve(getScrollAmount());
}
}
+1 -1
View File
@@ -2,4 +2,4 @@ pull_request_title: 'chore: Update translations'
commit_message: '[ci skip]'
files:
- source: /strings/languages/en/strings.json
translation: /strings/languages/%two_letters_code%/strings.json
translation: /strings/languages/%locale_with_underscore%/strings.json
+101 -481
View File
@@ -21,7 +21,6 @@
"@react-navigation/stack": "^6.0.11",
"@reduxjs/toolkit": "^1.7.2",
"@shopify/flash-list": "^1.2.1",
"buffer": "^6.0.3",
"cheerio": "^1.0.0-rc.10",
"color": "^4.2.3",
"dayjs": "^1.11.4",
@@ -37,7 +36,7 @@
"expo-sqlite": "~9.1.0",
"expo-web-browser": "~9.1.0",
"i18n-js": "^3.8.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lottie-react-native": "^5.1.4",
"prop-types": "^15.7.2",
"qs": "^6.10.3",
@@ -54,13 +53,11 @@
"react-native-markdown-display": "^7.0.0-alpha.2",
"react-native-mmkv": "^2.4.3",
"react-native-pager-view": "^5.4.9",
"react-native-paper": "^5.1.2",
"react-native-paper": "^5.2.0",
"react-native-reanimated": "^2.13.0",
"react-native-render-html": "^6.3.4",
"react-native-safe-area-context": "^3.2.0",
"react-native-screens": "^3.9.0",
"react-native-shimmer-placeholder": "^2.0.9",
"react-native-swipe-gestures": "^1.0.5",
"react-native-tab-view": "^3.1.1",
"react-native-tts": "^4.1.0",
"react-native-unimodules": "^0.13.3",
@@ -78,7 +75,7 @@
"@types/color": "^3.0.3",
"@types/i18n-js": "^3.8.2",
"@types/jest": "^27.4.0",
"@types/lodash": "^4.14.179",
"@types/lodash-es": "^4.17.6",
"@types/react-native": "^0.66.15",
"@types/react-native-vector-icons": "^6.4.10",
"@types/react-test-renderer": "^17.0.1",
@@ -2998,141 +2995,6 @@
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"node_modules/@jsamr/counter-style": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@jsamr/counter-style/-/counter-style-2.0.2.tgz",
"integrity": "sha512-2mXudGVtSzVxWEA7B9jZLKjoXUeUFYDDtFrQoC0IFX9/Dszz4t1vZOmafi3JSw/FxD+udMQ+4TAFR8Qs0J3URQ=="
},
"node_modules/@jsamr/react-native-li": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@jsamr/react-native-li/-/react-native-li-2.3.1.tgz",
"integrity": "sha512-Qbo4NEj48SQ4k8FZJHFE2fgZDKTWaUGmVxcIQh3msg5JezLdTMMHuRRDYctfdHI6L0FZGObmEv3haWbIvmol8w==",
"peerDependencies": {
"@jsamr/counter-style": "^1.0.0 || ^2.0.0",
"react": "*",
"react-native": "*"
}
},
"node_modules/@native-html/css-processor": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@native-html/css-processor/-/css-processor-1.11.0.tgz",
"integrity": "sha512-NnhBEbJX5M2gBGltPKOetiLlKhNf3OHdRafc8//e2ZQxXN8JaSW/Hy8cm94pnIckQxwaMKxrtaNT3x4ZcffoNQ==",
"dependencies": {
"css-to-react-native": "^3.0.0",
"csstype": "^3.0.8"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-native": "*"
}
},
"node_modules/@native-html/transient-render-engine": {
"version": "11.2.3",
"resolved": "https://registry.npmjs.org/@native-html/transient-render-engine/-/transient-render-engine-11.2.3.tgz",
"integrity": "sha512-zXwgA3gPUEmFs3I3syfnvDvS6WiUHXEE6jY09OBzK+trq7wkweOSFWIoyXiGkbXrozGYG0KY90YgPyr8Tg8Uyg==",
"dependencies": {
"@native-html/css-processor": "1.11.0",
"@types/ramda": "^0.27.44",
"csstype": "^3.0.9",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.2",
"domutils": "^2.8.0",
"htmlparser2": "^7.1.2",
"ramda": "^0.27.2"
},
"peerDependencies": {
"@types/react-native": "*",
"react-native": "^*"
}
},
"node_modules/@native-html/transient-render-engine/node_modules/dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/@native-html/transient-render-engine/node_modules/domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/@native-html/transient-render-engine/node_modules/domhandler": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
"integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
"dependencies": {
"domelementtype": "^2.2.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/@native-html/transient-render-engine/node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/@native-html/transient-render-engine/node_modules/entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/@native-html/transient-render-engine/node_modules/htmlparser2": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
"integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.2",
"domutils": "^2.8.0",
"entities": "^3.0.1"
}
},
"node_modules/@native-html/transient-render-engine/node_modules/htmlparser2/node_modules/entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -3905,9 +3767,9 @@
}
},
"node_modules/@sideway/formula": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
},
"node_modules/@sideway/pinpoint": {
"version": "2.0.0",
@@ -4041,11 +3903,20 @@
"dev": true
},
"node_modules/@types/lodash": {
"version": "4.14.179",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
"version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
"dev": true
},
"node_modules/@types/lodash-es": {
"version": "4.17.6",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.6.tgz",
"integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/node": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
@@ -4056,14 +3927,6 @@
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
},
"node_modules/@types/ramda": {
"version": "0.27.66",
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.66.tgz",
"integrity": "sha512-i2YW+E2U6NfMt3dp0RxNcejox+bxJUNDjB7BpYuRuoHIzv5juPHkJkNgcUOu+YSQEmaWu8cnAo/8r63C0NnuVA==",
"dependencies": {
"ts-toolbelt": "^6.15.1"
}
},
"node_modules/@types/react": {
"version": "17.0.39",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
@@ -4127,11 +3990,6 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
},
"node_modules/@types/urijs": {
"version": "1.19.19",
"resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz",
"integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg=="
},
"node_modules/@types/yargs": {
"version": "15.0.13",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz",
@@ -4652,9 +4510,9 @@
}
},
"node_modules/@xmldom/xmldom": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.5.tgz",
"integrity": "sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A==",
"version": "0.7.9",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.9.tgz",
"integrity": "sha512-yceMpm/xd4W2a85iqZyO09gTnHvXF6pyiWjD2jcOJs7hRoZtNNOO1eJlhHj1ixA+xip2hOyGn+LgcvLCMo5zXA==",
"engines": {
"node": ">=10.0.0"
}
@@ -5466,29 +5324,6 @@
"node-int64": "^0.4.0"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-alloc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
@@ -5604,13 +5439,19 @@
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
},
"node_modules/caniuse-lite": {
"version": "1.0.30001281",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001281.tgz",
"integrity": "sha512-1ktfDV2+4LYxOGeAZygMyvhiPBJKV1gsqPmfGGwRbwYOzDWbsQHgBFo+2lpRTMO2gWjPWJrCMLWa2fnSVnxn0A==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
}
"version": "1.0.30001452",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz",
"integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
}
]
},
"node_modules/capture-exit": {
"version": "2.0.0",
@@ -5623,24 +5464,6 @@
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/character-entities-html4": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz",
"integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-entities-legacy": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/cheerio": {
"version": "1.0.0-rc.10",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz",
@@ -6376,9 +6199,9 @@
}
},
"node_modules/decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"engines": {
"node": ">=0.10"
}
@@ -10376,6 +10199,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -10636,11 +10464,6 @@
"loose-envify": "cli.js"
}
},
"node_modules/lottie-ios": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/lottie-ios/-/lottie-ios-3.2.3.tgz",
"integrity": "sha512-mubYMN6+1HXa8z3EJKBvNBkl4UoVM4McjESeB2PgvRMSngmJtC5yUMRdhbbrIAn5Liu3hFGao/14s5hQIgtkRQ=="
},
"node_modules/lottie-react-native": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-5.1.4.tgz",
@@ -11427,9 +11250,9 @@
}
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -12697,11 +12520,6 @@
}
]
},
"node_modules/ramda": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz",
"integrity": "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA=="
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -12911,6 +12729,11 @@
"react-native": ">=0.57.0"
}
},
"node_modules/react-native-lottie-splash-screen/node_modules/lottie-ios": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/lottie-ios/-/lottie-ios-3.2.3.tgz",
"integrity": "sha512-mubYMN6+1HXa8z3EJKBvNBkl4UoVM4McjESeB2PgvRMSngmJtC5yUMRdhbbrIAn5Liu3hFGao/14s5hQIgtkRQ=="
},
"node_modules/react-native-markdown-display": {
"version": "7.0.0-alpha.2",
"resolved": "https://registry.npmjs.org/react-native-markdown-display/-/react-native-markdown-display-7.0.0-alpha.2.tgz",
@@ -12945,9 +12768,9 @@
}
},
"node_modules/react-native-paper": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.1.2.tgz",
"integrity": "sha512-muf70CPtji6gTukV+9ecvzOYFOflK2hi4t4GLt9y4Ynhf/YQCvXsefPTp45n7RS6bz9+ueG4jtuGg64oOY8dxA==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.2.0.tgz",
"integrity": "sha512-FfKvfh4nBvetTusiuc1QMuNxXdNai4lthoyD+dNPM03RnIapcjoKHrhCD1EvVi5c1kBSKHrlXQ3OAW/8QCQXNw==",
"dependencies": {
"@callstack/react-theme-provider": "^3.0.8",
"color": "^3.1.2",
@@ -13004,26 +12827,6 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/react-native-render-html": {
"version": "6.3.4",
"resolved": "https://registry.npmjs.org/react-native-render-html/-/react-native-render-html-6.3.4.tgz",
"integrity": "sha512-H2jSMzZjidE+Wo3qCWPUMU1nm98Vs2SGCvQCz/i6xf0P3Y9uVtG/b0sDbG/cYFir2mSYBYCIlS1Dv0WC1LjYig==",
"dependencies": {
"@jsamr/counter-style": "^2.0.1",
"@jsamr/react-native-li": "^2.3.0",
"@native-html/transient-render-engine": "11.2.3",
"@types/ramda": "^0.27.40",
"@types/urijs": "^1.19.15",
"prop-types": "^15.5.7",
"ramda": "^0.27.2",
"stringify-entities": "^3.1.0",
"urijs": "^1.19.6"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-safe-area-context": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.2.0.tgz",
@@ -13077,11 +12880,6 @@
"react-native-linear-gradient": ">=2.4.0"
}
},
"node_modules/react-native-swipe-gestures": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz",
"integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw=="
},
"node_modules/react-native-tab-view": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-3.1.1.tgz",
@@ -14920,20 +14718,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/stringify-entities": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz",
"integrity": "sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg==",
"dependencies": {
"character-entities-html4": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"xtend": "^4.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@@ -15331,11 +15115,6 @@
"resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz",
"integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA=="
},
"node_modules/ts-toolbelt": {
"version": "6.15.5",
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz",
"integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A=="
},
"node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
@@ -15396,9 +15175,9 @@
}
},
"node_modules/ua-parser-js": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==",
"version": "0.7.33",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==",
"funding": [
{
"type": "opencollective",
@@ -15656,11 +15435,6 @@
"punycode": "^2.1.0"
}
},
"node_modules/urijs": {
"version": "1.19.11",
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
},
"node_modules/urix": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
@@ -18413,98 +18187,6 @@
"@jridgewell/sourcemap-codec": "1.4.14"
}
},
"@jsamr/counter-style": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@jsamr/counter-style/-/counter-style-2.0.2.tgz",
"integrity": "sha512-2mXudGVtSzVxWEA7B9jZLKjoXUeUFYDDtFrQoC0IFX9/Dszz4t1vZOmafi3JSw/FxD+udMQ+4TAFR8Qs0J3URQ=="
},
"@jsamr/react-native-li": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@jsamr/react-native-li/-/react-native-li-2.3.1.tgz",
"integrity": "sha512-Qbo4NEj48SQ4k8FZJHFE2fgZDKTWaUGmVxcIQh3msg5JezLdTMMHuRRDYctfdHI6L0FZGObmEv3haWbIvmol8w=="
},
"@native-html/css-processor": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@native-html/css-processor/-/css-processor-1.11.0.tgz",
"integrity": "sha512-NnhBEbJX5M2gBGltPKOetiLlKhNf3OHdRafc8//e2ZQxXN8JaSW/Hy8cm94pnIckQxwaMKxrtaNT3x4ZcffoNQ==",
"requires": {
"css-to-react-native": "^3.0.0",
"csstype": "^3.0.8"
}
},
"@native-html/transient-render-engine": {
"version": "11.2.3",
"resolved": "https://registry.npmjs.org/@native-html/transient-render-engine/-/transient-render-engine-11.2.3.tgz",
"integrity": "sha512-zXwgA3gPUEmFs3I3syfnvDvS6WiUHXEE6jY09OBzK+trq7wkweOSFWIoyXiGkbXrozGYG0KY90YgPyr8Tg8Uyg==",
"requires": {
"@native-html/css-processor": "1.11.0",
"@types/ramda": "^0.27.44",
"csstype": "^3.0.9",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.2",
"domutils": "^2.8.0",
"htmlparser2": "^7.1.2",
"ramda": "^0.27.2"
},
"dependencies": {
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
},
"domhandler": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
"integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
"requires": {
"domelementtype": "^2.2.0"
}
},
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
}
},
"entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
},
"htmlparser2": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
"integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.2",
"domutils": "^2.8.0",
"entities": "^3.0.1"
},
"dependencies": {
"entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="
}
}
}
}
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -19082,9 +18764,9 @@
}
},
"@sideway/formula": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
},
"@sideway/pinpoint": {
"version": "2.0.0",
@@ -19211,11 +18893,20 @@
"dev": true
},
"@types/lodash": {
"version": "4.14.179",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
"integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
"version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
"dev": true
},
"@types/lodash-es": {
"version": "4.17.6",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.6.tgz",
"integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==",
"dev": true,
"requires": {
"@types/lodash": "*"
}
},
"@types/node": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
@@ -19226,14 +18917,6 @@
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
},
"@types/ramda": {
"version": "0.27.66",
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.66.tgz",
"integrity": "sha512-i2YW+E2U6NfMt3dp0RxNcejox+bxJUNDjB7BpYuRuoHIzv5juPHkJkNgcUOu+YSQEmaWu8cnAo/8r63C0NnuVA==",
"requires": {
"ts-toolbelt": "^6.15.1"
}
},
"@types/react": {
"version": "17.0.39",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
@@ -19297,11 +18980,6 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
},
"@types/urijs": {
"version": "1.19.19",
"resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz",
"integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg=="
},
"@types/yargs": {
"version": "15.0.13",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz",
@@ -19611,9 +19289,9 @@
}
},
"@xmldom/xmldom": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.5.tgz",
"integrity": "sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A=="
"version": "0.7.9",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.9.tgz",
"integrity": "sha512-yceMpm/xd4W2a85iqZyO09gTnHvXF6pyiWjD2jcOJs7hRoZtNNOO1eJlhHj1ixA+xip2hOyGn+LgcvLCMo5zXA=="
},
"abort-controller": {
"version": "3.0.0",
@@ -20235,15 +19913,6 @@
"node-int64": "^0.4.0"
}
},
"buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"buffer-alloc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
@@ -20335,9 +20004,9 @@
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
},
"caniuse-lite": {
"version": "1.0.30001281",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001281.tgz",
"integrity": "sha512-1ktfDV2+4LYxOGeAZygMyvhiPBJKV1gsqPmfGGwRbwYOzDWbsQHgBFo+2lpRTMO2gWjPWJrCMLWa2fnSVnxn0A=="
"version": "1.0.30001452",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz",
"integrity": "sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w=="
},
"capture-exit": {
"version": "2.0.0",
@@ -20347,16 +20016,6 @@
"rsvp": "^4.8.4"
}
},
"character-entities-html4": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz",
"integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g=="
},
"character-entities-legacy": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="
},
"cheerio": {
"version": "1.0.0-rc.10",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz",
@@ -20914,9 +20573,9 @@
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
},
"dedent": {
"version": "0.6.0",
@@ -23932,6 +23591,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
@@ -24139,11 +23803,6 @@
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lottie-ios": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/lottie-ios/-/lottie-ios-3.2.3.tgz",
"integrity": "sha512-mubYMN6+1HXa8z3EJKBvNBkl4UoVM4McjESeB2PgvRMSngmJtC5yUMRdhbbrIAn5Liu3hFGao/14s5hQIgtkRQ=="
},
"lottie-react-native": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-5.1.4.tgz",
@@ -24820,9 +24479,9 @@
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -25750,11 +25409,6 @@
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true
},
"ramda": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz",
"integrity": "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -26185,6 +25839,13 @@
"requires": {
"lottie-ios": "3.2.3",
"lottie-react-native": "^5.1.3"
},
"dependencies": {
"lottie-ios": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/lottie-ios/-/lottie-ios-3.2.3.tgz",
"integrity": "sha512-mubYMN6+1HXa8z3EJKBvNBkl4UoVM4McjESeB2PgvRMSngmJtC5yUMRdhbbrIAn5Liu3hFGao/14s5hQIgtkRQ=="
}
}
},
"react-native-markdown-display": {
@@ -26209,9 +25870,9 @@
"integrity": "sha512-D6tzxpwMGdl6CXgtskGWhKRc5cJakCazESRGt7PkqnpyiH3N35ft1KmR82pCSQetAFlytFiToeu3a/dG5CELvA=="
},
"react-native-paper": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.1.2.tgz",
"integrity": "sha512-muf70CPtji6gTukV+9ecvzOYFOflK2hi4t4GLt9y4Ynhf/YQCvXsefPTp45n7RS6bz9+ueG4jtuGg64oOY8dxA==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.2.0.tgz",
"integrity": "sha512-FfKvfh4nBvetTusiuc1QMuNxXdNai4lthoyD+dNPM03RnIapcjoKHrhCD1EvVi5c1kBSKHrlXQ3OAW/8QCQXNw==",
"requires": {
"@callstack/react-theme-provider": "^3.0.8",
"color": "^3.1.2",
@@ -26255,22 +25916,6 @@
}
}
},
"react-native-render-html": {
"version": "6.3.4",
"resolved": "https://registry.npmjs.org/react-native-render-html/-/react-native-render-html-6.3.4.tgz",
"integrity": "sha512-H2jSMzZjidE+Wo3qCWPUMU1nm98Vs2SGCvQCz/i6xf0P3Y9uVtG/b0sDbG/cYFir2mSYBYCIlS1Dv0WC1LjYig==",
"requires": {
"@jsamr/counter-style": "^2.0.1",
"@jsamr/react-native-li": "^2.3.0",
"@native-html/transient-render-engine": "11.2.3",
"@types/ramda": "^0.27.40",
"@types/urijs": "^1.19.15",
"prop-types": "^15.5.7",
"ramda": "^0.27.2",
"stringify-entities": "^3.1.0",
"urijs": "^1.19.6"
}
},
"react-native-safe-area-context": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.2.0.tgz",
@@ -26305,11 +25950,6 @@
"resolved": "https://registry.npmjs.org/react-native-shimmer-placeholder/-/react-native-shimmer-placeholder-2.0.9.tgz",
"integrity": "sha512-s2pfAAO6uaybREYM2KcaxP3c2XlZvLfHfzmhMlGEoOhSKaoM92KpA1Hx0BeC75yilkliJuowqO/hFzvLB0h7hg=="
},
"react-native-swipe-gestures": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz",
"integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw=="
},
"react-native-tab-view": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-3.1.1.tgz",
@@ -27507,16 +27147,6 @@
"define-properties": "^1.1.3"
}
},
"stringify-entities": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz",
"integrity": "sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg==",
"requires": {
"character-entities-html4": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"xtend": "^4.0.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@@ -27826,11 +27456,6 @@
"resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz",
"integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA=="
},
"ts-toolbelt": {
"version": "6.15.5",
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz",
"integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A=="
},
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
@@ -27874,9 +27499,9 @@
"dev": true
},
"ua-parser-js": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
"version": "0.7.33",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw=="
},
"uc.micro": {
"version": "1.0.6",
@@ -28078,11 +27703,6 @@
"punycode": "^2.1.0"
}
},
"urijs": {
"version": "1.19.11",
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
},
"urix": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+3 -6
View File
@@ -29,7 +29,6 @@
"@react-navigation/stack": "^6.0.11",
"@reduxjs/toolkit": "^1.7.2",
"@shopify/flash-list": "^1.2.1",
"buffer": "^6.0.3",
"cheerio": "^1.0.0-rc.10",
"color": "^4.2.3",
"dayjs": "^1.11.4",
@@ -45,7 +44,7 @@
"expo-sqlite": "~9.1.0",
"expo-web-browser": "~9.1.0",
"i18n-js": "^3.8.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lottie-react-native": "^5.1.4",
"prop-types": "^15.7.2",
"qs": "^6.10.3",
@@ -62,13 +61,11 @@
"react-native-markdown-display": "^7.0.0-alpha.2",
"react-native-mmkv": "^2.4.3",
"react-native-pager-view": "^5.4.9",
"react-native-paper": "^5.1.2",
"react-native-paper": "^5.2.0",
"react-native-reanimated": "^2.13.0",
"react-native-render-html": "^6.3.4",
"react-native-safe-area-context": "^3.2.0",
"react-native-screens": "^3.9.0",
"react-native-shimmer-placeholder": "^2.0.9",
"react-native-swipe-gestures": "^1.0.5",
"react-native-tab-view": "^3.1.1",
"react-native-tts": "^4.1.0",
"react-native-unimodules": "^0.13.3",
@@ -86,7 +83,7 @@
"@types/color": "^3.0.3",
"@types/i18n-js": "^3.8.2",
"@types/jest": "^27.4.0",
"@types/lodash": "^4.14.179",
"@types/lodash-es": "^4.17.6",
"@types/react-native": "^0.66.15",
"@types/react-native-vector-icons": "^6.4.10",
"@types/react-test-renderer": "^17.0.1",
+2 -3
View File
@@ -18,6 +18,7 @@ const getLanguageCode = language => {
Chinese: 'cn',
Japanese: 'jp',
Vietnamese: 'vi',
Korean: 'kr',
};
return codes[language];
@@ -58,7 +59,6 @@ try {
: ''
}
const popularNovels = async (page: number) => {
const totalPages = ${source.popularNovels.totalPages};
const url = \`${source.popularNovels.url}\`;
const result = await fetch(url);
@@ -102,8 +102,7 @@ try {
}
});
return { totalPages, novels };
};
return { novels };
const parseNovelAndChapters = async (novelUrl: string) => {
const url = novelUrl;
+3 -8
View File
@@ -8,11 +8,8 @@ import {
View,
ViewProps,
} from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import color from 'color';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { overlay } from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
type Action = {
icon: string;
@@ -40,9 +37,7 @@ export const Actionbar: React.FC<ActionbarProps> = ({
style={[
styles.actionbarContainer,
{
backgroundColor: theme.isDark
? overlay(2, theme.surface)
: theme.secondaryContainer,
backgroundColor: theme.surface2,
minHeight: 80 + bottom,
paddingBottom: bottom,
},
@@ -54,7 +49,7 @@ export const Actionbar: React.FC<ActionbarProps> = ({
key={id}
android_ripple={{
radius: 50,
color: color(theme.primary).alpha(0.12).string(),
color: theme.rippleColor,
borderless: true,
}}
onPress={onPress}
@@ -4,7 +4,6 @@ import ErrorBoundary from 'react-native-error-boundary';
import { Button, List } from '@components';
import { useTheme } from '@hooks';
import { ButtonVariation } from '@components/Button/Button';
interface ErrorFallbackProps {
error: Error;
@@ -44,11 +43,10 @@ export const ErrorFallback: React.FC<ErrorFallbackProps> = ({
</View>
<List.Divider theme={theme} />
<Button
variation={ButtonVariation.DEFAULT}
onPress={resetError}
title={'Restart the application'}
style={styles.buttonCtn}
theme={theme}
mode="contained"
/>
</View>
);
+4 -4
View File
@@ -2,12 +2,12 @@ import React from 'react';
import { StatusBar } from 'react-native';
import { Appbar as PaperAppbar } from 'react-native-paper';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
interface AppbarProps {
title: string;
handleGoBack: () => void;
theme: MD3ThemeType;
theme: ThemeColors;
mode?: 'small' | 'medium' | 'large' | 'center-aligned';
}
@@ -26,12 +26,12 @@ const Appbar: React.FC<AppbarProps> = ({
{handleGoBack && (
<PaperAppbar.BackAction
onPress={handleGoBack}
iconColor={theme.textColorPrimary}
iconColor={theme.onSurface}
/>
)}
<PaperAppbar.Content
title={title}
titleStyle={{ color: theme.textColorPrimary }}
titleStyle={{ color: theme.onSurface }}
/>
{children}
</PaperAppbar.Header>
+2 -2
View File
@@ -2,14 +2,14 @@ import React, { ReactElement, Ref, useCallback } from 'react';
import { overlay } from 'react-native-paper';
import { default as BS, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { MD3ThemeType } from '@theme/types';
import { ThemeColors } from '@theme/types';
import { StyleSheet } from 'react-native';
interface BottomSheetProps {
bottomSheetRef: Ref<BS>;
snapPoints: Array<number>;
height: number;
theme: MD3ThemeType;
theme: ThemeColors;
children: ReactElement;
}
+12 -94
View File
@@ -1,105 +1,23 @@
import React from 'react';
import {
Pressable,
StyleProp,
StyleSheet,
Text,
TextStyle,
View,
ViewStyle,
} from 'react-native';
import React, { useMemo } from 'react';
import color from 'color';
Button as PaperButton,
ButtonProps as PaperButtonProps,
} from 'react-native-paper';
import { MD3ThemeType } from '../../theme/types';
import { useTheme } from '@hooks';
export enum ButtonVariation {
DEFAULT = 'default',
OUTLINED = 'outlined',
CLEAR = 'clear',
interface ButtonProps extends Omit<PaperButtonProps, 'children' | 'theme'> {
title?: string;
}
interface Props {
variation?: ButtonVariation;
title: string;
textColor?: string;
onPress?: () => void;
theme: MD3ThemeType;
style?: ViewStyle;
labelStyle?: StyleProp<TextStyle>;
disabled?: boolean;
}
const Button: React.FC<Props> = ({
variation = ButtonVariation.DEFAULT,
title,
textColor,
onPress,
theme,
style,
disabled,
labelStyle,
}) => {
const textStyles: StyleProp<TextStyle> = useMemo(
() => ({
color: disabled
? theme.textColorHint
: textColor
? textColor
: variation === ButtonVariation.OUTLINED
? theme.primary
: variation === ButtonVariation.CLEAR
? theme.primary
: theme.onSecondary,
}),
[],
);
const Button: React.FC<ButtonProps> = props => {
const theme = useTheme();
return (
<View
style={[
styles.button,
variation === ButtonVariation.OUTLINED
? {
...styles.outlined,
borderColor: theme.textColorHint,
}
: variation !== ButtonVariation.CLEAR && {
backgroundColor: theme.secondary,
},
style,
]}
>
<Pressable
style={styles.pressable}
onPress={onPress}
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
disabled={disabled}
>
<Text style={[textStyles, labelStyle]} numberOfLines={1}>
{title}
</Text>
</Pressable>
</View>
<PaperButton {...props} theme={{ colors: theme }}>
{props.title || props.children}
</PaperButton>
);
};
export default Button;
const styles = StyleSheet.create({
button: {
minHeight: 40,
justifyContent: 'center',
borderRadius: 50,
overflow: 'hidden',
},
pressable: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 8,
paddingHorizontal: 24,
},
outlined: {
borderWidth: 1,
},
});
+6 -7
View File
@@ -9,16 +9,15 @@ import {
} from 'react-native';
import { Checkbox as PaperCheckbox } from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import color from 'color';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
interface CheckboxProps {
label: string;
status: boolean | 'indeterminate';
onPress?: () => void;
disabled?: boolean;
theme: MD3ThemeType;
theme: ThemeColors;
viewStyle?: StyleProp<ViewStyle>;
labelStyle?: StyleProp<TextStyle>;
}
@@ -33,7 +32,7 @@ export const Checkbox: React.FC<CheckboxProps> = ({
labelStyle,
}) => (
<Pressable
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
style={[styles.pressable, viewStyle]}
onPress={onPress}
disabled={disabled}
@@ -49,9 +48,9 @@ export const Checkbox: React.FC<CheckboxProps> = ({
onPress={onPress}
color={theme.primary}
theme={{
colors: { disabled: theme.textColorSecondary },
colors: { disabled: theme.onSurfaceVariant },
}}
uncheckedColor={theme.textColorSecondary}
uncheckedColor={theme.onSurfaceVariant}
disabled={disabled}
/>
<Text style={[styles.defaultLabel, { color: theme.onSurface }, labelStyle]}>
@@ -62,7 +61,7 @@ export const Checkbox: React.FC<CheckboxProps> = ({
export const SortItem = ({ label, status, onPress, theme }) => (
<Pressable
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
style={[styles.pressable, { paddingVertical: 16, paddingLeft: 64 }]}
onPress={onPress}
>
+3 -4
View File
@@ -1,13 +1,12 @@
import React from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import color from 'color';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
import { overlay } from 'react-native-paper';
interface ChipProps {
label: string;
theme: MD3ThemeType;
theme: ThemeColors;
}
const Chip: React.FC<ChipProps> = ({ label, theme }) => (
@@ -22,7 +21,7 @@ const Chip: React.FC<ChipProps> = ({ label, theme }) => (
]}
>
<Pressable
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
style={styles.pressable}
>
<Text
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { FlatList, Pressable, StyleSheet, Text, View } from 'react-native';
import { Modal, overlay, Portal, TextInput } from 'react-native-paper';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
interface ColorPickerModalProps {
visible: boolean;
@@ -10,7 +10,7 @@ interface ColorPickerModalProps {
color: string;
onSubmit: (val: string) => void;
closeModal: () => void;
theme: MD3ThemeType;
theme: ThemeColors;
showAccentColors?: boolean;
}
@@ -80,7 +80,7 @@ const ColorPickerModal: React.FC<ColorPickerModalProps> = ({
{ backgroundColor: overlay(2, theme.surface) },
]}
>
<Text style={[styles.modalTitle, { color: theme.textColorPrimary }]}>
<Text style={[styles.modalTitle, { color: theme.onSurface }]}>
{title}
</Text>
{showAccentColors ? (
@@ -123,7 +123,7 @@ const ColorPickerModal: React.FC<ColorPickerModalProps> = ({
onSubmitEditing={onSubmitEditing}
mode="outlined"
theme={{ colors: { ...theme } }}
underlineColor={theme.textColorHint}
underlineColor={theme.outline}
dense
error={Boolean(error)}
/>
@@ -22,10 +22,8 @@ const ColorPreferenceItem: React.FC<ColorPreferenceItemProps> = ({
onPress={onPress}
>
<View>
<Text style={[styles.label, { color: theme.textColorPrimary }]}>
{label}
</Text>
<Text style={{ color: theme.textColorSecondary }}>
<Text style={[styles.label, { color: theme.onSurface }]}>{label}</Text>
<Text style={{ color: theme.onSurfaceVariant }}>
{description?.toUpperCase?.()}
</Text>
</View>
-110
View File
@@ -1,110 +0,0 @@
/* eslint-disable no-bitwise */
import PropTypes from 'prop-types';
import React, { PureComponent, Children } from 'react';
import { View, Animated, StyleSheet } from 'react-native';
export default class FadeView extends PureComponent {
static defaultProps = {
animationDuration: 225,
active: false,
removeHiddenSubviews: true,
};
static propTypes = {
animationDuration: PropTypes.number,
active: PropTypes.bool,
removeHiddenSubviews: PropTypes.bool,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
};
constructor(props) {
super(props);
this.renderChild = this.renderChild.bind(this);
this.mounted = false;
let { active } = this.props;
this.state = {
progress: new Animated.Value(Number(active)),
animating: false,
};
}
componentDidMount() {
this.mounted = true;
}
componentWillUnmount() {
this.mounted = false;
}
UNSAFE_componentWillReceiveProps({ active, animationDuration }) {
let { progress } = this.state;
if (active ^ this.props.active) {
this.setState({ animating: true });
Animated.timing(progress, {
toValue: Number(active),
duration: animationDuration,
useNativeDriver: true,
}).start(() => {
if (this.mounted) {
this.setState({ animating: false });
}
});
}
}
renderChild(child, index) {
let { active, removeHiddenSubviews } = this.props;
let { animating, progress } = this.state;
let opacity = progress.interpolate({
inputRange: [0, 1],
outputRange: index ? [0, 1] : [1, 0],
});
let hidden = active ^ !!index;
if (removeHiddenSubviews) {
if (!animating && hidden) {
return null;
}
}
let pointerEvents = hidden ? 'none' : 'box-none';
return (
<Animated.View
style={[styles.container, { opacity }]}
pointerEvents={pointerEvents}
>
{child}
</Animated.View>
);
}
render() {
let { children, ...props } = this.props;
return <View {...props}>{Children.map(children, this.renderChild)}</View>;
}
}
const styles = StyleSheet.create({
container: {
// ...StyleSheet.absoluteFillObject,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
});
+4 -6
View File
@@ -1,13 +1,13 @@
import React from 'react';
import { Pressable, View } from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
import Color from 'color';
interface ToggleButtonProps {
icon: string;
selected: boolean;
theme: MD3ThemeType;
theme: ThemeColors;
color?: string;
onPress: () => void;
}
@@ -27,7 +27,7 @@ export const ToggleButton: React.FC<ToggleButtonProps> = ({
}}
>
<Pressable
android_ripple={{ color: Color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
style={{
padding: 8,
alignItems: 'center',
@@ -40,9 +40,7 @@ export const ToggleButton: React.FC<ToggleButtonProps> = ({
>
<MaterialCommunityIcons
name={icon}
color={
selected ? theme.primary : color ? color : theme.textColorPrimary
}
color={selected ? theme.primary : color ? color : theme.onSurface}
size={24}
/>
</Pressable>
@@ -4,15 +4,13 @@ import { StyleSheet, View } from 'react-native';
import { getString } from '@strings/translations';
import { Dialog, Portal } from 'react-native-paper';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
import { Button } from '@components/index';
import { ButtonVariation } from '@components/Button/Button';
import { getDialogBackground } from '@theme/colors';
interface ConfirmationDialogProps {
title: string;
visible: boolean;
theme: MD3ThemeType;
theme: ThemeColors;
onSubmit: () => void;
onDismiss: () => void;
}
@@ -34,27 +32,14 @@ const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
<Dialog
visible={visible}
onDismiss={onDismiss}
style={[
styles.container,
{ backgroundColor: getDialogBackground(theme) },
]}
style={[styles.container, { backgroundColor: theme.overlay3 }]}
>
<Dialog.Title style={[styles.title, { color: theme.textColorPrimary }]}>
<Dialog.Title style={[styles.title, { color: theme.onSurface }]}>
{title}
</Dialog.Title>
<View style={styles.buttonCtn}>
<Button
theme={theme}
onPress={handleOnSubmit}
title={getString('common.ok')}
variation={ButtonVariation.CLEAR}
/>
<Button
theme={theme}
onPress={onDismiss}
title={getString('common.cancel')}
variation={ButtonVariation.CLEAR}
/>
<Button onPress={handleOnSubmit} title={getString('common.ok')} />
<Button onPress={onDismiss} title={getString('common.cancel')} />
</View>
</Dialog>
</Portal>
+2 -4
View File
@@ -10,16 +10,14 @@ const EmptyView = ({ icon, description, style, children, iconStyle }) => {
<Text
style={[
styles.emptyViewIcon,
{ color: theme.textColorHint },
{ color: theme.outline },
style,
iconStyle,
]}
>
{icon}
</Text>
<Text
style={[styles.emptyViewText, { color: theme.textColorHint }, style]}
>
<Text style={[styles.emptyViewText, { color: theme.outline }, style]}>
{description}
</Text>
{children}
+4 -6
View File
@@ -1,22 +1,20 @@
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
interface EmptyViewProps {
icon?: string;
description: string;
theme: MD3ThemeType;
theme: ThemeColors;
}
const EmptyView: React.FC<EmptyViewProps> = ({ icon, description, theme }) => (
<View style={styles.container}>
{icon ? (
<Text style={[styles.icon, { color: theme.textColorHint }]}>{icon}</Text>
<Text style={[styles.icon, { color: theme.outline }]}>{icon}</Text>
) : null}
<Text style={[styles.text, { color: theme.textColorHint }]}>
{description}
</Text>
<Text style={[styles.text, { color: theme.outline }]}>{description}</Text>
</View>
);
@@ -1,20 +1,21 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { MD3ThemeType } from '@theme/types';
import { useTheme } from '@hooks/useTheme';
interface ErrorScreenProps {
error?: string;
theme: MD3ThemeType;
error: string;
onRetry?: () => void;
retryIconColor?: string;
}
const ErrorScreen: React.FC<ErrorScreenProps> = ({ error, theme }) => {
const ErrorScreen: React.FC<ErrorScreenProps> = ({ error }) => {
const theme = useTheme();
return (
<View style={styles.container}>
<Text style={[styles.icon, { color: theme.textColorHint }]}>ಥ_ಥ</Text>
<Text style={[styles.error, { color: theme.textColorHint }]}>
{error}
</Text>
<Text style={[styles.icon, { color: theme.outline }]}>ಥ_ಥ</Text>
<Text style={[styles.error, { color: theme.outline }]}>{error}</Text>
</View>
);
};
+4 -4
View File
@@ -6,11 +6,11 @@ import { IconButton } from 'react-native-paper';
export const ErrorView = ({ errorName, actions, theme }) => (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={[styles.emptyViewIcon, { color: theme.textColorHint }]}>
<Text style={[styles.emptyViewIcon, { color: theme.outline }]}>
{/* {icons[Math.floor(Math.random() * 5)]} */}
ಥ_ಥ
</Text>
<Text style={[styles.emptyViewText, { color: theme.textColorHint }]}>
<Text style={[styles.emptyViewText, { color: theme.outline }]}>
{errorName}
</Text>
<View style={{ flexDirection: 'row' }}>
@@ -38,14 +38,14 @@ export const ErrorView = ({ errorName, actions, theme }) => (
>
<IconButton
icon={action.icon}
color={theme.textColorHint}
color={theme.outline}
size={24}
style={{ margin: 0 }}
/>
<Text
style={{
fontSize: 12,
color: theme.textColorHint,
color: theme.outline,
}}
>
{action.name}
-42
View File
@@ -1,42 +0,0 @@
import React from 'react';
import { Pressable, StyleSheet, ViewPropTypes } from 'react-native';
import PropTypes from 'prop-types';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
export const IconButton = ({
icon,
size,
color,
onPress,
containerStyle,
theme,
}) => (
<Pressable
style={[styles.container, containerStyle]}
android_ripple={{ color: theme.rippleColor, borderless: true, radius: 20 }}
onPress={onPress}
>
<MaterialCommunityIcons name={icon} size={size} color={color} />
</Pressable>
);
IconButton.defaultProps = {
size: 24,
color: '#FFFFFF',
};
IconButton.propTypes = {
icon: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
color: PropTypes.string,
onPress: PropTypes.func,
containerStyle: ViewPropTypes.style,
theme: PropTypes.object.isRequired,
};
const styles = StyleSheet.create({
container: {
margin: 6,
},
});
+3 -3
View File
@@ -3,7 +3,7 @@ import { Pressable, StyleSheet, View, ViewStyle } from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Color from 'color';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
type Props = {
name: string;
@@ -12,7 +12,7 @@ type Props = {
disabled?: boolean;
padding?: number;
onPress?: () => void;
theme: MD3ThemeType;
theme: ThemeColors;
style?: ViewStyle;
};
@@ -40,7 +40,7 @@ const IconButton: React.FC<Props> = ({
<MaterialCommunityIcons
name={name}
size={size}
color={disabled ? theme.textColorHint : color || theme.textColorPrimary}
color={disabled ? theme.outline : color || theme.onSurface}
/>
</Pressable>
</View>
-141
View File
@@ -1,141 +0,0 @@
import React from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import color from 'color';
import { List as PaperList, Divider as PaperDivider } from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
const ListSection = ({ children }) => (
<PaperList.Section style={styles.listSection}>{children}</PaperList.Section>
);
const ListSubHeader = ({ children, theme }) => (
<PaperList.Subheader style={[styles.listSubHeader, { color: theme.primary }]}>
{children}
</PaperList.Subheader>
);
const ListItem = ({
title,
description,
icon,
onPress,
theme,
right,
iconColor,
titleStyle,
style,
}) => (
<PaperList.Item
title={title}
style={[{ paddingHorizontal: 16, paddingVertical: 12 }, style]}
titleStyle={[{ color: theme.textColorPrimary }, titleStyle]}
description={description}
descriptionStyle={{ color: theme.textColorSecondary }}
descriptionNumberOfLines={1}
left={
icon
? () => (
<View style={{ justifyContent: 'center' }}>
<PaperList.Icon
color={theme.primary}
icon={icon}
style={{
marginVertical: 0,
}}
/>
</View>
)
: undefined
}
right={() =>
right && (
<MaterialCommunityIcons
name={right}
color={iconColor || '#47a84a'}
size={23}
style={{ marginRight: 16, textAlignVertical: 'center' }}
/>
)
}
onPress={onPress}
rippleColor={color(theme.primary).alpha(0.12).string()}
/>
);
const Divider = ({ theme }) => (
<PaperDivider
style={{
backgroundColor: color(theme.isDark ? '#FFFFFF' : '#000000')
.alpha(0.12)
.rgb()
.string(),
height: 1,
}}
/>
);
const InfoItem = ({ title, icon, theme }) => (
<PaperList.Item
title={title}
titleStyle={{ color: theme.textColorSecondary, fontSize: 14 }}
titleNumberOfLines={5}
left={() =>
icon && <PaperList.Icon color={theme.textColorSecondary} icon={icon} />
}
/>
);
const ColorItem = ({ title, description, theme, onPress }) => (
<Pressable
style={{
padding: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
onPress={onPress}
>
<View>
<Text style={{ color: theme.textColorPrimary, fontSize: 16 }}>
{title}
</Text>
<Text style={{ color: theme.textColorSecondary }}>{description}</Text>
</View>
<View
style={{
backgroundColor: description,
height: 24,
width: 24,
borderRadius: 50,
marginRight: 16,
}}
/>
</Pressable>
);
const Icon = ({ icon, theme }) => (
<PaperList.Icon color={theme.primary} icon={icon} style={{ margin: 0 }} />
);
export const List = {
Section: ListSection,
SubHeader: ListSubHeader,
Item: ListItem,
Divider,
InfoItem,
ColorItem,
Icon,
};
const styles = StyleSheet.create({
listSection: {
flex: 1,
marginVertical: 0,
},
listSubHeader: {
fontWeight: 'bold',
paddingBottom: 5,
},
});
+58 -32
View File
@@ -1,18 +1,16 @@
import React, { ReactNode } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import color from 'color';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons';
import { List as PaperList, Divider as PaperDivider } from 'react-native-paper';
import { MD3ThemeType } from '../../theme/types';
import { dividerColor } from '../../theme/colors';
import { ThemeColors } from '../../theme/types';
interface ListItemProps {
title: string;
description?: string | null;
icon?: string;
onPress?: () => void;
theme: MD3ThemeType;
theme: ThemeColors;
}
const Section = ({ children }: { children: ReactNode }) => (
@@ -24,7 +22,7 @@ const SubHeader = ({
theme,
}: {
children: ReactNode;
theme: MD3ThemeType;
theme: ThemeColors;
}) => (
<PaperList.Subheader style={{ color: theme.primary }}>
{children}
@@ -40,29 +38,28 @@ const Item: React.FC<ListItemProps> = ({
}) => (
<PaperList.Item
title={title}
titleStyle={{ color: theme.textColorPrimary }}
titleStyle={{ color: theme.onSurface }}
description={description}
descriptionStyle={{ color: theme.textColorSecondary }}
left={() => (
<View style={styles.iconContainer}>
{icon && (
<PaperList.Icon
color={theme.primary}
icon={icon}
style={styles.icon}
/>
)}
</View>
)}
descriptionStyle={{ color: theme.onSurfaceVariant }}
left={
icon
? () => (
<PaperList.Icon
color={theme.primary}
icon={icon}
style={styles.iconCtn}
/>
)
: undefined
}
onPress={onPress}
rippleColor={color(theme.primary).alpha(0.12).string()}
rippleColor={theme.rippleColor}
style={styles.listItemCtn}
/>
);
const Divider = ({ theme }: { theme: MD3ThemeType }) => (
<PaperDivider
style={[styles.divider, { backgroundColor: dividerColor(theme.isDark) }]}
/>
const Divider = ({ theme }: { theme: ThemeColors }) => (
<PaperDivider style={[styles.divider, { backgroundColor: theme.outline }]} />
);
const InfoItem = ({
@@ -72,7 +69,7 @@ const InfoItem = ({
}: {
title: string;
icon: string;
theme: MD3ThemeType;
theme: ThemeColors;
}) => (
<View style={styles.infoCtn}>
<MaterialIcon size={20} color={theme.onSurfaceVariant} name={icon} />
@@ -82,10 +79,37 @@ const InfoItem = ({
</View>
);
const Icon = ({ icon, theme }: { icon: string; theme: MD3ThemeType }) => (
const Icon = ({ icon, theme }: { icon: string; theme: ThemeColors }) => (
<PaperList.Icon color={theme.primary} icon={icon} style={{ margin: 0 }} />
);
const ColorItem = ({ title, description, theme, onPress }) => (
<Pressable
style={{
padding: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}
android_ripple={{ color: theme.rippleColor }}
onPress={onPress}
>
<View>
<Text style={{ color: theme.onSurface, fontSize: 16 }}>{title}</Text>
<Text style={{ color: theme.onSurfaceVariant }}>{description}</Text>
</View>
<View
style={{
backgroundColor: description,
height: 24,
width: 24,
borderRadius: 50,
marginRight: 16,
}}
/>
</Pressable>
);
export default {
Section,
SubHeader,
@@ -93,6 +117,7 @@ export default {
Divider,
InfoItem,
Icon,
ColorItem,
};
const styles = StyleSheet.create({
@@ -102,12 +127,7 @@ const styles = StyleSheet.create({
},
divider: {
height: 1,
},
iconContainer: {
justifyContent: 'center',
},
icon: {
marginVertical: 0,
opacity: 0.5,
},
infoCtn: {
paddingVertical: 12,
@@ -117,4 +137,10 @@ const styles = StyleSheet.create({
marginTop: 12,
fontSize: 12,
},
iconCtn: {
paddingLeft: 16,
},
listItemCtn: {
paddingVertical: 12,
},
});
+2 -3
View File
@@ -2,7 +2,6 @@ import React from 'react';
import { StyleSheet, View, Text, Pressable } from 'react-native';
import FastImage from 'react-native-fast-image';
import { defaultCoverUri } from '../sources/helpers/constants';
import { coverPlaceholderColor } from '../theme/colors';
import color from 'color';
@@ -19,7 +18,7 @@ const ListView = ({
}) => {
return (
<Pressable
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
style={[
styles.listView,
isSelected && {
@@ -36,7 +35,7 @@ const ListView = ({
style={[styles.extensionIcon, inLibraryBadge && { opacity: 0.5 }]}
/>
<Text
style={[{ color: theme.textColorPrimary }, styles.novelName]}
style={[{ color: theme.onSurface }, styles.novelName]}
numberOfLines={1}
>
{item.novelName}
@@ -1,9 +1,9 @@
import React from 'react';
import { ActivityIndicator, StyleSheet } from 'react-native';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
interface Props {
theme: MD3ThemeType;
theme: ThemeColors;
}
const LoadingMoreIndicator: React.FC<Props> = ({ theme }) => (
@@ -1,9 +1,9 @@
import React from 'react';
import { StyleSheet, ActivityIndicator } from 'react-native';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
const LoadingScreen: React.FC<{ theme: MD3ThemeType }> = ({ theme }) => (
const LoadingScreen: React.FC<{ theme: ThemeColors }> = ({ theme }) => (
<ActivityIndicator size={50} color={theme.primary} style={styles.indicator} />
);
+7 -11
View File
@@ -3,10 +3,10 @@ import { StyleSheet, View, Text, Dimensions } from 'react-native';
import { Portal, Modal } from 'react-native-paper';
import * as Linking from 'expo-linking';
import { ScrollView } from 'react-native-gesture-handler';
import Button, { ButtonVariation } from './Button/Button';
import Button from './Button/Button';
import { getString } from '@strings/translations';
import Markdown from 'react-native-markdown-display';
import { dividerColor, getDialogBackground } from '@theme/colors';
import { useTheme } from '@hooks/useTheme';
interface NewUpdateDialogProps {
@@ -31,26 +31,26 @@ const NewUpdateDialog: React.FC<NewUpdateDialogProps> = ({ newVersion }) => {
onDismiss={() => showNewUpdateDialog(false)}
contentContainerStyle={[
styles.containerStyle,
{ backgroundColor: getDialogBackground(theme) },
{ backgroundColor: theme.overlay3 },
]}
>
<Text style={[styles.modalHeader, { color: theme.textColorPrimary }]}>
<Text style={[styles.modalHeader, { color: theme.onSurface }]}>
{`${getString('common.newUpdateAvailable')} ${newVersion.tag_name}`}
</Text>
<ScrollView style={{ height: modalHeight }}>
<Markdown
style={{
body: {
color: theme.textColorSecondary,
color: theme.onSurfaceVariant,
lineHeight: 20,
paddingVertical: 8,
},
hr: {
backgroundColor: dividerColor(theme.isDark),
backgroundColor: theme.outline,
marginVertical: 16,
},
code_inline: {
backgroundColor: getDialogBackground(theme),
backgroundColor: theme.overlay3,
},
}}
>
@@ -60,14 +60,10 @@ const NewUpdateDialog: React.FC<NewUpdateDialogProps> = ({ newVersion }) => {
<View style={styles.buttonCtn}>
<Button
title={getString('common.cancel')}
theme={theme}
variation={ButtonVariation.CLEAR}
onPress={() => showNewUpdateDialog(false)}
/>
<Button
title={getString('common.install')}
theme={theme}
variation={ButtonVariation.CLEAR}
onPress={() => Linking.openURL(newVersion.downloadUrl)}
/>
</View>
+3 -3
View File
@@ -12,7 +12,7 @@ import { LinearGradient } from 'expo-linear-gradient';
import FastImage from 'react-native-fast-image';
import ListView from './ListView';
import { useDeviceOrientation } from '../services/utils/helpers';
import { useDeviceOrientation } from '@hooks/useDeviceOrientation';
import { coverPlaceholderColor } from '../theme/colors';
import { useLibrarySettings } from '@hooks/useSettings';
import { DisplayModes } from '@screens/library/constants/constants';
@@ -67,7 +67,7 @@ const NovelCover = ({
]}
>
<Pressable
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
style={styles.opac}
onPress={
selectedNovels && selectedNovels.length > 0 ? selectNovel : onPress
@@ -158,7 +158,7 @@ const ComfortableTitle = ({ theme, novelName }) => (
style={[
styles.title,
{
color: theme.textColorPrimary,
color: theme.onSurface,
padding: 4,
},
]}
+1 -2
View File
@@ -9,8 +9,7 @@ import {
} from 'react-native';
import { SourceNovelItem } from '../sources/types';
import { LibraryNovelInfo, NovelInfo } from '../database/types';
import { useDeviceOrientation } from '../services/utils/helpers';
import { useDeviceOrientation } from '@hooks/useDeviceOrientation';
export type NovelListRenderItem = ListRenderItem<
LibraryNovelInfo | NovelInfo | SourceNovelItem
+2 -6
View File
@@ -13,14 +13,10 @@ export const RadioButton = ({ value, label, theme, labelStyle }) => (
<MaterialRadioButton
value={value}
color={theme.primary}
uncheckedColor={theme.textColorSecondary}
uncheckedColor={theme.onSurfaceVariant}
/>
<Text
style={[
styles.radioButtonLabel,
{ color: theme.textColorPrimary },
labelStyle,
]}
style={[styles.radioButtonLabel, { color: theme.onSurface }, labelStyle]}
>
{label}
</Text>
+4 -5
View File
@@ -8,8 +8,7 @@ import {
ViewStyle,
} from 'react-native';
import { RadioButton as PaperRadioButton } from 'react-native-paper';
import { MD3ThemeType } from '../../theme/types';
import color from 'color';
import { ThemeColors } from '../../theme/types';
interface Props {
label: string;
@@ -17,7 +16,7 @@ interface Props {
onPress?: () => void;
style?: StyleProp<ViewStyle>;
labelStyle?: StyleProp<TextStyle>;
theme: MD3ThemeType;
theme: ThemeColors;
}
export const RadioButton: React.FC<Props> = ({
@@ -29,7 +28,7 @@ export const RadioButton: React.FC<Props> = ({
theme,
}) => (
<Pressable
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
style={[styles.pressable, style]}
onPress={onPress}
>
@@ -38,7 +37,7 @@ export const RadioButton: React.FC<Props> = ({
value={label}
onPress={onPress}
color={theme.primary}
uncheckedColor={theme.textColorSecondary}
uncheckedColor={theme.onSurfaceVariant}
/>
<Text style={[styles.label, labelStyle, { color: theme.onSurface }]}>
{label}
+4 -5
View File
@@ -1,10 +1,9 @@
import React, { useRef } from 'react';
import { Pressable, StyleSheet, View, TextInput } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import color from 'color';
import IconButtonV2 from '../IconButtonV2/IconButtonV2';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
interface RightIcon {
iconName: string;
@@ -22,7 +21,7 @@ interface SearcbarProps {
handleBackAction?: () => void;
clearSearchbar: () => void;
onLeftIconPress?: () => void;
theme: MD3ThemeType;
theme: ThemeColors;
}
const Searchbar: React.FC<SearcbarProps> = ({
@@ -53,13 +52,13 @@ const Searchbar: React.FC<SearcbarProps> = ({
marginTop,
marginRight,
marginLeft,
backgroundColor: color(theme.primary).alpha(0.08).string(),
backgroundColor: theme.surface2,
},
]}
>
<Pressable
onPress={focusSearchbar}
android_ripple={{ color: color(theme.primary).alpha(12).toString() }}
android_ripple={{ color: theme.rippleColor }}
style={[styles.searchbar]}
>
<IconButtonV2
-49
View File
@@ -1,49 +0,0 @@
import React from 'react';
import { Pressable, StyleSheet, View, Text } from 'react-native';
import color from 'color';
import { Switch, List } from 'react-native-paper';
const SwitchSetting = ({ label, description, onPress, theme, icon, value }) => (
<Pressable
android_ripple={{ color: color(theme.primary).alpha(0.12).rgb().string() }}
style={[styles.container, { paddingVertical: description ? 16 : 12 }]}
onPress={onPress}
>
<View style={{ flexDirection: 'row', flex: 1 }}>
{icon && (
<List.Icon color={theme.primary} icon={icon} style={{ margin: 0 }} />
)}
<View style={[icon && { marginLeft: 16 }]}>
<Text
style={{
color: theme.textColorPrimary,
fontSize: 16,
flex: 1,
textAlignVertical: 'center',
}}
>
{label}
</Text>
{description && (
<Text style={{ color: theme.textColorSecondary }}>{description}</Text>
)}
</View>
</View>
<Switch
value={value}
onValueChange={onPress}
color={theme.primary}
style={{ marginLeft: 8 }}
/>
</Pressable>
);
export default SwitchSetting;
const styles = StyleSheet.create({
container: {
paddingHorizontal: 16,
flexDirection: 'row',
justifyContent: 'space-between',
},
});
+77
View File
@@ -0,0 +1,77 @@
import React from 'react';
import { Pressable, StyleSheet, View, Text } from 'react-native';
import { Switch, List } from 'react-native-paper';
import { ThemeColors } from '@theme/types';
interface SwitchProps {
label: string;
description?: string;
onPress: () => void;
theme: ThemeColors;
icon?: string;
value: boolean;
}
const SwitchSetting: React.FC<SwitchProps> = ({
label,
description,
onPress,
theme,
icon,
value,
}) => {
const styles = StyleSheet.create({
container: {
paddingHorizontal: 16,
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: description ? 16 : 12,
},
row: {
flexDirection: 'row',
flex: 1,
},
icon: {
margin: 0,
},
textContainer: {
marginLeft: icon && 16,
},
text: {
fontSize: 16,
flex: 1,
textAlignVertical: 'center',
},
switch: { marginLeft: 8 },
});
return (
<Pressable
android_ripple={{
color: theme.rippleColor,
}}
style={styles.container}
onPress={onPress}
>
<View style={styles.row}>
{icon && (
<List.Icon color={theme.primary} icon={icon} style={styles.icon} />
)}
<View style={styles.textContainer}>
<Text style={[styles.text, { color: theme.onSurface }]}>{label}</Text>
{description && (
<Text style={{ color: theme.onSurfaceVariant }}>{description}</Text>
)}
</View>
</View>
<Switch
value={value}
onValueChange={onPress}
color={theme.primary}
style={styles.switch}
/>
</Pressable>
);
};
export default SwitchSetting;
+5 -8
View File
@@ -1,16 +1,15 @@
import React from 'react';
import { Pressable, StyleSheet, View, Text } from 'react-native';
import color from 'color';
import { Switch } from 'react-native-paper';
import { MD3ThemeType } from '../../theme/types';
import { ThemeColors } from '../../theme/types';
interface SwitchItemProps {
value: boolean;
label: string;
description?: string;
onPress: () => void;
theme: MD3ThemeType;
theme: ThemeColors;
}
const SwitchItem: React.FC<SwitchItemProps> = ({
@@ -21,16 +20,14 @@ const SwitchItem: React.FC<SwitchItemProps> = ({
value,
}) => (
<Pressable
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
style={[styles.container]}
onPress={onPress}
>
<View style={styles.labelContainer}>
<Text style={[{ color: theme.textColorPrimary }, styles.label]}>
{label}
</Text>
<Text style={[{ color: theme.onSurface }, styles.label]}>{label}</Text>
{description ? (
<Text style={{ color: theme.textColorSecondary }}>{description}</Text>
<Text style={{ color: theme.onSurfaceVariant }}>{description}</Text>
) : null}
</View>
<Switch
+8 -8
View File
@@ -54,7 +54,7 @@ export const ThemePicker = ({ theme, currentTheme, onPress }) => (
>
<View
style={{
backgroundColor: theme.textColorPrimary,
backgroundColor: theme.onSurface,
width: 44,
height: 10,
marginLeft: 8,
@@ -66,7 +66,7 @@ export const ThemePicker = ({ theme, currentTheme, onPress }) => (
<View
style={{
height: 18,
backgroundColor: theme.textColorSecondary,
backgroundColor: theme.onSurfaceVariant,
borderRadius: 4,
}}
/>
@@ -75,7 +75,7 @@ export const ThemePicker = ({ theme, currentTheme, onPress }) => (
style={{
height: 10,
width: 44,
backgroundColor: theme.textColorPrimary,
backgroundColor: theme.onSurface,
borderRadius: 50,
}}
/>
@@ -94,7 +94,7 @@ export const ThemePicker = ({ theme, currentTheme, onPress }) => (
style={{
height: 10,
width: 24,
backgroundColor: theme.textColorSecondary,
backgroundColor: theme.onSurfaceVariant,
borderRadius: 50,
}}
/>
@@ -102,7 +102,7 @@ export const ThemePicker = ({ theme, currentTheme, onPress }) => (
style={{
height: 10,
width: 24,
backgroundColor: theme.textColorSecondary,
backgroundColor: theme.onSurfaceVariant,
borderRadius: 50,
marginLeft: 4,
}}
@@ -134,7 +134,7 @@ export const ThemePicker = ({ theme, currentTheme, onPress }) => (
height: 12,
width: 12,
borderRadius: 50,
backgroundColor: theme.textColorPrimary,
backgroundColor: theme.onSurface,
opacity: 0.54,
}}
/>
@@ -151,7 +151,7 @@ export const ThemePicker = ({ theme, currentTheme, onPress }) => (
height: 12,
width: 12,
borderRadius: 50,
backgroundColor: theme.textColorPrimary,
backgroundColor: theme.onSurface,
opacity: 0.54,
}}
/>
@@ -161,7 +161,7 @@ export const ThemePicker = ({ theme, currentTheme, onPress }) => (
</View>
<Text
style={{
color: currentTheme.textColorPrimary,
color: currentTheme.onSurface,
fontSize: 12,
paddingVertical: 4,
}}
+6 -2
View File
@@ -14,7 +14,10 @@ import {
createHistoryTableQuery,
createChapterIdIndexQuery,
} from './tables/HistoryTable';
import { createDownloadTableQuery } from './tables/DownloadTable';
import {
createDownloadIdIndex,
createDownloadTableQuery,
} from './tables/DownloadTable';
import { createUpdatesTableQuery } from './tables/UpdateTable';
import {
addCategorySortColumnQuery,
@@ -26,7 +29,7 @@ import {
dbTxnErrorCallback,
txnErrorCallbackWithoutToast,
} from './utils/helpers';
import { noop } from 'lodash';
import { noop } from 'lodash-es';
const dbName = 'lnreader.db';
@@ -71,6 +74,7 @@ const createIndexes = () => {
tx.executeSql(createUnreadChaptersIndexQuery);
tx.executeSql(createChapterIdIndexQuery);
tx.executeSql(createCategorydIndexQuery);
tx.executeSql(createDownloadIdIndex);
});
};
+5 -3
View File
@@ -1,5 +1,5 @@
import * as SQLite from 'expo-sqlite';
import { noop } from 'lodash';
import { noop } from 'lodash-es';
import { Category } from '../types';
import { txnErrorCallback } from '../utils/helpers';
const db = SQLite.openDatabase('lnreader.db');
@@ -72,8 +72,10 @@ export const isCategoryNameDuplicate = (
tx.executeSql(
isCategoryNameDuplicateQuery,
[categoryName],
(txObj, { rows: { _array } }) =>
resolve(Boolean(_array[0]?.isDuplicate)),
(txObj, { rows }) => {
const { _array } = rows as any;
resolve(Boolean(_array[0]?.isDuplicate));
},
txnErrorCallback,
);
}),
+40 -20
View File
@@ -243,13 +243,15 @@ const createImageFolder = async (
},
): Promise<string> => {
const mkdirIfNot = async (p: string) => {
const nomediaPath =
p + (p.charAt(p.length - 1) === '/' ? '' : '/') + '.nomedia';
if (!(await RNFetchBlob.fs.exists(p))) {
await RNFetchBlob.fs.mkdir(p);
await RNFetchBlob.fs.createFile(nomediaPath, ',', 'utf8');
}
};
await mkdirIfNot(path);
await RNFetchBlob.fs.createFile(path, '.nomedia', 'utf8');
if (data) {
const { sourceId, novelId, chapterId } = data;
@@ -269,31 +271,53 @@ const downloadImages = async (
chapterId: number,
): Promise<string> => {
try {
const headers = sourceManager(sourceId)?.headers || {};
const loadedCheerio = cheerio.load(html);
const imgs = loadedCheerio('img').toArray();
for (let i = 0; i < imgs.length; i++) {
const elem = loadedCheerio(imgs[i]);
const url = elem.attr('src');
if (url) {
const imageb64 = (await RNFetchBlob.fetch('GET', url)).base64();
const imageb64 = (
await RNFetchBlob.fetch('GET', url, headers)
).base64();
const fileurl =
(await createImageFolder(
`${RNFetchBlob.fs.dirs.DownloadDir}/LNReader`,
{ sourceId, novelId, chapterId },
)) +
).catch(() => {
showToast(
`Unexpected storage error!\nRemove ${fileurl} and try downloading again`,
);
return '--';
})) +
i +
'.b64.png';
elem.replaceWith(
`<img type="file" src="${url}" file-src="${fileurl}" file-id="${i}">`,
);
const exists = await RNFetchBlob.fs.exists(fileurl);
if (fileurl.charAt(0) === '-') {
return loadedCheerio.html();
}
elem.attr('src', `file://${fileurl}`);
const exists = await RNFetchBlob.fs.exists(fileurl).catch(() => {
showToast(
`Unexpected storage error!\nRemove ${fileurl} and try downloading again`,
);
});
if (!exists) {
RNFetchBlob.fs.createFile(fileurl, imageb64, 'utf8');
RNFetchBlob.fs.createFile(fileurl, imageb64, 'base64').catch(() => {
showToast(
`Unexpected storage error!\nRemove ${fileurl} and try downloading again`,
);
});
} else {
RNFetchBlob.fs.writeFile(fileurl, imageb64, 'utf8');
RNFetchBlob.fs.writeFile(fileurl, imageb64, 'base64').catch(() => {
showToast(
`Unexpected storage error!\nRemove ${fileurl} and try downloading again`,
);
});
}
}
}
loadedCheerio('body').prepend("<input type='hidden' offline />");
return loadedCheerio.html();
} catch (e) {
return html;
@@ -350,20 +374,16 @@ const deleteDownloadedImages = async (
chapterId: number,
) => {
try {
const path = `${RNFetchBlob.fs.dirs.DownloadDir}/LNReader/`;
const files = await RNFetchBlob.fs.ls(
await createImageFolder(path, { sourceId, novelId, chapterId }),
const path = await createImageFolder(
`${RNFetchBlob.fs.dirs.DownloadDir}/LNReader`,
{ sourceId, novelId, chapterId },
);
const files = await RNFetchBlob.fs.ls(path);
for (let i = 0; i < files.length; i++) {
const ex = /(.*?)_(.*?)#(.*?)/.exec(files[i]);
const ex = /\.b64\.png/.exec(files[i]);
if (ex) {
if (
parseInt(ex[1], 10) === sourceId &&
parseInt(ex[2], 10) === chapterId
) {
if (await RNFetchBlob.fs.exists(`${path}${files[i]}`)) {
RNFetchBlob.fs.unlink(`${path}${files[i]}`);
}
if (await RNFetchBlob.fs.exists(`${path}${files[i]}`)) {
RNFetchBlob.fs.unlink(`${path}${files[i]}`);
}
}
}
+4 -1
View File
@@ -79,7 +79,10 @@ export const getChapterFromDb = async (
getChapterFromDbQuery,
[chapterId],
(txObj, results) => resolve(results.rows.item(0)),
(txObj, error) => reject(error),
(_, error) => {
reject(error);
return false;
},
);
}),
);
+9 -1
View File
@@ -6,7 +6,7 @@ import { txnErrorCallback } from '../utils/helpers';
const db = SQLite.openDatabase('lnreader.db');
const getLibraryQuery = `
SELECT novels.*, C.chaptersUnread, D.chaptersDownloaded, H.lastReadAt
SELECT novels.*, C.chaptersUnread, D.chaptersDownloaded, H.lastReadAt, U.lastUpdatedAt
FROM novels
LEFT JOIN (
SELECT chapters.novelId, COUNT(*) AS chaptersUnread
@@ -30,6 +30,14 @@ const getLibraryQuery = `
ORDER BY history.historyTimeRead DESC
) AS H
ON novels.novelId = H.novelId
LEFT JOIN (
SELECT updates.novelId, updateTime AS lastUpdatedAt
FROM updates
GROUP BY updates.novelId
HAVING updates.updateTime = MAX(updates.updateTime)
ORDER BY updates.updateTime DESC
) AS U
ON novels.novelId = U.novelId
WHERE novels.followed = 1
`;
+1 -1
View File
@@ -9,7 +9,7 @@ import { insertChapters } from './ChapterQueries';
import { showToast } from '../../hooks/showToast';
import { txnErrorCallback } from '../utils/helpers';
import { noop } from 'lodash';
import { noop } from 'lodash-es';
const insertNovelQuery =
'INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre, categoryIds) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
+1 -1
View File
@@ -1,4 +1,4 @@
import { noop, xor } from 'lodash';
import { noop, xor } from 'lodash-es';
import * as SQLite from 'expo-sqlite';
const db = SQLite.openDatabase('lnreader.db');
+1 -1
View File
@@ -1,5 +1,5 @@
import * as SQLite from 'expo-sqlite';
import { countBy } from 'lodash';
import { countBy } from 'lodash-es';
import { LibraryStats } from '../types';
import { txnErrorCallback } from '../utils/helpers';
+3
View File
@@ -7,3 +7,6 @@ export const createDownloadTableQuery = `
FOREIGN KEY (downloadChapterId) REFERENCES chapters(chapterId) ON DELETE CASCADE
)
`;
export const createDownloadIdIndex =
'CREATE INDEX IF NOT EXISTS downloadChapterIdIndex ON downloads(downloadChapterId)';
+6
View File
@@ -30,6 +30,12 @@ export interface ChapterItem {
bookmark: number;
downloaded: number;
}
export interface ChapterItemExtended extends ChapterItem {
novelId: number;
novelUrl: string;
sourceId: number;
novelName: string;
}
export interface DownloadedChapter {
downloadId: number;
+1
View File
@@ -9,3 +9,4 @@ export { default as useBoolean } from './useBoolean';
export { useTheme } from './useTheme';
export { default as useCategories } from './useCategories';
export { useCategorySettings, useLibrarySettings } from './useSettings';
export { usePreviousRouteName } from './usePreviousRouteName';
+1 -1
View File
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import sanitize from 'sanitize-html';
import { defaultTo } from 'lodash';
import { defaultTo } from 'lodash-es';
import { getChapterFromDb } from '../database/queries/DownloadQueries';
import { sourceManager } from '../sources/sourceManager';
+11
View File
@@ -0,0 +1,11 @@
import { useWindowDimensions } from 'react-native';
export const useDeviceOrientation = () => {
const window = useWindowDimensions();
if (window.width > window.height) {
return 'landscape';
} else {
return 'potrait';
}
};
+1 -2
View File
@@ -8,7 +8,6 @@ import {
hideNavigationBar,
showNavigationBar,
} from '../theme/NativeModules/NavigationBarColor';
import { readerBackground } from '../screens/reader/utils/readerStyles';
import { useTheme } from './useTheme';
const useFullscreenMode = () => {
@@ -16,7 +15,7 @@ const useFullscreenMode = () => {
const readerSettings = useReaderSettings();
const { fullScreenMode } = useSettingsV1();
const backgroundColor = readerBackground(readerSettings.theme);
const backgroundColor = readerSettings.theme;
const theme = useTheme();
+9
View File
@@ -0,0 +1,9 @@
import { useNavigationState } from '@react-navigation/native';
export const usePreviousRouteName = () => {
return useNavigationState(state =>
state.routes[state.index - 1]?.name
? state.routes[state.index - 1].name
: 'None',
);
};
+3 -1
View File
@@ -1,3 +1,4 @@
import CookieManager from '@react-native-cookies/cookies';
import { useMMKVObject } from 'react-native-mmkv';
import { MMKVStorage } from '@utils/mmkv/mmkv';
import { showToast } from './showToast';
@@ -38,7 +39,8 @@ const useSourceStorage = ({ sourceId = -1 }: { sourceId?: number }) => {
// }),
// );
// }
CookieManager.clearAll();
CookieManager.clearAll(true); //clears cookies in webkit
setValues({});
showToast('Cookies cleared');
};
+23 -80
View File
@@ -1,57 +1,37 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
import Tts from 'react-native-tts';
import { htmlToText } from '../sources/helpers/htmlToText';
export const useTextToSpeech = (html, markChapterAdRead) => {
const [ttsStatus, setTtsStatus] = useState();
const [ttsPosition, setTtsPosition] = useState({ end: 0 });
let text = htmlToText(html);
export const useTextToSpeech = (sentences, markChapterAsRead) => {
const [ttsStatus, setTtsStatus] = useState({});
const ttsPosition = useRef(0);
useEffect(() => {
Tts.addEventListener('tts-start', () => setTtsStatus('start'));
Tts.addEventListener('tts-progress', event => {
setTtsStatus('progress');
setTtsPosition(event);
});
Tts.addEventListener('tts-finish', () => {
setTtsStatus('finish');
markChapterAdRead();
if (ttsPosition.current == sentences.length - 1) {
markChapterAsRead();
setTtsStatus('finish');
}
ttsPosition.current = ttsPosition.current + 1;
});
Tts.addEventListener('tts-cancel', () => setTtsStatus('paused'));
return () => Tts.stop();
}, []);
return () => {
Tts.removeAllListeners('tts-finish');
Tts.stop();
};
}, [sentences.length]);
// for the first time rendering, chapter was not loaded.
// then htmlToText function would return
// "Chapter is empty.\n\nReport if it's available in webview." (length = 3)
const startTts = () => {
if (ttsStatus === 'progress') {
setTtsPosition({ end: 0 });
setTtsStatus('finish');
setTtsStatus('paused');
Tts.stop();
return;
}
if (text.length >= 3999) {
const splitNChars = (txt, num) => {
let result = [];
for (let i = 0; i < txt.length; i += num) {
result.push(txt.substr(i, num));
}
return result;
};
let splitMe = splitNChars(text, 3999);
splitMe.forEach(value => {
Tts.speak(value, {
androidParams: {
KEY_PARAM_STREAM: 'STREAM_MUSIC',
},
});
});
} else {
Tts.stop();
Tts.speak(text, {
setTtsStatus('progress');
Tts.stop();
for (let i = ttsPosition.current; i < sentences.length; i++) {
Tts.speak(sentences[i], {
androidParams: {
KEY_PARAM_STREAM: 'STREAM_MUSIC',
},
@@ -59,42 +39,5 @@ export const useTextToSpeech = (html, markChapterAdRead) => {
}
};
const pauseTts = () => {
if (ttsStatus === 'progress') {
setTtsStatus('paused');
Tts.stop();
return;
} else {
text = text.slice(ttsPosition.end);
if (text.length >= 3999) {
const splitNChars = (txt, num) => {
let result = [];
for (let i = 0; i < txt.length; i += num) {
result.push(txt.substr(i, num));
}
return result;
};
let splitMe = splitNChars(text, 3999);
splitMe.forEach(value => {
Tts.speak(value, {
androidParams: {
KEY_PARAM_STREAM: 'STREAM_MUSIC',
},
});
});
} else {
Tts.stop();
Tts.speak(text, {
androidParams: {
KEY_PARAM_STREAM: 'STREAM_MUSIC',
},
});
}
}
};
return [ttsStatus, ttsPosition, startTts, pauseTts];
return [ttsStatus, startTts];
};
+39 -13
View File
@@ -1,33 +1,59 @@
import { useMemo } from 'react';
import { Appearance } from 'react-native';
import {
useMMKVBoolean,
useMMKVObject,
useMMKVString,
} from 'react-native-mmkv';
import { Appearance } from 'react-native';
import { defaultTheme } from '../theme/md3/defaultTheme';
import { MD3ThemeType } from '../theme/types';
import { useMemo } from 'react';
import { overlay } from 'react-native-paper';
import Color from 'color';
export const useTheme = (): MD3ThemeType => {
const [appTheme] = useMMKVObject<MD3ThemeType>('APP_THEME');
import { defaultTheme } from '@theme/md3/defaultTheme';
import { ThemeColors } from '@theme/types';
const getElevationColor = (colors: ThemeColors, elevation: number) => {
return Color(colors.surface)
.mix(Color(colors.primary), elevation)
.rgb()
.string();
};
export const useTheme = (): ThemeColors => {
const [appTheme] = useMMKVObject<ThemeColors>('APP_THEME');
const [isAmoledBlack] = useMMKVBoolean('AMOLED_BLACK');
const [customAccent] = useMMKVString('CUSTOM_ACCENT_COLOR');
const theme: MD3ThemeType = useMemo(() => {
let colors =
const theme: ThemeColors = useMemo(() => {
const isDeviveColorSchemeDark = Appearance.getColorScheme() === 'dark';
let colors: ThemeColors =
appTheme ||
((Appearance.getColorScheme() === 'dark'
? defaultTheme.dark
: defaultTheme.light) as MD3ThemeType);
(isDeviveColorSchemeDark ? defaultTheme.dark : defaultTheme.light);
if (isAmoledBlack && colors.isDark) {
colors = { ...colors, background: '#000000', surface: '#000000' };
colors = {
...colors,
background: '#000000',
surface: '#000000',
};
}
if (customAccent) {
colors = { ...colors, primary: customAccent, secondary: customAccent };
colors = {
...colors,
primary: customAccent,
secondary: customAccent,
};
}
colors = {
...colors,
surface2: getElevationColor(colors, 0.08),
overlay3: overlay(3, colors.surface),
rippleColor: Color(colors.primary).alpha(0.12).toString(),
surfaceReader: Color(colors.surface).alpha(0.9).toString(),
};
return colors;
}, [appTheme?.id, isAmoledBlack, customAccent]);
+1 -6
View File
@@ -25,12 +25,7 @@ const BottomNavigator = () => {
return (
<Tab.Navigator
barStyle={{
backgroundColor: Color(theme.surface)
.mix(Color(theme.primary), 0.08)
.rgb()
.string(),
}}
barStyle={{ backgroundColor: theme.surface2 }}
theme={{ colors: theme }}
activeColor={theme.onSecondaryContainer}
shifting={!showLabelsInNav}
+1 -1
View File
@@ -9,7 +9,7 @@ import TrackerSettings from '../screens/settings/SettingsTrackerScreen';
import ReaderSettings from '../screens/settings/SettingsReaderScreen/SettingsReaderScreen';
import BackupSettings from '../screens/settings/SettingsBackupScreen';
import AdvancedSettings from '../screens/settings/SettingsAdvancedScreen';
import GeneralSettings from '../screens/settings/SettingsGeneralScreen';
import GeneralSettings from '../screens/settings/SettingsGeneralScreen/SettingsGeneralScreen';
import DownloadQueue from '../screens/more/DownloadQueueScreen';
import Downloads from '../screens/more/DownloadsScreen';
import AppearanceSettings from '../screens/settings/SettingsAppearanceScreen';
-6
View File
@@ -13,9 +13,6 @@ export const useSettingsV2 = () =>
export const useBrowseSettings = () =>
useAppSelector((state: RootState) => state.settingsReducerV2.browse) || {};
export const useAppearanceSettings = () =>
useAppSelector((state: RootState) => state.settingsReducerV2.appearance);
export const useReaderSettings = () =>
useAppSelector((state: RootState) => state.settingsReducer.reader);
@@ -23,9 +20,6 @@ export const useReaderSettings = () =>
export const useLibrarySettings = () =>
useAppSelector((state: RootState) => state.settingsReducerV2.library);
export const useUpdateSettings = () =>
useAppSelector((state: RootState) => state.settingsReducerV2.updates);
export const useDownloadQueue = () =>
useAppSelector((state: RootState) => state.downloadsReducer.downloadQueue);
-24
View File
@@ -39,7 +39,6 @@ import {
} from '../../database/queries/ChapterQueries';
import { deleteUpdateFromDb } from '../../database/queries/UpdateQueries';
import {
SAVE_SCROLL_POSITION,
SET_CHAPTER_LIST_PREF,
SET_LAST_READ,
} from '../preferences/preference.types';
@@ -47,16 +46,6 @@ import { showToast } from '../../hooks/showToast';
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => {
return {
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
};
},
});
import BackgroundService from 'react-native-background-actions';
import { SET_DOWNLOAD_QUEUE } from '../downloads/donwloads.types';
import { updateNovel } from '../../services/updates/LibraryUpdateQueries';
@@ -205,19 +194,6 @@ export const markChapterReadAction = (chapterId, novelId) => async dispatch => {
type: SET_LAST_READ,
payload: { novelId, chapterId: nextChapter.chapterId },
});
/**
* Reset progress on marked read
*/
dispatch({
type: SAVE_SCROLL_POSITION,
payload: {
position: 0,
percentage: 0,
chapterId,
novelId,
},
});
};
export const markPreviousChaptersReadAction =
+3 -5
View File
@@ -63,17 +63,14 @@ export const initialState = {
fullScreenMode: true,
swipeGestures: false,
showScrollPercentage: true,
useWebViewForChapter: false,
wvUseNewSwipes: false,
wvShowSwipeMargins: true,
wvUseVolumeButtons: false,
useVolumeButtons: false,
scrollAmount: 200,
showBatteryAndTime: false,
autoScroll: false,
autoScrollInterval: 10,
autoScrollOffset: null,
verticalSeekbar: true,
removeExtraParagraphSpacing: false,
useChapterDrawerSwipeNavigation: true,
reader: {
theme: '#292832',
@@ -84,6 +81,7 @@ export const initialState = {
fontFamily: '',
lineHeight: 1.5,
customCSS: '',
customJS: '',
},
};
+27 -1
View File
@@ -1,8 +1,34 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import AllSources from '../../sources/sources.json';
import { translations } from 'i18n-js';
import { localization } from '../../../strings/translations';
import { Source } from '../../sources/types';
const languages = {
'en': 'English',
'es': 'Spanish',
'tr': 'Turkish',
'ru': 'Russian',
'ar': 'Arabic',
'uk': 'Ukrainian',
'pt': 'Portuguese',
'pt-BR': 'Portuguese',
'de': 'German',
'it': 'Italian',
'zh-CN': 'Chinese',
'zh-TW': 'Chinese',
'vi': 'Vietnamese',
'ja': 'Japanese',
};
// Hope you can do something to synchronize translation system.
// Also take care of some case like vi-VN: Vietnamese not vi
export const defaultLanguage =
localization in translations || localization.split('-')[0] in translations
? languages[localization] || languages[localization.split('-')[0]]
: 'English';
// That's why this code is so long :(
interface SourcesState {
allSources: Source[];
searchResults: Source[];
@@ -16,7 +42,7 @@ const initialState: SourcesState = {
searchResults: [],
pinnedSourceIds: [],
lastUsed: null,
languageFilters: ['English'],
languageFilters: [defaultLanguage],
};
export const sourcesSlice = createSlice({
@@ -1,4 +1,4 @@
import React, { useCallback, useRef } from 'react';
import React, { useCallback, useEffect, useRef } from 'react';
import { useNavigation } from '@react-navigation/native';
import { FAB, Portal } from 'react-native-paper';
@@ -8,7 +8,7 @@ import NovelCover from '@components/NovelCover';
import Bottomsheet from '@gorhom/bottom-sheet';
import FilterBottomSheet from './components/FilterBottomSheet';
import { useCategorySettings, useSearch } from '../../hooks';
import { useCategorySettings, usePreviousRouteName, useSearch } from '@hooks';
import { useTheme } from '@hooks/useTheme';
import { useBrowseSource, useSearchSource } from './useBrowseSource';
@@ -35,6 +35,7 @@ interface BrowseSourceScreenProps {
const BrowseSourceScreen: React.FC<BrowseSourceScreenProps> = ({ route }) => {
const theme = useTheme();
const { navigate, goBack } = useNavigation();
const previousScreen = usePreviousRouteName();
const {
sourceId,
@@ -52,6 +53,7 @@ const BrowseSourceScreen: React.FC<BrowseSourceScreenProps> = ({ route }) => {
filterValues,
setFilters,
clearFilters,
refetchNovels,
} = useBrowseSource(sourceId, showLatestNovels);
const { defaultCategoryId = 1 } = useCategorySettings();
@@ -74,6 +76,12 @@ const BrowseSourceScreen: React.FC<BrowseSourceScreenProps> = ({ route }) => {
clearSearchResults();
};
useEffect(() => {
if (previousScreen === 'WebviewScreen') {
refetchNovels();
}
}, [previousScreen]);
const handleOpenWebView = async () => {
navigate('WebviewScreen', {
sourceId,
@@ -94,10 +102,8 @@ const BrowseSourceScreen: React.FC<BrowseSourceScreenProps> = ({ route }) => {
navigate(
'Novel' as never,
{
novelName: item.novelName,
novelCover: item.novelCover,
novelUrl: item.novelUrl,
sourceId,
...item,
sourceId: sourceId,
} as never,
),
[sourceId],
@@ -123,7 +129,6 @@ const BrowseSourceScreen: React.FC<BrowseSourceScreenProps> = ({ route }) => {
) : errorMessage || novelList.length === 0 ? (
<ErrorScreenV2
error={errorMessage || getString('sourceScreen.noResultsFound')}
theme={theme}
/>
) : (
<NovelList
@@ -1,6 +1,5 @@
import React, { Ref, useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import color from 'color';
import BottomSheet from '../../../components/BottomSheet/BottomSheet';
import BottomSheetType, { BottomSheetView } from '@gorhom/bottom-sheet';
@@ -13,10 +12,8 @@ import {
} from '../../../sources/types/filterTypes';
import { FlatList } from 'react-native-gesture-handler';
import { Button, IconButtonV2 } from '@components/index';
import { ButtonVariation } from '@components/Button/Button';
import { dividerColor } from '../../../theme/colors';
import { Checkbox } from '@components/Checkbox/Checkbox';
import { defaultTo } from 'lodash';
import { defaultTo } from 'lodash-es';
import { Picker } from '@react-native-picker/picker';
import useBoolean from '@hooks/useBoolean';
import { overlay } from 'react-native-paper';
@@ -44,7 +41,7 @@ const FilterItem: React.FC<FilterItemProps> = ({
if (filter.inputType === FilterInputs.Picker) {
return (
<View style={styles.pickerContainer}>
<Text style={[styles.pickerLabel, { color: theme.textColorSecondary }]}>
<Text style={[styles.pickerLabel, { color: theme.onSurfaceVariant }]}>
{filter.label}
</Text>
<Picker
@@ -77,11 +74,9 @@ const FilterItem: React.FC<FilterItemProps> = ({
<Pressable
style={styles.checkboxHeader}
onPress={toggleCard}
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
>
<Text
style={[styles.filterLabel, { color: theme.textColorSecondary }]}
>
<Text style={[styles.filterLabel, { color: theme.onSurfaceVariant }]}>
{filter.label}
</Text>
<IconButtonV2
@@ -149,28 +144,23 @@ const FilterBottomSheet: React.FC<BottomSheetProps> = ({
]}
>
<BottomSheetView
style={[
styles.buttonContainer,
{ borderBottomColor: dividerColor(theme.isDark) },
]}
style={[styles.buttonContainer, { borderBottomColor: theme.outline }]}
>
<Button
title={'Reset'}
variation={ButtonVariation.CLEAR}
onPress={() => {
setSelectedFilters(undefined);
clearFilters();
}}
theme={theme}
/>
<Button
title={'Filter'}
textColor={theme.textColorPrimary}
textColor={theme.onSurface}
onPress={() => {
setFilters(selectedFilters);
filterSheetRef?.current?.collapse();
}}
theme={theme}
mode="contained"
/>
</BottomSheetView>
<FlatList
@@ -11,17 +11,15 @@ export const useBrowseSource = (
const [novels, setNovels] = useState<SourceNovelItem[]>([]);
const [error, setError] = useState<string>();
const [totalPages, setTotalPages] = useState(1);
const [currentPage, setCurrentPage] = useState(1);
const [filterValues, setFilterValues] = useState<SourceFilter[]>();
const [selectedFilters, setSelectedFilters] = useState<
SelectedFilter | undefined
>();
const [hasNextPage, setHasNextPage] = useState(true);
const isScreenMounted = useRef(true);
const hasNextPage = currentPage <= totalPages;
const fetchNovels = useCallback(
async (page: number, filters?: SelectedFilter) => {
if (isScreenMounted.current === true) {
@@ -34,7 +32,9 @@ export const useBrowseSource = (
setNovels(prevState =>
page === 1 ? res.novels : [...prevState, ...res.novels],
);
setTotalPages(res.totalPages);
if (!res.novels.length) {
setHasNextPage(false);
}
setFilterValues(source.filters);
} catch (err: unknown) {
setError(`${err}`);
@@ -54,6 +54,11 @@ export const useBrowseSource = (
fetchNovels(currentPage, selectedFilters);
}, [fetchNovels, currentPage]);
const refetchNovels = () => {
setCurrentPage(1);
fetchNovels(1);
};
/**
* On screen unmount
*/
@@ -81,6 +86,7 @@ export const useBrowseSource = (
filterValues,
setFilters,
clearFilters,
refetchNovels,
};
};
+1 -1
View File
@@ -16,7 +16,7 @@ import { getString } from '@strings/translations';
import { Category } from '../../database/types';
import CategoryCard from './components/CategoryCard';
import { orderBy } from 'lodash';
import { orderBy } from 'lodash-es';
import CategorySkeletonLoading from './components/CategorySkeletonLoading';
const CategoriesScreen = () => {
@@ -2,7 +2,6 @@ import React, { useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Modal, overlay, Portal, TextInput } from 'react-native-paper';
import { ButtonVariation } from '@components/Button/Button';
import { Button } from '@components/index';
import { Category } from '../../../database/types';
@@ -44,7 +43,7 @@ const AddCategoryModal: React.FC<AddCategoryModalProps> = ({
{ backgroundColor: overlay(2, theme.surface) },
]}
>
<Text style={[styles.modalTitle, { color: theme.textColorPrimary }]}>
<Text style={[styles.modalTitle, { color: theme.onSurface }]}>
{getString(
isEditMode
? 'categories.editCategories'
@@ -57,14 +56,12 @@ const AddCategoryModal: React.FC<AddCategoryModalProps> = ({
placeholder={getString('common.name')}
onChangeText={setCategoryName}
mode="outlined"
underlineColor={theme.textColorHint}
underlineColor={theme.outline}
theme={{ colors: { ...theme } }}
/>
<View style={styles.btnContainer}>
<Button
title={getString(isEditMode ? 'common.ok' : 'common.add')}
theme={theme}
variation={ButtonVariation.CLEAR}
onPress={async () => {
if (await isCategoryNameDuplicate(categoryName)) {
showToast(getString('categories.duplicateError'));
@@ -80,12 +77,7 @@ const AddCategoryModal: React.FC<AddCategoryModalProps> = ({
closeModal();
}}
/>
<Button
title={getString('common.cancel')}
theme={theme}
variation={ButtonVariation.CLEAR}
onPress={closeModal}
/>
<Button title={getString('common.cancel')} onPress={closeModal} />
</View>
</Modal>
</Portal>
@@ -2,13 +2,13 @@ import React, { memo } from 'react';
import { StyleSheet, View } from 'react-native';
import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder';
import { LinearGradient } from 'expo-linear-gradient';
import { MD3ThemeType } from '@theme/types';
import { ThemeColors } from '@theme/types';
import getLoadingColors from '@utils/getLoadingColors';
interface Props {
width: number;
height: number;
theme: MD3ThemeType;
theme: ThemeColors;
}
const CategorySkeletonLoading: React.FC<Props> = ({ height, width, theme }) => {
@@ -2,7 +2,6 @@ import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Modal, Portal } from 'react-native-paper';
import { ButtonVariation } from '@components/Button/Button';
import { Button } from '@components/index';
import { Category } from '../../../database/types';
@@ -11,7 +10,6 @@ import { useTheme } from '@hooks/useTheme';
import { getString } from '@strings/translations';
import { resetCategoryIdsToDefault } from '../../../database/queries/NovelQueriesV2';
import { getDialogBackground } from '../../../theme/colors';
interface DeleteCategoryModalProps {
category: Category;
@@ -35,21 +33,19 @@ const DeleteCategoryModal: React.FC<DeleteCategoryModalProps> = ({
onDismiss={closeModal}
contentContainerStyle={[
styles.modalContainer,
{ backgroundColor: getDialogBackground(theme) },
{ backgroundColor: theme.overlay3 },
]}
>
<Text style={[styles.modalTitle, { color: theme.textColorPrimary }]}>
<Text style={[styles.modalTitle, { color: theme.onSurface }]}>
{getString('categories.deleteModal.header')}
</Text>
<Text style={[styles.modalDesc, { color: theme.textColorSecondary }]}>
<Text style={[styles.modalDesc, { color: theme.onSurfaceVariant }]}>
{getString('categories.deleteModal.desc')}
{` "${category.name}"?`}
</Text>
<View style={styles.btnContainer}>
<Button
title={getString('common.ok')}
theme={theme}
variation={ButtonVariation.CLEAR}
onPress={() => {
deleteCategoryById(category.id);
resetCategoryIdsToDefault(category.id);
@@ -57,12 +53,7 @@ const DeleteCategoryModal: React.FC<DeleteCategoryModalProps> = ({
onSuccess();
}}
/>
<Button
title={getString('common.cancel')}
theme={theme}
variation={ButtonVariation.CLEAR}
onPress={closeModal}
/>
<Button title={getString('common.cancel')} onPress={closeModal} />
</View>
</Modal>
</Portal>
@@ -1,18 +1,17 @@
import { Dimensions, Pressable, StyleSheet, Text, View } from 'react-native';
import React, { useMemo } from 'react';
import color from 'color';
import FastImage from 'react-native-fast-image';
import { coverPlaceholderColor } from '../../../theme/colors';
import { SourceNovelItem } from '../../../sources/types';
import { MD3ThemeType } from '../../../theme/types';
import { ThemeColors } from '../../../theme/types';
import { getString } from '@strings/translations';
interface Props {
novel: SourceNovelItem;
navigateToNovel: (novel: SourceNovelItem) => void;
theme: MD3ThemeType;
theme: ThemeColors;
onLongPress?: () => void;
inLibrary?: boolean;
}
@@ -37,7 +36,7 @@ const GlobalSearchNovelItem: React.FC<Props> = ({
<View style={styles.novelItem}>
<Pressable
style={styles.pressable}
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
onPress={() => navigateToNovel(novel)}
onLongPress={onLongPress}
>
@@ -60,7 +59,7 @@ const GlobalSearchNovelItem: React.FC<Props> = ({
) : null}
<Text
style={[
{ color: theme.textColorPrimary, width: novelItemDimensions.width },
{ color: theme.onSurface, width: novelItemDimensions.width },
styles.novelName,
]}
numberOfLines={2}
@@ -66,13 +66,11 @@ const GlobalSearchResultsList: React.FC<GlobalSearchResultsListProps> = ({
}
>
<View>
<Text
style={[styles.sourceName, { color: theme.textColorPrimary }]}
>
<Text style={[styles.sourceName, { color: theme.onSurface }]}>
{item.source.sourceName}
</Text>
<Text
style={[styles.language, { color: theme.textColorSecondary }]}
style={[styles.language, { color: theme.onSurfaceVariant }]}
>
{item.source.lang}
</Text>
@@ -80,7 +78,7 @@ const GlobalSearchResultsList: React.FC<GlobalSearchResultsListProps> = ({
<MaterialCommunityIcons
name="arrow-right"
size={24}
color={theme.textColorPrimary}
color={theme.onSurface}
/>
</Pressable>
{item.isLoading ? (
@@ -101,7 +99,7 @@ const GlobalSearchResultsList: React.FC<GlobalSearchResultsListProps> = ({
<Text
style={[
styles.listEmpty,
{ color: theme.textColorSecondary },
{ color: theme.onSurfaceVariant },
]}
>
{getString('sourceScreen.noResultsFound')}
+5 -5
View File
@@ -72,7 +72,7 @@ const StatsScreen = () => {
return (
<>
{Header}
<ErrorScreenV2 theme={theme} />
<ErrorScreenV2 error={error} />
</>
);
}
@@ -84,7 +84,7 @@ const StatsScreen = () => {
style={styles.screenCtn}
contentContainerStyle={styles.contentCtn}
>
<Text style={[styles.header, { color: theme.textColorSecondary }]}>
<Text style={[styles.header, { color: theme.onSurfaceVariant }]}>
{getString('moreScreen.settingsScreen.generalSettings')}
</Text>
<Row style={styles.statsRow}>
@@ -114,7 +114,7 @@ const StatsScreen = () => {
<Row style={styles.statsRow}>
<StatsCard label="Sources" value={stats.sourcesCount} />
</Row>
<Text style={[styles.header, { color: theme.textColorSecondary }]}>
<Text style={[styles.header, { color: theme.onSurfaceVariant }]}>
{getString('statsScreen.genreDistribution')}
</Text>
<Row style={[styles.statsRow, styles.genreRow]}>
@@ -122,7 +122,7 @@ const StatsScreen = () => {
<StatsCard key={item[0]} label={item[0]} value={item[1]} />
))}
</Row>
<Text style={[styles.header, { color: theme.textColorSecondary }]}>
<Text style={[styles.header, { color: theme.onSurfaceVariant }]}>
{getString('statsScreen.statusDistribution')}
</Text>
<Row style={[styles.statsRow, styles.genreRow]}>
@@ -159,7 +159,7 @@ export const StatsCard: React.FC<{ label: string; value?: number }> = ({
]}
>
<Text style={[styles.statsVal, { color: theme.primary }]}>{value}</Text>
<Text style={{ color: theme.textColorPrimary }}> {label}</Text>
<Text style={{ color: theme.onSurface }}> {label}</Text>
</View>
);
};
+4 -3
View File
@@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo } from 'react';
import { EmptyView, SearchbarV2 } from '../../components';
import {
defaultLanguage,
getSourcesAction,
searchSourcesAction,
setLastUsedSource,
@@ -42,7 +43,7 @@ const BrowseScreen = () => {
allSources,
searchResults,
pinnedSourceIds = [],
languageFilters = ['English'],
languageFilters = [defaultLanguage || 'English'], //defaultLang cant be null, but just for sure
lastUsed,
} = useSourcesReducer();
@@ -159,7 +160,7 @@ const BrowseScreen = () => {
<Text
style={[
styles.sectionHeader,
{ color: theme.textColorSecondary },
{ color: theme.onSurfaceVariant },
]}
>
{getString('browseScreen.discover')}
@@ -174,7 +175,7 @@ const BrowseScreen = () => {
<Text
style={[
styles.sectionHeader,
{ color: theme.textColorSecondary },
{ color: theme.onSurfaceVariant },
]}
>
{header}
+1 -1
View File
@@ -42,7 +42,7 @@ const SourceNovels = ({ navigation, route }) => {
ListEmptyComponent={
<Text
style={{
color: theme.textColorSecondary,
color: theme.onSurfaceVariant,
padding: 20,
textAlign: 'center',
}}
@@ -1,19 +1,17 @@
import React from 'react';
import { StyleSheet, Text, View, Image, Pressable } from 'react-native';
import color from 'color';
import { getString } from '../../../../../strings/translations';
import { Button, IconButtonV2 } from '../../../../components';
import { ButtonVariation } from '../../../../components/Button/Button';
import { Source } from '../../../../sources/types';
import { coverPlaceholderColor } from '../../../../theme/colors';
import { MD3ThemeType } from '../../../../theme/types';
import { ThemeColors } from '../../../../theme/types';
interface Props {
source: Source;
isPinned: boolean;
theme: MD3ThemeType;
theme: ThemeColors;
onTogglePinSource: (sourceId: number) => void;
navigateToSource: (source: Source, showLatestNovels?: boolean) => void;
}
@@ -28,15 +26,13 @@ const SourceCard: React.FC<Props> = ({
<Pressable
style={styles.container}
onPress={() => navigateToSource(source)}
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
>
<View style={styles.flexRow}>
<Image source={{ uri: source.icon }} style={styles.icon} />
<View style={styles.details}>
<Text style={{ color: theme.textColorPrimary }}>
{source.sourceName}
</Text>
<Text style={[{ color: theme.textColorSecondary }, styles.lang]}>
<Text style={{ color: theme.onSurface }}>{source.sourceName}</Text>
<Text style={[{ color: theme.onSurfaceVariant }, styles.lang]}>
{source.lang}
</Text>
</View>
@@ -44,15 +40,13 @@ const SourceCard: React.FC<Props> = ({
<View style={styles.flexRow}>
<Button
title={getString('browseScreen.latest')}
variation={ButtonVariation.CLEAR}
textColor={theme.primary}
onPress={() => navigateToSource(source, true)}
theme={theme}
/>
<IconButtonV2
name={isPinned ? 'pin' : 'pin-outline'}
size={22}
color={isPinned ? theme.primary : theme.textColorSecondary}
color={isPinned ? theme.primary : theme.onSurfaceVariant}
onPress={() => onTogglePinSource(source.sourceId)}
theme={theme}
/>
@@ -1,15 +1,13 @@
import { useNavigation } from '@react-navigation/native';
import { getRippleColor } from '@theme/colors';
import React from 'react';
import { StyleSheet, Text, View, Image, Pressable } from 'react-native';
import { getString } from '../../../../../strings/translations';
import { Button } from '../../../../components';
import { ButtonVariation } from '../../../../components/Button/Button';
import { MD3ThemeType } from '../../../../theme/types';
import { ThemeColors } from '../../../../theme/types';
interface Props {
theme: MD3ThemeType;
theme: ThemeColors;
}
const MalCard: React.FC<Props> = ({ theme }) => {
@@ -19,7 +17,7 @@ const MalCard: React.FC<Props> = ({ theme }) => {
<Pressable
style={styles.container}
onPress={() => navigate('BrowseMal' as never)}
android_ripple={{ color: getRippleColor(theme.primary) }}
android_ripple={{ color: theme.rippleColor }}
>
<View style={styles.flexRow}>
<Image
@@ -27,16 +25,14 @@ const MalCard: React.FC<Props> = ({ theme }) => {
style={styles.icon}
/>
<View style={styles.details}>
<Text style={{ color: theme.textColorPrimary }}>MyAnimeList</Text>
<Text style={{ color: theme.onSurface }}>MyAnimeList</Text>
</View>
</View>
<View style={styles.flexRow}>
<Button
title={getString('browse')}
variation={ButtonVariation.CLEAR}
textColor={theme.primary}
onPress={() => navigate('BrowseMal' as never)}
theme={theme}
/>
</View>
</Pressable>
@@ -1,55 +1,47 @@
import { getDialogBackground, getRippleColor } from '@theme/colors';
import React from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import FastImage from 'react-native-fast-image';
import { MD3ThemeType } from '../../../../theme/types';
import { ThemeColors } from '../../../../theme/types';
interface Props {
novel: { novelName: string; novelCover: string; score: string; info: string };
onPress: () => void;
theme: MD3ThemeType;
theme: ThemeColors;
}
const MalNovelCard: React.FC<Props> = ({ novel, onPress, theme }) => {
return (
<View
style={[
styles.container,
{ backgroundColor: getDialogBackground(theme) },
]}
>
<View style={[styles.container, { backgroundColor: theme.overlay3 }]}>
<Pressable
style={styles.pressable}
onPress={onPress}
android_ripple={{ color: getRippleColor(theme.primary) }}
android_ripple={{ color: theme.rippleColor }}
>
<FastImage source={{ uri: novel.novelCover }} style={styles.cover} />
<View style={styles.infoContainer}>
<Text
style={[styles.title, { color: theme.textColorPrimary }]}
style={[styles.title, { color: theme.onSurface }]}
numberOfLines={2}
>
{novel.novelName}
</Text>
<Text style={[styles.small, { color: theme.textColorPrimary }]}>
<Text style={[styles.small, { color: theme.onSurface }]}>
Score:{' '}
<Text style={{ color: theme.textColorSecondary }}>
{novel.score}
</Text>
<Text style={{ color: theme.onSurfaceVariant }}>{novel.score}</Text>
</Text>
{novel?.info?.[1] ? (
<Text style={[styles.small, { color: theme.textColorPrimary }]}>
<Text style={[styles.small, { color: theme.onSurface }]}>
Type:{' '}
<Text style={{ color: theme.textColorSecondary }}>
<Text style={{ color: theme.onSurfaceVariant }}>
{novel.info[1]}
</Text>
</Text>
) : null}
{novel?.info?.[2] ? (
<Text style={[styles.small, { color: theme.textColorPrimary }]}>
<Text style={[styles.small, { color: theme.onSurface }]}>
Published:{' '}
<Text style={{ color: theme.textColorSecondary }}>
<Text style={{ color: theme.onSurfaceVariant }}>
{novel.info[2]}
</Text>
</Text>
@@ -29,7 +29,7 @@ const GlobalSearchNovelCover = ({
/>
<Text
numberOfLines={2}
style={[styles.title, { color: theme.textColorPrimary }]}
style={[styles.title, { color: theme.onSurface }]}
>
{novelName}
</Text>
@@ -1,11 +1,11 @@
import React, { memo } from 'react';
import { View, StyleSheet } from 'react-native';
import { MD3ThemeType } from '@theme/types';
import { ThemeColors } from '@theme/types';
import LoadingNovel from './LoadingNovel';
import getLoadingColors from '@utils/getLoadingColors';
interface Props {
theme: MD3ThemeType;
theme: ThemeColors;
}
const GlobalSearchSkeletonLoading: React.FC<Props> = ({ theme }) => {
@@ -2,12 +2,12 @@ import React, { memo } from 'react';
import { View, Dimensions, StyleSheet } from 'react-native';
import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder';
import { LinearGradient } from 'expo-linear-gradient';
import { MD3ThemeType } from '@theme/types';
import { getDialogBackground } from '@theme/colors';
import { ThemeColors } from '@theme/types';
import getLoadingColors from '@utils/getLoadingColors';
interface Props {
theme: MD3ThemeType;
theme: ThemeColors;
}
const MalLoading: React.FC<Props> = ({ theme }) => {
@@ -53,7 +53,7 @@ const MalLoading: React.FC<Props> = ({ theme }) => {
return <View style={styles.container}>{items.map(renderLoadingRect)}</View>;
};
const createStyleSheet = (theme: MD3ThemeType) => {
const createStyleSheet = (theme: ThemeColors) => {
return StyleSheet.create({
container: {
position: 'relative',
@@ -67,7 +67,7 @@ const createStyleSheet = (theme: MD3ThemeType) => {
loadingContainer: {
margin: 10,
borderRadius: 8,
backgroundColor: getDialogBackground(theme),
backgroundColor: theme.overlay3,
width: Dimensions.get('window').width - 20,
overflow: 'hidden',
flexDirection: 'row',
@@ -1,14 +1,14 @@
import React, { memo, useMemo } from 'react';
import { View, StyleSheet, useWindowDimensions } from 'react-native';
import { MD3ThemeType } from '@theme/types';
import { ThemeColors } from '@theme/types';
import getLoadingColors from '@utils/getLoadingColors';
import LoadingNovel from '@screens/browse/loadingAnimation/LoadingNovel';
import { useLibrarySettings } from '@hooks/useSettings';
import { useDeviceOrientation } from '../../../services/utils/helpers';
import { DisplayModes } from '@screens/library/constants/constants';
import { useDeviceOrientation } from '@hooks/useDeviceOrientation';
interface Props {
theme: MD3ThemeType;
theme: ThemeColors;
}
const SourceScreenSkeletonLoading: React.FC<Props> = ({ theme }) => {
+1 -1
View File
@@ -32,7 +32,7 @@ const GlobalSearch = ({ navigation }) => {
);
const ListHeaderComponent = (
<Text style={[{ color: theme.textColorSecondary }, styles.listHeader]}>
<Text style={[{ color: theme.onSurfaceVariant }, styles.listHeader]}>
Select a Source To Migrate From
</Text>
);
@@ -7,9 +7,8 @@ import GlobalSearchNovelCover from '../globalsearch/GlobalSearchNovelCover';
import { migrateNovel } from '../../../database/queries/NovelQueries';
import { showToast } from '../../../hooks/showToast';
import { getDialogBackground } from '@theme/colors';
import { Button } from '@components';
import { ButtonVariation } from '@components/Button/Button';
import { getString } from '@strings/translations';
import { openNovel } from '@utils/handleNavigateParams';
@@ -61,7 +60,7 @@ const MigrationNovelList = ({ data, theme, library, navigation }) => {
ListEmptyComponent={
<Text
style={{
color: theme.textColorSecondary,
color: theme.onSurfaceVariant,
padding: 8,
paddingVertical: 4,
}}
@@ -78,12 +77,12 @@ const MigrationNovelList = ({ data, theme, library, navigation }) => {
padding: 24,
margin: 20,
borderRadius: 28,
backgroundColor: getDialogBackground(theme),
backgroundColor: theme.overlay3,
}}
>
<Text
style={{
color: theme.textColorPrimary,
color: theme.onSurface,
fontSize: 18,
marginBottom: 16,
}}
@@ -97,15 +96,11 @@ const MigrationNovelList = ({ data, theme, library, navigation }) => {
}}
>
<Button
variation={ButtonVariation.CLEAR}
theme={theme}
onPress={hideMigrateNovelDialog}
title={getString('common.cancel')}
/>
<Button
Button
variation={ButtonVariation.CLEAR}
theme={theme}
onPress={async () => {
hideMigrateNovelDialog();
await migrateNovel(
@@ -101,8 +101,8 @@ const MigrationNovels = ({ navigation, route }) => {
const renderItem = ({ item }) => (
<>
<View style={{ padding: 8, paddingVertical: 16 }}>
<Text style={{ color: theme.textColorPrimary }}>{item.sourceName}</Text>
<Text style={{ color: theme.textColorSecondary, fontSize: 12 }}>
<Text style={{ color: theme.onSurface }}>{item.sourceName}</Text>
<Text style={{ color: theme.onSurfaceVariant, fontSize: 12 }}>
{item.lang}
</Text>
</View>
@@ -16,7 +16,7 @@ const MigrationSourceCard = ({ item, theme, noOfNovels, onPress }) => {
<View style={styles.sourceDetailsContainer}>
<Text
style={{
color: theme.textColorPrimary,
color: theme.onSurface,
fontSize: 14,
}}
>
@@ -24,7 +24,7 @@ const MigrationSourceCard = ({ item, theme, noOfNovels, onPress }) => {
</Text>
<Text
style={{
color: theme.textColorSecondary,
color: theme.onSurfaceVariant,
fontSize: 12,
}}
>
+2 -2
View File
@@ -112,13 +112,13 @@ const HistoryScreen = () => {
{isLoading ? (
<HistorySkeletonLoading theme={theme} />
) : error ? (
<ErrorScreenV2 error={error} theme={theme} />
<ErrorScreenV2 error={error} />
) : (
<>
<SectionList
contentContainerStyle={styles.listContainer}
sections={groupHistoryByDate(searchText ? searchResults : history)}
keyExtractor={item => item.chapterId.toString()}
keyExtractor={(item, index) => 'history' + index}
renderSectionHeader={({ section: { date } }) => (
<Text style={[styles.dateHeader, { color: theme.onSurface }]}>
{dayjs(date).calendar()}
@@ -1,14 +1,14 @@
import React from 'react';
import { StyleSheet } from 'react-native';
import { Dialog, Portal } from 'react-native-paper';
import { Button } from '@components';
import { getString } from '@strings/translations';
import { Button, Dialog, Portal } from 'react-native-paper';
import { MD3ThemeType } from '../../../theme/types';
import { ThemeColors } from '@theme/types';
interface ClearHistoryDialogProps {
visible: boolean;
theme: MD3ThemeType;
theme: ThemeColors;
onSubmit: () => void;
onDismiss: () => void;
}
@@ -26,17 +26,17 @@ const ClearHistoryDialog: React.FC<ClearHistoryDialogProps> = ({
return (
<Portal>
<Dialog visible={visible} onDismiss={onDismiss} style={styles.container}>
<Dialog.Title style={[styles.title, { color: theme.textColorPrimary }]}>
<Dialog
visible={visible}
onDismiss={onDismiss}
style={[styles.container, { backgroundColor: theme.overlay3 }]}
>
<Dialog.Title style={[styles.title, { color: theme.onSurface }]}>
{getString('historyScreen.clearHistorWarning')}
</Dialog.Title>
<Dialog.Actions>
<Button theme={{ colors: { ...theme } }} onPress={onDismiss}>
{getString('common.cancel')}
</Button>
<Button theme={{ colors: { ...theme } }} onPress={handleOnSubmit}>
{getString('common.ok')}
</Button>
<Button onPress={onDismiss}>{getString('common.cancel')}</Button>
<Button onPress={handleOnSubmit}>{getString('common.ok')}</Button>
</Dialog.Actions>
</Dialog>
</Portal>
@@ -47,7 +47,8 @@ export default ClearHistoryDialog;
const styles = StyleSheet.create({
container: {
borderRadius: 8,
margin: 20,
borderRadius: 28,
},
title: {
letterSpacing: 0,
@@ -1,17 +1,15 @@
import React, { useMemo } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import color from 'color';
import dayjs from 'dayjs';
import FastImage from 'react-native-fast-image';
import { IconButtonV2 } from '../../../../components';
import { IconButtonV2 } from '@components';
import { parseChapterNumber } from '@utils/parseChapterNumber';
import { parseChapterNumber } from '../../../../utils/parseChapterNumber';
import { History } from '../../../../database/types';
import { MD3ThemeType } from '../../../../theme/types';
import { coverPlaceholderColor } from '../../../../theme/colors';
import { History } from '@database/types';
import { ThemeColors } from '@theme/types';
import { coverPlaceholderColor } from '@theme/colors';
import {
openChapterChapterTypes,
openChapterNovelTypes,
@@ -26,7 +24,7 @@ interface HistoryCardProps {
) => void;
handleRemoveFromHistory: (historyId: number) => void;
handleNavigateToNovel: (novel: openNovelProps) => void;
theme: MD3ThemeType;
theme: ThemeColors;
}
const HistoryCard: React.FC<HistoryCardProps> = ({
@@ -61,7 +59,7 @@ const HistoryCard: React.FC<HistoryCardProps> = ({
return (
<Pressable
style={styles.container}
android_ripple={{ color: color(theme.primary).alpha(0.12).string() }}
android_ripple={{ color: theme.rippleColor }}
onPress={() =>
handleNavigateToNovel({
sourceId,
@@ -81,7 +79,7 @@ const HistoryCard: React.FC<HistoryCardProps> = ({
>
{novelName}
</Text>
<Text style={{ color: theme.textColorSecondary }}>
<Text style={{ color: theme.onSurfaceVariant }}>
{chapterNoAndTime}
</Text>
</View>
@@ -2,11 +2,11 @@ import React, { memo } from 'react';
import { StyleSheet, View } from 'react-native';
import { createShimmerPlaceholder } from 'react-native-shimmer-placeholder';
import { LinearGradient } from 'expo-linear-gradient';
import { MD3ThemeType } from '@theme/types';
import { ThemeColors } from '@theme/types';
import getLoadingColors from '@utils/getLoadingColors';
interface Props {
theme: MD3ThemeType;
theme: ThemeColors;
}
const HistorySkeletonLoading: React.FC<Props> = ({ theme }) => {
+3 -9
View File
@@ -29,8 +29,7 @@ import {
import { unfollowNovel } from '../../database/queries/NovelQueries';
import SetCategoryModal from '@screens/novel/components/SetCategoriesModal';
import useBoolean from '@hooks/useBoolean';
import { debounce, intersection } from 'lodash';
import { ButtonVariation } from '@components/Button/Button';
import { debounce, intersection } from 'lodash-es';
import { useBackHandler } from '@hooks/useBackHandler';
import { openChapter } from '@utils/handleNavigateParams';
import useHistory from '@hooks/useHistory';
@@ -120,7 +119,7 @@ const LibraryScreen = () => {
)}
inactiveColor={theme.secondary}
activeColor={theme.primary}
pressColor={color(theme.primary).alpha(0.12).rgb().string()}
pressColor={theme.rippleColor}
/>
);
@@ -219,10 +218,7 @@ const LibraryScreen = () => {
title={`${getString(
'common.searchFor',
)} "${searchText}" ${getString('common.globally')}`}
theme={theme}
variation={ButtonVariation.CLEAR}
style={styles.globalSearchBtn}
labelStyle={styles.globalSearchBtnLabel}
onPress={() =>
navigate(
'GlobalSearchScreen' as never,
@@ -328,9 +324,7 @@ const styles = StyleSheet.create({
globalSearchBtn: {
margin: 16,
},
globalSearchBtnLabel: {
fontWeight: 'bold',
},
fab: {
position: 'absolute',
margin: 16,
+2 -2
View File
@@ -2,14 +2,14 @@ import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { MD3ThemeType } from '../../../theme/types';
import { ThemeColors } from '../../../theme/types';
interface Props {
label: string;
icon?: string;
backgroundColor?: string;
textColor?: string;
theme: MD3ThemeType;
theme: ThemeColors;
}
export const Banner: React.FC<Props> = ({
@@ -108,7 +108,7 @@ const ThirdRoute = () => {
return (
<View style={{ flex: 1 }}>
<Text style={[styles.sectionHeader, { color: theme.textColorSecondary }]}>
<Text style={[styles.sectionHeader, { color: theme.onSurfaceVariant }]}>
{getString('libraryScreen.bottomSheet.display.badges')}
</Text>
<Checkbox
@@ -141,7 +141,7 @@ const ThirdRoute = () => {
}
theme={theme}
/>
<Text style={[styles.sectionHeader, { color: theme.textColorSecondary }]}>
<Text style={[styles.sectionHeader, { color: theme.onSurfaceVariant }]}>
{getString('libraryScreen.bottomSheet.display.displayMode')}
</Text>
<FlashList
@@ -184,7 +184,7 @@ const LibraryBottomSheet: React.FC<LibraryBottomSheetProps> = ({
renderLabel={({ route, color }) => (
<Text style={{ color }}>{route.title}</Text>
)}
inactiveColor={theme.textColorSecondary}
inactiveColor={theme.onSurfaceVariant}
activeColor={theme.primary}
pressColor={color(theme.primary).alpha(0.12).string()}
/>
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { RefreshControl, View } from 'react-native';
import { xor } from 'lodash';
import { xor } from 'lodash-es';
import { useNavigation } from '@react-navigation/native';
import { EmptyView } from '@components/index';
+12 -5
View File
@@ -40,31 +40,38 @@ export enum LibrarySortOrder {
DateAdded_DESC = 'novelId DESC',
LastRead_ASC = 'lastReadAt ASC',
LastRead_DESC = 'lastReadAt DESC',
LastUpdated_ASC = 'lastUpdatedAt ASC',
LastUpdated_DESC = 'lastUpdatedAt DESC',
}
export const librarySortOrderList = [
{
label: 'Alphabetically',
label: getString('libraryScreen.bottomSheet.sortOrders.alphabetically'),
ASC: LibrarySortOrder.Alphabetically_ASC,
DESC: LibrarySortOrder.Alphabetically_DESC,
},
{
label: 'Last Read',
label: getString('libraryScreen.bottomSheet.sortOrders.lastRead'),
ASC: LibrarySortOrder.LastRead_ASC,
DESC: LibrarySortOrder.LastRead_DESC,
},
{
label: 'Unread',
label: getString('libraryScreen.bottomSheet.sortOrders.lastUpdated'),
ASC: LibrarySortOrder.LastUpdated_ASC,
DESC: LibrarySortOrder.LastUpdated_DESC,
},
{
label: getString('libraryScreen.bottomSheet.sortOrders.unread'),
ASC: LibrarySortOrder.Unread_ASC,
DESC: LibrarySortOrder.Unread_DESC,
},
{
label: 'Downloaded',
label: getString('libraryScreen.bottomSheet.sortOrders.download'),
ASC: LibrarySortOrder.Downloaded_ASC,
DESC: LibrarySortOrder.Downloaded_DESC,
},
{
label: 'Total chapters',
label: getString('libraryScreen.bottomSheet.sortOrders.totalChapters'),
ASC: LibrarySortOrder.TotalChapters_ASC,
DESC: LibrarySortOrder.TotalChapters_DESC,
},
+3 -5
View File
@@ -46,7 +46,7 @@ const DownloadQueue = ({ navigation }) => {
anchor={
<MaterialAppbar.Action
icon="dots-vertical"
iconColor={theme.textColorPrimary}
iconColor={theme.onSurface}
onPress={openMenu}
/>
}
@@ -61,7 +61,7 @@ const DownloadQueue = ({ navigation }) => {
closeMenu();
}}
title="Cancel downloads"
titleStyle={{ color: theme.textColorPrimary }}
titleStyle={{ color: theme.onSurface }}
/>
</Menu>
</Appbar>
@@ -71,9 +71,7 @@ const DownloadQueue = ({ navigation }) => {
data={downloadQueue}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text style={{ color: theme.textColorPrimary }}>
{item.chapterName}
</Text>
<Text style={{ color: theme.onSurface }}>{item.chapterName}</Text>
<ProgressBar
indeterminate={BackgroundService.isRunning() ? true : false}
progress={!BackgroundService.isRunning() && 0}

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