diff --git a/src/Components/NovelList.js b/src/Components/NovelList.js
index 0ee6c8a5..b7dd937a 100644
--- a/src/Components/NovelList.js
+++ b/src/Components/NovelList.js
@@ -6,9 +6,11 @@ import { getDeviceOrientation } from "../Services/utils/helpers";
const NovelList = ({
data,
+ onScroll,
renderItem,
refreshControl,
ListEmptyComponent,
+ ListFooterComponent,
}) => {
const { displayMode, novelsPerRow } = useSettings();
@@ -36,6 +38,8 @@ const NovelList = ({
renderItem={renderItem}
refreshControl={refreshControl}
ListEmptyComponent={ListEmptyComponent}
+ ListFooterComponent={ListFooterComponent}
+ onScroll={onScroll}
/>
);
};
diff --git a/src/Screens/Browse/components/SourceCard.js b/src/Screens/Browse/components/SourceCard.js
index 90c62a35..36b391e4 100644
--- a/src/Screens/Browse/components/SourceCard.js
+++ b/src/Screens/Browse/components/SourceCard.js
@@ -7,7 +7,7 @@ import { pinSourceAction } from "../../../redux/source/source.actions";
import FastImage from "react-native-fast-image";
const ExtensionCard = ({ item, theme, isPinned }) => {
- const { sourceId, sourceName, sourceCover, sourceLanguage, status } = item;
+ const { sourceId, sourceName, icon, lang } = item;
const navigation = useNavigation();
const dispatch = useDispatch();
@@ -27,7 +27,7 @@ const ExtensionCard = ({ item, theme, isPinned }) => {
>
<>
@@ -41,19 +41,14 @@ const ExtensionCard = ({ item, theme, isPinned }) => {
>
{sourceName}
-
-
- {sourceLanguage}
-
-
- {status === 0 && "• Down"}
-
-
+
+ {lang}
+
{
const { sourceId, sourceName, sourceUrl } = route.params;
@@ -22,35 +24,38 @@ const Extension = ({ navigation, route }) => {
const { displayMode } = useSettings();
const [loading, setLoading] = useState(true);
- const [novels, setNovels] = useState();
+ const [novels, setNovels] = useState([]);
+ const [page, setPage] = useState(1);
const [error, setError] = useState();
const [searchText, setSearchText] = useState("");
- const getSourceUrl = () => {
- if (sourceId === 1) {
- return `https://lnreader-extensions.vercel.app/api/1/novels/1/?o=rating`;
- } else {
- return `https://lnreader-extensions.vercel.app/api/${sourceId}/novels/`;
- }
- };
+ // const getSourceUrl = () => {
+ // if (sourceId === 1) {
+ // return `https://lnreader-extensions.vercel.app/api/1/novels/1/?o=rating`;
+ // } else {
+ // return `https://lnreader-extensions.vercel.app/api/${sourceId}/novels/`;
+ // }
+ // };
- const getSearchUrl = () => {
- if (sourceId === 1) {
- return `https://lnreader-extensions.vercel.app/api/1/search/?s=${searchText}&?o=rating`;
- } else {
- return `https://lnreader-extensions.vercel.app/api/${sourceId}/search/?s=${searchText}`;
- }
- };
+ // const getSearchUrl = () => {
+ // if (sourceId === 1) {
+ // return `https://lnreader-extensions.vercel.app/api/1/search/?s=${searchText}&?o=rating`;
+ // } else {
+ // return `https://lnreader-extensions.vercel.app/api/${sourceId}/search/?s=${searchText}`;
+ // }
+ // };
- const getNovels = async () => {
+ const getNovels = async (page) => {
try {
- const url = getSourceUrl();
+ const source = getSource(sourceId);
- const res = await fetch(url);
- const data = await res.json();
+ // console.log(page);
+ const res = await source.popularNovels(page);
- setNovels(data);
+ // console.log(res);
+
+ setNovels((novels) => novels.concat(res));
setLoading(false);
} catch (error) {
setError(error.message);
@@ -88,9 +93,28 @@ const Extension = ({ navigation, route }) => {
};
useEffect(() => {
- getNovels();
+ getNovels(1);
}, []);
+ const isCloseToBottom = ({
+ layoutMeasurement,
+ contentOffset,
+ contentSize,
+ }) => {
+ const paddingToBottom = 20;
+ return (
+ layoutMeasurement.height + contentOffset.y >=
+ contentSize.height - paddingToBottom
+ );
+ };
+
+ const onScroll = ({ nativeEvent }) => {
+ if (!searchText && isCloseToBottom(nativeEvent)) {
+ getNovels(page + 1);
+ setPage((page) => page + 1);
+ }
+ };
+
const renderItem = ({ item }) => (
{
novelName: item.novelName,
novelCover: item.novelCover,
novelUrl: item.novelUrl,
- sourceId: item.extensionId,
+ sourceId,
})
}
libraryStatus={checkIFInLibrary(item.extensionId, item.novelUrl)}
@@ -174,6 +198,14 @@ const Extension = ({ navigation, route }) => {
data={novels}
renderItem={renderItem}
ListEmptyComponent={listEmptyComponent()}
+ onScroll={onScroll}
+ ListFooterComponent={
+ !searchText && (
+
+
+
+ )
+ }
/>
)}
diff --git a/src/Services/Source/source.js b/src/Services/Source/source.js
index 3d17effa..71b6c843 100644
--- a/src/Services/Source/source.js
+++ b/src/Services/Source/source.js
@@ -1,4 +1,5 @@
import { showToast } from "../../Hooks/showToast";
+import { getSource } from "../../sources/sources";
export const fetchSources = async () => {
const url = `https://lnreader-extensions.vercel.app/api/`;
@@ -10,35 +11,35 @@ export const fetchSources = async () => {
};
export const fetchNovel = async (sourceId, novelUrl) => {
- const url = `https://lnreader-extensions.vercel.app/api/${sourceId}/novel/${novelUrl}`;
+ const source = getSource(sourceId);
- let data = await fetch(url);
- let res = await data.json();
+ const res = await source.parseNovelAndChapters(novelUrl);
const novel = {
novelUrl: res.novelUrl,
- sourceUrl: res.sourceUrl,
+ sourceUrl: res.url,
source: res.sourceName,
- sourceId: res.extensionId,
+ sourceId: res.sourceId,
novelName: res.novelName,
novelCover: res.novelCover,
- novelSummary: res.novelSummary,
- author: res["Author(s)"],
- artist: res["Artist(s)"],
- status: res.Status,
- genre: res["Genre(s)"],
+ novelSummary: res.summary,
+ author: res.author,
+ artist: res.artist,
+ status: res.status,
+ genre: res.genre,
followed: 0,
- chapters: res.novelChapters,
+ chapters: res.chapters,
};
+ // console.log(novel);
+
return novel;
};
export const fetchChapter = async (sourceId, novelUrl, chapterUrl) => {
- const url = `https://lnreader-extensions.vercel.app/api/${sourceId}/novel/${novelUrl}${chapterUrl}`;
+ const source = getSource(sourceId);
- let res = await fetch(url);
- let chapter = await res.json();
+ let chapter = await source.parseChapter(novelUrl, chapterUrl);
return chapter;
};
diff --git a/src/redux/novel/novel.actions.js b/src/redux/novel/novel.actions.js
index 429a57c5..b503067b 100644
--- a/src/redux/novel/novel.actions.js
+++ b/src/redux/novel/novel.actions.js
@@ -57,6 +57,7 @@ export const setNovel = (novel) => async (dispatch) => {
export const getNovelAction =
(followed, sourceId, novelUrl, novelId, sort, filter) =>
async (dispatch) => {
+ console.log(followed, sourceId, novelUrl, novelId, sort, filter);
try {
dispatch({ type: LOADING_NOVEL });
diff --git a/src/redux/source/source.actions.js b/src/redux/source/source.actions.js
index 0f5961fa..8014503d 100644
--- a/src/redux/source/source.actions.js
+++ b/src/redux/source/source.actions.js
@@ -1,15 +1,11 @@
import { GET_SOURCES, PIN_SOURCES, SEARCH_SOURCES } from "./source.types";
import { fetchSources } from "../../Services/Source/source";
import { showToast } from "../../Hooks/showToast";
+import { sources } from "../../sources/sources";
export const getSourcesAction = () => async (dispatch) => {
try {
- const res = await fetchSources();
-
- dispatch({
- type: GET_SOURCES,
- payload: res,
- });
+ dispatch({ type: GET_SOURCES, payload: sources });
} catch (error) {
showToast(error.message);
}
diff --git a/src/sources/en/readlightnovel.js b/src/sources/en/readlightnovel.js
index a04c1553..6778591e 100644
--- a/src/sources/en/readlightnovel.js
+++ b/src/sources/en/readlightnovel.js
@@ -54,7 +54,7 @@ const parseNovelAndChapters = async (novelUrl) => {
const novelCover = $(".novel-cover > a > img").attr("src");
- let author, artist, genre, summary;
+ let author, artist, genre, summary, status;
$(".novel-detail-item").each(function (result) {
const detailName = $(this).find(".novel-detail-header > h6").text();
@@ -62,7 +62,7 @@ const parseNovelAndChapters = async (novelUrl) => {
switch (detailName) {
case "Genre":
- genre = detail.replace(/[\t\n]/g, ",");
+ genre = detail.trim().replace(/[\t\n]/g, ",");
break;
case "Author(s)":
author = detail.trim();
@@ -73,6 +73,9 @@ const parseNovelAndChapters = async (novelUrl) => {
case "Description":
summary = detail;
break;
+ case "Status":
+ status = detail;
+ break;
}
});
@@ -111,10 +114,12 @@ const parseNovelAndChapters = async (novelUrl) => {
sourceId,
sourceName,
url,
+ novelUrl,
novelName,
novelCover,
genre,
author,
+ status,
artist,
summary,
chapters,
@@ -123,4 +128,67 @@ const parseNovelAndChapters = async (novelUrl) => {
return novel;
};
-export { popularNovels, parseNovelAndChapters };
+const parseChapter = async (novelUrl, chapterUrl) => {
+ const url = `${baseUrl}/${novelUrl}/${chapterUrl}/`;
+
+ const result = await fetch(url);
+ const body = await result.text();
+
+ $ = cheerio.load(body);
+
+ $(".block-title > h1").find("a").remove();
+
+ const chapterName = $(".block-title > h1").text().replace(" - ", "");
+
+ $(".desc")
+ .find("hr")
+ .each(function (result) {
+ $(this).remove();
+ });
+
+ $(".alert").remove();
+ $(".hidden").remove();
+
+ let chapterText = $(".desc").html();
+
+ // chapterText = parseHtml(chapterText).replace(
+ // /\n\nSponsored Content\n\n|If audio player doesn't work, press Stop then Play button again/g,
+ // ""
+ // );
+
+ let nextChapter = null;
+
+ if ($("a.next.next-link").length) {
+ nextChapter = $("a.next.next-link")
+ .attr("href")
+ .replace(`${baseUrl}/${novelUrl}/`, "");
+ }
+
+ let prevChapter = null;
+
+ if ($("a.prev.prev-link").length) {
+ prevChapter = $("a.prev.prev-link")
+ .attr("href")
+ .replace(`${baseUrl}/${novelUrl}/`, "");
+ }
+
+ const chapter = {
+ extensionId: 2,
+ novelUrl,
+ chapterUrl,
+ chapterName,
+ chapterText,
+ nextChapter,
+ prevChapter,
+ };
+
+ return chapter;
+};
+
+const ReadLightNovelScraper = {
+ popularNovels,
+ parseNovelAndChapters,
+ parseChapter,
+};
+
+export default ReadLightNovelScraper;
diff --git a/src/sources/sources.js b/src/sources/sources.js
index e69de29b..1a888cd1 100644
--- a/src/sources/sources.js
+++ b/src/sources/sources.js
@@ -0,0 +1,19 @@
+import ReadLightNovelScraper from "./en/readlightnovel";
+
+export const sources = [
+ {
+ sourceId: 2,
+ sourceName: "ReadLightNovel",
+ url: "https://www.readlightnovel.org",
+ lang: "English",
+ icon: "https://github.com/rajarsheechatterjee/lnreader-extensions/blob/main/src/en/readlightnovel/icon.png?raw=true",
+ },
+];
+
+export const getSource = (sourceId) => {
+ switch (sourceId) {
+ case 2: {
+ return ReadLightNovelScraper;
+ }
+ }
+};