Add chapter screen
This commit is contained in:
Generated
+18
@@ -3766,6 +3766,11 @@
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"optional": true
|
||||
},
|
||||
"fast-memoize": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
|
||||
"integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
|
||||
},
|
||||
"fb-watchman": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
|
||||
@@ -7124,6 +7129,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-native-collapsible-header-views": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-collapsible-header-views/-/react-native-collapsible-header-views-1.1.2.tgz",
|
||||
"integrity": "sha512-HjhIKkJa3tkPC6i1G9hh9IdjiG4Df2aB3jU1z1RbNIWBqrCa83BfkstwtbzeAADJVkTBkLTldDd6EbMybbm0DA==",
|
||||
"requires": {
|
||||
"fast-memoize": "^2.5.1"
|
||||
}
|
||||
},
|
||||
"react-native-gesture-handler": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.8.0.tgz",
|
||||
@@ -7150,6 +7163,11 @@
|
||||
"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",
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
"react": "16.13.1",
|
||||
"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-paper": "^4.5.0",
|
||||
"react-native-read-more-text": "^1.1.2",
|
||||
"react-native-reanimated": "~1.13.0",
|
||||
"react-native-safe-area-context": "3.1.9",
|
||||
"react-native-screens": "~2.15.0",
|
||||
|
||||
@@ -10,6 +10,7 @@ import Latest from "../screens/Latest";
|
||||
import More from "../screens/More";
|
||||
import Search from "../screens/Search";
|
||||
import NovelItem from "../screens/NovelItem";
|
||||
import ChapterItem from "../screens/ChapterItem";
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
@@ -70,6 +71,13 @@ const SearchStack = () => {
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ChapterItem"
|
||||
component={ChapterItem}
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Image,
|
||||
ImageBackground,
|
||||
Text,
|
||||
FlatList,
|
||||
ScrollView,
|
||||
RefreshControl,
|
||||
} from "react-native";
|
||||
import { Appbar } from "react-native-paper";
|
||||
import { theme } from "../theming/theme";
|
||||
|
||||
const ChapterItem = ({ route, navigation }) => {
|
||||
const { chapterName, chapterUrl } = route.params;
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(true);
|
||||
|
||||
const [chapter, setChapter] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
getChapter();
|
||||
}, []);
|
||||
|
||||
const getChapter = () => {
|
||||
fetch(`http://192.168.1.38:5000/api/${chapterUrl}`)
|
||||
.then((response) => response.json())
|
||||
.then((json) => setChapter(json))
|
||||
.catch((error) => console.error(error))
|
||||
.finally(() => {
|
||||
setRefreshing(false);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onRefresh = async () => {
|
||||
setRefreshing(true);
|
||||
getChapter();
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
|
||||
<Appbar.BackAction
|
||||
onPress={() => {
|
||||
navigation.goBack();
|
||||
}}
|
||||
color={"white"}
|
||||
size={26}
|
||||
style={{ marginRight: 0 }}
|
||||
/>
|
||||
<Appbar.Content
|
||||
title={loading ? "Chapter" : chapter.chapterName}
|
||||
titleStyle={{ color: theme.textColorPrimaryDark }}
|
||||
/>
|
||||
{!loading && (
|
||||
<>
|
||||
<Appbar.Action
|
||||
icon="chevron-left"
|
||||
size={26}
|
||||
disabled={!chapter.prevChapter}
|
||||
onPress={() => {
|
||||
navigation.navigate("ChapterItem", {
|
||||
chapterUrl: chapter.prevChapter,
|
||||
});
|
||||
}}
|
||||
color={"white"}
|
||||
/>
|
||||
<Appbar.Action
|
||||
icon="chevron-right"
|
||||
size={26}
|
||||
disabled={!chapter.nextChapter}
|
||||
onPress={() => {
|
||||
navigation.navigate("ChapterItem", {
|
||||
chapterUrl: chapter.nextChapter,
|
||||
});
|
||||
}}
|
||||
color={"white"}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Appbar.Header>
|
||||
<ScrollView
|
||||
style={styles.container}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
colors={["white"]}
|
||||
progressBackgroundColor={theme.colorAccentDark}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{!loading && (
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimaryDark,
|
||||
lineHeight: 20,
|
||||
}}
|
||||
>
|
||||
{chapter.chapterText.trim()}
|
||||
</Text>
|
||||
)}
|
||||
</ScrollView>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChapterItem;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#202125",
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
});
|
||||
+228
-38
@@ -3,33 +3,49 @@ import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Image,
|
||||
ActivityIndicator,
|
||||
ImageBackground,
|
||||
Text,
|
||||
FlatList,
|
||||
ScrollView,
|
||||
RefreshControl,
|
||||
} from "react-native";
|
||||
import { Appbar } from "react-native-paper";
|
||||
import { Appbar, TouchableRipple } from "react-native-paper";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
|
||||
import { theme } from "../theming/theme";
|
||||
|
||||
const NovelItem = ({ route, navigation }) => {
|
||||
const item = route.params;
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [novel, setNovel] = useState();
|
||||
const [refreshing, setRefreshing] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const [novel, setNovel] = 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))
|
||||
.catch((error) => console.error(error))
|
||||
.finally(() => {
|
||||
setRefreshing(false);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getNovel();
|
||||
}, []);
|
||||
|
||||
const onRefresh = async () => {
|
||||
setRefreshing(true);
|
||||
getNovel();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Appbar.Header style={{ backgroundColor: "#242529" }}>
|
||||
<Appbar.Header style={{ backgroundColor: theme.colorDarkPrimary }}>
|
||||
<Appbar.BackAction
|
||||
onPress={() => {
|
||||
navigation.goBack();
|
||||
@@ -40,10 +56,20 @@ const NovelItem = ({ route, navigation }) => {
|
||||
/>
|
||||
<Appbar.Content
|
||||
title={item.novelName}
|
||||
titleStyle={{ color: "white" }}
|
||||
titleStyle={{ color: theme.textColorPrimaryDark }}
|
||||
/>
|
||||
</Appbar.Header>
|
||||
<ScrollView style={styles.container}>
|
||||
<ScrollView
|
||||
style={styles.container}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
colors={["white"]}
|
||||
progressBackgroundColor={theme.colorAccentDark}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ImageBackground
|
||||
source={{
|
||||
uri: item.novelCover,
|
||||
@@ -61,44 +87,188 @@ const NovelItem = ({ route, navigation }) => {
|
||||
}}
|
||||
style={styles.logo}
|
||||
/>
|
||||
<View style={styles.nameContainer}>
|
||||
<Text
|
||||
numberOfLines={4}
|
||||
style={[
|
||||
styles.name,
|
||||
{ color: theme.textColorPrimaryDark },
|
||||
]}
|
||||
>
|
||||
{item.novelName}
|
||||
</Text>
|
||||
{!loading && (
|
||||
<>
|
||||
<Text
|
||||
style={{
|
||||
color:
|
||||
theme.textColorSecondaryDark,
|
||||
marginVertical: 3,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
{novel.novelDetails[
|
||||
"Alternative"
|
||||
].replace(",", ", ")}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color:
|
||||
theme.textColorSecondaryDark,
|
||||
marginVertical: 3,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
{novel.novelDetails[
|
||||
"Author(s)"
|
||||
].replace(",", ", ")}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color:
|
||||
theme.textColorSecondaryDark,
|
||||
marginVertical: 3,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
{novel.novelDetails["Release"]}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color:
|
||||
theme.textColorSecondaryDark,
|
||||
marginVertical: 3,
|
||||
fontSize: 16,
|
||||
}}
|
||||
>
|
||||
{novel.novelDetails["Status"] +
|
||||
" • " +
|
||||
novel.novelDetails["Type"]}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</LinearGradient>
|
||||
</ImageBackground>
|
||||
{loading ? (
|
||||
<View style={{ flex: 1, justifyContent: "center" }}>
|
||||
<ActivityIndicator size="large" color="white" />
|
||||
</View>
|
||||
) : (
|
||||
{!loading && (
|
||||
<>
|
||||
<View
|
||||
style={{ paddingHorizontal: 15, marginBottom: 10 }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimaryDark,
|
||||
marginTop: 5,
|
||||
paddingVertical: 5,
|
||||
fontSize: 15,
|
||||
}}
|
||||
>
|
||||
About
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorSecondaryDark,
|
||||
lineHeight: 20,
|
||||
}}
|
||||
numberOfLines={more ? 100 : 2}
|
||||
onPress={() => setMore(!more)}
|
||||
ellipsizeMode="clip"
|
||||
>
|
||||
{novel.novelSummary}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.colorAccentDark,
|
||||
fontWeight: "bold",
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
right: 15,
|
||||
}}
|
||||
onPress={() => setMore(!more)}
|
||||
>
|
||||
{more ? "Less" : "More"}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<FlatList
|
||||
contentContainerStyle={{
|
||||
paddingHorizontal: 15,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
horizontal
|
||||
data={novel.novelDetails["Genre(s)"].split(",")}
|
||||
keyExtractor={(item) => item}
|
||||
renderItem={({ item }) => (
|
||||
<Text
|
||||
style={[
|
||||
styles.genre,
|
||||
{
|
||||
color: theme.colorAccentDark,
|
||||
borderColor: theme.colorAccentDark,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{item}
|
||||
</Text>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimaryDark,
|
||||
paddingHorizontal: 15,
|
||||
marginTop: 5,
|
||||
paddingVertical: 5,
|
||||
fontSize: 15,
|
||||
}}
|
||||
>
|
||||
{novel.novelChapters.length + " Chapters"}
|
||||
</Text>
|
||||
<FlatList
|
||||
data={novel.novelChapters}
|
||||
showsVerticalScrollIndicator={false}
|
||||
keyExtractor={(item) => item.chapterUrl}
|
||||
removeClippedSubviews={true}
|
||||
maxToRenderPerBatch={10}
|
||||
initialNumToRender={10}
|
||||
renderItem={({ item }) => (
|
||||
<>
|
||||
<Text
|
||||
style={{
|
||||
color: "white",
|
||||
paddingLeft: 7,
|
||||
paddingRight: 7,
|
||||
paddingTop: 15,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.chapterName}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color: "grey",
|
||||
paddingLeft: 7,
|
||||
paddingBottom: 7,
|
||||
fontSize: 13,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.releaseDate}
|
||||
</Text>
|
||||
</>
|
||||
<TouchableRipple
|
||||
style={{
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 12,
|
||||
}}
|
||||
onPress={() =>
|
||||
navigation.navigate("ChapterItem", {
|
||||
chapterName: item.chapterName,
|
||||
chapterUrl: item.chapterUrl,
|
||||
})
|
||||
}
|
||||
rippleColor={theme.rippleColorDark}
|
||||
>
|
||||
<>
|
||||
<Text
|
||||
style={{
|
||||
color:
|
||||
theme.textColorPrimaryDark,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.chapterName}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
color:
|
||||
theme.textColorSecondaryDark,
|
||||
marginTop: 5,
|
||||
fontSize: 13,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.releaseDate}
|
||||
</Text>
|
||||
</>
|
||||
</TouchableRipple>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
@@ -115,6 +285,12 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
backgroundColor: "#202125",
|
||||
},
|
||||
nameContainer: {
|
||||
flex: 1,
|
||||
width: "100%",
|
||||
marginHorizontal: 15,
|
||||
// justifyContent: "center",
|
||||
},
|
||||
background: {
|
||||
height: 240,
|
||||
},
|
||||
@@ -125,7 +301,7 @@ const styles = StyleSheet.create({
|
||||
detailsContainer: {
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
margin: 17,
|
||||
margin: 15,
|
||||
},
|
||||
logo: {
|
||||
height: 180,
|
||||
@@ -133,4 +309,18 @@ const styles = StyleSheet.create({
|
||||
margin: 3.2,
|
||||
borderRadius: 6,
|
||||
},
|
||||
genre: {
|
||||
borderRadius: 24,
|
||||
borderWidth: 1,
|
||||
paddingHorizontal: 10,
|
||||
marginHorizontal: 2,
|
||||
fontSize: 13,
|
||||
paddingVertical: 1,
|
||||
justifyContent: "center",
|
||||
flex: 1,
|
||||
},
|
||||
name: {
|
||||
fontWeight: "bold",
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export const theme = {
|
||||
colorDarkPrimary: "#242529",
|
||||
colorDarkPrimaryDark: "#202125",
|
||||
colorAccentDark: "#3399FF",
|
||||
textColorPrimaryDark: "#FFFFFF",
|
||||
textColorSecondaryDark: "rgba(265,265,265,0.7)",
|
||||
textColorHintDark: "rgba(265,265,265,0.5)",
|
||||
rippleColorDark: "rgba(265,265,265,0.2)",
|
||||
};
|
||||
Reference in New Issue
Block a user