Compare commits

...

259 Commits

Author SHA1 Message Date
Rajarshee Chatterjee 8084cdbffb feat(source): Add ArthurScans
LNReader/lnreader-sources#71
2021-09-23 11:31:45 +05:30
Rajarshee Chatterjee dc9529c322 feat(source): Add ReaperScans Br
LNReader/lnreader-sources#71
2021-09-23 10:40:26 +05:30
Rajarshee Chatterjee 91168a335b Minor fixes 2021-09-23 10:39:08 +05:30
Rajarshee Chatterjee a6b3eb9350 Add text to speech play/pause 2021-09-21 09:51:01 +05:30
Rajarshee Chatterjee 0ce4e6b985 Increase bottom padding 2021-09-21 09:49:47 +05:30
Rajarshee Chatterjee 6684848a42 Fix FAB text color 2021-09-21 08:56:03 +05:30
Rajarshee Chatterjee 87d12cabb3 fix(source): RanobeHub 2021-09-21 08:51:40 +05:30
Rajarshee Chatterjee e01fc21a49 feat(source): Add Rain Of Snow
Closes LNReader/lnreader-sources#60
2021-09-15 10:09:09 +05:30
Rajarshee Chatterjee f0cfd9066b feat(source): Add Novel De Glace
Closes LNReader/lnreader-sources#57
2021-09-15 09:41:58 +05:30
Rajarshee Chatterjee c2cc6ea96b Option to clear updates tab 2021-09-15 09:26:24 +05:30
Rajarshee Chatterjee eeeea6b4e0 feat(source): Add Travis Translations
Closes (lnreader-sources#59)
2021-09-13 16:48:10 +05:30
Rajarshee Chatterjee ddce153b53 fix(source): ReaperScans 2021-09-13 16:01:11 +05:30
Rajarshee Chatterjee 38e45ca097 Downloaded only mode 2021-09-13 15:36:05 +05:30
Rajarshee Chatterjee a6a60607a7 Minor fixes 2021-09-12 08:46:58 +05:30
Rajarshee Chatterjee d3e772f0dd Add option to remove backdrop 2021-09-12 08:46:35 +05:30
Rajarshee Chatterjee 095d46681f fix(source): TuNovelaLigera 2021-09-12 08:46:06 +05:30
Rajarshee Chatterjee 3e4e967197 Enlarge cover 2021-09-11 21:42:54 +05:30
Rajarshee Chatterjee c5b4e7b902 Option to use fab instead of button 2021-09-11 10:13:41 +05:30
Rajarshee Chatterjee e9421a5fd0 fix: WPMangaStream (Closes #75) 2021-09-11 08:44:18 +05:30
Rajarshee Chatterjee afef0d63ce Edit status in edit info modal 2021-09-11 08:40:32 +05:30
Rajarshee Chatterjee e68a5c5f82 Minor fixes 2021-09-10 10:04:07 +05:30
Rajarshee Chatterjee ff1635b817 fix(source): Fix ReadLightNovel
Closes LNReader/LNReader-sources#65
2021-09-10 09:35:25 +05:30
Rajarshee Chatterjee f1ca221b91 Add option to only update ongoing novels (Closes #73) 2021-09-10 09:21:13 +05:30
Rajarshee Chatterjee 5a1106fc68 Add Text to Speech (Closes #22) 2021-08-31 10:01:23 +05:30
Rajarshee Chatterjee 3b49a1e516 Minor fixes 2021-08-31 08:03:33 +05:30
Rajarshee Chatterjee 85acbc8c4a Appearance settings 2021-08-29 11:02:24 +05:30
Rajarshee Chatterjee 66e0be5e84 Add option to turn off fullscreen (Closes #70) 2021-08-28 07:27:05 +05:30
Rajarshee Chatterjee 2234006272 Fix fullscreen not working on changing chapters 2021-08-28 07:00:48 +05:30
Rajarshee Chatterjee eca00ddcfa Experimental auto scroll (Closes #63) 2021-08-27 14:28:15 +05:30
Rajarshee Chatterjee 2328e55105 Show time and battery level (Closes #65) 2021-08-27 11:43:17 +05:30
Rajarshee Chatterjee b8a25b9172 Multiple select in chapters (Closes #69) 2021-08-26 21:50:05 +05:30
Rajarshee Chatterjee 9af8d55985 Add option to download unread chapters 2021-08-26 20:18:07 +05:30
Rajarshee Chatterjee 4777f453a4 Refactor themes 2021-08-25 11:15:40 +05:30
Rajarshee Chatterjee 4a0195762d Cross fade 2021-08-25 08:52:51 +05:30
Rajarshee Chatterjee e8785c2905 Minor UI adjustments 2021-08-24 19:26:11 +05:30
Rajarshee Chatterjee 1302cbde16 Draw content under navbar 2021-08-24 16:09:09 +05:30
Rajarshee Chatterjee 43d0adced8 feat(source): Add Mystical Series
Closes LNReader/LNReader-sources#61
2021-08-24 11:16:19 +05:30
Rajarshee Chatterjee 0c8b30cd72 feat(source): Add WuxiaWorld.Site
Closes LNReader/LNReader-sources#58
2021-08-24 11:08:58 +05:30
Rajarshee Chatterjee cc86fc7da4 feat(source): Add EpikNovel
Closes LNReader/LNReader-sources#55
2021-08-24 10:56:16 +05:30
Rajarshee Chatterjee 625acdb585 Release v1.1.4 2021-08-15 13:37:13 +05:30
Rajarshee Chatterjee 93617e60bd feat(source): Add Light Novel Updates 2021-08-14 08:56:59 +05:30
Rajarshee Chatterjee d75c607f67 feat(source): Add NovelOnlineFull.com 2021-08-14 08:04:31 +05:30
Marcin Majewski dd087ff485 fix(chapterlist): Bookmark not updating when added (#64)
When a chapter was bookmarked the list wasn't updating
2021-08-14 07:34:04 +05:30
Rajarshee Chatterjee 5da3e05bba feat: Show last update time 2021-08-13 10:12:26 +05:30
Rajarshee Chatterjee e628c3cfd6 fix: Remove text shadow in comfortable grid 2021-08-13 09:32:13 +05:30
Rajarshee Chatterjee deba021384 Readwn.com: Add user agents 2021-08-12 18:23:43 +05:30
Rajarshee Chatterjee c6d2ba8442 feat: Use tabs for settings and preview in reader settings 2021-08-12 18:21:56 +05:30
Rajarshee Chatterjee 6a3736dced fix: Crash when long pressing a cover in browse 2021-08-12 16:21:22 +05:30
Rajarshee Chatterjee f63ae573ba fix: Mark chapter as unread 2021-08-12 16:19:41 +05:30
Rajarshee Chatterjee 8f3020a544 Release v1.1.3 2021-08-11 16:25:05 +05:30
Rajarshee Chatterjee 04e32a4234 Minor fixes 2021-08-11 16:24:47 +05:30
Rajarshee Chatterjee 9b3d65dded refactor: Reorganize reader sheet 2021-08-11 09:53:18 +05:30
Rajarshee Chatterjee aa3a5c2d0c Minor fixes 2021-08-10 14:32:25 +05:30
Rajarshee Chatterjee 2aab5621a6 feat: Select novels in library 2021-08-10 13:34:19 +05:30
Rajarshee Chatterjee b4b7e6770d feat(source): Add Divine Dao Library
Closes LNReader/lnreader-sources#54
2021-08-09 12:44:32 +05:30
Rajarshee Chatterjee 0ed65d64e4 fix: novelCover crash in list view 2021-08-09 07:26:04 +05:30
Rajarshee Chatterjee a7aaa7d230 feat: Custom CSS 2021-08-07 12:08:31 +05:30
Rajarshee Chatterjee e358b3753f feat(source): Add ranobehub 2021-08-07 08:34:08 +05:30
Rajarshee Chatterjee 780a67e1a6 Update README.md 2021-08-06 10:50:00 +05:30
Rajarshee Chatterjee 5b57e78486 Update README.md 2021-08-06 10:37:22 +05:30
Rajarshee Chatterjee 6430c11cae Update README.md 2021-08-06 10:31:09 +05:30
Rajarshee Chatterjee 322bfc224d Full backups (Closes #34) 2021-08-05 12:24:54 +05:30
Rajarshee Chatterjee a14d35f031 Minor fixes 2021-08-05 12:23:59 +05:30
Rajarshee Chatterjee f03b1703e2 fix(source): ArNovel (Closes #61)
Closes LNReader/lnreader-sources#52
2021-08-04 09:20:35 +05:30
Rajarshee Chatterjee 40d2052f16 Custom covers and open links in reader 2021-08-03 14:38:54 +05:30
Rajarshee Chatterjee 01814e92d0 Fix empty novelCover crash 2021-08-03 10:52:13 +05:30
Rajarshee Chatterjee dffc9c3b99 fix(source): LightNovelPub 2021-08-03 10:48:12 +05:30
Rajarshee Chatterjee 8bb3ffe82c fix(source): ComradeMao 2021-08-03 10:40:34 +05:30
Rajarshee Chatterjee c746081dee Minor fixes 2021-08-03 09:44:14 +05:30
Rajarshee Chatterjee d25fe85a64 Fix discord link 2021-08-02 16:52:32 +05:30
Rajarshee Chatterjee 92d9d53da7 Fix no novelCover crash 2021-07-31 13:32:00 +05:30
Rajarshee Chatterjee 17fcd96f16 Source fixes 2021-07-30 09:15:15 +05:30
Rajarshee Chatterjee 31bada1c1a Refactor 2021-07-29 20:49:21 +05:30
Rajarshee Chatterjee 605bd6a514 Add edit info modal 2021-07-29 11:17:13 +05:30
Rajarshee Chatterjee 01bac76bed Minor fixes 2021-07-29 09:35:55 +05:30
Rajarshee Chatterjee b5ed62d9f8 Fix global search and splashscreen 2021-07-28 17:47:23 +05:30
Rajarshee Chatterjee fd21cc6e85 Minor fixes 2021-07-28 12:14:38 +05:30
Rajarshee Chatterjee 40f1495cf1 Add webview option to support tables and original text style 2021-07-28 11:46:57 +05:30
Rajarshee Chatterjee 983b069dba Add Readwn.com source 2021-07-28 09:47:12 +05:30
Rajarshee Chatterjee e21101ca28 Fix build 2021-07-26 17:01:18 +05:30
Bread Genie d381e878bc docs(readme): Add badges (#60) 2021-07-26 16:59:43 +05:30
Rajarshee Chatterjee bcf5632668 Remove history from redux state and minor fixes 2021-07-26 08:24:59 +05:30
Bread Genie ac8bca7f03 feat(sources): Add ReaperScans Novels (#59) 2021-07-25 21:38:33 +05:30
Bread Genie 62a5e27b1e fix: Indonesia -> Indonesian (#58) 2021-07-25 19:19:08 +05:30
Rajarshee Chatterjee 7c5a9a407f Add new sources 2021-07-23 09:04:47 +05:30
Rajarshee Chatterjee d8d231cb90 Add source WLNUpdates 2021-07-22 09:08:40 +05:30
Rajarshee Chatterjee a5f90eaf8b Add new sources 2021-07-21 20:32:30 +05:30
Rajarshee Chatterjee ec50189ea4 Minor fixes 2021-07-21 08:34:45 +05:30
Rajarshee Chatterjee ccdccdb6bb Remove prevChapter and nextChapter 2021-07-20 08:38:55 +05:30
Bread Genie 059f440713 fix(source): nextChapter link undefined (#55) 2021-07-20 07:55:48 +05:30
Rajarshee Chatterjee b695f57ef4 Refactor 2021-07-19 09:32:48 +05:30
Rajarshee Chatterjee db86b707e7 Fix bookmark icon 2021-07-19 06:00:51 +05:30
Rajarshee Chatterjee 7db4e4b872 Minor fixes 2021-07-18 18:21:25 +05:30
Rajarshee Chatterjee a2c6e9fe58 Custom checkbox 2021-07-18 10:31:07 +05:30
Rajarshee Chatterjee 6c27993454 Remove [hr]() chapter content 2021-07-18 10:30:21 +05:30
Rajarshee Chatterjee 63de0a3794 Fix long chapter names (Closes #53) 2021-07-17 20:24:32 +05:30
Marcin Majewski 93fbcf1246 fix(syosetu): change oneshot chapter name (#52)
Changed oneshot chapter names to "Oneshot" to avoid too long chapters names destroying UI.
2021-07-17 19:55:24 +05:30
Rajarshee Chatterjee 50357f7d6f Release v1.1.2 2021-07-17 08:12:04 +05:30
Rajarshee Chatterjee 1a8d640db1 Add delete downloads dialog and refactor themes 2021-07-17 07:13:00 +05:30
Rajarshee Chatterjee dc6b2deddd Minor fixes 2021-07-16 22:05:49 +05:30
Rajarshee Chatterjee a9fcf2fe08 Fix NovelCover 2021-07-16 17:46:04 +05:30
Rajarshee Chatterjee f195b9e6df Minor fixes 2021-07-16 15:50:01 +05:30
Rajarshee Chatterjee d07fd26cb3 Refactor 2021-07-16 15:22:36 +05:30
Rajarshee Chatterjee 5aee0d1b04 Refactor 2021-07-15 22:51:50 +05:30
Rajarshee Chatterjee c4457d74c4 Parse links in chapter 2021-07-15 08:52:04 +05:30
Rajarshee Chatterjee ba90a6fb84 Refactor sources and show headings 2021-07-14 12:27:07 +05:30
Bread Genie e7424cea06 Fix: source images (#50)
One of the source image was linked to a fork and rest were linked to the old extension repo (this would still work, but for consistency's sake every image is linked to the repo under the org).
2021-07-14 10:19:16 +05:30
Rajarshee Chatterjee e274feda6e Minor fixes 2021-07-13 17:07:55 +05:30
Bread Genie 8ec2378191 feat(source): Add Yushubo (#48) 2021-07-13 17:05:42 +05:30
Rajarshee Chatterjee ef57a98681 Minor fixes 2021-07-12 09:49:05 +05:30
Rajarshee Chatterjee bdda959f36 Improve novel updates 2021-07-11 16:14:17 +05:30
Rajarshee Chatterjee 04f3ba2b45 Refactor themes 2021-07-11 09:43:12 +05:30
Rajarshee Chatterjee f1a261ab28 Add ranobes.net 2021-07-10 10:52:26 +05:30
Rajarshee Chatterjee da2ee3b883 Pause/Resume downloads in queue 2021-07-09 17:58:19 +05:30
Marcin Majewski b98d3861b4 format: Setting Prettier formating in package.json (#47)
Because of different formatting on some computers
Prettier settings were set in package.json file so that everyone
will format their code in the same way using Prettier
2021-07-09 15:13:56 +05:30
Rajarshee Chatterjee ff59330d20 Add Novel Updates 2021-07-09 14:24:26 +05:30
Rajarshee Chatterjee b0639e79a8 Add new sources 2021-07-09 08:51:08 +05:30
Rajarshee Chatterjee 5931a0dc07 Cleanup 2021-07-08 11:22:45 +05:30
Rajarshee Chatterjee cf40acb725 Create multisrc 2021-07-07 14:56:01 +05:30
Rajarshee Chatterjee a2974ccb44 Merge 2021-07-06 20:11:45 +05:30
Rajarshee Chatterjee 5eda558f09 Add LNMTL 2021-07-06 20:07:45 +05:30
Marcin Majewski cf7f1402d3 Fixed Syosetu name and Oneshot not working (#46)
* Fixed Syosetu name and Oneshot not working

Oneshots didn't work at all because of use of scaner from lnreader-sources
Oneshots now load properly desciptions (it didn't work before)
Syosetsu changed into Syosetu as it should be

* Remove unnecessary console.logs
2021-07-06 20:06:36 +05:30
Rajarshee Chatterjee 834a8d9e2e Minor fix 2021-07-06 06:16:28 +05:30
Rajarshee Chatterjee fe5b3f4a5b Add Syosetu 2021-07-06 06:11:30 +05:30
Rajarshee Chatterjee ec4eaf4d49 Remove mipmap-anydpi-v26 folder 2021-07-05 16:15:20 +05:30
Rajarshee Chatterjee 0c0b599452 v1.1.0 2021-07-04 21:04:18 +05:30
Rajarshee Chatterjee ac26c67361 Fix build workflow 2021-07-04 10:45:19 +05:30
Rajarshee Chatterjee d205572a5e Add Gradle cache 2021-07-04 10:36:53 +05:30
Rajarshee Chatterjee e5d52fcd9d Fix build workflow 2021-07-04 10:23:39 +05:30
Rajarshee Chatterjee 61424e533a Minor fixes 2021-07-04 10:16:29 +05:30
Rajarshee Chatterjee c3ef170385 Lightnovelpub: Fix chapterUrl 2021-07-03 16:31:55 +05:30
Rajarshee Chatterjee 430c7a7341 Minor fixes 2021-07-03 14:59:55 +05:30
Rajarshee Chatterjee e4b934061b History and UI fixes 2021-07-03 13:43:14 +05:30
Rajarshee Chatterjee 3579671ade Fix global search and migration 2021-07-03 13:42:48 +05:30
Rajarshee Chatterjee 893d229c25 Add themes 2021-07-03 11:52:31 +05:30
Rajarshee Chatterjee ea3f5ac7ee Add jump to chapter 2021-07-03 11:24:04 +05:30
Rajarshee Chatterjee e4027efbb1 Add update library on start option and save select text option 2021-07-03 10:06:07 +05:30
Rajarshee Chatterjee f5dd7153cc Add details in novel updates & mal 2021-06-30 10:23:26 +05:30
Rajarshee Chatterjee 38b0097261 Add Scribble Hub 2021-06-30 10:22:00 +05:30
Rajarshee Chatterjee 2f7215512f Fix continue reading button 2021-06-27 19:18:00 +05:30
Rajarshee Chatterjee 9a96a34cda Add NovelPassion & Royal Road 2021-06-27 19:17:43 +05:30
Rajarshee Chatterjee a7f6d2cd3c Fix sources 2021-06-27 10:51:27 +05:30
Rajarshee Chatterjee 5dfe6a7ab8 Minor fixes 2021-06-24 07:53:30 +05:30
Rajarshee Chatterjee 3fb35e2d32 v1.1.0 2021-06-20 23:06:06 +05:30
Rajarshee Chatterjee 35d5cf987a Fixes 2021-06-20 23:03:02 +05:30
Rajarshee Chatterjee 7e28d12a8a v1.1.0 2021-06-20 21:46:16 +05:30
Rajarshee Chatterjee ff575de2e8 Fixes 2021-06-20 21:44:58 +05:30
Rajarshee Chatterjee 962fcc534d Minor fixes 2021-06-20 18:19:50 +05:30
Rajarshee Chatterjee acd8843b0e Fix sources 2021-06-20 17:49:53 +05:30
Rajarshee Chatterjee c956d764c5 Add option to search all sources 2021-06-20 17:39:31 +05:30
Rajarshee Chatterjee ca0f62f118 Add pagination 2021-06-20 11:20:40 +05:30
Rajarshee Chatterjee 4d766555b7 Add incognito mode 2021-06-20 08:31:51 +05:30
Rajarshee Chatterjee edcc4c0bcd Use background service for downloads and updates (fixes #36) 2021-06-19 13:47:07 +05:30
Rajarshee Chatterjee 8e1a9274e5 Rename 2021-06-19 09:18:50 +05:30
Rajarshee Chatterjee d0b11c9b47 Rename folders 2021-06-19 09:17:52 +05:30
Rajarshee Chatterjee 3a9784a7a0 Fix launch crash on android 7 2021-06-15 09:46:50 +05:30
Rajarshee Chatterjee 66e3945427 Add option to disbale swipe gestures 2021-06-15 09:46:24 +05:30
Rajarshee Chatterjee 7773d50607 Filter sources by languages 2021-06-13 10:47:41 +05:30
Rajarshee Chatterjee 721d3842fb Fix swipe directions 2021-06-12 20:29:07 +05:30
Rajarshee Chatterjee 773c33f3bf Add swipe gestures 2021-06-12 15:52:42 +05:30
Rajarshee Chatterjee 1addb54690 Vertical Scrollbar 2021-06-12 14:32:34 +05:30
Rajarshee Chatterjee ce29a5456d Html to text 2021-06-11 17:54:07 +05:30
Rajarshee Chatterjee 4eda2d4624 Add action bar and use pressable 2021-06-11 17:53:33 +05:30
Rajarshee Chatterjee bae2fa40f3 Chapter footer 2021-06-11 17:52:34 +05:30
Rajarshee Chatterjee cf5d9e18bb Update all english sources 2021-06-10 21:53:59 +05:30
Rajarshee Chatterjee e87ecb48b4 Add more sources and fix sources 1-9 2021-06-10 18:29:13 +05:30
Rajarshee Chatterjee d4140fcc52 (~_~;) 2021-06-10 15:46:39 +05:30
Rajarshee Chatterjee 4858f22a88 Parse chapter 2021-06-10 11:51:22 +05:30
Rajarshee Chatterjee 6aa977a562 Add ReadLightNovel 2021-06-10 10:33:12 +05:30
Rajarshee Chatterjee 499e2b1333 Release v1.0.21 2021-06-09 15:28:26 +05:30
Rajarshee Chatterjee 810e8c2407 Minor fixes 2021-06-09 14:42:51 +05:30
Rajarshee Chatterjee 4e4f02c5e6 Add Novel Updates 2021-06-09 12:50:47 +05:30
Rajarshee Chatterjee 50cd2af10c Add reader settings and use pressable 2021-06-09 10:09:58 +05:30
Rajarshee Chatterjee 4618b82d2b Refactor History 2021-06-08 17:14:11 +05:30
Rajarshee Chatterjee 9c7d136c6d Refactor 2021-06-08 10:27:26 +05:30
Rajarshee Chatterjee 8b4b55dcf3 Refactor 2021-06-07 18:25:58 +05:30
Rajarshee Chatterjee b9d9cee32f Refactor searchbar 2021-06-07 10:40:12 +05:30
Rajarshee Chatterjee 22078309eb Custom background and text color 2021-06-06 20:35:04 +05:30
Rajarshee Chatterjee 223f00ef7d Eject to bare workflow 2021-06-06 10:35:53 +05:30
Rajarshee Chatterjee 4b5ee1667c Release v1.0.19 2021-05-31 22:54:49 +05:30
Rajarshee Chatterjee d5b3915ef1 Minor fixes 2021-05-31 21:20:17 +05:30
Rajarshee Chatterjee 7b41f10dd6 Add empty view component 2021-05-31 21:19:29 +05:30
Rajarshee Chatterjee b12eef2699 Global search from novel name 2021-05-31 21:18:26 +05:30
Rajarshee Chatterjee 7a8c8ded24 Add option to select text 2021-05-31 21:16:41 +05:30
Rajarshee Chatterjee b6e8b0057b Use tabs in chapter filter sheet 2021-05-31 17:08:48 +05:30
Rajarshee Chatterjee c06ce93581 Global search from library 2021-05-31 16:00:48 +05:30
Rajarshee Chatterjee c4c156eb1d Minor fixes 2021-05-31 15:38:19 +05:30
Rajarshee Chatterjee f1d86b798e UI fixes 2021-05-31 15:38:05 +05:30
Rajarshee Chatterjee 4f1aa7c2cc History fixes 2021-05-31 15:37:07 +05:30
Rajarshee Chatterjee f8199b4e4e Reorganize library filter sheet 2021-05-31 15:32:42 +05:30
Rajarshee Chatterjee 82e7111a40 Add searchbar in history screen (#24) 2021-05-31 09:13:15 +05:30
Rajarshee Chatterjee a15369967f Add custom accent color 2021-05-30 18:19:59 +05:30
Rajarshee Chatterjee 5a936bea02 Rename extensions to sources 2021-05-30 18:19:39 +05:30
Rajarshee Chatterjee 13b49dfd21 Minor UI changes 2021-05-30 18:19:14 +05:30
Rajarshee Chatterjee 689185bab6 Display images if links are present in chapterText 2021-05-30 18:18:25 +05:30
Rajarshee Chatterjee 917dea2261 Fix white border between screens 2021-05-29 18:59:53 +05:30
Rajarshee Chatterjee 6afcdcf96e Reorganize novel details 2021-05-29 17:15:30 +05:30
Rajarshee Chatterjee b74066111f Parse images in chapter and reorganize reader sheet 2021-05-29 12:04:24 +05:30
Rajarshee Chatterjee b42fb6319b Add hide chapter titles 2021-05-28 17:11:23 +05:30
Rajarshee Chatterjee cb05231335 Group updates and history by date 2021-05-28 10:30:07 +05:30
Rajarshee Chatterjee 7819c7dcdb Add selecting chapters logic 2021-05-27 10:37:43 +05:30
Rajarshee Chatterjee 9a7e1f7e53 Refactor Chapter 2021-05-25 10:32:21 +05:30
Rajarshee Chatterjee 2c2e814a0d Fix reader sheet buttons 2021-05-20 10:17:33 +05:30
Rajarshee Chatterjee f4fc3d096f Refactor NovelHeader 2021-05-17 09:50:01 +05:30
Rajarshee Chatterjee d230b64846 Refactor settings and minor fixes 2021-05-16 18:44:37 +05:30
Rajarshee Chatterjee a280fc95e1 Reset progress if marked read 2021-05-16 16:36:42 +05:30
Rajarshee Chatterjee 1f87a50762 Fix scroll to saved progress 2021-05-16 08:58:39 +05:30
Rajarshee Chatterjee 99945aa0d8 Release v1.0.18 2021-05-14 23:29:08 +05:30
Rajarshee Chatterjee fda59f825f Fix library filters and minor fixes 2021-05-14 21:00:55 +05:30
Rajarshee Chatterjee 67096eb1ff Add library sort/filter sheet and notification icon 2021-05-14 18:30:15 +05:30
Rajarshee Chatterjee 3a156f32a0 Save scroll percentage and add new theme 2021-05-14 12:03:32 +05:30
Rajarshee Chatterjee 555552a64d Update app icon and splash screen 2021-05-14 09:14:31 +05:30
Rajarshee Chatterjee 53fc800b90 Minor fixes 2021-05-14 08:56:46 +05:30
Rajarshee Chatterjee d1e10433e2 Add bottomsheet for chapter actions 2021-05-13 08:21:46 +05:30
Rajarshee Chatterjee cc53e309ad Fix last read chapter getting reset on opening another novel (fixes #19) 2021-05-12 14:20:48 +05:30
Rajarshee Chatterjee 2170b77607 Add swipe to mark chapter read/ bookmark logic 2021-05-12 10:00:46 +05:30
Rajarshee Chatterjee 632c2e47c7 Add scroll percentage in chapter screen (fixes #17) 2021-05-11 08:43:42 +05:30
Rajarshee Chatterjee 5e2c4d4e27 Add searchbar in updates screen 2021-05-10 09:15:09 +05:30
Rajarshee Chatterjee a21800f208 Minor fixes 2021-05-09 10:10:09 +05:30
Rajarshee Chatterjee 5f943fcc92 Merge branch 'main' 2021-05-09 10:00:59 +05:30
Rajarshee Chatterjee 8f254b4ef5 Add Migration (#15)
* Refactor

* Add migration

* Finish migration

* Add progress bar
2021-05-09 09:57:21 +05:30
Rajarshee Chatterjee 773913048c Refactor 2021-05-07 09:36:07 +05:30
Rajarshee Chatterjee daf9444e87 Merge branch 'main' of https://github.com/rajarsheechatterjee/LNReader into main 2021-05-06 21:00:17 +05:30
Rajarshee Chatterjee e4b1b757e6 Fix navigating to novel from history screen 2021-05-06 20:59:22 +05:30
Rajarshee Chatterjee b01e63791e Release v1.0.17 2021-05-06 20:55:23 +05:30
Rajarshee Chatterjee ffe4950966 Update screenshots 2021-05-06 17:07:25 +05:30
Rajarshee Chatterjee caac2c3227 Refactor 2021-05-06 16:10:22 +05:30
Rajarshee Chatterjee 489d5ba714 Add global search (#12)
* Add global search

* Split GlobalSearch into components
2021-05-05 20:17:17 +05:30
Rajarshee Chatterjee 4251ec7f20 Pin sources and refactor 2021-05-05 14:19:08 +05:30
Rajarshee Chatterjee c1e0b01df8 Refactor and some fixes 2021-05-04 21:44:17 +05:30
Rajarshee Chatterjee 66dc1c4bdb fix continue reading button 2021-05-04 15:13:12 +05:30
Rajarshee Chatterjee 1ba88a16ff Save chapter sort and filter settings 2021-05-04 12:15:02 +05:30
Rajarshee Chatterjee 381c779c36 Release v1.0.16 2021-05-03 22:06:08 +05:30
Rajarshee Chatterjee 90df6b5aca Refactor 2021-05-03 20:49:06 +05:30
Rajarshee Chatterjee 87ff17d5e5 Use Expo StorageAccessFramework for backups 2021-05-01 17:51:50 +05:30
Rajarshee Chatterjee 913d319222 Check github for new release (closes #9) 2021-05-01 17:24:33 +05:30
Rajarshee Chatterjee cca143383e Upgrade Expo SDK to 41 (closes #7) 2021-05-01 08:38:06 +05:30
Rajarshee Chatterjee 4a874b30be Move useTheme hook to Hooks folder 2021-05-01 08:11:50 +05:30
Rajarshee Chatterjee e958b0fa09 Refactor 2021-05-01 07:57:16 +05:30
Rajarshee Chatterjee a484d550a8 Fix navigation to novel from history item 2021-04-30 23:35:48 +05:30
Rajarshee Chatterjee 4acafdb741 Add backup/restore (closes #6) 2021-04-30 23:18:09 +05:30
Rajarshee Chatterjee 1e6c4758ff Check github for new release on launch 2021-04-30 20:52:05 +05:30
Rajarshee Chatterjee 1ed0e8b4c4 Fix tracker crashes and update app version 2021-04-30 12:01:34 +05:30
Rajarshee Chatterjee 458cdb2c2a Add update chapter progress after reading 2021-04-30 11:16:14 +05:30
Rajarshee Chatterjee 0a476ba6b4 Update api url 2021-04-30 11:15:58 +05:30
Rajarshee Chatterjee 6e85909f6f Finish mal tracking and refactor 2021-04-29 20:36:32 +05:30
Rajarshee Chatterjee 5642122a5e Add updating mal list logic 2021-04-28 22:33:41 +05:30
Rajarshee Chatterjee 77481dfc06 Add MAL tracking logic 2021-04-27 20:33:22 +05:30
Rajarshee Chatterjee a870442022 Remove unused dependencies 2021-04-26 20:43:48 +05:30
Rajarshee Chatterjee a9a176297e Use single screen for all extensions 2021-04-26 18:34:12 +05:30
Rajarshee Chatterjee f4c0a647fb Add fonts in reader 2021-04-26 17:02:36 +05:30
Rajarshee Chatterjee 06e7e45df0 Fix theme name 2021-04-26 12:07:19 +05:30
Rajarshee Chatterjee c538bf4956 Add logo in more screen 2021-04-26 12:06:59 +05:30
Rajarshee Chatterjee e1679a41f1 Fix navigating to chapter from history screen 2021-04-26 12:05:35 +05:30
Rajarshee Chatterjee 3c2f4a59b7 Refactor settings screen 2021-04-26 11:34:04 +05:30
Rajarshee Chatterjee 8c13b1ff12 Add discord link 2021-04-25 19:59:48 +05:30
Rajarshee Chatterjee 45083e5fd1 Fix menu color in light theme 2021-04-25 19:48:49 +05:30
Rajarshee Chatterjee 3b37c1cb33 Disable OTA updates 2021-04-25 08:25:53 +05:30
Rajarshee Chatterjee 48187d7d54 Fix reader padding and update version 2021-04-24 21:55:19 +05:30
302 changed files with 43629 additions and 8035 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

+45
View File
@@ -0,0 +1,45 @@
name: CI
on:
push:
branches:
- main
jobs:
install-and-test:
name: Install dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install npm dependencies
run: |
npm install
build-android:
name: Build app
needs: install-and-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
run: |
npm install
- name: Cache Gradle Wrapper
uses: actions/cache@v2
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
- name: Cache Gradle Dependencies
uses: actions/cache@v2
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-caches-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-caches-
- name: Make Gradlew Executable
run: cd android && chmod +x ./gradlew
- name: Build Android Release
run: |
cd android && ./gradlew assembleRelease --no-daemon
+60
View File
@@ -11,3 +11,63 @@ web-build/
# macOS
.DS_Store
# @generated expo-cli sync-2138f1e3e130677ea10ea873f6d498e3890e677b
# The following patterns were generated by expo-cli
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
*.hprof
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore
# Bundle artifacts
*.jsbundle
# CocoaPods
/ios/Pods/
# Expo
.expo/
web-build/
# @end expo-cli
+27 -34
View File
@@ -1,50 +1,43 @@
import React, { useState, useEffect } from "react";
import { StatusBar } from "expo-status-bar";
import { NavigationContainer } from "@react-navigation/native";
import * as Font from "expo-font";
import AppLoading from "expo-app-loading";
import React, { useEffect } from "react";
import { StatusBar } from "react-native";
import Router from "./src/navigation/Router";
// Redux
import { useFonts } from "expo-font";
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 { createDB, deleteDb } from "./src/database/DBHelper";
import * as SplashScreen from "expo-splash-screen";
const getFonts = () =>
Font.loadAsync({
"pt-sans-bold": require("./assets/fonts/PTSansNarrow-Bold.ttf"),
});
import Main from "./src/navigators/Main";
import { createDB } from "./src/database/DBHelper";
import { fonts } from "./src/theme/fonts";
const App = () => {
const [fontsLoaded, setFontsLoaded] = useState(false);
const [loaded] = useFonts(fonts);
useEffect(() => {
// deleteDb();
SplashScreen.preventAutoHideAsync();
createDB();
}, []);
if (fontsLoaded) {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<StatusBar />
<NavigationContainer>
<Router />
</NavigationContainer>
</PersistGate>
</Provider>
);
} else {
return (
<AppLoading
startAsync={getFonts}
onError={() => warn(error)}
onFinish={() => setFontsLoaded(true)}
/>
);
if (!loaded) {
return null;
}
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<PaperProvider>
<StatusBar
translucent={true}
backgroundColor="transparent"
/>
<Main />
</PaperProvider>
</PersistGate>
</Provider>
);
};
export default App;
+33 -12
View File
@@ -1,20 +1,41 @@
<h1 align="center">
<br>
LNReader
<br>
</h1>
<p align="center">LNReader is a Tachiyomi-like, free and open source light novel reader for Android.</p>
---
## Screenshots:
<p align="center">
<img src="./.github/readme-images/screenshots.png" align="center" height="350" />
<img src="./.github/readme-images/icon_alt.png" align="center" width="128" />
<p>
<h1 align="center">
LNReader
</h1>
<p align="center">
LNReader is a Tachiyomi-like, free and open source light novel reader for Android.
</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">
</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">
</a>
<a href="https://github.com/LNReader/lnreader/releases/latest">
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/LNReader/lnreader">
</a>
<a href="https://github.com/LNReader/lnreader/blob/main/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/LNReader/lnreader">
</a>
</div>
<br>
<br>
<p align="center">
<img src="./.github/readme-images/screenshots.png" align="center" />
</p>
## Download
Get the app from our [releases page](https://github.com/rajarsheechatterjee/LNReader/releases).
## Sources
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).
## License
[MIT ](https://github.com/rajarsheechatterjee/lnreader/blob/main/LICENSE)
+55
View File
@@ -0,0 +1,55 @@
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
create_aar_targets(glob(["libs/*.aar"]))
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "com.rajarsheechatterjee.LNReader",
)
android_resource(
name = "res",
package = "com.rajarsheechatterjee.LNReader",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)
+219
View File
@@ -0,0 +1,219 @@
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
enableHermes: true
]
apply from: '../../node_modules/react-native-unimodules/gradle.groovy'
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/expo-constants/scripts/get-app-config-android.gradle"
apply from: "../../node_modules/expo-updates/scripts/create-manifest-android.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and mirrored here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
android {
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId 'com.rajarsheechatterjee.LNReader'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.1.4"
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
addUnimodulesDependencies()
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
+19
View File
@@ -0,0 +1,19 @@
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
Binary file not shown.
+10
View File
@@ -0,0 +1,10 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
</manifest>
@@ -0,0 +1,69 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.lnreader;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
@Override
public void apply(OkHttpClient.Builder builder) {
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
client.addPlugin(new FrescoFlipperPlugin());
}
});
}
});
} else {
client.addPlugin(new FrescoFlipperPlugin());
}
}
}
}
+89
View File
@@ -0,0 +1,89 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rajarsheechatterjee.LNReader">
<!-- Internet -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- Storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- For background jobs -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<meta-data
android:name="expo.modules.updates.EXPO_UPDATE_URL"
android:value="https://exp.host/@rajarsheechatterjee/LNReader"/>
<meta-data
android:name="expo.modules.updates.EXPO_SDK_VERSION"
android:value="41.0.0"/>
<meta-data
android:name="expo.modules.updates.ENABLED"
android:value="false"/>
<meta-data
android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH"
android:value="ALWAYS"/>
<meta-data
android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS"
android:value="0"/>
<meta-data
android:name="expo.modules.notifications.default_notification_icon"
android:resource="@drawable/notification_icon"/>
<meta-data
android:name="expo.modules.notifications.default_notification_color"
android:resource="@color/notification_icon_color"/>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.App.SplashScreen"
android:screenOrientation="unspecified">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="lnreader"/>
<data android:scheme="com.rajarsheechatterjee.LNReader"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
</application>
</manifest>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,71 @@
package com.rajarsheechatterjee.LNReader;
import android.content.res.Configuration;
import android.content.Intent;
import android.os.Bundle;
import android.graphics.Color;
import android.view.WindowManager;
import android.os.Build;
import android.view.View;
import android.view.Window;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
import expo.modules.splashscreen.singletons.SplashScreen;
import expo.modules.splashscreen.SplashScreenImageResizeMode;
public class MainActivity extends ReactActivity {
// Added automatically by Expo Config
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Intent intent = new Intent("onConfigurationChanged");
intent.putExtra("newConfig", newConfig);
sendBroadcast(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// SplashScreen.show(...) has to be called after super.onCreate(...)
// Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(layoutParams);
}
Window w = getWindow();
w.setStatusBarColor(Color.TRANSPARENT);
w.setNavigationBarColor(Color.TRANSPARENT);
w.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
super.onCreate(savedInstanceState);
SplashScreen.show(this, SplashScreenImageResizeMode.COVER, ReactRootView.class, false);
}
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "main";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
};
}
}
@@ -0,0 +1,128 @@
package com.rajarsheechatterjee.LNReader;
import android.app.Application;
import android.content.Context;
import android.net.Uri;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.rajarsheechatterjee.LNReader.generated.BasePackageList;
import org.unimodules.adapters.react.ReactAdapterPackage;
import org.unimodules.adapters.react.ModuleRegistryAdapter;
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
import org.unimodules.core.interfaces.Package;
import org.unimodules.core.interfaces.SingletonModule;
import expo.modules.constants.ConstantsPackage;
import expo.modules.permissions.PermissionsPackage;
import expo.modules.filesystem.FileSystemPackage;
import expo.modules.updates.UpdatesController;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
public class MainApplication extends Application implements ReactApplication {
private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(
new BasePackageList().getPackageList()
);
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider));
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
@Override
protected JSIModulePackage getJSIModulePackage() {
return new ReanimatedJSIModulePackage();
}
@Override
protected @Nullable String getJSBundleFile() {
if (BuildConfig.DEBUG) {
return super.getJSBundleFile();
} else {
return UpdatesController.getInstance().getLaunchAssetFile();
}
}
@Override
protected @Nullable String getBundleAssetName() {
if (BuildConfig.DEBUG) {
return super.getBundleAssetName();
} else {
return UpdatesController.getInstance().getBundleAssetName();
}
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
if (!BuildConfig.DEBUG) {
UpdatesController.initialize(this);
}
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.rajarsheechatterjee.LNReader.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
@@ -0,0 +1,28 @@
package com.rajarsheechatterjee.LNReader.generated;
import java.util.Arrays;
import java.util.List;
import org.unimodules.core.interfaces.Package;
public class BasePackageList {
public List<Package> getPackageList() {
return Arrays.<Package>asList(
new expo.modules.application.ApplicationPackage(),
new expo.modules.constants.ConstantsPackage(),
new expo.modules.documentpicker.DocumentPickerPackage(),
new expo.modules.errorrecovery.ErrorRecoveryPackage(),
new expo.modules.filesystem.FileSystemPackage(),
new expo.modules.font.FontLoaderPackage(),
new expo.modules.haptics.HapticsPackage(),
new expo.modules.imageloader.ImageLoaderPackage(),
new expo.modules.keepawake.KeepAwakePackage(),
new expo.modules.lineargradient.LinearGradientPackage(),
new expo.modules.notifications.NotificationsPackage(),
new expo.modules.permissions.PermissionsPackage(),
new expo.modules.splashscreen.SplashScreenPackage(),
new expo.modules.sqlite.SQLitePackage(),
new expo.modules.updates.UpdatesPackage(),
new expo.modules.webbrowser.WebBrowserPackage()
);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file was created by '@expo/configure-splash-screen' and some of it's content shouldn't be modified by hand
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/splashscreen_background"/>
</layer-list>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/iconBackground"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/iconBackground"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources>
<color name="iconBackground">#FFFFFF</color>
<color name="splashscreen_background">#000000</color>
<color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#00000000</color>
<color name="navigationBarColor">#000000</color>
<color name="notification_icon_color">#00adb5</color>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources>
<string name="app_name">LNReader</string>
</resources>
@@ -0,0 +1,23 @@
<?xml
version="1.0"
encoding="UTF-8"
standalone="yes"
?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColor">#000000</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="android:navigationBarColor">#000000</item>
<item name="android:statusBarColor">#000000</item>
</style>
<style name="Theme.App.SplashScreen" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Below line is handled by '@expo/configure-splash-screen' command and it's discouraged to modify it manually -->
<!-- Customize your splash screen theme here -->
<item name="android:windowBackground">@drawable/splashscreen</item>
<item name="android:navigationBarColor">#000000</item>
<item name="android:statusBarColor">#000000</item>
</style>
</resources>
+38
View File
@@ -0,0 +1,38 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "29.0.3"
minSdkVersion = 21
compileSdkVersion = 30
targetSdkVersion = 30
}
repositories {
google()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:4.1.0")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
google()
jcenter()
maven { url 'https://www.jitpack.io' }
}
}
+29
View File
@@ -0,0 +1,29 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.54.0
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+183
View File
@@ -0,0 +1,183 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
+103
View File
@@ -0,0 +1,103 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+9
View File
@@ -0,0 +1,9 @@
rootProject.name = 'LNReader'
apply from: '../node_modules/react-native-unimodules/gradle.groovy'
includeUnimodulesProjects()
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
applyNativeModulesSettingsGradle(settings)
include ':app'
+4 -29
View File
@@ -2,35 +2,10 @@
"expo": {
"name": "LNReader",
"slug": "LNReader",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "cover",
"backgroundColor": "#000000"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/icon.png",
"backgroundColor": "#FFFFFF"
},
"package": "com.rajarsheechatterjee.LNReader"
},
"androidNavigationBar": {
"barStyle": "light-content",
"backgroundColor": "#000000"
},
"web": {
"favicon": "./assets/favicon.png"
"scheme": "lnreader",
"notification": {
"icon": "./assets/notification.png",
"color": "#00adb5"
}
}
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 266 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 28 KiB

+9
View File
@@ -0,0 +1,9 @@
import 'react-native-gesture-handler';
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
+4
View File
@@ -0,0 +1,4 @@
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');
module.exports = getDefaultConfig(__dirname);
+14255 -3630
View File
File diff suppressed because it is too large Load Diff
+72 -54
View File
@@ -1,56 +1,74 @@
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.13.2",
"@react-native-community/masked-view": "0.1.10",
"@react-native-community/netinfo": "^5.9.7",
"@react-native-community/slider": "^3.0.3",
"@react-navigation/material-bottom-tabs": "^5.3.10",
"@react-navigation/native": "^5.8.10",
"@react-navigation/stack": "^5.12.8",
"expo": "~40.0.0",
"expo-app-loading": "^1.0.1",
"expo-font": "^8.4.0",
"expo-linear-gradient": "^8.4.0",
"expo-sharing": "~8.5.0",
"expo-sqlite": "~8.5.0",
"expo-status-bar": "~1.0.3",
"expo-web-browser": "~8.6.0",
"moment": "^2.29.1",
"react": "16.13.1",
"react-color-extractor": "^1.1.2",
"react-dom": "16.13.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
"react-native-collapsible-header-views": "^1.1.2",
"react-native-gesture-handler": "^1.8.0",
"react-native-input-spinner": "^1.7.7",
"react-native-offline": "^5.7.0",
"react-native-paper": "^4.5.0",
"react-native-reanimated": "^1.13.2",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.0",
"react-native-vector-icons": "^7.1.0",
"react-native-web": "~0.13.12",
"react-native-webview": "11.0.0",
"react-redux": "^7.2.2",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.9",
"redux-persist": "^6.0.0",
"redux-thunk": "^2.3.0",
"reflect-metadata": "^0.1.13",
"rn-sliding-up-panel": "^2.4.4",
"typeorm": "^0.2.31"
},
"devDependencies": {
"@babel/core": "~7.9.0",
"@types/node": "^14.14.35"
},
"private": true
"scripts": {
"start": "react-native start",
"android": "react-native run-android",
"ios": "react-native run-ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.13.0",
"@react-native-community/masked-view": "0.1.10",
"@react-native-community/netinfo": "6.0.0",
"@react-native-community/slider": "3.0.3",
"@react-navigation/material-bottom-tabs": "^5.3.10",
"@react-navigation/native": "^5.8.10",
"@react-navigation/stack": "^5.12.8",
"expo": "^41.0.1",
"expo-document-picker": "^9.1.2",
"expo-file-system": "~11.0.2",
"expo-font": "~9.1.0",
"expo-haptics": "~10.0.0",
"expo-linear-gradient": "~9.1.0",
"expo-linking": "~2.2.3",
"expo-notifications": "~0.11.6",
"expo-splash-screen": "^0.11.2",
"expo-sqlite": "~9.1.0",
"expo-updates": "~0.5.4",
"expo-web-browser": "~9.1.0",
"moment": "^2.29.1",
"qs": "^6.10.1",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-native": "^0.64.2",
"react-native-background-actions": "^2.6.1",
"react-native-cheerio": "^1.0.0-rc.4",
"react-native-device-info": "^8.1.3",
"react-native-easing-gradient": "^0.1.2",
"react-native-fast-image": "^8.3.4",
"react-native-gesture-handler": "~1.10.2",
"react-native-navigation-bar-color": "^2.0.1",
"react-native-pager-view": "^5.1.10",
"react-native-paper": "^4.5.0",
"react-native-reanimated": "^2.1.0",
"react-native-safe-area-context": "3.2.0",
"react-native-screens": "~3.0.0",
"react-native-swipe-gestures": "^1.0.5",
"react-native-tab-view": "^3.0.1",
"react-native-tts": "^4.1.0",
"react-native-unimodules": "~0.13.3",
"react-native-vector-icons": "^7.1.0",
"react-native-web": "^0.16.5",
"react-native-webview": "^11.6.2",
"react-redux": "^7.2.2",
"redux": "^4.0.5",
"redux-persist": "^6.0.0",
"redux-thunk": "^2.3.0",
"rn-sliding-up-panel": "^2.4.4"
},
"devDependencies": {
"@babel/core": "~7.9.0"
},
"prettier": {
"tabWidth": 4,
"semi": true,
"singleQuote": false,
"quoteProps": "as-needed",
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "always"
},
"private": true,
"name": "lnreader",
"version": "1.0.0"
}
+47
View File
@@ -0,0 +1,47 @@
import React from "react";
import { Dimensions, StyleSheet, View } from "react-native";
import { IconButton } from "react-native-paper";
export const Actionbar = ({ active, actions, theme, style }) => {
if (active) {
return (
<View
style={[
styles.actionbarContainer,
{ backgroundColor: theme.colorPrimary },
style,
]}
>
{actions.map(
(action, index) =>
action.icon && (
<IconButton
key={index}
icon={action.icon}
color={theme.textColorPrimary}
onPress={action.onPress}
/>
)
)}
</View>
);
} else {
return null;
}
};
const styles = StyleSheet.create({
actionbarContainer: {
position: "absolute",
width: Dimensions.get("window").width - 32,
bottom: 0,
margin: 16,
borderRadius: 6,
flexDirection: "row",
justifyContent: "space-between",
paddingHorizontal: 8,
paddingVertical: 4,
elevation: 2,
},
});
+24
View File
@@ -0,0 +1,24 @@
import React from "react";
import { Appbar as MaterialAppbar } from "react-native-paper";
import { useTheme } from "../hooks/reduxHooks";
export const Appbar = ({ title, onBackAction, style, children }) => {
const theme = useTheme();
return (
<MaterialAppbar.Header
style={[{ backgroundColor: theme.colorPrimary }, style]}
>
{onBackAction && (
<MaterialAppbar.BackAction onPress={onBackAction} />
)}
<MaterialAppbar.Content
title={title}
titleStyle={{ color: theme.textColorPrimary }}
/>
{children}
</MaterialAppbar.Header>
);
};
+17
View File
@@ -0,0 +1,17 @@
import React from "react";
import { View } from "react-native";
const BottomSheetHandle = ({ theme }) => (
<View
style={{
backgroundColor: `${theme.textColorPrimary}66`,
height: 4,
width: 40,
borderRadius: 50,
top: 8,
alignSelf: "center",
}}
/>
);
export default BottomSheetHandle;
+85
View File
@@ -0,0 +1,85 @@
import React from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
export const Checkbox = ({ label, status, onPress, theme }) => (
<View
style={[
styles.checkboxContainer,
status && { backgroundColor: theme.rippleColor },
]}
>
<Pressable
android_ripple={{ color: theme.rippleColor }}
style={styles.pressable}
onPress={onPress}
>
<Text
style={{
color: status ? theme.colorAccent : theme.textColorPrimary,
}}
>
{label}
</Text>
{status && (
<MaterialCommunityIcons
name="check"
color={theme.colorAccent}
size={21}
style={styles.icon}
/>
)}
</Pressable>
</View>
);
export const SortItem = ({ label, status, onPress, theme }) => (
<View
style={[
styles.checkboxContainer,
status && { backgroundColor: theme.rippleColor },
]}
>
<Pressable
android_ripple={{ color: theme.rippleColor }}
style={styles.pressable}
onPress={onPress}
>
<Text
style={{
color: status ? theme.colorAccent : theme.textColorPrimary,
}}
>
{label}
</Text>
{status && (
<MaterialCommunityIcons
name={status === "asc" ? "arrow-up" : "arrow-down"}
color={theme.colorAccent}
size={21}
style={styles.icon}
/>
)}
</Pressable>
</View>
);
const styles = StyleSheet.create({
checkboxContainer: {
borderRadius: 8,
overflow: "hidden",
marginVertical: 4,
},
pressable: {
paddingHorizontal: 16,
paddingVertical: 12,
flexDirection: "row",
justifyContent: "space-between",
},
icon: {
position: "absolute",
right: 16,
alignSelf: "center",
},
});
+153
View File
@@ -0,0 +1,153 @@
import React, { useState } from "react";
import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
import { Modal, Portal, TextInput } from "react-native-paper";
const ColorPickerModal = ({
theme,
color,
title,
onSubmit,
hideModal,
modalVisible,
showAccentColors,
}) => {
const [text, setText] = useState(color);
const [error, setError] = useState();
const onDismiss = () => {
hideModal();
setText();
setError();
};
const onChangeText = (text) => setText(text);
const onSubmitEditing = () => {
const re = /^#([0-9a-f]{6}|[0-9a-f]{3})$/i;
if (text.match(re)) {
onSubmit(text);
hideModal();
} else {
setError("Enter a valid hex color code");
}
};
const textInputTheme = {
colors: {
primary: color,
placeholder: theme.textColorHint,
text: theme.textColorPrimary,
background: "transparent",
},
};
const accentColors = [
"#EF5350",
"#EC407A",
"#AB47BC",
"#7E57C2",
"#5C6BC0",
"#42A5F5",
"#29B6FC",
"#26C6DA",
"#26A69A",
"#66BB6A",
"#9CCC65",
"#D4E157",
"#FFEE58",
"#FFCA28",
"#FFA726",
"#FF7043",
"#8D6E63",
"#BDBDBD",
"#78909C",
"#000000",
];
return (
<Portal>
<Modal
visible={modalVisible}
onDismiss={onDismiss}
contentContainerStyle={[
styles.modalContainer,
{ backgroundColor: theme.colorPrimary },
]}
>
<Text
style={[
styles.modalTitle,
{ color: theme.textColorPrimary },
]}
>
{title}
</Text>
{showAccentColors && (
<FlatList
contentContainerStyle={{ marginBottom: 8 }}
data={accentColors}
numColumns={4}
keyExtractor={(item) => item}
renderItem={({ item }) => (
<View
style={{
borderRadius: 4,
overflow: "hidden",
backgroundColor: item,
flex: 1 / 4,
height: 40,
marginHorizontal: 4,
marginVertical: 4,
}}
>
<Pressable
style={{ flex: 1 }}
android_ripple={{
color: "rgba(0,0,0,0.12)",
}}
onPress={() => {
onSubmit(item);
hideModal();
}}
/>
</View>
)}
/>
)}
<TextInput
value={text}
defaultValue={color}
placeholder="Hex Color Code (E.g. #3399FF)"
onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing}
mode="outlined"
theme={textInputTheme}
underlineColor={theme.textColorHint}
dense
error={error}
/>
<Text style={styles.errorText}>{error}</Text>
</Modal>
</Portal>
);
};
export default ColorPickerModal;
const styles = StyleSheet.create({
modalContainer: {
margin: 30,
padding: 20,
borderRadius: 8,
},
modalTitle: {
fontSize: 18,
marginBottom: 16,
},
errorText: {
color: "#FF0033",
paddingTop: 8,
},
});
+27
View File
@@ -0,0 +1,27 @@
import React from "react";
import { View, StyleSheet } from "react-native";
const ScreenContainer = ({ children, theme }) => (
<View
style={{
flex: 1,
backgroundColor: theme.colorPrimaryDark,
paddingBottom: 16,
}}
>
{children}
</View>
);
const Row = ({ children, style }) => (
<View style={[styles.row, style]}>{children}</View>
);
export { ScreenContainer, Row };
const styles = StyleSheet.create({
row: {
flexDirection: "row",
alignItems: "center",
},
});
+110
View File
@@ -0,0 +1,110 @@
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",
},
});
+72
View File
@@ -0,0 +1,72 @@
import React from "react";
import { Pressable, View } from "react-native";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
export const ToggleButton = ({ icon, selected, theme, color, onPress }) => (
<View
style={{
borderRadius: 8,
overflow: "hidden",
marginHorizontal: 4,
}}
>
<Pressable
android_ripple={{ color: theme.rippleColor }}
style={{
padding: 10,
alignItems: "center",
justifyContent: "center",
}}
onPress={onPress}
>
<MaterialCommunityIcons
name={icon}
color={
selected
? theme.colorAccent
: color
? color
: theme.textColorPrimary
}
size={24}
/>
</Pressable>
</View>
);
export const ToggleColorButton = ({
selected,
backgroundColor,
textColor,
onPress,
}) => (
<View
style={{
borderRadius: 50,
overflow: "hidden",
marginHorizontal: 6,
height: 44,
width: 44,
}}
>
<Pressable
android_ripple={{ color: textColor }}
style={{
flex: 1,
padding: 10,
backgroundColor: backgroundColor,
alignItems: "center",
justifyContent: "center",
}}
onPress={onPress}
>
{selected && (
<MaterialCommunityIcons
name="check"
color={textColor}
size={24}
/>
)}
</Pressable>
</View>
);
@@ -2,17 +2,17 @@ import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { useSelector } from "react-redux";
const EmptyView = ({ icon, description }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const EmptyView = ({ icon, description, style, children, iconStyle }) => {
const theme = useSelector((state) => state.settingsReducer.theme);
return (
<View style={styles.emptyViewContainer}>
<Text
style={[
styles.emptyViewIcon,
{
color: theme.textColorSecondary,
},
{ color: theme.textColorHint },
style,
iconStyle,
]}
>
{icon}
@@ -20,13 +20,13 @@ const EmptyView = ({ icon, description }) => {
<Text
style={[
styles.emptyViewText,
{
color: theme.textColorSecondary,
},
{ color: theme.textColorHint },
style,
]}
>
{description}
</Text>
{children}
</View>
);
};
@@ -41,7 +41,6 @@ const styles = StyleSheet.create({
},
emptyViewIcon: {
fontSize: 45,
fontWeight: "bold",
},
emptyViewText: {
fontWeight: "bold",
+28
View File
@@ -0,0 +1,28 @@
import React from "react";
import { Text, View } from "react-native";
import { IconButton } from "react-native-paper";
import { Row } from "./Common";
import EmptyView from "./EmptyView";
const ErrorView = ({ theme, onRetry, error, openWebView }) => (
<View style={{ flex: 1, justifyContent: "center" }}>
<EmptyView
icon="(; ̄Д ̄)"
description={error}
style={{ color: theme.textColorSecondary }}
>
<IconButton
icon="reload"
size={25}
style={{ margin: 0, marginTop: 24 }}
color={theme.text}
onPress={onRetry}
/>
<Text style={{ color: theme.textColorPrimary }}>Retry</Text>
</EmptyView>
</View>
);
export default ErrorView;
+75
View File
@@ -0,0 +1,75 @@
import React from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
import { IconButton } from "react-native-paper";
const icons = ["(・o・;)", "Σ(ಠ_ಠ)", "ಥ_ಥ", "(˘・_・˘)", "(; ̄Д ̄)", "(・Д・。"];
export const ErrorView = ({ errorName, actions, theme }) => (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text style={[styles.emptyViewIcon, { color: theme.textColorHint }]}>
{/* {icons[Math.floor(Math.random() * 5)]} */}
ಥ_ಥ
</Text>
<Text style={[styles.emptyViewText, { color: theme.textColorHint }]}>
{errorName}
</Text>
<View style={{ flexDirection: "row" }}>
{actions.map((action) => (
<View
style={{
borderRadius: 4,
overflow: "hidden",
margin: 16,
}}
>
<Pressable
android_ripple={{
color: theme.rippleColor,
borderless: false,
}}
onPress={action.onPress}
style={{
justifyContent: "center",
alignItems: "center",
paddingBottom: 8,
paddingHorizontal: 20,
minWidth: 100,
}}
>
<IconButton
icon={action.icon}
color={theme.textColorHint}
size={24}
style={{ margin: 0 }}
/>
<Text
style={{
fontSize: 12,
color: theme.textColorHint,
}}
>
{action.name}
</Text>
</Pressable>
</View>
))}
</View>
</View>
);
const styles = StyleSheet.create({
emptyViewContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
emptyViewIcon: {
fontSize: 45,
},
emptyViewText: {
fontWeight: "bold",
marginTop: 10,
textAlign: "center",
paddingHorizontal: 30,
},
});
+2
View File
@@ -0,0 +1,2 @@
import { NativeModules } from "react-native";
module.exports = NativeModules.FullScreen;
+143
View File
@@ -0,0 +1,143 @@
import React from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
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.colorAccent }]}
>
{children}
</PaperList.Subheader>
);
const ListItem = ({
title,
description,
icon,
onPress,
theme,
right,
iconColor,
titleStyle,
style,
}) => (
<PaperList.Item
title={title}
style={style}
titleStyle={[{ color: theme.textColorPrimary }, titleStyle]}
description={description}
descriptionStyle={{ color: theme.textColorSecondary }}
descriptionNumberOfLines={1}
left={() => (
<View style={{ justifyContent: "center" }}>
{icon && (
<PaperList.Icon
color={theme.colorAccent}
icon={icon}
style={{
marginVertical: 0,
}}
/>
)}
</View>
)}
right={() =>
right && (
<MaterialCommunityIcons
name={right}
color={iconColor || "#47a84a"}
size={23}
style={{ marginRight: 16, textAlignVertical: "center" }}
/>
)
}
onPress={onPress}
rippleColor={theme.rippleColor}
/>
);
const Divider = ({ theme }) => (
<PaperDivider style={{ backgroundColor: theme.dividerColor, 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}
style={{ marginVertical: 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.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.colorAccent}
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: {
paddingBottom: 5,
},
});
+80
View File
@@ -0,0 +1,80 @@
import React from "react";
import { StyleSheet, View, Text, Pressable } from "react-native";
import FastImage from "react-native-fast-image";
const ListView = ({
item,
downloadBadge,
unreadBadge,
inLibraryBadge,
theme,
onPress,
isSelected,
onLongPress,
}) => {
return (
<Pressable
android_ripple={{ color: theme.rippleColor }}
style={[
styles.listView,
isSelected && { backgroundColor: theme.rippleColor },
]}
onPress={onPress}
onLongPress={onLongPress}
>
<FastImage
source={{
uri:
item.novelCover && !item.novelCover.startsWith("/")
? item.novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
}}
style={[
styles.extensionIcon,
inLibraryBadge && { opacity: 0.5 },
]}
/>
<Text
style={[{ color: theme.textColorPrimary }, styles.novelName]}
numberOfLines={1}
>
{item.novelName}
</Text>
<View style={styles.badgeContainer}>
{downloadBadge}
{unreadBadge}
{inLibraryBadge}
</View>
</Pressable>
);
};
export default ListView;
const styles = StyleSheet.create({
listView: {
flex: 1,
flexDirection: "row",
alignItems: "center",
paddingVertical: 8,
paddingHorizontal: 12,
},
extensionIcon: {
width: 40,
height: 40,
borderRadius: 4,
},
novelName: {
flex: 1,
marginLeft: 16,
fontSize: 15,
paddingRight: 8,
flexWrap: "wrap",
},
badgeContainer: {
flexDirection: "row",
justifyContent: "flex-end",
alignItems: "flex-end",
},
});
@@ -0,0 +1,8 @@
import React from "react";
import { ActivityIndicator, View } from "react-native";
export const LoadingScreen = ({ theme }) => (
<View style={{ flex: 1, justifyContent: "center" }}>
<ActivityIndicator size={50} color={theme.colorAccent} />
</View>
);
+80
View File
@@ -0,0 +1,80 @@
import React, { useState } from "react";
import { StyleSheet, View, Text } from "react-native";
import { Portal, Modal, Button } from "react-native-paper";
import * as Linking from "expo-linking";
import { useTheme } from "../hooks/reduxHooks";
const NewUpdateDialog = ({ newVersion }) => {
const [newUpdateDialog, showNewUpdateDialog] = useState(true);
const theme = useTheme();
return (
<Portal>
<Modal
visible={newUpdateDialog}
onDismiss={() => showNewUpdateDialog(false)}
contentContainerStyle={[
styles.containerStyle,
{ backgroundColor: theme.colorPrimaryDark },
]}
>
<Text
style={[
styles.modalHeader,
{ color: theme.textColorPrimary },
]}
>
{`New update available ${newVersion.tag_name}`}
</Text>
<Text style={{ color: theme.textColorSecondary, fontSize: 16 }}>
{newVersion.body}
</Text>
<View
style={{
flexDirection: "row",
marginTop: 30,
justifyContent: "flex-end",
}}
>
<Button
labelStyle={{
color: theme.colorAccent,
letterSpacing: 0,
textTransform: "none",
}}
theme={{ colors: { primary: theme.colorAccent } }}
onPress={() => showNewUpdateDialog(false)}
>
Cancel
</Button>
<Button
labelStyle={{
color: theme.colorAccent,
letterSpacing: 0,
textTransform: "none",
}}
theme={{ colors: { primary: theme.colorAccent } }}
onPress={() => Linking.openURL(newVersion.downloadUrl)}
>
Install
</Button>
</View>
</Modal>
</Portal>
);
};
export default NewUpdateDialog;
const styles = StyleSheet.create({
containerStyle: {
padding: 20,
margin: 20,
borderRadius: 6,
},
modalHeader: {
fontSize: 20,
marginBottom: 10,
},
});
+322
View File
@@ -0,0 +1,322 @@
import React, { useCallback } from "react";
import {
StyleSheet,
View,
Text,
useWindowDimensions,
Pressable,
} from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import FastImage from "react-native-fast-image";
import ListView from "./ListView";
import { getDeviceOrientation } from "../services/utils/helpers";
import { useSettings } from "../hooks/reduxHooks";
const NovelCover = ({
item,
onPress,
libraryStatus,
theme,
isSelected,
onLongPress,
selectedNovels,
}) => {
const { displayMode, novelsPerRow, showDownloadBadges, showUnreadBadges } =
useSettings();
const window = useWindowDimensions();
const orientation = getDeviceOrientation();
const getNovelsPerRow = () =>
orientation === "landscape" ? 6 : novelsPerRow;
const getHeight = useCallback(
() => (window.width / getNovelsPerRow()) * (4 / 3),
[]
);
const selectNovel = () => onLongPress && onLongPress(item.novelId);
return displayMode !== 2 ? (
<View
style={[
{
flex: 1 / getNovelsPerRow(),
borderRadius: 6,
overflow: "hidden",
margin: 2,
},
isSelected && {
backgroundColor: theme.colorAccent,
opacity: 0.8,
},
]}
>
<Pressable
android_ripple={{ color: theme.colorAccent }}
style={styles.opac}
onPress={
selectedNovels && selectedNovels.length > 0
? selectNovel
: onPress
}
onLongPress={selectNovel}
>
<>
<View style={styles.badgeContainer}>
{libraryStatus && <InLibraryBadge theme={theme} />}
{showDownloadBadges && item.chaptersDownloaded && (
<DownloadBadge
showUnreadBadges={showUnreadBadges}
chaptersDownloaded={item.chaptersDownloaded}
chaptersUnread={item.chaptersUnread}
/>
)}
{showUnreadBadges && item.chaptersUnread && (
<UnreadBadge
theme={theme}
chaptersDownloaded={item.chaptersDownloaded}
chaptersUnread={item.chaptersUnread}
showDownloadBadges={showDownloadBadges}
/>
)}
</View>
<FastImage
source={{
uri:
item.novelCover &&
!item.novelCover.startsWith("/")
? item.novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
}}
style={[
{ height: getHeight(), borderRadius: 4 },
libraryStatus && { opacity: 0.5 },
]}
/>
<View style={styles.compactTitleContainer}>
{displayMode === 0 && (
<CompactTitle novelName={item.novelName} />
)}
</View>
{displayMode === 1 && (
<ComfortableTitle
novelName={item.novelName}
theme={theme}
/>
)}
</>
</Pressable>
</View>
) : (
<ListView
item={item}
downloadBadge={
showDownloadBadges &&
item.chaptersDownloaded && (
<DownloadBadge
showUnreadBadges={showUnreadBadges}
chaptersDownloaded={item.chaptersDownloaded}
chaptersUnread={item.chaptersUnread}
/>
)
}
unreadBadge={
showUnreadBadges &&
item.chaptersUnread && (
<UnreadBadge
theme={theme}
chaptersDownloaded={item.chaptersDownloaded}
chaptersUnread={item.chaptersUnread}
showDownloadBadges={showDownloadBadges}
/>
)
}
inLibraryBadge={libraryStatus && <InLibraryBadge theme={theme} />}
theme={theme}
onPress={
selectedNovels && selectedNovels.length > 0
? selectNovel
: onPress
}
onLongPress={selectNovel}
isSelected={isSelected}
/>
);
};
export default NovelCover;
const ComfortableTitle = ({ theme, novelName }) => (
<Text
numberOfLines={2}
style={[
styles.title,
{
color: theme.textColorPrimary,
padding: 4,
},
]}
>
{novelName}
</Text>
);
const CompactTitle = ({ novelName }) => (
<View style={styles.titleContainer}>
<LinearGradient
colors={["transparent", "rgba(0,0,0,0.7)"]}
style={styles.linearGradient}
>
<Text
numberOfLines={2}
style={[
styles.title,
{
color: "rgba(255,255,255,1)",
textShadowColor: "rgba(0, 0, 0, 0.75)",
textShadowOffset: { width: -1, height: 1 },
textShadowRadius: 10,
},
]}
>
{novelName}
</Text>
</LinearGradient>
</View>
);
const InLibraryBadge = ({ theme }) => (
<Text
style={[
styles.inLibraryBadge,
{
backgroundColor: theme.colorAccent,
color: theme.colorButtonText,
borderRadius: 4,
},
]}
>
In library
</Text>
);
const UnreadBadge = ({
chaptersDownloaded,
chaptersUnread,
showDownloadBadges,
theme,
}) => (
<Text
style={[
styles.unreadBadge,
!chaptersDownloaded && {
borderTopLeftRadius: 4,
borderBottomLeftRadius: 4,
},
!showDownloadBadges && {
borderRadius: 4,
},
{
backgroundColor: theme.colorAccent,
color: theme.colorButtonText,
},
]}
>
{chaptersUnread}
</Text>
);
const DownloadBadge = ({
chaptersDownloaded,
showUnreadBadges,
chaptersUnread,
}) => (
<Text
style={[
styles.downloadBadge,
!chaptersUnread && {
borderTopRightRadius: 4,
borderBottomRightRadius: 4,
},
!showUnreadBadges && {
borderRadius: 4,
},
]}
>
{chaptersDownloaded}
</Text>
);
const styles = StyleSheet.create({
titleContainer: {
flex: 1,
borderRadius: 4,
},
title: {
fontFamily: "pt-sans-bold",
fontSize: 14,
padding: 8,
},
linearGradient: {
borderRadius: 4,
},
opac: {
padding: 4.8,
borderRadius: 4,
flex: 1,
},
extensionIcon: {
width: 42,
height: 42,
borderRadius: 4,
},
listView: {
flex: 1,
paddingVertical: 8,
paddingHorizontal: 12,
flexDirection: "row",
alignItems: "center",
borderRadius: 4,
},
downloadBadge: {
backgroundColor: "#2979FF",
color: "#FFFFFF",
borderTopLeftRadius: 4,
borderBottomLeftRadius: 4,
paddingTop: 2,
paddingHorizontal: 4,
fontSize: 12,
},
unreadBadge: {
color: "#FFFFFF",
borderTopRightRadius: 4,
borderBottomRightRadius: 4,
paddingTop: 2,
paddingHorizontal: 4,
fontSize: 12,
},
inLibraryBadge: {
paddingVertical: 2,
paddingHorizontal: 4,
fontSize: 12,
},
compactTitleContainer: {
position: "absolute",
bottom: 4,
left: 4,
right: 4,
},
badgeContainer: {
position: "absolute",
zIndex: 1,
top: 8,
left: 8,
flexDirection: "row",
},
});
+61
View File
@@ -0,0 +1,61 @@
import React, { useCallback } from "react";
import { StyleSheet, FlatList } from "react-native";
import { useSettings } from "../hooks/reduxHooks";
import { getDeviceOrientation } from "../services/utils/helpers";
const NovelList = ({
data,
onScroll,
onEndReached,
renderItem,
refreshControl,
ListEmptyComponent,
ListFooterComponent,
}) => {
const { displayMode, novelsPerRow } = useSettings();
const orientation = getDeviceOrientation();
const getNovelsPerRow = () => {
if (displayMode === 2) {
return 1;
}
if (orientation === "landscape") {
return 6;
} else {
return novelsPerRow;
}
};
const keyExtractor = useCallback((item) => item.novelUrl, []);
return (
<FlatList
contentContainerStyle={[
styles.flatListCont,
displayMode !== 2 && { paddingHorizontal: 4 },
]}
numColumns={getNovelsPerRow()}
key={[orientation, getNovelsPerRow()]}
data={data}
keyExtractor={keyExtractor}
renderItem={renderItem}
refreshControl={refreshControl}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
onScroll={onScroll}
onEndReached={onEndReached}
/>
);
};
export default NovelList;
const styles = StyleSheet.create({
flatListCont: {
flexGrow: 1,
paddingVertical: 8,
},
});
+40
View File
@@ -0,0 +1,40 @@
import React from "react";
import { StyleSheet, View, Text } from "react-native";
import { RadioButton as MaterialRadioButton } from "react-native-paper";
export const RadioButtonGroup = ({ children, value, onValueChange }) => (
<MaterialRadioButton.Group onValueChange={onValueChange} value={value}>
{children}
</MaterialRadioButton.Group>
);
export const RadioButton = ({ value, label, theme, labelStyle }) => (
<View style={styles.radioButtonContainer}>
<MaterialRadioButton
value={value}
color={theme.colorAccent}
uncheckedColor={theme.textColorSecondary}
/>
<Text
style={[
styles.radioButtonLabel,
{ color: theme.textColorPrimary },
labelStyle,
]}
>
{label}
</Text>
</View>
);
const styles = StyleSheet.create({
radioButtonContainer: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 8,
},
radioButtonLabel: {
marginLeft: 16,
fontSize: 16,
},
});
+97
View File
@@ -0,0 +1,97 @@
import React, { useRef } from "react";
import { StyleSheet, View, TextInput, Text, StatusBar } from "react-native";
import { TouchableRipple, IconButton } from "react-native-paper";
import { Row } from "../Common";
export const Searchbar = ({
theme,
placeholder,
searchText,
backAction,
onBackAction,
clearSearchbar,
onChangeText,
onSubmitEditing,
actions,
}) => {
const searchRef = useRef(null);
return (
<TouchableRipple
borderless
onPress={() => searchRef.current.focus()}
style={[
styles.searchbarContainer,
{ backgroundColor: theme.searchBarColor },
]}
rippleColor={theme.rippleColor}
>
<View style={styles.container}>
<Row style={{ flex: 1 }}>
{backAction && (
<IconButton
icon={backAction}
color={theme.textColorPrimary}
style={{ marginLeft: 0 }}
size={23}
onPress={onBackAction}
/>
)}
<TextInput
ref={searchRef}
style={{
fontSize: 16,
color: theme.textColorPrimary,
}}
placeholder={placeholder}
placeholderTextColor={theme.textColorSecondary}
onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing}
defaultValue={searchText}
/>
</Row>
{searchText !== "" && (
<IconButton
icon="close"
color={theme.textColorPrimary}
style={{ marginRight: 0 }}
size={23}
onPress={clearSearchbar}
/>
)}
{actions &&
actions.map((action, index) => (
<IconButton
key={index}
icon={action.icon}
color={action.color ?? theme.textColorPrimary}
style={{ marginRight: 0 }}
size={23}
onPress={action.onPress}
/>
))}
</View>
</TouchableRipple>
);
};
const styles = StyleSheet.create({
searchbarContainer: {
marginTop: StatusBar.currentHeight + 8,
height: 46,
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 12,
marginBottom: 6,
borderRadius: 8,
marginHorizontal: 12,
elevation: 2,
},
container: {
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
});
+56
View File
@@ -0,0 +1,56 @@
import React from "react";
import { Pressable, StyleSheet, View, Text } from "react-native";
import { Switch, List } from "react-native-paper";
const SwitchSetting = ({ label, description, onPress, theme, icon, value }) => (
<Pressable
android_ripple={{ color: theme.rippleColor }}
style={styles.container}
onPress={onPress}
>
<View style={{ flexDirection: "row", flex: 1 }}>
{icon && (
<List.Icon
color={theme.colorAccent}
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.colorAccent}
style={{ marginRight: 8 }}
/>
</Pressable>
);
export default SwitchSetting;
const styles = StyleSheet.create({
container: {
paddingHorizontal: 16,
paddingVertical: 12,
flexDirection: "row",
justifyContent: "space-between",
},
});
+172
View File
@@ -0,0 +1,172 @@
import React from "react";
import { View, Text, StyleSheet, Pressable } from "react-native";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
export const ThemePicker = ({ theme, currentTheme, onPress }) => (
<View
style={{
justifyContent: "center",
alignItems: "center",
marginRight: 16,
}}
>
<View
style={{
backgroundColor: theme.colorPrimaryDark,
borderWidth: 3.6,
borderColor:
currentTheme.id == theme.id
? theme.colorAccent
: currentTheme.colorPrimaryDark,
width: 95,
height: 140,
borderRadius: 16,
overflow: "hidden",
elevation: 1,
}}
>
<Pressable style={{ flex: 1 }} onPress={onPress}>
{currentTheme.id == theme.id && (
<MaterialCommunityIcons
name="check"
color={theme.colorButtonText}
size={15}
style={{
backgroundColor: theme.colorAccent,
position: "absolute",
top: 5,
right: 5,
elevation: 2,
borderRadius: 50,
padding: 1.6,
}}
/>
)}
<View
style={{
height: 20,
backgroundColor: theme.colorPrimary,
elevation: 1,
justifyContent: "center",
}}
>
<View
style={{
backgroundColor: theme.textColorPrimary,
width: 44,
height: 10,
marginLeft: 8,
borderRadius: 50,
}}
/>
</View>
<View style={{ padding: 8 }}>
<View
style={{
height: 18,
backgroundColor: theme.textColorSecondary,
borderRadius: 4,
}}
/>
<View style={{ paddingVertical: 4, flexDirection: "row" }}>
<View
style={{
height: 10,
width: 44,
backgroundColor: theme.textColorPrimary,
borderRadius: 50,
}}
/>
<View
style={{
height: 10,
width: 16,
marginLeft: 4,
backgroundColor: theme.colorAccent,
borderRadius: 50,
}}
/>
</View>
<View style={{ flexDirection: "row" }}>
<View
style={{
height: 10,
width: 24,
backgroundColor: theme.textColorSecondary,
borderRadius: 50,
}}
/>
<View
style={{
height: 10,
width: 24,
backgroundColor: theme.textColorSecondary,
borderRadius: 50,
marginLeft: 4,
}}
/>
</View>
</View>
<View
style={{
position: "absolute",
bottom: 0,
left: 0,
right: 0,
height: 24,
backgroundColor: theme.colorPrimary,
justifyContent: "center",
}}
>
<View
style={{
flex: 1,
justifyContent: "space-between",
alignItems: "center",
flexDirection: "row",
paddingHorizontal: 16,
}}
>
<View
style={{
height: 12,
width: 12,
borderRadius: 50,
backgroundColor: theme.textColorPrimary,
opacity: 0.54,
}}
/>
<View
style={{
height: 12,
width: 12,
borderRadius: 50,
backgroundColor: theme.colorAccent,
}}
/>
<View
style={{
height: 12,
width: 12,
borderRadius: 50,
backgroundColor: theme.textColorPrimary,
opacity: 0.54,
}}
/>
</View>
</View>
</Pressable>
</View>
<Text
style={{
color: currentTheme.textColorPrimary,
fontSize: 12,
paddingVertical: 4,
}}
>
{theme.name}
</Text>
</View>
);
const styles = StyleSheet.create({});
-141
View File
@@ -1,141 +0,0 @@
import React, { useRef } from "react";
import { View, TextInput, StyleSheet } from "react-native";
import {
TouchableRipple,
IconButton,
Appbar as MaterialAppbar,
} from "react-native-paper";
import Constants from "expo-constants";
import { useNavigation } from "@react-navigation/native";
import { useSelector } from "react-redux";
export const Appbar = ({ title, onBackAction }) => {
const theme = useSelector((state) => state.themeReducer.theme);
return (
<MaterialAppbar.Header style={{ backgroundColor: theme.colorPrimary }}>
{onBackAction && (
<MaterialAppbar.BackAction onPress={onBackAction} />
)}
<MaterialAppbar.Content
title={title}
titleStyle={{ color: theme.textColorPrimary }}
/>
</MaterialAppbar.Header>
);
};
export const SearchAppbar = ({
placeholder,
getSearchResults,
setSearchText,
searchText,
getNovels,
setLoading,
screen,
onFilter,
}) => {
const searchRef = useRef(null);
const navigation = useNavigation();
const theme = useSelector((state) => state.themeReducer.theme);
return (
<TouchableRipple
borderless
onPress={() => searchRef.current.focus()}
style={[
styles.searchAppbarContainer,
{ backgroundColor: theme.searchBarColor },
]}
>
<View
style={{
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<View style={{ flex: 1, flexDirection: "row" }}>
{screen === "Library" ? (
<IconButton
icon="magnify"
color={theme.textColorSecondary}
style={{ marginLeft: 0 }}
size={23}
/>
) : (
<IconButton
icon="arrow-left"
color={theme.textColorSecondary}
style={{ marginLeft: 0 }}
size={23}
onPress={() => navigation.goBack()}
/>
)}
<TextInput
ref={searchRef}
style={{
fontSize: 16,
color: theme.textColorSecondary,
}}
placeholder={placeholder}
placeholderTextColor={theme.textColorSecondary}
onChangeText={(text) => {
setSearchText(text);
if (screen === "Library") {
getSearchResults(text);
}
}}
onSubmitEditing={() => {
if (screen === "Extension") {
getSearchResults(searchText);
}
}}
defaultValue={searchText}
/>
</View>
{searchText !== "" && (
<IconButton
icon="close"
color={theme.textColorSecondary}
style={{ marginRight: 0 }}
size={23}
onPress={() => {
setLoading?.(true);
getNovels();
setSearchText("");
}}
/>
)}
<IconButton
icon="filter-variant"
color={theme.textColorSecondary}
style={{ marginRight: 0 }}
size={23}
disabled={typeof onFilter === "function" ? false : true}
/**
* TODO
*/
onPress={() => onFilter?.()}
/>
</View>
</TouchableRipple>
);
};
const styles = StyleSheet.create({
searchAppbarContainer: {
marginTop: Constants.statusBarHeight + 8,
height: 48,
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 16,
marginBottom: 12,
borderRadius: 8,
marginHorizontal: 12,
elevation: 2,
},
});
@@ -1,23 +0,0 @@
import React from "react";
import { View } from "react-native";
import { useSelector } from "react-redux";
const BottomSheetHandle = () => {
const theme = useSelector((state) => state.themeReducer.theme);
return (
<>
<View
style={{
backgroundColor: theme.textColorHintDark,
height: 5,
width: 30,
borderRadius: 50,
top: 10,
alignSelf: "center",
}}
/>
</>
);
};
export default BottomSheetHandle;
-212
View File
@@ -1,212 +0,0 @@
import React from "react";
import { StyleSheet, View, Text, ImageBackground, Image } from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { TouchableRipple } from "react-native-paper";
import { useSelector } from "react-redux";
const NovelCover = ({ item, onPress, libraryStatus }) => {
const displayMode = useSelector(
(state) => state.settingsReducer.displayMode
);
const theme = useSelector((state) => state.themeReducer.theme);
const itemsPerRow = useSelector(
(state) => state.settingsReducer.itemsPerRow
);
const height = {
1: 550,
2: 280,
3: 180,
4: 140,
5: 100,
6: 80,
7: 70,
8: 58,
9: 55,
10: 50,
};
return displayMode !== 2 ? (
<View style={{ flex: 1 / itemsPerRow }}>
<TouchableRipple
borderless
centered
rippleColor={theme.rippleColor}
style={styles.opac}
onPress={onPress}
>
<>
{item.novelCover && (
<ImageBackground
source={{ uri: item.novelCover }}
style={{ height: height[itemsPerRow] }}
imageStyle={[
{ borderRadius: 4 },
libraryStatus && { opacity: 0.5 },
]}
progressiveRenderingEnabled={true}
>
<View style={{ flexDirection: "row", margin: 4 }}>
{item.chaptersDownloaded && (
<Text
style={[
{
flex: 0,
backgroundColor: "#47a84a",
color: "#FFFFFF",
borderTopLeftRadius: 4,
borderBottomLeftRadius: 4,
paddingTop: 2,
paddingHorizontal: 4,
alignSelf: "flex-start",
fontSize: 12,
},
!item.chaptersUnread && {
borderTopRightRadius: 4,
borderBottomRightRadius: 4,
},
]}
>
{item.chaptersDownloaded}
</Text>
)}
{item.chaptersUnread && (
<Text
style={[
{
flex: 0,
backgroundColor: "#2979FF",
color: "#FFFFFF",
borderTopRightRadius: 4,
borderBottomRightRadius: 4,
paddingTop: 2,
paddingHorizontal: 4,
alignSelf: "flex-start",
fontSize: 12,
},
!item.chaptersDownloaded && {
borderTopLeftRadius: 4,
borderBottomLeftRadius: 4,
},
]}
>
{item.chaptersUnread}
</Text>
)}
</View>
{displayMode === 0 && (
<View style={styles.titleContainer}>
<LinearGradient
colors={[
"transparent",
"rgba(0,0,0,0.6)",
]}
style={styles.linearGradient}
>
<Text
numberOfLines={2}
style={[
styles.title,
{
color:
"rgba(255,255,255,1)",
},
]}
>
{item.novelName}
</Text>
</LinearGradient>
</View>
)}
</ImageBackground>
)}
{displayMode === 1 && (
<Text
numberOfLines={2}
style={[
styles.title,
{
color: theme.textColorPrimary,
padding: 4,
},
]}
>
{item.novelName}
</Text>
)}
</>
</TouchableRipple>
</View>
) : (
<TouchableRipple
borderless
centered
rippleColor={theme.rippleColor}
style={styles.listView}
onPress={onPress}
>
<>
<Image
source={{ uri: item.novelCover }}
style={styles.extensionIcon}
/>
<Text
style={{
flex: 1,
color: theme.textColorPrimary,
marginLeft: 16,
fontSize: 15,
paddingRight: 8,
}}
numberOfLines={1}
>
{item.novelName}
</Text>
</>
</TouchableRipple>
);
};
export default NovelCover;
const styles = StyleSheet.create({
// logo: {
// height: 180,
// },
titleContainer: {
flex: 1,
justifyContent: "flex-end",
borderRadius: 4,
},
title: {
fontFamily: "pt-sans-bold",
fontSize: 14,
padding: 8,
},
linearGradient: {
bottom: -1,
borderRadius: 4,
},
opac: {
paddingHorizontal: 4.5,
paddingVertical: 4,
borderRadius: 4,
},
extensionIcon: {
width: 42,
height: 42,
borderRadius: 4,
},
listView: {
flex: 1,
paddingVertical: 8,
paddingHorizontal: 12,
flexDirection: "row",
alignItems: "center",
borderRadius: 4,
},
});
-41
View File
@@ -1,41 +0,0 @@
import React from "react";
import { Checkbox } from "react-native-paper";
import { useSelector } from "react-redux";
export const DisplayCheckbox = ({ displayMode, onPress, value }) => {
const theme = useSelector((state) => state.themeReducer.theme);
const label = {
0: "Compact Grid",
1: "Comfortable Grid",
2: "List",
};
return (
<Checkbox.Item
label={label[value]}
labelStyle={{ color: theme.textColorPrimary }}
status={displayMode === value ? "checked" : "unchecked"}
mode="ios"
uncheckedColor={theme.textColorSecondary}
color={theme.colorAccentDark}
onPress={onPress}
/>
);
};
export const ThemeCheckbox = ({ onPress, label, checked }) => {
const theme = useSelector((state) => state.themeReducer.theme);
return (
<Checkbox.Item
label={label}
labelStyle={{ color: theme.textColorPrimary }}
status={checked ? "checked" : "unchecked"}
mode="ios"
uncheckedColor={theme.textColorSecondary}
color={theme.colorAccentDark}
onPress={onPress}
/>
);
};
+159 -49
View File
@@ -1,4 +1,5 @@
import * as SQLite from "expo-sqlite";
import { getSource } from "../../sources/sources";
const db = SQLite.openDatabase("lnreader.db");
const insertChaptersQuery = `INSERT INTO chapters (chapterUrl, chapterName, releaseDate, novelId) values (?, ?, ?, ?)`;
@@ -16,15 +17,15 @@ export const insertChapters = async (novelId, chapters) => {
novelId,
],
(txObj, res) => {},
(txObj, error) =>
console.log(
"Error ",
error,
" Chapter URL: ",
chapter.chapterUrl,
" Novel ID ",
novelId
)
(txObj, error) => {}
// console.log(
// "Error ",
// error,
// " Chapter URL: ",
// chapter.chapterUrl,
// " Novel ID ",
// novelId
// )
),
(txObj, res) => {
console.log("Success");
@@ -34,8 +35,8 @@ export const insertChapters = async (novelId, chapters) => {
});
};
const getChaptersQuery = (sort, filter) =>
`SELECT * FROM chapters WHERE novelId = ? ${filter ?? ""} ${sort ?? ""}`;
const getChaptersQuery = (sort = "ORDER BY chapterId ASC", filter = "") =>
`SELECT * FROM chapters WHERE novelId = ? ${filter} ${sort}`;
export const getChapters = (novelId, sort, filter) => {
return new Promise((resolve, reject) =>
@@ -54,7 +55,7 @@ export const getChapters = (novelId, sort, filter) => {
const getChapterQuery = `SELECT * FROM downloads WHERE downloadChapterId = ?`;
export const getChapter = async (chapterId) => {
export const getChapterFromDB = async (chapterId) => {
return new Promise((resolve, reject) =>
db.transaction((tx) => {
tx.executeSql(
@@ -69,6 +70,36 @@ export const getChapter = async (chapterId) => {
);
};
export const getNextChapterFromDB = async (novelId, chapterId) => {
return new Promise((resolve, reject) =>
db.transaction((tx) => {
tx.executeSql(
`SELECT * FROM chapters WHERE novelId = ? AND chapterId > ?`,
[novelId, chapterId],
(tx, results) => {
resolve(results.rows.item(0));
},
(txObj, error) => console.log("Error ", error)
);
})
);
};
export const getPrevChapterFromDB = async (novelId, chapterId) => {
return new Promise((resolve, reject) =>
db.transaction((tx) => {
tx.executeSql(
`SELECT * FROM chapters WHERE novelId = ? AND chapterId < ?`,
[novelId, chapterId],
(tx, results) => {
resolve(results.rows.item(results.rows.length - 1));
},
(txObj, error) => console.log("Error ", error)
);
})
);
};
const markChapterReadQuery = `UPDATE chapters SET \`read\` = 1 WHERE chapterId = ?`;
export const markChapterRead = async (chapterId) => {
@@ -82,6 +113,45 @@ export const markChapterRead = async (chapterId) => {
});
};
const markChapterUnreadQuery = `UPDATE chapters SET \`read\` = 0 WHERE chapterId = ?`;
export const markChapterUnread = async (chapterId) => {
db.transaction((tx) => {
tx.executeSql(
markChapterUnreadQuery,
[chapterId],
(tx, res) => {},
(tx, error) => console.log(error)
);
});
};
const markAllChaptersReadQuery = `UPDATE chapters SET \`read\` = 1 WHERE novelId = ?`;
export const markAllChaptersRead = async (novelId) => {
db.transaction((tx) => {
tx.executeSql(
markAllChaptersReadQuery,
[novelId],
(tx, res) => {},
(tx, error) => console.log(error)
);
});
};
const markAllChaptersUnreadQuery = `UPDATE chapters SET \`read\` = 0 WHERE novelId = ?`;
export const markAllChaptersUnread = async (novelId) => {
db.transaction((tx) => {
tx.executeSql(
markAllChaptersUnreadQuery,
[novelId],
(tx, res) => {},
(tx, error) => console.log(error)
);
});
};
const isChapterDownloadedQuery = `SELECT * FROM downloads WHERE downloadChapterId=?`;
export const isChapterDownloaded = async (chapterId) => {
@@ -103,20 +173,17 @@ export const isChapterDownloaded = async (chapterId) => {
);
};
const downloadChapterQuery = `INSERT INTO downloads (downloadChapterId, chapterName, chapterText, prevChapter, nextChapter) VALUES (?, ?, ?, ?, ?)`;
const downloadChapterQuery = `INSERT INTO downloads (downloadChapterId, chapterName, chapterText) VALUES (?, ?, ?)`;
export const downloadChapter = async (
extensionId,
sourceId,
novelUrl,
chapterUrl,
chapterId
) => {
const downloadUrl = `https://lnreader-extensions.herokuapp.com/api/${extensionId}/novel/${novelUrl}${chapterUrl}`;
const source = getSource(sourceId);
// console.log(downloadUrl);
const response = await fetch(downloadUrl);
const chapter = await response.json();
const chapter = await source.parseChapter(novelUrl, chapterUrl);
db.transaction((tx) => {
tx.executeSql(
@@ -125,15 +192,10 @@ export const downloadChapter = async (
);
tx.executeSql(
downloadChapterQuery,
[
chapterId,
chapter.chapterName,
chapter.chapterText,
chapter.prevChapter,
chapter.nextChapter,
],
(tx, res) =>
console.log(`Downloaded Chapter ${chapter.chapterUrl}`),
[chapterId, chapter.chapterName, chapter.chapterText],
(tx, res) => {
// console.log(`Downloaded Chapter ${chapter.chapterUrl}`);
},
(txObj, error) => console.log("Error ", error)
);
});
@@ -147,7 +209,7 @@ export const deleteChapter = async (chapterId) => {
tx.executeSql(
updateIsDownloadedQuery,
[chapterId],
(tx, res) => console.log(`Chaptermarked not downapdeod deleted`),
(tx, res) => {},
(txObj, error) => console.log("Error ", error)
);
tx.executeSql(
@@ -159,26 +221,6 @@ export const deleteChapter = async (chapterId) => {
});
};
// export const deleteAllChapters = async (novelId) => {
// const updateIsDownloadedQuery = `UPDATE chapters SET downloaded = 0 WHERE novelId=?`;
// const deleteChapterQuery = `DELETE FROM downloads WHERE downloadChapterId=?`;
// db.transaction((tx) => {
// tx.executeSql(
// updateIsDownloadedQuery,
// [chapterId],
// (tx, res) => console.log(`Chaptermarked not downapdeod deleted`),
// (txObj, error) => console.log("Error ", error)
// );
// tx.executeSql(
// deleteChapterQuery,
// [chapterId],
// (tx, res) => console.log(`Chapter deleted`),
// (txObj, error) => console.log("Error ", error)
// );
// });
// };
const getLastReadChapterQuery = `
SELECT chapters.*
FROM history
@@ -199,3 +241,71 @@ export const getLastReadChapter = async (novelId) => {
});
});
};
const bookmarkChapterQuery = `UPDATE chapters SET bookmark = ? WHERE chapterId = ?`;
export const bookmarkChapter = async (bookmark, chapterId) => {
db.transaction((tx) => {
tx.executeSql(
bookmarkChapterQuery,
[!bookmark, chapterId],
(tx, res) => {},
(txObj, error) => console.log("Error ", error)
);
});
};
const markPreviuschaptersReadQuery = `UPDATE chapters SET \`read\` = 1 WHERE chapterId < ? AND novelId = ?`;
export const markPreviuschaptersRead = async (chapterId, novelId) => {
db.transaction((tx) => {
tx.executeSql(
markPreviuschaptersReadQuery,
[chapterId, novelId],
(tx, res) => {},
(tx, error) => console.log(error)
);
});
};
const markPreviousChaptersUnreadQuery = `UPDATE chapters SET \`read\` = 0 WHERE chapterId < ? AND novelId = ?`;
export const markPreviousChaptersUnread = async (chapterId, novelId) => {
db.transaction((tx) => {
tx.executeSql(
markPreviousChaptersUnreadQuery,
[chapterId, novelId],
(tx, res) => {},
(tx, error) => console.log(error)
);
});
};
const getDownloadedChaptersQuery = `
SELECT chapters.*, novels.sourceId, novels.novelName, novels.novelCover, novels.novelUrl
FROM chapters
JOIN novels
ON chapters.novelId = novels.novelId
WHERE chapters.downloaded = 1`;
export const getDownloadedChapters = () => {
return new Promise((resolve, reject) =>
db.transaction((tx) => {
tx.executeSql(
getDownloadedChaptersQuery,
null,
(txObj, { rows: { _array } }) => {
resolve(_array);
},
(txObj, error) => console.log("Error ", error)
);
})
);
};
export const deleteDownloads = async () => {
db.transaction((tx) => {
tx.executeSql(`UPDATE chapters SET downloaded = 0`);
tx.executeSql("DELETE FROM downloads; VACCUM;");
});
};
+13 -7
View File
@@ -8,9 +8,12 @@ const getHistoryQuery = `
ON history.historyChapterId = chapters.chapterId
JOIN novels
ON history.historyNovelId = novels.novelId
ORDER BY history.historyTimeRead DESC`;
GROUP BY novels.novelId
HAVING history.historyTimeRead = MAX(history.historyTimeRead)
ORDER BY history.historyTimeRead DESC
`;
export const getHistory = async () => {
export const getHistoryFromDb = async () => {
return new Promise((resolve, reject) => {
db.transaction((tx) => {
tx.executeSql(
@@ -41,15 +44,18 @@ export const insertHistory = async (novelId, chapterId) => {
});
};
export const deleteHistory = async (novelId) => {
export const deleteHistory = async (historyId) => {
db.transaction((tx) => {
tx.executeSql("DELETE FROM history WHERE historyNovelId = ?", [
novelId,
]);
tx.executeSql(
"DELETE FROM history WHERE historyId = ?",
[historyId],
(txObj, res) => {},
(txObj, error) => console.log("Error ", error)
);
});
};
export const deleteAllHistory = () => {
export const deleteAllHistory = async () => {
db.transaction((tx) => {
tx.executeSql("DELETE FROM history; VACCUM;");
});
+29 -27
View File
@@ -1,7 +1,7 @@
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
const getLibraryQuery = `
const getLibraryQuery = (sort, filter) => `
SELECT novels.*, C.chaptersUnread, D.chaptersDownloaded
FROM novels
LEFT JOIN (
@@ -18,15 +18,15 @@ const getLibraryQuery = `
GROUP BY chapters.novelId
) AS D
ON novels.novelId = D.novelId
WHERE novels.followed = 1
GROUP BY novels.novelId
WHERE novels.followed = 1 ${filter ? "AND " + filter : ""}
${sort ? "ORDER BY " + sort : ""}
`;
export const getLibrary = () => {
export const getLibrary = (sort, filter) => {
return new Promise((resolve, reject) =>
db.transaction((tx) => {
tx.executeSql(
getLibraryQuery,
getLibraryQuery(sort, filter),
null,
(txObj, { rows: { _array } }) => {
// console.log(_array);
@@ -38,32 +38,34 @@ export const getLibrary = () => {
);
};
// const searchLibraryQuery = ``;
const searchLibraryQuery = (searchText, sort, filter) =>
`
SELECT novels.*, C.chaptersUnread, D.chaptersDownloaded
FROM novels
LEFT JOIN (
SELECT chapters.novelId, COUNT(*) AS chaptersUnread
FROM chapters
WHERE chapters.read = 0
GROUP BY chapters.novelId
) AS C
ON novels.novelId = C.novelId
LEFT JOIN (
SELECT chapters.novelId, COUNT(*) AS chaptersDownloaded
FROM chapters
WHERE chapters.downloaded = 1
GROUP BY chapters.novelId
) AS D
ON novels.novelId = D.novelId
WHERE novels.followed = 1 AND novelName LIKE '%${searchText}%' ${
filter ? "AND " + filter : ""
}
${sort ? "ORDER BY " + sort : ""} `;
export const searchLibrary = (searchText) => {
export const searchLibrary = (searchText, sort, filter) => {
return new Promise((resolve, reject) => {
db.transaction((tx) => {
tx.executeSql(
`
SELECT novels.*, C.chaptersUnread, D.chaptersDownloaded
FROM novels
LEFT JOIN (
SELECT chapters.novelId, COUNT(*) AS chaptersUnread
FROM chapters
WHERE chapters.read = 0
GROUP BY chapters.novelId
) AS C
ON novels.novelId = C.novelId
LEFT JOIN (
SELECT chapters.novelId, COUNT(*) AS chaptersDownloaded
FROM chapters
WHERE chapters.downloaded = 1
GROUP BY chapters.novelId
) AS D
ON novels.novelId = D.novelId
WHERE novels.followed = 1 AND novelName LIKE '%${searchText}%'
GROUP BY novels.novelId
`,
searchLibraryQuery(searchText, sort, filter),
null,
(txObj, { rows: { _array } }) => resolve(_array),
(txObj, error) => console.log("Error ", error)
+160 -6
View File
@@ -1,6 +1,14 @@
import * as SQLite from "expo-sqlite";
const db = SQLite.openDatabase("lnreader.db");
import BackgroundService from "react-native-background-actions";
import * as DocumentPicker from "expo-document-picker";
import { fetchChapters, fetchNovel } from "../../services/Source/source";
import { insertChapters } from "./ChapterQueries";
import { showToast } from "../../hooks/showToast";
const insertNovelQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
export const insertNovel = async (novel) => {
@@ -39,6 +47,17 @@ export const followNovel = async (followed, novelId) => {
});
};
export const unfollowNovel = async (novelId) => {
db.transaction((tx) => {
tx.executeSql(
"UPDATE novels SET followed = 0 WHERE novelId = ?",
[novelId],
(tx, res) => {},
(txObj, error) => console.log("Error ", error)
);
});
};
const checkNovelInCacheQuery = `SELECT * FROM novels WHERE novelUrl=? LIMIT 1`;
export const checkNovelInCache = (novelUrl) => {
@@ -60,12 +79,12 @@ export const checkNovelInCache = (novelUrl) => {
);
};
export const getNovel = async (novelUrl) => {
export const getNovel = async (sourceId, novelUrl) => {
return new Promise((resolve, reject) =>
db.transaction((tx) => {
tx.executeSql(
`SELECT * FROM novels WHERE novelUrl = ?`,
[novelUrl],
`SELECT * FROM novels WHERE novelUrl = ? AND sourceId = ?`,
[novelUrl, sourceId],
(txObj, { rows }) => resolve(rows.item(0)),
(txObj, error) => console.log("Error ", error)
);
@@ -78,10 +97,145 @@ export const deleteNovelCache = () => {
tx.executeSql(
"DELETE FROM novels WHERE followed = 0",
null,
(txObj, res) => {
console.log("Cleared Novel Cache");
},
(txObj, res) => showToast("Entries deleted"),
(txObj, error) => console.log("Error ", error)
);
});
};
const restoreFromBackupQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre, followed, unread) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
export const restoreLibrary = async (novel) => {
return new Promise((resolve, reject) =>
db.transaction((tx) =>
tx.executeSql(
restoreFromBackupQuery,
[
novel.novelUrl,
novel.sourceUrl,
novel.sourceId,
novel.source,
novel.novelName,
novel.novelCover,
novel.novelSummary,
novel.author,
novel.artist,
novel.status,
novel.genre,
novel.followed,
novel.unread,
],
async (txObj, { insertId }) => {
const chapters = await fetchChapters(
novel.sourceId,
novel.novelUrl
);
await insertChapters(insertId, chapters);
},
(txObj, error) => console.log("Error ", error)
)
)
);
};
const migrateNovelQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre, followed) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
export const migrateNovel = async (sourceId, novelUrl) => {
try {
const novel = await fetchNovel(sourceId, novelUrl);
const options = {
taskName: "Migration",
taskTitle: `Migrating ${novel.novelName} to new source`,
taskDesc: novel.source,
taskIcon: {
name: "notification_icon",
type: "drawable",
},
color: "#00adb5",
parameters: {
delay: 1000,
},
progressBar: {
max: 1,
value: 0,
indeterminate: true,
},
};
const veryIntensiveTask = async () => {
await new Promise(async (resolve) => {
db.transaction((tx) =>
tx.executeSql(
migrateNovelQuery,
[
novel.novelUrl,
novel.sourceUrl,
novel.sourceId,
novel.source,
novel.novelName,
novel.novelCover,
novel.novelSummary,
novel.author,
novel.artist,
novel.status,
novel.genre,
1,
],
async (txObj, { insertId }) => {
const chapters = await fetchChapters(
novel.sourceId,
novel.novelUrl
);
await insertChapters(insertId, chapters);
resolve();
},
(txObj, error) => console.log("Error ", error)
)
);
});
};
await BackgroundService.start(veryIntensiveTask, options);
await BackgroundService.updateNotification({
progressBar: { value: 1, indeterminate: false },
});
} catch (error) {
showToast(error.message);
}
};
export const updateNovelInfo = async (info, novelId) => {
db.transaction((tx) => {
tx.executeSql(
"UPDATE novels SET novelName = ?, novelSummary = ?, author = ?, genre = ?, status = ? WHERE novelId = ?",
[
info.novelName,
info.novelSummary,
info.author,
info.genre,
info.status,
novelId,
],
(txObj, res) => {},
(txObj, error) => console.log("Error ", error)
);
});
};
export const pickCustomNovelCover = async (novelId) => {
const image = await DocumentPicker.getDocumentAsync({ type: "image/*" });
if (image.type === "success" && image.uri) {
db.transaction((tx) => {
tx.executeSql(
"UPDATE novels SET novelCover = ? WHERE novelId = ?",
[image.uri, novelId],
(txObj, res) => {},
(txObj, error) => console.log("Error ", error)
);
});
return image.uri;
}
};

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