Custom covers and open links in reader

This commit is contained in:
Rajarshee Chatterjee
2021-08-03 14:38:54 +05:30
parent 01814e92d0
commit 40d2052f16
16 changed files with 155 additions and 28 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ const NovelCover = ({ item, onPress, libraryStatus, theme }) => {
source={{
uri:
item.novelCover &&
item.novelCover.startsWith("https")
!item.novelCover.startsWith("/")
? item.novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
}}
+19
View File
@@ -2,11 +2,13 @@ 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";
import { setNovel } from "../../redux/novel/novel.actions";
const insertNovelQuery = `INSERT INTO novels (novelUrl, sourceUrl, sourceId, source, novelName, novelCover, novelSummary, author, artist, status, genre) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
@@ -211,3 +213,20 @@ export const updateNovelInfo = async (info, novelId) => {
);
});
};
export const setCustomNovelCover = async (novelId) => {
const image = await DocumentPicker.getDocumentAsync({ type: "image/*" });
if (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;
}
};
@@ -22,7 +22,7 @@ const GlobalSearchNovelCover = ({
<Image
source={{
uri:
novelCover && novelCover.startsWith("https")
novelCover && !novelCover.startsWith("/")
? novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
}}
@@ -9,7 +9,7 @@ const GlobalSearchNovelList = ({ data, theme, library, navigation }) => {
(obj) => obj.novelUrl === novelUrl && obj.sourceId === sourceId
);
const navigateToNovel = (item) => navigation.navigate("Novel", item);
const navigateToNovel = (item) => navigation.push("Novel", item);
const renderItem = ({ item }) => (
<GlobalSearchNovelCover
+21 -1
View File
@@ -54,6 +54,7 @@ import { SET_LAST_READ } from "../../redux/preferences/preference.types";
import WebView from "react-native-webview";
import { htmlToText } from "../../sources/helpers/htmlToText";
import { setAppSettings } from "../../redux/settings/settings.actions";
import { cleanHtml } from "../../sources/helpers/cleanHtml";
const Chapter = ({ route, navigation }) => {
const {
@@ -370,11 +371,30 @@ const Chapter = ({ route, navigation }) => {
backgroundColor: readerBackground(
reader.theme
),
paddingBottom: 24,
}}
originWhitelist={["*"]}
scalesPageToFit={true}
showsVerticalScrollIndicator={false}
onScroll={onScroll}
onNavigationStateChange={async ({
url,
}) => {
if (
(sourceId === 50 ||
sourceId == 62) &&
url !== "about:blank"
) {
setLoading(true);
const res = await fetchChapter(
sourceId,
novelUrl,
url
);
setChapter(res);
setLoading(false);
}
}}
source={{
html: `
<html>
@@ -416,7 +436,7 @@ const Chapter = ({ route, navigation }) => {
</style>
</head>
<body>
${chapter.chapterText.trim()}
${cleanHtml(chapter.chapterText)}
</body>
</html>
`,
+1 -1
View File
@@ -68,7 +68,7 @@ const HistoryItem = ({
<Image
source={{
uri:
novelCover && novelCover.startsWith("https")
novelCover && !novelCover.startsWith("/")
? novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
}}
+52 -10
View File
@@ -23,6 +23,7 @@ import {
markChaptersRead,
markChapterUnreadAction,
markPreviousChaptersReadAction,
setNovel,
sortAndFilterChapters,
updateNovelAction,
} from "../../redux/novel/novel.actions";
@@ -42,6 +43,7 @@ import { Row } from "../../components/Common";
import JumpToChapterModal from "./components/JumpToChapterModal";
import { Actionbar } from "../../components/Actionbar/Actionbar";
import EditInfoModal from "./components/EditInfoModal";
import { setCustomNovelCover } from "../../database/queries/NovelQueries";
const Novel = ({ route, navigation }) => {
const item = route.params;
@@ -54,6 +56,7 @@ const Novel = ({ route, navigation }) => {
const [selected, setSelected] = useState([]);
const [downloadMenu, showDownloadMenu] = useState(false);
const [extraMenu, showExtraMenu] = useState(false);
let flatlistRef = useRef(null);
let chaptersSettingsSheetRef = useRef(null);
@@ -305,22 +308,13 @@ const Novel = ({ route, navigation }) => {
}}
onPress={() => showJumpToChapterModal(true)}
/>
<IconButton
icon="file-document-edit-outline"
color="white"
size={21}
style={{
marginTop: StatusBar.currentHeight + 8,
}}
onPress={() => showEditInfoModal(true)}
/>
<IconButton
icon="share-variant"
color="white"
size={21}
style={{
marginTop: StatusBar.currentHeight + 8,
marginRight: 16,
}}
onPress={() =>
Share.share({
@@ -328,6 +322,54 @@ const Novel = ({ route, navigation }) => {
})
}
/>
<Menu
visible={extraMenu}
onDismiss={() => showExtraMenu(false)}
anchor={
<IconButton
icon="dots-vertical"
color="white"
size={21}
style={{
marginTop:
StatusBar.currentHeight + 8,
marginRight: 16,
}}
onPress={() => showExtraMenu(true)}
/>
}
contentStyle={{
backgroundColor: theme.menuColor,
}}
>
<Menu.Item
title="Edit info"
style={{ backgroundColor: theme.menuColor }}
titleStyle={{
color: theme.textColorPrimary,
}}
onPress={() => showEditInfoModal(true)}
/>
<Menu.Item
title="Edit cover"
style={{ backgroundColor: theme.menuColor }}
titleStyle={{
color: theme.textColorPrimary,
}}
onPress={async () => {
const cover = await setCustomNovelCover(
novelId
);
dispatch(
setNovel({
...novel,
novelCover: cover,
})
);
}}
/>
</Menu>
</Row>
</View>
)}
+14 -7
View File
@@ -55,21 +55,28 @@ const NovelInfoHeader = ({
<View>
<CoverImage
source={{
uri:
item.novelCover && item.novelCover.startsWith("https")
uri: loading
? item.novelCover && !item.novelCover.startsWith("/")
? item.novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"
: novel.novelCover && !novel.novelCover.startsWith("/")
? novel.novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
}}
theme={theme}
>
<NovelInfoContainer>
<NovelThumbnail
source={{
uri:
item.novelCover &&
item.novelCover.startsWith("https")
uri: loading
? item.novelCover &&
!item.novelCover.startsWith("/")
? item.novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true"
: novel.novelCover &&
!novel.novelCover.startsWith("/")
? novel.novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
}}
/>
<View style={styles.novelDetails}>
@@ -29,7 +29,7 @@ const UpdateCard = ({
source={{
uri:
item.novelCover &&
item.novelCover.startsWith("http")
!item.novelCover.startsWith("/")
? item.novelCover
: "https://github.com/LNReader/lnreader-sources/blob/main/src/coverNotAvailable.jpg?raw=true",
}}
-1
View File
@@ -6,7 +6,6 @@ import { restoreLibrary } from "../database/queries/NovelQueries";
import { showToast } from "../hooks/showToast";
import * as Notifications from "expo-notifications";
import BackgroundService from "react-native-background-actions";
Notifications.setNotificationHandler({
handleNotification: async () => {
+1 -1
View File
@@ -85,7 +85,7 @@ const parseNovelAndChapters = async (novelUrl) => {
.replace(/[\t\n]/g, "")
.trim();
novel.novelCover = $("figure.cover > img").attr("src");
novel.novelCover = $("figure.cover > img").attr("data-src");
novel.genre = "";
+21 -1
View File
@@ -80,7 +80,7 @@ const parseNovelAndChapters = async (novelUrl) => {
let summary = $("#editdescription").text().trim();
novel.summary = summary + `\nType: ${type}`;
novel.summary = summary + `\n\nType: ${type}`;
let novelChapters = [];
@@ -119,6 +119,13 @@ const parseNovelAndChapters = async (novelUrl) => {
return novel;
};
function getLocation(href) {
var match = href.match(
/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/
);
return match && `${match[1]}//${match[3]}`;
}
const parseChapter = async (novelUrl, chapterUrl) => {
const url = chapterUrl;
@@ -135,6 +142,8 @@ const parseChapter = async (novelUrl, chapterUrl) => {
});
body = await result.text();
// console.log(result.url);
// console.log("Redirected URL: ", result.url);
$ = cheerio.load(body);
@@ -173,6 +182,8 @@ const parseChapter = async (novelUrl, chapterUrl) => {
} else if (isTumblr) {
chapterText = $(".post").html();
} else if (isBlogspot) {
$(".post-share-buttons").remove();
chapterText = $(".entry-content").html();
} else if (isHostedNovel) {
chapterText = $(".chapter").html();
@@ -196,6 +207,7 @@ const parseChapter = async (novelUrl, chapterUrl) => {
".post-cats",
".sidebar",
".author-avatar",
".ezoic-ad",
];
bloatClasses.map((tag) => $(tag).remove());
@@ -228,6 +240,14 @@ const parseChapter = async (novelUrl, chapterUrl) => {
if (!chapterText) {
chapterText =
"Chapter not available.\n\nReport if it's available in webview.";
} else {
/**
* Convert relative urls to absolute
*/
chapterText = chapterText.replace(
/href="\//g,
`href="${getLocation(result.url)}/`
);
}
} catch (error) {
chapterText = `Chapter not available (Error: ${error.message}).\n\nReport if it's available in webview.`;
+2
View File
@@ -115,6 +115,8 @@ const parseChapter = async (novelUrl, chapterUrl) => {
$ = cheerio.load(body);
$(".chapter-nav").remove();
let chapterName = $("#sidebar-toggler-container").next().text();
chapterName = chapterName.replace(/[\t\n]/g, "");
+16
View File
@@ -0,0 +1,16 @@
export const cleanHtml = (html) => {
let text = html;
text = text
.replace(/(font-family|line-height|font-size|text-align):.*?;/gi, "")
.replace(/class=".*?"/gi, "")
.replace(/<!--.*?-->/gim, "")
.replace(/<\s*script[^>]*>[\s\S]*?<\/script>/gim, "")
.replace(/<\s*noscript[^>]*>[\s\S]*?<\/noscript>/gim, "")
.replace(/<\s*form[^>]*>[\s\S]*?<\/form>/gim, "")
.trim();
// console.log(text);
return text;
};
+3 -1
View File
@@ -99,7 +99,9 @@ const htmlToText = (html, options = {}) => {
* Decode HTML entities.
*/
.replace(/&([^;]+);/g, decodeHtmlEntity);
.replace(/&([^;]+);/g, decodeHtmlEntity)
.trim();
return text;
};
+1 -1
View File
@@ -150,7 +150,7 @@ export const yinYangTheme = {
...PrimaryColors.dark,
colorAccent: AccentColors.yinYang,
rippleColor: RippleColors.yinYang,
searchBarColor: "#484861",
searchBarColor: "#303135",
menuColor: "#484861",
filterColor: "#FFEB3B",
dividerColor: DividerColors.DARK,