diff --git a/package-lock.json b/package-lock.json
index 75fdc685..299333e0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2681,6 +2681,11 @@
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
},
+ "clamp": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
+ "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
+ },
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -7163,11 +7168,6 @@
"react-native-iphone-x-helper": "^1.3.1"
}
},
- "react-native-read-more-text": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/react-native-read-more-text/-/react-native-read-more-text-1.1.2.tgz",
- "integrity": "sha512-FIQhOpvikUanwPRl9QVBYmx6Wx7IXxXH/jlzElOLcP+NSgFLw2fRzSekQUq0yicymZsi7cAWW5TtIj8FHWtNDw=="
- },
"react-native-reanimated": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.13.2.tgz",
@@ -7435,6 +7435,15 @@
"glob": "^7.1.3"
}
},
+ "rn-sliding-up-panel": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/rn-sliding-up-panel/-/rn-sliding-up-panel-2.4.4.tgz",
+ "integrity": "sha512-NbvDiDVsRosno1t7BXMnw+ZmusznqNC0R7px9JcJNNLLfIdGmfwpJI+RCFz5t/pDSO2Jql4093W2On+mYLrX8Q==",
+ "requires": {
+ "clamp": "^1.0.1",
+ "prop-types": "^15.7.2"
+ }
+ },
"rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
diff --git a/package.json b/package.json
index cac01181..2c7dbec9 100644
--- a/package.json
+++ b/package.json
@@ -22,14 +22,14 @@
"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-gesture-handler": "^1.8.0",
"react-native-paper": "^4.5.0",
- "react-native-read-more-text": "^1.1.2",
- "react-native-reanimated": "~1.13.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-web": "~0.13.12",
+ "rn-sliding-up-panel": "^2.4.4"
},
"devDependencies": {
"@babel/core": "~7.9.0"
diff --git a/src/components/BottomSheet.js b/src/components/BottomSheet.js
new file mode 100644
index 00000000..32602e7d
--- /dev/null
+++ b/src/components/BottomSheet.js
@@ -0,0 +1,172 @@
+import React from "react";
+import { StyleSheet, View } from "react-native";
+import { List } from "react-native-paper";
+import Bottomsheet from "rn-sliding-up-panel";
+import { theme } from "../theming/theme";
+
+export const BottomSheet = ({
+ bottomSheetRef,
+ setSort,
+ sort,
+ setRefreshing,
+}) => (
+
+
+
+
+ sort === "latest" && (
+
+ )
+ }
+ title="Latest"
+ titleStyle={{
+ fontSize: 15,
+ color: theme.textColorPrimaryDark,
+ }}
+ onPress={() => setSort("latest")}
+ />
+
+ sort === "alphabet" && (
+
+ )
+ }
+ title="A-Z"
+ titleStyle={{
+ fontSize: 15,
+ color: theme.textColorPrimaryDark,
+ }}
+ onPress={() => {
+ setSort("alphabet");
+ setRefreshing(true);
+ }}
+ />
+
+ sort === "rating" && (
+
+ )
+ }
+ title="Rating"
+ titleStyle={{
+ fontSize: 15,
+ color: theme.textColorPrimaryDark,
+ }}
+ onPress={() => {
+ setSort("rating");
+ setRefreshing(true);
+ }}
+ />
+
+ sort === "trending" && (
+
+ )
+ }
+ title="Trending"
+ titleStyle={{
+ fontSize: 15,
+ color: theme.textColorPrimaryDark,
+ }}
+ onPress={() => {
+ setSort("trending");
+ setRefreshing(true);
+ }}
+ />
+
+ sort === "views" && (
+
+ )
+ }
+ title="Most Views"
+ titleStyle={{
+ fontSize: 15,
+ color: theme.textColorPrimaryDark,
+ }}
+ onPress={() => {
+ setSort("views");
+ setRefreshing(true);
+ }}
+ />
+
+ sort === "new-manga" && (
+
+ )
+ }
+ title="New"
+ titleStyle={{
+ fontSize: 15,
+ color: theme.textColorPrimaryDark,
+ }}
+ onPress={() => {
+ setSort("new-manga");
+ setRefreshing(true);
+ }}
+ />
+
+
+
+);
+
+const styles = StyleSheet.create({
+ contentContainer: {
+ flex: 1,
+ // paddingTop: ,
+ // alignItems: "center",
+ },
+});
diff --git a/src/components/NovelCover.js b/src/components/NovelCover.js
index 198e4af7..69034400 100644
--- a/src/components/NovelCover.js
+++ b/src/components/NovelCover.js
@@ -10,6 +10,7 @@ const NovelCover = ({ item }) => {
}}
style={styles.logo}
imageStyle={{ borderRadius: 6 }}
+ progressiveRenderingEnabled={true}
>
{
return (
-
+
);
};
@@ -33,18 +39,11 @@ const LatestStack = () => {
return (
-
);
};
@@ -53,8 +52,8 @@ const SearchStack = () => {
return (
{
headerShown: false,
}}
/>
-
-
);
};
@@ -86,8 +71,8 @@ const UpdatesStack = () => {
return (
@@ -98,8 +83,8 @@ const MoreStack = () => {
return (
@@ -111,11 +96,11 @@ const Router = () => {
return (
(
@@ -182,4 +167,28 @@ const Router = () => {
);
};
-export default Router;
+
+const MainStack = () => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default MainStack;
diff --git a/src/screens/About.js b/src/screens/About.js
new file mode 100644
index 00000000..cbd321ae
--- /dev/null
+++ b/src/screens/About.js
@@ -0,0 +1,68 @@
+import React from "react";
+import { List, Divider } from "react-native-paper";
+import * as Linking from "expo-linking";
+import { theme } from "../theming/theme";
+
+const AboutScreen = ({ navigation }) => {
+ return (
+ <>
+
+
+
+
+
+ Linking.openURL(
+ "https://github.com/rajarsheechatterjee/LNReader/commits/master"
+ )
+ }
+ />
+
+
+
+ Linking.openURL(
+ "https://github.com/rajarsheechatterjee/LNReader"
+ )
+ }
+ />
+
+ Linking.openURL(
+ "https://github.com/rajarsheechatterjee/LNReader-backend"
+ )
+ }
+ />
+
+ >
+ );
+};
+
+export default AboutScreen;
diff --git a/src/screens/ChapterItem.js b/src/screens/ChapterItem.js
index 204d1211..79cc8637 100644
--- a/src/screens/ChapterItem.js
+++ b/src/screens/ChapterItem.js
@@ -7,16 +7,20 @@ import {
Text,
FlatList,
ScrollView,
+ Platform,
RefreshControl,
+ StatusBar,
+ ActivityIndicator,
} from "react-native";
+
import { Appbar } from "react-native-paper";
import { theme } from "../theming/theme";
+import { CollapsibleHeaderScrollView } from "react-native-collapsible-header-views";
const ChapterItem = ({ route, navigation }) => {
const { chapterName, chapterUrl } = route.params;
const [loading, setLoading] = useState(true);
- const [refreshing, setRefreshing] = useState(true);
const [chapter, setChapter] = useState();
@@ -30,79 +34,80 @@ const ChapterItem = ({ route, navigation }) => {
.then((json) => setChapter(json))
.catch((error) => console.error(error))
.finally(() => {
- setRefreshing(false);
setLoading(false);
});
};
- const onRefresh = async () => {
- setRefreshing(true);
- getChapter();
- };
return (
<>
-
- {
- navigation.goBack();
- }}
- color={"white"}
- size={26}
- style={{ marginRight: 0 }}
- />
-
- {!loading && (
- <>
-
+ {
- navigation.navigate("ChapterItem", {
- chapterUrl: chapter.prevChapter,
- });
+ navigation.goBack();
}}
color={"white"}
- />
- {
- navigation.navigate("ChapterItem", {
- chapterUrl: chapter.nextChapter,
- });
- }}
- color={"white"}
+ style={{ marginRight: 0 }}
/>
- >
- )}
-
-
+
+ {!loading && (
+ <>
+ {
+ navigation.push("ChapterItem", {
+ chapterUrl: chapter.prevChapter,
+ });
+ }}
+ color={"white"}
+ />
+ {
+ navigation.push("ChapterItem", {
+ chapterUrl: chapter.nextChapter,
+ });
+ }}
+ color={"white"}
+ />
+ >
+ )}
+
}
+ headerHeight={100}
+ contentContainerStyle={styles.container}
>
- {!loading && (
+ {loading ? (
+
+
+
+ ) : (
{chapter.chapterText.trim()}
)}
-
+
>
);
};
@@ -111,7 +116,7 @@ export default ChapterItem;
const styles = StyleSheet.create({
container: {
- flex: 1,
+ flexGrow: 1,
backgroundColor: "#202125",
paddingHorizontal: 10,
paddingVertical: 10,
diff --git a/src/screens/Latest.js b/src/screens/Latest.js
index e41a249f..22c93b94 100644
--- a/src/screens/Latest.js
+++ b/src/screens/Latest.js
@@ -1,27 +1,48 @@
import React, { useEffect, useState } from "react";
-import { StyleSheet, View, FlatList, ActivityIndicator } from "react-native";
+import {
+ StyleSheet,
+ View,
+ FlatList,
+ ActivityIndicator,
+ RefreshControl,
+} from "react-native";
import { TouchableRipple } from "react-native-paper";
import NovelCover from "../components/NovelCover";
+import { theme } from "../theming/theme";
const LatestScreen = ({ navigation }) => {
const [loading, setLoading] = useState(true);
+ const [refreshing, setRefreshing] = useState(true);
const [novels, setNovels] = useState();
- useEffect(() => {
+ const getNovels = () => {
fetch("http://192.168.1.38:5000/api/latest")
.then((response) => response.json())
.then((json) => setNovels(json))
.catch((error) => console.error(error))
.finally(() => {
+ setRefreshing(false);
setLoading(false);
});
+ };
+
+ useEffect(() => {
+ getNovels();
}, []);
+ const onRefresh = () => {
+ setRefreshing(true);
+ getNovels();
+ };
+
return (
{loading ? (
-
+
) : (
{
)}
+ refreshControl={
+
+ }
/>
)}
diff --git a/src/screens/Library.js b/src/screens/Library.js
index fdf65203..a9439081 100644
--- a/src/screens/Library.js
+++ b/src/screens/Library.js
@@ -1,19 +1,170 @@
-import React from "react";
-import { StyleSheet, Text, View } from "react-native";
+import React, { useEffect, useState } from "react";
+import {
+ StyleSheet,
+ View,
+ FlatList,
+ ActivityIndicator,
+ RefreshControl,
+} from "react-native";
+import {
+ TouchableRipple,
+ Appbar,
+ Provider,
+ Portal,
+ Button,
+} from "react-native-paper";
+import NovelCover from "../components/NovelCover";
+import { theme } from "../theming/theme";
+import { BottomSheet } from "../components/BottomSheet";
+
+const AllNovels = ({ navigation }) => {
+ const [loading, setLoading] = useState(true);
+ const [refreshing, setRefreshing] = useState(true);
+ const [novels, setNovels] = useState();
+ const [sort, setSort] = useState("rating");
+ const [pageNo, setPageNo] = useState(2);
+
+ const getNovels = () => {
+ fetch(`http://192.168.1.38:5000/api/novels/1/?o=${sort}`)
+ .then((response) => response.json())
+ .then((json) => setNovels(json))
+ .catch((error) => console.error(error))
+ .finally(() => {
+ setPageNo(2);
+ setRefreshing(false);
+ setLoading(false);
+ });
+ };
+
+ const loadMore = () => {
+ setRefreshing(true);
+ fetch(`http://192.168.1.38:5000/api/novels/${pageNo}/?o=${sort}`)
+ .then((response) => response.json())
+ .then((json) => setNovels((novels) => novels.concat(json)))
+ .catch((error) => console.error(error))
+ .finally(() => {
+ setRefreshing(false);
+ setLoading(false);
+ setPageNo(pageNo + 1);
+ });
+ };
+
+ useEffect(() => {
+ getNovels();
+ }, [sort]);
+
+ const onRefresh = () => {
+ setRefreshing(true);
+ setPageNo(2);
+ getNovels();
+ };
-const MoreScreen = () => {
return (
-
- Library
-
+
+
+
+ {
+ _panel.show({ velocity: -1.5 });
+ }}
+ />
+
+
+ {loading ? (
+
+
+
+ ) : (
+ item.novelUrl}
+ ListFooterComponent={() => (
+
+
+
+ )}
+ renderItem={({ item }) => (
+
+ navigation.navigate("NovelItem", item)
+ }
+ >
+
+
+ )}
+ refreshControl={
+
+ }
+ />
+ )}
+
+
+ (_panel = c)}
+ setSort={setSort}
+ sort={sort}
+ setRefreshing={setRefreshing}
+ />
+
+
);
};
-export default MoreScreen;
+export default AllNovels;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#202125",
},
+ opac: {
+ height: 190,
+ flex: 1 / 3,
+ margin: 3.2,
+ },
+ contentContainer: {
+ flex: 1,
+ },
+ fab: {
+ position: "absolute",
+ margin: 16,
+ right: 0,
+ bottom: 0,
+ },
});
diff --git a/src/screens/More.js b/src/screens/More.js
index 752579e1..5ef14366 100644
--- a/src/screens/More.js
+++ b/src/screens/More.js
@@ -1,11 +1,30 @@
import React from "react";
-import { StyleSheet, Text, View } from "react-native";
+import { StyleSheet } from "react-native";
+import { List } from "react-native-paper";
+import { theme } from "../theming/theme";
-const MoreScreen = () => {
+const MoreScreen = ({ navigation }) => {
return (
-
- More
-
+
+ (
+
+ )}
+ onPress={() => navigation.navigate("About")}
+ />
+
);
};
diff --git a/src/screens/NovelItem.js b/src/screens/NovelItem.js
index 4fb2419d..729ef30f 100644
--- a/src/screens/NovelItem.js
+++ b/src/screens/NovelItem.js
@@ -9,7 +9,7 @@ import {
ScrollView,
RefreshControl,
} from "react-native";
-import { Appbar, TouchableRipple } from "react-native-paper";
+import { Appbar, TouchableRipple, IconButton } from "react-native-paper";
import { LinearGradient } from "expo-linear-gradient";
import { theme } from "../theming/theme";
@@ -21,12 +21,16 @@ const NovelItem = ({ route, navigation }) => {
const [refreshing, setRefreshing] = useState(true);
const [novel, setNovel] = useState();
+ const [chapters, setChapters] = useState();
const [more, setMore] = useState(false);
const getNovel = () => {
fetch(`http://192.168.1.38:5000/api/novel/${item.novelUrl}`)
.then((response) => response.json())
- .then((json) => setNovel(json))
+ .then((json) => {
+ setNovel(json);
+ setChapters(json.novelChapters);
+ })
.catch((error) => console.error(error))
.finally(() => {
setRefreshing(false);
@@ -59,6 +63,7 @@ const NovelItem = ({ route, navigation }) => {
titleStyle={{ color: theme.textColorPrimaryDark }}
/>
+
{
{!loading && (
<>
-
- 0 && (
+
- About
-
- setMore(!more)}
- ellipsizeMode="clip"
- >
- {novel.novelSummary}
-
- setMore(!more)}
- >
- {more ? "Less" : "More"}
-
-
-
+
+ About
+
+ setMore(!more)}
+ ellipsizeMode="clip"
+ >
+ {novel.novelSummary}
+
+ setMore(!more)}
+ >
+ {more ? "Less" : "More"}
+
+
+ )}
{
)}
/>
-
+ setChapters((chapters) => chapters.reverse())
+ }
+ rippleColor={theme.rippleColorDark}
>
- {novel.novelChapters.length + " Chapters"}
-
+ <>
+
+ {chapters.length + " Chapters"}
+
+
+ setChapters((chapters) =>
+ chapters.reverse()
+ )
+ }
+ />
+ >
+
item.chapterUrl}
removeClippedSubviews={true}
diff --git a/src/screens/Search.js b/src/screens/Search.js
index b7a5d118..e72143ac 100644
--- a/src/screens/Search.js
+++ b/src/screens/Search.js
@@ -9,6 +9,7 @@ import {
} from "react-native";
import { Appbar, TouchableRipple } from "react-native-paper";
import NovelCover from "../components/NovelCover";
+import { theme } from "../theming/theme";
const SearchScreen = ({ navigation }) => {
const [loading, setLoading] = useState(false);
@@ -25,10 +26,11 @@ const SearchScreen = ({ navigation }) => {
};
return (
<>
-
+
{
navigation.goBack();
+ setSearchText("");
setNovels([]);
}}
color={"white"}
@@ -60,7 +62,10 @@ const SearchScreen = ({ navigation }) => {
{loading ? (
-
+
) : (