From 08860c07152ef80e989d69a512baa2acd22c3503 Mon Sep 17 00:00:00 2001 From: nyagami <86464880+nyagami@users.noreply.github.com> Date: Sun, 5 Mar 2023 11:13:00 +0700 Subject: [PATCH] fix: Minor Fixes and Optimizations (#566) Closes #557 --- .eslintrc.js | 1 - package-lock.json | 33 - package.json | 1 - src/database/queries/ChapterQueries.ts | 5 +- .../BrowseSourceScreen/BrowseSourceScreen.tsx | 6 +- src/screens/history/HistoryScreen.tsx | 2 +- src/screens/novel/NovelScreen.js | 626 +++++++++--------- .../components/DownloadCustomChapterModal.js | 9 +- src/screens/novel/components/EditInfoModal.js | 9 +- .../components/Info/NovelInfoComponents.js | 19 +- .../novel/components/Info/NovelInfoHeader.js | 91 ++- src/screens/reader/ReaderScreen.js | 69 +- src/screens/reader/components/ReaderAppbar.js | 5 - src/screens/reader/components/ReaderFooter.js | 6 - .../reader/components/ReaderSeekBar.js | 5 - .../reader/components/WebViewReader.tsx | 113 +--- .../reader/utils/sanitizeChapterText.ts | 18 +- .../SettingsGeneralScreen.tsx | 1 + 18 files changed, 456 insertions(+), 563 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 9c8a46b3..c8afe715 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,7 +7,6 @@ module.exports = { { files: ['*.js', '*.jsx', '*.ts', '*.tsx'], rules: { - 'prettier/prettier': ['error', { 'endOfLine': 'auto' }], '@typescript-eslint/no-shadow': ['warn'], 'no-shadow': 'off', 'no-undef': 'off', diff --git a/package-lock.json b/package-lock.json index 80ea3f77..bf0abb10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,6 @@ "@react-navigation/stack": "^6.0.11", "@reduxjs/toolkit": "^1.7.2", "@shopify/flash-list": "^1.2.1", - "buffer": "^6.0.3", "cheerio": "^1.0.0-rc.10", "color": "^4.2.3", "dayjs": "^1.11.4", @@ -5325,29 +5324,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", @@ -19937,15 +19913,6 @@ "node-int64": "^0.4.0" } }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", diff --git a/package.json b/package.json index dd9ba60d..eb8d1337 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "@react-navigation/stack": "^6.0.11", "@reduxjs/toolkit": "^1.7.2", "@shopify/flash-list": "^1.2.1", - "buffer": "^6.0.3", "cheerio": "^1.0.0-rc.10", "color": "^4.2.3", "dayjs": "^1.11.4", diff --git a/src/database/queries/ChapterQueries.ts b/src/database/queries/ChapterQueries.ts index 2a795cb0..a1f6a82d 100644 --- a/src/database/queries/ChapterQueries.ts +++ b/src/database/queries/ChapterQueries.ts @@ -6,7 +6,6 @@ import { ChapterItem } from '../types'; import * as cheerio from 'cheerio'; import RNFetchBlob from 'rn-fetch-blob'; import { txnErrorCallback } from '@database/utils/helpers'; -import { LoadingImageSrc } from '@screens/reader/utils/LoadImage'; const db = SQLite.openDatabase('lnreader.db'); @@ -297,9 +296,7 @@ const downloadImages = async ( if (fileurl.charAt(0) === '-') { return loadedCheerio.html(); } - elem.replaceWith( - ``, - ); + elem.attr('src', `file://${fileurl}`); const exists = await RNFetchBlob.fs.exists(fileurl).catch(() => { showToast( `Unexpected storage error!\nRemove ${fileurl} and try downloading again`, diff --git a/src/screens/BrowseSourceScreen/BrowseSourceScreen.tsx b/src/screens/BrowseSourceScreen/BrowseSourceScreen.tsx index 476fbd2a..e5a17cf0 100644 --- a/src/screens/BrowseSourceScreen/BrowseSourceScreen.tsx +++ b/src/screens/BrowseSourceScreen/BrowseSourceScreen.tsx @@ -94,10 +94,8 @@ const BrowseSourceScreen: React.FC = ({ route }) => { navigate( 'Novel' as never, { - novelName: item.novelName, - novelCover: item.novelCover, - novelUrl: item.novelUrl, - sourceId, + ...item, + sourceId: sourceId, } as never, ), [sourceId], diff --git a/src/screens/history/HistoryScreen.tsx b/src/screens/history/HistoryScreen.tsx index 2b494e90..091bff78 100644 --- a/src/screens/history/HistoryScreen.tsx +++ b/src/screens/history/HistoryScreen.tsx @@ -118,7 +118,7 @@ const HistoryScreen = () => { item.chapterId.toString()} + keyExtractor={(item, index) => 'history' + index} renderSectionHeader={({ section: { date } }) => ( {dayjs(date).calendar()} diff --git a/src/screens/novel/NovelScreen.js b/src/screens/novel/NovelScreen.js index dcdc71f6..23a080a4 100644 --- a/src/screens/novel/NovelScreen.js +++ b/src/screens/novel/NovelScreen.js @@ -358,237 +358,273 @@ const Novel = ({ route, navigation }) => { const [editInfoModal, showEditInfoModal] = useState(false); const downloadCustomChapterModal = useBoolean(); + if (loading) { + return ; + } + return ( - {loading && } - {selected.length === 0 && ( - - navigation.goBack()} - /> - + + {selected.length === 0 ? ( + - Share.share({ - message: novel.sourceUrl, - }) - } + size={24} + style={{ marginTop: StatusBar.currentHeight + 8 }} + onPress={() => navigation.goBack()} /> - showJumpToChapterModal(true)} - /> - - showDownloadMenu(false)} - anchor={ - showDownloadMenu(true)} - /> - } - contentStyle={{ backgroundColor: theme.surface2 }} - > - { - dispatch( - downloadAllChaptersAction( - novel.sourceId, - novel.novelUrl, - [ - chapters.find( - chapter => - !!chapter.read === false && - !!chapter.downloaded === false, - ), - ], - ), - ); - showDownloadMenu(false); - }} - /> - { - dispatch( - downloadAllChaptersAction( - novel.sourceId, - novel.novelUrl, - chapters - .filter( - chapter => - !!chapter.read === false && - !!chapter.downloaded === false, - ) - .slice(0, 5), - ), - ); - showDownloadMenu(false); - }} - /> - { - dispatch( - downloadAllChaptersAction( - novel.sourceId, - novel.novelUrl, - chapters - .filter( - chapter => - !!chapter.read === false && - !!chapter.downloaded === false, - ) - .slice(0, 10), - ), - ); - showDownloadMenu(false); - }} - /> - { - downloadCustomChapterModal.setTrue(); - showDownloadMenu(false); - }} - /> - { - dispatch( - downloadAllChaptersAction( - novel.sourceId, - novel.novelUrl, - chapters.filter(chapter => !!chapter.read === false), - ), - ); - showDownloadMenu(false); - }} - /> - { - dispatch( - downloadAllChaptersAction( - novel.sourceId, - novel.novelUrl, - chapters, - ), - ); - showDownloadMenu(false); - }} - /> - + - dispatch(deleteAllChaptersAction(sourceId, chapters)) + Share.share({ + message: novel.sourceUrl, + }) } /> - + showJumpToChapterModal(true)} + /> - showExtraMenu(false)} - anchor={ - showDownloadMenu(false)} + anchor={ + showDownloadMenu(true)} + /> + } + contentStyle={{ backgroundColor: theme.surface2 }} + > + { + dispatch( + downloadAllChaptersAction( + novel.sourceId, + novel.novelUrl, + [ + chapters.find( + chapter => + !!chapter.read === false && + !!chapter.downloaded === false, + ), + ], + ), + ); + showDownloadMenu(false); }} - onPress={() => showExtraMenu(true)} /> - } - contentStyle={{ - backgroundColor: theme.surface2, + { + dispatch( + downloadAllChaptersAction( + novel.sourceId, + novel.novelUrl, + chapters + .filter( + chapter => + !!chapter.read === false && + !!chapter.downloaded === false, + ) + .slice(0, 5), + ), + ); + showDownloadMenu(false); + }} + /> + { + dispatch( + downloadAllChaptersAction( + novel.sourceId, + novel.novelUrl, + chapters + .filter( + chapter => + !!chapter.read === false && + !!chapter.downloaded === false, + ) + .slice(0, 10), + ), + ); + showDownloadMenu(false); + }} + /> + { + downloadCustomChapterModal.setTrue(); + showDownloadMenu(false); + }} + /> + { + dispatch( + downloadAllChaptersAction( + novel.sourceId, + novel.novelUrl, + chapters.filter(chapter => !!chapter.read === false), + ), + ); + showDownloadMenu(false); + }} + /> + { + dispatch( + downloadAllChaptersAction( + novel.sourceId, + novel.novelUrl, + chapters, + ), + ); + showDownloadMenu(false); + }} + /> + + dispatch(deleteAllChaptersAction(sourceId, chapters)) + } + /> + + + showExtraMenu(false)} + anchor={ + showExtraMenu(true)} + /> + } + contentStyle={{ + backgroundColor: theme.surface2, + }} + > + { + showEditInfoModal(true); + showExtraMenu(false); + }} + /> + + + + + ) : ( + + setSelected([])} + /> + + { + setSelected(chapters); }} - > - { - showEditInfoModal(true); - showExtraMenu(false); - }} - /> - - - - - )} + /> + + )} + (flatlistRef.current = ref)} + ref={flatlistRef} estimatedItemSize={64} - data={!loading && chapters} + data={chapters} extraData={[downloadQueue, selected]} removeClippedSubviews={true} maxToRenderPerBatch={5} @@ -603,7 +639,6 @@ const Novel = ({ route, navigation }) => { novel={novel} theme={theme} filter={filter} - loading={loading} lastRead={lastReadChapter} setCustomNovelCover={setCustomNovelCover} dispatch={dispatch} @@ -617,28 +652,25 @@ const Novel = ({ route, navigation }) => { refreshControl={refreshControl()} /> - {useFabForContinueReading && - !loading && - chapters.length > 0 && - lastReadChapter && ( - { - navigation.navigate( - 'Chapter', - openChapter(novel, lastReadChapter), - ); - }} - /> - )} + {useFabForContinueReading && chapters.length > 0 && lastReadChapter && ( + { + navigation.navigate( + 'Chapter', + openChapter(novel, lastReadChapter), + ); + }} + /> + )} 0} @@ -663,84 +695,48 @@ const Novel = ({ route, navigation }) => { - {!loading && ( - - showJumpToChapterModal(false)} - chapters={chapters} - novel={novel} - chapterListRef={flatlistRef.current} - navigation={navigation} - /> - showEditInfoModal(false)} - novel={novel} - theme={theme} - dispatch={dispatch} - /> - - - - - )} - {selected.length > 0 ? ( - - setSelected([])} - /> - - { - setSelected(chapters); - }} - /> - - ) : null} + + showJumpToChapterModal(false)} + chapters={chapters} + novel={novel} + chapterListRef={flatlistRef.current} + navigation={navigation} + /> + showEditInfoModal(false)} + novel={novel} + theme={theme} + dispatch={dispatch} + /> + + + + ); diff --git a/src/screens/novel/components/DownloadCustomChapterModal.js b/src/screens/novel/components/DownloadCustomChapterModal.js index 3b1d7b76..3ff5d249 100644 --- a/src/screens/novel/components/DownloadCustomChapterModal.js +++ b/src/screens/novel/components/DownloadCustomChapterModal.js @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { StyleSheet, Text, View, TextInput } from 'react-native'; -import { IconButton, Modal, Portal } from 'react-native-paper'; +import { Button, IconButton, Modal, Portal } from 'react-native-paper'; import { downloadAllChaptersAction } from '../../../redux/novel/novel.actions'; const DownloadCustomChapterModal = ({ @@ -91,6 +91,13 @@ const DownloadCustomChapterModal = ({ onPress={() => setText(prevState => prevState + 10)} /> + ); diff --git a/src/screens/novel/components/EditInfoModal.js b/src/screens/novel/components/EditInfoModal.js index 3a435309..44e531ee 100644 --- a/src/screens/novel/components/EditInfoModal.js +++ b/src/screens/novel/components/EditInfoModal.js @@ -54,8 +54,11 @@ const EditInfoModal = ({ theme, hideModal, modalVisible, novel, dispatch }) => { horizontal showsHorizontalScrollIndicator={false} > - {status.map(item => ( - + {status.map((item, index) => ( + { contentContainerStyle={{ marginVertical: 8 }} horizontal data={info.genre.split(',')} - keyExtractor={item => item} + keyExtractor={(item, index) => 'novelTag' + index} renderItem={({ item }) => ( removeTag(item)}> {item} diff --git a/src/screens/novel/components/Info/NovelInfoComponents.js b/src/screens/novel/components/Info/NovelInfoComponents.js index b98a62c7..ab0076c3 100644 --- a/src/screens/novel/components/Info/NovelInfoComponents.js +++ b/src/screens/novel/components/Info/NovelInfoComponents.js @@ -37,7 +37,7 @@ const CoverImage = ({ children, source, theme, hideBackdrop }) => { {source.uri ? ( @@ -57,7 +57,7 @@ const CoverImage = ({ children, source, theme, hideBackdrop }) => { } }; -const NovelThumbnail = ({ source, setCustomNovelCover }) => { +const NovelThumbnail = ({ source, theme, setCustomNovelCover }) => { const [expanded, setExpanded] = useState(false); if (!expanded) { @@ -69,24 +69,15 @@ const NovelThumbnail = ({ source, setCustomNovelCover }) => { } else { return ( - setExpanded(false)} - /> @@ -85,7 +84,7 @@ const NovelInfoHeader = ({ <> - {!loading && novel.author ? novel.author : 'Unknown author'} + {novel.author || 'Unknown author'} - {!loading - ? (novel.status || 'Unknown status') + ' • ' + novel.source - : 'Unknown status • Unknown source'} + {(novel.status || 'Unknown status') + ' • ' + novel.source} - {!loading && ( - <> - { - dispatch(followNovelAction(novel)); - if ( - novel.followed && - chapters.some(chapter => chapter.downloaded === 1) - ) { - deleteDownloadsSnackbar.setTrue(); - } - }} - handleTrackerSheet={() => trackerSheetRef.current.expand()} - theme={theme} + <> + { + dispatch(followNovelAction(novel)); + if ( + novel.followed && + chapters.some(chapter => chapter.downloaded === 1) + ) { + deleteDownloadsSnackbar.setTrue(); + } + }} + handleTrackerSheet={() => trackerSheetRef.current.expand()} + theme={theme} + /> + + {novel.genre ? : null} + + novelBottomSheetRef.current.expand()} + android_ripple={{ + color: color(theme.primary).alpha(0.12).string(), + }} + > + + {`${chapters.length} ${getString('novelScreen.chapters')}`} + + - - {novel.genre ? ( - - ) : null} - - novelBottomSheetRef.current.expand()} - android_ripple={{ - color: color(theme.primary).alpha(0.12).string(), - }} - > - - {`${chapters.length} ${getString('novelScreen.chapters')}`} - - - - - )} + + ); }; diff --git a/src/screens/reader/ReaderScreen.js b/src/screens/reader/ReaderScreen.js index 686ab146..292507eb 100644 --- a/src/screens/reader/ReaderScreen.js +++ b/src/screens/reader/ReaderScreen.js @@ -325,9 +325,7 @@ const ChapterContent = ({ route, navigation }) => { nextChapter={nextChapter} webViewRef={webViewRef} onLayout={() => { - if (useVolumeButtons) { - onLayout(); - } + useVolumeButtons && onLayout(); scrollTo(position?.position); }} onPress={hideHeader} @@ -335,39 +333,44 @@ const ChapterContent = ({ route, navigation }) => { navigateToChapterBySwipe={navigateToChapterBySwipe} onWebViewNavigationStateChange={onWebViewNavigationStateChange} /> - + - - - + {!hidden && ( + <> + + + + + )} ); }; diff --git a/src/screens/reader/components/ReaderAppbar.js b/src/screens/reader/components/ReaderAppbar.js index 938bd92c..2d945b77 100644 --- a/src/screens/reader/components/ReaderAppbar.js +++ b/src/screens/reader/components/ReaderAppbar.js @@ -14,7 +14,6 @@ const ReaderAppbar = ({ novelName, chapterId, chapterName, - hide, tts, textToSpeech, theme, @@ -23,10 +22,6 @@ const ReaderAppbar = ({ const { goBack } = useNavigation(); const [bookmarked, setBookmarked] = useState(bookmark); - if (hide) { - return null; - } - return ( = props => { = props => { case 'prev': navigateToChapterBySwipe('SWIPE_RIGHT'); break; - case 'imgfiles': - if (event.data) { - if (Array.isArray(event.data.imgs)) { - const promises: Promise<{ data: any; id: number }>[] = []; - if (event.data.type === 'online') { - for (let i = 0; i < event.data.imgs.length; i++) { - const { url, id } = event.data.imgs[i]; - if (url && id) { - promises.push( - RNFetchBlob.fetch('get', url, headers).then(res => ({ - data: res.base64(), - id: id, - })), - ); - } - } - } else { - for (let i = 0; i < event.data.imgs.length; i++) { - const { url, id } = event.data.imgs[i]; - if (url && id) { - promises.push( - RNFetchBlob.fs.readFile(url, 'base64').then(base64 => { - return { data: base64, id: id }; - }), - ); - } - } - } - - Promise.all(promises) - .then(datas => { - const inject = datas.reduce((p, data) => { - return ( - p + - `document.querySelector("img[file-id='${data.id}']").setAttribute("src", "data:image/jpg;base64,${data.data}"); - document.querySelector("img[file-id='${data.id}']").classList.remove("load-icon");` - ); - }, ''); - webViewRef.current?.injectJavaScript( - inject + - 'window.requestAnimationFrame(()=>sendHeight());', - ); - }) - .catch(e => { - e; // CloudFlare is too strong :D - }); - } + case 'imgfile': + if (event.data && typeof event.data === 'string') { + RNFetchBlob.fetch('get', event.data, headers).then(res => { + const base64 = res.base64(); + webViewRef.current?.injectJavaScript( + `document.querySelector("img[delayed-src='${event.data}']").src="data:image/jpg;base64,${base64}"; + document.querySelector("img[delayed-src='${event.data}']").classList.remove("load-icon"); + sendHeight(500);`, + ); + }); } break; case 'scrollend': @@ -151,6 +115,7 @@ const WebViewReader: React.FC = props => { const contentHeight = Math.round(Number(event.data)); minScroll.current = (layoutHeight / contentHeight) * 100; } + break; } }} source={{ @@ -261,22 +226,13 @@ const WebViewReader: React.FC = props => {
${getString( @@ -325,28 +281,19 @@ const WebViewReader: React.FC = props => { swipeGestures ? ` ` : '' diff --git a/src/screens/reader/utils/sanitizeChapterText.ts b/src/screens/reader/utils/sanitizeChapterText.ts index 10274e68..a6c6fb2a 100644 --- a/src/screens/reader/utils/sanitizeChapterText.ts +++ b/src/screens/reader/utils/sanitizeChapterText.ts @@ -13,16 +13,25 @@ export const sanitizeChapterText = ( html: string, options?: Options, ): string => { + if (html) { + html = html.replace(/</g, '<').replace(/>/g, '>'); + } let text = sanitizeHtml(html, { - allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'input']), + allowedTags: sanitizeHtml.defaults.allowedTags.concat([ + 'img', + 'input', + 'i', + 'em', + 'b', + 'a', + ]), allowedAttributes: { - 'img': ['src', 'type', 'file-path', 'file-id', 'offline', 'class'], + 'img': ['src', 'class'], 'a': ['href'], 'input': ['type', 'offline'], }, - allowedSchemes: ['data', 'http', 'https'], + allowedSchemes: ['data', 'http', 'https', 'file'], }); - if (text) { if (options?.removeExtraParagraphSpacing) { text = text.replace(/<\s*br[^>]*>/gi, '\n').replace(/\n{2,}/g, '\n\n'); @@ -49,6 +58,5 @@ export const sanitizeChapterText = ( text = "Chapter is empty.\n\nReport on github if it's available in webview."; } - return text; }; diff --git a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreen.tsx b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreen.tsx index 6e0a5bfe..ed988e69 100644 --- a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreen.tsx +++ b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreen.tsx @@ -169,6 +169,7 @@ const GenralSettings: React.FC = ({ navigation }) => { label={getString( 'moreScreen.settingsScreen.generalSettingsScreen.updateLibrary', )} + description={'Not recommended for low devices'} value={updateLibraryOnLaunch} onPress={() => dispatch(