From d34b7493ce45eff420968a3ad2f208ce0a84ccdd Mon Sep 17 00:00:00 2001 From: Rajarshee Chatterjee Date: Thu, 24 Dec 2020 20:31:35 +0530 Subject: [PATCH] Add library screen --- package-lock.json | 19 +++- package.json | 8 +- src/components/BottomSheet.js | 172 ++++++++++++++++++++++++++++++++++ src/components/NovelCover.js | 1 + src/navigation/Router.js | 79 +++++++++------- src/screens/About.js | 68 ++++++++++++++ src/screens/ChapterItem.js | 111 +++++++++++----------- src/screens/Latest.js | 35 ++++++- src/screens/Library.js | 165 ++++++++++++++++++++++++++++++-- src/screens/More.js | 29 +++++- src/screens/NovelItem.js | 128 +++++++++++++++---------- src/screens/Search.js | 9 +- 12 files changed, 663 insertions(+), 161 deletions(-) create mode 100644 src/components/BottomSheet.js create mode 100644 src/screens/About.js 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 ? ( - + ) : (